##// END OF EJS Templates
util: raise ParseError when parsing dates (BC)...
Boris Feld -
r32462:bb18728e default
parent child Browse files
Show More
@@ -1,1699 +1,1699 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import getpass
13 import getpass
14 import inspect
14 import inspect
15 import os
15 import os
16 import re
16 import re
17 import signal
17 import signal
18 import socket
18 import socket
19 import subprocess
19 import subprocess
20 import sys
20 import sys
21 import tempfile
21 import tempfile
22 import traceback
22 import traceback
23
23
24 from .i18n import _
24 from .i18n import _
25 from .node import hex
25 from .node import hex
26
26
27 from . import (
27 from . import (
28 color,
28 color,
29 config,
29 config,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 progress,
33 progress,
34 pycompat,
34 pycompat,
35 rcutil,
35 rcutil,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 )
38 )
39
39
40 urlreq = util.urlreq
40 urlreq = util.urlreq
41
41
42 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
42 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
43 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
43 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
44 if not c.isalnum())
44 if not c.isalnum())
45
45
46 samplehgrcs = {
46 samplehgrcs = {
47 'user':
47 'user':
48 """# example user config (see 'hg help config' for more info)
48 """# example user config (see 'hg help config' for more info)
49 [ui]
49 [ui]
50 # name and email, e.g.
50 # name and email, e.g.
51 # username = Jane Doe <jdoe@example.com>
51 # username = Jane Doe <jdoe@example.com>
52 username =
52 username =
53
53
54 # uncomment to disable color in command output
54 # uncomment to disable color in command output
55 # (see 'hg help color' for details)
55 # (see 'hg help color' for details)
56 # color = never
56 # color = never
57
57
58 # uncomment to disable command output pagination
58 # uncomment to disable command output pagination
59 # (see 'hg help pager' for details)
59 # (see 'hg help pager' for details)
60 # paginate = never
60 # paginate = never
61
61
62 [extensions]
62 [extensions]
63 # uncomment these lines to enable some popular extensions
63 # uncomment these lines to enable some popular extensions
64 # (see 'hg help extensions' for more info)
64 # (see 'hg help extensions' for more info)
65 #
65 #
66 # churn =
66 # churn =
67 """,
67 """,
68
68
69 'cloned':
69 'cloned':
70 """# example repository config (see 'hg help config' for more info)
70 """# example repository config (see 'hg help config' for more info)
71 [paths]
71 [paths]
72 default = %s
72 default = %s
73
73
74 # path aliases to other clones of this repo in URLs or filesystem paths
74 # path aliases to other clones of this repo in URLs or filesystem paths
75 # (see 'hg help config.paths' for more info)
75 # (see 'hg help config.paths' for more info)
76 #
76 #
77 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
77 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
78 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
78 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
79 # my-clone = /home/jdoe/jdoes-clone
79 # my-clone = /home/jdoe/jdoes-clone
80
80
81 [ui]
81 [ui]
82 # name and email (local to this repository, optional), e.g.
82 # name and email (local to this repository, optional), e.g.
83 # username = Jane Doe <jdoe@example.com>
83 # username = Jane Doe <jdoe@example.com>
84 """,
84 """,
85
85
86 'local':
86 'local':
87 """# example repository config (see 'hg help config' for more info)
87 """# example repository config (see 'hg help config' for more info)
88 [paths]
88 [paths]
89 # path aliases to other clones of this repo in URLs or filesystem paths
89 # path aliases to other clones of this repo in URLs or filesystem paths
90 # (see 'hg help config.paths' for more info)
90 # (see 'hg help config.paths' for more info)
91 #
91 #
92 # default = http://example.com/hg/example-repo
92 # default = http://example.com/hg/example-repo
93 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
93 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
94 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
94 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
95 # my-clone = /home/jdoe/jdoes-clone
95 # my-clone = /home/jdoe/jdoes-clone
96
96
97 [ui]
97 [ui]
98 # name and email (local to this repository, optional), e.g.
98 # name and email (local to this repository, optional), e.g.
99 # username = Jane Doe <jdoe@example.com>
99 # username = Jane Doe <jdoe@example.com>
100 """,
100 """,
101
101
102 'global':
102 'global':
103 """# example system-wide hg config (see 'hg help config' for more info)
103 """# example system-wide hg config (see 'hg help config' for more info)
104
104
105 [ui]
105 [ui]
106 # uncomment to disable color in command output
106 # uncomment to disable color in command output
107 # (see 'hg help color' for details)
107 # (see 'hg help color' for details)
108 # color = never
108 # color = never
109
109
110 # uncomment to disable command output pagination
110 # uncomment to disable command output pagination
111 # (see 'hg help pager' for details)
111 # (see 'hg help pager' for details)
112 # paginate = never
112 # paginate = never
113
113
114 [extensions]
114 [extensions]
115 # uncomment these lines to enable some popular extensions
115 # uncomment these lines to enable some popular extensions
116 # (see 'hg help extensions' for more info)
116 # (see 'hg help extensions' for more info)
117 #
117 #
118 # blackbox =
118 # blackbox =
119 # churn =
119 # churn =
120 """,
120 """,
121 }
121 }
122
122
123
123
124 class httppasswordmgrdbproxy(object):
124 class httppasswordmgrdbproxy(object):
125 """Delays loading urllib2 until it's needed."""
125 """Delays loading urllib2 until it's needed."""
126 def __init__(self):
126 def __init__(self):
127 self._mgr = None
127 self._mgr = None
128
128
129 def _get_mgr(self):
129 def _get_mgr(self):
130 if self._mgr is None:
130 if self._mgr is None:
131 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
131 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
132 return self._mgr
132 return self._mgr
133
133
134 def add_password(self, *args, **kwargs):
134 def add_password(self, *args, **kwargs):
135 return self._get_mgr().add_password(*args, **kwargs)
135 return self._get_mgr().add_password(*args, **kwargs)
136
136
137 def find_user_password(self, *args, **kwargs):
137 def find_user_password(self, *args, **kwargs):
138 return self._get_mgr().find_user_password(*args, **kwargs)
138 return self._get_mgr().find_user_password(*args, **kwargs)
139
139
140 def _catchterm(*args):
140 def _catchterm(*args):
141 raise error.SignalInterrupt
141 raise error.SignalInterrupt
142
142
143 class ui(object):
143 class ui(object):
144 def __init__(self, src=None):
144 def __init__(self, src=None):
145 """Create a fresh new ui object if no src given
145 """Create a fresh new ui object if no src given
146
146
147 Use uimod.ui.load() to create a ui which knows global and user configs.
147 Use uimod.ui.load() to create a ui which knows global and user configs.
148 In most cases, you should use ui.copy() to create a copy of an existing
148 In most cases, you should use ui.copy() to create a copy of an existing
149 ui object.
149 ui object.
150 """
150 """
151 # _buffers: used for temporary capture of output
151 # _buffers: used for temporary capture of output
152 self._buffers = []
152 self._buffers = []
153 # _exithandlers: callbacks run at the end of a request
153 # _exithandlers: callbacks run at the end of a request
154 self._exithandlers = []
154 self._exithandlers = []
155 # 3-tuple describing how each buffer in the stack behaves.
155 # 3-tuple describing how each buffer in the stack behaves.
156 # Values are (capture stderr, capture subprocesses, apply labels).
156 # Values are (capture stderr, capture subprocesses, apply labels).
157 self._bufferstates = []
157 self._bufferstates = []
158 # When a buffer is active, defines whether we are expanding labels.
158 # When a buffer is active, defines whether we are expanding labels.
159 # This exists to prevent an extra list lookup.
159 # This exists to prevent an extra list lookup.
160 self._bufferapplylabels = None
160 self._bufferapplylabels = None
161 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
161 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
162 self._reportuntrusted = True
162 self._reportuntrusted = True
163 self._ocfg = config.config() # overlay
163 self._ocfg = config.config() # overlay
164 self._tcfg = config.config() # trusted
164 self._tcfg = config.config() # trusted
165 self._ucfg = config.config() # untrusted
165 self._ucfg = config.config() # untrusted
166 self._trustusers = set()
166 self._trustusers = set()
167 self._trustgroups = set()
167 self._trustgroups = set()
168 self.callhooks = True
168 self.callhooks = True
169 # Insecure server connections requested.
169 # Insecure server connections requested.
170 self.insecureconnections = False
170 self.insecureconnections = False
171 # Blocked time
171 # Blocked time
172 self.logblockedtimes = False
172 self.logblockedtimes = False
173 # color mode: see mercurial/color.py for possible value
173 # color mode: see mercurial/color.py for possible value
174 self._colormode = None
174 self._colormode = None
175 self._terminfoparams = {}
175 self._terminfoparams = {}
176 self._styles = {}
176 self._styles = {}
177
177
178 if src:
178 if src:
179 self._exithandlers = src._exithandlers
179 self._exithandlers = src._exithandlers
180 self.fout = src.fout
180 self.fout = src.fout
181 self.ferr = src.ferr
181 self.ferr = src.ferr
182 self.fin = src.fin
182 self.fin = src.fin
183 self.pageractive = src.pageractive
183 self.pageractive = src.pageractive
184 self._disablepager = src._disablepager
184 self._disablepager = src._disablepager
185
185
186 self._tcfg = src._tcfg.copy()
186 self._tcfg = src._tcfg.copy()
187 self._ucfg = src._ucfg.copy()
187 self._ucfg = src._ucfg.copy()
188 self._ocfg = src._ocfg.copy()
188 self._ocfg = src._ocfg.copy()
189 self._trustusers = src._trustusers.copy()
189 self._trustusers = src._trustusers.copy()
190 self._trustgroups = src._trustgroups.copy()
190 self._trustgroups = src._trustgroups.copy()
191 self.environ = src.environ
191 self.environ = src.environ
192 self.callhooks = src.callhooks
192 self.callhooks = src.callhooks
193 self.insecureconnections = src.insecureconnections
193 self.insecureconnections = src.insecureconnections
194 self._colormode = src._colormode
194 self._colormode = src._colormode
195 self._terminfoparams = src._terminfoparams.copy()
195 self._terminfoparams = src._terminfoparams.copy()
196 self._styles = src._styles.copy()
196 self._styles = src._styles.copy()
197
197
198 self.fixconfig()
198 self.fixconfig()
199
199
200 self.httppasswordmgrdb = src.httppasswordmgrdb
200 self.httppasswordmgrdb = src.httppasswordmgrdb
201 self._blockedtimes = src._blockedtimes
201 self._blockedtimes = src._blockedtimes
202 else:
202 else:
203 self.fout = util.stdout
203 self.fout = util.stdout
204 self.ferr = util.stderr
204 self.ferr = util.stderr
205 self.fin = util.stdin
205 self.fin = util.stdin
206 self.pageractive = False
206 self.pageractive = False
207 self._disablepager = False
207 self._disablepager = False
208
208
209 # shared read-only environment
209 # shared read-only environment
210 self.environ = encoding.environ
210 self.environ = encoding.environ
211
211
212 self.httppasswordmgrdb = httppasswordmgrdbproxy()
212 self.httppasswordmgrdb = httppasswordmgrdbproxy()
213 self._blockedtimes = collections.defaultdict(int)
213 self._blockedtimes = collections.defaultdict(int)
214
214
215 allowed = self.configlist('experimental', 'exportableenviron')
215 allowed = self.configlist('experimental', 'exportableenviron')
216 if '*' in allowed:
216 if '*' in allowed:
217 self._exportableenviron = self.environ
217 self._exportableenviron = self.environ
218 else:
218 else:
219 self._exportableenviron = {}
219 self._exportableenviron = {}
220 for k in allowed:
220 for k in allowed:
221 if k in self.environ:
221 if k in self.environ:
222 self._exportableenviron[k] = self.environ[k]
222 self._exportableenviron[k] = self.environ[k]
223
223
224 @classmethod
224 @classmethod
225 def load(cls):
225 def load(cls):
226 """Create a ui and load global and user configs"""
226 """Create a ui and load global and user configs"""
227 u = cls()
227 u = cls()
228 # we always trust global config files and environment variables
228 # we always trust global config files and environment variables
229 for t, f in rcutil.rccomponents():
229 for t, f in rcutil.rccomponents():
230 if t == 'path':
230 if t == 'path':
231 u.readconfig(f, trust=True)
231 u.readconfig(f, trust=True)
232 elif t == 'items':
232 elif t == 'items':
233 sections = set()
233 sections = set()
234 for section, name, value, source in f:
234 for section, name, value, source in f:
235 # do not set u._ocfg
235 # do not set u._ocfg
236 # XXX clean this up once immutable config object is a thing
236 # XXX clean this up once immutable config object is a thing
237 u._tcfg.set(section, name, value, source)
237 u._tcfg.set(section, name, value, source)
238 u._ucfg.set(section, name, value, source)
238 u._ucfg.set(section, name, value, source)
239 sections.add(section)
239 sections.add(section)
240 for section in sections:
240 for section in sections:
241 u.fixconfig(section=section)
241 u.fixconfig(section=section)
242 else:
242 else:
243 raise error.ProgrammingError('unknown rctype: %s' % t)
243 raise error.ProgrammingError('unknown rctype: %s' % t)
244 return u
244 return u
245
245
246 def copy(self):
246 def copy(self):
247 return self.__class__(self)
247 return self.__class__(self)
248
248
249 def resetstate(self):
249 def resetstate(self):
250 """Clear internal state that shouldn't persist across commands"""
250 """Clear internal state that shouldn't persist across commands"""
251 if self._progbar:
251 if self._progbar:
252 self._progbar.resetstate() # reset last-print time of progress bar
252 self._progbar.resetstate() # reset last-print time of progress bar
253 self.httppasswordmgrdb = httppasswordmgrdbproxy()
253 self.httppasswordmgrdb = httppasswordmgrdbproxy()
254
254
255 @contextlib.contextmanager
255 @contextlib.contextmanager
256 def timeblockedsection(self, key):
256 def timeblockedsection(self, key):
257 # this is open-coded below - search for timeblockedsection to find them
257 # this is open-coded below - search for timeblockedsection to find them
258 starttime = util.timer()
258 starttime = util.timer()
259 try:
259 try:
260 yield
260 yield
261 finally:
261 finally:
262 self._blockedtimes[key + '_blocked'] += \
262 self._blockedtimes[key + '_blocked'] += \
263 (util.timer() - starttime) * 1000
263 (util.timer() - starttime) * 1000
264
264
265 def formatter(self, topic, opts):
265 def formatter(self, topic, opts):
266 return formatter.formatter(self, topic, opts)
266 return formatter.formatter(self, topic, opts)
267
267
268 def _trusted(self, fp, f):
268 def _trusted(self, fp, f):
269 st = util.fstat(fp)
269 st = util.fstat(fp)
270 if util.isowner(st):
270 if util.isowner(st):
271 return True
271 return True
272
272
273 tusers, tgroups = self._trustusers, self._trustgroups
273 tusers, tgroups = self._trustusers, self._trustgroups
274 if '*' in tusers or '*' in tgroups:
274 if '*' in tusers or '*' in tgroups:
275 return True
275 return True
276
276
277 user = util.username(st.st_uid)
277 user = util.username(st.st_uid)
278 group = util.groupname(st.st_gid)
278 group = util.groupname(st.st_gid)
279 if user in tusers or group in tgroups or user == util.username():
279 if user in tusers or group in tgroups or user == util.username():
280 return True
280 return True
281
281
282 if self._reportuntrusted:
282 if self._reportuntrusted:
283 self.warn(_('not trusting file %s from untrusted '
283 self.warn(_('not trusting file %s from untrusted '
284 'user %s, group %s\n') % (f, user, group))
284 'user %s, group %s\n') % (f, user, group))
285 return False
285 return False
286
286
287 def readconfig(self, filename, root=None, trust=False,
287 def readconfig(self, filename, root=None, trust=False,
288 sections=None, remap=None):
288 sections=None, remap=None):
289 try:
289 try:
290 fp = open(filename, u'rb')
290 fp = open(filename, u'rb')
291 except IOError:
291 except IOError:
292 if not sections: # ignore unless we were looking for something
292 if not sections: # ignore unless we were looking for something
293 return
293 return
294 raise
294 raise
295
295
296 cfg = config.config()
296 cfg = config.config()
297 trusted = sections or trust or self._trusted(fp, filename)
297 trusted = sections or trust or self._trusted(fp, filename)
298
298
299 try:
299 try:
300 cfg.read(filename, fp, sections=sections, remap=remap)
300 cfg.read(filename, fp, sections=sections, remap=remap)
301 fp.close()
301 fp.close()
302 except error.ConfigError as inst:
302 except error.ConfigError as inst:
303 if trusted:
303 if trusted:
304 raise
304 raise
305 self.warn(_("ignored: %s\n") % str(inst))
305 self.warn(_("ignored: %s\n") % str(inst))
306
306
307 if self.plain():
307 if self.plain():
308 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
308 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
309 'logtemplate', 'statuscopies', 'style',
309 'logtemplate', 'statuscopies', 'style',
310 'traceback', 'verbose'):
310 'traceback', 'verbose'):
311 if k in cfg['ui']:
311 if k in cfg['ui']:
312 del cfg['ui'][k]
312 del cfg['ui'][k]
313 for k, v in cfg.items('defaults'):
313 for k, v in cfg.items('defaults'):
314 del cfg['defaults'][k]
314 del cfg['defaults'][k]
315 for k, v in cfg.items('commands'):
315 for k, v in cfg.items('commands'):
316 del cfg['commands'][k]
316 del cfg['commands'][k]
317 # Don't remove aliases from the configuration if in the exceptionlist
317 # Don't remove aliases from the configuration if in the exceptionlist
318 if self.plain('alias'):
318 if self.plain('alias'):
319 for k, v in cfg.items('alias'):
319 for k, v in cfg.items('alias'):
320 del cfg['alias'][k]
320 del cfg['alias'][k]
321 if self.plain('revsetalias'):
321 if self.plain('revsetalias'):
322 for k, v in cfg.items('revsetalias'):
322 for k, v in cfg.items('revsetalias'):
323 del cfg['revsetalias'][k]
323 del cfg['revsetalias'][k]
324 if self.plain('templatealias'):
324 if self.plain('templatealias'):
325 for k, v in cfg.items('templatealias'):
325 for k, v in cfg.items('templatealias'):
326 del cfg['templatealias'][k]
326 del cfg['templatealias'][k]
327
327
328 if trusted:
328 if trusted:
329 self._tcfg.update(cfg)
329 self._tcfg.update(cfg)
330 self._tcfg.update(self._ocfg)
330 self._tcfg.update(self._ocfg)
331 self._ucfg.update(cfg)
331 self._ucfg.update(cfg)
332 self._ucfg.update(self._ocfg)
332 self._ucfg.update(self._ocfg)
333
333
334 if root is None:
334 if root is None:
335 root = os.path.expanduser('~')
335 root = os.path.expanduser('~')
336 self.fixconfig(root=root)
336 self.fixconfig(root=root)
337
337
338 def fixconfig(self, root=None, section=None):
338 def fixconfig(self, root=None, section=None):
339 if section in (None, 'paths'):
339 if section in (None, 'paths'):
340 # expand vars and ~
340 # expand vars and ~
341 # translate paths relative to root (or home) into absolute paths
341 # translate paths relative to root (or home) into absolute paths
342 root = root or pycompat.getcwd()
342 root = root or pycompat.getcwd()
343 for c in self._tcfg, self._ucfg, self._ocfg:
343 for c in self._tcfg, self._ucfg, self._ocfg:
344 for n, p in c.items('paths'):
344 for n, p in c.items('paths'):
345 # Ignore sub-options.
345 # Ignore sub-options.
346 if ':' in n:
346 if ':' in n:
347 continue
347 continue
348 if not p:
348 if not p:
349 continue
349 continue
350 if '%%' in p:
350 if '%%' in p:
351 s = self.configsource('paths', n) or 'none'
351 s = self.configsource('paths', n) or 'none'
352 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
352 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
353 % (n, p, s))
353 % (n, p, s))
354 p = p.replace('%%', '%')
354 p = p.replace('%%', '%')
355 p = util.expandpath(p)
355 p = util.expandpath(p)
356 if not util.hasscheme(p) and not os.path.isabs(p):
356 if not util.hasscheme(p) and not os.path.isabs(p):
357 p = os.path.normpath(os.path.join(root, p))
357 p = os.path.normpath(os.path.join(root, p))
358 c.set("paths", n, p)
358 c.set("paths", n, p)
359
359
360 if section in (None, 'ui'):
360 if section in (None, 'ui'):
361 # update ui options
361 # update ui options
362 self.debugflag = self.configbool('ui', 'debug')
362 self.debugflag = self.configbool('ui', 'debug')
363 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
363 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
364 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
364 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
365 if self.verbose and self.quiet:
365 if self.verbose and self.quiet:
366 self.quiet = self.verbose = False
366 self.quiet = self.verbose = False
367 self._reportuntrusted = self.debugflag or self.configbool("ui",
367 self._reportuntrusted = self.debugflag or self.configbool("ui",
368 "report_untrusted", True)
368 "report_untrusted", True)
369 self.tracebackflag = self.configbool('ui', 'traceback', False)
369 self.tracebackflag = self.configbool('ui', 'traceback', False)
370 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
370 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
371
371
372 if section in (None, 'trusted'):
372 if section in (None, 'trusted'):
373 # update trust information
373 # update trust information
374 self._trustusers.update(self.configlist('trusted', 'users'))
374 self._trustusers.update(self.configlist('trusted', 'users'))
375 self._trustgroups.update(self.configlist('trusted', 'groups'))
375 self._trustgroups.update(self.configlist('trusted', 'groups'))
376
376
377 def backupconfig(self, section, item):
377 def backupconfig(self, section, item):
378 return (self._ocfg.backup(section, item),
378 return (self._ocfg.backup(section, item),
379 self._tcfg.backup(section, item),
379 self._tcfg.backup(section, item),
380 self._ucfg.backup(section, item),)
380 self._ucfg.backup(section, item),)
381 def restoreconfig(self, data):
381 def restoreconfig(self, data):
382 self._ocfg.restore(data[0])
382 self._ocfg.restore(data[0])
383 self._tcfg.restore(data[1])
383 self._tcfg.restore(data[1])
384 self._ucfg.restore(data[2])
384 self._ucfg.restore(data[2])
385
385
386 def setconfig(self, section, name, value, source=''):
386 def setconfig(self, section, name, value, source=''):
387 for cfg in (self._ocfg, self._tcfg, self._ucfg):
387 for cfg in (self._ocfg, self._tcfg, self._ucfg):
388 cfg.set(section, name, value, source)
388 cfg.set(section, name, value, source)
389 self.fixconfig(section=section)
389 self.fixconfig(section=section)
390
390
391 def _data(self, untrusted):
391 def _data(self, untrusted):
392 return untrusted and self._ucfg or self._tcfg
392 return untrusted and self._ucfg or self._tcfg
393
393
394 def configsource(self, section, name, untrusted=False):
394 def configsource(self, section, name, untrusted=False):
395 return self._data(untrusted).source(section, name)
395 return self._data(untrusted).source(section, name)
396
396
397 def config(self, section, name, default=None, untrusted=False):
397 def config(self, section, name, default=None, untrusted=False):
398 if isinstance(name, list):
398 if isinstance(name, list):
399 alternates = name
399 alternates = name
400 else:
400 else:
401 alternates = [name]
401 alternates = [name]
402
402
403 for n in alternates:
403 for n in alternates:
404 value = self._data(untrusted).get(section, n, None)
404 value = self._data(untrusted).get(section, n, None)
405 if value is not None:
405 if value is not None:
406 name = n
406 name = n
407 break
407 break
408 else:
408 else:
409 value = default
409 value = default
410
410
411 if self.debugflag and not untrusted and self._reportuntrusted:
411 if self.debugflag and not untrusted and self._reportuntrusted:
412 for n in alternates:
412 for n in alternates:
413 uvalue = self._ucfg.get(section, n)
413 uvalue = self._ucfg.get(section, n)
414 if uvalue is not None and uvalue != value:
414 if uvalue is not None and uvalue != value:
415 self.debug("ignoring untrusted configuration option "
415 self.debug("ignoring untrusted configuration option "
416 "%s.%s = %s\n" % (section, n, uvalue))
416 "%s.%s = %s\n" % (section, n, uvalue))
417 return value
417 return value
418
418
419 def configsuboptions(self, section, name, default=None, untrusted=False):
419 def configsuboptions(self, section, name, default=None, untrusted=False):
420 """Get a config option and all sub-options.
420 """Get a config option and all sub-options.
421
421
422 Some config options have sub-options that are declared with the
422 Some config options have sub-options that are declared with the
423 format "key:opt = value". This method is used to return the main
423 format "key:opt = value". This method is used to return the main
424 option and all its declared sub-options.
424 option and all its declared sub-options.
425
425
426 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
426 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
427 is a dict of defined sub-options where keys and values are strings.
427 is a dict of defined sub-options where keys and values are strings.
428 """
428 """
429 data = self._data(untrusted)
429 data = self._data(untrusted)
430 main = data.get(section, name, default)
430 main = data.get(section, name, default)
431 if self.debugflag and not untrusted and self._reportuntrusted:
431 if self.debugflag and not untrusted and self._reportuntrusted:
432 uvalue = self._ucfg.get(section, name)
432 uvalue = self._ucfg.get(section, name)
433 if uvalue is not None and uvalue != main:
433 if uvalue is not None and uvalue != main:
434 self.debug('ignoring untrusted configuration option '
434 self.debug('ignoring untrusted configuration option '
435 '%s.%s = %s\n' % (section, name, uvalue))
435 '%s.%s = %s\n' % (section, name, uvalue))
436
436
437 sub = {}
437 sub = {}
438 prefix = '%s:' % name
438 prefix = '%s:' % name
439 for k, v in data.items(section):
439 for k, v in data.items(section):
440 if k.startswith(prefix):
440 if k.startswith(prefix):
441 sub[k[len(prefix):]] = v
441 sub[k[len(prefix):]] = v
442
442
443 if self.debugflag and not untrusted and self._reportuntrusted:
443 if self.debugflag and not untrusted and self._reportuntrusted:
444 for k, v in sub.items():
444 for k, v in sub.items():
445 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
445 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
446 if uvalue is not None and uvalue != v:
446 if uvalue is not None and uvalue != v:
447 self.debug('ignoring untrusted configuration option '
447 self.debug('ignoring untrusted configuration option '
448 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
448 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
449
449
450 return main, sub
450 return main, sub
451
451
452 def configpath(self, section, name, default=None, untrusted=False):
452 def configpath(self, section, name, default=None, untrusted=False):
453 'get a path config item, expanded relative to repo root or config file'
453 'get a path config item, expanded relative to repo root or config file'
454 v = self.config(section, name, default, untrusted)
454 v = self.config(section, name, default, untrusted)
455 if v is None:
455 if v is None:
456 return None
456 return None
457 if not os.path.isabs(v) or "://" not in v:
457 if not os.path.isabs(v) or "://" not in v:
458 src = self.configsource(section, name, untrusted)
458 src = self.configsource(section, name, untrusted)
459 if ':' in src:
459 if ':' in src:
460 base = os.path.dirname(src.rsplit(':')[0])
460 base = os.path.dirname(src.rsplit(':')[0])
461 v = os.path.join(base, os.path.expanduser(v))
461 v = os.path.join(base, os.path.expanduser(v))
462 return v
462 return v
463
463
464 def configbool(self, section, name, default=False, untrusted=False):
464 def configbool(self, section, name, default=False, untrusted=False):
465 """parse a configuration element as a boolean
465 """parse a configuration element as a boolean
466
466
467 >>> u = ui(); s = 'foo'
467 >>> u = ui(); s = 'foo'
468 >>> u.setconfig(s, 'true', 'yes')
468 >>> u.setconfig(s, 'true', 'yes')
469 >>> u.configbool(s, 'true')
469 >>> u.configbool(s, 'true')
470 True
470 True
471 >>> u.setconfig(s, 'false', 'no')
471 >>> u.setconfig(s, 'false', 'no')
472 >>> u.configbool(s, 'false')
472 >>> u.configbool(s, 'false')
473 False
473 False
474 >>> u.configbool(s, 'unknown')
474 >>> u.configbool(s, 'unknown')
475 False
475 False
476 >>> u.configbool(s, 'unknown', True)
476 >>> u.configbool(s, 'unknown', True)
477 True
477 True
478 >>> u.setconfig(s, 'invalid', 'somevalue')
478 >>> u.setconfig(s, 'invalid', 'somevalue')
479 >>> u.configbool(s, 'invalid')
479 >>> u.configbool(s, 'invalid')
480 Traceback (most recent call last):
480 Traceback (most recent call last):
481 ...
481 ...
482 ConfigError: foo.invalid is not a boolean ('somevalue')
482 ConfigError: foo.invalid is not a boolean ('somevalue')
483 """
483 """
484
484
485 v = self.config(section, name, None, untrusted)
485 v = self.config(section, name, None, untrusted)
486 if v is None:
486 if v is None:
487 return default
487 return default
488 if isinstance(v, bool):
488 if isinstance(v, bool):
489 return v
489 return v
490 b = util.parsebool(v)
490 b = util.parsebool(v)
491 if b is None:
491 if b is None:
492 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
492 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
493 % (section, name, v))
493 % (section, name, v))
494 return b
494 return b
495
495
496 def configwith(self, convert, section, name, default=None,
496 def configwith(self, convert, section, name, default=None,
497 desc=None, untrusted=False):
497 desc=None, untrusted=False):
498 """parse a configuration element with a conversion function
498 """parse a configuration element with a conversion function
499
499
500 >>> u = ui(); s = 'foo'
500 >>> u = ui(); s = 'foo'
501 >>> u.setconfig(s, 'float1', '42')
501 >>> u.setconfig(s, 'float1', '42')
502 >>> u.configwith(float, s, 'float1')
502 >>> u.configwith(float, s, 'float1')
503 42.0
503 42.0
504 >>> u.setconfig(s, 'float2', '-4.25')
504 >>> u.setconfig(s, 'float2', '-4.25')
505 >>> u.configwith(float, s, 'float2')
505 >>> u.configwith(float, s, 'float2')
506 -4.25
506 -4.25
507 >>> u.configwith(float, s, 'unknown', 7)
507 >>> u.configwith(float, s, 'unknown', 7)
508 7
508 7
509 >>> u.setconfig(s, 'invalid', 'somevalue')
509 >>> u.setconfig(s, 'invalid', 'somevalue')
510 >>> u.configwith(float, s, 'invalid')
510 >>> u.configwith(float, s, 'invalid')
511 Traceback (most recent call last):
511 Traceback (most recent call last):
512 ...
512 ...
513 ConfigError: foo.invalid is not a valid float ('somevalue')
513 ConfigError: foo.invalid is not a valid float ('somevalue')
514 >>> u.configwith(float, s, 'invalid', desc='womble')
514 >>> u.configwith(float, s, 'invalid', desc='womble')
515 Traceback (most recent call last):
515 Traceback (most recent call last):
516 ...
516 ...
517 ConfigError: foo.invalid is not a valid womble ('somevalue')
517 ConfigError: foo.invalid is not a valid womble ('somevalue')
518 """
518 """
519
519
520 v = self.config(section, name, None, untrusted)
520 v = self.config(section, name, None, untrusted)
521 if v is None:
521 if v is None:
522 return default
522 return default
523 try:
523 try:
524 return convert(v)
524 return convert(v)
525 except ValueError:
525 except (ValueError, error.ParseError):
526 if desc is None:
526 if desc is None:
527 desc = convert.__name__
527 desc = convert.__name__
528 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
528 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
529 % (section, name, desc, v))
529 % (section, name, desc, v))
530
530
531 def configint(self, section, name, default=None, untrusted=False):
531 def configint(self, section, name, default=None, untrusted=False):
532 """parse a configuration element as an integer
532 """parse a configuration element as an integer
533
533
534 >>> u = ui(); s = 'foo'
534 >>> u = ui(); s = 'foo'
535 >>> u.setconfig(s, 'int1', '42')
535 >>> u.setconfig(s, 'int1', '42')
536 >>> u.configint(s, 'int1')
536 >>> u.configint(s, 'int1')
537 42
537 42
538 >>> u.setconfig(s, 'int2', '-42')
538 >>> u.setconfig(s, 'int2', '-42')
539 >>> u.configint(s, 'int2')
539 >>> u.configint(s, 'int2')
540 -42
540 -42
541 >>> u.configint(s, 'unknown', 7)
541 >>> u.configint(s, 'unknown', 7)
542 7
542 7
543 >>> u.setconfig(s, 'invalid', 'somevalue')
543 >>> u.setconfig(s, 'invalid', 'somevalue')
544 >>> u.configint(s, 'invalid')
544 >>> u.configint(s, 'invalid')
545 Traceback (most recent call last):
545 Traceback (most recent call last):
546 ...
546 ...
547 ConfigError: foo.invalid is not a valid integer ('somevalue')
547 ConfigError: foo.invalid is not a valid integer ('somevalue')
548 """
548 """
549
549
550 return self.configwith(int, section, name, default, 'integer',
550 return self.configwith(int, section, name, default, 'integer',
551 untrusted)
551 untrusted)
552
552
553 def configbytes(self, section, name, default=0, untrusted=False):
553 def configbytes(self, section, name, default=0, untrusted=False):
554 """parse a configuration element as a quantity in bytes
554 """parse a configuration element as a quantity in bytes
555
555
556 Units can be specified as b (bytes), k or kb (kilobytes), m or
556 Units can be specified as b (bytes), k or kb (kilobytes), m or
557 mb (megabytes), g or gb (gigabytes).
557 mb (megabytes), g or gb (gigabytes).
558
558
559 >>> u = ui(); s = 'foo'
559 >>> u = ui(); s = 'foo'
560 >>> u.setconfig(s, 'val1', '42')
560 >>> u.setconfig(s, 'val1', '42')
561 >>> u.configbytes(s, 'val1')
561 >>> u.configbytes(s, 'val1')
562 42
562 42
563 >>> u.setconfig(s, 'val2', '42.5 kb')
563 >>> u.setconfig(s, 'val2', '42.5 kb')
564 >>> u.configbytes(s, 'val2')
564 >>> u.configbytes(s, 'val2')
565 43520
565 43520
566 >>> u.configbytes(s, 'unknown', '7 MB')
566 >>> u.configbytes(s, 'unknown', '7 MB')
567 7340032
567 7340032
568 >>> u.setconfig(s, 'invalid', 'somevalue')
568 >>> u.setconfig(s, 'invalid', 'somevalue')
569 >>> u.configbytes(s, 'invalid')
569 >>> u.configbytes(s, 'invalid')
570 Traceback (most recent call last):
570 Traceback (most recent call last):
571 ...
571 ...
572 ConfigError: foo.invalid is not a byte quantity ('somevalue')
572 ConfigError: foo.invalid is not a byte quantity ('somevalue')
573 """
573 """
574
574
575 value = self.config(section, name, None, untrusted)
575 value = self.config(section, name, None, untrusted)
576 if value is None:
576 if value is None:
577 if not isinstance(default, str):
577 if not isinstance(default, str):
578 return default
578 return default
579 value = default
579 value = default
580 try:
580 try:
581 return util.sizetoint(value)
581 return util.sizetoint(value)
582 except error.ParseError:
582 except error.ParseError:
583 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
583 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
584 % (section, name, value))
584 % (section, name, value))
585
585
586 def configlist(self, section, name, default=None, untrusted=False):
586 def configlist(self, section, name, default=None, untrusted=False):
587 """parse a configuration element as a list of comma/space separated
587 """parse a configuration element as a list of comma/space separated
588 strings
588 strings
589
589
590 >>> u = ui(); s = 'foo'
590 >>> u = ui(); s = 'foo'
591 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
591 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
592 >>> u.configlist(s, 'list1')
592 >>> u.configlist(s, 'list1')
593 ['this', 'is', 'a small', 'test']
593 ['this', 'is', 'a small', 'test']
594 """
594 """
595 # default is not always a list
595 # default is not always a list
596 if isinstance(default, bytes):
596 if isinstance(default, bytes):
597 default = config.parselist(default)
597 default = config.parselist(default)
598 return self.configwith(config.parselist, section, name, default or [],
598 return self.configwith(config.parselist, section, name, default or [],
599 'list', untrusted)
599 'list', untrusted)
600
600
601 def configdate(self, section, name, default=None, untrusted=False):
601 def configdate(self, section, name, default=None, untrusted=False):
602 """parse a configuration element as a tuple of ints
602 """parse a configuration element as a tuple of ints
603
603
604 >>> u = ui(); s = 'foo'
604 >>> u = ui(); s = 'foo'
605 >>> u.setconfig(s, 'date', '0 0')
605 >>> u.setconfig(s, 'date', '0 0')
606 >>> u.configdate(s, 'date')
606 >>> u.configdate(s, 'date')
607 (0, 0)
607 (0, 0)
608 """
608 """
609 if self.config(section, name, default, untrusted):
609 if self.config(section, name, default, untrusted):
610 return self.configwith(util.rawparsedate, section, name, default,
610 return self.configwith(util.parsedate, section, name, default,
611 'date', untrusted)
611 'date', untrusted)
612 return default
612 return default
613
613
614 def hasconfig(self, section, name, untrusted=False):
614 def hasconfig(self, section, name, untrusted=False):
615 return self._data(untrusted).hasitem(section, name)
615 return self._data(untrusted).hasitem(section, name)
616
616
617 def has_section(self, section, untrusted=False):
617 def has_section(self, section, untrusted=False):
618 '''tell whether section exists in config.'''
618 '''tell whether section exists in config.'''
619 return section in self._data(untrusted)
619 return section in self._data(untrusted)
620
620
621 def configitems(self, section, untrusted=False, ignoresub=False):
621 def configitems(self, section, untrusted=False, ignoresub=False):
622 items = self._data(untrusted).items(section)
622 items = self._data(untrusted).items(section)
623 if ignoresub:
623 if ignoresub:
624 newitems = {}
624 newitems = {}
625 for k, v in items:
625 for k, v in items:
626 if ':' not in k:
626 if ':' not in k:
627 newitems[k] = v
627 newitems[k] = v
628 items = newitems.items()
628 items = newitems.items()
629 if self.debugflag and not untrusted and self._reportuntrusted:
629 if self.debugflag and not untrusted and self._reportuntrusted:
630 for k, v in self._ucfg.items(section):
630 for k, v in self._ucfg.items(section):
631 if self._tcfg.get(section, k) != v:
631 if self._tcfg.get(section, k) != v:
632 self.debug("ignoring untrusted configuration option "
632 self.debug("ignoring untrusted configuration option "
633 "%s.%s = %s\n" % (section, k, v))
633 "%s.%s = %s\n" % (section, k, v))
634 return items
634 return items
635
635
636 def walkconfig(self, untrusted=False):
636 def walkconfig(self, untrusted=False):
637 cfg = self._data(untrusted)
637 cfg = self._data(untrusted)
638 for section in cfg.sections():
638 for section in cfg.sections():
639 for name, value in self.configitems(section, untrusted):
639 for name, value in self.configitems(section, untrusted):
640 yield section, name, value
640 yield section, name, value
641
641
642 def plain(self, feature=None):
642 def plain(self, feature=None):
643 '''is plain mode active?
643 '''is plain mode active?
644
644
645 Plain mode means that all configuration variables which affect
645 Plain mode means that all configuration variables which affect
646 the behavior and output of Mercurial should be
646 the behavior and output of Mercurial should be
647 ignored. Additionally, the output should be stable,
647 ignored. Additionally, the output should be stable,
648 reproducible and suitable for use in scripts or applications.
648 reproducible and suitable for use in scripts or applications.
649
649
650 The only way to trigger plain mode is by setting either the
650 The only way to trigger plain mode is by setting either the
651 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
651 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
652
652
653 The return value can either be
653 The return value can either be
654 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
654 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
655 - True otherwise
655 - True otherwise
656 '''
656 '''
657 if ('HGPLAIN' not in encoding.environ and
657 if ('HGPLAIN' not in encoding.environ and
658 'HGPLAINEXCEPT' not in encoding.environ):
658 'HGPLAINEXCEPT' not in encoding.environ):
659 return False
659 return False
660 exceptions = encoding.environ.get('HGPLAINEXCEPT',
660 exceptions = encoding.environ.get('HGPLAINEXCEPT',
661 '').strip().split(',')
661 '').strip().split(',')
662 if feature and exceptions:
662 if feature and exceptions:
663 return feature not in exceptions
663 return feature not in exceptions
664 return True
664 return True
665
665
666 def username(self):
666 def username(self):
667 """Return default username to be used in commits.
667 """Return default username to be used in commits.
668
668
669 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
669 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
670 and stop searching if one of these is set.
670 and stop searching if one of these is set.
671 If not found and ui.askusername is True, ask the user, else use
671 If not found and ui.askusername is True, ask the user, else use
672 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
672 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
673 """
673 """
674 user = encoding.environ.get("HGUSER")
674 user = encoding.environ.get("HGUSER")
675 if user is None:
675 if user is None:
676 user = self.config("ui", ["username", "user"])
676 user = self.config("ui", ["username", "user"])
677 if user is not None:
677 if user is not None:
678 user = os.path.expandvars(user)
678 user = os.path.expandvars(user)
679 if user is None:
679 if user is None:
680 user = encoding.environ.get("EMAIL")
680 user = encoding.environ.get("EMAIL")
681 if user is None and self.configbool("ui", "askusername"):
681 if user is None and self.configbool("ui", "askusername"):
682 user = self.prompt(_("enter a commit username:"), default=None)
682 user = self.prompt(_("enter a commit username:"), default=None)
683 if user is None and not self.interactive():
683 if user is None and not self.interactive():
684 try:
684 try:
685 user = '%s@%s' % (util.getuser(), socket.getfqdn())
685 user = '%s@%s' % (util.getuser(), socket.getfqdn())
686 self.warn(_("no username found, using '%s' instead\n") % user)
686 self.warn(_("no username found, using '%s' instead\n") % user)
687 except KeyError:
687 except KeyError:
688 pass
688 pass
689 if not user:
689 if not user:
690 raise error.Abort(_('no username supplied'),
690 raise error.Abort(_('no username supplied'),
691 hint=_("use 'hg config --edit' "
691 hint=_("use 'hg config --edit' "
692 'to set your username'))
692 'to set your username'))
693 if "\n" in user:
693 if "\n" in user:
694 raise error.Abort(_("username %s contains a newline\n")
694 raise error.Abort(_("username %s contains a newline\n")
695 % repr(user))
695 % repr(user))
696 return user
696 return user
697
697
698 def shortuser(self, user):
698 def shortuser(self, user):
699 """Return a short representation of a user name or email address."""
699 """Return a short representation of a user name or email address."""
700 if not self.verbose:
700 if not self.verbose:
701 user = util.shortuser(user)
701 user = util.shortuser(user)
702 return user
702 return user
703
703
704 def expandpath(self, loc, default=None):
704 def expandpath(self, loc, default=None):
705 """Return repository location relative to cwd or from [paths]"""
705 """Return repository location relative to cwd or from [paths]"""
706 try:
706 try:
707 p = self.paths.getpath(loc)
707 p = self.paths.getpath(loc)
708 if p:
708 if p:
709 return p.rawloc
709 return p.rawloc
710 except error.RepoError:
710 except error.RepoError:
711 pass
711 pass
712
712
713 if default:
713 if default:
714 try:
714 try:
715 p = self.paths.getpath(default)
715 p = self.paths.getpath(default)
716 if p:
716 if p:
717 return p.rawloc
717 return p.rawloc
718 except error.RepoError:
718 except error.RepoError:
719 pass
719 pass
720
720
721 return loc
721 return loc
722
722
723 @util.propertycache
723 @util.propertycache
724 def paths(self):
724 def paths(self):
725 return paths(self)
725 return paths(self)
726
726
727 def pushbuffer(self, error=False, subproc=False, labeled=False):
727 def pushbuffer(self, error=False, subproc=False, labeled=False):
728 """install a buffer to capture standard output of the ui object
728 """install a buffer to capture standard output of the ui object
729
729
730 If error is True, the error output will be captured too.
730 If error is True, the error output will be captured too.
731
731
732 If subproc is True, output from subprocesses (typically hooks) will be
732 If subproc is True, output from subprocesses (typically hooks) will be
733 captured too.
733 captured too.
734
734
735 If labeled is True, any labels associated with buffered
735 If labeled is True, any labels associated with buffered
736 output will be handled. By default, this has no effect
736 output will be handled. By default, this has no effect
737 on the output returned, but extensions and GUI tools may
737 on the output returned, but extensions and GUI tools may
738 handle this argument and returned styled output. If output
738 handle this argument and returned styled output. If output
739 is being buffered so it can be captured and parsed or
739 is being buffered so it can be captured and parsed or
740 processed, labeled should not be set to True.
740 processed, labeled should not be set to True.
741 """
741 """
742 self._buffers.append([])
742 self._buffers.append([])
743 self._bufferstates.append((error, subproc, labeled))
743 self._bufferstates.append((error, subproc, labeled))
744 self._bufferapplylabels = labeled
744 self._bufferapplylabels = labeled
745
745
746 def popbuffer(self):
746 def popbuffer(self):
747 '''pop the last buffer and return the buffered output'''
747 '''pop the last buffer and return the buffered output'''
748 self._bufferstates.pop()
748 self._bufferstates.pop()
749 if self._bufferstates:
749 if self._bufferstates:
750 self._bufferapplylabels = self._bufferstates[-1][2]
750 self._bufferapplylabels = self._bufferstates[-1][2]
751 else:
751 else:
752 self._bufferapplylabels = None
752 self._bufferapplylabels = None
753
753
754 return "".join(self._buffers.pop())
754 return "".join(self._buffers.pop())
755
755
756 def write(self, *args, **opts):
756 def write(self, *args, **opts):
757 '''write args to output
757 '''write args to output
758
758
759 By default, this method simply writes to the buffer or stdout.
759 By default, this method simply writes to the buffer or stdout.
760 Color mode can be set on the UI class to have the output decorated
760 Color mode can be set on the UI class to have the output decorated
761 with color modifier before being written to stdout.
761 with color modifier before being written to stdout.
762
762
763 The color used is controlled by an optional keyword argument, "label".
763 The color used is controlled by an optional keyword argument, "label".
764 This should be a string containing label names separated by space.
764 This should be a string containing label names separated by space.
765 Label names take the form of "topic.type". For example, ui.debug()
765 Label names take the form of "topic.type". For example, ui.debug()
766 issues a label of "ui.debug".
766 issues a label of "ui.debug".
767
767
768 When labeling output for a specific command, a label of
768 When labeling output for a specific command, a label of
769 "cmdname.type" is recommended. For example, status issues
769 "cmdname.type" is recommended. For example, status issues
770 a label of "status.modified" for modified files.
770 a label of "status.modified" for modified files.
771 '''
771 '''
772 if self._buffers and not opts.get('prompt', False):
772 if self._buffers and not opts.get('prompt', False):
773 if self._bufferapplylabels:
773 if self._bufferapplylabels:
774 label = opts.get('label', '')
774 label = opts.get('label', '')
775 self._buffers[-1].extend(self.label(a, label) for a in args)
775 self._buffers[-1].extend(self.label(a, label) for a in args)
776 else:
776 else:
777 self._buffers[-1].extend(args)
777 self._buffers[-1].extend(args)
778 elif self._colormode == 'win32':
778 elif self._colormode == 'win32':
779 # windows color printing is its own can of crab, defer to
779 # windows color printing is its own can of crab, defer to
780 # the color module and that is it.
780 # the color module and that is it.
781 color.win32print(self, self._write, *args, **opts)
781 color.win32print(self, self._write, *args, **opts)
782 else:
782 else:
783 msgs = args
783 msgs = args
784 if self._colormode is not None:
784 if self._colormode is not None:
785 label = opts.get('label', '')
785 label = opts.get('label', '')
786 msgs = [self.label(a, label) for a in args]
786 msgs = [self.label(a, label) for a in args]
787 self._write(*msgs, **opts)
787 self._write(*msgs, **opts)
788
788
789 def _write(self, *msgs, **opts):
789 def _write(self, *msgs, **opts):
790 self._progclear()
790 self._progclear()
791 # opencode timeblockedsection because this is a critical path
791 # opencode timeblockedsection because this is a critical path
792 starttime = util.timer()
792 starttime = util.timer()
793 try:
793 try:
794 for a in msgs:
794 for a in msgs:
795 self.fout.write(a)
795 self.fout.write(a)
796 except IOError as err:
796 except IOError as err:
797 raise error.StdioError(err)
797 raise error.StdioError(err)
798 finally:
798 finally:
799 self._blockedtimes['stdio_blocked'] += \
799 self._blockedtimes['stdio_blocked'] += \
800 (util.timer() - starttime) * 1000
800 (util.timer() - starttime) * 1000
801
801
802 def write_err(self, *args, **opts):
802 def write_err(self, *args, **opts):
803 self._progclear()
803 self._progclear()
804 if self._bufferstates and self._bufferstates[-1][0]:
804 if self._bufferstates and self._bufferstates[-1][0]:
805 self.write(*args, **opts)
805 self.write(*args, **opts)
806 elif self._colormode == 'win32':
806 elif self._colormode == 'win32':
807 # windows color printing is its own can of crab, defer to
807 # windows color printing is its own can of crab, defer to
808 # the color module and that is it.
808 # the color module and that is it.
809 color.win32print(self, self._write_err, *args, **opts)
809 color.win32print(self, self._write_err, *args, **opts)
810 else:
810 else:
811 msgs = args
811 msgs = args
812 if self._colormode is not None:
812 if self._colormode is not None:
813 label = opts.get('label', '')
813 label = opts.get('label', '')
814 msgs = [self.label(a, label) for a in args]
814 msgs = [self.label(a, label) for a in args]
815 self._write_err(*msgs, **opts)
815 self._write_err(*msgs, **opts)
816
816
817 def _write_err(self, *msgs, **opts):
817 def _write_err(self, *msgs, **opts):
818 try:
818 try:
819 with self.timeblockedsection('stdio'):
819 with self.timeblockedsection('stdio'):
820 if not getattr(self.fout, 'closed', False):
820 if not getattr(self.fout, 'closed', False):
821 self.fout.flush()
821 self.fout.flush()
822 for a in msgs:
822 for a in msgs:
823 self.ferr.write(a)
823 self.ferr.write(a)
824 # stderr may be buffered under win32 when redirected to files,
824 # stderr may be buffered under win32 when redirected to files,
825 # including stdout.
825 # including stdout.
826 if not getattr(self.ferr, 'closed', False):
826 if not getattr(self.ferr, 'closed', False):
827 self.ferr.flush()
827 self.ferr.flush()
828 except IOError as inst:
828 except IOError as inst:
829 raise error.StdioError(inst)
829 raise error.StdioError(inst)
830
830
831 def flush(self):
831 def flush(self):
832 # opencode timeblockedsection because this is a critical path
832 # opencode timeblockedsection because this is a critical path
833 starttime = util.timer()
833 starttime = util.timer()
834 try:
834 try:
835 try:
835 try:
836 self.fout.flush()
836 self.fout.flush()
837 except IOError as err:
837 except IOError as err:
838 raise error.StdioError(err)
838 raise error.StdioError(err)
839 finally:
839 finally:
840 try:
840 try:
841 self.ferr.flush()
841 self.ferr.flush()
842 except IOError as err:
842 except IOError as err:
843 raise error.StdioError(err)
843 raise error.StdioError(err)
844 finally:
844 finally:
845 self._blockedtimes['stdio_blocked'] += \
845 self._blockedtimes['stdio_blocked'] += \
846 (util.timer() - starttime) * 1000
846 (util.timer() - starttime) * 1000
847
847
848 def _isatty(self, fh):
848 def _isatty(self, fh):
849 if self.configbool('ui', 'nontty', False):
849 if self.configbool('ui', 'nontty', False):
850 return False
850 return False
851 return util.isatty(fh)
851 return util.isatty(fh)
852
852
853 def disablepager(self):
853 def disablepager(self):
854 self._disablepager = True
854 self._disablepager = True
855
855
856 def pager(self, command):
856 def pager(self, command):
857 """Start a pager for subsequent command output.
857 """Start a pager for subsequent command output.
858
858
859 Commands which produce a long stream of output should call
859 Commands which produce a long stream of output should call
860 this function to activate the user's preferred pagination
860 this function to activate the user's preferred pagination
861 mechanism (which may be no pager). Calling this function
861 mechanism (which may be no pager). Calling this function
862 precludes any future use of interactive functionality, such as
862 precludes any future use of interactive functionality, such as
863 prompting the user or activating curses.
863 prompting the user or activating curses.
864
864
865 Args:
865 Args:
866 command: The full, non-aliased name of the command. That is, "log"
866 command: The full, non-aliased name of the command. That is, "log"
867 not "history, "summary" not "summ", etc.
867 not "history, "summary" not "summ", etc.
868 """
868 """
869 if (self._disablepager
869 if (self._disablepager
870 or self.pageractive
870 or self.pageractive
871 or command in self.configlist('pager', 'ignore')
871 or command in self.configlist('pager', 'ignore')
872 or not self.configbool('ui', 'paginate', True)
872 or not self.configbool('ui', 'paginate', True)
873 or not self.configbool('pager', 'attend-' + command, True)
873 or not self.configbool('pager', 'attend-' + command, True)
874 # TODO: if we want to allow HGPLAINEXCEPT=pager,
874 # TODO: if we want to allow HGPLAINEXCEPT=pager,
875 # formatted() will need some adjustment.
875 # formatted() will need some adjustment.
876 or not self.formatted()
876 or not self.formatted()
877 or self.plain()
877 or self.plain()
878 # TODO: expose debugger-enabled on the UI object
878 # TODO: expose debugger-enabled on the UI object
879 or '--debugger' in pycompat.sysargv):
879 or '--debugger' in pycompat.sysargv):
880 # We only want to paginate if the ui appears to be
880 # We only want to paginate if the ui appears to be
881 # interactive, the user didn't say HGPLAIN or
881 # interactive, the user didn't say HGPLAIN or
882 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
882 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
883 return
883 return
884
884
885 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
885 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
886 if not pagercmd:
886 if not pagercmd:
887 return
887 return
888
888
889 pagerenv = {}
889 pagerenv = {}
890 for name, value in rcutil.defaultpagerenv().items():
890 for name, value in rcutil.defaultpagerenv().items():
891 if name not in encoding.environ:
891 if name not in encoding.environ:
892 pagerenv[name] = value
892 pagerenv[name] = value
893
893
894 self.debug('starting pager for command %r\n' % command)
894 self.debug('starting pager for command %r\n' % command)
895 self.flush()
895 self.flush()
896
896
897 wasformatted = self.formatted()
897 wasformatted = self.formatted()
898 if util.safehasattr(signal, "SIGPIPE"):
898 if util.safehasattr(signal, "SIGPIPE"):
899 signal.signal(signal.SIGPIPE, _catchterm)
899 signal.signal(signal.SIGPIPE, _catchterm)
900 if self._runpager(pagercmd, pagerenv):
900 if self._runpager(pagercmd, pagerenv):
901 self.pageractive = True
901 self.pageractive = True
902 # Preserve the formatted-ness of the UI. This is important
902 # Preserve the formatted-ness of the UI. This is important
903 # because we mess with stdout, which might confuse
903 # because we mess with stdout, which might confuse
904 # auto-detection of things being formatted.
904 # auto-detection of things being formatted.
905 self.setconfig('ui', 'formatted', wasformatted, 'pager')
905 self.setconfig('ui', 'formatted', wasformatted, 'pager')
906 self.setconfig('ui', 'interactive', False, 'pager')
906 self.setconfig('ui', 'interactive', False, 'pager')
907
907
908 # If pagermode differs from color.mode, reconfigure color now that
908 # If pagermode differs from color.mode, reconfigure color now that
909 # pageractive is set.
909 # pageractive is set.
910 cm = self._colormode
910 cm = self._colormode
911 if cm != self.config('color', 'pagermode', cm):
911 if cm != self.config('color', 'pagermode', cm):
912 color.setup(self)
912 color.setup(self)
913 else:
913 else:
914 # If the pager can't be spawned in dispatch when --pager=on is
914 # If the pager can't be spawned in dispatch when --pager=on is
915 # given, don't try again when the command runs, to avoid a duplicate
915 # given, don't try again when the command runs, to avoid a duplicate
916 # warning about a missing pager command.
916 # warning about a missing pager command.
917 self.disablepager()
917 self.disablepager()
918
918
919 def _runpager(self, command, env=None):
919 def _runpager(self, command, env=None):
920 """Actually start the pager and set up file descriptors.
920 """Actually start the pager and set up file descriptors.
921
921
922 This is separate in part so that extensions (like chg) can
922 This is separate in part so that extensions (like chg) can
923 override how a pager is invoked.
923 override how a pager is invoked.
924 """
924 """
925 if command == 'cat':
925 if command == 'cat':
926 # Save ourselves some work.
926 # Save ourselves some work.
927 return False
927 return False
928 # If the command doesn't contain any of these characters, we
928 # If the command doesn't contain any of these characters, we
929 # assume it's a binary and exec it directly. This means for
929 # assume it's a binary and exec it directly. This means for
930 # simple pager command configurations, we can degrade
930 # simple pager command configurations, we can degrade
931 # gracefully and tell the user about their broken pager.
931 # gracefully and tell the user about their broken pager.
932 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
932 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
933
933
934 if pycompat.osname == 'nt' and not shell:
934 if pycompat.osname == 'nt' and not shell:
935 # Window's built-in `more` cannot be invoked with shell=False, but
935 # Window's built-in `more` cannot be invoked with shell=False, but
936 # its `more.com` can. Hide this implementation detail from the
936 # its `more.com` can. Hide this implementation detail from the
937 # user so we can also get sane bad PAGER behavior. MSYS has
937 # user so we can also get sane bad PAGER behavior. MSYS has
938 # `more.exe`, so do a cmd.exe style resolution of the executable to
938 # `more.exe`, so do a cmd.exe style resolution of the executable to
939 # determine which one to use.
939 # determine which one to use.
940 fullcmd = util.findexe(command)
940 fullcmd = util.findexe(command)
941 if not fullcmd:
941 if not fullcmd:
942 self.warn(_("missing pager command '%s', skipping pager\n")
942 self.warn(_("missing pager command '%s', skipping pager\n")
943 % command)
943 % command)
944 return False
944 return False
945
945
946 command = fullcmd
946 command = fullcmd
947
947
948 try:
948 try:
949 pager = subprocess.Popen(
949 pager = subprocess.Popen(
950 command, shell=shell, bufsize=-1,
950 command, shell=shell, bufsize=-1,
951 close_fds=util.closefds, stdin=subprocess.PIPE,
951 close_fds=util.closefds, stdin=subprocess.PIPE,
952 stdout=util.stdout, stderr=util.stderr,
952 stdout=util.stdout, stderr=util.stderr,
953 env=util.shellenviron(env))
953 env=util.shellenviron(env))
954 except OSError as e:
954 except OSError as e:
955 if e.errno == errno.ENOENT and not shell:
955 if e.errno == errno.ENOENT and not shell:
956 self.warn(_("missing pager command '%s', skipping pager\n")
956 self.warn(_("missing pager command '%s', skipping pager\n")
957 % command)
957 % command)
958 return False
958 return False
959 raise
959 raise
960
960
961 # back up original file descriptors
961 # back up original file descriptors
962 stdoutfd = os.dup(util.stdout.fileno())
962 stdoutfd = os.dup(util.stdout.fileno())
963 stderrfd = os.dup(util.stderr.fileno())
963 stderrfd = os.dup(util.stderr.fileno())
964
964
965 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
965 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
966 if self._isatty(util.stderr):
966 if self._isatty(util.stderr):
967 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
967 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
968
968
969 @self.atexit
969 @self.atexit
970 def killpager():
970 def killpager():
971 if util.safehasattr(signal, "SIGINT"):
971 if util.safehasattr(signal, "SIGINT"):
972 signal.signal(signal.SIGINT, signal.SIG_IGN)
972 signal.signal(signal.SIGINT, signal.SIG_IGN)
973 # restore original fds, closing pager.stdin copies in the process
973 # restore original fds, closing pager.stdin copies in the process
974 os.dup2(stdoutfd, util.stdout.fileno())
974 os.dup2(stdoutfd, util.stdout.fileno())
975 os.dup2(stderrfd, util.stderr.fileno())
975 os.dup2(stderrfd, util.stderr.fileno())
976 pager.stdin.close()
976 pager.stdin.close()
977 pager.wait()
977 pager.wait()
978
978
979 return True
979 return True
980
980
981 def atexit(self, func, *args, **kwargs):
981 def atexit(self, func, *args, **kwargs):
982 '''register a function to run after dispatching a request
982 '''register a function to run after dispatching a request
983
983
984 Handlers do not stay registered across request boundaries.'''
984 Handlers do not stay registered across request boundaries.'''
985 self._exithandlers.append((func, args, kwargs))
985 self._exithandlers.append((func, args, kwargs))
986 return func
986 return func
987
987
988 def interface(self, feature):
988 def interface(self, feature):
989 """what interface to use for interactive console features?
989 """what interface to use for interactive console features?
990
990
991 The interface is controlled by the value of `ui.interface` but also by
991 The interface is controlled by the value of `ui.interface` but also by
992 the value of feature-specific configuration. For example:
992 the value of feature-specific configuration. For example:
993
993
994 ui.interface.histedit = text
994 ui.interface.histedit = text
995 ui.interface.chunkselector = curses
995 ui.interface.chunkselector = curses
996
996
997 Here the features are "histedit" and "chunkselector".
997 Here the features are "histedit" and "chunkselector".
998
998
999 The configuration above means that the default interfaces for commands
999 The configuration above means that the default interfaces for commands
1000 is curses, the interface for histedit is text and the interface for
1000 is curses, the interface for histedit is text and the interface for
1001 selecting chunk is crecord (the best curses interface available).
1001 selecting chunk is crecord (the best curses interface available).
1002
1002
1003 Consider the following example:
1003 Consider the following example:
1004 ui.interface = curses
1004 ui.interface = curses
1005 ui.interface.histedit = text
1005 ui.interface.histedit = text
1006
1006
1007 Then histedit will use the text interface and chunkselector will use
1007 Then histedit will use the text interface and chunkselector will use
1008 the default curses interface (crecord at the moment).
1008 the default curses interface (crecord at the moment).
1009 """
1009 """
1010 alldefaults = frozenset(["text", "curses"])
1010 alldefaults = frozenset(["text", "curses"])
1011
1011
1012 featureinterfaces = {
1012 featureinterfaces = {
1013 "chunkselector": [
1013 "chunkselector": [
1014 "text",
1014 "text",
1015 "curses",
1015 "curses",
1016 ]
1016 ]
1017 }
1017 }
1018
1018
1019 # Feature-specific interface
1019 # Feature-specific interface
1020 if feature not in featureinterfaces.keys():
1020 if feature not in featureinterfaces.keys():
1021 # Programming error, not user error
1021 # Programming error, not user error
1022 raise ValueError("Unknown feature requested %s" % feature)
1022 raise ValueError("Unknown feature requested %s" % feature)
1023
1023
1024 availableinterfaces = frozenset(featureinterfaces[feature])
1024 availableinterfaces = frozenset(featureinterfaces[feature])
1025 if alldefaults > availableinterfaces:
1025 if alldefaults > availableinterfaces:
1026 # Programming error, not user error. We need a use case to
1026 # Programming error, not user error. We need a use case to
1027 # define the right thing to do here.
1027 # define the right thing to do here.
1028 raise ValueError(
1028 raise ValueError(
1029 "Feature %s does not handle all default interfaces" %
1029 "Feature %s does not handle all default interfaces" %
1030 feature)
1030 feature)
1031
1031
1032 if self.plain():
1032 if self.plain():
1033 return "text"
1033 return "text"
1034
1034
1035 # Default interface for all the features
1035 # Default interface for all the features
1036 defaultinterface = "text"
1036 defaultinterface = "text"
1037 i = self.config("ui", "interface", None)
1037 i = self.config("ui", "interface", None)
1038 if i in alldefaults:
1038 if i in alldefaults:
1039 defaultinterface = i
1039 defaultinterface = i
1040
1040
1041 choseninterface = defaultinterface
1041 choseninterface = defaultinterface
1042 f = self.config("ui", "interface.%s" % feature, None)
1042 f = self.config("ui", "interface.%s" % feature, None)
1043 if f in availableinterfaces:
1043 if f in availableinterfaces:
1044 choseninterface = f
1044 choseninterface = f
1045
1045
1046 if i is not None and defaultinterface != i:
1046 if i is not None and defaultinterface != i:
1047 if f is not None:
1047 if f is not None:
1048 self.warn(_("invalid value for ui.interface: %s\n") %
1048 self.warn(_("invalid value for ui.interface: %s\n") %
1049 (i,))
1049 (i,))
1050 else:
1050 else:
1051 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1051 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1052 (i, choseninterface))
1052 (i, choseninterface))
1053 if f is not None and choseninterface != f:
1053 if f is not None and choseninterface != f:
1054 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1054 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1055 (feature, f, choseninterface))
1055 (feature, f, choseninterface))
1056
1056
1057 return choseninterface
1057 return choseninterface
1058
1058
1059 def interactive(self):
1059 def interactive(self):
1060 '''is interactive input allowed?
1060 '''is interactive input allowed?
1061
1061
1062 An interactive session is a session where input can be reasonably read
1062 An interactive session is a session where input can be reasonably read
1063 from `sys.stdin'. If this function returns false, any attempt to read
1063 from `sys.stdin'. If this function returns false, any attempt to read
1064 from stdin should fail with an error, unless a sensible default has been
1064 from stdin should fail with an error, unless a sensible default has been
1065 specified.
1065 specified.
1066
1066
1067 Interactiveness is triggered by the value of the `ui.interactive'
1067 Interactiveness is triggered by the value of the `ui.interactive'
1068 configuration variable or - if it is unset - when `sys.stdin' points
1068 configuration variable or - if it is unset - when `sys.stdin' points
1069 to a terminal device.
1069 to a terminal device.
1070
1070
1071 This function refers to input only; for output, see `ui.formatted()'.
1071 This function refers to input only; for output, see `ui.formatted()'.
1072 '''
1072 '''
1073 i = self.configbool("ui", "interactive", None)
1073 i = self.configbool("ui", "interactive", None)
1074 if i is None:
1074 if i is None:
1075 # some environments replace stdin without implementing isatty
1075 # some environments replace stdin without implementing isatty
1076 # usually those are non-interactive
1076 # usually those are non-interactive
1077 return self._isatty(self.fin)
1077 return self._isatty(self.fin)
1078
1078
1079 return i
1079 return i
1080
1080
1081 def termwidth(self):
1081 def termwidth(self):
1082 '''how wide is the terminal in columns?
1082 '''how wide is the terminal in columns?
1083 '''
1083 '''
1084 if 'COLUMNS' in encoding.environ:
1084 if 'COLUMNS' in encoding.environ:
1085 try:
1085 try:
1086 return int(encoding.environ['COLUMNS'])
1086 return int(encoding.environ['COLUMNS'])
1087 except ValueError:
1087 except ValueError:
1088 pass
1088 pass
1089 return scmutil.termsize(self)[0]
1089 return scmutil.termsize(self)[0]
1090
1090
1091 def formatted(self):
1091 def formatted(self):
1092 '''should formatted output be used?
1092 '''should formatted output be used?
1093
1093
1094 It is often desirable to format the output to suite the output medium.
1094 It is often desirable to format the output to suite the output medium.
1095 Examples of this are truncating long lines or colorizing messages.
1095 Examples of this are truncating long lines or colorizing messages.
1096 However, this is not often not desirable when piping output into other
1096 However, this is not often not desirable when piping output into other
1097 utilities, e.g. `grep'.
1097 utilities, e.g. `grep'.
1098
1098
1099 Formatted output is triggered by the value of the `ui.formatted'
1099 Formatted output is triggered by the value of the `ui.formatted'
1100 configuration variable or - if it is unset - when `sys.stdout' points
1100 configuration variable or - if it is unset - when `sys.stdout' points
1101 to a terminal device. Please note that `ui.formatted' should be
1101 to a terminal device. Please note that `ui.formatted' should be
1102 considered an implementation detail; it is not intended for use outside
1102 considered an implementation detail; it is not intended for use outside
1103 Mercurial or its extensions.
1103 Mercurial or its extensions.
1104
1104
1105 This function refers to output only; for input, see `ui.interactive()'.
1105 This function refers to output only; for input, see `ui.interactive()'.
1106 This function always returns false when in plain mode, see `ui.plain()'.
1106 This function always returns false when in plain mode, see `ui.plain()'.
1107 '''
1107 '''
1108 if self.plain():
1108 if self.plain():
1109 return False
1109 return False
1110
1110
1111 i = self.configbool("ui", "formatted", None)
1111 i = self.configbool("ui", "formatted", None)
1112 if i is None:
1112 if i is None:
1113 # some environments replace stdout without implementing isatty
1113 # some environments replace stdout without implementing isatty
1114 # usually those are non-interactive
1114 # usually those are non-interactive
1115 return self._isatty(self.fout)
1115 return self._isatty(self.fout)
1116
1116
1117 return i
1117 return i
1118
1118
1119 def _readline(self, prompt=''):
1119 def _readline(self, prompt=''):
1120 if self._isatty(self.fin):
1120 if self._isatty(self.fin):
1121 try:
1121 try:
1122 # magically add command line editing support, where
1122 # magically add command line editing support, where
1123 # available
1123 # available
1124 import readline
1124 import readline
1125 # force demandimport to really load the module
1125 # force demandimport to really load the module
1126 readline.read_history_file
1126 readline.read_history_file
1127 # windows sometimes raises something other than ImportError
1127 # windows sometimes raises something other than ImportError
1128 except Exception:
1128 except Exception:
1129 pass
1129 pass
1130
1130
1131 # call write() so output goes through subclassed implementation
1131 # call write() so output goes through subclassed implementation
1132 # e.g. color extension on Windows
1132 # e.g. color extension on Windows
1133 self.write(prompt, prompt=True)
1133 self.write(prompt, prompt=True)
1134
1134
1135 # instead of trying to emulate raw_input, swap (self.fin,
1135 # instead of trying to emulate raw_input, swap (self.fin,
1136 # self.fout) with (sys.stdin, sys.stdout)
1136 # self.fout) with (sys.stdin, sys.stdout)
1137 oldin = sys.stdin
1137 oldin = sys.stdin
1138 oldout = sys.stdout
1138 oldout = sys.stdout
1139 sys.stdin = self.fin
1139 sys.stdin = self.fin
1140 sys.stdout = self.fout
1140 sys.stdout = self.fout
1141 # prompt ' ' must exist; otherwise readline may delete entire line
1141 # prompt ' ' must exist; otherwise readline may delete entire line
1142 # - http://bugs.python.org/issue12833
1142 # - http://bugs.python.org/issue12833
1143 with self.timeblockedsection('stdio'):
1143 with self.timeblockedsection('stdio'):
1144 line = raw_input(' ')
1144 line = raw_input(' ')
1145 sys.stdin = oldin
1145 sys.stdin = oldin
1146 sys.stdout = oldout
1146 sys.stdout = oldout
1147
1147
1148 # When stdin is in binary mode on Windows, it can cause
1148 # When stdin is in binary mode on Windows, it can cause
1149 # raw_input() to emit an extra trailing carriage return
1149 # raw_input() to emit an extra trailing carriage return
1150 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1150 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1151 line = line[:-1]
1151 line = line[:-1]
1152 return line
1152 return line
1153
1153
1154 def prompt(self, msg, default="y"):
1154 def prompt(self, msg, default="y"):
1155 """Prompt user with msg, read response.
1155 """Prompt user with msg, read response.
1156 If ui is not interactive, the default is returned.
1156 If ui is not interactive, the default is returned.
1157 """
1157 """
1158 if not self.interactive():
1158 if not self.interactive():
1159 self.write(msg, ' ', default or '', "\n")
1159 self.write(msg, ' ', default or '', "\n")
1160 return default
1160 return default
1161 try:
1161 try:
1162 r = self._readline(self.label(msg, 'ui.prompt'))
1162 r = self._readline(self.label(msg, 'ui.prompt'))
1163 if not r:
1163 if not r:
1164 r = default
1164 r = default
1165 if self.configbool('ui', 'promptecho'):
1165 if self.configbool('ui', 'promptecho'):
1166 self.write(r, "\n")
1166 self.write(r, "\n")
1167 return r
1167 return r
1168 except EOFError:
1168 except EOFError:
1169 raise error.ResponseExpected()
1169 raise error.ResponseExpected()
1170
1170
1171 @staticmethod
1171 @staticmethod
1172 def extractchoices(prompt):
1172 def extractchoices(prompt):
1173 """Extract prompt message and list of choices from specified prompt.
1173 """Extract prompt message and list of choices from specified prompt.
1174
1174
1175 This returns tuple "(message, choices)", and "choices" is the
1175 This returns tuple "(message, choices)", and "choices" is the
1176 list of tuple "(response character, text without &)".
1176 list of tuple "(response character, text without &)".
1177
1177
1178 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1178 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1179 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1179 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1180 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1180 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1181 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1181 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1182 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1182 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1183 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1183 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1184 """
1184 """
1185
1185
1186 # Sadly, the prompt string may have been built with a filename
1186 # Sadly, the prompt string may have been built with a filename
1187 # containing "$$" so let's try to find the first valid-looking
1187 # containing "$$" so let's try to find the first valid-looking
1188 # prompt to start parsing. Sadly, we also can't rely on
1188 # prompt to start parsing. Sadly, we also can't rely on
1189 # choices containing spaces, ASCII, or basically anything
1189 # choices containing spaces, ASCII, or basically anything
1190 # except an ampersand followed by a character.
1190 # except an ampersand followed by a character.
1191 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1191 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1192 msg = m.group(1)
1192 msg = m.group(1)
1193 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1193 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1194 return (msg,
1194 return (msg,
1195 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1195 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1196 for s in choices])
1196 for s in choices])
1197
1197
1198 def promptchoice(self, prompt, default=0):
1198 def promptchoice(self, prompt, default=0):
1199 """Prompt user with a message, read response, and ensure it matches
1199 """Prompt user with a message, read response, and ensure it matches
1200 one of the provided choices. The prompt is formatted as follows:
1200 one of the provided choices. The prompt is formatted as follows:
1201
1201
1202 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1202 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1203
1203
1204 The index of the choice is returned. Responses are case
1204 The index of the choice is returned. Responses are case
1205 insensitive. If ui is not interactive, the default is
1205 insensitive. If ui is not interactive, the default is
1206 returned.
1206 returned.
1207 """
1207 """
1208
1208
1209 msg, choices = self.extractchoices(prompt)
1209 msg, choices = self.extractchoices(prompt)
1210 resps = [r for r, t in choices]
1210 resps = [r for r, t in choices]
1211 while True:
1211 while True:
1212 r = self.prompt(msg, resps[default])
1212 r = self.prompt(msg, resps[default])
1213 if r.lower() in resps:
1213 if r.lower() in resps:
1214 return resps.index(r.lower())
1214 return resps.index(r.lower())
1215 self.write(_("unrecognized response\n"))
1215 self.write(_("unrecognized response\n"))
1216
1216
1217 def getpass(self, prompt=None, default=None):
1217 def getpass(self, prompt=None, default=None):
1218 if not self.interactive():
1218 if not self.interactive():
1219 return default
1219 return default
1220 try:
1220 try:
1221 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1221 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1222 # disable getpass() only if explicitly specified. it's still valid
1222 # disable getpass() only if explicitly specified. it's still valid
1223 # to interact with tty even if fin is not a tty.
1223 # to interact with tty even if fin is not a tty.
1224 with self.timeblockedsection('stdio'):
1224 with self.timeblockedsection('stdio'):
1225 if self.configbool('ui', 'nontty'):
1225 if self.configbool('ui', 'nontty'):
1226 l = self.fin.readline()
1226 l = self.fin.readline()
1227 if not l:
1227 if not l:
1228 raise EOFError
1228 raise EOFError
1229 return l.rstrip('\n')
1229 return l.rstrip('\n')
1230 else:
1230 else:
1231 return getpass.getpass('')
1231 return getpass.getpass('')
1232 except EOFError:
1232 except EOFError:
1233 raise error.ResponseExpected()
1233 raise error.ResponseExpected()
1234 def status(self, *msg, **opts):
1234 def status(self, *msg, **opts):
1235 '''write status message to output (if ui.quiet is False)
1235 '''write status message to output (if ui.quiet is False)
1236
1236
1237 This adds an output label of "ui.status".
1237 This adds an output label of "ui.status".
1238 '''
1238 '''
1239 if not self.quiet:
1239 if not self.quiet:
1240 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1240 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1241 self.write(*msg, **opts)
1241 self.write(*msg, **opts)
1242 def warn(self, *msg, **opts):
1242 def warn(self, *msg, **opts):
1243 '''write warning message to output (stderr)
1243 '''write warning message to output (stderr)
1244
1244
1245 This adds an output label of "ui.warning".
1245 This adds an output label of "ui.warning".
1246 '''
1246 '''
1247 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1247 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1248 self.write_err(*msg, **opts)
1248 self.write_err(*msg, **opts)
1249 def note(self, *msg, **opts):
1249 def note(self, *msg, **opts):
1250 '''write note to output (if ui.verbose is True)
1250 '''write note to output (if ui.verbose is True)
1251
1251
1252 This adds an output label of "ui.note".
1252 This adds an output label of "ui.note".
1253 '''
1253 '''
1254 if self.verbose:
1254 if self.verbose:
1255 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1255 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1256 self.write(*msg, **opts)
1256 self.write(*msg, **opts)
1257 def debug(self, *msg, **opts):
1257 def debug(self, *msg, **opts):
1258 '''write debug message to output (if ui.debugflag is True)
1258 '''write debug message to output (if ui.debugflag is True)
1259
1259
1260 This adds an output label of "ui.debug".
1260 This adds an output label of "ui.debug".
1261 '''
1261 '''
1262 if self.debugflag:
1262 if self.debugflag:
1263 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1263 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1264 self.write(*msg, **opts)
1264 self.write(*msg, **opts)
1265
1265
1266 def edit(self, text, user, extra=None, editform=None, pending=None,
1266 def edit(self, text, user, extra=None, editform=None, pending=None,
1267 repopath=None):
1267 repopath=None):
1268 extra_defaults = {
1268 extra_defaults = {
1269 'prefix': 'editor',
1269 'prefix': 'editor',
1270 'suffix': '.txt',
1270 'suffix': '.txt',
1271 }
1271 }
1272 if extra is not None:
1272 if extra is not None:
1273 extra_defaults.update(extra)
1273 extra_defaults.update(extra)
1274 extra = extra_defaults
1274 extra = extra_defaults
1275
1275
1276 rdir = None
1276 rdir = None
1277 if self.configbool('experimental', 'editortmpinhg'):
1277 if self.configbool('experimental', 'editortmpinhg'):
1278 rdir = repopath
1278 rdir = repopath
1279 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1279 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1280 suffix=extra['suffix'],
1280 suffix=extra['suffix'],
1281 dir=rdir)
1281 dir=rdir)
1282 try:
1282 try:
1283 f = os.fdopen(fd, r'wb')
1283 f = os.fdopen(fd, r'wb')
1284 f.write(util.tonativeeol(text))
1284 f.write(util.tonativeeol(text))
1285 f.close()
1285 f.close()
1286
1286
1287 environ = {'HGUSER': user}
1287 environ = {'HGUSER': user}
1288 if 'transplant_source' in extra:
1288 if 'transplant_source' in extra:
1289 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1289 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1290 for label in ('intermediate-source', 'source', 'rebase_source'):
1290 for label in ('intermediate-source', 'source', 'rebase_source'):
1291 if label in extra:
1291 if label in extra:
1292 environ.update({'HGREVISION': extra[label]})
1292 environ.update({'HGREVISION': extra[label]})
1293 break
1293 break
1294 if editform:
1294 if editform:
1295 environ.update({'HGEDITFORM': editform})
1295 environ.update({'HGEDITFORM': editform})
1296 if pending:
1296 if pending:
1297 environ.update({'HG_PENDING': pending})
1297 environ.update({'HG_PENDING': pending})
1298
1298
1299 editor = self.geteditor()
1299 editor = self.geteditor()
1300
1300
1301 self.system("%s \"%s\"" % (editor, name),
1301 self.system("%s \"%s\"" % (editor, name),
1302 environ=environ,
1302 environ=environ,
1303 onerr=error.Abort, errprefix=_("edit failed"),
1303 onerr=error.Abort, errprefix=_("edit failed"),
1304 blockedtag='editor')
1304 blockedtag='editor')
1305
1305
1306 f = open(name, r'rb')
1306 f = open(name, r'rb')
1307 t = util.fromnativeeol(f.read())
1307 t = util.fromnativeeol(f.read())
1308 f.close()
1308 f.close()
1309 finally:
1309 finally:
1310 os.unlink(name)
1310 os.unlink(name)
1311
1311
1312 return t
1312 return t
1313
1313
1314 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1314 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1315 blockedtag=None):
1315 blockedtag=None):
1316 '''execute shell command with appropriate output stream. command
1316 '''execute shell command with appropriate output stream. command
1317 output will be redirected if fout is not stdout.
1317 output will be redirected if fout is not stdout.
1318
1318
1319 if command fails and onerr is None, return status, else raise onerr
1319 if command fails and onerr is None, return status, else raise onerr
1320 object as exception.
1320 object as exception.
1321 '''
1321 '''
1322 if blockedtag is None:
1322 if blockedtag is None:
1323 # Long cmds tend to be because of an absolute path on cmd. Keep
1323 # Long cmds tend to be because of an absolute path on cmd. Keep
1324 # the tail end instead
1324 # the tail end instead
1325 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1325 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1326 blockedtag = 'unknown_system_' + cmdsuffix
1326 blockedtag = 'unknown_system_' + cmdsuffix
1327 out = self.fout
1327 out = self.fout
1328 if any(s[1] for s in self._bufferstates):
1328 if any(s[1] for s in self._bufferstates):
1329 out = self
1329 out = self
1330 with self.timeblockedsection(blockedtag):
1330 with self.timeblockedsection(blockedtag):
1331 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1331 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1332 if rc and onerr:
1332 if rc and onerr:
1333 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1333 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1334 util.explainexit(rc)[0])
1334 util.explainexit(rc)[0])
1335 if errprefix:
1335 if errprefix:
1336 errmsg = '%s: %s' % (errprefix, errmsg)
1336 errmsg = '%s: %s' % (errprefix, errmsg)
1337 raise onerr(errmsg)
1337 raise onerr(errmsg)
1338 return rc
1338 return rc
1339
1339
1340 def _runsystem(self, cmd, environ, cwd, out):
1340 def _runsystem(self, cmd, environ, cwd, out):
1341 """actually execute the given shell command (can be overridden by
1341 """actually execute the given shell command (can be overridden by
1342 extensions like chg)"""
1342 extensions like chg)"""
1343 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1343 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1344
1344
1345 def traceback(self, exc=None, force=False):
1345 def traceback(self, exc=None, force=False):
1346 '''print exception traceback if traceback printing enabled or forced.
1346 '''print exception traceback if traceback printing enabled or forced.
1347 only to call in exception handler. returns true if traceback
1347 only to call in exception handler. returns true if traceback
1348 printed.'''
1348 printed.'''
1349 if self.tracebackflag or force:
1349 if self.tracebackflag or force:
1350 if exc is None:
1350 if exc is None:
1351 exc = sys.exc_info()
1351 exc = sys.exc_info()
1352 cause = getattr(exc[1], 'cause', None)
1352 cause = getattr(exc[1], 'cause', None)
1353
1353
1354 if cause is not None:
1354 if cause is not None:
1355 causetb = traceback.format_tb(cause[2])
1355 causetb = traceback.format_tb(cause[2])
1356 exctb = traceback.format_tb(exc[2])
1356 exctb = traceback.format_tb(exc[2])
1357 exconly = traceback.format_exception_only(cause[0], cause[1])
1357 exconly = traceback.format_exception_only(cause[0], cause[1])
1358
1358
1359 # exclude frame where 'exc' was chained and rethrown from exctb
1359 # exclude frame where 'exc' was chained and rethrown from exctb
1360 self.write_err('Traceback (most recent call last):\n',
1360 self.write_err('Traceback (most recent call last):\n',
1361 ''.join(exctb[:-1]),
1361 ''.join(exctb[:-1]),
1362 ''.join(causetb),
1362 ''.join(causetb),
1363 ''.join(exconly))
1363 ''.join(exconly))
1364 else:
1364 else:
1365 output = traceback.format_exception(exc[0], exc[1], exc[2])
1365 output = traceback.format_exception(exc[0], exc[1], exc[2])
1366 data = r''.join(output)
1366 data = r''.join(output)
1367 if pycompat.ispy3:
1367 if pycompat.ispy3:
1368 enc = pycompat.sysstr(encoding.encoding)
1368 enc = pycompat.sysstr(encoding.encoding)
1369 data = data.encode(enc, errors=r'replace')
1369 data = data.encode(enc, errors=r'replace')
1370 self.write_err(data)
1370 self.write_err(data)
1371 return self.tracebackflag or force
1371 return self.tracebackflag or force
1372
1372
1373 def geteditor(self):
1373 def geteditor(self):
1374 '''return editor to use'''
1374 '''return editor to use'''
1375 if pycompat.sysplatform == 'plan9':
1375 if pycompat.sysplatform == 'plan9':
1376 # vi is the MIPS instruction simulator on Plan 9. We
1376 # vi is the MIPS instruction simulator on Plan 9. We
1377 # instead default to E to plumb commit messages to
1377 # instead default to E to plumb commit messages to
1378 # avoid confusion.
1378 # avoid confusion.
1379 editor = 'E'
1379 editor = 'E'
1380 else:
1380 else:
1381 editor = 'vi'
1381 editor = 'vi'
1382 return (encoding.environ.get("HGEDITOR") or
1382 return (encoding.environ.get("HGEDITOR") or
1383 self.config("ui", "editor", editor))
1383 self.config("ui", "editor", editor))
1384
1384
1385 @util.propertycache
1385 @util.propertycache
1386 def _progbar(self):
1386 def _progbar(self):
1387 """setup the progbar singleton to the ui object"""
1387 """setup the progbar singleton to the ui object"""
1388 if (self.quiet or self.debugflag
1388 if (self.quiet or self.debugflag
1389 or self.configbool('progress', 'disable', False)
1389 or self.configbool('progress', 'disable', False)
1390 or not progress.shouldprint(self)):
1390 or not progress.shouldprint(self)):
1391 return None
1391 return None
1392 return getprogbar(self)
1392 return getprogbar(self)
1393
1393
1394 def _progclear(self):
1394 def _progclear(self):
1395 """clear progress bar output if any. use it before any output"""
1395 """clear progress bar output if any. use it before any output"""
1396 if '_progbar' not in vars(self): # nothing loaded yet
1396 if '_progbar' not in vars(self): # nothing loaded yet
1397 return
1397 return
1398 if self._progbar is not None and self._progbar.printed:
1398 if self._progbar is not None and self._progbar.printed:
1399 self._progbar.clear()
1399 self._progbar.clear()
1400
1400
1401 def progress(self, topic, pos, item="", unit="", total=None):
1401 def progress(self, topic, pos, item="", unit="", total=None):
1402 '''show a progress message
1402 '''show a progress message
1403
1403
1404 By default a textual progress bar will be displayed if an operation
1404 By default a textual progress bar will be displayed if an operation
1405 takes too long. 'topic' is the current operation, 'item' is a
1405 takes too long. 'topic' is the current operation, 'item' is a
1406 non-numeric marker of the current position (i.e. the currently
1406 non-numeric marker of the current position (i.e. the currently
1407 in-process file), 'pos' is the current numeric position (i.e.
1407 in-process file), 'pos' is the current numeric position (i.e.
1408 revision, bytes, etc.), unit is a corresponding unit label,
1408 revision, bytes, etc.), unit is a corresponding unit label,
1409 and total is the highest expected pos.
1409 and total is the highest expected pos.
1410
1410
1411 Multiple nested topics may be active at a time.
1411 Multiple nested topics may be active at a time.
1412
1412
1413 All topics should be marked closed by setting pos to None at
1413 All topics should be marked closed by setting pos to None at
1414 termination.
1414 termination.
1415 '''
1415 '''
1416 if self._progbar is not None:
1416 if self._progbar is not None:
1417 self._progbar.progress(topic, pos, item=item, unit=unit,
1417 self._progbar.progress(topic, pos, item=item, unit=unit,
1418 total=total)
1418 total=total)
1419 if pos is None or not self.configbool('progress', 'debug'):
1419 if pos is None or not self.configbool('progress', 'debug'):
1420 return
1420 return
1421
1421
1422 if unit:
1422 if unit:
1423 unit = ' ' + unit
1423 unit = ' ' + unit
1424 if item:
1424 if item:
1425 item = ' ' + item
1425 item = ' ' + item
1426
1426
1427 if total:
1427 if total:
1428 pct = 100.0 * pos / total
1428 pct = 100.0 * pos / total
1429 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1429 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1430 % (topic, item, pos, total, unit, pct))
1430 % (topic, item, pos, total, unit, pct))
1431 else:
1431 else:
1432 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1432 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1433
1433
1434 def log(self, service, *msg, **opts):
1434 def log(self, service, *msg, **opts):
1435 '''hook for logging facility extensions
1435 '''hook for logging facility extensions
1436
1436
1437 service should be a readily-identifiable subsystem, which will
1437 service should be a readily-identifiable subsystem, which will
1438 allow filtering.
1438 allow filtering.
1439
1439
1440 *msg should be a newline-terminated format string to log, and
1440 *msg should be a newline-terminated format string to log, and
1441 then any values to %-format into that format string.
1441 then any values to %-format into that format string.
1442
1442
1443 **opts currently has no defined meanings.
1443 **opts currently has no defined meanings.
1444 '''
1444 '''
1445
1445
1446 def label(self, msg, label):
1446 def label(self, msg, label):
1447 '''style msg based on supplied label
1447 '''style msg based on supplied label
1448
1448
1449 If some color mode is enabled, this will add the necessary control
1449 If some color mode is enabled, this will add the necessary control
1450 characters to apply such color. In addition, 'debug' color mode adds
1450 characters to apply such color. In addition, 'debug' color mode adds
1451 markup showing which label affects a piece of text.
1451 markup showing which label affects a piece of text.
1452
1452
1453 ui.write(s, 'label') is equivalent to
1453 ui.write(s, 'label') is equivalent to
1454 ui.write(ui.label(s, 'label')).
1454 ui.write(ui.label(s, 'label')).
1455 '''
1455 '''
1456 if self._colormode is not None:
1456 if self._colormode is not None:
1457 return color.colorlabel(self, msg, label)
1457 return color.colorlabel(self, msg, label)
1458 return msg
1458 return msg
1459
1459
1460 def develwarn(self, msg, stacklevel=1, config=None):
1460 def develwarn(self, msg, stacklevel=1, config=None):
1461 """issue a developer warning message
1461 """issue a developer warning message
1462
1462
1463 Use 'stacklevel' to report the offender some layers further up in the
1463 Use 'stacklevel' to report the offender some layers further up in the
1464 stack.
1464 stack.
1465 """
1465 """
1466 if not self.configbool('devel', 'all-warnings'):
1466 if not self.configbool('devel', 'all-warnings'):
1467 if config is not None and not self.configbool('devel', config):
1467 if config is not None and not self.configbool('devel', config):
1468 return
1468 return
1469 msg = 'devel-warn: ' + msg
1469 msg = 'devel-warn: ' + msg
1470 stacklevel += 1 # get in develwarn
1470 stacklevel += 1 # get in develwarn
1471 if self.tracebackflag:
1471 if self.tracebackflag:
1472 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1472 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1473 self.log('develwarn', '%s at:\n%s' %
1473 self.log('develwarn', '%s at:\n%s' %
1474 (msg, ''.join(util.getstackframes(stacklevel))))
1474 (msg, ''.join(util.getstackframes(stacklevel))))
1475 else:
1475 else:
1476 curframe = inspect.currentframe()
1476 curframe = inspect.currentframe()
1477 calframe = inspect.getouterframes(curframe, 2)
1477 calframe = inspect.getouterframes(curframe, 2)
1478 self.write_err('%s at: %s:%s (%s)\n'
1478 self.write_err('%s at: %s:%s (%s)\n'
1479 % ((msg,) + calframe[stacklevel][1:4]))
1479 % ((msg,) + calframe[stacklevel][1:4]))
1480 self.log('develwarn', '%s at: %s:%s (%s)\n',
1480 self.log('develwarn', '%s at: %s:%s (%s)\n',
1481 msg, *calframe[stacklevel][1:4])
1481 msg, *calframe[stacklevel][1:4])
1482 curframe = calframe = None # avoid cycles
1482 curframe = calframe = None # avoid cycles
1483
1483
1484 def deprecwarn(self, msg, version):
1484 def deprecwarn(self, msg, version):
1485 """issue a deprecation warning
1485 """issue a deprecation warning
1486
1486
1487 - msg: message explaining what is deprecated and how to upgrade,
1487 - msg: message explaining what is deprecated and how to upgrade,
1488 - version: last version where the API will be supported,
1488 - version: last version where the API will be supported,
1489 """
1489 """
1490 if not (self.configbool('devel', 'all-warnings')
1490 if not (self.configbool('devel', 'all-warnings')
1491 or self.configbool('devel', 'deprec-warn')):
1491 or self.configbool('devel', 'deprec-warn')):
1492 return
1492 return
1493 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1493 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1494 " update your code.)") % version
1494 " update your code.)") % version
1495 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1495 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1496
1496
1497 def exportableenviron(self):
1497 def exportableenviron(self):
1498 """The environment variables that are safe to export, e.g. through
1498 """The environment variables that are safe to export, e.g. through
1499 hgweb.
1499 hgweb.
1500 """
1500 """
1501 return self._exportableenviron
1501 return self._exportableenviron
1502
1502
1503 @contextlib.contextmanager
1503 @contextlib.contextmanager
1504 def configoverride(self, overrides, source=""):
1504 def configoverride(self, overrides, source=""):
1505 """Context manager for temporary config overrides
1505 """Context manager for temporary config overrides
1506 `overrides` must be a dict of the following structure:
1506 `overrides` must be a dict of the following structure:
1507 {(section, name) : value}"""
1507 {(section, name) : value}"""
1508 backups = {}
1508 backups = {}
1509 try:
1509 try:
1510 for (section, name), value in overrides.items():
1510 for (section, name), value in overrides.items():
1511 backups[(section, name)] = self.backupconfig(section, name)
1511 backups[(section, name)] = self.backupconfig(section, name)
1512 self.setconfig(section, name, value, source)
1512 self.setconfig(section, name, value, source)
1513 yield
1513 yield
1514 finally:
1514 finally:
1515 for __, backup in backups.items():
1515 for __, backup in backups.items():
1516 self.restoreconfig(backup)
1516 self.restoreconfig(backup)
1517 # just restoring ui.quiet config to the previous value is not enough
1517 # just restoring ui.quiet config to the previous value is not enough
1518 # as it does not update ui.quiet class member
1518 # as it does not update ui.quiet class member
1519 if ('ui', 'quiet') in overrides:
1519 if ('ui', 'quiet') in overrides:
1520 self.fixconfig(section='ui')
1520 self.fixconfig(section='ui')
1521
1521
1522 class paths(dict):
1522 class paths(dict):
1523 """Represents a collection of paths and their configs.
1523 """Represents a collection of paths and their configs.
1524
1524
1525 Data is initially derived from ui instances and the config files they have
1525 Data is initially derived from ui instances and the config files they have
1526 loaded.
1526 loaded.
1527 """
1527 """
1528 def __init__(self, ui):
1528 def __init__(self, ui):
1529 dict.__init__(self)
1529 dict.__init__(self)
1530
1530
1531 for name, loc in ui.configitems('paths', ignoresub=True):
1531 for name, loc in ui.configitems('paths', ignoresub=True):
1532 # No location is the same as not existing.
1532 # No location is the same as not existing.
1533 if not loc:
1533 if not loc:
1534 continue
1534 continue
1535 loc, sub = ui.configsuboptions('paths', name)
1535 loc, sub = ui.configsuboptions('paths', name)
1536 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1536 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1537
1537
1538 def getpath(self, name, default=None):
1538 def getpath(self, name, default=None):
1539 """Return a ``path`` from a string, falling back to default.
1539 """Return a ``path`` from a string, falling back to default.
1540
1540
1541 ``name`` can be a named path or locations. Locations are filesystem
1541 ``name`` can be a named path or locations. Locations are filesystem
1542 paths or URIs.
1542 paths or URIs.
1543
1543
1544 Returns None if ``name`` is not a registered path, a URI, or a local
1544 Returns None if ``name`` is not a registered path, a URI, or a local
1545 path to a repo.
1545 path to a repo.
1546 """
1546 """
1547 # Only fall back to default if no path was requested.
1547 # Only fall back to default if no path was requested.
1548 if name is None:
1548 if name is None:
1549 if not default:
1549 if not default:
1550 default = ()
1550 default = ()
1551 elif not isinstance(default, (tuple, list)):
1551 elif not isinstance(default, (tuple, list)):
1552 default = (default,)
1552 default = (default,)
1553 for k in default:
1553 for k in default:
1554 try:
1554 try:
1555 return self[k]
1555 return self[k]
1556 except KeyError:
1556 except KeyError:
1557 continue
1557 continue
1558 return None
1558 return None
1559
1559
1560 # Most likely empty string.
1560 # Most likely empty string.
1561 # This may need to raise in the future.
1561 # This may need to raise in the future.
1562 if not name:
1562 if not name:
1563 return None
1563 return None
1564
1564
1565 try:
1565 try:
1566 return self[name]
1566 return self[name]
1567 except KeyError:
1567 except KeyError:
1568 # Try to resolve as a local path or URI.
1568 # Try to resolve as a local path or URI.
1569 try:
1569 try:
1570 # We don't pass sub-options in, so no need to pass ui instance.
1570 # We don't pass sub-options in, so no need to pass ui instance.
1571 return path(None, None, rawloc=name)
1571 return path(None, None, rawloc=name)
1572 except ValueError:
1572 except ValueError:
1573 raise error.RepoError(_('repository %s does not exist') %
1573 raise error.RepoError(_('repository %s does not exist') %
1574 name)
1574 name)
1575
1575
1576 _pathsuboptions = {}
1576 _pathsuboptions = {}
1577
1577
1578 def pathsuboption(option, attr):
1578 def pathsuboption(option, attr):
1579 """Decorator used to declare a path sub-option.
1579 """Decorator used to declare a path sub-option.
1580
1580
1581 Arguments are the sub-option name and the attribute it should set on
1581 Arguments are the sub-option name and the attribute it should set on
1582 ``path`` instances.
1582 ``path`` instances.
1583
1583
1584 The decorated function will receive as arguments a ``ui`` instance,
1584 The decorated function will receive as arguments a ``ui`` instance,
1585 ``path`` instance, and the string value of this option from the config.
1585 ``path`` instance, and the string value of this option from the config.
1586 The function should return the value that will be set on the ``path``
1586 The function should return the value that will be set on the ``path``
1587 instance.
1587 instance.
1588
1588
1589 This decorator can be used to perform additional verification of
1589 This decorator can be used to perform additional verification of
1590 sub-options and to change the type of sub-options.
1590 sub-options and to change the type of sub-options.
1591 """
1591 """
1592 def register(func):
1592 def register(func):
1593 _pathsuboptions[option] = (attr, func)
1593 _pathsuboptions[option] = (attr, func)
1594 return func
1594 return func
1595 return register
1595 return register
1596
1596
1597 @pathsuboption('pushurl', 'pushloc')
1597 @pathsuboption('pushurl', 'pushloc')
1598 def pushurlpathoption(ui, path, value):
1598 def pushurlpathoption(ui, path, value):
1599 u = util.url(value)
1599 u = util.url(value)
1600 # Actually require a URL.
1600 # Actually require a URL.
1601 if not u.scheme:
1601 if not u.scheme:
1602 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1602 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1603 return None
1603 return None
1604
1604
1605 # Don't support the #foo syntax in the push URL to declare branch to
1605 # Don't support the #foo syntax in the push URL to declare branch to
1606 # push.
1606 # push.
1607 if u.fragment:
1607 if u.fragment:
1608 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1608 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1609 'ignoring)\n') % path.name)
1609 'ignoring)\n') % path.name)
1610 u.fragment = None
1610 u.fragment = None
1611
1611
1612 return str(u)
1612 return str(u)
1613
1613
1614 @pathsuboption('pushrev', 'pushrev')
1614 @pathsuboption('pushrev', 'pushrev')
1615 def pushrevpathoption(ui, path, value):
1615 def pushrevpathoption(ui, path, value):
1616 return value
1616 return value
1617
1617
1618 class path(object):
1618 class path(object):
1619 """Represents an individual path and its configuration."""
1619 """Represents an individual path and its configuration."""
1620
1620
1621 def __init__(self, ui, name, rawloc=None, suboptions=None):
1621 def __init__(self, ui, name, rawloc=None, suboptions=None):
1622 """Construct a path from its config options.
1622 """Construct a path from its config options.
1623
1623
1624 ``ui`` is the ``ui`` instance the path is coming from.
1624 ``ui`` is the ``ui`` instance the path is coming from.
1625 ``name`` is the symbolic name of the path.
1625 ``name`` is the symbolic name of the path.
1626 ``rawloc`` is the raw location, as defined in the config.
1626 ``rawloc`` is the raw location, as defined in the config.
1627 ``pushloc`` is the raw locations pushes should be made to.
1627 ``pushloc`` is the raw locations pushes should be made to.
1628
1628
1629 If ``name`` is not defined, we require that the location be a) a local
1629 If ``name`` is not defined, we require that the location be a) a local
1630 filesystem path with a .hg directory or b) a URL. If not,
1630 filesystem path with a .hg directory or b) a URL. If not,
1631 ``ValueError`` is raised.
1631 ``ValueError`` is raised.
1632 """
1632 """
1633 if not rawloc:
1633 if not rawloc:
1634 raise ValueError('rawloc must be defined')
1634 raise ValueError('rawloc must be defined')
1635
1635
1636 # Locations may define branches via syntax <base>#<branch>.
1636 # Locations may define branches via syntax <base>#<branch>.
1637 u = util.url(rawloc)
1637 u = util.url(rawloc)
1638 branch = None
1638 branch = None
1639 if u.fragment:
1639 if u.fragment:
1640 branch = u.fragment
1640 branch = u.fragment
1641 u.fragment = None
1641 u.fragment = None
1642
1642
1643 self.url = u
1643 self.url = u
1644 self.branch = branch
1644 self.branch = branch
1645
1645
1646 self.name = name
1646 self.name = name
1647 self.rawloc = rawloc
1647 self.rawloc = rawloc
1648 self.loc = '%s' % u
1648 self.loc = '%s' % u
1649
1649
1650 # When given a raw location but not a symbolic name, validate the
1650 # When given a raw location but not a symbolic name, validate the
1651 # location is valid.
1651 # location is valid.
1652 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1652 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1653 raise ValueError('location is not a URL or path to a local '
1653 raise ValueError('location is not a URL or path to a local '
1654 'repo: %s' % rawloc)
1654 'repo: %s' % rawloc)
1655
1655
1656 suboptions = suboptions or {}
1656 suboptions = suboptions or {}
1657
1657
1658 # Now process the sub-options. If a sub-option is registered, its
1658 # Now process the sub-options. If a sub-option is registered, its
1659 # attribute will always be present. The value will be None if there
1659 # attribute will always be present. The value will be None if there
1660 # was no valid sub-option.
1660 # was no valid sub-option.
1661 for suboption, (attr, func) in _pathsuboptions.iteritems():
1661 for suboption, (attr, func) in _pathsuboptions.iteritems():
1662 if suboption not in suboptions:
1662 if suboption not in suboptions:
1663 setattr(self, attr, None)
1663 setattr(self, attr, None)
1664 continue
1664 continue
1665
1665
1666 value = func(ui, self, suboptions[suboption])
1666 value = func(ui, self, suboptions[suboption])
1667 setattr(self, attr, value)
1667 setattr(self, attr, value)
1668
1668
1669 def _isvalidlocalpath(self, path):
1669 def _isvalidlocalpath(self, path):
1670 """Returns True if the given path is a potentially valid repository.
1670 """Returns True if the given path is a potentially valid repository.
1671 This is its own function so that extensions can change the definition of
1671 This is its own function so that extensions can change the definition of
1672 'valid' in this case (like when pulling from a git repo into a hg
1672 'valid' in this case (like when pulling from a git repo into a hg
1673 one)."""
1673 one)."""
1674 return os.path.isdir(os.path.join(path, '.hg'))
1674 return os.path.isdir(os.path.join(path, '.hg'))
1675
1675
1676 @property
1676 @property
1677 def suboptions(self):
1677 def suboptions(self):
1678 """Return sub-options and their values for this path.
1678 """Return sub-options and their values for this path.
1679
1679
1680 This is intended to be used for presentation purposes.
1680 This is intended to be used for presentation purposes.
1681 """
1681 """
1682 d = {}
1682 d = {}
1683 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1683 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1684 value = getattr(self, attr)
1684 value = getattr(self, attr)
1685 if value is not None:
1685 if value is not None:
1686 d[subopt] = value
1686 d[subopt] = value
1687 return d
1687 return d
1688
1688
1689 # we instantiate one globally shared progress bar to avoid
1689 # we instantiate one globally shared progress bar to avoid
1690 # competing progress bars when multiple UI objects get created
1690 # competing progress bars when multiple UI objects get created
1691 _progresssingleton = None
1691 _progresssingleton = None
1692
1692
1693 def getprogbar(ui):
1693 def getprogbar(ui):
1694 global _progresssingleton
1694 global _progresssingleton
1695 if _progresssingleton is None:
1695 if _progresssingleton is None:
1696 # passing 'ui' object to the singleton is fishy,
1696 # passing 'ui' object to the singleton is fishy,
1697 # this is how the extension used to work but feel free to rework it.
1697 # this is how the extension used to work but feel free to rework it.
1698 _progresssingleton = progress.progbar(ui)
1698 _progresssingleton = progress.progbar(ui)
1699 return _progresssingleton
1699 return _progresssingleton
@@ -1,3748 +1,3731 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from __future__ import absolute_import
16 from __future__ import absolute_import
17
17
18 import bz2
18 import bz2
19 import calendar
19 import calendar
20 import codecs
20 import codecs
21 import collections
21 import collections
22 import datetime
22 import datetime
23 import errno
23 import errno
24 import gc
24 import gc
25 import hashlib
25 import hashlib
26 import imp
26 import imp
27 import os
27 import os
28 import platform as pyplatform
28 import platform as pyplatform
29 import re as remod
29 import re as remod
30 import shutil
30 import shutil
31 import signal
31 import signal
32 import socket
32 import socket
33 import stat
33 import stat
34 import string
34 import string
35 import subprocess
35 import subprocess
36 import sys
36 import sys
37 import tempfile
37 import tempfile
38 import textwrap
38 import textwrap
39 import time
39 import time
40 import traceback
40 import traceback
41 import warnings
41 import warnings
42 import zlib
42 import zlib
43
43
44 from . import (
44 from . import (
45 encoding,
45 encoding,
46 error,
46 error,
47 i18n,
47 i18n,
48 policy,
48 policy,
49 pycompat,
49 pycompat,
50 )
50 )
51
51
52 base85 = policy.importmod(r'base85')
52 base85 = policy.importmod(r'base85')
53 osutil = policy.importmod(r'osutil')
53 osutil = policy.importmod(r'osutil')
54 parsers = policy.importmod(r'parsers')
54 parsers = policy.importmod(r'parsers')
55
55
56 b85decode = base85.b85decode
56 b85decode = base85.b85decode
57 b85encode = base85.b85encode
57 b85encode = base85.b85encode
58
58
59 cookielib = pycompat.cookielib
59 cookielib = pycompat.cookielib
60 empty = pycompat.empty
60 empty = pycompat.empty
61 httplib = pycompat.httplib
61 httplib = pycompat.httplib
62 httpserver = pycompat.httpserver
62 httpserver = pycompat.httpserver
63 pickle = pycompat.pickle
63 pickle = pycompat.pickle
64 queue = pycompat.queue
64 queue = pycompat.queue
65 socketserver = pycompat.socketserver
65 socketserver = pycompat.socketserver
66 stderr = pycompat.stderr
66 stderr = pycompat.stderr
67 stdin = pycompat.stdin
67 stdin = pycompat.stdin
68 stdout = pycompat.stdout
68 stdout = pycompat.stdout
69 stringio = pycompat.stringio
69 stringio = pycompat.stringio
70 urlerr = pycompat.urlerr
70 urlerr = pycompat.urlerr
71 urlreq = pycompat.urlreq
71 urlreq = pycompat.urlreq
72 xmlrpclib = pycompat.xmlrpclib
72 xmlrpclib = pycompat.xmlrpclib
73
73
74 def isatty(fp):
74 def isatty(fp):
75 try:
75 try:
76 return fp.isatty()
76 return fp.isatty()
77 except AttributeError:
77 except AttributeError:
78 return False
78 return False
79
79
80 # glibc determines buffering on first write to stdout - if we replace a TTY
80 # glibc determines buffering on first write to stdout - if we replace a TTY
81 # destined stdout with a pipe destined stdout (e.g. pager), we want line
81 # destined stdout with a pipe destined stdout (e.g. pager), we want line
82 # buffering
82 # buffering
83 if isatty(stdout):
83 if isatty(stdout):
84 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
84 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
85
85
86 if pycompat.osname == 'nt':
86 if pycompat.osname == 'nt':
87 from . import windows as platform
87 from . import windows as platform
88 stdout = platform.winstdout(stdout)
88 stdout = platform.winstdout(stdout)
89 else:
89 else:
90 from . import posix as platform
90 from . import posix as platform
91
91
92 _ = i18n._
92 _ = i18n._
93
93
94 bindunixsocket = platform.bindunixsocket
94 bindunixsocket = platform.bindunixsocket
95 cachestat = platform.cachestat
95 cachestat = platform.cachestat
96 checkexec = platform.checkexec
96 checkexec = platform.checkexec
97 checklink = platform.checklink
97 checklink = platform.checklink
98 copymode = platform.copymode
98 copymode = platform.copymode
99 executablepath = platform.executablepath
99 executablepath = platform.executablepath
100 expandglobs = platform.expandglobs
100 expandglobs = platform.expandglobs
101 explainexit = platform.explainexit
101 explainexit = platform.explainexit
102 findexe = platform.findexe
102 findexe = platform.findexe
103 gethgcmd = platform.gethgcmd
103 gethgcmd = platform.gethgcmd
104 getuser = platform.getuser
104 getuser = platform.getuser
105 getpid = os.getpid
105 getpid = os.getpid
106 groupmembers = platform.groupmembers
106 groupmembers = platform.groupmembers
107 groupname = platform.groupname
107 groupname = platform.groupname
108 hidewindow = platform.hidewindow
108 hidewindow = platform.hidewindow
109 isexec = platform.isexec
109 isexec = platform.isexec
110 isowner = platform.isowner
110 isowner = platform.isowner
111 listdir = osutil.listdir
111 listdir = osutil.listdir
112 localpath = platform.localpath
112 localpath = platform.localpath
113 lookupreg = platform.lookupreg
113 lookupreg = platform.lookupreg
114 makedir = platform.makedir
114 makedir = platform.makedir
115 nlinks = platform.nlinks
115 nlinks = platform.nlinks
116 normpath = platform.normpath
116 normpath = platform.normpath
117 normcase = platform.normcase
117 normcase = platform.normcase
118 normcasespec = platform.normcasespec
118 normcasespec = platform.normcasespec
119 normcasefallback = platform.normcasefallback
119 normcasefallback = platform.normcasefallback
120 openhardlinks = platform.openhardlinks
120 openhardlinks = platform.openhardlinks
121 oslink = platform.oslink
121 oslink = platform.oslink
122 parsepatchoutput = platform.parsepatchoutput
122 parsepatchoutput = platform.parsepatchoutput
123 pconvert = platform.pconvert
123 pconvert = platform.pconvert
124 poll = platform.poll
124 poll = platform.poll
125 popen = platform.popen
125 popen = platform.popen
126 posixfile = platform.posixfile
126 posixfile = platform.posixfile
127 quotecommand = platform.quotecommand
127 quotecommand = platform.quotecommand
128 readpipe = platform.readpipe
128 readpipe = platform.readpipe
129 rename = platform.rename
129 rename = platform.rename
130 removedirs = platform.removedirs
130 removedirs = platform.removedirs
131 samedevice = platform.samedevice
131 samedevice = platform.samedevice
132 samefile = platform.samefile
132 samefile = platform.samefile
133 samestat = platform.samestat
133 samestat = platform.samestat
134 setbinary = platform.setbinary
134 setbinary = platform.setbinary
135 setflags = platform.setflags
135 setflags = platform.setflags
136 setsignalhandler = platform.setsignalhandler
136 setsignalhandler = platform.setsignalhandler
137 shellquote = platform.shellquote
137 shellquote = platform.shellquote
138 spawndetached = platform.spawndetached
138 spawndetached = platform.spawndetached
139 split = platform.split
139 split = platform.split
140 sshargs = platform.sshargs
140 sshargs = platform.sshargs
141 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
141 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
142 statisexec = platform.statisexec
142 statisexec = platform.statisexec
143 statislink = platform.statislink
143 statislink = platform.statislink
144 testpid = platform.testpid
144 testpid = platform.testpid
145 umask = platform.umask
145 umask = platform.umask
146 unlink = platform.unlink
146 unlink = platform.unlink
147 username = platform.username
147 username = platform.username
148
148
149 try:
149 try:
150 recvfds = osutil.recvfds
150 recvfds = osutil.recvfds
151 except AttributeError:
151 except AttributeError:
152 pass
152 pass
153 try:
153 try:
154 setprocname = osutil.setprocname
154 setprocname = osutil.setprocname
155 except AttributeError:
155 except AttributeError:
156 pass
156 pass
157
157
158 # Python compatibility
158 # Python compatibility
159
159
160 _notset = object()
160 _notset = object()
161
161
162 # disable Python's problematic floating point timestamps (issue4836)
162 # disable Python's problematic floating point timestamps (issue4836)
163 # (Python hypocritically says you shouldn't change this behavior in
163 # (Python hypocritically says you shouldn't change this behavior in
164 # libraries, and sure enough Mercurial is not a library.)
164 # libraries, and sure enough Mercurial is not a library.)
165 os.stat_float_times(False)
165 os.stat_float_times(False)
166
166
167 def safehasattr(thing, attr):
167 def safehasattr(thing, attr):
168 return getattr(thing, attr, _notset) is not _notset
168 return getattr(thing, attr, _notset) is not _notset
169
169
170 def bitsfrom(container):
170 def bitsfrom(container):
171 bits = 0
171 bits = 0
172 for bit in container:
172 for bit in container:
173 bits |= bit
173 bits |= bit
174 return bits
174 return bits
175
175
176 # python 2.6 still have deprecation warning enabled by default. We do not want
176 # python 2.6 still have deprecation warning enabled by default. We do not want
177 # to display anything to standard user so detect if we are running test and
177 # to display anything to standard user so detect if we are running test and
178 # only use python deprecation warning in this case.
178 # only use python deprecation warning in this case.
179 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
179 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
180 if _dowarn:
180 if _dowarn:
181 # explicitly unfilter our warning for python 2.7
181 # explicitly unfilter our warning for python 2.7
182 #
182 #
183 # The option of setting PYTHONWARNINGS in the test runner was investigated.
183 # The option of setting PYTHONWARNINGS in the test runner was investigated.
184 # However, module name set through PYTHONWARNINGS was exactly matched, so
184 # However, module name set through PYTHONWARNINGS was exactly matched, so
185 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
185 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
186 # makes the whole PYTHONWARNINGS thing useless for our usecase.
186 # makes the whole PYTHONWARNINGS thing useless for our usecase.
187 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
187 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
188 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
188 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
189 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
189 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
190
190
191 def nouideprecwarn(msg, version, stacklevel=1):
191 def nouideprecwarn(msg, version, stacklevel=1):
192 """Issue an python native deprecation warning
192 """Issue an python native deprecation warning
193
193
194 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
194 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
195 """
195 """
196 if _dowarn:
196 if _dowarn:
197 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
197 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
198 " update your code.)") % version
198 " update your code.)") % version
199 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
199 warnings.warn(msg, DeprecationWarning, stacklevel + 1)
200
200
201 DIGESTS = {
201 DIGESTS = {
202 'md5': hashlib.md5,
202 'md5': hashlib.md5,
203 'sha1': hashlib.sha1,
203 'sha1': hashlib.sha1,
204 'sha512': hashlib.sha512,
204 'sha512': hashlib.sha512,
205 }
205 }
206 # List of digest types from strongest to weakest
206 # List of digest types from strongest to weakest
207 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
207 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
208
208
209 for k in DIGESTS_BY_STRENGTH:
209 for k in DIGESTS_BY_STRENGTH:
210 assert k in DIGESTS
210 assert k in DIGESTS
211
211
212 class digester(object):
212 class digester(object):
213 """helper to compute digests.
213 """helper to compute digests.
214
214
215 This helper can be used to compute one or more digests given their name.
215 This helper can be used to compute one or more digests given their name.
216
216
217 >>> d = digester(['md5', 'sha1'])
217 >>> d = digester(['md5', 'sha1'])
218 >>> d.update('foo')
218 >>> d.update('foo')
219 >>> [k for k in sorted(d)]
219 >>> [k for k in sorted(d)]
220 ['md5', 'sha1']
220 ['md5', 'sha1']
221 >>> d['md5']
221 >>> d['md5']
222 'acbd18db4cc2f85cedef654fccc4a4d8'
222 'acbd18db4cc2f85cedef654fccc4a4d8'
223 >>> d['sha1']
223 >>> d['sha1']
224 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
224 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
225 >>> digester.preferred(['md5', 'sha1'])
225 >>> digester.preferred(['md5', 'sha1'])
226 'sha1'
226 'sha1'
227 """
227 """
228
228
229 def __init__(self, digests, s=''):
229 def __init__(self, digests, s=''):
230 self._hashes = {}
230 self._hashes = {}
231 for k in digests:
231 for k in digests:
232 if k not in DIGESTS:
232 if k not in DIGESTS:
233 raise Abort(_('unknown digest type: %s') % k)
233 raise Abort(_('unknown digest type: %s') % k)
234 self._hashes[k] = DIGESTS[k]()
234 self._hashes[k] = DIGESTS[k]()
235 if s:
235 if s:
236 self.update(s)
236 self.update(s)
237
237
238 def update(self, data):
238 def update(self, data):
239 for h in self._hashes.values():
239 for h in self._hashes.values():
240 h.update(data)
240 h.update(data)
241
241
242 def __getitem__(self, key):
242 def __getitem__(self, key):
243 if key not in DIGESTS:
243 if key not in DIGESTS:
244 raise Abort(_('unknown digest type: %s') % k)
244 raise Abort(_('unknown digest type: %s') % k)
245 return self._hashes[key].hexdigest()
245 return self._hashes[key].hexdigest()
246
246
247 def __iter__(self):
247 def __iter__(self):
248 return iter(self._hashes)
248 return iter(self._hashes)
249
249
250 @staticmethod
250 @staticmethod
251 def preferred(supported):
251 def preferred(supported):
252 """returns the strongest digest type in both supported and DIGESTS."""
252 """returns the strongest digest type in both supported and DIGESTS."""
253
253
254 for k in DIGESTS_BY_STRENGTH:
254 for k in DIGESTS_BY_STRENGTH:
255 if k in supported:
255 if k in supported:
256 return k
256 return k
257 return None
257 return None
258
258
259 class digestchecker(object):
259 class digestchecker(object):
260 """file handle wrapper that additionally checks content against a given
260 """file handle wrapper that additionally checks content against a given
261 size and digests.
261 size and digests.
262
262
263 d = digestchecker(fh, size, {'md5': '...'})
263 d = digestchecker(fh, size, {'md5': '...'})
264
264
265 When multiple digests are given, all of them are validated.
265 When multiple digests are given, all of them are validated.
266 """
266 """
267
267
268 def __init__(self, fh, size, digests):
268 def __init__(self, fh, size, digests):
269 self._fh = fh
269 self._fh = fh
270 self._size = size
270 self._size = size
271 self._got = 0
271 self._got = 0
272 self._digests = dict(digests)
272 self._digests = dict(digests)
273 self._digester = digester(self._digests.keys())
273 self._digester = digester(self._digests.keys())
274
274
275 def read(self, length=-1):
275 def read(self, length=-1):
276 content = self._fh.read(length)
276 content = self._fh.read(length)
277 self._digester.update(content)
277 self._digester.update(content)
278 self._got += len(content)
278 self._got += len(content)
279 return content
279 return content
280
280
281 def validate(self):
281 def validate(self):
282 if self._size != self._got:
282 if self._size != self._got:
283 raise Abort(_('size mismatch: expected %d, got %d') %
283 raise Abort(_('size mismatch: expected %d, got %d') %
284 (self._size, self._got))
284 (self._size, self._got))
285 for k, v in self._digests.items():
285 for k, v in self._digests.items():
286 if v != self._digester[k]:
286 if v != self._digester[k]:
287 # i18n: first parameter is a digest name
287 # i18n: first parameter is a digest name
288 raise Abort(_('%s mismatch: expected %s, got %s') %
288 raise Abort(_('%s mismatch: expected %s, got %s') %
289 (k, v, self._digester[k]))
289 (k, v, self._digester[k]))
290
290
291 try:
291 try:
292 buffer = buffer
292 buffer = buffer
293 except NameError:
293 except NameError:
294 if not pycompat.ispy3:
294 if not pycompat.ispy3:
295 def buffer(sliceable, offset=0, length=None):
295 def buffer(sliceable, offset=0, length=None):
296 if length is not None:
296 if length is not None:
297 return sliceable[offset:offset + length]
297 return sliceable[offset:offset + length]
298 return sliceable[offset:]
298 return sliceable[offset:]
299 else:
299 else:
300 def buffer(sliceable, offset=0, length=None):
300 def buffer(sliceable, offset=0, length=None):
301 if length is not None:
301 if length is not None:
302 return memoryview(sliceable)[offset:offset + length]
302 return memoryview(sliceable)[offset:offset + length]
303 return memoryview(sliceable)[offset:]
303 return memoryview(sliceable)[offset:]
304
304
305 closefds = pycompat.osname == 'posix'
305 closefds = pycompat.osname == 'posix'
306
306
307 _chunksize = 4096
307 _chunksize = 4096
308
308
309 class bufferedinputpipe(object):
309 class bufferedinputpipe(object):
310 """a manually buffered input pipe
310 """a manually buffered input pipe
311
311
312 Python will not let us use buffered IO and lazy reading with 'polling' at
312 Python will not let us use buffered IO and lazy reading with 'polling' at
313 the same time. We cannot probe the buffer state and select will not detect
313 the same time. We cannot probe the buffer state and select will not detect
314 that data are ready to read if they are already buffered.
314 that data are ready to read if they are already buffered.
315
315
316 This class let us work around that by implementing its own buffering
316 This class let us work around that by implementing its own buffering
317 (allowing efficient readline) while offering a way to know if the buffer is
317 (allowing efficient readline) while offering a way to know if the buffer is
318 empty from the output (allowing collaboration of the buffer with polling).
318 empty from the output (allowing collaboration of the buffer with polling).
319
319
320 This class lives in the 'util' module because it makes use of the 'os'
320 This class lives in the 'util' module because it makes use of the 'os'
321 module from the python stdlib.
321 module from the python stdlib.
322 """
322 """
323
323
324 def __init__(self, input):
324 def __init__(self, input):
325 self._input = input
325 self._input = input
326 self._buffer = []
326 self._buffer = []
327 self._eof = False
327 self._eof = False
328 self._lenbuf = 0
328 self._lenbuf = 0
329
329
330 @property
330 @property
331 def hasbuffer(self):
331 def hasbuffer(self):
332 """True is any data is currently buffered
332 """True is any data is currently buffered
333
333
334 This will be used externally a pre-step for polling IO. If there is
334 This will be used externally a pre-step for polling IO. If there is
335 already data then no polling should be set in place."""
335 already data then no polling should be set in place."""
336 return bool(self._buffer)
336 return bool(self._buffer)
337
337
338 @property
338 @property
339 def closed(self):
339 def closed(self):
340 return self._input.closed
340 return self._input.closed
341
341
342 def fileno(self):
342 def fileno(self):
343 return self._input.fileno()
343 return self._input.fileno()
344
344
345 def close(self):
345 def close(self):
346 return self._input.close()
346 return self._input.close()
347
347
348 def read(self, size):
348 def read(self, size):
349 while (not self._eof) and (self._lenbuf < size):
349 while (not self._eof) and (self._lenbuf < size):
350 self._fillbuffer()
350 self._fillbuffer()
351 return self._frombuffer(size)
351 return self._frombuffer(size)
352
352
353 def readline(self, *args, **kwargs):
353 def readline(self, *args, **kwargs):
354 if 1 < len(self._buffer):
354 if 1 < len(self._buffer):
355 # this should not happen because both read and readline end with a
355 # this should not happen because both read and readline end with a
356 # _frombuffer call that collapse it.
356 # _frombuffer call that collapse it.
357 self._buffer = [''.join(self._buffer)]
357 self._buffer = [''.join(self._buffer)]
358 self._lenbuf = len(self._buffer[0])
358 self._lenbuf = len(self._buffer[0])
359 lfi = -1
359 lfi = -1
360 if self._buffer:
360 if self._buffer:
361 lfi = self._buffer[-1].find('\n')
361 lfi = self._buffer[-1].find('\n')
362 while (not self._eof) and lfi < 0:
362 while (not self._eof) and lfi < 0:
363 self._fillbuffer()
363 self._fillbuffer()
364 if self._buffer:
364 if self._buffer:
365 lfi = self._buffer[-1].find('\n')
365 lfi = self._buffer[-1].find('\n')
366 size = lfi + 1
366 size = lfi + 1
367 if lfi < 0: # end of file
367 if lfi < 0: # end of file
368 size = self._lenbuf
368 size = self._lenbuf
369 elif 1 < len(self._buffer):
369 elif 1 < len(self._buffer):
370 # we need to take previous chunks into account
370 # we need to take previous chunks into account
371 size += self._lenbuf - len(self._buffer[-1])
371 size += self._lenbuf - len(self._buffer[-1])
372 return self._frombuffer(size)
372 return self._frombuffer(size)
373
373
374 def _frombuffer(self, size):
374 def _frombuffer(self, size):
375 """return at most 'size' data from the buffer
375 """return at most 'size' data from the buffer
376
376
377 The data are removed from the buffer."""
377 The data are removed from the buffer."""
378 if size == 0 or not self._buffer:
378 if size == 0 or not self._buffer:
379 return ''
379 return ''
380 buf = self._buffer[0]
380 buf = self._buffer[0]
381 if 1 < len(self._buffer):
381 if 1 < len(self._buffer):
382 buf = ''.join(self._buffer)
382 buf = ''.join(self._buffer)
383
383
384 data = buf[:size]
384 data = buf[:size]
385 buf = buf[len(data):]
385 buf = buf[len(data):]
386 if buf:
386 if buf:
387 self._buffer = [buf]
387 self._buffer = [buf]
388 self._lenbuf = len(buf)
388 self._lenbuf = len(buf)
389 else:
389 else:
390 self._buffer = []
390 self._buffer = []
391 self._lenbuf = 0
391 self._lenbuf = 0
392 return data
392 return data
393
393
394 def _fillbuffer(self):
394 def _fillbuffer(self):
395 """read data to the buffer"""
395 """read data to the buffer"""
396 data = os.read(self._input.fileno(), _chunksize)
396 data = os.read(self._input.fileno(), _chunksize)
397 if not data:
397 if not data:
398 self._eof = True
398 self._eof = True
399 else:
399 else:
400 self._lenbuf += len(data)
400 self._lenbuf += len(data)
401 self._buffer.append(data)
401 self._buffer.append(data)
402
402
403 def popen2(cmd, env=None, newlines=False):
403 def popen2(cmd, env=None, newlines=False):
404 # Setting bufsize to -1 lets the system decide the buffer size.
404 # Setting bufsize to -1 lets the system decide the buffer size.
405 # The default for bufsize is 0, meaning unbuffered. This leads to
405 # The default for bufsize is 0, meaning unbuffered. This leads to
406 # poor performance on Mac OS X: http://bugs.python.org/issue4194
406 # poor performance on Mac OS X: http://bugs.python.org/issue4194
407 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
407 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
408 close_fds=closefds,
408 close_fds=closefds,
409 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
409 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
410 universal_newlines=newlines,
410 universal_newlines=newlines,
411 env=env)
411 env=env)
412 return p.stdin, p.stdout
412 return p.stdin, p.stdout
413
413
414 def popen3(cmd, env=None, newlines=False):
414 def popen3(cmd, env=None, newlines=False):
415 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
415 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
416 return stdin, stdout, stderr
416 return stdin, stdout, stderr
417
417
418 def popen4(cmd, env=None, newlines=False, bufsize=-1):
418 def popen4(cmd, env=None, newlines=False, bufsize=-1):
419 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
419 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
420 close_fds=closefds,
420 close_fds=closefds,
421 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
421 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
422 stderr=subprocess.PIPE,
422 stderr=subprocess.PIPE,
423 universal_newlines=newlines,
423 universal_newlines=newlines,
424 env=env)
424 env=env)
425 return p.stdin, p.stdout, p.stderr, p
425 return p.stdin, p.stdout, p.stderr, p
426
426
427 def version():
427 def version():
428 """Return version information if available."""
428 """Return version information if available."""
429 try:
429 try:
430 from . import __version__
430 from . import __version__
431 return __version__.version
431 return __version__.version
432 except ImportError:
432 except ImportError:
433 return 'unknown'
433 return 'unknown'
434
434
435 def versiontuple(v=None, n=4):
435 def versiontuple(v=None, n=4):
436 """Parses a Mercurial version string into an N-tuple.
436 """Parses a Mercurial version string into an N-tuple.
437
437
438 The version string to be parsed is specified with the ``v`` argument.
438 The version string to be parsed is specified with the ``v`` argument.
439 If it isn't defined, the current Mercurial version string will be parsed.
439 If it isn't defined, the current Mercurial version string will be parsed.
440
440
441 ``n`` can be 2, 3, or 4. Here is how some version strings map to
441 ``n`` can be 2, 3, or 4. Here is how some version strings map to
442 returned values:
442 returned values:
443
443
444 >>> v = '3.6.1+190-df9b73d2d444'
444 >>> v = '3.6.1+190-df9b73d2d444'
445 >>> versiontuple(v, 2)
445 >>> versiontuple(v, 2)
446 (3, 6)
446 (3, 6)
447 >>> versiontuple(v, 3)
447 >>> versiontuple(v, 3)
448 (3, 6, 1)
448 (3, 6, 1)
449 >>> versiontuple(v, 4)
449 >>> versiontuple(v, 4)
450 (3, 6, 1, '190-df9b73d2d444')
450 (3, 6, 1, '190-df9b73d2d444')
451
451
452 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
452 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
453 (3, 6, 1, '190-df9b73d2d444+20151118')
453 (3, 6, 1, '190-df9b73d2d444+20151118')
454
454
455 >>> v = '3.6'
455 >>> v = '3.6'
456 >>> versiontuple(v, 2)
456 >>> versiontuple(v, 2)
457 (3, 6)
457 (3, 6)
458 >>> versiontuple(v, 3)
458 >>> versiontuple(v, 3)
459 (3, 6, None)
459 (3, 6, None)
460 >>> versiontuple(v, 4)
460 >>> versiontuple(v, 4)
461 (3, 6, None, None)
461 (3, 6, None, None)
462
462
463 >>> v = '3.9-rc'
463 >>> v = '3.9-rc'
464 >>> versiontuple(v, 2)
464 >>> versiontuple(v, 2)
465 (3, 9)
465 (3, 9)
466 >>> versiontuple(v, 3)
466 >>> versiontuple(v, 3)
467 (3, 9, None)
467 (3, 9, None)
468 >>> versiontuple(v, 4)
468 >>> versiontuple(v, 4)
469 (3, 9, None, 'rc')
469 (3, 9, None, 'rc')
470
470
471 >>> v = '3.9-rc+2-02a8fea4289b'
471 >>> v = '3.9-rc+2-02a8fea4289b'
472 >>> versiontuple(v, 2)
472 >>> versiontuple(v, 2)
473 (3, 9)
473 (3, 9)
474 >>> versiontuple(v, 3)
474 >>> versiontuple(v, 3)
475 (3, 9, None)
475 (3, 9, None)
476 >>> versiontuple(v, 4)
476 >>> versiontuple(v, 4)
477 (3, 9, None, 'rc+2-02a8fea4289b')
477 (3, 9, None, 'rc+2-02a8fea4289b')
478 """
478 """
479 if not v:
479 if not v:
480 v = version()
480 v = version()
481 parts = remod.split('[\+-]', v, 1)
481 parts = remod.split('[\+-]', v, 1)
482 if len(parts) == 1:
482 if len(parts) == 1:
483 vparts, extra = parts[0], None
483 vparts, extra = parts[0], None
484 else:
484 else:
485 vparts, extra = parts
485 vparts, extra = parts
486
486
487 vints = []
487 vints = []
488 for i in vparts.split('.'):
488 for i in vparts.split('.'):
489 try:
489 try:
490 vints.append(int(i))
490 vints.append(int(i))
491 except ValueError:
491 except ValueError:
492 break
492 break
493 # (3, 6) -> (3, 6, None)
493 # (3, 6) -> (3, 6, None)
494 while len(vints) < 3:
494 while len(vints) < 3:
495 vints.append(None)
495 vints.append(None)
496
496
497 if n == 2:
497 if n == 2:
498 return (vints[0], vints[1])
498 return (vints[0], vints[1])
499 if n == 3:
499 if n == 3:
500 return (vints[0], vints[1], vints[2])
500 return (vints[0], vints[1], vints[2])
501 if n == 4:
501 if n == 4:
502 return (vints[0], vints[1], vints[2], extra)
502 return (vints[0], vints[1], vints[2], extra)
503
503
504 # used by parsedate
504 # used by parsedate
505 defaultdateformats = (
505 defaultdateformats = (
506 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
506 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
507 '%Y-%m-%dT%H:%M', # without seconds
507 '%Y-%m-%dT%H:%M', # without seconds
508 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
508 '%Y-%m-%dT%H%M%S', # another awful but legal variant without :
509 '%Y-%m-%dT%H%M', # without seconds
509 '%Y-%m-%dT%H%M', # without seconds
510 '%Y-%m-%d %H:%M:%S', # our common legal variant
510 '%Y-%m-%d %H:%M:%S', # our common legal variant
511 '%Y-%m-%d %H:%M', # without seconds
511 '%Y-%m-%d %H:%M', # without seconds
512 '%Y-%m-%d %H%M%S', # without :
512 '%Y-%m-%d %H%M%S', # without :
513 '%Y-%m-%d %H%M', # without seconds
513 '%Y-%m-%d %H%M', # without seconds
514 '%Y-%m-%d %I:%M:%S%p',
514 '%Y-%m-%d %I:%M:%S%p',
515 '%Y-%m-%d %H:%M',
515 '%Y-%m-%d %H:%M',
516 '%Y-%m-%d %I:%M%p',
516 '%Y-%m-%d %I:%M%p',
517 '%Y-%m-%d',
517 '%Y-%m-%d',
518 '%m-%d',
518 '%m-%d',
519 '%m/%d',
519 '%m/%d',
520 '%m/%d/%y',
520 '%m/%d/%y',
521 '%m/%d/%Y',
521 '%m/%d/%Y',
522 '%a %b %d %H:%M:%S %Y',
522 '%a %b %d %H:%M:%S %Y',
523 '%a %b %d %I:%M:%S%p %Y',
523 '%a %b %d %I:%M:%S%p %Y',
524 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
524 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
525 '%b %d %H:%M:%S %Y',
525 '%b %d %H:%M:%S %Y',
526 '%b %d %I:%M:%S%p %Y',
526 '%b %d %I:%M:%S%p %Y',
527 '%b %d %H:%M:%S',
527 '%b %d %H:%M:%S',
528 '%b %d %I:%M:%S%p',
528 '%b %d %I:%M:%S%p',
529 '%b %d %H:%M',
529 '%b %d %H:%M',
530 '%b %d %I:%M%p',
530 '%b %d %I:%M%p',
531 '%b %d %Y',
531 '%b %d %Y',
532 '%b %d',
532 '%b %d',
533 '%H:%M:%S',
533 '%H:%M:%S',
534 '%I:%M:%S%p',
534 '%I:%M:%S%p',
535 '%H:%M',
535 '%H:%M',
536 '%I:%M%p',
536 '%I:%M%p',
537 )
537 )
538
538
539 extendeddateformats = defaultdateformats + (
539 extendeddateformats = defaultdateformats + (
540 "%Y",
540 "%Y",
541 "%Y-%m",
541 "%Y-%m",
542 "%b",
542 "%b",
543 "%b %Y",
543 "%b %Y",
544 )
544 )
545
545
546 def cachefunc(func):
546 def cachefunc(func):
547 '''cache the result of function calls'''
547 '''cache the result of function calls'''
548 # XXX doesn't handle keywords args
548 # XXX doesn't handle keywords args
549 if func.__code__.co_argcount == 0:
549 if func.__code__.co_argcount == 0:
550 cache = []
550 cache = []
551 def f():
551 def f():
552 if len(cache) == 0:
552 if len(cache) == 0:
553 cache.append(func())
553 cache.append(func())
554 return cache[0]
554 return cache[0]
555 return f
555 return f
556 cache = {}
556 cache = {}
557 if func.__code__.co_argcount == 1:
557 if func.__code__.co_argcount == 1:
558 # we gain a small amount of time because
558 # we gain a small amount of time because
559 # we don't need to pack/unpack the list
559 # we don't need to pack/unpack the list
560 def f(arg):
560 def f(arg):
561 if arg not in cache:
561 if arg not in cache:
562 cache[arg] = func(arg)
562 cache[arg] = func(arg)
563 return cache[arg]
563 return cache[arg]
564 else:
564 else:
565 def f(*args):
565 def f(*args):
566 if args not in cache:
566 if args not in cache:
567 cache[args] = func(*args)
567 cache[args] = func(*args)
568 return cache[args]
568 return cache[args]
569
569
570 return f
570 return f
571
571
572 class sortdict(collections.OrderedDict):
572 class sortdict(collections.OrderedDict):
573 '''a simple sorted dictionary
573 '''a simple sorted dictionary
574
574
575 >>> d1 = sortdict([('a', 0), ('b', 1)])
575 >>> d1 = sortdict([('a', 0), ('b', 1)])
576 >>> d2 = d1.copy()
576 >>> d2 = d1.copy()
577 >>> d2
577 >>> d2
578 sortdict([('a', 0), ('b', 1)])
578 sortdict([('a', 0), ('b', 1)])
579 >>> d2.update([('a', 2)])
579 >>> d2.update([('a', 2)])
580 >>> d2.keys() # should still be in last-set order
580 >>> d2.keys() # should still be in last-set order
581 ['b', 'a']
581 ['b', 'a']
582 '''
582 '''
583
583
584 def __setitem__(self, key, value):
584 def __setitem__(self, key, value):
585 if key in self:
585 if key in self:
586 del self[key]
586 del self[key]
587 super(sortdict, self).__setitem__(key, value)
587 super(sortdict, self).__setitem__(key, value)
588
588
589 class _lrucachenode(object):
589 class _lrucachenode(object):
590 """A node in a doubly linked list.
590 """A node in a doubly linked list.
591
591
592 Holds a reference to nodes on either side as well as a key-value
592 Holds a reference to nodes on either side as well as a key-value
593 pair for the dictionary entry.
593 pair for the dictionary entry.
594 """
594 """
595 __slots__ = (u'next', u'prev', u'key', u'value')
595 __slots__ = (u'next', u'prev', u'key', u'value')
596
596
597 def __init__(self):
597 def __init__(self):
598 self.next = None
598 self.next = None
599 self.prev = None
599 self.prev = None
600
600
601 self.key = _notset
601 self.key = _notset
602 self.value = None
602 self.value = None
603
603
604 def markempty(self):
604 def markempty(self):
605 """Mark the node as emptied."""
605 """Mark the node as emptied."""
606 self.key = _notset
606 self.key = _notset
607
607
608 class lrucachedict(object):
608 class lrucachedict(object):
609 """Dict that caches most recent accesses and sets.
609 """Dict that caches most recent accesses and sets.
610
610
611 The dict consists of an actual backing dict - indexed by original
611 The dict consists of an actual backing dict - indexed by original
612 key - and a doubly linked circular list defining the order of entries in
612 key - and a doubly linked circular list defining the order of entries in
613 the cache.
613 the cache.
614
614
615 The head node is the newest entry in the cache. If the cache is full,
615 The head node is the newest entry in the cache. If the cache is full,
616 we recycle head.prev and make it the new head. Cache accesses result in
616 we recycle head.prev and make it the new head. Cache accesses result in
617 the node being moved to before the existing head and being marked as the
617 the node being moved to before the existing head and being marked as the
618 new head node.
618 new head node.
619 """
619 """
620 def __init__(self, max):
620 def __init__(self, max):
621 self._cache = {}
621 self._cache = {}
622
622
623 self._head = head = _lrucachenode()
623 self._head = head = _lrucachenode()
624 head.prev = head
624 head.prev = head
625 head.next = head
625 head.next = head
626 self._size = 1
626 self._size = 1
627 self._capacity = max
627 self._capacity = max
628
628
629 def __len__(self):
629 def __len__(self):
630 return len(self._cache)
630 return len(self._cache)
631
631
632 def __contains__(self, k):
632 def __contains__(self, k):
633 return k in self._cache
633 return k in self._cache
634
634
635 def __iter__(self):
635 def __iter__(self):
636 # We don't have to iterate in cache order, but why not.
636 # We don't have to iterate in cache order, but why not.
637 n = self._head
637 n = self._head
638 for i in range(len(self._cache)):
638 for i in range(len(self._cache)):
639 yield n.key
639 yield n.key
640 n = n.next
640 n = n.next
641
641
642 def __getitem__(self, k):
642 def __getitem__(self, k):
643 node = self._cache[k]
643 node = self._cache[k]
644 self._movetohead(node)
644 self._movetohead(node)
645 return node.value
645 return node.value
646
646
647 def __setitem__(self, k, v):
647 def __setitem__(self, k, v):
648 node = self._cache.get(k)
648 node = self._cache.get(k)
649 # Replace existing value and mark as newest.
649 # Replace existing value and mark as newest.
650 if node is not None:
650 if node is not None:
651 node.value = v
651 node.value = v
652 self._movetohead(node)
652 self._movetohead(node)
653 return
653 return
654
654
655 if self._size < self._capacity:
655 if self._size < self._capacity:
656 node = self._addcapacity()
656 node = self._addcapacity()
657 else:
657 else:
658 # Grab the last/oldest item.
658 # Grab the last/oldest item.
659 node = self._head.prev
659 node = self._head.prev
660
660
661 # At capacity. Kill the old entry.
661 # At capacity. Kill the old entry.
662 if node.key is not _notset:
662 if node.key is not _notset:
663 del self._cache[node.key]
663 del self._cache[node.key]
664
664
665 node.key = k
665 node.key = k
666 node.value = v
666 node.value = v
667 self._cache[k] = node
667 self._cache[k] = node
668 # And mark it as newest entry. No need to adjust order since it
668 # And mark it as newest entry. No need to adjust order since it
669 # is already self._head.prev.
669 # is already self._head.prev.
670 self._head = node
670 self._head = node
671
671
672 def __delitem__(self, k):
672 def __delitem__(self, k):
673 node = self._cache.pop(k)
673 node = self._cache.pop(k)
674 node.markempty()
674 node.markempty()
675
675
676 # Temporarily mark as newest item before re-adjusting head to make
676 # Temporarily mark as newest item before re-adjusting head to make
677 # this node the oldest item.
677 # this node the oldest item.
678 self._movetohead(node)
678 self._movetohead(node)
679 self._head = node.next
679 self._head = node.next
680
680
681 # Additional dict methods.
681 # Additional dict methods.
682
682
683 def get(self, k, default=None):
683 def get(self, k, default=None):
684 try:
684 try:
685 return self._cache[k].value
685 return self._cache[k].value
686 except KeyError:
686 except KeyError:
687 return default
687 return default
688
688
689 def clear(self):
689 def clear(self):
690 n = self._head
690 n = self._head
691 while n.key is not _notset:
691 while n.key is not _notset:
692 n.markempty()
692 n.markempty()
693 n = n.next
693 n = n.next
694
694
695 self._cache.clear()
695 self._cache.clear()
696
696
697 def copy(self):
697 def copy(self):
698 result = lrucachedict(self._capacity)
698 result = lrucachedict(self._capacity)
699 n = self._head.prev
699 n = self._head.prev
700 # Iterate in oldest-to-newest order, so the copy has the right ordering
700 # Iterate in oldest-to-newest order, so the copy has the right ordering
701 for i in range(len(self._cache)):
701 for i in range(len(self._cache)):
702 result[n.key] = n.value
702 result[n.key] = n.value
703 n = n.prev
703 n = n.prev
704 return result
704 return result
705
705
706 def _movetohead(self, node):
706 def _movetohead(self, node):
707 """Mark a node as the newest, making it the new head.
707 """Mark a node as the newest, making it the new head.
708
708
709 When a node is accessed, it becomes the freshest entry in the LRU
709 When a node is accessed, it becomes the freshest entry in the LRU
710 list, which is denoted by self._head.
710 list, which is denoted by self._head.
711
711
712 Visually, let's make ``N`` the new head node (* denotes head):
712 Visually, let's make ``N`` the new head node (* denotes head):
713
713
714 previous/oldest <-> head <-> next/next newest
714 previous/oldest <-> head <-> next/next newest
715
715
716 ----<->--- A* ---<->-----
716 ----<->--- A* ---<->-----
717 | |
717 | |
718 E <-> D <-> N <-> C <-> B
718 E <-> D <-> N <-> C <-> B
719
719
720 To:
720 To:
721
721
722 ----<->--- N* ---<->-----
722 ----<->--- N* ---<->-----
723 | |
723 | |
724 E <-> D <-> C <-> B <-> A
724 E <-> D <-> C <-> B <-> A
725
725
726 This requires the following moves:
726 This requires the following moves:
727
727
728 C.next = D (node.prev.next = node.next)
728 C.next = D (node.prev.next = node.next)
729 D.prev = C (node.next.prev = node.prev)
729 D.prev = C (node.next.prev = node.prev)
730 E.next = N (head.prev.next = node)
730 E.next = N (head.prev.next = node)
731 N.prev = E (node.prev = head.prev)
731 N.prev = E (node.prev = head.prev)
732 N.next = A (node.next = head)
732 N.next = A (node.next = head)
733 A.prev = N (head.prev = node)
733 A.prev = N (head.prev = node)
734 """
734 """
735 head = self._head
735 head = self._head
736 # C.next = D
736 # C.next = D
737 node.prev.next = node.next
737 node.prev.next = node.next
738 # D.prev = C
738 # D.prev = C
739 node.next.prev = node.prev
739 node.next.prev = node.prev
740 # N.prev = E
740 # N.prev = E
741 node.prev = head.prev
741 node.prev = head.prev
742 # N.next = A
742 # N.next = A
743 # It is tempting to do just "head" here, however if node is
743 # It is tempting to do just "head" here, however if node is
744 # adjacent to head, this will do bad things.
744 # adjacent to head, this will do bad things.
745 node.next = head.prev.next
745 node.next = head.prev.next
746 # E.next = N
746 # E.next = N
747 node.next.prev = node
747 node.next.prev = node
748 # A.prev = N
748 # A.prev = N
749 node.prev.next = node
749 node.prev.next = node
750
750
751 self._head = node
751 self._head = node
752
752
753 def _addcapacity(self):
753 def _addcapacity(self):
754 """Add a node to the circular linked list.
754 """Add a node to the circular linked list.
755
755
756 The new node is inserted before the head node.
756 The new node is inserted before the head node.
757 """
757 """
758 head = self._head
758 head = self._head
759 node = _lrucachenode()
759 node = _lrucachenode()
760 head.prev.next = node
760 head.prev.next = node
761 node.prev = head.prev
761 node.prev = head.prev
762 node.next = head
762 node.next = head
763 head.prev = node
763 head.prev = node
764 self._size += 1
764 self._size += 1
765 return node
765 return node
766
766
767 def lrucachefunc(func):
767 def lrucachefunc(func):
768 '''cache most recent results of function calls'''
768 '''cache most recent results of function calls'''
769 cache = {}
769 cache = {}
770 order = collections.deque()
770 order = collections.deque()
771 if func.__code__.co_argcount == 1:
771 if func.__code__.co_argcount == 1:
772 def f(arg):
772 def f(arg):
773 if arg not in cache:
773 if arg not in cache:
774 if len(cache) > 20:
774 if len(cache) > 20:
775 del cache[order.popleft()]
775 del cache[order.popleft()]
776 cache[arg] = func(arg)
776 cache[arg] = func(arg)
777 else:
777 else:
778 order.remove(arg)
778 order.remove(arg)
779 order.append(arg)
779 order.append(arg)
780 return cache[arg]
780 return cache[arg]
781 else:
781 else:
782 def f(*args):
782 def f(*args):
783 if args not in cache:
783 if args not in cache:
784 if len(cache) > 20:
784 if len(cache) > 20:
785 del cache[order.popleft()]
785 del cache[order.popleft()]
786 cache[args] = func(*args)
786 cache[args] = func(*args)
787 else:
787 else:
788 order.remove(args)
788 order.remove(args)
789 order.append(args)
789 order.append(args)
790 return cache[args]
790 return cache[args]
791
791
792 return f
792 return f
793
793
794 class propertycache(object):
794 class propertycache(object):
795 def __init__(self, func):
795 def __init__(self, func):
796 self.func = func
796 self.func = func
797 self.name = func.__name__
797 self.name = func.__name__
798 def __get__(self, obj, type=None):
798 def __get__(self, obj, type=None):
799 result = self.func(obj)
799 result = self.func(obj)
800 self.cachevalue(obj, result)
800 self.cachevalue(obj, result)
801 return result
801 return result
802
802
803 def cachevalue(self, obj, value):
803 def cachevalue(self, obj, value):
804 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
804 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
805 obj.__dict__[self.name] = value
805 obj.__dict__[self.name] = value
806
806
807 def pipefilter(s, cmd):
807 def pipefilter(s, cmd):
808 '''filter string S through command CMD, returning its output'''
808 '''filter string S through command CMD, returning its output'''
809 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
809 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
810 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
810 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
811 pout, perr = p.communicate(s)
811 pout, perr = p.communicate(s)
812 return pout
812 return pout
813
813
814 def tempfilter(s, cmd):
814 def tempfilter(s, cmd):
815 '''filter string S through a pair of temporary files with CMD.
815 '''filter string S through a pair of temporary files with CMD.
816 CMD is used as a template to create the real command to be run,
816 CMD is used as a template to create the real command to be run,
817 with the strings INFILE and OUTFILE replaced by the real names of
817 with the strings INFILE and OUTFILE replaced by the real names of
818 the temporary files generated.'''
818 the temporary files generated.'''
819 inname, outname = None, None
819 inname, outname = None, None
820 try:
820 try:
821 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
821 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
822 fp = os.fdopen(infd, pycompat.sysstr('wb'))
822 fp = os.fdopen(infd, pycompat.sysstr('wb'))
823 fp.write(s)
823 fp.write(s)
824 fp.close()
824 fp.close()
825 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
825 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
826 os.close(outfd)
826 os.close(outfd)
827 cmd = cmd.replace('INFILE', inname)
827 cmd = cmd.replace('INFILE', inname)
828 cmd = cmd.replace('OUTFILE', outname)
828 cmd = cmd.replace('OUTFILE', outname)
829 code = os.system(cmd)
829 code = os.system(cmd)
830 if pycompat.sysplatform == 'OpenVMS' and code & 1:
830 if pycompat.sysplatform == 'OpenVMS' and code & 1:
831 code = 0
831 code = 0
832 if code:
832 if code:
833 raise Abort(_("command '%s' failed: %s") %
833 raise Abort(_("command '%s' failed: %s") %
834 (cmd, explainexit(code)))
834 (cmd, explainexit(code)))
835 return readfile(outname)
835 return readfile(outname)
836 finally:
836 finally:
837 try:
837 try:
838 if inname:
838 if inname:
839 os.unlink(inname)
839 os.unlink(inname)
840 except OSError:
840 except OSError:
841 pass
841 pass
842 try:
842 try:
843 if outname:
843 if outname:
844 os.unlink(outname)
844 os.unlink(outname)
845 except OSError:
845 except OSError:
846 pass
846 pass
847
847
848 filtertable = {
848 filtertable = {
849 'tempfile:': tempfilter,
849 'tempfile:': tempfilter,
850 'pipe:': pipefilter,
850 'pipe:': pipefilter,
851 }
851 }
852
852
853 def filter(s, cmd):
853 def filter(s, cmd):
854 "filter a string through a command that transforms its input to its output"
854 "filter a string through a command that transforms its input to its output"
855 for name, fn in filtertable.iteritems():
855 for name, fn in filtertable.iteritems():
856 if cmd.startswith(name):
856 if cmd.startswith(name):
857 return fn(s, cmd[len(name):].lstrip())
857 return fn(s, cmd[len(name):].lstrip())
858 return pipefilter(s, cmd)
858 return pipefilter(s, cmd)
859
859
860 def binary(s):
860 def binary(s):
861 """return true if a string is binary data"""
861 """return true if a string is binary data"""
862 return bool(s and '\0' in s)
862 return bool(s and '\0' in s)
863
863
864 def increasingchunks(source, min=1024, max=65536):
864 def increasingchunks(source, min=1024, max=65536):
865 '''return no less than min bytes per chunk while data remains,
865 '''return no less than min bytes per chunk while data remains,
866 doubling min after each chunk until it reaches max'''
866 doubling min after each chunk until it reaches max'''
867 def log2(x):
867 def log2(x):
868 if not x:
868 if not x:
869 return 0
869 return 0
870 i = 0
870 i = 0
871 while x:
871 while x:
872 x >>= 1
872 x >>= 1
873 i += 1
873 i += 1
874 return i - 1
874 return i - 1
875
875
876 buf = []
876 buf = []
877 blen = 0
877 blen = 0
878 for chunk in source:
878 for chunk in source:
879 buf.append(chunk)
879 buf.append(chunk)
880 blen += len(chunk)
880 blen += len(chunk)
881 if blen >= min:
881 if blen >= min:
882 if min < max:
882 if min < max:
883 min = min << 1
883 min = min << 1
884 nmin = 1 << log2(blen)
884 nmin = 1 << log2(blen)
885 if nmin > min:
885 if nmin > min:
886 min = nmin
886 min = nmin
887 if min > max:
887 if min > max:
888 min = max
888 min = max
889 yield ''.join(buf)
889 yield ''.join(buf)
890 blen = 0
890 blen = 0
891 buf = []
891 buf = []
892 if buf:
892 if buf:
893 yield ''.join(buf)
893 yield ''.join(buf)
894
894
895 Abort = error.Abort
895 Abort = error.Abort
896
896
897 def always(fn):
897 def always(fn):
898 return True
898 return True
899
899
900 def never(fn):
900 def never(fn):
901 return False
901 return False
902
902
903 def nogc(func):
903 def nogc(func):
904 """disable garbage collector
904 """disable garbage collector
905
905
906 Python's garbage collector triggers a GC each time a certain number of
906 Python's garbage collector triggers a GC each time a certain number of
907 container objects (the number being defined by gc.get_threshold()) are
907 container objects (the number being defined by gc.get_threshold()) are
908 allocated even when marked not to be tracked by the collector. Tracking has
908 allocated even when marked not to be tracked by the collector. Tracking has
909 no effect on when GCs are triggered, only on what objects the GC looks
909 no effect on when GCs are triggered, only on what objects the GC looks
910 into. As a workaround, disable GC while building complex (huge)
910 into. As a workaround, disable GC while building complex (huge)
911 containers.
911 containers.
912
912
913 This garbage collector issue have been fixed in 2.7.
913 This garbage collector issue have been fixed in 2.7.
914 """
914 """
915 if sys.version_info >= (2, 7):
915 if sys.version_info >= (2, 7):
916 return func
916 return func
917 def wrapper(*args, **kwargs):
917 def wrapper(*args, **kwargs):
918 gcenabled = gc.isenabled()
918 gcenabled = gc.isenabled()
919 gc.disable()
919 gc.disable()
920 try:
920 try:
921 return func(*args, **kwargs)
921 return func(*args, **kwargs)
922 finally:
922 finally:
923 if gcenabled:
923 if gcenabled:
924 gc.enable()
924 gc.enable()
925 return wrapper
925 return wrapper
926
926
927 def pathto(root, n1, n2):
927 def pathto(root, n1, n2):
928 '''return the relative path from one place to another.
928 '''return the relative path from one place to another.
929 root should use os.sep to separate directories
929 root should use os.sep to separate directories
930 n1 should use os.sep to separate directories
930 n1 should use os.sep to separate directories
931 n2 should use "/" to separate directories
931 n2 should use "/" to separate directories
932 returns an os.sep-separated path.
932 returns an os.sep-separated path.
933
933
934 If n1 is a relative path, it's assumed it's
934 If n1 is a relative path, it's assumed it's
935 relative to root.
935 relative to root.
936 n2 should always be relative to root.
936 n2 should always be relative to root.
937 '''
937 '''
938 if not n1:
938 if not n1:
939 return localpath(n2)
939 return localpath(n2)
940 if os.path.isabs(n1):
940 if os.path.isabs(n1):
941 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
941 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
942 return os.path.join(root, localpath(n2))
942 return os.path.join(root, localpath(n2))
943 n2 = '/'.join((pconvert(root), n2))
943 n2 = '/'.join((pconvert(root), n2))
944 a, b = splitpath(n1), n2.split('/')
944 a, b = splitpath(n1), n2.split('/')
945 a.reverse()
945 a.reverse()
946 b.reverse()
946 b.reverse()
947 while a and b and a[-1] == b[-1]:
947 while a and b and a[-1] == b[-1]:
948 a.pop()
948 a.pop()
949 b.pop()
949 b.pop()
950 b.reverse()
950 b.reverse()
951 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
951 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
952
952
953 def mainfrozen():
953 def mainfrozen():
954 """return True if we are a frozen executable.
954 """return True if we are a frozen executable.
955
955
956 The code supports py2exe (most common, Windows only) and tools/freeze
956 The code supports py2exe (most common, Windows only) and tools/freeze
957 (portable, not much used).
957 (portable, not much used).
958 """
958 """
959 return (safehasattr(sys, "frozen") or # new py2exe
959 return (safehasattr(sys, "frozen") or # new py2exe
960 safehasattr(sys, "importers") or # old py2exe
960 safehasattr(sys, "importers") or # old py2exe
961 imp.is_frozen(u"__main__")) # tools/freeze
961 imp.is_frozen(u"__main__")) # tools/freeze
962
962
963 # the location of data files matching the source code
963 # the location of data files matching the source code
964 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
964 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
965 # executable version (py2exe) doesn't support __file__
965 # executable version (py2exe) doesn't support __file__
966 datapath = os.path.dirname(pycompat.sysexecutable)
966 datapath = os.path.dirname(pycompat.sysexecutable)
967 else:
967 else:
968 datapath = os.path.dirname(pycompat.fsencode(__file__))
968 datapath = os.path.dirname(pycompat.fsencode(__file__))
969
969
970 i18n.setdatapath(datapath)
970 i18n.setdatapath(datapath)
971
971
972 _hgexecutable = None
972 _hgexecutable = None
973
973
974 def hgexecutable():
974 def hgexecutable():
975 """return location of the 'hg' executable.
975 """return location of the 'hg' executable.
976
976
977 Defaults to $HG or 'hg' in the search path.
977 Defaults to $HG or 'hg' in the search path.
978 """
978 """
979 if _hgexecutable is None:
979 if _hgexecutable is None:
980 hg = encoding.environ.get('HG')
980 hg = encoding.environ.get('HG')
981 mainmod = sys.modules[pycompat.sysstr('__main__')]
981 mainmod = sys.modules[pycompat.sysstr('__main__')]
982 if hg:
982 if hg:
983 _sethgexecutable(hg)
983 _sethgexecutable(hg)
984 elif mainfrozen():
984 elif mainfrozen():
985 if getattr(sys, 'frozen', None) == 'macosx_app':
985 if getattr(sys, 'frozen', None) == 'macosx_app':
986 # Env variable set by py2app
986 # Env variable set by py2app
987 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
987 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
988 else:
988 else:
989 _sethgexecutable(pycompat.sysexecutable)
989 _sethgexecutable(pycompat.sysexecutable)
990 elif (os.path.basename(
990 elif (os.path.basename(
991 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
991 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
992 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
992 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
993 else:
993 else:
994 exe = findexe('hg') or os.path.basename(sys.argv[0])
994 exe = findexe('hg') or os.path.basename(sys.argv[0])
995 _sethgexecutable(exe)
995 _sethgexecutable(exe)
996 return _hgexecutable
996 return _hgexecutable
997
997
998 def _sethgexecutable(path):
998 def _sethgexecutable(path):
999 """set location of the 'hg' executable"""
999 """set location of the 'hg' executable"""
1000 global _hgexecutable
1000 global _hgexecutable
1001 _hgexecutable = path
1001 _hgexecutable = path
1002
1002
1003 def _isstdout(f):
1003 def _isstdout(f):
1004 fileno = getattr(f, 'fileno', None)
1004 fileno = getattr(f, 'fileno', None)
1005 return fileno and fileno() == sys.__stdout__.fileno()
1005 return fileno and fileno() == sys.__stdout__.fileno()
1006
1006
1007 def shellenviron(environ=None):
1007 def shellenviron(environ=None):
1008 """return environ with optional override, useful for shelling out"""
1008 """return environ with optional override, useful for shelling out"""
1009 def py2shell(val):
1009 def py2shell(val):
1010 'convert python object into string that is useful to shell'
1010 'convert python object into string that is useful to shell'
1011 if val is None or val is False:
1011 if val is None or val is False:
1012 return '0'
1012 return '0'
1013 if val is True:
1013 if val is True:
1014 return '1'
1014 return '1'
1015 return str(val)
1015 return str(val)
1016 env = dict(encoding.environ)
1016 env = dict(encoding.environ)
1017 if environ:
1017 if environ:
1018 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1018 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1019 env['HG'] = hgexecutable()
1019 env['HG'] = hgexecutable()
1020 return env
1020 return env
1021
1021
1022 def system(cmd, environ=None, cwd=None, out=None):
1022 def system(cmd, environ=None, cwd=None, out=None):
1023 '''enhanced shell command execution.
1023 '''enhanced shell command execution.
1024 run with environment maybe modified, maybe in different dir.
1024 run with environment maybe modified, maybe in different dir.
1025
1025
1026 if out is specified, it is assumed to be a file-like object that has a
1026 if out is specified, it is assumed to be a file-like object that has a
1027 write() method. stdout and stderr will be redirected to out.'''
1027 write() method. stdout and stderr will be redirected to out.'''
1028 try:
1028 try:
1029 stdout.flush()
1029 stdout.flush()
1030 except Exception:
1030 except Exception:
1031 pass
1031 pass
1032 cmd = quotecommand(cmd)
1032 cmd = quotecommand(cmd)
1033 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1033 if pycompat.sysplatform == 'plan9' and (sys.version_info[0] == 2
1034 and sys.version_info[1] < 7):
1034 and sys.version_info[1] < 7):
1035 # subprocess kludge to work around issues in half-baked Python
1035 # subprocess kludge to work around issues in half-baked Python
1036 # ports, notably bichued/python:
1036 # ports, notably bichued/python:
1037 if not cwd is None:
1037 if not cwd is None:
1038 os.chdir(cwd)
1038 os.chdir(cwd)
1039 rc = os.system(cmd)
1039 rc = os.system(cmd)
1040 else:
1040 else:
1041 env = shellenviron(environ)
1041 env = shellenviron(environ)
1042 if out is None or _isstdout(out):
1042 if out is None or _isstdout(out):
1043 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1043 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1044 env=env, cwd=cwd)
1044 env=env, cwd=cwd)
1045 else:
1045 else:
1046 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1046 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1047 env=env, cwd=cwd, stdout=subprocess.PIPE,
1047 env=env, cwd=cwd, stdout=subprocess.PIPE,
1048 stderr=subprocess.STDOUT)
1048 stderr=subprocess.STDOUT)
1049 for line in iter(proc.stdout.readline, ''):
1049 for line in iter(proc.stdout.readline, ''):
1050 out.write(line)
1050 out.write(line)
1051 proc.wait()
1051 proc.wait()
1052 rc = proc.returncode
1052 rc = proc.returncode
1053 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1053 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1054 rc = 0
1054 rc = 0
1055 return rc
1055 return rc
1056
1056
1057 def checksignature(func):
1057 def checksignature(func):
1058 '''wrap a function with code to check for calling errors'''
1058 '''wrap a function with code to check for calling errors'''
1059 def check(*args, **kwargs):
1059 def check(*args, **kwargs):
1060 try:
1060 try:
1061 return func(*args, **kwargs)
1061 return func(*args, **kwargs)
1062 except TypeError:
1062 except TypeError:
1063 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1063 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1064 raise error.SignatureError
1064 raise error.SignatureError
1065 raise
1065 raise
1066
1066
1067 return check
1067 return check
1068
1068
1069 # a whilelist of known filesystems where hardlink works reliably
1069 # a whilelist of known filesystems where hardlink works reliably
1070 _hardlinkfswhitelist = {
1070 _hardlinkfswhitelist = {
1071 'btrfs',
1071 'btrfs',
1072 'ext2',
1072 'ext2',
1073 'ext3',
1073 'ext3',
1074 'ext4',
1074 'ext4',
1075 'hfs',
1075 'hfs',
1076 'jfs',
1076 'jfs',
1077 'reiserfs',
1077 'reiserfs',
1078 'tmpfs',
1078 'tmpfs',
1079 'ufs',
1079 'ufs',
1080 'xfs',
1080 'xfs',
1081 'zfs',
1081 'zfs',
1082 }
1082 }
1083
1083
1084 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1084 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1085 '''copy a file, preserving mode and optionally other stat info like
1085 '''copy a file, preserving mode and optionally other stat info like
1086 atime/mtime
1086 atime/mtime
1087
1087
1088 checkambig argument is used with filestat, and is useful only if
1088 checkambig argument is used with filestat, and is useful only if
1089 destination file is guarded by any lock (e.g. repo.lock or
1089 destination file is guarded by any lock (e.g. repo.lock or
1090 repo.wlock).
1090 repo.wlock).
1091
1091
1092 copystat and checkambig should be exclusive.
1092 copystat and checkambig should be exclusive.
1093 '''
1093 '''
1094 assert not (copystat and checkambig)
1094 assert not (copystat and checkambig)
1095 oldstat = None
1095 oldstat = None
1096 if os.path.lexists(dest):
1096 if os.path.lexists(dest):
1097 if checkambig:
1097 if checkambig:
1098 oldstat = checkambig and filestat(dest)
1098 oldstat = checkambig and filestat(dest)
1099 unlink(dest)
1099 unlink(dest)
1100 if hardlink:
1100 if hardlink:
1101 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1101 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1102 # unless we are confident that dest is on a whitelisted filesystem.
1102 # unless we are confident that dest is on a whitelisted filesystem.
1103 try:
1103 try:
1104 fstype = getfstype(os.path.dirname(dest))
1104 fstype = getfstype(os.path.dirname(dest))
1105 except OSError:
1105 except OSError:
1106 fstype = None
1106 fstype = None
1107 if fstype not in _hardlinkfswhitelist:
1107 if fstype not in _hardlinkfswhitelist:
1108 hardlink = False
1108 hardlink = False
1109 if hardlink:
1109 if hardlink:
1110 try:
1110 try:
1111 oslink(src, dest)
1111 oslink(src, dest)
1112 return
1112 return
1113 except (IOError, OSError):
1113 except (IOError, OSError):
1114 pass # fall back to normal copy
1114 pass # fall back to normal copy
1115 if os.path.islink(src):
1115 if os.path.islink(src):
1116 os.symlink(os.readlink(src), dest)
1116 os.symlink(os.readlink(src), dest)
1117 # copytime is ignored for symlinks, but in general copytime isn't needed
1117 # copytime is ignored for symlinks, but in general copytime isn't needed
1118 # for them anyway
1118 # for them anyway
1119 else:
1119 else:
1120 try:
1120 try:
1121 shutil.copyfile(src, dest)
1121 shutil.copyfile(src, dest)
1122 if copystat:
1122 if copystat:
1123 # copystat also copies mode
1123 # copystat also copies mode
1124 shutil.copystat(src, dest)
1124 shutil.copystat(src, dest)
1125 else:
1125 else:
1126 shutil.copymode(src, dest)
1126 shutil.copymode(src, dest)
1127 if oldstat and oldstat.stat:
1127 if oldstat and oldstat.stat:
1128 newstat = filestat(dest)
1128 newstat = filestat(dest)
1129 if newstat.isambig(oldstat):
1129 if newstat.isambig(oldstat):
1130 # stat of copied file is ambiguous to original one
1130 # stat of copied file is ambiguous to original one
1131 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1131 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1132 os.utime(dest, (advanced, advanced))
1132 os.utime(dest, (advanced, advanced))
1133 except shutil.Error as inst:
1133 except shutil.Error as inst:
1134 raise Abort(str(inst))
1134 raise Abort(str(inst))
1135
1135
1136 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1136 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1137 """Copy a directory tree using hardlinks if possible."""
1137 """Copy a directory tree using hardlinks if possible."""
1138 num = 0
1138 num = 0
1139
1139
1140 gettopic = lambda: hardlink and _('linking') or _('copying')
1140 gettopic = lambda: hardlink and _('linking') or _('copying')
1141
1141
1142 if os.path.isdir(src):
1142 if os.path.isdir(src):
1143 if hardlink is None:
1143 if hardlink is None:
1144 hardlink = (os.stat(src).st_dev ==
1144 hardlink = (os.stat(src).st_dev ==
1145 os.stat(os.path.dirname(dst)).st_dev)
1145 os.stat(os.path.dirname(dst)).st_dev)
1146 topic = gettopic()
1146 topic = gettopic()
1147 os.mkdir(dst)
1147 os.mkdir(dst)
1148 for name, kind in listdir(src):
1148 for name, kind in listdir(src):
1149 srcname = os.path.join(src, name)
1149 srcname = os.path.join(src, name)
1150 dstname = os.path.join(dst, name)
1150 dstname = os.path.join(dst, name)
1151 def nprog(t, pos):
1151 def nprog(t, pos):
1152 if pos is not None:
1152 if pos is not None:
1153 return progress(t, pos + num)
1153 return progress(t, pos + num)
1154 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1154 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1155 num += n
1155 num += n
1156 else:
1156 else:
1157 if hardlink is None:
1157 if hardlink is None:
1158 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1158 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1159 os.stat(os.path.dirname(dst)).st_dev)
1159 os.stat(os.path.dirname(dst)).st_dev)
1160 topic = gettopic()
1160 topic = gettopic()
1161
1161
1162 if hardlink:
1162 if hardlink:
1163 try:
1163 try:
1164 oslink(src, dst)
1164 oslink(src, dst)
1165 except (IOError, OSError):
1165 except (IOError, OSError):
1166 hardlink = False
1166 hardlink = False
1167 shutil.copy(src, dst)
1167 shutil.copy(src, dst)
1168 else:
1168 else:
1169 shutil.copy(src, dst)
1169 shutil.copy(src, dst)
1170 num += 1
1170 num += 1
1171 progress(topic, num)
1171 progress(topic, num)
1172 progress(topic, None)
1172 progress(topic, None)
1173
1173
1174 return hardlink, num
1174 return hardlink, num
1175
1175
1176 _winreservednames = '''con prn aux nul
1176 _winreservednames = '''con prn aux nul
1177 com1 com2 com3 com4 com5 com6 com7 com8 com9
1177 com1 com2 com3 com4 com5 com6 com7 com8 com9
1178 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1178 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1179 _winreservedchars = ':*?"<>|'
1179 _winreservedchars = ':*?"<>|'
1180 def checkwinfilename(path):
1180 def checkwinfilename(path):
1181 r'''Check that the base-relative path is a valid filename on Windows.
1181 r'''Check that the base-relative path is a valid filename on Windows.
1182 Returns None if the path is ok, or a UI string describing the problem.
1182 Returns None if the path is ok, or a UI string describing the problem.
1183
1183
1184 >>> checkwinfilename("just/a/normal/path")
1184 >>> checkwinfilename("just/a/normal/path")
1185 >>> checkwinfilename("foo/bar/con.xml")
1185 >>> checkwinfilename("foo/bar/con.xml")
1186 "filename contains 'con', which is reserved on Windows"
1186 "filename contains 'con', which is reserved on Windows"
1187 >>> checkwinfilename("foo/con.xml/bar")
1187 >>> checkwinfilename("foo/con.xml/bar")
1188 "filename contains 'con', which is reserved on Windows"
1188 "filename contains 'con', which is reserved on Windows"
1189 >>> checkwinfilename("foo/bar/xml.con")
1189 >>> checkwinfilename("foo/bar/xml.con")
1190 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1190 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1191 "filename contains 'AUX', which is reserved on Windows"
1191 "filename contains 'AUX', which is reserved on Windows"
1192 >>> checkwinfilename("foo/bar/bla:.txt")
1192 >>> checkwinfilename("foo/bar/bla:.txt")
1193 "filename contains ':', which is reserved on Windows"
1193 "filename contains ':', which is reserved on Windows"
1194 >>> checkwinfilename("foo/bar/b\07la.txt")
1194 >>> checkwinfilename("foo/bar/b\07la.txt")
1195 "filename contains '\\x07', which is invalid on Windows"
1195 "filename contains '\\x07', which is invalid on Windows"
1196 >>> checkwinfilename("foo/bar/bla ")
1196 >>> checkwinfilename("foo/bar/bla ")
1197 "filename ends with ' ', which is not allowed on Windows"
1197 "filename ends with ' ', which is not allowed on Windows"
1198 >>> checkwinfilename("../bar")
1198 >>> checkwinfilename("../bar")
1199 >>> checkwinfilename("foo\\")
1199 >>> checkwinfilename("foo\\")
1200 "filename ends with '\\', which is invalid on Windows"
1200 "filename ends with '\\', which is invalid on Windows"
1201 >>> checkwinfilename("foo\\/bar")
1201 >>> checkwinfilename("foo\\/bar")
1202 "directory name ends with '\\', which is invalid on Windows"
1202 "directory name ends with '\\', which is invalid on Windows"
1203 '''
1203 '''
1204 if path.endswith('\\'):
1204 if path.endswith('\\'):
1205 return _("filename ends with '\\', which is invalid on Windows")
1205 return _("filename ends with '\\', which is invalid on Windows")
1206 if '\\/' in path:
1206 if '\\/' in path:
1207 return _("directory name ends with '\\', which is invalid on Windows")
1207 return _("directory name ends with '\\', which is invalid on Windows")
1208 for n in path.replace('\\', '/').split('/'):
1208 for n in path.replace('\\', '/').split('/'):
1209 if not n:
1209 if not n:
1210 continue
1210 continue
1211 for c in pycompat.bytestr(n):
1211 for c in pycompat.bytestr(n):
1212 if c in _winreservedchars:
1212 if c in _winreservedchars:
1213 return _("filename contains '%s', which is reserved "
1213 return _("filename contains '%s', which is reserved "
1214 "on Windows") % c
1214 "on Windows") % c
1215 if ord(c) <= 31:
1215 if ord(c) <= 31:
1216 return _("filename contains %r, which is invalid "
1216 return _("filename contains %r, which is invalid "
1217 "on Windows") % c
1217 "on Windows") % c
1218 base = n.split('.')[0]
1218 base = n.split('.')[0]
1219 if base and base.lower() in _winreservednames:
1219 if base and base.lower() in _winreservednames:
1220 return _("filename contains '%s', which is reserved "
1220 return _("filename contains '%s', which is reserved "
1221 "on Windows") % base
1221 "on Windows") % base
1222 t = n[-1]
1222 t = n[-1]
1223 if t in '. ' and n not in '..':
1223 if t in '. ' and n not in '..':
1224 return _("filename ends with '%s', which is not allowed "
1224 return _("filename ends with '%s', which is not allowed "
1225 "on Windows") % t
1225 "on Windows") % t
1226
1226
1227 if pycompat.osname == 'nt':
1227 if pycompat.osname == 'nt':
1228 checkosfilename = checkwinfilename
1228 checkosfilename = checkwinfilename
1229 timer = time.clock
1229 timer = time.clock
1230 else:
1230 else:
1231 checkosfilename = platform.checkosfilename
1231 checkosfilename = platform.checkosfilename
1232 timer = time.time
1232 timer = time.time
1233
1233
1234 if safehasattr(time, "perf_counter"):
1234 if safehasattr(time, "perf_counter"):
1235 timer = time.perf_counter
1235 timer = time.perf_counter
1236
1236
1237 def makelock(info, pathname):
1237 def makelock(info, pathname):
1238 try:
1238 try:
1239 return os.symlink(info, pathname)
1239 return os.symlink(info, pathname)
1240 except OSError as why:
1240 except OSError as why:
1241 if why.errno == errno.EEXIST:
1241 if why.errno == errno.EEXIST:
1242 raise
1242 raise
1243 except AttributeError: # no symlink in os
1243 except AttributeError: # no symlink in os
1244 pass
1244 pass
1245
1245
1246 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1246 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1247 os.write(ld, info)
1247 os.write(ld, info)
1248 os.close(ld)
1248 os.close(ld)
1249
1249
1250 def readlock(pathname):
1250 def readlock(pathname):
1251 try:
1251 try:
1252 return os.readlink(pathname)
1252 return os.readlink(pathname)
1253 except OSError as why:
1253 except OSError as why:
1254 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1254 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1255 raise
1255 raise
1256 except AttributeError: # no symlink in os
1256 except AttributeError: # no symlink in os
1257 pass
1257 pass
1258 fp = posixfile(pathname)
1258 fp = posixfile(pathname)
1259 r = fp.read()
1259 r = fp.read()
1260 fp.close()
1260 fp.close()
1261 return r
1261 return r
1262
1262
1263 def fstat(fp):
1263 def fstat(fp):
1264 '''stat file object that may not have fileno method.'''
1264 '''stat file object that may not have fileno method.'''
1265 try:
1265 try:
1266 return os.fstat(fp.fileno())
1266 return os.fstat(fp.fileno())
1267 except AttributeError:
1267 except AttributeError:
1268 return os.stat(fp.name)
1268 return os.stat(fp.name)
1269
1269
1270 # File system features
1270 # File system features
1271
1271
1272 def fscasesensitive(path):
1272 def fscasesensitive(path):
1273 """
1273 """
1274 Return true if the given path is on a case-sensitive filesystem
1274 Return true if the given path is on a case-sensitive filesystem
1275
1275
1276 Requires a path (like /foo/.hg) ending with a foldable final
1276 Requires a path (like /foo/.hg) ending with a foldable final
1277 directory component.
1277 directory component.
1278 """
1278 """
1279 s1 = os.lstat(path)
1279 s1 = os.lstat(path)
1280 d, b = os.path.split(path)
1280 d, b = os.path.split(path)
1281 b2 = b.upper()
1281 b2 = b.upper()
1282 if b == b2:
1282 if b == b2:
1283 b2 = b.lower()
1283 b2 = b.lower()
1284 if b == b2:
1284 if b == b2:
1285 return True # no evidence against case sensitivity
1285 return True # no evidence against case sensitivity
1286 p2 = os.path.join(d, b2)
1286 p2 = os.path.join(d, b2)
1287 try:
1287 try:
1288 s2 = os.lstat(p2)
1288 s2 = os.lstat(p2)
1289 if s2 == s1:
1289 if s2 == s1:
1290 return False
1290 return False
1291 return True
1291 return True
1292 except OSError:
1292 except OSError:
1293 return True
1293 return True
1294
1294
1295 try:
1295 try:
1296 import re2
1296 import re2
1297 _re2 = None
1297 _re2 = None
1298 except ImportError:
1298 except ImportError:
1299 _re2 = False
1299 _re2 = False
1300
1300
1301 class _re(object):
1301 class _re(object):
1302 def _checkre2(self):
1302 def _checkre2(self):
1303 global _re2
1303 global _re2
1304 try:
1304 try:
1305 # check if match works, see issue3964
1305 # check if match works, see issue3964
1306 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1306 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1307 except ImportError:
1307 except ImportError:
1308 _re2 = False
1308 _re2 = False
1309
1309
1310 def compile(self, pat, flags=0):
1310 def compile(self, pat, flags=0):
1311 '''Compile a regular expression, using re2 if possible
1311 '''Compile a regular expression, using re2 if possible
1312
1312
1313 For best performance, use only re2-compatible regexp features. The
1313 For best performance, use only re2-compatible regexp features. The
1314 only flags from the re module that are re2-compatible are
1314 only flags from the re module that are re2-compatible are
1315 IGNORECASE and MULTILINE.'''
1315 IGNORECASE and MULTILINE.'''
1316 if _re2 is None:
1316 if _re2 is None:
1317 self._checkre2()
1317 self._checkre2()
1318 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1318 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1319 if flags & remod.IGNORECASE:
1319 if flags & remod.IGNORECASE:
1320 pat = '(?i)' + pat
1320 pat = '(?i)' + pat
1321 if flags & remod.MULTILINE:
1321 if flags & remod.MULTILINE:
1322 pat = '(?m)' + pat
1322 pat = '(?m)' + pat
1323 try:
1323 try:
1324 return re2.compile(pat)
1324 return re2.compile(pat)
1325 except re2.error:
1325 except re2.error:
1326 pass
1326 pass
1327 return remod.compile(pat, flags)
1327 return remod.compile(pat, flags)
1328
1328
1329 @propertycache
1329 @propertycache
1330 def escape(self):
1330 def escape(self):
1331 '''Return the version of escape corresponding to self.compile.
1331 '''Return the version of escape corresponding to self.compile.
1332
1332
1333 This is imperfect because whether re2 or re is used for a particular
1333 This is imperfect because whether re2 or re is used for a particular
1334 function depends on the flags, etc, but it's the best we can do.
1334 function depends on the flags, etc, but it's the best we can do.
1335 '''
1335 '''
1336 global _re2
1336 global _re2
1337 if _re2 is None:
1337 if _re2 is None:
1338 self._checkre2()
1338 self._checkre2()
1339 if _re2:
1339 if _re2:
1340 return re2.escape
1340 return re2.escape
1341 else:
1341 else:
1342 return remod.escape
1342 return remod.escape
1343
1343
1344 re = _re()
1344 re = _re()
1345
1345
1346 _fspathcache = {}
1346 _fspathcache = {}
1347 def fspath(name, root):
1347 def fspath(name, root):
1348 '''Get name in the case stored in the filesystem
1348 '''Get name in the case stored in the filesystem
1349
1349
1350 The name should be relative to root, and be normcase-ed for efficiency.
1350 The name should be relative to root, and be normcase-ed for efficiency.
1351
1351
1352 Note that this function is unnecessary, and should not be
1352 Note that this function is unnecessary, and should not be
1353 called, for case-sensitive filesystems (simply because it's expensive).
1353 called, for case-sensitive filesystems (simply because it's expensive).
1354
1354
1355 The root should be normcase-ed, too.
1355 The root should be normcase-ed, too.
1356 '''
1356 '''
1357 def _makefspathcacheentry(dir):
1357 def _makefspathcacheentry(dir):
1358 return dict((normcase(n), n) for n in os.listdir(dir))
1358 return dict((normcase(n), n) for n in os.listdir(dir))
1359
1359
1360 seps = pycompat.ossep
1360 seps = pycompat.ossep
1361 if pycompat.osaltsep:
1361 if pycompat.osaltsep:
1362 seps = seps + pycompat.osaltsep
1362 seps = seps + pycompat.osaltsep
1363 # Protect backslashes. This gets silly very quickly.
1363 # Protect backslashes. This gets silly very quickly.
1364 seps.replace('\\','\\\\')
1364 seps.replace('\\','\\\\')
1365 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1365 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1366 dir = os.path.normpath(root)
1366 dir = os.path.normpath(root)
1367 result = []
1367 result = []
1368 for part, sep in pattern.findall(name):
1368 for part, sep in pattern.findall(name):
1369 if sep:
1369 if sep:
1370 result.append(sep)
1370 result.append(sep)
1371 continue
1371 continue
1372
1372
1373 if dir not in _fspathcache:
1373 if dir not in _fspathcache:
1374 _fspathcache[dir] = _makefspathcacheentry(dir)
1374 _fspathcache[dir] = _makefspathcacheentry(dir)
1375 contents = _fspathcache[dir]
1375 contents = _fspathcache[dir]
1376
1376
1377 found = contents.get(part)
1377 found = contents.get(part)
1378 if not found:
1378 if not found:
1379 # retry "once per directory" per "dirstate.walk" which
1379 # retry "once per directory" per "dirstate.walk" which
1380 # may take place for each patches of "hg qpush", for example
1380 # may take place for each patches of "hg qpush", for example
1381 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1381 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1382 found = contents.get(part)
1382 found = contents.get(part)
1383
1383
1384 result.append(found or part)
1384 result.append(found or part)
1385 dir = os.path.join(dir, part)
1385 dir = os.path.join(dir, part)
1386
1386
1387 return ''.join(result)
1387 return ''.join(result)
1388
1388
1389 def getfstype(dirpath):
1389 def getfstype(dirpath):
1390 '''Get the filesystem type name from a directory (best-effort)
1390 '''Get the filesystem type name from a directory (best-effort)
1391
1391
1392 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1392 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
1393 '''
1393 '''
1394 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1394 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
1395
1395
1396 def checknlink(testfile):
1396 def checknlink(testfile):
1397 '''check whether hardlink count reporting works properly'''
1397 '''check whether hardlink count reporting works properly'''
1398
1398
1399 # testfile may be open, so we need a separate file for checking to
1399 # testfile may be open, so we need a separate file for checking to
1400 # work around issue2543 (or testfile may get lost on Samba shares)
1400 # work around issue2543 (or testfile may get lost on Samba shares)
1401 f1 = testfile + ".hgtmp1"
1401 f1 = testfile + ".hgtmp1"
1402 if os.path.lexists(f1):
1402 if os.path.lexists(f1):
1403 return False
1403 return False
1404 try:
1404 try:
1405 posixfile(f1, 'w').close()
1405 posixfile(f1, 'w').close()
1406 except IOError:
1406 except IOError:
1407 try:
1407 try:
1408 os.unlink(f1)
1408 os.unlink(f1)
1409 except OSError:
1409 except OSError:
1410 pass
1410 pass
1411 return False
1411 return False
1412
1412
1413 f2 = testfile + ".hgtmp2"
1413 f2 = testfile + ".hgtmp2"
1414 fd = None
1414 fd = None
1415 try:
1415 try:
1416 oslink(f1, f2)
1416 oslink(f1, f2)
1417 # nlinks() may behave differently for files on Windows shares if
1417 # nlinks() may behave differently for files on Windows shares if
1418 # the file is open.
1418 # the file is open.
1419 fd = posixfile(f2)
1419 fd = posixfile(f2)
1420 return nlinks(f2) > 1
1420 return nlinks(f2) > 1
1421 except OSError:
1421 except OSError:
1422 return False
1422 return False
1423 finally:
1423 finally:
1424 if fd is not None:
1424 if fd is not None:
1425 fd.close()
1425 fd.close()
1426 for f in (f1, f2):
1426 for f in (f1, f2):
1427 try:
1427 try:
1428 os.unlink(f)
1428 os.unlink(f)
1429 except OSError:
1429 except OSError:
1430 pass
1430 pass
1431
1431
1432 def endswithsep(path):
1432 def endswithsep(path):
1433 '''Check path ends with os.sep or os.altsep.'''
1433 '''Check path ends with os.sep or os.altsep.'''
1434 return (path.endswith(pycompat.ossep)
1434 return (path.endswith(pycompat.ossep)
1435 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1435 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1436
1436
1437 def splitpath(path):
1437 def splitpath(path):
1438 '''Split path by os.sep.
1438 '''Split path by os.sep.
1439 Note that this function does not use os.altsep because this is
1439 Note that this function does not use os.altsep because this is
1440 an alternative of simple "xxx.split(os.sep)".
1440 an alternative of simple "xxx.split(os.sep)".
1441 It is recommended to use os.path.normpath() before using this
1441 It is recommended to use os.path.normpath() before using this
1442 function if need.'''
1442 function if need.'''
1443 return path.split(pycompat.ossep)
1443 return path.split(pycompat.ossep)
1444
1444
1445 def gui():
1445 def gui():
1446 '''Are we running in a GUI?'''
1446 '''Are we running in a GUI?'''
1447 if pycompat.sysplatform == 'darwin':
1447 if pycompat.sysplatform == 'darwin':
1448 if 'SSH_CONNECTION' in encoding.environ:
1448 if 'SSH_CONNECTION' in encoding.environ:
1449 # handle SSH access to a box where the user is logged in
1449 # handle SSH access to a box where the user is logged in
1450 return False
1450 return False
1451 elif getattr(osutil, 'isgui', None):
1451 elif getattr(osutil, 'isgui', None):
1452 # check if a CoreGraphics session is available
1452 # check if a CoreGraphics session is available
1453 return osutil.isgui()
1453 return osutil.isgui()
1454 else:
1454 else:
1455 # pure build; use a safe default
1455 # pure build; use a safe default
1456 return True
1456 return True
1457 else:
1457 else:
1458 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1458 return pycompat.osname == "nt" or encoding.environ.get("DISPLAY")
1459
1459
1460 def mktempcopy(name, emptyok=False, createmode=None):
1460 def mktempcopy(name, emptyok=False, createmode=None):
1461 """Create a temporary file with the same contents from name
1461 """Create a temporary file with the same contents from name
1462
1462
1463 The permission bits are copied from the original file.
1463 The permission bits are copied from the original file.
1464
1464
1465 If the temporary file is going to be truncated immediately, you
1465 If the temporary file is going to be truncated immediately, you
1466 can use emptyok=True as an optimization.
1466 can use emptyok=True as an optimization.
1467
1467
1468 Returns the name of the temporary file.
1468 Returns the name of the temporary file.
1469 """
1469 """
1470 d, fn = os.path.split(name)
1470 d, fn = os.path.split(name)
1471 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1471 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1472 os.close(fd)
1472 os.close(fd)
1473 # Temporary files are created with mode 0600, which is usually not
1473 # Temporary files are created with mode 0600, which is usually not
1474 # what we want. If the original file already exists, just copy
1474 # what we want. If the original file already exists, just copy
1475 # its mode. Otherwise, manually obey umask.
1475 # its mode. Otherwise, manually obey umask.
1476 copymode(name, temp, createmode)
1476 copymode(name, temp, createmode)
1477 if emptyok:
1477 if emptyok:
1478 return temp
1478 return temp
1479 try:
1479 try:
1480 try:
1480 try:
1481 ifp = posixfile(name, "rb")
1481 ifp = posixfile(name, "rb")
1482 except IOError as inst:
1482 except IOError as inst:
1483 if inst.errno == errno.ENOENT:
1483 if inst.errno == errno.ENOENT:
1484 return temp
1484 return temp
1485 if not getattr(inst, 'filename', None):
1485 if not getattr(inst, 'filename', None):
1486 inst.filename = name
1486 inst.filename = name
1487 raise
1487 raise
1488 ofp = posixfile(temp, "wb")
1488 ofp = posixfile(temp, "wb")
1489 for chunk in filechunkiter(ifp):
1489 for chunk in filechunkiter(ifp):
1490 ofp.write(chunk)
1490 ofp.write(chunk)
1491 ifp.close()
1491 ifp.close()
1492 ofp.close()
1492 ofp.close()
1493 except: # re-raises
1493 except: # re-raises
1494 try: os.unlink(temp)
1494 try: os.unlink(temp)
1495 except OSError: pass
1495 except OSError: pass
1496 raise
1496 raise
1497 return temp
1497 return temp
1498
1498
1499 class filestat(object):
1499 class filestat(object):
1500 """help to exactly detect change of a file
1500 """help to exactly detect change of a file
1501
1501
1502 'stat' attribute is result of 'os.stat()' if specified 'path'
1502 'stat' attribute is result of 'os.stat()' if specified 'path'
1503 exists. Otherwise, it is None. This can avoid preparative
1503 exists. Otherwise, it is None. This can avoid preparative
1504 'exists()' examination on client side of this class.
1504 'exists()' examination on client side of this class.
1505 """
1505 """
1506 def __init__(self, path):
1506 def __init__(self, path):
1507 try:
1507 try:
1508 self.stat = os.stat(path)
1508 self.stat = os.stat(path)
1509 except OSError as err:
1509 except OSError as err:
1510 if err.errno != errno.ENOENT:
1510 if err.errno != errno.ENOENT:
1511 raise
1511 raise
1512 self.stat = None
1512 self.stat = None
1513
1513
1514 __hash__ = object.__hash__
1514 __hash__ = object.__hash__
1515
1515
1516 def __eq__(self, old):
1516 def __eq__(self, old):
1517 try:
1517 try:
1518 # if ambiguity between stat of new and old file is
1518 # if ambiguity between stat of new and old file is
1519 # avoided, comparison of size, ctime and mtime is enough
1519 # avoided, comparison of size, ctime and mtime is enough
1520 # to exactly detect change of a file regardless of platform
1520 # to exactly detect change of a file regardless of platform
1521 return (self.stat.st_size == old.stat.st_size and
1521 return (self.stat.st_size == old.stat.st_size and
1522 self.stat.st_ctime == old.stat.st_ctime and
1522 self.stat.st_ctime == old.stat.st_ctime and
1523 self.stat.st_mtime == old.stat.st_mtime)
1523 self.stat.st_mtime == old.stat.st_mtime)
1524 except AttributeError:
1524 except AttributeError:
1525 return False
1525 return False
1526
1526
1527 def isambig(self, old):
1527 def isambig(self, old):
1528 """Examine whether new (= self) stat is ambiguous against old one
1528 """Examine whether new (= self) stat is ambiguous against old one
1529
1529
1530 "S[N]" below means stat of a file at N-th change:
1530 "S[N]" below means stat of a file at N-th change:
1531
1531
1532 - S[n-1].ctime < S[n].ctime: can detect change of a file
1532 - S[n-1].ctime < S[n].ctime: can detect change of a file
1533 - S[n-1].ctime == S[n].ctime
1533 - S[n-1].ctime == S[n].ctime
1534 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1534 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1535 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1535 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1536 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1536 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1537 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1537 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1538
1538
1539 Case (*2) above means that a file was changed twice or more at
1539 Case (*2) above means that a file was changed twice or more at
1540 same time in sec (= S[n-1].ctime), and comparison of timestamp
1540 same time in sec (= S[n-1].ctime), and comparison of timestamp
1541 is ambiguous.
1541 is ambiguous.
1542
1542
1543 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1543 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1544 timestamp is ambiguous".
1544 timestamp is ambiguous".
1545
1545
1546 But advancing mtime only in case (*2) doesn't work as
1546 But advancing mtime only in case (*2) doesn't work as
1547 expected, because naturally advanced S[n].mtime in case (*1)
1547 expected, because naturally advanced S[n].mtime in case (*1)
1548 might be equal to manually advanced S[n-1 or earlier].mtime.
1548 might be equal to manually advanced S[n-1 or earlier].mtime.
1549
1549
1550 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1550 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1551 treated as ambiguous regardless of mtime, to avoid overlooking
1551 treated as ambiguous regardless of mtime, to avoid overlooking
1552 by confliction between such mtime.
1552 by confliction between such mtime.
1553
1553
1554 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1554 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1555 S[n].mtime", even if size of a file isn't changed.
1555 S[n].mtime", even if size of a file isn't changed.
1556 """
1556 """
1557 try:
1557 try:
1558 return (self.stat.st_ctime == old.stat.st_ctime)
1558 return (self.stat.st_ctime == old.stat.st_ctime)
1559 except AttributeError:
1559 except AttributeError:
1560 return False
1560 return False
1561
1561
1562 def avoidambig(self, path, old):
1562 def avoidambig(self, path, old):
1563 """Change file stat of specified path to avoid ambiguity
1563 """Change file stat of specified path to avoid ambiguity
1564
1564
1565 'old' should be previous filestat of 'path'.
1565 'old' should be previous filestat of 'path'.
1566
1566
1567 This skips avoiding ambiguity, if a process doesn't have
1567 This skips avoiding ambiguity, if a process doesn't have
1568 appropriate privileges for 'path'.
1568 appropriate privileges for 'path'.
1569 """
1569 """
1570 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1570 advanced = (old.stat.st_mtime + 1) & 0x7fffffff
1571 try:
1571 try:
1572 os.utime(path, (advanced, advanced))
1572 os.utime(path, (advanced, advanced))
1573 except OSError as inst:
1573 except OSError as inst:
1574 if inst.errno == errno.EPERM:
1574 if inst.errno == errno.EPERM:
1575 # utime() on the file created by another user causes EPERM,
1575 # utime() on the file created by another user causes EPERM,
1576 # if a process doesn't have appropriate privileges
1576 # if a process doesn't have appropriate privileges
1577 return
1577 return
1578 raise
1578 raise
1579
1579
1580 def __ne__(self, other):
1580 def __ne__(self, other):
1581 return not self == other
1581 return not self == other
1582
1582
1583 class atomictempfile(object):
1583 class atomictempfile(object):
1584 '''writable file object that atomically updates a file
1584 '''writable file object that atomically updates a file
1585
1585
1586 All writes will go to a temporary copy of the original file. Call
1586 All writes will go to a temporary copy of the original file. Call
1587 close() when you are done writing, and atomictempfile will rename
1587 close() when you are done writing, and atomictempfile will rename
1588 the temporary copy to the original name, making the changes
1588 the temporary copy to the original name, making the changes
1589 visible. If the object is destroyed without being closed, all your
1589 visible. If the object is destroyed without being closed, all your
1590 writes are discarded.
1590 writes are discarded.
1591
1591
1592 checkambig argument of constructor is used with filestat, and is
1592 checkambig argument of constructor is used with filestat, and is
1593 useful only if target file is guarded by any lock (e.g. repo.lock
1593 useful only if target file is guarded by any lock (e.g. repo.lock
1594 or repo.wlock).
1594 or repo.wlock).
1595 '''
1595 '''
1596 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1596 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1597 self.__name = name # permanent name
1597 self.__name = name # permanent name
1598 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1598 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1599 createmode=createmode)
1599 createmode=createmode)
1600 self._fp = posixfile(self._tempname, mode)
1600 self._fp = posixfile(self._tempname, mode)
1601 self._checkambig = checkambig
1601 self._checkambig = checkambig
1602
1602
1603 # delegated methods
1603 # delegated methods
1604 self.read = self._fp.read
1604 self.read = self._fp.read
1605 self.write = self._fp.write
1605 self.write = self._fp.write
1606 self.seek = self._fp.seek
1606 self.seek = self._fp.seek
1607 self.tell = self._fp.tell
1607 self.tell = self._fp.tell
1608 self.fileno = self._fp.fileno
1608 self.fileno = self._fp.fileno
1609
1609
1610 def close(self):
1610 def close(self):
1611 if not self._fp.closed:
1611 if not self._fp.closed:
1612 self._fp.close()
1612 self._fp.close()
1613 filename = localpath(self.__name)
1613 filename = localpath(self.__name)
1614 oldstat = self._checkambig and filestat(filename)
1614 oldstat = self._checkambig and filestat(filename)
1615 if oldstat and oldstat.stat:
1615 if oldstat and oldstat.stat:
1616 rename(self._tempname, filename)
1616 rename(self._tempname, filename)
1617 newstat = filestat(filename)
1617 newstat = filestat(filename)
1618 if newstat.isambig(oldstat):
1618 if newstat.isambig(oldstat):
1619 # stat of changed file is ambiguous to original one
1619 # stat of changed file is ambiguous to original one
1620 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1620 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1621 os.utime(filename, (advanced, advanced))
1621 os.utime(filename, (advanced, advanced))
1622 else:
1622 else:
1623 rename(self._tempname, filename)
1623 rename(self._tempname, filename)
1624
1624
1625 def discard(self):
1625 def discard(self):
1626 if not self._fp.closed:
1626 if not self._fp.closed:
1627 try:
1627 try:
1628 os.unlink(self._tempname)
1628 os.unlink(self._tempname)
1629 except OSError:
1629 except OSError:
1630 pass
1630 pass
1631 self._fp.close()
1631 self._fp.close()
1632
1632
1633 def __del__(self):
1633 def __del__(self):
1634 if safehasattr(self, '_fp'): # constructor actually did something
1634 if safehasattr(self, '_fp'): # constructor actually did something
1635 self.discard()
1635 self.discard()
1636
1636
1637 def __enter__(self):
1637 def __enter__(self):
1638 return self
1638 return self
1639
1639
1640 def __exit__(self, exctype, excvalue, traceback):
1640 def __exit__(self, exctype, excvalue, traceback):
1641 if exctype is not None:
1641 if exctype is not None:
1642 self.discard()
1642 self.discard()
1643 else:
1643 else:
1644 self.close()
1644 self.close()
1645
1645
1646 def unlinkpath(f, ignoremissing=False):
1646 def unlinkpath(f, ignoremissing=False):
1647 """unlink and remove the directory if it is empty"""
1647 """unlink and remove the directory if it is empty"""
1648 if ignoremissing:
1648 if ignoremissing:
1649 tryunlink(f)
1649 tryunlink(f)
1650 else:
1650 else:
1651 unlink(f)
1651 unlink(f)
1652 # try removing directories that might now be empty
1652 # try removing directories that might now be empty
1653 try:
1653 try:
1654 removedirs(os.path.dirname(f))
1654 removedirs(os.path.dirname(f))
1655 except OSError:
1655 except OSError:
1656 pass
1656 pass
1657
1657
1658 def tryunlink(f):
1658 def tryunlink(f):
1659 """Attempt to remove a file, ignoring ENOENT errors."""
1659 """Attempt to remove a file, ignoring ENOENT errors."""
1660 try:
1660 try:
1661 unlink(f)
1661 unlink(f)
1662 except OSError as e:
1662 except OSError as e:
1663 if e.errno != errno.ENOENT:
1663 if e.errno != errno.ENOENT:
1664 raise
1664 raise
1665
1665
1666 def makedirs(name, mode=None, notindexed=False):
1666 def makedirs(name, mode=None, notindexed=False):
1667 """recursive directory creation with parent mode inheritance
1667 """recursive directory creation with parent mode inheritance
1668
1668
1669 Newly created directories are marked as "not to be indexed by
1669 Newly created directories are marked as "not to be indexed by
1670 the content indexing service", if ``notindexed`` is specified
1670 the content indexing service", if ``notindexed`` is specified
1671 for "write" mode access.
1671 for "write" mode access.
1672 """
1672 """
1673 try:
1673 try:
1674 makedir(name, notindexed)
1674 makedir(name, notindexed)
1675 except OSError as err:
1675 except OSError as err:
1676 if err.errno == errno.EEXIST:
1676 if err.errno == errno.EEXIST:
1677 return
1677 return
1678 if err.errno != errno.ENOENT or not name:
1678 if err.errno != errno.ENOENT or not name:
1679 raise
1679 raise
1680 parent = os.path.dirname(os.path.abspath(name))
1680 parent = os.path.dirname(os.path.abspath(name))
1681 if parent == name:
1681 if parent == name:
1682 raise
1682 raise
1683 makedirs(parent, mode, notindexed)
1683 makedirs(parent, mode, notindexed)
1684 try:
1684 try:
1685 makedir(name, notindexed)
1685 makedir(name, notindexed)
1686 except OSError as err:
1686 except OSError as err:
1687 # Catch EEXIST to handle races
1687 # Catch EEXIST to handle races
1688 if err.errno == errno.EEXIST:
1688 if err.errno == errno.EEXIST:
1689 return
1689 return
1690 raise
1690 raise
1691 if mode is not None:
1691 if mode is not None:
1692 os.chmod(name, mode)
1692 os.chmod(name, mode)
1693
1693
1694 def readfile(path):
1694 def readfile(path):
1695 with open(path, 'rb') as fp:
1695 with open(path, 'rb') as fp:
1696 return fp.read()
1696 return fp.read()
1697
1697
1698 def writefile(path, text):
1698 def writefile(path, text):
1699 with open(path, 'wb') as fp:
1699 with open(path, 'wb') as fp:
1700 fp.write(text)
1700 fp.write(text)
1701
1701
1702 def appendfile(path, text):
1702 def appendfile(path, text):
1703 with open(path, 'ab') as fp:
1703 with open(path, 'ab') as fp:
1704 fp.write(text)
1704 fp.write(text)
1705
1705
1706 class chunkbuffer(object):
1706 class chunkbuffer(object):
1707 """Allow arbitrary sized chunks of data to be efficiently read from an
1707 """Allow arbitrary sized chunks of data to be efficiently read from an
1708 iterator over chunks of arbitrary size."""
1708 iterator over chunks of arbitrary size."""
1709
1709
1710 def __init__(self, in_iter):
1710 def __init__(self, in_iter):
1711 """in_iter is the iterator that's iterating over the input chunks."""
1711 """in_iter is the iterator that's iterating over the input chunks."""
1712 def splitbig(chunks):
1712 def splitbig(chunks):
1713 for chunk in chunks:
1713 for chunk in chunks:
1714 if len(chunk) > 2**20:
1714 if len(chunk) > 2**20:
1715 pos = 0
1715 pos = 0
1716 while pos < len(chunk):
1716 while pos < len(chunk):
1717 end = pos + 2 ** 18
1717 end = pos + 2 ** 18
1718 yield chunk[pos:end]
1718 yield chunk[pos:end]
1719 pos = end
1719 pos = end
1720 else:
1720 else:
1721 yield chunk
1721 yield chunk
1722 self.iter = splitbig(in_iter)
1722 self.iter = splitbig(in_iter)
1723 self._queue = collections.deque()
1723 self._queue = collections.deque()
1724 self._chunkoffset = 0
1724 self._chunkoffset = 0
1725
1725
1726 def read(self, l=None):
1726 def read(self, l=None):
1727 """Read L bytes of data from the iterator of chunks of data.
1727 """Read L bytes of data from the iterator of chunks of data.
1728 Returns less than L bytes if the iterator runs dry.
1728 Returns less than L bytes if the iterator runs dry.
1729
1729
1730 If size parameter is omitted, read everything"""
1730 If size parameter is omitted, read everything"""
1731 if l is None:
1731 if l is None:
1732 return ''.join(self.iter)
1732 return ''.join(self.iter)
1733
1733
1734 left = l
1734 left = l
1735 buf = []
1735 buf = []
1736 queue = self._queue
1736 queue = self._queue
1737 while left > 0:
1737 while left > 0:
1738 # refill the queue
1738 # refill the queue
1739 if not queue:
1739 if not queue:
1740 target = 2**18
1740 target = 2**18
1741 for chunk in self.iter:
1741 for chunk in self.iter:
1742 queue.append(chunk)
1742 queue.append(chunk)
1743 target -= len(chunk)
1743 target -= len(chunk)
1744 if target <= 0:
1744 if target <= 0:
1745 break
1745 break
1746 if not queue:
1746 if not queue:
1747 break
1747 break
1748
1748
1749 # The easy way to do this would be to queue.popleft(), modify the
1749 # The easy way to do this would be to queue.popleft(), modify the
1750 # chunk (if necessary), then queue.appendleft(). However, for cases
1750 # chunk (if necessary), then queue.appendleft(). However, for cases
1751 # where we read partial chunk content, this incurs 2 dequeue
1751 # where we read partial chunk content, this incurs 2 dequeue
1752 # mutations and creates a new str for the remaining chunk in the
1752 # mutations and creates a new str for the remaining chunk in the
1753 # queue. Our code below avoids this overhead.
1753 # queue. Our code below avoids this overhead.
1754
1754
1755 chunk = queue[0]
1755 chunk = queue[0]
1756 chunkl = len(chunk)
1756 chunkl = len(chunk)
1757 offset = self._chunkoffset
1757 offset = self._chunkoffset
1758
1758
1759 # Use full chunk.
1759 # Use full chunk.
1760 if offset == 0 and left >= chunkl:
1760 if offset == 0 and left >= chunkl:
1761 left -= chunkl
1761 left -= chunkl
1762 queue.popleft()
1762 queue.popleft()
1763 buf.append(chunk)
1763 buf.append(chunk)
1764 # self._chunkoffset remains at 0.
1764 # self._chunkoffset remains at 0.
1765 continue
1765 continue
1766
1766
1767 chunkremaining = chunkl - offset
1767 chunkremaining = chunkl - offset
1768
1768
1769 # Use all of unconsumed part of chunk.
1769 # Use all of unconsumed part of chunk.
1770 if left >= chunkremaining:
1770 if left >= chunkremaining:
1771 left -= chunkremaining
1771 left -= chunkremaining
1772 queue.popleft()
1772 queue.popleft()
1773 # offset == 0 is enabled by block above, so this won't merely
1773 # offset == 0 is enabled by block above, so this won't merely
1774 # copy via ``chunk[0:]``.
1774 # copy via ``chunk[0:]``.
1775 buf.append(chunk[offset:])
1775 buf.append(chunk[offset:])
1776 self._chunkoffset = 0
1776 self._chunkoffset = 0
1777
1777
1778 # Partial chunk needed.
1778 # Partial chunk needed.
1779 else:
1779 else:
1780 buf.append(chunk[offset:offset + left])
1780 buf.append(chunk[offset:offset + left])
1781 self._chunkoffset += left
1781 self._chunkoffset += left
1782 left -= chunkremaining
1782 left -= chunkremaining
1783
1783
1784 return ''.join(buf)
1784 return ''.join(buf)
1785
1785
1786 def filechunkiter(f, size=131072, limit=None):
1786 def filechunkiter(f, size=131072, limit=None):
1787 """Create a generator that produces the data in the file size
1787 """Create a generator that produces the data in the file size
1788 (default 131072) bytes at a time, up to optional limit (default is
1788 (default 131072) bytes at a time, up to optional limit (default is
1789 to read all data). Chunks may be less than size bytes if the
1789 to read all data). Chunks may be less than size bytes if the
1790 chunk is the last chunk in the file, or the file is a socket or
1790 chunk is the last chunk in the file, or the file is a socket or
1791 some other type of file that sometimes reads less data than is
1791 some other type of file that sometimes reads less data than is
1792 requested."""
1792 requested."""
1793 assert size >= 0
1793 assert size >= 0
1794 assert limit is None or limit >= 0
1794 assert limit is None or limit >= 0
1795 while True:
1795 while True:
1796 if limit is None:
1796 if limit is None:
1797 nbytes = size
1797 nbytes = size
1798 else:
1798 else:
1799 nbytes = min(limit, size)
1799 nbytes = min(limit, size)
1800 s = nbytes and f.read(nbytes)
1800 s = nbytes and f.read(nbytes)
1801 if not s:
1801 if not s:
1802 break
1802 break
1803 if limit:
1803 if limit:
1804 limit -= len(s)
1804 limit -= len(s)
1805 yield s
1805 yield s
1806
1806
1807 def makedate(timestamp=None):
1807 def makedate(timestamp=None):
1808 '''Return a unix timestamp (or the current time) as a (unixtime,
1808 '''Return a unix timestamp (or the current time) as a (unixtime,
1809 offset) tuple based off the local timezone.'''
1809 offset) tuple based off the local timezone.'''
1810 if timestamp is None:
1810 if timestamp is None:
1811 timestamp = time.time()
1811 timestamp = time.time()
1812 if timestamp < 0:
1812 if timestamp < 0:
1813 hint = _("check your clock")
1813 hint = _("check your clock")
1814 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1814 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1815 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1815 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1816 datetime.datetime.fromtimestamp(timestamp))
1816 datetime.datetime.fromtimestamp(timestamp))
1817 tz = delta.days * 86400 + delta.seconds
1817 tz = delta.days * 86400 + delta.seconds
1818 return timestamp, tz
1818 return timestamp, tz
1819
1819
1820 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1820 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1821 """represent a (unixtime, offset) tuple as a localized time.
1821 """represent a (unixtime, offset) tuple as a localized time.
1822 unixtime is seconds since the epoch, and offset is the time zone's
1822 unixtime is seconds since the epoch, and offset is the time zone's
1823 number of seconds away from UTC.
1823 number of seconds away from UTC.
1824
1824
1825 >>> datestr((0, 0))
1825 >>> datestr((0, 0))
1826 'Thu Jan 01 00:00:00 1970 +0000'
1826 'Thu Jan 01 00:00:00 1970 +0000'
1827 >>> datestr((42, 0))
1827 >>> datestr((42, 0))
1828 'Thu Jan 01 00:00:42 1970 +0000'
1828 'Thu Jan 01 00:00:42 1970 +0000'
1829 >>> datestr((-42, 0))
1829 >>> datestr((-42, 0))
1830 'Wed Dec 31 23:59:18 1969 +0000'
1830 'Wed Dec 31 23:59:18 1969 +0000'
1831 >>> datestr((0x7fffffff, 0))
1831 >>> datestr((0x7fffffff, 0))
1832 'Tue Jan 19 03:14:07 2038 +0000'
1832 'Tue Jan 19 03:14:07 2038 +0000'
1833 >>> datestr((-0x80000000, 0))
1833 >>> datestr((-0x80000000, 0))
1834 'Fri Dec 13 20:45:52 1901 +0000'
1834 'Fri Dec 13 20:45:52 1901 +0000'
1835 """
1835 """
1836 t, tz = date or makedate()
1836 t, tz = date or makedate()
1837 if "%1" in format or "%2" in format or "%z" in format:
1837 if "%1" in format or "%2" in format or "%z" in format:
1838 sign = (tz > 0) and "-" or "+"
1838 sign = (tz > 0) and "-" or "+"
1839 minutes = abs(tz) // 60
1839 minutes = abs(tz) // 60
1840 q, r = divmod(minutes, 60)
1840 q, r = divmod(minutes, 60)
1841 format = format.replace("%z", "%1%2")
1841 format = format.replace("%z", "%1%2")
1842 format = format.replace("%1", "%c%02d" % (sign, q))
1842 format = format.replace("%1", "%c%02d" % (sign, q))
1843 format = format.replace("%2", "%02d" % r)
1843 format = format.replace("%2", "%02d" % r)
1844 d = t - tz
1844 d = t - tz
1845 if d > 0x7fffffff:
1845 if d > 0x7fffffff:
1846 d = 0x7fffffff
1846 d = 0x7fffffff
1847 elif d < -0x80000000:
1847 elif d < -0x80000000:
1848 d = -0x80000000
1848 d = -0x80000000
1849 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1849 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1850 # because they use the gmtime() system call which is buggy on Windows
1850 # because they use the gmtime() system call which is buggy on Windows
1851 # for negative values.
1851 # for negative values.
1852 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1852 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1853 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1853 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format)))
1854 return s
1854 return s
1855
1855
1856 def shortdate(date=None):
1856 def shortdate(date=None):
1857 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1857 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1858 return datestr(date, format='%Y-%m-%d')
1858 return datestr(date, format='%Y-%m-%d')
1859
1859
1860 def parsetimezone(s):
1860 def parsetimezone(s):
1861 """find a trailing timezone, if any, in string, and return a
1861 """find a trailing timezone, if any, in string, and return a
1862 (offset, remainder) pair"""
1862 (offset, remainder) pair"""
1863
1863
1864 if s.endswith("GMT") or s.endswith("UTC"):
1864 if s.endswith("GMT") or s.endswith("UTC"):
1865 return 0, s[:-3].rstrip()
1865 return 0, s[:-3].rstrip()
1866
1866
1867 # Unix-style timezones [+-]hhmm
1867 # Unix-style timezones [+-]hhmm
1868 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1868 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit():
1869 sign = (s[-5] == "+") and 1 or -1
1869 sign = (s[-5] == "+") and 1 or -1
1870 hours = int(s[-4:-2])
1870 hours = int(s[-4:-2])
1871 minutes = int(s[-2:])
1871 minutes = int(s[-2:])
1872 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1872 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
1873
1873
1874 # ISO8601 trailing Z
1874 # ISO8601 trailing Z
1875 if s.endswith("Z") and s[-2:-1].isdigit():
1875 if s.endswith("Z") and s[-2:-1].isdigit():
1876 return 0, s[:-1]
1876 return 0, s[:-1]
1877
1877
1878 # ISO8601-style [+-]hh:mm
1878 # ISO8601-style [+-]hh:mm
1879 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1879 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and
1880 s[-5:-3].isdigit() and s[-2:].isdigit()):
1880 s[-5:-3].isdigit() and s[-2:].isdigit()):
1881 sign = (s[-6] == "+") and 1 or -1
1881 sign = (s[-6] == "+") and 1 or -1
1882 hours = int(s[-5:-3])
1882 hours = int(s[-5:-3])
1883 minutes = int(s[-2:])
1883 minutes = int(s[-2:])
1884 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1884 return -sign * (hours * 60 + minutes) * 60, s[:-6]
1885
1885
1886 return None, s
1886 return None, s
1887
1887
1888 def strdate(string, format, defaults=None):
1888 def strdate(string, format, defaults=None):
1889 """parse a localized time string and return a (unixtime, offset) tuple.
1889 """parse a localized time string and return a (unixtime, offset) tuple.
1890 if the string cannot be parsed, ValueError is raised."""
1890 if the string cannot be parsed, ValueError is raised."""
1891 if defaults is None:
1891 if defaults is None:
1892 defaults = {}
1892 defaults = {}
1893
1893
1894 # NOTE: unixtime = localunixtime + offset
1894 # NOTE: unixtime = localunixtime + offset
1895 offset, date = parsetimezone(string)
1895 offset, date = parsetimezone(string)
1896
1896
1897 # add missing elements from defaults
1897 # add missing elements from defaults
1898 usenow = False # default to using biased defaults
1898 usenow = False # default to using biased defaults
1899 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1899 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1900 part = pycompat.bytestr(part)
1900 part = pycompat.bytestr(part)
1901 found = [True for p in part if ("%"+p) in format]
1901 found = [True for p in part if ("%"+p) in format]
1902 if not found:
1902 if not found:
1903 date += "@" + defaults[part][usenow]
1903 date += "@" + defaults[part][usenow]
1904 format += "@%" + part[0]
1904 format += "@%" + part[0]
1905 else:
1905 else:
1906 # We've found a specific time element, less specific time
1906 # We've found a specific time element, less specific time
1907 # elements are relative to today
1907 # elements are relative to today
1908 usenow = True
1908 usenow = True
1909
1909
1910 timetuple = time.strptime(encoding.strfromlocal(date),
1910 timetuple = time.strptime(encoding.strfromlocal(date),
1911 encoding.strfromlocal(format))
1911 encoding.strfromlocal(format))
1912 localunixtime = int(calendar.timegm(timetuple))
1912 localunixtime = int(calendar.timegm(timetuple))
1913 if offset is None:
1913 if offset is None:
1914 # local timezone
1914 # local timezone
1915 unixtime = int(time.mktime(timetuple))
1915 unixtime = int(time.mktime(timetuple))
1916 offset = unixtime - localunixtime
1916 offset = unixtime - localunixtime
1917 else:
1917 else:
1918 unixtime = localunixtime + offset
1918 unixtime = localunixtime + offset
1919 return unixtime, offset
1919 return unixtime, offset
1920
1920
1921 def parsedate(date, formats=None, bias=None):
1921 def parsedate(date, formats=None, bias=None):
1922 """parse a localized date/time and return a (unixtime, offset) tuple.
1922 """parse a localized date/time and return a (unixtime, offset) tuple.
1923
1923
1924 The date may be a "unixtime offset" string or in one of the specified
1924 The date may be a "unixtime offset" string or in one of the specified
1925 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1925 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1926
1926
1927 This function calls rawparsedate and convert ValueError to Abort for
1928 functions that needs higher level exception.
1929
1930 >>> parsedate(' today ') == parsedate(\
1927 >>> parsedate(' today ') == parsedate(\
1931 datetime.date.today().strftime('%b %d'))
1928 datetime.date.today().strftime('%b %d'))
1932 True
1929 True
1933 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1930 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1934 datetime.timedelta(days=1)\
1931 datetime.timedelta(days=1)\
1935 ).strftime('%b %d'))
1932 ).strftime('%b %d'))
1936 True
1933 True
1937 >>> now, tz = makedate()
1934 >>> now, tz = makedate()
1938 >>> strnow, strtz = parsedate('now')
1935 >>> strnow, strtz = parsedate('now')
1939 >>> (strnow - now) < 1
1936 >>> (strnow - now) < 1
1940 True
1937 True
1941 >>> tz == strtz
1938 >>> tz == strtz
1942 True
1939 True
1943 """
1940 """
1944 try:
1945 return rawparsedate(date, formats=formats, bias=bias)
1946 except ValueError as exception:
1947 raise Abort(str(exception))
1948
1949 def rawparsedate(date, formats=None, bias=None):
1950 """parse a localized date/time and return a (unixtime, offset) tuple.
1951
1952 The date may be a "unixtime offset" string or in one of the specified
1953 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1954
1955 See docstring of parsedate for example.
1956 Raise ValueError for invalid date value.
1957 """
1958 if bias is None:
1941 if bias is None:
1959 bias = {}
1942 bias = {}
1960 if not date:
1943 if not date:
1961 return 0, 0
1944 return 0, 0
1962 if isinstance(date, tuple) and len(date) == 2:
1945 if isinstance(date, tuple) and len(date) == 2:
1963 return date
1946 return date
1964 if not formats:
1947 if not formats:
1965 formats = defaultdateformats
1948 formats = defaultdateformats
1966 date = date.strip()
1949 date = date.strip()
1967
1950
1968 if date == 'now' or date == _('now'):
1951 if date == 'now' or date == _('now'):
1969 return makedate()
1952 return makedate()
1970 if date == 'today' or date == _('today'):
1953 if date == 'today' or date == _('today'):
1971 date = datetime.date.today().strftime('%b %d')
1954 date = datetime.date.today().strftime('%b %d')
1972 elif date == 'yesterday' or date == _('yesterday'):
1955 elif date == 'yesterday' or date == _('yesterday'):
1973 date = (datetime.date.today() -
1956 date = (datetime.date.today() -
1974 datetime.timedelta(days=1)).strftime('%b %d')
1957 datetime.timedelta(days=1)).strftime('%b %d')
1975
1958
1976 try:
1959 try:
1977 when, offset = map(int, date.split(' '))
1960 when, offset = map(int, date.split(' '))
1978 except ValueError:
1961 except ValueError:
1979 # fill out defaults
1962 # fill out defaults
1980 now = makedate()
1963 now = makedate()
1981 defaults = {}
1964 defaults = {}
1982 for part in ("d", "mb", "yY", "HI", "M", "S"):
1965 for part in ("d", "mb", "yY", "HI", "M", "S"):
1983 # this piece is for rounding the specific end of unknowns
1966 # this piece is for rounding the specific end of unknowns
1984 b = bias.get(part)
1967 b = bias.get(part)
1985 if b is None:
1968 if b is None:
1986 if part[0:1] in "HMS":
1969 if part[0:1] in "HMS":
1987 b = "00"
1970 b = "00"
1988 else:
1971 else:
1989 b = "0"
1972 b = "0"
1990
1973
1991 # this piece is for matching the generic end to today's date
1974 # this piece is for matching the generic end to today's date
1992 n = datestr(now, "%" + part[0:1])
1975 n = datestr(now, "%" + part[0:1])
1993
1976
1994 defaults[part] = (b, n)
1977 defaults[part] = (b, n)
1995
1978
1996 for format in formats:
1979 for format in formats:
1997 try:
1980 try:
1998 when, offset = strdate(date, format, defaults)
1981 when, offset = strdate(date, format, defaults)
1999 except (ValueError, OverflowError):
1982 except (ValueError, OverflowError):
2000 pass
1983 pass
2001 else:
1984 else:
2002 break
1985 break
2003 else:
1986 else:
2004 raise ValueError(_('invalid date: %r') % date)
1987 raise error.ParseError(_('invalid date: %r') % date)
2005 # validate explicit (probably user-specified) date and
1988 # validate explicit (probably user-specified) date and
2006 # time zone offset. values must fit in signed 32 bits for
1989 # time zone offset. values must fit in signed 32 bits for
2007 # current 32-bit linux runtimes. timezones go from UTC-12
1990 # current 32-bit linux runtimes. timezones go from UTC-12
2008 # to UTC+14
1991 # to UTC+14
2009 if when < -0x80000000 or when > 0x7fffffff:
1992 if when < -0x80000000 or when > 0x7fffffff:
2010 raise ValueError(_('date exceeds 32 bits: %d') % when)
1993 raise error.ParseError(_('date exceeds 32 bits: %d') % when)
2011 if offset < -50400 or offset > 43200:
1994 if offset < -50400 or offset > 43200:
2012 raise ValueError(_('impossible time zone offset: %d') % offset)
1995 raise error.ParseError(_('impossible time zone offset: %d') % offset)
2013 return when, offset
1996 return when, offset
2014
1997
2015 def matchdate(date):
1998 def matchdate(date):
2016 """Return a function that matches a given date match specifier
1999 """Return a function that matches a given date match specifier
2017
2000
2018 Formats include:
2001 Formats include:
2019
2002
2020 '{date}' match a given date to the accuracy provided
2003 '{date}' match a given date to the accuracy provided
2021
2004
2022 '<{date}' on or before a given date
2005 '<{date}' on or before a given date
2023
2006
2024 '>{date}' on or after a given date
2007 '>{date}' on or after a given date
2025
2008
2026 >>> p1 = parsedate("10:29:59")
2009 >>> p1 = parsedate("10:29:59")
2027 >>> p2 = parsedate("10:30:00")
2010 >>> p2 = parsedate("10:30:00")
2028 >>> p3 = parsedate("10:30:59")
2011 >>> p3 = parsedate("10:30:59")
2029 >>> p4 = parsedate("10:31:00")
2012 >>> p4 = parsedate("10:31:00")
2030 >>> p5 = parsedate("Sep 15 10:30:00 1999")
2013 >>> p5 = parsedate("Sep 15 10:30:00 1999")
2031 >>> f = matchdate("10:30")
2014 >>> f = matchdate("10:30")
2032 >>> f(p1[0])
2015 >>> f(p1[0])
2033 False
2016 False
2034 >>> f(p2[0])
2017 >>> f(p2[0])
2035 True
2018 True
2036 >>> f(p3[0])
2019 >>> f(p3[0])
2037 True
2020 True
2038 >>> f(p4[0])
2021 >>> f(p4[0])
2039 False
2022 False
2040 >>> f(p5[0])
2023 >>> f(p5[0])
2041 False
2024 False
2042 """
2025 """
2043
2026
2044 def lower(date):
2027 def lower(date):
2045 d = {'mb': "1", 'd': "1"}
2028 d = {'mb': "1", 'd': "1"}
2046 return parsedate(date, extendeddateformats, d)[0]
2029 return parsedate(date, extendeddateformats, d)[0]
2047
2030
2048 def upper(date):
2031 def upper(date):
2049 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2032 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
2050 for days in ("31", "30", "29"):
2033 for days in ("31", "30", "29"):
2051 try:
2034 try:
2052 d["d"] = days
2035 d["d"] = days
2053 return parsedate(date, extendeddateformats, d)[0]
2036 return parsedate(date, extendeddateformats, d)[0]
2054 except Abort:
2037 except Abort:
2055 pass
2038 pass
2056 d["d"] = "28"
2039 d["d"] = "28"
2057 return parsedate(date, extendeddateformats, d)[0]
2040 return parsedate(date, extendeddateformats, d)[0]
2058
2041
2059 date = date.strip()
2042 date = date.strip()
2060
2043
2061 if not date:
2044 if not date:
2062 raise Abort(_("dates cannot consist entirely of whitespace"))
2045 raise Abort(_("dates cannot consist entirely of whitespace"))
2063 elif date[0] == "<":
2046 elif date[0] == "<":
2064 if not date[1:]:
2047 if not date[1:]:
2065 raise Abort(_("invalid day spec, use '<DATE'"))
2048 raise Abort(_("invalid day spec, use '<DATE'"))
2066 when = upper(date[1:])
2049 when = upper(date[1:])
2067 return lambda x: x <= when
2050 return lambda x: x <= when
2068 elif date[0] == ">":
2051 elif date[0] == ">":
2069 if not date[1:]:
2052 if not date[1:]:
2070 raise Abort(_("invalid day spec, use '>DATE'"))
2053 raise Abort(_("invalid day spec, use '>DATE'"))
2071 when = lower(date[1:])
2054 when = lower(date[1:])
2072 return lambda x: x >= when
2055 return lambda x: x >= when
2073 elif date[0] == "-":
2056 elif date[0] == "-":
2074 try:
2057 try:
2075 days = int(date[1:])
2058 days = int(date[1:])
2076 except ValueError:
2059 except ValueError:
2077 raise Abort(_("invalid day spec: %s") % date[1:])
2060 raise Abort(_("invalid day spec: %s") % date[1:])
2078 if days < 0:
2061 if days < 0:
2079 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2062 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
2080 % date[1:])
2063 % date[1:])
2081 when = makedate()[0] - days * 3600 * 24
2064 when = makedate()[0] - days * 3600 * 24
2082 return lambda x: x >= when
2065 return lambda x: x >= when
2083 elif " to " in date:
2066 elif " to " in date:
2084 a, b = date.split(" to ")
2067 a, b = date.split(" to ")
2085 start, stop = lower(a), upper(b)
2068 start, stop = lower(a), upper(b)
2086 return lambda x: x >= start and x <= stop
2069 return lambda x: x >= start and x <= stop
2087 else:
2070 else:
2088 start, stop = lower(date), upper(date)
2071 start, stop = lower(date), upper(date)
2089 return lambda x: x >= start and x <= stop
2072 return lambda x: x >= start and x <= stop
2090
2073
2091 def stringmatcher(pattern, casesensitive=True):
2074 def stringmatcher(pattern, casesensitive=True):
2092 """
2075 """
2093 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2076 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2094 returns the matcher name, pattern, and matcher function.
2077 returns the matcher name, pattern, and matcher function.
2095 missing or unknown prefixes are treated as literal matches.
2078 missing or unknown prefixes are treated as literal matches.
2096
2079
2097 helper for tests:
2080 helper for tests:
2098 >>> def test(pattern, *tests):
2081 >>> def test(pattern, *tests):
2099 ... kind, pattern, matcher = stringmatcher(pattern)
2082 ... kind, pattern, matcher = stringmatcher(pattern)
2100 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2083 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2101 >>> def itest(pattern, *tests):
2084 >>> def itest(pattern, *tests):
2102 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2085 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2103 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2086 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2104
2087
2105 exact matching (no prefix):
2088 exact matching (no prefix):
2106 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2089 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2107 ('literal', 'abcdefg', [False, False, True])
2090 ('literal', 'abcdefg', [False, False, True])
2108
2091
2109 regex matching ('re:' prefix)
2092 regex matching ('re:' prefix)
2110 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2093 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2111 ('re', 'a.+b', [False, False, True])
2094 ('re', 'a.+b', [False, False, True])
2112
2095
2113 force exact matches ('literal:' prefix)
2096 force exact matches ('literal:' prefix)
2114 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2097 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2115 ('literal', 're:foobar', [False, True])
2098 ('literal', 're:foobar', [False, True])
2116
2099
2117 unknown prefixes are ignored and treated as literals
2100 unknown prefixes are ignored and treated as literals
2118 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2101 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2119 ('literal', 'foo:bar', [False, False, True])
2102 ('literal', 'foo:bar', [False, False, True])
2120
2103
2121 case insensitive regex matches
2104 case insensitive regex matches
2122 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2105 >>> itest('re:A.+b', 'nomatch', 'fooadef', 'fooadefBar')
2123 ('re', 'A.+b', [False, False, True])
2106 ('re', 'A.+b', [False, False, True])
2124
2107
2125 case insensitive literal matches
2108 case insensitive literal matches
2126 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2109 >>> itest('ABCDEFG', 'abc', 'def', 'abcdefg')
2127 ('literal', 'ABCDEFG', [False, False, True])
2110 ('literal', 'ABCDEFG', [False, False, True])
2128 """
2111 """
2129 if pattern.startswith('re:'):
2112 if pattern.startswith('re:'):
2130 pattern = pattern[3:]
2113 pattern = pattern[3:]
2131 try:
2114 try:
2132 flags = 0
2115 flags = 0
2133 if not casesensitive:
2116 if not casesensitive:
2134 flags = remod.I
2117 flags = remod.I
2135 regex = remod.compile(pattern, flags)
2118 regex = remod.compile(pattern, flags)
2136 except remod.error as e:
2119 except remod.error as e:
2137 raise error.ParseError(_('invalid regular expression: %s')
2120 raise error.ParseError(_('invalid regular expression: %s')
2138 % e)
2121 % e)
2139 return 're', pattern, regex.search
2122 return 're', pattern, regex.search
2140 elif pattern.startswith('literal:'):
2123 elif pattern.startswith('literal:'):
2141 pattern = pattern[8:]
2124 pattern = pattern[8:]
2142
2125
2143 match = pattern.__eq__
2126 match = pattern.__eq__
2144
2127
2145 if not casesensitive:
2128 if not casesensitive:
2146 ipat = encoding.lower(pattern)
2129 ipat = encoding.lower(pattern)
2147 match = lambda s: ipat == encoding.lower(s)
2130 match = lambda s: ipat == encoding.lower(s)
2148 return 'literal', pattern, match
2131 return 'literal', pattern, match
2149
2132
2150 def shortuser(user):
2133 def shortuser(user):
2151 """Return a short representation of a user name or email address."""
2134 """Return a short representation of a user name or email address."""
2152 f = user.find('@')
2135 f = user.find('@')
2153 if f >= 0:
2136 if f >= 0:
2154 user = user[:f]
2137 user = user[:f]
2155 f = user.find('<')
2138 f = user.find('<')
2156 if f >= 0:
2139 if f >= 0:
2157 user = user[f + 1:]
2140 user = user[f + 1:]
2158 f = user.find(' ')
2141 f = user.find(' ')
2159 if f >= 0:
2142 if f >= 0:
2160 user = user[:f]
2143 user = user[:f]
2161 f = user.find('.')
2144 f = user.find('.')
2162 if f >= 0:
2145 if f >= 0:
2163 user = user[:f]
2146 user = user[:f]
2164 return user
2147 return user
2165
2148
2166 def emailuser(user):
2149 def emailuser(user):
2167 """Return the user portion of an email address."""
2150 """Return the user portion of an email address."""
2168 f = user.find('@')
2151 f = user.find('@')
2169 if f >= 0:
2152 if f >= 0:
2170 user = user[:f]
2153 user = user[:f]
2171 f = user.find('<')
2154 f = user.find('<')
2172 if f >= 0:
2155 if f >= 0:
2173 user = user[f + 1:]
2156 user = user[f + 1:]
2174 return user
2157 return user
2175
2158
2176 def email(author):
2159 def email(author):
2177 '''get email of author.'''
2160 '''get email of author.'''
2178 r = author.find('>')
2161 r = author.find('>')
2179 if r == -1:
2162 if r == -1:
2180 r = None
2163 r = None
2181 return author[author.find('<') + 1:r]
2164 return author[author.find('<') + 1:r]
2182
2165
2183 def ellipsis(text, maxlength=400):
2166 def ellipsis(text, maxlength=400):
2184 """Trim string to at most maxlength (default: 400) columns in display."""
2167 """Trim string to at most maxlength (default: 400) columns in display."""
2185 return encoding.trim(text, maxlength, ellipsis='...')
2168 return encoding.trim(text, maxlength, ellipsis='...')
2186
2169
2187 def unitcountfn(*unittable):
2170 def unitcountfn(*unittable):
2188 '''return a function that renders a readable count of some quantity'''
2171 '''return a function that renders a readable count of some quantity'''
2189
2172
2190 def go(count):
2173 def go(count):
2191 for multiplier, divisor, format in unittable:
2174 for multiplier, divisor, format in unittable:
2192 if abs(count) >= divisor * multiplier:
2175 if abs(count) >= divisor * multiplier:
2193 return format % (count / float(divisor))
2176 return format % (count / float(divisor))
2194 return unittable[-1][2] % count
2177 return unittable[-1][2] % count
2195
2178
2196 return go
2179 return go
2197
2180
2198 def processlinerange(fromline, toline):
2181 def processlinerange(fromline, toline):
2199 """Check that linerange <fromline>:<toline> makes sense and return a
2182 """Check that linerange <fromline>:<toline> makes sense and return a
2200 0-based range.
2183 0-based range.
2201
2184
2202 >>> processlinerange(10, 20)
2185 >>> processlinerange(10, 20)
2203 (9, 20)
2186 (9, 20)
2204 >>> processlinerange(2, 1)
2187 >>> processlinerange(2, 1)
2205 Traceback (most recent call last):
2188 Traceback (most recent call last):
2206 ...
2189 ...
2207 ParseError: line range must be positive
2190 ParseError: line range must be positive
2208 >>> processlinerange(0, 5)
2191 >>> processlinerange(0, 5)
2209 Traceback (most recent call last):
2192 Traceback (most recent call last):
2210 ...
2193 ...
2211 ParseError: fromline must be strictly positive
2194 ParseError: fromline must be strictly positive
2212 """
2195 """
2213 if toline - fromline < 0:
2196 if toline - fromline < 0:
2214 raise error.ParseError(_("line range must be positive"))
2197 raise error.ParseError(_("line range must be positive"))
2215 if fromline < 1:
2198 if fromline < 1:
2216 raise error.ParseError(_("fromline must be strictly positive"))
2199 raise error.ParseError(_("fromline must be strictly positive"))
2217 return fromline - 1, toline
2200 return fromline - 1, toline
2218
2201
2219 bytecount = unitcountfn(
2202 bytecount = unitcountfn(
2220 (100, 1 << 30, _('%.0f GB')),
2203 (100, 1 << 30, _('%.0f GB')),
2221 (10, 1 << 30, _('%.1f GB')),
2204 (10, 1 << 30, _('%.1f GB')),
2222 (1, 1 << 30, _('%.2f GB')),
2205 (1, 1 << 30, _('%.2f GB')),
2223 (100, 1 << 20, _('%.0f MB')),
2206 (100, 1 << 20, _('%.0f MB')),
2224 (10, 1 << 20, _('%.1f MB')),
2207 (10, 1 << 20, _('%.1f MB')),
2225 (1, 1 << 20, _('%.2f MB')),
2208 (1, 1 << 20, _('%.2f MB')),
2226 (100, 1 << 10, _('%.0f KB')),
2209 (100, 1 << 10, _('%.0f KB')),
2227 (10, 1 << 10, _('%.1f KB')),
2210 (10, 1 << 10, _('%.1f KB')),
2228 (1, 1 << 10, _('%.2f KB')),
2211 (1, 1 << 10, _('%.2f KB')),
2229 (1, 1, _('%.0f bytes')),
2212 (1, 1, _('%.0f bytes')),
2230 )
2213 )
2231
2214
2232 # Matches a single EOL which can either be a CRLF where repeated CR
2215 # Matches a single EOL which can either be a CRLF where repeated CR
2233 # are removed or a LF. We do not care about old Macintosh files, so a
2216 # are removed or a LF. We do not care about old Macintosh files, so a
2234 # stray CR is an error.
2217 # stray CR is an error.
2235 _eolre = remod.compile(br'\r*\n')
2218 _eolre = remod.compile(br'\r*\n')
2236
2219
2237 def tolf(s):
2220 def tolf(s):
2238 return _eolre.sub('\n', s)
2221 return _eolre.sub('\n', s)
2239
2222
2240 def tocrlf(s):
2223 def tocrlf(s):
2241 return _eolre.sub('\r\n', s)
2224 return _eolre.sub('\r\n', s)
2242
2225
2243 if pycompat.oslinesep == '\r\n':
2226 if pycompat.oslinesep == '\r\n':
2244 tonativeeol = tocrlf
2227 tonativeeol = tocrlf
2245 fromnativeeol = tolf
2228 fromnativeeol = tolf
2246 else:
2229 else:
2247 tonativeeol = pycompat.identity
2230 tonativeeol = pycompat.identity
2248 fromnativeeol = pycompat.identity
2231 fromnativeeol = pycompat.identity
2249
2232
2250 def escapestr(s):
2233 def escapestr(s):
2251 # call underlying function of s.encode('string_escape') directly for
2234 # call underlying function of s.encode('string_escape') directly for
2252 # Python 3 compatibility
2235 # Python 3 compatibility
2253 return codecs.escape_encode(s)[0]
2236 return codecs.escape_encode(s)[0]
2254
2237
2255 def unescapestr(s):
2238 def unescapestr(s):
2256 return codecs.escape_decode(s)[0]
2239 return codecs.escape_decode(s)[0]
2257
2240
2258 def uirepr(s):
2241 def uirepr(s):
2259 # Avoid double backslash in Windows path repr()
2242 # Avoid double backslash in Windows path repr()
2260 return repr(s).replace('\\\\', '\\')
2243 return repr(s).replace('\\\\', '\\')
2261
2244
2262 # delay import of textwrap
2245 # delay import of textwrap
2263 def MBTextWrapper(**kwargs):
2246 def MBTextWrapper(**kwargs):
2264 class tw(textwrap.TextWrapper):
2247 class tw(textwrap.TextWrapper):
2265 """
2248 """
2266 Extend TextWrapper for width-awareness.
2249 Extend TextWrapper for width-awareness.
2267
2250
2268 Neither number of 'bytes' in any encoding nor 'characters' is
2251 Neither number of 'bytes' in any encoding nor 'characters' is
2269 appropriate to calculate terminal columns for specified string.
2252 appropriate to calculate terminal columns for specified string.
2270
2253
2271 Original TextWrapper implementation uses built-in 'len()' directly,
2254 Original TextWrapper implementation uses built-in 'len()' directly,
2272 so overriding is needed to use width information of each characters.
2255 so overriding is needed to use width information of each characters.
2273
2256
2274 In addition, characters classified into 'ambiguous' width are
2257 In addition, characters classified into 'ambiguous' width are
2275 treated as wide in East Asian area, but as narrow in other.
2258 treated as wide in East Asian area, but as narrow in other.
2276
2259
2277 This requires use decision to determine width of such characters.
2260 This requires use decision to determine width of such characters.
2278 """
2261 """
2279 def _cutdown(self, ucstr, space_left):
2262 def _cutdown(self, ucstr, space_left):
2280 l = 0
2263 l = 0
2281 colwidth = encoding.ucolwidth
2264 colwidth = encoding.ucolwidth
2282 for i in xrange(len(ucstr)):
2265 for i in xrange(len(ucstr)):
2283 l += colwidth(ucstr[i])
2266 l += colwidth(ucstr[i])
2284 if space_left < l:
2267 if space_left < l:
2285 return (ucstr[:i], ucstr[i:])
2268 return (ucstr[:i], ucstr[i:])
2286 return ucstr, ''
2269 return ucstr, ''
2287
2270
2288 # overriding of base class
2271 # overriding of base class
2289 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2272 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2290 space_left = max(width - cur_len, 1)
2273 space_left = max(width - cur_len, 1)
2291
2274
2292 if self.break_long_words:
2275 if self.break_long_words:
2293 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2276 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2294 cur_line.append(cut)
2277 cur_line.append(cut)
2295 reversed_chunks[-1] = res
2278 reversed_chunks[-1] = res
2296 elif not cur_line:
2279 elif not cur_line:
2297 cur_line.append(reversed_chunks.pop())
2280 cur_line.append(reversed_chunks.pop())
2298
2281
2299 # this overriding code is imported from TextWrapper of Python 2.6
2282 # this overriding code is imported from TextWrapper of Python 2.6
2300 # to calculate columns of string by 'encoding.ucolwidth()'
2283 # to calculate columns of string by 'encoding.ucolwidth()'
2301 def _wrap_chunks(self, chunks):
2284 def _wrap_chunks(self, chunks):
2302 colwidth = encoding.ucolwidth
2285 colwidth = encoding.ucolwidth
2303
2286
2304 lines = []
2287 lines = []
2305 if self.width <= 0:
2288 if self.width <= 0:
2306 raise ValueError("invalid width %r (must be > 0)" % self.width)
2289 raise ValueError("invalid width %r (must be > 0)" % self.width)
2307
2290
2308 # Arrange in reverse order so items can be efficiently popped
2291 # Arrange in reverse order so items can be efficiently popped
2309 # from a stack of chucks.
2292 # from a stack of chucks.
2310 chunks.reverse()
2293 chunks.reverse()
2311
2294
2312 while chunks:
2295 while chunks:
2313
2296
2314 # Start the list of chunks that will make up the current line.
2297 # Start the list of chunks that will make up the current line.
2315 # cur_len is just the length of all the chunks in cur_line.
2298 # cur_len is just the length of all the chunks in cur_line.
2316 cur_line = []
2299 cur_line = []
2317 cur_len = 0
2300 cur_len = 0
2318
2301
2319 # Figure out which static string will prefix this line.
2302 # Figure out which static string will prefix this line.
2320 if lines:
2303 if lines:
2321 indent = self.subsequent_indent
2304 indent = self.subsequent_indent
2322 else:
2305 else:
2323 indent = self.initial_indent
2306 indent = self.initial_indent
2324
2307
2325 # Maximum width for this line.
2308 # Maximum width for this line.
2326 width = self.width - len(indent)
2309 width = self.width - len(indent)
2327
2310
2328 # First chunk on line is whitespace -- drop it, unless this
2311 # First chunk on line is whitespace -- drop it, unless this
2329 # is the very beginning of the text (i.e. no lines started yet).
2312 # is the very beginning of the text (i.e. no lines started yet).
2330 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2313 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2331 del chunks[-1]
2314 del chunks[-1]
2332
2315
2333 while chunks:
2316 while chunks:
2334 l = colwidth(chunks[-1])
2317 l = colwidth(chunks[-1])
2335
2318
2336 # Can at least squeeze this chunk onto the current line.
2319 # Can at least squeeze this chunk onto the current line.
2337 if cur_len + l <= width:
2320 if cur_len + l <= width:
2338 cur_line.append(chunks.pop())
2321 cur_line.append(chunks.pop())
2339 cur_len += l
2322 cur_len += l
2340
2323
2341 # Nope, this line is full.
2324 # Nope, this line is full.
2342 else:
2325 else:
2343 break
2326 break
2344
2327
2345 # The current line is full, and the next chunk is too big to
2328 # The current line is full, and the next chunk is too big to
2346 # fit on *any* line (not just this one).
2329 # fit on *any* line (not just this one).
2347 if chunks and colwidth(chunks[-1]) > width:
2330 if chunks and colwidth(chunks[-1]) > width:
2348 self._handle_long_word(chunks, cur_line, cur_len, width)
2331 self._handle_long_word(chunks, cur_line, cur_len, width)
2349
2332
2350 # If the last chunk on this line is all whitespace, drop it.
2333 # If the last chunk on this line is all whitespace, drop it.
2351 if (self.drop_whitespace and
2334 if (self.drop_whitespace and
2352 cur_line and cur_line[-1].strip() == ''):
2335 cur_line and cur_line[-1].strip() == ''):
2353 del cur_line[-1]
2336 del cur_line[-1]
2354
2337
2355 # Convert current line back to a string and store it in list
2338 # Convert current line back to a string and store it in list
2356 # of all lines (return value).
2339 # of all lines (return value).
2357 if cur_line:
2340 if cur_line:
2358 lines.append(indent + ''.join(cur_line))
2341 lines.append(indent + ''.join(cur_line))
2359
2342
2360 return lines
2343 return lines
2361
2344
2362 global MBTextWrapper
2345 global MBTextWrapper
2363 MBTextWrapper = tw
2346 MBTextWrapper = tw
2364 return tw(**kwargs)
2347 return tw(**kwargs)
2365
2348
2366 def wrap(line, width, initindent='', hangindent=''):
2349 def wrap(line, width, initindent='', hangindent=''):
2367 maxindent = max(len(hangindent), len(initindent))
2350 maxindent = max(len(hangindent), len(initindent))
2368 if width <= maxindent:
2351 if width <= maxindent:
2369 # adjust for weird terminal size
2352 # adjust for weird terminal size
2370 width = max(78, maxindent + 1)
2353 width = max(78, maxindent + 1)
2371 line = line.decode(pycompat.sysstr(encoding.encoding),
2354 line = line.decode(pycompat.sysstr(encoding.encoding),
2372 pycompat.sysstr(encoding.encodingmode))
2355 pycompat.sysstr(encoding.encodingmode))
2373 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2356 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2374 pycompat.sysstr(encoding.encodingmode))
2357 pycompat.sysstr(encoding.encodingmode))
2375 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2358 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2376 pycompat.sysstr(encoding.encodingmode))
2359 pycompat.sysstr(encoding.encodingmode))
2377 wrapper = MBTextWrapper(width=width,
2360 wrapper = MBTextWrapper(width=width,
2378 initial_indent=initindent,
2361 initial_indent=initindent,
2379 subsequent_indent=hangindent)
2362 subsequent_indent=hangindent)
2380 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2363 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2381
2364
2382 if (pyplatform.python_implementation() == 'CPython' and
2365 if (pyplatform.python_implementation() == 'CPython' and
2383 sys.version_info < (3, 0)):
2366 sys.version_info < (3, 0)):
2384 # There is an issue in CPython that some IO methods do not handle EINTR
2367 # There is an issue in CPython that some IO methods do not handle EINTR
2385 # correctly. The following table shows what CPython version (and functions)
2368 # correctly. The following table shows what CPython version (and functions)
2386 # are affected (buggy: has the EINTR bug, okay: otherwise):
2369 # are affected (buggy: has the EINTR bug, okay: otherwise):
2387 #
2370 #
2388 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2371 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2389 # --------------------------------------------------
2372 # --------------------------------------------------
2390 # fp.__iter__ | buggy | buggy | okay
2373 # fp.__iter__ | buggy | buggy | okay
2391 # fp.read* | buggy | okay [1] | okay
2374 # fp.read* | buggy | okay [1] | okay
2392 #
2375 #
2393 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2376 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2394 #
2377 #
2395 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2378 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2396 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2379 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2397 #
2380 #
2398 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2381 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2399 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2382 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2400 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2383 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2401 # fp.__iter__ but not other fp.read* methods.
2384 # fp.__iter__ but not other fp.read* methods.
2402 #
2385 #
2403 # On modern systems like Linux, the "read" syscall cannot be interrupted
2386 # On modern systems like Linux, the "read" syscall cannot be interrupted
2404 # when reading "fast" files like on-disk files. So the EINTR issue only
2387 # when reading "fast" files like on-disk files. So the EINTR issue only
2405 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2388 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2406 # files approximately as "fast" files and use the fast (unsafe) code path,
2389 # files approximately as "fast" files and use the fast (unsafe) code path,
2407 # to minimize the performance impact.
2390 # to minimize the performance impact.
2408 if sys.version_info >= (2, 7, 4):
2391 if sys.version_info >= (2, 7, 4):
2409 # fp.readline deals with EINTR correctly, use it as a workaround.
2392 # fp.readline deals with EINTR correctly, use it as a workaround.
2410 def _safeiterfile(fp):
2393 def _safeiterfile(fp):
2411 return iter(fp.readline, '')
2394 return iter(fp.readline, '')
2412 else:
2395 else:
2413 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2396 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2414 # note: this may block longer than necessary because of bufsize.
2397 # note: this may block longer than necessary because of bufsize.
2415 def _safeiterfile(fp, bufsize=4096):
2398 def _safeiterfile(fp, bufsize=4096):
2416 fd = fp.fileno()
2399 fd = fp.fileno()
2417 line = ''
2400 line = ''
2418 while True:
2401 while True:
2419 try:
2402 try:
2420 buf = os.read(fd, bufsize)
2403 buf = os.read(fd, bufsize)
2421 except OSError as ex:
2404 except OSError as ex:
2422 # os.read only raises EINTR before any data is read
2405 # os.read only raises EINTR before any data is read
2423 if ex.errno == errno.EINTR:
2406 if ex.errno == errno.EINTR:
2424 continue
2407 continue
2425 else:
2408 else:
2426 raise
2409 raise
2427 line += buf
2410 line += buf
2428 if '\n' in buf:
2411 if '\n' in buf:
2429 splitted = line.splitlines(True)
2412 splitted = line.splitlines(True)
2430 line = ''
2413 line = ''
2431 for l in splitted:
2414 for l in splitted:
2432 if l[-1] == '\n':
2415 if l[-1] == '\n':
2433 yield l
2416 yield l
2434 else:
2417 else:
2435 line = l
2418 line = l
2436 if not buf:
2419 if not buf:
2437 break
2420 break
2438 if line:
2421 if line:
2439 yield line
2422 yield line
2440
2423
2441 def iterfile(fp):
2424 def iterfile(fp):
2442 fastpath = True
2425 fastpath = True
2443 if type(fp) is file:
2426 if type(fp) is file:
2444 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2427 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2445 if fastpath:
2428 if fastpath:
2446 return fp
2429 return fp
2447 else:
2430 else:
2448 return _safeiterfile(fp)
2431 return _safeiterfile(fp)
2449 else:
2432 else:
2450 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2433 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2451 def iterfile(fp):
2434 def iterfile(fp):
2452 return fp
2435 return fp
2453
2436
2454 def iterlines(iterator):
2437 def iterlines(iterator):
2455 for chunk in iterator:
2438 for chunk in iterator:
2456 for line in chunk.splitlines():
2439 for line in chunk.splitlines():
2457 yield line
2440 yield line
2458
2441
2459 def expandpath(path):
2442 def expandpath(path):
2460 return os.path.expanduser(os.path.expandvars(path))
2443 return os.path.expanduser(os.path.expandvars(path))
2461
2444
2462 def hgcmd():
2445 def hgcmd():
2463 """Return the command used to execute current hg
2446 """Return the command used to execute current hg
2464
2447
2465 This is different from hgexecutable() because on Windows we want
2448 This is different from hgexecutable() because on Windows we want
2466 to avoid things opening new shell windows like batch files, so we
2449 to avoid things opening new shell windows like batch files, so we
2467 get either the python call or current executable.
2450 get either the python call or current executable.
2468 """
2451 """
2469 if mainfrozen():
2452 if mainfrozen():
2470 if getattr(sys, 'frozen', None) == 'macosx_app':
2453 if getattr(sys, 'frozen', None) == 'macosx_app':
2471 # Env variable set by py2app
2454 # Env variable set by py2app
2472 return [encoding.environ['EXECUTABLEPATH']]
2455 return [encoding.environ['EXECUTABLEPATH']]
2473 else:
2456 else:
2474 return [pycompat.sysexecutable]
2457 return [pycompat.sysexecutable]
2475 return gethgcmd()
2458 return gethgcmd()
2476
2459
2477 def rundetached(args, condfn):
2460 def rundetached(args, condfn):
2478 """Execute the argument list in a detached process.
2461 """Execute the argument list in a detached process.
2479
2462
2480 condfn is a callable which is called repeatedly and should return
2463 condfn is a callable which is called repeatedly and should return
2481 True once the child process is known to have started successfully.
2464 True once the child process is known to have started successfully.
2482 At this point, the child process PID is returned. If the child
2465 At this point, the child process PID is returned. If the child
2483 process fails to start or finishes before condfn() evaluates to
2466 process fails to start or finishes before condfn() evaluates to
2484 True, return -1.
2467 True, return -1.
2485 """
2468 """
2486 # Windows case is easier because the child process is either
2469 # Windows case is easier because the child process is either
2487 # successfully starting and validating the condition or exiting
2470 # successfully starting and validating the condition or exiting
2488 # on failure. We just poll on its PID. On Unix, if the child
2471 # on failure. We just poll on its PID. On Unix, if the child
2489 # process fails to start, it will be left in a zombie state until
2472 # process fails to start, it will be left in a zombie state until
2490 # the parent wait on it, which we cannot do since we expect a long
2473 # the parent wait on it, which we cannot do since we expect a long
2491 # running process on success. Instead we listen for SIGCHLD telling
2474 # running process on success. Instead we listen for SIGCHLD telling
2492 # us our child process terminated.
2475 # us our child process terminated.
2493 terminated = set()
2476 terminated = set()
2494 def handler(signum, frame):
2477 def handler(signum, frame):
2495 terminated.add(os.wait())
2478 terminated.add(os.wait())
2496 prevhandler = None
2479 prevhandler = None
2497 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2480 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2498 if SIGCHLD is not None:
2481 if SIGCHLD is not None:
2499 prevhandler = signal.signal(SIGCHLD, handler)
2482 prevhandler = signal.signal(SIGCHLD, handler)
2500 try:
2483 try:
2501 pid = spawndetached(args)
2484 pid = spawndetached(args)
2502 while not condfn():
2485 while not condfn():
2503 if ((pid in terminated or not testpid(pid))
2486 if ((pid in terminated or not testpid(pid))
2504 and not condfn()):
2487 and not condfn()):
2505 return -1
2488 return -1
2506 time.sleep(0.1)
2489 time.sleep(0.1)
2507 return pid
2490 return pid
2508 finally:
2491 finally:
2509 if prevhandler is not None:
2492 if prevhandler is not None:
2510 signal.signal(signal.SIGCHLD, prevhandler)
2493 signal.signal(signal.SIGCHLD, prevhandler)
2511
2494
2512 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2495 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2513 """Return the result of interpolating items in the mapping into string s.
2496 """Return the result of interpolating items in the mapping into string s.
2514
2497
2515 prefix is a single character string, or a two character string with
2498 prefix is a single character string, or a two character string with
2516 a backslash as the first character if the prefix needs to be escaped in
2499 a backslash as the first character if the prefix needs to be escaped in
2517 a regular expression.
2500 a regular expression.
2518
2501
2519 fn is an optional function that will be applied to the replacement text
2502 fn is an optional function that will be applied to the replacement text
2520 just before replacement.
2503 just before replacement.
2521
2504
2522 escape_prefix is an optional flag that allows using doubled prefix for
2505 escape_prefix is an optional flag that allows using doubled prefix for
2523 its escaping.
2506 its escaping.
2524 """
2507 """
2525 fn = fn or (lambda s: s)
2508 fn = fn or (lambda s: s)
2526 patterns = '|'.join(mapping.keys())
2509 patterns = '|'.join(mapping.keys())
2527 if escape_prefix:
2510 if escape_prefix:
2528 patterns += '|' + prefix
2511 patterns += '|' + prefix
2529 if len(prefix) > 1:
2512 if len(prefix) > 1:
2530 prefix_char = prefix[1:]
2513 prefix_char = prefix[1:]
2531 else:
2514 else:
2532 prefix_char = prefix
2515 prefix_char = prefix
2533 mapping[prefix_char] = prefix_char
2516 mapping[prefix_char] = prefix_char
2534 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2517 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2535 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2518 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2536
2519
2537 def getport(port):
2520 def getport(port):
2538 """Return the port for a given network service.
2521 """Return the port for a given network service.
2539
2522
2540 If port is an integer, it's returned as is. If it's a string, it's
2523 If port is an integer, it's returned as is. If it's a string, it's
2541 looked up using socket.getservbyname(). If there's no matching
2524 looked up using socket.getservbyname(). If there's no matching
2542 service, error.Abort is raised.
2525 service, error.Abort is raised.
2543 """
2526 """
2544 try:
2527 try:
2545 return int(port)
2528 return int(port)
2546 except ValueError:
2529 except ValueError:
2547 pass
2530 pass
2548
2531
2549 try:
2532 try:
2550 return socket.getservbyname(port)
2533 return socket.getservbyname(port)
2551 except socket.error:
2534 except socket.error:
2552 raise Abort(_("no port number associated with service '%s'") % port)
2535 raise Abort(_("no port number associated with service '%s'") % port)
2553
2536
2554 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2537 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2555 '0': False, 'no': False, 'false': False, 'off': False,
2538 '0': False, 'no': False, 'false': False, 'off': False,
2556 'never': False}
2539 'never': False}
2557
2540
2558 def parsebool(s):
2541 def parsebool(s):
2559 """Parse s into a boolean.
2542 """Parse s into a boolean.
2560
2543
2561 If s is not a valid boolean, returns None.
2544 If s is not a valid boolean, returns None.
2562 """
2545 """
2563 return _booleans.get(s.lower(), None)
2546 return _booleans.get(s.lower(), None)
2564
2547
2565 _hextochr = dict((a + b, chr(int(a + b, 16)))
2548 _hextochr = dict((a + b, chr(int(a + b, 16)))
2566 for a in string.hexdigits for b in string.hexdigits)
2549 for a in string.hexdigits for b in string.hexdigits)
2567
2550
2568 class url(object):
2551 class url(object):
2569 r"""Reliable URL parser.
2552 r"""Reliable URL parser.
2570
2553
2571 This parses URLs and provides attributes for the following
2554 This parses URLs and provides attributes for the following
2572 components:
2555 components:
2573
2556
2574 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2557 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2575
2558
2576 Missing components are set to None. The only exception is
2559 Missing components are set to None. The only exception is
2577 fragment, which is set to '' if present but empty.
2560 fragment, which is set to '' if present but empty.
2578
2561
2579 If parsefragment is False, fragment is included in query. If
2562 If parsefragment is False, fragment is included in query. If
2580 parsequery is False, query is included in path. If both are
2563 parsequery is False, query is included in path. If both are
2581 False, both fragment and query are included in path.
2564 False, both fragment and query are included in path.
2582
2565
2583 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2566 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2584
2567
2585 Note that for backward compatibility reasons, bundle URLs do not
2568 Note that for backward compatibility reasons, bundle URLs do not
2586 take host names. That means 'bundle://../' has a path of '../'.
2569 take host names. That means 'bundle://../' has a path of '../'.
2587
2570
2588 Examples:
2571 Examples:
2589
2572
2590 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2573 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2591 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2574 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2592 >>> url('ssh://[::1]:2200//home/joe/repo')
2575 >>> url('ssh://[::1]:2200//home/joe/repo')
2593 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2576 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2594 >>> url('file:///home/joe/repo')
2577 >>> url('file:///home/joe/repo')
2595 <url scheme: 'file', path: '/home/joe/repo'>
2578 <url scheme: 'file', path: '/home/joe/repo'>
2596 >>> url('file:///c:/temp/foo/')
2579 >>> url('file:///c:/temp/foo/')
2597 <url scheme: 'file', path: 'c:/temp/foo/'>
2580 <url scheme: 'file', path: 'c:/temp/foo/'>
2598 >>> url('bundle:foo')
2581 >>> url('bundle:foo')
2599 <url scheme: 'bundle', path: 'foo'>
2582 <url scheme: 'bundle', path: 'foo'>
2600 >>> url('bundle://../foo')
2583 >>> url('bundle://../foo')
2601 <url scheme: 'bundle', path: '../foo'>
2584 <url scheme: 'bundle', path: '../foo'>
2602 >>> url(r'c:\foo\bar')
2585 >>> url(r'c:\foo\bar')
2603 <url path: 'c:\\foo\\bar'>
2586 <url path: 'c:\\foo\\bar'>
2604 >>> url(r'\\blah\blah\blah')
2587 >>> url(r'\\blah\blah\blah')
2605 <url path: '\\\\blah\\blah\\blah'>
2588 <url path: '\\\\blah\\blah\\blah'>
2606 >>> url(r'\\blah\blah\blah#baz')
2589 >>> url(r'\\blah\blah\blah#baz')
2607 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2590 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2608 >>> url(r'file:///C:\users\me')
2591 >>> url(r'file:///C:\users\me')
2609 <url scheme: 'file', path: 'C:\\users\\me'>
2592 <url scheme: 'file', path: 'C:\\users\\me'>
2610
2593
2611 Authentication credentials:
2594 Authentication credentials:
2612
2595
2613 >>> url('ssh://joe:xyz@x/repo')
2596 >>> url('ssh://joe:xyz@x/repo')
2614 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2597 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2615 >>> url('ssh://joe@x/repo')
2598 >>> url('ssh://joe@x/repo')
2616 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2599 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2617
2600
2618 Query strings and fragments:
2601 Query strings and fragments:
2619
2602
2620 >>> url('http://host/a?b#c')
2603 >>> url('http://host/a?b#c')
2621 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2604 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2622 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2605 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2623 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2606 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2624
2607
2625 Empty path:
2608 Empty path:
2626
2609
2627 >>> url('')
2610 >>> url('')
2628 <url path: ''>
2611 <url path: ''>
2629 >>> url('#a')
2612 >>> url('#a')
2630 <url path: '', fragment: 'a'>
2613 <url path: '', fragment: 'a'>
2631 >>> url('http://host/')
2614 >>> url('http://host/')
2632 <url scheme: 'http', host: 'host', path: ''>
2615 <url scheme: 'http', host: 'host', path: ''>
2633 >>> url('http://host/#a')
2616 >>> url('http://host/#a')
2634 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2617 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2635
2618
2636 Only scheme:
2619 Only scheme:
2637
2620
2638 >>> url('http:')
2621 >>> url('http:')
2639 <url scheme: 'http'>
2622 <url scheme: 'http'>
2640 """
2623 """
2641
2624
2642 _safechars = "!~*'()+"
2625 _safechars = "!~*'()+"
2643 _safepchars = "/!~*'()+:\\"
2626 _safepchars = "/!~*'()+:\\"
2644 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2627 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2645
2628
2646 def __init__(self, path, parsequery=True, parsefragment=True):
2629 def __init__(self, path, parsequery=True, parsefragment=True):
2647 # We slowly chomp away at path until we have only the path left
2630 # We slowly chomp away at path until we have only the path left
2648 self.scheme = self.user = self.passwd = self.host = None
2631 self.scheme = self.user = self.passwd = self.host = None
2649 self.port = self.path = self.query = self.fragment = None
2632 self.port = self.path = self.query = self.fragment = None
2650 self._localpath = True
2633 self._localpath = True
2651 self._hostport = ''
2634 self._hostport = ''
2652 self._origpath = path
2635 self._origpath = path
2653
2636
2654 if parsefragment and '#' in path:
2637 if parsefragment and '#' in path:
2655 path, self.fragment = path.split('#', 1)
2638 path, self.fragment = path.split('#', 1)
2656
2639
2657 # special case for Windows drive letters and UNC paths
2640 # special case for Windows drive letters and UNC paths
2658 if hasdriveletter(path) or path.startswith('\\\\'):
2641 if hasdriveletter(path) or path.startswith('\\\\'):
2659 self.path = path
2642 self.path = path
2660 return
2643 return
2661
2644
2662 # For compatibility reasons, we can't handle bundle paths as
2645 # For compatibility reasons, we can't handle bundle paths as
2663 # normal URLS
2646 # normal URLS
2664 if path.startswith('bundle:'):
2647 if path.startswith('bundle:'):
2665 self.scheme = 'bundle'
2648 self.scheme = 'bundle'
2666 path = path[7:]
2649 path = path[7:]
2667 if path.startswith('//'):
2650 if path.startswith('//'):
2668 path = path[2:]
2651 path = path[2:]
2669 self.path = path
2652 self.path = path
2670 return
2653 return
2671
2654
2672 if self._matchscheme(path):
2655 if self._matchscheme(path):
2673 parts = path.split(':', 1)
2656 parts = path.split(':', 1)
2674 if parts[0]:
2657 if parts[0]:
2675 self.scheme, path = parts
2658 self.scheme, path = parts
2676 self._localpath = False
2659 self._localpath = False
2677
2660
2678 if not path:
2661 if not path:
2679 path = None
2662 path = None
2680 if self._localpath:
2663 if self._localpath:
2681 self.path = ''
2664 self.path = ''
2682 return
2665 return
2683 else:
2666 else:
2684 if self._localpath:
2667 if self._localpath:
2685 self.path = path
2668 self.path = path
2686 return
2669 return
2687
2670
2688 if parsequery and '?' in path:
2671 if parsequery and '?' in path:
2689 path, self.query = path.split('?', 1)
2672 path, self.query = path.split('?', 1)
2690 if not path:
2673 if not path:
2691 path = None
2674 path = None
2692 if not self.query:
2675 if not self.query:
2693 self.query = None
2676 self.query = None
2694
2677
2695 # // is required to specify a host/authority
2678 # // is required to specify a host/authority
2696 if path and path.startswith('//'):
2679 if path and path.startswith('//'):
2697 parts = path[2:].split('/', 1)
2680 parts = path[2:].split('/', 1)
2698 if len(parts) > 1:
2681 if len(parts) > 1:
2699 self.host, path = parts
2682 self.host, path = parts
2700 else:
2683 else:
2701 self.host = parts[0]
2684 self.host = parts[0]
2702 path = None
2685 path = None
2703 if not self.host:
2686 if not self.host:
2704 self.host = None
2687 self.host = None
2705 # path of file:///d is /d
2688 # path of file:///d is /d
2706 # path of file:///d:/ is d:/, not /d:/
2689 # path of file:///d:/ is d:/, not /d:/
2707 if path and not hasdriveletter(path):
2690 if path and not hasdriveletter(path):
2708 path = '/' + path
2691 path = '/' + path
2709
2692
2710 if self.host and '@' in self.host:
2693 if self.host and '@' in self.host:
2711 self.user, self.host = self.host.rsplit('@', 1)
2694 self.user, self.host = self.host.rsplit('@', 1)
2712 if ':' in self.user:
2695 if ':' in self.user:
2713 self.user, self.passwd = self.user.split(':', 1)
2696 self.user, self.passwd = self.user.split(':', 1)
2714 if not self.host:
2697 if not self.host:
2715 self.host = None
2698 self.host = None
2716
2699
2717 # Don't split on colons in IPv6 addresses without ports
2700 # Don't split on colons in IPv6 addresses without ports
2718 if (self.host and ':' in self.host and
2701 if (self.host and ':' in self.host and
2719 not (self.host.startswith('[') and self.host.endswith(']'))):
2702 not (self.host.startswith('[') and self.host.endswith(']'))):
2720 self._hostport = self.host
2703 self._hostport = self.host
2721 self.host, self.port = self.host.rsplit(':', 1)
2704 self.host, self.port = self.host.rsplit(':', 1)
2722 if not self.host:
2705 if not self.host:
2723 self.host = None
2706 self.host = None
2724
2707
2725 if (self.host and self.scheme == 'file' and
2708 if (self.host and self.scheme == 'file' and
2726 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2709 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2727 raise Abort(_('file:// URLs can only refer to localhost'))
2710 raise Abort(_('file:// URLs can only refer to localhost'))
2728
2711
2729 self.path = path
2712 self.path = path
2730
2713
2731 # leave the query string escaped
2714 # leave the query string escaped
2732 for a in ('user', 'passwd', 'host', 'port',
2715 for a in ('user', 'passwd', 'host', 'port',
2733 'path', 'fragment'):
2716 'path', 'fragment'):
2734 v = getattr(self, a)
2717 v = getattr(self, a)
2735 if v is not None:
2718 if v is not None:
2736 setattr(self, a, urlreq.unquote(v))
2719 setattr(self, a, urlreq.unquote(v))
2737
2720
2738 def __repr__(self):
2721 def __repr__(self):
2739 attrs = []
2722 attrs = []
2740 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2723 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2741 'query', 'fragment'):
2724 'query', 'fragment'):
2742 v = getattr(self, a)
2725 v = getattr(self, a)
2743 if v is not None:
2726 if v is not None:
2744 attrs.append('%s: %r' % (a, v))
2727 attrs.append('%s: %r' % (a, v))
2745 return '<url %s>' % ', '.join(attrs)
2728 return '<url %s>' % ', '.join(attrs)
2746
2729
2747 def __str__(self):
2730 def __str__(self):
2748 r"""Join the URL's components back into a URL string.
2731 r"""Join the URL's components back into a URL string.
2749
2732
2750 Examples:
2733 Examples:
2751
2734
2752 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2735 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2753 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2736 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2754 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2737 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2755 'http://user:pw@host:80/?foo=bar&baz=42'
2738 'http://user:pw@host:80/?foo=bar&baz=42'
2756 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2739 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2757 'http://user:pw@host:80/?foo=bar%3dbaz'
2740 'http://user:pw@host:80/?foo=bar%3dbaz'
2758 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2741 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2759 'ssh://user:pw@[::1]:2200//home/joe#'
2742 'ssh://user:pw@[::1]:2200//home/joe#'
2760 >>> str(url('http://localhost:80//'))
2743 >>> str(url('http://localhost:80//'))
2761 'http://localhost:80//'
2744 'http://localhost:80//'
2762 >>> str(url('http://localhost:80/'))
2745 >>> str(url('http://localhost:80/'))
2763 'http://localhost:80/'
2746 'http://localhost:80/'
2764 >>> str(url('http://localhost:80'))
2747 >>> str(url('http://localhost:80'))
2765 'http://localhost:80/'
2748 'http://localhost:80/'
2766 >>> str(url('bundle:foo'))
2749 >>> str(url('bundle:foo'))
2767 'bundle:foo'
2750 'bundle:foo'
2768 >>> str(url('bundle://../foo'))
2751 >>> str(url('bundle://../foo'))
2769 'bundle:../foo'
2752 'bundle:../foo'
2770 >>> str(url('path'))
2753 >>> str(url('path'))
2771 'path'
2754 'path'
2772 >>> str(url('file:///tmp/foo/bar'))
2755 >>> str(url('file:///tmp/foo/bar'))
2773 'file:///tmp/foo/bar'
2756 'file:///tmp/foo/bar'
2774 >>> str(url('file:///c:/tmp/foo/bar'))
2757 >>> str(url('file:///c:/tmp/foo/bar'))
2775 'file:///c:/tmp/foo/bar'
2758 'file:///c:/tmp/foo/bar'
2776 >>> print url(r'bundle:foo\bar')
2759 >>> print url(r'bundle:foo\bar')
2777 bundle:foo\bar
2760 bundle:foo\bar
2778 >>> print url(r'file:///D:\data\hg')
2761 >>> print url(r'file:///D:\data\hg')
2779 file:///D:\data\hg
2762 file:///D:\data\hg
2780 """
2763 """
2781 return encoding.strfromlocal(self.__bytes__())
2764 return encoding.strfromlocal(self.__bytes__())
2782
2765
2783 def __bytes__(self):
2766 def __bytes__(self):
2784 if self._localpath:
2767 if self._localpath:
2785 s = self.path
2768 s = self.path
2786 if self.scheme == 'bundle':
2769 if self.scheme == 'bundle':
2787 s = 'bundle:' + s
2770 s = 'bundle:' + s
2788 if self.fragment:
2771 if self.fragment:
2789 s += '#' + self.fragment
2772 s += '#' + self.fragment
2790 return s
2773 return s
2791
2774
2792 s = self.scheme + ':'
2775 s = self.scheme + ':'
2793 if self.user or self.passwd or self.host:
2776 if self.user or self.passwd or self.host:
2794 s += '//'
2777 s += '//'
2795 elif self.scheme and (not self.path or self.path.startswith('/')
2778 elif self.scheme and (not self.path or self.path.startswith('/')
2796 or hasdriveletter(self.path)):
2779 or hasdriveletter(self.path)):
2797 s += '//'
2780 s += '//'
2798 if hasdriveletter(self.path):
2781 if hasdriveletter(self.path):
2799 s += '/'
2782 s += '/'
2800 if self.user:
2783 if self.user:
2801 s += urlreq.quote(self.user, safe=self._safechars)
2784 s += urlreq.quote(self.user, safe=self._safechars)
2802 if self.passwd:
2785 if self.passwd:
2803 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2786 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2804 if self.user or self.passwd:
2787 if self.user or self.passwd:
2805 s += '@'
2788 s += '@'
2806 if self.host:
2789 if self.host:
2807 if not (self.host.startswith('[') and self.host.endswith(']')):
2790 if not (self.host.startswith('[') and self.host.endswith(']')):
2808 s += urlreq.quote(self.host)
2791 s += urlreq.quote(self.host)
2809 else:
2792 else:
2810 s += self.host
2793 s += self.host
2811 if self.port:
2794 if self.port:
2812 s += ':' + urlreq.quote(self.port)
2795 s += ':' + urlreq.quote(self.port)
2813 if self.host:
2796 if self.host:
2814 s += '/'
2797 s += '/'
2815 if self.path:
2798 if self.path:
2816 # TODO: similar to the query string, we should not unescape the
2799 # TODO: similar to the query string, we should not unescape the
2817 # path when we store it, the path might contain '%2f' = '/',
2800 # path when we store it, the path might contain '%2f' = '/',
2818 # which we should *not* escape.
2801 # which we should *not* escape.
2819 s += urlreq.quote(self.path, safe=self._safepchars)
2802 s += urlreq.quote(self.path, safe=self._safepchars)
2820 if self.query:
2803 if self.query:
2821 # we store the query in escaped form.
2804 # we store the query in escaped form.
2822 s += '?' + self.query
2805 s += '?' + self.query
2823 if self.fragment is not None:
2806 if self.fragment is not None:
2824 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2807 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2825 return s
2808 return s
2826
2809
2827 def authinfo(self):
2810 def authinfo(self):
2828 user, passwd = self.user, self.passwd
2811 user, passwd = self.user, self.passwd
2829 try:
2812 try:
2830 self.user, self.passwd = None, None
2813 self.user, self.passwd = None, None
2831 s = bytes(self)
2814 s = bytes(self)
2832 finally:
2815 finally:
2833 self.user, self.passwd = user, passwd
2816 self.user, self.passwd = user, passwd
2834 if not self.user:
2817 if not self.user:
2835 return (s, None)
2818 return (s, None)
2836 # authinfo[1] is passed to urllib2 password manager, and its
2819 # authinfo[1] is passed to urllib2 password manager, and its
2837 # URIs must not contain credentials. The host is passed in the
2820 # URIs must not contain credentials. The host is passed in the
2838 # URIs list because Python < 2.4.3 uses only that to search for
2821 # URIs list because Python < 2.4.3 uses only that to search for
2839 # a password.
2822 # a password.
2840 return (s, (None, (s, self.host),
2823 return (s, (None, (s, self.host),
2841 self.user, self.passwd or ''))
2824 self.user, self.passwd or ''))
2842
2825
2843 def isabs(self):
2826 def isabs(self):
2844 if self.scheme and self.scheme != 'file':
2827 if self.scheme and self.scheme != 'file':
2845 return True # remote URL
2828 return True # remote URL
2846 if hasdriveletter(self.path):
2829 if hasdriveletter(self.path):
2847 return True # absolute for our purposes - can't be joined()
2830 return True # absolute for our purposes - can't be joined()
2848 if self.path.startswith(r'\\'):
2831 if self.path.startswith(r'\\'):
2849 return True # Windows UNC path
2832 return True # Windows UNC path
2850 if self.path.startswith('/'):
2833 if self.path.startswith('/'):
2851 return True # POSIX-style
2834 return True # POSIX-style
2852 return False
2835 return False
2853
2836
2854 def localpath(self):
2837 def localpath(self):
2855 if self.scheme == 'file' or self.scheme == 'bundle':
2838 if self.scheme == 'file' or self.scheme == 'bundle':
2856 path = self.path or '/'
2839 path = self.path or '/'
2857 # For Windows, we need to promote hosts containing drive
2840 # For Windows, we need to promote hosts containing drive
2858 # letters to paths with drive letters.
2841 # letters to paths with drive letters.
2859 if hasdriveletter(self._hostport):
2842 if hasdriveletter(self._hostport):
2860 path = self._hostport + '/' + self.path
2843 path = self._hostport + '/' + self.path
2861 elif (self.host is not None and self.path
2844 elif (self.host is not None and self.path
2862 and not hasdriveletter(path)):
2845 and not hasdriveletter(path)):
2863 path = '/' + path
2846 path = '/' + path
2864 return path
2847 return path
2865 return self._origpath
2848 return self._origpath
2866
2849
2867 def islocal(self):
2850 def islocal(self):
2868 '''whether localpath will return something that posixfile can open'''
2851 '''whether localpath will return something that posixfile can open'''
2869 return (not self.scheme or self.scheme == 'file'
2852 return (not self.scheme or self.scheme == 'file'
2870 or self.scheme == 'bundle')
2853 or self.scheme == 'bundle')
2871
2854
2872 def hasscheme(path):
2855 def hasscheme(path):
2873 return bool(url(path).scheme)
2856 return bool(url(path).scheme)
2874
2857
2875 def hasdriveletter(path):
2858 def hasdriveletter(path):
2876 return path and path[1:2] == ':' and path[0:1].isalpha()
2859 return path and path[1:2] == ':' and path[0:1].isalpha()
2877
2860
2878 def urllocalpath(path):
2861 def urllocalpath(path):
2879 return url(path, parsequery=False, parsefragment=False).localpath()
2862 return url(path, parsequery=False, parsefragment=False).localpath()
2880
2863
2881 def hidepassword(u):
2864 def hidepassword(u):
2882 '''hide user credential in a url string'''
2865 '''hide user credential in a url string'''
2883 u = url(u)
2866 u = url(u)
2884 if u.passwd:
2867 if u.passwd:
2885 u.passwd = '***'
2868 u.passwd = '***'
2886 return bytes(u)
2869 return bytes(u)
2887
2870
2888 def removeauth(u):
2871 def removeauth(u):
2889 '''remove all authentication information from a url string'''
2872 '''remove all authentication information from a url string'''
2890 u = url(u)
2873 u = url(u)
2891 u.user = u.passwd = None
2874 u.user = u.passwd = None
2892 return str(u)
2875 return str(u)
2893
2876
2894 timecount = unitcountfn(
2877 timecount = unitcountfn(
2895 (1, 1e3, _('%.0f s')),
2878 (1, 1e3, _('%.0f s')),
2896 (100, 1, _('%.1f s')),
2879 (100, 1, _('%.1f s')),
2897 (10, 1, _('%.2f s')),
2880 (10, 1, _('%.2f s')),
2898 (1, 1, _('%.3f s')),
2881 (1, 1, _('%.3f s')),
2899 (100, 0.001, _('%.1f ms')),
2882 (100, 0.001, _('%.1f ms')),
2900 (10, 0.001, _('%.2f ms')),
2883 (10, 0.001, _('%.2f ms')),
2901 (1, 0.001, _('%.3f ms')),
2884 (1, 0.001, _('%.3f ms')),
2902 (100, 0.000001, _('%.1f us')),
2885 (100, 0.000001, _('%.1f us')),
2903 (10, 0.000001, _('%.2f us')),
2886 (10, 0.000001, _('%.2f us')),
2904 (1, 0.000001, _('%.3f us')),
2887 (1, 0.000001, _('%.3f us')),
2905 (100, 0.000000001, _('%.1f ns')),
2888 (100, 0.000000001, _('%.1f ns')),
2906 (10, 0.000000001, _('%.2f ns')),
2889 (10, 0.000000001, _('%.2f ns')),
2907 (1, 0.000000001, _('%.3f ns')),
2890 (1, 0.000000001, _('%.3f ns')),
2908 )
2891 )
2909
2892
2910 _timenesting = [0]
2893 _timenesting = [0]
2911
2894
2912 def timed(func):
2895 def timed(func):
2913 '''Report the execution time of a function call to stderr.
2896 '''Report the execution time of a function call to stderr.
2914
2897
2915 During development, use as a decorator when you need to measure
2898 During development, use as a decorator when you need to measure
2916 the cost of a function, e.g. as follows:
2899 the cost of a function, e.g. as follows:
2917
2900
2918 @util.timed
2901 @util.timed
2919 def foo(a, b, c):
2902 def foo(a, b, c):
2920 pass
2903 pass
2921 '''
2904 '''
2922
2905
2923 def wrapper(*args, **kwargs):
2906 def wrapper(*args, **kwargs):
2924 start = timer()
2907 start = timer()
2925 indent = 2
2908 indent = 2
2926 _timenesting[0] += indent
2909 _timenesting[0] += indent
2927 try:
2910 try:
2928 return func(*args, **kwargs)
2911 return func(*args, **kwargs)
2929 finally:
2912 finally:
2930 elapsed = timer() - start
2913 elapsed = timer() - start
2931 _timenesting[0] -= indent
2914 _timenesting[0] -= indent
2932 stderr.write('%s%s: %s\n' %
2915 stderr.write('%s%s: %s\n' %
2933 (' ' * _timenesting[0], func.__name__,
2916 (' ' * _timenesting[0], func.__name__,
2934 timecount(elapsed)))
2917 timecount(elapsed)))
2935 return wrapper
2918 return wrapper
2936
2919
2937 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2920 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2938 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2921 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2939
2922
2940 def sizetoint(s):
2923 def sizetoint(s):
2941 '''Convert a space specifier to a byte count.
2924 '''Convert a space specifier to a byte count.
2942
2925
2943 >>> sizetoint('30')
2926 >>> sizetoint('30')
2944 30
2927 30
2945 >>> sizetoint('2.2kb')
2928 >>> sizetoint('2.2kb')
2946 2252
2929 2252
2947 >>> sizetoint('6M')
2930 >>> sizetoint('6M')
2948 6291456
2931 6291456
2949 '''
2932 '''
2950 t = s.strip().lower()
2933 t = s.strip().lower()
2951 try:
2934 try:
2952 for k, u in _sizeunits:
2935 for k, u in _sizeunits:
2953 if t.endswith(k):
2936 if t.endswith(k):
2954 return int(float(t[:-len(k)]) * u)
2937 return int(float(t[:-len(k)]) * u)
2955 return int(t)
2938 return int(t)
2956 except ValueError:
2939 except ValueError:
2957 raise error.ParseError(_("couldn't parse size: %s") % s)
2940 raise error.ParseError(_("couldn't parse size: %s") % s)
2958
2941
2959 class hooks(object):
2942 class hooks(object):
2960 '''A collection of hook functions that can be used to extend a
2943 '''A collection of hook functions that can be used to extend a
2961 function's behavior. Hooks are called in lexicographic order,
2944 function's behavior. Hooks are called in lexicographic order,
2962 based on the names of their sources.'''
2945 based on the names of their sources.'''
2963
2946
2964 def __init__(self):
2947 def __init__(self):
2965 self._hooks = []
2948 self._hooks = []
2966
2949
2967 def add(self, source, hook):
2950 def add(self, source, hook):
2968 self._hooks.append((source, hook))
2951 self._hooks.append((source, hook))
2969
2952
2970 def __call__(self, *args):
2953 def __call__(self, *args):
2971 self._hooks.sort(key=lambda x: x[0])
2954 self._hooks.sort(key=lambda x: x[0])
2972 results = []
2955 results = []
2973 for source, hook in self._hooks:
2956 for source, hook in self._hooks:
2974 results.append(hook(*args))
2957 results.append(hook(*args))
2975 return results
2958 return results
2976
2959
2977 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2960 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s', depth=0):
2978 '''Yields lines for a nicely formatted stacktrace.
2961 '''Yields lines for a nicely formatted stacktrace.
2979 Skips the 'skip' last entries, then return the last 'depth' entries.
2962 Skips the 'skip' last entries, then return the last 'depth' entries.
2980 Each file+linenumber is formatted according to fileline.
2963 Each file+linenumber is formatted according to fileline.
2981 Each line is formatted according to line.
2964 Each line is formatted according to line.
2982 If line is None, it yields:
2965 If line is None, it yields:
2983 length of longest filepath+line number,
2966 length of longest filepath+line number,
2984 filepath+linenumber,
2967 filepath+linenumber,
2985 function
2968 function
2986
2969
2987 Not be used in production code but very convenient while developing.
2970 Not be used in production code but very convenient while developing.
2988 '''
2971 '''
2989 entries = [(fileline % (fn, ln), func)
2972 entries = [(fileline % (fn, ln), func)
2990 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2973 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
2991 ][-depth:]
2974 ][-depth:]
2992 if entries:
2975 if entries:
2993 fnmax = max(len(entry[0]) for entry in entries)
2976 fnmax = max(len(entry[0]) for entry in entries)
2994 for fnln, func in entries:
2977 for fnln, func in entries:
2995 if line is None:
2978 if line is None:
2996 yield (fnmax, fnln, func)
2979 yield (fnmax, fnln, func)
2997 else:
2980 else:
2998 yield line % (fnmax, fnln, func)
2981 yield line % (fnmax, fnln, func)
2999
2982
3000 def debugstacktrace(msg='stacktrace', skip=0,
2983 def debugstacktrace(msg='stacktrace', skip=0,
3001 f=stderr, otherf=stdout, depth=0):
2984 f=stderr, otherf=stdout, depth=0):
3002 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2985 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3003 Skips the 'skip' entries closest to the call, then show 'depth' entries.
2986 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3004 By default it will flush stdout first.
2987 By default it will flush stdout first.
3005 It can be used everywhere and intentionally does not require an ui object.
2988 It can be used everywhere and intentionally does not require an ui object.
3006 Not be used in production code but very convenient while developing.
2989 Not be used in production code but very convenient while developing.
3007 '''
2990 '''
3008 if otherf:
2991 if otherf:
3009 otherf.flush()
2992 otherf.flush()
3010 f.write('%s at:\n' % msg.rstrip())
2993 f.write('%s at:\n' % msg.rstrip())
3011 for line in getstackframes(skip + 1, depth=depth):
2994 for line in getstackframes(skip + 1, depth=depth):
3012 f.write(line)
2995 f.write(line)
3013 f.flush()
2996 f.flush()
3014
2997
3015 class dirs(object):
2998 class dirs(object):
3016 '''a multiset of directory names from a dirstate or manifest'''
2999 '''a multiset of directory names from a dirstate or manifest'''
3017
3000
3018 def __init__(self, map, skip=None):
3001 def __init__(self, map, skip=None):
3019 self._dirs = {}
3002 self._dirs = {}
3020 addpath = self.addpath
3003 addpath = self.addpath
3021 if safehasattr(map, 'iteritems') and skip is not None:
3004 if safehasattr(map, 'iteritems') and skip is not None:
3022 for f, s in map.iteritems():
3005 for f, s in map.iteritems():
3023 if s[0] != skip:
3006 if s[0] != skip:
3024 addpath(f)
3007 addpath(f)
3025 else:
3008 else:
3026 for f in map:
3009 for f in map:
3027 addpath(f)
3010 addpath(f)
3028
3011
3029 def addpath(self, path):
3012 def addpath(self, path):
3030 dirs = self._dirs
3013 dirs = self._dirs
3031 for base in finddirs(path):
3014 for base in finddirs(path):
3032 if base in dirs:
3015 if base in dirs:
3033 dirs[base] += 1
3016 dirs[base] += 1
3034 return
3017 return
3035 dirs[base] = 1
3018 dirs[base] = 1
3036
3019
3037 def delpath(self, path):
3020 def delpath(self, path):
3038 dirs = self._dirs
3021 dirs = self._dirs
3039 for base in finddirs(path):
3022 for base in finddirs(path):
3040 if dirs[base] > 1:
3023 if dirs[base] > 1:
3041 dirs[base] -= 1
3024 dirs[base] -= 1
3042 return
3025 return
3043 del dirs[base]
3026 del dirs[base]
3044
3027
3045 def __iter__(self):
3028 def __iter__(self):
3046 return iter(self._dirs)
3029 return iter(self._dirs)
3047
3030
3048 def __contains__(self, d):
3031 def __contains__(self, d):
3049 return d in self._dirs
3032 return d in self._dirs
3050
3033
3051 if safehasattr(parsers, 'dirs'):
3034 if safehasattr(parsers, 'dirs'):
3052 dirs = parsers.dirs
3035 dirs = parsers.dirs
3053
3036
3054 def finddirs(path):
3037 def finddirs(path):
3055 pos = path.rfind('/')
3038 pos = path.rfind('/')
3056 while pos != -1:
3039 while pos != -1:
3057 yield path[:pos]
3040 yield path[:pos]
3058 pos = path.rfind('/', 0, pos)
3041 pos = path.rfind('/', 0, pos)
3059
3042
3060 class ctxmanager(object):
3043 class ctxmanager(object):
3061 '''A context manager for use in 'with' blocks to allow multiple
3044 '''A context manager for use in 'with' blocks to allow multiple
3062 contexts to be entered at once. This is both safer and more
3045 contexts to be entered at once. This is both safer and more
3063 flexible than contextlib.nested.
3046 flexible than contextlib.nested.
3064
3047
3065 Once Mercurial supports Python 2.7+, this will become mostly
3048 Once Mercurial supports Python 2.7+, this will become mostly
3066 unnecessary.
3049 unnecessary.
3067 '''
3050 '''
3068
3051
3069 def __init__(self, *args):
3052 def __init__(self, *args):
3070 '''Accepts a list of no-argument functions that return context
3053 '''Accepts a list of no-argument functions that return context
3071 managers. These will be invoked at __call__ time.'''
3054 managers. These will be invoked at __call__ time.'''
3072 self._pending = args
3055 self._pending = args
3073 self._atexit = []
3056 self._atexit = []
3074
3057
3075 def __enter__(self):
3058 def __enter__(self):
3076 return self
3059 return self
3077
3060
3078 def enter(self):
3061 def enter(self):
3079 '''Create and enter context managers in the order in which they were
3062 '''Create and enter context managers in the order in which they were
3080 passed to the constructor.'''
3063 passed to the constructor.'''
3081 values = []
3064 values = []
3082 for func in self._pending:
3065 for func in self._pending:
3083 obj = func()
3066 obj = func()
3084 values.append(obj.__enter__())
3067 values.append(obj.__enter__())
3085 self._atexit.append(obj.__exit__)
3068 self._atexit.append(obj.__exit__)
3086 del self._pending
3069 del self._pending
3087 return values
3070 return values
3088
3071
3089 def atexit(self, func, *args, **kwargs):
3072 def atexit(self, func, *args, **kwargs):
3090 '''Add a function to call when this context manager exits. The
3073 '''Add a function to call when this context manager exits. The
3091 ordering of multiple atexit calls is unspecified, save that
3074 ordering of multiple atexit calls is unspecified, save that
3092 they will happen before any __exit__ functions.'''
3075 they will happen before any __exit__ functions.'''
3093 def wrapper(exc_type, exc_val, exc_tb):
3076 def wrapper(exc_type, exc_val, exc_tb):
3094 func(*args, **kwargs)
3077 func(*args, **kwargs)
3095 self._atexit.append(wrapper)
3078 self._atexit.append(wrapper)
3096 return func
3079 return func
3097
3080
3098 def __exit__(self, exc_type, exc_val, exc_tb):
3081 def __exit__(self, exc_type, exc_val, exc_tb):
3099 '''Context managers are exited in the reverse order from which
3082 '''Context managers are exited in the reverse order from which
3100 they were created.'''
3083 they were created.'''
3101 received = exc_type is not None
3084 received = exc_type is not None
3102 suppressed = False
3085 suppressed = False
3103 pending = None
3086 pending = None
3104 self._atexit.reverse()
3087 self._atexit.reverse()
3105 for exitfunc in self._atexit:
3088 for exitfunc in self._atexit:
3106 try:
3089 try:
3107 if exitfunc(exc_type, exc_val, exc_tb):
3090 if exitfunc(exc_type, exc_val, exc_tb):
3108 suppressed = True
3091 suppressed = True
3109 exc_type = None
3092 exc_type = None
3110 exc_val = None
3093 exc_val = None
3111 exc_tb = None
3094 exc_tb = None
3112 except BaseException:
3095 except BaseException:
3113 pending = sys.exc_info()
3096 pending = sys.exc_info()
3114 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3097 exc_type, exc_val, exc_tb = pending = sys.exc_info()
3115 del self._atexit
3098 del self._atexit
3116 if pending:
3099 if pending:
3117 raise exc_val
3100 raise exc_val
3118 return received and suppressed
3101 return received and suppressed
3119
3102
3120 # compression code
3103 # compression code
3121
3104
3122 SERVERROLE = 'server'
3105 SERVERROLE = 'server'
3123 CLIENTROLE = 'client'
3106 CLIENTROLE = 'client'
3124
3107
3125 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3108 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3126 (u'name', u'serverpriority',
3109 (u'name', u'serverpriority',
3127 u'clientpriority'))
3110 u'clientpriority'))
3128
3111
3129 class compressormanager(object):
3112 class compressormanager(object):
3130 """Holds registrations of various compression engines.
3113 """Holds registrations of various compression engines.
3131
3114
3132 This class essentially abstracts the differences between compression
3115 This class essentially abstracts the differences between compression
3133 engines to allow new compression formats to be added easily, possibly from
3116 engines to allow new compression formats to be added easily, possibly from
3134 extensions.
3117 extensions.
3135
3118
3136 Compressors are registered against the global instance by calling its
3119 Compressors are registered against the global instance by calling its
3137 ``register()`` method.
3120 ``register()`` method.
3138 """
3121 """
3139 def __init__(self):
3122 def __init__(self):
3140 self._engines = {}
3123 self._engines = {}
3141 # Bundle spec human name to engine name.
3124 # Bundle spec human name to engine name.
3142 self._bundlenames = {}
3125 self._bundlenames = {}
3143 # Internal bundle identifier to engine name.
3126 # Internal bundle identifier to engine name.
3144 self._bundletypes = {}
3127 self._bundletypes = {}
3145 # Revlog header to engine name.
3128 # Revlog header to engine name.
3146 self._revlogheaders = {}
3129 self._revlogheaders = {}
3147 # Wire proto identifier to engine name.
3130 # Wire proto identifier to engine name.
3148 self._wiretypes = {}
3131 self._wiretypes = {}
3149
3132
3150 def __getitem__(self, key):
3133 def __getitem__(self, key):
3151 return self._engines[key]
3134 return self._engines[key]
3152
3135
3153 def __contains__(self, key):
3136 def __contains__(self, key):
3154 return key in self._engines
3137 return key in self._engines
3155
3138
3156 def __iter__(self):
3139 def __iter__(self):
3157 return iter(self._engines.keys())
3140 return iter(self._engines.keys())
3158
3141
3159 def register(self, engine):
3142 def register(self, engine):
3160 """Register a compression engine with the manager.
3143 """Register a compression engine with the manager.
3161
3144
3162 The argument must be a ``compressionengine`` instance.
3145 The argument must be a ``compressionengine`` instance.
3163 """
3146 """
3164 if not isinstance(engine, compressionengine):
3147 if not isinstance(engine, compressionengine):
3165 raise ValueError(_('argument must be a compressionengine'))
3148 raise ValueError(_('argument must be a compressionengine'))
3166
3149
3167 name = engine.name()
3150 name = engine.name()
3168
3151
3169 if name in self._engines:
3152 if name in self._engines:
3170 raise error.Abort(_('compression engine %s already registered') %
3153 raise error.Abort(_('compression engine %s already registered') %
3171 name)
3154 name)
3172
3155
3173 bundleinfo = engine.bundletype()
3156 bundleinfo = engine.bundletype()
3174 if bundleinfo:
3157 if bundleinfo:
3175 bundlename, bundletype = bundleinfo
3158 bundlename, bundletype = bundleinfo
3176
3159
3177 if bundlename in self._bundlenames:
3160 if bundlename in self._bundlenames:
3178 raise error.Abort(_('bundle name %s already registered') %
3161 raise error.Abort(_('bundle name %s already registered') %
3179 bundlename)
3162 bundlename)
3180 if bundletype in self._bundletypes:
3163 if bundletype in self._bundletypes:
3181 raise error.Abort(_('bundle type %s already registered by %s') %
3164 raise error.Abort(_('bundle type %s already registered by %s') %
3182 (bundletype, self._bundletypes[bundletype]))
3165 (bundletype, self._bundletypes[bundletype]))
3183
3166
3184 # No external facing name declared.
3167 # No external facing name declared.
3185 if bundlename:
3168 if bundlename:
3186 self._bundlenames[bundlename] = name
3169 self._bundlenames[bundlename] = name
3187
3170
3188 self._bundletypes[bundletype] = name
3171 self._bundletypes[bundletype] = name
3189
3172
3190 wiresupport = engine.wireprotosupport()
3173 wiresupport = engine.wireprotosupport()
3191 if wiresupport:
3174 if wiresupport:
3192 wiretype = wiresupport.name
3175 wiretype = wiresupport.name
3193 if wiretype in self._wiretypes:
3176 if wiretype in self._wiretypes:
3194 raise error.Abort(_('wire protocol compression %s already '
3177 raise error.Abort(_('wire protocol compression %s already '
3195 'registered by %s') %
3178 'registered by %s') %
3196 (wiretype, self._wiretypes[wiretype]))
3179 (wiretype, self._wiretypes[wiretype]))
3197
3180
3198 self._wiretypes[wiretype] = name
3181 self._wiretypes[wiretype] = name
3199
3182
3200 revlogheader = engine.revlogheader()
3183 revlogheader = engine.revlogheader()
3201 if revlogheader and revlogheader in self._revlogheaders:
3184 if revlogheader and revlogheader in self._revlogheaders:
3202 raise error.Abort(_('revlog header %s already registered by %s') %
3185 raise error.Abort(_('revlog header %s already registered by %s') %
3203 (revlogheader, self._revlogheaders[revlogheader]))
3186 (revlogheader, self._revlogheaders[revlogheader]))
3204
3187
3205 if revlogheader:
3188 if revlogheader:
3206 self._revlogheaders[revlogheader] = name
3189 self._revlogheaders[revlogheader] = name
3207
3190
3208 self._engines[name] = engine
3191 self._engines[name] = engine
3209
3192
3210 @property
3193 @property
3211 def supportedbundlenames(self):
3194 def supportedbundlenames(self):
3212 return set(self._bundlenames.keys())
3195 return set(self._bundlenames.keys())
3213
3196
3214 @property
3197 @property
3215 def supportedbundletypes(self):
3198 def supportedbundletypes(self):
3216 return set(self._bundletypes.keys())
3199 return set(self._bundletypes.keys())
3217
3200
3218 def forbundlename(self, bundlename):
3201 def forbundlename(self, bundlename):
3219 """Obtain a compression engine registered to a bundle name.
3202 """Obtain a compression engine registered to a bundle name.
3220
3203
3221 Will raise KeyError if the bundle type isn't registered.
3204 Will raise KeyError if the bundle type isn't registered.
3222
3205
3223 Will abort if the engine is known but not available.
3206 Will abort if the engine is known but not available.
3224 """
3207 """
3225 engine = self._engines[self._bundlenames[bundlename]]
3208 engine = self._engines[self._bundlenames[bundlename]]
3226 if not engine.available():
3209 if not engine.available():
3227 raise error.Abort(_('compression engine %s could not be loaded') %
3210 raise error.Abort(_('compression engine %s could not be loaded') %
3228 engine.name())
3211 engine.name())
3229 return engine
3212 return engine
3230
3213
3231 def forbundletype(self, bundletype):
3214 def forbundletype(self, bundletype):
3232 """Obtain a compression engine registered to a bundle type.
3215 """Obtain a compression engine registered to a bundle type.
3233
3216
3234 Will raise KeyError if the bundle type isn't registered.
3217 Will raise KeyError if the bundle type isn't registered.
3235
3218
3236 Will abort if the engine is known but not available.
3219 Will abort if the engine is known but not available.
3237 """
3220 """
3238 engine = self._engines[self._bundletypes[bundletype]]
3221 engine = self._engines[self._bundletypes[bundletype]]
3239 if not engine.available():
3222 if not engine.available():
3240 raise error.Abort(_('compression engine %s could not be loaded') %
3223 raise error.Abort(_('compression engine %s could not be loaded') %
3241 engine.name())
3224 engine.name())
3242 return engine
3225 return engine
3243
3226
3244 def supportedwireengines(self, role, onlyavailable=True):
3227 def supportedwireengines(self, role, onlyavailable=True):
3245 """Obtain compression engines that support the wire protocol.
3228 """Obtain compression engines that support the wire protocol.
3246
3229
3247 Returns a list of engines in prioritized order, most desired first.
3230 Returns a list of engines in prioritized order, most desired first.
3248
3231
3249 If ``onlyavailable`` is set, filter out engines that can't be
3232 If ``onlyavailable`` is set, filter out engines that can't be
3250 loaded.
3233 loaded.
3251 """
3234 """
3252 assert role in (SERVERROLE, CLIENTROLE)
3235 assert role in (SERVERROLE, CLIENTROLE)
3253
3236
3254 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3237 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3255
3238
3256 engines = [self._engines[e] for e in self._wiretypes.values()]
3239 engines = [self._engines[e] for e in self._wiretypes.values()]
3257 if onlyavailable:
3240 if onlyavailable:
3258 engines = [e for e in engines if e.available()]
3241 engines = [e for e in engines if e.available()]
3259
3242
3260 def getkey(e):
3243 def getkey(e):
3261 # Sort first by priority, highest first. In case of tie, sort
3244 # Sort first by priority, highest first. In case of tie, sort
3262 # alphabetically. This is arbitrary, but ensures output is
3245 # alphabetically. This is arbitrary, but ensures output is
3263 # stable.
3246 # stable.
3264 w = e.wireprotosupport()
3247 w = e.wireprotosupport()
3265 return -1 * getattr(w, attr), w.name
3248 return -1 * getattr(w, attr), w.name
3266
3249
3267 return list(sorted(engines, key=getkey))
3250 return list(sorted(engines, key=getkey))
3268
3251
3269 def forwiretype(self, wiretype):
3252 def forwiretype(self, wiretype):
3270 engine = self._engines[self._wiretypes[wiretype]]
3253 engine = self._engines[self._wiretypes[wiretype]]
3271 if not engine.available():
3254 if not engine.available():
3272 raise error.Abort(_('compression engine %s could not be loaded') %
3255 raise error.Abort(_('compression engine %s could not be loaded') %
3273 engine.name())
3256 engine.name())
3274 return engine
3257 return engine
3275
3258
3276 def forrevlogheader(self, header):
3259 def forrevlogheader(self, header):
3277 """Obtain a compression engine registered to a revlog header.
3260 """Obtain a compression engine registered to a revlog header.
3278
3261
3279 Will raise KeyError if the revlog header value isn't registered.
3262 Will raise KeyError if the revlog header value isn't registered.
3280 """
3263 """
3281 return self._engines[self._revlogheaders[header]]
3264 return self._engines[self._revlogheaders[header]]
3282
3265
3283 compengines = compressormanager()
3266 compengines = compressormanager()
3284
3267
3285 class compressionengine(object):
3268 class compressionengine(object):
3286 """Base class for compression engines.
3269 """Base class for compression engines.
3287
3270
3288 Compression engines must implement the interface defined by this class.
3271 Compression engines must implement the interface defined by this class.
3289 """
3272 """
3290 def name(self):
3273 def name(self):
3291 """Returns the name of the compression engine.
3274 """Returns the name of the compression engine.
3292
3275
3293 This is the key the engine is registered under.
3276 This is the key the engine is registered under.
3294
3277
3295 This method must be implemented.
3278 This method must be implemented.
3296 """
3279 """
3297 raise NotImplementedError()
3280 raise NotImplementedError()
3298
3281
3299 def available(self):
3282 def available(self):
3300 """Whether the compression engine is available.
3283 """Whether the compression engine is available.
3301
3284
3302 The intent of this method is to allow optional compression engines
3285 The intent of this method is to allow optional compression engines
3303 that may not be available in all installations (such as engines relying
3286 that may not be available in all installations (such as engines relying
3304 on C extensions that may not be present).
3287 on C extensions that may not be present).
3305 """
3288 """
3306 return True
3289 return True
3307
3290
3308 def bundletype(self):
3291 def bundletype(self):
3309 """Describes bundle identifiers for this engine.
3292 """Describes bundle identifiers for this engine.
3310
3293
3311 If this compression engine isn't supported for bundles, returns None.
3294 If this compression engine isn't supported for bundles, returns None.
3312
3295
3313 If this engine can be used for bundles, returns a 2-tuple of strings of
3296 If this engine can be used for bundles, returns a 2-tuple of strings of
3314 the user-facing "bundle spec" compression name and an internal
3297 the user-facing "bundle spec" compression name and an internal
3315 identifier used to denote the compression format within bundles. To
3298 identifier used to denote the compression format within bundles. To
3316 exclude the name from external usage, set the first element to ``None``.
3299 exclude the name from external usage, set the first element to ``None``.
3317
3300
3318 If bundle compression is supported, the class must also implement
3301 If bundle compression is supported, the class must also implement
3319 ``compressstream`` and `decompressorreader``.
3302 ``compressstream`` and `decompressorreader``.
3320
3303
3321 The docstring of this method is used in the help system to tell users
3304 The docstring of this method is used in the help system to tell users
3322 about this engine.
3305 about this engine.
3323 """
3306 """
3324 return None
3307 return None
3325
3308
3326 def wireprotosupport(self):
3309 def wireprotosupport(self):
3327 """Declare support for this compression format on the wire protocol.
3310 """Declare support for this compression format on the wire protocol.
3328
3311
3329 If this compression engine isn't supported for compressing wire
3312 If this compression engine isn't supported for compressing wire
3330 protocol payloads, returns None.
3313 protocol payloads, returns None.
3331
3314
3332 Otherwise, returns ``compenginewireprotosupport`` with the following
3315 Otherwise, returns ``compenginewireprotosupport`` with the following
3333 fields:
3316 fields:
3334
3317
3335 * String format identifier
3318 * String format identifier
3336 * Integer priority for the server
3319 * Integer priority for the server
3337 * Integer priority for the client
3320 * Integer priority for the client
3338
3321
3339 The integer priorities are used to order the advertisement of format
3322 The integer priorities are used to order the advertisement of format
3340 support by server and client. The highest integer is advertised
3323 support by server and client. The highest integer is advertised
3341 first. Integers with non-positive values aren't advertised.
3324 first. Integers with non-positive values aren't advertised.
3342
3325
3343 The priority values are somewhat arbitrary and only used for default
3326 The priority values are somewhat arbitrary and only used for default
3344 ordering. The relative order can be changed via config options.
3327 ordering. The relative order can be changed via config options.
3345
3328
3346 If wire protocol compression is supported, the class must also implement
3329 If wire protocol compression is supported, the class must also implement
3347 ``compressstream`` and ``decompressorreader``.
3330 ``compressstream`` and ``decompressorreader``.
3348 """
3331 """
3349 return None
3332 return None
3350
3333
3351 def revlogheader(self):
3334 def revlogheader(self):
3352 """Header added to revlog chunks that identifies this engine.
3335 """Header added to revlog chunks that identifies this engine.
3353
3336
3354 If this engine can be used to compress revlogs, this method should
3337 If this engine can be used to compress revlogs, this method should
3355 return the bytes used to identify chunks compressed with this engine.
3338 return the bytes used to identify chunks compressed with this engine.
3356 Else, the method should return ``None`` to indicate it does not
3339 Else, the method should return ``None`` to indicate it does not
3357 participate in revlog compression.
3340 participate in revlog compression.
3358 """
3341 """
3359 return None
3342 return None
3360
3343
3361 def compressstream(self, it, opts=None):
3344 def compressstream(self, it, opts=None):
3362 """Compress an iterator of chunks.
3345 """Compress an iterator of chunks.
3363
3346
3364 The method receives an iterator (ideally a generator) of chunks of
3347 The method receives an iterator (ideally a generator) of chunks of
3365 bytes to be compressed. It returns an iterator (ideally a generator)
3348 bytes to be compressed. It returns an iterator (ideally a generator)
3366 of bytes of chunks representing the compressed output.
3349 of bytes of chunks representing the compressed output.
3367
3350
3368 Optionally accepts an argument defining how to perform compression.
3351 Optionally accepts an argument defining how to perform compression.
3369 Each engine treats this argument differently.
3352 Each engine treats this argument differently.
3370 """
3353 """
3371 raise NotImplementedError()
3354 raise NotImplementedError()
3372
3355
3373 def decompressorreader(self, fh):
3356 def decompressorreader(self, fh):
3374 """Perform decompression on a file object.
3357 """Perform decompression on a file object.
3375
3358
3376 Argument is an object with a ``read(size)`` method that returns
3359 Argument is an object with a ``read(size)`` method that returns
3377 compressed data. Return value is an object with a ``read(size)`` that
3360 compressed data. Return value is an object with a ``read(size)`` that
3378 returns uncompressed data.
3361 returns uncompressed data.
3379 """
3362 """
3380 raise NotImplementedError()
3363 raise NotImplementedError()
3381
3364
3382 def revlogcompressor(self, opts=None):
3365 def revlogcompressor(self, opts=None):
3383 """Obtain an object that can be used to compress revlog entries.
3366 """Obtain an object that can be used to compress revlog entries.
3384
3367
3385 The object has a ``compress(data)`` method that compresses binary
3368 The object has a ``compress(data)`` method that compresses binary
3386 data. This method returns compressed binary data or ``None`` if
3369 data. This method returns compressed binary data or ``None`` if
3387 the data could not be compressed (too small, not compressible, etc).
3370 the data could not be compressed (too small, not compressible, etc).
3388 The returned data should have a header uniquely identifying this
3371 The returned data should have a header uniquely identifying this
3389 compression format so decompression can be routed to this engine.
3372 compression format so decompression can be routed to this engine.
3390 This header should be identified by the ``revlogheader()`` return
3373 This header should be identified by the ``revlogheader()`` return
3391 value.
3374 value.
3392
3375
3393 The object has a ``decompress(data)`` method that decompresses
3376 The object has a ``decompress(data)`` method that decompresses
3394 data. The method will only be called if ``data`` begins with
3377 data. The method will only be called if ``data`` begins with
3395 ``revlogheader()``. The method should return the raw, uncompressed
3378 ``revlogheader()``. The method should return the raw, uncompressed
3396 data or raise a ``RevlogError``.
3379 data or raise a ``RevlogError``.
3397
3380
3398 The object is reusable but is not thread safe.
3381 The object is reusable but is not thread safe.
3399 """
3382 """
3400 raise NotImplementedError()
3383 raise NotImplementedError()
3401
3384
3402 class _zlibengine(compressionengine):
3385 class _zlibengine(compressionengine):
3403 def name(self):
3386 def name(self):
3404 return 'zlib'
3387 return 'zlib'
3405
3388
3406 def bundletype(self):
3389 def bundletype(self):
3407 """zlib compression using the DEFLATE algorithm.
3390 """zlib compression using the DEFLATE algorithm.
3408
3391
3409 All Mercurial clients should support this format. The compression
3392 All Mercurial clients should support this format. The compression
3410 algorithm strikes a reasonable balance between compression ratio
3393 algorithm strikes a reasonable balance between compression ratio
3411 and size.
3394 and size.
3412 """
3395 """
3413 return 'gzip', 'GZ'
3396 return 'gzip', 'GZ'
3414
3397
3415 def wireprotosupport(self):
3398 def wireprotosupport(self):
3416 return compewireprotosupport('zlib', 20, 20)
3399 return compewireprotosupport('zlib', 20, 20)
3417
3400
3418 def revlogheader(self):
3401 def revlogheader(self):
3419 return 'x'
3402 return 'x'
3420
3403
3421 def compressstream(self, it, opts=None):
3404 def compressstream(self, it, opts=None):
3422 opts = opts or {}
3405 opts = opts or {}
3423
3406
3424 z = zlib.compressobj(opts.get('level', -1))
3407 z = zlib.compressobj(opts.get('level', -1))
3425 for chunk in it:
3408 for chunk in it:
3426 data = z.compress(chunk)
3409 data = z.compress(chunk)
3427 # Not all calls to compress emit data. It is cheaper to inspect
3410 # Not all calls to compress emit data. It is cheaper to inspect
3428 # here than to feed empty chunks through generator.
3411 # here than to feed empty chunks through generator.
3429 if data:
3412 if data:
3430 yield data
3413 yield data
3431
3414
3432 yield z.flush()
3415 yield z.flush()
3433
3416
3434 def decompressorreader(self, fh):
3417 def decompressorreader(self, fh):
3435 def gen():
3418 def gen():
3436 d = zlib.decompressobj()
3419 d = zlib.decompressobj()
3437 for chunk in filechunkiter(fh):
3420 for chunk in filechunkiter(fh):
3438 while chunk:
3421 while chunk:
3439 # Limit output size to limit memory.
3422 # Limit output size to limit memory.
3440 yield d.decompress(chunk, 2 ** 18)
3423 yield d.decompress(chunk, 2 ** 18)
3441 chunk = d.unconsumed_tail
3424 chunk = d.unconsumed_tail
3442
3425
3443 return chunkbuffer(gen())
3426 return chunkbuffer(gen())
3444
3427
3445 class zlibrevlogcompressor(object):
3428 class zlibrevlogcompressor(object):
3446 def compress(self, data):
3429 def compress(self, data):
3447 insize = len(data)
3430 insize = len(data)
3448 # Caller handles empty input case.
3431 # Caller handles empty input case.
3449 assert insize > 0
3432 assert insize > 0
3450
3433
3451 if insize < 44:
3434 if insize < 44:
3452 return None
3435 return None
3453
3436
3454 elif insize <= 1000000:
3437 elif insize <= 1000000:
3455 compressed = zlib.compress(data)
3438 compressed = zlib.compress(data)
3456 if len(compressed) < insize:
3439 if len(compressed) < insize:
3457 return compressed
3440 return compressed
3458 return None
3441 return None
3459
3442
3460 # zlib makes an internal copy of the input buffer, doubling
3443 # zlib makes an internal copy of the input buffer, doubling
3461 # memory usage for large inputs. So do streaming compression
3444 # memory usage for large inputs. So do streaming compression
3462 # on large inputs.
3445 # on large inputs.
3463 else:
3446 else:
3464 z = zlib.compressobj()
3447 z = zlib.compressobj()
3465 parts = []
3448 parts = []
3466 pos = 0
3449 pos = 0
3467 while pos < insize:
3450 while pos < insize:
3468 pos2 = pos + 2**20
3451 pos2 = pos + 2**20
3469 parts.append(z.compress(data[pos:pos2]))
3452 parts.append(z.compress(data[pos:pos2]))
3470 pos = pos2
3453 pos = pos2
3471 parts.append(z.flush())
3454 parts.append(z.flush())
3472
3455
3473 if sum(map(len, parts)) < insize:
3456 if sum(map(len, parts)) < insize:
3474 return ''.join(parts)
3457 return ''.join(parts)
3475 return None
3458 return None
3476
3459
3477 def decompress(self, data):
3460 def decompress(self, data):
3478 try:
3461 try:
3479 return zlib.decompress(data)
3462 return zlib.decompress(data)
3480 except zlib.error as e:
3463 except zlib.error as e:
3481 raise error.RevlogError(_('revlog decompress error: %s') %
3464 raise error.RevlogError(_('revlog decompress error: %s') %
3482 str(e))
3465 str(e))
3483
3466
3484 def revlogcompressor(self, opts=None):
3467 def revlogcompressor(self, opts=None):
3485 return self.zlibrevlogcompressor()
3468 return self.zlibrevlogcompressor()
3486
3469
3487 compengines.register(_zlibengine())
3470 compengines.register(_zlibengine())
3488
3471
3489 class _bz2engine(compressionengine):
3472 class _bz2engine(compressionengine):
3490 def name(self):
3473 def name(self):
3491 return 'bz2'
3474 return 'bz2'
3492
3475
3493 def bundletype(self):
3476 def bundletype(self):
3494 """An algorithm that produces smaller bundles than ``gzip``.
3477 """An algorithm that produces smaller bundles than ``gzip``.
3495
3478
3496 All Mercurial clients should support this format.
3479 All Mercurial clients should support this format.
3497
3480
3498 This engine will likely produce smaller bundles than ``gzip`` but
3481 This engine will likely produce smaller bundles than ``gzip`` but
3499 will be significantly slower, both during compression and
3482 will be significantly slower, both during compression and
3500 decompression.
3483 decompression.
3501
3484
3502 If available, the ``zstd`` engine can yield similar or better
3485 If available, the ``zstd`` engine can yield similar or better
3503 compression at much higher speeds.
3486 compression at much higher speeds.
3504 """
3487 """
3505 return 'bzip2', 'BZ'
3488 return 'bzip2', 'BZ'
3506
3489
3507 # We declare a protocol name but don't advertise by default because
3490 # We declare a protocol name but don't advertise by default because
3508 # it is slow.
3491 # it is slow.
3509 def wireprotosupport(self):
3492 def wireprotosupport(self):
3510 return compewireprotosupport('bzip2', 0, 0)
3493 return compewireprotosupport('bzip2', 0, 0)
3511
3494
3512 def compressstream(self, it, opts=None):
3495 def compressstream(self, it, opts=None):
3513 opts = opts or {}
3496 opts = opts or {}
3514 z = bz2.BZ2Compressor(opts.get('level', 9))
3497 z = bz2.BZ2Compressor(opts.get('level', 9))
3515 for chunk in it:
3498 for chunk in it:
3516 data = z.compress(chunk)
3499 data = z.compress(chunk)
3517 if data:
3500 if data:
3518 yield data
3501 yield data
3519
3502
3520 yield z.flush()
3503 yield z.flush()
3521
3504
3522 def decompressorreader(self, fh):
3505 def decompressorreader(self, fh):
3523 def gen():
3506 def gen():
3524 d = bz2.BZ2Decompressor()
3507 d = bz2.BZ2Decompressor()
3525 for chunk in filechunkiter(fh):
3508 for chunk in filechunkiter(fh):
3526 yield d.decompress(chunk)
3509 yield d.decompress(chunk)
3527
3510
3528 return chunkbuffer(gen())
3511 return chunkbuffer(gen())
3529
3512
3530 compengines.register(_bz2engine())
3513 compengines.register(_bz2engine())
3531
3514
3532 class _truncatedbz2engine(compressionengine):
3515 class _truncatedbz2engine(compressionengine):
3533 def name(self):
3516 def name(self):
3534 return 'bz2truncated'
3517 return 'bz2truncated'
3535
3518
3536 def bundletype(self):
3519 def bundletype(self):
3537 return None, '_truncatedBZ'
3520 return None, '_truncatedBZ'
3538
3521
3539 # We don't implement compressstream because it is hackily handled elsewhere.
3522 # We don't implement compressstream because it is hackily handled elsewhere.
3540
3523
3541 def decompressorreader(self, fh):
3524 def decompressorreader(self, fh):
3542 def gen():
3525 def gen():
3543 # The input stream doesn't have the 'BZ' header. So add it back.
3526 # The input stream doesn't have the 'BZ' header. So add it back.
3544 d = bz2.BZ2Decompressor()
3527 d = bz2.BZ2Decompressor()
3545 d.decompress('BZ')
3528 d.decompress('BZ')
3546 for chunk in filechunkiter(fh):
3529 for chunk in filechunkiter(fh):
3547 yield d.decompress(chunk)
3530 yield d.decompress(chunk)
3548
3531
3549 return chunkbuffer(gen())
3532 return chunkbuffer(gen())
3550
3533
3551 compengines.register(_truncatedbz2engine())
3534 compengines.register(_truncatedbz2engine())
3552
3535
3553 class _noopengine(compressionengine):
3536 class _noopengine(compressionengine):
3554 def name(self):
3537 def name(self):
3555 return 'none'
3538 return 'none'
3556
3539
3557 def bundletype(self):
3540 def bundletype(self):
3558 """No compression is performed.
3541 """No compression is performed.
3559
3542
3560 Use this compression engine to explicitly disable compression.
3543 Use this compression engine to explicitly disable compression.
3561 """
3544 """
3562 return 'none', 'UN'
3545 return 'none', 'UN'
3563
3546
3564 # Clients always support uncompressed payloads. Servers don't because
3547 # Clients always support uncompressed payloads. Servers don't because
3565 # unless you are on a fast network, uncompressed payloads can easily
3548 # unless you are on a fast network, uncompressed payloads can easily
3566 # saturate your network pipe.
3549 # saturate your network pipe.
3567 def wireprotosupport(self):
3550 def wireprotosupport(self):
3568 return compewireprotosupport('none', 0, 10)
3551 return compewireprotosupport('none', 0, 10)
3569
3552
3570 # We don't implement revlogheader because it is handled specially
3553 # We don't implement revlogheader because it is handled specially
3571 # in the revlog class.
3554 # in the revlog class.
3572
3555
3573 def compressstream(self, it, opts=None):
3556 def compressstream(self, it, opts=None):
3574 return it
3557 return it
3575
3558
3576 def decompressorreader(self, fh):
3559 def decompressorreader(self, fh):
3577 return fh
3560 return fh
3578
3561
3579 class nooprevlogcompressor(object):
3562 class nooprevlogcompressor(object):
3580 def compress(self, data):
3563 def compress(self, data):
3581 return None
3564 return None
3582
3565
3583 def revlogcompressor(self, opts=None):
3566 def revlogcompressor(self, opts=None):
3584 return self.nooprevlogcompressor()
3567 return self.nooprevlogcompressor()
3585
3568
3586 compengines.register(_noopengine())
3569 compengines.register(_noopengine())
3587
3570
3588 class _zstdengine(compressionengine):
3571 class _zstdengine(compressionengine):
3589 def name(self):
3572 def name(self):
3590 return 'zstd'
3573 return 'zstd'
3591
3574
3592 @propertycache
3575 @propertycache
3593 def _module(self):
3576 def _module(self):
3594 # Not all installs have the zstd module available. So defer importing
3577 # Not all installs have the zstd module available. So defer importing
3595 # until first access.
3578 # until first access.
3596 try:
3579 try:
3597 from . import zstd
3580 from . import zstd
3598 # Force delayed import.
3581 # Force delayed import.
3599 zstd.__version__
3582 zstd.__version__
3600 return zstd
3583 return zstd
3601 except ImportError:
3584 except ImportError:
3602 return None
3585 return None
3603
3586
3604 def available(self):
3587 def available(self):
3605 return bool(self._module)
3588 return bool(self._module)
3606
3589
3607 def bundletype(self):
3590 def bundletype(self):
3608 """A modern compression algorithm that is fast and highly flexible.
3591 """A modern compression algorithm that is fast and highly flexible.
3609
3592
3610 Only supported by Mercurial 4.1 and newer clients.
3593 Only supported by Mercurial 4.1 and newer clients.
3611
3594
3612 With the default settings, zstd compression is both faster and yields
3595 With the default settings, zstd compression is both faster and yields
3613 better compression than ``gzip``. It also frequently yields better
3596 better compression than ``gzip``. It also frequently yields better
3614 compression than ``bzip2`` while operating at much higher speeds.
3597 compression than ``bzip2`` while operating at much higher speeds.
3615
3598
3616 If this engine is available and backwards compatibility is not a
3599 If this engine is available and backwards compatibility is not a
3617 concern, it is likely the best available engine.
3600 concern, it is likely the best available engine.
3618 """
3601 """
3619 return 'zstd', 'ZS'
3602 return 'zstd', 'ZS'
3620
3603
3621 def wireprotosupport(self):
3604 def wireprotosupport(self):
3622 return compewireprotosupport('zstd', 50, 50)
3605 return compewireprotosupport('zstd', 50, 50)
3623
3606
3624 def revlogheader(self):
3607 def revlogheader(self):
3625 return '\x28'
3608 return '\x28'
3626
3609
3627 def compressstream(self, it, opts=None):
3610 def compressstream(self, it, opts=None):
3628 opts = opts or {}
3611 opts = opts or {}
3629 # zstd level 3 is almost always significantly faster than zlib
3612 # zstd level 3 is almost always significantly faster than zlib
3630 # while providing no worse compression. It strikes a good balance
3613 # while providing no worse compression. It strikes a good balance
3631 # between speed and compression.
3614 # between speed and compression.
3632 level = opts.get('level', 3)
3615 level = opts.get('level', 3)
3633
3616
3634 zstd = self._module
3617 zstd = self._module
3635 z = zstd.ZstdCompressor(level=level).compressobj()
3618 z = zstd.ZstdCompressor(level=level).compressobj()
3636 for chunk in it:
3619 for chunk in it:
3637 data = z.compress(chunk)
3620 data = z.compress(chunk)
3638 if data:
3621 if data:
3639 yield data
3622 yield data
3640
3623
3641 yield z.flush()
3624 yield z.flush()
3642
3625
3643 def decompressorreader(self, fh):
3626 def decompressorreader(self, fh):
3644 zstd = self._module
3627 zstd = self._module
3645 dctx = zstd.ZstdDecompressor()
3628 dctx = zstd.ZstdDecompressor()
3646 return chunkbuffer(dctx.read_from(fh))
3629 return chunkbuffer(dctx.read_from(fh))
3647
3630
3648 class zstdrevlogcompressor(object):
3631 class zstdrevlogcompressor(object):
3649 def __init__(self, zstd, level=3):
3632 def __init__(self, zstd, level=3):
3650 # Writing the content size adds a few bytes to the output. However,
3633 # Writing the content size adds a few bytes to the output. However,
3651 # it allows decompression to be more optimal since we can
3634 # it allows decompression to be more optimal since we can
3652 # pre-allocate a buffer to hold the result.
3635 # pre-allocate a buffer to hold the result.
3653 self._cctx = zstd.ZstdCompressor(level=level,
3636 self._cctx = zstd.ZstdCompressor(level=level,
3654 write_content_size=True)
3637 write_content_size=True)
3655 self._dctx = zstd.ZstdDecompressor()
3638 self._dctx = zstd.ZstdDecompressor()
3656 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3639 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3657 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3640 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3658
3641
3659 def compress(self, data):
3642 def compress(self, data):
3660 insize = len(data)
3643 insize = len(data)
3661 # Caller handles empty input case.
3644 # Caller handles empty input case.
3662 assert insize > 0
3645 assert insize > 0
3663
3646
3664 if insize < 50:
3647 if insize < 50:
3665 return None
3648 return None
3666
3649
3667 elif insize <= 1000000:
3650 elif insize <= 1000000:
3668 compressed = self._cctx.compress(data)
3651 compressed = self._cctx.compress(data)
3669 if len(compressed) < insize:
3652 if len(compressed) < insize:
3670 return compressed
3653 return compressed
3671 return None
3654 return None
3672 else:
3655 else:
3673 z = self._cctx.compressobj()
3656 z = self._cctx.compressobj()
3674 chunks = []
3657 chunks = []
3675 pos = 0
3658 pos = 0
3676 while pos < insize:
3659 while pos < insize:
3677 pos2 = pos + self._compinsize
3660 pos2 = pos + self._compinsize
3678 chunk = z.compress(data[pos:pos2])
3661 chunk = z.compress(data[pos:pos2])
3679 if chunk:
3662 if chunk:
3680 chunks.append(chunk)
3663 chunks.append(chunk)
3681 pos = pos2
3664 pos = pos2
3682 chunks.append(z.flush())
3665 chunks.append(z.flush())
3683
3666
3684 if sum(map(len, chunks)) < insize:
3667 if sum(map(len, chunks)) < insize:
3685 return ''.join(chunks)
3668 return ''.join(chunks)
3686 return None
3669 return None
3687
3670
3688 def decompress(self, data):
3671 def decompress(self, data):
3689 insize = len(data)
3672 insize = len(data)
3690
3673
3691 try:
3674 try:
3692 # This was measured to be faster than other streaming
3675 # This was measured to be faster than other streaming
3693 # decompressors.
3676 # decompressors.
3694 dobj = self._dctx.decompressobj()
3677 dobj = self._dctx.decompressobj()
3695 chunks = []
3678 chunks = []
3696 pos = 0
3679 pos = 0
3697 while pos < insize:
3680 while pos < insize:
3698 pos2 = pos + self._decompinsize
3681 pos2 = pos + self._decompinsize
3699 chunk = dobj.decompress(data[pos:pos2])
3682 chunk = dobj.decompress(data[pos:pos2])
3700 if chunk:
3683 if chunk:
3701 chunks.append(chunk)
3684 chunks.append(chunk)
3702 pos = pos2
3685 pos = pos2
3703 # Frame should be exhausted, so no finish() API.
3686 # Frame should be exhausted, so no finish() API.
3704
3687
3705 return ''.join(chunks)
3688 return ''.join(chunks)
3706 except Exception as e:
3689 except Exception as e:
3707 raise error.RevlogError(_('revlog decompress error: %s') %
3690 raise error.RevlogError(_('revlog decompress error: %s') %
3708 str(e))
3691 str(e))
3709
3692
3710 def revlogcompressor(self, opts=None):
3693 def revlogcompressor(self, opts=None):
3711 opts = opts or {}
3694 opts = opts or {}
3712 return self.zstdrevlogcompressor(self._module,
3695 return self.zstdrevlogcompressor(self._module,
3713 level=opts.get('level', 3))
3696 level=opts.get('level', 3))
3714
3697
3715 compengines.register(_zstdengine())
3698 compengines.register(_zstdengine())
3716
3699
3717 def bundlecompressiontopics():
3700 def bundlecompressiontopics():
3718 """Obtains a list of available bundle compressions for use in help."""
3701 """Obtains a list of available bundle compressions for use in help."""
3719 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3702 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3720 items = {}
3703 items = {}
3721
3704
3722 # We need to format the docstring. So use a dummy object/type to hold it
3705 # We need to format the docstring. So use a dummy object/type to hold it
3723 # rather than mutating the original.
3706 # rather than mutating the original.
3724 class docobject(object):
3707 class docobject(object):
3725 pass
3708 pass
3726
3709
3727 for name in compengines:
3710 for name in compengines:
3728 engine = compengines[name]
3711 engine = compengines[name]
3729
3712
3730 if not engine.available():
3713 if not engine.available():
3731 continue
3714 continue
3732
3715
3733 bt = engine.bundletype()
3716 bt = engine.bundletype()
3734 if not bt or not bt[0]:
3717 if not bt or not bt[0]:
3735 continue
3718 continue
3736
3719
3737 doc = pycompat.sysstr('``%s``\n %s') % (
3720 doc = pycompat.sysstr('``%s``\n %s') % (
3738 bt[0], engine.bundletype.__doc__)
3721 bt[0], engine.bundletype.__doc__)
3739
3722
3740 value = docobject()
3723 value = docobject()
3741 value.__doc__ = doc
3724 value.__doc__ = doc
3742
3725
3743 items[bt[0]] = value
3726 items[bt[0]] = value
3744
3727
3745 return items
3728 return items
3746
3729
3747 # convenient shortcut
3730 # convenient shortcut
3748 dst = debugstacktrace
3731 dst = debugstacktrace
@@ -1,793 +1,793 b''
1 commit date test
1 commit date test
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo > foo
5 $ echo foo > foo
6 $ hg add foo
6 $ hg add foo
7 $ cat > $TESTTMP/checkeditform.sh <<EOF
7 $ cat > $TESTTMP/checkeditform.sh <<EOF
8 > env | grep HGEDITFORM
8 > env | grep HGEDITFORM
9 > true
9 > true
10 > EOF
10 > EOF
11 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg commit -m ""
11 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg commit -m ""
12 HGEDITFORM=commit.normal.normal
12 HGEDITFORM=commit.normal.normal
13 abort: empty commit message
13 abort: empty commit message
14 [255]
14 [255]
15 $ hg commit -d '0 0' -m commit-1
15 $ hg commit -d '0 0' -m commit-1
16 $ echo foo >> foo
16 $ echo foo >> foo
17 $ hg commit -d '1 4444444' -m commit-3
17 $ hg commit -d '1 4444444' -m commit-3
18 abort: impossible time zone offset: 4444444
18 hg: parse error: impossible time zone offset: 4444444
19 [255]
19 [255]
20 $ hg commit -d '1 15.1' -m commit-4
20 $ hg commit -d '1 15.1' -m commit-4
21 abort: invalid date: '1\t15.1'
21 hg: parse error: invalid date: '1\t15.1'
22 [255]
22 [255]
23 $ hg commit -d 'foo bar' -m commit-5
23 $ hg commit -d 'foo bar' -m commit-5
24 abort: invalid date: 'foo bar'
24 hg: parse error: invalid date: 'foo bar'
25 [255]
25 [255]
26 $ hg commit -d ' 1 4444' -m commit-6
26 $ hg commit -d ' 1 4444' -m commit-6
27 $ hg commit -d '111111111111 0' -m commit-7
27 $ hg commit -d '111111111111 0' -m commit-7
28 abort: date exceeds 32 bits: 111111111111
28 hg: parse error: date exceeds 32 bits: 111111111111
29 [255]
29 [255]
30 $ hg commit -d '-111111111111 0' -m commit-7
30 $ hg commit -d '-111111111111 0' -m commit-7
31 abort: date exceeds 32 bits: -111111111111
31 hg: parse error: date exceeds 32 bits: -111111111111
32 [255]
32 [255]
33 $ echo foo >> foo
33 $ echo foo >> foo
34 $ hg commit -d '1901-12-13 20:45:52 +0000' -m commit-7-2
34 $ hg commit -d '1901-12-13 20:45:52 +0000' -m commit-7-2
35 $ echo foo >> foo
35 $ echo foo >> foo
36 $ hg commit -d '-2147483648 0' -m commit-7-3
36 $ hg commit -d '-2147483648 0' -m commit-7-3
37 $ hg log -T '{rev} {date|isodatesec}\n' -l2
37 $ hg log -T '{rev} {date|isodatesec}\n' -l2
38 3 1901-12-13 20:45:52 +0000
38 3 1901-12-13 20:45:52 +0000
39 2 1901-12-13 20:45:52 +0000
39 2 1901-12-13 20:45:52 +0000
40 $ hg commit -d '1901-12-13 20:45:51 +0000' -m commit-7
40 $ hg commit -d '1901-12-13 20:45:51 +0000' -m commit-7
41 abort: date exceeds 32 bits: -2147483649
41 hg: parse error: date exceeds 32 bits: -2147483649
42 [255]
42 [255]
43 $ hg commit -d '-2147483649 0' -m commit-7
43 $ hg commit -d '-2147483649 0' -m commit-7
44 abort: date exceeds 32 bits: -2147483649
44 hg: parse error: date exceeds 32 bits: -2147483649
45 [255]
45 [255]
46
46
47 commit added file that has been deleted
47 commit added file that has been deleted
48
48
49 $ echo bar > bar
49 $ echo bar > bar
50 $ hg add bar
50 $ hg add bar
51 $ rm bar
51 $ rm bar
52 $ hg commit -m commit-8
52 $ hg commit -m commit-8
53 nothing changed (1 missing files, see 'hg status')
53 nothing changed (1 missing files, see 'hg status')
54 [1]
54 [1]
55 $ hg commit -m commit-8-2 bar
55 $ hg commit -m commit-8-2 bar
56 abort: bar: file not found!
56 abort: bar: file not found!
57 [255]
57 [255]
58
58
59 $ hg -q revert -a --no-backup
59 $ hg -q revert -a --no-backup
60
60
61 $ mkdir dir
61 $ mkdir dir
62 $ echo boo > dir/file
62 $ echo boo > dir/file
63 $ hg add
63 $ hg add
64 adding dir/file (glob)
64 adding dir/file (glob)
65 $ hg -v commit -m commit-9 dir
65 $ hg -v commit -m commit-9 dir
66 committing files:
66 committing files:
67 dir/file
67 dir/file
68 committing manifest
68 committing manifest
69 committing changelog
69 committing changelog
70 committed changeset 4:1957363f1ced
70 committed changeset 4:1957363f1ced
71
71
72 $ echo > dir.file
72 $ echo > dir.file
73 $ hg add
73 $ hg add
74 adding dir.file
74 adding dir.file
75 $ hg commit -m commit-10 dir dir.file
75 $ hg commit -m commit-10 dir dir.file
76 abort: dir: no match under directory!
76 abort: dir: no match under directory!
77 [255]
77 [255]
78
78
79 $ echo >> dir/file
79 $ echo >> dir/file
80 $ mkdir bleh
80 $ mkdir bleh
81 $ mkdir dir2
81 $ mkdir dir2
82 $ cd bleh
82 $ cd bleh
83 $ hg commit -m commit-11 .
83 $ hg commit -m commit-11 .
84 abort: bleh: no match under directory!
84 abort: bleh: no match under directory!
85 [255]
85 [255]
86 $ hg commit -m commit-12 ../dir ../dir2
86 $ hg commit -m commit-12 ../dir ../dir2
87 abort: dir2: no match under directory!
87 abort: dir2: no match under directory!
88 [255]
88 [255]
89 $ hg -v commit -m commit-13 ../dir
89 $ hg -v commit -m commit-13 ../dir
90 committing files:
90 committing files:
91 dir/file
91 dir/file
92 committing manifest
92 committing manifest
93 committing changelog
93 committing changelog
94 committed changeset 5:a31d8f87544a
94 committed changeset 5:a31d8f87544a
95 $ cd ..
95 $ cd ..
96
96
97 $ hg commit -m commit-14 does-not-exist
97 $ hg commit -m commit-14 does-not-exist
98 abort: does-not-exist: * (glob)
98 abort: does-not-exist: * (glob)
99 [255]
99 [255]
100
100
101 #if symlink
101 #if symlink
102 $ ln -s foo baz
102 $ ln -s foo baz
103 $ hg commit -m commit-15 baz
103 $ hg commit -m commit-15 baz
104 abort: baz: file not tracked!
104 abort: baz: file not tracked!
105 [255]
105 [255]
106 #endif
106 #endif
107
107
108 $ touch quux
108 $ touch quux
109 $ hg commit -m commit-16 quux
109 $ hg commit -m commit-16 quux
110 abort: quux: file not tracked!
110 abort: quux: file not tracked!
111 [255]
111 [255]
112 $ echo >> dir/file
112 $ echo >> dir/file
113 $ hg -v commit -m commit-17 dir/file
113 $ hg -v commit -m commit-17 dir/file
114 committing files:
114 committing files:
115 dir/file
115 dir/file
116 committing manifest
116 committing manifest
117 committing changelog
117 committing changelog
118 committed changeset 6:32d054c9d085
118 committed changeset 6:32d054c9d085
119
119
120 An empty date was interpreted as epoch origin
120 An empty date was interpreted as epoch origin
121
121
122 $ echo foo >> foo
122 $ echo foo >> foo
123 $ hg commit -d '' -m commit-no-date --config devel.default-date=
123 $ hg commit -d '' -m commit-no-date --config devel.default-date=
124 $ hg tip --template '{date|isodate}\n' | grep '1970'
124 $ hg tip --template '{date|isodate}\n' | grep '1970'
125 [1]
125 [1]
126
126
127 Make sure we do not obscure unknown requires file entries (issue2649)
127 Make sure we do not obscure unknown requires file entries (issue2649)
128
128
129 $ echo foo >> foo
129 $ echo foo >> foo
130 $ echo fake >> .hg/requires
130 $ echo fake >> .hg/requires
131 $ hg commit -m bla
131 $ hg commit -m bla
132 abort: repository requires features unknown to this Mercurial: fake!
132 abort: repository requires features unknown to this Mercurial: fake!
133 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
133 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
134 [255]
134 [255]
135
135
136 $ cd ..
136 $ cd ..
137
137
138
138
139 partial subdir commit test
139 partial subdir commit test
140
140
141 $ hg init test2
141 $ hg init test2
142 $ cd test2
142 $ cd test2
143 $ mkdir foo
143 $ mkdir foo
144 $ echo foo > foo/foo
144 $ echo foo > foo/foo
145 $ mkdir bar
145 $ mkdir bar
146 $ echo bar > bar/bar
146 $ echo bar > bar/bar
147 $ hg add
147 $ hg add
148 adding bar/bar (glob)
148 adding bar/bar (glob)
149 adding foo/foo (glob)
149 adding foo/foo (glob)
150 $ HGEDITOR=cat hg ci -e -m commit-subdir-1 foo
150 $ HGEDITOR=cat hg ci -e -m commit-subdir-1 foo
151 commit-subdir-1
151 commit-subdir-1
152
152
153
153
154 HG: Enter commit message. Lines beginning with 'HG:' are removed.
154 HG: Enter commit message. Lines beginning with 'HG:' are removed.
155 HG: Leave message empty to abort commit.
155 HG: Leave message empty to abort commit.
156 HG: --
156 HG: --
157 HG: user: test
157 HG: user: test
158 HG: branch 'default'
158 HG: branch 'default'
159 HG: added foo/foo
159 HG: added foo/foo
160
160
161
161
162 $ hg ci -m commit-subdir-2 bar
162 $ hg ci -m commit-subdir-2 bar
163
163
164 subdir log 1
164 subdir log 1
165
165
166 $ hg log -v foo
166 $ hg log -v foo
167 changeset: 0:f97e73a25882
167 changeset: 0:f97e73a25882
168 user: test
168 user: test
169 date: Thu Jan 01 00:00:00 1970 +0000
169 date: Thu Jan 01 00:00:00 1970 +0000
170 files: foo/foo
170 files: foo/foo
171 description:
171 description:
172 commit-subdir-1
172 commit-subdir-1
173
173
174
174
175
175
176 subdir log 2
176 subdir log 2
177
177
178 $ hg log -v bar
178 $ hg log -v bar
179 changeset: 1:aa809156d50d
179 changeset: 1:aa809156d50d
180 tag: tip
180 tag: tip
181 user: test
181 user: test
182 date: Thu Jan 01 00:00:00 1970 +0000
182 date: Thu Jan 01 00:00:00 1970 +0000
183 files: bar/bar
183 files: bar/bar
184 description:
184 description:
185 commit-subdir-2
185 commit-subdir-2
186
186
187
187
188
188
189 full log
189 full log
190
190
191 $ hg log -v
191 $ hg log -v
192 changeset: 1:aa809156d50d
192 changeset: 1:aa809156d50d
193 tag: tip
193 tag: tip
194 user: test
194 user: test
195 date: Thu Jan 01 00:00:00 1970 +0000
195 date: Thu Jan 01 00:00:00 1970 +0000
196 files: bar/bar
196 files: bar/bar
197 description:
197 description:
198 commit-subdir-2
198 commit-subdir-2
199
199
200
200
201 changeset: 0:f97e73a25882
201 changeset: 0:f97e73a25882
202 user: test
202 user: test
203 date: Thu Jan 01 00:00:00 1970 +0000
203 date: Thu Jan 01 00:00:00 1970 +0000
204 files: foo/foo
204 files: foo/foo
205 description:
205 description:
206 commit-subdir-1
206 commit-subdir-1
207
207
208
208
209 $ cd ..
209 $ cd ..
210
210
211
211
212 dot and subdir commit test
212 dot and subdir commit test
213
213
214 $ hg init test3
214 $ hg init test3
215 $ echo commit-foo-subdir > commit-log-test
215 $ echo commit-foo-subdir > commit-log-test
216 $ cd test3
216 $ cd test3
217 $ mkdir foo
217 $ mkdir foo
218 $ echo foo content > foo/plain-file
218 $ echo foo content > foo/plain-file
219 $ hg add foo/plain-file
219 $ hg add foo/plain-file
220 $ HGEDITOR=cat hg ci --edit -l ../commit-log-test foo
220 $ HGEDITOR=cat hg ci --edit -l ../commit-log-test foo
221 commit-foo-subdir
221 commit-foo-subdir
222
222
223
223
224 HG: Enter commit message. Lines beginning with 'HG:' are removed.
224 HG: Enter commit message. Lines beginning with 'HG:' are removed.
225 HG: Leave message empty to abort commit.
225 HG: Leave message empty to abort commit.
226 HG: --
226 HG: --
227 HG: user: test
227 HG: user: test
228 HG: branch 'default'
228 HG: branch 'default'
229 HG: added foo/plain-file
229 HG: added foo/plain-file
230
230
231
231
232 $ echo modified foo content > foo/plain-file
232 $ echo modified foo content > foo/plain-file
233 $ hg ci -m commit-foo-dot .
233 $ hg ci -m commit-foo-dot .
234
234
235 full log
235 full log
236
236
237 $ hg log -v
237 $ hg log -v
238 changeset: 1:95b38e3a5b2e
238 changeset: 1:95b38e3a5b2e
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:00 1970 +0000
241 date: Thu Jan 01 00:00:00 1970 +0000
242 files: foo/plain-file
242 files: foo/plain-file
243 description:
243 description:
244 commit-foo-dot
244 commit-foo-dot
245
245
246
246
247 changeset: 0:65d4e9386227
247 changeset: 0:65d4e9386227
248 user: test
248 user: test
249 date: Thu Jan 01 00:00:00 1970 +0000
249 date: Thu Jan 01 00:00:00 1970 +0000
250 files: foo/plain-file
250 files: foo/plain-file
251 description:
251 description:
252 commit-foo-subdir
252 commit-foo-subdir
253
253
254
254
255
255
256 subdir log
256 subdir log
257
257
258 $ cd foo
258 $ cd foo
259 $ hg log .
259 $ hg log .
260 changeset: 1:95b38e3a5b2e
260 changeset: 1:95b38e3a5b2e
261 tag: tip
261 tag: tip
262 user: test
262 user: test
263 date: Thu Jan 01 00:00:00 1970 +0000
263 date: Thu Jan 01 00:00:00 1970 +0000
264 summary: commit-foo-dot
264 summary: commit-foo-dot
265
265
266 changeset: 0:65d4e9386227
266 changeset: 0:65d4e9386227
267 user: test
267 user: test
268 date: Thu Jan 01 00:00:00 1970 +0000
268 date: Thu Jan 01 00:00:00 1970 +0000
269 summary: commit-foo-subdir
269 summary: commit-foo-subdir
270
270
271 $ cd ..
271 $ cd ..
272 $ cd ..
272 $ cd ..
273
273
274 Issue1049: Hg permits partial commit of merge without warning
274 Issue1049: Hg permits partial commit of merge without warning
275
275
276 $ hg init issue1049
276 $ hg init issue1049
277 $ cd issue1049
277 $ cd issue1049
278 $ echo a > a
278 $ echo a > a
279 $ hg ci -Ama
279 $ hg ci -Ama
280 adding a
280 adding a
281 $ echo a >> a
281 $ echo a >> a
282 $ hg ci -mb
282 $ hg ci -mb
283 $ hg up 0
283 $ hg up 0
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 $ echo b >> a
285 $ echo b >> a
286 $ hg ci -mc
286 $ hg ci -mc
287 created new head
287 created new head
288 $ HGMERGE=true hg merge
288 $ HGMERGE=true hg merge
289 merging a
289 merging a
290 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
290 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
291 (branch merge, don't forget to commit)
291 (branch merge, don't forget to commit)
292
292
293 should fail because we are specifying a file name
293 should fail because we are specifying a file name
294
294
295 $ hg ci -mmerge a
295 $ hg ci -mmerge a
296 abort: cannot partially commit a merge (do not specify files or patterns)
296 abort: cannot partially commit a merge (do not specify files or patterns)
297 [255]
297 [255]
298
298
299 should fail because we are specifying a pattern
299 should fail because we are specifying a pattern
300
300
301 $ hg ci -mmerge -I a
301 $ hg ci -mmerge -I a
302 abort: cannot partially commit a merge (do not specify files or patterns)
302 abort: cannot partially commit a merge (do not specify files or patterns)
303 [255]
303 [255]
304
304
305 should succeed
305 should succeed
306
306
307 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg ci -mmerge --edit
307 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg ci -mmerge --edit
308 HGEDITFORM=commit.normal.merge
308 HGEDITFORM=commit.normal.merge
309 $ cd ..
309 $ cd ..
310
310
311
311
312 test commit message content
312 test commit message content
313
313
314 $ hg init commitmsg
314 $ hg init commitmsg
315 $ cd commitmsg
315 $ cd commitmsg
316 $ echo changed > changed
316 $ echo changed > changed
317 $ echo removed > removed
317 $ echo removed > removed
318 $ hg book activebookmark
318 $ hg book activebookmark
319 $ hg ci -qAm init
319 $ hg ci -qAm init
320
320
321 $ hg rm removed
321 $ hg rm removed
322 $ echo changed >> changed
322 $ echo changed >> changed
323 $ echo added > added
323 $ echo added > added
324 $ hg add added
324 $ hg add added
325 $ HGEDITOR=cat hg ci -A
325 $ HGEDITOR=cat hg ci -A
326
326
327
327
328 HG: Enter commit message. Lines beginning with 'HG:' are removed.
328 HG: Enter commit message. Lines beginning with 'HG:' are removed.
329 HG: Leave message empty to abort commit.
329 HG: Leave message empty to abort commit.
330 HG: --
330 HG: --
331 HG: user: test
331 HG: user: test
332 HG: branch 'default'
332 HG: branch 'default'
333 HG: bookmark 'activebookmark'
333 HG: bookmark 'activebookmark'
334 HG: added added
334 HG: added added
335 HG: changed changed
335 HG: changed changed
336 HG: removed removed
336 HG: removed removed
337 abort: empty commit message
337 abort: empty commit message
338 [255]
338 [255]
339
339
340 test saving last-message.txt
340 test saving last-message.txt
341
341
342 $ hg init sub
342 $ hg init sub
343 $ echo a > sub/a
343 $ echo a > sub/a
344 $ hg -R sub add sub/a
344 $ hg -R sub add sub/a
345 $ cat > sub/.hg/hgrc <<EOF
345 $ cat > sub/.hg/hgrc <<EOF
346 > [hooks]
346 > [hooks]
347 > precommit.test-saving-last-message = false
347 > precommit.test-saving-last-message = false
348 > EOF
348 > EOF
349
349
350 $ echo 'sub = sub' > .hgsub
350 $ echo 'sub = sub' > .hgsub
351 $ hg add .hgsub
351 $ hg add .hgsub
352
352
353 $ cat > $TESTTMP/editor.sh <<EOF
353 $ cat > $TESTTMP/editor.sh <<EOF
354 > echo "==== before editing:"
354 > echo "==== before editing:"
355 > cat \$1
355 > cat \$1
356 > echo "===="
356 > echo "===="
357 > echo "test saving last-message.txt" >> \$1
357 > echo "test saving last-message.txt" >> \$1
358 > EOF
358 > EOF
359
359
360 $ rm -f .hg/last-message.txt
360 $ rm -f .hg/last-message.txt
361 $ HGEDITOR="sh $TESTTMP/editor.sh" hg commit -S -q
361 $ HGEDITOR="sh $TESTTMP/editor.sh" hg commit -S -q
362 ==== before editing:
362 ==== before editing:
363
363
364
364
365 HG: Enter commit message. Lines beginning with 'HG:' are removed.
365 HG: Enter commit message. Lines beginning with 'HG:' are removed.
366 HG: Leave message empty to abort commit.
366 HG: Leave message empty to abort commit.
367 HG: --
367 HG: --
368 HG: user: test
368 HG: user: test
369 HG: branch 'default'
369 HG: branch 'default'
370 HG: bookmark 'activebookmark'
370 HG: bookmark 'activebookmark'
371 HG: subrepo sub
371 HG: subrepo sub
372 HG: added .hgsub
372 HG: added .hgsub
373 HG: added added
373 HG: added added
374 HG: changed .hgsubstate
374 HG: changed .hgsubstate
375 HG: changed changed
375 HG: changed changed
376 HG: removed removed
376 HG: removed removed
377 ====
377 ====
378 abort: precommit.test-saving-last-message hook exited with status 1 (in subrepo sub)
378 abort: precommit.test-saving-last-message hook exited with status 1 (in subrepo sub)
379 [255]
379 [255]
380 $ cat .hg/last-message.txt
380 $ cat .hg/last-message.txt
381
381
382
382
383 test saving last-message.txt
383 test saving last-message.txt
384
384
385 test that '[committemplate] changeset' definition and commit log
385 test that '[committemplate] changeset' definition and commit log
386 specific template keywords work well
386 specific template keywords work well
387
387
388 $ cat >> .hg/hgrc <<EOF
388 $ cat >> .hg/hgrc <<EOF
389 > [committemplate]
389 > [committemplate]
390 > changeset.commit.normal = 'HG: this is "commit.normal" template
390 > changeset.commit.normal = 'HG: this is "commit.normal" template
391 > HG: {extramsg}
391 > HG: {extramsg}
392 > {if(activebookmark,
392 > {if(activebookmark,
393 > "HG: bookmark '{activebookmark}' is activated\n",
393 > "HG: bookmark '{activebookmark}' is activated\n",
394 > "HG: no bookmark is activated\n")}{subrepos %
394 > "HG: no bookmark is activated\n")}{subrepos %
395 > "HG: subrepo '{subrepo}' is changed\n"}'
395 > "HG: subrepo '{subrepo}' is changed\n"}'
396 >
396 >
397 > changeset.commit = HG: this is "commit" template
397 > changeset.commit = HG: this is "commit" template
398 > HG: {extramsg}
398 > HG: {extramsg}
399 > {if(activebookmark,
399 > {if(activebookmark,
400 > "HG: bookmark '{activebookmark}' is activated\n",
400 > "HG: bookmark '{activebookmark}' is activated\n",
401 > "HG: no bookmark is activated\n")}{subrepos %
401 > "HG: no bookmark is activated\n")}{subrepos %
402 > "HG: subrepo '{subrepo}' is changed\n"}
402 > "HG: subrepo '{subrepo}' is changed\n"}
403 >
403 >
404 > changeset = HG: this is customized commit template
404 > changeset = HG: this is customized commit template
405 > HG: {extramsg}
405 > HG: {extramsg}
406 > {if(activebookmark,
406 > {if(activebookmark,
407 > "HG: bookmark '{activebookmark}' is activated\n",
407 > "HG: bookmark '{activebookmark}' is activated\n",
408 > "HG: no bookmark is activated\n")}{subrepos %
408 > "HG: no bookmark is activated\n")}{subrepos %
409 > "HG: subrepo '{subrepo}' is changed\n"}
409 > "HG: subrepo '{subrepo}' is changed\n"}
410 > EOF
410 > EOF
411
411
412 $ hg init sub2
412 $ hg init sub2
413 $ echo a > sub2/a
413 $ echo a > sub2/a
414 $ hg -R sub2 add sub2/a
414 $ hg -R sub2 add sub2/a
415 $ echo 'sub2 = sub2' >> .hgsub
415 $ echo 'sub2 = sub2' >> .hgsub
416
416
417 $ HGEDITOR=cat hg commit -S -q
417 $ HGEDITOR=cat hg commit -S -q
418 HG: this is "commit.normal" template
418 HG: this is "commit.normal" template
419 HG: Leave message empty to abort commit.
419 HG: Leave message empty to abort commit.
420 HG: bookmark 'activebookmark' is activated
420 HG: bookmark 'activebookmark' is activated
421 HG: subrepo 'sub' is changed
421 HG: subrepo 'sub' is changed
422 HG: subrepo 'sub2' is changed
422 HG: subrepo 'sub2' is changed
423 abort: empty commit message
423 abort: empty commit message
424 [255]
424 [255]
425
425
426 $ cat >> .hg/hgrc <<EOF
426 $ cat >> .hg/hgrc <<EOF
427 > [committemplate]
427 > [committemplate]
428 > changeset.commit.normal =
428 > changeset.commit.normal =
429 > # now, "changeset.commit" should be chosen for "hg commit"
429 > # now, "changeset.commit" should be chosen for "hg commit"
430 > EOF
430 > EOF
431
431
432 $ hg bookmark --inactive activebookmark
432 $ hg bookmark --inactive activebookmark
433 $ hg forget .hgsub
433 $ hg forget .hgsub
434 $ HGEDITOR=cat hg commit -q
434 $ HGEDITOR=cat hg commit -q
435 HG: this is "commit" template
435 HG: this is "commit" template
436 HG: Leave message empty to abort commit.
436 HG: Leave message empty to abort commit.
437 HG: no bookmark is activated
437 HG: no bookmark is activated
438 abort: empty commit message
438 abort: empty commit message
439 [255]
439 [255]
440
440
441 $ cat >> .hg/hgrc <<EOF
441 $ cat >> .hg/hgrc <<EOF
442 > [committemplate]
442 > [committemplate]
443 > changeset.commit =
443 > changeset.commit =
444 > # now, "changeset" should be chosen for "hg commit"
444 > # now, "changeset" should be chosen for "hg commit"
445 > EOF
445 > EOF
446
446
447 $ HGEDITOR=cat hg commit -q
447 $ HGEDITOR=cat hg commit -q
448 HG: this is customized commit template
448 HG: this is customized commit template
449 HG: Leave message empty to abort commit.
449 HG: Leave message empty to abort commit.
450 HG: no bookmark is activated
450 HG: no bookmark is activated
451 abort: empty commit message
451 abort: empty commit message
452 [255]
452 [255]
453
453
454 $ cat >> .hg/hgrc <<EOF
454 $ cat >> .hg/hgrc <<EOF
455 > [committemplate]
455 > [committemplate]
456 > changeset = {desc}
456 > changeset = {desc}
457 > HG: mods={file_mods}
457 > HG: mods={file_mods}
458 > HG: adds={file_adds}
458 > HG: adds={file_adds}
459 > HG: dels={file_dels}
459 > HG: dels={file_dels}
460 > HG: files={files}
460 > HG: files={files}
461 > HG:
461 > HG:
462 > {splitlines(diff()) % 'HG: {line}\n'
462 > {splitlines(diff()) % 'HG: {line}\n'
463 > }HG:
463 > }HG:
464 > HG: mods={file_mods}
464 > HG: mods={file_mods}
465 > HG: adds={file_adds}
465 > HG: adds={file_adds}
466 > HG: dels={file_dels}
466 > HG: dels={file_dels}
467 > HG: files={files}\n
467 > HG: files={files}\n
468 > EOF
468 > EOF
469 $ hg status -amr
469 $ hg status -amr
470 M changed
470 M changed
471 A added
471 A added
472 R removed
472 R removed
473 $ HGEDITOR=cat hg commit -q -e -m "foo bar" changed
473 $ HGEDITOR=cat hg commit -q -e -m "foo bar" changed
474 foo bar
474 foo bar
475 HG: mods=changed
475 HG: mods=changed
476 HG: adds=
476 HG: adds=
477 HG: dels=
477 HG: dels=
478 HG: files=changed
478 HG: files=changed
479 HG:
479 HG:
480 HG: --- a/changed Thu Jan 01 00:00:00 1970 +0000
480 HG: --- a/changed Thu Jan 01 00:00:00 1970 +0000
481 HG: +++ b/changed Thu Jan 01 00:00:00 1970 +0000
481 HG: +++ b/changed Thu Jan 01 00:00:00 1970 +0000
482 HG: @@ -1,1 +1,2 @@
482 HG: @@ -1,1 +1,2 @@
483 HG: changed
483 HG: changed
484 HG: +changed
484 HG: +changed
485 HG:
485 HG:
486 HG: mods=changed
486 HG: mods=changed
487 HG: adds=
487 HG: adds=
488 HG: dels=
488 HG: dels=
489 HG: files=changed
489 HG: files=changed
490 $ hg status -amr
490 $ hg status -amr
491 A added
491 A added
492 R removed
492 R removed
493 $ hg parents --template "M {file_mods}\nA {file_adds}\nR {file_dels}\n"
493 $ hg parents --template "M {file_mods}\nA {file_adds}\nR {file_dels}\n"
494 M changed
494 M changed
495 A
495 A
496 R
496 R
497 $ hg rollback -q
497 $ hg rollback -q
498
498
499 $ cat >> .hg/hgrc <<EOF
499 $ cat >> .hg/hgrc <<EOF
500 > [committemplate]
500 > [committemplate]
501 > changeset = {desc}
501 > changeset = {desc}
502 > HG: mods={file_mods}
502 > HG: mods={file_mods}
503 > HG: adds={file_adds}
503 > HG: adds={file_adds}
504 > HG: dels={file_dels}
504 > HG: dels={file_dels}
505 > HG: files={files}
505 > HG: files={files}
506 > HG:
506 > HG:
507 > {splitlines(diff("changed")) % 'HG: {line}\n'
507 > {splitlines(diff("changed")) % 'HG: {line}\n'
508 > }HG:
508 > }HG:
509 > HG: mods={file_mods}
509 > HG: mods={file_mods}
510 > HG: adds={file_adds}
510 > HG: adds={file_adds}
511 > HG: dels={file_dels}
511 > HG: dels={file_dels}
512 > HG: files={files}
512 > HG: files={files}
513 > HG:
513 > HG:
514 > {splitlines(diff("added")) % 'HG: {line}\n'
514 > {splitlines(diff("added")) % 'HG: {line}\n'
515 > }HG:
515 > }HG:
516 > HG: mods={file_mods}
516 > HG: mods={file_mods}
517 > HG: adds={file_adds}
517 > HG: adds={file_adds}
518 > HG: dels={file_dels}
518 > HG: dels={file_dels}
519 > HG: files={files}
519 > HG: files={files}
520 > HG:
520 > HG:
521 > {splitlines(diff("removed")) % 'HG: {line}\n'
521 > {splitlines(diff("removed")) % 'HG: {line}\n'
522 > }HG:
522 > }HG:
523 > HG: mods={file_mods}
523 > HG: mods={file_mods}
524 > HG: adds={file_adds}
524 > HG: adds={file_adds}
525 > HG: dels={file_dels}
525 > HG: dels={file_dels}
526 > HG: files={files}\n
526 > HG: files={files}\n
527 > EOF
527 > EOF
528 $ HGEDITOR=cat hg commit -q -e -m "foo bar" added removed
528 $ HGEDITOR=cat hg commit -q -e -m "foo bar" added removed
529 foo bar
529 foo bar
530 HG: mods=
530 HG: mods=
531 HG: adds=added
531 HG: adds=added
532 HG: dels=removed
532 HG: dels=removed
533 HG: files=added removed
533 HG: files=added removed
534 HG:
534 HG:
535 HG:
535 HG:
536 HG: mods=
536 HG: mods=
537 HG: adds=added
537 HG: adds=added
538 HG: dels=removed
538 HG: dels=removed
539 HG: files=added removed
539 HG: files=added removed
540 HG:
540 HG:
541 HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000
541 HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000
542 HG: +++ b/added Thu Jan 01 00:00:00 1970 +0000
542 HG: +++ b/added Thu Jan 01 00:00:00 1970 +0000
543 HG: @@ -0,0 +1,1 @@
543 HG: @@ -0,0 +1,1 @@
544 HG: +added
544 HG: +added
545 HG:
545 HG:
546 HG: mods=
546 HG: mods=
547 HG: adds=added
547 HG: adds=added
548 HG: dels=removed
548 HG: dels=removed
549 HG: files=added removed
549 HG: files=added removed
550 HG:
550 HG:
551 HG: --- a/removed Thu Jan 01 00:00:00 1970 +0000
551 HG: --- a/removed Thu Jan 01 00:00:00 1970 +0000
552 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
552 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
553 HG: @@ -1,1 +0,0 @@
553 HG: @@ -1,1 +0,0 @@
554 HG: -removed
554 HG: -removed
555 HG:
555 HG:
556 HG: mods=
556 HG: mods=
557 HG: adds=added
557 HG: adds=added
558 HG: dels=removed
558 HG: dels=removed
559 HG: files=added removed
559 HG: files=added removed
560 $ hg status -amr
560 $ hg status -amr
561 M changed
561 M changed
562 $ hg parents --template "M {file_mods}\nA {file_adds}\nR {file_dels}\n"
562 $ hg parents --template "M {file_mods}\nA {file_adds}\nR {file_dels}\n"
563 M
563 M
564 A added
564 A added
565 R removed
565 R removed
566 $ hg rollback -q
566 $ hg rollback -q
567
567
568 $ cat >> .hg/hgrc <<EOF
568 $ cat >> .hg/hgrc <<EOF
569 > # disable customizing for subsequent tests
569 > # disable customizing for subsequent tests
570 > [committemplate]
570 > [committemplate]
571 > changeset =
571 > changeset =
572 > EOF
572 > EOF
573
573
574 $ cd ..
574 $ cd ..
575
575
576
576
577 commit copy
577 commit copy
578
578
579 $ hg init dir2
579 $ hg init dir2
580 $ cd dir2
580 $ cd dir2
581 $ echo bleh > bar
581 $ echo bleh > bar
582 $ hg add bar
582 $ hg add bar
583 $ hg ci -m 'add bar'
583 $ hg ci -m 'add bar'
584
584
585 $ hg cp bar foo
585 $ hg cp bar foo
586 $ echo >> bar
586 $ echo >> bar
587 $ hg ci -m 'cp bar foo; change bar'
587 $ hg ci -m 'cp bar foo; change bar'
588
588
589 $ hg debugrename foo
589 $ hg debugrename foo
590 foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9
590 foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9
591 $ hg debugindex bar
591 $ hg debugindex bar
592 rev offset length ..... linkrev nodeid p1 p2 (re)
592 rev offset length ..... linkrev nodeid p1 p2 (re)
593 0 0 6 ..... 0 26d3ca0dfd18 000000000000 000000000000 (re)
593 0 0 6 ..... 0 26d3ca0dfd18 000000000000 000000000000 (re)
594 1 6 7 ..... 1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
594 1 6 7 ..... 1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
595
595
596 Test making empty commits
596 Test making empty commits
597 $ hg commit --config ui.allowemptycommit=True -m "empty commit"
597 $ hg commit --config ui.allowemptycommit=True -m "empty commit"
598 $ hg log -r . -v --stat
598 $ hg log -r . -v --stat
599 changeset: 2:d809f3644287
599 changeset: 2:d809f3644287
600 tag: tip
600 tag: tip
601 user: test
601 user: test
602 date: Thu Jan 01 00:00:00 1970 +0000
602 date: Thu Jan 01 00:00:00 1970 +0000
603 description:
603 description:
604 empty commit
604 empty commit
605
605
606
606
607
607
608 verify pathauditor blocks evil filepaths
608 verify pathauditor blocks evil filepaths
609 $ cat > evil-commit.py <<EOF
609 $ cat > evil-commit.py <<EOF
610 > from mercurial import ui, hg, context, node
610 > from mercurial import ui, hg, context, node
611 > notrc = u".h\u200cg".encode('utf-8') + '/hgrc'
611 > notrc = u".h\u200cg".encode('utf-8') + '/hgrc'
612 > u = ui.ui.load()
612 > u = ui.ui.load()
613 > r = hg.repository(u, '.')
613 > r = hg.repository(u, '.')
614 > def filectxfn(repo, memctx, path):
614 > def filectxfn(repo, memctx, path):
615 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
615 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
616 > c = context.memctx(r, [r['tip'].node(), node.nullid],
616 > c = context.memctx(r, [r['tip'].node(), node.nullid],
617 > 'evil', [notrc], filectxfn, 0)
617 > 'evil', [notrc], filectxfn, 0)
618 > r.commitctx(c)
618 > r.commitctx(c)
619 > EOF
619 > EOF
620 $ $PYTHON evil-commit.py
620 $ $PYTHON evil-commit.py
621 #if windows
621 #if windows
622 $ hg co --clean tip
622 $ hg co --clean tip
623 abort: path contains illegal component: .h\xe2\x80\x8cg\\hgrc (esc)
623 abort: path contains illegal component: .h\xe2\x80\x8cg\\hgrc (esc)
624 [255]
624 [255]
625 #else
625 #else
626 $ hg co --clean tip
626 $ hg co --clean tip
627 abort: path contains illegal component: .h\xe2\x80\x8cg/hgrc (esc)
627 abort: path contains illegal component: .h\xe2\x80\x8cg/hgrc (esc)
628 [255]
628 [255]
629 #endif
629 #endif
630
630
631 $ hg rollback -f
631 $ hg rollback -f
632 repository tip rolled back to revision 2 (undo commit)
632 repository tip rolled back to revision 2 (undo commit)
633 $ cat > evil-commit.py <<EOF
633 $ cat > evil-commit.py <<EOF
634 > from mercurial import ui, hg, context, node
634 > from mercurial import ui, hg, context, node
635 > notrc = "HG~1/hgrc"
635 > notrc = "HG~1/hgrc"
636 > u = ui.ui.load()
636 > u = ui.ui.load()
637 > r = hg.repository(u, '.')
637 > r = hg.repository(u, '.')
638 > def filectxfn(repo, memctx, path):
638 > def filectxfn(repo, memctx, path):
639 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
639 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
640 > c = context.memctx(r, [r['tip'].node(), node.nullid],
640 > c = context.memctx(r, [r['tip'].node(), node.nullid],
641 > 'evil', [notrc], filectxfn, 0)
641 > 'evil', [notrc], filectxfn, 0)
642 > r.commitctx(c)
642 > r.commitctx(c)
643 > EOF
643 > EOF
644 $ $PYTHON evil-commit.py
644 $ $PYTHON evil-commit.py
645 $ hg co --clean tip
645 $ hg co --clean tip
646 abort: path contains illegal component: HG~1/hgrc (glob)
646 abort: path contains illegal component: HG~1/hgrc (glob)
647 [255]
647 [255]
648
648
649 $ hg rollback -f
649 $ hg rollback -f
650 repository tip rolled back to revision 2 (undo commit)
650 repository tip rolled back to revision 2 (undo commit)
651 $ cat > evil-commit.py <<EOF
651 $ cat > evil-commit.py <<EOF
652 > from mercurial import ui, hg, context, node
652 > from mercurial import ui, hg, context, node
653 > notrc = "HG8B6C~2/hgrc"
653 > notrc = "HG8B6C~2/hgrc"
654 > u = ui.ui.load()
654 > u = ui.ui.load()
655 > r = hg.repository(u, '.')
655 > r = hg.repository(u, '.')
656 > def filectxfn(repo, memctx, path):
656 > def filectxfn(repo, memctx, path):
657 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
657 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
658 > c = context.memctx(r, [r['tip'].node(), node.nullid],
658 > c = context.memctx(r, [r['tip'].node(), node.nullid],
659 > 'evil', [notrc], filectxfn, 0)
659 > 'evil', [notrc], filectxfn, 0)
660 > r.commitctx(c)
660 > r.commitctx(c)
661 > EOF
661 > EOF
662 $ $PYTHON evil-commit.py
662 $ $PYTHON evil-commit.py
663 $ hg co --clean tip
663 $ hg co --clean tip
664 abort: path contains illegal component: HG8B6C~2/hgrc (glob)
664 abort: path contains illegal component: HG8B6C~2/hgrc (glob)
665 [255]
665 [255]
666
666
667 # test that an unmodified commit template message aborts
667 # test that an unmodified commit template message aborts
668
668
669 $ hg init unmodified_commit_template
669 $ hg init unmodified_commit_template
670 $ cd unmodified_commit_template
670 $ cd unmodified_commit_template
671 $ echo foo > foo
671 $ echo foo > foo
672 $ hg add foo
672 $ hg add foo
673 $ hg commit -m "foo"
673 $ hg commit -m "foo"
674 $ cat >> .hg/hgrc <<EOF
674 $ cat >> .hg/hgrc <<EOF
675 > [committemplate]
675 > [committemplate]
676 > changeset.commit = HI THIS IS NOT STRIPPED
676 > changeset.commit = HI THIS IS NOT STRIPPED
677 > HG: this is customized commit template
677 > HG: this is customized commit template
678 > HG: {extramsg}
678 > HG: {extramsg}
679 > {if(activebookmark,
679 > {if(activebookmark,
680 > "HG: bookmark '{activebookmark}' is activated\n",
680 > "HG: bookmark '{activebookmark}' is activated\n",
681 > "HG: no bookmark is activated\n")}{subrepos %
681 > "HG: no bookmark is activated\n")}{subrepos %
682 > "HG: subrepo '{subrepo}' is changed\n"}
682 > "HG: subrepo '{subrepo}' is changed\n"}
683 > EOF
683 > EOF
684 $ cat > $TESTTMP/notouching.sh <<EOF
684 $ cat > $TESTTMP/notouching.sh <<EOF
685 > true
685 > true
686 > EOF
686 > EOF
687 $ echo foo2 > foo2
687 $ echo foo2 > foo2
688 $ hg add foo2
688 $ hg add foo2
689 $ HGEDITOR="sh $TESTTMP/notouching.sh" hg commit
689 $ HGEDITOR="sh $TESTTMP/notouching.sh" hg commit
690 abort: commit message unchanged
690 abort: commit message unchanged
691 [255]
691 [255]
692
692
693 test that text below the --- >8 --- special string is ignored
693 test that text below the --- >8 --- special string is ignored
694
694
695 $ cat <<'EOF' > $TESTTMP/lowercaseline.sh
695 $ cat <<'EOF' > $TESTTMP/lowercaseline.sh
696 > cat $1 | sed s/LINE/line/ | tee $1.new
696 > cat $1 | sed s/LINE/line/ | tee $1.new
697 > mv $1.new $1
697 > mv $1.new $1
698 > EOF
698 > EOF
699
699
700 $ hg init ignore_below_special_string
700 $ hg init ignore_below_special_string
701 $ cd ignore_below_special_string
701 $ cd ignore_below_special_string
702 $ echo foo > foo
702 $ echo foo > foo
703 $ hg add foo
703 $ hg add foo
704 $ hg commit -m "foo"
704 $ hg commit -m "foo"
705 $ cat >> .hg/hgrc <<EOF
705 $ cat >> .hg/hgrc <<EOF
706 > [committemplate]
706 > [committemplate]
707 > changeset.commit = first LINE
707 > changeset.commit = first LINE
708 > HG: this is customized commit template
708 > HG: this is customized commit template
709 > HG: {extramsg}
709 > HG: {extramsg}
710 > HG: ------------------------ >8 ------------------------
710 > HG: ------------------------ >8 ------------------------
711 > {diff()}
711 > {diff()}
712 > EOF
712 > EOF
713 $ echo foo2 > foo2
713 $ echo foo2 > foo2
714 $ hg add foo2
714 $ hg add foo2
715 $ HGEDITOR="sh $TESTTMP/notouching.sh" hg ci
715 $ HGEDITOR="sh $TESTTMP/notouching.sh" hg ci
716 abort: commit message unchanged
716 abort: commit message unchanged
717 [255]
717 [255]
718 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
718 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
719 first line
719 first line
720 HG: this is customized commit template
720 HG: this is customized commit template
721 HG: Leave message empty to abort commit.
721 HG: Leave message empty to abort commit.
722 HG: ------------------------ >8 ------------------------
722 HG: ------------------------ >8 ------------------------
723 diff -r e63c23eaa88a foo2
723 diff -r e63c23eaa88a foo2
724 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
724 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
725 +++ b/foo2 Thu Jan 01 00:00:00 1970 +0000
725 +++ b/foo2 Thu Jan 01 00:00:00 1970 +0000
726 @@ -0,0 +1,1 @@
726 @@ -0,0 +1,1 @@
727 +foo2
727 +foo2
728 $ hg log -T '{desc}\n' -r .
728 $ hg log -T '{desc}\n' -r .
729 first line
729 first line
730
730
731 test that the special string --- >8 --- isn't used when not at the beginning of
731 test that the special string --- >8 --- isn't used when not at the beginning of
732 a line
732 a line
733
733
734 $ cat >> .hg/hgrc <<EOF
734 $ cat >> .hg/hgrc <<EOF
735 > [committemplate]
735 > [committemplate]
736 > changeset.commit = first LINE2
736 > changeset.commit = first LINE2
737 > another line HG: ------------------------ >8 ------------------------
737 > another line HG: ------------------------ >8 ------------------------
738 > HG: this is customized commit template
738 > HG: this is customized commit template
739 > HG: {extramsg}
739 > HG: {extramsg}
740 > HG: ------------------------ >8 ------------------------
740 > HG: ------------------------ >8 ------------------------
741 > {diff()}
741 > {diff()}
742 > EOF
742 > EOF
743 $ echo foo >> foo
743 $ echo foo >> foo
744 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
744 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
745 first line2
745 first line2
746 another line HG: ------------------------ >8 ------------------------
746 another line HG: ------------------------ >8 ------------------------
747 HG: this is customized commit template
747 HG: this is customized commit template
748 HG: Leave message empty to abort commit.
748 HG: Leave message empty to abort commit.
749 HG: ------------------------ >8 ------------------------
749 HG: ------------------------ >8 ------------------------
750 diff -r 3661b22b0702 foo
750 diff -r 3661b22b0702 foo
751 --- a/foo Thu Jan 01 00:00:00 1970 +0000
751 --- a/foo Thu Jan 01 00:00:00 1970 +0000
752 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
752 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
753 @@ -1,1 +1,2 @@
753 @@ -1,1 +1,2 @@
754 foo
754 foo
755 +foo
755 +foo
756 $ hg log -T '{desc}\n' -r .
756 $ hg log -T '{desc}\n' -r .
757 first line2
757 first line2
758 another line HG: ------------------------ >8 ------------------------
758 another line HG: ------------------------ >8 ------------------------
759
759
760 also test that this special string isn't accepted when there is some extra text
760 also test that this special string isn't accepted when there is some extra text
761 at the end
761 at the end
762
762
763 $ cat >> .hg/hgrc <<EOF
763 $ cat >> .hg/hgrc <<EOF
764 > [committemplate]
764 > [committemplate]
765 > changeset.commit = first LINE3
765 > changeset.commit = first LINE3
766 > HG: ------------------------ >8 ------------------------foobar
766 > HG: ------------------------ >8 ------------------------foobar
767 > second line
767 > second line
768 > HG: this is customized commit template
768 > HG: this is customized commit template
769 > HG: {extramsg}
769 > HG: {extramsg}
770 > HG: ------------------------ >8 ------------------------
770 > HG: ------------------------ >8 ------------------------
771 > {diff()}
771 > {diff()}
772 > EOF
772 > EOF
773 $ echo foo >> foo
773 $ echo foo >> foo
774 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
774 $ HGEDITOR="sh $TESTTMP/lowercaseline.sh" hg ci
775 first line3
775 first line3
776 HG: ------------------------ >8 ------------------------foobar
776 HG: ------------------------ >8 ------------------------foobar
777 second line
777 second line
778 HG: this is customized commit template
778 HG: this is customized commit template
779 HG: Leave message empty to abort commit.
779 HG: Leave message empty to abort commit.
780 HG: ------------------------ >8 ------------------------
780 HG: ------------------------ >8 ------------------------
781 diff -r ce648f5f066f foo
781 diff -r ce648f5f066f foo
782 --- a/foo Thu Jan 01 00:00:00 1970 +0000
782 --- a/foo Thu Jan 01 00:00:00 1970 +0000
783 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
783 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
784 @@ -1,2 +1,3 @@
784 @@ -1,2 +1,3 @@
785 foo
785 foo
786 foo
786 foo
787 +foo
787 +foo
788 $ hg log -T '{desc}\n' -r .
788 $ hg log -T '{desc}\n' -r .
789 first line3
789 first line3
790 second line
790 second line
791
791
792 $ cd ..
792 $ cd ..
793
793
@@ -1,3462 +1,3462 b''
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71
71
72 $ commit()
72 $ commit()
73 > {
73 > {
74 > rev=$1
74 > rev=$1
75 > msg=$2
75 > msg=$2
76 > shift 2
76 > shift 2
77 > if [ "$#" -gt 0 ]; then
77 > if [ "$#" -gt 0 ]; then
78 > hg debugsetparents "$@"
78 > hg debugsetparents "$@"
79 > fi
79 > fi
80 > echo $rev > a
80 > echo $rev > a
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revsetlang, commands, cmdutil
85 > from mercurial import extensions, revsetlang, commands, cmdutil
86 >
86 >
87 > def uisetup(ui):
87 > def uisetup(ui):
88 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
89 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 > if expr:
91 > if expr:
92 > tree = revsetlang.parse(expr)
92 > tree = revsetlang.parse(expr)
93 > else:
93 > else:
94 > tree = []
94 > tree = []
95 > ui.write('%r\n' % (opts.get('rev', []),))
95 > ui.write('%r\n' % (opts.get('rev', []),))
96 > ui.write(revsetlang.prettyformat(tree) + '\n')
96 > ui.write(revsetlang.prettyformat(tree) + '\n')
97 > return 0
97 > return 0
98 > return orig(ui, repo, *pats, **opts)
98 > return orig(ui, repo, *pats, **opts)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 > entry[1].append(('', 'print-revset', False,
100 > entry[1].append(('', 'print-revset', False,
101 > 'print generated revset and exit (DEPRECATED)'))
101 > 'print generated revset and exit (DEPRECATED)'))
102 > EOF
102 > EOF
103
103
104 $ echo "[extensions]" >> $HGRCPATH
104 $ echo "[extensions]" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106
106
107 $ hg init repo
107 $ hg init repo
108 $ cd repo
108 $ cd repo
109
109
110 Empty repo:
110 Empty repo:
111
111
112 $ hg log -G
112 $ hg log -G
113
113
114
114
115 Building DAG:
115 Building DAG:
116
116
117 $ commit 0 "root"
117 $ commit 0 "root"
118 $ commit 1 "collapse" 0
118 $ commit 1 "collapse" 0
119 $ commit 2 "collapse" 1
119 $ commit 2 "collapse" 1
120 $ commit 3 "collapse" 2
120 $ commit 3 "collapse" 2
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 $ commit 5 "expand" 3 4
122 $ commit 5 "expand" 3 4
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 $ commit 7 "expand" 2 5
124 $ commit 7 "expand" 2 5
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 $ commit 9 "expand" 7 8
126 $ commit 9 "expand" 7 8
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 $ commit 11 "expand" 6 10
128 $ commit 11 "expand" 6 10
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 $ commit 13 "expand" 9 11
130 $ commit 13 "expand" 9 11
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 $ commit 15 "expand" 13 14
132 $ commit 15 "expand" 13 14
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 $ commit 17 "expand" 12 16
134 $ commit 17 "expand" 12 16
135 $ commit 18 "merge two known; two far left" 1 15
135 $ commit 18 "merge two known; two far left" 1 15
136 $ commit 19 "expand" 15 17
136 $ commit 19 "expand" 15 17
137 $ commit 20 "merge two known; two far right" 0 18
137 $ commit 20 "merge two known; two far right" 0 18
138 $ commit 21 "expand" 19 20
138 $ commit 21 "expand" 19 20
139 $ commit 22 "merge two known; one far left, one far right" 18 21
139 $ commit 22 "merge two known; one far left, one far right" 18 21
140 $ commit 23 "merge one known; immediate left" 1 22
140 $ commit 23 "merge one known; immediate left" 1 22
141 $ commit 24 "merge one known; immediate right" 0 23
141 $ commit 24 "merge one known; immediate right" 0 23
142 $ commit 25 "merge one known; far left" 21 24
142 $ commit 25 "merge one known; far left" 21 24
143 $ commit 26 "merge one known; far right" 18 25
143 $ commit 26 "merge one known; far right" 18 25
144 $ commit 27 "collapse" 21
144 $ commit 27 "collapse" 21
145 $ commit 28 "merge zero known" 1 26
145 $ commit 28 "merge zero known" 1 26
146 $ commit 29 "regular commit" 0
146 $ commit 29 "regular commit" 0
147 $ commit 30 "expand" 28 29
147 $ commit 30 "expand" 28 29
148 $ commit 31 "expand" 21 30
148 $ commit 31 "expand" 21 30
149 $ commit 32 "expand" 27 31
149 $ commit 32 "expand" 27 31
150 $ commit 33 "head" 18
150 $ commit 33 "head" 18
151 $ commit 34 "head" 32
151 $ commit 34 "head" 32
152
152
153
153
154 $ hg log -G -q
154 $ hg log -G -q
155 @ 34:fea3ac5810e0
155 @ 34:fea3ac5810e0
156 |
156 |
157 | o 33:68608f5145f9
157 | o 33:68608f5145f9
158 | |
158 | |
159 o | 32:d06dffa21a31
159 o | 32:d06dffa21a31
160 |\ \
160 |\ \
161 | o \ 31:621d83e11f67
161 | o \ 31:621d83e11f67
162 | |\ \
162 | |\ \
163 | | o \ 30:6e11cd4b648f
163 | | o \ 30:6e11cd4b648f
164 | | |\ \
164 | | |\ \
165 | | | o | 29:cd9bb2be7593
165 | | | o | 29:cd9bb2be7593
166 | | | | |
166 | | | | |
167 | | o | | 28:44ecd0b9ae99
167 | | o | | 28:44ecd0b9ae99
168 | | |\ \ \
168 | | |\ \ \
169 o | | | | | 27:886ed638191b
169 o | | | | | 27:886ed638191b
170 |/ / / / /
170 |/ / / / /
171 | | o---+ 26:7f25b6c2f0b9
171 | | o---+ 26:7f25b6c2f0b9
172 | | | | |
172 | | | | |
173 +---o | | 25:91da8ed57247
173 +---o | | 25:91da8ed57247
174 | | | | |
174 | | | | |
175 | | o | | 24:a9c19a3d96b7
175 | | o | | 24:a9c19a3d96b7
176 | | |\| |
176 | | |\| |
177 | | o | | 23:a01cddf0766d
177 | | o | | 23:a01cddf0766d
178 | |/| | |
178 | |/| | |
179 +---o---+ 22:e0d9cccacb5d
179 +---o---+ 22:e0d9cccacb5d
180 | | / /
180 | | / /
181 o | | | 21:d42a756af44d
181 o | | | 21:d42a756af44d
182 |\ \ \ \
182 |\ \ \ \
183 | o---+-+ 20:d30ed6450e32
183 | o---+-+ 20:d30ed6450e32
184 | / / /
184 | / / /
185 o | | | 19:31ddc2c1573b
185 o | | | 19:31ddc2c1573b
186 |\ \ \ \
186 |\ \ \ \
187 +---+---o 18:1aa84d96232a
187 +---+---o 18:1aa84d96232a
188 | | | |
188 | | | |
189 | o | | 17:44765d7c06e0
189 | o | | 17:44765d7c06e0
190 | |\ \ \
190 | |\ \ \
191 | | o---+ 16:3677d192927d
191 | | o---+ 16:3677d192927d
192 | | |/ /
192 | | |/ /
193 o | | | 15:1dda3f72782d
193 o | | | 15:1dda3f72782d
194 |\ \ \ \
194 |\ \ \ \
195 | o-----+ 14:8eac370358ef
195 | o-----+ 14:8eac370358ef
196 | |/ / /
196 | |/ / /
197 o | | | 13:22d8966a97e3
197 o | | | 13:22d8966a97e3
198 |\ \ \ \
198 |\ \ \ \
199 +---o | | 12:86b91144a6e9
199 +---o | | 12:86b91144a6e9
200 | | |/ /
200 | | |/ /
201 | o | | 11:832d76e6bdf2
201 | o | | 11:832d76e6bdf2
202 | |\ \ \
202 | |\ \ \
203 | | o---+ 10:74c64d036d72
203 | | o---+ 10:74c64d036d72
204 | |/ / /
204 | |/ / /
205 o | | | 9:7010c0af0a35
205 o | | | 9:7010c0af0a35
206 |\ \ \ \
206 |\ \ \ \
207 | o-----+ 8:7a0b11f71937
207 | o-----+ 8:7a0b11f71937
208 |/ / / /
208 |/ / / /
209 o | | | 7:b632bb1b1224
209 o | | | 7:b632bb1b1224
210 |\ \ \ \
210 |\ \ \ \
211 +---o | | 6:b105a072e251
211 +---o | | 6:b105a072e251
212 | |/ / /
212 | |/ / /
213 | o | | 5:4409d547b708
213 | o | | 5:4409d547b708
214 | |\ \ \
214 | |\ \ \
215 | | o | | 4:26a8bac39d9f
215 | | o | | 4:26a8bac39d9f
216 | |/|/ /
216 | |/|/ /
217 | o / / 3:27eef8ed80b4
217 | o / / 3:27eef8ed80b4
218 |/ / /
218 |/ / /
219 o / / 2:3d9a33b8d1e1
219 o / / 2:3d9a33b8d1e1
220 |/ /
220 |/ /
221 o / 1:6db2ef61d156
221 o / 1:6db2ef61d156
222 |/
222 |/
223 o 0:e6eb3150255d
223 o 0:e6eb3150255d
224
224
225
225
226 $ hg log -G
226 $ hg log -G
227 @ changeset: 34:fea3ac5810e0
227 @ changeset: 34:fea3ac5810e0
228 | tag: tip
228 | tag: tip
229 | parent: 32:d06dffa21a31
229 | parent: 32:d06dffa21a31
230 | user: test
230 | user: test
231 | date: Thu Jan 01 00:00:34 1970 +0000
231 | date: Thu Jan 01 00:00:34 1970 +0000
232 | summary: (34) head
232 | summary: (34) head
233 |
233 |
234 | o changeset: 33:68608f5145f9
234 | o changeset: 33:68608f5145f9
235 | | parent: 18:1aa84d96232a
235 | | parent: 18:1aa84d96232a
236 | | user: test
236 | | user: test
237 | | date: Thu Jan 01 00:00:33 1970 +0000
237 | | date: Thu Jan 01 00:00:33 1970 +0000
238 | | summary: (33) head
238 | | summary: (33) head
239 | |
239 | |
240 o | changeset: 32:d06dffa21a31
240 o | changeset: 32:d06dffa21a31
241 |\ \ parent: 27:886ed638191b
241 |\ \ parent: 27:886ed638191b
242 | | | parent: 31:621d83e11f67
242 | | | parent: 31:621d83e11f67
243 | | | user: test
243 | | | user: test
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 | | | summary: (32) expand
245 | | | summary: (32) expand
246 | | |
246 | | |
247 | o | changeset: 31:621d83e11f67
247 | o | changeset: 31:621d83e11f67
248 | |\ \ parent: 21:d42a756af44d
248 | |\ \ parent: 21:d42a756af44d
249 | | | | parent: 30:6e11cd4b648f
249 | | | | parent: 30:6e11cd4b648f
250 | | | | user: test
250 | | | | user: test
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 | | | | summary: (31) expand
252 | | | | summary: (31) expand
253 | | | |
253 | | | |
254 | | o | changeset: 30:6e11cd4b648f
254 | | o | changeset: 30:6e11cd4b648f
255 | | |\ \ parent: 28:44ecd0b9ae99
255 | | |\ \ parent: 28:44ecd0b9ae99
256 | | | | | parent: 29:cd9bb2be7593
256 | | | | | parent: 29:cd9bb2be7593
257 | | | | | user: test
257 | | | | | user: test
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 | | | | | summary: (30) expand
259 | | | | | summary: (30) expand
260 | | | | |
260 | | | | |
261 | | | o | changeset: 29:cd9bb2be7593
261 | | | o | changeset: 29:cd9bb2be7593
262 | | | | | parent: 0:e6eb3150255d
262 | | | | | parent: 0:e6eb3150255d
263 | | | | | user: test
263 | | | | | user: test
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 | | | | | summary: (29) regular commit
265 | | | | | summary: (29) regular commit
266 | | | | |
266 | | | | |
267 | | o | | changeset: 28:44ecd0b9ae99
267 | | o | | changeset: 28:44ecd0b9ae99
268 | | |\ \ \ parent: 1:6db2ef61d156
268 | | |\ \ \ parent: 1:6db2ef61d156
269 | | | | | | parent: 26:7f25b6c2f0b9
269 | | | | | | parent: 26:7f25b6c2f0b9
270 | | | | | | user: test
270 | | | | | | user: test
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 | | | | | | summary: (28) merge zero known
272 | | | | | | summary: (28) merge zero known
273 | | | | | |
273 | | | | | |
274 o | | | | | changeset: 27:886ed638191b
274 o | | | | | changeset: 27:886ed638191b
275 |/ / / / / parent: 21:d42a756af44d
275 |/ / / / / parent: 21:d42a756af44d
276 | | | | | user: test
276 | | | | | user: test
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 | | | | | summary: (27) collapse
278 | | | | | summary: (27) collapse
279 | | | | |
279 | | | | |
280 | | o---+ changeset: 26:7f25b6c2f0b9
280 | | o---+ changeset: 26:7f25b6c2f0b9
281 | | | | | parent: 18:1aa84d96232a
281 | | | | | parent: 18:1aa84d96232a
282 | | | | | parent: 25:91da8ed57247
282 | | | | | parent: 25:91da8ed57247
283 | | | | | user: test
283 | | | | | user: test
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 | | | | | summary: (26) merge one known; far right
285 | | | | | summary: (26) merge one known; far right
286 | | | | |
286 | | | | |
287 +---o | | changeset: 25:91da8ed57247
287 +---o | | changeset: 25:91da8ed57247
288 | | | | | parent: 21:d42a756af44d
288 | | | | | parent: 21:d42a756af44d
289 | | | | | parent: 24:a9c19a3d96b7
289 | | | | | parent: 24:a9c19a3d96b7
290 | | | | | user: test
290 | | | | | user: test
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 | | | | | summary: (25) merge one known; far left
292 | | | | | summary: (25) merge one known; far left
293 | | | | |
293 | | | | |
294 | | o | | changeset: 24:a9c19a3d96b7
294 | | o | | changeset: 24:a9c19a3d96b7
295 | | |\| | parent: 0:e6eb3150255d
295 | | |\| | parent: 0:e6eb3150255d
296 | | | | | parent: 23:a01cddf0766d
296 | | | | | parent: 23:a01cddf0766d
297 | | | | | user: test
297 | | | | | user: test
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 | | | | | summary: (24) merge one known; immediate right
299 | | | | | summary: (24) merge one known; immediate right
300 | | | | |
300 | | | | |
301 | | o | | changeset: 23:a01cddf0766d
301 | | o | | changeset: 23:a01cddf0766d
302 | |/| | | parent: 1:6db2ef61d156
302 | |/| | | parent: 1:6db2ef61d156
303 | | | | | parent: 22:e0d9cccacb5d
303 | | | | | parent: 22:e0d9cccacb5d
304 | | | | | user: test
304 | | | | | user: test
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 | | | | | summary: (23) merge one known; immediate left
306 | | | | | summary: (23) merge one known; immediate left
307 | | | | |
307 | | | | |
308 +---o---+ changeset: 22:e0d9cccacb5d
308 +---o---+ changeset: 22:e0d9cccacb5d
309 | | | | parent: 18:1aa84d96232a
309 | | | | parent: 18:1aa84d96232a
310 | | / / parent: 21:d42a756af44d
310 | | / / parent: 21:d42a756af44d
311 | | | | user: test
311 | | | | user: test
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 | | | | summary: (22) merge two known; one far left, one far right
313 | | | | summary: (22) merge two known; one far left, one far right
314 | | | |
314 | | | |
315 o | | | changeset: 21:d42a756af44d
315 o | | | changeset: 21:d42a756af44d
316 |\ \ \ \ parent: 19:31ddc2c1573b
316 |\ \ \ \ parent: 19:31ddc2c1573b
317 | | | | | parent: 20:d30ed6450e32
317 | | | | | parent: 20:d30ed6450e32
318 | | | | | user: test
318 | | | | | user: test
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 | | | | | summary: (21) expand
320 | | | | | summary: (21) expand
321 | | | | |
321 | | | | |
322 | o---+-+ changeset: 20:d30ed6450e32
322 | o---+-+ changeset: 20:d30ed6450e32
323 | | | | parent: 0:e6eb3150255d
323 | | | | parent: 0:e6eb3150255d
324 | / / / parent: 18:1aa84d96232a
324 | / / / parent: 18:1aa84d96232a
325 | | | | user: test
325 | | | | user: test
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 | | | | summary: (20) merge two known; two far right
327 | | | | summary: (20) merge two known; two far right
328 | | | |
328 | | | |
329 o | | | changeset: 19:31ddc2c1573b
329 o | | | changeset: 19:31ddc2c1573b
330 |\ \ \ \ parent: 15:1dda3f72782d
330 |\ \ \ \ parent: 15:1dda3f72782d
331 | | | | | parent: 17:44765d7c06e0
331 | | | | | parent: 17:44765d7c06e0
332 | | | | | user: test
332 | | | | | user: test
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 | | | | | summary: (19) expand
334 | | | | | summary: (19) expand
335 | | | | |
335 | | | | |
336 +---+---o changeset: 18:1aa84d96232a
336 +---+---o changeset: 18:1aa84d96232a
337 | | | | parent: 1:6db2ef61d156
337 | | | | parent: 1:6db2ef61d156
338 | | | | parent: 15:1dda3f72782d
338 | | | | parent: 15:1dda3f72782d
339 | | | | user: test
339 | | | | user: test
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 | | | | summary: (18) merge two known; two far left
341 | | | | summary: (18) merge two known; two far left
342 | | | |
342 | | | |
343 | o | | changeset: 17:44765d7c06e0
343 | o | | changeset: 17:44765d7c06e0
344 | |\ \ \ parent: 12:86b91144a6e9
344 | |\ \ \ parent: 12:86b91144a6e9
345 | | | | | parent: 16:3677d192927d
345 | | | | | parent: 16:3677d192927d
346 | | | | | user: test
346 | | | | | user: test
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 | | | | | summary: (17) expand
348 | | | | | summary: (17) expand
349 | | | | |
349 | | | | |
350 | | o---+ changeset: 16:3677d192927d
350 | | o---+ changeset: 16:3677d192927d
351 | | | | | parent: 0:e6eb3150255d
351 | | | | | parent: 0:e6eb3150255d
352 | | |/ / parent: 1:6db2ef61d156
352 | | |/ / parent: 1:6db2ef61d156
353 | | | | user: test
353 | | | | user: test
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 | | | | summary: (16) merge two known; one immediate right, one near right
355 | | | | summary: (16) merge two known; one immediate right, one near right
356 | | | |
356 | | | |
357 o | | | changeset: 15:1dda3f72782d
357 o | | | changeset: 15:1dda3f72782d
358 |\ \ \ \ parent: 13:22d8966a97e3
358 |\ \ \ \ parent: 13:22d8966a97e3
359 | | | | | parent: 14:8eac370358ef
359 | | | | | parent: 14:8eac370358ef
360 | | | | | user: test
360 | | | | | user: test
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 | | | | | summary: (15) expand
362 | | | | | summary: (15) expand
363 | | | | |
363 | | | | |
364 | o-----+ changeset: 14:8eac370358ef
364 | o-----+ changeset: 14:8eac370358ef
365 | | | | | parent: 0:e6eb3150255d
365 | | | | | parent: 0:e6eb3150255d
366 | |/ / / parent: 12:86b91144a6e9
366 | |/ / / parent: 12:86b91144a6e9
367 | | | | user: test
367 | | | | user: test
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 | | | | summary: (14) merge two known; one immediate right, one far right
369 | | | | summary: (14) merge two known; one immediate right, one far right
370 | | | |
370 | | | |
371 o | | | changeset: 13:22d8966a97e3
371 o | | | changeset: 13:22d8966a97e3
372 |\ \ \ \ parent: 9:7010c0af0a35
372 |\ \ \ \ parent: 9:7010c0af0a35
373 | | | | | parent: 11:832d76e6bdf2
373 | | | | | parent: 11:832d76e6bdf2
374 | | | | | user: test
374 | | | | | user: test
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 | | | | | summary: (13) expand
376 | | | | | summary: (13) expand
377 | | | | |
377 | | | | |
378 +---o | | changeset: 12:86b91144a6e9
378 +---o | | changeset: 12:86b91144a6e9
379 | | |/ / parent: 1:6db2ef61d156
379 | | |/ / parent: 1:6db2ef61d156
380 | | | | parent: 9:7010c0af0a35
380 | | | | parent: 9:7010c0af0a35
381 | | | | user: test
381 | | | | user: test
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 | | | | summary: (12) merge two known; one immediate right, one far left
383 | | | | summary: (12) merge two known; one immediate right, one far left
384 | | | |
384 | | | |
385 | o | | changeset: 11:832d76e6bdf2
385 | o | | changeset: 11:832d76e6bdf2
386 | |\ \ \ parent: 6:b105a072e251
386 | |\ \ \ parent: 6:b105a072e251
387 | | | | | parent: 10:74c64d036d72
387 | | | | | parent: 10:74c64d036d72
388 | | | | | user: test
388 | | | | | user: test
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 | | | | | summary: (11) expand
390 | | | | | summary: (11) expand
391 | | | | |
391 | | | | |
392 | | o---+ changeset: 10:74c64d036d72
392 | | o---+ changeset: 10:74c64d036d72
393 | | | | | parent: 0:e6eb3150255d
393 | | | | | parent: 0:e6eb3150255d
394 | |/ / / parent: 6:b105a072e251
394 | |/ / / parent: 6:b105a072e251
395 | | | | user: test
395 | | | | user: test
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 | | | | summary: (10) merge two known; one immediate left, one near right
397 | | | | summary: (10) merge two known; one immediate left, one near right
398 | | | |
398 | | | |
399 o | | | changeset: 9:7010c0af0a35
399 o | | | changeset: 9:7010c0af0a35
400 |\ \ \ \ parent: 7:b632bb1b1224
400 |\ \ \ \ parent: 7:b632bb1b1224
401 | | | | | parent: 8:7a0b11f71937
401 | | | | | parent: 8:7a0b11f71937
402 | | | | | user: test
402 | | | | | user: test
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 | | | | | summary: (9) expand
404 | | | | | summary: (9) expand
405 | | | | |
405 | | | | |
406 | o-----+ changeset: 8:7a0b11f71937
406 | o-----+ changeset: 8:7a0b11f71937
407 | | | | | parent: 0:e6eb3150255d
407 | | | | | parent: 0:e6eb3150255d
408 |/ / / / parent: 7:b632bb1b1224
408 |/ / / / parent: 7:b632bb1b1224
409 | | | | user: test
409 | | | | user: test
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 | | | | summary: (8) merge two known; one immediate left, one far right
411 | | | | summary: (8) merge two known; one immediate left, one far right
412 | | | |
412 | | | |
413 o | | | changeset: 7:b632bb1b1224
413 o | | | changeset: 7:b632bb1b1224
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 | | | | | parent: 5:4409d547b708
415 | | | | | parent: 5:4409d547b708
416 | | | | | user: test
416 | | | | | user: test
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 | | | | | summary: (7) expand
418 | | | | | summary: (7) expand
419 | | | | |
419 | | | | |
420 +---o | | changeset: 6:b105a072e251
420 +---o | | changeset: 6:b105a072e251
421 | |/ / / parent: 2:3d9a33b8d1e1
421 | |/ / / parent: 2:3d9a33b8d1e1
422 | | | | parent: 5:4409d547b708
422 | | | | parent: 5:4409d547b708
423 | | | | user: test
423 | | | | user: test
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 | | | | summary: (6) merge two known; one immediate left, one far left
425 | | | | summary: (6) merge two known; one immediate left, one far left
426 | | | |
426 | | | |
427 | o | | changeset: 5:4409d547b708
427 | o | | changeset: 5:4409d547b708
428 | |\ \ \ parent: 3:27eef8ed80b4
428 | |\ \ \ parent: 3:27eef8ed80b4
429 | | | | | parent: 4:26a8bac39d9f
429 | | | | | parent: 4:26a8bac39d9f
430 | | | | | user: test
430 | | | | | user: test
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 | | | | | summary: (5) expand
432 | | | | | summary: (5) expand
433 | | | | |
433 | | | | |
434 | | o | | changeset: 4:26a8bac39d9f
434 | | o | | changeset: 4:26a8bac39d9f
435 | |/|/ / parent: 1:6db2ef61d156
435 | |/|/ / parent: 1:6db2ef61d156
436 | | | | parent: 3:27eef8ed80b4
436 | | | | parent: 3:27eef8ed80b4
437 | | | | user: test
437 | | | | user: test
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 | | | |
440 | | | |
441 | o | | changeset: 3:27eef8ed80b4
441 | o | | changeset: 3:27eef8ed80b4
442 |/ / / user: test
442 |/ / / user: test
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 | | | summary: (3) collapse
444 | | | summary: (3) collapse
445 | | |
445 | | |
446 o | | changeset: 2:3d9a33b8d1e1
446 o | | changeset: 2:3d9a33b8d1e1
447 |/ / user: test
447 |/ / user: test
448 | | date: Thu Jan 01 00:00:02 1970 +0000
448 | | date: Thu Jan 01 00:00:02 1970 +0000
449 | | summary: (2) collapse
449 | | summary: (2) collapse
450 | |
450 | |
451 o | changeset: 1:6db2ef61d156
451 o | changeset: 1:6db2ef61d156
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: (1) collapse
454 | summary: (1) collapse
455 |
455 |
456 o changeset: 0:e6eb3150255d
456 o changeset: 0:e6eb3150255d
457 user: test
457 user: test
458 date: Thu Jan 01 00:00:00 1970 +0000
458 date: Thu Jan 01 00:00:00 1970 +0000
459 summary: (0) root
459 summary: (0) root
460
460
461
461
462 File glog:
462 File glog:
463 $ hg log -G a
463 $ hg log -G a
464 @ changeset: 34:fea3ac5810e0
464 @ changeset: 34:fea3ac5810e0
465 | tag: tip
465 | tag: tip
466 | parent: 32:d06dffa21a31
466 | parent: 32:d06dffa21a31
467 | user: test
467 | user: test
468 | date: Thu Jan 01 00:00:34 1970 +0000
468 | date: Thu Jan 01 00:00:34 1970 +0000
469 | summary: (34) head
469 | summary: (34) head
470 |
470 |
471 | o changeset: 33:68608f5145f9
471 | o changeset: 33:68608f5145f9
472 | | parent: 18:1aa84d96232a
472 | | parent: 18:1aa84d96232a
473 | | user: test
473 | | user: test
474 | | date: Thu Jan 01 00:00:33 1970 +0000
474 | | date: Thu Jan 01 00:00:33 1970 +0000
475 | | summary: (33) head
475 | | summary: (33) head
476 | |
476 | |
477 o | changeset: 32:d06dffa21a31
477 o | changeset: 32:d06dffa21a31
478 |\ \ parent: 27:886ed638191b
478 |\ \ parent: 27:886ed638191b
479 | | | parent: 31:621d83e11f67
479 | | | parent: 31:621d83e11f67
480 | | | user: test
480 | | | user: test
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 | | | summary: (32) expand
482 | | | summary: (32) expand
483 | | |
483 | | |
484 | o | changeset: 31:621d83e11f67
484 | o | changeset: 31:621d83e11f67
485 | |\ \ parent: 21:d42a756af44d
485 | |\ \ parent: 21:d42a756af44d
486 | | | | parent: 30:6e11cd4b648f
486 | | | | parent: 30:6e11cd4b648f
487 | | | | user: test
487 | | | | user: test
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 | | | | summary: (31) expand
489 | | | | summary: (31) expand
490 | | | |
490 | | | |
491 | | o | changeset: 30:6e11cd4b648f
491 | | o | changeset: 30:6e11cd4b648f
492 | | |\ \ parent: 28:44ecd0b9ae99
492 | | |\ \ parent: 28:44ecd0b9ae99
493 | | | | | parent: 29:cd9bb2be7593
493 | | | | | parent: 29:cd9bb2be7593
494 | | | | | user: test
494 | | | | | user: test
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 | | | | | summary: (30) expand
496 | | | | | summary: (30) expand
497 | | | | |
497 | | | | |
498 | | | o | changeset: 29:cd9bb2be7593
498 | | | o | changeset: 29:cd9bb2be7593
499 | | | | | parent: 0:e6eb3150255d
499 | | | | | parent: 0:e6eb3150255d
500 | | | | | user: test
500 | | | | | user: test
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 | | | | | summary: (29) regular commit
502 | | | | | summary: (29) regular commit
503 | | | | |
503 | | | | |
504 | | o | | changeset: 28:44ecd0b9ae99
504 | | o | | changeset: 28:44ecd0b9ae99
505 | | |\ \ \ parent: 1:6db2ef61d156
505 | | |\ \ \ parent: 1:6db2ef61d156
506 | | | | | | parent: 26:7f25b6c2f0b9
506 | | | | | | parent: 26:7f25b6c2f0b9
507 | | | | | | user: test
507 | | | | | | user: test
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 | | | | | | summary: (28) merge zero known
509 | | | | | | summary: (28) merge zero known
510 | | | | | |
510 | | | | | |
511 o | | | | | changeset: 27:886ed638191b
511 o | | | | | changeset: 27:886ed638191b
512 |/ / / / / parent: 21:d42a756af44d
512 |/ / / / / parent: 21:d42a756af44d
513 | | | | | user: test
513 | | | | | user: test
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 | | | | | summary: (27) collapse
515 | | | | | summary: (27) collapse
516 | | | | |
516 | | | | |
517 | | o---+ changeset: 26:7f25b6c2f0b9
517 | | o---+ changeset: 26:7f25b6c2f0b9
518 | | | | | parent: 18:1aa84d96232a
518 | | | | | parent: 18:1aa84d96232a
519 | | | | | parent: 25:91da8ed57247
519 | | | | | parent: 25:91da8ed57247
520 | | | | | user: test
520 | | | | | user: test
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 | | | | | summary: (26) merge one known; far right
522 | | | | | summary: (26) merge one known; far right
523 | | | | |
523 | | | | |
524 +---o | | changeset: 25:91da8ed57247
524 +---o | | changeset: 25:91da8ed57247
525 | | | | | parent: 21:d42a756af44d
525 | | | | | parent: 21:d42a756af44d
526 | | | | | parent: 24:a9c19a3d96b7
526 | | | | | parent: 24:a9c19a3d96b7
527 | | | | | user: test
527 | | | | | user: test
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 | | | | | summary: (25) merge one known; far left
529 | | | | | summary: (25) merge one known; far left
530 | | | | |
530 | | | | |
531 | | o | | changeset: 24:a9c19a3d96b7
531 | | o | | changeset: 24:a9c19a3d96b7
532 | | |\| | parent: 0:e6eb3150255d
532 | | |\| | parent: 0:e6eb3150255d
533 | | | | | parent: 23:a01cddf0766d
533 | | | | | parent: 23:a01cddf0766d
534 | | | | | user: test
534 | | | | | user: test
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 | | | | | summary: (24) merge one known; immediate right
536 | | | | | summary: (24) merge one known; immediate right
537 | | | | |
537 | | | | |
538 | | o | | changeset: 23:a01cddf0766d
538 | | o | | changeset: 23:a01cddf0766d
539 | |/| | | parent: 1:6db2ef61d156
539 | |/| | | parent: 1:6db2ef61d156
540 | | | | | parent: 22:e0d9cccacb5d
540 | | | | | parent: 22:e0d9cccacb5d
541 | | | | | user: test
541 | | | | | user: test
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 | | | | | summary: (23) merge one known; immediate left
543 | | | | | summary: (23) merge one known; immediate left
544 | | | | |
544 | | | | |
545 +---o---+ changeset: 22:e0d9cccacb5d
545 +---o---+ changeset: 22:e0d9cccacb5d
546 | | | | parent: 18:1aa84d96232a
546 | | | | parent: 18:1aa84d96232a
547 | | / / parent: 21:d42a756af44d
547 | | / / parent: 21:d42a756af44d
548 | | | | user: test
548 | | | | user: test
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 | | | | summary: (22) merge two known; one far left, one far right
550 | | | | summary: (22) merge two known; one far left, one far right
551 | | | |
551 | | | |
552 o | | | changeset: 21:d42a756af44d
552 o | | | changeset: 21:d42a756af44d
553 |\ \ \ \ parent: 19:31ddc2c1573b
553 |\ \ \ \ parent: 19:31ddc2c1573b
554 | | | | | parent: 20:d30ed6450e32
554 | | | | | parent: 20:d30ed6450e32
555 | | | | | user: test
555 | | | | | user: test
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 | | | | | summary: (21) expand
557 | | | | | summary: (21) expand
558 | | | | |
558 | | | | |
559 | o---+-+ changeset: 20:d30ed6450e32
559 | o---+-+ changeset: 20:d30ed6450e32
560 | | | | parent: 0:e6eb3150255d
560 | | | | parent: 0:e6eb3150255d
561 | / / / parent: 18:1aa84d96232a
561 | / / / parent: 18:1aa84d96232a
562 | | | | user: test
562 | | | | user: test
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 | | | | summary: (20) merge two known; two far right
564 | | | | summary: (20) merge two known; two far right
565 | | | |
565 | | | |
566 o | | | changeset: 19:31ddc2c1573b
566 o | | | changeset: 19:31ddc2c1573b
567 |\ \ \ \ parent: 15:1dda3f72782d
567 |\ \ \ \ parent: 15:1dda3f72782d
568 | | | | | parent: 17:44765d7c06e0
568 | | | | | parent: 17:44765d7c06e0
569 | | | | | user: test
569 | | | | | user: test
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 | | | | | summary: (19) expand
571 | | | | | summary: (19) expand
572 | | | | |
572 | | | | |
573 +---+---o changeset: 18:1aa84d96232a
573 +---+---o changeset: 18:1aa84d96232a
574 | | | | parent: 1:6db2ef61d156
574 | | | | parent: 1:6db2ef61d156
575 | | | | parent: 15:1dda3f72782d
575 | | | | parent: 15:1dda3f72782d
576 | | | | user: test
576 | | | | user: test
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 | | | | summary: (18) merge two known; two far left
578 | | | | summary: (18) merge two known; two far left
579 | | | |
579 | | | |
580 | o | | changeset: 17:44765d7c06e0
580 | o | | changeset: 17:44765d7c06e0
581 | |\ \ \ parent: 12:86b91144a6e9
581 | |\ \ \ parent: 12:86b91144a6e9
582 | | | | | parent: 16:3677d192927d
582 | | | | | parent: 16:3677d192927d
583 | | | | | user: test
583 | | | | | user: test
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 | | | | | summary: (17) expand
585 | | | | | summary: (17) expand
586 | | | | |
586 | | | | |
587 | | o---+ changeset: 16:3677d192927d
587 | | o---+ changeset: 16:3677d192927d
588 | | | | | parent: 0:e6eb3150255d
588 | | | | | parent: 0:e6eb3150255d
589 | | |/ / parent: 1:6db2ef61d156
589 | | |/ / parent: 1:6db2ef61d156
590 | | | | user: test
590 | | | | user: test
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 | | | | summary: (16) merge two known; one immediate right, one near right
592 | | | | summary: (16) merge two known; one immediate right, one near right
593 | | | |
593 | | | |
594 o | | | changeset: 15:1dda3f72782d
594 o | | | changeset: 15:1dda3f72782d
595 |\ \ \ \ parent: 13:22d8966a97e3
595 |\ \ \ \ parent: 13:22d8966a97e3
596 | | | | | parent: 14:8eac370358ef
596 | | | | | parent: 14:8eac370358ef
597 | | | | | user: test
597 | | | | | user: test
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 | | | | | summary: (15) expand
599 | | | | | summary: (15) expand
600 | | | | |
600 | | | | |
601 | o-----+ changeset: 14:8eac370358ef
601 | o-----+ changeset: 14:8eac370358ef
602 | | | | | parent: 0:e6eb3150255d
602 | | | | | parent: 0:e6eb3150255d
603 | |/ / / parent: 12:86b91144a6e9
603 | |/ / / parent: 12:86b91144a6e9
604 | | | | user: test
604 | | | | user: test
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 | | | | summary: (14) merge two known; one immediate right, one far right
606 | | | | summary: (14) merge two known; one immediate right, one far right
607 | | | |
607 | | | |
608 o | | | changeset: 13:22d8966a97e3
608 o | | | changeset: 13:22d8966a97e3
609 |\ \ \ \ parent: 9:7010c0af0a35
609 |\ \ \ \ parent: 9:7010c0af0a35
610 | | | | | parent: 11:832d76e6bdf2
610 | | | | | parent: 11:832d76e6bdf2
611 | | | | | user: test
611 | | | | | user: test
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 | | | | | summary: (13) expand
613 | | | | | summary: (13) expand
614 | | | | |
614 | | | | |
615 +---o | | changeset: 12:86b91144a6e9
615 +---o | | changeset: 12:86b91144a6e9
616 | | |/ / parent: 1:6db2ef61d156
616 | | |/ / parent: 1:6db2ef61d156
617 | | | | parent: 9:7010c0af0a35
617 | | | | parent: 9:7010c0af0a35
618 | | | | user: test
618 | | | | user: test
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 | | | | summary: (12) merge two known; one immediate right, one far left
620 | | | | summary: (12) merge two known; one immediate right, one far left
621 | | | |
621 | | | |
622 | o | | changeset: 11:832d76e6bdf2
622 | o | | changeset: 11:832d76e6bdf2
623 | |\ \ \ parent: 6:b105a072e251
623 | |\ \ \ parent: 6:b105a072e251
624 | | | | | parent: 10:74c64d036d72
624 | | | | | parent: 10:74c64d036d72
625 | | | | | user: test
625 | | | | | user: test
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 | | | | | summary: (11) expand
627 | | | | | summary: (11) expand
628 | | | | |
628 | | | | |
629 | | o---+ changeset: 10:74c64d036d72
629 | | o---+ changeset: 10:74c64d036d72
630 | | | | | parent: 0:e6eb3150255d
630 | | | | | parent: 0:e6eb3150255d
631 | |/ / / parent: 6:b105a072e251
631 | |/ / / parent: 6:b105a072e251
632 | | | | user: test
632 | | | | user: test
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 | | | | summary: (10) merge two known; one immediate left, one near right
634 | | | | summary: (10) merge two known; one immediate left, one near right
635 | | | |
635 | | | |
636 o | | | changeset: 9:7010c0af0a35
636 o | | | changeset: 9:7010c0af0a35
637 |\ \ \ \ parent: 7:b632bb1b1224
637 |\ \ \ \ parent: 7:b632bb1b1224
638 | | | | | parent: 8:7a0b11f71937
638 | | | | | parent: 8:7a0b11f71937
639 | | | | | user: test
639 | | | | | user: test
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 | | | | | summary: (9) expand
641 | | | | | summary: (9) expand
642 | | | | |
642 | | | | |
643 | o-----+ changeset: 8:7a0b11f71937
643 | o-----+ changeset: 8:7a0b11f71937
644 | | | | | parent: 0:e6eb3150255d
644 | | | | | parent: 0:e6eb3150255d
645 |/ / / / parent: 7:b632bb1b1224
645 |/ / / / parent: 7:b632bb1b1224
646 | | | | user: test
646 | | | | user: test
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 | | | | summary: (8) merge two known; one immediate left, one far right
648 | | | | summary: (8) merge two known; one immediate left, one far right
649 | | | |
649 | | | |
650 o | | | changeset: 7:b632bb1b1224
650 o | | | changeset: 7:b632bb1b1224
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 | | | | | parent: 5:4409d547b708
652 | | | | | parent: 5:4409d547b708
653 | | | | | user: test
653 | | | | | user: test
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 | | | | | summary: (7) expand
655 | | | | | summary: (7) expand
656 | | | | |
656 | | | | |
657 +---o | | changeset: 6:b105a072e251
657 +---o | | changeset: 6:b105a072e251
658 | |/ / / parent: 2:3d9a33b8d1e1
658 | |/ / / parent: 2:3d9a33b8d1e1
659 | | | | parent: 5:4409d547b708
659 | | | | parent: 5:4409d547b708
660 | | | | user: test
660 | | | | user: test
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 | | | | summary: (6) merge two known; one immediate left, one far left
662 | | | | summary: (6) merge two known; one immediate left, one far left
663 | | | |
663 | | | |
664 | o | | changeset: 5:4409d547b708
664 | o | | changeset: 5:4409d547b708
665 | |\ \ \ parent: 3:27eef8ed80b4
665 | |\ \ \ parent: 3:27eef8ed80b4
666 | | | | | parent: 4:26a8bac39d9f
666 | | | | | parent: 4:26a8bac39d9f
667 | | | | | user: test
667 | | | | | user: test
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 | | | | | summary: (5) expand
669 | | | | | summary: (5) expand
670 | | | | |
670 | | | | |
671 | | o | | changeset: 4:26a8bac39d9f
671 | | o | | changeset: 4:26a8bac39d9f
672 | |/|/ / parent: 1:6db2ef61d156
672 | |/|/ / parent: 1:6db2ef61d156
673 | | | | parent: 3:27eef8ed80b4
673 | | | | parent: 3:27eef8ed80b4
674 | | | | user: test
674 | | | | user: test
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 | | | |
677 | | | |
678 | o | | changeset: 3:27eef8ed80b4
678 | o | | changeset: 3:27eef8ed80b4
679 |/ / / user: test
679 |/ / / user: test
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 | | | summary: (3) collapse
681 | | | summary: (3) collapse
682 | | |
682 | | |
683 o | | changeset: 2:3d9a33b8d1e1
683 o | | changeset: 2:3d9a33b8d1e1
684 |/ / user: test
684 |/ / user: test
685 | | date: Thu Jan 01 00:00:02 1970 +0000
685 | | date: Thu Jan 01 00:00:02 1970 +0000
686 | | summary: (2) collapse
686 | | summary: (2) collapse
687 | |
687 | |
688 o | changeset: 1:6db2ef61d156
688 o | changeset: 1:6db2ef61d156
689 |/ user: test
689 |/ user: test
690 | date: Thu Jan 01 00:00:01 1970 +0000
690 | date: Thu Jan 01 00:00:01 1970 +0000
691 | summary: (1) collapse
691 | summary: (1) collapse
692 |
692 |
693 o changeset: 0:e6eb3150255d
693 o changeset: 0:e6eb3150255d
694 user: test
694 user: test
695 date: Thu Jan 01 00:00:00 1970 +0000
695 date: Thu Jan 01 00:00:00 1970 +0000
696 summary: (0) root
696 summary: (0) root
697
697
698
698
699 File glog per revset:
699 File glog per revset:
700
700
701 $ hg log -G -r 'file("a")'
701 $ hg log -G -r 'file("a")'
702 @ changeset: 34:fea3ac5810e0
702 @ changeset: 34:fea3ac5810e0
703 | tag: tip
703 | tag: tip
704 | parent: 32:d06dffa21a31
704 | parent: 32:d06dffa21a31
705 | user: test
705 | user: test
706 | date: Thu Jan 01 00:00:34 1970 +0000
706 | date: Thu Jan 01 00:00:34 1970 +0000
707 | summary: (34) head
707 | summary: (34) head
708 |
708 |
709 | o changeset: 33:68608f5145f9
709 | o changeset: 33:68608f5145f9
710 | | parent: 18:1aa84d96232a
710 | | parent: 18:1aa84d96232a
711 | | user: test
711 | | user: test
712 | | date: Thu Jan 01 00:00:33 1970 +0000
712 | | date: Thu Jan 01 00:00:33 1970 +0000
713 | | summary: (33) head
713 | | summary: (33) head
714 | |
714 | |
715 o | changeset: 32:d06dffa21a31
715 o | changeset: 32:d06dffa21a31
716 |\ \ parent: 27:886ed638191b
716 |\ \ parent: 27:886ed638191b
717 | | | parent: 31:621d83e11f67
717 | | | parent: 31:621d83e11f67
718 | | | user: test
718 | | | user: test
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 | | | summary: (32) expand
720 | | | summary: (32) expand
721 | | |
721 | | |
722 | o | changeset: 31:621d83e11f67
722 | o | changeset: 31:621d83e11f67
723 | |\ \ parent: 21:d42a756af44d
723 | |\ \ parent: 21:d42a756af44d
724 | | | | parent: 30:6e11cd4b648f
724 | | | | parent: 30:6e11cd4b648f
725 | | | | user: test
725 | | | | user: test
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 | | | | summary: (31) expand
727 | | | | summary: (31) expand
728 | | | |
728 | | | |
729 | | o | changeset: 30:6e11cd4b648f
729 | | o | changeset: 30:6e11cd4b648f
730 | | |\ \ parent: 28:44ecd0b9ae99
730 | | |\ \ parent: 28:44ecd0b9ae99
731 | | | | | parent: 29:cd9bb2be7593
731 | | | | | parent: 29:cd9bb2be7593
732 | | | | | user: test
732 | | | | | user: test
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 | | | | | summary: (30) expand
734 | | | | | summary: (30) expand
735 | | | | |
735 | | | | |
736 | | | o | changeset: 29:cd9bb2be7593
736 | | | o | changeset: 29:cd9bb2be7593
737 | | | | | parent: 0:e6eb3150255d
737 | | | | | parent: 0:e6eb3150255d
738 | | | | | user: test
738 | | | | | user: test
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 | | | | | summary: (29) regular commit
740 | | | | | summary: (29) regular commit
741 | | | | |
741 | | | | |
742 | | o | | changeset: 28:44ecd0b9ae99
742 | | o | | changeset: 28:44ecd0b9ae99
743 | | |\ \ \ parent: 1:6db2ef61d156
743 | | |\ \ \ parent: 1:6db2ef61d156
744 | | | | | | parent: 26:7f25b6c2f0b9
744 | | | | | | parent: 26:7f25b6c2f0b9
745 | | | | | | user: test
745 | | | | | | user: test
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 | | | | | | summary: (28) merge zero known
747 | | | | | | summary: (28) merge zero known
748 | | | | | |
748 | | | | | |
749 o | | | | | changeset: 27:886ed638191b
749 o | | | | | changeset: 27:886ed638191b
750 |/ / / / / parent: 21:d42a756af44d
750 |/ / / / / parent: 21:d42a756af44d
751 | | | | | user: test
751 | | | | | user: test
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 | | | | | summary: (27) collapse
753 | | | | | summary: (27) collapse
754 | | | | |
754 | | | | |
755 | | o---+ changeset: 26:7f25b6c2f0b9
755 | | o---+ changeset: 26:7f25b6c2f0b9
756 | | | | | parent: 18:1aa84d96232a
756 | | | | | parent: 18:1aa84d96232a
757 | | | | | parent: 25:91da8ed57247
757 | | | | | parent: 25:91da8ed57247
758 | | | | | user: test
758 | | | | | user: test
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 | | | | | summary: (26) merge one known; far right
760 | | | | | summary: (26) merge one known; far right
761 | | | | |
761 | | | | |
762 +---o | | changeset: 25:91da8ed57247
762 +---o | | changeset: 25:91da8ed57247
763 | | | | | parent: 21:d42a756af44d
763 | | | | | parent: 21:d42a756af44d
764 | | | | | parent: 24:a9c19a3d96b7
764 | | | | | parent: 24:a9c19a3d96b7
765 | | | | | user: test
765 | | | | | user: test
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 | | | | | summary: (25) merge one known; far left
767 | | | | | summary: (25) merge one known; far left
768 | | | | |
768 | | | | |
769 | | o | | changeset: 24:a9c19a3d96b7
769 | | o | | changeset: 24:a9c19a3d96b7
770 | | |\| | parent: 0:e6eb3150255d
770 | | |\| | parent: 0:e6eb3150255d
771 | | | | | parent: 23:a01cddf0766d
771 | | | | | parent: 23:a01cddf0766d
772 | | | | | user: test
772 | | | | | user: test
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 | | | | | summary: (24) merge one known; immediate right
774 | | | | | summary: (24) merge one known; immediate right
775 | | | | |
775 | | | | |
776 | | o | | changeset: 23:a01cddf0766d
776 | | o | | changeset: 23:a01cddf0766d
777 | |/| | | parent: 1:6db2ef61d156
777 | |/| | | parent: 1:6db2ef61d156
778 | | | | | parent: 22:e0d9cccacb5d
778 | | | | | parent: 22:e0d9cccacb5d
779 | | | | | user: test
779 | | | | | user: test
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 | | | | | summary: (23) merge one known; immediate left
781 | | | | | summary: (23) merge one known; immediate left
782 | | | | |
782 | | | | |
783 +---o---+ changeset: 22:e0d9cccacb5d
783 +---o---+ changeset: 22:e0d9cccacb5d
784 | | | | parent: 18:1aa84d96232a
784 | | | | parent: 18:1aa84d96232a
785 | | / / parent: 21:d42a756af44d
785 | | / / parent: 21:d42a756af44d
786 | | | | user: test
786 | | | | user: test
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 | | | | summary: (22) merge two known; one far left, one far right
788 | | | | summary: (22) merge two known; one far left, one far right
789 | | | |
789 | | | |
790 o | | | changeset: 21:d42a756af44d
790 o | | | changeset: 21:d42a756af44d
791 |\ \ \ \ parent: 19:31ddc2c1573b
791 |\ \ \ \ parent: 19:31ddc2c1573b
792 | | | | | parent: 20:d30ed6450e32
792 | | | | | parent: 20:d30ed6450e32
793 | | | | | user: test
793 | | | | | user: test
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 | | | | | summary: (21) expand
795 | | | | | summary: (21) expand
796 | | | | |
796 | | | | |
797 | o---+-+ changeset: 20:d30ed6450e32
797 | o---+-+ changeset: 20:d30ed6450e32
798 | | | | parent: 0:e6eb3150255d
798 | | | | parent: 0:e6eb3150255d
799 | / / / parent: 18:1aa84d96232a
799 | / / / parent: 18:1aa84d96232a
800 | | | | user: test
800 | | | | user: test
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 | | | | summary: (20) merge two known; two far right
802 | | | | summary: (20) merge two known; two far right
803 | | | |
803 | | | |
804 o | | | changeset: 19:31ddc2c1573b
804 o | | | changeset: 19:31ddc2c1573b
805 |\ \ \ \ parent: 15:1dda3f72782d
805 |\ \ \ \ parent: 15:1dda3f72782d
806 | | | | | parent: 17:44765d7c06e0
806 | | | | | parent: 17:44765d7c06e0
807 | | | | | user: test
807 | | | | | user: test
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 | | | | | summary: (19) expand
809 | | | | | summary: (19) expand
810 | | | | |
810 | | | | |
811 +---+---o changeset: 18:1aa84d96232a
811 +---+---o changeset: 18:1aa84d96232a
812 | | | | parent: 1:6db2ef61d156
812 | | | | parent: 1:6db2ef61d156
813 | | | | parent: 15:1dda3f72782d
813 | | | | parent: 15:1dda3f72782d
814 | | | | user: test
814 | | | | user: test
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 | | | | summary: (18) merge two known; two far left
816 | | | | summary: (18) merge two known; two far left
817 | | | |
817 | | | |
818 | o | | changeset: 17:44765d7c06e0
818 | o | | changeset: 17:44765d7c06e0
819 | |\ \ \ parent: 12:86b91144a6e9
819 | |\ \ \ parent: 12:86b91144a6e9
820 | | | | | parent: 16:3677d192927d
820 | | | | | parent: 16:3677d192927d
821 | | | | | user: test
821 | | | | | user: test
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 | | | | | summary: (17) expand
823 | | | | | summary: (17) expand
824 | | | | |
824 | | | | |
825 | | o---+ changeset: 16:3677d192927d
825 | | o---+ changeset: 16:3677d192927d
826 | | | | | parent: 0:e6eb3150255d
826 | | | | | parent: 0:e6eb3150255d
827 | | |/ / parent: 1:6db2ef61d156
827 | | |/ / parent: 1:6db2ef61d156
828 | | | | user: test
828 | | | | user: test
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 | | | | summary: (16) merge two known; one immediate right, one near right
830 | | | | summary: (16) merge two known; one immediate right, one near right
831 | | | |
831 | | | |
832 o | | | changeset: 15:1dda3f72782d
832 o | | | changeset: 15:1dda3f72782d
833 |\ \ \ \ parent: 13:22d8966a97e3
833 |\ \ \ \ parent: 13:22d8966a97e3
834 | | | | | parent: 14:8eac370358ef
834 | | | | | parent: 14:8eac370358ef
835 | | | | | user: test
835 | | | | | user: test
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 | | | | | summary: (15) expand
837 | | | | | summary: (15) expand
838 | | | | |
838 | | | | |
839 | o-----+ changeset: 14:8eac370358ef
839 | o-----+ changeset: 14:8eac370358ef
840 | | | | | parent: 0:e6eb3150255d
840 | | | | | parent: 0:e6eb3150255d
841 | |/ / / parent: 12:86b91144a6e9
841 | |/ / / parent: 12:86b91144a6e9
842 | | | | user: test
842 | | | | user: test
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 | | | | summary: (14) merge two known; one immediate right, one far right
844 | | | | summary: (14) merge two known; one immediate right, one far right
845 | | | |
845 | | | |
846 o | | | changeset: 13:22d8966a97e3
846 o | | | changeset: 13:22d8966a97e3
847 |\ \ \ \ parent: 9:7010c0af0a35
847 |\ \ \ \ parent: 9:7010c0af0a35
848 | | | | | parent: 11:832d76e6bdf2
848 | | | | | parent: 11:832d76e6bdf2
849 | | | | | user: test
849 | | | | | user: test
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 | | | | | summary: (13) expand
851 | | | | | summary: (13) expand
852 | | | | |
852 | | | | |
853 +---o | | changeset: 12:86b91144a6e9
853 +---o | | changeset: 12:86b91144a6e9
854 | | |/ / parent: 1:6db2ef61d156
854 | | |/ / parent: 1:6db2ef61d156
855 | | | | parent: 9:7010c0af0a35
855 | | | | parent: 9:7010c0af0a35
856 | | | | user: test
856 | | | | user: test
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 | | | | summary: (12) merge two known; one immediate right, one far left
858 | | | | summary: (12) merge two known; one immediate right, one far left
859 | | | |
859 | | | |
860 | o | | changeset: 11:832d76e6bdf2
860 | o | | changeset: 11:832d76e6bdf2
861 | |\ \ \ parent: 6:b105a072e251
861 | |\ \ \ parent: 6:b105a072e251
862 | | | | | parent: 10:74c64d036d72
862 | | | | | parent: 10:74c64d036d72
863 | | | | | user: test
863 | | | | | user: test
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 | | | | | summary: (11) expand
865 | | | | | summary: (11) expand
866 | | | | |
866 | | | | |
867 | | o---+ changeset: 10:74c64d036d72
867 | | o---+ changeset: 10:74c64d036d72
868 | | | | | parent: 0:e6eb3150255d
868 | | | | | parent: 0:e6eb3150255d
869 | |/ / / parent: 6:b105a072e251
869 | |/ / / parent: 6:b105a072e251
870 | | | | user: test
870 | | | | user: test
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 | | | | summary: (10) merge two known; one immediate left, one near right
872 | | | | summary: (10) merge two known; one immediate left, one near right
873 | | | |
873 | | | |
874 o | | | changeset: 9:7010c0af0a35
874 o | | | changeset: 9:7010c0af0a35
875 |\ \ \ \ parent: 7:b632bb1b1224
875 |\ \ \ \ parent: 7:b632bb1b1224
876 | | | | | parent: 8:7a0b11f71937
876 | | | | | parent: 8:7a0b11f71937
877 | | | | | user: test
877 | | | | | user: test
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 | | | | | summary: (9) expand
879 | | | | | summary: (9) expand
880 | | | | |
880 | | | | |
881 | o-----+ changeset: 8:7a0b11f71937
881 | o-----+ changeset: 8:7a0b11f71937
882 | | | | | parent: 0:e6eb3150255d
882 | | | | | parent: 0:e6eb3150255d
883 |/ / / / parent: 7:b632bb1b1224
883 |/ / / / parent: 7:b632bb1b1224
884 | | | | user: test
884 | | | | user: test
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 | | | | summary: (8) merge two known; one immediate left, one far right
886 | | | | summary: (8) merge two known; one immediate left, one far right
887 | | | |
887 | | | |
888 o | | | changeset: 7:b632bb1b1224
888 o | | | changeset: 7:b632bb1b1224
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 | | | | | parent: 5:4409d547b708
890 | | | | | parent: 5:4409d547b708
891 | | | | | user: test
891 | | | | | user: test
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 | | | | | summary: (7) expand
893 | | | | | summary: (7) expand
894 | | | | |
894 | | | | |
895 +---o | | changeset: 6:b105a072e251
895 +---o | | changeset: 6:b105a072e251
896 | |/ / / parent: 2:3d9a33b8d1e1
896 | |/ / / parent: 2:3d9a33b8d1e1
897 | | | | parent: 5:4409d547b708
897 | | | | parent: 5:4409d547b708
898 | | | | user: test
898 | | | | user: test
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 | | | | summary: (6) merge two known; one immediate left, one far left
900 | | | | summary: (6) merge two known; one immediate left, one far left
901 | | | |
901 | | | |
902 | o | | changeset: 5:4409d547b708
902 | o | | changeset: 5:4409d547b708
903 | |\ \ \ parent: 3:27eef8ed80b4
903 | |\ \ \ parent: 3:27eef8ed80b4
904 | | | | | parent: 4:26a8bac39d9f
904 | | | | | parent: 4:26a8bac39d9f
905 | | | | | user: test
905 | | | | | user: test
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 | | | | | summary: (5) expand
907 | | | | | summary: (5) expand
908 | | | | |
908 | | | | |
909 | | o | | changeset: 4:26a8bac39d9f
909 | | o | | changeset: 4:26a8bac39d9f
910 | |/|/ / parent: 1:6db2ef61d156
910 | |/|/ / parent: 1:6db2ef61d156
911 | | | | parent: 3:27eef8ed80b4
911 | | | | parent: 3:27eef8ed80b4
912 | | | | user: test
912 | | | | user: test
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 | | | |
915 | | | |
916 | o | | changeset: 3:27eef8ed80b4
916 | o | | changeset: 3:27eef8ed80b4
917 |/ / / user: test
917 |/ / / user: test
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 | | | summary: (3) collapse
919 | | | summary: (3) collapse
920 | | |
920 | | |
921 o | | changeset: 2:3d9a33b8d1e1
921 o | | changeset: 2:3d9a33b8d1e1
922 |/ / user: test
922 |/ / user: test
923 | | date: Thu Jan 01 00:00:02 1970 +0000
923 | | date: Thu Jan 01 00:00:02 1970 +0000
924 | | summary: (2) collapse
924 | | summary: (2) collapse
925 | |
925 | |
926 o | changeset: 1:6db2ef61d156
926 o | changeset: 1:6db2ef61d156
927 |/ user: test
927 |/ user: test
928 | date: Thu Jan 01 00:00:01 1970 +0000
928 | date: Thu Jan 01 00:00:01 1970 +0000
929 | summary: (1) collapse
929 | summary: (1) collapse
930 |
930 |
931 o changeset: 0:e6eb3150255d
931 o changeset: 0:e6eb3150255d
932 user: test
932 user: test
933 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
934 summary: (0) root
934 summary: (0) root
935
935
936
936
937
937
938 File glog per revset (only merges):
938 File glog per revset (only merges):
939
939
940 $ hg log -G -r 'file("a")' -m
940 $ hg log -G -r 'file("a")' -m
941 o changeset: 32:d06dffa21a31
941 o changeset: 32:d06dffa21a31
942 |\ parent: 27:886ed638191b
942 |\ parent: 27:886ed638191b
943 | : parent: 31:621d83e11f67
943 | : parent: 31:621d83e11f67
944 | : user: test
944 | : user: test
945 | : date: Thu Jan 01 00:00:32 1970 +0000
945 | : date: Thu Jan 01 00:00:32 1970 +0000
946 | : summary: (32) expand
946 | : summary: (32) expand
947 | :
947 | :
948 o : changeset: 31:621d83e11f67
948 o : changeset: 31:621d83e11f67
949 |\: parent: 21:d42a756af44d
949 |\: parent: 21:d42a756af44d
950 | : parent: 30:6e11cd4b648f
950 | : parent: 30:6e11cd4b648f
951 | : user: test
951 | : user: test
952 | : date: Thu Jan 01 00:00:31 1970 +0000
952 | : date: Thu Jan 01 00:00:31 1970 +0000
953 | : summary: (31) expand
953 | : summary: (31) expand
954 | :
954 | :
955 o : changeset: 30:6e11cd4b648f
955 o : changeset: 30:6e11cd4b648f
956 |\ \ parent: 28:44ecd0b9ae99
956 |\ \ parent: 28:44ecd0b9ae99
957 | ~ : parent: 29:cd9bb2be7593
957 | ~ : parent: 29:cd9bb2be7593
958 | : user: test
958 | : user: test
959 | : date: Thu Jan 01 00:00:30 1970 +0000
959 | : date: Thu Jan 01 00:00:30 1970 +0000
960 | : summary: (30) expand
960 | : summary: (30) expand
961 | /
961 | /
962 o : changeset: 28:44ecd0b9ae99
962 o : changeset: 28:44ecd0b9ae99
963 |\ \ parent: 1:6db2ef61d156
963 |\ \ parent: 1:6db2ef61d156
964 | ~ : parent: 26:7f25b6c2f0b9
964 | ~ : parent: 26:7f25b6c2f0b9
965 | : user: test
965 | : user: test
966 | : date: Thu Jan 01 00:00:28 1970 +0000
966 | : date: Thu Jan 01 00:00:28 1970 +0000
967 | : summary: (28) merge zero known
967 | : summary: (28) merge zero known
968 | /
968 | /
969 o : changeset: 26:7f25b6c2f0b9
969 o : changeset: 26:7f25b6c2f0b9
970 |\ \ parent: 18:1aa84d96232a
970 |\ \ parent: 18:1aa84d96232a
971 | | : parent: 25:91da8ed57247
971 | | : parent: 25:91da8ed57247
972 | | : user: test
972 | | : user: test
973 | | : date: Thu Jan 01 00:00:26 1970 +0000
973 | | : date: Thu Jan 01 00:00:26 1970 +0000
974 | | : summary: (26) merge one known; far right
974 | | : summary: (26) merge one known; far right
975 | | :
975 | | :
976 | o : changeset: 25:91da8ed57247
976 | o : changeset: 25:91da8ed57247
977 | |\: parent: 21:d42a756af44d
977 | |\: parent: 21:d42a756af44d
978 | | : parent: 24:a9c19a3d96b7
978 | | : parent: 24:a9c19a3d96b7
979 | | : user: test
979 | | : user: test
980 | | : date: Thu Jan 01 00:00:25 1970 +0000
980 | | : date: Thu Jan 01 00:00:25 1970 +0000
981 | | : summary: (25) merge one known; far left
981 | | : summary: (25) merge one known; far left
982 | | :
982 | | :
983 | o : changeset: 24:a9c19a3d96b7
983 | o : changeset: 24:a9c19a3d96b7
984 | |\ \ parent: 0:e6eb3150255d
984 | |\ \ parent: 0:e6eb3150255d
985 | | ~ : parent: 23:a01cddf0766d
985 | | ~ : parent: 23:a01cddf0766d
986 | | : user: test
986 | | : user: test
987 | | : date: Thu Jan 01 00:00:24 1970 +0000
987 | | : date: Thu Jan 01 00:00:24 1970 +0000
988 | | : summary: (24) merge one known; immediate right
988 | | : summary: (24) merge one known; immediate right
989 | | /
989 | | /
990 | o : changeset: 23:a01cddf0766d
990 | o : changeset: 23:a01cddf0766d
991 | |\ \ parent: 1:6db2ef61d156
991 | |\ \ parent: 1:6db2ef61d156
992 | | ~ : parent: 22:e0d9cccacb5d
992 | | ~ : parent: 22:e0d9cccacb5d
993 | | : user: test
993 | | : user: test
994 | | : date: Thu Jan 01 00:00:23 1970 +0000
994 | | : date: Thu Jan 01 00:00:23 1970 +0000
995 | | : summary: (23) merge one known; immediate left
995 | | : summary: (23) merge one known; immediate left
996 | | /
996 | | /
997 | o : changeset: 22:e0d9cccacb5d
997 | o : changeset: 22:e0d9cccacb5d
998 |/:/ parent: 18:1aa84d96232a
998 |/:/ parent: 18:1aa84d96232a
999 | : parent: 21:d42a756af44d
999 | : parent: 21:d42a756af44d
1000 | : user: test
1000 | : user: test
1001 | : date: Thu Jan 01 00:00:22 1970 +0000
1001 | : date: Thu Jan 01 00:00:22 1970 +0000
1002 | : summary: (22) merge two known; one far left, one far right
1002 | : summary: (22) merge two known; one far left, one far right
1003 | :
1003 | :
1004 | o changeset: 21:d42a756af44d
1004 | o changeset: 21:d42a756af44d
1005 | |\ parent: 19:31ddc2c1573b
1005 | |\ parent: 19:31ddc2c1573b
1006 | | | parent: 20:d30ed6450e32
1006 | | | parent: 20:d30ed6450e32
1007 | | | user: test
1007 | | | user: test
1008 | | | date: Thu Jan 01 00:00:21 1970 +0000
1008 | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 | | | summary: (21) expand
1009 | | | summary: (21) expand
1010 | | |
1010 | | |
1011 +---o changeset: 20:d30ed6450e32
1011 +---o changeset: 20:d30ed6450e32
1012 | | | parent: 0:e6eb3150255d
1012 | | | parent: 0:e6eb3150255d
1013 | | ~ parent: 18:1aa84d96232a
1013 | | ~ parent: 18:1aa84d96232a
1014 | | user: test
1014 | | user: test
1015 | | date: Thu Jan 01 00:00:20 1970 +0000
1015 | | date: Thu Jan 01 00:00:20 1970 +0000
1016 | | summary: (20) merge two known; two far right
1016 | | summary: (20) merge two known; two far right
1017 | |
1017 | |
1018 | o changeset: 19:31ddc2c1573b
1018 | o changeset: 19:31ddc2c1573b
1019 | |\ parent: 15:1dda3f72782d
1019 | |\ parent: 15:1dda3f72782d
1020 | | | parent: 17:44765d7c06e0
1020 | | | parent: 17:44765d7c06e0
1021 | | | user: test
1021 | | | user: test
1022 | | | date: Thu Jan 01 00:00:19 1970 +0000
1022 | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 | | | summary: (19) expand
1023 | | | summary: (19) expand
1024 | | |
1024 | | |
1025 o | | changeset: 18:1aa84d96232a
1025 o | | changeset: 18:1aa84d96232a
1026 |\| | parent: 1:6db2ef61d156
1026 |\| | parent: 1:6db2ef61d156
1027 ~ | | parent: 15:1dda3f72782d
1027 ~ | | parent: 15:1dda3f72782d
1028 | | user: test
1028 | | user: test
1029 | | date: Thu Jan 01 00:00:18 1970 +0000
1029 | | date: Thu Jan 01 00:00:18 1970 +0000
1030 | | summary: (18) merge two known; two far left
1030 | | summary: (18) merge two known; two far left
1031 / /
1031 / /
1032 | o changeset: 17:44765d7c06e0
1032 | o changeset: 17:44765d7c06e0
1033 | |\ parent: 12:86b91144a6e9
1033 | |\ parent: 12:86b91144a6e9
1034 | | | parent: 16:3677d192927d
1034 | | | parent: 16:3677d192927d
1035 | | | user: test
1035 | | | user: test
1036 | | | date: Thu Jan 01 00:00:17 1970 +0000
1036 | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 | | | summary: (17) expand
1037 | | | summary: (17) expand
1038 | | |
1038 | | |
1039 | | o changeset: 16:3677d192927d
1039 | | o changeset: 16:3677d192927d
1040 | | |\ parent: 0:e6eb3150255d
1040 | | |\ parent: 0:e6eb3150255d
1041 | | ~ ~ parent: 1:6db2ef61d156
1041 | | ~ ~ parent: 1:6db2ef61d156
1042 | | user: test
1042 | | user: test
1043 | | date: Thu Jan 01 00:00:16 1970 +0000
1043 | | date: Thu Jan 01 00:00:16 1970 +0000
1044 | | summary: (16) merge two known; one immediate right, one near right
1044 | | summary: (16) merge two known; one immediate right, one near right
1045 | |
1045 | |
1046 o | changeset: 15:1dda3f72782d
1046 o | changeset: 15:1dda3f72782d
1047 |\ \ parent: 13:22d8966a97e3
1047 |\ \ parent: 13:22d8966a97e3
1048 | | | parent: 14:8eac370358ef
1048 | | | parent: 14:8eac370358ef
1049 | | | user: test
1049 | | | user: test
1050 | | | date: Thu Jan 01 00:00:15 1970 +0000
1050 | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 | | | summary: (15) expand
1051 | | | summary: (15) expand
1052 | | |
1052 | | |
1053 | o | changeset: 14:8eac370358ef
1053 | o | changeset: 14:8eac370358ef
1054 | |\| parent: 0:e6eb3150255d
1054 | |\| parent: 0:e6eb3150255d
1055 | ~ | parent: 12:86b91144a6e9
1055 | ~ | parent: 12:86b91144a6e9
1056 | | user: test
1056 | | user: test
1057 | | date: Thu Jan 01 00:00:14 1970 +0000
1057 | | date: Thu Jan 01 00:00:14 1970 +0000
1058 | | summary: (14) merge two known; one immediate right, one far right
1058 | | summary: (14) merge two known; one immediate right, one far right
1059 | /
1059 | /
1060 o | changeset: 13:22d8966a97e3
1060 o | changeset: 13:22d8966a97e3
1061 |\ \ parent: 9:7010c0af0a35
1061 |\ \ parent: 9:7010c0af0a35
1062 | | | parent: 11:832d76e6bdf2
1062 | | | parent: 11:832d76e6bdf2
1063 | | | user: test
1063 | | | user: test
1064 | | | date: Thu Jan 01 00:00:13 1970 +0000
1064 | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 | | | summary: (13) expand
1065 | | | summary: (13) expand
1066 | | |
1066 | | |
1067 +---o changeset: 12:86b91144a6e9
1067 +---o changeset: 12:86b91144a6e9
1068 | | | parent: 1:6db2ef61d156
1068 | | | parent: 1:6db2ef61d156
1069 | | ~ parent: 9:7010c0af0a35
1069 | | ~ parent: 9:7010c0af0a35
1070 | | user: test
1070 | | user: test
1071 | | date: Thu Jan 01 00:00:12 1970 +0000
1071 | | date: Thu Jan 01 00:00:12 1970 +0000
1072 | | summary: (12) merge two known; one immediate right, one far left
1072 | | summary: (12) merge two known; one immediate right, one far left
1073 | |
1073 | |
1074 | o changeset: 11:832d76e6bdf2
1074 | o changeset: 11:832d76e6bdf2
1075 | |\ parent: 6:b105a072e251
1075 | |\ parent: 6:b105a072e251
1076 | | | parent: 10:74c64d036d72
1076 | | | parent: 10:74c64d036d72
1077 | | | user: test
1077 | | | user: test
1078 | | | date: Thu Jan 01 00:00:11 1970 +0000
1078 | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 | | | summary: (11) expand
1079 | | | summary: (11) expand
1080 | | |
1080 | | |
1081 | | o changeset: 10:74c64d036d72
1081 | | o changeset: 10:74c64d036d72
1082 | |/| parent: 0:e6eb3150255d
1082 | |/| parent: 0:e6eb3150255d
1083 | | ~ parent: 6:b105a072e251
1083 | | ~ parent: 6:b105a072e251
1084 | | user: test
1084 | | user: test
1085 | | date: Thu Jan 01 00:00:10 1970 +0000
1085 | | date: Thu Jan 01 00:00:10 1970 +0000
1086 | | summary: (10) merge two known; one immediate left, one near right
1086 | | summary: (10) merge two known; one immediate left, one near right
1087 | |
1087 | |
1088 o | changeset: 9:7010c0af0a35
1088 o | changeset: 9:7010c0af0a35
1089 |\ \ parent: 7:b632bb1b1224
1089 |\ \ parent: 7:b632bb1b1224
1090 | | | parent: 8:7a0b11f71937
1090 | | | parent: 8:7a0b11f71937
1091 | | | user: test
1091 | | | user: test
1092 | | | date: Thu Jan 01 00:00:09 1970 +0000
1092 | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 | | | summary: (9) expand
1093 | | | summary: (9) expand
1094 | | |
1094 | | |
1095 | o | changeset: 8:7a0b11f71937
1095 | o | changeset: 8:7a0b11f71937
1096 |/| | parent: 0:e6eb3150255d
1096 |/| | parent: 0:e6eb3150255d
1097 | ~ | parent: 7:b632bb1b1224
1097 | ~ | parent: 7:b632bb1b1224
1098 | | user: test
1098 | | user: test
1099 | | date: Thu Jan 01 00:00:08 1970 +0000
1099 | | date: Thu Jan 01 00:00:08 1970 +0000
1100 | | summary: (8) merge two known; one immediate left, one far right
1100 | | summary: (8) merge two known; one immediate left, one far right
1101 | /
1101 | /
1102 o | changeset: 7:b632bb1b1224
1102 o | changeset: 7:b632bb1b1224
1103 |\ \ parent: 2:3d9a33b8d1e1
1103 |\ \ parent: 2:3d9a33b8d1e1
1104 | ~ | parent: 5:4409d547b708
1104 | ~ | parent: 5:4409d547b708
1105 | | user: test
1105 | | user: test
1106 | | date: Thu Jan 01 00:00:07 1970 +0000
1106 | | date: Thu Jan 01 00:00:07 1970 +0000
1107 | | summary: (7) expand
1107 | | summary: (7) expand
1108 | /
1108 | /
1109 | o changeset: 6:b105a072e251
1109 | o changeset: 6:b105a072e251
1110 |/| parent: 2:3d9a33b8d1e1
1110 |/| parent: 2:3d9a33b8d1e1
1111 | ~ parent: 5:4409d547b708
1111 | ~ parent: 5:4409d547b708
1112 | user: test
1112 | user: test
1113 | date: Thu Jan 01 00:00:06 1970 +0000
1113 | date: Thu Jan 01 00:00:06 1970 +0000
1114 | summary: (6) merge two known; one immediate left, one far left
1114 | summary: (6) merge two known; one immediate left, one far left
1115 |
1115 |
1116 o changeset: 5:4409d547b708
1116 o changeset: 5:4409d547b708
1117 |\ parent: 3:27eef8ed80b4
1117 |\ parent: 3:27eef8ed80b4
1118 | ~ parent: 4:26a8bac39d9f
1118 | ~ parent: 4:26a8bac39d9f
1119 | user: test
1119 | user: test
1120 | date: Thu Jan 01 00:00:05 1970 +0000
1120 | date: Thu Jan 01 00:00:05 1970 +0000
1121 | summary: (5) expand
1121 | summary: (5) expand
1122 |
1122 |
1123 o changeset: 4:26a8bac39d9f
1123 o changeset: 4:26a8bac39d9f
1124 |\ parent: 1:6db2ef61d156
1124 |\ parent: 1:6db2ef61d156
1125 ~ ~ parent: 3:27eef8ed80b4
1125 ~ ~ parent: 3:27eef8ed80b4
1126 user: test
1126 user: test
1127 date: Thu Jan 01 00:00:04 1970 +0000
1127 date: Thu Jan 01 00:00:04 1970 +0000
1128 summary: (4) merge two known; one immediate left, one immediate right
1128 summary: (4) merge two known; one immediate left, one immediate right
1129
1129
1130
1130
1131
1131
1132 Empty revision range - display nothing:
1132 Empty revision range - display nothing:
1133 $ hg log -G -r 1..0
1133 $ hg log -G -r 1..0
1134
1134
1135 $ cd ..
1135 $ cd ..
1136
1136
1137 #if no-outer-repo
1137 #if no-outer-repo
1138
1138
1139 From outer space:
1139 From outer space:
1140 $ hg log -G -l1 repo
1140 $ hg log -G -l1 repo
1141 @ changeset: 34:fea3ac5810e0
1141 @ changeset: 34:fea3ac5810e0
1142 | tag: tip
1142 | tag: tip
1143 ~ parent: 32:d06dffa21a31
1143 ~ parent: 32:d06dffa21a31
1144 user: test
1144 user: test
1145 date: Thu Jan 01 00:00:34 1970 +0000
1145 date: Thu Jan 01 00:00:34 1970 +0000
1146 summary: (34) head
1146 summary: (34) head
1147
1147
1148 $ hg log -G -l1 repo/a
1148 $ hg log -G -l1 repo/a
1149 @ changeset: 34:fea3ac5810e0
1149 @ changeset: 34:fea3ac5810e0
1150 | tag: tip
1150 | tag: tip
1151 ~ parent: 32:d06dffa21a31
1151 ~ parent: 32:d06dffa21a31
1152 user: test
1152 user: test
1153 date: Thu Jan 01 00:00:34 1970 +0000
1153 date: Thu Jan 01 00:00:34 1970 +0000
1154 summary: (34) head
1154 summary: (34) head
1155
1155
1156 $ hg log -G -l1 repo/missing
1156 $ hg log -G -l1 repo/missing
1157
1157
1158 #endif
1158 #endif
1159
1159
1160 File log with revs != cset revs:
1160 File log with revs != cset revs:
1161 $ hg init flog
1161 $ hg init flog
1162 $ cd flog
1162 $ cd flog
1163 $ echo one >one
1163 $ echo one >one
1164 $ hg add one
1164 $ hg add one
1165 $ hg commit -mone
1165 $ hg commit -mone
1166 $ echo two >two
1166 $ echo two >two
1167 $ hg add two
1167 $ hg add two
1168 $ hg commit -mtwo
1168 $ hg commit -mtwo
1169 $ echo more >two
1169 $ echo more >two
1170 $ hg commit -mmore
1170 $ hg commit -mmore
1171 $ hg log -G two
1171 $ hg log -G two
1172 @ changeset: 2:12c28321755b
1172 @ changeset: 2:12c28321755b
1173 | tag: tip
1173 | tag: tip
1174 | user: test
1174 | user: test
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1176 | summary: more
1176 | summary: more
1177 |
1177 |
1178 o changeset: 1:5ac72c0599bf
1178 o changeset: 1:5ac72c0599bf
1179 | user: test
1179 | user: test
1180 ~ date: Thu Jan 01 00:00:00 1970 +0000
1180 ~ date: Thu Jan 01 00:00:00 1970 +0000
1181 summary: two
1181 summary: two
1182
1182
1183
1183
1184 Issue1896: File log with explicit style
1184 Issue1896: File log with explicit style
1185 $ hg log -G --style=default one
1185 $ hg log -G --style=default one
1186 o changeset: 0:3d578b4a1f53
1186 o changeset: 0:3d578b4a1f53
1187 user: test
1187 user: test
1188 date: Thu Jan 01 00:00:00 1970 +0000
1188 date: Thu Jan 01 00:00:00 1970 +0000
1189 summary: one
1189 summary: one
1190
1190
1191 Issue2395: glog --style header and footer
1191 Issue2395: glog --style header and footer
1192 $ hg log -G --style=xml one
1192 $ hg log -G --style=xml one
1193 <?xml version="1.0"?>
1193 <?xml version="1.0"?>
1194 <log>
1194 <log>
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 <author email="test">test</author>
1196 <author email="test">test</author>
1197 <date>1970-01-01T00:00:00+00:00</date>
1197 <date>1970-01-01T00:00:00+00:00</date>
1198 <msg xml:space="preserve">one</msg>
1198 <msg xml:space="preserve">one</msg>
1199 </logentry>
1199 </logentry>
1200 </log>
1200 </log>
1201
1201
1202 $ cd ..
1202 $ cd ..
1203
1203
1204 Incoming and outgoing:
1204 Incoming and outgoing:
1205
1205
1206 $ hg clone -U -r31 repo repo2
1206 $ hg clone -U -r31 repo repo2
1207 adding changesets
1207 adding changesets
1208 adding manifests
1208 adding manifests
1209 adding file changes
1209 adding file changes
1210 added 31 changesets with 31 changes to 1 files
1210 added 31 changesets with 31 changes to 1 files
1211 $ cd repo2
1211 $ cd repo2
1212
1212
1213 $ hg incoming --graph ../repo
1213 $ hg incoming --graph ../repo
1214 comparing with ../repo
1214 comparing with ../repo
1215 searching for changes
1215 searching for changes
1216 o changeset: 34:fea3ac5810e0
1216 o changeset: 34:fea3ac5810e0
1217 | tag: tip
1217 | tag: tip
1218 | parent: 32:d06dffa21a31
1218 | parent: 32:d06dffa21a31
1219 | user: test
1219 | user: test
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1221 | summary: (34) head
1221 | summary: (34) head
1222 |
1222 |
1223 | o changeset: 33:68608f5145f9
1223 | o changeset: 33:68608f5145f9
1224 | parent: 18:1aa84d96232a
1224 | parent: 18:1aa84d96232a
1225 | user: test
1225 | user: test
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1227 | summary: (33) head
1227 | summary: (33) head
1228 |
1228 |
1229 o changeset: 32:d06dffa21a31
1229 o changeset: 32:d06dffa21a31
1230 | parent: 27:886ed638191b
1230 | parent: 27:886ed638191b
1231 | parent: 31:621d83e11f67
1231 | parent: 31:621d83e11f67
1232 | user: test
1232 | user: test
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1234 | summary: (32) expand
1234 | summary: (32) expand
1235 |
1235 |
1236 o changeset: 27:886ed638191b
1236 o changeset: 27:886ed638191b
1237 parent: 21:d42a756af44d
1237 parent: 21:d42a756af44d
1238 user: test
1238 user: test
1239 date: Thu Jan 01 00:00:27 1970 +0000
1239 date: Thu Jan 01 00:00:27 1970 +0000
1240 summary: (27) collapse
1240 summary: (27) collapse
1241
1241
1242 $ cd ..
1242 $ cd ..
1243
1243
1244 $ hg -R repo outgoing --graph repo2
1244 $ hg -R repo outgoing --graph repo2
1245 comparing with repo2
1245 comparing with repo2
1246 searching for changes
1246 searching for changes
1247 @ changeset: 34:fea3ac5810e0
1247 @ changeset: 34:fea3ac5810e0
1248 | tag: tip
1248 | tag: tip
1249 | parent: 32:d06dffa21a31
1249 | parent: 32:d06dffa21a31
1250 | user: test
1250 | user: test
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1252 | summary: (34) head
1252 | summary: (34) head
1253 |
1253 |
1254 | o changeset: 33:68608f5145f9
1254 | o changeset: 33:68608f5145f9
1255 | parent: 18:1aa84d96232a
1255 | parent: 18:1aa84d96232a
1256 | user: test
1256 | user: test
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1258 | summary: (33) head
1258 | summary: (33) head
1259 |
1259 |
1260 o changeset: 32:d06dffa21a31
1260 o changeset: 32:d06dffa21a31
1261 | parent: 27:886ed638191b
1261 | parent: 27:886ed638191b
1262 | parent: 31:621d83e11f67
1262 | parent: 31:621d83e11f67
1263 | user: test
1263 | user: test
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1265 | summary: (32) expand
1265 | summary: (32) expand
1266 |
1266 |
1267 o changeset: 27:886ed638191b
1267 o changeset: 27:886ed638191b
1268 parent: 21:d42a756af44d
1268 parent: 21:d42a756af44d
1269 user: test
1269 user: test
1270 date: Thu Jan 01 00:00:27 1970 +0000
1270 date: Thu Jan 01 00:00:27 1970 +0000
1271 summary: (27) collapse
1271 summary: (27) collapse
1272
1272
1273
1273
1274 File + limit with revs != cset revs:
1274 File + limit with revs != cset revs:
1275 $ cd repo
1275 $ cd repo
1276 $ touch b
1276 $ touch b
1277 $ hg ci -Aqm0
1277 $ hg ci -Aqm0
1278 $ hg log -G -l2 a
1278 $ hg log -G -l2 a
1279 o changeset: 34:fea3ac5810e0
1279 o changeset: 34:fea3ac5810e0
1280 | parent: 32:d06dffa21a31
1280 | parent: 32:d06dffa21a31
1281 ~ user: test
1281 ~ user: test
1282 date: Thu Jan 01 00:00:34 1970 +0000
1282 date: Thu Jan 01 00:00:34 1970 +0000
1283 summary: (34) head
1283 summary: (34) head
1284
1284
1285 o changeset: 33:68608f5145f9
1285 o changeset: 33:68608f5145f9
1286 | parent: 18:1aa84d96232a
1286 | parent: 18:1aa84d96232a
1287 ~ user: test
1287 ~ user: test
1288 date: Thu Jan 01 00:00:33 1970 +0000
1288 date: Thu Jan 01 00:00:33 1970 +0000
1289 summary: (33) head
1289 summary: (33) head
1290
1290
1291
1291
1292 File + limit + -ra:b, (b - a) < limit:
1292 File + limit + -ra:b, (b - a) < limit:
1293 $ hg log -G -l3000 -r32:tip a
1293 $ hg log -G -l3000 -r32:tip a
1294 o changeset: 34:fea3ac5810e0
1294 o changeset: 34:fea3ac5810e0
1295 | parent: 32:d06dffa21a31
1295 | parent: 32:d06dffa21a31
1296 | user: test
1296 | user: test
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1298 | summary: (34) head
1298 | summary: (34) head
1299 |
1299 |
1300 | o changeset: 33:68608f5145f9
1300 | o changeset: 33:68608f5145f9
1301 | | parent: 18:1aa84d96232a
1301 | | parent: 18:1aa84d96232a
1302 | ~ user: test
1302 | ~ user: test
1303 | date: Thu Jan 01 00:00:33 1970 +0000
1303 | date: Thu Jan 01 00:00:33 1970 +0000
1304 | summary: (33) head
1304 | summary: (33) head
1305 |
1305 |
1306 o changeset: 32:d06dffa21a31
1306 o changeset: 32:d06dffa21a31
1307 |\ parent: 27:886ed638191b
1307 |\ parent: 27:886ed638191b
1308 ~ ~ parent: 31:621d83e11f67
1308 ~ ~ parent: 31:621d83e11f67
1309 user: test
1309 user: test
1310 date: Thu Jan 01 00:00:32 1970 +0000
1310 date: Thu Jan 01 00:00:32 1970 +0000
1311 summary: (32) expand
1311 summary: (32) expand
1312
1312
1313
1313
1314 Point out a common and an uncommon unshown parent
1314 Point out a common and an uncommon unshown parent
1315
1315
1316 $ hg log -G -r 'rev(8) or rev(9)'
1316 $ hg log -G -r 'rev(8) or rev(9)'
1317 o changeset: 9:7010c0af0a35
1317 o changeset: 9:7010c0af0a35
1318 |\ parent: 7:b632bb1b1224
1318 |\ parent: 7:b632bb1b1224
1319 | ~ parent: 8:7a0b11f71937
1319 | ~ parent: 8:7a0b11f71937
1320 | user: test
1320 | user: test
1321 | date: Thu Jan 01 00:00:09 1970 +0000
1321 | date: Thu Jan 01 00:00:09 1970 +0000
1322 | summary: (9) expand
1322 | summary: (9) expand
1323 |
1323 |
1324 o changeset: 8:7a0b11f71937
1324 o changeset: 8:7a0b11f71937
1325 |\ parent: 0:e6eb3150255d
1325 |\ parent: 0:e6eb3150255d
1326 ~ ~ parent: 7:b632bb1b1224
1326 ~ ~ parent: 7:b632bb1b1224
1327 user: test
1327 user: test
1328 date: Thu Jan 01 00:00:08 1970 +0000
1328 date: Thu Jan 01 00:00:08 1970 +0000
1329 summary: (8) merge two known; one immediate left, one far right
1329 summary: (8) merge two known; one immediate left, one far right
1330
1330
1331
1331
1332 File + limit + -ra:b, b < tip:
1332 File + limit + -ra:b, b < tip:
1333
1333
1334 $ hg log -G -l1 -r32:34 a
1334 $ hg log -G -l1 -r32:34 a
1335 o changeset: 34:fea3ac5810e0
1335 o changeset: 34:fea3ac5810e0
1336 | parent: 32:d06dffa21a31
1336 | parent: 32:d06dffa21a31
1337 ~ user: test
1337 ~ user: test
1338 date: Thu Jan 01 00:00:34 1970 +0000
1338 date: Thu Jan 01 00:00:34 1970 +0000
1339 summary: (34) head
1339 summary: (34) head
1340
1340
1341
1341
1342 file(File) + limit + -ra:b, b < tip:
1342 file(File) + limit + -ra:b, b < tip:
1343
1343
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1345 o changeset: 34:fea3ac5810e0
1345 o changeset: 34:fea3ac5810e0
1346 | parent: 32:d06dffa21a31
1346 | parent: 32:d06dffa21a31
1347 ~ user: test
1347 ~ user: test
1348 date: Thu Jan 01 00:00:34 1970 +0000
1348 date: Thu Jan 01 00:00:34 1970 +0000
1349 summary: (34) head
1349 summary: (34) head
1350
1350
1351
1351
1352 limit(file(File) and a::b), b < tip:
1352 limit(file(File) and a::b), b < tip:
1353
1353
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1355 o changeset: 32:d06dffa21a31
1355 o changeset: 32:d06dffa21a31
1356 |\ parent: 27:886ed638191b
1356 |\ parent: 27:886ed638191b
1357 ~ ~ parent: 31:621d83e11f67
1357 ~ ~ parent: 31:621d83e11f67
1358 user: test
1358 user: test
1359 date: Thu Jan 01 00:00:32 1970 +0000
1359 date: Thu Jan 01 00:00:32 1970 +0000
1360 summary: (32) expand
1360 summary: (32) expand
1361
1361
1362
1362
1363 File + limit + -ra:b, b < tip:
1363 File + limit + -ra:b, b < tip:
1364
1364
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1366
1366
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1368
1368
1369 $ hg log -G -l10 -r33:34 a
1369 $ hg log -G -l10 -r33:34 a
1370 o changeset: 34:fea3ac5810e0
1370 o changeset: 34:fea3ac5810e0
1371 | parent: 32:d06dffa21a31
1371 | parent: 32:d06dffa21a31
1372 ~ user: test
1372 ~ user: test
1373 date: Thu Jan 01 00:00:34 1970 +0000
1373 date: Thu Jan 01 00:00:34 1970 +0000
1374 summary: (34) head
1374 summary: (34) head
1375
1375
1376 o changeset: 33:68608f5145f9
1376 o changeset: 33:68608f5145f9
1377 | parent: 18:1aa84d96232a
1377 | parent: 18:1aa84d96232a
1378 ~ user: test
1378 ~ user: test
1379 date: Thu Jan 01 00:00:33 1970 +0000
1379 date: Thu Jan 01 00:00:33 1970 +0000
1380 summary: (33) head
1380 summary: (33) head
1381
1381
1382
1382
1383 Do not crash or produce strange graphs if history is buggy
1383 Do not crash or produce strange graphs if history is buggy
1384
1384
1385 $ hg branch branch
1385 $ hg branch branch
1386 marked working directory as branch branch
1386 marked working directory as branch branch
1387 (branches are permanent and global, did you want a bookmark?)
1387 (branches are permanent and global, did you want a bookmark?)
1388 $ commit 36 "buggy merge: identical parents" 35 35
1388 $ commit 36 "buggy merge: identical parents" 35 35
1389 $ hg log -G -l5
1389 $ hg log -G -l5
1390 @ changeset: 36:08a19a744424
1390 @ changeset: 36:08a19a744424
1391 | branch: branch
1391 | branch: branch
1392 | tag: tip
1392 | tag: tip
1393 | parent: 35:9159c3644c5e
1393 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1395 | user: test
1395 | user: test
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1397 | summary: (36) buggy merge: identical parents
1397 | summary: (36) buggy merge: identical parents
1398 |
1398 |
1399 o changeset: 35:9159c3644c5e
1399 o changeset: 35:9159c3644c5e
1400 | user: test
1400 | user: test
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1402 | summary: 0
1402 | summary: 0
1403 |
1403 |
1404 o changeset: 34:fea3ac5810e0
1404 o changeset: 34:fea3ac5810e0
1405 | parent: 32:d06dffa21a31
1405 | parent: 32:d06dffa21a31
1406 | user: test
1406 | user: test
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1408 | summary: (34) head
1408 | summary: (34) head
1409 |
1409 |
1410 | o changeset: 33:68608f5145f9
1410 | o changeset: 33:68608f5145f9
1411 | | parent: 18:1aa84d96232a
1411 | | parent: 18:1aa84d96232a
1412 | ~ user: test
1412 | ~ user: test
1413 | date: Thu Jan 01 00:00:33 1970 +0000
1413 | date: Thu Jan 01 00:00:33 1970 +0000
1414 | summary: (33) head
1414 | summary: (33) head
1415 |
1415 |
1416 o changeset: 32:d06dffa21a31
1416 o changeset: 32:d06dffa21a31
1417 |\ parent: 27:886ed638191b
1417 |\ parent: 27:886ed638191b
1418 ~ ~ parent: 31:621d83e11f67
1418 ~ ~ parent: 31:621d83e11f67
1419 user: test
1419 user: test
1420 date: Thu Jan 01 00:00:32 1970 +0000
1420 date: Thu Jan 01 00:00:32 1970 +0000
1421 summary: (32) expand
1421 summary: (32) expand
1422
1422
1423
1423
1424 Test log -G options
1424 Test log -G options
1425
1425
1426 $ testlog() {
1426 $ testlog() {
1427 > hg log -G --print-revset "$@"
1427 > hg log -G --print-revset "$@"
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1432 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1433 > | grep '^[-+@ ]' || :
1433 > | grep '^[-+@ ]' || :
1434 > }
1434 > }
1435
1435
1436 glog always reorders nodes which explains the difference with log
1436 glog always reorders nodes which explains the difference with log
1437
1437
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1439 ['27', '25', '21', '34', '32', '31']
1439 ['27', '25', '21', '34', '32', '31']
1440 []
1440 []
1441 --- log.nodes * (glob)
1441 --- log.nodes * (glob)
1442 +++ glog.nodes * (glob)
1442 +++ glog.nodes * (glob)
1443 @@ -1,6 +1,6 @@
1443 @@ -1,6 +1,6 @@
1444 -nodetag 27
1444 -nodetag 27
1445 -nodetag 25
1445 -nodetag 25
1446 -nodetag 21
1446 -nodetag 21
1447 nodetag 34
1447 nodetag 34
1448 nodetag 32
1448 nodetag 32
1449 nodetag 31
1449 nodetag 31
1450 +nodetag 27
1450 +nodetag 27
1451 +nodetag 25
1451 +nodetag 25
1452 +nodetag 21
1452 +nodetag 21
1453 $ testlog -u test -u not-a-user
1453 $ testlog -u test -u not-a-user
1454 []
1454 []
1455 (group
1455 (group
1456 (group
1456 (group
1457 (or
1457 (or
1458 (list
1458 (list
1459 (func
1459 (func
1460 ('symbol', 'user')
1460 ('symbol', 'user')
1461 ('string', 'test'))
1461 ('string', 'test'))
1462 (func
1462 (func
1463 ('symbol', 'user')
1463 ('symbol', 'user')
1464 ('string', 'not-a-user'))))))
1464 ('string', 'not-a-user'))))))
1465 $ testlog -b not-a-branch
1465 $ testlog -b not-a-branch
1466 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1468 abort: unknown revision 'not-a-branch'!
1468 abort: unknown revision 'not-a-branch'!
1469 $ testlog -b 35 -b 36 --only-branch branch
1469 $ testlog -b 35 -b 36 --only-branch branch
1470 []
1470 []
1471 (group
1471 (group
1472 (group
1472 (group
1473 (or
1473 (or
1474 (list
1474 (list
1475 (func
1475 (func
1476 ('symbol', 'branch')
1476 ('symbol', 'branch')
1477 ('string', 'default'))
1477 ('string', 'default'))
1478 (func
1478 (func
1479 ('symbol', 'branch')
1479 ('symbol', 'branch')
1480 ('string', 'branch'))
1480 ('string', 'branch'))
1481 (func
1481 (func
1482 ('symbol', 'branch')
1482 ('symbol', 'branch')
1483 ('string', 'branch'))))))
1483 ('string', 'branch'))))))
1484 $ testlog -k expand -k merge
1484 $ testlog -k expand -k merge
1485 []
1485 []
1486 (group
1486 (group
1487 (group
1487 (group
1488 (or
1488 (or
1489 (list
1489 (list
1490 (func
1490 (func
1491 ('symbol', 'keyword')
1491 ('symbol', 'keyword')
1492 ('string', 'expand'))
1492 ('string', 'expand'))
1493 (func
1493 (func
1494 ('symbol', 'keyword')
1494 ('symbol', 'keyword')
1495 ('string', 'merge'))))))
1495 ('string', 'merge'))))))
1496 $ testlog --only-merges
1496 $ testlog --only-merges
1497 []
1497 []
1498 (group
1498 (group
1499 (func
1499 (func
1500 ('symbol', 'merge')
1500 ('symbol', 'merge')
1501 None))
1501 None))
1502 $ testlog --no-merges
1502 $ testlog --no-merges
1503 []
1503 []
1504 (group
1504 (group
1505 (not
1505 (not
1506 (func
1506 (func
1507 ('symbol', 'merge')
1507 ('symbol', 'merge')
1508 None)))
1508 None)))
1509 $ testlog --date '2 0 to 4 0'
1509 $ testlog --date '2 0 to 4 0'
1510 []
1510 []
1511 (group
1511 (group
1512 (func
1512 (func
1513 ('symbol', 'date')
1513 ('symbol', 'date')
1514 ('string', '2 0 to 4 0')))
1514 ('string', '2 0 to 4 0')))
1515 $ hg log -G -d 'brace ) in a date'
1515 $ hg log -G -d 'brace ) in a date'
1516 abort: invalid date: 'brace ) in a date'
1516 hg: parse error: invalid date: 'brace ) in a date'
1517 [255]
1517 [255]
1518 $ testlog --prune 31 --prune 32
1518 $ testlog --prune 31 --prune 32
1519 []
1519 []
1520 (group
1520 (group
1521 (group
1521 (group
1522 (and
1522 (and
1523 (not
1523 (not
1524 (group
1524 (group
1525 (or
1525 (or
1526 (list
1526 (list
1527 ('string', '31')
1527 ('string', '31')
1528 (func
1528 (func
1529 ('symbol', 'ancestors')
1529 ('symbol', 'ancestors')
1530 ('string', '31'))))))
1530 ('string', '31'))))))
1531 (not
1531 (not
1532 (group
1532 (group
1533 (or
1533 (or
1534 (list
1534 (list
1535 ('string', '32')
1535 ('string', '32')
1536 (func
1536 (func
1537 ('symbol', 'ancestors')
1537 ('symbol', 'ancestors')
1538 ('string', '32')))))))))
1538 ('string', '32')))))))))
1539
1539
1540 Dedicated repo for --follow and paths filtering. The g is crafted to
1540 Dedicated repo for --follow and paths filtering. The g is crafted to
1541 have 2 filelog topological heads in a linear changeset graph.
1541 have 2 filelog topological heads in a linear changeset graph.
1542
1542
1543 $ cd ..
1543 $ cd ..
1544 $ hg init follow
1544 $ hg init follow
1545 $ cd follow
1545 $ cd follow
1546 $ testlog --follow
1546 $ testlog --follow
1547 []
1547 []
1548 []
1548 []
1549 $ testlog -rnull
1549 $ testlog -rnull
1550 ['null']
1550 ['null']
1551 []
1551 []
1552 $ echo a > a
1552 $ echo a > a
1553 $ echo aa > aa
1553 $ echo aa > aa
1554 $ echo f > f
1554 $ echo f > f
1555 $ hg ci -Am "add a" a aa f
1555 $ hg ci -Am "add a" a aa f
1556 $ hg cp a b
1556 $ hg cp a b
1557 $ hg cp f g
1557 $ hg cp f g
1558 $ hg ci -m "copy a b"
1558 $ hg ci -m "copy a b"
1559 $ mkdir dir
1559 $ mkdir dir
1560 $ hg mv b dir
1560 $ hg mv b dir
1561 $ echo g >> g
1561 $ echo g >> g
1562 $ echo f >> f
1562 $ echo f >> f
1563 $ hg ci -m "mv b dir/b"
1563 $ hg ci -m "mv b dir/b"
1564 $ hg mv a b
1564 $ hg mv a b
1565 $ hg cp -f f g
1565 $ hg cp -f f g
1566 $ echo a > d
1566 $ echo a > d
1567 $ hg add d
1567 $ hg add d
1568 $ hg ci -m "mv a b; add d"
1568 $ hg ci -m "mv a b; add d"
1569 $ hg mv dir/b e
1569 $ hg mv dir/b e
1570 $ hg ci -m "mv dir/b e"
1570 $ hg ci -m "mv dir/b e"
1571 $ hg log -G --template '({rev}) {desc|firstline}\n'
1571 $ hg log -G --template '({rev}) {desc|firstline}\n'
1572 @ (4) mv dir/b e
1572 @ (4) mv dir/b e
1573 |
1573 |
1574 o (3) mv a b; add d
1574 o (3) mv a b; add d
1575 |
1575 |
1576 o (2) mv b dir/b
1576 o (2) mv b dir/b
1577 |
1577 |
1578 o (1) copy a b
1578 o (1) copy a b
1579 |
1579 |
1580 o (0) add a
1580 o (0) add a
1581
1581
1582
1582
1583 $ testlog a
1583 $ testlog a
1584 []
1584 []
1585 (group
1585 (group
1586 (group
1586 (group
1587 (func
1587 (func
1588 ('symbol', 'filelog')
1588 ('symbol', 'filelog')
1589 ('string', 'a'))))
1589 ('string', 'a'))))
1590 $ testlog a b
1590 $ testlog a b
1591 []
1591 []
1592 (group
1592 (group
1593 (group
1593 (group
1594 (or
1594 (or
1595 (list
1595 (list
1596 (func
1596 (func
1597 ('symbol', 'filelog')
1597 ('symbol', 'filelog')
1598 ('string', 'a'))
1598 ('string', 'a'))
1599 (func
1599 (func
1600 ('symbol', 'filelog')
1600 ('symbol', 'filelog')
1601 ('string', 'b'))))))
1601 ('string', 'b'))))))
1602
1602
1603 Test falling back to slow path for non-existing files
1603 Test falling back to slow path for non-existing files
1604
1604
1605 $ testlog a c
1605 $ testlog a c
1606 []
1606 []
1607 (group
1607 (group
1608 (func
1608 (func
1609 ('symbol', '_matchfiles')
1609 ('symbol', '_matchfiles')
1610 (list
1610 (list
1611 ('string', 'r:')
1611 ('string', 'r:')
1612 ('string', 'd:relpath')
1612 ('string', 'd:relpath')
1613 ('string', 'p:a')
1613 ('string', 'p:a')
1614 ('string', 'p:c'))))
1614 ('string', 'p:c'))))
1615
1615
1616 Test multiple --include/--exclude/paths
1616 Test multiple --include/--exclude/paths
1617
1617
1618 $ testlog --include a --include e --exclude b --exclude e a e
1618 $ testlog --include a --include e --exclude b --exclude e a e
1619 []
1619 []
1620 (group
1620 (group
1621 (func
1621 (func
1622 ('symbol', '_matchfiles')
1622 ('symbol', '_matchfiles')
1623 (list
1623 (list
1624 ('string', 'r:')
1624 ('string', 'r:')
1625 ('string', 'd:relpath')
1625 ('string', 'd:relpath')
1626 ('string', 'p:a')
1626 ('string', 'p:a')
1627 ('string', 'p:e')
1627 ('string', 'p:e')
1628 ('string', 'i:a')
1628 ('string', 'i:a')
1629 ('string', 'i:e')
1629 ('string', 'i:e')
1630 ('string', 'x:b')
1630 ('string', 'x:b')
1631 ('string', 'x:e'))))
1631 ('string', 'x:e'))))
1632
1632
1633 Test glob expansion of pats
1633 Test glob expansion of pats
1634
1634
1635 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1635 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1636 > print mercurial.util.expandglobs and 'true' or 'false'"`
1636 > print mercurial.util.expandglobs and 'true' or 'false'"`
1637 $ if [ $expandglobs = "true" ]; then
1637 $ if [ $expandglobs = "true" ]; then
1638 > testlog 'a*';
1638 > testlog 'a*';
1639 > else
1639 > else
1640 > testlog a*;
1640 > testlog a*;
1641 > fi;
1641 > fi;
1642 []
1642 []
1643 (group
1643 (group
1644 (group
1644 (group
1645 (func
1645 (func
1646 ('symbol', 'filelog')
1646 ('symbol', 'filelog')
1647 ('string', 'aa'))))
1647 ('string', 'aa'))))
1648
1648
1649 Test --follow on a non-existent directory
1649 Test --follow on a non-existent directory
1650
1650
1651 $ testlog -f dir
1651 $ testlog -f dir
1652 abort: cannot follow file not in parent revision: "dir"
1652 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1654 abort: cannot follow file not in parent revision: "dir"
1654 abort: cannot follow file not in parent revision: "dir"
1655
1655
1656 Test --follow on a directory
1656 Test --follow on a directory
1657
1657
1658 $ hg up -q '.^'
1658 $ hg up -q '.^'
1659 $ testlog -f dir
1659 $ testlog -f dir
1660 []
1660 []
1661 (group
1661 (group
1662 (and
1662 (and
1663 (func
1663 (func
1664 ('symbol', 'ancestors')
1664 ('symbol', 'ancestors')
1665 ('symbol', '.'))
1665 ('symbol', '.'))
1666 (func
1666 (func
1667 ('symbol', '_matchfiles')
1667 ('symbol', '_matchfiles')
1668 (list
1668 (list
1669 ('string', 'r:')
1669 ('string', 'r:')
1670 ('string', 'd:relpath')
1670 ('string', 'd:relpath')
1671 ('string', 'p:dir')))))
1671 ('string', 'p:dir')))))
1672 $ hg up -q tip
1672 $ hg up -q tip
1673
1673
1674 Test --follow on file not in parent revision
1674 Test --follow on file not in parent revision
1675
1675
1676 $ testlog -f a
1676 $ testlog -f a
1677 abort: cannot follow file not in parent revision: "a"
1677 abort: cannot follow file not in parent revision: "a"
1678 abort: cannot follow file not in parent revision: "a"
1678 abort: cannot follow file not in parent revision: "a"
1679 abort: cannot follow file not in parent revision: "a"
1679 abort: cannot follow file not in parent revision: "a"
1680
1680
1681 Test --follow and patterns
1681 Test --follow and patterns
1682
1682
1683 $ testlog -f 'glob:*'
1683 $ testlog -f 'glob:*'
1684 []
1684 []
1685 (group
1685 (group
1686 (and
1686 (and
1687 (func
1687 (func
1688 ('symbol', 'ancestors')
1688 ('symbol', 'ancestors')
1689 ('symbol', '.'))
1689 ('symbol', '.'))
1690 (func
1690 (func
1691 ('symbol', '_matchfiles')
1691 ('symbol', '_matchfiles')
1692 (list
1692 (list
1693 ('string', 'r:')
1693 ('string', 'r:')
1694 ('string', 'd:relpath')
1694 ('string', 'd:relpath')
1695 ('string', 'p:glob:*')))))
1695 ('string', 'p:glob:*')))))
1696
1696
1697 Test --follow on a single rename
1697 Test --follow on a single rename
1698
1698
1699 $ hg up -q 2
1699 $ hg up -q 2
1700 $ testlog -f a
1700 $ testlog -f a
1701 []
1701 []
1702 (group
1702 (group
1703 (group
1703 (group
1704 (func
1704 (func
1705 ('symbol', 'follow')
1705 ('symbol', 'follow')
1706 ('string', 'a'))))
1706 ('string', 'a'))))
1707
1707
1708 Test --follow and multiple renames
1708 Test --follow and multiple renames
1709
1709
1710 $ hg up -q tip
1710 $ hg up -q tip
1711 $ testlog -f e
1711 $ testlog -f e
1712 []
1712 []
1713 (group
1713 (group
1714 (group
1714 (group
1715 (func
1715 (func
1716 ('symbol', 'follow')
1716 ('symbol', 'follow')
1717 ('string', 'e'))))
1717 ('string', 'e'))))
1718
1718
1719 Test --follow and multiple filelog heads
1719 Test --follow and multiple filelog heads
1720
1720
1721 $ hg up -q 2
1721 $ hg up -q 2
1722 $ testlog -f g
1722 $ testlog -f g
1723 []
1723 []
1724 (group
1724 (group
1725 (group
1725 (group
1726 (func
1726 (func
1727 ('symbol', 'follow')
1727 ('symbol', 'follow')
1728 ('string', 'g'))))
1728 ('string', 'g'))))
1729 $ cat log.nodes
1729 $ cat log.nodes
1730 nodetag 2
1730 nodetag 2
1731 nodetag 1
1731 nodetag 1
1732 nodetag 0
1732 nodetag 0
1733 $ hg up -q tip
1733 $ hg up -q tip
1734 $ testlog -f g
1734 $ testlog -f g
1735 []
1735 []
1736 (group
1736 (group
1737 (group
1737 (group
1738 (func
1738 (func
1739 ('symbol', 'follow')
1739 ('symbol', 'follow')
1740 ('string', 'g'))))
1740 ('string', 'g'))))
1741 $ cat log.nodes
1741 $ cat log.nodes
1742 nodetag 3
1742 nodetag 3
1743 nodetag 2
1743 nodetag 2
1744 nodetag 0
1744 nodetag 0
1745
1745
1746 Test --follow and multiple files
1746 Test --follow and multiple files
1747
1747
1748 $ testlog -f g e
1748 $ testlog -f g e
1749 []
1749 []
1750 (group
1750 (group
1751 (group
1751 (group
1752 (or
1752 (or
1753 (list
1753 (list
1754 (func
1754 (func
1755 ('symbol', 'follow')
1755 ('symbol', 'follow')
1756 ('string', 'g'))
1756 ('string', 'g'))
1757 (func
1757 (func
1758 ('symbol', 'follow')
1758 ('symbol', 'follow')
1759 ('string', 'e'))))))
1759 ('string', 'e'))))))
1760 $ cat log.nodes
1760 $ cat log.nodes
1761 nodetag 4
1761 nodetag 4
1762 nodetag 3
1762 nodetag 3
1763 nodetag 2
1763 nodetag 2
1764 nodetag 1
1764 nodetag 1
1765 nodetag 0
1765 nodetag 0
1766
1766
1767 Test --follow null parent
1767 Test --follow null parent
1768
1768
1769 $ hg up -q null
1769 $ hg up -q null
1770 $ testlog -f
1770 $ testlog -f
1771 []
1771 []
1772 []
1772 []
1773
1773
1774 Test --follow-first
1774 Test --follow-first
1775
1775
1776 $ hg up -q 3
1776 $ hg up -q 3
1777 $ echo ee > e
1777 $ echo ee > e
1778 $ hg ci -Am "add another e" e
1778 $ hg ci -Am "add another e" e
1779 created new head
1779 created new head
1780 $ hg merge --tool internal:other 4
1780 $ hg merge --tool internal:other 4
1781 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1781 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1782 (branch merge, don't forget to commit)
1782 (branch merge, don't forget to commit)
1783 $ echo merge > e
1783 $ echo merge > e
1784 $ hg ci -m "merge 5 and 4"
1784 $ hg ci -m "merge 5 and 4"
1785 $ testlog --follow-first
1785 $ testlog --follow-first
1786 []
1786 []
1787 (group
1787 (group
1788 (func
1788 (func
1789 ('symbol', '_firstancestors')
1789 ('symbol', '_firstancestors')
1790 (func
1790 (func
1791 ('symbol', 'rev')
1791 ('symbol', 'rev')
1792 ('symbol', '6'))))
1792 ('symbol', '6'))))
1793
1793
1794 Cannot compare with log --follow-first FILE as it never worked
1794 Cannot compare with log --follow-first FILE as it never worked
1795
1795
1796 $ hg log -G --print-revset --follow-first e
1796 $ hg log -G --print-revset --follow-first e
1797 []
1797 []
1798 (group
1798 (group
1799 (group
1799 (group
1800 (func
1800 (func
1801 ('symbol', '_followfirst')
1801 ('symbol', '_followfirst')
1802 ('string', 'e'))))
1802 ('string', 'e'))))
1803 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1803 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1804 @ 6 merge 5 and 4
1804 @ 6 merge 5 and 4
1805 |\
1805 |\
1806 | ~
1806 | ~
1807 o 5 add another e
1807 o 5 add another e
1808 |
1808 |
1809 ~
1809 ~
1810
1810
1811 Test --copies
1811 Test --copies
1812
1812
1813 $ hg log -G --copies --template "{rev} {desc|firstline} \
1813 $ hg log -G --copies --template "{rev} {desc|firstline} \
1814 > copies: {file_copies_switch}\n"
1814 > copies: {file_copies_switch}\n"
1815 @ 6 merge 5 and 4 copies:
1815 @ 6 merge 5 and 4 copies:
1816 |\
1816 |\
1817 | o 5 add another e copies:
1817 | o 5 add another e copies:
1818 | |
1818 | |
1819 o | 4 mv dir/b e copies: e (dir/b)
1819 o | 4 mv dir/b e copies: e (dir/b)
1820 |/
1820 |/
1821 o 3 mv a b; add d copies: b (a)g (f)
1821 o 3 mv a b; add d copies: b (a)g (f)
1822 |
1822 |
1823 o 2 mv b dir/b copies: dir/b (b)
1823 o 2 mv b dir/b copies: dir/b (b)
1824 |
1824 |
1825 o 1 copy a b copies: b (a)g (f)
1825 o 1 copy a b copies: b (a)g (f)
1826 |
1826 |
1827 o 0 add a copies:
1827 o 0 add a copies:
1828
1828
1829 Test "set:..." and parent revision
1829 Test "set:..." and parent revision
1830
1830
1831 $ hg up -q 4
1831 $ hg up -q 4
1832 $ testlog "set:copied()"
1832 $ testlog "set:copied()"
1833 []
1833 []
1834 (group
1834 (group
1835 (func
1835 (func
1836 ('symbol', '_matchfiles')
1836 ('symbol', '_matchfiles')
1837 (list
1837 (list
1838 ('string', 'r:')
1838 ('string', 'r:')
1839 ('string', 'd:relpath')
1839 ('string', 'd:relpath')
1840 ('string', 'p:set:copied()'))))
1840 ('string', 'p:set:copied()'))))
1841 $ testlog --include "set:copied()"
1841 $ testlog --include "set:copied()"
1842 []
1842 []
1843 (group
1843 (group
1844 (func
1844 (func
1845 ('symbol', '_matchfiles')
1845 ('symbol', '_matchfiles')
1846 (list
1846 (list
1847 ('string', 'r:')
1847 ('string', 'r:')
1848 ('string', 'd:relpath')
1848 ('string', 'd:relpath')
1849 ('string', 'i:set:copied()'))))
1849 ('string', 'i:set:copied()'))))
1850 $ testlog -r "sort(file('set:copied()'), -rev)"
1850 $ testlog -r "sort(file('set:copied()'), -rev)"
1851 ["sort(file('set:copied()'), -rev)"]
1851 ["sort(file('set:copied()'), -rev)"]
1852 []
1852 []
1853
1853
1854 Test --removed
1854 Test --removed
1855
1855
1856 $ testlog --removed
1856 $ testlog --removed
1857 []
1857 []
1858 []
1858 []
1859 $ testlog --removed a
1859 $ testlog --removed a
1860 []
1860 []
1861 (group
1861 (group
1862 (func
1862 (func
1863 ('symbol', '_matchfiles')
1863 ('symbol', '_matchfiles')
1864 (list
1864 (list
1865 ('string', 'r:')
1865 ('string', 'r:')
1866 ('string', 'd:relpath')
1866 ('string', 'd:relpath')
1867 ('string', 'p:a'))))
1867 ('string', 'p:a'))))
1868 $ testlog --removed --follow a
1868 $ testlog --removed --follow a
1869 []
1869 []
1870 (group
1870 (group
1871 (and
1871 (and
1872 (func
1872 (func
1873 ('symbol', 'ancestors')
1873 ('symbol', 'ancestors')
1874 ('symbol', '.'))
1874 ('symbol', '.'))
1875 (func
1875 (func
1876 ('symbol', '_matchfiles')
1876 ('symbol', '_matchfiles')
1877 (list
1877 (list
1878 ('string', 'r:')
1878 ('string', 'r:')
1879 ('string', 'd:relpath')
1879 ('string', 'd:relpath')
1880 ('string', 'p:a')))))
1880 ('string', 'p:a')))))
1881
1881
1882 Test --patch and --stat with --follow and --follow-first
1882 Test --patch and --stat with --follow and --follow-first
1883
1883
1884 $ hg up -q 3
1884 $ hg up -q 3
1885 $ hg log -G --git --patch b
1885 $ hg log -G --git --patch b
1886 o changeset: 1:216d4c92cf98
1886 o changeset: 1:216d4c92cf98
1887 | user: test
1887 | user: test
1888 ~ date: Thu Jan 01 00:00:00 1970 +0000
1888 ~ date: Thu Jan 01 00:00:00 1970 +0000
1889 summary: copy a b
1889 summary: copy a b
1890
1890
1891 diff --git a/a b/b
1891 diff --git a/a b/b
1892 copy from a
1892 copy from a
1893 copy to b
1893 copy to b
1894
1894
1895
1895
1896 $ hg log -G --git --stat b
1896 $ hg log -G --git --stat b
1897 o changeset: 1:216d4c92cf98
1897 o changeset: 1:216d4c92cf98
1898 | user: test
1898 | user: test
1899 ~ date: Thu Jan 01 00:00:00 1970 +0000
1899 ~ date: Thu Jan 01 00:00:00 1970 +0000
1900 summary: copy a b
1900 summary: copy a b
1901
1901
1902 b | 0
1902 b | 0
1903 1 files changed, 0 insertions(+), 0 deletions(-)
1903 1 files changed, 0 insertions(+), 0 deletions(-)
1904
1904
1905
1905
1906 $ hg log -G --git --patch --follow b
1906 $ hg log -G --git --patch --follow b
1907 o changeset: 1:216d4c92cf98
1907 o changeset: 1:216d4c92cf98
1908 | user: test
1908 | user: test
1909 | date: Thu Jan 01 00:00:00 1970 +0000
1909 | date: Thu Jan 01 00:00:00 1970 +0000
1910 | summary: copy a b
1910 | summary: copy a b
1911 |
1911 |
1912 | diff --git a/a b/b
1912 | diff --git a/a b/b
1913 | copy from a
1913 | copy from a
1914 | copy to b
1914 | copy to b
1915 |
1915 |
1916 o changeset: 0:f8035bb17114
1916 o changeset: 0:f8035bb17114
1917 user: test
1917 user: test
1918 date: Thu Jan 01 00:00:00 1970 +0000
1918 date: Thu Jan 01 00:00:00 1970 +0000
1919 summary: add a
1919 summary: add a
1920
1920
1921 diff --git a/a b/a
1921 diff --git a/a b/a
1922 new file mode 100644
1922 new file mode 100644
1923 --- /dev/null
1923 --- /dev/null
1924 +++ b/a
1924 +++ b/a
1925 @@ -0,0 +1,1 @@
1925 @@ -0,0 +1,1 @@
1926 +a
1926 +a
1927
1927
1928
1928
1929 $ hg log -G --git --stat --follow b
1929 $ hg log -G --git --stat --follow b
1930 o changeset: 1:216d4c92cf98
1930 o changeset: 1:216d4c92cf98
1931 | user: test
1931 | user: test
1932 | date: Thu Jan 01 00:00:00 1970 +0000
1932 | date: Thu Jan 01 00:00:00 1970 +0000
1933 | summary: copy a b
1933 | summary: copy a b
1934 |
1934 |
1935 | b | 0
1935 | b | 0
1936 | 1 files changed, 0 insertions(+), 0 deletions(-)
1936 | 1 files changed, 0 insertions(+), 0 deletions(-)
1937 |
1937 |
1938 o changeset: 0:f8035bb17114
1938 o changeset: 0:f8035bb17114
1939 user: test
1939 user: test
1940 date: Thu Jan 01 00:00:00 1970 +0000
1940 date: Thu Jan 01 00:00:00 1970 +0000
1941 summary: add a
1941 summary: add a
1942
1942
1943 a | 1 +
1943 a | 1 +
1944 1 files changed, 1 insertions(+), 0 deletions(-)
1944 1 files changed, 1 insertions(+), 0 deletions(-)
1945
1945
1946
1946
1947 $ hg up -q 6
1947 $ hg up -q 6
1948 $ hg log -G --git --patch --follow-first e
1948 $ hg log -G --git --patch --follow-first e
1949 @ changeset: 6:fc281d8ff18d
1949 @ changeset: 6:fc281d8ff18d
1950 |\ tag: tip
1950 |\ tag: tip
1951 | ~ parent: 5:99b31f1c2782
1951 | ~ parent: 5:99b31f1c2782
1952 | parent: 4:17d952250a9d
1952 | parent: 4:17d952250a9d
1953 | user: test
1953 | user: test
1954 | date: Thu Jan 01 00:00:00 1970 +0000
1954 | date: Thu Jan 01 00:00:00 1970 +0000
1955 | summary: merge 5 and 4
1955 | summary: merge 5 and 4
1956 |
1956 |
1957 | diff --git a/e b/e
1957 | diff --git a/e b/e
1958 | --- a/e
1958 | --- a/e
1959 | +++ b/e
1959 | +++ b/e
1960 | @@ -1,1 +1,1 @@
1960 | @@ -1,1 +1,1 @@
1961 | -ee
1961 | -ee
1962 | +merge
1962 | +merge
1963 |
1963 |
1964 o changeset: 5:99b31f1c2782
1964 o changeset: 5:99b31f1c2782
1965 | parent: 3:5918b8d165d1
1965 | parent: 3:5918b8d165d1
1966 ~ user: test
1966 ~ user: test
1967 date: Thu Jan 01 00:00:00 1970 +0000
1967 date: Thu Jan 01 00:00:00 1970 +0000
1968 summary: add another e
1968 summary: add another e
1969
1969
1970 diff --git a/e b/e
1970 diff --git a/e b/e
1971 new file mode 100644
1971 new file mode 100644
1972 --- /dev/null
1972 --- /dev/null
1973 +++ b/e
1973 +++ b/e
1974 @@ -0,0 +1,1 @@
1974 @@ -0,0 +1,1 @@
1975 +ee
1975 +ee
1976
1976
1977
1977
1978 Test old-style --rev
1978 Test old-style --rev
1979
1979
1980 $ hg tag 'foo-bar'
1980 $ hg tag 'foo-bar'
1981 $ testlog -r 'foo-bar'
1981 $ testlog -r 'foo-bar'
1982 ['foo-bar']
1982 ['foo-bar']
1983 []
1983 []
1984
1984
1985 Test --follow and forward --rev
1985 Test --follow and forward --rev
1986
1986
1987 $ hg up -q 6
1987 $ hg up -q 6
1988 $ echo g > g
1988 $ echo g > g
1989 $ hg ci -Am 'add g' g
1989 $ hg ci -Am 'add g' g
1990 created new head
1990 created new head
1991 $ hg up -q 2
1991 $ hg up -q 2
1992 $ hg log -G --template "{rev} {desc|firstline}\n"
1992 $ hg log -G --template "{rev} {desc|firstline}\n"
1993 o 8 add g
1993 o 8 add g
1994 |
1994 |
1995 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1995 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1996 |/
1996 |/
1997 o 6 merge 5 and 4
1997 o 6 merge 5 and 4
1998 |\
1998 |\
1999 | o 5 add another e
1999 | o 5 add another e
2000 | |
2000 | |
2001 o | 4 mv dir/b e
2001 o | 4 mv dir/b e
2002 |/
2002 |/
2003 o 3 mv a b; add d
2003 o 3 mv a b; add d
2004 |
2004 |
2005 @ 2 mv b dir/b
2005 @ 2 mv b dir/b
2006 |
2006 |
2007 o 1 copy a b
2007 o 1 copy a b
2008 |
2008 |
2009 o 0 add a
2009 o 0 add a
2010
2010
2011 $ hg archive -r 7 archive
2011 $ hg archive -r 7 archive
2012 $ grep changessincelatesttag archive/.hg_archival.txt
2012 $ grep changessincelatesttag archive/.hg_archival.txt
2013 changessincelatesttag: 1
2013 changessincelatesttag: 1
2014 $ rm -r archive
2014 $ rm -r archive
2015
2015
2016 changessincelatesttag with no prior tag
2016 changessincelatesttag with no prior tag
2017 $ hg archive -r 4 archive
2017 $ hg archive -r 4 archive
2018 $ grep changessincelatesttag archive/.hg_archival.txt
2018 $ grep changessincelatesttag archive/.hg_archival.txt
2019 changessincelatesttag: 5
2019 changessincelatesttag: 5
2020
2020
2021 $ hg export 'all()'
2021 $ hg export 'all()'
2022 # HG changeset patch
2022 # HG changeset patch
2023 # User test
2023 # User test
2024 # Date 0 0
2024 # Date 0 0
2025 # Thu Jan 01 00:00:00 1970 +0000
2025 # Thu Jan 01 00:00:00 1970 +0000
2026 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2026 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2027 # Parent 0000000000000000000000000000000000000000
2027 # Parent 0000000000000000000000000000000000000000
2028 add a
2028 add a
2029
2029
2030 diff -r 000000000000 -r f8035bb17114 a
2030 diff -r 000000000000 -r f8035bb17114 a
2031 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2031 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2032 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2032 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2033 @@ -0,0 +1,1 @@
2033 @@ -0,0 +1,1 @@
2034 +a
2034 +a
2035 diff -r 000000000000 -r f8035bb17114 aa
2035 diff -r 000000000000 -r f8035bb17114 aa
2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2037 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2037 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2038 @@ -0,0 +1,1 @@
2038 @@ -0,0 +1,1 @@
2039 +aa
2039 +aa
2040 diff -r 000000000000 -r f8035bb17114 f
2040 diff -r 000000000000 -r f8035bb17114 f
2041 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2041 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2042 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2042 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2043 @@ -0,0 +1,1 @@
2043 @@ -0,0 +1,1 @@
2044 +f
2044 +f
2045 # HG changeset patch
2045 # HG changeset patch
2046 # User test
2046 # User test
2047 # Date 0 0
2047 # Date 0 0
2048 # Thu Jan 01 00:00:00 1970 +0000
2048 # Thu Jan 01 00:00:00 1970 +0000
2049 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2049 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2050 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2050 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2051 copy a b
2051 copy a b
2052
2052
2053 diff -r f8035bb17114 -r 216d4c92cf98 b
2053 diff -r f8035bb17114 -r 216d4c92cf98 b
2054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2055 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2055 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2056 @@ -0,0 +1,1 @@
2056 @@ -0,0 +1,1 @@
2057 +a
2057 +a
2058 diff -r f8035bb17114 -r 216d4c92cf98 g
2058 diff -r f8035bb17114 -r 216d4c92cf98 g
2059 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2059 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2060 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2060 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2061 @@ -0,0 +1,1 @@
2061 @@ -0,0 +1,1 @@
2062 +f
2062 +f
2063 # HG changeset patch
2063 # HG changeset patch
2064 # User test
2064 # User test
2065 # Date 0 0
2065 # Date 0 0
2066 # Thu Jan 01 00:00:00 1970 +0000
2066 # Thu Jan 01 00:00:00 1970 +0000
2067 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2067 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2068 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2068 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2069 mv b dir/b
2069 mv b dir/b
2070
2070
2071 diff -r 216d4c92cf98 -r bb573313a9e8 b
2071 diff -r 216d4c92cf98 -r bb573313a9e8 b
2072 --- a/b Thu Jan 01 00:00:00 1970 +0000
2072 --- a/b Thu Jan 01 00:00:00 1970 +0000
2073 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2073 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2074 @@ -1,1 +0,0 @@
2074 @@ -1,1 +0,0 @@
2075 -a
2075 -a
2076 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2076 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2077 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2077 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2078 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2078 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2079 @@ -0,0 +1,1 @@
2079 @@ -0,0 +1,1 @@
2080 +a
2080 +a
2081 diff -r 216d4c92cf98 -r bb573313a9e8 f
2081 diff -r 216d4c92cf98 -r bb573313a9e8 f
2082 --- a/f Thu Jan 01 00:00:00 1970 +0000
2082 --- a/f Thu Jan 01 00:00:00 1970 +0000
2083 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2083 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2084 @@ -1,1 +1,2 @@
2084 @@ -1,1 +1,2 @@
2085 f
2085 f
2086 +f
2086 +f
2087 diff -r 216d4c92cf98 -r bb573313a9e8 g
2087 diff -r 216d4c92cf98 -r bb573313a9e8 g
2088 --- a/g Thu Jan 01 00:00:00 1970 +0000
2088 --- a/g Thu Jan 01 00:00:00 1970 +0000
2089 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2089 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2090 @@ -1,1 +1,2 @@
2090 @@ -1,1 +1,2 @@
2091 f
2091 f
2092 +g
2092 +g
2093 # HG changeset patch
2093 # HG changeset patch
2094 # User test
2094 # User test
2095 # Date 0 0
2095 # Date 0 0
2096 # Thu Jan 01 00:00:00 1970 +0000
2096 # Thu Jan 01 00:00:00 1970 +0000
2097 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2097 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2098 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2098 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2099 mv a b; add d
2099 mv a b; add d
2100
2100
2101 diff -r bb573313a9e8 -r 5918b8d165d1 a
2101 diff -r bb573313a9e8 -r 5918b8d165d1 a
2102 --- a/a Thu Jan 01 00:00:00 1970 +0000
2102 --- a/a Thu Jan 01 00:00:00 1970 +0000
2103 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2103 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2104 @@ -1,1 +0,0 @@
2104 @@ -1,1 +0,0 @@
2105 -a
2105 -a
2106 diff -r bb573313a9e8 -r 5918b8d165d1 b
2106 diff -r bb573313a9e8 -r 5918b8d165d1 b
2107 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2107 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2108 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2108 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2109 @@ -0,0 +1,1 @@
2109 @@ -0,0 +1,1 @@
2110 +a
2110 +a
2111 diff -r bb573313a9e8 -r 5918b8d165d1 d
2111 diff -r bb573313a9e8 -r 5918b8d165d1 d
2112 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2112 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2113 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2113 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2114 @@ -0,0 +1,1 @@
2114 @@ -0,0 +1,1 @@
2115 +a
2115 +a
2116 diff -r bb573313a9e8 -r 5918b8d165d1 g
2116 diff -r bb573313a9e8 -r 5918b8d165d1 g
2117 --- a/g Thu Jan 01 00:00:00 1970 +0000
2117 --- a/g Thu Jan 01 00:00:00 1970 +0000
2118 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2118 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2119 @@ -1,2 +1,2 @@
2119 @@ -1,2 +1,2 @@
2120 f
2120 f
2121 -g
2121 -g
2122 +f
2122 +f
2123 # HG changeset patch
2123 # HG changeset patch
2124 # User test
2124 # User test
2125 # Date 0 0
2125 # Date 0 0
2126 # Thu Jan 01 00:00:00 1970 +0000
2126 # Thu Jan 01 00:00:00 1970 +0000
2127 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2127 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2128 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2128 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2129 mv dir/b e
2129 mv dir/b e
2130
2130
2131 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2131 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2132 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2132 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2133 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2133 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2134 @@ -1,1 +0,0 @@
2134 @@ -1,1 +0,0 @@
2135 -a
2135 -a
2136 diff -r 5918b8d165d1 -r 17d952250a9d e
2136 diff -r 5918b8d165d1 -r 17d952250a9d e
2137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2138 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2138 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2139 @@ -0,0 +1,1 @@
2139 @@ -0,0 +1,1 @@
2140 +a
2140 +a
2141 # HG changeset patch
2141 # HG changeset patch
2142 # User test
2142 # User test
2143 # Date 0 0
2143 # Date 0 0
2144 # Thu Jan 01 00:00:00 1970 +0000
2144 # Thu Jan 01 00:00:00 1970 +0000
2145 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2145 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2146 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2146 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2147 add another e
2147 add another e
2148
2148
2149 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2149 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2150 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2150 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2151 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2151 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2152 @@ -0,0 +1,1 @@
2152 @@ -0,0 +1,1 @@
2153 +ee
2153 +ee
2154 # HG changeset patch
2154 # HG changeset patch
2155 # User test
2155 # User test
2156 # Date 0 0
2156 # Date 0 0
2157 # Thu Jan 01 00:00:00 1970 +0000
2157 # Thu Jan 01 00:00:00 1970 +0000
2158 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2158 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2159 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2159 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2160 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2160 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2161 merge 5 and 4
2161 merge 5 and 4
2162
2162
2163 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2163 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2164 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2164 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2165 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2165 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2166 @@ -1,1 +0,0 @@
2166 @@ -1,1 +0,0 @@
2167 -a
2167 -a
2168 diff -r 99b31f1c2782 -r fc281d8ff18d e
2168 diff -r 99b31f1c2782 -r fc281d8ff18d e
2169 --- a/e Thu Jan 01 00:00:00 1970 +0000
2169 --- a/e Thu Jan 01 00:00:00 1970 +0000
2170 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2170 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2171 @@ -1,1 +1,1 @@
2171 @@ -1,1 +1,1 @@
2172 -ee
2172 -ee
2173 +merge
2173 +merge
2174 # HG changeset patch
2174 # HG changeset patch
2175 # User test
2175 # User test
2176 # Date 0 0
2176 # Date 0 0
2177 # Thu Jan 01 00:00:00 1970 +0000
2177 # Thu Jan 01 00:00:00 1970 +0000
2178 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2178 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2179 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2179 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2180 Added tag foo-bar for changeset fc281d8ff18d
2180 Added tag foo-bar for changeset fc281d8ff18d
2181
2181
2182 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2182 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2183 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2183 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2184 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2184 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2185 @@ -0,0 +1,1 @@
2185 @@ -0,0 +1,1 @@
2186 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2186 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2187 # HG changeset patch
2187 # HG changeset patch
2188 # User test
2188 # User test
2189 # Date 0 0
2189 # Date 0 0
2190 # Thu Jan 01 00:00:00 1970 +0000
2190 # Thu Jan 01 00:00:00 1970 +0000
2191 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2191 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2192 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2192 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2193 add g
2193 add g
2194
2194
2195 diff -r fc281d8ff18d -r 24c2e826ddeb g
2195 diff -r fc281d8ff18d -r 24c2e826ddeb g
2196 --- a/g Thu Jan 01 00:00:00 1970 +0000
2196 --- a/g Thu Jan 01 00:00:00 1970 +0000
2197 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2197 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2198 @@ -1,2 +1,1 @@
2198 @@ -1,2 +1,1 @@
2199 -f
2199 -f
2200 -f
2200 -f
2201 +g
2201 +g
2202 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2202 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2203 ['6', '8', '5', '7', '4']
2203 ['6', '8', '5', '7', '4']
2204 (group
2204 (group
2205 (func
2205 (func
2206 ('symbol', 'descendants')
2206 ('symbol', 'descendants')
2207 (func
2207 (func
2208 ('symbol', 'rev')
2208 ('symbol', 'rev')
2209 ('symbol', '6'))))
2209 ('symbol', '6'))))
2210
2210
2211 Test --follow-first and forward --rev
2211 Test --follow-first and forward --rev
2212
2212
2213 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2213 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2214 ['6', '8', '5', '7', '4']
2214 ['6', '8', '5', '7', '4']
2215 (group
2215 (group
2216 (func
2216 (func
2217 ('symbol', '_firstdescendants')
2217 ('symbol', '_firstdescendants')
2218 (func
2218 (func
2219 ('symbol', 'rev')
2219 ('symbol', 'rev')
2220 ('symbol', '6'))))
2220 ('symbol', '6'))))
2221 --- log.nodes * (glob)
2221 --- log.nodes * (glob)
2222 +++ glog.nodes * (glob)
2222 +++ glog.nodes * (glob)
2223 @@ -1,3 +1,3 @@
2223 @@ -1,3 +1,3 @@
2224 -nodetag 6
2224 -nodetag 6
2225 nodetag 8
2225 nodetag 8
2226 nodetag 7
2226 nodetag 7
2227 +nodetag 6
2227 +nodetag 6
2228
2228
2229 Test --follow and backward --rev
2229 Test --follow and backward --rev
2230
2230
2231 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2231 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2232 ['6', '5', '7', '8', '4']
2232 ['6', '5', '7', '8', '4']
2233 (group
2233 (group
2234 (func
2234 (func
2235 ('symbol', 'ancestors')
2235 ('symbol', 'ancestors')
2236 (func
2236 (func
2237 ('symbol', 'rev')
2237 ('symbol', 'rev')
2238 ('symbol', '6'))))
2238 ('symbol', '6'))))
2239
2239
2240 Test --follow-first and backward --rev
2240 Test --follow-first and backward --rev
2241
2241
2242 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2242 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2243 ['6', '5', '7', '8', '4']
2243 ['6', '5', '7', '8', '4']
2244 (group
2244 (group
2245 (func
2245 (func
2246 ('symbol', '_firstancestors')
2246 ('symbol', '_firstancestors')
2247 (func
2247 (func
2248 ('symbol', 'rev')
2248 ('symbol', 'rev')
2249 ('symbol', '6'))))
2249 ('symbol', '6'))))
2250
2250
2251 Test --follow with --rev of graphlog extension
2251 Test --follow with --rev of graphlog extension
2252
2252
2253 $ hg --config extensions.graphlog= glog -qfr1
2253 $ hg --config extensions.graphlog= glog -qfr1
2254 o 1:216d4c92cf98
2254 o 1:216d4c92cf98
2255 |
2255 |
2256 o 0:f8035bb17114
2256 o 0:f8035bb17114
2257
2257
2258
2258
2259 Test subdir
2259 Test subdir
2260
2260
2261 $ hg up -q 3
2261 $ hg up -q 3
2262 $ cd dir
2262 $ cd dir
2263 $ testlog .
2263 $ testlog .
2264 []
2264 []
2265 (group
2265 (group
2266 (func
2266 (func
2267 ('symbol', '_matchfiles')
2267 ('symbol', '_matchfiles')
2268 (list
2268 (list
2269 ('string', 'r:')
2269 ('string', 'r:')
2270 ('string', 'd:relpath')
2270 ('string', 'd:relpath')
2271 ('string', 'p:.'))))
2271 ('string', 'p:.'))))
2272 $ testlog ../b
2272 $ testlog ../b
2273 []
2273 []
2274 (group
2274 (group
2275 (group
2275 (group
2276 (func
2276 (func
2277 ('symbol', 'filelog')
2277 ('symbol', 'filelog')
2278 ('string', '../b'))))
2278 ('string', '../b'))))
2279 $ testlog -f ../b
2279 $ testlog -f ../b
2280 []
2280 []
2281 (group
2281 (group
2282 (group
2282 (group
2283 (func
2283 (func
2284 ('symbol', 'follow')
2284 ('symbol', 'follow')
2285 ('string', 'b'))))
2285 ('string', 'b'))))
2286 $ cd ..
2286 $ cd ..
2287
2287
2288 Test --hidden
2288 Test --hidden
2289 (enable obsolete)
2289 (enable obsolete)
2290
2290
2291 $ cat >> $HGRCPATH << EOF
2291 $ cat >> $HGRCPATH << EOF
2292 > [experimental]
2292 > [experimental]
2293 > evolution=createmarkers
2293 > evolution=createmarkers
2294 > EOF
2294 > EOF
2295
2295
2296 $ hg debugobsolete `hg id --debug -i -r 8`
2296 $ hg debugobsolete `hg id --debug -i -r 8`
2297 $ testlog
2297 $ testlog
2298 []
2298 []
2299 []
2299 []
2300 $ testlog --hidden
2300 $ testlog --hidden
2301 []
2301 []
2302 []
2302 []
2303 $ hg log -G --template '{rev} {desc}\n'
2303 $ hg log -G --template '{rev} {desc}\n'
2304 o 7 Added tag foo-bar for changeset fc281d8ff18d
2304 o 7 Added tag foo-bar for changeset fc281d8ff18d
2305 |
2305 |
2306 o 6 merge 5 and 4
2306 o 6 merge 5 and 4
2307 |\
2307 |\
2308 | o 5 add another e
2308 | o 5 add another e
2309 | |
2309 | |
2310 o | 4 mv dir/b e
2310 o | 4 mv dir/b e
2311 |/
2311 |/
2312 @ 3 mv a b; add d
2312 @ 3 mv a b; add d
2313 |
2313 |
2314 o 2 mv b dir/b
2314 o 2 mv b dir/b
2315 |
2315 |
2316 o 1 copy a b
2316 o 1 copy a b
2317 |
2317 |
2318 o 0 add a
2318 o 0 add a
2319
2319
2320
2320
2321 A template without trailing newline should do something sane
2321 A template without trailing newline should do something sane
2322
2322
2323 $ hg log -G -r ::2 --template '{rev} {desc}'
2323 $ hg log -G -r ::2 --template '{rev} {desc}'
2324 o 2 mv b dir/b
2324 o 2 mv b dir/b
2325 |
2325 |
2326 o 1 copy a b
2326 o 1 copy a b
2327 |
2327 |
2328 o 0 add a
2328 o 0 add a
2329
2329
2330
2330
2331 Extra newlines must be preserved
2331 Extra newlines must be preserved
2332
2332
2333 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2333 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2334 o
2334 o
2335 | 2 mv b dir/b
2335 | 2 mv b dir/b
2336 |
2336 |
2337 o
2337 o
2338 | 1 copy a b
2338 | 1 copy a b
2339 |
2339 |
2340 o
2340 o
2341 0 add a
2341 0 add a
2342
2342
2343
2343
2344 The almost-empty template should do something sane too ...
2344 The almost-empty template should do something sane too ...
2345
2345
2346 $ hg log -G -r ::2 --template '\n'
2346 $ hg log -G -r ::2 --template '\n'
2347 o
2347 o
2348 |
2348 |
2349 o
2349 o
2350 |
2350 |
2351 o
2351 o
2352
2352
2353
2353
2354 issue3772
2354 issue3772
2355
2355
2356 $ hg log -G -r :null
2356 $ hg log -G -r :null
2357 o changeset: 0:f8035bb17114
2357 o changeset: 0:f8035bb17114
2358 | user: test
2358 | user: test
2359 | date: Thu Jan 01 00:00:00 1970 +0000
2359 | date: Thu Jan 01 00:00:00 1970 +0000
2360 | summary: add a
2360 | summary: add a
2361 |
2361 |
2362 o changeset: -1:000000000000
2362 o changeset: -1:000000000000
2363 user:
2363 user:
2364 date: Thu Jan 01 00:00:00 1970 +0000
2364 date: Thu Jan 01 00:00:00 1970 +0000
2365
2365
2366 $ hg log -G -r null:null
2366 $ hg log -G -r null:null
2367 o changeset: -1:000000000000
2367 o changeset: -1:000000000000
2368 user:
2368 user:
2369 date: Thu Jan 01 00:00:00 1970 +0000
2369 date: Thu Jan 01 00:00:00 1970 +0000
2370
2370
2371
2371
2372 should not draw line down to null due to the magic of fullreposet
2372 should not draw line down to null due to the magic of fullreposet
2373
2373
2374 $ hg log -G -r 'all()' | tail -6
2374 $ hg log -G -r 'all()' | tail -6
2375 |
2375 |
2376 o changeset: 0:f8035bb17114
2376 o changeset: 0:f8035bb17114
2377 user: test
2377 user: test
2378 date: Thu Jan 01 00:00:00 1970 +0000
2378 date: Thu Jan 01 00:00:00 1970 +0000
2379 summary: add a
2379 summary: add a
2380
2380
2381
2381
2382 $ hg log -G -r 'branch(default)' | tail -6
2382 $ hg log -G -r 'branch(default)' | tail -6
2383 |
2383 |
2384 o changeset: 0:f8035bb17114
2384 o changeset: 0:f8035bb17114
2385 user: test
2385 user: test
2386 date: Thu Jan 01 00:00:00 1970 +0000
2386 date: Thu Jan 01 00:00:00 1970 +0000
2387 summary: add a
2387 summary: add a
2388
2388
2389
2389
2390 working-directory revision
2390 working-directory revision
2391
2391
2392 $ hg log -G -qr '. + wdir()'
2392 $ hg log -G -qr '. + wdir()'
2393 o 2147483647:ffffffffffff
2393 o 2147483647:ffffffffffff
2394 |
2394 |
2395 @ 3:5918b8d165d1
2395 @ 3:5918b8d165d1
2396 |
2396 |
2397 ~
2397 ~
2398
2398
2399 node template with changeset_printer:
2399 node template with changeset_printer:
2400
2400
2401 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2401 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2402 7 7:02dbb8e276b8
2402 7 7:02dbb8e276b8
2403 |
2403 |
2404 6 6:fc281d8ff18d
2404 6 6:fc281d8ff18d
2405 |\
2405 |\
2406 | ~
2406 | ~
2407 5 5:99b31f1c2782
2407 5 5:99b31f1c2782
2408 |
2408 |
2409 ~
2409 ~
2410
2410
2411 node template with changeset_templater (shared cache variable):
2411 node template with changeset_templater (shared cache variable):
2412
2412
2413 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2413 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2414 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2414 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2415 o 7 foo-bar+1
2415 o 7 foo-bar+1
2416 |
2416 |
2417 # 6 foo-bar+0
2417 # 6 foo-bar+0
2418 |\
2418 |\
2419 | ~
2419 | ~
2420 o 5 null+5
2420 o 5 null+5
2421 |
2421 |
2422 ~
2422 ~
2423
2423
2424 label() should just work in node template:
2424 label() should just work in node template:
2425
2425
2426 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2426 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2427 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2427 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2428 [branch.default|7] [log.node|7:02dbb8e276b8]
2428 [branch.default|7] [log.node|7:02dbb8e276b8]
2429 |
2429 |
2430 ~
2430 ~
2431
2431
2432 $ cd ..
2432 $ cd ..
2433
2433
2434 change graph edge styling
2434 change graph edge styling
2435
2435
2436 $ cd repo
2436 $ cd repo
2437 $ cat << EOF >> $HGRCPATH
2437 $ cat << EOF >> $HGRCPATH
2438 > [experimental]
2438 > [experimental]
2439 > graphstyle.parent = |
2439 > graphstyle.parent = |
2440 > graphstyle.grandparent = :
2440 > graphstyle.grandparent = :
2441 > graphstyle.missing =
2441 > graphstyle.missing =
2442 > EOF
2442 > EOF
2443 $ hg log -G -r 'file("a")' -m
2443 $ hg log -G -r 'file("a")' -m
2444 @ changeset: 36:08a19a744424
2444 @ changeset: 36:08a19a744424
2445 : branch: branch
2445 : branch: branch
2446 : tag: tip
2446 : tag: tip
2447 : parent: 35:9159c3644c5e
2447 : parent: 35:9159c3644c5e
2448 : parent: 35:9159c3644c5e
2448 : parent: 35:9159c3644c5e
2449 : user: test
2449 : user: test
2450 : date: Thu Jan 01 00:00:36 1970 +0000
2450 : date: Thu Jan 01 00:00:36 1970 +0000
2451 : summary: (36) buggy merge: identical parents
2451 : summary: (36) buggy merge: identical parents
2452 :
2452 :
2453 o changeset: 32:d06dffa21a31
2453 o changeset: 32:d06dffa21a31
2454 |\ parent: 27:886ed638191b
2454 |\ parent: 27:886ed638191b
2455 | : parent: 31:621d83e11f67
2455 | : parent: 31:621d83e11f67
2456 | : user: test
2456 | : user: test
2457 | : date: Thu Jan 01 00:00:32 1970 +0000
2457 | : date: Thu Jan 01 00:00:32 1970 +0000
2458 | : summary: (32) expand
2458 | : summary: (32) expand
2459 | :
2459 | :
2460 o : changeset: 31:621d83e11f67
2460 o : changeset: 31:621d83e11f67
2461 |\: parent: 21:d42a756af44d
2461 |\: parent: 21:d42a756af44d
2462 | : parent: 30:6e11cd4b648f
2462 | : parent: 30:6e11cd4b648f
2463 | : user: test
2463 | : user: test
2464 | : date: Thu Jan 01 00:00:31 1970 +0000
2464 | : date: Thu Jan 01 00:00:31 1970 +0000
2465 | : summary: (31) expand
2465 | : summary: (31) expand
2466 | :
2466 | :
2467 o : changeset: 30:6e11cd4b648f
2467 o : changeset: 30:6e11cd4b648f
2468 |\ \ parent: 28:44ecd0b9ae99
2468 |\ \ parent: 28:44ecd0b9ae99
2469 | ~ : parent: 29:cd9bb2be7593
2469 | ~ : parent: 29:cd9bb2be7593
2470 | : user: test
2470 | : user: test
2471 | : date: Thu Jan 01 00:00:30 1970 +0000
2471 | : date: Thu Jan 01 00:00:30 1970 +0000
2472 | : summary: (30) expand
2472 | : summary: (30) expand
2473 | /
2473 | /
2474 o : changeset: 28:44ecd0b9ae99
2474 o : changeset: 28:44ecd0b9ae99
2475 |\ \ parent: 1:6db2ef61d156
2475 |\ \ parent: 1:6db2ef61d156
2476 | ~ : parent: 26:7f25b6c2f0b9
2476 | ~ : parent: 26:7f25b6c2f0b9
2477 | : user: test
2477 | : user: test
2478 | : date: Thu Jan 01 00:00:28 1970 +0000
2478 | : date: Thu Jan 01 00:00:28 1970 +0000
2479 | : summary: (28) merge zero known
2479 | : summary: (28) merge zero known
2480 | /
2480 | /
2481 o : changeset: 26:7f25b6c2f0b9
2481 o : changeset: 26:7f25b6c2f0b9
2482 |\ \ parent: 18:1aa84d96232a
2482 |\ \ parent: 18:1aa84d96232a
2483 | | : parent: 25:91da8ed57247
2483 | | : parent: 25:91da8ed57247
2484 | | : user: test
2484 | | : user: test
2485 | | : date: Thu Jan 01 00:00:26 1970 +0000
2485 | | : date: Thu Jan 01 00:00:26 1970 +0000
2486 | | : summary: (26) merge one known; far right
2486 | | : summary: (26) merge one known; far right
2487 | | :
2487 | | :
2488 | o : changeset: 25:91da8ed57247
2488 | o : changeset: 25:91da8ed57247
2489 | |\: parent: 21:d42a756af44d
2489 | |\: parent: 21:d42a756af44d
2490 | | : parent: 24:a9c19a3d96b7
2490 | | : parent: 24:a9c19a3d96b7
2491 | | : user: test
2491 | | : user: test
2492 | | : date: Thu Jan 01 00:00:25 1970 +0000
2492 | | : date: Thu Jan 01 00:00:25 1970 +0000
2493 | | : summary: (25) merge one known; far left
2493 | | : summary: (25) merge one known; far left
2494 | | :
2494 | | :
2495 | o : changeset: 24:a9c19a3d96b7
2495 | o : changeset: 24:a9c19a3d96b7
2496 | |\ \ parent: 0:e6eb3150255d
2496 | |\ \ parent: 0:e6eb3150255d
2497 | | ~ : parent: 23:a01cddf0766d
2497 | | ~ : parent: 23:a01cddf0766d
2498 | | : user: test
2498 | | : user: test
2499 | | : date: Thu Jan 01 00:00:24 1970 +0000
2499 | | : date: Thu Jan 01 00:00:24 1970 +0000
2500 | | : summary: (24) merge one known; immediate right
2500 | | : summary: (24) merge one known; immediate right
2501 | | /
2501 | | /
2502 | o : changeset: 23:a01cddf0766d
2502 | o : changeset: 23:a01cddf0766d
2503 | |\ \ parent: 1:6db2ef61d156
2503 | |\ \ parent: 1:6db2ef61d156
2504 | | ~ : parent: 22:e0d9cccacb5d
2504 | | ~ : parent: 22:e0d9cccacb5d
2505 | | : user: test
2505 | | : user: test
2506 | | : date: Thu Jan 01 00:00:23 1970 +0000
2506 | | : date: Thu Jan 01 00:00:23 1970 +0000
2507 | | : summary: (23) merge one known; immediate left
2507 | | : summary: (23) merge one known; immediate left
2508 | | /
2508 | | /
2509 | o : changeset: 22:e0d9cccacb5d
2509 | o : changeset: 22:e0d9cccacb5d
2510 |/:/ parent: 18:1aa84d96232a
2510 |/:/ parent: 18:1aa84d96232a
2511 | : parent: 21:d42a756af44d
2511 | : parent: 21:d42a756af44d
2512 | : user: test
2512 | : user: test
2513 | : date: Thu Jan 01 00:00:22 1970 +0000
2513 | : date: Thu Jan 01 00:00:22 1970 +0000
2514 | : summary: (22) merge two known; one far left, one far right
2514 | : summary: (22) merge two known; one far left, one far right
2515 | :
2515 | :
2516 | o changeset: 21:d42a756af44d
2516 | o changeset: 21:d42a756af44d
2517 | |\ parent: 19:31ddc2c1573b
2517 | |\ parent: 19:31ddc2c1573b
2518 | | | parent: 20:d30ed6450e32
2518 | | | parent: 20:d30ed6450e32
2519 | | | user: test
2519 | | | user: test
2520 | | | date: Thu Jan 01 00:00:21 1970 +0000
2520 | | | date: Thu Jan 01 00:00:21 1970 +0000
2521 | | | summary: (21) expand
2521 | | | summary: (21) expand
2522 | | |
2522 | | |
2523 +---o changeset: 20:d30ed6450e32
2523 +---o changeset: 20:d30ed6450e32
2524 | | | parent: 0:e6eb3150255d
2524 | | | parent: 0:e6eb3150255d
2525 | | ~ parent: 18:1aa84d96232a
2525 | | ~ parent: 18:1aa84d96232a
2526 | | user: test
2526 | | user: test
2527 | | date: Thu Jan 01 00:00:20 1970 +0000
2527 | | date: Thu Jan 01 00:00:20 1970 +0000
2528 | | summary: (20) merge two known; two far right
2528 | | summary: (20) merge two known; two far right
2529 | |
2529 | |
2530 | o changeset: 19:31ddc2c1573b
2530 | o changeset: 19:31ddc2c1573b
2531 | |\ parent: 15:1dda3f72782d
2531 | |\ parent: 15:1dda3f72782d
2532 | | | parent: 17:44765d7c06e0
2532 | | | parent: 17:44765d7c06e0
2533 | | | user: test
2533 | | | user: test
2534 | | | date: Thu Jan 01 00:00:19 1970 +0000
2534 | | | date: Thu Jan 01 00:00:19 1970 +0000
2535 | | | summary: (19) expand
2535 | | | summary: (19) expand
2536 | | |
2536 | | |
2537 o | | changeset: 18:1aa84d96232a
2537 o | | changeset: 18:1aa84d96232a
2538 |\| | parent: 1:6db2ef61d156
2538 |\| | parent: 1:6db2ef61d156
2539 ~ | | parent: 15:1dda3f72782d
2539 ~ | | parent: 15:1dda3f72782d
2540 | | user: test
2540 | | user: test
2541 | | date: Thu Jan 01 00:00:18 1970 +0000
2541 | | date: Thu Jan 01 00:00:18 1970 +0000
2542 | | summary: (18) merge two known; two far left
2542 | | summary: (18) merge two known; two far left
2543 / /
2543 / /
2544 | o changeset: 17:44765d7c06e0
2544 | o changeset: 17:44765d7c06e0
2545 | |\ parent: 12:86b91144a6e9
2545 | |\ parent: 12:86b91144a6e9
2546 | | | parent: 16:3677d192927d
2546 | | | parent: 16:3677d192927d
2547 | | | user: test
2547 | | | user: test
2548 | | | date: Thu Jan 01 00:00:17 1970 +0000
2548 | | | date: Thu Jan 01 00:00:17 1970 +0000
2549 | | | summary: (17) expand
2549 | | | summary: (17) expand
2550 | | |
2550 | | |
2551 | | o changeset: 16:3677d192927d
2551 | | o changeset: 16:3677d192927d
2552 | | |\ parent: 0:e6eb3150255d
2552 | | |\ parent: 0:e6eb3150255d
2553 | | ~ ~ parent: 1:6db2ef61d156
2553 | | ~ ~ parent: 1:6db2ef61d156
2554 | | user: test
2554 | | user: test
2555 | | date: Thu Jan 01 00:00:16 1970 +0000
2555 | | date: Thu Jan 01 00:00:16 1970 +0000
2556 | | summary: (16) merge two known; one immediate right, one near right
2556 | | summary: (16) merge two known; one immediate right, one near right
2557 | |
2557 | |
2558 o | changeset: 15:1dda3f72782d
2558 o | changeset: 15:1dda3f72782d
2559 |\ \ parent: 13:22d8966a97e3
2559 |\ \ parent: 13:22d8966a97e3
2560 | | | parent: 14:8eac370358ef
2560 | | | parent: 14:8eac370358ef
2561 | | | user: test
2561 | | | user: test
2562 | | | date: Thu Jan 01 00:00:15 1970 +0000
2562 | | | date: Thu Jan 01 00:00:15 1970 +0000
2563 | | | summary: (15) expand
2563 | | | summary: (15) expand
2564 | | |
2564 | | |
2565 | o | changeset: 14:8eac370358ef
2565 | o | changeset: 14:8eac370358ef
2566 | |\| parent: 0:e6eb3150255d
2566 | |\| parent: 0:e6eb3150255d
2567 | ~ | parent: 12:86b91144a6e9
2567 | ~ | parent: 12:86b91144a6e9
2568 | | user: test
2568 | | user: test
2569 | | date: Thu Jan 01 00:00:14 1970 +0000
2569 | | date: Thu Jan 01 00:00:14 1970 +0000
2570 | | summary: (14) merge two known; one immediate right, one far right
2570 | | summary: (14) merge two known; one immediate right, one far right
2571 | /
2571 | /
2572 o | changeset: 13:22d8966a97e3
2572 o | changeset: 13:22d8966a97e3
2573 |\ \ parent: 9:7010c0af0a35
2573 |\ \ parent: 9:7010c0af0a35
2574 | | | parent: 11:832d76e6bdf2
2574 | | | parent: 11:832d76e6bdf2
2575 | | | user: test
2575 | | | user: test
2576 | | | date: Thu Jan 01 00:00:13 1970 +0000
2576 | | | date: Thu Jan 01 00:00:13 1970 +0000
2577 | | | summary: (13) expand
2577 | | | summary: (13) expand
2578 | | |
2578 | | |
2579 +---o changeset: 12:86b91144a6e9
2579 +---o changeset: 12:86b91144a6e9
2580 | | | parent: 1:6db2ef61d156
2580 | | | parent: 1:6db2ef61d156
2581 | | ~ parent: 9:7010c0af0a35
2581 | | ~ parent: 9:7010c0af0a35
2582 | | user: test
2582 | | user: test
2583 | | date: Thu Jan 01 00:00:12 1970 +0000
2583 | | date: Thu Jan 01 00:00:12 1970 +0000
2584 | | summary: (12) merge two known; one immediate right, one far left
2584 | | summary: (12) merge two known; one immediate right, one far left
2585 | |
2585 | |
2586 | o changeset: 11:832d76e6bdf2
2586 | o changeset: 11:832d76e6bdf2
2587 | |\ parent: 6:b105a072e251
2587 | |\ parent: 6:b105a072e251
2588 | | | parent: 10:74c64d036d72
2588 | | | parent: 10:74c64d036d72
2589 | | | user: test
2589 | | | user: test
2590 | | | date: Thu Jan 01 00:00:11 1970 +0000
2590 | | | date: Thu Jan 01 00:00:11 1970 +0000
2591 | | | summary: (11) expand
2591 | | | summary: (11) expand
2592 | | |
2592 | | |
2593 | | o changeset: 10:74c64d036d72
2593 | | o changeset: 10:74c64d036d72
2594 | |/| parent: 0:e6eb3150255d
2594 | |/| parent: 0:e6eb3150255d
2595 | | ~ parent: 6:b105a072e251
2595 | | ~ parent: 6:b105a072e251
2596 | | user: test
2596 | | user: test
2597 | | date: Thu Jan 01 00:00:10 1970 +0000
2597 | | date: Thu Jan 01 00:00:10 1970 +0000
2598 | | summary: (10) merge two known; one immediate left, one near right
2598 | | summary: (10) merge two known; one immediate left, one near right
2599 | |
2599 | |
2600 o | changeset: 9:7010c0af0a35
2600 o | changeset: 9:7010c0af0a35
2601 |\ \ parent: 7:b632bb1b1224
2601 |\ \ parent: 7:b632bb1b1224
2602 | | | parent: 8:7a0b11f71937
2602 | | | parent: 8:7a0b11f71937
2603 | | | user: test
2603 | | | user: test
2604 | | | date: Thu Jan 01 00:00:09 1970 +0000
2604 | | | date: Thu Jan 01 00:00:09 1970 +0000
2605 | | | summary: (9) expand
2605 | | | summary: (9) expand
2606 | | |
2606 | | |
2607 | o | changeset: 8:7a0b11f71937
2607 | o | changeset: 8:7a0b11f71937
2608 |/| | parent: 0:e6eb3150255d
2608 |/| | parent: 0:e6eb3150255d
2609 | ~ | parent: 7:b632bb1b1224
2609 | ~ | parent: 7:b632bb1b1224
2610 | | user: test
2610 | | user: test
2611 | | date: Thu Jan 01 00:00:08 1970 +0000
2611 | | date: Thu Jan 01 00:00:08 1970 +0000
2612 | | summary: (8) merge two known; one immediate left, one far right
2612 | | summary: (8) merge two known; one immediate left, one far right
2613 | /
2613 | /
2614 o | changeset: 7:b632bb1b1224
2614 o | changeset: 7:b632bb1b1224
2615 |\ \ parent: 2:3d9a33b8d1e1
2615 |\ \ parent: 2:3d9a33b8d1e1
2616 | ~ | parent: 5:4409d547b708
2616 | ~ | parent: 5:4409d547b708
2617 | | user: test
2617 | | user: test
2618 | | date: Thu Jan 01 00:00:07 1970 +0000
2618 | | date: Thu Jan 01 00:00:07 1970 +0000
2619 | | summary: (7) expand
2619 | | summary: (7) expand
2620 | /
2620 | /
2621 | o changeset: 6:b105a072e251
2621 | o changeset: 6:b105a072e251
2622 |/| parent: 2:3d9a33b8d1e1
2622 |/| parent: 2:3d9a33b8d1e1
2623 | ~ parent: 5:4409d547b708
2623 | ~ parent: 5:4409d547b708
2624 | user: test
2624 | user: test
2625 | date: Thu Jan 01 00:00:06 1970 +0000
2625 | date: Thu Jan 01 00:00:06 1970 +0000
2626 | summary: (6) merge two known; one immediate left, one far left
2626 | summary: (6) merge two known; one immediate left, one far left
2627 |
2627 |
2628 o changeset: 5:4409d547b708
2628 o changeset: 5:4409d547b708
2629 |\ parent: 3:27eef8ed80b4
2629 |\ parent: 3:27eef8ed80b4
2630 | ~ parent: 4:26a8bac39d9f
2630 | ~ parent: 4:26a8bac39d9f
2631 | user: test
2631 | user: test
2632 | date: Thu Jan 01 00:00:05 1970 +0000
2632 | date: Thu Jan 01 00:00:05 1970 +0000
2633 | summary: (5) expand
2633 | summary: (5) expand
2634 |
2634 |
2635 o changeset: 4:26a8bac39d9f
2635 o changeset: 4:26a8bac39d9f
2636 |\ parent: 1:6db2ef61d156
2636 |\ parent: 1:6db2ef61d156
2637 ~ ~ parent: 3:27eef8ed80b4
2637 ~ ~ parent: 3:27eef8ed80b4
2638 user: test
2638 user: test
2639 date: Thu Jan 01 00:00:04 1970 +0000
2639 date: Thu Jan 01 00:00:04 1970 +0000
2640 summary: (4) merge two known; one immediate left, one immediate right
2640 summary: (4) merge two known; one immediate left, one immediate right
2641
2641
2642
2642
2643 Setting HGPLAIN ignores graphmod styling:
2643 Setting HGPLAIN ignores graphmod styling:
2644
2644
2645 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2645 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2646 @ changeset: 36:08a19a744424
2646 @ changeset: 36:08a19a744424
2647 | branch: branch
2647 | branch: branch
2648 | tag: tip
2648 | tag: tip
2649 | parent: 35:9159c3644c5e
2649 | parent: 35:9159c3644c5e
2650 | parent: 35:9159c3644c5e
2650 | parent: 35:9159c3644c5e
2651 | user: test
2651 | user: test
2652 | date: Thu Jan 01 00:00:36 1970 +0000
2652 | date: Thu Jan 01 00:00:36 1970 +0000
2653 | summary: (36) buggy merge: identical parents
2653 | summary: (36) buggy merge: identical parents
2654 |
2654 |
2655 o changeset: 32:d06dffa21a31
2655 o changeset: 32:d06dffa21a31
2656 |\ parent: 27:886ed638191b
2656 |\ parent: 27:886ed638191b
2657 | | parent: 31:621d83e11f67
2657 | | parent: 31:621d83e11f67
2658 | | user: test
2658 | | user: test
2659 | | date: Thu Jan 01 00:00:32 1970 +0000
2659 | | date: Thu Jan 01 00:00:32 1970 +0000
2660 | | summary: (32) expand
2660 | | summary: (32) expand
2661 | |
2661 | |
2662 o | changeset: 31:621d83e11f67
2662 o | changeset: 31:621d83e11f67
2663 |\| parent: 21:d42a756af44d
2663 |\| parent: 21:d42a756af44d
2664 | | parent: 30:6e11cd4b648f
2664 | | parent: 30:6e11cd4b648f
2665 | | user: test
2665 | | user: test
2666 | | date: Thu Jan 01 00:00:31 1970 +0000
2666 | | date: Thu Jan 01 00:00:31 1970 +0000
2667 | | summary: (31) expand
2667 | | summary: (31) expand
2668 | |
2668 | |
2669 o | changeset: 30:6e11cd4b648f
2669 o | changeset: 30:6e11cd4b648f
2670 |\ \ parent: 28:44ecd0b9ae99
2670 |\ \ parent: 28:44ecd0b9ae99
2671 | | | parent: 29:cd9bb2be7593
2671 | | | parent: 29:cd9bb2be7593
2672 | | | user: test
2672 | | | user: test
2673 | | | date: Thu Jan 01 00:00:30 1970 +0000
2673 | | | date: Thu Jan 01 00:00:30 1970 +0000
2674 | | | summary: (30) expand
2674 | | | summary: (30) expand
2675 | | |
2675 | | |
2676 o | | changeset: 28:44ecd0b9ae99
2676 o | | changeset: 28:44ecd0b9ae99
2677 |\ \ \ parent: 1:6db2ef61d156
2677 |\ \ \ parent: 1:6db2ef61d156
2678 | | | | parent: 26:7f25b6c2f0b9
2678 | | | | parent: 26:7f25b6c2f0b9
2679 | | | | user: test
2679 | | | | user: test
2680 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2680 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2681 | | | | summary: (28) merge zero known
2681 | | | | summary: (28) merge zero known
2682 | | | |
2682 | | | |
2683 o | | | changeset: 26:7f25b6c2f0b9
2683 o | | | changeset: 26:7f25b6c2f0b9
2684 |\ \ \ \ parent: 18:1aa84d96232a
2684 |\ \ \ \ parent: 18:1aa84d96232a
2685 | | | | | parent: 25:91da8ed57247
2685 | | | | | parent: 25:91da8ed57247
2686 | | | | | user: test
2686 | | | | | user: test
2687 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2687 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2688 | | | | | summary: (26) merge one known; far right
2688 | | | | | summary: (26) merge one known; far right
2689 | | | | |
2689 | | | | |
2690 | o-----+ changeset: 25:91da8ed57247
2690 | o-----+ changeset: 25:91da8ed57247
2691 | | | | | parent: 21:d42a756af44d
2691 | | | | | parent: 21:d42a756af44d
2692 | | | | | parent: 24:a9c19a3d96b7
2692 | | | | | parent: 24:a9c19a3d96b7
2693 | | | | | user: test
2693 | | | | | user: test
2694 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2694 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2695 | | | | | summary: (25) merge one known; far left
2695 | | | | | summary: (25) merge one known; far left
2696 | | | | |
2696 | | | | |
2697 | o | | | changeset: 24:a9c19a3d96b7
2697 | o | | | changeset: 24:a9c19a3d96b7
2698 | |\ \ \ \ parent: 0:e6eb3150255d
2698 | |\ \ \ \ parent: 0:e6eb3150255d
2699 | | | | | | parent: 23:a01cddf0766d
2699 | | | | | | parent: 23:a01cddf0766d
2700 | | | | | | user: test
2700 | | | | | | user: test
2701 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2701 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2702 | | | | | | summary: (24) merge one known; immediate right
2702 | | | | | | summary: (24) merge one known; immediate right
2703 | | | | | |
2703 | | | | | |
2704 | o---+ | | changeset: 23:a01cddf0766d
2704 | o---+ | | changeset: 23:a01cddf0766d
2705 | | | | | | parent: 1:6db2ef61d156
2705 | | | | | | parent: 1:6db2ef61d156
2706 | | | | | | parent: 22:e0d9cccacb5d
2706 | | | | | | parent: 22:e0d9cccacb5d
2707 | | | | | | user: test
2707 | | | | | | user: test
2708 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2708 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2709 | | | | | | summary: (23) merge one known; immediate left
2709 | | | | | | summary: (23) merge one known; immediate left
2710 | | | | | |
2710 | | | | | |
2711 | o-------+ changeset: 22:e0d9cccacb5d
2711 | o-------+ changeset: 22:e0d9cccacb5d
2712 | | | | | | parent: 18:1aa84d96232a
2712 | | | | | | parent: 18:1aa84d96232a
2713 |/ / / / / parent: 21:d42a756af44d
2713 |/ / / / / parent: 21:d42a756af44d
2714 | | | | | user: test
2714 | | | | | user: test
2715 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2715 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2716 | | | | | summary: (22) merge two known; one far left, one far right
2716 | | | | | summary: (22) merge two known; one far left, one far right
2717 | | | | |
2717 | | | | |
2718 | | | | o changeset: 21:d42a756af44d
2718 | | | | o changeset: 21:d42a756af44d
2719 | | | | |\ parent: 19:31ddc2c1573b
2719 | | | | |\ parent: 19:31ddc2c1573b
2720 | | | | | | parent: 20:d30ed6450e32
2720 | | | | | | parent: 20:d30ed6450e32
2721 | | | | | | user: test
2721 | | | | | | user: test
2722 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2722 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2723 | | | | | | summary: (21) expand
2723 | | | | | | summary: (21) expand
2724 | | | | | |
2724 | | | | | |
2725 +-+-------o changeset: 20:d30ed6450e32
2725 +-+-------o changeset: 20:d30ed6450e32
2726 | | | | | parent: 0:e6eb3150255d
2726 | | | | | parent: 0:e6eb3150255d
2727 | | | | | parent: 18:1aa84d96232a
2727 | | | | | parent: 18:1aa84d96232a
2728 | | | | | user: test
2728 | | | | | user: test
2729 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2729 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2730 | | | | | summary: (20) merge two known; two far right
2730 | | | | | summary: (20) merge two known; two far right
2731 | | | | |
2731 | | | | |
2732 | | | | o changeset: 19:31ddc2c1573b
2732 | | | | o changeset: 19:31ddc2c1573b
2733 | | | | |\ parent: 15:1dda3f72782d
2733 | | | | |\ parent: 15:1dda3f72782d
2734 | | | | | | parent: 17:44765d7c06e0
2734 | | | | | | parent: 17:44765d7c06e0
2735 | | | | | | user: test
2735 | | | | | | user: test
2736 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2736 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2737 | | | | | | summary: (19) expand
2737 | | | | | | summary: (19) expand
2738 | | | | | |
2738 | | | | | |
2739 o---+---+ | changeset: 18:1aa84d96232a
2739 o---+---+ | changeset: 18:1aa84d96232a
2740 | | | | | parent: 1:6db2ef61d156
2740 | | | | | parent: 1:6db2ef61d156
2741 / / / / / parent: 15:1dda3f72782d
2741 / / / / / parent: 15:1dda3f72782d
2742 | | | | | user: test
2742 | | | | | user: test
2743 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2743 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2744 | | | | | summary: (18) merge two known; two far left
2744 | | | | | summary: (18) merge two known; two far left
2745 | | | | |
2745 | | | | |
2746 | | | | o changeset: 17:44765d7c06e0
2746 | | | | o changeset: 17:44765d7c06e0
2747 | | | | |\ parent: 12:86b91144a6e9
2747 | | | | |\ parent: 12:86b91144a6e9
2748 | | | | | | parent: 16:3677d192927d
2748 | | | | | | parent: 16:3677d192927d
2749 | | | | | | user: test
2749 | | | | | | user: test
2750 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2750 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2751 | | | | | | summary: (17) expand
2751 | | | | | | summary: (17) expand
2752 | | | | | |
2752 | | | | | |
2753 +-+-------o changeset: 16:3677d192927d
2753 +-+-------o changeset: 16:3677d192927d
2754 | | | | | parent: 0:e6eb3150255d
2754 | | | | | parent: 0:e6eb3150255d
2755 | | | | | parent: 1:6db2ef61d156
2755 | | | | | parent: 1:6db2ef61d156
2756 | | | | | user: test
2756 | | | | | user: test
2757 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2757 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2758 | | | | | summary: (16) merge two known; one immediate right, one near right
2758 | | | | | summary: (16) merge two known; one immediate right, one near right
2759 | | | | |
2759 | | | | |
2760 | | | o | changeset: 15:1dda3f72782d
2760 | | | o | changeset: 15:1dda3f72782d
2761 | | | |\ \ parent: 13:22d8966a97e3
2761 | | | |\ \ parent: 13:22d8966a97e3
2762 | | | | | | parent: 14:8eac370358ef
2762 | | | | | | parent: 14:8eac370358ef
2763 | | | | | | user: test
2763 | | | | | | user: test
2764 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2764 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2765 | | | | | | summary: (15) expand
2765 | | | | | | summary: (15) expand
2766 | | | | | |
2766 | | | | | |
2767 +-------o | changeset: 14:8eac370358ef
2767 +-------o | changeset: 14:8eac370358ef
2768 | | | | |/ parent: 0:e6eb3150255d
2768 | | | | |/ parent: 0:e6eb3150255d
2769 | | | | | parent: 12:86b91144a6e9
2769 | | | | | parent: 12:86b91144a6e9
2770 | | | | | user: test
2770 | | | | | user: test
2771 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2771 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2772 | | | | | summary: (14) merge two known; one immediate right, one far right
2772 | | | | | summary: (14) merge two known; one immediate right, one far right
2773 | | | | |
2773 | | | | |
2774 | | | o | changeset: 13:22d8966a97e3
2774 | | | o | changeset: 13:22d8966a97e3
2775 | | | |\ \ parent: 9:7010c0af0a35
2775 | | | |\ \ parent: 9:7010c0af0a35
2776 | | | | | | parent: 11:832d76e6bdf2
2776 | | | | | | parent: 11:832d76e6bdf2
2777 | | | | | | user: test
2777 | | | | | | user: test
2778 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2778 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2779 | | | | | | summary: (13) expand
2779 | | | | | | summary: (13) expand
2780 | | | | | |
2780 | | | | | |
2781 | +---+---o changeset: 12:86b91144a6e9
2781 | +---+---o changeset: 12:86b91144a6e9
2782 | | | | | parent: 1:6db2ef61d156
2782 | | | | | parent: 1:6db2ef61d156
2783 | | | | | parent: 9:7010c0af0a35
2783 | | | | | parent: 9:7010c0af0a35
2784 | | | | | user: test
2784 | | | | | user: test
2785 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2785 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2786 | | | | | summary: (12) merge two known; one immediate right, one far left
2786 | | | | | summary: (12) merge two known; one immediate right, one far left
2787 | | | | |
2787 | | | | |
2788 | | | | o changeset: 11:832d76e6bdf2
2788 | | | | o changeset: 11:832d76e6bdf2
2789 | | | | |\ parent: 6:b105a072e251
2789 | | | | |\ parent: 6:b105a072e251
2790 | | | | | | parent: 10:74c64d036d72
2790 | | | | | | parent: 10:74c64d036d72
2791 | | | | | | user: test
2791 | | | | | | user: test
2792 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2792 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2793 | | | | | | summary: (11) expand
2793 | | | | | | summary: (11) expand
2794 | | | | | |
2794 | | | | | |
2795 +---------o changeset: 10:74c64d036d72
2795 +---------o changeset: 10:74c64d036d72
2796 | | | | |/ parent: 0:e6eb3150255d
2796 | | | | |/ parent: 0:e6eb3150255d
2797 | | | | | parent: 6:b105a072e251
2797 | | | | | parent: 6:b105a072e251
2798 | | | | | user: test
2798 | | | | | user: test
2799 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2799 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2800 | | | | | summary: (10) merge two known; one immediate left, one near right
2800 | | | | | summary: (10) merge two known; one immediate left, one near right
2801 | | | | |
2801 | | | | |
2802 | | | o | changeset: 9:7010c0af0a35
2802 | | | o | changeset: 9:7010c0af0a35
2803 | | | |\ \ parent: 7:b632bb1b1224
2803 | | | |\ \ parent: 7:b632bb1b1224
2804 | | | | | | parent: 8:7a0b11f71937
2804 | | | | | | parent: 8:7a0b11f71937
2805 | | | | | | user: test
2805 | | | | | | user: test
2806 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2806 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2807 | | | | | | summary: (9) expand
2807 | | | | | | summary: (9) expand
2808 | | | | | |
2808 | | | | | |
2809 +-------o | changeset: 8:7a0b11f71937
2809 +-------o | changeset: 8:7a0b11f71937
2810 | | | |/ / parent: 0:e6eb3150255d
2810 | | | |/ / parent: 0:e6eb3150255d
2811 | | | | | parent: 7:b632bb1b1224
2811 | | | | | parent: 7:b632bb1b1224
2812 | | | | | user: test
2812 | | | | | user: test
2813 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2813 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2814 | | | | | summary: (8) merge two known; one immediate left, one far right
2814 | | | | | summary: (8) merge two known; one immediate left, one far right
2815 | | | | |
2815 | | | | |
2816 | | | o | changeset: 7:b632bb1b1224
2816 | | | o | changeset: 7:b632bb1b1224
2817 | | | |\ \ parent: 2:3d9a33b8d1e1
2817 | | | |\ \ parent: 2:3d9a33b8d1e1
2818 | | | | | | parent: 5:4409d547b708
2818 | | | | | | parent: 5:4409d547b708
2819 | | | | | | user: test
2819 | | | | | | user: test
2820 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2820 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2821 | | | | | | summary: (7) expand
2821 | | | | | | summary: (7) expand
2822 | | | | | |
2822 | | | | | |
2823 | | | +---o changeset: 6:b105a072e251
2823 | | | +---o changeset: 6:b105a072e251
2824 | | | | |/ parent: 2:3d9a33b8d1e1
2824 | | | | |/ parent: 2:3d9a33b8d1e1
2825 | | | | | parent: 5:4409d547b708
2825 | | | | | parent: 5:4409d547b708
2826 | | | | | user: test
2826 | | | | | user: test
2827 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2827 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2828 | | | | | summary: (6) merge two known; one immediate left, one far left
2828 | | | | | summary: (6) merge two known; one immediate left, one far left
2829 | | | | |
2829 | | | | |
2830 | | | o | changeset: 5:4409d547b708
2830 | | | o | changeset: 5:4409d547b708
2831 | | | |\ \ parent: 3:27eef8ed80b4
2831 | | | |\ \ parent: 3:27eef8ed80b4
2832 | | | | | | parent: 4:26a8bac39d9f
2832 | | | | | | parent: 4:26a8bac39d9f
2833 | | | | | | user: test
2833 | | | | | | user: test
2834 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2834 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2835 | | | | | | summary: (5) expand
2835 | | | | | | summary: (5) expand
2836 | | | | | |
2836 | | | | | |
2837 | +---o | | changeset: 4:26a8bac39d9f
2837 | +---o | | changeset: 4:26a8bac39d9f
2838 | | | |/ / parent: 1:6db2ef61d156
2838 | | | |/ / parent: 1:6db2ef61d156
2839 | | | | | parent: 3:27eef8ed80b4
2839 | | | | | parent: 3:27eef8ed80b4
2840 | | | | | user: test
2840 | | | | | user: test
2841 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2841 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2842 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2842 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2843 | | | | |
2843 | | | | |
2844
2844
2845 .. unless HGPLAINEXCEPT=graph is set:
2845 .. unless HGPLAINEXCEPT=graph is set:
2846
2846
2847 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2847 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2848 @ changeset: 36:08a19a744424
2848 @ changeset: 36:08a19a744424
2849 : branch: branch
2849 : branch: branch
2850 : tag: tip
2850 : tag: tip
2851 : parent: 35:9159c3644c5e
2851 : parent: 35:9159c3644c5e
2852 : parent: 35:9159c3644c5e
2852 : parent: 35:9159c3644c5e
2853 : user: test
2853 : user: test
2854 : date: Thu Jan 01 00:00:36 1970 +0000
2854 : date: Thu Jan 01 00:00:36 1970 +0000
2855 : summary: (36) buggy merge: identical parents
2855 : summary: (36) buggy merge: identical parents
2856 :
2856 :
2857 o changeset: 32:d06dffa21a31
2857 o changeset: 32:d06dffa21a31
2858 |\ parent: 27:886ed638191b
2858 |\ parent: 27:886ed638191b
2859 | : parent: 31:621d83e11f67
2859 | : parent: 31:621d83e11f67
2860 | : user: test
2860 | : user: test
2861 | : date: Thu Jan 01 00:00:32 1970 +0000
2861 | : date: Thu Jan 01 00:00:32 1970 +0000
2862 | : summary: (32) expand
2862 | : summary: (32) expand
2863 | :
2863 | :
2864 o : changeset: 31:621d83e11f67
2864 o : changeset: 31:621d83e11f67
2865 |\: parent: 21:d42a756af44d
2865 |\: parent: 21:d42a756af44d
2866 | : parent: 30:6e11cd4b648f
2866 | : parent: 30:6e11cd4b648f
2867 | : user: test
2867 | : user: test
2868 | : date: Thu Jan 01 00:00:31 1970 +0000
2868 | : date: Thu Jan 01 00:00:31 1970 +0000
2869 | : summary: (31) expand
2869 | : summary: (31) expand
2870 | :
2870 | :
2871 o : changeset: 30:6e11cd4b648f
2871 o : changeset: 30:6e11cd4b648f
2872 |\ \ parent: 28:44ecd0b9ae99
2872 |\ \ parent: 28:44ecd0b9ae99
2873 | ~ : parent: 29:cd9bb2be7593
2873 | ~ : parent: 29:cd9bb2be7593
2874 | : user: test
2874 | : user: test
2875 | : date: Thu Jan 01 00:00:30 1970 +0000
2875 | : date: Thu Jan 01 00:00:30 1970 +0000
2876 | : summary: (30) expand
2876 | : summary: (30) expand
2877 | /
2877 | /
2878 o : changeset: 28:44ecd0b9ae99
2878 o : changeset: 28:44ecd0b9ae99
2879 |\ \ parent: 1:6db2ef61d156
2879 |\ \ parent: 1:6db2ef61d156
2880 | ~ : parent: 26:7f25b6c2f0b9
2880 | ~ : parent: 26:7f25b6c2f0b9
2881 | : user: test
2881 | : user: test
2882 | : date: Thu Jan 01 00:00:28 1970 +0000
2882 | : date: Thu Jan 01 00:00:28 1970 +0000
2883 | : summary: (28) merge zero known
2883 | : summary: (28) merge zero known
2884 | /
2884 | /
2885 o : changeset: 26:7f25b6c2f0b9
2885 o : changeset: 26:7f25b6c2f0b9
2886 |\ \ parent: 18:1aa84d96232a
2886 |\ \ parent: 18:1aa84d96232a
2887 | | : parent: 25:91da8ed57247
2887 | | : parent: 25:91da8ed57247
2888 | | : user: test
2888 | | : user: test
2889 | | : date: Thu Jan 01 00:00:26 1970 +0000
2889 | | : date: Thu Jan 01 00:00:26 1970 +0000
2890 | | : summary: (26) merge one known; far right
2890 | | : summary: (26) merge one known; far right
2891 | | :
2891 | | :
2892 | o : changeset: 25:91da8ed57247
2892 | o : changeset: 25:91da8ed57247
2893 | |\: parent: 21:d42a756af44d
2893 | |\: parent: 21:d42a756af44d
2894 | | : parent: 24:a9c19a3d96b7
2894 | | : parent: 24:a9c19a3d96b7
2895 | | : user: test
2895 | | : user: test
2896 | | : date: Thu Jan 01 00:00:25 1970 +0000
2896 | | : date: Thu Jan 01 00:00:25 1970 +0000
2897 | | : summary: (25) merge one known; far left
2897 | | : summary: (25) merge one known; far left
2898 | | :
2898 | | :
2899 | o : changeset: 24:a9c19a3d96b7
2899 | o : changeset: 24:a9c19a3d96b7
2900 | |\ \ parent: 0:e6eb3150255d
2900 | |\ \ parent: 0:e6eb3150255d
2901 | | ~ : parent: 23:a01cddf0766d
2901 | | ~ : parent: 23:a01cddf0766d
2902 | | : user: test
2902 | | : user: test
2903 | | : date: Thu Jan 01 00:00:24 1970 +0000
2903 | | : date: Thu Jan 01 00:00:24 1970 +0000
2904 | | : summary: (24) merge one known; immediate right
2904 | | : summary: (24) merge one known; immediate right
2905 | | /
2905 | | /
2906 | o : changeset: 23:a01cddf0766d
2906 | o : changeset: 23:a01cddf0766d
2907 | |\ \ parent: 1:6db2ef61d156
2907 | |\ \ parent: 1:6db2ef61d156
2908 | | ~ : parent: 22:e0d9cccacb5d
2908 | | ~ : parent: 22:e0d9cccacb5d
2909 | | : user: test
2909 | | : user: test
2910 | | : date: Thu Jan 01 00:00:23 1970 +0000
2910 | | : date: Thu Jan 01 00:00:23 1970 +0000
2911 | | : summary: (23) merge one known; immediate left
2911 | | : summary: (23) merge one known; immediate left
2912 | | /
2912 | | /
2913 | o : changeset: 22:e0d9cccacb5d
2913 | o : changeset: 22:e0d9cccacb5d
2914 |/:/ parent: 18:1aa84d96232a
2914 |/:/ parent: 18:1aa84d96232a
2915 | : parent: 21:d42a756af44d
2915 | : parent: 21:d42a756af44d
2916 | : user: test
2916 | : user: test
2917 | : date: Thu Jan 01 00:00:22 1970 +0000
2917 | : date: Thu Jan 01 00:00:22 1970 +0000
2918 | : summary: (22) merge two known; one far left, one far right
2918 | : summary: (22) merge two known; one far left, one far right
2919 | :
2919 | :
2920 | o changeset: 21:d42a756af44d
2920 | o changeset: 21:d42a756af44d
2921 | |\ parent: 19:31ddc2c1573b
2921 | |\ parent: 19:31ddc2c1573b
2922 | | | parent: 20:d30ed6450e32
2922 | | | parent: 20:d30ed6450e32
2923 | | | user: test
2923 | | | user: test
2924 | | | date: Thu Jan 01 00:00:21 1970 +0000
2924 | | | date: Thu Jan 01 00:00:21 1970 +0000
2925 | | | summary: (21) expand
2925 | | | summary: (21) expand
2926 | | |
2926 | | |
2927 +---o changeset: 20:d30ed6450e32
2927 +---o changeset: 20:d30ed6450e32
2928 | | | parent: 0:e6eb3150255d
2928 | | | parent: 0:e6eb3150255d
2929 | | ~ parent: 18:1aa84d96232a
2929 | | ~ parent: 18:1aa84d96232a
2930 | | user: test
2930 | | user: test
2931 | | date: Thu Jan 01 00:00:20 1970 +0000
2931 | | date: Thu Jan 01 00:00:20 1970 +0000
2932 | | summary: (20) merge two known; two far right
2932 | | summary: (20) merge two known; two far right
2933 | |
2933 | |
2934 | o changeset: 19:31ddc2c1573b
2934 | o changeset: 19:31ddc2c1573b
2935 | |\ parent: 15:1dda3f72782d
2935 | |\ parent: 15:1dda3f72782d
2936 | | | parent: 17:44765d7c06e0
2936 | | | parent: 17:44765d7c06e0
2937 | | | user: test
2937 | | | user: test
2938 | | | date: Thu Jan 01 00:00:19 1970 +0000
2938 | | | date: Thu Jan 01 00:00:19 1970 +0000
2939 | | | summary: (19) expand
2939 | | | summary: (19) expand
2940 | | |
2940 | | |
2941 o | | changeset: 18:1aa84d96232a
2941 o | | changeset: 18:1aa84d96232a
2942 |\| | parent: 1:6db2ef61d156
2942 |\| | parent: 1:6db2ef61d156
2943 ~ | | parent: 15:1dda3f72782d
2943 ~ | | parent: 15:1dda3f72782d
2944 | | user: test
2944 | | user: test
2945 | | date: Thu Jan 01 00:00:18 1970 +0000
2945 | | date: Thu Jan 01 00:00:18 1970 +0000
2946 | | summary: (18) merge two known; two far left
2946 | | summary: (18) merge two known; two far left
2947 / /
2947 / /
2948 | o changeset: 17:44765d7c06e0
2948 | o changeset: 17:44765d7c06e0
2949 | |\ parent: 12:86b91144a6e9
2949 | |\ parent: 12:86b91144a6e9
2950 | | | parent: 16:3677d192927d
2950 | | | parent: 16:3677d192927d
2951 | | | user: test
2951 | | | user: test
2952 | | | date: Thu Jan 01 00:00:17 1970 +0000
2952 | | | date: Thu Jan 01 00:00:17 1970 +0000
2953 | | | summary: (17) expand
2953 | | | summary: (17) expand
2954 | | |
2954 | | |
2955 | | o changeset: 16:3677d192927d
2955 | | o changeset: 16:3677d192927d
2956 | | |\ parent: 0:e6eb3150255d
2956 | | |\ parent: 0:e6eb3150255d
2957 | | ~ ~ parent: 1:6db2ef61d156
2957 | | ~ ~ parent: 1:6db2ef61d156
2958 | | user: test
2958 | | user: test
2959 | | date: Thu Jan 01 00:00:16 1970 +0000
2959 | | date: Thu Jan 01 00:00:16 1970 +0000
2960 | | summary: (16) merge two known; one immediate right, one near right
2960 | | summary: (16) merge two known; one immediate right, one near right
2961 | |
2961 | |
2962 o | changeset: 15:1dda3f72782d
2962 o | changeset: 15:1dda3f72782d
2963 |\ \ parent: 13:22d8966a97e3
2963 |\ \ parent: 13:22d8966a97e3
2964 | | | parent: 14:8eac370358ef
2964 | | | parent: 14:8eac370358ef
2965 | | | user: test
2965 | | | user: test
2966 | | | date: Thu Jan 01 00:00:15 1970 +0000
2966 | | | date: Thu Jan 01 00:00:15 1970 +0000
2967 | | | summary: (15) expand
2967 | | | summary: (15) expand
2968 | | |
2968 | | |
2969 | o | changeset: 14:8eac370358ef
2969 | o | changeset: 14:8eac370358ef
2970 | |\| parent: 0:e6eb3150255d
2970 | |\| parent: 0:e6eb3150255d
2971 | ~ | parent: 12:86b91144a6e9
2971 | ~ | parent: 12:86b91144a6e9
2972 | | user: test
2972 | | user: test
2973 | | date: Thu Jan 01 00:00:14 1970 +0000
2973 | | date: Thu Jan 01 00:00:14 1970 +0000
2974 | | summary: (14) merge two known; one immediate right, one far right
2974 | | summary: (14) merge two known; one immediate right, one far right
2975 | /
2975 | /
2976 o | changeset: 13:22d8966a97e3
2976 o | changeset: 13:22d8966a97e3
2977 |\ \ parent: 9:7010c0af0a35
2977 |\ \ parent: 9:7010c0af0a35
2978 | | | parent: 11:832d76e6bdf2
2978 | | | parent: 11:832d76e6bdf2
2979 | | | user: test
2979 | | | user: test
2980 | | | date: Thu Jan 01 00:00:13 1970 +0000
2980 | | | date: Thu Jan 01 00:00:13 1970 +0000
2981 | | | summary: (13) expand
2981 | | | summary: (13) expand
2982 | | |
2982 | | |
2983 +---o changeset: 12:86b91144a6e9
2983 +---o changeset: 12:86b91144a6e9
2984 | | | parent: 1:6db2ef61d156
2984 | | | parent: 1:6db2ef61d156
2985 | | ~ parent: 9:7010c0af0a35
2985 | | ~ parent: 9:7010c0af0a35
2986 | | user: test
2986 | | user: test
2987 | | date: Thu Jan 01 00:00:12 1970 +0000
2987 | | date: Thu Jan 01 00:00:12 1970 +0000
2988 | | summary: (12) merge two known; one immediate right, one far left
2988 | | summary: (12) merge two known; one immediate right, one far left
2989 | |
2989 | |
2990 | o changeset: 11:832d76e6bdf2
2990 | o changeset: 11:832d76e6bdf2
2991 | |\ parent: 6:b105a072e251
2991 | |\ parent: 6:b105a072e251
2992 | | | parent: 10:74c64d036d72
2992 | | | parent: 10:74c64d036d72
2993 | | | user: test
2993 | | | user: test
2994 | | | date: Thu Jan 01 00:00:11 1970 +0000
2994 | | | date: Thu Jan 01 00:00:11 1970 +0000
2995 | | | summary: (11) expand
2995 | | | summary: (11) expand
2996 | | |
2996 | | |
2997 | | o changeset: 10:74c64d036d72
2997 | | o changeset: 10:74c64d036d72
2998 | |/| parent: 0:e6eb3150255d
2998 | |/| parent: 0:e6eb3150255d
2999 | | ~ parent: 6:b105a072e251
2999 | | ~ parent: 6:b105a072e251
3000 | | user: test
3000 | | user: test
3001 | | date: Thu Jan 01 00:00:10 1970 +0000
3001 | | date: Thu Jan 01 00:00:10 1970 +0000
3002 | | summary: (10) merge two known; one immediate left, one near right
3002 | | summary: (10) merge two known; one immediate left, one near right
3003 | |
3003 | |
3004 o | changeset: 9:7010c0af0a35
3004 o | changeset: 9:7010c0af0a35
3005 |\ \ parent: 7:b632bb1b1224
3005 |\ \ parent: 7:b632bb1b1224
3006 | | | parent: 8:7a0b11f71937
3006 | | | parent: 8:7a0b11f71937
3007 | | | user: test
3007 | | | user: test
3008 | | | date: Thu Jan 01 00:00:09 1970 +0000
3008 | | | date: Thu Jan 01 00:00:09 1970 +0000
3009 | | | summary: (9) expand
3009 | | | summary: (9) expand
3010 | | |
3010 | | |
3011 | o | changeset: 8:7a0b11f71937
3011 | o | changeset: 8:7a0b11f71937
3012 |/| | parent: 0:e6eb3150255d
3012 |/| | parent: 0:e6eb3150255d
3013 | ~ | parent: 7:b632bb1b1224
3013 | ~ | parent: 7:b632bb1b1224
3014 | | user: test
3014 | | user: test
3015 | | date: Thu Jan 01 00:00:08 1970 +0000
3015 | | date: Thu Jan 01 00:00:08 1970 +0000
3016 | | summary: (8) merge two known; one immediate left, one far right
3016 | | summary: (8) merge two known; one immediate left, one far right
3017 | /
3017 | /
3018 o | changeset: 7:b632bb1b1224
3018 o | changeset: 7:b632bb1b1224
3019 |\ \ parent: 2:3d9a33b8d1e1
3019 |\ \ parent: 2:3d9a33b8d1e1
3020 | ~ | parent: 5:4409d547b708
3020 | ~ | parent: 5:4409d547b708
3021 | | user: test
3021 | | user: test
3022 | | date: Thu Jan 01 00:00:07 1970 +0000
3022 | | date: Thu Jan 01 00:00:07 1970 +0000
3023 | | summary: (7) expand
3023 | | summary: (7) expand
3024 | /
3024 | /
3025 | o changeset: 6:b105a072e251
3025 | o changeset: 6:b105a072e251
3026 |/| parent: 2:3d9a33b8d1e1
3026 |/| parent: 2:3d9a33b8d1e1
3027 | ~ parent: 5:4409d547b708
3027 | ~ parent: 5:4409d547b708
3028 | user: test
3028 | user: test
3029 | date: Thu Jan 01 00:00:06 1970 +0000
3029 | date: Thu Jan 01 00:00:06 1970 +0000
3030 | summary: (6) merge two known; one immediate left, one far left
3030 | summary: (6) merge two known; one immediate left, one far left
3031 |
3031 |
3032 o changeset: 5:4409d547b708
3032 o changeset: 5:4409d547b708
3033 |\ parent: 3:27eef8ed80b4
3033 |\ parent: 3:27eef8ed80b4
3034 | ~ parent: 4:26a8bac39d9f
3034 | ~ parent: 4:26a8bac39d9f
3035 | user: test
3035 | user: test
3036 | date: Thu Jan 01 00:00:05 1970 +0000
3036 | date: Thu Jan 01 00:00:05 1970 +0000
3037 | summary: (5) expand
3037 | summary: (5) expand
3038 |
3038 |
3039 o changeset: 4:26a8bac39d9f
3039 o changeset: 4:26a8bac39d9f
3040 |\ parent: 1:6db2ef61d156
3040 |\ parent: 1:6db2ef61d156
3041 ~ ~ parent: 3:27eef8ed80b4
3041 ~ ~ parent: 3:27eef8ed80b4
3042 user: test
3042 user: test
3043 date: Thu Jan 01 00:00:04 1970 +0000
3043 date: Thu Jan 01 00:00:04 1970 +0000
3044 summary: (4) merge two known; one immediate left, one immediate right
3044 summary: (4) merge two known; one immediate left, one immediate right
3045
3045
3046 Draw only part of a grandparent line differently with "<N><char>"; only the
3046 Draw only part of a grandparent line differently with "<N><char>"; only the
3047 last N lines (for positive N) or everything but the first N lines (for
3047 last N lines (for positive N) or everything but the first N lines (for
3048 negative N) along the current node use the style, the rest of the edge uses
3048 negative N) along the current node use the style, the rest of the edge uses
3049 the parent edge styling.
3049 the parent edge styling.
3050
3050
3051 Last 3 lines:
3051 Last 3 lines:
3052
3052
3053 $ cat << EOF >> $HGRCPATH
3053 $ cat << EOF >> $HGRCPATH
3054 > [experimental]
3054 > [experimental]
3055 > graphstyle.parent = !
3055 > graphstyle.parent = !
3056 > graphstyle.grandparent = 3.
3056 > graphstyle.grandparent = 3.
3057 > graphstyle.missing =
3057 > graphstyle.missing =
3058 > EOF
3058 > EOF
3059 $ hg log -G -r '36:18 & file("a")' -m
3059 $ hg log -G -r '36:18 & file("a")' -m
3060 @ changeset: 36:08a19a744424
3060 @ changeset: 36:08a19a744424
3061 ! branch: branch
3061 ! branch: branch
3062 ! tag: tip
3062 ! tag: tip
3063 ! parent: 35:9159c3644c5e
3063 ! parent: 35:9159c3644c5e
3064 ! parent: 35:9159c3644c5e
3064 ! parent: 35:9159c3644c5e
3065 ! user: test
3065 ! user: test
3066 . date: Thu Jan 01 00:00:36 1970 +0000
3066 . date: Thu Jan 01 00:00:36 1970 +0000
3067 . summary: (36) buggy merge: identical parents
3067 . summary: (36) buggy merge: identical parents
3068 .
3068 .
3069 o changeset: 32:d06dffa21a31
3069 o changeset: 32:d06dffa21a31
3070 !\ parent: 27:886ed638191b
3070 !\ parent: 27:886ed638191b
3071 ! ! parent: 31:621d83e11f67
3071 ! ! parent: 31:621d83e11f67
3072 ! ! user: test
3072 ! ! user: test
3073 ! . date: Thu Jan 01 00:00:32 1970 +0000
3073 ! . date: Thu Jan 01 00:00:32 1970 +0000
3074 ! . summary: (32) expand
3074 ! . summary: (32) expand
3075 ! .
3075 ! .
3076 o ! changeset: 31:621d83e11f67
3076 o ! changeset: 31:621d83e11f67
3077 !\! parent: 21:d42a756af44d
3077 !\! parent: 21:d42a756af44d
3078 ! ! parent: 30:6e11cd4b648f
3078 ! ! parent: 30:6e11cd4b648f
3079 ! ! user: test
3079 ! ! user: test
3080 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3080 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3081 ! ! summary: (31) expand
3081 ! ! summary: (31) expand
3082 ! !
3082 ! !
3083 o ! changeset: 30:6e11cd4b648f
3083 o ! changeset: 30:6e11cd4b648f
3084 !\ \ parent: 28:44ecd0b9ae99
3084 !\ \ parent: 28:44ecd0b9ae99
3085 ! ~ ! parent: 29:cd9bb2be7593
3085 ! ~ ! parent: 29:cd9bb2be7593
3086 ! ! user: test
3086 ! ! user: test
3087 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3087 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3088 ! ! summary: (30) expand
3088 ! ! summary: (30) expand
3089 ! /
3089 ! /
3090 o ! changeset: 28:44ecd0b9ae99
3090 o ! changeset: 28:44ecd0b9ae99
3091 !\ \ parent: 1:6db2ef61d156
3091 !\ \ parent: 1:6db2ef61d156
3092 ! ~ ! parent: 26:7f25b6c2f0b9
3092 ! ~ ! parent: 26:7f25b6c2f0b9
3093 ! ! user: test
3093 ! ! user: test
3094 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3094 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3095 ! ! summary: (28) merge zero known
3095 ! ! summary: (28) merge zero known
3096 ! /
3096 ! /
3097 o ! changeset: 26:7f25b6c2f0b9
3097 o ! changeset: 26:7f25b6c2f0b9
3098 !\ \ parent: 18:1aa84d96232a
3098 !\ \ parent: 18:1aa84d96232a
3099 ! ! ! parent: 25:91da8ed57247
3099 ! ! ! parent: 25:91da8ed57247
3100 ! ! ! user: test
3100 ! ! ! user: test
3101 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3101 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3102 ! ! ! summary: (26) merge one known; far right
3102 ! ! ! summary: (26) merge one known; far right
3103 ! ! !
3103 ! ! !
3104 ! o ! changeset: 25:91da8ed57247
3104 ! o ! changeset: 25:91da8ed57247
3105 ! !\! parent: 21:d42a756af44d
3105 ! !\! parent: 21:d42a756af44d
3106 ! ! ! parent: 24:a9c19a3d96b7
3106 ! ! ! parent: 24:a9c19a3d96b7
3107 ! ! ! user: test
3107 ! ! ! user: test
3108 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3108 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3109 ! ! ! summary: (25) merge one known; far left
3109 ! ! ! summary: (25) merge one known; far left
3110 ! ! !
3110 ! ! !
3111 ! o ! changeset: 24:a9c19a3d96b7
3111 ! o ! changeset: 24:a9c19a3d96b7
3112 ! !\ \ parent: 0:e6eb3150255d
3112 ! !\ \ parent: 0:e6eb3150255d
3113 ! ! ~ ! parent: 23:a01cddf0766d
3113 ! ! ~ ! parent: 23:a01cddf0766d
3114 ! ! ! user: test
3114 ! ! ! user: test
3115 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3115 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3116 ! ! ! summary: (24) merge one known; immediate right
3116 ! ! ! summary: (24) merge one known; immediate right
3117 ! ! /
3117 ! ! /
3118 ! o ! changeset: 23:a01cddf0766d
3118 ! o ! changeset: 23:a01cddf0766d
3119 ! !\ \ parent: 1:6db2ef61d156
3119 ! !\ \ parent: 1:6db2ef61d156
3120 ! ! ~ ! parent: 22:e0d9cccacb5d
3120 ! ! ~ ! parent: 22:e0d9cccacb5d
3121 ! ! ! user: test
3121 ! ! ! user: test
3122 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3122 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3123 ! ! ! summary: (23) merge one known; immediate left
3123 ! ! ! summary: (23) merge one known; immediate left
3124 ! ! /
3124 ! ! /
3125 ! o ! changeset: 22:e0d9cccacb5d
3125 ! o ! changeset: 22:e0d9cccacb5d
3126 !/!/ parent: 18:1aa84d96232a
3126 !/!/ parent: 18:1aa84d96232a
3127 ! ! parent: 21:d42a756af44d
3127 ! ! parent: 21:d42a756af44d
3128 ! ! user: test
3128 ! ! user: test
3129 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3129 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3130 ! ! summary: (22) merge two known; one far left, one far right
3130 ! ! summary: (22) merge two known; one far left, one far right
3131 ! !
3131 ! !
3132 ! o changeset: 21:d42a756af44d
3132 ! o changeset: 21:d42a756af44d
3133 ! !\ parent: 19:31ddc2c1573b
3133 ! !\ parent: 19:31ddc2c1573b
3134 ! ! ! parent: 20:d30ed6450e32
3134 ! ! ! parent: 20:d30ed6450e32
3135 ! ! ! user: test
3135 ! ! ! user: test
3136 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3136 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3137 ! ! ! summary: (21) expand
3137 ! ! ! summary: (21) expand
3138 ! ! !
3138 ! ! !
3139 +---o changeset: 20:d30ed6450e32
3139 +---o changeset: 20:d30ed6450e32
3140 ! ! | parent: 0:e6eb3150255d
3140 ! ! | parent: 0:e6eb3150255d
3141 ! ! ~ parent: 18:1aa84d96232a
3141 ! ! ~ parent: 18:1aa84d96232a
3142 ! ! user: test
3142 ! ! user: test
3143 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3143 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3144 ! ! summary: (20) merge two known; two far right
3144 ! ! summary: (20) merge two known; two far right
3145 ! !
3145 ! !
3146 ! o changeset: 19:31ddc2c1573b
3146 ! o changeset: 19:31ddc2c1573b
3147 ! |\ parent: 15:1dda3f72782d
3147 ! |\ parent: 15:1dda3f72782d
3148 ! ~ ~ parent: 17:44765d7c06e0
3148 ! ~ ~ parent: 17:44765d7c06e0
3149 ! user: test
3149 ! user: test
3150 ! date: Thu Jan 01 00:00:19 1970 +0000
3150 ! date: Thu Jan 01 00:00:19 1970 +0000
3151 ! summary: (19) expand
3151 ! summary: (19) expand
3152 !
3152 !
3153 o changeset: 18:1aa84d96232a
3153 o changeset: 18:1aa84d96232a
3154 |\ parent: 1:6db2ef61d156
3154 |\ parent: 1:6db2ef61d156
3155 ~ ~ parent: 15:1dda3f72782d
3155 ~ ~ parent: 15:1dda3f72782d
3156 user: test
3156 user: test
3157 date: Thu Jan 01 00:00:18 1970 +0000
3157 date: Thu Jan 01 00:00:18 1970 +0000
3158 summary: (18) merge two known; two far left
3158 summary: (18) merge two known; two far left
3159
3159
3160 All but the first 3 lines:
3160 All but the first 3 lines:
3161
3161
3162 $ cat << EOF >> $HGRCPATH
3162 $ cat << EOF >> $HGRCPATH
3163 > [experimental]
3163 > [experimental]
3164 > graphstyle.parent = !
3164 > graphstyle.parent = !
3165 > graphstyle.grandparent = -3.
3165 > graphstyle.grandparent = -3.
3166 > graphstyle.missing =
3166 > graphstyle.missing =
3167 > EOF
3167 > EOF
3168 $ hg log -G -r '36:18 & file("a")' -m
3168 $ hg log -G -r '36:18 & file("a")' -m
3169 @ changeset: 36:08a19a744424
3169 @ changeset: 36:08a19a744424
3170 ! branch: branch
3170 ! branch: branch
3171 ! tag: tip
3171 ! tag: tip
3172 . parent: 35:9159c3644c5e
3172 . parent: 35:9159c3644c5e
3173 . parent: 35:9159c3644c5e
3173 . parent: 35:9159c3644c5e
3174 . user: test
3174 . user: test
3175 . date: Thu Jan 01 00:00:36 1970 +0000
3175 . date: Thu Jan 01 00:00:36 1970 +0000
3176 . summary: (36) buggy merge: identical parents
3176 . summary: (36) buggy merge: identical parents
3177 .
3177 .
3178 o changeset: 32:d06dffa21a31
3178 o changeset: 32:d06dffa21a31
3179 !\ parent: 27:886ed638191b
3179 !\ parent: 27:886ed638191b
3180 ! ! parent: 31:621d83e11f67
3180 ! ! parent: 31:621d83e11f67
3181 ! . user: test
3181 ! . user: test
3182 ! . date: Thu Jan 01 00:00:32 1970 +0000
3182 ! . date: Thu Jan 01 00:00:32 1970 +0000
3183 ! . summary: (32) expand
3183 ! . summary: (32) expand
3184 ! .
3184 ! .
3185 o ! changeset: 31:621d83e11f67
3185 o ! changeset: 31:621d83e11f67
3186 !\! parent: 21:d42a756af44d
3186 !\! parent: 21:d42a756af44d
3187 ! ! parent: 30:6e11cd4b648f
3187 ! ! parent: 30:6e11cd4b648f
3188 ! ! user: test
3188 ! ! user: test
3189 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3189 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3190 ! ! summary: (31) expand
3190 ! ! summary: (31) expand
3191 ! !
3191 ! !
3192 o ! changeset: 30:6e11cd4b648f
3192 o ! changeset: 30:6e11cd4b648f
3193 !\ \ parent: 28:44ecd0b9ae99
3193 !\ \ parent: 28:44ecd0b9ae99
3194 ! ~ ! parent: 29:cd9bb2be7593
3194 ! ~ ! parent: 29:cd9bb2be7593
3195 ! ! user: test
3195 ! ! user: test
3196 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3196 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3197 ! ! summary: (30) expand
3197 ! ! summary: (30) expand
3198 ! /
3198 ! /
3199 o ! changeset: 28:44ecd0b9ae99
3199 o ! changeset: 28:44ecd0b9ae99
3200 !\ \ parent: 1:6db2ef61d156
3200 !\ \ parent: 1:6db2ef61d156
3201 ! ~ ! parent: 26:7f25b6c2f0b9
3201 ! ~ ! parent: 26:7f25b6c2f0b9
3202 ! ! user: test
3202 ! ! user: test
3203 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3203 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3204 ! ! summary: (28) merge zero known
3204 ! ! summary: (28) merge zero known
3205 ! /
3205 ! /
3206 o ! changeset: 26:7f25b6c2f0b9
3206 o ! changeset: 26:7f25b6c2f0b9
3207 !\ \ parent: 18:1aa84d96232a
3207 !\ \ parent: 18:1aa84d96232a
3208 ! ! ! parent: 25:91da8ed57247
3208 ! ! ! parent: 25:91da8ed57247
3209 ! ! ! user: test
3209 ! ! ! user: test
3210 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3210 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3211 ! ! ! summary: (26) merge one known; far right
3211 ! ! ! summary: (26) merge one known; far right
3212 ! ! !
3212 ! ! !
3213 ! o ! changeset: 25:91da8ed57247
3213 ! o ! changeset: 25:91da8ed57247
3214 ! !\! parent: 21:d42a756af44d
3214 ! !\! parent: 21:d42a756af44d
3215 ! ! ! parent: 24:a9c19a3d96b7
3215 ! ! ! parent: 24:a9c19a3d96b7
3216 ! ! ! user: test
3216 ! ! ! user: test
3217 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3217 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3218 ! ! ! summary: (25) merge one known; far left
3218 ! ! ! summary: (25) merge one known; far left
3219 ! ! !
3219 ! ! !
3220 ! o ! changeset: 24:a9c19a3d96b7
3220 ! o ! changeset: 24:a9c19a3d96b7
3221 ! !\ \ parent: 0:e6eb3150255d
3221 ! !\ \ parent: 0:e6eb3150255d
3222 ! ! ~ ! parent: 23:a01cddf0766d
3222 ! ! ~ ! parent: 23:a01cddf0766d
3223 ! ! ! user: test
3223 ! ! ! user: test
3224 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3224 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3225 ! ! ! summary: (24) merge one known; immediate right
3225 ! ! ! summary: (24) merge one known; immediate right
3226 ! ! /
3226 ! ! /
3227 ! o ! changeset: 23:a01cddf0766d
3227 ! o ! changeset: 23:a01cddf0766d
3228 ! !\ \ parent: 1:6db2ef61d156
3228 ! !\ \ parent: 1:6db2ef61d156
3229 ! ! ~ ! parent: 22:e0d9cccacb5d
3229 ! ! ~ ! parent: 22:e0d9cccacb5d
3230 ! ! ! user: test
3230 ! ! ! user: test
3231 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3231 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3232 ! ! ! summary: (23) merge one known; immediate left
3232 ! ! ! summary: (23) merge one known; immediate left
3233 ! ! /
3233 ! ! /
3234 ! o ! changeset: 22:e0d9cccacb5d
3234 ! o ! changeset: 22:e0d9cccacb5d
3235 !/!/ parent: 18:1aa84d96232a
3235 !/!/ parent: 18:1aa84d96232a
3236 ! ! parent: 21:d42a756af44d
3236 ! ! parent: 21:d42a756af44d
3237 ! ! user: test
3237 ! ! user: test
3238 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3238 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3239 ! ! summary: (22) merge two known; one far left, one far right
3239 ! ! summary: (22) merge two known; one far left, one far right
3240 ! !
3240 ! !
3241 ! o changeset: 21:d42a756af44d
3241 ! o changeset: 21:d42a756af44d
3242 ! !\ parent: 19:31ddc2c1573b
3242 ! !\ parent: 19:31ddc2c1573b
3243 ! ! ! parent: 20:d30ed6450e32
3243 ! ! ! parent: 20:d30ed6450e32
3244 ! ! ! user: test
3244 ! ! ! user: test
3245 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3245 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3246 ! ! ! summary: (21) expand
3246 ! ! ! summary: (21) expand
3247 ! ! !
3247 ! ! !
3248 +---o changeset: 20:d30ed6450e32
3248 +---o changeset: 20:d30ed6450e32
3249 ! ! | parent: 0:e6eb3150255d
3249 ! ! | parent: 0:e6eb3150255d
3250 ! ! ~ parent: 18:1aa84d96232a
3250 ! ! ~ parent: 18:1aa84d96232a
3251 ! ! user: test
3251 ! ! user: test
3252 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3252 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3253 ! ! summary: (20) merge two known; two far right
3253 ! ! summary: (20) merge two known; two far right
3254 ! !
3254 ! !
3255 ! o changeset: 19:31ddc2c1573b
3255 ! o changeset: 19:31ddc2c1573b
3256 ! |\ parent: 15:1dda3f72782d
3256 ! |\ parent: 15:1dda3f72782d
3257 ! ~ ~ parent: 17:44765d7c06e0
3257 ! ~ ~ parent: 17:44765d7c06e0
3258 ! user: test
3258 ! user: test
3259 ! date: Thu Jan 01 00:00:19 1970 +0000
3259 ! date: Thu Jan 01 00:00:19 1970 +0000
3260 ! summary: (19) expand
3260 ! summary: (19) expand
3261 !
3261 !
3262 o changeset: 18:1aa84d96232a
3262 o changeset: 18:1aa84d96232a
3263 |\ parent: 1:6db2ef61d156
3263 |\ parent: 1:6db2ef61d156
3264 ~ ~ parent: 15:1dda3f72782d
3264 ~ ~ parent: 15:1dda3f72782d
3265 user: test
3265 user: test
3266 date: Thu Jan 01 00:00:18 1970 +0000
3266 date: Thu Jan 01 00:00:18 1970 +0000
3267 summary: (18) merge two known; two far left
3267 summary: (18) merge two known; two far left
3268
3268
3269 $ cd ..
3269 $ cd ..
3270
3270
3271 Change graph shorten, test better with graphstyle.missing not none
3271 Change graph shorten, test better with graphstyle.missing not none
3272
3272
3273 $ cd repo
3273 $ cd repo
3274 $ cat << EOF >> $HGRCPATH
3274 $ cat << EOF >> $HGRCPATH
3275 > [experimental]
3275 > [experimental]
3276 > graphstyle.parent = |
3276 > graphstyle.parent = |
3277 > graphstyle.grandparent = :
3277 > graphstyle.grandparent = :
3278 > graphstyle.missing = '
3278 > graphstyle.missing = '
3279 > graphshorten = true
3279 > graphshorten = true
3280 > EOF
3280 > EOF
3281 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3281 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3282 @ 36 (36) buggy merge: identical parents
3282 @ 36 (36) buggy merge: identical parents
3283 o 32 (32) expand
3283 o 32 (32) expand
3284 |\
3284 |\
3285 o : 31 (31) expand
3285 o : 31 (31) expand
3286 |\:
3286 |\:
3287 o : 30 (30) expand
3287 o : 30 (30) expand
3288 |\ \
3288 |\ \
3289 o \ \ 28 (28) merge zero known
3289 o \ \ 28 (28) merge zero known
3290 |\ \ \
3290 |\ \ \
3291 o \ \ \ 26 (26) merge one known; far right
3291 o \ \ \ 26 (26) merge one known; far right
3292 |\ \ \ \
3292 |\ \ \ \
3293 | o-----+ 25 (25) merge one known; far left
3293 | o-----+ 25 (25) merge one known; far left
3294 | o ' ' : 24 (24) merge one known; immediate right
3294 | o ' ' : 24 (24) merge one known; immediate right
3295 | |\ \ \ \
3295 | |\ \ \ \
3296 | o---+ ' : 23 (23) merge one known; immediate left
3296 | o---+ ' : 23 (23) merge one known; immediate left
3297 | o-------+ 22 (22) merge two known; one far left, one far right
3297 | o-------+ 22 (22) merge two known; one far left, one far right
3298 |/ / / / /
3298 |/ / / / /
3299 | ' ' ' o 21 (21) expand
3299 | ' ' ' o 21 (21) expand
3300 | ' ' ' |\
3300 | ' ' ' |\
3301 +-+-------o 20 (20) merge two known; two far right
3301 +-+-------o 20 (20) merge two known; two far right
3302 | ' ' ' o 19 (19) expand
3302 | ' ' ' o 19 (19) expand
3303 | ' ' ' |\
3303 | ' ' ' |\
3304 o---+---+ | 18 (18) merge two known; two far left
3304 o---+---+ | 18 (18) merge two known; two far left
3305 / / / / /
3305 / / / / /
3306 ' ' ' | o 17 (17) expand
3306 ' ' ' | o 17 (17) expand
3307 ' ' ' | |\
3307 ' ' ' | |\
3308 +-+-------o 16 (16) merge two known; one immediate right, one near right
3308 +-+-------o 16 (16) merge two known; one immediate right, one near right
3309 ' ' ' o | 15 (15) expand
3309 ' ' ' o | 15 (15) expand
3310 ' ' ' |\ \
3310 ' ' ' |\ \
3311 +-------o | 14 (14) merge two known; one immediate right, one far right
3311 +-------o | 14 (14) merge two known; one immediate right, one far right
3312 ' ' ' | |/
3312 ' ' ' | |/
3313 ' ' ' o | 13 (13) expand
3313 ' ' ' o | 13 (13) expand
3314 ' ' ' |\ \
3314 ' ' ' |\ \
3315 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3315 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3316 ' ' ' | o 11 (11) expand
3316 ' ' ' | o 11 (11) expand
3317 ' ' ' | |\
3317 ' ' ' | |\
3318 +---------o 10 (10) merge two known; one immediate left, one near right
3318 +---------o 10 (10) merge two known; one immediate left, one near right
3319 ' ' ' | |/
3319 ' ' ' | |/
3320 ' ' ' o | 9 (9) expand
3320 ' ' ' o | 9 (9) expand
3321 ' ' ' |\ \
3321 ' ' ' |\ \
3322 +-------o | 8 (8) merge two known; one immediate left, one far right
3322 +-------o | 8 (8) merge two known; one immediate left, one far right
3323 ' ' ' |/ /
3323 ' ' ' |/ /
3324 ' ' ' o | 7 (7) expand
3324 ' ' ' o | 7 (7) expand
3325 ' ' ' |\ \
3325 ' ' ' |\ \
3326 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3326 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3327 ' ' ' | '/
3327 ' ' ' | '/
3328 ' ' ' o ' 5 (5) expand
3328 ' ' ' o ' 5 (5) expand
3329 ' ' ' |\ \
3329 ' ' ' |\ \
3330 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3330 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3331 ' ' ' '/ /
3331 ' ' ' '/ /
3332
3332
3333 behavior with newlines
3333 behavior with newlines
3334
3334
3335 $ hg log -G -r ::2 -T '{rev} {desc}'
3335 $ hg log -G -r ::2 -T '{rev} {desc}'
3336 o 2 (2) collapse
3336 o 2 (2) collapse
3337 o 1 (1) collapse
3337 o 1 (1) collapse
3338 o 0 (0) root
3338 o 0 (0) root
3339
3339
3340 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3340 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3341 o 2 (2) collapse
3341 o 2 (2) collapse
3342 o 1 (1) collapse
3342 o 1 (1) collapse
3343 o 0 (0) root
3343 o 0 (0) root
3344
3344
3345 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3345 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3346 o 2 (2) collapse
3346 o 2 (2) collapse
3347 |
3347 |
3348 o 1 (1) collapse
3348 o 1 (1) collapse
3349 |
3349 |
3350 o 0 (0) root
3350 o 0 (0) root
3351
3351
3352
3352
3353 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3353 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3354 o
3354 o
3355 | 2 (2) collapse
3355 | 2 (2) collapse
3356 o
3356 o
3357 | 1 (1) collapse
3357 | 1 (1) collapse
3358 o
3358 o
3359 0 (0) root
3359 0 (0) root
3360
3360
3361 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3361 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3362 o 2 (2) collapse
3362 o 2 (2) collapse
3363 |
3363 |
3364 |
3364 |
3365 o 1 (1) collapse
3365 o 1 (1) collapse
3366 |
3366 |
3367 |
3367 |
3368 o 0 (0) root
3368 o 0 (0) root
3369
3369
3370
3370
3371 $ cd ..
3371 $ cd ..
3372
3372
3373 When inserting extra line nodes to handle more than 2 parents, ensure that
3373 When inserting extra line nodes to handle more than 2 parents, ensure that
3374 the right node styles are used (issue5174):
3374 the right node styles are used (issue5174):
3375
3375
3376 $ hg init repo-issue5174
3376 $ hg init repo-issue5174
3377 $ cd repo-issue5174
3377 $ cd repo-issue5174
3378 $ echo a > f0
3378 $ echo a > f0
3379 $ hg ci -Aqm 0
3379 $ hg ci -Aqm 0
3380 $ echo a > f1
3380 $ echo a > f1
3381 $ hg ci -Aqm 1
3381 $ hg ci -Aqm 1
3382 $ echo a > f2
3382 $ echo a > f2
3383 $ hg ci -Aqm 2
3383 $ hg ci -Aqm 2
3384 $ hg co ".^"
3384 $ hg co ".^"
3385 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3385 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3386 $ echo a > f3
3386 $ echo a > f3
3387 $ hg ci -Aqm 3
3387 $ hg ci -Aqm 3
3388 $ hg co ".^^"
3388 $ hg co ".^^"
3389 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3389 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3390 $ echo a > f4
3390 $ echo a > f4
3391 $ hg ci -Aqm 4
3391 $ hg ci -Aqm 4
3392 $ hg merge -r 2
3392 $ hg merge -r 2
3393 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3393 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3394 (branch merge, don't forget to commit)
3394 (branch merge, don't forget to commit)
3395 $ hg ci -qm 5
3395 $ hg ci -qm 5
3396 $ hg merge -r 3
3396 $ hg merge -r 3
3397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3398 (branch merge, don't forget to commit)
3398 (branch merge, don't forget to commit)
3399 $ hg ci -qm 6
3399 $ hg ci -qm 6
3400 $ hg log -G -r '0 | 1 | 2 | 6'
3400 $ hg log -G -r '0 | 1 | 2 | 6'
3401 @ changeset: 6:851fe89689ad
3401 @ changeset: 6:851fe89689ad
3402 :\ tag: tip
3402 :\ tag: tip
3403 : : parent: 5:4f1e3cf15f5d
3403 : : parent: 5:4f1e3cf15f5d
3404 : : parent: 3:b74ba7084d2d
3404 : : parent: 3:b74ba7084d2d
3405 : : user: test
3405 : : user: test
3406 : : date: Thu Jan 01 00:00:00 1970 +0000
3406 : : date: Thu Jan 01 00:00:00 1970 +0000
3407 : : summary: 6
3407 : : summary: 6
3408 : :
3408 : :
3409 : \
3409 : \
3410 : :\
3410 : :\
3411 : o : changeset: 2:3e6599df4cce
3411 : o : changeset: 2:3e6599df4cce
3412 : :/ user: test
3412 : :/ user: test
3413 : : date: Thu Jan 01 00:00:00 1970 +0000
3413 : : date: Thu Jan 01 00:00:00 1970 +0000
3414 : : summary: 2
3414 : : summary: 2
3415 : :
3415 : :
3416 : o changeset: 1:bd9a55143933
3416 : o changeset: 1:bd9a55143933
3417 :/ user: test
3417 :/ user: test
3418 : date: Thu Jan 01 00:00:00 1970 +0000
3418 : date: Thu Jan 01 00:00:00 1970 +0000
3419 : summary: 1
3419 : summary: 1
3420 :
3420 :
3421 o changeset: 0:870a5edc339c
3421 o changeset: 0:870a5edc339c
3422 user: test
3422 user: test
3423 date: Thu Jan 01 00:00:00 1970 +0000
3423 date: Thu Jan 01 00:00:00 1970 +0000
3424 summary: 0
3424 summary: 0
3425
3425
3426
3426
3427 $ cd ..
3427 $ cd ..
3428
3428
3429 Multiple roots (issue5440):
3429 Multiple roots (issue5440):
3430
3430
3431 $ hg init multiroots
3431 $ hg init multiroots
3432 $ cd multiroots
3432 $ cd multiroots
3433 $ cat <<EOF > .hg/hgrc
3433 $ cat <<EOF > .hg/hgrc
3434 > [ui]
3434 > [ui]
3435 > logtemplate = '{rev} {desc}\n\n'
3435 > logtemplate = '{rev} {desc}\n\n'
3436 > EOF
3436 > EOF
3437
3437
3438 $ touch foo
3438 $ touch foo
3439 $ hg ci -Aqm foo
3439 $ hg ci -Aqm foo
3440 $ hg co -q null
3440 $ hg co -q null
3441 $ touch bar
3441 $ touch bar
3442 $ hg ci -Aqm bar
3442 $ hg ci -Aqm bar
3443
3443
3444 $ hg log -Gr null:
3444 $ hg log -Gr null:
3445 @ 1 bar
3445 @ 1 bar
3446 |
3446 |
3447 | o 0 foo
3447 | o 0 foo
3448 |/
3448 |/
3449 o -1
3449 o -1
3450
3450
3451 $ hg log -Gr null+0
3451 $ hg log -Gr null+0
3452 o 0 foo
3452 o 0 foo
3453 |
3453 |
3454 o -1
3454 o -1
3455
3455
3456 $ hg log -Gr null+1
3456 $ hg log -Gr null+1
3457 @ 1 bar
3457 @ 1 bar
3458 |
3458 |
3459 o -1
3459 o -1
3460
3460
3461
3461
3462 $ cd ..
3462 $ cd ..
@@ -1,288 +1,288 b''
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 hg: parse error: 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 hg: parse error: 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 hg: parse error: 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+7,M4.1.0/02:00:00,M10.5.0/02:00:00"
31 $ TZ="PST+8PDT+7,M4.1.0/02:00:00,M10.5.0/02:00:00"
32 $ export TZ
32 $ export TZ
33
33
34 PST=UTC-8 / PDT=UTC-7
34 PST=UTC-8 / PDT=UTC-7
35 Summer time begins on April's first Sunday at 2:00am,
35 Summer time begins on April's first Sunday at 2:00am,
36 and ends on October's last Sunday at 2:00am.
36 and ends on October's last Sunday at 2:00am.
37
37
38 $ hg debugrebuildstate
38 $ hg debugrebuildstate
39 $ echo "a" > a
39 $ echo "a" > a
40 $ hg ci -d "2006-07-15 13:30" -m "summer@UTC-7"
40 $ hg ci -d "2006-07-15 13:30" -m "summer@UTC-7"
41 $ hg debugrebuildstate
41 $ hg debugrebuildstate
42 $ echo "b" > a
42 $ echo "b" > a
43 $ hg ci -d "2006-07-15 13:30 +0500" -m "summer@UTC+5"
43 $ hg ci -d "2006-07-15 13:30 +0500" -m "summer@UTC+5"
44 $ hg debugrebuildstate
44 $ hg debugrebuildstate
45 $ echo "c" > a
45 $ echo "c" > a
46 $ hg ci -d "2006-01-15 13:30" -m "winter@UTC-8"
46 $ hg ci -d "2006-01-15 13:30" -m "winter@UTC-8"
47 $ hg debugrebuildstate
47 $ hg debugrebuildstate
48 $ echo "d" > a
48 $ echo "d" > a
49 $ hg ci -d "2006-01-15 13:30 +0500" -m "winter@UTC+5"
49 $ hg ci -d "2006-01-15 13:30 +0500" -m "winter@UTC+5"
50 $ hg log --template '{date|date}\n'
50 $ hg log --template '{date|date}\n'
51 Sun Jan 15 13:30:00 2006 +0500
51 Sun Jan 15 13:30:00 2006 +0500
52 Sun Jan 15 13:30:00 2006 -0800
52 Sun Jan 15 13:30:00 2006 -0800
53 Sat Jul 15 13:30:00 2006 +0500
53 Sat Jul 15 13:30:00 2006 +0500
54 Sat Jul 15 13:30:00 2006 -0700
54 Sat Jul 15 13:30:00 2006 -0700
55 Sun Jun 11 00:26:40 2006 -0400
55 Sun Jun 11 00:26:40 2006 -0400
56 Sat Apr 15 13:30:00 2006 +0200
56 Sat Apr 15 13:30:00 2006 +0200
57 Sat Apr 15 13:30:00 2006 +0000
57 Sat Apr 15 13:30:00 2006 +0000
58 Wed Feb 01 13:00:30 2006 -0500
58 Wed Feb 01 13:00:30 2006 -0500
59 Wed Feb 01 13:00:30 2006 +0000
59 Wed Feb 01 13:00:30 2006 +0000
60
60
61 Test issue1014 (fractional timezones)
61 Test issue1014 (fractional timezones)
62
62
63 $ hg debugdate "1000000000 -16200" # 0430
63 $ hg debugdate "1000000000 -16200" # 0430
64 internal: 1000000000 -16200
64 internal: 1000000000 -16200
65 standard: Sun Sep 09 06:16:40 2001 +0430
65 standard: Sun Sep 09 06:16:40 2001 +0430
66 $ hg debugdate "1000000000 -15300" # 0415
66 $ hg debugdate "1000000000 -15300" # 0415
67 internal: 1000000000 -15300
67 internal: 1000000000 -15300
68 standard: Sun Sep 09 06:01:40 2001 +0415
68 standard: Sun Sep 09 06:01:40 2001 +0415
69 $ hg debugdate "1000000000 -14400" # 0400
69 $ hg debugdate "1000000000 -14400" # 0400
70 internal: 1000000000 -14400
70 internal: 1000000000 -14400
71 standard: Sun Sep 09 05:46:40 2001 +0400
71 standard: Sun Sep 09 05:46:40 2001 +0400
72 $ hg debugdate "1000000000 0" # GMT
72 $ hg debugdate "1000000000 0" # GMT
73 internal: 1000000000 0
73 internal: 1000000000 0
74 standard: Sun Sep 09 01:46:40 2001 +0000
74 standard: Sun Sep 09 01:46:40 2001 +0000
75 $ hg debugdate "1000000000 14400" # -0400
75 $ hg debugdate "1000000000 14400" # -0400
76 internal: 1000000000 14400
76 internal: 1000000000 14400
77 standard: Sat Sep 08 21:46:40 2001 -0400
77 standard: Sat Sep 08 21:46:40 2001 -0400
78 $ hg debugdate "1000000000 15300" # -0415
78 $ hg debugdate "1000000000 15300" # -0415
79 internal: 1000000000 15300
79 internal: 1000000000 15300
80 standard: Sat Sep 08 21:31:40 2001 -0415
80 standard: Sat Sep 08 21:31:40 2001 -0415
81 $ hg debugdate "1000000000 16200" # -0430
81 $ hg debugdate "1000000000 16200" # -0430
82 internal: 1000000000 16200
82 internal: 1000000000 16200
83 standard: Sat Sep 08 21:16:40 2001 -0430
83 standard: Sat Sep 08 21:16:40 2001 -0430
84 $ hg debugdate "Sat Sep 08 21:16:40 2001 +0430"
84 $ hg debugdate "Sat Sep 08 21:16:40 2001 +0430"
85 internal: 999967600 -16200
85 internal: 999967600 -16200
86 standard: Sat Sep 08 21:16:40 2001 +0430
86 standard: Sat Sep 08 21:16:40 2001 +0430
87 $ hg debugdate "Sat Sep 08 21:16:40 2001 -0430"
87 $ hg debugdate "Sat Sep 08 21:16:40 2001 -0430"
88 internal: 1000000000 16200
88 internal: 1000000000 16200
89 standard: Sat Sep 08 21:16:40 2001 -0430
89 standard: Sat Sep 08 21:16:40 2001 -0430
90
90
91 Test 12-hours times
91 Test 12-hours times
92
92
93 $ hg debugdate "2006-02-01 1:00:30PM +0000"
93 $ hg debugdate "2006-02-01 1:00:30PM +0000"
94 internal: 1138798830 0
94 internal: 1138798830 0
95 standard: Wed Feb 01 13:00:30 2006 +0000
95 standard: Wed Feb 01 13:00:30 2006 +0000
96 $ hg debugdate "1:00:30PM" > /dev/null
96 $ hg debugdate "1:00:30PM" > /dev/null
97
97
98 Normal range
98 Normal range
99
99
100 $ hg log -d -1
100 $ hg log -d -1
101
101
102 Negative range
102 Negative range
103
103
104 $ hg log -d "--2"
104 $ hg log -d "--2"
105 abort: -2 must be nonnegative (see 'hg help dates')
105 abort: -2 must be nonnegative (see 'hg help dates')
106 [255]
106 [255]
107
107
108 Whitespace only
108 Whitespace only
109
109
110 $ hg log -d " "
110 $ hg log -d " "
111 abort: dates cannot consist entirely of whitespace
111 abort: dates cannot consist entirely of whitespace
112 [255]
112 [255]
113
113
114 Test date formats with '>' or '<' accompanied by space characters
114 Test date formats with '>' or '<' accompanied by space characters
115
115
116 $ hg log -d '>' --template '{date|date}\n'
116 $ hg log -d '>' --template '{date|date}\n'
117 abort: invalid day spec, use '>DATE'
117 abort: invalid day spec, use '>DATE'
118 [255]
118 [255]
119 $ hg log -d '<' --template '{date|date}\n'
119 $ hg log -d '<' --template '{date|date}\n'
120 abort: invalid day spec, use '<DATE'
120 abort: invalid day spec, use '<DATE'
121 [255]
121 [255]
122
122
123 $ hg log -d ' >' --template '{date|date}\n'
123 $ hg log -d ' >' --template '{date|date}\n'
124 abort: invalid day spec, use '>DATE'
124 abort: invalid day spec, use '>DATE'
125 [255]
125 [255]
126 $ hg log -d ' <' --template '{date|date}\n'
126 $ hg log -d ' <' --template '{date|date}\n'
127 abort: invalid day spec, use '<DATE'
127 abort: invalid day spec, use '<DATE'
128 [255]
128 [255]
129
129
130 $ hg log -d '> ' --template '{date|date}\n'
130 $ hg log -d '> ' --template '{date|date}\n'
131 abort: invalid day spec, use '>DATE'
131 abort: invalid day spec, use '>DATE'
132 [255]
132 [255]
133 $ hg log -d '< ' --template '{date|date}\n'
133 $ hg log -d '< ' --template '{date|date}\n'
134 abort: invalid day spec, use '<DATE'
134 abort: invalid day spec, use '<DATE'
135 [255]
135 [255]
136
136
137 $ hg log -d ' > ' --template '{date|date}\n'
137 $ hg log -d ' > ' --template '{date|date}\n'
138 abort: invalid day spec, use '>DATE'
138 abort: invalid day spec, use '>DATE'
139 [255]
139 [255]
140 $ hg log -d ' < ' --template '{date|date}\n'
140 $ hg log -d ' < ' --template '{date|date}\n'
141 abort: invalid day spec, use '<DATE'
141 abort: invalid day spec, use '<DATE'
142 [255]
142 [255]
143
143
144 $ hg log -d '>02/01' --template '{date|date}\n'
144 $ hg log -d '>02/01' --template '{date|date}\n'
145 $ hg log -d '<02/01' --template '{date|date}\n'
145 $ hg log -d '<02/01' --template '{date|date}\n'
146 Sun Jan 15 13:30:00 2006 +0500
146 Sun Jan 15 13:30:00 2006 +0500
147 Sun Jan 15 13:30:00 2006 -0800
147 Sun Jan 15 13:30:00 2006 -0800
148 Sat Jul 15 13:30:00 2006 +0500
148 Sat Jul 15 13:30:00 2006 +0500
149 Sat Jul 15 13:30:00 2006 -0700
149 Sat Jul 15 13:30:00 2006 -0700
150 Sun Jun 11 00:26:40 2006 -0400
150 Sun Jun 11 00:26:40 2006 -0400
151 Sat Apr 15 13:30:00 2006 +0200
151 Sat Apr 15 13:30:00 2006 +0200
152 Sat Apr 15 13:30:00 2006 +0000
152 Sat Apr 15 13:30:00 2006 +0000
153 Wed Feb 01 13:00:30 2006 -0500
153 Wed Feb 01 13:00:30 2006 -0500
154 Wed Feb 01 13:00:30 2006 +0000
154 Wed Feb 01 13:00:30 2006 +0000
155
155
156 $ hg log -d ' >02/01' --template '{date|date}\n'
156 $ hg log -d ' >02/01' --template '{date|date}\n'
157 $ hg log -d ' <02/01' --template '{date|date}\n'
157 $ hg log -d ' <02/01' --template '{date|date}\n'
158 Sun Jan 15 13:30:00 2006 +0500
158 Sun Jan 15 13:30:00 2006 +0500
159 Sun Jan 15 13:30:00 2006 -0800
159 Sun Jan 15 13:30:00 2006 -0800
160 Sat Jul 15 13:30:00 2006 +0500
160 Sat Jul 15 13:30:00 2006 +0500
161 Sat Jul 15 13:30:00 2006 -0700
161 Sat Jul 15 13:30:00 2006 -0700
162 Sun Jun 11 00:26:40 2006 -0400
162 Sun Jun 11 00:26:40 2006 -0400
163 Sat Apr 15 13:30:00 2006 +0200
163 Sat Apr 15 13:30:00 2006 +0200
164 Sat Apr 15 13:30:00 2006 +0000
164 Sat Apr 15 13:30:00 2006 +0000
165 Wed Feb 01 13:00:30 2006 -0500
165 Wed Feb 01 13:00:30 2006 -0500
166 Wed Feb 01 13:00:30 2006 +0000
166 Wed Feb 01 13:00:30 2006 +0000
167
167
168 $ hg log -d '> 02/01' --template '{date|date}\n'
168 $ hg log -d '> 02/01' --template '{date|date}\n'
169 $ hg log -d '< 02/01' --template '{date|date}\n'
169 $ hg log -d '< 02/01' --template '{date|date}\n'
170 Sun Jan 15 13:30:00 2006 +0500
170 Sun Jan 15 13:30:00 2006 +0500
171 Sun Jan 15 13:30:00 2006 -0800
171 Sun Jan 15 13:30:00 2006 -0800
172 Sat Jul 15 13:30:00 2006 +0500
172 Sat Jul 15 13:30:00 2006 +0500
173 Sat Jul 15 13:30:00 2006 -0700
173 Sat Jul 15 13:30:00 2006 -0700
174 Sun Jun 11 00:26:40 2006 -0400
174 Sun Jun 11 00:26:40 2006 -0400
175 Sat Apr 15 13:30:00 2006 +0200
175 Sat Apr 15 13:30:00 2006 +0200
176 Sat Apr 15 13:30:00 2006 +0000
176 Sat Apr 15 13:30:00 2006 +0000
177 Wed Feb 01 13:00:30 2006 -0500
177 Wed Feb 01 13:00:30 2006 -0500
178 Wed Feb 01 13:00:30 2006 +0000
178 Wed Feb 01 13:00:30 2006 +0000
179
179
180 $ hg log -d ' > 02/01' --template '{date|date}\n'
180 $ hg log -d ' > 02/01' --template '{date|date}\n'
181 $ hg log -d ' < 02/01' --template '{date|date}\n'
181 $ hg log -d ' < 02/01' --template '{date|date}\n'
182 Sun Jan 15 13:30:00 2006 +0500
182 Sun Jan 15 13:30:00 2006 +0500
183 Sun Jan 15 13:30:00 2006 -0800
183 Sun Jan 15 13:30:00 2006 -0800
184 Sat Jul 15 13:30:00 2006 +0500
184 Sat Jul 15 13:30:00 2006 +0500
185 Sat Jul 15 13:30:00 2006 -0700
185 Sat Jul 15 13:30:00 2006 -0700
186 Sun Jun 11 00:26:40 2006 -0400
186 Sun Jun 11 00:26:40 2006 -0400
187 Sat Apr 15 13:30:00 2006 +0200
187 Sat Apr 15 13:30:00 2006 +0200
188 Sat Apr 15 13:30:00 2006 +0000
188 Sat Apr 15 13:30:00 2006 +0000
189 Wed Feb 01 13:00:30 2006 -0500
189 Wed Feb 01 13:00:30 2006 -0500
190 Wed Feb 01 13:00:30 2006 +0000
190 Wed Feb 01 13:00:30 2006 +0000
191
191
192 $ hg log -d '>02/01 ' --template '{date|date}\n'
192 $ hg log -d '>02/01 ' --template '{date|date}\n'
193 $ hg log -d '<02/01 ' --template '{date|date}\n'
193 $ hg log -d '<02/01 ' --template '{date|date}\n'
194 Sun Jan 15 13:30:00 2006 +0500
194 Sun Jan 15 13:30:00 2006 +0500
195 Sun Jan 15 13:30:00 2006 -0800
195 Sun Jan 15 13:30:00 2006 -0800
196 Sat Jul 15 13:30:00 2006 +0500
196 Sat Jul 15 13:30:00 2006 +0500
197 Sat Jul 15 13:30:00 2006 -0700
197 Sat Jul 15 13:30:00 2006 -0700
198 Sun Jun 11 00:26:40 2006 -0400
198 Sun Jun 11 00:26:40 2006 -0400
199 Sat Apr 15 13:30:00 2006 +0200
199 Sat Apr 15 13:30:00 2006 +0200
200 Sat Apr 15 13:30:00 2006 +0000
200 Sat Apr 15 13:30:00 2006 +0000
201 Wed Feb 01 13:00:30 2006 -0500
201 Wed Feb 01 13:00:30 2006 -0500
202 Wed Feb 01 13:00:30 2006 +0000
202 Wed Feb 01 13:00:30 2006 +0000
203
203
204 $ hg log -d ' >02/01 ' --template '{date|date}\n'
204 $ hg log -d ' >02/01 ' --template '{date|date}\n'
205 $ hg log -d ' <02/01 ' --template '{date|date}\n'
205 $ hg log -d ' <02/01 ' --template '{date|date}\n'
206 Sun Jan 15 13:30:00 2006 +0500
206 Sun Jan 15 13:30:00 2006 +0500
207 Sun Jan 15 13:30:00 2006 -0800
207 Sun Jan 15 13:30:00 2006 -0800
208 Sat Jul 15 13:30:00 2006 +0500
208 Sat Jul 15 13:30:00 2006 +0500
209 Sat Jul 15 13:30:00 2006 -0700
209 Sat Jul 15 13:30:00 2006 -0700
210 Sun Jun 11 00:26:40 2006 -0400
210 Sun Jun 11 00:26:40 2006 -0400
211 Sat Apr 15 13:30:00 2006 +0200
211 Sat Apr 15 13:30:00 2006 +0200
212 Sat Apr 15 13:30:00 2006 +0000
212 Sat Apr 15 13:30:00 2006 +0000
213 Wed Feb 01 13:00:30 2006 -0500
213 Wed Feb 01 13:00:30 2006 -0500
214 Wed Feb 01 13:00:30 2006 +0000
214 Wed Feb 01 13:00:30 2006 +0000
215
215
216 $ hg log -d '> 02/01 ' --template '{date|date}\n'
216 $ hg log -d '> 02/01 ' --template '{date|date}\n'
217 $ hg log -d '< 02/01 ' --template '{date|date}\n'
217 $ hg log -d '< 02/01 ' --template '{date|date}\n'
218 Sun Jan 15 13:30:00 2006 +0500
218 Sun Jan 15 13:30:00 2006 +0500
219 Sun Jan 15 13:30:00 2006 -0800
219 Sun Jan 15 13:30:00 2006 -0800
220 Sat Jul 15 13:30:00 2006 +0500
220 Sat Jul 15 13:30:00 2006 +0500
221 Sat Jul 15 13:30:00 2006 -0700
221 Sat Jul 15 13:30:00 2006 -0700
222 Sun Jun 11 00:26:40 2006 -0400
222 Sun Jun 11 00:26:40 2006 -0400
223 Sat Apr 15 13:30:00 2006 +0200
223 Sat Apr 15 13:30:00 2006 +0200
224 Sat Apr 15 13:30:00 2006 +0000
224 Sat Apr 15 13:30:00 2006 +0000
225 Wed Feb 01 13:00:30 2006 -0500
225 Wed Feb 01 13:00:30 2006 -0500
226 Wed Feb 01 13:00:30 2006 +0000
226 Wed Feb 01 13:00:30 2006 +0000
227
227
228 $ hg log -d ' > 02/01 ' --template '{date|date}\n'
228 $ hg log -d ' > 02/01 ' --template '{date|date}\n'
229 $ hg log -d ' < 02/01 ' --template '{date|date}\n'
229 $ hg log -d ' < 02/01 ' --template '{date|date}\n'
230 Sun Jan 15 13:30:00 2006 +0500
230 Sun Jan 15 13:30:00 2006 +0500
231 Sun Jan 15 13:30:00 2006 -0800
231 Sun Jan 15 13:30:00 2006 -0800
232 Sat Jul 15 13:30:00 2006 +0500
232 Sat Jul 15 13:30:00 2006 +0500
233 Sat Jul 15 13:30:00 2006 -0700
233 Sat Jul 15 13:30:00 2006 -0700
234 Sun Jun 11 00:26:40 2006 -0400
234 Sun Jun 11 00:26:40 2006 -0400
235 Sat Apr 15 13:30:00 2006 +0200
235 Sat Apr 15 13:30:00 2006 +0200
236 Sat Apr 15 13:30:00 2006 +0000
236 Sat Apr 15 13:30:00 2006 +0000
237 Wed Feb 01 13:00:30 2006 -0500
237 Wed Feb 01 13:00:30 2006 -0500
238 Wed Feb 01 13:00:30 2006 +0000
238 Wed Feb 01 13:00:30 2006 +0000
239
239
240 Test issue 3764 (interpreting 'today' and 'yesterday')
240 Test issue 3764 (interpreting 'today' and 'yesterday')
241 $ echo "hello" >> a
241 $ echo "hello" >> a
242 >>> import datetime
242 >>> import datetime
243 >>> today = datetime.date.today().strftime("%b %d")
243 >>> today = datetime.date.today().strftime("%b %d")
244 >>> yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%b %d")
244 >>> yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%b %d")
245 >>> dates = open('dates', 'w')
245 >>> dates = open('dates', 'w')
246 >>> dates.write(today + '\n')
246 >>> dates.write(today + '\n')
247 >>> dates.write(yesterday + '\n')
247 >>> dates.write(yesterday + '\n')
248 >>> dates.close()
248 >>> dates.close()
249 $ hg ci -d "`sed -n '1p' dates`" -m "today is a good day to code"
249 $ hg ci -d "`sed -n '1p' dates`" -m "today is a good day to code"
250 $ hg log -d today --template '{desc}\n'
250 $ hg log -d today --template '{desc}\n'
251 today is a good day to code
251 today is a good day to code
252 $ echo "goodbye" >> a
252 $ echo "goodbye" >> a
253 $ hg ci -d "`sed -n '2p' dates`" -m "the time traveler's code"
253 $ hg ci -d "`sed -n '2p' dates`" -m "the time traveler's code"
254 $ hg log -d yesterday --template '{desc}\n'
254 $ hg log -d yesterday --template '{desc}\n'
255 the time traveler's code
255 the time traveler's code
256 $ echo "foo" >> a
256 $ echo "foo" >> a
257 $ hg commit -d now -m 'Explicitly committed now.'
257 $ hg commit -d now -m 'Explicitly committed now.'
258 $ hg log -d today --template '{desc}\n'
258 $ hg log -d today --template '{desc}\n'
259 Explicitly committed now.
259 Explicitly committed now.
260 today is a good day to code
260 today is a good day to code
261
261
262 Test parsing various ISO8601 forms
262 Test parsing various ISO8601 forms
263
263
264 $ hg debugdate "2016-07-27T12:10:21"
264 $ hg debugdate "2016-07-27T12:10:21"
265 internal: 1469646621 * (glob)
265 internal: 1469646621 * (glob)
266 standard: Wed Jul 27 12:10:21 2016 -0700
266 standard: Wed Jul 27 12:10:21 2016 -0700
267 $ hg debugdate "2016-07-27T12:10:21Z"
267 $ hg debugdate "2016-07-27T12:10:21Z"
268 internal: 1469621421 0
268 internal: 1469621421 0
269 standard: Wed Jul 27 12:10:21 2016 +0000
269 standard: Wed Jul 27 12:10:21 2016 +0000
270 $ hg debugdate "2016-07-27T12:10:21+00:00"
270 $ hg debugdate "2016-07-27T12:10:21+00:00"
271 internal: 1469621421 0
271 internal: 1469621421 0
272 standard: Wed Jul 27 12:10:21 2016 +0000
272 standard: Wed Jul 27 12:10:21 2016 +0000
273 $ hg debugdate "2016-07-27T121021Z"
273 $ hg debugdate "2016-07-27T121021Z"
274 internal: 1469621421 0
274 internal: 1469621421 0
275 standard: Wed Jul 27 12:10:21 2016 +0000
275 standard: Wed Jul 27 12:10:21 2016 +0000
276
276
277 $ hg debugdate "2016-07-27 12:10:21"
277 $ hg debugdate "2016-07-27 12:10:21"
278 internal: 1469646621 * (glob)
278 internal: 1469646621 * (glob)
279 standard: Wed Jul 27 12:10:21 2016 -0700
279 standard: Wed Jul 27 12:10:21 2016 -0700
280 $ hg debugdate "2016-07-27 12:10:21Z"
280 $ hg debugdate "2016-07-27 12:10:21Z"
281 internal: 1469621421 0
281 internal: 1469621421 0
282 standard: Wed Jul 27 12:10:21 2016 +0000
282 standard: Wed Jul 27 12:10:21 2016 +0000
283 $ hg debugdate "2016-07-27 12:10:21+00:00"
283 $ hg debugdate "2016-07-27 12:10:21+00:00"
284 internal: 1469621421 0
284 internal: 1469621421 0
285 standard: Wed Jul 27 12:10:21 2016 +0000
285 standard: Wed Jul 27 12:10:21 2016 +0000
286 $ hg debugdate "2016-07-27 121021Z"
286 $ hg debugdate "2016-07-27 121021Z"
287 internal: 1469621421 0
287 internal: 1469621421 0
288 standard: Wed Jul 27 12:10:21 2016 +0000
288 standard: Wed Jul 27 12:10:21 2016 +0000
@@ -1,3756 +1,3756 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 these predicates use '\0' as a separator:
35 these predicates use '\0' as a separator:
36
36
37 $ cat <<EOF > debugrevlistspec.py
37 $ cat <<EOF > debugrevlistspec.py
38 > from __future__ import absolute_import
38 > from __future__ import absolute_import
39 > from mercurial import (
39 > from mercurial import (
40 > node as nodemod,
40 > node as nodemod,
41 > registrar,
41 > registrar,
42 > revset,
42 > revset,
43 > revsetlang,
43 > revsetlang,
44 > smartset,
44 > smartset,
45 > )
45 > )
46 > cmdtable = {}
46 > cmdtable = {}
47 > command = registrar.command(cmdtable)
47 > command = registrar.command(cmdtable)
48 > @command('debugrevlistspec',
48 > @command('debugrevlistspec',
49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
50 > ('', 'bin', None, 'unhexlify arguments')])
50 > ('', 'bin', None, 'unhexlify arguments')])
51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 > if opts['bin']:
52 > if opts['bin']:
53 > args = map(nodemod.bin, args)
53 > args = map(nodemod.bin, args)
54 > expr = revsetlang.formatspec(fmt, list(args))
54 > expr = revsetlang.formatspec(fmt, list(args))
55 > if ui.verbose:
55 > if ui.verbose:
56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
57 > ui.note(revsetlang.prettyformat(tree), "\n")
57 > ui.note(revsetlang.prettyformat(tree), "\n")
58 > if opts["optimize"]:
58 > if opts["optimize"]:
59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
61 > "\n")
61 > "\n")
62 > func = revset.match(ui, expr, repo)
62 > func = revset.match(ui, expr, repo)
63 > revs = func(repo)
63 > revs = func(repo)
64 > if ui.verbose:
64 > if ui.verbose:
65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
66 > for c in revs:
66 > for c in revs:
67 > ui.write("%s\n" % c)
67 > ui.write("%s\n" % c)
68 > EOF
68 > EOF
69 $ cat <<EOF >> $HGRCPATH
69 $ cat <<EOF >> $HGRCPATH
70 > [extensions]
70 > [extensions]
71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 > EOF
72 > EOF
73 $ trylist() {
73 $ trylist() {
74 > hg debugrevlistspec --debug "$@"
74 > hg debugrevlistspec --debug "$@"
75 > }
75 > }
76
76
77 $ hg init repo
77 $ hg init repo
78 $ cd repo
78 $ cd repo
79
79
80 $ echo a > a
80 $ echo a > a
81 $ hg branch a
81 $ hg branch a
82 marked working directory as branch a
82 marked working directory as branch a
83 (branches are permanent and global, did you want a bookmark?)
83 (branches are permanent and global, did you want a bookmark?)
84 $ hg ci -Aqm0
84 $ hg ci -Aqm0
85
85
86 $ echo b > b
86 $ echo b > b
87 $ hg branch b
87 $ hg branch b
88 marked working directory as branch b
88 marked working directory as branch b
89 $ hg ci -Aqm1
89 $ hg ci -Aqm1
90
90
91 $ rm a
91 $ rm a
92 $ hg branch a-b-c-
92 $ hg branch a-b-c-
93 marked working directory as branch a-b-c-
93 marked working directory as branch a-b-c-
94 $ hg ci -Aqm2 -u Bob
94 $ hg ci -Aqm2 -u Bob
95
95
96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 2
97 2
98 $ hg log -r "extra('branch')" --template '{rev}\n'
98 $ hg log -r "extra('branch')" --template '{rev}\n'
99 0
99 0
100 1
100 1
101 2
101 2
102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 0 a
103 0 a
104 2 a-b-c-
104 2 a-b-c-
105
105
106 $ hg co 1
106 $ hg co 1
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 $ hg branch +a+b+c+
108 $ hg branch +a+b+c+
109 marked working directory as branch +a+b+c+
109 marked working directory as branch +a+b+c+
110 $ hg ci -Aqm3
110 $ hg ci -Aqm3
111
111
112 $ hg co 2 # interleave
112 $ hg co 2 # interleave
113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 $ echo bb > b
114 $ echo bb > b
115 $ hg branch -- -a-b-c-
115 $ hg branch -- -a-b-c-
116 marked working directory as branch -a-b-c-
116 marked working directory as branch -a-b-c-
117 $ hg ci -Aqm4 -d "May 12 2005"
117 $ hg ci -Aqm4 -d "May 12 2005"
118
118
119 $ hg co 3
119 $ hg co 3
120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 $ hg branch !a/b/c/
121 $ hg branch !a/b/c/
122 marked working directory as branch !a/b/c/
122 marked working directory as branch !a/b/c/
123 $ hg ci -Aqm"5 bug"
123 $ hg ci -Aqm"5 bug"
124
124
125 $ hg merge 4
125 $ hg merge 4
126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 (branch merge, don't forget to commit)
127 (branch merge, don't forget to commit)
128 $ hg branch _a_b_c_
128 $ hg branch _a_b_c_
129 marked working directory as branch _a_b_c_
129 marked working directory as branch _a_b_c_
130 $ hg ci -Aqm"6 issue619"
130 $ hg ci -Aqm"6 issue619"
131
131
132 $ hg branch .a.b.c.
132 $ hg branch .a.b.c.
133 marked working directory as branch .a.b.c.
133 marked working directory as branch .a.b.c.
134 $ hg ci -Aqm7
134 $ hg ci -Aqm7
135
135
136 $ hg branch all
136 $ hg branch all
137 marked working directory as branch all
137 marked working directory as branch all
138
138
139 $ hg co 4
139 $ hg co 4
140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ hg branch Γ©
141 $ hg branch Γ©
142 marked working directory as branch \xc3\xa9 (esc)
142 marked working directory as branch \xc3\xa9 (esc)
143 $ hg ci -Aqm9
143 $ hg ci -Aqm9
144
144
145 $ hg tag -r6 1.0
145 $ hg tag -r6 1.0
146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147
147
148 $ hg clone --quiet -U -r 7 . ../remote1
148 $ hg clone --quiet -U -r 7 . ../remote1
149 $ hg clone --quiet -U -r 8 . ../remote2
149 $ hg clone --quiet -U -r 8 . ../remote2
150 $ echo "[paths]" >> .hg/hgrc
150 $ echo "[paths]" >> .hg/hgrc
151 $ echo "default = ../remote1" >> .hg/hgrc
151 $ echo "default = ../remote1" >> .hg/hgrc
152
152
153 trivial
153 trivial
154
154
155 $ try 0:1
155 $ try 0:1
156 (range
156 (range
157 ('symbol', '0')
157 ('symbol', '0')
158 ('symbol', '1'))
158 ('symbol', '1'))
159 * set:
159 * set:
160 <spanset+ 0:1>
160 <spanset+ 0:1>
161 0
161 0
162 1
162 1
163 $ try --optimize :
163 $ try --optimize :
164 (rangeall
164 (rangeall
165 None)
165 None)
166 * optimized:
166 * optimized:
167 (rangeall
167 (rangeall
168 None
168 None
169 define)
169 define)
170 * set:
170 * set:
171 <spanset+ 0:9>
171 <spanset+ 0:9>
172 0
172 0
173 1
173 1
174 2
174 2
175 3
175 3
176 4
176 4
177 5
177 5
178 6
178 6
179 7
179 7
180 8
180 8
181 9
181 9
182 $ try 3::6
182 $ try 3::6
183 (dagrange
183 (dagrange
184 ('symbol', '3')
184 ('symbol', '3')
185 ('symbol', '6'))
185 ('symbol', '6'))
186 * set:
186 * set:
187 <baseset+ [3, 5, 6]>
187 <baseset+ [3, 5, 6]>
188 3
188 3
189 5
189 5
190 6
190 6
191 $ try '0|1|2'
191 $ try '0|1|2'
192 (or
192 (or
193 (list
193 (list
194 ('symbol', '0')
194 ('symbol', '0')
195 ('symbol', '1')
195 ('symbol', '1')
196 ('symbol', '2')))
196 ('symbol', '2')))
197 * set:
197 * set:
198 <baseset [0, 1, 2]>
198 <baseset [0, 1, 2]>
199 0
199 0
200 1
200 1
201 2
201 2
202
202
203 names that should work without quoting
203 names that should work without quoting
204
204
205 $ try a
205 $ try a
206 ('symbol', 'a')
206 ('symbol', 'a')
207 * set:
207 * set:
208 <baseset [0]>
208 <baseset [0]>
209 0
209 0
210 $ try b-a
210 $ try b-a
211 (minus
211 (minus
212 ('symbol', 'b')
212 ('symbol', 'b')
213 ('symbol', 'a'))
213 ('symbol', 'a'))
214 * set:
214 * set:
215 <filteredset
215 <filteredset
216 <baseset [1]>,
216 <baseset [1]>,
217 <not
217 <not
218 <baseset [0]>>>
218 <baseset [0]>>>
219 1
219 1
220 $ try _a_b_c_
220 $ try _a_b_c_
221 ('symbol', '_a_b_c_')
221 ('symbol', '_a_b_c_')
222 * set:
222 * set:
223 <baseset [6]>
223 <baseset [6]>
224 6
224 6
225 $ try _a_b_c_-a
225 $ try _a_b_c_-a
226 (minus
226 (minus
227 ('symbol', '_a_b_c_')
227 ('symbol', '_a_b_c_')
228 ('symbol', 'a'))
228 ('symbol', 'a'))
229 * set:
229 * set:
230 <filteredset
230 <filteredset
231 <baseset [6]>,
231 <baseset [6]>,
232 <not
232 <not
233 <baseset [0]>>>
233 <baseset [0]>>>
234 6
234 6
235 $ try .a.b.c.
235 $ try .a.b.c.
236 ('symbol', '.a.b.c.')
236 ('symbol', '.a.b.c.')
237 * set:
237 * set:
238 <baseset [7]>
238 <baseset [7]>
239 7
239 7
240 $ try .a.b.c.-a
240 $ try .a.b.c.-a
241 (minus
241 (minus
242 ('symbol', '.a.b.c.')
242 ('symbol', '.a.b.c.')
243 ('symbol', 'a'))
243 ('symbol', 'a'))
244 * set:
244 * set:
245 <filteredset
245 <filteredset
246 <baseset [7]>,
246 <baseset [7]>,
247 <not
247 <not
248 <baseset [0]>>>
248 <baseset [0]>>>
249 7
249 7
250
250
251 names that should be caught by fallback mechanism
251 names that should be caught by fallback mechanism
252
252
253 $ try -- '-a-b-c-'
253 $ try -- '-a-b-c-'
254 ('symbol', '-a-b-c-')
254 ('symbol', '-a-b-c-')
255 * set:
255 * set:
256 <baseset [4]>
256 <baseset [4]>
257 4
257 4
258 $ log -a-b-c-
258 $ log -a-b-c-
259 4
259 4
260 $ try '+a+b+c+'
260 $ try '+a+b+c+'
261 ('symbol', '+a+b+c+')
261 ('symbol', '+a+b+c+')
262 * set:
262 * set:
263 <baseset [3]>
263 <baseset [3]>
264 3
264 3
265 $ try '+a+b+c+:'
265 $ try '+a+b+c+:'
266 (rangepost
266 (rangepost
267 ('symbol', '+a+b+c+'))
267 ('symbol', '+a+b+c+'))
268 * set:
268 * set:
269 <spanset+ 3:9>
269 <spanset+ 3:9>
270 3
270 3
271 4
271 4
272 5
272 5
273 6
273 6
274 7
274 7
275 8
275 8
276 9
276 9
277 $ try ':+a+b+c+'
277 $ try ':+a+b+c+'
278 (rangepre
278 (rangepre
279 ('symbol', '+a+b+c+'))
279 ('symbol', '+a+b+c+'))
280 * set:
280 * set:
281 <spanset+ 0:3>
281 <spanset+ 0:3>
282 0
282 0
283 1
283 1
284 2
284 2
285 3
285 3
286 $ try -- '-a-b-c-:+a+b+c+'
286 $ try -- '-a-b-c-:+a+b+c+'
287 (range
287 (range
288 ('symbol', '-a-b-c-')
288 ('symbol', '-a-b-c-')
289 ('symbol', '+a+b+c+'))
289 ('symbol', '+a+b+c+'))
290 * set:
290 * set:
291 <spanset- 3:4>
291 <spanset- 3:4>
292 4
292 4
293 3
293 3
294 $ log '-a-b-c-:+a+b+c+'
294 $ log '-a-b-c-:+a+b+c+'
295 4
295 4
296 3
296 3
297
297
298 $ try -- -a-b-c--a # complains
298 $ try -- -a-b-c--a # complains
299 (minus
299 (minus
300 (minus
300 (minus
301 (minus
301 (minus
302 (negate
302 (negate
303 ('symbol', 'a'))
303 ('symbol', 'a'))
304 ('symbol', 'b'))
304 ('symbol', 'b'))
305 ('symbol', 'c'))
305 ('symbol', 'c'))
306 (negate
306 (negate
307 ('symbol', 'a')))
307 ('symbol', 'a')))
308 abort: unknown revision '-a'!
308 abort: unknown revision '-a'!
309 [255]
309 [255]
310 $ try Γ©
310 $ try Γ©
311 ('symbol', '\xc3\xa9')
311 ('symbol', '\xc3\xa9')
312 * set:
312 * set:
313 <baseset [9]>
313 <baseset [9]>
314 9
314 9
315
315
316 no quoting needed
316 no quoting needed
317
317
318 $ log ::a-b-c-
318 $ log ::a-b-c-
319 0
319 0
320 1
320 1
321 2
321 2
322
322
323 quoting needed
323 quoting needed
324
324
325 $ try '"-a-b-c-"-a'
325 $ try '"-a-b-c-"-a'
326 (minus
326 (minus
327 ('string', '-a-b-c-')
327 ('string', '-a-b-c-')
328 ('symbol', 'a'))
328 ('symbol', 'a'))
329 * set:
329 * set:
330 <filteredset
330 <filteredset
331 <baseset [4]>,
331 <baseset [4]>,
332 <not
332 <not
333 <baseset [0]>>>
333 <baseset [0]>>>
334 4
334 4
335
335
336 $ log '1 or 2'
336 $ log '1 or 2'
337 1
337 1
338 2
338 2
339 $ log '1|2'
339 $ log '1|2'
340 1
340 1
341 2
341 2
342 $ log '1 and 2'
342 $ log '1 and 2'
343 $ log '1&2'
343 $ log '1&2'
344 $ try '1&2|3' # precedence - and is higher
344 $ try '1&2|3' # precedence - and is higher
345 (or
345 (or
346 (list
346 (list
347 (and
347 (and
348 ('symbol', '1')
348 ('symbol', '1')
349 ('symbol', '2'))
349 ('symbol', '2'))
350 ('symbol', '3')))
350 ('symbol', '3')))
351 * set:
351 * set:
352 <addset
352 <addset
353 <baseset []>,
353 <baseset []>,
354 <baseset [3]>>
354 <baseset [3]>>
355 3
355 3
356 $ try '1|2&3'
356 $ try '1|2&3'
357 (or
357 (or
358 (list
358 (list
359 ('symbol', '1')
359 ('symbol', '1')
360 (and
360 (and
361 ('symbol', '2')
361 ('symbol', '2')
362 ('symbol', '3'))))
362 ('symbol', '3'))))
363 * set:
363 * set:
364 <addset
364 <addset
365 <baseset [1]>,
365 <baseset [1]>,
366 <baseset []>>
366 <baseset []>>
367 1
367 1
368 $ try '1&2&3' # associativity
368 $ try '1&2&3' # associativity
369 (and
369 (and
370 (and
370 (and
371 ('symbol', '1')
371 ('symbol', '1')
372 ('symbol', '2'))
372 ('symbol', '2'))
373 ('symbol', '3'))
373 ('symbol', '3'))
374 * set:
374 * set:
375 <baseset []>
375 <baseset []>
376 $ try '1|(2|3)'
376 $ try '1|(2|3)'
377 (or
377 (or
378 (list
378 (list
379 ('symbol', '1')
379 ('symbol', '1')
380 (group
380 (group
381 (or
381 (or
382 (list
382 (list
383 ('symbol', '2')
383 ('symbol', '2')
384 ('symbol', '3'))))))
384 ('symbol', '3'))))))
385 * set:
385 * set:
386 <addset
386 <addset
387 <baseset [1]>,
387 <baseset [1]>,
388 <baseset [2, 3]>>
388 <baseset [2, 3]>>
389 1
389 1
390 2
390 2
391 3
391 3
392 $ log '1.0' # tag
392 $ log '1.0' # tag
393 6
393 6
394 $ log 'a' # branch
394 $ log 'a' # branch
395 0
395 0
396 $ log '2785f51ee'
396 $ log '2785f51ee'
397 0
397 0
398 $ log 'date(2005)'
398 $ log 'date(2005)'
399 4
399 4
400 $ log 'date(this is a test)'
400 $ log 'date(this is a test)'
401 hg: parse error at 10: unexpected token: symbol
401 hg: parse error at 10: unexpected token: symbol
402 [255]
402 [255]
403 $ log 'date()'
403 $ log 'date()'
404 hg: parse error: date requires a string
404 hg: parse error: date requires a string
405 [255]
405 [255]
406 $ log 'date'
406 $ log 'date'
407 abort: unknown revision 'date'!
407 abort: unknown revision 'date'!
408 [255]
408 [255]
409 $ log 'date('
409 $ log 'date('
410 hg: parse error at 5: not a prefix: end
410 hg: parse error at 5: not a prefix: end
411 [255]
411 [255]
412 $ log 'date("\xy")'
412 $ log 'date("\xy")'
413 hg: parse error: invalid \x escape
413 hg: parse error: invalid \x escape
414 [255]
414 [255]
415 $ log 'date(tip)'
415 $ log 'date(tip)'
416 abort: invalid date: 'tip'
416 hg: parse error: invalid date: 'tip'
417 [255]
417 [255]
418 $ log '0:date'
418 $ log '0:date'
419 abort: unknown revision 'date'!
419 abort: unknown revision 'date'!
420 [255]
420 [255]
421 $ log '::"date"'
421 $ log '::"date"'
422 abort: unknown revision 'date'!
422 abort: unknown revision 'date'!
423 [255]
423 [255]
424 $ hg book date -r 4
424 $ hg book date -r 4
425 $ log '0:date'
425 $ log '0:date'
426 0
426 0
427 1
427 1
428 2
428 2
429 3
429 3
430 4
430 4
431 $ log '::date'
431 $ log '::date'
432 0
432 0
433 1
433 1
434 2
434 2
435 4
435 4
436 $ log '::"date"'
436 $ log '::"date"'
437 0
437 0
438 1
438 1
439 2
439 2
440 4
440 4
441 $ log 'date(2005) and 1::'
441 $ log 'date(2005) and 1::'
442 4
442 4
443 $ hg book -d date
443 $ hg book -d date
444
444
445 function name should be a symbol
445 function name should be a symbol
446
446
447 $ log '"date"(2005)'
447 $ log '"date"(2005)'
448 hg: parse error: not a symbol
448 hg: parse error: not a symbol
449 [255]
449 [255]
450
450
451 keyword arguments
451 keyword arguments
452
452
453 $ log 'extra(branch, value=a)'
453 $ log 'extra(branch, value=a)'
454 0
454 0
455
455
456 $ log 'extra(branch, a, b)'
456 $ log 'extra(branch, a, b)'
457 hg: parse error: extra takes at most 2 positional arguments
457 hg: parse error: extra takes at most 2 positional arguments
458 [255]
458 [255]
459 $ log 'extra(a, label=b)'
459 $ log 'extra(a, label=b)'
460 hg: parse error: extra got multiple values for keyword argument 'label'
460 hg: parse error: extra got multiple values for keyword argument 'label'
461 [255]
461 [255]
462 $ log 'extra(label=branch, default)'
462 $ log 'extra(label=branch, default)'
463 hg: parse error: extra got an invalid argument
463 hg: parse error: extra got an invalid argument
464 [255]
464 [255]
465 $ log 'extra(branch, foo+bar=baz)'
465 $ log 'extra(branch, foo+bar=baz)'
466 hg: parse error: extra got an invalid argument
466 hg: parse error: extra got an invalid argument
467 [255]
467 [255]
468 $ log 'extra(unknown=branch)'
468 $ log 'extra(unknown=branch)'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 [255]
470 [255]
471
471
472 $ try 'foo=bar|baz'
472 $ try 'foo=bar|baz'
473 (keyvalue
473 (keyvalue
474 ('symbol', 'foo')
474 ('symbol', 'foo')
475 (or
475 (or
476 (list
476 (list
477 ('symbol', 'bar')
477 ('symbol', 'bar')
478 ('symbol', 'baz'))))
478 ('symbol', 'baz'))))
479 hg: parse error: can't use a key-value pair in this context
479 hg: parse error: can't use a key-value pair in this context
480 [255]
480 [255]
481
481
482 right-hand side should be optimized recursively
482 right-hand side should be optimized recursively
483
483
484 $ try --optimize 'foo=(not public())'
484 $ try --optimize 'foo=(not public())'
485 (keyvalue
485 (keyvalue
486 ('symbol', 'foo')
486 ('symbol', 'foo')
487 (group
487 (group
488 (not
488 (not
489 (func
489 (func
490 ('symbol', 'public')
490 ('symbol', 'public')
491 None))))
491 None))))
492 * optimized:
492 * optimized:
493 (keyvalue
493 (keyvalue
494 ('symbol', 'foo')
494 ('symbol', 'foo')
495 (func
495 (func
496 ('symbol', '_notpublic')
496 ('symbol', '_notpublic')
497 None
497 None
498 any))
498 any))
499 hg: parse error: can't use a key-value pair in this context
499 hg: parse error: can't use a key-value pair in this context
500 [255]
500 [255]
501
501
502 parsed tree at stages:
502 parsed tree at stages:
503
503
504 $ hg debugrevspec -p all '()'
504 $ hg debugrevspec -p all '()'
505 * parsed:
505 * parsed:
506 (group
506 (group
507 None)
507 None)
508 * expanded:
508 * expanded:
509 (group
509 (group
510 None)
510 None)
511 * concatenated:
511 * concatenated:
512 (group
512 (group
513 None)
513 None)
514 * analyzed:
514 * analyzed:
515 None
515 None
516 * optimized:
516 * optimized:
517 None
517 None
518 hg: parse error: missing argument
518 hg: parse error: missing argument
519 [255]
519 [255]
520
520
521 $ hg debugrevspec --no-optimized -p all '()'
521 $ hg debugrevspec --no-optimized -p all '()'
522 * parsed:
522 * parsed:
523 (group
523 (group
524 None)
524 None)
525 * expanded:
525 * expanded:
526 (group
526 (group
527 None)
527 None)
528 * concatenated:
528 * concatenated:
529 (group
529 (group
530 None)
530 None)
531 * analyzed:
531 * analyzed:
532 None
532 None
533 hg: parse error: missing argument
533 hg: parse error: missing argument
534 [255]
534 [255]
535
535
536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
537 * parsed:
537 * parsed:
538 (minus
538 (minus
539 (group
539 (group
540 (or
540 (or
541 (list
541 (list
542 ('symbol', '0')
542 ('symbol', '0')
543 ('symbol', '1'))))
543 ('symbol', '1'))))
544 ('symbol', '1'))
544 ('symbol', '1'))
545 * analyzed:
545 * analyzed:
546 (and
546 (and
547 (or
547 (or
548 (list
548 (list
549 ('symbol', '0')
549 ('symbol', '0')
550 ('symbol', '1'))
550 ('symbol', '1'))
551 define)
551 define)
552 (not
552 (not
553 ('symbol', '1')
553 ('symbol', '1')
554 follow)
554 follow)
555 define)
555 define)
556 * optimized:
556 * optimized:
557 (difference
557 (difference
558 (func
558 (func
559 ('symbol', '_list')
559 ('symbol', '_list')
560 ('string', '0\x001')
560 ('string', '0\x001')
561 define)
561 define)
562 ('symbol', '1')
562 ('symbol', '1')
563 define)
563 define)
564 0
564 0
565
565
566 $ hg debugrevspec -p unknown '0'
566 $ hg debugrevspec -p unknown '0'
567 abort: invalid stage name: unknown
567 abort: invalid stage name: unknown
568 [255]
568 [255]
569
569
570 $ hg debugrevspec -p all --optimize '0'
570 $ hg debugrevspec -p all --optimize '0'
571 abort: cannot use --optimize with --show-stage
571 abort: cannot use --optimize with --show-stage
572 [255]
572 [255]
573
573
574 verify optimized tree:
574 verify optimized tree:
575
575
576 $ hg debugrevspec --verify '0|1'
576 $ hg debugrevspec --verify '0|1'
577
577
578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
579 * analyzed:
579 * analyzed:
580 (and
580 (and
581 (func
581 (func
582 ('symbol', 'r3232')
582 ('symbol', 'r3232')
583 None
583 None
584 define)
584 define)
585 ('symbol', '2')
585 ('symbol', '2')
586 define)
586 define)
587 * optimized:
587 * optimized:
588 (and
588 (and
589 ('symbol', '2')
589 ('symbol', '2')
590 (func
590 (func
591 ('symbol', 'r3232')
591 ('symbol', 'r3232')
592 None
592 None
593 define)
593 define)
594 define)
594 define)
595 * analyzed set:
595 * analyzed set:
596 <baseset [2]>
596 <baseset [2]>
597 * optimized set:
597 * optimized set:
598 <baseset [2, 2]>
598 <baseset [2, 2]>
599 --- analyzed
599 --- analyzed
600 +++ optimized
600 +++ optimized
601 2
601 2
602 +2
602 +2
603 [1]
603 [1]
604
604
605 $ hg debugrevspec --no-optimized --verify-optimized '0'
605 $ hg debugrevspec --no-optimized --verify-optimized '0'
606 abort: cannot use --verify-optimized with --no-optimized
606 abort: cannot use --verify-optimized with --no-optimized
607 [255]
607 [255]
608
608
609 Test that symbols only get parsed as functions if there's an opening
609 Test that symbols only get parsed as functions if there's an opening
610 parenthesis.
610 parenthesis.
611
611
612 $ hg book only -r 9
612 $ hg book only -r 9
613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
614 8
614 8
615 9
615 9
616
616
617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
618 may be hidden (issue5385)
618 may be hidden (issue5385)
619
619
620 $ try -p parsed -p analyzed ':'
620 $ try -p parsed -p analyzed ':'
621 * parsed:
621 * parsed:
622 (rangeall
622 (rangeall
623 None)
623 None)
624 * analyzed:
624 * analyzed:
625 (rangeall
625 (rangeall
626 None
626 None
627 define)
627 define)
628 * set:
628 * set:
629 <spanset+ 0:9>
629 <spanset+ 0:9>
630 0
630 0
631 1
631 1
632 2
632 2
633 3
633 3
634 4
634 4
635 5
635 5
636 6
636 6
637 7
637 7
638 8
638 8
639 9
639 9
640 $ try -p analyzed ':1'
640 $ try -p analyzed ':1'
641 * analyzed:
641 * analyzed:
642 (rangepre
642 (rangepre
643 ('symbol', '1')
643 ('symbol', '1')
644 define)
644 define)
645 * set:
645 * set:
646 <spanset+ 0:1>
646 <spanset+ 0:1>
647 0
647 0
648 1
648 1
649 $ try -p analyzed ':(1|2)'
649 $ try -p analyzed ':(1|2)'
650 * analyzed:
650 * analyzed:
651 (rangepre
651 (rangepre
652 (or
652 (or
653 (list
653 (list
654 ('symbol', '1')
654 ('symbol', '1')
655 ('symbol', '2'))
655 ('symbol', '2'))
656 define)
656 define)
657 define)
657 define)
658 * set:
658 * set:
659 <spanset+ 0:2>
659 <spanset+ 0:2>
660 0
660 0
661 1
661 1
662 2
662 2
663 $ try -p analyzed ':(1&2)'
663 $ try -p analyzed ':(1&2)'
664 * analyzed:
664 * analyzed:
665 (rangepre
665 (rangepre
666 (and
666 (and
667 ('symbol', '1')
667 ('symbol', '1')
668 ('symbol', '2')
668 ('symbol', '2')
669 define)
669 define)
670 define)
670 define)
671 * set:
671 * set:
672 <baseset []>
672 <baseset []>
673
673
674 infix/suffix resolution of ^ operator (issue2884):
674 infix/suffix resolution of ^ operator (issue2884):
675
675
676 x^:y means (x^):y
676 x^:y means (x^):y
677
677
678 $ try '1^:2'
678 $ try '1^:2'
679 (range
679 (range
680 (parentpost
680 (parentpost
681 ('symbol', '1'))
681 ('symbol', '1'))
682 ('symbol', '2'))
682 ('symbol', '2'))
683 * set:
683 * set:
684 <spanset+ 0:2>
684 <spanset+ 0:2>
685 0
685 0
686 1
686 1
687 2
687 2
688
688
689 $ try '1^::2'
689 $ try '1^::2'
690 (dagrange
690 (dagrange
691 (parentpost
691 (parentpost
692 ('symbol', '1'))
692 ('symbol', '1'))
693 ('symbol', '2'))
693 ('symbol', '2'))
694 * set:
694 * set:
695 <baseset+ [0, 1, 2]>
695 <baseset+ [0, 1, 2]>
696 0
696 0
697 1
697 1
698 2
698 2
699
699
700 $ try '9^:'
700 $ try '9^:'
701 (rangepost
701 (rangepost
702 (parentpost
702 (parentpost
703 ('symbol', '9')))
703 ('symbol', '9')))
704 * set:
704 * set:
705 <spanset+ 8:9>
705 <spanset+ 8:9>
706 8
706 8
707 9
707 9
708
708
709 x^:y should be resolved before omitting group operators
709 x^:y should be resolved before omitting group operators
710
710
711 $ try '1^(:2)'
711 $ try '1^(:2)'
712 (parent
712 (parent
713 ('symbol', '1')
713 ('symbol', '1')
714 (group
714 (group
715 (rangepre
715 (rangepre
716 ('symbol', '2'))))
716 ('symbol', '2'))))
717 hg: parse error: ^ expects a number 0, 1, or 2
717 hg: parse error: ^ expects a number 0, 1, or 2
718 [255]
718 [255]
719
719
720 x^:y should be resolved recursively
720 x^:y should be resolved recursively
721
721
722 $ try 'sort(1^:2)'
722 $ try 'sort(1^:2)'
723 (func
723 (func
724 ('symbol', 'sort')
724 ('symbol', 'sort')
725 (range
725 (range
726 (parentpost
726 (parentpost
727 ('symbol', '1'))
727 ('symbol', '1'))
728 ('symbol', '2')))
728 ('symbol', '2')))
729 * set:
729 * set:
730 <spanset+ 0:2>
730 <spanset+ 0:2>
731 0
731 0
732 1
732 1
733 2
733 2
734
734
735 $ try '(3^:4)^:2'
735 $ try '(3^:4)^:2'
736 (range
736 (range
737 (parentpost
737 (parentpost
738 (group
738 (group
739 (range
739 (range
740 (parentpost
740 (parentpost
741 ('symbol', '3'))
741 ('symbol', '3'))
742 ('symbol', '4'))))
742 ('symbol', '4'))))
743 ('symbol', '2'))
743 ('symbol', '2'))
744 * set:
744 * set:
745 <spanset+ 0:2>
745 <spanset+ 0:2>
746 0
746 0
747 1
747 1
748 2
748 2
749
749
750 $ try '(3^::4)^::2'
750 $ try '(3^::4)^::2'
751 (dagrange
751 (dagrange
752 (parentpost
752 (parentpost
753 (group
753 (group
754 (dagrange
754 (dagrange
755 (parentpost
755 (parentpost
756 ('symbol', '3'))
756 ('symbol', '3'))
757 ('symbol', '4'))))
757 ('symbol', '4'))))
758 ('symbol', '2'))
758 ('symbol', '2'))
759 * set:
759 * set:
760 <baseset+ [0, 1, 2]>
760 <baseset+ [0, 1, 2]>
761 0
761 0
762 1
762 1
763 2
763 2
764
764
765 $ try '(9^:)^:'
765 $ try '(9^:)^:'
766 (rangepost
766 (rangepost
767 (parentpost
767 (parentpost
768 (group
768 (group
769 (rangepost
769 (rangepost
770 (parentpost
770 (parentpost
771 ('symbol', '9'))))))
771 ('symbol', '9'))))))
772 * set:
772 * set:
773 <spanset+ 4:9>
773 <spanset+ 4:9>
774 4
774 4
775 5
775 5
776 6
776 6
777 7
777 7
778 8
778 8
779 9
779 9
780
780
781 x^ in alias should also be resolved
781 x^ in alias should also be resolved
782
782
783 $ try 'A' --config 'revsetalias.A=1^:2'
783 $ try 'A' --config 'revsetalias.A=1^:2'
784 ('symbol', 'A')
784 ('symbol', 'A')
785 * expanded:
785 * expanded:
786 (range
786 (range
787 (parentpost
787 (parentpost
788 ('symbol', '1'))
788 ('symbol', '1'))
789 ('symbol', '2'))
789 ('symbol', '2'))
790 * set:
790 * set:
791 <spanset+ 0:2>
791 <spanset+ 0:2>
792 0
792 0
793 1
793 1
794 2
794 2
795
795
796 $ try 'A:2' --config 'revsetalias.A=1^'
796 $ try 'A:2' --config 'revsetalias.A=1^'
797 (range
797 (range
798 ('symbol', 'A')
798 ('symbol', 'A')
799 ('symbol', '2'))
799 ('symbol', '2'))
800 * expanded:
800 * expanded:
801 (range
801 (range
802 (parentpost
802 (parentpost
803 ('symbol', '1'))
803 ('symbol', '1'))
804 ('symbol', '2'))
804 ('symbol', '2'))
805 * set:
805 * set:
806 <spanset+ 0:2>
806 <spanset+ 0:2>
807 0
807 0
808 1
808 1
809 2
809 2
810
810
811 but not beyond the boundary of alias expansion, because the resolution should
811 but not beyond the boundary of alias expansion, because the resolution should
812 be made at the parsing stage
812 be made at the parsing stage
813
813
814 $ try '1^A' --config 'revsetalias.A=:2'
814 $ try '1^A' --config 'revsetalias.A=:2'
815 (parent
815 (parent
816 ('symbol', '1')
816 ('symbol', '1')
817 ('symbol', 'A'))
817 ('symbol', 'A'))
818 * expanded:
818 * expanded:
819 (parent
819 (parent
820 ('symbol', '1')
820 ('symbol', '1')
821 (rangepre
821 (rangepre
822 ('symbol', '2')))
822 ('symbol', '2')))
823 hg: parse error: ^ expects a number 0, 1, or 2
823 hg: parse error: ^ expects a number 0, 1, or 2
824 [255]
824 [255]
825
825
826 ancestor can accept 0 or more arguments
826 ancestor can accept 0 or more arguments
827
827
828 $ log 'ancestor()'
828 $ log 'ancestor()'
829 $ log 'ancestor(1)'
829 $ log 'ancestor(1)'
830 1
830 1
831 $ log 'ancestor(4,5)'
831 $ log 'ancestor(4,5)'
832 1
832 1
833 $ log 'ancestor(4,5) and 4'
833 $ log 'ancestor(4,5) and 4'
834 $ log 'ancestor(0,0,1,3)'
834 $ log 'ancestor(0,0,1,3)'
835 0
835 0
836 $ log 'ancestor(3,1,5,3,5,1)'
836 $ log 'ancestor(3,1,5,3,5,1)'
837 1
837 1
838 $ log 'ancestor(0,1,3,5)'
838 $ log 'ancestor(0,1,3,5)'
839 0
839 0
840 $ log 'ancestor(1,2,3,4,5)'
840 $ log 'ancestor(1,2,3,4,5)'
841 1
841 1
842
842
843 test ancestors
843 test ancestors
844
844
845 $ log 'ancestors(5)'
845 $ log 'ancestors(5)'
846 0
846 0
847 1
847 1
848 3
848 3
849 5
849 5
850 $ log 'ancestor(ancestors(5))'
850 $ log 'ancestor(ancestors(5))'
851 0
851 0
852 $ log '::r3232()'
852 $ log '::r3232()'
853 0
853 0
854 1
854 1
855 2
855 2
856 3
856 3
857
857
858 $ log 'author(bob)'
858 $ log 'author(bob)'
859 2
859 2
860 $ log 'author("re:bob|test")'
860 $ log 'author("re:bob|test")'
861 0
861 0
862 1
862 1
863 2
863 2
864 3
864 3
865 4
865 4
866 5
866 5
867 6
867 6
868 7
868 7
869 8
869 8
870 9
870 9
871 $ log 'author(r"re:\S")'
871 $ log 'author(r"re:\S")'
872 0
872 0
873 1
873 1
874 2
874 2
875 3
875 3
876 4
876 4
877 5
877 5
878 6
878 6
879 7
879 7
880 8
880 8
881 9
881 9
882 $ log 'branch(Γ©)'
882 $ log 'branch(Γ©)'
883 8
883 8
884 9
884 9
885 $ log 'branch(a)'
885 $ log 'branch(a)'
886 0
886 0
887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
888 0 a
888 0 a
889 2 a-b-c-
889 2 a-b-c-
890 3 +a+b+c+
890 3 +a+b+c+
891 4 -a-b-c-
891 4 -a-b-c-
892 5 !a/b/c/
892 5 !a/b/c/
893 6 _a_b_c_
893 6 _a_b_c_
894 7 .a.b.c.
894 7 .a.b.c.
895 $ log 'children(ancestor(4,5))'
895 $ log 'children(ancestor(4,5))'
896 2
896 2
897 3
897 3
898
898
899 $ log 'children(4)'
899 $ log 'children(4)'
900 6
900 6
901 8
901 8
902 $ log 'children(null)'
902 $ log 'children(null)'
903 0
903 0
904
904
905 $ log 'closed()'
905 $ log 'closed()'
906 $ log 'contains(a)'
906 $ log 'contains(a)'
907 0
907 0
908 1
908 1
909 3
909 3
910 5
910 5
911 $ log 'contains("../repo/a")'
911 $ log 'contains("../repo/a")'
912 0
912 0
913 1
913 1
914 3
914 3
915 5
915 5
916 $ log 'desc(B)'
916 $ log 'desc(B)'
917 5
917 5
918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
919 5 5 bug
919 5 5 bug
920 6 6 issue619
920 6 6 issue619
921 $ log 'descendants(2 or 3)'
921 $ log 'descendants(2 or 3)'
922 2
922 2
923 3
923 3
924 4
924 4
925 5
925 5
926 6
926 6
927 7
927 7
928 8
928 8
929 9
929 9
930 $ log 'file("b*")'
930 $ log 'file("b*")'
931 1
931 1
932 4
932 4
933 $ log 'filelog("b")'
933 $ log 'filelog("b")'
934 1
934 1
935 4
935 4
936 $ log 'filelog("../repo/b")'
936 $ log 'filelog("../repo/b")'
937 1
937 1
938 4
938 4
939 $ log 'follow()'
939 $ log 'follow()'
940 0
940 0
941 1
941 1
942 2
942 2
943 4
943 4
944 8
944 8
945 9
945 9
946 $ log 'grep("issue\d+")'
946 $ log 'grep("issue\d+")'
947 6
947 6
948 $ try 'grep("(")' # invalid regular expression
948 $ try 'grep("(")' # invalid regular expression
949 (func
949 (func
950 ('symbol', 'grep')
950 ('symbol', 'grep')
951 ('string', '('))
951 ('string', '('))
952 hg: parse error: invalid match pattern: unbalanced parenthesis
952 hg: parse error: invalid match pattern: unbalanced parenthesis
953 [255]
953 [255]
954 $ try 'grep("\bissue\d+")'
954 $ try 'grep("\bissue\d+")'
955 (func
955 (func
956 ('symbol', 'grep')
956 ('symbol', 'grep')
957 ('string', '\x08issue\\d+'))
957 ('string', '\x08issue\\d+'))
958 * set:
958 * set:
959 <filteredset
959 <filteredset
960 <fullreposet+ 0:9>,
960 <fullreposet+ 0:9>,
961 <grep '\x08issue\\d+'>>
961 <grep '\x08issue\\d+'>>
962 $ try 'grep(r"\bissue\d+")'
962 $ try 'grep(r"\bissue\d+")'
963 (func
963 (func
964 ('symbol', 'grep')
964 ('symbol', 'grep')
965 ('string', '\\bissue\\d+'))
965 ('string', '\\bissue\\d+'))
966 * set:
966 * set:
967 <filteredset
967 <filteredset
968 <fullreposet+ 0:9>,
968 <fullreposet+ 0:9>,
969 <grep '\\bissue\\d+'>>
969 <grep '\\bissue\\d+'>>
970 6
970 6
971 $ try 'grep(r"\")'
971 $ try 'grep(r"\")'
972 hg: parse error at 7: unterminated string
972 hg: parse error at 7: unterminated string
973 [255]
973 [255]
974 $ log 'head()'
974 $ log 'head()'
975 0
975 0
976 1
976 1
977 2
977 2
978 3
978 3
979 4
979 4
980 5
980 5
981 6
981 6
982 7
982 7
983 9
983 9
984 $ log 'heads(6::)'
984 $ log 'heads(6::)'
985 7
985 7
986 $ log 'keyword(issue)'
986 $ log 'keyword(issue)'
987 6
987 6
988 $ log 'keyword("test a")'
988 $ log 'keyword("test a")'
989 $ log 'limit(head(), 1)'
989 $ log 'limit(head(), 1)'
990 0
990 0
991 $ log 'limit(author("re:bob|test"), 3, 5)'
991 $ log 'limit(author("re:bob|test"), 3, 5)'
992 5
992 5
993 6
993 6
994 7
994 7
995 $ log 'limit(author("re:bob|test"), offset=6)'
995 $ log 'limit(author("re:bob|test"), offset=6)'
996 6
996 6
997 $ log 'limit(author("re:bob|test"), offset=10)'
997 $ log 'limit(author("re:bob|test"), offset=10)'
998 $ log 'limit(all(), 1, -1)'
998 $ log 'limit(all(), 1, -1)'
999 hg: parse error: negative offset
999 hg: parse error: negative offset
1000 [255]
1000 [255]
1001 $ log 'matching(6)'
1001 $ log 'matching(6)'
1002 6
1002 6
1003 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1003 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1004 6
1004 6
1005 7
1005 7
1006
1006
1007 Testing min and max
1007 Testing min and max
1008
1008
1009 max: simple
1009 max: simple
1010
1010
1011 $ log 'max(contains(a))'
1011 $ log 'max(contains(a))'
1012 5
1012 5
1013
1013
1014 max: simple on unordered set)
1014 max: simple on unordered set)
1015
1015
1016 $ log 'max((4+0+2+5+7) and contains(a))'
1016 $ log 'max((4+0+2+5+7) and contains(a))'
1017 5
1017 5
1018
1018
1019 max: no result
1019 max: no result
1020
1020
1021 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1021 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1022
1022
1023 max: no result on unordered set
1023 max: no result on unordered set
1024
1024
1025 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1025 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1026
1026
1027 min: simple
1027 min: simple
1028
1028
1029 $ log 'min(contains(a))'
1029 $ log 'min(contains(a))'
1030 0
1030 0
1031
1031
1032 min: simple on unordered set
1032 min: simple on unordered set
1033
1033
1034 $ log 'min((4+0+2+5+7) and contains(a))'
1034 $ log 'min((4+0+2+5+7) and contains(a))'
1035 0
1035 0
1036
1036
1037 min: empty
1037 min: empty
1038
1038
1039 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1039 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1040
1040
1041 min: empty on unordered set
1041 min: empty on unordered set
1042
1042
1043 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1043 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1044
1044
1045
1045
1046 $ log 'merge()'
1046 $ log 'merge()'
1047 6
1047 6
1048 $ log 'branchpoint()'
1048 $ log 'branchpoint()'
1049 1
1049 1
1050 4
1050 4
1051 $ log 'modifies(b)'
1051 $ log 'modifies(b)'
1052 4
1052 4
1053 $ log 'modifies("path:b")'
1053 $ log 'modifies("path:b")'
1054 4
1054 4
1055 $ log 'modifies("*")'
1055 $ log 'modifies("*")'
1056 4
1056 4
1057 6
1057 6
1058 $ log 'modifies("set:modified()")'
1058 $ log 'modifies("set:modified()")'
1059 4
1059 4
1060 $ log 'id(5)'
1060 $ log 'id(5)'
1061 2
1061 2
1062 $ log 'only(9)'
1062 $ log 'only(9)'
1063 8
1063 8
1064 9
1064 9
1065 $ log 'only(8)'
1065 $ log 'only(8)'
1066 8
1066 8
1067 $ log 'only(9, 5)'
1067 $ log 'only(9, 5)'
1068 2
1068 2
1069 4
1069 4
1070 8
1070 8
1071 9
1071 9
1072 $ log 'only(7 + 9, 5 + 2)'
1072 $ log 'only(7 + 9, 5 + 2)'
1073 4
1073 4
1074 6
1074 6
1075 7
1075 7
1076 8
1076 8
1077 9
1077 9
1078
1078
1079 Test empty set input
1079 Test empty set input
1080 $ log 'only(p2())'
1080 $ log 'only(p2())'
1081 $ log 'only(p1(), p2())'
1081 $ log 'only(p1(), p2())'
1082 0
1082 0
1083 1
1083 1
1084 2
1084 2
1085 4
1085 4
1086 8
1086 8
1087 9
1087 9
1088
1088
1089 Test '%' operator
1089 Test '%' operator
1090
1090
1091 $ log '9%'
1091 $ log '9%'
1092 8
1092 8
1093 9
1093 9
1094 $ log '9%5'
1094 $ log '9%5'
1095 2
1095 2
1096 4
1096 4
1097 8
1097 8
1098 9
1098 9
1099 $ log '(7 + 9)%(5 + 2)'
1099 $ log '(7 + 9)%(5 + 2)'
1100 4
1100 4
1101 6
1101 6
1102 7
1102 7
1103 8
1103 8
1104 9
1104 9
1105
1105
1106 Test operand of '%' is optimized recursively (issue4670)
1106 Test operand of '%' is optimized recursively (issue4670)
1107
1107
1108 $ try --optimize '8:9-8%'
1108 $ try --optimize '8:9-8%'
1109 (onlypost
1109 (onlypost
1110 (minus
1110 (minus
1111 (range
1111 (range
1112 ('symbol', '8')
1112 ('symbol', '8')
1113 ('symbol', '9'))
1113 ('symbol', '9'))
1114 ('symbol', '8')))
1114 ('symbol', '8')))
1115 * optimized:
1115 * optimized:
1116 (func
1116 (func
1117 ('symbol', 'only')
1117 ('symbol', 'only')
1118 (difference
1118 (difference
1119 (range
1119 (range
1120 ('symbol', '8')
1120 ('symbol', '8')
1121 ('symbol', '9')
1121 ('symbol', '9')
1122 define)
1122 define)
1123 ('symbol', '8')
1123 ('symbol', '8')
1124 define)
1124 define)
1125 define)
1125 define)
1126 * set:
1126 * set:
1127 <baseset+ [8, 9]>
1127 <baseset+ [8, 9]>
1128 8
1128 8
1129 9
1129 9
1130 $ try --optimize '(9)%(5)'
1130 $ try --optimize '(9)%(5)'
1131 (only
1131 (only
1132 (group
1132 (group
1133 ('symbol', '9'))
1133 ('symbol', '9'))
1134 (group
1134 (group
1135 ('symbol', '5')))
1135 ('symbol', '5')))
1136 * optimized:
1136 * optimized:
1137 (func
1137 (func
1138 ('symbol', 'only')
1138 ('symbol', 'only')
1139 (list
1139 (list
1140 ('symbol', '9')
1140 ('symbol', '9')
1141 ('symbol', '5'))
1141 ('symbol', '5'))
1142 define)
1142 define)
1143 * set:
1143 * set:
1144 <baseset+ [2, 4, 8, 9]>
1144 <baseset+ [2, 4, 8, 9]>
1145 2
1145 2
1146 4
1146 4
1147 8
1147 8
1148 9
1148 9
1149
1149
1150 Test the order of operations
1150 Test the order of operations
1151
1151
1152 $ log '7 + 9%5 + 2'
1152 $ log '7 + 9%5 + 2'
1153 7
1153 7
1154 2
1154 2
1155 4
1155 4
1156 8
1156 8
1157 9
1157 9
1158
1158
1159 Test explicit numeric revision
1159 Test explicit numeric revision
1160 $ log 'rev(-2)'
1160 $ log 'rev(-2)'
1161 $ log 'rev(-1)'
1161 $ log 'rev(-1)'
1162 -1
1162 -1
1163 $ log 'rev(0)'
1163 $ log 'rev(0)'
1164 0
1164 0
1165 $ log 'rev(9)'
1165 $ log 'rev(9)'
1166 9
1166 9
1167 $ log 'rev(10)'
1167 $ log 'rev(10)'
1168 $ log 'rev(tip)'
1168 $ log 'rev(tip)'
1169 hg: parse error: rev expects a number
1169 hg: parse error: rev expects a number
1170 [255]
1170 [255]
1171
1171
1172 Test hexadecimal revision
1172 Test hexadecimal revision
1173 $ log 'id(2)'
1173 $ log 'id(2)'
1174 abort: 00changelog.i@2: ambiguous identifier!
1174 abort: 00changelog.i@2: ambiguous identifier!
1175 [255]
1175 [255]
1176 $ log 'id(23268)'
1176 $ log 'id(23268)'
1177 4
1177 4
1178 $ log 'id(2785f51eece)'
1178 $ log 'id(2785f51eece)'
1179 0
1179 0
1180 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1180 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1181 8
1181 8
1182 $ log 'id(d5d0dcbdc4a)'
1182 $ log 'id(d5d0dcbdc4a)'
1183 $ log 'id(d5d0dcbdc4w)'
1183 $ log 'id(d5d0dcbdc4w)'
1184 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1184 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1185 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1185 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1186 $ log 'id(1.0)'
1186 $ log 'id(1.0)'
1187 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1187 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1188
1188
1189 Test null revision
1189 Test null revision
1190 $ log '(null)'
1190 $ log '(null)'
1191 -1
1191 -1
1192 $ log '(null:0)'
1192 $ log '(null:0)'
1193 -1
1193 -1
1194 0
1194 0
1195 $ log '(0:null)'
1195 $ log '(0:null)'
1196 0
1196 0
1197 -1
1197 -1
1198 $ log 'null::0'
1198 $ log 'null::0'
1199 -1
1199 -1
1200 0
1200 0
1201 $ log 'null:tip - 0:'
1201 $ log 'null:tip - 0:'
1202 -1
1202 -1
1203 $ log 'null: and null::' | head -1
1203 $ log 'null: and null::' | head -1
1204 -1
1204 -1
1205 $ log 'null: or 0:' | head -2
1205 $ log 'null: or 0:' | head -2
1206 -1
1206 -1
1207 0
1207 0
1208 $ log 'ancestors(null)'
1208 $ log 'ancestors(null)'
1209 -1
1209 -1
1210 $ log 'reverse(null:)' | tail -2
1210 $ log 'reverse(null:)' | tail -2
1211 0
1211 0
1212 -1
1212 -1
1213 BROKEN: should be '-1'
1213 BROKEN: should be '-1'
1214 $ log 'first(null:)'
1214 $ log 'first(null:)'
1215 BROKEN: should be '-1'
1215 BROKEN: should be '-1'
1216 $ log 'min(null:)'
1216 $ log 'min(null:)'
1217 $ log 'tip:null and all()' | tail -2
1217 $ log 'tip:null and all()' | tail -2
1218 1
1218 1
1219 0
1219 0
1220
1220
1221 Test working-directory revision
1221 Test working-directory revision
1222 $ hg debugrevspec 'wdir()'
1222 $ hg debugrevspec 'wdir()'
1223 2147483647
1223 2147483647
1224 $ hg debugrevspec 'wdir()^'
1224 $ hg debugrevspec 'wdir()^'
1225 9
1225 9
1226 $ hg up 7
1226 $ hg up 7
1227 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1227 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1228 $ hg debugrevspec 'wdir()^'
1228 $ hg debugrevspec 'wdir()^'
1229 7
1229 7
1230 $ hg debugrevspec 'wdir()^0'
1230 $ hg debugrevspec 'wdir()^0'
1231 2147483647
1231 2147483647
1232 $ hg debugrevspec 'wdir()~3'
1232 $ hg debugrevspec 'wdir()~3'
1233 5
1233 5
1234 $ hg debugrevspec 'ancestors(wdir())'
1234 $ hg debugrevspec 'ancestors(wdir())'
1235 0
1235 0
1236 1
1236 1
1237 2
1237 2
1238 3
1238 3
1239 4
1239 4
1240 5
1240 5
1241 6
1241 6
1242 7
1242 7
1243 2147483647
1243 2147483647
1244 $ hg debugrevspec 'wdir()~0'
1244 $ hg debugrevspec 'wdir()~0'
1245 2147483647
1245 2147483647
1246 $ hg debugrevspec 'p1(wdir())'
1246 $ hg debugrevspec 'p1(wdir())'
1247 7
1247 7
1248 $ hg debugrevspec 'p2(wdir())'
1248 $ hg debugrevspec 'p2(wdir())'
1249 $ hg debugrevspec 'parents(wdir())'
1249 $ hg debugrevspec 'parents(wdir())'
1250 7
1250 7
1251 $ hg debugrevspec 'wdir()^1'
1251 $ hg debugrevspec 'wdir()^1'
1252 7
1252 7
1253 $ hg debugrevspec 'wdir()^2'
1253 $ hg debugrevspec 'wdir()^2'
1254 $ hg debugrevspec 'wdir()^3'
1254 $ hg debugrevspec 'wdir()^3'
1255 hg: parse error: ^ expects a number 0, 1, or 2
1255 hg: parse error: ^ expects a number 0, 1, or 2
1256 [255]
1256 [255]
1257 For tests consistency
1257 For tests consistency
1258 $ hg up 9
1258 $ hg up 9
1259 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1259 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1260 $ hg debugrevspec 'tip or wdir()'
1260 $ hg debugrevspec 'tip or wdir()'
1261 9
1261 9
1262 2147483647
1262 2147483647
1263 $ hg debugrevspec '0:tip and wdir()'
1263 $ hg debugrevspec '0:tip and wdir()'
1264 $ log '0:wdir()' | tail -3
1264 $ log '0:wdir()' | tail -3
1265 8
1265 8
1266 9
1266 9
1267 2147483647
1267 2147483647
1268 $ log 'wdir():0' | head -3
1268 $ log 'wdir():0' | head -3
1269 2147483647
1269 2147483647
1270 9
1270 9
1271 8
1271 8
1272 $ log 'wdir():wdir()'
1272 $ log 'wdir():wdir()'
1273 2147483647
1273 2147483647
1274 $ log '(all() + wdir()) & min(. + wdir())'
1274 $ log '(all() + wdir()) & min(. + wdir())'
1275 9
1275 9
1276 $ log '(all() + wdir()) & max(. + wdir())'
1276 $ log '(all() + wdir()) & max(. + wdir())'
1277 2147483647
1277 2147483647
1278 $ log '(all() + wdir()) & first(wdir() + .)'
1278 $ log '(all() + wdir()) & first(wdir() + .)'
1279 2147483647
1279 2147483647
1280 $ log '(all() + wdir()) & last(. + wdir())'
1280 $ log '(all() + wdir()) & last(. + wdir())'
1281 2147483647
1281 2147483647
1282
1282
1283 $ log 'outgoing()'
1283 $ log 'outgoing()'
1284 8
1284 8
1285 9
1285 9
1286 $ log 'outgoing("../remote1")'
1286 $ log 'outgoing("../remote1")'
1287 8
1287 8
1288 9
1288 9
1289 $ log 'outgoing("../remote2")'
1289 $ log 'outgoing("../remote2")'
1290 3
1290 3
1291 5
1291 5
1292 6
1292 6
1293 7
1293 7
1294 9
1294 9
1295 $ log 'p1(merge())'
1295 $ log 'p1(merge())'
1296 5
1296 5
1297 $ log 'p2(merge())'
1297 $ log 'p2(merge())'
1298 4
1298 4
1299 $ log 'parents(merge())'
1299 $ log 'parents(merge())'
1300 4
1300 4
1301 5
1301 5
1302 $ log 'p1(branchpoint())'
1302 $ log 'p1(branchpoint())'
1303 0
1303 0
1304 2
1304 2
1305 $ log 'p2(branchpoint())'
1305 $ log 'p2(branchpoint())'
1306 $ log 'parents(branchpoint())'
1306 $ log 'parents(branchpoint())'
1307 0
1307 0
1308 2
1308 2
1309 $ log 'removes(a)'
1309 $ log 'removes(a)'
1310 2
1310 2
1311 6
1311 6
1312 $ log 'roots(all())'
1312 $ log 'roots(all())'
1313 0
1313 0
1314 $ log 'reverse(2 or 3 or 4 or 5)'
1314 $ log 'reverse(2 or 3 or 4 or 5)'
1315 5
1315 5
1316 4
1316 4
1317 3
1317 3
1318 2
1318 2
1319 $ log 'reverse(all())'
1319 $ log 'reverse(all())'
1320 9
1320 9
1321 8
1321 8
1322 7
1322 7
1323 6
1323 6
1324 5
1324 5
1325 4
1325 4
1326 3
1326 3
1327 2
1327 2
1328 1
1328 1
1329 0
1329 0
1330 $ log 'reverse(all()) & filelog(b)'
1330 $ log 'reverse(all()) & filelog(b)'
1331 4
1331 4
1332 1
1332 1
1333 $ log 'rev(5)'
1333 $ log 'rev(5)'
1334 5
1334 5
1335 $ log 'sort(limit(reverse(all()), 3))'
1335 $ log 'sort(limit(reverse(all()), 3))'
1336 7
1336 7
1337 8
1337 8
1338 9
1338 9
1339 $ log 'sort(2 or 3 or 4 or 5, date)'
1339 $ log 'sort(2 or 3 or 4 or 5, date)'
1340 2
1340 2
1341 3
1341 3
1342 5
1342 5
1343 4
1343 4
1344 $ log 'tagged()'
1344 $ log 'tagged()'
1345 6
1345 6
1346 $ log 'tag()'
1346 $ log 'tag()'
1347 6
1347 6
1348 $ log 'tag(1.0)'
1348 $ log 'tag(1.0)'
1349 6
1349 6
1350 $ log 'tag(tip)'
1350 $ log 'tag(tip)'
1351 9
1351 9
1352
1352
1353 Test order of revisions in compound expression
1353 Test order of revisions in compound expression
1354 ----------------------------------------------
1354 ----------------------------------------------
1355
1355
1356 The general rule is that only the outermost (= leftmost) predicate can
1356 The general rule is that only the outermost (= leftmost) predicate can
1357 enforce its ordering requirement. The other predicates should take the
1357 enforce its ordering requirement. The other predicates should take the
1358 ordering defined by it.
1358 ordering defined by it.
1359
1359
1360 'A & B' should follow the order of 'A':
1360 'A & B' should follow the order of 'A':
1361
1361
1362 $ log '2:0 & 0::2'
1362 $ log '2:0 & 0::2'
1363 2
1363 2
1364 1
1364 1
1365 0
1365 0
1366
1366
1367 'head()' combines sets in right order:
1367 'head()' combines sets in right order:
1368
1368
1369 $ log '2:0 & head()'
1369 $ log '2:0 & head()'
1370 2
1370 2
1371 1
1371 1
1372 0
1372 0
1373
1373
1374 'x:y' takes ordering parameter into account:
1374 'x:y' takes ordering parameter into account:
1375
1375
1376 $ try -p optimized '3:0 & 0:3 & not 2:1'
1376 $ try -p optimized '3:0 & 0:3 & not 2:1'
1377 * optimized:
1377 * optimized:
1378 (difference
1378 (difference
1379 (and
1379 (and
1380 (range
1380 (range
1381 ('symbol', '3')
1381 ('symbol', '3')
1382 ('symbol', '0')
1382 ('symbol', '0')
1383 define)
1383 define)
1384 (range
1384 (range
1385 ('symbol', '0')
1385 ('symbol', '0')
1386 ('symbol', '3')
1386 ('symbol', '3')
1387 follow)
1387 follow)
1388 define)
1388 define)
1389 (range
1389 (range
1390 ('symbol', '2')
1390 ('symbol', '2')
1391 ('symbol', '1')
1391 ('symbol', '1')
1392 any)
1392 any)
1393 define)
1393 define)
1394 * set:
1394 * set:
1395 <filteredset
1395 <filteredset
1396 <filteredset
1396 <filteredset
1397 <spanset- 0:3>,
1397 <spanset- 0:3>,
1398 <spanset+ 0:3>>,
1398 <spanset+ 0:3>>,
1399 <not
1399 <not
1400 <spanset+ 1:2>>>
1400 <spanset+ 1:2>>>
1401 3
1401 3
1402 0
1402 0
1403
1403
1404 'a + b', which is optimized to '_list(a b)', should take the ordering of
1404 'a + b', which is optimized to '_list(a b)', should take the ordering of
1405 the left expression:
1405 the left expression:
1406
1406
1407 $ try --optimize '2:0 & (0 + 1 + 2)'
1407 $ try --optimize '2:0 & (0 + 1 + 2)'
1408 (and
1408 (and
1409 (range
1409 (range
1410 ('symbol', '2')
1410 ('symbol', '2')
1411 ('symbol', '0'))
1411 ('symbol', '0'))
1412 (group
1412 (group
1413 (or
1413 (or
1414 (list
1414 (list
1415 ('symbol', '0')
1415 ('symbol', '0')
1416 ('symbol', '1')
1416 ('symbol', '1')
1417 ('symbol', '2')))))
1417 ('symbol', '2')))))
1418 * optimized:
1418 * optimized:
1419 (and
1419 (and
1420 (range
1420 (range
1421 ('symbol', '2')
1421 ('symbol', '2')
1422 ('symbol', '0')
1422 ('symbol', '0')
1423 define)
1423 define)
1424 (func
1424 (func
1425 ('symbol', '_list')
1425 ('symbol', '_list')
1426 ('string', '0\x001\x002')
1426 ('string', '0\x001\x002')
1427 follow)
1427 follow)
1428 define)
1428 define)
1429 * set:
1429 * set:
1430 <filteredset
1430 <filteredset
1431 <spanset- 0:2>,
1431 <spanset- 0:2>,
1432 <baseset [0, 1, 2]>>
1432 <baseset [0, 1, 2]>>
1433 2
1433 2
1434 1
1434 1
1435 0
1435 0
1436
1436
1437 'A + B' should take the ordering of the left expression:
1437 'A + B' should take the ordering of the left expression:
1438
1438
1439 $ try --optimize '2:0 & (0:1 + 2)'
1439 $ try --optimize '2:0 & (0:1 + 2)'
1440 (and
1440 (and
1441 (range
1441 (range
1442 ('symbol', '2')
1442 ('symbol', '2')
1443 ('symbol', '0'))
1443 ('symbol', '0'))
1444 (group
1444 (group
1445 (or
1445 (or
1446 (list
1446 (list
1447 (range
1447 (range
1448 ('symbol', '0')
1448 ('symbol', '0')
1449 ('symbol', '1'))
1449 ('symbol', '1'))
1450 ('symbol', '2')))))
1450 ('symbol', '2')))))
1451 * optimized:
1451 * optimized:
1452 (and
1452 (and
1453 (range
1453 (range
1454 ('symbol', '2')
1454 ('symbol', '2')
1455 ('symbol', '0')
1455 ('symbol', '0')
1456 define)
1456 define)
1457 (or
1457 (or
1458 (list
1458 (list
1459 ('symbol', '2')
1459 ('symbol', '2')
1460 (range
1460 (range
1461 ('symbol', '0')
1461 ('symbol', '0')
1462 ('symbol', '1')
1462 ('symbol', '1')
1463 follow))
1463 follow))
1464 follow)
1464 follow)
1465 define)
1465 define)
1466 * set:
1466 * set:
1467 <filteredset
1467 <filteredset
1468 <spanset- 0:2>,
1468 <spanset- 0:2>,
1469 <addset
1469 <addset
1470 <baseset [2]>,
1470 <baseset [2]>,
1471 <spanset+ 0:1>>>
1471 <spanset+ 0:1>>>
1472 2
1472 2
1473 1
1473 1
1474 0
1474 0
1475
1475
1476 '_intlist(a b)' should behave like 'a + b':
1476 '_intlist(a b)' should behave like 'a + b':
1477
1477
1478 $ trylist --optimize '2:0 & %ld' 0 1 2
1478 $ trylist --optimize '2:0 & %ld' 0 1 2
1479 (and
1479 (and
1480 (range
1480 (range
1481 ('symbol', '2')
1481 ('symbol', '2')
1482 ('symbol', '0'))
1482 ('symbol', '0'))
1483 (func
1483 (func
1484 ('symbol', '_intlist')
1484 ('symbol', '_intlist')
1485 ('string', '0\x001\x002')))
1485 ('string', '0\x001\x002')))
1486 * optimized:
1486 * optimized:
1487 (and
1487 (and
1488 (func
1488 (func
1489 ('symbol', '_intlist')
1489 ('symbol', '_intlist')
1490 ('string', '0\x001\x002')
1490 ('string', '0\x001\x002')
1491 follow)
1491 follow)
1492 (range
1492 (range
1493 ('symbol', '2')
1493 ('symbol', '2')
1494 ('symbol', '0')
1494 ('symbol', '0')
1495 define)
1495 define)
1496 define)
1496 define)
1497 * set:
1497 * set:
1498 <filteredset
1498 <filteredset
1499 <spanset- 0:2>,
1499 <spanset- 0:2>,
1500 <baseset+ [0, 1, 2]>>
1500 <baseset+ [0, 1, 2]>>
1501 2
1501 2
1502 1
1502 1
1503 0
1503 0
1504
1504
1505 $ trylist --optimize '%ld & 2:0' 0 2 1
1505 $ trylist --optimize '%ld & 2:0' 0 2 1
1506 (and
1506 (and
1507 (func
1507 (func
1508 ('symbol', '_intlist')
1508 ('symbol', '_intlist')
1509 ('string', '0\x002\x001'))
1509 ('string', '0\x002\x001'))
1510 (range
1510 (range
1511 ('symbol', '2')
1511 ('symbol', '2')
1512 ('symbol', '0')))
1512 ('symbol', '0')))
1513 * optimized:
1513 * optimized:
1514 (and
1514 (and
1515 (func
1515 (func
1516 ('symbol', '_intlist')
1516 ('symbol', '_intlist')
1517 ('string', '0\x002\x001')
1517 ('string', '0\x002\x001')
1518 define)
1518 define)
1519 (range
1519 (range
1520 ('symbol', '2')
1520 ('symbol', '2')
1521 ('symbol', '0')
1521 ('symbol', '0')
1522 follow)
1522 follow)
1523 define)
1523 define)
1524 * set:
1524 * set:
1525 <filteredset
1525 <filteredset
1526 <baseset [0, 2, 1]>,
1526 <baseset [0, 2, 1]>,
1527 <spanset- 0:2>>
1527 <spanset- 0:2>>
1528 0
1528 0
1529 2
1529 2
1530 1
1530 1
1531
1531
1532 '_hexlist(a b)' should behave like 'a + b':
1532 '_hexlist(a b)' should behave like 'a + b':
1533
1533
1534 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1534 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1535 (and
1535 (and
1536 (range
1536 (range
1537 ('symbol', '2')
1537 ('symbol', '2')
1538 ('symbol', '0'))
1538 ('symbol', '0'))
1539 (func
1539 (func
1540 ('symbol', '_hexlist')
1540 ('symbol', '_hexlist')
1541 ('string', '*'))) (glob)
1541 ('string', '*'))) (glob)
1542 * optimized:
1542 * optimized:
1543 (and
1543 (and
1544 (range
1544 (range
1545 ('symbol', '2')
1545 ('symbol', '2')
1546 ('symbol', '0')
1546 ('symbol', '0')
1547 define)
1547 define)
1548 (func
1548 (func
1549 ('symbol', '_hexlist')
1549 ('symbol', '_hexlist')
1550 ('string', '*') (glob)
1550 ('string', '*') (glob)
1551 follow)
1551 follow)
1552 define)
1552 define)
1553 * set:
1553 * set:
1554 <filteredset
1554 <filteredset
1555 <spanset- 0:2>,
1555 <spanset- 0:2>,
1556 <baseset [0, 1, 2]>>
1556 <baseset [0, 1, 2]>>
1557 2
1557 2
1558 1
1558 1
1559 0
1559 0
1560
1560
1561 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1561 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1562 (and
1562 (and
1563 (func
1563 (func
1564 ('symbol', '_hexlist')
1564 ('symbol', '_hexlist')
1565 ('string', '*')) (glob)
1565 ('string', '*')) (glob)
1566 (range
1566 (range
1567 ('symbol', '2')
1567 ('symbol', '2')
1568 ('symbol', '0')))
1568 ('symbol', '0')))
1569 * optimized:
1569 * optimized:
1570 (and
1570 (and
1571 (range
1571 (range
1572 ('symbol', '2')
1572 ('symbol', '2')
1573 ('symbol', '0')
1573 ('symbol', '0')
1574 follow)
1574 follow)
1575 (func
1575 (func
1576 ('symbol', '_hexlist')
1576 ('symbol', '_hexlist')
1577 ('string', '*') (glob)
1577 ('string', '*') (glob)
1578 define)
1578 define)
1579 define)
1579 define)
1580 * set:
1580 * set:
1581 <baseset [0, 2, 1]>
1581 <baseset [0, 2, 1]>
1582 0
1582 0
1583 2
1583 2
1584 1
1584 1
1585
1585
1586 '_list' should not go through the slow follow-order path if order doesn't
1586 '_list' should not go through the slow follow-order path if order doesn't
1587 matter:
1587 matter:
1588
1588
1589 $ try -p optimized '2:0 & not (0 + 1)'
1589 $ try -p optimized '2:0 & not (0 + 1)'
1590 * optimized:
1590 * optimized:
1591 (difference
1591 (difference
1592 (range
1592 (range
1593 ('symbol', '2')
1593 ('symbol', '2')
1594 ('symbol', '0')
1594 ('symbol', '0')
1595 define)
1595 define)
1596 (func
1596 (func
1597 ('symbol', '_list')
1597 ('symbol', '_list')
1598 ('string', '0\x001')
1598 ('string', '0\x001')
1599 any)
1599 any)
1600 define)
1600 define)
1601 * set:
1601 * set:
1602 <filteredset
1602 <filteredset
1603 <spanset- 0:2>,
1603 <spanset- 0:2>,
1604 <not
1604 <not
1605 <baseset [0, 1]>>>
1605 <baseset [0, 1]>>>
1606 2
1606 2
1607
1607
1608 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1608 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1609 * optimized:
1609 * optimized:
1610 (difference
1610 (difference
1611 (range
1611 (range
1612 ('symbol', '2')
1612 ('symbol', '2')
1613 ('symbol', '0')
1613 ('symbol', '0')
1614 define)
1614 define)
1615 (and
1615 (and
1616 (range
1616 (range
1617 ('symbol', '0')
1617 ('symbol', '0')
1618 ('symbol', '2')
1618 ('symbol', '2')
1619 any)
1619 any)
1620 (func
1620 (func
1621 ('symbol', '_list')
1621 ('symbol', '_list')
1622 ('string', '0\x001')
1622 ('string', '0\x001')
1623 any)
1623 any)
1624 any)
1624 any)
1625 define)
1625 define)
1626 * set:
1626 * set:
1627 <filteredset
1627 <filteredset
1628 <spanset- 0:2>,
1628 <spanset- 0:2>,
1629 <not
1629 <not
1630 <baseset [0, 1]>>>
1630 <baseset [0, 1]>>>
1631 2
1631 2
1632
1632
1633 because 'present()' does nothing other than suppressing an error, the
1633 because 'present()' does nothing other than suppressing an error, the
1634 ordering requirement should be forwarded to the nested expression
1634 ordering requirement should be forwarded to the nested expression
1635
1635
1636 $ try -p optimized 'present(2 + 0 + 1)'
1636 $ try -p optimized 'present(2 + 0 + 1)'
1637 * optimized:
1637 * optimized:
1638 (func
1638 (func
1639 ('symbol', 'present')
1639 ('symbol', 'present')
1640 (func
1640 (func
1641 ('symbol', '_list')
1641 ('symbol', '_list')
1642 ('string', '2\x000\x001')
1642 ('string', '2\x000\x001')
1643 define)
1643 define)
1644 define)
1644 define)
1645 * set:
1645 * set:
1646 <baseset [2, 0, 1]>
1646 <baseset [2, 0, 1]>
1647 2
1647 2
1648 0
1648 0
1649 1
1649 1
1650
1650
1651 $ try --optimize '2:0 & present(0 + 1 + 2)'
1651 $ try --optimize '2:0 & present(0 + 1 + 2)'
1652 (and
1652 (and
1653 (range
1653 (range
1654 ('symbol', '2')
1654 ('symbol', '2')
1655 ('symbol', '0'))
1655 ('symbol', '0'))
1656 (func
1656 (func
1657 ('symbol', 'present')
1657 ('symbol', 'present')
1658 (or
1658 (or
1659 (list
1659 (list
1660 ('symbol', '0')
1660 ('symbol', '0')
1661 ('symbol', '1')
1661 ('symbol', '1')
1662 ('symbol', '2')))))
1662 ('symbol', '2')))))
1663 * optimized:
1663 * optimized:
1664 (and
1664 (and
1665 (range
1665 (range
1666 ('symbol', '2')
1666 ('symbol', '2')
1667 ('symbol', '0')
1667 ('symbol', '0')
1668 define)
1668 define)
1669 (func
1669 (func
1670 ('symbol', 'present')
1670 ('symbol', 'present')
1671 (func
1671 (func
1672 ('symbol', '_list')
1672 ('symbol', '_list')
1673 ('string', '0\x001\x002')
1673 ('string', '0\x001\x002')
1674 follow)
1674 follow)
1675 follow)
1675 follow)
1676 define)
1676 define)
1677 * set:
1677 * set:
1678 <filteredset
1678 <filteredset
1679 <spanset- 0:2>,
1679 <spanset- 0:2>,
1680 <baseset [0, 1, 2]>>
1680 <baseset [0, 1, 2]>>
1681 2
1681 2
1682 1
1682 1
1683 0
1683 0
1684
1684
1685 'reverse()' should take effect only if it is the outermost expression:
1685 'reverse()' should take effect only if it is the outermost expression:
1686
1686
1687 $ try --optimize '0:2 & reverse(all())'
1687 $ try --optimize '0:2 & reverse(all())'
1688 (and
1688 (and
1689 (range
1689 (range
1690 ('symbol', '0')
1690 ('symbol', '0')
1691 ('symbol', '2'))
1691 ('symbol', '2'))
1692 (func
1692 (func
1693 ('symbol', 'reverse')
1693 ('symbol', 'reverse')
1694 (func
1694 (func
1695 ('symbol', 'all')
1695 ('symbol', 'all')
1696 None)))
1696 None)))
1697 * optimized:
1697 * optimized:
1698 (and
1698 (and
1699 (range
1699 (range
1700 ('symbol', '0')
1700 ('symbol', '0')
1701 ('symbol', '2')
1701 ('symbol', '2')
1702 define)
1702 define)
1703 (func
1703 (func
1704 ('symbol', 'reverse')
1704 ('symbol', 'reverse')
1705 (func
1705 (func
1706 ('symbol', 'all')
1706 ('symbol', 'all')
1707 None
1707 None
1708 define)
1708 define)
1709 follow)
1709 follow)
1710 define)
1710 define)
1711 * set:
1711 * set:
1712 <filteredset
1712 <filteredset
1713 <spanset+ 0:2>,
1713 <spanset+ 0:2>,
1714 <spanset+ 0:9>>
1714 <spanset+ 0:9>>
1715 0
1715 0
1716 1
1716 1
1717 2
1717 2
1718
1718
1719 'sort()' should take effect only if it is the outermost expression:
1719 'sort()' should take effect only if it is the outermost expression:
1720
1720
1721 $ try --optimize '0:2 & sort(all(), -rev)'
1721 $ try --optimize '0:2 & sort(all(), -rev)'
1722 (and
1722 (and
1723 (range
1723 (range
1724 ('symbol', '0')
1724 ('symbol', '0')
1725 ('symbol', '2'))
1725 ('symbol', '2'))
1726 (func
1726 (func
1727 ('symbol', 'sort')
1727 ('symbol', 'sort')
1728 (list
1728 (list
1729 (func
1729 (func
1730 ('symbol', 'all')
1730 ('symbol', 'all')
1731 None)
1731 None)
1732 (negate
1732 (negate
1733 ('symbol', 'rev')))))
1733 ('symbol', 'rev')))))
1734 * optimized:
1734 * optimized:
1735 (and
1735 (and
1736 (range
1736 (range
1737 ('symbol', '0')
1737 ('symbol', '0')
1738 ('symbol', '2')
1738 ('symbol', '2')
1739 define)
1739 define)
1740 (func
1740 (func
1741 ('symbol', 'sort')
1741 ('symbol', 'sort')
1742 (list
1742 (list
1743 (func
1743 (func
1744 ('symbol', 'all')
1744 ('symbol', 'all')
1745 None
1745 None
1746 define)
1746 define)
1747 ('string', '-rev'))
1747 ('string', '-rev'))
1748 follow)
1748 follow)
1749 define)
1749 define)
1750 * set:
1750 * set:
1751 <filteredset
1751 <filteredset
1752 <spanset+ 0:2>,
1752 <spanset+ 0:2>,
1753 <spanset+ 0:9>>
1753 <spanset+ 0:9>>
1754 0
1754 0
1755 1
1755 1
1756 2
1756 2
1757
1757
1758 invalid argument passed to noop sort():
1758 invalid argument passed to noop sort():
1759
1759
1760 $ log '0:2 & sort()'
1760 $ log '0:2 & sort()'
1761 hg: parse error: sort requires one or two arguments
1761 hg: parse error: sort requires one or two arguments
1762 [255]
1762 [255]
1763 $ log '0:2 & sort(all(), -invalid)'
1763 $ log '0:2 & sort(all(), -invalid)'
1764 hg: parse error: unknown sort key '-invalid'
1764 hg: parse error: unknown sort key '-invalid'
1765 [255]
1765 [255]
1766
1766
1767 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1767 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1768
1768
1769 $ try --optimize '2:0 & first(1 + 0 + 2)'
1769 $ try --optimize '2:0 & first(1 + 0 + 2)'
1770 (and
1770 (and
1771 (range
1771 (range
1772 ('symbol', '2')
1772 ('symbol', '2')
1773 ('symbol', '0'))
1773 ('symbol', '0'))
1774 (func
1774 (func
1775 ('symbol', 'first')
1775 ('symbol', 'first')
1776 (or
1776 (or
1777 (list
1777 (list
1778 ('symbol', '1')
1778 ('symbol', '1')
1779 ('symbol', '0')
1779 ('symbol', '0')
1780 ('symbol', '2')))))
1780 ('symbol', '2')))))
1781 * optimized:
1781 * optimized:
1782 (and
1782 (and
1783 (range
1783 (range
1784 ('symbol', '2')
1784 ('symbol', '2')
1785 ('symbol', '0')
1785 ('symbol', '0')
1786 define)
1786 define)
1787 (func
1787 (func
1788 ('symbol', 'first')
1788 ('symbol', 'first')
1789 (func
1789 (func
1790 ('symbol', '_list')
1790 ('symbol', '_list')
1791 ('string', '1\x000\x002')
1791 ('string', '1\x000\x002')
1792 define)
1792 define)
1793 follow)
1793 follow)
1794 define)
1794 define)
1795 * set:
1795 * set:
1796 <baseset
1796 <baseset
1797 <limit n=1, offset=0,
1797 <limit n=1, offset=0,
1798 <spanset- 0:2>,
1798 <spanset- 0:2>,
1799 <baseset [1, 0, 2]>>>
1799 <baseset [1, 0, 2]>>>
1800 1
1800 1
1801
1801
1802 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1802 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1803 (and
1803 (and
1804 (range
1804 (range
1805 ('symbol', '2')
1805 ('symbol', '2')
1806 ('symbol', '0'))
1806 ('symbol', '0'))
1807 (not
1807 (not
1808 (func
1808 (func
1809 ('symbol', 'last')
1809 ('symbol', 'last')
1810 (or
1810 (or
1811 (list
1811 (list
1812 ('symbol', '0')
1812 ('symbol', '0')
1813 ('symbol', '2')
1813 ('symbol', '2')
1814 ('symbol', '1'))))))
1814 ('symbol', '1'))))))
1815 * optimized:
1815 * optimized:
1816 (difference
1816 (difference
1817 (range
1817 (range
1818 ('symbol', '2')
1818 ('symbol', '2')
1819 ('symbol', '0')
1819 ('symbol', '0')
1820 define)
1820 define)
1821 (func
1821 (func
1822 ('symbol', 'last')
1822 ('symbol', 'last')
1823 (func
1823 (func
1824 ('symbol', '_list')
1824 ('symbol', '_list')
1825 ('string', '0\x002\x001')
1825 ('string', '0\x002\x001')
1826 define)
1826 define)
1827 any)
1827 any)
1828 define)
1828 define)
1829 * set:
1829 * set:
1830 <filteredset
1830 <filteredset
1831 <spanset- 0:2>,
1831 <spanset- 0:2>,
1832 <not
1832 <not
1833 <baseset
1833 <baseset
1834 <last n=1,
1834 <last n=1,
1835 <fullreposet+ 0:9>,
1835 <fullreposet+ 0:9>,
1836 <baseset [1, 2, 0]>>>>>
1836 <baseset [1, 2, 0]>>>>>
1837 2
1837 2
1838 0
1838 0
1839
1839
1840 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1840 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1841
1841
1842 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1842 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1843 (and
1843 (and
1844 (range
1844 (range
1845 ('symbol', '2')
1845 ('symbol', '2')
1846 ('symbol', '0'))
1846 ('symbol', '0'))
1847 (range
1847 (range
1848 (group
1848 (group
1849 (or
1849 (or
1850 (list
1850 (list
1851 ('symbol', '1')
1851 ('symbol', '1')
1852 ('symbol', '0')
1852 ('symbol', '0')
1853 ('symbol', '2'))))
1853 ('symbol', '2'))))
1854 (group
1854 (group
1855 (or
1855 (or
1856 (list
1856 (list
1857 ('symbol', '0')
1857 ('symbol', '0')
1858 ('symbol', '2')
1858 ('symbol', '2')
1859 ('symbol', '1'))))))
1859 ('symbol', '1'))))))
1860 * optimized:
1860 * optimized:
1861 (and
1861 (and
1862 (range
1862 (range
1863 ('symbol', '2')
1863 ('symbol', '2')
1864 ('symbol', '0')
1864 ('symbol', '0')
1865 define)
1865 define)
1866 (range
1866 (range
1867 (func
1867 (func
1868 ('symbol', '_list')
1868 ('symbol', '_list')
1869 ('string', '1\x000\x002')
1869 ('string', '1\x000\x002')
1870 define)
1870 define)
1871 (func
1871 (func
1872 ('symbol', '_list')
1872 ('symbol', '_list')
1873 ('string', '0\x002\x001')
1873 ('string', '0\x002\x001')
1874 define)
1874 define)
1875 follow)
1875 follow)
1876 define)
1876 define)
1877 * set:
1877 * set:
1878 <filteredset
1878 <filteredset
1879 <spanset- 0:2>,
1879 <spanset- 0:2>,
1880 <baseset [1]>>
1880 <baseset [1]>>
1881 1
1881 1
1882
1882
1883 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1883 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1884 the ordering rule is determined before the rewrite; in this example,
1884 the ordering rule is determined before the rewrite; in this example,
1885 'B' follows the order of the initial set, which is the same order as 'A'
1885 'B' follows the order of the initial set, which is the same order as 'A'
1886 since 'A' also follows the order:
1886 since 'A' also follows the order:
1887
1887
1888 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1888 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1889 (and
1889 (and
1890 (func
1890 (func
1891 ('symbol', 'contains')
1891 ('symbol', 'contains')
1892 ('string', 'glob:*'))
1892 ('string', 'glob:*'))
1893 (group
1893 (group
1894 (or
1894 (or
1895 (list
1895 (list
1896 ('symbol', '2')
1896 ('symbol', '2')
1897 ('symbol', '0')
1897 ('symbol', '0')
1898 ('symbol', '1')))))
1898 ('symbol', '1')))))
1899 * optimized:
1899 * optimized:
1900 (and
1900 (and
1901 (func
1901 (func
1902 ('symbol', '_list')
1902 ('symbol', '_list')
1903 ('string', '2\x000\x001')
1903 ('string', '2\x000\x001')
1904 follow)
1904 follow)
1905 (func
1905 (func
1906 ('symbol', 'contains')
1906 ('symbol', 'contains')
1907 ('string', 'glob:*')
1907 ('string', 'glob:*')
1908 define)
1908 define)
1909 define)
1909 define)
1910 * set:
1910 * set:
1911 <filteredset
1911 <filteredset
1912 <baseset+ [0, 1, 2]>,
1912 <baseset+ [0, 1, 2]>,
1913 <contains 'glob:*'>>
1913 <contains 'glob:*'>>
1914 0
1914 0
1915 1
1915 1
1916 2
1916 2
1917
1917
1918 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1918 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1919 the order appropriately:
1919 the order appropriately:
1920
1920
1921 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1921 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1922 (and
1922 (and
1923 (func
1923 (func
1924 ('symbol', 'reverse')
1924 ('symbol', 'reverse')
1925 (func
1925 (func
1926 ('symbol', 'contains')
1926 ('symbol', 'contains')
1927 ('string', 'glob:*')))
1927 ('string', 'glob:*')))
1928 (group
1928 (group
1929 (or
1929 (or
1930 (list
1930 (list
1931 ('symbol', '0')
1931 ('symbol', '0')
1932 ('symbol', '2')
1932 ('symbol', '2')
1933 ('symbol', '1')))))
1933 ('symbol', '1')))))
1934 * optimized:
1934 * optimized:
1935 (and
1935 (and
1936 (func
1936 (func
1937 ('symbol', '_list')
1937 ('symbol', '_list')
1938 ('string', '0\x002\x001')
1938 ('string', '0\x002\x001')
1939 follow)
1939 follow)
1940 (func
1940 (func
1941 ('symbol', 'reverse')
1941 ('symbol', 'reverse')
1942 (func
1942 (func
1943 ('symbol', 'contains')
1943 ('symbol', 'contains')
1944 ('string', 'glob:*')
1944 ('string', 'glob:*')
1945 define)
1945 define)
1946 define)
1946 define)
1947 define)
1947 define)
1948 * set:
1948 * set:
1949 <filteredset
1949 <filteredset
1950 <baseset- [0, 1, 2]>,
1950 <baseset- [0, 1, 2]>,
1951 <contains 'glob:*'>>
1951 <contains 'glob:*'>>
1952 2
1952 2
1953 1
1953 1
1954 0
1954 0
1955
1955
1956 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
1956 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
1957 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
1957 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
1958
1958
1959 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
1959 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
1960 * optimized:
1960 * optimized:
1961 (and
1961 (and
1962 (range
1962 (range
1963 ('symbol', '0')
1963 ('symbol', '0')
1964 ('symbol', '2')
1964 ('symbol', '2')
1965 define)
1965 define)
1966 (or
1966 (or
1967 (list
1967 (list
1968 ('symbol', '2')
1968 ('symbol', '2')
1969 (func
1969 (func
1970 ('symbol', 'reverse')
1970 ('symbol', 'reverse')
1971 (func
1971 (func
1972 ('symbol', 'contains')
1972 ('symbol', 'contains')
1973 ('string', 'a')
1973 ('string', 'a')
1974 define)
1974 define)
1975 follow))
1975 follow))
1976 follow)
1976 follow)
1977 define)
1977 define)
1978 * set:
1978 * set:
1979 <filteredset
1979 <filteredset
1980 <spanset+ 0:2>,
1980 <spanset+ 0:2>,
1981 <addset
1981 <addset
1982 <baseset [2]>,
1982 <baseset [2]>,
1983 <filteredset
1983 <filteredset
1984 <fullreposet+ 0:9>,
1984 <fullreposet+ 0:9>,
1985 <contains 'a'>>>>
1985 <contains 'a'>>>>
1986 0
1986 0
1987 1
1987 1
1988 2
1988 2
1989
1989
1990 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
1990 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
1991 * optimized:
1991 * optimized:
1992 (and
1992 (and
1993 (range
1993 (range
1994 ('symbol', '0')
1994 ('symbol', '0')
1995 ('symbol', '2')
1995 ('symbol', '2')
1996 follow)
1996 follow)
1997 (or
1997 (or
1998 (list
1998 (list
1999 (func
1999 (func
2000 ('symbol', 'reverse')
2000 ('symbol', 'reverse')
2001 (func
2001 (func
2002 ('symbol', 'contains')
2002 ('symbol', 'contains')
2003 ('string', 'a')
2003 ('string', 'a')
2004 define)
2004 define)
2005 define)
2005 define)
2006 ('symbol', '2'))
2006 ('symbol', '2'))
2007 define)
2007 define)
2008 define)
2008 define)
2009 * set:
2009 * set:
2010 <addset
2010 <addset
2011 <filteredset
2011 <filteredset
2012 <spanset- 0:2>,
2012 <spanset- 0:2>,
2013 <contains 'a'>>,
2013 <contains 'a'>>,
2014 <baseset [2]>>
2014 <baseset [2]>>
2015 1
2015 1
2016 0
2016 0
2017 2
2017 2
2018
2018
2019 test sort revset
2019 test sort revset
2020 --------------------------------------------
2020 --------------------------------------------
2021
2021
2022 test when adding two unordered revsets
2022 test when adding two unordered revsets
2023
2023
2024 $ log 'sort(keyword(issue) or modifies(b))'
2024 $ log 'sort(keyword(issue) or modifies(b))'
2025 4
2025 4
2026 6
2026 6
2027
2027
2028 test when sorting a reversed collection in the same way it is
2028 test when sorting a reversed collection in the same way it is
2029
2029
2030 $ log 'sort(reverse(all()), -rev)'
2030 $ log 'sort(reverse(all()), -rev)'
2031 9
2031 9
2032 8
2032 8
2033 7
2033 7
2034 6
2034 6
2035 5
2035 5
2036 4
2036 4
2037 3
2037 3
2038 2
2038 2
2039 1
2039 1
2040 0
2040 0
2041
2041
2042 test when sorting a reversed collection
2042 test when sorting a reversed collection
2043
2043
2044 $ log 'sort(reverse(all()), rev)'
2044 $ log 'sort(reverse(all()), rev)'
2045 0
2045 0
2046 1
2046 1
2047 2
2047 2
2048 3
2048 3
2049 4
2049 4
2050 5
2050 5
2051 6
2051 6
2052 7
2052 7
2053 8
2053 8
2054 9
2054 9
2055
2055
2056
2056
2057 test sorting two sorted collections in different orders
2057 test sorting two sorted collections in different orders
2058
2058
2059 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2059 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2060 2
2060 2
2061 6
2061 6
2062 8
2062 8
2063 9
2063 9
2064
2064
2065 test sorting two sorted collections in different orders backwards
2065 test sorting two sorted collections in different orders backwards
2066
2066
2067 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2067 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2068 9
2068 9
2069 8
2069 8
2070 6
2070 6
2071 2
2071 2
2072
2072
2073 test empty sort key which is noop
2073 test empty sort key which is noop
2074
2074
2075 $ log 'sort(0 + 2 + 1, "")'
2075 $ log 'sort(0 + 2 + 1, "")'
2076 0
2076 0
2077 2
2077 2
2078 1
2078 1
2079
2079
2080 test invalid sort keys
2080 test invalid sort keys
2081
2081
2082 $ log 'sort(all(), -invalid)'
2082 $ log 'sort(all(), -invalid)'
2083 hg: parse error: unknown sort key '-invalid'
2083 hg: parse error: unknown sort key '-invalid'
2084 [255]
2084 [255]
2085
2085
2086 $ cd ..
2086 $ cd ..
2087
2087
2088 test sorting by multiple keys including variable-length strings
2088 test sorting by multiple keys including variable-length strings
2089
2089
2090 $ hg init sorting
2090 $ hg init sorting
2091 $ cd sorting
2091 $ cd sorting
2092 $ cat <<EOF >> .hg/hgrc
2092 $ cat <<EOF >> .hg/hgrc
2093 > [ui]
2093 > [ui]
2094 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2094 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2095 > [templatealias]
2095 > [templatealias]
2096 > p5(s) = pad(s, 5)
2096 > p5(s) = pad(s, 5)
2097 > EOF
2097 > EOF
2098 $ hg branch -qf b12
2098 $ hg branch -qf b12
2099 $ hg ci -m m111 -u u112 -d '111 10800'
2099 $ hg ci -m m111 -u u112 -d '111 10800'
2100 $ hg branch -qf b11
2100 $ hg branch -qf b11
2101 $ hg ci -m m12 -u u111 -d '112 7200'
2101 $ hg ci -m m12 -u u111 -d '112 7200'
2102 $ hg branch -qf b111
2102 $ hg branch -qf b111
2103 $ hg ci -m m11 -u u12 -d '111 3600'
2103 $ hg ci -m m11 -u u12 -d '111 3600'
2104 $ hg branch -qf b112
2104 $ hg branch -qf b112
2105 $ hg ci -m m111 -u u11 -d '120 0'
2105 $ hg ci -m m111 -u u11 -d '120 0'
2106 $ hg branch -qf b111
2106 $ hg branch -qf b111
2107 $ hg ci -m m112 -u u111 -d '110 14400'
2107 $ hg ci -m m112 -u u111 -d '110 14400'
2108 created new head
2108 created new head
2109
2109
2110 compare revisions (has fast path):
2110 compare revisions (has fast path):
2111
2111
2112 $ hg log -r 'sort(all(), rev)'
2112 $ hg log -r 'sort(all(), rev)'
2113 0 b12 m111 u112 111 10800
2113 0 b12 m111 u112 111 10800
2114 1 b11 m12 u111 112 7200
2114 1 b11 m12 u111 112 7200
2115 2 b111 m11 u12 111 3600
2115 2 b111 m11 u12 111 3600
2116 3 b112 m111 u11 120 0
2116 3 b112 m111 u11 120 0
2117 4 b111 m112 u111 110 14400
2117 4 b111 m112 u111 110 14400
2118
2118
2119 $ hg log -r 'sort(all(), -rev)'
2119 $ hg log -r 'sort(all(), -rev)'
2120 4 b111 m112 u111 110 14400
2120 4 b111 m112 u111 110 14400
2121 3 b112 m111 u11 120 0
2121 3 b112 m111 u11 120 0
2122 2 b111 m11 u12 111 3600
2122 2 b111 m11 u12 111 3600
2123 1 b11 m12 u111 112 7200
2123 1 b11 m12 u111 112 7200
2124 0 b12 m111 u112 111 10800
2124 0 b12 m111 u112 111 10800
2125
2125
2126 compare variable-length strings (issue5218):
2126 compare variable-length strings (issue5218):
2127
2127
2128 $ hg log -r 'sort(all(), branch)'
2128 $ hg log -r 'sort(all(), branch)'
2129 1 b11 m12 u111 112 7200
2129 1 b11 m12 u111 112 7200
2130 2 b111 m11 u12 111 3600
2130 2 b111 m11 u12 111 3600
2131 4 b111 m112 u111 110 14400
2131 4 b111 m112 u111 110 14400
2132 3 b112 m111 u11 120 0
2132 3 b112 m111 u11 120 0
2133 0 b12 m111 u112 111 10800
2133 0 b12 m111 u112 111 10800
2134
2134
2135 $ hg log -r 'sort(all(), -branch)'
2135 $ hg log -r 'sort(all(), -branch)'
2136 0 b12 m111 u112 111 10800
2136 0 b12 m111 u112 111 10800
2137 3 b112 m111 u11 120 0
2137 3 b112 m111 u11 120 0
2138 2 b111 m11 u12 111 3600
2138 2 b111 m11 u12 111 3600
2139 4 b111 m112 u111 110 14400
2139 4 b111 m112 u111 110 14400
2140 1 b11 m12 u111 112 7200
2140 1 b11 m12 u111 112 7200
2141
2141
2142 $ hg log -r 'sort(all(), desc)'
2142 $ hg log -r 'sort(all(), desc)'
2143 2 b111 m11 u12 111 3600
2143 2 b111 m11 u12 111 3600
2144 0 b12 m111 u112 111 10800
2144 0 b12 m111 u112 111 10800
2145 3 b112 m111 u11 120 0
2145 3 b112 m111 u11 120 0
2146 4 b111 m112 u111 110 14400
2146 4 b111 m112 u111 110 14400
2147 1 b11 m12 u111 112 7200
2147 1 b11 m12 u111 112 7200
2148
2148
2149 $ hg log -r 'sort(all(), -desc)'
2149 $ hg log -r 'sort(all(), -desc)'
2150 1 b11 m12 u111 112 7200
2150 1 b11 m12 u111 112 7200
2151 4 b111 m112 u111 110 14400
2151 4 b111 m112 u111 110 14400
2152 0 b12 m111 u112 111 10800
2152 0 b12 m111 u112 111 10800
2153 3 b112 m111 u11 120 0
2153 3 b112 m111 u11 120 0
2154 2 b111 m11 u12 111 3600
2154 2 b111 m11 u12 111 3600
2155
2155
2156 $ hg log -r 'sort(all(), user)'
2156 $ hg log -r 'sort(all(), user)'
2157 3 b112 m111 u11 120 0
2157 3 b112 m111 u11 120 0
2158 1 b11 m12 u111 112 7200
2158 1 b11 m12 u111 112 7200
2159 4 b111 m112 u111 110 14400
2159 4 b111 m112 u111 110 14400
2160 0 b12 m111 u112 111 10800
2160 0 b12 m111 u112 111 10800
2161 2 b111 m11 u12 111 3600
2161 2 b111 m11 u12 111 3600
2162
2162
2163 $ hg log -r 'sort(all(), -user)'
2163 $ hg log -r 'sort(all(), -user)'
2164 2 b111 m11 u12 111 3600
2164 2 b111 m11 u12 111 3600
2165 0 b12 m111 u112 111 10800
2165 0 b12 m111 u112 111 10800
2166 1 b11 m12 u111 112 7200
2166 1 b11 m12 u111 112 7200
2167 4 b111 m112 u111 110 14400
2167 4 b111 m112 u111 110 14400
2168 3 b112 m111 u11 120 0
2168 3 b112 m111 u11 120 0
2169
2169
2170 compare dates (tz offset should have no effect):
2170 compare dates (tz offset should have no effect):
2171
2171
2172 $ hg log -r 'sort(all(), date)'
2172 $ hg log -r 'sort(all(), date)'
2173 4 b111 m112 u111 110 14400
2173 4 b111 m112 u111 110 14400
2174 0 b12 m111 u112 111 10800
2174 0 b12 m111 u112 111 10800
2175 2 b111 m11 u12 111 3600
2175 2 b111 m11 u12 111 3600
2176 1 b11 m12 u111 112 7200
2176 1 b11 m12 u111 112 7200
2177 3 b112 m111 u11 120 0
2177 3 b112 m111 u11 120 0
2178
2178
2179 $ hg log -r 'sort(all(), -date)'
2179 $ hg log -r 'sort(all(), -date)'
2180 3 b112 m111 u11 120 0
2180 3 b112 m111 u11 120 0
2181 1 b11 m12 u111 112 7200
2181 1 b11 m12 u111 112 7200
2182 0 b12 m111 u112 111 10800
2182 0 b12 m111 u112 111 10800
2183 2 b111 m11 u12 111 3600
2183 2 b111 m11 u12 111 3600
2184 4 b111 m112 u111 110 14400
2184 4 b111 m112 u111 110 14400
2185
2185
2186 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2186 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2187 because '-k' reverses the comparison, not the list itself:
2187 because '-k' reverses the comparison, not the list itself:
2188
2188
2189 $ hg log -r 'sort(0 + 2, date)'
2189 $ hg log -r 'sort(0 + 2, date)'
2190 0 b12 m111 u112 111 10800
2190 0 b12 m111 u112 111 10800
2191 2 b111 m11 u12 111 3600
2191 2 b111 m11 u12 111 3600
2192
2192
2193 $ hg log -r 'sort(0 + 2, -date)'
2193 $ hg log -r 'sort(0 + 2, -date)'
2194 0 b12 m111 u112 111 10800
2194 0 b12 m111 u112 111 10800
2195 2 b111 m11 u12 111 3600
2195 2 b111 m11 u12 111 3600
2196
2196
2197 $ hg log -r 'reverse(sort(0 + 2, date))'
2197 $ hg log -r 'reverse(sort(0 + 2, date))'
2198 2 b111 m11 u12 111 3600
2198 2 b111 m11 u12 111 3600
2199 0 b12 m111 u112 111 10800
2199 0 b12 m111 u112 111 10800
2200
2200
2201 sort by multiple keys:
2201 sort by multiple keys:
2202
2202
2203 $ hg log -r 'sort(all(), "branch -rev")'
2203 $ hg log -r 'sort(all(), "branch -rev")'
2204 1 b11 m12 u111 112 7200
2204 1 b11 m12 u111 112 7200
2205 4 b111 m112 u111 110 14400
2205 4 b111 m112 u111 110 14400
2206 2 b111 m11 u12 111 3600
2206 2 b111 m11 u12 111 3600
2207 3 b112 m111 u11 120 0
2207 3 b112 m111 u11 120 0
2208 0 b12 m111 u112 111 10800
2208 0 b12 m111 u112 111 10800
2209
2209
2210 $ hg log -r 'sort(all(), "-desc -date")'
2210 $ hg log -r 'sort(all(), "-desc -date")'
2211 1 b11 m12 u111 112 7200
2211 1 b11 m12 u111 112 7200
2212 4 b111 m112 u111 110 14400
2212 4 b111 m112 u111 110 14400
2213 3 b112 m111 u11 120 0
2213 3 b112 m111 u11 120 0
2214 0 b12 m111 u112 111 10800
2214 0 b12 m111 u112 111 10800
2215 2 b111 m11 u12 111 3600
2215 2 b111 m11 u12 111 3600
2216
2216
2217 $ hg log -r 'sort(all(), "user -branch date rev")'
2217 $ hg log -r 'sort(all(), "user -branch date rev")'
2218 3 b112 m111 u11 120 0
2218 3 b112 m111 u11 120 0
2219 4 b111 m112 u111 110 14400
2219 4 b111 m112 u111 110 14400
2220 1 b11 m12 u111 112 7200
2220 1 b11 m12 u111 112 7200
2221 0 b12 m111 u112 111 10800
2221 0 b12 m111 u112 111 10800
2222 2 b111 m11 u12 111 3600
2222 2 b111 m11 u12 111 3600
2223
2223
2224 toposort prioritises graph branches
2224 toposort prioritises graph branches
2225
2225
2226 $ hg up 2
2226 $ hg up 2
2227 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2227 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2228 $ touch a
2228 $ touch a
2229 $ hg addremove
2229 $ hg addremove
2230 adding a
2230 adding a
2231 $ hg ci -m 't1' -u 'tu' -d '130 0'
2231 $ hg ci -m 't1' -u 'tu' -d '130 0'
2232 created new head
2232 created new head
2233 $ echo 'a' >> a
2233 $ echo 'a' >> a
2234 $ hg ci -m 't2' -u 'tu' -d '130 0'
2234 $ hg ci -m 't2' -u 'tu' -d '130 0'
2235 $ hg book book1
2235 $ hg book book1
2236 $ hg up 4
2236 $ hg up 4
2237 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2237 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2238 (leaving bookmark book1)
2238 (leaving bookmark book1)
2239 $ touch a
2239 $ touch a
2240 $ hg addremove
2240 $ hg addremove
2241 adding a
2241 adding a
2242 $ hg ci -m 't3' -u 'tu' -d '130 0'
2242 $ hg ci -m 't3' -u 'tu' -d '130 0'
2243
2243
2244 $ hg log -r 'sort(all(), topo)'
2244 $ hg log -r 'sort(all(), topo)'
2245 7 b111 t3 tu 130 0
2245 7 b111 t3 tu 130 0
2246 4 b111 m112 u111 110 14400
2246 4 b111 m112 u111 110 14400
2247 3 b112 m111 u11 120 0
2247 3 b112 m111 u11 120 0
2248 6 b111 t2 tu 130 0
2248 6 b111 t2 tu 130 0
2249 5 b111 t1 tu 130 0
2249 5 b111 t1 tu 130 0
2250 2 b111 m11 u12 111 3600
2250 2 b111 m11 u12 111 3600
2251 1 b11 m12 u111 112 7200
2251 1 b11 m12 u111 112 7200
2252 0 b12 m111 u112 111 10800
2252 0 b12 m111 u112 111 10800
2253
2253
2254 $ hg log -r 'sort(all(), -topo)'
2254 $ hg log -r 'sort(all(), -topo)'
2255 0 b12 m111 u112 111 10800
2255 0 b12 m111 u112 111 10800
2256 1 b11 m12 u111 112 7200
2256 1 b11 m12 u111 112 7200
2257 2 b111 m11 u12 111 3600
2257 2 b111 m11 u12 111 3600
2258 5 b111 t1 tu 130 0
2258 5 b111 t1 tu 130 0
2259 6 b111 t2 tu 130 0
2259 6 b111 t2 tu 130 0
2260 3 b112 m111 u11 120 0
2260 3 b112 m111 u11 120 0
2261 4 b111 m112 u111 110 14400
2261 4 b111 m112 u111 110 14400
2262 7 b111 t3 tu 130 0
2262 7 b111 t3 tu 130 0
2263
2263
2264 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2264 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2265 6 b111 t2 tu 130 0
2265 6 b111 t2 tu 130 0
2266 5 b111 t1 tu 130 0
2266 5 b111 t1 tu 130 0
2267 7 b111 t3 tu 130 0
2267 7 b111 t3 tu 130 0
2268 4 b111 m112 u111 110 14400
2268 4 b111 m112 u111 110 14400
2269 3 b112 m111 u11 120 0
2269 3 b112 m111 u11 120 0
2270 2 b111 m11 u12 111 3600
2270 2 b111 m11 u12 111 3600
2271 1 b11 m12 u111 112 7200
2271 1 b11 m12 u111 112 7200
2272 0 b12 m111 u112 111 10800
2272 0 b12 m111 u112 111 10800
2273
2273
2274 topographical sorting can't be combined with other sort keys, and you can't
2274 topographical sorting can't be combined with other sort keys, and you can't
2275 use the topo.firstbranch option when topo sort is not active:
2275 use the topo.firstbranch option when topo sort is not active:
2276
2276
2277 $ hg log -r 'sort(all(), "topo user")'
2277 $ hg log -r 'sort(all(), "topo user")'
2278 hg: parse error: topo sort order cannot be combined with other sort keys
2278 hg: parse error: topo sort order cannot be combined with other sort keys
2279 [255]
2279 [255]
2280
2280
2281 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2281 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2282 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2282 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2283 [255]
2283 [255]
2284
2284
2285 topo.firstbranch should accept any kind of expressions:
2285 topo.firstbranch should accept any kind of expressions:
2286
2286
2287 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2287 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2288 0 b12 m111 u112 111 10800
2288 0 b12 m111 u112 111 10800
2289
2289
2290 $ cd ..
2290 $ cd ..
2291 $ cd repo
2291 $ cd repo
2292
2292
2293 test subtracting something from an addset
2293 test subtracting something from an addset
2294
2294
2295 $ log '(outgoing() or removes(a)) - removes(a)'
2295 $ log '(outgoing() or removes(a)) - removes(a)'
2296 8
2296 8
2297 9
2297 9
2298
2298
2299 test intersecting something with an addset
2299 test intersecting something with an addset
2300
2300
2301 $ log 'parents(outgoing() or removes(a))'
2301 $ log 'parents(outgoing() or removes(a))'
2302 1
2302 1
2303 4
2303 4
2304 5
2304 5
2305 8
2305 8
2306
2306
2307 test that `or` operation combines elements in the right order:
2307 test that `or` operation combines elements in the right order:
2308
2308
2309 $ log '3:4 or 2:5'
2309 $ log '3:4 or 2:5'
2310 3
2310 3
2311 4
2311 4
2312 2
2312 2
2313 5
2313 5
2314 $ log '3:4 or 5:2'
2314 $ log '3:4 or 5:2'
2315 3
2315 3
2316 4
2316 4
2317 5
2317 5
2318 2
2318 2
2319 $ log 'sort(3:4 or 2:5)'
2319 $ log 'sort(3:4 or 2:5)'
2320 2
2320 2
2321 3
2321 3
2322 4
2322 4
2323 5
2323 5
2324 $ log 'sort(3:4 or 5:2)'
2324 $ log 'sort(3:4 or 5:2)'
2325 2
2325 2
2326 3
2326 3
2327 4
2327 4
2328 5
2328 5
2329
2329
2330 test that more than one `-r`s are combined in the right order and deduplicated:
2330 test that more than one `-r`s are combined in the right order and deduplicated:
2331
2331
2332 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2332 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2333 3
2333 3
2334 4
2334 4
2335 5
2335 5
2336 2
2336 2
2337 0
2337 0
2338 1
2338 1
2339
2339
2340 test that `or` operation skips duplicated revisions from right-hand side
2340 test that `or` operation skips duplicated revisions from right-hand side
2341
2341
2342 $ try 'reverse(1::5) or ancestors(4)'
2342 $ try 'reverse(1::5) or ancestors(4)'
2343 (or
2343 (or
2344 (list
2344 (list
2345 (func
2345 (func
2346 ('symbol', 'reverse')
2346 ('symbol', 'reverse')
2347 (dagrange
2347 (dagrange
2348 ('symbol', '1')
2348 ('symbol', '1')
2349 ('symbol', '5')))
2349 ('symbol', '5')))
2350 (func
2350 (func
2351 ('symbol', 'ancestors')
2351 ('symbol', 'ancestors')
2352 ('symbol', '4'))))
2352 ('symbol', '4'))))
2353 * set:
2353 * set:
2354 <addset
2354 <addset
2355 <baseset- [1, 3, 5]>,
2355 <baseset- [1, 3, 5]>,
2356 <generatorset+>>
2356 <generatorset+>>
2357 5
2357 5
2358 3
2358 3
2359 1
2359 1
2360 0
2360 0
2361 2
2361 2
2362 4
2362 4
2363 $ try 'sort(ancestors(4) or reverse(1::5))'
2363 $ try 'sort(ancestors(4) or reverse(1::5))'
2364 (func
2364 (func
2365 ('symbol', 'sort')
2365 ('symbol', 'sort')
2366 (or
2366 (or
2367 (list
2367 (list
2368 (func
2368 (func
2369 ('symbol', 'ancestors')
2369 ('symbol', 'ancestors')
2370 ('symbol', '4'))
2370 ('symbol', '4'))
2371 (func
2371 (func
2372 ('symbol', 'reverse')
2372 ('symbol', 'reverse')
2373 (dagrange
2373 (dagrange
2374 ('symbol', '1')
2374 ('symbol', '1')
2375 ('symbol', '5'))))))
2375 ('symbol', '5'))))))
2376 * set:
2376 * set:
2377 <addset+
2377 <addset+
2378 <generatorset+>,
2378 <generatorset+>,
2379 <baseset- [1, 3, 5]>>
2379 <baseset- [1, 3, 5]>>
2380 0
2380 0
2381 1
2381 1
2382 2
2382 2
2383 3
2383 3
2384 4
2384 4
2385 5
2385 5
2386
2386
2387 test optimization of trivial `or` operation
2387 test optimization of trivial `or` operation
2388
2388
2389 $ try --optimize '0|(1)|"2"|-2|tip|null'
2389 $ try --optimize '0|(1)|"2"|-2|tip|null'
2390 (or
2390 (or
2391 (list
2391 (list
2392 ('symbol', '0')
2392 ('symbol', '0')
2393 (group
2393 (group
2394 ('symbol', '1'))
2394 ('symbol', '1'))
2395 ('string', '2')
2395 ('string', '2')
2396 (negate
2396 (negate
2397 ('symbol', '2'))
2397 ('symbol', '2'))
2398 ('symbol', 'tip')
2398 ('symbol', 'tip')
2399 ('symbol', 'null')))
2399 ('symbol', 'null')))
2400 * optimized:
2400 * optimized:
2401 (func
2401 (func
2402 ('symbol', '_list')
2402 ('symbol', '_list')
2403 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2403 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2404 define)
2404 define)
2405 * set:
2405 * set:
2406 <baseset [0, 1, 2, 8, 9, -1]>
2406 <baseset [0, 1, 2, 8, 9, -1]>
2407 0
2407 0
2408 1
2408 1
2409 2
2409 2
2410 8
2410 8
2411 9
2411 9
2412 -1
2412 -1
2413
2413
2414 $ try --optimize '0|1|2:3'
2414 $ try --optimize '0|1|2:3'
2415 (or
2415 (or
2416 (list
2416 (list
2417 ('symbol', '0')
2417 ('symbol', '0')
2418 ('symbol', '1')
2418 ('symbol', '1')
2419 (range
2419 (range
2420 ('symbol', '2')
2420 ('symbol', '2')
2421 ('symbol', '3'))))
2421 ('symbol', '3'))))
2422 * optimized:
2422 * optimized:
2423 (or
2423 (or
2424 (list
2424 (list
2425 (func
2425 (func
2426 ('symbol', '_list')
2426 ('symbol', '_list')
2427 ('string', '0\x001')
2427 ('string', '0\x001')
2428 define)
2428 define)
2429 (range
2429 (range
2430 ('symbol', '2')
2430 ('symbol', '2')
2431 ('symbol', '3')
2431 ('symbol', '3')
2432 define))
2432 define))
2433 define)
2433 define)
2434 * set:
2434 * set:
2435 <addset
2435 <addset
2436 <baseset [0, 1]>,
2436 <baseset [0, 1]>,
2437 <spanset+ 2:3>>
2437 <spanset+ 2:3>>
2438 0
2438 0
2439 1
2439 1
2440 2
2440 2
2441 3
2441 3
2442
2442
2443 $ try --optimize '0:1|2|3:4|5|6'
2443 $ try --optimize '0:1|2|3:4|5|6'
2444 (or
2444 (or
2445 (list
2445 (list
2446 (range
2446 (range
2447 ('symbol', '0')
2447 ('symbol', '0')
2448 ('symbol', '1'))
2448 ('symbol', '1'))
2449 ('symbol', '2')
2449 ('symbol', '2')
2450 (range
2450 (range
2451 ('symbol', '3')
2451 ('symbol', '3')
2452 ('symbol', '4'))
2452 ('symbol', '4'))
2453 ('symbol', '5')
2453 ('symbol', '5')
2454 ('symbol', '6')))
2454 ('symbol', '6')))
2455 * optimized:
2455 * optimized:
2456 (or
2456 (or
2457 (list
2457 (list
2458 (range
2458 (range
2459 ('symbol', '0')
2459 ('symbol', '0')
2460 ('symbol', '1')
2460 ('symbol', '1')
2461 define)
2461 define)
2462 ('symbol', '2')
2462 ('symbol', '2')
2463 (range
2463 (range
2464 ('symbol', '3')
2464 ('symbol', '3')
2465 ('symbol', '4')
2465 ('symbol', '4')
2466 define)
2466 define)
2467 (func
2467 (func
2468 ('symbol', '_list')
2468 ('symbol', '_list')
2469 ('string', '5\x006')
2469 ('string', '5\x006')
2470 define))
2470 define))
2471 define)
2471 define)
2472 * set:
2472 * set:
2473 <addset
2473 <addset
2474 <addset
2474 <addset
2475 <spanset+ 0:1>,
2475 <spanset+ 0:1>,
2476 <baseset [2]>>,
2476 <baseset [2]>>,
2477 <addset
2477 <addset
2478 <spanset+ 3:4>,
2478 <spanset+ 3:4>,
2479 <baseset [5, 6]>>>
2479 <baseset [5, 6]>>>
2480 0
2480 0
2481 1
2481 1
2482 2
2482 2
2483 3
2483 3
2484 4
2484 4
2485 5
2485 5
2486 6
2486 6
2487
2487
2488 unoptimized `or` looks like this
2488 unoptimized `or` looks like this
2489
2489
2490 $ try --no-optimized -p analyzed '0|1|2|3|4'
2490 $ try --no-optimized -p analyzed '0|1|2|3|4'
2491 * analyzed:
2491 * analyzed:
2492 (or
2492 (or
2493 (list
2493 (list
2494 ('symbol', '0')
2494 ('symbol', '0')
2495 ('symbol', '1')
2495 ('symbol', '1')
2496 ('symbol', '2')
2496 ('symbol', '2')
2497 ('symbol', '3')
2497 ('symbol', '3')
2498 ('symbol', '4'))
2498 ('symbol', '4'))
2499 define)
2499 define)
2500 * set:
2500 * set:
2501 <addset
2501 <addset
2502 <addset
2502 <addset
2503 <baseset [0]>,
2503 <baseset [0]>,
2504 <baseset [1]>>,
2504 <baseset [1]>>,
2505 <addset
2505 <addset
2506 <baseset [2]>,
2506 <baseset [2]>,
2507 <addset
2507 <addset
2508 <baseset [3]>,
2508 <baseset [3]>,
2509 <baseset [4]>>>>
2509 <baseset [4]>>>>
2510 0
2510 0
2511 1
2511 1
2512 2
2512 2
2513 3
2513 3
2514 4
2514 4
2515
2515
2516 test that `_list` should be narrowed by provided `subset`
2516 test that `_list` should be narrowed by provided `subset`
2517
2517
2518 $ log '0:2 and (null|1|2|3)'
2518 $ log '0:2 and (null|1|2|3)'
2519 1
2519 1
2520 2
2520 2
2521
2521
2522 test that `_list` should remove duplicates
2522 test that `_list` should remove duplicates
2523
2523
2524 $ log '0|1|2|1|2|-1|tip'
2524 $ log '0|1|2|1|2|-1|tip'
2525 0
2525 0
2526 1
2526 1
2527 2
2527 2
2528 9
2528 9
2529
2529
2530 test unknown revision in `_list`
2530 test unknown revision in `_list`
2531
2531
2532 $ log '0|unknown'
2532 $ log '0|unknown'
2533 abort: unknown revision 'unknown'!
2533 abort: unknown revision 'unknown'!
2534 [255]
2534 [255]
2535
2535
2536 test integer range in `_list`
2536 test integer range in `_list`
2537
2537
2538 $ log '-1|-10'
2538 $ log '-1|-10'
2539 9
2539 9
2540 0
2540 0
2541
2541
2542 $ log '-10|-11'
2542 $ log '-10|-11'
2543 abort: unknown revision '-11'!
2543 abort: unknown revision '-11'!
2544 [255]
2544 [255]
2545
2545
2546 $ log '9|10'
2546 $ log '9|10'
2547 abort: unknown revision '10'!
2547 abort: unknown revision '10'!
2548 [255]
2548 [255]
2549
2549
2550 test '0000' != '0' in `_list`
2550 test '0000' != '0' in `_list`
2551
2551
2552 $ log '0|0000'
2552 $ log '0|0000'
2553 0
2553 0
2554 -1
2554 -1
2555
2555
2556 test ',' in `_list`
2556 test ',' in `_list`
2557 $ log '0,1'
2557 $ log '0,1'
2558 hg: parse error: can't use a list in this context
2558 hg: parse error: can't use a list in this context
2559 (see hg help "revsets.x or y")
2559 (see hg help "revsets.x or y")
2560 [255]
2560 [255]
2561 $ try '0,1,2'
2561 $ try '0,1,2'
2562 (list
2562 (list
2563 ('symbol', '0')
2563 ('symbol', '0')
2564 ('symbol', '1')
2564 ('symbol', '1')
2565 ('symbol', '2'))
2565 ('symbol', '2'))
2566 hg: parse error: can't use a list in this context
2566 hg: parse error: can't use a list in this context
2567 (see hg help "revsets.x or y")
2567 (see hg help "revsets.x or y")
2568 [255]
2568 [255]
2569
2569
2570 test that chained `or` operations make balanced addsets
2570 test that chained `or` operations make balanced addsets
2571
2571
2572 $ try '0:1|1:2|2:3|3:4|4:5'
2572 $ try '0:1|1:2|2:3|3:4|4:5'
2573 (or
2573 (or
2574 (list
2574 (list
2575 (range
2575 (range
2576 ('symbol', '0')
2576 ('symbol', '0')
2577 ('symbol', '1'))
2577 ('symbol', '1'))
2578 (range
2578 (range
2579 ('symbol', '1')
2579 ('symbol', '1')
2580 ('symbol', '2'))
2580 ('symbol', '2'))
2581 (range
2581 (range
2582 ('symbol', '2')
2582 ('symbol', '2')
2583 ('symbol', '3'))
2583 ('symbol', '3'))
2584 (range
2584 (range
2585 ('symbol', '3')
2585 ('symbol', '3')
2586 ('symbol', '4'))
2586 ('symbol', '4'))
2587 (range
2587 (range
2588 ('symbol', '4')
2588 ('symbol', '4')
2589 ('symbol', '5'))))
2589 ('symbol', '5'))))
2590 * set:
2590 * set:
2591 <addset
2591 <addset
2592 <addset
2592 <addset
2593 <spanset+ 0:1>,
2593 <spanset+ 0:1>,
2594 <spanset+ 1:2>>,
2594 <spanset+ 1:2>>,
2595 <addset
2595 <addset
2596 <spanset+ 2:3>,
2596 <spanset+ 2:3>,
2597 <addset
2597 <addset
2598 <spanset+ 3:4>,
2598 <spanset+ 3:4>,
2599 <spanset+ 4:5>>>>
2599 <spanset+ 4:5>>>>
2600 0
2600 0
2601 1
2601 1
2602 2
2602 2
2603 3
2603 3
2604 4
2604 4
2605 5
2605 5
2606
2606
2607 no crash by empty group "()" while optimizing `or` operations
2607 no crash by empty group "()" while optimizing `or` operations
2608
2608
2609 $ try --optimize '0|()'
2609 $ try --optimize '0|()'
2610 (or
2610 (or
2611 (list
2611 (list
2612 ('symbol', '0')
2612 ('symbol', '0')
2613 (group
2613 (group
2614 None)))
2614 None)))
2615 * optimized:
2615 * optimized:
2616 (or
2616 (or
2617 (list
2617 (list
2618 ('symbol', '0')
2618 ('symbol', '0')
2619 None)
2619 None)
2620 define)
2620 define)
2621 hg: parse error: missing argument
2621 hg: parse error: missing argument
2622 [255]
2622 [255]
2623
2623
2624 test that chained `or` operations never eat up stack (issue4624)
2624 test that chained `or` operations never eat up stack (issue4624)
2625 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2625 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2626
2626
2627 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2627 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2628 0
2628 0
2629 1
2629 1
2630
2630
2631 test that repeated `-r` options never eat up stack (issue4565)
2631 test that repeated `-r` options never eat up stack (issue4565)
2632 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2632 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2633
2633
2634 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2634 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2635 0
2635 0
2636 1
2636 1
2637
2637
2638 check that conversion to only works
2638 check that conversion to only works
2639 $ try --optimize '::3 - ::1'
2639 $ try --optimize '::3 - ::1'
2640 (minus
2640 (minus
2641 (dagrangepre
2641 (dagrangepre
2642 ('symbol', '3'))
2642 ('symbol', '3'))
2643 (dagrangepre
2643 (dagrangepre
2644 ('symbol', '1')))
2644 ('symbol', '1')))
2645 * optimized:
2645 * optimized:
2646 (func
2646 (func
2647 ('symbol', 'only')
2647 ('symbol', 'only')
2648 (list
2648 (list
2649 ('symbol', '3')
2649 ('symbol', '3')
2650 ('symbol', '1'))
2650 ('symbol', '1'))
2651 define)
2651 define)
2652 * set:
2652 * set:
2653 <baseset+ [3]>
2653 <baseset+ [3]>
2654 3
2654 3
2655 $ try --optimize 'ancestors(1) - ancestors(3)'
2655 $ try --optimize 'ancestors(1) - ancestors(3)'
2656 (minus
2656 (minus
2657 (func
2657 (func
2658 ('symbol', 'ancestors')
2658 ('symbol', 'ancestors')
2659 ('symbol', '1'))
2659 ('symbol', '1'))
2660 (func
2660 (func
2661 ('symbol', 'ancestors')
2661 ('symbol', 'ancestors')
2662 ('symbol', '3')))
2662 ('symbol', '3')))
2663 * optimized:
2663 * optimized:
2664 (func
2664 (func
2665 ('symbol', 'only')
2665 ('symbol', 'only')
2666 (list
2666 (list
2667 ('symbol', '1')
2667 ('symbol', '1')
2668 ('symbol', '3'))
2668 ('symbol', '3'))
2669 define)
2669 define)
2670 * set:
2670 * set:
2671 <baseset+ []>
2671 <baseset+ []>
2672 $ try --optimize 'not ::2 and ::6'
2672 $ try --optimize 'not ::2 and ::6'
2673 (and
2673 (and
2674 (not
2674 (not
2675 (dagrangepre
2675 (dagrangepre
2676 ('symbol', '2')))
2676 ('symbol', '2')))
2677 (dagrangepre
2677 (dagrangepre
2678 ('symbol', '6')))
2678 ('symbol', '6')))
2679 * optimized:
2679 * optimized:
2680 (func
2680 (func
2681 ('symbol', 'only')
2681 ('symbol', 'only')
2682 (list
2682 (list
2683 ('symbol', '6')
2683 ('symbol', '6')
2684 ('symbol', '2'))
2684 ('symbol', '2'))
2685 define)
2685 define)
2686 * set:
2686 * set:
2687 <baseset+ [3, 4, 5, 6]>
2687 <baseset+ [3, 4, 5, 6]>
2688 3
2688 3
2689 4
2689 4
2690 5
2690 5
2691 6
2691 6
2692 $ try --optimize 'ancestors(6) and not ancestors(4)'
2692 $ try --optimize 'ancestors(6) and not ancestors(4)'
2693 (and
2693 (and
2694 (func
2694 (func
2695 ('symbol', 'ancestors')
2695 ('symbol', 'ancestors')
2696 ('symbol', '6'))
2696 ('symbol', '6'))
2697 (not
2697 (not
2698 (func
2698 (func
2699 ('symbol', 'ancestors')
2699 ('symbol', 'ancestors')
2700 ('symbol', '4'))))
2700 ('symbol', '4'))))
2701 * optimized:
2701 * optimized:
2702 (func
2702 (func
2703 ('symbol', 'only')
2703 ('symbol', 'only')
2704 (list
2704 (list
2705 ('symbol', '6')
2705 ('symbol', '6')
2706 ('symbol', '4'))
2706 ('symbol', '4'))
2707 define)
2707 define)
2708 * set:
2708 * set:
2709 <baseset+ [3, 5, 6]>
2709 <baseset+ [3, 5, 6]>
2710 3
2710 3
2711 5
2711 5
2712 6
2712 6
2713
2713
2714 no crash by empty group "()" while optimizing to "only()"
2714 no crash by empty group "()" while optimizing to "only()"
2715
2715
2716 $ try --optimize '::1 and ()'
2716 $ try --optimize '::1 and ()'
2717 (and
2717 (and
2718 (dagrangepre
2718 (dagrangepre
2719 ('symbol', '1'))
2719 ('symbol', '1'))
2720 (group
2720 (group
2721 None))
2721 None))
2722 * optimized:
2722 * optimized:
2723 (and
2723 (and
2724 None
2724 None
2725 (func
2725 (func
2726 ('symbol', 'ancestors')
2726 ('symbol', 'ancestors')
2727 ('symbol', '1')
2727 ('symbol', '1')
2728 define)
2728 define)
2729 define)
2729 define)
2730 hg: parse error: missing argument
2730 hg: parse error: missing argument
2731 [255]
2731 [255]
2732
2732
2733 invalid function call should not be optimized to only()
2733 invalid function call should not be optimized to only()
2734
2734
2735 $ log '"ancestors"(6) and not ancestors(4)'
2735 $ log '"ancestors"(6) and not ancestors(4)'
2736 hg: parse error: not a symbol
2736 hg: parse error: not a symbol
2737 [255]
2737 [255]
2738
2738
2739 $ log 'ancestors(6) and not "ancestors"(4)'
2739 $ log 'ancestors(6) and not "ancestors"(4)'
2740 hg: parse error: not a symbol
2740 hg: parse error: not a symbol
2741 [255]
2741 [255]
2742
2742
2743 we can use patterns when searching for tags
2743 we can use patterns when searching for tags
2744
2744
2745 $ log 'tag("1..*")'
2745 $ log 'tag("1..*")'
2746 abort: tag '1..*' does not exist!
2746 abort: tag '1..*' does not exist!
2747 [255]
2747 [255]
2748 $ log 'tag("re:1..*")'
2748 $ log 'tag("re:1..*")'
2749 6
2749 6
2750 $ log 'tag("re:[0-9].[0-9]")'
2750 $ log 'tag("re:[0-9].[0-9]")'
2751 6
2751 6
2752 $ log 'tag("literal:1.0")'
2752 $ log 'tag("literal:1.0")'
2753 6
2753 6
2754 $ log 'tag("re:0..*")'
2754 $ log 'tag("re:0..*")'
2755
2755
2756 $ log 'tag(unknown)'
2756 $ log 'tag(unknown)'
2757 abort: tag 'unknown' does not exist!
2757 abort: tag 'unknown' does not exist!
2758 [255]
2758 [255]
2759 $ log 'tag("re:unknown")'
2759 $ log 'tag("re:unknown")'
2760 $ log 'present(tag("unknown"))'
2760 $ log 'present(tag("unknown"))'
2761 $ log 'present(tag("re:unknown"))'
2761 $ log 'present(tag("re:unknown"))'
2762 $ log 'branch(unknown)'
2762 $ log 'branch(unknown)'
2763 abort: unknown revision 'unknown'!
2763 abort: unknown revision 'unknown'!
2764 [255]
2764 [255]
2765 $ log 'branch("literal:unknown")'
2765 $ log 'branch("literal:unknown")'
2766 abort: branch 'unknown' does not exist!
2766 abort: branch 'unknown' does not exist!
2767 [255]
2767 [255]
2768 $ log 'branch("re:unknown")'
2768 $ log 'branch("re:unknown")'
2769 $ log 'present(branch("unknown"))'
2769 $ log 'present(branch("unknown"))'
2770 $ log 'present(branch("re:unknown"))'
2770 $ log 'present(branch("re:unknown"))'
2771 $ log 'user(bob)'
2771 $ log 'user(bob)'
2772 2
2772 2
2773
2773
2774 $ log '4::8'
2774 $ log '4::8'
2775 4
2775 4
2776 8
2776 8
2777 $ log '4:8'
2777 $ log '4:8'
2778 4
2778 4
2779 5
2779 5
2780 6
2780 6
2781 7
2781 7
2782 8
2782 8
2783
2783
2784 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2784 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2785 4
2785 4
2786 2
2786 2
2787 5
2787 5
2788
2788
2789 $ log 'not 0 and 0:2'
2789 $ log 'not 0 and 0:2'
2790 1
2790 1
2791 2
2791 2
2792 $ log 'not 1 and 0:2'
2792 $ log 'not 1 and 0:2'
2793 0
2793 0
2794 2
2794 2
2795 $ log 'not 2 and 0:2'
2795 $ log 'not 2 and 0:2'
2796 0
2796 0
2797 1
2797 1
2798 $ log '(1 and 2)::'
2798 $ log '(1 and 2)::'
2799 $ log '(1 and 2):'
2799 $ log '(1 and 2):'
2800 $ log '(1 and 2):3'
2800 $ log '(1 and 2):3'
2801 $ log 'sort(head(), -rev)'
2801 $ log 'sort(head(), -rev)'
2802 9
2802 9
2803 7
2803 7
2804 6
2804 6
2805 5
2805 5
2806 4
2806 4
2807 3
2807 3
2808 2
2808 2
2809 1
2809 1
2810 0
2810 0
2811 $ log '4::8 - 8'
2811 $ log '4::8 - 8'
2812 4
2812 4
2813
2813
2814 matching() should preserve the order of the input set:
2814 matching() should preserve the order of the input set:
2815
2815
2816 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2816 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2817 2
2817 2
2818 3
2818 3
2819 1
2819 1
2820
2820
2821 $ log 'named("unknown")'
2821 $ log 'named("unknown")'
2822 abort: namespace 'unknown' does not exist!
2822 abort: namespace 'unknown' does not exist!
2823 [255]
2823 [255]
2824 $ log 'named("re:unknown")'
2824 $ log 'named("re:unknown")'
2825 abort: no namespace exists that match 'unknown'!
2825 abort: no namespace exists that match 'unknown'!
2826 [255]
2826 [255]
2827 $ log 'present(named("unknown"))'
2827 $ log 'present(named("unknown"))'
2828 $ log 'present(named("re:unknown"))'
2828 $ log 'present(named("re:unknown"))'
2829
2829
2830 $ log 'tag()'
2830 $ log 'tag()'
2831 6
2831 6
2832 $ log 'named("tags")'
2832 $ log 'named("tags")'
2833 6
2833 6
2834
2834
2835 issue2437
2835 issue2437
2836
2836
2837 $ log '3 and p1(5)'
2837 $ log '3 and p1(5)'
2838 3
2838 3
2839 $ log '4 and p2(6)'
2839 $ log '4 and p2(6)'
2840 4
2840 4
2841 $ log '1 and parents(:2)'
2841 $ log '1 and parents(:2)'
2842 1
2842 1
2843 $ log '2 and children(1:)'
2843 $ log '2 and children(1:)'
2844 2
2844 2
2845 $ log 'roots(all()) or roots(all())'
2845 $ log 'roots(all()) or roots(all())'
2846 0
2846 0
2847 $ hg debugrevspec 'roots(all()) or roots(all())'
2847 $ hg debugrevspec 'roots(all()) or roots(all())'
2848 0
2848 0
2849 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2849 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2850 9
2850 9
2851 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2851 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2852 4
2852 4
2853
2853
2854 issue2654: report a parse error if the revset was not completely parsed
2854 issue2654: report a parse error if the revset was not completely parsed
2855
2855
2856 $ log '1 OR 2'
2856 $ log '1 OR 2'
2857 hg: parse error at 2: invalid token
2857 hg: parse error at 2: invalid token
2858 [255]
2858 [255]
2859
2859
2860 or operator should preserve ordering:
2860 or operator should preserve ordering:
2861 $ log 'reverse(2::4) or tip'
2861 $ log 'reverse(2::4) or tip'
2862 4
2862 4
2863 2
2863 2
2864 9
2864 9
2865
2865
2866 parentrevspec
2866 parentrevspec
2867
2867
2868 $ log 'merge()^0'
2868 $ log 'merge()^0'
2869 6
2869 6
2870 $ log 'merge()^'
2870 $ log 'merge()^'
2871 5
2871 5
2872 $ log 'merge()^1'
2872 $ log 'merge()^1'
2873 5
2873 5
2874 $ log 'merge()^2'
2874 $ log 'merge()^2'
2875 4
2875 4
2876 $ log '(not merge())^2'
2876 $ log '(not merge())^2'
2877 $ log 'merge()^^'
2877 $ log 'merge()^^'
2878 3
2878 3
2879 $ log 'merge()^1^'
2879 $ log 'merge()^1^'
2880 3
2880 3
2881 $ log 'merge()^^^'
2881 $ log 'merge()^^^'
2882 1
2882 1
2883
2883
2884 $ log 'merge()~0'
2884 $ log 'merge()~0'
2885 6
2885 6
2886 $ log 'merge()~1'
2886 $ log 'merge()~1'
2887 5
2887 5
2888 $ log 'merge()~2'
2888 $ log 'merge()~2'
2889 3
2889 3
2890 $ log 'merge()~2^1'
2890 $ log 'merge()~2^1'
2891 1
2891 1
2892 $ log 'merge()~3'
2892 $ log 'merge()~3'
2893 1
2893 1
2894
2894
2895 $ log '(-3:tip)^'
2895 $ log '(-3:tip)^'
2896 4
2896 4
2897 6
2897 6
2898 8
2898 8
2899
2899
2900 $ log 'tip^foo'
2900 $ log 'tip^foo'
2901 hg: parse error: ^ expects a number 0, 1, or 2
2901 hg: parse error: ^ expects a number 0, 1, or 2
2902 [255]
2902 [255]
2903
2903
2904 Bogus function gets suggestions
2904 Bogus function gets suggestions
2905 $ log 'add()'
2905 $ log 'add()'
2906 hg: parse error: unknown identifier: add
2906 hg: parse error: unknown identifier: add
2907 (did you mean adds?)
2907 (did you mean adds?)
2908 [255]
2908 [255]
2909 $ log 'added()'
2909 $ log 'added()'
2910 hg: parse error: unknown identifier: added
2910 hg: parse error: unknown identifier: added
2911 (did you mean adds?)
2911 (did you mean adds?)
2912 [255]
2912 [255]
2913 $ log 'remo()'
2913 $ log 'remo()'
2914 hg: parse error: unknown identifier: remo
2914 hg: parse error: unknown identifier: remo
2915 (did you mean one of remote, removes?)
2915 (did you mean one of remote, removes?)
2916 [255]
2916 [255]
2917 $ log 'babar()'
2917 $ log 'babar()'
2918 hg: parse error: unknown identifier: babar
2918 hg: parse error: unknown identifier: babar
2919 [255]
2919 [255]
2920
2920
2921 Bogus function with a similar internal name doesn't suggest the internal name
2921 Bogus function with a similar internal name doesn't suggest the internal name
2922 $ log 'matches()'
2922 $ log 'matches()'
2923 hg: parse error: unknown identifier: matches
2923 hg: parse error: unknown identifier: matches
2924 (did you mean matching?)
2924 (did you mean matching?)
2925 [255]
2925 [255]
2926
2926
2927 Undocumented functions aren't suggested as similar either
2927 Undocumented functions aren't suggested as similar either
2928 $ log 'tagged2()'
2928 $ log 'tagged2()'
2929 hg: parse error: unknown identifier: tagged2
2929 hg: parse error: unknown identifier: tagged2
2930 [255]
2930 [255]
2931
2931
2932 multiple revspecs
2932 multiple revspecs
2933
2933
2934 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2934 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2935 8
2935 8
2936 9
2936 9
2937 4
2937 4
2938 5
2938 5
2939 6
2939 6
2940 7
2940 7
2941
2941
2942 test usage in revpair (with "+")
2942 test usage in revpair (with "+")
2943
2943
2944 (real pair)
2944 (real pair)
2945
2945
2946 $ hg diff -r 'tip^^' -r 'tip'
2946 $ hg diff -r 'tip^^' -r 'tip'
2947 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2947 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2948 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2948 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2949 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2949 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2950 @@ -0,0 +1,1 @@
2950 @@ -0,0 +1,1 @@
2951 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2951 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2952 $ hg diff -r 'tip^^::tip'
2952 $ hg diff -r 'tip^^::tip'
2953 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2953 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2954 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2954 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2955 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2955 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2956 @@ -0,0 +1,1 @@
2956 @@ -0,0 +1,1 @@
2957 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2957 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2958
2958
2959 (single rev)
2959 (single rev)
2960
2960
2961 $ hg diff -r 'tip^' -r 'tip^'
2961 $ hg diff -r 'tip^' -r 'tip^'
2962 $ hg diff -r 'tip^:tip^'
2962 $ hg diff -r 'tip^:tip^'
2963
2963
2964 (single rev that does not looks like a range)
2964 (single rev that does not looks like a range)
2965
2965
2966 $ hg diff -r 'tip^::tip^ or tip^'
2966 $ hg diff -r 'tip^::tip^ or tip^'
2967 diff -r d5d0dcbdc4d9 .hgtags
2967 diff -r d5d0dcbdc4d9 .hgtags
2968 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2968 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2969 +++ b/.hgtags * (glob)
2969 +++ b/.hgtags * (glob)
2970 @@ -0,0 +1,1 @@
2970 @@ -0,0 +1,1 @@
2971 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2971 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2972 $ hg diff -r 'tip^ or tip^'
2972 $ hg diff -r 'tip^ or tip^'
2973 diff -r d5d0dcbdc4d9 .hgtags
2973 diff -r d5d0dcbdc4d9 .hgtags
2974 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2974 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2975 +++ b/.hgtags * (glob)
2975 +++ b/.hgtags * (glob)
2976 @@ -0,0 +1,1 @@
2976 @@ -0,0 +1,1 @@
2977 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2977 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2978
2978
2979 (no rev)
2979 (no rev)
2980
2980
2981 $ hg diff -r 'author("babar") or author("celeste")'
2981 $ hg diff -r 'author("babar") or author("celeste")'
2982 abort: empty revision range
2982 abort: empty revision range
2983 [255]
2983 [255]
2984
2984
2985 aliases:
2985 aliases:
2986
2986
2987 $ echo '[revsetalias]' >> .hg/hgrc
2987 $ echo '[revsetalias]' >> .hg/hgrc
2988 $ echo 'm = merge()' >> .hg/hgrc
2988 $ echo 'm = merge()' >> .hg/hgrc
2989 (revset aliases can override builtin revsets)
2989 (revset aliases can override builtin revsets)
2990 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2990 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2991 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2991 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2992 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2992 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2993 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2993 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2994 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2994 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2995
2995
2996 $ try m
2996 $ try m
2997 ('symbol', 'm')
2997 ('symbol', 'm')
2998 * expanded:
2998 * expanded:
2999 (func
2999 (func
3000 ('symbol', 'merge')
3000 ('symbol', 'merge')
3001 None)
3001 None)
3002 * set:
3002 * set:
3003 <filteredset
3003 <filteredset
3004 <fullreposet+ 0:9>,
3004 <fullreposet+ 0:9>,
3005 <merge>>
3005 <merge>>
3006 6
3006 6
3007
3007
3008 $ HGPLAIN=1
3008 $ HGPLAIN=1
3009 $ export HGPLAIN
3009 $ export HGPLAIN
3010 $ try m
3010 $ try m
3011 ('symbol', 'm')
3011 ('symbol', 'm')
3012 abort: unknown revision 'm'!
3012 abort: unknown revision 'm'!
3013 [255]
3013 [255]
3014
3014
3015 $ HGPLAINEXCEPT=revsetalias
3015 $ HGPLAINEXCEPT=revsetalias
3016 $ export HGPLAINEXCEPT
3016 $ export HGPLAINEXCEPT
3017 $ try m
3017 $ try m
3018 ('symbol', 'm')
3018 ('symbol', 'm')
3019 * expanded:
3019 * expanded:
3020 (func
3020 (func
3021 ('symbol', 'merge')
3021 ('symbol', 'merge')
3022 None)
3022 None)
3023 * set:
3023 * set:
3024 <filteredset
3024 <filteredset
3025 <fullreposet+ 0:9>,
3025 <fullreposet+ 0:9>,
3026 <merge>>
3026 <merge>>
3027 6
3027 6
3028
3028
3029 $ unset HGPLAIN
3029 $ unset HGPLAIN
3030 $ unset HGPLAINEXCEPT
3030 $ unset HGPLAINEXCEPT
3031
3031
3032 $ try 'p2(.)'
3032 $ try 'p2(.)'
3033 (func
3033 (func
3034 ('symbol', 'p2')
3034 ('symbol', 'p2')
3035 ('symbol', '.'))
3035 ('symbol', '.'))
3036 * expanded:
3036 * expanded:
3037 (func
3037 (func
3038 ('symbol', 'p1')
3038 ('symbol', 'p1')
3039 ('symbol', '.'))
3039 ('symbol', '.'))
3040 * set:
3040 * set:
3041 <baseset+ [8]>
3041 <baseset+ [8]>
3042 8
3042 8
3043
3043
3044 $ HGPLAIN=1
3044 $ HGPLAIN=1
3045 $ export HGPLAIN
3045 $ export HGPLAIN
3046 $ try 'p2(.)'
3046 $ try 'p2(.)'
3047 (func
3047 (func
3048 ('symbol', 'p2')
3048 ('symbol', 'p2')
3049 ('symbol', '.'))
3049 ('symbol', '.'))
3050 * set:
3050 * set:
3051 <baseset+ []>
3051 <baseset+ []>
3052
3052
3053 $ HGPLAINEXCEPT=revsetalias
3053 $ HGPLAINEXCEPT=revsetalias
3054 $ export HGPLAINEXCEPT
3054 $ export HGPLAINEXCEPT
3055 $ try 'p2(.)'
3055 $ try 'p2(.)'
3056 (func
3056 (func
3057 ('symbol', 'p2')
3057 ('symbol', 'p2')
3058 ('symbol', '.'))
3058 ('symbol', '.'))
3059 * expanded:
3059 * expanded:
3060 (func
3060 (func
3061 ('symbol', 'p1')
3061 ('symbol', 'p1')
3062 ('symbol', '.'))
3062 ('symbol', '.'))
3063 * set:
3063 * set:
3064 <baseset+ [8]>
3064 <baseset+ [8]>
3065 8
3065 8
3066
3066
3067 $ unset HGPLAIN
3067 $ unset HGPLAIN
3068 $ unset HGPLAINEXCEPT
3068 $ unset HGPLAINEXCEPT
3069
3069
3070 test alias recursion
3070 test alias recursion
3071
3071
3072 $ try sincem
3072 $ try sincem
3073 ('symbol', 'sincem')
3073 ('symbol', 'sincem')
3074 * expanded:
3074 * expanded:
3075 (func
3075 (func
3076 ('symbol', 'descendants')
3076 ('symbol', 'descendants')
3077 (func
3077 (func
3078 ('symbol', 'merge')
3078 ('symbol', 'merge')
3079 None))
3079 None))
3080 * set:
3080 * set:
3081 <addset+
3081 <addset+
3082 <filteredset
3082 <filteredset
3083 <fullreposet+ 0:9>,
3083 <fullreposet+ 0:9>,
3084 <merge>>,
3084 <merge>>,
3085 <generatorset+>>
3085 <generatorset+>>
3086 6
3086 6
3087 7
3087 7
3088
3088
3089 test infinite recursion
3089 test infinite recursion
3090
3090
3091 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3091 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3092 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3092 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3093 $ try recurse1
3093 $ try recurse1
3094 ('symbol', 'recurse1')
3094 ('symbol', 'recurse1')
3095 hg: parse error: infinite expansion of revset alias "recurse1" detected
3095 hg: parse error: infinite expansion of revset alias "recurse1" detected
3096 [255]
3096 [255]
3097
3097
3098 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3098 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3099 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3099 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3100 $ try "level2(level1(1, 2), 3)"
3100 $ try "level2(level1(1, 2), 3)"
3101 (func
3101 (func
3102 ('symbol', 'level2')
3102 ('symbol', 'level2')
3103 (list
3103 (list
3104 (func
3104 (func
3105 ('symbol', 'level1')
3105 ('symbol', 'level1')
3106 (list
3106 (list
3107 ('symbol', '1')
3107 ('symbol', '1')
3108 ('symbol', '2')))
3108 ('symbol', '2')))
3109 ('symbol', '3')))
3109 ('symbol', '3')))
3110 * expanded:
3110 * expanded:
3111 (or
3111 (or
3112 (list
3112 (list
3113 ('symbol', '3')
3113 ('symbol', '3')
3114 (or
3114 (or
3115 (list
3115 (list
3116 ('symbol', '1')
3116 ('symbol', '1')
3117 ('symbol', '2')))))
3117 ('symbol', '2')))))
3118 * set:
3118 * set:
3119 <addset
3119 <addset
3120 <baseset [3]>,
3120 <baseset [3]>,
3121 <baseset [1, 2]>>
3121 <baseset [1, 2]>>
3122 3
3122 3
3123 1
3123 1
3124 2
3124 2
3125
3125
3126 test nesting and variable passing
3126 test nesting and variable passing
3127
3127
3128 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3128 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3129 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3129 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3130 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3130 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3131 $ try 'nested(2:5)'
3131 $ try 'nested(2:5)'
3132 (func
3132 (func
3133 ('symbol', 'nested')
3133 ('symbol', 'nested')
3134 (range
3134 (range
3135 ('symbol', '2')
3135 ('symbol', '2')
3136 ('symbol', '5')))
3136 ('symbol', '5')))
3137 * expanded:
3137 * expanded:
3138 (func
3138 (func
3139 ('symbol', 'max')
3139 ('symbol', 'max')
3140 (range
3140 (range
3141 ('symbol', '2')
3141 ('symbol', '2')
3142 ('symbol', '5')))
3142 ('symbol', '5')))
3143 * set:
3143 * set:
3144 <baseset
3144 <baseset
3145 <max
3145 <max
3146 <fullreposet+ 0:9>,
3146 <fullreposet+ 0:9>,
3147 <spanset+ 2:5>>>
3147 <spanset+ 2:5>>>
3148 5
3148 5
3149
3149
3150 test chained `or` operations are flattened at parsing phase
3150 test chained `or` operations are flattened at parsing phase
3151
3151
3152 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3152 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3153 $ try 'chainedorops(0:1, 1:2, 2:3)'
3153 $ try 'chainedorops(0:1, 1:2, 2:3)'
3154 (func
3154 (func
3155 ('symbol', 'chainedorops')
3155 ('symbol', 'chainedorops')
3156 (list
3156 (list
3157 (range
3157 (range
3158 ('symbol', '0')
3158 ('symbol', '0')
3159 ('symbol', '1'))
3159 ('symbol', '1'))
3160 (range
3160 (range
3161 ('symbol', '1')
3161 ('symbol', '1')
3162 ('symbol', '2'))
3162 ('symbol', '2'))
3163 (range
3163 (range
3164 ('symbol', '2')
3164 ('symbol', '2')
3165 ('symbol', '3'))))
3165 ('symbol', '3'))))
3166 * expanded:
3166 * expanded:
3167 (or
3167 (or
3168 (list
3168 (list
3169 (range
3169 (range
3170 ('symbol', '0')
3170 ('symbol', '0')
3171 ('symbol', '1'))
3171 ('symbol', '1'))
3172 (range
3172 (range
3173 ('symbol', '1')
3173 ('symbol', '1')
3174 ('symbol', '2'))
3174 ('symbol', '2'))
3175 (range
3175 (range
3176 ('symbol', '2')
3176 ('symbol', '2')
3177 ('symbol', '3'))))
3177 ('symbol', '3'))))
3178 * set:
3178 * set:
3179 <addset
3179 <addset
3180 <spanset+ 0:1>,
3180 <spanset+ 0:1>,
3181 <addset
3181 <addset
3182 <spanset+ 1:2>,
3182 <spanset+ 1:2>,
3183 <spanset+ 2:3>>>
3183 <spanset+ 2:3>>>
3184 0
3184 0
3185 1
3185 1
3186 2
3186 2
3187 3
3187 3
3188
3188
3189 test variable isolation, variable placeholders are rewritten as string
3189 test variable isolation, variable placeholders are rewritten as string
3190 then parsed and matched again as string. Check they do not leak too
3190 then parsed and matched again as string. Check they do not leak too
3191 far away.
3191 far away.
3192
3192
3193 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3193 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3194 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3194 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3195 $ try 'callinjection(2:5)'
3195 $ try 'callinjection(2:5)'
3196 (func
3196 (func
3197 ('symbol', 'callinjection')
3197 ('symbol', 'callinjection')
3198 (range
3198 (range
3199 ('symbol', '2')
3199 ('symbol', '2')
3200 ('symbol', '5')))
3200 ('symbol', '5')))
3201 * expanded:
3201 * expanded:
3202 (func
3202 (func
3203 ('symbol', 'descendants')
3203 ('symbol', 'descendants')
3204 (func
3204 (func
3205 ('symbol', 'max')
3205 ('symbol', 'max')
3206 ('string', '$1')))
3206 ('string', '$1')))
3207 abort: unknown revision '$1'!
3207 abort: unknown revision '$1'!
3208 [255]
3208 [255]
3209
3209
3210 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3210 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3211 but 'all()' should never be substituted to '0()'.
3211 but 'all()' should never be substituted to '0()'.
3212
3212
3213 $ echo 'universe = all()' >> .hg/hgrc
3213 $ echo 'universe = all()' >> .hg/hgrc
3214 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3214 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3215 $ try 'shadowall(0)'
3215 $ try 'shadowall(0)'
3216 (func
3216 (func
3217 ('symbol', 'shadowall')
3217 ('symbol', 'shadowall')
3218 ('symbol', '0'))
3218 ('symbol', '0'))
3219 * expanded:
3219 * expanded:
3220 (and
3220 (and
3221 ('symbol', '0')
3221 ('symbol', '0')
3222 (func
3222 (func
3223 ('symbol', 'all')
3223 ('symbol', 'all')
3224 None))
3224 None))
3225 * set:
3225 * set:
3226 <filteredset
3226 <filteredset
3227 <baseset [0]>,
3227 <baseset [0]>,
3228 <spanset+ 0:9>>
3228 <spanset+ 0:9>>
3229 0
3229 0
3230
3230
3231 test unknown reference:
3231 test unknown reference:
3232
3232
3233 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3233 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3234 (func
3234 (func
3235 ('symbol', 'unknownref')
3235 ('symbol', 'unknownref')
3236 ('symbol', '0'))
3236 ('symbol', '0'))
3237 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3237 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3238 [255]
3238 [255]
3239
3239
3240 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3240 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3241 ('symbol', 'tip')
3241 ('symbol', 'tip')
3242 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3242 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3243 * set:
3243 * set:
3244 <baseset [9]>
3244 <baseset [9]>
3245 9
3245 9
3246
3246
3247 $ try 'tip'
3247 $ try 'tip'
3248 ('symbol', 'tip')
3248 ('symbol', 'tip')
3249 * set:
3249 * set:
3250 <baseset [9]>
3250 <baseset [9]>
3251 9
3251 9
3252
3252
3253 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3253 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3254 ('symbol', 'tip')
3254 ('symbol', 'tip')
3255 warning: bad declaration of revset alias "bad name": at 4: invalid token
3255 warning: bad declaration of revset alias "bad name": at 4: invalid token
3256 * set:
3256 * set:
3257 <baseset [9]>
3257 <baseset [9]>
3258 9
3258 9
3259 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3259 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3260 $ try 'strictreplacing("foo", tip)'
3260 $ try 'strictreplacing("foo", tip)'
3261 (func
3261 (func
3262 ('symbol', 'strictreplacing')
3262 ('symbol', 'strictreplacing')
3263 (list
3263 (list
3264 ('string', 'foo')
3264 ('string', 'foo')
3265 ('symbol', 'tip')))
3265 ('symbol', 'tip')))
3266 * expanded:
3266 * expanded:
3267 (or
3267 (or
3268 (list
3268 (list
3269 ('symbol', 'tip')
3269 ('symbol', 'tip')
3270 (func
3270 (func
3271 ('symbol', 'desc')
3271 ('symbol', 'desc')
3272 ('string', '$1'))))
3272 ('string', '$1'))))
3273 * set:
3273 * set:
3274 <addset
3274 <addset
3275 <baseset [9]>,
3275 <baseset [9]>,
3276 <filteredset
3276 <filteredset
3277 <fullreposet+ 0:9>,
3277 <fullreposet+ 0:9>,
3278 <desc '$1'>>>
3278 <desc '$1'>>>
3279 9
3279 9
3280
3280
3281 $ try 'd(2:5)'
3281 $ try 'd(2:5)'
3282 (func
3282 (func
3283 ('symbol', 'd')
3283 ('symbol', 'd')
3284 (range
3284 (range
3285 ('symbol', '2')
3285 ('symbol', '2')
3286 ('symbol', '5')))
3286 ('symbol', '5')))
3287 * expanded:
3287 * expanded:
3288 (func
3288 (func
3289 ('symbol', 'reverse')
3289 ('symbol', 'reverse')
3290 (func
3290 (func
3291 ('symbol', 'sort')
3291 ('symbol', 'sort')
3292 (list
3292 (list
3293 (range
3293 (range
3294 ('symbol', '2')
3294 ('symbol', '2')
3295 ('symbol', '5'))
3295 ('symbol', '5'))
3296 ('symbol', 'date'))))
3296 ('symbol', 'date'))))
3297 * set:
3297 * set:
3298 <baseset [4, 5, 3, 2]>
3298 <baseset [4, 5, 3, 2]>
3299 4
3299 4
3300 5
3300 5
3301 3
3301 3
3302 2
3302 2
3303 $ try 'rs(2 or 3, date)'
3303 $ try 'rs(2 or 3, date)'
3304 (func
3304 (func
3305 ('symbol', 'rs')
3305 ('symbol', 'rs')
3306 (list
3306 (list
3307 (or
3307 (or
3308 (list
3308 (list
3309 ('symbol', '2')
3309 ('symbol', '2')
3310 ('symbol', '3')))
3310 ('symbol', '3')))
3311 ('symbol', 'date')))
3311 ('symbol', 'date')))
3312 * expanded:
3312 * expanded:
3313 (func
3313 (func
3314 ('symbol', 'reverse')
3314 ('symbol', 'reverse')
3315 (func
3315 (func
3316 ('symbol', 'sort')
3316 ('symbol', 'sort')
3317 (list
3317 (list
3318 (or
3318 (or
3319 (list
3319 (list
3320 ('symbol', '2')
3320 ('symbol', '2')
3321 ('symbol', '3')))
3321 ('symbol', '3')))
3322 ('symbol', 'date'))))
3322 ('symbol', 'date'))))
3323 * set:
3323 * set:
3324 <baseset [3, 2]>
3324 <baseset [3, 2]>
3325 3
3325 3
3326 2
3326 2
3327 $ try 'rs()'
3327 $ try 'rs()'
3328 (func
3328 (func
3329 ('symbol', 'rs')
3329 ('symbol', 'rs')
3330 None)
3330 None)
3331 hg: parse error: invalid number of arguments: 0
3331 hg: parse error: invalid number of arguments: 0
3332 [255]
3332 [255]
3333 $ try 'rs(2)'
3333 $ try 'rs(2)'
3334 (func
3334 (func
3335 ('symbol', 'rs')
3335 ('symbol', 'rs')
3336 ('symbol', '2'))
3336 ('symbol', '2'))
3337 hg: parse error: invalid number of arguments: 1
3337 hg: parse error: invalid number of arguments: 1
3338 [255]
3338 [255]
3339 $ try 'rs(2, data, 7)'
3339 $ try 'rs(2, data, 7)'
3340 (func
3340 (func
3341 ('symbol', 'rs')
3341 ('symbol', 'rs')
3342 (list
3342 (list
3343 ('symbol', '2')
3343 ('symbol', '2')
3344 ('symbol', 'data')
3344 ('symbol', 'data')
3345 ('symbol', '7')))
3345 ('symbol', '7')))
3346 hg: parse error: invalid number of arguments: 3
3346 hg: parse error: invalid number of arguments: 3
3347 [255]
3347 [255]
3348 $ try 'rs4(2 or 3, x, x, date)'
3348 $ try 'rs4(2 or 3, x, x, date)'
3349 (func
3349 (func
3350 ('symbol', 'rs4')
3350 ('symbol', 'rs4')
3351 (list
3351 (list
3352 (or
3352 (or
3353 (list
3353 (list
3354 ('symbol', '2')
3354 ('symbol', '2')
3355 ('symbol', '3')))
3355 ('symbol', '3')))
3356 ('symbol', 'x')
3356 ('symbol', 'x')
3357 ('symbol', 'x')
3357 ('symbol', 'x')
3358 ('symbol', 'date')))
3358 ('symbol', 'date')))
3359 * expanded:
3359 * expanded:
3360 (func
3360 (func
3361 ('symbol', 'reverse')
3361 ('symbol', 'reverse')
3362 (func
3362 (func
3363 ('symbol', 'sort')
3363 ('symbol', 'sort')
3364 (list
3364 (list
3365 (or
3365 (or
3366 (list
3366 (list
3367 ('symbol', '2')
3367 ('symbol', '2')
3368 ('symbol', '3')))
3368 ('symbol', '3')))
3369 ('symbol', 'date'))))
3369 ('symbol', 'date'))))
3370 * set:
3370 * set:
3371 <baseset [3, 2]>
3371 <baseset [3, 2]>
3372 3
3372 3
3373 2
3373 2
3374
3374
3375 issue4553: check that revset aliases override existing hash prefix
3375 issue4553: check that revset aliases override existing hash prefix
3376
3376
3377 $ hg log -qr e
3377 $ hg log -qr e
3378 6:e0cc66ef77e8
3378 6:e0cc66ef77e8
3379
3379
3380 $ hg log -qr e --config revsetalias.e="all()"
3380 $ hg log -qr e --config revsetalias.e="all()"
3381 0:2785f51eece5
3381 0:2785f51eece5
3382 1:d75937da8da0
3382 1:d75937da8da0
3383 2:5ed5505e9f1c
3383 2:5ed5505e9f1c
3384 3:8528aa5637f2
3384 3:8528aa5637f2
3385 4:2326846efdab
3385 4:2326846efdab
3386 5:904fa392b941
3386 5:904fa392b941
3387 6:e0cc66ef77e8
3387 6:e0cc66ef77e8
3388 7:013af1973af4
3388 7:013af1973af4
3389 8:d5d0dcbdc4d9
3389 8:d5d0dcbdc4d9
3390 9:24286f4ae135
3390 9:24286f4ae135
3391
3391
3392 $ hg log -qr e: --config revsetalias.e="0"
3392 $ hg log -qr e: --config revsetalias.e="0"
3393 0:2785f51eece5
3393 0:2785f51eece5
3394 1:d75937da8da0
3394 1:d75937da8da0
3395 2:5ed5505e9f1c
3395 2:5ed5505e9f1c
3396 3:8528aa5637f2
3396 3:8528aa5637f2
3397 4:2326846efdab
3397 4:2326846efdab
3398 5:904fa392b941
3398 5:904fa392b941
3399 6:e0cc66ef77e8
3399 6:e0cc66ef77e8
3400 7:013af1973af4
3400 7:013af1973af4
3401 8:d5d0dcbdc4d9
3401 8:d5d0dcbdc4d9
3402 9:24286f4ae135
3402 9:24286f4ae135
3403
3403
3404 $ hg log -qr :e --config revsetalias.e="9"
3404 $ hg log -qr :e --config revsetalias.e="9"
3405 0:2785f51eece5
3405 0:2785f51eece5
3406 1:d75937da8da0
3406 1:d75937da8da0
3407 2:5ed5505e9f1c
3407 2:5ed5505e9f1c
3408 3:8528aa5637f2
3408 3:8528aa5637f2
3409 4:2326846efdab
3409 4:2326846efdab
3410 5:904fa392b941
3410 5:904fa392b941
3411 6:e0cc66ef77e8
3411 6:e0cc66ef77e8
3412 7:013af1973af4
3412 7:013af1973af4
3413 8:d5d0dcbdc4d9
3413 8:d5d0dcbdc4d9
3414 9:24286f4ae135
3414 9:24286f4ae135
3415
3415
3416 $ hg log -qr e:
3416 $ hg log -qr e:
3417 6:e0cc66ef77e8
3417 6:e0cc66ef77e8
3418 7:013af1973af4
3418 7:013af1973af4
3419 8:d5d0dcbdc4d9
3419 8:d5d0dcbdc4d9
3420 9:24286f4ae135
3420 9:24286f4ae135
3421
3421
3422 $ hg log -qr :e
3422 $ hg log -qr :e
3423 0:2785f51eece5
3423 0:2785f51eece5
3424 1:d75937da8da0
3424 1:d75937da8da0
3425 2:5ed5505e9f1c
3425 2:5ed5505e9f1c
3426 3:8528aa5637f2
3426 3:8528aa5637f2
3427 4:2326846efdab
3427 4:2326846efdab
3428 5:904fa392b941
3428 5:904fa392b941
3429 6:e0cc66ef77e8
3429 6:e0cc66ef77e8
3430
3430
3431 issue2549 - correct optimizations
3431 issue2549 - correct optimizations
3432
3432
3433 $ try 'limit(1 or 2 or 3, 2) and not 2'
3433 $ try 'limit(1 or 2 or 3, 2) and not 2'
3434 (and
3434 (and
3435 (func
3435 (func
3436 ('symbol', 'limit')
3436 ('symbol', 'limit')
3437 (list
3437 (list
3438 (or
3438 (or
3439 (list
3439 (list
3440 ('symbol', '1')
3440 ('symbol', '1')
3441 ('symbol', '2')
3441 ('symbol', '2')
3442 ('symbol', '3')))
3442 ('symbol', '3')))
3443 ('symbol', '2')))
3443 ('symbol', '2')))
3444 (not
3444 (not
3445 ('symbol', '2')))
3445 ('symbol', '2')))
3446 * set:
3446 * set:
3447 <filteredset
3447 <filteredset
3448 <baseset
3448 <baseset
3449 <limit n=2, offset=0,
3449 <limit n=2, offset=0,
3450 <fullreposet+ 0:9>,
3450 <fullreposet+ 0:9>,
3451 <baseset [1, 2, 3]>>>,
3451 <baseset [1, 2, 3]>>>,
3452 <not
3452 <not
3453 <baseset [2]>>>
3453 <baseset [2]>>>
3454 1
3454 1
3455 $ try 'max(1 or 2) and not 2'
3455 $ try 'max(1 or 2) and not 2'
3456 (and
3456 (and
3457 (func
3457 (func
3458 ('symbol', 'max')
3458 ('symbol', 'max')
3459 (or
3459 (or
3460 (list
3460 (list
3461 ('symbol', '1')
3461 ('symbol', '1')
3462 ('symbol', '2'))))
3462 ('symbol', '2'))))
3463 (not
3463 (not
3464 ('symbol', '2')))
3464 ('symbol', '2')))
3465 * set:
3465 * set:
3466 <filteredset
3466 <filteredset
3467 <baseset
3467 <baseset
3468 <max
3468 <max
3469 <fullreposet+ 0:9>,
3469 <fullreposet+ 0:9>,
3470 <baseset [1, 2]>>>,
3470 <baseset [1, 2]>>>,
3471 <not
3471 <not
3472 <baseset [2]>>>
3472 <baseset [2]>>>
3473 $ try 'min(1 or 2) and not 1'
3473 $ try 'min(1 or 2) and not 1'
3474 (and
3474 (and
3475 (func
3475 (func
3476 ('symbol', 'min')
3476 ('symbol', 'min')
3477 (or
3477 (or
3478 (list
3478 (list
3479 ('symbol', '1')
3479 ('symbol', '1')
3480 ('symbol', '2'))))
3480 ('symbol', '2'))))
3481 (not
3481 (not
3482 ('symbol', '1')))
3482 ('symbol', '1')))
3483 * set:
3483 * set:
3484 <filteredset
3484 <filteredset
3485 <baseset
3485 <baseset
3486 <min
3486 <min
3487 <fullreposet+ 0:9>,
3487 <fullreposet+ 0:9>,
3488 <baseset [1, 2]>>>,
3488 <baseset [1, 2]>>>,
3489 <not
3489 <not
3490 <baseset [1]>>>
3490 <baseset [1]>>>
3491 $ try 'last(1 or 2, 1) and not 2'
3491 $ try 'last(1 or 2, 1) and not 2'
3492 (and
3492 (and
3493 (func
3493 (func
3494 ('symbol', 'last')
3494 ('symbol', 'last')
3495 (list
3495 (list
3496 (or
3496 (or
3497 (list
3497 (list
3498 ('symbol', '1')
3498 ('symbol', '1')
3499 ('symbol', '2')))
3499 ('symbol', '2')))
3500 ('symbol', '1')))
3500 ('symbol', '1')))
3501 (not
3501 (not
3502 ('symbol', '2')))
3502 ('symbol', '2')))
3503 * set:
3503 * set:
3504 <filteredset
3504 <filteredset
3505 <baseset
3505 <baseset
3506 <last n=1,
3506 <last n=1,
3507 <fullreposet+ 0:9>,
3507 <fullreposet+ 0:9>,
3508 <baseset [2, 1]>>>,
3508 <baseset [2, 1]>>>,
3509 <not
3509 <not
3510 <baseset [2]>>>
3510 <baseset [2]>>>
3511
3511
3512 issue4289 - ordering of built-ins
3512 issue4289 - ordering of built-ins
3513 $ hg log -M -q -r 3:2
3513 $ hg log -M -q -r 3:2
3514 3:8528aa5637f2
3514 3:8528aa5637f2
3515 2:5ed5505e9f1c
3515 2:5ed5505e9f1c
3516
3516
3517 test revsets started with 40-chars hash (issue3669)
3517 test revsets started with 40-chars hash (issue3669)
3518
3518
3519 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3519 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3520 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3520 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3521 9
3521 9
3522 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3522 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3523 8
3523 8
3524
3524
3525 test or-ed indirect predicates (issue3775)
3525 test or-ed indirect predicates (issue3775)
3526
3526
3527 $ log '6 or 6^1' | sort
3527 $ log '6 or 6^1' | sort
3528 5
3528 5
3529 6
3529 6
3530 $ log '6^1 or 6' | sort
3530 $ log '6^1 or 6' | sort
3531 5
3531 5
3532 6
3532 6
3533 $ log '4 or 4~1' | sort
3533 $ log '4 or 4~1' | sort
3534 2
3534 2
3535 4
3535 4
3536 $ log '4~1 or 4' | sort
3536 $ log '4~1 or 4' | sort
3537 2
3537 2
3538 4
3538 4
3539 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3539 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3540 0
3540 0
3541 1
3541 1
3542 2
3542 2
3543 3
3543 3
3544 4
3544 4
3545 5
3545 5
3546 6
3546 6
3547 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3547 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3548 0
3548 0
3549 1
3549 1
3550 2
3550 2
3551 3
3551 3
3552 4
3552 4
3553 5
3553 5
3554 6
3554 6
3555
3555
3556 tests for 'remote()' predicate:
3556 tests for 'remote()' predicate:
3557 #. (csets in remote) (id) (remote)
3557 #. (csets in remote) (id) (remote)
3558 1. less than local current branch "default"
3558 1. less than local current branch "default"
3559 2. same with local specified "default"
3559 2. same with local specified "default"
3560 3. more than local specified specified
3560 3. more than local specified specified
3561
3561
3562 $ hg clone --quiet -U . ../remote3
3562 $ hg clone --quiet -U . ../remote3
3563 $ cd ../remote3
3563 $ cd ../remote3
3564 $ hg update -q 7
3564 $ hg update -q 7
3565 $ echo r > r
3565 $ echo r > r
3566 $ hg ci -Aqm 10
3566 $ hg ci -Aqm 10
3567 $ log 'remote()'
3567 $ log 'remote()'
3568 7
3568 7
3569 $ log 'remote("a-b-c-")'
3569 $ log 'remote("a-b-c-")'
3570 2
3570 2
3571 $ cd ../repo
3571 $ cd ../repo
3572 $ log 'remote(".a.b.c.", "../remote3")'
3572 $ log 'remote(".a.b.c.", "../remote3")'
3573
3573
3574 tests for concatenation of strings/symbols by "##"
3574 tests for concatenation of strings/symbols by "##"
3575
3575
3576 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3576 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3577 (_concat
3577 (_concat
3578 (_concat
3578 (_concat
3579 (_concat
3579 (_concat
3580 ('symbol', '278')
3580 ('symbol', '278')
3581 ('string', '5f5'))
3581 ('string', '5f5'))
3582 ('symbol', '1ee'))
3582 ('symbol', '1ee'))
3583 ('string', 'ce5'))
3583 ('string', 'ce5'))
3584 * concatenated:
3584 * concatenated:
3585 ('string', '2785f51eece5')
3585 ('string', '2785f51eece5')
3586 * set:
3586 * set:
3587 <baseset [0]>
3587 <baseset [0]>
3588 0
3588 0
3589
3589
3590 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3590 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3591 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3591 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3592 (func
3592 (func
3593 ('symbol', 'cat4')
3593 ('symbol', 'cat4')
3594 (list
3594 (list
3595 ('symbol', '278')
3595 ('symbol', '278')
3596 ('string', '5f5')
3596 ('string', '5f5')
3597 ('symbol', '1ee')
3597 ('symbol', '1ee')
3598 ('string', 'ce5')))
3598 ('string', 'ce5')))
3599 * expanded:
3599 * expanded:
3600 (_concat
3600 (_concat
3601 (_concat
3601 (_concat
3602 (_concat
3602 (_concat
3603 ('symbol', '278')
3603 ('symbol', '278')
3604 ('string', '5f5'))
3604 ('string', '5f5'))
3605 ('symbol', '1ee'))
3605 ('symbol', '1ee'))
3606 ('string', 'ce5'))
3606 ('string', 'ce5'))
3607 * concatenated:
3607 * concatenated:
3608 ('string', '2785f51eece5')
3608 ('string', '2785f51eece5')
3609 * set:
3609 * set:
3610 <baseset [0]>
3610 <baseset [0]>
3611 0
3611 0
3612
3612
3613 (check concatenation in alias nesting)
3613 (check concatenation in alias nesting)
3614
3614
3615 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3615 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3616 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3616 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3617 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3617 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3618 0
3618 0
3619
3619
3620 (check operator priority)
3620 (check operator priority)
3621
3621
3622 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3622 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3623 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3623 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3624 0
3624 0
3625 4
3625 4
3626
3626
3627 $ cd ..
3627 $ cd ..
3628
3628
3629 prepare repository that has "default" branches of multiple roots
3629 prepare repository that has "default" branches of multiple roots
3630
3630
3631 $ hg init namedbranch
3631 $ hg init namedbranch
3632 $ cd namedbranch
3632 $ cd namedbranch
3633
3633
3634 $ echo default0 >> a
3634 $ echo default0 >> a
3635 $ hg ci -Aqm0
3635 $ hg ci -Aqm0
3636 $ echo default1 >> a
3636 $ echo default1 >> a
3637 $ hg ci -m1
3637 $ hg ci -m1
3638
3638
3639 $ hg branch -q stable
3639 $ hg branch -q stable
3640 $ echo stable2 >> a
3640 $ echo stable2 >> a
3641 $ hg ci -m2
3641 $ hg ci -m2
3642 $ echo stable3 >> a
3642 $ echo stable3 >> a
3643 $ hg ci -m3
3643 $ hg ci -m3
3644
3644
3645 $ hg update -q null
3645 $ hg update -q null
3646 $ echo default4 >> a
3646 $ echo default4 >> a
3647 $ hg ci -Aqm4
3647 $ hg ci -Aqm4
3648 $ echo default5 >> a
3648 $ echo default5 >> a
3649 $ hg ci -m5
3649 $ hg ci -m5
3650
3650
3651 "null" revision belongs to "default" branch (issue4683)
3651 "null" revision belongs to "default" branch (issue4683)
3652
3652
3653 $ log 'branch(null)'
3653 $ log 'branch(null)'
3654 0
3654 0
3655 1
3655 1
3656 4
3656 4
3657 5
3657 5
3658
3658
3659 "null" revision belongs to "default" branch, but it shouldn't appear in set
3659 "null" revision belongs to "default" branch, but it shouldn't appear in set
3660 unless explicitly specified (issue4682)
3660 unless explicitly specified (issue4682)
3661
3661
3662 $ log 'children(branch(default))'
3662 $ log 'children(branch(default))'
3663 1
3663 1
3664 2
3664 2
3665 5
3665 5
3666
3666
3667 $ cd ..
3667 $ cd ..
3668
3668
3669 test author/desc/keyword in problematic encoding
3669 test author/desc/keyword in problematic encoding
3670 # unicode: cp932:
3670 # unicode: cp932:
3671 # u30A2 0x83 0x41(= 'A')
3671 # u30A2 0x83 0x41(= 'A')
3672 # u30C2 0x83 0x61(= 'a')
3672 # u30C2 0x83 0x61(= 'a')
3673
3673
3674 $ hg init problematicencoding
3674 $ hg init problematicencoding
3675 $ cd problematicencoding
3675 $ cd problematicencoding
3676
3676
3677 $ python > setup.sh <<EOF
3677 $ python > setup.sh <<EOF
3678 > print u'''
3678 > print u'''
3679 > echo a > text
3679 > echo a > text
3680 > hg add text
3680 > hg add text
3681 > hg --encoding utf-8 commit -u '\u30A2' -m none
3681 > hg --encoding utf-8 commit -u '\u30A2' -m none
3682 > echo b > text
3682 > echo b > text
3683 > hg --encoding utf-8 commit -u '\u30C2' -m none
3683 > hg --encoding utf-8 commit -u '\u30C2' -m none
3684 > echo c > text
3684 > echo c > text
3685 > hg --encoding utf-8 commit -u none -m '\u30A2'
3685 > hg --encoding utf-8 commit -u none -m '\u30A2'
3686 > echo d > text
3686 > echo d > text
3687 > hg --encoding utf-8 commit -u none -m '\u30C2'
3687 > hg --encoding utf-8 commit -u none -m '\u30C2'
3688 > '''.encode('utf-8')
3688 > '''.encode('utf-8')
3689 > EOF
3689 > EOF
3690 $ sh < setup.sh
3690 $ sh < setup.sh
3691
3691
3692 test in problematic encoding
3692 test in problematic encoding
3693 $ python > test.sh <<EOF
3693 $ python > test.sh <<EOF
3694 > print u'''
3694 > print u'''
3695 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3695 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3696 > echo ====
3696 > echo ====
3697 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3697 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3698 > echo ====
3698 > echo ====
3699 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3699 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3700 > echo ====
3700 > echo ====
3701 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3701 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3702 > echo ====
3702 > echo ====
3703 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3703 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3704 > echo ====
3704 > echo ====
3705 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3705 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3706 > '''.encode('cp932')
3706 > '''.encode('cp932')
3707 > EOF
3707 > EOF
3708 $ sh < test.sh
3708 $ sh < test.sh
3709 0
3709 0
3710 ====
3710 ====
3711 1
3711 1
3712 ====
3712 ====
3713 2
3713 2
3714 ====
3714 ====
3715 3
3715 3
3716 ====
3716 ====
3717 0
3717 0
3718 2
3718 2
3719 ====
3719 ====
3720 1
3720 1
3721 3
3721 3
3722
3722
3723 test error message of bad revset
3723 test error message of bad revset
3724 $ hg log -r 'foo\\'
3724 $ hg log -r 'foo\\'
3725 hg: parse error at 3: syntax error in revset 'foo\\'
3725 hg: parse error at 3: syntax error in revset 'foo\\'
3726 [255]
3726 [255]
3727
3727
3728 $ cd ..
3728 $ cd ..
3729
3729
3730 Test that revset predicate of extension isn't loaded at failure of
3730 Test that revset predicate of extension isn't loaded at failure of
3731 loading it
3731 loading it
3732
3732
3733 $ cd repo
3733 $ cd repo
3734
3734
3735 $ cat <<EOF > $TESTTMP/custompredicate.py
3735 $ cat <<EOF > $TESTTMP/custompredicate.py
3736 > from mercurial import error, registrar, revset
3736 > from mercurial import error, registrar, revset
3737 >
3737 >
3738 > revsetpredicate = registrar.revsetpredicate()
3738 > revsetpredicate = registrar.revsetpredicate()
3739 >
3739 >
3740 > @revsetpredicate('custom1()')
3740 > @revsetpredicate('custom1()')
3741 > def custom1(repo, subset, x):
3741 > def custom1(repo, subset, x):
3742 > return revset.baseset([1])
3742 > return revset.baseset([1])
3743 >
3743 >
3744 > raise error.Abort('intentional failure of loading extension')
3744 > raise error.Abort('intentional failure of loading extension')
3745 > EOF
3745 > EOF
3746 $ cat <<EOF > .hg/hgrc
3746 $ cat <<EOF > .hg/hgrc
3747 > [extensions]
3747 > [extensions]
3748 > custompredicate = $TESTTMP/custompredicate.py
3748 > custompredicate = $TESTTMP/custompredicate.py
3749 > EOF
3749 > EOF
3750
3750
3751 $ hg debugrevspec "custom1()"
3751 $ hg debugrevspec "custom1()"
3752 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3752 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3753 hg: parse error: unknown identifier: custom1
3753 hg: parse error: unknown identifier: custom1
3754 [255]
3754 [255]
3755
3755
3756 $ cd ..
3756 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now