##// END OF EJS Templates
hg log: Move filtering implicit parents to own method and use it in templater....
Thomas Arendsen Hein -
r4825:3cf94964 default
parent child Browse files
Show More
@@ -1,1272 +1,1278 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
11 import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
11 import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
12 import fancyopts, revlog, version, extensions, hook
12 import fancyopts, revlog, version, extensions, hook
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20 class ParseError(Exception):
20 class ParseError(Exception):
21 """Exception raised on errors in parsing the command line."""
21 """Exception raised on errors in parsing the command line."""
22
22
23 def runcatch(ui, args, argv0=None):
23 def runcatch(ui, args, argv0=None):
24 def catchterm(*args):
24 def catchterm(*args):
25 raise util.SignalInterrupt
25 raise util.SignalInterrupt
26
26
27 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
27 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
28 num = getattr(signal, name, None)
28 num = getattr(signal, name, None)
29 if num: signal.signal(num, catchterm)
29 if num: signal.signal(num, catchterm)
30
30
31 try:
31 try:
32 try:
32 try:
33 # enter the debugger before command execution
33 # enter the debugger before command execution
34 if '--debugger' in args:
34 if '--debugger' in args:
35 pdb.set_trace()
35 pdb.set_trace()
36 try:
36 try:
37 return dispatch(ui, args, argv0=argv0)
37 return dispatch(ui, args, argv0=argv0)
38 finally:
38 finally:
39 ui.flush()
39 ui.flush()
40 except:
40 except:
41 # enter the debugger when we hit an exception
41 # enter the debugger when we hit an exception
42 if '--debugger' in args:
42 if '--debugger' in args:
43 pdb.post_mortem(sys.exc_info()[2])
43 pdb.post_mortem(sys.exc_info()[2])
44 ui.print_exc()
44 ui.print_exc()
45 raise
45 raise
46
46
47 except ParseError, inst:
47 except ParseError, inst:
48 if inst.args[0]:
48 if inst.args[0]:
49 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
49 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
50 commands.help_(ui, inst.args[0])
50 commands.help_(ui, inst.args[0])
51 else:
51 else:
52 ui.warn(_("hg: %s\n") % inst.args[1])
52 ui.warn(_("hg: %s\n") % inst.args[1])
53 commands.help_(ui, 'shortlist')
53 commands.help_(ui, 'shortlist')
54 except AmbiguousCommand, inst:
54 except AmbiguousCommand, inst:
55 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
55 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
56 (inst.args[0], " ".join(inst.args[1])))
56 (inst.args[0], " ".join(inst.args[1])))
57 except UnknownCommand, inst:
57 except UnknownCommand, inst:
58 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
58 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
59 commands.help_(ui, 'shortlist')
59 commands.help_(ui, 'shortlist')
60 except hg.RepoError, inst:
60 except hg.RepoError, inst:
61 ui.warn(_("abort: %s!\n") % inst)
61 ui.warn(_("abort: %s!\n") % inst)
62 except lock.LockHeld, inst:
62 except lock.LockHeld, inst:
63 if inst.errno == errno.ETIMEDOUT:
63 if inst.errno == errno.ETIMEDOUT:
64 reason = _('timed out waiting for lock held by %s') % inst.locker
64 reason = _('timed out waiting for lock held by %s') % inst.locker
65 else:
65 else:
66 reason = _('lock held by %s') % inst.locker
66 reason = _('lock held by %s') % inst.locker
67 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
67 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
68 except lock.LockUnavailable, inst:
68 except lock.LockUnavailable, inst:
69 ui.warn(_("abort: could not lock %s: %s\n") %
69 ui.warn(_("abort: could not lock %s: %s\n") %
70 (inst.desc or inst.filename, inst.strerror))
70 (inst.desc or inst.filename, inst.strerror))
71 except revlog.RevlogError, inst:
71 except revlog.RevlogError, inst:
72 ui.warn(_("abort: %s!\n") % inst)
72 ui.warn(_("abort: %s!\n") % inst)
73 except util.SignalInterrupt:
73 except util.SignalInterrupt:
74 ui.warn(_("killed!\n"))
74 ui.warn(_("killed!\n"))
75 except KeyboardInterrupt:
75 except KeyboardInterrupt:
76 try:
76 try:
77 ui.warn(_("interrupted!\n"))
77 ui.warn(_("interrupted!\n"))
78 except IOError, inst:
78 except IOError, inst:
79 if inst.errno == errno.EPIPE:
79 if inst.errno == errno.EPIPE:
80 if ui.debugflag:
80 if ui.debugflag:
81 ui.warn(_("\nbroken pipe\n"))
81 ui.warn(_("\nbroken pipe\n"))
82 else:
82 else:
83 raise
83 raise
84 except socket.error, inst:
84 except socket.error, inst:
85 ui.warn(_("abort: %s\n") % inst[1])
85 ui.warn(_("abort: %s\n") % inst[1])
86 except IOError, inst:
86 except IOError, inst:
87 if hasattr(inst, "code"):
87 if hasattr(inst, "code"):
88 ui.warn(_("abort: %s\n") % inst)
88 ui.warn(_("abort: %s\n") % inst)
89 elif hasattr(inst, "reason"):
89 elif hasattr(inst, "reason"):
90 try: # usually it is in the form (errno, strerror)
90 try: # usually it is in the form (errno, strerror)
91 reason = inst.reason.args[1]
91 reason = inst.reason.args[1]
92 except: # it might be anything, for example a string
92 except: # it might be anything, for example a string
93 reason = inst.reason
93 reason = inst.reason
94 ui.warn(_("abort: error: %s\n") % reason)
94 ui.warn(_("abort: error: %s\n") % reason)
95 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
95 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
96 if ui.debugflag:
96 if ui.debugflag:
97 ui.warn(_("broken pipe\n"))
97 ui.warn(_("broken pipe\n"))
98 elif getattr(inst, "strerror", None):
98 elif getattr(inst, "strerror", None):
99 if getattr(inst, "filename", None):
99 if getattr(inst, "filename", None):
100 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
100 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
101 else:
101 else:
102 ui.warn(_("abort: %s\n") % inst.strerror)
102 ui.warn(_("abort: %s\n") % inst.strerror)
103 else:
103 else:
104 raise
104 raise
105 except OSError, inst:
105 except OSError, inst:
106 if getattr(inst, "filename", None):
106 if getattr(inst, "filename", None):
107 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
107 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
108 else:
108 else:
109 ui.warn(_("abort: %s\n") % inst.strerror)
109 ui.warn(_("abort: %s\n") % inst.strerror)
110 except util.UnexpectedOutput, inst:
110 except util.UnexpectedOutput, inst:
111 ui.warn(_("abort: %s") % inst[0])
111 ui.warn(_("abort: %s") % inst[0])
112 if not isinstance(inst[1], basestring):
112 if not isinstance(inst[1], basestring):
113 ui.warn(" %r\n" % (inst[1],))
113 ui.warn(" %r\n" % (inst[1],))
114 elif not inst[1]:
114 elif not inst[1]:
115 ui.warn(_(" empty string\n"))
115 ui.warn(_(" empty string\n"))
116 else:
116 else:
117 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
117 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
118 except ImportError, inst:
118 except ImportError, inst:
119 m = str(inst).split()[-1]
119 m = str(inst).split()[-1]
120 ui.warn(_("abort: could not import module %s!\n" % m))
120 ui.warn(_("abort: could not import module %s!\n" % m))
121 if m in "mpatch bdiff".split():
121 if m in "mpatch bdiff".split():
122 ui.warn(_("(did you forget to compile extensions?)\n"))
122 ui.warn(_("(did you forget to compile extensions?)\n"))
123 elif m in "zlib".split():
123 elif m in "zlib".split():
124 ui.warn(_("(is your Python install correct?)\n"))
124 ui.warn(_("(is your Python install correct?)\n"))
125
125
126 except util.Abort, inst:
126 except util.Abort, inst:
127 ui.warn(_("abort: %s\n") % inst)
127 ui.warn(_("abort: %s\n") % inst)
128 except SystemExit, inst:
128 except SystemExit, inst:
129 # Commands shouldn't sys.exit directly, but give a return code.
129 # Commands shouldn't sys.exit directly, but give a return code.
130 # Just in case catch this and and pass exit code to caller.
130 # Just in case catch this and and pass exit code to caller.
131 return inst.code
131 return inst.code
132 except:
132 except:
133 ui.warn(_("** unknown exception encountered, details follow\n"))
133 ui.warn(_("** unknown exception encountered, details follow\n"))
134 ui.warn(_("** report bug details to "
134 ui.warn(_("** report bug details to "
135 "http://www.selenic.com/mercurial/bts\n"))
135 "http://www.selenic.com/mercurial/bts\n"))
136 ui.warn(_("** or mercurial@selenic.com\n"))
136 ui.warn(_("** or mercurial@selenic.com\n"))
137 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
137 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
138 % version.get_version())
138 % version.get_version())
139 raise
139 raise
140
140
141 return -1
141 return -1
142
142
143 def findpossible(ui, cmd):
143 def findpossible(ui, cmd):
144 """
144 """
145 Return cmd -> (aliases, command table entry)
145 Return cmd -> (aliases, command table entry)
146 for each matching command.
146 for each matching command.
147 Return debug commands (or their aliases) only if no normal command matches.
147 Return debug commands (or their aliases) only if no normal command matches.
148 """
148 """
149 choice = {}
149 choice = {}
150 debugchoice = {}
150 debugchoice = {}
151 for e in commands.table.keys():
151 for e in commands.table.keys():
152 aliases = e.lstrip("^").split("|")
152 aliases = e.lstrip("^").split("|")
153 found = None
153 found = None
154 if cmd in aliases:
154 if cmd in aliases:
155 found = cmd
155 found = cmd
156 elif not ui.config("ui", "strict"):
156 elif not ui.config("ui", "strict"):
157 for a in aliases:
157 for a in aliases:
158 if a.startswith(cmd):
158 if a.startswith(cmd):
159 found = a
159 found = a
160 break
160 break
161 if found is not None:
161 if found is not None:
162 if aliases[0].startswith("debug") or found.startswith("debug"):
162 if aliases[0].startswith("debug") or found.startswith("debug"):
163 debugchoice[found] = (aliases, commands.table[e])
163 debugchoice[found] = (aliases, commands.table[e])
164 else:
164 else:
165 choice[found] = (aliases, commands.table[e])
165 choice[found] = (aliases, commands.table[e])
166
166
167 if not choice and debugchoice:
167 if not choice and debugchoice:
168 choice = debugchoice
168 choice = debugchoice
169
169
170 return choice
170 return choice
171
171
172 def findcmd(ui, cmd):
172 def findcmd(ui, cmd):
173 """Return (aliases, command table entry) for command string."""
173 """Return (aliases, command table entry) for command string."""
174 choice = findpossible(ui, cmd)
174 choice = findpossible(ui, cmd)
175
175
176 if choice.has_key(cmd):
176 if choice.has_key(cmd):
177 return choice[cmd]
177 return choice[cmd]
178
178
179 if len(choice) > 1:
179 if len(choice) > 1:
180 clist = choice.keys()
180 clist = choice.keys()
181 clist.sort()
181 clist.sort()
182 raise AmbiguousCommand(cmd, clist)
182 raise AmbiguousCommand(cmd, clist)
183
183
184 if choice:
184 if choice:
185 return choice.values()[0]
185 return choice.values()[0]
186
186
187 raise UnknownCommand(cmd)
187 raise UnknownCommand(cmd)
188
188
189 def findrepo():
189 def findrepo():
190 p = os.getcwd()
190 p = os.getcwd()
191 while not os.path.isdir(os.path.join(p, ".hg")):
191 while not os.path.isdir(os.path.join(p, ".hg")):
192 oldp, p = p, os.path.dirname(p)
192 oldp, p = p, os.path.dirname(p)
193 if p == oldp:
193 if p == oldp:
194 return None
194 return None
195
195
196 return p
196 return p
197
197
198 def parse(ui, args):
198 def parse(ui, args):
199 options = {}
199 options = {}
200 cmdoptions = {}
200 cmdoptions = {}
201
201
202 try:
202 try:
203 args = fancyopts.fancyopts(args, commands.globalopts, options)
203 args = fancyopts.fancyopts(args, commands.globalopts, options)
204 except fancyopts.getopt.GetoptError, inst:
204 except fancyopts.getopt.GetoptError, inst:
205 raise ParseError(None, inst)
205 raise ParseError(None, inst)
206
206
207 if args:
207 if args:
208 cmd, args = args[0], args[1:]
208 cmd, args = args[0], args[1:]
209 aliases, i = findcmd(ui, cmd)
209 aliases, i = findcmd(ui, cmd)
210 cmd = aliases[0]
210 cmd = aliases[0]
211 defaults = ui.config("defaults", cmd)
211 defaults = ui.config("defaults", cmd)
212 if defaults:
212 if defaults:
213 args = shlex.split(defaults) + args
213 args = shlex.split(defaults) + args
214 c = list(i[1])
214 c = list(i[1])
215 else:
215 else:
216 cmd = None
216 cmd = None
217 c = []
217 c = []
218
218
219 # combine global options into local
219 # combine global options into local
220 for o in commands.globalopts:
220 for o in commands.globalopts:
221 c.append((o[0], o[1], options[o[1]], o[3]))
221 c.append((o[0], o[1], options[o[1]], o[3]))
222
222
223 try:
223 try:
224 args = fancyopts.fancyopts(args, c, cmdoptions)
224 args = fancyopts.fancyopts(args, c, cmdoptions)
225 except fancyopts.getopt.GetoptError, inst:
225 except fancyopts.getopt.GetoptError, inst:
226 raise ParseError(cmd, inst)
226 raise ParseError(cmd, inst)
227
227
228 # separate global options back out
228 # separate global options back out
229 for o in commands.globalopts:
229 for o in commands.globalopts:
230 n = o[1]
230 n = o[1]
231 options[n] = cmdoptions[n]
231 options[n] = cmdoptions[n]
232 del cmdoptions[n]
232 del cmdoptions[n]
233
233
234 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
234 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
235
235
236 def parseconfig(config):
236 def parseconfig(config):
237 """parse the --config options from the command line"""
237 """parse the --config options from the command line"""
238 parsed = []
238 parsed = []
239 for cfg in config:
239 for cfg in config:
240 try:
240 try:
241 name, value = cfg.split('=', 1)
241 name, value = cfg.split('=', 1)
242 section, name = name.split('.', 1)
242 section, name = name.split('.', 1)
243 if not section or not name:
243 if not section or not name:
244 raise IndexError
244 raise IndexError
245 parsed.append((section, name, value))
245 parsed.append((section, name, value))
246 except (IndexError, ValueError):
246 except (IndexError, ValueError):
247 raise util.Abort(_('malformed --config option: %s') % cfg)
247 raise util.Abort(_('malformed --config option: %s') % cfg)
248 return parsed
248 return parsed
249
249
250 def earlygetopt(aliases, args):
250 def earlygetopt(aliases, args):
251 """Return list of values for an option (or aliases).
251 """Return list of values for an option (or aliases).
252
252
253 The values are listed in the order they appear in args.
253 The values are listed in the order they appear in args.
254 The options and values are removed from args.
254 The options and values are removed from args.
255 """
255 """
256 try:
256 try:
257 argcount = args.index("--")
257 argcount = args.index("--")
258 except ValueError:
258 except ValueError:
259 argcount = len(args)
259 argcount = len(args)
260 shortopts = [opt for opt in aliases if len(opt) == 2]
260 shortopts = [opt for opt in aliases if len(opt) == 2]
261 values = []
261 values = []
262 pos = 0
262 pos = 0
263 while pos < argcount:
263 while pos < argcount:
264 if args[pos] in aliases:
264 if args[pos] in aliases:
265 if pos + 1 >= argcount:
265 if pos + 1 >= argcount:
266 # ignore and let getopt report an error if there is no value
266 # ignore and let getopt report an error if there is no value
267 break
267 break
268 del args[pos]
268 del args[pos]
269 values.append(args.pop(pos))
269 values.append(args.pop(pos))
270 argcount -= 2
270 argcount -= 2
271 elif args[pos][:2] in shortopts:
271 elif args[pos][:2] in shortopts:
272 # short option can have no following space, e.g. hg log -Rfoo
272 # short option can have no following space, e.g. hg log -Rfoo
273 values.append(args.pop(pos)[2:])
273 values.append(args.pop(pos)[2:])
274 argcount -= 1
274 argcount -= 1
275 else:
275 else:
276 pos += 1
276 pos += 1
277 return values
277 return values
278
278
279 def dispatch(ui, args, argv0=None):
279 def dispatch(ui, args, argv0=None):
280 # remember how to call 'hg' before changing the working dir
280 # remember how to call 'hg' before changing the working dir
281 util.set_hgexecutable(argv0)
281 util.set_hgexecutable(argv0)
282
282
283 # read --config before doing anything else
283 # read --config before doing anything else
284 # (e.g. to change trust settings for reading .hg/hgrc)
284 # (e.g. to change trust settings for reading .hg/hgrc)
285 config = earlygetopt(['--config'], args)
285 config = earlygetopt(['--config'], args)
286 if config:
286 if config:
287 ui.updateopts(config=parseconfig(config))
287 ui.updateopts(config=parseconfig(config))
288
288
289 # check for cwd
289 # check for cwd
290 cwd = earlygetopt(['--cwd'], args)
290 cwd = earlygetopt(['--cwd'], args)
291 if cwd:
291 if cwd:
292 os.chdir(cwd[-1])
292 os.chdir(cwd[-1])
293
293
294 # read the local repository .hgrc into a local ui object
294 # read the local repository .hgrc into a local ui object
295 path = findrepo() or ""
295 path = findrepo() or ""
296 if not path:
296 if not path:
297 lui = ui
297 lui = ui
298 if path:
298 if path:
299 try:
299 try:
300 lui = commands.ui.ui(parentui=ui)
300 lui = commands.ui.ui(parentui=ui)
301 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
301 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
302 except IOError:
302 except IOError:
303 pass
303 pass
304
304
305 # now we can expand paths, even ones in .hg/hgrc
305 # now we can expand paths, even ones in .hg/hgrc
306 rpath = earlygetopt(["-R", "--repository", "--repo"], args)
306 rpath = earlygetopt(["-R", "--repository", "--repo"], args)
307 if rpath:
307 if rpath:
308 path = lui.expandpath(rpath[-1])
308 path = lui.expandpath(rpath[-1])
309 lui = commands.ui.ui(parentui=ui)
309 lui = commands.ui.ui(parentui=ui)
310 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
310 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
311
311
312 extensions.loadall(lui)
312 extensions.loadall(lui)
313 # check for fallback encoding
313 # check for fallback encoding
314 fallback = lui.config('ui', 'fallbackencoding')
314 fallback = lui.config('ui', 'fallbackencoding')
315 if fallback:
315 if fallback:
316 util._fallbackencoding = fallback
316 util._fallbackencoding = fallback
317
317
318 fullargs = args
318 fullargs = args
319 cmd, func, args, options, cmdoptions = parse(ui, args)
319 cmd, func, args, options, cmdoptions = parse(ui, args)
320
320
321 if options["config"]:
321 if options["config"]:
322 raise util.Abort(_("Option --config may not be abbreviated!"))
322 raise util.Abort(_("Option --config may not be abbreviated!"))
323 if options["cwd"]:
323 if options["cwd"]:
324 raise util.Abort(_("Option --cwd may not be abbreviated!"))
324 raise util.Abort(_("Option --cwd may not be abbreviated!"))
325 if options["repository"]:
325 if options["repository"]:
326 raise util.Abort(_(
326 raise util.Abort(_(
327 "Option -R has to be separated from other options (i.e. not -qR) "
327 "Option -R has to be separated from other options (i.e. not -qR) "
328 "and --repository may only be abbreviated as --repo!"))
328 "and --repository may only be abbreviated as --repo!"))
329
329
330 if options["encoding"]:
330 if options["encoding"]:
331 util._encoding = options["encoding"]
331 util._encoding = options["encoding"]
332 if options["encodingmode"]:
332 if options["encodingmode"]:
333 util._encodingmode = options["encodingmode"]
333 util._encodingmode = options["encodingmode"]
334 if options["time"]:
334 if options["time"]:
335 def get_times():
335 def get_times():
336 t = os.times()
336 t = os.times()
337 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
337 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
338 t = (t[0], t[1], t[2], t[3], time.clock())
338 t = (t[0], t[1], t[2], t[3], time.clock())
339 return t
339 return t
340 s = get_times()
340 s = get_times()
341 def print_time():
341 def print_time():
342 t = get_times()
342 t = get_times()
343 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
343 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
344 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
344 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
345 atexit.register(print_time)
345 atexit.register(print_time)
346
346
347 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
347 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
348 not options["noninteractive"], options["traceback"])
348 not options["noninteractive"], options["traceback"])
349
349
350 if options['help']:
350 if options['help']:
351 return commands.help_(ui, cmd, options['version'])
351 return commands.help_(ui, cmd, options['version'])
352 elif options['version']:
352 elif options['version']:
353 return commands.version_(ui)
353 return commands.version_(ui)
354 elif not cmd:
354 elif not cmd:
355 return commands.help_(ui, 'shortlist')
355 return commands.help_(ui, 'shortlist')
356
356
357 repo = None
357 repo = None
358 if cmd not in commands.norepo.split():
358 if cmd not in commands.norepo.split():
359 try:
359 try:
360 repo = hg.repository(ui, path=path)
360 repo = hg.repository(ui, path=path)
361 ui = repo.ui
361 ui = repo.ui
362 if not repo.local():
362 if not repo.local():
363 raise util.Abort(_("repository '%s' is not local") % path)
363 raise util.Abort(_("repository '%s' is not local") % path)
364 except hg.RepoError:
364 except hg.RepoError:
365 if cmd not in commands.optionalrepo.split():
365 if cmd not in commands.optionalrepo.split():
366 if not path:
366 if not path:
367 raise hg.RepoError(_("There is no Mercurial repository here"
367 raise hg.RepoError(_("There is no Mercurial repository here"
368 " (.hg not found)"))
368 " (.hg not found)"))
369 raise
369 raise
370 d = lambda: func(ui, repo, *args, **cmdoptions)
370 d = lambda: func(ui, repo, *args, **cmdoptions)
371 else:
371 else:
372 d = lambda: func(ui, *args, **cmdoptions)
372 d = lambda: func(ui, *args, **cmdoptions)
373
373
374 # run pre-hook, and abort if it fails
374 # run pre-hook, and abort if it fails
375 ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
375 ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
376 if ret:
376 if ret:
377 return ret
377 return ret
378 ret = runcommand(ui, options, cmd, d)
378 ret = runcommand(ui, options, cmd, d)
379 # run post-hook, passing command result
379 # run post-hook, passing command result
380 hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
380 hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
381 result = ret)
381 result = ret)
382 return ret
382 return ret
383
383
384 def runcommand(ui, options, cmd, cmdfunc):
384 def runcommand(ui, options, cmd, cmdfunc):
385 def checkargs():
385 def checkargs():
386 try:
386 try:
387 return cmdfunc()
387 return cmdfunc()
388 except TypeError, inst:
388 except TypeError, inst:
389 # was this an argument error?
389 # was this an argument error?
390 tb = traceback.extract_tb(sys.exc_info()[2])
390 tb = traceback.extract_tb(sys.exc_info()[2])
391 if len(tb) != 2: # no
391 if len(tb) != 2: # no
392 raise
392 raise
393 raise ParseError(cmd, _("invalid arguments"))
393 raise ParseError(cmd, _("invalid arguments"))
394
394
395 if options['profile']:
395 if options['profile']:
396 import hotshot, hotshot.stats
396 import hotshot, hotshot.stats
397 prof = hotshot.Profile("hg.prof")
397 prof = hotshot.Profile("hg.prof")
398 try:
398 try:
399 try:
399 try:
400 return prof.runcall(checkargs)
400 return prof.runcall(checkargs)
401 except:
401 except:
402 try:
402 try:
403 ui.warn(_('exception raised - generating '
403 ui.warn(_('exception raised - generating '
404 'profile anyway\n'))
404 'profile anyway\n'))
405 except:
405 except:
406 pass
406 pass
407 raise
407 raise
408 finally:
408 finally:
409 prof.close()
409 prof.close()
410 stats = hotshot.stats.load("hg.prof")
410 stats = hotshot.stats.load("hg.prof")
411 stats.strip_dirs()
411 stats.strip_dirs()
412 stats.sort_stats('time', 'calls')
412 stats.sort_stats('time', 'calls')
413 stats.print_stats(40)
413 stats.print_stats(40)
414 elif options['lsprof']:
414 elif options['lsprof']:
415 try:
415 try:
416 from mercurial import lsprof
416 from mercurial import lsprof
417 except ImportError:
417 except ImportError:
418 raise util.Abort(_(
418 raise util.Abort(_(
419 'lsprof not available - install from '
419 'lsprof not available - install from '
420 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
420 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
421 p = lsprof.Profiler()
421 p = lsprof.Profiler()
422 p.enable(subcalls=True)
422 p.enable(subcalls=True)
423 try:
423 try:
424 return checkargs()
424 return checkargs()
425 finally:
425 finally:
426 p.disable()
426 p.disable()
427 stats = lsprof.Stats(p.getstats())
427 stats = lsprof.Stats(p.getstats())
428 stats.sort()
428 stats.sort()
429 stats.pprint(top=10, file=sys.stderr, climit=5)
429 stats.pprint(top=10, file=sys.stderr, climit=5)
430 else:
430 else:
431 return checkargs()
431 return checkargs()
432
432
433 def bail_if_changed(repo):
433 def bail_if_changed(repo):
434 modified, added, removed, deleted = repo.status()[:4]
434 modified, added, removed, deleted = repo.status()[:4]
435 if modified or added or removed or deleted:
435 if modified or added or removed or deleted:
436 raise util.Abort(_("outstanding uncommitted changes"))
436 raise util.Abort(_("outstanding uncommitted changes"))
437
437
438 def logmessage(opts):
438 def logmessage(opts):
439 """ get the log message according to -m and -l option """
439 """ get the log message according to -m and -l option """
440 message = opts['message']
440 message = opts['message']
441 logfile = opts['logfile']
441 logfile = opts['logfile']
442
442
443 if message and logfile:
443 if message and logfile:
444 raise util.Abort(_('options --message and --logfile are mutually '
444 raise util.Abort(_('options --message and --logfile are mutually '
445 'exclusive'))
445 'exclusive'))
446 if not message and logfile:
446 if not message and logfile:
447 try:
447 try:
448 if logfile == '-':
448 if logfile == '-':
449 message = sys.stdin.read()
449 message = sys.stdin.read()
450 else:
450 else:
451 message = open(logfile).read()
451 message = open(logfile).read()
452 except IOError, inst:
452 except IOError, inst:
453 raise util.Abort(_("can't read commit message '%s': %s") %
453 raise util.Abort(_("can't read commit message '%s': %s") %
454 (logfile, inst.strerror))
454 (logfile, inst.strerror))
455 return message
455 return message
456
456
457 def setremoteconfig(ui, opts):
457 def setremoteconfig(ui, opts):
458 "copy remote options to ui tree"
458 "copy remote options to ui tree"
459 if opts.get('ssh'):
459 if opts.get('ssh'):
460 ui.setconfig("ui", "ssh", opts['ssh'])
460 ui.setconfig("ui", "ssh", opts['ssh'])
461 if opts.get('remotecmd'):
461 if opts.get('remotecmd'):
462 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
462 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
463
463
464 def parseurl(url, revs):
464 def parseurl(url, revs):
465 '''parse url#branch, returning url, branch + revs'''
465 '''parse url#branch, returning url, branch + revs'''
466
466
467 if '#' not in url:
467 if '#' not in url:
468 return url, (revs or None)
468 return url, (revs or None)
469
469
470 url, rev = url.split('#', 1)
470 url, rev = url.split('#', 1)
471 return url, revs + [rev]
471 return url, revs + [rev]
472
472
473 def revpair(repo, revs):
473 def revpair(repo, revs):
474 '''return pair of nodes, given list of revisions. second item can
474 '''return pair of nodes, given list of revisions. second item can
475 be None, meaning use working dir.'''
475 be None, meaning use working dir.'''
476
476
477 def revfix(repo, val, defval):
477 def revfix(repo, val, defval):
478 if not val and val != 0 and defval is not None:
478 if not val and val != 0 and defval is not None:
479 val = defval
479 val = defval
480 return repo.lookup(val)
480 return repo.lookup(val)
481
481
482 if not revs:
482 if not revs:
483 return repo.dirstate.parents()[0], None
483 return repo.dirstate.parents()[0], None
484 end = None
484 end = None
485 if len(revs) == 1:
485 if len(revs) == 1:
486 if revrangesep in revs[0]:
486 if revrangesep in revs[0]:
487 start, end = revs[0].split(revrangesep, 1)
487 start, end = revs[0].split(revrangesep, 1)
488 start = revfix(repo, start, 0)
488 start = revfix(repo, start, 0)
489 end = revfix(repo, end, repo.changelog.count() - 1)
489 end = revfix(repo, end, repo.changelog.count() - 1)
490 else:
490 else:
491 start = revfix(repo, revs[0], None)
491 start = revfix(repo, revs[0], None)
492 elif len(revs) == 2:
492 elif len(revs) == 2:
493 if revrangesep in revs[0] or revrangesep in revs[1]:
493 if revrangesep in revs[0] or revrangesep in revs[1]:
494 raise util.Abort(_('too many revisions specified'))
494 raise util.Abort(_('too many revisions specified'))
495 start = revfix(repo, revs[0], None)
495 start = revfix(repo, revs[0], None)
496 end = revfix(repo, revs[1], None)
496 end = revfix(repo, revs[1], None)
497 else:
497 else:
498 raise util.Abort(_('too many revisions specified'))
498 raise util.Abort(_('too many revisions specified'))
499 return start, end
499 return start, end
500
500
501 def revrange(repo, revs):
501 def revrange(repo, revs):
502 """Yield revision as strings from a list of revision specifications."""
502 """Yield revision as strings from a list of revision specifications."""
503
503
504 def revfix(repo, val, defval):
504 def revfix(repo, val, defval):
505 if not val and val != 0 and defval is not None:
505 if not val and val != 0 and defval is not None:
506 return defval
506 return defval
507 return repo.changelog.rev(repo.lookup(val))
507 return repo.changelog.rev(repo.lookup(val))
508
508
509 seen, l = {}, []
509 seen, l = {}, []
510 for spec in revs:
510 for spec in revs:
511 if revrangesep in spec:
511 if revrangesep in spec:
512 start, end = spec.split(revrangesep, 1)
512 start, end = spec.split(revrangesep, 1)
513 start = revfix(repo, start, 0)
513 start = revfix(repo, start, 0)
514 end = revfix(repo, end, repo.changelog.count() - 1)
514 end = revfix(repo, end, repo.changelog.count() - 1)
515 step = start > end and -1 or 1
515 step = start > end and -1 or 1
516 for rev in xrange(start, end+step, step):
516 for rev in xrange(start, end+step, step):
517 if rev in seen:
517 if rev in seen:
518 continue
518 continue
519 seen[rev] = 1
519 seen[rev] = 1
520 l.append(rev)
520 l.append(rev)
521 else:
521 else:
522 rev = revfix(repo, spec, None)
522 rev = revfix(repo, spec, None)
523 if rev in seen:
523 if rev in seen:
524 continue
524 continue
525 seen[rev] = 1
525 seen[rev] = 1
526 l.append(rev)
526 l.append(rev)
527
527
528 return l
528 return l
529
529
530 def make_filename(repo, pat, node,
530 def make_filename(repo, pat, node,
531 total=None, seqno=None, revwidth=None, pathname=None):
531 total=None, seqno=None, revwidth=None, pathname=None):
532 node_expander = {
532 node_expander = {
533 'H': lambda: hex(node),
533 'H': lambda: hex(node),
534 'R': lambda: str(repo.changelog.rev(node)),
534 'R': lambda: str(repo.changelog.rev(node)),
535 'h': lambda: short(node),
535 'h': lambda: short(node),
536 }
536 }
537 expander = {
537 expander = {
538 '%': lambda: '%',
538 '%': lambda: '%',
539 'b': lambda: os.path.basename(repo.root),
539 'b': lambda: os.path.basename(repo.root),
540 }
540 }
541
541
542 try:
542 try:
543 if node:
543 if node:
544 expander.update(node_expander)
544 expander.update(node_expander)
545 if node and revwidth is not None:
545 if node and revwidth is not None:
546 expander['r'] = (lambda:
546 expander['r'] = (lambda:
547 str(repo.changelog.rev(node)).zfill(revwidth))
547 str(repo.changelog.rev(node)).zfill(revwidth))
548 if total is not None:
548 if total is not None:
549 expander['N'] = lambda: str(total)
549 expander['N'] = lambda: str(total)
550 if seqno is not None:
550 if seqno is not None:
551 expander['n'] = lambda: str(seqno)
551 expander['n'] = lambda: str(seqno)
552 if total is not None and seqno is not None:
552 if total is not None and seqno is not None:
553 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
553 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
554 if pathname is not None:
554 if pathname is not None:
555 expander['s'] = lambda: os.path.basename(pathname)
555 expander['s'] = lambda: os.path.basename(pathname)
556 expander['d'] = lambda: os.path.dirname(pathname) or '.'
556 expander['d'] = lambda: os.path.dirname(pathname) or '.'
557 expander['p'] = lambda: pathname
557 expander['p'] = lambda: pathname
558
558
559 newname = []
559 newname = []
560 patlen = len(pat)
560 patlen = len(pat)
561 i = 0
561 i = 0
562 while i < patlen:
562 while i < patlen:
563 c = pat[i]
563 c = pat[i]
564 if c == '%':
564 if c == '%':
565 i += 1
565 i += 1
566 c = pat[i]
566 c = pat[i]
567 c = expander[c]()
567 c = expander[c]()
568 newname.append(c)
568 newname.append(c)
569 i += 1
569 i += 1
570 return ''.join(newname)
570 return ''.join(newname)
571 except KeyError, inst:
571 except KeyError, inst:
572 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
572 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
573 inst.args[0])
573 inst.args[0])
574
574
575 def make_file(repo, pat, node=None,
575 def make_file(repo, pat, node=None,
576 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
576 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
577 if not pat or pat == '-':
577 if not pat or pat == '-':
578 return 'w' in mode and sys.stdout or sys.stdin
578 return 'w' in mode and sys.stdout or sys.stdin
579 if hasattr(pat, 'write') and 'w' in mode:
579 if hasattr(pat, 'write') and 'w' in mode:
580 return pat
580 return pat
581 if hasattr(pat, 'read') and 'r' in mode:
581 if hasattr(pat, 'read') and 'r' in mode:
582 return pat
582 return pat
583 return open(make_filename(repo, pat, node, total, seqno, revwidth,
583 return open(make_filename(repo, pat, node, total, seqno, revwidth,
584 pathname),
584 pathname),
585 mode)
585 mode)
586
586
587 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
587 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
588 cwd = repo.getcwd()
588 cwd = repo.getcwd()
589 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
589 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
590 opts.get('exclude'), globbed=globbed,
590 opts.get('exclude'), globbed=globbed,
591 default=default)
591 default=default)
592
592
593 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
593 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
594 default=None):
594 default=None):
595 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
595 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
596 default=default)
596 default=default)
597 exact = dict.fromkeys(files)
597 exact = dict.fromkeys(files)
598 cwd = repo.getcwd()
598 cwd = repo.getcwd()
599 for src, fn in repo.walk(node=node, files=files, match=matchfn,
599 for src, fn in repo.walk(node=node, files=files, match=matchfn,
600 badmatch=badmatch):
600 badmatch=badmatch):
601 yield src, fn, repo.pathto(fn, cwd), fn in exact
601 yield src, fn, repo.pathto(fn, cwd), fn in exact
602
602
603 def findrenames(repo, added=None, removed=None, threshold=0.5):
603 def findrenames(repo, added=None, removed=None, threshold=0.5):
604 '''find renamed files -- yields (before, after, score) tuples'''
604 '''find renamed files -- yields (before, after, score) tuples'''
605 if added is None or removed is None:
605 if added is None or removed is None:
606 added, removed = repo.status()[1:3]
606 added, removed = repo.status()[1:3]
607 ctx = repo.changectx()
607 ctx = repo.changectx()
608 for a in added:
608 for a in added:
609 aa = repo.wread(a)
609 aa = repo.wread(a)
610 bestname, bestscore = None, threshold
610 bestname, bestscore = None, threshold
611 for r in removed:
611 for r in removed:
612 rr = ctx.filectx(r).data()
612 rr = ctx.filectx(r).data()
613
613
614 # bdiff.blocks() returns blocks of matching lines
614 # bdiff.blocks() returns blocks of matching lines
615 # count the number of bytes in each
615 # count the number of bytes in each
616 equal = 0
616 equal = 0
617 alines = mdiff.splitnewlines(aa)
617 alines = mdiff.splitnewlines(aa)
618 matches = bdiff.blocks(aa, rr)
618 matches = bdiff.blocks(aa, rr)
619 for x1,x2,y1,y2 in matches:
619 for x1,x2,y1,y2 in matches:
620 for line in alines[x1:x2]:
620 for line in alines[x1:x2]:
621 equal += len(line)
621 equal += len(line)
622
622
623 lengths = len(aa) + len(rr)
623 lengths = len(aa) + len(rr)
624 if lengths:
624 if lengths:
625 myscore = equal*2.0 / lengths
625 myscore = equal*2.0 / lengths
626 if myscore >= bestscore:
626 if myscore >= bestscore:
627 bestname, bestscore = r, myscore
627 bestname, bestscore = r, myscore
628 if bestname:
628 if bestname:
629 yield bestname, a, bestscore
629 yield bestname, a, bestscore
630
630
631 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
631 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
632 similarity=None):
632 similarity=None):
633 if dry_run is None:
633 if dry_run is None:
634 dry_run = opts.get('dry_run')
634 dry_run = opts.get('dry_run')
635 if similarity is None:
635 if similarity is None:
636 similarity = float(opts.get('similarity') or 0)
636 similarity = float(opts.get('similarity') or 0)
637 add, remove = [], []
637 add, remove = [], []
638 mapping = {}
638 mapping = {}
639 for src, abs, rel, exact in walk(repo, pats, opts):
639 for src, abs, rel, exact in walk(repo, pats, opts):
640 target = repo.wjoin(abs)
640 target = repo.wjoin(abs)
641 if src == 'f' and repo.dirstate.state(abs) == '?':
641 if src == 'f' and repo.dirstate.state(abs) == '?':
642 add.append(abs)
642 add.append(abs)
643 mapping[abs] = rel, exact
643 mapping[abs] = rel, exact
644 if repo.ui.verbose or not exact:
644 if repo.ui.verbose or not exact:
645 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
645 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
646 if repo.dirstate.state(abs) != 'r' and not util.lexists(target):
646 if repo.dirstate.state(abs) != 'r' and not util.lexists(target):
647 remove.append(abs)
647 remove.append(abs)
648 mapping[abs] = rel, exact
648 mapping[abs] = rel, exact
649 if repo.ui.verbose or not exact:
649 if repo.ui.verbose or not exact:
650 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
650 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
651 if not dry_run:
651 if not dry_run:
652 repo.add(add, wlock=wlock)
652 repo.add(add, wlock=wlock)
653 repo.remove(remove, wlock=wlock)
653 repo.remove(remove, wlock=wlock)
654 if similarity > 0:
654 if similarity > 0:
655 for old, new, score in findrenames(repo, add, remove, similarity):
655 for old, new, score in findrenames(repo, add, remove, similarity):
656 oldrel, oldexact = mapping[old]
656 oldrel, oldexact = mapping[old]
657 newrel, newexact = mapping[new]
657 newrel, newexact = mapping[new]
658 if repo.ui.verbose or not oldexact or not newexact:
658 if repo.ui.verbose or not oldexact or not newexact:
659 repo.ui.status(_('recording removal of %s as rename to %s '
659 repo.ui.status(_('recording removal of %s as rename to %s '
660 '(%d%% similar)\n') %
660 '(%d%% similar)\n') %
661 (oldrel, newrel, score * 100))
661 (oldrel, newrel, score * 100))
662 if not dry_run:
662 if not dry_run:
663 repo.copy(old, new, wlock=wlock)
663 repo.copy(old, new, wlock=wlock)
664
664
665 def service(opts, parentfn=None, initfn=None, runfn=None):
665 def service(opts, parentfn=None, initfn=None, runfn=None):
666 '''Run a command as a service.'''
666 '''Run a command as a service.'''
667
667
668 if opts['daemon'] and not opts['daemon_pipefds']:
668 if opts['daemon'] and not opts['daemon_pipefds']:
669 rfd, wfd = os.pipe()
669 rfd, wfd = os.pipe()
670 args = sys.argv[:]
670 args = sys.argv[:]
671 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
671 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
672 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
672 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
673 args[0], args)
673 args[0], args)
674 os.close(wfd)
674 os.close(wfd)
675 os.read(rfd, 1)
675 os.read(rfd, 1)
676 if parentfn:
676 if parentfn:
677 return parentfn(pid)
677 return parentfn(pid)
678 else:
678 else:
679 os._exit(0)
679 os._exit(0)
680
680
681 if initfn:
681 if initfn:
682 initfn()
682 initfn()
683
683
684 if opts['pid_file']:
684 if opts['pid_file']:
685 fp = open(opts['pid_file'], 'w')
685 fp = open(opts['pid_file'], 'w')
686 fp.write(str(os.getpid()) + '\n')
686 fp.write(str(os.getpid()) + '\n')
687 fp.close()
687 fp.close()
688
688
689 if opts['daemon_pipefds']:
689 if opts['daemon_pipefds']:
690 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
690 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
691 os.close(rfd)
691 os.close(rfd)
692 try:
692 try:
693 os.setsid()
693 os.setsid()
694 except AttributeError:
694 except AttributeError:
695 pass
695 pass
696 os.write(wfd, 'y')
696 os.write(wfd, 'y')
697 os.close(wfd)
697 os.close(wfd)
698 sys.stdout.flush()
698 sys.stdout.flush()
699 sys.stderr.flush()
699 sys.stderr.flush()
700 fd = os.open(util.nulldev, os.O_RDWR)
700 fd = os.open(util.nulldev, os.O_RDWR)
701 if fd != 0: os.dup2(fd, 0)
701 if fd != 0: os.dup2(fd, 0)
702 if fd != 1: os.dup2(fd, 1)
702 if fd != 1: os.dup2(fd, 1)
703 if fd != 2: os.dup2(fd, 2)
703 if fd != 2: os.dup2(fd, 2)
704 if fd not in (0, 1, 2): os.close(fd)
704 if fd not in (0, 1, 2): os.close(fd)
705
705
706 if runfn:
706 if runfn:
707 return runfn()
707 return runfn()
708
708
709 class changeset_printer(object):
709 class changeset_printer(object):
710 '''show changeset information when templating not requested.'''
710 '''show changeset information when templating not requested.'''
711
711
712 def __init__(self, ui, repo, patch, buffered):
712 def __init__(self, ui, repo, patch, buffered):
713 self.ui = ui
713 self.ui = ui
714 self.repo = repo
714 self.repo = repo
715 self.buffered = buffered
715 self.buffered = buffered
716 self.patch = patch
716 self.patch = patch
717 self.header = {}
717 self.header = {}
718 self.hunk = {}
718 self.hunk = {}
719 self.lastheader = None
719 self.lastheader = None
720
720
721 def flush(self, rev):
721 def flush(self, rev):
722 if rev in self.header:
722 if rev in self.header:
723 h = self.header[rev]
723 h = self.header[rev]
724 if h != self.lastheader:
724 if h != self.lastheader:
725 self.lastheader = h
725 self.lastheader = h
726 self.ui.write(h)
726 self.ui.write(h)
727 del self.header[rev]
727 del self.header[rev]
728 if rev in self.hunk:
728 if rev in self.hunk:
729 self.ui.write(self.hunk[rev])
729 self.ui.write(self.hunk[rev])
730 del self.hunk[rev]
730 del self.hunk[rev]
731 return 1
731 return 1
732 return 0
732 return 0
733
733
734 def show(self, rev=0, changenode=None, copies=(), **props):
734 def show(self, rev=0, changenode=None, copies=(), **props):
735 if self.buffered:
735 if self.buffered:
736 self.ui.pushbuffer()
736 self.ui.pushbuffer()
737 self._show(rev, changenode, copies, props)
737 self._show(rev, changenode, copies, props)
738 self.hunk[rev] = self.ui.popbuffer()
738 self.hunk[rev] = self.ui.popbuffer()
739 else:
739 else:
740 self._show(rev, changenode, copies, props)
740 self._show(rev, changenode, copies, props)
741
741
742 def _show(self, rev, changenode, copies, props):
742 def _show(self, rev, changenode, copies, props):
743 '''show a single changeset or file revision'''
743 '''show a single changeset or file revision'''
744 log = self.repo.changelog
744 log = self.repo.changelog
745 if changenode is None:
745 if changenode is None:
746 changenode = log.node(rev)
746 changenode = log.node(rev)
747 elif not rev:
747 elif not rev:
748 rev = log.rev(changenode)
748 rev = log.rev(changenode)
749
749
750 if self.ui.quiet:
750 if self.ui.quiet:
751 self.ui.write("%d:%s\n" % (rev, short(changenode)))
751 self.ui.write("%d:%s\n" % (rev, short(changenode)))
752 return
752 return
753
753
754 changes = log.read(changenode)
754 changes = log.read(changenode)
755 date = util.datestr(changes[2])
755 date = util.datestr(changes[2])
756 extra = changes[5]
756 extra = changes[5]
757 branch = extra.get("branch")
757 branch = extra.get("branch")
758
758
759 hexfunc = self.ui.debugflag and hex or short
759 hexfunc = self.ui.debugflag and hex or short
760
760
761 parents = log.parentrevs(rev)
761 parents = [(p, hexfunc(log.node(p)))
762 if not self.ui.debugflag:
762 for p in self._meaningful_parentrevs(log, rev)]
763 if parents[1] == nullrev:
764 if parents[0] >= rev - 1:
765 parents = []
766 else:
767 parents = [parents[0]]
768 parents = [(p, hexfunc(log.node(p))) for p in parents]
769
763
770 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
764 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
771
765
772 # don't show the default branch name
766 # don't show the default branch name
773 if branch != 'default':
767 if branch != 'default':
774 branch = util.tolocal(branch)
768 branch = util.tolocal(branch)
775 self.ui.write(_("branch: %s\n") % branch)
769 self.ui.write(_("branch: %s\n") % branch)
776 for tag in self.repo.nodetags(changenode):
770 for tag in self.repo.nodetags(changenode):
777 self.ui.write(_("tag: %s\n") % tag)
771 self.ui.write(_("tag: %s\n") % tag)
778 for parent in parents:
772 for parent in parents:
779 self.ui.write(_("parent: %d:%s\n") % parent)
773 self.ui.write(_("parent: %d:%s\n") % parent)
780
774
781 if self.ui.debugflag:
775 if self.ui.debugflag:
782 self.ui.write(_("manifest: %d:%s\n") %
776 self.ui.write(_("manifest: %d:%s\n") %
783 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
777 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
784 self.ui.write(_("user: %s\n") % changes[1])
778 self.ui.write(_("user: %s\n") % changes[1])
785 self.ui.write(_("date: %s\n") % date)
779 self.ui.write(_("date: %s\n") % date)
786
780
787 if self.ui.debugflag:
781 if self.ui.debugflag:
788 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
782 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
789 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
783 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
790 files):
784 files):
791 if value:
785 if value:
792 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
786 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
793 elif changes[3] and self.ui.verbose:
787 elif changes[3] and self.ui.verbose:
794 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
788 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
795 if copies and self.ui.verbose:
789 if copies and self.ui.verbose:
796 copies = ['%s (%s)' % c for c in copies]
790 copies = ['%s (%s)' % c for c in copies]
797 self.ui.write(_("copies: %s\n") % ' '.join(copies))
791 self.ui.write(_("copies: %s\n") % ' '.join(copies))
798
792
799 if extra and self.ui.debugflag:
793 if extra and self.ui.debugflag:
800 extraitems = extra.items()
794 extraitems = extra.items()
801 extraitems.sort()
795 extraitems.sort()
802 for key, value in extraitems:
796 for key, value in extraitems:
803 self.ui.write(_("extra: %s=%s\n")
797 self.ui.write(_("extra: %s=%s\n")
804 % (key, value.encode('string_escape')))
798 % (key, value.encode('string_escape')))
805
799
806 description = changes[4].strip()
800 description = changes[4].strip()
807 if description:
801 if description:
808 if self.ui.verbose:
802 if self.ui.verbose:
809 self.ui.write(_("description:\n"))
803 self.ui.write(_("description:\n"))
810 self.ui.write(description)
804 self.ui.write(description)
811 self.ui.write("\n\n")
805 self.ui.write("\n\n")
812 else:
806 else:
813 self.ui.write(_("summary: %s\n") %
807 self.ui.write(_("summary: %s\n") %
814 description.splitlines()[0])
808 description.splitlines()[0])
815 self.ui.write("\n")
809 self.ui.write("\n")
816
810
817 self.showpatch(changenode)
811 self.showpatch(changenode)
818
812
819 def showpatch(self, node):
813 def showpatch(self, node):
820 if self.patch:
814 if self.patch:
821 prev = self.repo.changelog.parents(node)[0]
815 prev = self.repo.changelog.parents(node)[0]
822 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
816 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
823 opts=patch.diffopts(self.ui))
817 opts=patch.diffopts(self.ui))
824 self.ui.write("\n")
818 self.ui.write("\n")
825
819
820 def _meaningful_parentrevs(self, log, rev):
821 """Return list of meaningful (or all if debug) parentrevs for rev.
822
823 For merges (two non-nullrev revisions) both parents are meaningful.
824 Otherwise the first parent revision is considered meaningful if it
825 is not the preceding revision.
826 """
827 parents = log.parentrevs(rev)
828 if not self.ui.debugflag and parents[1] == nullrev:
829 if parents[0] >= rev - 1:
830 parents = []
831 else:
832 parents = [parents[0]]
833 return parents
834
835
826 class changeset_templater(changeset_printer):
836 class changeset_templater(changeset_printer):
827 '''format changeset information.'''
837 '''format changeset information.'''
828
838
829 def __init__(self, ui, repo, patch, mapfile, buffered):
839 def __init__(self, ui, repo, patch, mapfile, buffered):
830 changeset_printer.__init__(self, ui, repo, patch, buffered)
840 changeset_printer.__init__(self, ui, repo, patch, buffered)
831 filters = templater.common_filters.copy()
841 filters = templater.common_filters.copy()
832 filters['formatnode'] = (ui.debugflag and (lambda x: x)
842 filters['formatnode'] = (ui.debugflag and (lambda x: x)
833 or (lambda x: x[:12]))
843 or (lambda x: x[:12]))
834 self.t = templater.templater(mapfile, filters,
844 self.t = templater.templater(mapfile, filters,
835 cache={
845 cache={
836 'parent': '{rev}:{node|formatnode} ',
846 'parent': '{rev}:{node|formatnode} ',
837 'manifest': '{rev}:{node|formatnode}',
847 'manifest': '{rev}:{node|formatnode}',
838 'filecopy': '{name} ({source})'})
848 'filecopy': '{name} ({source})'})
839
849
840 def use_template(self, t):
850 def use_template(self, t):
841 '''set template string to use'''
851 '''set template string to use'''
842 self.t.cache['changeset'] = t
852 self.t.cache['changeset'] = t
843
853
844 def _show(self, rev, changenode, copies, props):
854 def _show(self, rev, changenode, copies, props):
845 '''show a single changeset or file revision'''
855 '''show a single changeset or file revision'''
846 log = self.repo.changelog
856 log = self.repo.changelog
847 if changenode is None:
857 if changenode is None:
848 changenode = log.node(rev)
858 changenode = log.node(rev)
849 elif not rev:
859 elif not rev:
850 rev = log.rev(changenode)
860 rev = log.rev(changenode)
851
861
852 changes = log.read(changenode)
862 changes = log.read(changenode)
853
863
854 def showlist(name, values, plural=None, **args):
864 def showlist(name, values, plural=None, **args):
855 '''expand set of values.
865 '''expand set of values.
856 name is name of key in template map.
866 name is name of key in template map.
857 values is list of strings or dicts.
867 values is list of strings or dicts.
858 plural is plural of name, if not simply name + 's'.
868 plural is plural of name, if not simply name + 's'.
859
869
860 expansion works like this, given name 'foo'.
870 expansion works like this, given name 'foo'.
861
871
862 if values is empty, expand 'no_foos'.
872 if values is empty, expand 'no_foos'.
863
873
864 if 'foo' not in template map, return values as a string,
874 if 'foo' not in template map, return values as a string,
865 joined by space.
875 joined by space.
866
876
867 expand 'start_foos'.
877 expand 'start_foos'.
868
878
869 for each value, expand 'foo'. if 'last_foo' in template
879 for each value, expand 'foo'. if 'last_foo' in template
870 map, expand it instead of 'foo' for last key.
880 map, expand it instead of 'foo' for last key.
871
881
872 expand 'end_foos'.
882 expand 'end_foos'.
873 '''
883 '''
874 if plural: names = plural
884 if plural: names = plural
875 else: names = name + 's'
885 else: names = name + 's'
876 if not values:
886 if not values:
877 noname = 'no_' + names
887 noname = 'no_' + names
878 if noname in self.t:
888 if noname in self.t:
879 yield self.t(noname, **args)
889 yield self.t(noname, **args)
880 return
890 return
881 if name not in self.t:
891 if name not in self.t:
882 if isinstance(values[0], str):
892 if isinstance(values[0], str):
883 yield ' '.join(values)
893 yield ' '.join(values)
884 else:
894 else:
885 for v in values:
895 for v in values:
886 yield dict(v, **args)
896 yield dict(v, **args)
887 return
897 return
888 startname = 'start_' + names
898 startname = 'start_' + names
889 if startname in self.t:
899 if startname in self.t:
890 yield self.t(startname, **args)
900 yield self.t(startname, **args)
891 vargs = args.copy()
901 vargs = args.copy()
892 def one(v, tag=name):
902 def one(v, tag=name):
893 try:
903 try:
894 vargs.update(v)
904 vargs.update(v)
895 except (AttributeError, ValueError):
905 except (AttributeError, ValueError):
896 try:
906 try:
897 for a, b in v:
907 for a, b in v:
898 vargs[a] = b
908 vargs[a] = b
899 except ValueError:
909 except ValueError:
900 vargs[name] = v
910 vargs[name] = v
901 return self.t(tag, **vargs)
911 return self.t(tag, **vargs)
902 lastname = 'last_' + name
912 lastname = 'last_' + name
903 if lastname in self.t:
913 if lastname in self.t:
904 last = values.pop()
914 last = values.pop()
905 else:
915 else:
906 last = None
916 last = None
907 for v in values:
917 for v in values:
908 yield one(v)
918 yield one(v)
909 if last is not None:
919 if last is not None:
910 yield one(last, tag=lastname)
920 yield one(last, tag=lastname)
911 endname = 'end_' + names
921 endname = 'end_' + names
912 if endname in self.t:
922 if endname in self.t:
913 yield self.t(endname, **args)
923 yield self.t(endname, **args)
914
924
915 def showbranches(**args):
925 def showbranches(**args):
916 branch = changes[5].get("branch")
926 branch = changes[5].get("branch")
917 if branch != 'default':
927 if branch != 'default':
918 branch = util.tolocal(branch)
928 branch = util.tolocal(branch)
919 return showlist('branch', [branch], plural='branches', **args)
929 return showlist('branch', [branch], plural='branches', **args)
920
930
921 def showparents(**args):
931 def showparents(**args):
922 parents = [[('rev', log.rev(p)), ('node', hex(p))]
932 parents = [[('rev', p), ('node', hex(log.node(p)))]
923 for p in log.parents(changenode)
933 for p in self._meaningful_parentrevs(log, rev)]
924 if self.ui.debugflag or p != nullid]
925 if (not self.ui.debugflag and len(parents) == 1 and
926 parents[0][0][1] == rev - 1):
927 return
928 return showlist('parent', parents, **args)
934 return showlist('parent', parents, **args)
929
935
930 def showtags(**args):
936 def showtags(**args):
931 return showlist('tag', self.repo.nodetags(changenode), **args)
937 return showlist('tag', self.repo.nodetags(changenode), **args)
932
938
933 def showextras(**args):
939 def showextras(**args):
934 extras = changes[5].items()
940 extras = changes[5].items()
935 extras.sort()
941 extras.sort()
936 for key, value in extras:
942 for key, value in extras:
937 args = args.copy()
943 args = args.copy()
938 args.update(dict(key=key, value=value))
944 args.update(dict(key=key, value=value))
939 yield self.t('extra', **args)
945 yield self.t('extra', **args)
940
946
941 def showcopies(**args):
947 def showcopies(**args):
942 c = [{'name': x[0], 'source': x[1]} for x in copies]
948 c = [{'name': x[0], 'source': x[1]} for x in copies]
943 return showlist('file_copy', c, plural='file_copies', **args)
949 return showlist('file_copy', c, plural='file_copies', **args)
944
950
945 if self.ui.debugflag:
951 if self.ui.debugflag:
946 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
952 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
947 def showfiles(**args):
953 def showfiles(**args):
948 return showlist('file', files[0], **args)
954 return showlist('file', files[0], **args)
949 def showadds(**args):
955 def showadds(**args):
950 return showlist('file_add', files[1], **args)
956 return showlist('file_add', files[1], **args)
951 def showdels(**args):
957 def showdels(**args):
952 return showlist('file_del', files[2], **args)
958 return showlist('file_del', files[2], **args)
953 def showmanifest(**args):
959 def showmanifest(**args):
954 args = args.copy()
960 args = args.copy()
955 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
961 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
956 node=hex(changes[0])))
962 node=hex(changes[0])))
957 return self.t('manifest', **args)
963 return self.t('manifest', **args)
958 else:
964 else:
959 def showfiles(**args):
965 def showfiles(**args):
960 return showlist('file', changes[3], **args)
966 return showlist('file', changes[3], **args)
961 showadds = ''
967 showadds = ''
962 showdels = ''
968 showdels = ''
963 showmanifest = ''
969 showmanifest = ''
964
970
965 defprops = {
971 defprops = {
966 'author': changes[1],
972 'author': changes[1],
967 'branches': showbranches,
973 'branches': showbranches,
968 'date': changes[2],
974 'date': changes[2],
969 'desc': changes[4].strip(),
975 'desc': changes[4].strip(),
970 'file_adds': showadds,
976 'file_adds': showadds,
971 'file_dels': showdels,
977 'file_dels': showdels,
972 'files': showfiles,
978 'files': showfiles,
973 'file_copies': showcopies,
979 'file_copies': showcopies,
974 'manifest': showmanifest,
980 'manifest': showmanifest,
975 'node': hex(changenode),
981 'node': hex(changenode),
976 'parents': showparents,
982 'parents': showparents,
977 'rev': rev,
983 'rev': rev,
978 'tags': showtags,
984 'tags': showtags,
979 'extras': showextras,
985 'extras': showextras,
980 }
986 }
981 props = props.copy()
987 props = props.copy()
982 props.update(defprops)
988 props.update(defprops)
983
989
984 try:
990 try:
985 if self.ui.debugflag and 'header_debug' in self.t:
991 if self.ui.debugflag and 'header_debug' in self.t:
986 key = 'header_debug'
992 key = 'header_debug'
987 elif self.ui.quiet and 'header_quiet' in self.t:
993 elif self.ui.quiet and 'header_quiet' in self.t:
988 key = 'header_quiet'
994 key = 'header_quiet'
989 elif self.ui.verbose and 'header_verbose' in self.t:
995 elif self.ui.verbose and 'header_verbose' in self.t:
990 key = 'header_verbose'
996 key = 'header_verbose'
991 elif 'header' in self.t:
997 elif 'header' in self.t:
992 key = 'header'
998 key = 'header'
993 else:
999 else:
994 key = ''
1000 key = ''
995 if key:
1001 if key:
996 h = templater.stringify(self.t(key, **props))
1002 h = templater.stringify(self.t(key, **props))
997 if self.buffered:
1003 if self.buffered:
998 self.header[rev] = h
1004 self.header[rev] = h
999 else:
1005 else:
1000 self.ui.write(h)
1006 self.ui.write(h)
1001 if self.ui.debugflag and 'changeset_debug' in self.t:
1007 if self.ui.debugflag and 'changeset_debug' in self.t:
1002 key = 'changeset_debug'
1008 key = 'changeset_debug'
1003 elif self.ui.quiet and 'changeset_quiet' in self.t:
1009 elif self.ui.quiet and 'changeset_quiet' in self.t:
1004 key = 'changeset_quiet'
1010 key = 'changeset_quiet'
1005 elif self.ui.verbose and 'changeset_verbose' in self.t:
1011 elif self.ui.verbose and 'changeset_verbose' in self.t:
1006 key = 'changeset_verbose'
1012 key = 'changeset_verbose'
1007 else:
1013 else:
1008 key = 'changeset'
1014 key = 'changeset'
1009 self.ui.write(templater.stringify(self.t(key, **props)))
1015 self.ui.write(templater.stringify(self.t(key, **props)))
1010 self.showpatch(changenode)
1016 self.showpatch(changenode)
1011 except KeyError, inst:
1017 except KeyError, inst:
1012 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
1018 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
1013 inst.args[0]))
1019 inst.args[0]))
1014 except SyntaxError, inst:
1020 except SyntaxError, inst:
1015 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
1021 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
1016
1022
1017 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
1023 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
1018 """show one changeset using template or regular display.
1024 """show one changeset using template or regular display.
1019
1025
1020 Display format will be the first non-empty hit of:
1026 Display format will be the first non-empty hit of:
1021 1. option 'template'
1027 1. option 'template'
1022 2. option 'style'
1028 2. option 'style'
1023 3. [ui] setting 'logtemplate'
1029 3. [ui] setting 'logtemplate'
1024 4. [ui] setting 'style'
1030 4. [ui] setting 'style'
1025 If all of these values are either the unset or the empty string,
1031 If all of these values are either the unset or the empty string,
1026 regular display via changeset_printer() is done.
1032 regular display via changeset_printer() is done.
1027 """
1033 """
1028 # options
1034 # options
1029 patch = False
1035 patch = False
1030 if opts.get('patch'):
1036 if opts.get('patch'):
1031 patch = matchfn or util.always
1037 patch = matchfn or util.always
1032
1038
1033 tmpl = opts.get('template')
1039 tmpl = opts.get('template')
1034 mapfile = None
1040 mapfile = None
1035 if tmpl:
1041 if tmpl:
1036 tmpl = templater.parsestring(tmpl, quoted=False)
1042 tmpl = templater.parsestring(tmpl, quoted=False)
1037 else:
1043 else:
1038 mapfile = opts.get('style')
1044 mapfile = opts.get('style')
1039 # ui settings
1045 # ui settings
1040 if not mapfile:
1046 if not mapfile:
1041 tmpl = ui.config('ui', 'logtemplate')
1047 tmpl = ui.config('ui', 'logtemplate')
1042 if tmpl:
1048 if tmpl:
1043 tmpl = templater.parsestring(tmpl)
1049 tmpl = templater.parsestring(tmpl)
1044 else:
1050 else:
1045 mapfile = ui.config('ui', 'style')
1051 mapfile = ui.config('ui', 'style')
1046
1052
1047 if tmpl or mapfile:
1053 if tmpl or mapfile:
1048 if mapfile:
1054 if mapfile:
1049 if not os.path.split(mapfile)[0]:
1055 if not os.path.split(mapfile)[0]:
1050 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1056 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1051 or templater.templatepath(mapfile))
1057 or templater.templatepath(mapfile))
1052 if mapname: mapfile = mapname
1058 if mapname: mapfile = mapname
1053 try:
1059 try:
1054 t = changeset_templater(ui, repo, patch, mapfile, buffered)
1060 t = changeset_templater(ui, repo, patch, mapfile, buffered)
1055 except SyntaxError, inst:
1061 except SyntaxError, inst:
1056 raise util.Abort(inst.args[0])
1062 raise util.Abort(inst.args[0])
1057 if tmpl: t.use_template(tmpl)
1063 if tmpl: t.use_template(tmpl)
1058 return t
1064 return t
1059 return changeset_printer(ui, repo, patch, buffered)
1065 return changeset_printer(ui, repo, patch, buffered)
1060
1066
1061 def finddate(ui, repo, date):
1067 def finddate(ui, repo, date):
1062 """Find the tipmost changeset that matches the given date spec"""
1068 """Find the tipmost changeset that matches the given date spec"""
1063 df = util.matchdate(date + " to " + date)
1069 df = util.matchdate(date + " to " + date)
1064 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1070 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1065 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
1071 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
1066 results = {}
1072 results = {}
1067 for st, rev, fns in changeiter:
1073 for st, rev, fns in changeiter:
1068 if st == 'add':
1074 if st == 'add':
1069 d = get(rev)[2]
1075 d = get(rev)[2]
1070 if df(d[0]):
1076 if df(d[0]):
1071 results[rev] = d
1077 results[rev] = d
1072 elif st == 'iter':
1078 elif st == 'iter':
1073 if rev in results:
1079 if rev in results:
1074 ui.status("Found revision %s from %s\n" %
1080 ui.status("Found revision %s from %s\n" %
1075 (rev, util.datestr(results[rev])))
1081 (rev, util.datestr(results[rev])))
1076 return str(rev)
1082 return str(rev)
1077
1083
1078 raise util.Abort(_("revision matching date not found"))
1084 raise util.Abort(_("revision matching date not found"))
1079
1085
1080 def walkchangerevs(ui, repo, pats, change, opts):
1086 def walkchangerevs(ui, repo, pats, change, opts):
1081 '''Iterate over files and the revs they changed in.
1087 '''Iterate over files and the revs they changed in.
1082
1088
1083 Callers most commonly need to iterate backwards over the history
1089 Callers most commonly need to iterate backwards over the history
1084 it is interested in. Doing so has awful (quadratic-looking)
1090 it is interested in. Doing so has awful (quadratic-looking)
1085 performance, so we use iterators in a "windowed" way.
1091 performance, so we use iterators in a "windowed" way.
1086
1092
1087 We walk a window of revisions in the desired order. Within the
1093 We walk a window of revisions in the desired order. Within the
1088 window, we first walk forwards to gather data, then in the desired
1094 window, we first walk forwards to gather data, then in the desired
1089 order (usually backwards) to display it.
1095 order (usually backwards) to display it.
1090
1096
1091 This function returns an (iterator, matchfn) tuple. The iterator
1097 This function returns an (iterator, matchfn) tuple. The iterator
1092 yields 3-tuples. They will be of one of the following forms:
1098 yields 3-tuples. They will be of one of the following forms:
1093
1099
1094 "window", incrementing, lastrev: stepping through a window,
1100 "window", incrementing, lastrev: stepping through a window,
1095 positive if walking forwards through revs, last rev in the
1101 positive if walking forwards through revs, last rev in the
1096 sequence iterated over - use to reset state for the current window
1102 sequence iterated over - use to reset state for the current window
1097
1103
1098 "add", rev, fns: out-of-order traversal of the given file names
1104 "add", rev, fns: out-of-order traversal of the given file names
1099 fns, which changed during revision rev - use to gather data for
1105 fns, which changed during revision rev - use to gather data for
1100 possible display
1106 possible display
1101
1107
1102 "iter", rev, None: in-order traversal of the revs earlier iterated
1108 "iter", rev, None: in-order traversal of the revs earlier iterated
1103 over with "add" - use to display data'''
1109 over with "add" - use to display data'''
1104
1110
1105 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1111 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1106 if start < end:
1112 if start < end:
1107 while start < end:
1113 while start < end:
1108 yield start, min(windowsize, end-start)
1114 yield start, min(windowsize, end-start)
1109 start += windowsize
1115 start += windowsize
1110 if windowsize < sizelimit:
1116 if windowsize < sizelimit:
1111 windowsize *= 2
1117 windowsize *= 2
1112 else:
1118 else:
1113 while start > end:
1119 while start > end:
1114 yield start, min(windowsize, start-end-1)
1120 yield start, min(windowsize, start-end-1)
1115 start -= windowsize
1121 start -= windowsize
1116 if windowsize < sizelimit:
1122 if windowsize < sizelimit:
1117 windowsize *= 2
1123 windowsize *= 2
1118
1124
1119 files, matchfn, anypats = matchpats(repo, pats, opts)
1125 files, matchfn, anypats = matchpats(repo, pats, opts)
1120 follow = opts.get('follow') or opts.get('follow_first')
1126 follow = opts.get('follow') or opts.get('follow_first')
1121
1127
1122 if repo.changelog.count() == 0:
1128 if repo.changelog.count() == 0:
1123 return [], matchfn
1129 return [], matchfn
1124
1130
1125 if follow:
1131 if follow:
1126 defrange = '%s:0' % repo.changectx().rev()
1132 defrange = '%s:0' % repo.changectx().rev()
1127 else:
1133 else:
1128 defrange = 'tip:0'
1134 defrange = 'tip:0'
1129 revs = revrange(repo, opts['rev'] or [defrange])
1135 revs = revrange(repo, opts['rev'] or [defrange])
1130 wanted = {}
1136 wanted = {}
1131 slowpath = anypats or opts.get('removed')
1137 slowpath = anypats or opts.get('removed')
1132 fncache = {}
1138 fncache = {}
1133
1139
1134 if not slowpath and not files:
1140 if not slowpath and not files:
1135 # No files, no patterns. Display all revs.
1141 # No files, no patterns. Display all revs.
1136 wanted = dict.fromkeys(revs)
1142 wanted = dict.fromkeys(revs)
1137 copies = []
1143 copies = []
1138 if not slowpath:
1144 if not slowpath:
1139 # Only files, no patterns. Check the history of each file.
1145 # Only files, no patterns. Check the history of each file.
1140 def filerevgen(filelog, node):
1146 def filerevgen(filelog, node):
1141 cl_count = repo.changelog.count()
1147 cl_count = repo.changelog.count()
1142 if node is None:
1148 if node is None:
1143 last = filelog.count() - 1
1149 last = filelog.count() - 1
1144 else:
1150 else:
1145 last = filelog.rev(node)
1151 last = filelog.rev(node)
1146 for i, window in increasing_windows(last, nullrev):
1152 for i, window in increasing_windows(last, nullrev):
1147 revs = []
1153 revs = []
1148 for j in xrange(i - window, i + 1):
1154 for j in xrange(i - window, i + 1):
1149 n = filelog.node(j)
1155 n = filelog.node(j)
1150 revs.append((filelog.linkrev(n),
1156 revs.append((filelog.linkrev(n),
1151 follow and filelog.renamed(n)))
1157 follow and filelog.renamed(n)))
1152 revs.reverse()
1158 revs.reverse()
1153 for rev in revs:
1159 for rev in revs:
1154 # only yield rev for which we have the changelog, it can
1160 # only yield rev for which we have the changelog, it can
1155 # happen while doing "hg log" during a pull or commit
1161 # happen while doing "hg log" during a pull or commit
1156 if rev[0] < cl_count:
1162 if rev[0] < cl_count:
1157 yield rev
1163 yield rev
1158 def iterfiles():
1164 def iterfiles():
1159 for filename in files:
1165 for filename in files:
1160 yield filename, None
1166 yield filename, None
1161 for filename_node in copies:
1167 for filename_node in copies:
1162 yield filename_node
1168 yield filename_node
1163 minrev, maxrev = min(revs), max(revs)
1169 minrev, maxrev = min(revs), max(revs)
1164 for file_, node in iterfiles():
1170 for file_, node in iterfiles():
1165 filelog = repo.file(file_)
1171 filelog = repo.file(file_)
1166 # A zero count may be a directory or deleted file, so
1172 # A zero count may be a directory or deleted file, so
1167 # try to find matching entries on the slow path.
1173 # try to find matching entries on the slow path.
1168 if filelog.count() == 0:
1174 if filelog.count() == 0:
1169 slowpath = True
1175 slowpath = True
1170 break
1176 break
1171 for rev, copied in filerevgen(filelog, node):
1177 for rev, copied in filerevgen(filelog, node):
1172 if rev <= maxrev:
1178 if rev <= maxrev:
1173 if rev < minrev:
1179 if rev < minrev:
1174 break
1180 break
1175 fncache.setdefault(rev, [])
1181 fncache.setdefault(rev, [])
1176 fncache[rev].append(file_)
1182 fncache[rev].append(file_)
1177 wanted[rev] = 1
1183 wanted[rev] = 1
1178 if follow and copied:
1184 if follow and copied:
1179 copies.append(copied)
1185 copies.append(copied)
1180 if slowpath:
1186 if slowpath:
1181 if follow:
1187 if follow:
1182 raise util.Abort(_('can only follow copies/renames for explicit '
1188 raise util.Abort(_('can only follow copies/renames for explicit '
1183 'file names'))
1189 'file names'))
1184
1190
1185 # The slow path checks files modified in every changeset.
1191 # The slow path checks files modified in every changeset.
1186 def changerevgen():
1192 def changerevgen():
1187 for i, window in increasing_windows(repo.changelog.count()-1,
1193 for i, window in increasing_windows(repo.changelog.count()-1,
1188 nullrev):
1194 nullrev):
1189 for j in xrange(i - window, i + 1):
1195 for j in xrange(i - window, i + 1):
1190 yield j, change(j)[3]
1196 yield j, change(j)[3]
1191
1197
1192 for rev, changefiles in changerevgen():
1198 for rev, changefiles in changerevgen():
1193 matches = filter(matchfn, changefiles)
1199 matches = filter(matchfn, changefiles)
1194 if matches:
1200 if matches:
1195 fncache[rev] = matches
1201 fncache[rev] = matches
1196 wanted[rev] = 1
1202 wanted[rev] = 1
1197
1203
1198 class followfilter:
1204 class followfilter:
1199 def __init__(self, onlyfirst=False):
1205 def __init__(self, onlyfirst=False):
1200 self.startrev = nullrev
1206 self.startrev = nullrev
1201 self.roots = []
1207 self.roots = []
1202 self.onlyfirst = onlyfirst
1208 self.onlyfirst = onlyfirst
1203
1209
1204 def match(self, rev):
1210 def match(self, rev):
1205 def realparents(rev):
1211 def realparents(rev):
1206 if self.onlyfirst:
1212 if self.onlyfirst:
1207 return repo.changelog.parentrevs(rev)[0:1]
1213 return repo.changelog.parentrevs(rev)[0:1]
1208 else:
1214 else:
1209 return filter(lambda x: x != nullrev,
1215 return filter(lambda x: x != nullrev,
1210 repo.changelog.parentrevs(rev))
1216 repo.changelog.parentrevs(rev))
1211
1217
1212 if self.startrev == nullrev:
1218 if self.startrev == nullrev:
1213 self.startrev = rev
1219 self.startrev = rev
1214 return True
1220 return True
1215
1221
1216 if rev > self.startrev:
1222 if rev > self.startrev:
1217 # forward: all descendants
1223 # forward: all descendants
1218 if not self.roots:
1224 if not self.roots:
1219 self.roots.append(self.startrev)
1225 self.roots.append(self.startrev)
1220 for parent in realparents(rev):
1226 for parent in realparents(rev):
1221 if parent in self.roots:
1227 if parent in self.roots:
1222 self.roots.append(rev)
1228 self.roots.append(rev)
1223 return True
1229 return True
1224 else:
1230 else:
1225 # backwards: all parents
1231 # backwards: all parents
1226 if not self.roots:
1232 if not self.roots:
1227 self.roots.extend(realparents(self.startrev))
1233 self.roots.extend(realparents(self.startrev))
1228 if rev in self.roots:
1234 if rev in self.roots:
1229 self.roots.remove(rev)
1235 self.roots.remove(rev)
1230 self.roots.extend(realparents(rev))
1236 self.roots.extend(realparents(rev))
1231 return True
1237 return True
1232
1238
1233 return False
1239 return False
1234
1240
1235 # it might be worthwhile to do this in the iterator if the rev range
1241 # it might be worthwhile to do this in the iterator if the rev range
1236 # is descending and the prune args are all within that range
1242 # is descending and the prune args are all within that range
1237 for rev in opts.get('prune', ()):
1243 for rev in opts.get('prune', ()):
1238 rev = repo.changelog.rev(repo.lookup(rev))
1244 rev = repo.changelog.rev(repo.lookup(rev))
1239 ff = followfilter()
1245 ff = followfilter()
1240 stop = min(revs[0], revs[-1])
1246 stop = min(revs[0], revs[-1])
1241 for x in xrange(rev, stop-1, -1):
1247 for x in xrange(rev, stop-1, -1):
1242 if ff.match(x) and x in wanted:
1248 if ff.match(x) and x in wanted:
1243 del wanted[x]
1249 del wanted[x]
1244
1250
1245 def iterate():
1251 def iterate():
1246 if follow and not files:
1252 if follow and not files:
1247 ff = followfilter(onlyfirst=opts.get('follow_first'))
1253 ff = followfilter(onlyfirst=opts.get('follow_first'))
1248 def want(rev):
1254 def want(rev):
1249 if ff.match(rev) and rev in wanted:
1255 if ff.match(rev) and rev in wanted:
1250 return True
1256 return True
1251 return False
1257 return False
1252 else:
1258 else:
1253 def want(rev):
1259 def want(rev):
1254 return rev in wanted
1260 return rev in wanted
1255
1261
1256 for i, window in increasing_windows(0, len(revs)):
1262 for i, window in increasing_windows(0, len(revs)):
1257 yield 'window', revs[0] < revs[-1], revs[-1]
1263 yield 'window', revs[0] < revs[-1], revs[-1]
1258 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1264 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1259 srevs = list(nrevs)
1265 srevs = list(nrevs)
1260 srevs.sort()
1266 srevs.sort()
1261 for rev in srevs:
1267 for rev in srevs:
1262 fns = fncache.get(rev)
1268 fns = fncache.get(rev)
1263 if not fns:
1269 if not fns:
1264 def fns_generator():
1270 def fns_generator():
1265 for f in change(rev)[3]:
1271 for f in change(rev)[3]:
1266 if matchfn(f):
1272 if matchfn(f):
1267 yield f
1273 yield f
1268 fns = fns_generator()
1274 fns = fns_generator()
1269 yield 'add', rev, fns
1275 yield 'add', rev, fns
1270 for rev in nrevs:
1276 for rev in nrevs:
1271 yield 'iter', rev, None
1277 yield 'iter', rev, None
1272 return iterate(), matchfn
1278 return iterate(), matchfn
@@ -1,119 +1,124 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5 echo a > a
5 echo a > a
6 hg add a
6 hg add a
7 echo line 1 > b
7 echo line 1 > b
8 echo line 2 >> b
8 echo line 2 >> b
9 hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
9 hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
10 hg add b
10 hg add b
11 echo other 1 > c
11 echo other 1 > c
12 echo other 2 >> c
12 echo other 2 >> c
13 echo >> c
13 echo >> c
14 echo other 3 >> c
14 echo other 3 >> c
15 hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
16 hg add c
16 hg add c
17 hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 echo c >> c
18 echo c >> c
19 hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 echo foo > .hg/branch
20 echo foo > .hg/branch
21 hg commit -m 'new branch' -d '1400000 0' -u 'person'
21 hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 hg co -q 3
22 hg co -q 3
23 echo other 4 >> d
23 echo other 4 >> d
24 hg add d
24 hg add d
25 hg commit -m 'new head' -d '1500000 0' -u 'person'
25 hg commit -m 'new head' -d '1500000 0' -u 'person'
26 hg merge -q
26 hg merge -q
27 hg commit -m 'merge' -d '1500001 0' -u 'person'
27 hg commit -m 'merge' -d '1500001 0' -u 'person'
28 # second branch starting at nullrev
29 hg update null
30 echo second > second
31 hg add second
32 hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
28
33
29 # make sure user/global hgrc does not affect tests
34 # make sure user/global hgrc does not affect tests
30 echo '[ui]' > .hg/hgrc
35 echo '[ui]' > .hg/hgrc
31 echo 'logtemplate =' >> .hg/hgrc
36 echo 'logtemplate =' >> .hg/hgrc
32 echo 'style =' >> .hg/hgrc
37 echo 'style =' >> .hg/hgrc
33
38
34 echo '# default style is like normal output'
39 echo '# default style is like normal output'
35 echo '# normal'
40 echo '# normal'
36 hg log > log.out
41 hg log > log.out
37 hg log --style default > style.out
42 hg log --style default > style.out
38 diff log.out style.out
43 diff log.out style.out
39 echo '# verbose'
44 echo '# verbose'
40 hg log -v > log.out
45 hg log -v > log.out
41 hg log -v --style default > style.out
46 hg log -v --style default > style.out
42 diff log.out style.out
47 diff log.out style.out
43 echo '# debug'
48 echo '# debug'
44 hg log --debug > log.out
49 hg log --debug > log.out
45 hg log --debug --style default > style.out
50 hg log --debug --style default > style.out
46 diff log.out style.out
51 diff log.out style.out
47
52
48 echo '# revision with no copies (used to print a traceback)'
53 echo '# revision with no copies (used to print a traceback)'
49 hg tip -v --template '\n'
54 hg tip -v --template '\n'
50
55
51 echo '# compact style works'
56 echo '# compact style works'
52 hg log --style compact
57 hg log --style compact
53 hg log -v --style compact
58 hg log -v --style compact
54 hg log --debug --style compact
59 hg log --debug --style compact
55
60
56 echo '# error if style not readable'
61 echo '# error if style not readable'
57 touch q
62 touch q
58 chmod 0 q
63 chmod 0 q
59 hg log --style ./q
64 hg log --style ./q
60
65
61 echo '# error if no style'
66 echo '# error if no style'
62 hg log --style notexist
67 hg log --style notexist
63
68
64 echo '# error if style missing key'
69 echo '# error if style missing key'
65 echo 'q = q' > t
70 echo 'q = q' > t
66 hg log --style ./t
71 hg log --style ./t
67
72
68 echo '# error if include fails'
73 echo '# error if include fails'
69 echo 'changeset = q' >> t
74 echo 'changeset = q' >> t
70 hg log --style ./t
75 hg log --style ./t
71
76
72 echo '# include works'
77 echo '# include works'
73 rm q
78 rm q
74 echo '{rev}' > q
79 echo '{rev}' > q
75 hg log --style ./t
80 hg log --style ./t
76
81
77 echo '# ui.style works'
82 echo '# ui.style works'
78 echo '[ui]' > .hg/hgrc
83 echo '[ui]' > .hg/hgrc
79 echo 'style = t' >> .hg/hgrc
84 echo 'style = t' >> .hg/hgrc
80 hg log
85 hg log
81
86
82 echo '# issue338'
87 echo '# issue338'
83 hg log --style=changelog > changelog
88 hg log --style=changelog > changelog
84 cat changelog
89 cat changelog
85
90
86 echo "# keys work"
91 echo "# keys work"
87 for key in author branches date desc file_adds file_dels files \
92 for key in author branches date desc file_adds file_dels files \
88 manifest node parents rev tags; do
93 manifest node parents rev tags; do
89 for mode in '' --verbose --debug; do
94 for mode in '' --verbose --debug; do
90 hg log $mode --template "$key$mode: {$key}\n"
95 hg log $mode --template "$key$mode: {$key}\n"
91 done
96 done
92 done
97 done
93
98
94 echo '# filters work'
99 echo '# filters work'
95 hg log --template '{author|domain}\n'
100 hg log --template '{author|domain}\n'
96 hg log --template '{author|person}\n'
101 hg log --template '{author|person}\n'
97 hg log --template '{author|user}\n'
102 hg log --template '{author|user}\n'
98 hg log --template '{date|age}\n' > /dev/null || exit 1
103 hg log --template '{date|age}\n' > /dev/null || exit 1
99 hg log --template '{date|date}\n'
104 hg log --template '{date|date}\n'
100 hg log --template '{date|isodate}\n'
105 hg log --template '{date|isodate}\n'
101 hg log --template '{date|rfc822date}\n'
106 hg log --template '{date|rfc822date}\n'
102 hg log --template '{desc|firstline}\n'
107 hg log --template '{desc|firstline}\n'
103 hg log --template '{node|short}\n'
108 hg log --template '{node|short}\n'
104
109
105 echo '# formatnode filter works'
110 echo '# formatnode filter works'
106 echo '# quiet'
111 echo '# quiet'
107 hg -q log -r 0 --template '#node|formatnode#\n'
112 hg -q log -r 0 --template '#node|formatnode#\n'
108 echo '# normal'
113 echo '# normal'
109 hg log -r 0 --template '#node|formatnode#\n'
114 hg log -r 0 --template '#node|formatnode#\n'
110 echo '# verbose'
115 echo '# verbose'
111 hg -v log -r 0 --template '#node|formatnode#\n'
116 hg -v log -r 0 --template '#node|formatnode#\n'
112 echo '# debug'
117 echo '# debug'
113 hg --debug log -r 0 --template '#node|formatnode#\n'
118 hg --debug log -r 0 --template '#node|formatnode#\n'
114
119
115 echo '# error on syntax'
120 echo '# error on syntax'
116 echo 'x = "f' >> t
121 echo 'x = "f' >> t
117 hg log
122 hg log
118
123
119 echo '# done'
124 echo '# done'
@@ -1,469 +1,531 b''
1 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1 # default style is like normal output
2 # default style is like normal output
2 # normal
3 # normal
3 # verbose
4 # verbose
4 # debug
5 # debug
5 # revision with no copies (used to print a traceback)
6 # revision with no copies (used to print a traceback)
6
7
7 # compact style works
8 # compact style works
8 6[tip]:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
9 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
10 second
11
12 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
9 merge
13 merge
10
14
11 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
15 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
12 new head
16 new head
13
17
14 4 32a18f097fcc 1970-01-17 04:53 +0000 person
18 4 32a18f097fcc 1970-01-17 04:53 +0000 person
15 new branch
19 new branch
16
20
17 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
21 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
18 no user, no domain
22 no user, no domain
19
23
20 2 97054abb4ab8 1970-01-14 21:20 +0000 other
24 2 97054abb4ab8 1970-01-14 21:20 +0000 other
21 no person
25 no person
22
26
23 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
27 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
24 other 1
28 other 1
25
29
26 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
30 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
27 line 1
31 line 1
28
32
29 6[tip]:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
33 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
34 second
35
36 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
30 merge
37 merge
31
38
32 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
39 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
33 new head
40 new head
34
41
35 4 32a18f097fcc 1970-01-17 04:53 +0000 person
42 4 32a18f097fcc 1970-01-17 04:53 +0000 person
36 new branch
43 new branch
37
44
38 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
45 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
39 no user, no domain
46 no user, no domain
40
47
41 2 97054abb4ab8 1970-01-14 21:20 +0000 other
48 2 97054abb4ab8 1970-01-14 21:20 +0000 other
42 no person
49 no person
43
50
44 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
51 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
45 other 1
52 other 1
46
53
47 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
54 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
48 line 1
55 line 1
49
56
50 6[tip]:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
57 7[tip]:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 user
58 second
59
60 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
51 merge
61 merge
52
62
53 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
63 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
54 new head
64 new head
55
65
56 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
66 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
57 new branch
67 new branch
58
68
59 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
69 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
60 no user, no domain
70 no user, no domain
61
71
62 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other
72 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other
63 no person
73 no person
64
74
65 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
75 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
66 other 1
76 other 1
67
77
68 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
78 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
69 line 1
79 line 1
70
80
71 # error if style not readable
81 # error if style not readable
72 abort: Permission denied: ./q
82 abort: Permission denied: ./q
73 # error if no style
83 # error if no style
74 abort: No such file or directory: notexist
84 abort: No such file or directory: notexist
75 # error if style missing key
85 # error if style missing key
76 abort: ./t: no key named 'changeset'
86 abort: ./t: no key named 'changeset'
77 # error if include fails
87 # error if include fails
78 abort: template file ./q: Permission denied
88 abort: template file ./q: Permission denied
79 # include works
89 # include works
90 7
80 6
91 6
81 5
92 5
82 4
93 4
83 3
94 3
84 2
95 2
85 1
96 1
86 0
97 0
87 # ui.style works
98 # ui.style works
99 7
88 6
100 6
89 5
101 5
90 4
102 4
91 3
103 3
92 2
104 2
93 1
105 1
94 0
106 0
95 # issue338
107 # issue338
108 1970-01-12 User Name <user@hostname>
109
110 * second:
111 second
112 [29114dbae42b] [tip]
113
96 1970-01-18 person <person>
114 1970-01-18 person <person>
97
115
98 * merge
116 * merge
99 [c7b487c6c50e] [tip]
117 [c7b487c6c50e]
100
118
101 * d:
119 * d:
102 new head
120 new head
103 [13207e5a10d9]
121 [13207e5a10d9]
104
122
105 1970-01-17 person <person>
123 1970-01-17 person <person>
106
124
107 * new branch
125 * new branch
108 [32a18f097fcc]
126 [32a18f097fcc]
109
127
110 1970-01-16 person <person>
128 1970-01-16 person <person>
111
129
112 * c:
130 * c:
113 no user, no domain
131 no user, no domain
114 [10e46f2dcbf4]
132 [10e46f2dcbf4]
115
133
116 1970-01-14 other <other@place>
134 1970-01-14 other <other@place>
117
135
118 * c:
136 * c:
119 no person
137 no person
120 [97054abb4ab8]
138 [97054abb4ab8]
121
139
122 1970-01-13 A. N. Other <other@place>
140 1970-01-13 A. N. Other <other@place>
123
141
124 * b:
142 * b:
125 other 1 other 2
143 other 1 other 2
126
144
127 other 3
145 other 3
128 [b608e9d1a3f0]
146 [b608e9d1a3f0]
129
147
130 1970-01-12 User Name <user@hostname>
148 1970-01-12 User Name <user@hostname>
131
149
132 * a:
150 * a:
133 line 1 line 2
151 line 1 line 2
134 [1e4e1b8f71e0]
152 [1e4e1b8f71e0]
135
153
136 # keys work
154 # keys work
155 author: User Name <user@hostname>
137 author: person
156 author: person
138 author: person
157 author: person
139 author: person
158 author: person
140 author: person
159 author: person
141 author: other@place
160 author: other@place
142 author: A. N. Other <other@place>
161 author: A. N. Other <other@place>
143 author: User Name <user@hostname>
162 author: User Name <user@hostname>
163 author--verbose: User Name <user@hostname>
144 author--verbose: person
164 author--verbose: person
145 author--verbose: person
165 author--verbose: person
146 author--verbose: person
166 author--verbose: person
147 author--verbose: person
167 author--verbose: person
148 author--verbose: other@place
168 author--verbose: other@place
149 author--verbose: A. N. Other <other@place>
169 author--verbose: A. N. Other <other@place>
150 author--verbose: User Name <user@hostname>
170 author--verbose: User Name <user@hostname>
171 author--debug: User Name <user@hostname>
151 author--debug: person
172 author--debug: person
152 author--debug: person
173 author--debug: person
153 author--debug: person
174 author--debug: person
154 author--debug: person
175 author--debug: person
155 author--debug: other@place
176 author--debug: other@place
156 author--debug: A. N. Other <other@place>
177 author--debug: A. N. Other <other@place>
157 author--debug: User Name <user@hostname>
178 author--debug: User Name <user@hostname>
158 branches:
179 branches:
159 branches:
180 branches:
181 branches:
160 branches: foo
182 branches: foo
161 branches:
183 branches:
162 branches:
184 branches:
163 branches:
185 branches:
164 branches:
186 branches:
165 branches--verbose:
187 branches--verbose:
166 branches--verbose:
188 branches--verbose:
189 branches--verbose:
167 branches--verbose: foo
190 branches--verbose: foo
168 branches--verbose:
191 branches--verbose:
169 branches--verbose:
192 branches--verbose:
170 branches--verbose:
193 branches--verbose:
171 branches--verbose:
194 branches--verbose:
172 branches--debug:
195 branches--debug:
173 branches--debug:
196 branches--debug:
197 branches--debug:
174 branches--debug: foo
198 branches--debug: foo
175 branches--debug:
199 branches--debug:
176 branches--debug:
200 branches--debug:
177 branches--debug:
201 branches--debug:
178 branches--debug:
202 branches--debug:
203 date: 1000000.00
179 date: 1500001.00
204 date: 1500001.00
180 date: 1500000.00
205 date: 1500000.00
181 date: 1400000.00
206 date: 1400000.00
182 date: 1300000.00
207 date: 1300000.00
183 date: 1200000.00
208 date: 1200000.00
184 date: 1100000.00
209 date: 1100000.00
185 date: 1000000.00
210 date: 1000000.00
211 date--verbose: 1000000.00
186 date--verbose: 1500001.00
212 date--verbose: 1500001.00
187 date--verbose: 1500000.00
213 date--verbose: 1500000.00
188 date--verbose: 1400000.00
214 date--verbose: 1400000.00
189 date--verbose: 1300000.00
215 date--verbose: 1300000.00
190 date--verbose: 1200000.00
216 date--verbose: 1200000.00
191 date--verbose: 1100000.00
217 date--verbose: 1100000.00
192 date--verbose: 1000000.00
218 date--verbose: 1000000.00
219 date--debug: 1000000.00
193 date--debug: 1500001.00
220 date--debug: 1500001.00
194 date--debug: 1500000.00
221 date--debug: 1500000.00
195 date--debug: 1400000.00
222 date--debug: 1400000.00
196 date--debug: 1300000.00
223 date--debug: 1300000.00
197 date--debug: 1200000.00
224 date--debug: 1200000.00
198 date--debug: 1100000.00
225 date--debug: 1100000.00
199 date--debug: 1000000.00
226 date--debug: 1000000.00
227 desc: second
200 desc: merge
228 desc: merge
201 desc: new head
229 desc: new head
202 desc: new branch
230 desc: new branch
203 desc: no user, no domain
231 desc: no user, no domain
204 desc: no person
232 desc: no person
205 desc: other 1
233 desc: other 1
206 other 2
234 other 2
207
235
208 other 3
236 other 3
209 desc: line 1
237 desc: line 1
210 line 2
238 line 2
239 desc--verbose: second
211 desc--verbose: merge
240 desc--verbose: merge
212 desc--verbose: new head
241 desc--verbose: new head
213 desc--verbose: new branch
242 desc--verbose: new branch
214 desc--verbose: no user, no domain
243 desc--verbose: no user, no domain
215 desc--verbose: no person
244 desc--verbose: no person
216 desc--verbose: other 1
245 desc--verbose: other 1
217 other 2
246 other 2
218
247
219 other 3
248 other 3
220 desc--verbose: line 1
249 desc--verbose: line 1
221 line 2
250 line 2
251 desc--debug: second
222 desc--debug: merge
252 desc--debug: merge
223 desc--debug: new head
253 desc--debug: new head
224 desc--debug: new branch
254 desc--debug: new branch
225 desc--debug: no user, no domain
255 desc--debug: no user, no domain
226 desc--debug: no person
256 desc--debug: no person
227 desc--debug: other 1
257 desc--debug: other 1
228 other 2
258 other 2
229
259
230 other 3
260 other 3
231 desc--debug: line 1
261 desc--debug: line 1
232 line 2
262 line 2
233 file_adds:
263 file_adds:
234 file_adds:
264 file_adds:
235 file_adds:
265 file_adds:
236 file_adds:
266 file_adds:
237 file_adds:
267 file_adds:
238 file_adds:
268 file_adds:
239 file_adds:
269 file_adds:
270 file_adds:
271 file_adds--verbose:
240 file_adds--verbose:
272 file_adds--verbose:
241 file_adds--verbose:
273 file_adds--verbose:
242 file_adds--verbose:
274 file_adds--verbose:
243 file_adds--verbose:
275 file_adds--verbose:
244 file_adds--verbose:
276 file_adds--verbose:
245 file_adds--verbose:
277 file_adds--verbose:
246 file_adds--verbose:
278 file_adds--verbose:
279 file_adds--debug: second
247 file_adds--debug:
280 file_adds--debug:
248 file_adds--debug: d
281 file_adds--debug: d
249 file_adds--debug:
282 file_adds--debug:
250 file_adds--debug:
283 file_adds--debug:
251 file_adds--debug: c
284 file_adds--debug: c
252 file_adds--debug: b
285 file_adds--debug: b
253 file_adds--debug: a
286 file_adds--debug: a
254 file_dels:
287 file_dels:
255 file_dels:
288 file_dels:
256 file_dels:
289 file_dels:
257 file_dels:
290 file_dels:
258 file_dels:
291 file_dels:
259 file_dels:
292 file_dels:
260 file_dels:
293 file_dels:
294 file_dels:
261 file_dels--verbose:
295 file_dels--verbose:
262 file_dels--verbose:
296 file_dels--verbose:
263 file_dels--verbose:
297 file_dels--verbose:
264 file_dels--verbose:
298 file_dels--verbose:
265 file_dels--verbose:
299 file_dels--verbose:
266 file_dels--verbose:
300 file_dels--verbose:
267 file_dels--verbose:
301 file_dels--verbose:
302 file_dels--verbose:
303 file_dels--debug:
268 file_dels--debug:
304 file_dels--debug:
269 file_dels--debug:
305 file_dels--debug:
270 file_dels--debug:
306 file_dels--debug:
271 file_dels--debug:
307 file_dels--debug:
272 file_dels--debug:
308 file_dels--debug:
273 file_dels--debug:
309 file_dels--debug:
274 file_dels--debug:
310 file_dels--debug:
311 files: second
275 files:
312 files:
276 files: d
313 files: d
277 files:
314 files:
278 files: c
315 files: c
279 files: c
316 files: c
280 files: b
317 files: b
281 files: a
318 files: a
319 files--verbose: second
282 files--verbose:
320 files--verbose:
283 files--verbose: d
321 files--verbose: d
284 files--verbose:
322 files--verbose:
285 files--verbose: c
323 files--verbose: c
286 files--verbose: c
324 files--verbose: c
287 files--verbose: b
325 files--verbose: b
288 files--verbose: a
326 files--verbose: a
289 files--debug:
327 files--debug:
290 files--debug:
328 files--debug:
291 files--debug:
329 files--debug:
330 files--debug:
292 files--debug: c
331 files--debug: c
293 files--debug:
332 files--debug:
294 files--debug:
333 files--debug:
295 files--debug:
334 files--debug:
296 manifest:
335 manifest:
297 manifest:
336 manifest:
298 manifest:
337 manifest:
299 manifest:
338 manifest:
300 manifest:
339 manifest:
301 manifest:
340 manifest:
302 manifest:
341 manifest:
342 manifest:
303 manifest--verbose:
343 manifest--verbose:
304 manifest--verbose:
344 manifest--verbose:
305 manifest--verbose:
345 manifest--verbose:
306 manifest--verbose:
346 manifest--verbose:
307 manifest--verbose:
347 manifest--verbose:
308 manifest--verbose:
348 manifest--verbose:
309 manifest--verbose:
349 manifest--verbose:
350 manifest--verbose:
351 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
310 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
352 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
311 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
353 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
312 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
354 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
313 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
355 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
314 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
356 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
315 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
357 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
316 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
358 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
359 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
317 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
360 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
318 node: 13207e5a10d9fd28ec424934298e176197f2c67f
361 node: 13207e5a10d9fd28ec424934298e176197f2c67f
319 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
362 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
320 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
363 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
321 node: 97054abb4ab824450e9164180baf491ae0078465
364 node: 97054abb4ab824450e9164180baf491ae0078465
322 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
365 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
323 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
366 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
367 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
324 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
368 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
325 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
369 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
326 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
370 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
327 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
371 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
328 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
372 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
329 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
373 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
330 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
374 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
375 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
331 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
376 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
332 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
377 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
333 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
378 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
334 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
379 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
335 node--debug: 97054abb4ab824450e9164180baf491ae0078465
380 node--debug: 97054abb4ab824450e9164180baf491ae0078465
336 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
381 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
337 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
382 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
383 parents: -1:000000000000
338 parents: 5:13207e5a10d9 4:32a18f097fcc
384 parents: 5:13207e5a10d9 4:32a18f097fcc
339 parents: 3:10e46f2dcbf4
385 parents: 3:10e46f2dcbf4
340 parents:
386 parents:
341 parents:
387 parents:
342 parents:
388 parents:
343 parents:
389 parents:
344 parents:
390 parents:
391 parents--verbose: -1:000000000000
345 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
392 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
346 parents--verbose: 3:10e46f2dcbf4
393 parents--verbose: 3:10e46f2dcbf4
347 parents--verbose:
394 parents--verbose:
348 parents--verbose:
395 parents--verbose:
349 parents--verbose:
396 parents--verbose:
350 parents--verbose:
397 parents--verbose:
351 parents--verbose:
398 parents--verbose:
399 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
352 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
400 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
353 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
401 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
354 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
402 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
355 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
403 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
356 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
404 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
357 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
405 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
358 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
406 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
407 rev: 7
359 rev: 6
408 rev: 6
360 rev: 5
409 rev: 5
361 rev: 4
410 rev: 4
362 rev: 3
411 rev: 3
363 rev: 2
412 rev: 2
364 rev: 1
413 rev: 1
365 rev: 0
414 rev: 0
415 rev--verbose: 7
366 rev--verbose: 6
416 rev--verbose: 6
367 rev--verbose: 5
417 rev--verbose: 5
368 rev--verbose: 4
418 rev--verbose: 4
369 rev--verbose: 3
419 rev--verbose: 3
370 rev--verbose: 2
420 rev--verbose: 2
371 rev--verbose: 1
421 rev--verbose: 1
372 rev--verbose: 0
422 rev--verbose: 0
423 rev--debug: 7
373 rev--debug: 6
424 rev--debug: 6
374 rev--debug: 5
425 rev--debug: 5
375 rev--debug: 4
426 rev--debug: 4
376 rev--debug: 3
427 rev--debug: 3
377 rev--debug: 2
428 rev--debug: 2
378 rev--debug: 1
429 rev--debug: 1
379 rev--debug: 0
430 rev--debug: 0
380 tags: tip
431 tags: tip
381 tags:
432 tags:
382 tags:
433 tags:
383 tags:
434 tags:
384 tags:
435 tags:
385 tags:
436 tags:
386 tags:
437 tags:
438 tags:
387 tags--verbose: tip
439 tags--verbose: tip
388 tags--verbose:
440 tags--verbose:
389 tags--verbose:
441 tags--verbose:
390 tags--verbose:
442 tags--verbose:
391 tags--verbose:
443 tags--verbose:
392 tags--verbose:
444 tags--verbose:
393 tags--verbose:
445 tags--verbose:
446 tags--verbose:
394 tags--debug: tip
447 tags--debug: tip
395 tags--debug:
448 tags--debug:
396 tags--debug:
449 tags--debug:
397 tags--debug:
450 tags--debug:
398 tags--debug:
451 tags--debug:
399 tags--debug:
452 tags--debug:
400 tags--debug:
453 tags--debug:
454 tags--debug:
401 # filters work
455 # filters work
456 hostname
402
457
403
458
404
459
405
460
406 place
461 place
407 place
462 place
408 hostname
463 hostname
464 User Name
409 person
465 person
410 person
466 person
411 person
467 person
412 person
468 person
413 other
469 other
414 A. N. Other
470 A. N. Other
415 User Name
471 User Name
472 user
416 person
473 person
417 person
474 person
418 person
475 person
419 person
476 person
420 other
477 other
421 other
478 other
422 user
479 user
480 Mon Jan 12 13:46:40 1970 +0000
423 Sun Jan 18 08:40:01 1970 +0000
481 Sun Jan 18 08:40:01 1970 +0000
424 Sun Jan 18 08:40:00 1970 +0000
482 Sun Jan 18 08:40:00 1970 +0000
425 Sat Jan 17 04:53:20 1970 +0000
483 Sat Jan 17 04:53:20 1970 +0000
426 Fri Jan 16 01:06:40 1970 +0000
484 Fri Jan 16 01:06:40 1970 +0000
427 Wed Jan 14 21:20:00 1970 +0000
485 Wed Jan 14 21:20:00 1970 +0000
428 Tue Jan 13 17:33:20 1970 +0000
486 Tue Jan 13 17:33:20 1970 +0000
429 Mon Jan 12 13:46:40 1970 +0000
487 Mon Jan 12 13:46:40 1970 +0000
488 1970-01-12 13:46 +0000
430 1970-01-18 08:40 +0000
489 1970-01-18 08:40 +0000
431 1970-01-18 08:40 +0000
490 1970-01-18 08:40 +0000
432 1970-01-17 04:53 +0000
491 1970-01-17 04:53 +0000
433 1970-01-16 01:06 +0000
492 1970-01-16 01:06 +0000
434 1970-01-14 21:20 +0000
493 1970-01-14 21:20 +0000
435 1970-01-13 17:33 +0000
494 1970-01-13 17:33 +0000
436 1970-01-12 13:46 +0000
495 1970-01-12 13:46 +0000
496 Mon, 12 Jan 1970 13:46:40 +0000
437 Sun, 18 Jan 1970 08:40:01 +0000
497 Sun, 18 Jan 1970 08:40:01 +0000
438 Sun, 18 Jan 1970 08:40:00 +0000
498 Sun, 18 Jan 1970 08:40:00 +0000
439 Sat, 17 Jan 1970 04:53:20 +0000
499 Sat, 17 Jan 1970 04:53:20 +0000
440 Fri, 16 Jan 1970 01:06:40 +0000
500 Fri, 16 Jan 1970 01:06:40 +0000
441 Wed, 14 Jan 1970 21:20:00 +0000
501 Wed, 14 Jan 1970 21:20:00 +0000
442 Tue, 13 Jan 1970 17:33:20 +0000
502 Tue, 13 Jan 1970 17:33:20 +0000
443 Mon, 12 Jan 1970 13:46:40 +0000
503 Mon, 12 Jan 1970 13:46:40 +0000
504 second
444 merge
505 merge
445 new head
506 new head
446 new branch
507 new branch
447 no user, no domain
508 no user, no domain
448 no person
509 no person
449 other 1
510 other 1
450 line 1
511 line 1
512 29114dbae42b
451 c7b487c6c50e
513 c7b487c6c50e
452 13207e5a10d9
514 13207e5a10d9
453 32a18f097fcc
515 32a18f097fcc
454 10e46f2dcbf4
516 10e46f2dcbf4
455 97054abb4ab8
517 97054abb4ab8
456 b608e9d1a3f0
518 b608e9d1a3f0
457 1e4e1b8f71e0
519 1e4e1b8f71e0
458 # formatnode filter works
520 # formatnode filter works
459 # quiet
521 # quiet
460 1e4e1b8f71e0
522 1e4e1b8f71e0
461 # normal
523 # normal
462 1e4e1b8f71e0
524 1e4e1b8f71e0
463 # verbose
525 # verbose
464 1e4e1b8f71e0
526 1e4e1b8f71e0
465 # debug
527 # debug
466 1e4e1b8f71e05681d422154f5421e385fec3454f
528 1e4e1b8f71e05681d422154f5421e385fec3454f
467 # error on syntax
529 # error on syntax
468 abort: t:3: unmatched quotes
530 abort: t:3: unmatched quotes
469 # done
531 # done
General Comments 0
You need to be logged in to leave comments. Login now