##// END OF EJS Templates
cmdutil: always expose "files_add", "files_del" and "manifest" templater properties
Patrick Mezard -
r5545:5a124ce4 default
parent child Browse files
Show More
@@ -1,944 +1,949 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, patch
11 import mdiff, bdiff, util, templater, patch
12
12
13 revrangesep = ':'
13 revrangesep = ':'
14
14
15 class UnknownCommand(Exception):
15 class UnknownCommand(Exception):
16 """Exception raised if command is not in the command table."""
16 """Exception raised if command is not in the command table."""
17 class AmbiguousCommand(Exception):
17 class AmbiguousCommand(Exception):
18 """Exception raised if command shortcut matches more than one command."""
18 """Exception raised if command shortcut matches more than one command."""
19
19
20 def findpossible(ui, cmd, table):
20 def findpossible(ui, cmd, table):
21 """
21 """
22 Return cmd -> (aliases, command table entry)
22 Return cmd -> (aliases, command table entry)
23 for each matching command.
23 for each matching command.
24 Return debug commands (or their aliases) only if no normal command matches.
24 Return debug commands (or their aliases) only if no normal command matches.
25 """
25 """
26 choice = {}
26 choice = {}
27 debugchoice = {}
27 debugchoice = {}
28 for e in table.keys():
28 for e in table.keys():
29 aliases = e.lstrip("^").split("|")
29 aliases = e.lstrip("^").split("|")
30 found = None
30 found = None
31 if cmd in aliases:
31 if cmd in aliases:
32 found = cmd
32 found = cmd
33 elif not ui.config("ui", "strict"):
33 elif not ui.config("ui", "strict"):
34 for a in aliases:
34 for a in aliases:
35 if a.startswith(cmd):
35 if a.startswith(cmd):
36 found = a
36 found = a
37 break
37 break
38 if found is not None:
38 if found is not None:
39 if aliases[0].startswith("debug") or found.startswith("debug"):
39 if aliases[0].startswith("debug") or found.startswith("debug"):
40 debugchoice[found] = (aliases, table[e])
40 debugchoice[found] = (aliases, table[e])
41 else:
41 else:
42 choice[found] = (aliases, table[e])
42 choice[found] = (aliases, table[e])
43
43
44 if not choice and debugchoice:
44 if not choice and debugchoice:
45 choice = debugchoice
45 choice = debugchoice
46
46
47 return choice
47 return choice
48
48
49 def findcmd(ui, cmd, table):
49 def findcmd(ui, cmd, table):
50 """Return (aliases, command table entry) for command string."""
50 """Return (aliases, command table entry) for command string."""
51 choice = findpossible(ui, cmd, table)
51 choice = findpossible(ui, cmd, table)
52
52
53 if choice.has_key(cmd):
53 if choice.has_key(cmd):
54 return choice[cmd]
54 return choice[cmd]
55
55
56 if len(choice) > 1:
56 if len(choice) > 1:
57 clist = choice.keys()
57 clist = choice.keys()
58 clist.sort()
58 clist.sort()
59 raise AmbiguousCommand(cmd, clist)
59 raise AmbiguousCommand(cmd, clist)
60
60
61 if choice:
61 if choice:
62 return choice.values()[0]
62 return choice.values()[0]
63
63
64 raise UnknownCommand(cmd)
64 raise UnknownCommand(cmd)
65
65
66 def bail_if_changed(repo):
66 def bail_if_changed(repo):
67 modified, added, removed, deleted = repo.status()[:4]
67 modified, added, removed, deleted = repo.status()[:4]
68 if modified or added or removed or deleted:
68 if modified or added or removed or deleted:
69 raise util.Abort(_("outstanding uncommitted changes"))
69 raise util.Abort(_("outstanding uncommitted changes"))
70
70
71 def logmessage(opts):
71 def logmessage(opts):
72 """ get the log message according to -m and -l option """
72 """ get the log message according to -m and -l option """
73 message = opts['message']
73 message = opts['message']
74 logfile = opts['logfile']
74 logfile = opts['logfile']
75
75
76 if message and logfile:
76 if message and logfile:
77 raise util.Abort(_('options --message and --logfile are mutually '
77 raise util.Abort(_('options --message and --logfile are mutually '
78 'exclusive'))
78 'exclusive'))
79 if not message and logfile:
79 if not message and logfile:
80 try:
80 try:
81 if logfile == '-':
81 if logfile == '-':
82 message = sys.stdin.read()
82 message = sys.stdin.read()
83 else:
83 else:
84 message = open(logfile).read()
84 message = open(logfile).read()
85 except IOError, inst:
85 except IOError, inst:
86 raise util.Abort(_("can't read commit message '%s': %s") %
86 raise util.Abort(_("can't read commit message '%s': %s") %
87 (logfile, inst.strerror))
87 (logfile, inst.strerror))
88 return message
88 return message
89
89
90 def setremoteconfig(ui, opts):
90 def setremoteconfig(ui, opts):
91 "copy remote options to ui tree"
91 "copy remote options to ui tree"
92 if opts.get('ssh'):
92 if opts.get('ssh'):
93 ui.setconfig("ui", "ssh", opts['ssh'])
93 ui.setconfig("ui", "ssh", opts['ssh'])
94 if opts.get('remotecmd'):
94 if opts.get('remotecmd'):
95 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
95 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
96
96
97 def revpair(repo, revs):
97 def revpair(repo, revs):
98 '''return pair of nodes, given list of revisions. second item can
98 '''return pair of nodes, given list of revisions. second item can
99 be None, meaning use working dir.'''
99 be None, meaning use working dir.'''
100
100
101 def revfix(repo, val, defval):
101 def revfix(repo, val, defval):
102 if not val and val != 0 and defval is not None:
102 if not val and val != 0 and defval is not None:
103 val = defval
103 val = defval
104 return repo.lookup(val)
104 return repo.lookup(val)
105
105
106 if not revs:
106 if not revs:
107 return repo.dirstate.parents()[0], None
107 return repo.dirstate.parents()[0], None
108 end = None
108 end = None
109 if len(revs) == 1:
109 if len(revs) == 1:
110 if revrangesep in revs[0]:
110 if revrangesep in revs[0]:
111 start, end = revs[0].split(revrangesep, 1)
111 start, end = revs[0].split(revrangesep, 1)
112 start = revfix(repo, start, 0)
112 start = revfix(repo, start, 0)
113 end = revfix(repo, end, repo.changelog.count() - 1)
113 end = revfix(repo, end, repo.changelog.count() - 1)
114 else:
114 else:
115 start = revfix(repo, revs[0], None)
115 start = revfix(repo, revs[0], None)
116 elif len(revs) == 2:
116 elif len(revs) == 2:
117 if revrangesep in revs[0] or revrangesep in revs[1]:
117 if revrangesep in revs[0] or revrangesep in revs[1]:
118 raise util.Abort(_('too many revisions specified'))
118 raise util.Abort(_('too many revisions specified'))
119 start = revfix(repo, revs[0], None)
119 start = revfix(repo, revs[0], None)
120 end = revfix(repo, revs[1], None)
120 end = revfix(repo, revs[1], None)
121 else:
121 else:
122 raise util.Abort(_('too many revisions specified'))
122 raise util.Abort(_('too many revisions specified'))
123 return start, end
123 return start, end
124
124
125 def revrange(repo, revs):
125 def revrange(repo, revs):
126 """Yield revision as strings from a list of revision specifications."""
126 """Yield revision as strings from a list of revision specifications."""
127
127
128 def revfix(repo, val, defval):
128 def revfix(repo, val, defval):
129 if not val and val != 0 and defval is not None:
129 if not val and val != 0 and defval is not None:
130 return defval
130 return defval
131 return repo.changelog.rev(repo.lookup(val))
131 return repo.changelog.rev(repo.lookup(val))
132
132
133 seen, l = {}, []
133 seen, l = {}, []
134 for spec in revs:
134 for spec in revs:
135 if revrangesep in spec:
135 if revrangesep in spec:
136 start, end = spec.split(revrangesep, 1)
136 start, end = spec.split(revrangesep, 1)
137 start = revfix(repo, start, 0)
137 start = revfix(repo, start, 0)
138 end = revfix(repo, end, repo.changelog.count() - 1)
138 end = revfix(repo, end, repo.changelog.count() - 1)
139 step = start > end and -1 or 1
139 step = start > end and -1 or 1
140 for rev in xrange(start, end+step, step):
140 for rev in xrange(start, end+step, step):
141 if rev in seen:
141 if rev in seen:
142 continue
142 continue
143 seen[rev] = 1
143 seen[rev] = 1
144 l.append(rev)
144 l.append(rev)
145 else:
145 else:
146 rev = revfix(repo, spec, None)
146 rev = revfix(repo, spec, None)
147 if rev in seen:
147 if rev in seen:
148 continue
148 continue
149 seen[rev] = 1
149 seen[rev] = 1
150 l.append(rev)
150 l.append(rev)
151
151
152 return l
152 return l
153
153
154 def make_filename(repo, pat, node,
154 def make_filename(repo, pat, node,
155 total=None, seqno=None, revwidth=None, pathname=None):
155 total=None, seqno=None, revwidth=None, pathname=None):
156 node_expander = {
156 node_expander = {
157 'H': lambda: hex(node),
157 'H': lambda: hex(node),
158 'R': lambda: str(repo.changelog.rev(node)),
158 'R': lambda: str(repo.changelog.rev(node)),
159 'h': lambda: short(node),
159 'h': lambda: short(node),
160 }
160 }
161 expander = {
161 expander = {
162 '%': lambda: '%',
162 '%': lambda: '%',
163 'b': lambda: os.path.basename(repo.root),
163 'b': lambda: os.path.basename(repo.root),
164 }
164 }
165
165
166 try:
166 try:
167 if node:
167 if node:
168 expander.update(node_expander)
168 expander.update(node_expander)
169 if node:
169 if node:
170 expander['r'] = (lambda:
170 expander['r'] = (lambda:
171 str(repo.changelog.rev(node)).zfill(revwidth or 0))
171 str(repo.changelog.rev(node)).zfill(revwidth or 0))
172 if total is not None:
172 if total is not None:
173 expander['N'] = lambda: str(total)
173 expander['N'] = lambda: str(total)
174 if seqno is not None:
174 if seqno is not None:
175 expander['n'] = lambda: str(seqno)
175 expander['n'] = lambda: str(seqno)
176 if total is not None and seqno is not None:
176 if total is not None and seqno is not None:
177 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
177 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
178 if pathname is not None:
178 if pathname is not None:
179 expander['s'] = lambda: os.path.basename(pathname)
179 expander['s'] = lambda: os.path.basename(pathname)
180 expander['d'] = lambda: os.path.dirname(pathname) or '.'
180 expander['d'] = lambda: os.path.dirname(pathname) or '.'
181 expander['p'] = lambda: pathname
181 expander['p'] = lambda: pathname
182
182
183 newname = []
183 newname = []
184 patlen = len(pat)
184 patlen = len(pat)
185 i = 0
185 i = 0
186 while i < patlen:
186 while i < patlen:
187 c = pat[i]
187 c = pat[i]
188 if c == '%':
188 if c == '%':
189 i += 1
189 i += 1
190 c = pat[i]
190 c = pat[i]
191 c = expander[c]()
191 c = expander[c]()
192 newname.append(c)
192 newname.append(c)
193 i += 1
193 i += 1
194 return ''.join(newname)
194 return ''.join(newname)
195 except KeyError, inst:
195 except KeyError, inst:
196 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
196 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
197 inst.args[0])
197 inst.args[0])
198
198
199 def make_file(repo, pat, node=None,
199 def make_file(repo, pat, node=None,
200 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
200 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
201 if not pat or pat == '-':
201 if not pat or pat == '-':
202 return 'w' in mode and sys.stdout or sys.stdin
202 return 'w' in mode and sys.stdout or sys.stdin
203 if hasattr(pat, 'write') and 'w' in mode:
203 if hasattr(pat, 'write') and 'w' in mode:
204 return pat
204 return pat
205 if hasattr(pat, 'read') and 'r' in mode:
205 if hasattr(pat, 'read') and 'r' in mode:
206 return pat
206 return pat
207 return open(make_filename(repo, pat, node, total, seqno, revwidth,
207 return open(make_filename(repo, pat, node, total, seqno, revwidth,
208 pathname),
208 pathname),
209 mode)
209 mode)
210
210
211 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
211 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
212 cwd = repo.getcwd()
212 cwd = repo.getcwd()
213 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
213 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
214 opts.get('exclude'), globbed=globbed,
214 opts.get('exclude'), globbed=globbed,
215 default=default)
215 default=default)
216
216
217 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
217 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
218 default=None):
218 default=None):
219 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
219 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
220 default=default)
220 default=default)
221 exact = dict.fromkeys(files)
221 exact = dict.fromkeys(files)
222 cwd = repo.getcwd()
222 cwd = repo.getcwd()
223 for src, fn in repo.walk(node=node, files=files, match=matchfn,
223 for src, fn in repo.walk(node=node, files=files, match=matchfn,
224 badmatch=badmatch):
224 badmatch=badmatch):
225 yield src, fn, repo.pathto(fn, cwd), fn in exact
225 yield src, fn, repo.pathto(fn, cwd), fn in exact
226
226
227 def findrenames(repo, added=None, removed=None, threshold=0.5):
227 def findrenames(repo, added=None, removed=None, threshold=0.5):
228 '''find renamed files -- yields (before, after, score) tuples'''
228 '''find renamed files -- yields (before, after, score) tuples'''
229 if added is None or removed is None:
229 if added is None or removed is None:
230 added, removed = repo.status()[1:3]
230 added, removed = repo.status()[1:3]
231 ctx = repo.changectx()
231 ctx = repo.changectx()
232 for a in added:
232 for a in added:
233 aa = repo.wread(a)
233 aa = repo.wread(a)
234 bestname, bestscore = None, threshold
234 bestname, bestscore = None, threshold
235 for r in removed:
235 for r in removed:
236 rr = ctx.filectx(r).data()
236 rr = ctx.filectx(r).data()
237
237
238 # bdiff.blocks() returns blocks of matching lines
238 # bdiff.blocks() returns blocks of matching lines
239 # count the number of bytes in each
239 # count the number of bytes in each
240 equal = 0
240 equal = 0
241 alines = mdiff.splitnewlines(aa)
241 alines = mdiff.splitnewlines(aa)
242 matches = bdiff.blocks(aa, rr)
242 matches = bdiff.blocks(aa, rr)
243 for x1,x2,y1,y2 in matches:
243 for x1,x2,y1,y2 in matches:
244 for line in alines[x1:x2]:
244 for line in alines[x1:x2]:
245 equal += len(line)
245 equal += len(line)
246
246
247 lengths = len(aa) + len(rr)
247 lengths = len(aa) + len(rr)
248 if lengths:
248 if lengths:
249 myscore = equal*2.0 / lengths
249 myscore = equal*2.0 / lengths
250 if myscore >= bestscore:
250 if myscore >= bestscore:
251 bestname, bestscore = r, myscore
251 bestname, bestscore = r, myscore
252 if bestname:
252 if bestname:
253 yield bestname, a, bestscore
253 yield bestname, a, bestscore
254
254
255 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
255 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
256 if dry_run is None:
256 if dry_run is None:
257 dry_run = opts.get('dry_run')
257 dry_run = opts.get('dry_run')
258 if similarity is None:
258 if similarity is None:
259 similarity = float(opts.get('similarity') or 0)
259 similarity = float(opts.get('similarity') or 0)
260 add, remove = [], []
260 add, remove = [], []
261 mapping = {}
261 mapping = {}
262 for src, abs, rel, exact in walk(repo, pats, opts):
262 for src, abs, rel, exact in walk(repo, pats, opts):
263 target = repo.wjoin(abs)
263 target = repo.wjoin(abs)
264 if src == 'f' and abs not in repo.dirstate:
264 if src == 'f' and abs not in repo.dirstate:
265 add.append(abs)
265 add.append(abs)
266 mapping[abs] = rel, exact
266 mapping[abs] = rel, exact
267 if repo.ui.verbose or not exact:
267 if repo.ui.verbose or not exact:
268 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
268 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
269 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
269 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
270 or (os.path.isdir(target) and not os.path.islink(target))):
270 or (os.path.isdir(target) and not os.path.islink(target))):
271 remove.append(abs)
271 remove.append(abs)
272 mapping[abs] = rel, exact
272 mapping[abs] = rel, exact
273 if repo.ui.verbose or not exact:
273 if repo.ui.verbose or not exact:
274 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
274 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
275 if not dry_run:
275 if not dry_run:
276 repo.remove(remove)
276 repo.remove(remove)
277 repo.add(add)
277 repo.add(add)
278 if similarity > 0:
278 if similarity > 0:
279 for old, new, score in findrenames(repo, add, remove, similarity):
279 for old, new, score in findrenames(repo, add, remove, similarity):
280 oldrel, oldexact = mapping[old]
280 oldrel, oldexact = mapping[old]
281 newrel, newexact = mapping[new]
281 newrel, newexact = mapping[new]
282 if repo.ui.verbose or not oldexact or not newexact:
282 if repo.ui.verbose or not oldexact or not newexact:
283 repo.ui.status(_('recording removal of %s as rename to %s '
283 repo.ui.status(_('recording removal of %s as rename to %s '
284 '(%d%% similar)\n') %
284 '(%d%% similar)\n') %
285 (oldrel, newrel, score * 100))
285 (oldrel, newrel, score * 100))
286 if not dry_run:
286 if not dry_run:
287 repo.copy(old, new)
287 repo.copy(old, new)
288
288
289 def service(opts, parentfn=None, initfn=None, runfn=None):
289 def service(opts, parentfn=None, initfn=None, runfn=None):
290 '''Run a command as a service.'''
290 '''Run a command as a service.'''
291
291
292 if opts['daemon'] and not opts['daemon_pipefds']:
292 if opts['daemon'] and not opts['daemon_pipefds']:
293 rfd, wfd = os.pipe()
293 rfd, wfd = os.pipe()
294 args = sys.argv[:]
294 args = sys.argv[:]
295 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
295 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
296 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
296 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
297 args[0], args)
297 args[0], args)
298 os.close(wfd)
298 os.close(wfd)
299 os.read(rfd, 1)
299 os.read(rfd, 1)
300 if parentfn:
300 if parentfn:
301 return parentfn(pid)
301 return parentfn(pid)
302 else:
302 else:
303 os._exit(0)
303 os._exit(0)
304
304
305 if initfn:
305 if initfn:
306 initfn()
306 initfn()
307
307
308 if opts['pid_file']:
308 if opts['pid_file']:
309 fp = open(opts['pid_file'], 'w')
309 fp = open(opts['pid_file'], 'w')
310 fp.write(str(os.getpid()) + '\n')
310 fp.write(str(os.getpid()) + '\n')
311 fp.close()
311 fp.close()
312
312
313 if opts['daemon_pipefds']:
313 if opts['daemon_pipefds']:
314 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
314 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
315 os.close(rfd)
315 os.close(rfd)
316 try:
316 try:
317 os.setsid()
317 os.setsid()
318 except AttributeError:
318 except AttributeError:
319 pass
319 pass
320 os.write(wfd, 'y')
320 os.write(wfd, 'y')
321 os.close(wfd)
321 os.close(wfd)
322 sys.stdout.flush()
322 sys.stdout.flush()
323 sys.stderr.flush()
323 sys.stderr.flush()
324 fd = os.open(util.nulldev, os.O_RDWR)
324 fd = os.open(util.nulldev, os.O_RDWR)
325 if fd != 0: os.dup2(fd, 0)
325 if fd != 0: os.dup2(fd, 0)
326 if fd != 1: os.dup2(fd, 1)
326 if fd != 1: os.dup2(fd, 1)
327 if fd != 2: os.dup2(fd, 2)
327 if fd != 2: os.dup2(fd, 2)
328 if fd not in (0, 1, 2): os.close(fd)
328 if fd not in (0, 1, 2): os.close(fd)
329
329
330 if runfn:
330 if runfn:
331 return runfn()
331 return runfn()
332
332
333 class changeset_printer(object):
333 class changeset_printer(object):
334 '''show changeset information when templating not requested.'''
334 '''show changeset information when templating not requested.'''
335
335
336 def __init__(self, ui, repo, patch, buffered):
336 def __init__(self, ui, repo, patch, buffered):
337 self.ui = ui
337 self.ui = ui
338 self.repo = repo
338 self.repo = repo
339 self.buffered = buffered
339 self.buffered = buffered
340 self.patch = patch
340 self.patch = patch
341 self.header = {}
341 self.header = {}
342 self.hunk = {}
342 self.hunk = {}
343 self.lastheader = None
343 self.lastheader = None
344
344
345 def flush(self, rev):
345 def flush(self, rev):
346 if rev in self.header:
346 if rev in self.header:
347 h = self.header[rev]
347 h = self.header[rev]
348 if h != self.lastheader:
348 if h != self.lastheader:
349 self.lastheader = h
349 self.lastheader = h
350 self.ui.write(h)
350 self.ui.write(h)
351 del self.header[rev]
351 del self.header[rev]
352 if rev in self.hunk:
352 if rev in self.hunk:
353 self.ui.write(self.hunk[rev])
353 self.ui.write(self.hunk[rev])
354 del self.hunk[rev]
354 del self.hunk[rev]
355 return 1
355 return 1
356 return 0
356 return 0
357
357
358 def show(self, rev=0, changenode=None, copies=(), **props):
358 def show(self, rev=0, changenode=None, copies=(), **props):
359 if self.buffered:
359 if self.buffered:
360 self.ui.pushbuffer()
360 self.ui.pushbuffer()
361 self._show(rev, changenode, copies, props)
361 self._show(rev, changenode, copies, props)
362 self.hunk[rev] = self.ui.popbuffer()
362 self.hunk[rev] = self.ui.popbuffer()
363 else:
363 else:
364 self._show(rev, changenode, copies, props)
364 self._show(rev, changenode, copies, props)
365
365
366 def _show(self, rev, changenode, copies, props):
366 def _show(self, rev, changenode, copies, props):
367 '''show a single changeset or file revision'''
367 '''show a single changeset or file revision'''
368 log = self.repo.changelog
368 log = self.repo.changelog
369 if changenode is None:
369 if changenode is None:
370 changenode = log.node(rev)
370 changenode = log.node(rev)
371 elif not rev:
371 elif not rev:
372 rev = log.rev(changenode)
372 rev = log.rev(changenode)
373
373
374 if self.ui.quiet:
374 if self.ui.quiet:
375 self.ui.write("%d:%s\n" % (rev, short(changenode)))
375 self.ui.write("%d:%s\n" % (rev, short(changenode)))
376 return
376 return
377
377
378 changes = log.read(changenode)
378 changes = log.read(changenode)
379 date = util.datestr(changes[2])
379 date = util.datestr(changes[2])
380 extra = changes[5]
380 extra = changes[5]
381 branch = extra.get("branch")
381 branch = extra.get("branch")
382
382
383 hexfunc = self.ui.debugflag and hex or short
383 hexfunc = self.ui.debugflag and hex or short
384
384
385 parents = [(p, hexfunc(log.node(p)))
385 parents = [(p, hexfunc(log.node(p)))
386 for p in self._meaningful_parentrevs(log, rev)]
386 for p in self._meaningful_parentrevs(log, rev)]
387
387
388 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
388 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
389
389
390 # don't show the default branch name
390 # don't show the default branch name
391 if branch != 'default':
391 if branch != 'default':
392 branch = util.tolocal(branch)
392 branch = util.tolocal(branch)
393 self.ui.write(_("branch: %s\n") % branch)
393 self.ui.write(_("branch: %s\n") % branch)
394 for tag in self.repo.nodetags(changenode):
394 for tag in self.repo.nodetags(changenode):
395 self.ui.write(_("tag: %s\n") % tag)
395 self.ui.write(_("tag: %s\n") % tag)
396 for parent in parents:
396 for parent in parents:
397 self.ui.write(_("parent: %d:%s\n") % parent)
397 self.ui.write(_("parent: %d:%s\n") % parent)
398
398
399 if self.ui.debugflag:
399 if self.ui.debugflag:
400 self.ui.write(_("manifest: %d:%s\n") %
400 self.ui.write(_("manifest: %d:%s\n") %
401 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
401 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
402 self.ui.write(_("user: %s\n") % changes[1])
402 self.ui.write(_("user: %s\n") % changes[1])
403 self.ui.write(_("date: %s\n") % date)
403 self.ui.write(_("date: %s\n") % date)
404
404
405 if self.ui.debugflag:
405 if self.ui.debugflag:
406 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
406 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
407 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
407 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
408 files):
408 files):
409 if value:
409 if value:
410 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
410 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
411 elif changes[3] and self.ui.verbose:
411 elif changes[3] and self.ui.verbose:
412 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
412 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
413 if copies and self.ui.verbose:
413 if copies and self.ui.verbose:
414 copies = ['%s (%s)' % c for c in copies]
414 copies = ['%s (%s)' % c for c in copies]
415 self.ui.write(_("copies: %s\n") % ' '.join(copies))
415 self.ui.write(_("copies: %s\n") % ' '.join(copies))
416
416
417 if extra and self.ui.debugflag:
417 if extra and self.ui.debugflag:
418 extraitems = extra.items()
418 extraitems = extra.items()
419 extraitems.sort()
419 extraitems.sort()
420 for key, value in extraitems:
420 for key, value in extraitems:
421 self.ui.write(_("extra: %s=%s\n")
421 self.ui.write(_("extra: %s=%s\n")
422 % (key, value.encode('string_escape')))
422 % (key, value.encode('string_escape')))
423
423
424 description = changes[4].strip()
424 description = changes[4].strip()
425 if description:
425 if description:
426 if self.ui.verbose:
426 if self.ui.verbose:
427 self.ui.write(_("description:\n"))
427 self.ui.write(_("description:\n"))
428 self.ui.write(description)
428 self.ui.write(description)
429 self.ui.write("\n\n")
429 self.ui.write("\n\n")
430 else:
430 else:
431 self.ui.write(_("summary: %s\n") %
431 self.ui.write(_("summary: %s\n") %
432 description.splitlines()[0])
432 description.splitlines()[0])
433 self.ui.write("\n")
433 self.ui.write("\n")
434
434
435 self.showpatch(changenode)
435 self.showpatch(changenode)
436
436
437 def showpatch(self, node):
437 def showpatch(self, node):
438 if self.patch:
438 if self.patch:
439 prev = self.repo.changelog.parents(node)[0]
439 prev = self.repo.changelog.parents(node)[0]
440 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
440 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
441 opts=patch.diffopts(self.ui))
441 opts=patch.diffopts(self.ui))
442 self.ui.write("\n")
442 self.ui.write("\n")
443
443
444 def _meaningful_parentrevs(self, log, rev):
444 def _meaningful_parentrevs(self, log, rev):
445 """Return list of meaningful (or all if debug) parentrevs for rev.
445 """Return list of meaningful (or all if debug) parentrevs for rev.
446
446
447 For merges (two non-nullrev revisions) both parents are meaningful.
447 For merges (two non-nullrev revisions) both parents are meaningful.
448 Otherwise the first parent revision is considered meaningful if it
448 Otherwise the first parent revision is considered meaningful if it
449 is not the preceding revision.
449 is not the preceding revision.
450 """
450 """
451 parents = log.parentrevs(rev)
451 parents = log.parentrevs(rev)
452 if not self.ui.debugflag and parents[1] == nullrev:
452 if not self.ui.debugflag and parents[1] == nullrev:
453 if parents[0] >= rev - 1:
453 if parents[0] >= rev - 1:
454 parents = []
454 parents = []
455 else:
455 else:
456 parents = [parents[0]]
456 parents = [parents[0]]
457 return parents
457 return parents
458
458
459
459
460 class changeset_templater(changeset_printer):
460 class changeset_templater(changeset_printer):
461 '''format changeset information.'''
461 '''format changeset information.'''
462
462
463 def __init__(self, ui, repo, patch, mapfile, buffered):
463 def __init__(self, ui, repo, patch, mapfile, buffered):
464 changeset_printer.__init__(self, ui, repo, patch, buffered)
464 changeset_printer.__init__(self, ui, repo, patch, buffered)
465 filters = templater.common_filters.copy()
465 filters = templater.common_filters.copy()
466 filters['formatnode'] = (ui.debugflag and (lambda x: x)
466 filters['formatnode'] = (ui.debugflag and (lambda x: x)
467 or (lambda x: x[:12]))
467 or (lambda x: x[:12]))
468 self.t = templater.templater(mapfile, filters,
468 self.t = templater.templater(mapfile, filters,
469 cache={
469 cache={
470 'parent': '{rev}:{node|formatnode} ',
470 'parent': '{rev}:{node|formatnode} ',
471 'manifest': '{rev}:{node|formatnode}',
471 'manifest': '{rev}:{node|formatnode}',
472 'filecopy': '{name} ({source})'})
472 'filecopy': '{name} ({source})'})
473
473
474 def use_template(self, t):
474 def use_template(self, t):
475 '''set template string to use'''
475 '''set template string to use'''
476 self.t.cache['changeset'] = t
476 self.t.cache['changeset'] = t
477
477
478 def _show(self, rev, changenode, copies, props):
478 def _show(self, rev, changenode, copies, props):
479 '''show a single changeset or file revision'''
479 '''show a single changeset or file revision'''
480 log = self.repo.changelog
480 log = self.repo.changelog
481 if changenode is None:
481 if changenode is None:
482 changenode = log.node(rev)
482 changenode = log.node(rev)
483 elif not rev:
483 elif not rev:
484 rev = log.rev(changenode)
484 rev = log.rev(changenode)
485
485
486 changes = log.read(changenode)
486 changes = log.read(changenode)
487
487
488 def showlist(name, values, plural=None, **args):
488 def showlist(name, values, plural=None, **args):
489 '''expand set of values.
489 '''expand set of values.
490 name is name of key in template map.
490 name is name of key in template map.
491 values is list of strings or dicts.
491 values is list of strings or dicts.
492 plural is plural of name, if not simply name + 's'.
492 plural is plural of name, if not simply name + 's'.
493
493
494 expansion works like this, given name 'foo'.
494 expansion works like this, given name 'foo'.
495
495
496 if values is empty, expand 'no_foos'.
496 if values is empty, expand 'no_foos'.
497
497
498 if 'foo' not in template map, return values as a string,
498 if 'foo' not in template map, return values as a string,
499 joined by space.
499 joined by space.
500
500
501 expand 'start_foos'.
501 expand 'start_foos'.
502
502
503 for each value, expand 'foo'. if 'last_foo' in template
503 for each value, expand 'foo'. if 'last_foo' in template
504 map, expand it instead of 'foo' for last key.
504 map, expand it instead of 'foo' for last key.
505
505
506 expand 'end_foos'.
506 expand 'end_foos'.
507 '''
507 '''
508 if plural: names = plural
508 if plural: names = plural
509 else: names = name + 's'
509 else: names = name + 's'
510 if not values:
510 if not values:
511 noname = 'no_' + names
511 noname = 'no_' + names
512 if noname in self.t:
512 if noname in self.t:
513 yield self.t(noname, **args)
513 yield self.t(noname, **args)
514 return
514 return
515 if name not in self.t:
515 if name not in self.t:
516 if isinstance(values[0], str):
516 if isinstance(values[0], str):
517 yield ' '.join(values)
517 yield ' '.join(values)
518 else:
518 else:
519 for v in values:
519 for v in values:
520 yield dict(v, **args)
520 yield dict(v, **args)
521 return
521 return
522 startname = 'start_' + names
522 startname = 'start_' + names
523 if startname in self.t:
523 if startname in self.t:
524 yield self.t(startname, **args)
524 yield self.t(startname, **args)
525 vargs = args.copy()
525 vargs = args.copy()
526 def one(v, tag=name):
526 def one(v, tag=name):
527 try:
527 try:
528 vargs.update(v)
528 vargs.update(v)
529 except (AttributeError, ValueError):
529 except (AttributeError, ValueError):
530 try:
530 try:
531 for a, b in v:
531 for a, b in v:
532 vargs[a] = b
532 vargs[a] = b
533 except ValueError:
533 except ValueError:
534 vargs[name] = v
534 vargs[name] = v
535 return self.t(tag, **vargs)
535 return self.t(tag, **vargs)
536 lastname = 'last_' + name
536 lastname = 'last_' + name
537 if lastname in self.t:
537 if lastname in self.t:
538 last = values.pop()
538 last = values.pop()
539 else:
539 else:
540 last = None
540 last = None
541 for v in values:
541 for v in values:
542 yield one(v)
542 yield one(v)
543 if last is not None:
543 if last is not None:
544 yield one(last, tag=lastname)
544 yield one(last, tag=lastname)
545 endname = 'end_' + names
545 endname = 'end_' + names
546 if endname in self.t:
546 if endname in self.t:
547 yield self.t(endname, **args)
547 yield self.t(endname, **args)
548
548
549 def showbranches(**args):
549 def showbranches(**args):
550 branch = changes[5].get("branch")
550 branch = changes[5].get("branch")
551 if branch != 'default':
551 if branch != 'default':
552 branch = util.tolocal(branch)
552 branch = util.tolocal(branch)
553 return showlist('branch', [branch], plural='branches', **args)
553 return showlist('branch', [branch], plural='branches', **args)
554
554
555 def showparents(**args):
555 def showparents(**args):
556 parents = [[('rev', p), ('node', hex(log.node(p)))]
556 parents = [[('rev', p), ('node', hex(log.node(p)))]
557 for p in self._meaningful_parentrevs(log, rev)]
557 for p in self._meaningful_parentrevs(log, rev)]
558 return showlist('parent', parents, **args)
558 return showlist('parent', parents, **args)
559
559
560 def showtags(**args):
560 def showtags(**args):
561 return showlist('tag', self.repo.nodetags(changenode), **args)
561 return showlist('tag', self.repo.nodetags(changenode), **args)
562
562
563 def showextras(**args):
563 def showextras(**args):
564 extras = changes[5].items()
564 extras = changes[5].items()
565 extras.sort()
565 extras.sort()
566 for key, value in extras:
566 for key, value in extras:
567 args = args.copy()
567 args = args.copy()
568 args.update(dict(key=key, value=value))
568 args.update(dict(key=key, value=value))
569 yield self.t('extra', **args)
569 yield self.t('extra', **args)
570
570
571 def showcopies(**args):
571 def showcopies(**args):
572 c = [{'name': x[0], 'source': x[1]} for x in copies]
572 c = [{'name': x[0], 'source': x[1]} for x in copies]
573 return showlist('file_copy', c, plural='file_copies', **args)
573 return showlist('file_copy', c, plural='file_copies', **args)
574
574
575 files = []
576 def getfiles():
577 if not files:
578 files[:] = self.repo.status(
579 log.parents(changenode)[0], changenode)[:3]
580 return files
581 # XXX: "files" means "modified files" in debug, "all changed
582 # files" otherwise. This should be fixed and a "file_mods" be
583 # introduced instead.
575 if self.ui.debugflag:
584 if self.ui.debugflag:
576 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
577 def showfiles(**args):
585 def showfiles(**args):
578 return showlist('file', files[0], **args)
586 return showlist('file', getfiles()[0], **args)
579 def showadds(**args):
580 return showlist('file_add', files[1], **args)
581 def showdels(**args):
582 return showlist('file_del', files[2], **args)
583 def showmanifest(**args):
584 args = args.copy()
585 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
586 node=hex(changes[0])))
587 return self.t('manifest', **args)
588 else:
587 else:
589 def showfiles(**args):
588 def showfiles(**args):
590 return showlist('file', changes[3], **args)
589 return showlist('file', changes[3], **args)
591 showadds = ''
590 def showadds(**args):
592 showdels = ''
591 return showlist('file_add', getfiles()[1], **args)
593 showmanifest = ''
592 def showdels(**args):
593 return showlist('file_del', getfiles()[2], **args)
594 def showmanifest(**args):
595 args = args.copy()
596 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
597 node=hex(changes[0])))
598 return self.t('manifest', **args)
594
599
595 defprops = {
600 defprops = {
596 'author': changes[1],
601 'author': changes[1],
597 'branches': showbranches,
602 'branches': showbranches,
598 'date': changes[2],
603 'date': changes[2],
599 'desc': changes[4].strip(),
604 'desc': changes[4].strip(),
600 'file_adds': showadds,
605 'file_adds': showadds,
601 'file_dels': showdels,
606 'file_dels': showdels,
602 'files': showfiles,
607 'files': showfiles,
603 'file_copies': showcopies,
608 'file_copies': showcopies,
604 'manifest': showmanifest,
609 'manifest': showmanifest,
605 'node': hex(changenode),
610 'node': hex(changenode),
606 'parents': showparents,
611 'parents': showparents,
607 'rev': rev,
612 'rev': rev,
608 'tags': showtags,
613 'tags': showtags,
609 'extras': showextras,
614 'extras': showextras,
610 }
615 }
611 props = props.copy()
616 props = props.copy()
612 props.update(defprops)
617 props.update(defprops)
613
618
614 try:
619 try:
615 if self.ui.debugflag and 'header_debug' in self.t:
620 if self.ui.debugflag and 'header_debug' in self.t:
616 key = 'header_debug'
621 key = 'header_debug'
617 elif self.ui.quiet and 'header_quiet' in self.t:
622 elif self.ui.quiet and 'header_quiet' in self.t:
618 key = 'header_quiet'
623 key = 'header_quiet'
619 elif self.ui.verbose and 'header_verbose' in self.t:
624 elif self.ui.verbose and 'header_verbose' in self.t:
620 key = 'header_verbose'
625 key = 'header_verbose'
621 elif 'header' in self.t:
626 elif 'header' in self.t:
622 key = 'header'
627 key = 'header'
623 else:
628 else:
624 key = ''
629 key = ''
625 if key:
630 if key:
626 h = templater.stringify(self.t(key, **props))
631 h = templater.stringify(self.t(key, **props))
627 if self.buffered:
632 if self.buffered:
628 self.header[rev] = h
633 self.header[rev] = h
629 else:
634 else:
630 self.ui.write(h)
635 self.ui.write(h)
631 if self.ui.debugflag and 'changeset_debug' in self.t:
636 if self.ui.debugflag and 'changeset_debug' in self.t:
632 key = 'changeset_debug'
637 key = 'changeset_debug'
633 elif self.ui.quiet and 'changeset_quiet' in self.t:
638 elif self.ui.quiet and 'changeset_quiet' in self.t:
634 key = 'changeset_quiet'
639 key = 'changeset_quiet'
635 elif self.ui.verbose and 'changeset_verbose' in self.t:
640 elif self.ui.verbose and 'changeset_verbose' in self.t:
636 key = 'changeset_verbose'
641 key = 'changeset_verbose'
637 else:
642 else:
638 key = 'changeset'
643 key = 'changeset'
639 self.ui.write(templater.stringify(self.t(key, **props)))
644 self.ui.write(templater.stringify(self.t(key, **props)))
640 self.showpatch(changenode)
645 self.showpatch(changenode)
641 except KeyError, inst:
646 except KeyError, inst:
642 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
647 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
643 inst.args[0]))
648 inst.args[0]))
644 except SyntaxError, inst:
649 except SyntaxError, inst:
645 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
650 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
646
651
647 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
652 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
648 """show one changeset using template or regular display.
653 """show one changeset using template or regular display.
649
654
650 Display format will be the first non-empty hit of:
655 Display format will be the first non-empty hit of:
651 1. option 'template'
656 1. option 'template'
652 2. option 'style'
657 2. option 'style'
653 3. [ui] setting 'logtemplate'
658 3. [ui] setting 'logtemplate'
654 4. [ui] setting 'style'
659 4. [ui] setting 'style'
655 If all of these values are either the unset or the empty string,
660 If all of these values are either the unset or the empty string,
656 regular display via changeset_printer() is done.
661 regular display via changeset_printer() is done.
657 """
662 """
658 # options
663 # options
659 patch = False
664 patch = False
660 if opts.get('patch'):
665 if opts.get('patch'):
661 patch = matchfn or util.always
666 patch = matchfn or util.always
662
667
663 tmpl = opts.get('template')
668 tmpl = opts.get('template')
664 mapfile = None
669 mapfile = None
665 if tmpl:
670 if tmpl:
666 tmpl = templater.parsestring(tmpl, quoted=False)
671 tmpl = templater.parsestring(tmpl, quoted=False)
667 else:
672 else:
668 mapfile = opts.get('style')
673 mapfile = opts.get('style')
669 # ui settings
674 # ui settings
670 if not mapfile:
675 if not mapfile:
671 tmpl = ui.config('ui', 'logtemplate')
676 tmpl = ui.config('ui', 'logtemplate')
672 if tmpl:
677 if tmpl:
673 tmpl = templater.parsestring(tmpl)
678 tmpl = templater.parsestring(tmpl)
674 else:
679 else:
675 mapfile = ui.config('ui', 'style')
680 mapfile = ui.config('ui', 'style')
676
681
677 if tmpl or mapfile:
682 if tmpl or mapfile:
678 if mapfile:
683 if mapfile:
679 if not os.path.split(mapfile)[0]:
684 if not os.path.split(mapfile)[0]:
680 mapname = (templater.templatepath('map-cmdline.' + mapfile)
685 mapname = (templater.templatepath('map-cmdline.' + mapfile)
681 or templater.templatepath(mapfile))
686 or templater.templatepath(mapfile))
682 if mapname: mapfile = mapname
687 if mapname: mapfile = mapname
683 try:
688 try:
684 t = changeset_templater(ui, repo, patch, mapfile, buffered)
689 t = changeset_templater(ui, repo, patch, mapfile, buffered)
685 except SyntaxError, inst:
690 except SyntaxError, inst:
686 raise util.Abort(inst.args[0])
691 raise util.Abort(inst.args[0])
687 if tmpl: t.use_template(tmpl)
692 if tmpl: t.use_template(tmpl)
688 return t
693 return t
689 return changeset_printer(ui, repo, patch, buffered)
694 return changeset_printer(ui, repo, patch, buffered)
690
695
691 def finddate(ui, repo, date):
696 def finddate(ui, repo, date):
692 """Find the tipmost changeset that matches the given date spec"""
697 """Find the tipmost changeset that matches the given date spec"""
693 df = util.matchdate(date + " to " + date)
698 df = util.matchdate(date + " to " + date)
694 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
699 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
695 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
700 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
696 results = {}
701 results = {}
697 for st, rev, fns in changeiter:
702 for st, rev, fns in changeiter:
698 if st == 'add':
703 if st == 'add':
699 d = get(rev)[2]
704 d = get(rev)[2]
700 if df(d[0]):
705 if df(d[0]):
701 results[rev] = d
706 results[rev] = d
702 elif st == 'iter':
707 elif st == 'iter':
703 if rev in results:
708 if rev in results:
704 ui.status("Found revision %s from %s\n" %
709 ui.status("Found revision %s from %s\n" %
705 (rev, util.datestr(results[rev])))
710 (rev, util.datestr(results[rev])))
706 return str(rev)
711 return str(rev)
707
712
708 raise util.Abort(_("revision matching date not found"))
713 raise util.Abort(_("revision matching date not found"))
709
714
710 def walkchangerevs(ui, repo, pats, change, opts):
715 def walkchangerevs(ui, repo, pats, change, opts):
711 '''Iterate over files and the revs they changed in.
716 '''Iterate over files and the revs they changed in.
712
717
713 Callers most commonly need to iterate backwards over the history
718 Callers most commonly need to iterate backwards over the history
714 it is interested in. Doing so has awful (quadratic-looking)
719 it is interested in. Doing so has awful (quadratic-looking)
715 performance, so we use iterators in a "windowed" way.
720 performance, so we use iterators in a "windowed" way.
716
721
717 We walk a window of revisions in the desired order. Within the
722 We walk a window of revisions in the desired order. Within the
718 window, we first walk forwards to gather data, then in the desired
723 window, we first walk forwards to gather data, then in the desired
719 order (usually backwards) to display it.
724 order (usually backwards) to display it.
720
725
721 This function returns an (iterator, matchfn) tuple. The iterator
726 This function returns an (iterator, matchfn) tuple. The iterator
722 yields 3-tuples. They will be of one of the following forms:
727 yields 3-tuples. They will be of one of the following forms:
723
728
724 "window", incrementing, lastrev: stepping through a window,
729 "window", incrementing, lastrev: stepping through a window,
725 positive if walking forwards through revs, last rev in the
730 positive if walking forwards through revs, last rev in the
726 sequence iterated over - use to reset state for the current window
731 sequence iterated over - use to reset state for the current window
727
732
728 "add", rev, fns: out-of-order traversal of the given file names
733 "add", rev, fns: out-of-order traversal of the given file names
729 fns, which changed during revision rev - use to gather data for
734 fns, which changed during revision rev - use to gather data for
730 possible display
735 possible display
731
736
732 "iter", rev, None: in-order traversal of the revs earlier iterated
737 "iter", rev, None: in-order traversal of the revs earlier iterated
733 over with "add" - use to display data'''
738 over with "add" - use to display data'''
734
739
735 def increasing_windows(start, end, windowsize=8, sizelimit=512):
740 def increasing_windows(start, end, windowsize=8, sizelimit=512):
736 if start < end:
741 if start < end:
737 while start < end:
742 while start < end:
738 yield start, min(windowsize, end-start)
743 yield start, min(windowsize, end-start)
739 start += windowsize
744 start += windowsize
740 if windowsize < sizelimit:
745 if windowsize < sizelimit:
741 windowsize *= 2
746 windowsize *= 2
742 else:
747 else:
743 while start > end:
748 while start > end:
744 yield start, min(windowsize, start-end-1)
749 yield start, min(windowsize, start-end-1)
745 start -= windowsize
750 start -= windowsize
746 if windowsize < sizelimit:
751 if windowsize < sizelimit:
747 windowsize *= 2
752 windowsize *= 2
748
753
749 files, matchfn, anypats = matchpats(repo, pats, opts)
754 files, matchfn, anypats = matchpats(repo, pats, opts)
750 follow = opts.get('follow') or opts.get('follow_first')
755 follow = opts.get('follow') or opts.get('follow_first')
751
756
752 if repo.changelog.count() == 0:
757 if repo.changelog.count() == 0:
753 return [], matchfn
758 return [], matchfn
754
759
755 if follow:
760 if follow:
756 defrange = '%s:0' % repo.changectx().rev()
761 defrange = '%s:0' % repo.changectx().rev()
757 else:
762 else:
758 defrange = 'tip:0'
763 defrange = 'tip:0'
759 revs = revrange(repo, opts['rev'] or [defrange])
764 revs = revrange(repo, opts['rev'] or [defrange])
760 wanted = {}
765 wanted = {}
761 slowpath = anypats or opts.get('removed')
766 slowpath = anypats or opts.get('removed')
762 fncache = {}
767 fncache = {}
763
768
764 if not slowpath and not files:
769 if not slowpath and not files:
765 # No files, no patterns. Display all revs.
770 # No files, no patterns. Display all revs.
766 wanted = dict.fromkeys(revs)
771 wanted = dict.fromkeys(revs)
767 copies = []
772 copies = []
768 if not slowpath:
773 if not slowpath:
769 # Only files, no patterns. Check the history of each file.
774 # Only files, no patterns. Check the history of each file.
770 def filerevgen(filelog, node):
775 def filerevgen(filelog, node):
771 cl_count = repo.changelog.count()
776 cl_count = repo.changelog.count()
772 if node is None:
777 if node is None:
773 last = filelog.count() - 1
778 last = filelog.count() - 1
774 else:
779 else:
775 last = filelog.rev(node)
780 last = filelog.rev(node)
776 for i, window in increasing_windows(last, nullrev):
781 for i, window in increasing_windows(last, nullrev):
777 revs = []
782 revs = []
778 for j in xrange(i - window, i + 1):
783 for j in xrange(i - window, i + 1):
779 n = filelog.node(j)
784 n = filelog.node(j)
780 revs.append((filelog.linkrev(n),
785 revs.append((filelog.linkrev(n),
781 follow and filelog.renamed(n)))
786 follow and filelog.renamed(n)))
782 revs.reverse()
787 revs.reverse()
783 for rev in revs:
788 for rev in revs:
784 # only yield rev for which we have the changelog, it can
789 # only yield rev for which we have the changelog, it can
785 # happen while doing "hg log" during a pull or commit
790 # happen while doing "hg log" during a pull or commit
786 if rev[0] < cl_count:
791 if rev[0] < cl_count:
787 yield rev
792 yield rev
788 def iterfiles():
793 def iterfiles():
789 for filename in files:
794 for filename in files:
790 yield filename, None
795 yield filename, None
791 for filename_node in copies:
796 for filename_node in copies:
792 yield filename_node
797 yield filename_node
793 minrev, maxrev = min(revs), max(revs)
798 minrev, maxrev = min(revs), max(revs)
794 for file_, node in iterfiles():
799 for file_, node in iterfiles():
795 filelog = repo.file(file_)
800 filelog = repo.file(file_)
796 # A zero count may be a directory or deleted file, so
801 # A zero count may be a directory or deleted file, so
797 # try to find matching entries on the slow path.
802 # try to find matching entries on the slow path.
798 if filelog.count() == 0:
803 if filelog.count() == 0:
799 slowpath = True
804 slowpath = True
800 break
805 break
801 for rev, copied in filerevgen(filelog, node):
806 for rev, copied in filerevgen(filelog, node):
802 if rev <= maxrev:
807 if rev <= maxrev:
803 if rev < minrev:
808 if rev < minrev:
804 break
809 break
805 fncache.setdefault(rev, [])
810 fncache.setdefault(rev, [])
806 fncache[rev].append(file_)
811 fncache[rev].append(file_)
807 wanted[rev] = 1
812 wanted[rev] = 1
808 if follow and copied:
813 if follow and copied:
809 copies.append(copied)
814 copies.append(copied)
810 if slowpath:
815 if slowpath:
811 if follow:
816 if follow:
812 raise util.Abort(_('can only follow copies/renames for explicit '
817 raise util.Abort(_('can only follow copies/renames for explicit '
813 'file names'))
818 'file names'))
814
819
815 # The slow path checks files modified in every changeset.
820 # The slow path checks files modified in every changeset.
816 def changerevgen():
821 def changerevgen():
817 for i, window in increasing_windows(repo.changelog.count()-1,
822 for i, window in increasing_windows(repo.changelog.count()-1,
818 nullrev):
823 nullrev):
819 for j in xrange(i - window, i + 1):
824 for j in xrange(i - window, i + 1):
820 yield j, change(j)[3]
825 yield j, change(j)[3]
821
826
822 for rev, changefiles in changerevgen():
827 for rev, changefiles in changerevgen():
823 matches = filter(matchfn, changefiles)
828 matches = filter(matchfn, changefiles)
824 if matches:
829 if matches:
825 fncache[rev] = matches
830 fncache[rev] = matches
826 wanted[rev] = 1
831 wanted[rev] = 1
827
832
828 class followfilter:
833 class followfilter:
829 def __init__(self, onlyfirst=False):
834 def __init__(self, onlyfirst=False):
830 self.startrev = nullrev
835 self.startrev = nullrev
831 self.roots = []
836 self.roots = []
832 self.onlyfirst = onlyfirst
837 self.onlyfirst = onlyfirst
833
838
834 def match(self, rev):
839 def match(self, rev):
835 def realparents(rev):
840 def realparents(rev):
836 if self.onlyfirst:
841 if self.onlyfirst:
837 return repo.changelog.parentrevs(rev)[0:1]
842 return repo.changelog.parentrevs(rev)[0:1]
838 else:
843 else:
839 return filter(lambda x: x != nullrev,
844 return filter(lambda x: x != nullrev,
840 repo.changelog.parentrevs(rev))
845 repo.changelog.parentrevs(rev))
841
846
842 if self.startrev == nullrev:
847 if self.startrev == nullrev:
843 self.startrev = rev
848 self.startrev = rev
844 return True
849 return True
845
850
846 if rev > self.startrev:
851 if rev > self.startrev:
847 # forward: all descendants
852 # forward: all descendants
848 if not self.roots:
853 if not self.roots:
849 self.roots.append(self.startrev)
854 self.roots.append(self.startrev)
850 for parent in realparents(rev):
855 for parent in realparents(rev):
851 if parent in self.roots:
856 if parent in self.roots:
852 self.roots.append(rev)
857 self.roots.append(rev)
853 return True
858 return True
854 else:
859 else:
855 # backwards: all parents
860 # backwards: all parents
856 if not self.roots:
861 if not self.roots:
857 self.roots.extend(realparents(self.startrev))
862 self.roots.extend(realparents(self.startrev))
858 if rev in self.roots:
863 if rev in self.roots:
859 self.roots.remove(rev)
864 self.roots.remove(rev)
860 self.roots.extend(realparents(rev))
865 self.roots.extend(realparents(rev))
861 return True
866 return True
862
867
863 return False
868 return False
864
869
865 # it might be worthwhile to do this in the iterator if the rev range
870 # it might be worthwhile to do this in the iterator if the rev range
866 # is descending and the prune args are all within that range
871 # is descending and the prune args are all within that range
867 for rev in opts.get('prune', ()):
872 for rev in opts.get('prune', ()):
868 rev = repo.changelog.rev(repo.lookup(rev))
873 rev = repo.changelog.rev(repo.lookup(rev))
869 ff = followfilter()
874 ff = followfilter()
870 stop = min(revs[0], revs[-1])
875 stop = min(revs[0], revs[-1])
871 for x in xrange(rev, stop-1, -1):
876 for x in xrange(rev, stop-1, -1):
872 if ff.match(x) and x in wanted:
877 if ff.match(x) and x in wanted:
873 del wanted[x]
878 del wanted[x]
874
879
875 def iterate():
880 def iterate():
876 if follow and not files:
881 if follow and not files:
877 ff = followfilter(onlyfirst=opts.get('follow_first'))
882 ff = followfilter(onlyfirst=opts.get('follow_first'))
878 def want(rev):
883 def want(rev):
879 if ff.match(rev) and rev in wanted:
884 if ff.match(rev) and rev in wanted:
880 return True
885 return True
881 return False
886 return False
882 else:
887 else:
883 def want(rev):
888 def want(rev):
884 return rev in wanted
889 return rev in wanted
885
890
886 for i, window in increasing_windows(0, len(revs)):
891 for i, window in increasing_windows(0, len(revs)):
887 yield 'window', revs[0] < revs[-1], revs[-1]
892 yield 'window', revs[0] < revs[-1], revs[-1]
888 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
893 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
889 srevs = list(nrevs)
894 srevs = list(nrevs)
890 srevs.sort()
895 srevs.sort()
891 for rev in srevs:
896 for rev in srevs:
892 fns = fncache.get(rev)
897 fns = fncache.get(rev)
893 if not fns:
898 if not fns:
894 def fns_generator():
899 def fns_generator():
895 for f in change(rev)[3]:
900 for f in change(rev)[3]:
896 if matchfn(f):
901 if matchfn(f):
897 yield f
902 yield f
898 fns = fns_generator()
903 fns = fns_generator()
899 yield 'add', rev, fns
904 yield 'add', rev, fns
900 for rev in nrevs:
905 for rev in nrevs:
901 yield 'iter', rev, None
906 yield 'iter', rev, None
902 return iterate(), matchfn
907 return iterate(), matchfn
903
908
904 def commit(ui, repo, commitfunc, pats, opts):
909 def commit(ui, repo, commitfunc, pats, opts):
905 '''commit the specified files or all outstanding changes'''
910 '''commit the specified files or all outstanding changes'''
906 message = logmessage(opts)
911 message = logmessage(opts)
907
912
908 if opts['addremove']:
913 if opts['addremove']:
909 addremove(repo, pats, opts)
914 addremove(repo, pats, opts)
910 fns, match, anypats = matchpats(repo, pats, opts)
915 fns, match, anypats = matchpats(repo, pats, opts)
911 if pats:
916 if pats:
912 status = repo.status(files=fns, match=match)
917 status = repo.status(files=fns, match=match)
913 modified, added, removed, deleted, unknown = status[:5]
918 modified, added, removed, deleted, unknown = status[:5]
914 files = modified + added + removed
919 files = modified + added + removed
915 slist = None
920 slist = None
916 for f in fns:
921 for f in fns:
917 if f == '.':
922 if f == '.':
918 continue
923 continue
919 if f not in files:
924 if f not in files:
920 rf = repo.wjoin(f)
925 rf = repo.wjoin(f)
921 try:
926 try:
922 mode = os.lstat(rf)[stat.ST_MODE]
927 mode = os.lstat(rf)[stat.ST_MODE]
923 except OSError:
928 except OSError:
924 raise util.Abort(_("file %s not found!") % rf)
929 raise util.Abort(_("file %s not found!") % rf)
925 if stat.S_ISDIR(mode):
930 if stat.S_ISDIR(mode):
926 name = f + '/'
931 name = f + '/'
927 if slist is None:
932 if slist is None:
928 slist = list(files)
933 slist = list(files)
929 slist.sort()
934 slist.sort()
930 i = bisect.bisect(slist, name)
935 i = bisect.bisect(slist, name)
931 if i >= len(slist) or not slist[i].startswith(name):
936 if i >= len(slist) or not slist[i].startswith(name):
932 raise util.Abort(_("no match under directory %s!")
937 raise util.Abort(_("no match under directory %s!")
933 % rf)
938 % rf)
934 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
939 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
935 raise util.Abort(_("can't commit %s: "
940 raise util.Abort(_("can't commit %s: "
936 "unsupported file type!") % rf)
941 "unsupported file type!") % rf)
937 elif f not in repo.dirstate:
942 elif f not in repo.dirstate:
938 raise util.Abort(_("file %s not tracked!") % rf)
943 raise util.Abort(_("file %s not tracked!") % rf)
939 else:
944 else:
940 files = []
945 files = []
941 try:
946 try:
942 return commitfunc(ui, repo, files, message, match, opts)
947 return commitfunc(ui, repo, files, message, match, opts)
943 except ValueError, inst:
948 except ValueError, inst:
944 raise util.Abort(str(inst))
949 raise util.Abort(str(inst))
@@ -1,21 +1,21 b''
1 changeset = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
1 changeset = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
2 changeset_quiet = '{rev}:{node|short}\n'
2 changeset_quiet = '{rev}:{node|short}\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}user: {author}\ndate: {date|date}\n{files}{file_copies}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}{extras}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}{extras}description:\n{desc|strip}\n\n\n'
5 start_files = 'files: '
5 start_files = 'files: '
6 file = ' {file}'
6 file = ' {file}'
7 end_files = '\n'
7 end_files = '\n'
8 start_file_adds = 'files+: '
8 start_file_adds = 'files+: '
9 file_add = ' {file_add}'
9 file_add = ' {file_add}'
10 end_file_adds = '\n'
10 end_file_adds = '\n'
11 start_file_dels = 'files-: '
11 start_file_dels = 'files-: '
12 file_del = ' {file_del}'
12 file_del = ' {file_del}'
13 end_file_dels = '\n'
13 end_file_dels = '\n'
14 start_file_copies = 'copies: '
14 start_file_copies = 'copies: '
15 file_copy = ' {name} ({source})'
15 file_copy = ' {name} ({source})'
16 end_file_copies = '\n'
16 end_file_copies = '\n'
17 parent = 'parent: {rev}:{node|formatnode}\n'
17 parent = 'parent: {rev}:{node|formatnode}\n'
18 manifest = 'manifest: {rev}:{node}\n'
18 manifest = 'manifest: {rev}:{node}\n'
19 branch = 'branch: {branch}\n'
19 branch = 'branch: {branch}\n'
20 tag = 'tag: {tag}\n'
20 tag = 'tag: {tag}\n'
21 extra = 'extra: {key}={value|stringescape}\n'
21 extra = 'extra: {key}={value|stringescape}\n'
@@ -1,531 +1,531 b''
1 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
1 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
2 # default style is like normal output
2 # default style is like normal output
3 # normal
3 # normal
4 # verbose
4 # verbose
5 # debug
5 # debug
6 # revision with no copies (used to print a traceback)
6 # revision with no copies (used to print a traceback)
7
7
8 # compact style works
8 # compact style works
9 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
9 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
10 second
10 second
11
11
12 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
12 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
13 merge
13 merge
14
14
15 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
15 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
16 new head
16 new head
17
17
18 4 32a18f097fcc 1970-01-17 04:53 +0000 person
18 4 32a18f097fcc 1970-01-17 04:53 +0000 person
19 new branch
19 new branch
20
20
21 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
21 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
22 no user, no domain
22 no user, no domain
23
23
24 2 97054abb4ab8 1970-01-14 21:20 +0000 other
24 2 97054abb4ab8 1970-01-14 21:20 +0000 other
25 no person
25 no person
26
26
27 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
27 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
28 other 1
28 other 1
29
29
30 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
30 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
31 line 1
31 line 1
32
32
33 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
33 7[tip]:-1 29114dbae42b 1970-01-12 13:46 +0000 user
34 second
34 second
35
35
36 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
36 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
37 merge
37 merge
38
38
39 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
39 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
40 new head
40 new head
41
41
42 4 32a18f097fcc 1970-01-17 04:53 +0000 person
42 4 32a18f097fcc 1970-01-17 04:53 +0000 person
43 new branch
43 new branch
44
44
45 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
45 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
46 no user, no domain
46 no user, no domain
47
47
48 2 97054abb4ab8 1970-01-14 21:20 +0000 other
48 2 97054abb4ab8 1970-01-14 21:20 +0000 other
49 no person
49 no person
50
50
51 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
51 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
52 other 1
52 other 1
53
53
54 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
54 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
55 line 1
55 line 1
56
56
57 7[tip]:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 user
57 7[tip]:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 user
58 second
58 second
59
59
60 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
60 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
61 merge
61 merge
62
62
63 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
63 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
64 new head
64 new head
65
65
66 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
66 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
67 new branch
67 new branch
68
68
69 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
69 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
70 no user, no domain
70 no user, no domain
71
71
72 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other
72 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other
73 no person
73 no person
74
74
75 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
75 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
76 other 1
76 other 1
77
77
78 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
78 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
79 line 1
79 line 1
80
80
81 # error if style not readable
81 # error if style not readable
82 abort: Permission denied: ./q
82 abort: Permission denied: ./q
83 # error if no style
83 # error if no style
84 abort: No such file or directory: notexist
84 abort: No such file or directory: notexist
85 # error if style missing key
85 # error if style missing key
86 abort: ./t: no key named 'changeset'
86 abort: ./t: no key named 'changeset'
87 # error if include fails
87 # error if include fails
88 abort: template file ./q: Permission denied
88 abort: template file ./q: Permission denied
89 # include works
89 # include works
90 7
90 7
91 6
91 6
92 5
92 5
93 4
93 4
94 3
94 3
95 2
95 2
96 1
96 1
97 0
97 0
98 # ui.style works
98 # ui.style works
99 7
99 7
100 6
100 6
101 5
101 5
102 4
102 4
103 3
103 3
104 2
104 2
105 1
105 1
106 0
106 0
107 # issue338
107 # issue338
108 1970-01-12 User Name <user@hostname>
108 1970-01-12 User Name <user@hostname>
109
109
110 * second:
110 * second:
111 second
111 second
112 [29114dbae42b] [tip]
112 [29114dbae42b] [tip]
113
113
114 1970-01-18 person <person>
114 1970-01-18 person <person>
115
115
116 * merge
116 * merge
117 [c7b487c6c50e]
117 [c7b487c6c50e]
118
118
119 * d:
119 * d:
120 new head
120 new head
121 [13207e5a10d9]
121 [13207e5a10d9]
122
122
123 1970-01-17 person <person>
123 1970-01-17 person <person>
124
124
125 * new branch
125 * new branch
126 [32a18f097fcc]
126 [32a18f097fcc]
127
127
128 1970-01-16 person <person>
128 1970-01-16 person <person>
129
129
130 * c:
130 * c:
131 no user, no domain
131 no user, no domain
132 [10e46f2dcbf4]
132 [10e46f2dcbf4]
133
133
134 1970-01-14 other <other@place>
134 1970-01-14 other <other@place>
135
135
136 * c:
136 * c:
137 no person
137 no person
138 [97054abb4ab8]
138 [97054abb4ab8]
139
139
140 1970-01-13 A. N. Other <other@place>
140 1970-01-13 A. N. Other <other@place>
141
141
142 * b:
142 * b:
143 other 1 other 2
143 other 1 other 2
144
144
145 other 3
145 other 3
146 [b608e9d1a3f0]
146 [b608e9d1a3f0]
147
147
148 1970-01-12 User Name <user@hostname>
148 1970-01-12 User Name <user@hostname>
149
149
150 * a:
150 * a:
151 line 1 line 2
151 line 1 line 2
152 [1e4e1b8f71e0]
152 [1e4e1b8f71e0]
153
153
154 # keys work
154 # keys work
155 author: User Name <user@hostname>
155 author: User Name <user@hostname>
156 author: person
156 author: person
157 author: person
157 author: person
158 author: person
158 author: person
159 author: person
159 author: person
160 author: other@place
160 author: other@place
161 author: A. N. Other <other@place>
161 author: A. N. Other <other@place>
162 author: User Name <user@hostname>
162 author: User Name <user@hostname>
163 author--verbose: User Name <user@hostname>
163 author--verbose: User Name <user@hostname>
164 author--verbose: person
164 author--verbose: person
165 author--verbose: person
165 author--verbose: person
166 author--verbose: person
166 author--verbose: person
167 author--verbose: person
167 author--verbose: person
168 author--verbose: other@place
168 author--verbose: other@place
169 author--verbose: A. N. Other <other@place>
169 author--verbose: A. N. Other <other@place>
170 author--verbose: User Name <user@hostname>
170 author--verbose: User Name <user@hostname>
171 author--debug: User Name <user@hostname>
171 author--debug: User Name <user@hostname>
172 author--debug: person
172 author--debug: person
173 author--debug: person
173 author--debug: person
174 author--debug: person
174 author--debug: person
175 author--debug: person
175 author--debug: person
176 author--debug: other@place
176 author--debug: other@place
177 author--debug: A. N. Other <other@place>
177 author--debug: A. N. Other <other@place>
178 author--debug: User Name <user@hostname>
178 author--debug: User Name <user@hostname>
179 branches:
179 branches:
180 branches:
180 branches:
181 branches:
181 branches:
182 branches: foo
182 branches: foo
183 branches:
183 branches:
184 branches:
184 branches:
185 branches:
185 branches:
186 branches:
186 branches:
187 branches--verbose:
187 branches--verbose:
188 branches--verbose:
188 branches--verbose:
189 branches--verbose:
189 branches--verbose:
190 branches--verbose: foo
190 branches--verbose: foo
191 branches--verbose:
191 branches--verbose:
192 branches--verbose:
192 branches--verbose:
193 branches--verbose:
193 branches--verbose:
194 branches--verbose:
194 branches--verbose:
195 branches--debug:
195 branches--debug:
196 branches--debug:
196 branches--debug:
197 branches--debug:
197 branches--debug:
198 branches--debug: foo
198 branches--debug: foo
199 branches--debug:
199 branches--debug:
200 branches--debug:
200 branches--debug:
201 branches--debug:
201 branches--debug:
202 branches--debug:
202 branches--debug:
203 date: 1000000.00
203 date: 1000000.00
204 date: 1500001.00
204 date: 1500001.00
205 date: 1500000.00
205 date: 1500000.00
206 date: 1400000.00
206 date: 1400000.00
207 date: 1300000.00
207 date: 1300000.00
208 date: 1200000.00
208 date: 1200000.00
209 date: 1100000.00
209 date: 1100000.00
210 date: 1000000.00
210 date: 1000000.00
211 date--verbose: 1000000.00
211 date--verbose: 1000000.00
212 date--verbose: 1500001.00
212 date--verbose: 1500001.00
213 date--verbose: 1500000.00
213 date--verbose: 1500000.00
214 date--verbose: 1400000.00
214 date--verbose: 1400000.00
215 date--verbose: 1300000.00
215 date--verbose: 1300000.00
216 date--verbose: 1200000.00
216 date--verbose: 1200000.00
217 date--verbose: 1100000.00
217 date--verbose: 1100000.00
218 date--verbose: 1000000.00
218 date--verbose: 1000000.00
219 date--debug: 1000000.00
219 date--debug: 1000000.00
220 date--debug: 1500001.00
220 date--debug: 1500001.00
221 date--debug: 1500000.00
221 date--debug: 1500000.00
222 date--debug: 1400000.00
222 date--debug: 1400000.00
223 date--debug: 1300000.00
223 date--debug: 1300000.00
224 date--debug: 1200000.00
224 date--debug: 1200000.00
225 date--debug: 1100000.00
225 date--debug: 1100000.00
226 date--debug: 1000000.00
226 date--debug: 1000000.00
227 desc: second
227 desc: second
228 desc: merge
228 desc: merge
229 desc: new head
229 desc: new head
230 desc: new branch
230 desc: new branch
231 desc: no user, no domain
231 desc: no user, no domain
232 desc: no person
232 desc: no person
233 desc: other 1
233 desc: other 1
234 other 2
234 other 2
235
235
236 other 3
236 other 3
237 desc: line 1
237 desc: line 1
238 line 2
238 line 2
239 desc--verbose: second
239 desc--verbose: second
240 desc--verbose: merge
240 desc--verbose: merge
241 desc--verbose: new head
241 desc--verbose: new head
242 desc--verbose: new branch
242 desc--verbose: new branch
243 desc--verbose: no user, no domain
243 desc--verbose: no user, no domain
244 desc--verbose: no person
244 desc--verbose: no person
245 desc--verbose: other 1
245 desc--verbose: other 1
246 other 2
246 other 2
247
247
248 other 3
248 other 3
249 desc--verbose: line 1
249 desc--verbose: line 1
250 line 2
250 line 2
251 desc--debug: second
251 desc--debug: second
252 desc--debug: merge
252 desc--debug: merge
253 desc--debug: new head
253 desc--debug: new head
254 desc--debug: new branch
254 desc--debug: new branch
255 desc--debug: no user, no domain
255 desc--debug: no user, no domain
256 desc--debug: no person
256 desc--debug: no person
257 desc--debug: other 1
257 desc--debug: other 1
258 other 2
258 other 2
259
259
260 other 3
260 other 3
261 desc--debug: line 1
261 desc--debug: line 1
262 line 2
262 line 2
263 file_adds:
263 file_adds: second
264 file_adds:
265 file_adds:
264 file_adds:
266 file_adds:
265 file_adds: d
267 file_adds:
268 file_adds:
269 file_adds:
266 file_adds:
270 file_adds:
267 file_adds:
268 file_adds: c
269 file_adds: b
270 file_adds: a
271 file_adds--verbose: second
271 file_adds--verbose:
272 file_adds--verbose:
272 file_adds--verbose:
273 file_adds--verbose: d
273 file_adds--verbose:
274 file_adds--verbose:
274 file_adds--verbose:
275 file_adds--verbose:
275 file_adds--verbose:
276 file_adds--verbose: c
276 file_adds--verbose:
277 file_adds--verbose: b
277 file_adds--verbose:
278 file_adds--verbose: a
278 file_adds--verbose:
279 file_adds--debug: second
279 file_adds--debug: second
280 file_adds--debug:
280 file_adds--debug:
281 file_adds--debug: d
281 file_adds--debug: d
282 file_adds--debug:
282 file_adds--debug:
283 file_adds--debug:
283 file_adds--debug:
284 file_adds--debug: c
284 file_adds--debug: c
285 file_adds--debug: b
285 file_adds--debug: b
286 file_adds--debug: a
286 file_adds--debug: a
287 file_dels:
287 file_dels:
288 file_dels:
288 file_dels:
289 file_dels:
289 file_dels:
290 file_dels:
290 file_dels:
291 file_dels:
291 file_dels:
292 file_dels:
292 file_dels:
293 file_dels:
293 file_dels:
294 file_dels:
294 file_dels:
295 file_dels--verbose:
295 file_dels--verbose:
296 file_dels--verbose:
296 file_dels--verbose:
297 file_dels--verbose:
297 file_dels--verbose:
298 file_dels--verbose:
298 file_dels--verbose:
299 file_dels--verbose:
299 file_dels--verbose:
300 file_dels--verbose:
300 file_dels--verbose:
301 file_dels--verbose:
301 file_dels--verbose:
302 file_dels--verbose:
302 file_dels--verbose:
303 file_dels--debug:
303 file_dels--debug:
304 file_dels--debug:
304 file_dels--debug:
305 file_dels--debug:
305 file_dels--debug:
306 file_dels--debug:
306 file_dels--debug:
307 file_dels--debug:
307 file_dels--debug:
308 file_dels--debug:
308 file_dels--debug:
309 file_dels--debug:
309 file_dels--debug:
310 file_dels--debug:
310 file_dels--debug:
311 files: second
311 files: second
312 files:
312 files:
313 files: d
313 files: d
314 files:
314 files:
315 files: c
315 files: c
316 files: c
316 files: c
317 files: b
317 files: b
318 files: a
318 files: a
319 files--verbose: second
319 files--verbose: second
320 files--verbose:
320 files--verbose:
321 files--verbose: d
321 files--verbose: d
322 files--verbose:
322 files--verbose:
323 files--verbose: c
323 files--verbose: c
324 files--verbose: c
324 files--verbose: c
325 files--verbose: b
325 files--verbose: b
326 files--verbose: a
326 files--verbose: a
327 files--debug:
327 files--debug:
328 files--debug:
328 files--debug:
329 files--debug:
329 files--debug:
330 files--debug:
330 files--debug:
331 files--debug: c
331 files--debug: c
332 files--debug:
332 files--debug:
333 files--debug:
333 files--debug:
334 files--debug:
334 files--debug:
335 manifest:
335 manifest: 7:f2dbc354b94e
336 manifest:
336 manifest: 6:91015e9dbdd7
337 manifest:
337 manifest: 5:4dc3def4f9b4
338 manifest:
338 manifest: 4:90ae8dda64e1
339 manifest:
339 manifest: 3:cb5a1327723b
340 manifest:
340 manifest: 2:6e0e82995c35
341 manifest:
341 manifest: 1:4e8d705b1e53
342 manifest:
342 manifest: 0:a0c8bcbbb45c
343 manifest--verbose:
343 manifest--verbose: 7:f2dbc354b94e
344 manifest--verbose:
344 manifest--verbose: 6:91015e9dbdd7
345 manifest--verbose:
345 manifest--verbose: 5:4dc3def4f9b4
346 manifest--verbose:
346 manifest--verbose: 4:90ae8dda64e1
347 manifest--verbose:
347 manifest--verbose: 3:cb5a1327723b
348 manifest--verbose:
348 manifest--verbose: 2:6e0e82995c35
349 manifest--verbose:
349 manifest--verbose: 1:4e8d705b1e53
350 manifest--verbose:
350 manifest--verbose: 0:a0c8bcbbb45c
351 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
351 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
352 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
352 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
353 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
353 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
354 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
354 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
355 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
355 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
356 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
356 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
357 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
357 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
358 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
358 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
359 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
359 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
360 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
360 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
361 node: 13207e5a10d9fd28ec424934298e176197f2c67f
361 node: 13207e5a10d9fd28ec424934298e176197f2c67f
362 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
362 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
363 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
363 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
364 node: 97054abb4ab824450e9164180baf491ae0078465
364 node: 97054abb4ab824450e9164180baf491ae0078465
365 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
365 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
366 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
366 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
367 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
367 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
368 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
368 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
369 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
369 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
370 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
370 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
371 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
371 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
372 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
372 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
373 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
373 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
374 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
374 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
375 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
375 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
376 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
376 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
377 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
377 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
378 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
378 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
379 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
379 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
380 node--debug: 97054abb4ab824450e9164180baf491ae0078465
380 node--debug: 97054abb4ab824450e9164180baf491ae0078465
381 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
381 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
382 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
382 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
383 parents: -1:000000000000
383 parents: -1:000000000000
384 parents: 5:13207e5a10d9 4:32a18f097fcc
384 parents: 5:13207e5a10d9 4:32a18f097fcc
385 parents: 3:10e46f2dcbf4
385 parents: 3:10e46f2dcbf4
386 parents:
386 parents:
387 parents:
387 parents:
388 parents:
388 parents:
389 parents:
389 parents:
390 parents:
390 parents:
391 parents--verbose: -1:000000000000
391 parents--verbose: -1:000000000000
392 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
392 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
393 parents--verbose: 3:10e46f2dcbf4
393 parents--verbose: 3:10e46f2dcbf4
394 parents--verbose:
394 parents--verbose:
395 parents--verbose:
395 parents--verbose:
396 parents--verbose:
396 parents--verbose:
397 parents--verbose:
397 parents--verbose:
398 parents--verbose:
398 parents--verbose:
399 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
399 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
400 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
400 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
401 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
401 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
402 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
402 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
403 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
403 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
404 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
404 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
405 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
405 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
406 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
406 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
407 rev: 7
407 rev: 7
408 rev: 6
408 rev: 6
409 rev: 5
409 rev: 5
410 rev: 4
410 rev: 4
411 rev: 3
411 rev: 3
412 rev: 2
412 rev: 2
413 rev: 1
413 rev: 1
414 rev: 0
414 rev: 0
415 rev--verbose: 7
415 rev--verbose: 7
416 rev--verbose: 6
416 rev--verbose: 6
417 rev--verbose: 5
417 rev--verbose: 5
418 rev--verbose: 4
418 rev--verbose: 4
419 rev--verbose: 3
419 rev--verbose: 3
420 rev--verbose: 2
420 rev--verbose: 2
421 rev--verbose: 1
421 rev--verbose: 1
422 rev--verbose: 0
422 rev--verbose: 0
423 rev--debug: 7
423 rev--debug: 7
424 rev--debug: 6
424 rev--debug: 6
425 rev--debug: 5
425 rev--debug: 5
426 rev--debug: 4
426 rev--debug: 4
427 rev--debug: 3
427 rev--debug: 3
428 rev--debug: 2
428 rev--debug: 2
429 rev--debug: 1
429 rev--debug: 1
430 rev--debug: 0
430 rev--debug: 0
431 tags: tip
431 tags: tip
432 tags:
432 tags:
433 tags:
433 tags:
434 tags:
434 tags:
435 tags:
435 tags:
436 tags:
436 tags:
437 tags:
437 tags:
438 tags:
438 tags:
439 tags--verbose: tip
439 tags--verbose: tip
440 tags--verbose:
440 tags--verbose:
441 tags--verbose:
441 tags--verbose:
442 tags--verbose:
442 tags--verbose:
443 tags--verbose:
443 tags--verbose:
444 tags--verbose:
444 tags--verbose:
445 tags--verbose:
445 tags--verbose:
446 tags--verbose:
446 tags--verbose:
447 tags--debug: tip
447 tags--debug: tip
448 tags--debug:
448 tags--debug:
449 tags--debug:
449 tags--debug:
450 tags--debug:
450 tags--debug:
451 tags--debug:
451 tags--debug:
452 tags--debug:
452 tags--debug:
453 tags--debug:
453 tags--debug:
454 tags--debug:
454 tags--debug:
455 # filters work
455 # filters work
456 hostname
456 hostname
457
457
458
458
459
459
460
460
461 place
461 place
462 place
462 place
463 hostname
463 hostname
464 User Name
464 User Name
465 person
465 person
466 person
466 person
467 person
467 person
468 person
468 person
469 other
469 other
470 A. N. Other
470 A. N. Other
471 User Name
471 User Name
472 user
472 user
473 person
473 person
474 person
474 person
475 person
475 person
476 person
476 person
477 other
477 other
478 other
478 other
479 user
479 user
480 Mon Jan 12 13:46:40 1970 +0000
480 Mon Jan 12 13:46:40 1970 +0000
481 Sun Jan 18 08:40:01 1970 +0000
481 Sun Jan 18 08:40:01 1970 +0000
482 Sun Jan 18 08:40:00 1970 +0000
482 Sun Jan 18 08:40:00 1970 +0000
483 Sat Jan 17 04:53:20 1970 +0000
483 Sat Jan 17 04:53:20 1970 +0000
484 Fri Jan 16 01:06:40 1970 +0000
484 Fri Jan 16 01:06:40 1970 +0000
485 Wed Jan 14 21:20:00 1970 +0000
485 Wed Jan 14 21:20:00 1970 +0000
486 Tue Jan 13 17:33:20 1970 +0000
486 Tue Jan 13 17:33:20 1970 +0000
487 Mon Jan 12 13:46:40 1970 +0000
487 Mon Jan 12 13:46:40 1970 +0000
488 1970-01-12 13:46 +0000
488 1970-01-12 13:46 +0000
489 1970-01-18 08:40 +0000
489 1970-01-18 08:40 +0000
490 1970-01-18 08:40 +0000
490 1970-01-18 08:40 +0000
491 1970-01-17 04:53 +0000
491 1970-01-17 04:53 +0000
492 1970-01-16 01:06 +0000
492 1970-01-16 01:06 +0000
493 1970-01-14 21:20 +0000
493 1970-01-14 21:20 +0000
494 1970-01-13 17:33 +0000
494 1970-01-13 17:33 +0000
495 1970-01-12 13:46 +0000
495 1970-01-12 13:46 +0000
496 Mon, 12 Jan 1970 13:46:40 +0000
496 Mon, 12 Jan 1970 13:46:40 +0000
497 Sun, 18 Jan 1970 08:40:01 +0000
497 Sun, 18 Jan 1970 08:40:01 +0000
498 Sun, 18 Jan 1970 08:40:00 +0000
498 Sun, 18 Jan 1970 08:40:00 +0000
499 Sat, 17 Jan 1970 04:53:20 +0000
499 Sat, 17 Jan 1970 04:53:20 +0000
500 Fri, 16 Jan 1970 01:06:40 +0000
500 Fri, 16 Jan 1970 01:06:40 +0000
501 Wed, 14 Jan 1970 21:20:00 +0000
501 Wed, 14 Jan 1970 21:20:00 +0000
502 Tue, 13 Jan 1970 17:33:20 +0000
502 Tue, 13 Jan 1970 17:33:20 +0000
503 Mon, 12 Jan 1970 13:46:40 +0000
503 Mon, 12 Jan 1970 13:46:40 +0000
504 second
504 second
505 merge
505 merge
506 new head
506 new head
507 new branch
507 new branch
508 no user, no domain
508 no user, no domain
509 no person
509 no person
510 other 1
510 other 1
511 line 1
511 line 1
512 29114dbae42b
512 29114dbae42b
513 c7b487c6c50e
513 c7b487c6c50e
514 13207e5a10d9
514 13207e5a10d9
515 32a18f097fcc
515 32a18f097fcc
516 10e46f2dcbf4
516 10e46f2dcbf4
517 97054abb4ab8
517 97054abb4ab8
518 b608e9d1a3f0
518 b608e9d1a3f0
519 1e4e1b8f71e0
519 1e4e1b8f71e0
520 # formatnode filter works
520 # formatnode filter works
521 # quiet
521 # quiet
522 1e4e1b8f71e0
522 1e4e1b8f71e0
523 # normal
523 # normal
524 1e4e1b8f71e0
524 1e4e1b8f71e0
525 # verbose
525 # verbose
526 1e4e1b8f71e0
526 1e4e1b8f71e0
527 # debug
527 # debug
528 1e4e1b8f71e05681d422154f5421e385fec3454f
528 1e4e1b8f71e05681d422154f5421e385fec3454f
529 # error on syntax
529 # error on syntax
530 abort: t:3: unmatched quotes
530 abort: t:3: unmatched quotes
531 # done
531 # done
General Comments 0
You need to be logged in to leave comments. Login now