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