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