##// END OF EJS Templates
cmdutil: fix missing default 'extra' template
Patrick Mezard -
r9961:0aa1a632 default
parent child Browse files
Show More
@@ -1,1293 +1,1294
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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, glob
10 import os, sys, errno, re, glob
11 import mdiff, bdiff, util, templater, patch, error, encoding
11 import mdiff, bdiff, util, templater, patch, error, encoding
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def findpossible(cmd, table, strict=False):
16 def findpossible(cmd, table, strict=False):
17 """
17 """
18 Return cmd -> (aliases, command table entry)
18 Return cmd -> (aliases, command table entry)
19 for each matching command.
19 for each matching command.
20 Return debug commands (or their aliases) only if no normal command matches.
20 Return debug commands (or their aliases) only if no normal command matches.
21 """
21 """
22 choice = {}
22 choice = {}
23 debugchoice = {}
23 debugchoice = {}
24 for e in table.keys():
24 for e in table.keys():
25 aliases = e.lstrip("^").split("|")
25 aliases = e.lstrip("^").split("|")
26 found = None
26 found = None
27 if cmd in aliases:
27 if cmd in aliases:
28 found = cmd
28 found = cmd
29 elif not strict:
29 elif not strict:
30 for a in aliases:
30 for a in aliases:
31 if a.startswith(cmd):
31 if a.startswith(cmd):
32 found = a
32 found = a
33 break
33 break
34 if found is not None:
34 if found is not None:
35 if aliases[0].startswith("debug") or found.startswith("debug"):
35 if aliases[0].startswith("debug") or found.startswith("debug"):
36 debugchoice[found] = (aliases, table[e])
36 debugchoice[found] = (aliases, table[e])
37 else:
37 else:
38 choice[found] = (aliases, table[e])
38 choice[found] = (aliases, table[e])
39
39
40 if not choice and debugchoice:
40 if not choice and debugchoice:
41 choice = debugchoice
41 choice = debugchoice
42
42
43 return choice
43 return choice
44
44
45 def findcmd(cmd, table, strict=True):
45 def findcmd(cmd, table, strict=True):
46 """Return (aliases, command table entry) for command string."""
46 """Return (aliases, command table entry) for command string."""
47 choice = findpossible(cmd, table, strict)
47 choice = findpossible(cmd, table, strict)
48
48
49 if cmd in choice:
49 if cmd in choice:
50 return choice[cmd]
50 return choice[cmd]
51
51
52 if len(choice) > 1:
52 if len(choice) > 1:
53 clist = choice.keys()
53 clist = choice.keys()
54 clist.sort()
54 clist.sort()
55 raise error.AmbiguousCommand(cmd, clist)
55 raise error.AmbiguousCommand(cmd, clist)
56
56
57 if choice:
57 if choice:
58 return choice.values()[0]
58 return choice.values()[0]
59
59
60 raise error.UnknownCommand(cmd)
60 raise error.UnknownCommand(cmd)
61
61
62 def bail_if_changed(repo):
62 def bail_if_changed(repo):
63 if repo.dirstate.parents()[1] != nullid:
63 if repo.dirstate.parents()[1] != nullid:
64 raise util.Abort(_('outstanding uncommitted merge'))
64 raise util.Abort(_('outstanding uncommitted merge'))
65 modified, added, removed, deleted = repo.status()[:4]
65 modified, added, removed, deleted = repo.status()[:4]
66 if modified or added or removed or deleted:
66 if modified or added or removed or deleted:
67 raise util.Abort(_("outstanding uncommitted changes"))
67 raise util.Abort(_("outstanding uncommitted changes"))
68
68
69 def logmessage(opts):
69 def logmessage(opts):
70 """ get the log message according to -m and -l option """
70 """ get the log message according to -m and -l option """
71 message = opts.get('message')
71 message = opts.get('message')
72 logfile = opts.get('logfile')
72 logfile = opts.get('logfile')
73
73
74 if message and logfile:
74 if message and logfile:
75 raise util.Abort(_('options --message and --logfile are mutually '
75 raise util.Abort(_('options --message and --logfile are mutually '
76 'exclusive'))
76 'exclusive'))
77 if not message and logfile:
77 if not message and logfile:
78 try:
78 try:
79 if logfile == '-':
79 if logfile == '-':
80 message = sys.stdin.read()
80 message = sys.stdin.read()
81 else:
81 else:
82 message = open(logfile).read()
82 message = open(logfile).read()
83 except IOError, inst:
83 except IOError, inst:
84 raise util.Abort(_("can't read commit message '%s': %s") %
84 raise util.Abort(_("can't read commit message '%s': %s") %
85 (logfile, inst.strerror))
85 (logfile, inst.strerror))
86 return message
86 return message
87
87
88 def loglimit(opts):
88 def loglimit(opts):
89 """get the log limit according to option -l/--limit"""
89 """get the log limit according to option -l/--limit"""
90 limit = opts.get('limit')
90 limit = opts.get('limit')
91 if limit:
91 if limit:
92 try:
92 try:
93 limit = int(limit)
93 limit = int(limit)
94 except ValueError:
94 except ValueError:
95 raise util.Abort(_('limit must be a positive integer'))
95 raise util.Abort(_('limit must be a positive integer'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
96 if limit <= 0: raise util.Abort(_('limit must be positive'))
97 else:
97 else:
98 limit = sys.maxint
98 limit = sys.maxint
99 return limit
99 return limit
100
100
101 def remoteui(src, opts):
101 def remoteui(src, opts):
102 'build a remote ui from ui or repo and opts'
102 'build a remote ui from ui or repo and opts'
103 if hasattr(src, 'baseui'): # looks like a repository
103 if hasattr(src, 'baseui'): # looks like a repository
104 dst = src.baseui.copy() # drop repo-specific config
104 dst = src.baseui.copy() # drop repo-specific config
105 src = src.ui # copy target options from repo
105 src = src.ui # copy target options from repo
106 else: # assume it's a global ui object
106 else: # assume it's a global ui object
107 dst = src.copy() # keep all global options
107 dst = src.copy() # keep all global options
108
108
109 # copy ssh-specific options
109 # copy ssh-specific options
110 for o in 'ssh', 'remotecmd':
110 for o in 'ssh', 'remotecmd':
111 v = opts.get(o) or src.config('ui', o)
111 v = opts.get(o) or src.config('ui', o)
112 if v:
112 if v:
113 dst.setconfig("ui", o, v)
113 dst.setconfig("ui", o, v)
114 # copy bundle-specific options
114 # copy bundle-specific options
115 r = src.config('bundle', 'mainreporoot')
115 r = src.config('bundle', 'mainreporoot')
116 if r:
116 if r:
117 dst.setconfig('bundle', 'mainreporoot', r)
117 dst.setconfig('bundle', 'mainreporoot', r)
118
118
119 return dst
119 return dst
120
120
121 def revpair(repo, revs):
121 def revpair(repo, revs):
122 '''return pair of nodes, given list of revisions. second item can
122 '''return pair of nodes, given list of revisions. second item can
123 be None, meaning use working dir.'''
123 be None, meaning use working dir.'''
124
124
125 def revfix(repo, val, defval):
125 def revfix(repo, val, defval):
126 if not val and val != 0 and defval is not None:
126 if not val and val != 0 and defval is not None:
127 val = defval
127 val = defval
128 return repo.lookup(val)
128 return repo.lookup(val)
129
129
130 if not revs:
130 if not revs:
131 return repo.dirstate.parents()[0], None
131 return repo.dirstate.parents()[0], None
132 end = None
132 end = None
133 if len(revs) == 1:
133 if len(revs) == 1:
134 if revrangesep in revs[0]:
134 if revrangesep in revs[0]:
135 start, end = revs[0].split(revrangesep, 1)
135 start, end = revs[0].split(revrangesep, 1)
136 start = revfix(repo, start, 0)
136 start = revfix(repo, start, 0)
137 end = revfix(repo, end, len(repo) - 1)
137 end = revfix(repo, end, len(repo) - 1)
138 else:
138 else:
139 start = revfix(repo, revs[0], None)
139 start = revfix(repo, revs[0], None)
140 elif len(revs) == 2:
140 elif len(revs) == 2:
141 if revrangesep in revs[0] or revrangesep in revs[1]:
141 if revrangesep in revs[0] or revrangesep in revs[1]:
142 raise util.Abort(_('too many revisions specified'))
142 raise util.Abort(_('too many revisions specified'))
143 start = revfix(repo, revs[0], None)
143 start = revfix(repo, revs[0], None)
144 end = revfix(repo, revs[1], None)
144 end = revfix(repo, revs[1], None)
145 else:
145 else:
146 raise util.Abort(_('too many revisions specified'))
146 raise util.Abort(_('too many revisions specified'))
147 return start, end
147 return start, end
148
148
149 def revrange(repo, revs):
149 def revrange(repo, revs):
150 """Yield revision as strings from a list of revision specifications."""
150 """Yield revision as strings from a list of revision specifications."""
151
151
152 def revfix(repo, val, defval):
152 def revfix(repo, val, defval):
153 if not val and val != 0 and defval is not None:
153 if not val and val != 0 and defval is not None:
154 return defval
154 return defval
155 return repo.changelog.rev(repo.lookup(val))
155 return repo.changelog.rev(repo.lookup(val))
156
156
157 seen, l = set(), []
157 seen, l = set(), []
158 for spec in revs:
158 for spec in revs:
159 if revrangesep in spec:
159 if revrangesep in spec:
160 start, end = spec.split(revrangesep, 1)
160 start, end = spec.split(revrangesep, 1)
161 start = revfix(repo, start, 0)
161 start = revfix(repo, start, 0)
162 end = revfix(repo, end, len(repo) - 1)
162 end = revfix(repo, end, len(repo) - 1)
163 step = start > end and -1 or 1
163 step = start > end and -1 or 1
164 for rev in xrange(start, end+step, step):
164 for rev in xrange(start, end+step, step):
165 if rev in seen:
165 if rev in seen:
166 continue
166 continue
167 seen.add(rev)
167 seen.add(rev)
168 l.append(rev)
168 l.append(rev)
169 else:
169 else:
170 rev = revfix(repo, spec, None)
170 rev = revfix(repo, spec, None)
171 if rev in seen:
171 if rev in seen:
172 continue
172 continue
173 seen.add(rev)
173 seen.add(rev)
174 l.append(rev)
174 l.append(rev)
175
175
176 return l
176 return l
177
177
178 def make_filename(repo, pat, node,
178 def make_filename(repo, pat, node,
179 total=None, seqno=None, revwidth=None, pathname=None):
179 total=None, seqno=None, revwidth=None, pathname=None):
180 node_expander = {
180 node_expander = {
181 'H': lambda: hex(node),
181 'H': lambda: hex(node),
182 'R': lambda: str(repo.changelog.rev(node)),
182 'R': lambda: str(repo.changelog.rev(node)),
183 'h': lambda: short(node),
183 'h': lambda: short(node),
184 }
184 }
185 expander = {
185 expander = {
186 '%': lambda: '%',
186 '%': lambda: '%',
187 'b': lambda: os.path.basename(repo.root),
187 'b': lambda: os.path.basename(repo.root),
188 }
188 }
189
189
190 try:
190 try:
191 if node:
191 if node:
192 expander.update(node_expander)
192 expander.update(node_expander)
193 if node:
193 if node:
194 expander['r'] = (lambda:
194 expander['r'] = (lambda:
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
195 str(repo.changelog.rev(node)).zfill(revwidth or 0))
196 if total is not None:
196 if total is not None:
197 expander['N'] = lambda: str(total)
197 expander['N'] = lambda: str(total)
198 if seqno is not None:
198 if seqno is not None:
199 expander['n'] = lambda: str(seqno)
199 expander['n'] = lambda: str(seqno)
200 if total is not None and seqno is not None:
200 if total is not None and seqno is not None:
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
201 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
202 if pathname is not None:
202 if pathname is not None:
203 expander['s'] = lambda: os.path.basename(pathname)
203 expander['s'] = lambda: os.path.basename(pathname)
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
204 expander['d'] = lambda: os.path.dirname(pathname) or '.'
205 expander['p'] = lambda: pathname
205 expander['p'] = lambda: pathname
206
206
207 newname = []
207 newname = []
208 patlen = len(pat)
208 patlen = len(pat)
209 i = 0
209 i = 0
210 while i < patlen:
210 while i < patlen:
211 c = pat[i]
211 c = pat[i]
212 if c == '%':
212 if c == '%':
213 i += 1
213 i += 1
214 c = pat[i]
214 c = pat[i]
215 c = expander[c]()
215 c = expander[c]()
216 newname.append(c)
216 newname.append(c)
217 i += 1
217 i += 1
218 return ''.join(newname)
218 return ''.join(newname)
219 except KeyError, inst:
219 except KeyError, inst:
220 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
220 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
221 inst.args[0])
221 inst.args[0])
222
222
223 def make_file(repo, pat, node=None,
223 def make_file(repo, pat, node=None,
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
224 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
225
225
226 writable = 'w' in mode or 'a' in mode
226 writable = 'w' in mode or 'a' in mode
227
227
228 if not pat or pat == '-':
228 if not pat or pat == '-':
229 return writable and sys.stdout or sys.stdin
229 return writable and sys.stdout or sys.stdin
230 if hasattr(pat, 'write') and writable:
230 if hasattr(pat, 'write') and writable:
231 return pat
231 return pat
232 if hasattr(pat, 'read') and 'r' in mode:
232 if hasattr(pat, 'read') and 'r' in mode:
233 return pat
233 return pat
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
234 return open(make_filename(repo, pat, node, total, seqno, revwidth,
235 pathname),
235 pathname),
236 mode)
236 mode)
237
237
238 def expandpats(pats):
238 def expandpats(pats):
239 if not util.expandglobs:
239 if not util.expandglobs:
240 return list(pats)
240 return list(pats)
241 ret = []
241 ret = []
242 for p in pats:
242 for p in pats:
243 kind, name = _match._patsplit(p, None)
243 kind, name = _match._patsplit(p, None)
244 if kind is None:
244 if kind is None:
245 try:
245 try:
246 globbed = glob.glob(name)
246 globbed = glob.glob(name)
247 except re.error:
247 except re.error:
248 globbed = [name]
248 globbed = [name]
249 if globbed:
249 if globbed:
250 ret.extend(globbed)
250 ret.extend(globbed)
251 continue
251 continue
252 ret.append(p)
252 ret.append(p)
253 return ret
253 return ret
254
254
255 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
255 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
256 if not globbed and default == 'relpath':
256 if not globbed and default == 'relpath':
257 pats = expandpats(pats or [])
257 pats = expandpats(pats or [])
258 m = _match.match(repo.root, repo.getcwd(), pats,
258 m = _match.match(repo.root, repo.getcwd(), pats,
259 opts.get('include'), opts.get('exclude'), default)
259 opts.get('include'), opts.get('exclude'), default)
260 def badfn(f, msg):
260 def badfn(f, msg):
261 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
261 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
262 m.bad = badfn
262 m.bad = badfn
263 return m
263 return m
264
264
265 def matchall(repo):
265 def matchall(repo):
266 return _match.always(repo.root, repo.getcwd())
266 return _match.always(repo.root, repo.getcwd())
267
267
268 def matchfiles(repo, files):
268 def matchfiles(repo, files):
269 return _match.exact(repo.root, repo.getcwd(), files)
269 return _match.exact(repo.root, repo.getcwd(), files)
270
270
271 def findrenames(repo, added, removed, threshold):
271 def findrenames(repo, added, removed, threshold):
272 '''find renamed files -- yields (before, after, score) tuples'''
272 '''find renamed files -- yields (before, after, score) tuples'''
273 copies = {}
273 copies = {}
274 ctx = repo['.']
274 ctx = repo['.']
275 for r in removed:
275 for r in removed:
276 if r not in ctx:
276 if r not in ctx:
277 continue
277 continue
278 fctx = ctx.filectx(r)
278 fctx = ctx.filectx(r)
279
279
280 def score(text):
280 def score(text):
281 if not len(text):
281 if not len(text):
282 return 0.0
282 return 0.0
283 if not fctx.cmp(text):
283 if not fctx.cmp(text):
284 return 1.0
284 return 1.0
285 if threshold == 1.0:
285 if threshold == 1.0:
286 return 0.0
286 return 0.0
287 orig = fctx.data()
287 orig = fctx.data()
288 # bdiff.blocks() returns blocks of matching lines
288 # bdiff.blocks() returns blocks of matching lines
289 # count the number of bytes in each
289 # count the number of bytes in each
290 equal = 0
290 equal = 0
291 alines = mdiff.splitnewlines(text)
291 alines = mdiff.splitnewlines(text)
292 matches = bdiff.blocks(text, orig)
292 matches = bdiff.blocks(text, orig)
293 for x1, x2, y1, y2 in matches:
293 for x1, x2, y1, y2 in matches:
294 for line in alines[x1:x2]:
294 for line in alines[x1:x2]:
295 equal += len(line)
295 equal += len(line)
296
296
297 lengths = len(text) + len(orig)
297 lengths = len(text) + len(orig)
298 return equal * 2.0 / lengths
298 return equal * 2.0 / lengths
299
299
300 for a in added:
300 for a in added:
301 bestscore = copies.get(a, (None, threshold))[1]
301 bestscore = copies.get(a, (None, threshold))[1]
302 myscore = score(repo.wread(a))
302 myscore = score(repo.wread(a))
303 if myscore >= bestscore:
303 if myscore >= bestscore:
304 copies[a] = (r, myscore)
304 copies[a] = (r, myscore)
305
305
306 for dest, v in copies.iteritems():
306 for dest, v in copies.iteritems():
307 source, score = v
307 source, score = v
308 yield source, dest, score
308 yield source, dest, score
309
309
310 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
310 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
311 if dry_run is None:
311 if dry_run is None:
312 dry_run = opts.get('dry_run')
312 dry_run = opts.get('dry_run')
313 if similarity is None:
313 if similarity is None:
314 similarity = float(opts.get('similarity') or 0)
314 similarity = float(opts.get('similarity') or 0)
315 # we'd use status here, except handling of symlinks and ignore is tricky
315 # we'd use status here, except handling of symlinks and ignore is tricky
316 added, unknown, deleted, removed = [], [], [], []
316 added, unknown, deleted, removed = [], [], [], []
317 audit_path = util.path_auditor(repo.root)
317 audit_path = util.path_auditor(repo.root)
318 m = match(repo, pats, opts)
318 m = match(repo, pats, opts)
319 for abs in repo.walk(m):
319 for abs in repo.walk(m):
320 target = repo.wjoin(abs)
320 target = repo.wjoin(abs)
321 good = True
321 good = True
322 try:
322 try:
323 audit_path(abs)
323 audit_path(abs)
324 except:
324 except:
325 good = False
325 good = False
326 rel = m.rel(abs)
326 rel = m.rel(abs)
327 exact = m.exact(abs)
327 exact = m.exact(abs)
328 if good and abs not in repo.dirstate:
328 if good and abs not in repo.dirstate:
329 unknown.append(abs)
329 unknown.append(abs)
330 if repo.ui.verbose or not exact:
330 if repo.ui.verbose or not exact:
331 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
331 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
332 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
332 elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
333 or (os.path.isdir(target) and not os.path.islink(target))):
333 or (os.path.isdir(target) and not os.path.islink(target))):
334 deleted.append(abs)
334 deleted.append(abs)
335 if repo.ui.verbose or not exact:
335 if repo.ui.verbose or not exact:
336 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
336 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
337 # for finding renames
337 # for finding renames
338 elif repo.dirstate[abs] == 'r':
338 elif repo.dirstate[abs] == 'r':
339 removed.append(abs)
339 removed.append(abs)
340 elif repo.dirstate[abs] == 'a':
340 elif repo.dirstate[abs] == 'a':
341 added.append(abs)
341 added.append(abs)
342 if not dry_run:
342 if not dry_run:
343 repo.remove(deleted)
343 repo.remove(deleted)
344 repo.add(unknown)
344 repo.add(unknown)
345 if similarity > 0:
345 if similarity > 0:
346 for old, new, score in findrenames(repo, added + unknown,
346 for old, new, score in findrenames(repo, added + unknown,
347 removed + deleted, similarity):
347 removed + deleted, similarity):
348 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
348 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
349 repo.ui.status(_('recording removal of %s as rename to %s '
349 repo.ui.status(_('recording removal of %s as rename to %s '
350 '(%d%% similar)\n') %
350 '(%d%% similar)\n') %
351 (m.rel(old), m.rel(new), score * 100))
351 (m.rel(old), m.rel(new), score * 100))
352 if not dry_run:
352 if not dry_run:
353 repo.copy(old, new)
353 repo.copy(old, new)
354
354
355 def copy(ui, repo, pats, opts, rename=False):
355 def copy(ui, repo, pats, opts, rename=False):
356 # called with the repo lock held
356 # called with the repo lock held
357 #
357 #
358 # hgsep => pathname that uses "/" to separate directories
358 # hgsep => pathname that uses "/" to separate directories
359 # ossep => pathname that uses os.sep to separate directories
359 # ossep => pathname that uses os.sep to separate directories
360 cwd = repo.getcwd()
360 cwd = repo.getcwd()
361 targets = {}
361 targets = {}
362 after = opts.get("after")
362 after = opts.get("after")
363 dryrun = opts.get("dry_run")
363 dryrun = opts.get("dry_run")
364
364
365 def walkpat(pat):
365 def walkpat(pat):
366 srcs = []
366 srcs = []
367 m = match(repo, [pat], opts, globbed=True)
367 m = match(repo, [pat], opts, globbed=True)
368 for abs in repo.walk(m):
368 for abs in repo.walk(m):
369 state = repo.dirstate[abs]
369 state = repo.dirstate[abs]
370 rel = m.rel(abs)
370 rel = m.rel(abs)
371 exact = m.exact(abs)
371 exact = m.exact(abs)
372 if state in '?r':
372 if state in '?r':
373 if exact and state == '?':
373 if exact and state == '?':
374 ui.warn(_('%s: not copying - file is not managed\n') % rel)
374 ui.warn(_('%s: not copying - file is not managed\n') % rel)
375 if exact and state == 'r':
375 if exact and state == 'r':
376 ui.warn(_('%s: not copying - file has been marked for'
376 ui.warn(_('%s: not copying - file has been marked for'
377 ' remove\n') % rel)
377 ' remove\n') % rel)
378 continue
378 continue
379 # abs: hgsep
379 # abs: hgsep
380 # rel: ossep
380 # rel: ossep
381 srcs.append((abs, rel, exact))
381 srcs.append((abs, rel, exact))
382 return srcs
382 return srcs
383
383
384 # abssrc: hgsep
384 # abssrc: hgsep
385 # relsrc: ossep
385 # relsrc: ossep
386 # otarget: ossep
386 # otarget: ossep
387 def copyfile(abssrc, relsrc, otarget, exact):
387 def copyfile(abssrc, relsrc, otarget, exact):
388 abstarget = util.canonpath(repo.root, cwd, otarget)
388 abstarget = util.canonpath(repo.root, cwd, otarget)
389 reltarget = repo.pathto(abstarget, cwd)
389 reltarget = repo.pathto(abstarget, cwd)
390 target = repo.wjoin(abstarget)
390 target = repo.wjoin(abstarget)
391 src = repo.wjoin(abssrc)
391 src = repo.wjoin(abssrc)
392 state = repo.dirstate[abstarget]
392 state = repo.dirstate[abstarget]
393
393
394 # check for collisions
394 # check for collisions
395 prevsrc = targets.get(abstarget)
395 prevsrc = targets.get(abstarget)
396 if prevsrc is not None:
396 if prevsrc is not None:
397 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
397 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
398 (reltarget, repo.pathto(abssrc, cwd),
398 (reltarget, repo.pathto(abssrc, cwd),
399 repo.pathto(prevsrc, cwd)))
399 repo.pathto(prevsrc, cwd)))
400 return
400 return
401
401
402 # check for overwrites
402 # check for overwrites
403 exists = os.path.exists(target)
403 exists = os.path.exists(target)
404 if not after and exists or after and state in 'mn':
404 if not after and exists or after and state in 'mn':
405 if not opts['force']:
405 if not opts['force']:
406 ui.warn(_('%s: not overwriting - file exists\n') %
406 ui.warn(_('%s: not overwriting - file exists\n') %
407 reltarget)
407 reltarget)
408 return
408 return
409
409
410 if after:
410 if after:
411 if not exists:
411 if not exists:
412 return
412 return
413 elif not dryrun:
413 elif not dryrun:
414 try:
414 try:
415 if exists:
415 if exists:
416 os.unlink(target)
416 os.unlink(target)
417 targetdir = os.path.dirname(target) or '.'
417 targetdir = os.path.dirname(target) or '.'
418 if not os.path.isdir(targetdir):
418 if not os.path.isdir(targetdir):
419 os.makedirs(targetdir)
419 os.makedirs(targetdir)
420 util.copyfile(src, target)
420 util.copyfile(src, target)
421 except IOError, inst:
421 except IOError, inst:
422 if inst.errno == errno.ENOENT:
422 if inst.errno == errno.ENOENT:
423 ui.warn(_('%s: deleted in working copy\n') % relsrc)
423 ui.warn(_('%s: deleted in working copy\n') % relsrc)
424 else:
424 else:
425 ui.warn(_('%s: cannot copy - %s\n') %
425 ui.warn(_('%s: cannot copy - %s\n') %
426 (relsrc, inst.strerror))
426 (relsrc, inst.strerror))
427 return True # report a failure
427 return True # report a failure
428
428
429 if ui.verbose or not exact:
429 if ui.verbose or not exact:
430 if rename:
430 if rename:
431 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
431 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
432 else:
432 else:
433 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
433 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
434
434
435 targets[abstarget] = abssrc
435 targets[abstarget] = abssrc
436
436
437 # fix up dirstate
437 # fix up dirstate
438 origsrc = repo.dirstate.copied(abssrc) or abssrc
438 origsrc = repo.dirstate.copied(abssrc) or abssrc
439 if abstarget == origsrc: # copying back a copy?
439 if abstarget == origsrc: # copying back a copy?
440 if state not in 'mn' and not dryrun:
440 if state not in 'mn' and not dryrun:
441 repo.dirstate.normallookup(abstarget)
441 repo.dirstate.normallookup(abstarget)
442 else:
442 else:
443 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
443 if repo.dirstate[origsrc] == 'a' and origsrc == abssrc:
444 if not ui.quiet:
444 if not ui.quiet:
445 ui.warn(_("%s has not been committed yet, so no copy "
445 ui.warn(_("%s has not been committed yet, so no copy "
446 "data will be stored for %s.\n")
446 "data will be stored for %s.\n")
447 % (repo.pathto(origsrc, cwd), reltarget))
447 % (repo.pathto(origsrc, cwd), reltarget))
448 if repo.dirstate[abstarget] in '?r' and not dryrun:
448 if repo.dirstate[abstarget] in '?r' and not dryrun:
449 repo.add([abstarget])
449 repo.add([abstarget])
450 elif not dryrun:
450 elif not dryrun:
451 repo.copy(origsrc, abstarget)
451 repo.copy(origsrc, abstarget)
452
452
453 if rename and not dryrun:
453 if rename and not dryrun:
454 repo.remove([abssrc], not after)
454 repo.remove([abssrc], not after)
455
455
456 # pat: ossep
456 # pat: ossep
457 # dest ossep
457 # dest ossep
458 # srcs: list of (hgsep, hgsep, ossep, bool)
458 # srcs: list of (hgsep, hgsep, ossep, bool)
459 # return: function that takes hgsep and returns ossep
459 # return: function that takes hgsep and returns ossep
460 def targetpathfn(pat, dest, srcs):
460 def targetpathfn(pat, dest, srcs):
461 if os.path.isdir(pat):
461 if os.path.isdir(pat):
462 abspfx = util.canonpath(repo.root, cwd, pat)
462 abspfx = util.canonpath(repo.root, cwd, pat)
463 abspfx = util.localpath(abspfx)
463 abspfx = util.localpath(abspfx)
464 if destdirexists:
464 if destdirexists:
465 striplen = len(os.path.split(abspfx)[0])
465 striplen = len(os.path.split(abspfx)[0])
466 else:
466 else:
467 striplen = len(abspfx)
467 striplen = len(abspfx)
468 if striplen:
468 if striplen:
469 striplen += len(os.sep)
469 striplen += len(os.sep)
470 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
470 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
471 elif destdirexists:
471 elif destdirexists:
472 res = lambda p: os.path.join(dest,
472 res = lambda p: os.path.join(dest,
473 os.path.basename(util.localpath(p)))
473 os.path.basename(util.localpath(p)))
474 else:
474 else:
475 res = lambda p: dest
475 res = lambda p: dest
476 return res
476 return res
477
477
478 # pat: ossep
478 # pat: ossep
479 # dest ossep
479 # dest ossep
480 # srcs: list of (hgsep, hgsep, ossep, bool)
480 # srcs: list of (hgsep, hgsep, ossep, bool)
481 # return: function that takes hgsep and returns ossep
481 # return: function that takes hgsep and returns ossep
482 def targetpathafterfn(pat, dest, srcs):
482 def targetpathafterfn(pat, dest, srcs):
483 if _match.patkind(pat):
483 if _match.patkind(pat):
484 # a mercurial pattern
484 # a mercurial pattern
485 res = lambda p: os.path.join(dest,
485 res = lambda p: os.path.join(dest,
486 os.path.basename(util.localpath(p)))
486 os.path.basename(util.localpath(p)))
487 else:
487 else:
488 abspfx = util.canonpath(repo.root, cwd, pat)
488 abspfx = util.canonpath(repo.root, cwd, pat)
489 if len(abspfx) < len(srcs[0][0]):
489 if len(abspfx) < len(srcs[0][0]):
490 # A directory. Either the target path contains the last
490 # A directory. Either the target path contains the last
491 # component of the source path or it does not.
491 # component of the source path or it does not.
492 def evalpath(striplen):
492 def evalpath(striplen):
493 score = 0
493 score = 0
494 for s in srcs:
494 for s in srcs:
495 t = os.path.join(dest, util.localpath(s[0])[striplen:])
495 t = os.path.join(dest, util.localpath(s[0])[striplen:])
496 if os.path.exists(t):
496 if os.path.exists(t):
497 score += 1
497 score += 1
498 return score
498 return score
499
499
500 abspfx = util.localpath(abspfx)
500 abspfx = util.localpath(abspfx)
501 striplen = len(abspfx)
501 striplen = len(abspfx)
502 if striplen:
502 if striplen:
503 striplen += len(os.sep)
503 striplen += len(os.sep)
504 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
504 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
505 score = evalpath(striplen)
505 score = evalpath(striplen)
506 striplen1 = len(os.path.split(abspfx)[0])
506 striplen1 = len(os.path.split(abspfx)[0])
507 if striplen1:
507 if striplen1:
508 striplen1 += len(os.sep)
508 striplen1 += len(os.sep)
509 if evalpath(striplen1) > score:
509 if evalpath(striplen1) > score:
510 striplen = striplen1
510 striplen = striplen1
511 res = lambda p: os.path.join(dest,
511 res = lambda p: os.path.join(dest,
512 util.localpath(p)[striplen:])
512 util.localpath(p)[striplen:])
513 else:
513 else:
514 # a file
514 # a file
515 if destdirexists:
515 if destdirexists:
516 res = lambda p: os.path.join(dest,
516 res = lambda p: os.path.join(dest,
517 os.path.basename(util.localpath(p)))
517 os.path.basename(util.localpath(p)))
518 else:
518 else:
519 res = lambda p: dest
519 res = lambda p: dest
520 return res
520 return res
521
521
522
522
523 pats = expandpats(pats)
523 pats = expandpats(pats)
524 if not pats:
524 if not pats:
525 raise util.Abort(_('no source or destination specified'))
525 raise util.Abort(_('no source or destination specified'))
526 if len(pats) == 1:
526 if len(pats) == 1:
527 raise util.Abort(_('no destination specified'))
527 raise util.Abort(_('no destination specified'))
528 dest = pats.pop()
528 dest = pats.pop()
529 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
529 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
530 if not destdirexists:
530 if not destdirexists:
531 if len(pats) > 1 or _match.patkind(pats[0]):
531 if len(pats) > 1 or _match.patkind(pats[0]):
532 raise util.Abort(_('with multiple sources, destination must be an '
532 raise util.Abort(_('with multiple sources, destination must be an '
533 'existing directory'))
533 'existing directory'))
534 if util.endswithsep(dest):
534 if util.endswithsep(dest):
535 raise util.Abort(_('destination %s is not a directory') % dest)
535 raise util.Abort(_('destination %s is not a directory') % dest)
536
536
537 tfn = targetpathfn
537 tfn = targetpathfn
538 if after:
538 if after:
539 tfn = targetpathafterfn
539 tfn = targetpathafterfn
540 copylist = []
540 copylist = []
541 for pat in pats:
541 for pat in pats:
542 srcs = walkpat(pat)
542 srcs = walkpat(pat)
543 if not srcs:
543 if not srcs:
544 continue
544 continue
545 copylist.append((tfn(pat, dest, srcs), srcs))
545 copylist.append((tfn(pat, dest, srcs), srcs))
546 if not copylist:
546 if not copylist:
547 raise util.Abort(_('no files to copy'))
547 raise util.Abort(_('no files to copy'))
548
548
549 errors = 0
549 errors = 0
550 for targetpath, srcs in copylist:
550 for targetpath, srcs in copylist:
551 for abssrc, relsrc, exact in srcs:
551 for abssrc, relsrc, exact in srcs:
552 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
552 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
553 errors += 1
553 errors += 1
554
554
555 if errors:
555 if errors:
556 ui.warn(_('(consider using --after)\n'))
556 ui.warn(_('(consider using --after)\n'))
557
557
558 return errors
558 return errors
559
559
560 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
560 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
561 runargs=None):
561 runargs=None):
562 '''Run a command as a service.'''
562 '''Run a command as a service.'''
563
563
564 if opts['daemon'] and not opts['daemon_pipefds']:
564 if opts['daemon'] and not opts['daemon_pipefds']:
565 rfd, wfd = os.pipe()
565 rfd, wfd = os.pipe()
566 if not runargs:
566 if not runargs:
567 runargs = sys.argv[:]
567 runargs = sys.argv[:]
568 runargs.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
568 runargs.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
569 # Don't pass --cwd to the child process, because we've already
569 # Don't pass --cwd to the child process, because we've already
570 # changed directory.
570 # changed directory.
571 for i in xrange(1,len(runargs)):
571 for i in xrange(1,len(runargs)):
572 if runargs[i].startswith('--cwd='):
572 if runargs[i].startswith('--cwd='):
573 del runargs[i]
573 del runargs[i]
574 break
574 break
575 elif runargs[i].startswith('--cwd'):
575 elif runargs[i].startswith('--cwd'):
576 del runargs[i:i+2]
576 del runargs[i:i+2]
577 break
577 break
578 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
578 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
579 runargs[0], runargs)
579 runargs[0], runargs)
580 os.close(wfd)
580 os.close(wfd)
581 os.read(rfd, 1)
581 os.read(rfd, 1)
582 if parentfn:
582 if parentfn:
583 return parentfn(pid)
583 return parentfn(pid)
584 else:
584 else:
585 return
585 return
586
586
587 if initfn:
587 if initfn:
588 initfn()
588 initfn()
589
589
590 if opts['pid_file']:
590 if opts['pid_file']:
591 fp = open(opts['pid_file'], 'w')
591 fp = open(opts['pid_file'], 'w')
592 fp.write(str(os.getpid()) + '\n')
592 fp.write(str(os.getpid()) + '\n')
593 fp.close()
593 fp.close()
594
594
595 if opts['daemon_pipefds']:
595 if opts['daemon_pipefds']:
596 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
596 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
597 os.close(rfd)
597 os.close(rfd)
598 try:
598 try:
599 os.setsid()
599 os.setsid()
600 except AttributeError:
600 except AttributeError:
601 pass
601 pass
602 os.write(wfd, 'y')
602 os.write(wfd, 'y')
603 os.close(wfd)
603 os.close(wfd)
604 sys.stdout.flush()
604 sys.stdout.flush()
605 sys.stderr.flush()
605 sys.stderr.flush()
606
606
607 nullfd = os.open(util.nulldev, os.O_RDWR)
607 nullfd = os.open(util.nulldev, os.O_RDWR)
608 logfilefd = nullfd
608 logfilefd = nullfd
609 if logfile:
609 if logfile:
610 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
610 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
611 os.dup2(nullfd, 0)
611 os.dup2(nullfd, 0)
612 os.dup2(logfilefd, 1)
612 os.dup2(logfilefd, 1)
613 os.dup2(logfilefd, 2)
613 os.dup2(logfilefd, 2)
614 if nullfd not in (0, 1, 2):
614 if nullfd not in (0, 1, 2):
615 os.close(nullfd)
615 os.close(nullfd)
616 if logfile and logfilefd not in (0, 1, 2):
616 if logfile and logfilefd not in (0, 1, 2):
617 os.close(logfilefd)
617 os.close(logfilefd)
618
618
619 if runfn:
619 if runfn:
620 return runfn()
620 return runfn()
621
621
622 class changeset_printer(object):
622 class changeset_printer(object):
623 '''show changeset information when templating not requested.'''
623 '''show changeset information when templating not requested.'''
624
624
625 def __init__(self, ui, repo, patch, diffopts, buffered):
625 def __init__(self, ui, repo, patch, diffopts, buffered):
626 self.ui = ui
626 self.ui = ui
627 self.repo = repo
627 self.repo = repo
628 self.buffered = buffered
628 self.buffered = buffered
629 self.patch = patch
629 self.patch = patch
630 self.diffopts = diffopts
630 self.diffopts = diffopts
631 self.header = {}
631 self.header = {}
632 self.hunk = {}
632 self.hunk = {}
633 self.lastheader = None
633 self.lastheader = None
634
634
635 def flush(self, rev):
635 def flush(self, rev):
636 if rev in self.header:
636 if rev in self.header:
637 h = self.header[rev]
637 h = self.header[rev]
638 if h != self.lastheader:
638 if h != self.lastheader:
639 self.lastheader = h
639 self.lastheader = h
640 self.ui.write(h)
640 self.ui.write(h)
641 del self.header[rev]
641 del self.header[rev]
642 if rev in self.hunk:
642 if rev in self.hunk:
643 self.ui.write(self.hunk[rev])
643 self.ui.write(self.hunk[rev])
644 del self.hunk[rev]
644 del self.hunk[rev]
645 return 1
645 return 1
646 return 0
646 return 0
647
647
648 def show(self, ctx, copies=(), **props):
648 def show(self, ctx, copies=(), **props):
649 if self.buffered:
649 if self.buffered:
650 self.ui.pushbuffer()
650 self.ui.pushbuffer()
651 self._show(ctx, copies, props)
651 self._show(ctx, copies, props)
652 self.hunk[ctx.rev()] = self.ui.popbuffer()
652 self.hunk[ctx.rev()] = self.ui.popbuffer()
653 else:
653 else:
654 self._show(ctx, copies, props)
654 self._show(ctx, copies, props)
655
655
656 def _show(self, ctx, copies, props):
656 def _show(self, ctx, copies, props):
657 '''show a single changeset or file revision'''
657 '''show a single changeset or file revision'''
658 changenode = ctx.node()
658 changenode = ctx.node()
659 rev = ctx.rev()
659 rev = ctx.rev()
660
660
661 if self.ui.quiet:
661 if self.ui.quiet:
662 self.ui.write("%d:%s\n" % (rev, short(changenode)))
662 self.ui.write("%d:%s\n" % (rev, short(changenode)))
663 return
663 return
664
664
665 log = self.repo.changelog
665 log = self.repo.changelog
666 date = util.datestr(ctx.date())
666 date = util.datestr(ctx.date())
667
667
668 hexfunc = self.ui.debugflag and hex or short
668 hexfunc = self.ui.debugflag and hex or short
669
669
670 parents = [(p, hexfunc(log.node(p)))
670 parents = [(p, hexfunc(log.node(p)))
671 for p in self._meaningful_parentrevs(log, rev)]
671 for p in self._meaningful_parentrevs(log, rev)]
672
672
673 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
673 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
674
674
675 branch = ctx.branch()
675 branch = ctx.branch()
676 # don't show the default branch name
676 # don't show the default branch name
677 if branch != 'default':
677 if branch != 'default':
678 branch = encoding.tolocal(branch)
678 branch = encoding.tolocal(branch)
679 self.ui.write(_("branch: %s\n") % branch)
679 self.ui.write(_("branch: %s\n") % branch)
680 for tag in self.repo.nodetags(changenode):
680 for tag in self.repo.nodetags(changenode):
681 self.ui.write(_("tag: %s\n") % tag)
681 self.ui.write(_("tag: %s\n") % tag)
682 for parent in parents:
682 for parent in parents:
683 self.ui.write(_("parent: %d:%s\n") % parent)
683 self.ui.write(_("parent: %d:%s\n") % parent)
684
684
685 if self.ui.debugflag:
685 if self.ui.debugflag:
686 mnode = ctx.manifestnode()
686 mnode = ctx.manifestnode()
687 self.ui.write(_("manifest: %d:%s\n") %
687 self.ui.write(_("manifest: %d:%s\n") %
688 (self.repo.manifest.rev(mnode), hex(mnode)))
688 (self.repo.manifest.rev(mnode), hex(mnode)))
689 self.ui.write(_("user: %s\n") % ctx.user())
689 self.ui.write(_("user: %s\n") % ctx.user())
690 self.ui.write(_("date: %s\n") % date)
690 self.ui.write(_("date: %s\n") % date)
691
691
692 if self.ui.debugflag:
692 if self.ui.debugflag:
693 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
693 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
694 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
694 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
695 files):
695 files):
696 if value:
696 if value:
697 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
697 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
698 elif ctx.files() and self.ui.verbose:
698 elif ctx.files() and self.ui.verbose:
699 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
699 self.ui.write(_("files: %s\n") % " ".join(ctx.files()))
700 if copies and self.ui.verbose:
700 if copies and self.ui.verbose:
701 copies = ['%s (%s)' % c for c in copies]
701 copies = ['%s (%s)' % c for c in copies]
702 self.ui.write(_("copies: %s\n") % ' '.join(copies))
702 self.ui.write(_("copies: %s\n") % ' '.join(copies))
703
703
704 extra = ctx.extra()
704 extra = ctx.extra()
705 if extra and self.ui.debugflag:
705 if extra and self.ui.debugflag:
706 for key, value in sorted(extra.items()):
706 for key, value in sorted(extra.items()):
707 self.ui.write(_("extra: %s=%s\n")
707 self.ui.write(_("extra: %s=%s\n")
708 % (key, value.encode('string_escape')))
708 % (key, value.encode('string_escape')))
709
709
710 description = ctx.description().strip()
710 description = ctx.description().strip()
711 if description:
711 if description:
712 if self.ui.verbose:
712 if self.ui.verbose:
713 self.ui.write(_("description:\n"))
713 self.ui.write(_("description:\n"))
714 self.ui.write(description)
714 self.ui.write(description)
715 self.ui.write("\n\n")
715 self.ui.write("\n\n")
716 else:
716 else:
717 self.ui.write(_("summary: %s\n") %
717 self.ui.write(_("summary: %s\n") %
718 description.splitlines()[0])
718 description.splitlines()[0])
719 self.ui.write("\n")
719 self.ui.write("\n")
720
720
721 self.showpatch(changenode)
721 self.showpatch(changenode)
722
722
723 def showpatch(self, node):
723 def showpatch(self, node):
724 if self.patch:
724 if self.patch:
725 prev = self.repo.changelog.parents(node)[0]
725 prev = self.repo.changelog.parents(node)[0]
726 chunks = patch.diff(self.repo, prev, node, match=self.patch,
726 chunks = patch.diff(self.repo, prev, node, match=self.patch,
727 opts=patch.diffopts(self.ui, self.diffopts))
727 opts=patch.diffopts(self.ui, self.diffopts))
728 for chunk in chunks:
728 for chunk in chunks:
729 self.ui.write(chunk)
729 self.ui.write(chunk)
730 self.ui.write("\n")
730 self.ui.write("\n")
731
731
732 def _meaningful_parentrevs(self, log, rev):
732 def _meaningful_parentrevs(self, log, rev):
733 """Return list of meaningful (or all if debug) parentrevs for rev.
733 """Return list of meaningful (or all if debug) parentrevs for rev.
734
734
735 For merges (two non-nullrev revisions) both parents are meaningful.
735 For merges (two non-nullrev revisions) both parents are meaningful.
736 Otherwise the first parent revision is considered meaningful if it
736 Otherwise the first parent revision is considered meaningful if it
737 is not the preceding revision.
737 is not the preceding revision.
738 """
738 """
739 parents = log.parentrevs(rev)
739 parents = log.parentrevs(rev)
740 if not self.ui.debugflag and parents[1] == nullrev:
740 if not self.ui.debugflag and parents[1] == nullrev:
741 if parents[0] >= rev - 1:
741 if parents[0] >= rev - 1:
742 parents = []
742 parents = []
743 else:
743 else:
744 parents = [parents[0]]
744 parents = [parents[0]]
745 return parents
745 return parents
746
746
747
747
748 class changeset_templater(changeset_printer):
748 class changeset_templater(changeset_printer):
749 '''format changeset information.'''
749 '''format changeset information.'''
750
750
751 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
751 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
752 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
752 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
753 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
753 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
754 self.t = templater.templater(mapfile, {'formatnode': formatnode},
754 self.t = templater.templater(mapfile, {'formatnode': formatnode},
755 cache={
755 cache={
756 'parent': '{rev}:{node|formatnode} ',
756 'parent': '{rev}:{node|formatnode} ',
757 'manifest': '{rev}:{node|formatnode}',
757 'manifest': '{rev}:{node|formatnode}',
758 'filecopy': '{name} ({source})'})
758 'filecopy': '{name} ({source})',
759 'extra': '{key}={value|stringescape}'})
759 # Cache mapping from rev to a tuple with tag date, tag
760 # Cache mapping from rev to a tuple with tag date, tag
760 # distance and tag name
761 # distance and tag name
761 self._latesttagcache = {-1: (0, 0, 'null')}
762 self._latesttagcache = {-1: (0, 0, 'null')}
762
763
763 def use_template(self, t):
764 def use_template(self, t):
764 '''set template string to use'''
765 '''set template string to use'''
765 self.t.cache['changeset'] = t
766 self.t.cache['changeset'] = t
766
767
767 def _meaningful_parentrevs(self, ctx):
768 def _meaningful_parentrevs(self, ctx):
768 """Return list of meaningful (or all if debug) parentrevs for rev.
769 """Return list of meaningful (or all if debug) parentrevs for rev.
769 """
770 """
770 parents = ctx.parents()
771 parents = ctx.parents()
771 if len(parents) > 1:
772 if len(parents) > 1:
772 return parents
773 return parents
773 if self.ui.debugflag:
774 if self.ui.debugflag:
774 return [parents[0], self.repo['null']]
775 return [parents[0], self.repo['null']]
775 if parents[0].rev() >= ctx.rev() - 1:
776 if parents[0].rev() >= ctx.rev() - 1:
776 return []
777 return []
777 return parents
778 return parents
778
779
779 def _latesttaginfo(self, rev):
780 def _latesttaginfo(self, rev):
780 '''return date, distance and name for the latest tag of rev'''
781 '''return date, distance and name for the latest tag of rev'''
781 todo = [rev]
782 todo = [rev]
782 while todo:
783 while todo:
783 rev = todo.pop()
784 rev = todo.pop()
784 if rev in self._latesttagcache:
785 if rev in self._latesttagcache:
785 continue
786 continue
786 ctx = self.repo[rev]
787 ctx = self.repo[rev]
787 tags = [t for t in ctx.tags() if self.repo.tagtype(t) == 'global']
788 tags = [t for t in ctx.tags() if self.repo.tagtype(t) == 'global']
788 if tags:
789 if tags:
789 self._latesttagcache[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
790 self._latesttagcache[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
790 continue
791 continue
791 try:
792 try:
792 # The tuples are laid out so the right one can be found by comparison.
793 # The tuples are laid out so the right one can be found by comparison.
793 pdate, pdist, ptag = max(
794 pdate, pdist, ptag = max(
794 self._latesttagcache[p.rev()] for p in ctx.parents())
795 self._latesttagcache[p.rev()] for p in ctx.parents())
795 except KeyError:
796 except KeyError:
796 # Cache miss - recurse
797 # Cache miss - recurse
797 todo.append(rev)
798 todo.append(rev)
798 todo.extend(p.rev() for p in ctx.parents())
799 todo.extend(p.rev() for p in ctx.parents())
799 continue
800 continue
800 self._latesttagcache[rev] = pdate, pdist + 1, ptag
801 self._latesttagcache[rev] = pdate, pdist + 1, ptag
801 return self._latesttagcache[rev]
802 return self._latesttagcache[rev]
802
803
803 def _show(self, ctx, copies, props):
804 def _show(self, ctx, copies, props):
804 '''show a single changeset or file revision'''
805 '''show a single changeset or file revision'''
805
806
806 def showlist(name, values, plural=None, **args):
807 def showlist(name, values, plural=None, **args):
807 '''expand set of values.
808 '''expand set of values.
808 name is name of key in template map.
809 name is name of key in template map.
809 values is list of strings or dicts.
810 values is list of strings or dicts.
810 plural is plural of name, if not simply name + 's'.
811 plural is plural of name, if not simply name + 's'.
811
812
812 expansion works like this, given name 'foo'.
813 expansion works like this, given name 'foo'.
813
814
814 if values is empty, expand 'no_foos'.
815 if values is empty, expand 'no_foos'.
815
816
816 if 'foo' not in template map, return values as a string,
817 if 'foo' not in template map, return values as a string,
817 joined by space.
818 joined by space.
818
819
819 expand 'start_foos'.
820 expand 'start_foos'.
820
821
821 for each value, expand 'foo'. if 'last_foo' in template
822 for each value, expand 'foo'. if 'last_foo' in template
822 map, expand it instead of 'foo' for last key.
823 map, expand it instead of 'foo' for last key.
823
824
824 expand 'end_foos'.
825 expand 'end_foos'.
825 '''
826 '''
826 if plural: names = plural
827 if plural: names = plural
827 else: names = name + 's'
828 else: names = name + 's'
828 if not values:
829 if not values:
829 noname = 'no_' + names
830 noname = 'no_' + names
830 if noname in self.t:
831 if noname in self.t:
831 yield self.t(noname, **args)
832 yield self.t(noname, **args)
832 return
833 return
833 if name not in self.t:
834 if name not in self.t:
834 if isinstance(values[0], str):
835 if isinstance(values[0], str):
835 yield ' '.join(values)
836 yield ' '.join(values)
836 else:
837 else:
837 for v in values:
838 for v in values:
838 yield dict(v, **args)
839 yield dict(v, **args)
839 return
840 return
840 startname = 'start_' + names
841 startname = 'start_' + names
841 if startname in self.t:
842 if startname in self.t:
842 yield self.t(startname, **args)
843 yield self.t(startname, **args)
843 vargs = args.copy()
844 vargs = args.copy()
844 def one(v, tag=name):
845 def one(v, tag=name):
845 try:
846 try:
846 vargs.update(v)
847 vargs.update(v)
847 except (AttributeError, ValueError):
848 except (AttributeError, ValueError):
848 try:
849 try:
849 for a, b in v:
850 for a, b in v:
850 vargs[a] = b
851 vargs[a] = b
851 except ValueError:
852 except ValueError:
852 vargs[name] = v
853 vargs[name] = v
853 return self.t(tag, **vargs)
854 return self.t(tag, **vargs)
854 lastname = 'last_' + name
855 lastname = 'last_' + name
855 if lastname in self.t:
856 if lastname in self.t:
856 last = values.pop()
857 last = values.pop()
857 else:
858 else:
858 last = None
859 last = None
859 for v in values:
860 for v in values:
860 yield one(v)
861 yield one(v)
861 if last is not None:
862 if last is not None:
862 yield one(last, tag=lastname)
863 yield one(last, tag=lastname)
863 endname = 'end_' + names
864 endname = 'end_' + names
864 if endname in self.t:
865 if endname in self.t:
865 yield self.t(endname, **args)
866 yield self.t(endname, **args)
866
867
867 def showbranches(**args):
868 def showbranches(**args):
868 branch = ctx.branch()
869 branch = ctx.branch()
869 if branch != 'default':
870 if branch != 'default':
870 branch = encoding.tolocal(branch)
871 branch = encoding.tolocal(branch)
871 return showlist('branch', [branch], plural='branches', **args)
872 return showlist('branch', [branch], plural='branches', **args)
872
873
873 def showparents(**args):
874 def showparents(**args):
874 parents = [[('rev', p.rev()), ('node', p.hex())]
875 parents = [[('rev', p.rev()), ('node', p.hex())]
875 for p in self._meaningful_parentrevs(ctx)]
876 for p in self._meaningful_parentrevs(ctx)]
876 return showlist('parent', parents, **args)
877 return showlist('parent', parents, **args)
877
878
878 def showtags(**args):
879 def showtags(**args):
879 return showlist('tag', ctx.tags(), **args)
880 return showlist('tag', ctx.tags(), **args)
880
881
881 def showextras(**args):
882 def showextras(**args):
882 for key, value in sorted(ctx.extra().items()):
883 for key, value in sorted(ctx.extra().items()):
883 args = args.copy()
884 args = args.copy()
884 args.update(dict(key=key, value=value))
885 args.update(dict(key=key, value=value))
885 yield self.t('extra', **args)
886 yield self.t('extra', **args)
886
887
887 def showcopies(**args):
888 def showcopies(**args):
888 c = [{'name': x[0], 'source': x[1]} for x in copies]
889 c = [{'name': x[0], 'source': x[1]} for x in copies]
889 return showlist('file_copy', c, plural='file_copies', **args)
890 return showlist('file_copy', c, plural='file_copies', **args)
890
891
891 files = []
892 files = []
892 def getfiles():
893 def getfiles():
893 if not files:
894 if not files:
894 files[:] = self.repo.status(ctx.parents()[0].node(),
895 files[:] = self.repo.status(ctx.parents()[0].node(),
895 ctx.node())[:3]
896 ctx.node())[:3]
896 return files
897 return files
897 def showfiles(**args):
898 def showfiles(**args):
898 return showlist('file', ctx.files(), **args)
899 return showlist('file', ctx.files(), **args)
899 def showmods(**args):
900 def showmods(**args):
900 return showlist('file_mod', getfiles()[0], **args)
901 return showlist('file_mod', getfiles()[0], **args)
901 def showadds(**args):
902 def showadds(**args):
902 return showlist('file_add', getfiles()[1], **args)
903 return showlist('file_add', getfiles()[1], **args)
903 def showdels(**args):
904 def showdels(**args):
904 return showlist('file_del', getfiles()[2], **args)
905 return showlist('file_del', getfiles()[2], **args)
905 def showmanifest(**args):
906 def showmanifest(**args):
906 args = args.copy()
907 args = args.copy()
907 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
908 args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]),
908 node=hex(ctx.changeset()[0])))
909 node=hex(ctx.changeset()[0])))
909 return self.t('manifest', **args)
910 return self.t('manifest', **args)
910
911
911 def showdiffstat(**args):
912 def showdiffstat(**args):
912 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
913 diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node())
913 files, adds, removes = 0, 0, 0
914 files, adds, removes = 0, 0, 0
914 for i in patch.diffstatdata(util.iterlines(diff)):
915 for i in patch.diffstatdata(util.iterlines(diff)):
915 files += 1
916 files += 1
916 adds += i[1]
917 adds += i[1]
917 removes += i[2]
918 removes += i[2]
918 return '%s: +%s/-%s' % (files, adds, removes)
919 return '%s: +%s/-%s' % (files, adds, removes)
919
920
920 def showlatesttag(**args):
921 def showlatesttag(**args):
921 return self._latesttaginfo(ctx.rev())[2]
922 return self._latesttaginfo(ctx.rev())[2]
922 def showlatesttagdistance(**args):
923 def showlatesttagdistance(**args):
923 return self._latesttaginfo(ctx.rev())[1]
924 return self._latesttaginfo(ctx.rev())[1]
924
925
925 defprops = {
926 defprops = {
926 'author': ctx.user(),
927 'author': ctx.user(),
927 'branches': showbranches,
928 'branches': showbranches,
928 'date': ctx.date(),
929 'date': ctx.date(),
929 'desc': ctx.description().strip(),
930 'desc': ctx.description().strip(),
930 'file_adds': showadds,
931 'file_adds': showadds,
931 'file_dels': showdels,
932 'file_dels': showdels,
932 'file_mods': showmods,
933 'file_mods': showmods,
933 'files': showfiles,
934 'files': showfiles,
934 'file_copies': showcopies,
935 'file_copies': showcopies,
935 'manifest': showmanifest,
936 'manifest': showmanifest,
936 'node': ctx.hex(),
937 'node': ctx.hex(),
937 'parents': showparents,
938 'parents': showparents,
938 'rev': ctx.rev(),
939 'rev': ctx.rev(),
939 'tags': showtags,
940 'tags': showtags,
940 'extras': showextras,
941 'extras': showextras,
941 'diffstat': showdiffstat,
942 'diffstat': showdiffstat,
942 'latesttag': showlatesttag,
943 'latesttag': showlatesttag,
943 'latesttagdistance': showlatesttagdistance,
944 'latesttagdistance': showlatesttagdistance,
944 }
945 }
945 props = props.copy()
946 props = props.copy()
946 props.update(defprops)
947 props.update(defprops)
947
948
948 # find correct templates for current mode
949 # find correct templates for current mode
949
950
950 tmplmodes = [
951 tmplmodes = [
951 (True, None),
952 (True, None),
952 (self.ui.verbose, 'verbose'),
953 (self.ui.verbose, 'verbose'),
953 (self.ui.quiet, 'quiet'),
954 (self.ui.quiet, 'quiet'),
954 (self.ui.debugflag, 'debug'),
955 (self.ui.debugflag, 'debug'),
955 ]
956 ]
956
957
957 types = {'header': '', 'changeset': 'changeset'}
958 types = {'header': '', 'changeset': 'changeset'}
958 for mode, postfix in tmplmodes:
959 for mode, postfix in tmplmodes:
959 for type in types:
960 for type in types:
960 cur = postfix and ('%s_%s' % (type, postfix)) or type
961 cur = postfix and ('%s_%s' % (type, postfix)) or type
961 if mode and cur in self.t:
962 if mode and cur in self.t:
962 types[type] = cur
963 types[type] = cur
963
964
964 try:
965 try:
965
966
966 # write header
967 # write header
967 if types['header']:
968 if types['header']:
968 h = templater.stringify(self.t(types['header'], **props))
969 h = templater.stringify(self.t(types['header'], **props))
969 if self.buffered:
970 if self.buffered:
970 self.header[ctx.rev()] = h
971 self.header[ctx.rev()] = h
971 else:
972 else:
972 self.ui.write(h)
973 self.ui.write(h)
973
974
974 # write changeset metadata, then patch if requested
975 # write changeset metadata, then patch if requested
975 key = types['changeset']
976 key = types['changeset']
976 self.ui.write(templater.stringify(self.t(key, **props)))
977 self.ui.write(templater.stringify(self.t(key, **props)))
977 self.showpatch(ctx.node())
978 self.showpatch(ctx.node())
978
979
979 except KeyError, inst:
980 except KeyError, inst:
980 msg = _("%s: no key named '%s'")
981 msg = _("%s: no key named '%s'")
981 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
982 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
982 except SyntaxError, inst:
983 except SyntaxError, inst:
983 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
984 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
984
985
985 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
986 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
986 """show one changeset using template or regular display.
987 """show one changeset using template or regular display.
987
988
988 Display format will be the first non-empty hit of:
989 Display format will be the first non-empty hit of:
989 1. option 'template'
990 1. option 'template'
990 2. option 'style'
991 2. option 'style'
991 3. [ui] setting 'logtemplate'
992 3. [ui] setting 'logtemplate'
992 4. [ui] setting 'style'
993 4. [ui] setting 'style'
993 If all of these values are either the unset or the empty string,
994 If all of these values are either the unset or the empty string,
994 regular display via changeset_printer() is done.
995 regular display via changeset_printer() is done.
995 """
996 """
996 # options
997 # options
997 patch = False
998 patch = False
998 if opts.get('patch'):
999 if opts.get('patch'):
999 patch = matchfn or matchall(repo)
1000 patch = matchfn or matchall(repo)
1000
1001
1001 tmpl = opts.get('template')
1002 tmpl = opts.get('template')
1002 style = None
1003 style = None
1003 if tmpl:
1004 if tmpl:
1004 tmpl = templater.parsestring(tmpl, quoted=False)
1005 tmpl = templater.parsestring(tmpl, quoted=False)
1005 else:
1006 else:
1006 style = opts.get('style')
1007 style = opts.get('style')
1007
1008
1008 # ui settings
1009 # ui settings
1009 if not (tmpl or style):
1010 if not (tmpl or style):
1010 tmpl = ui.config('ui', 'logtemplate')
1011 tmpl = ui.config('ui', 'logtemplate')
1011 if tmpl:
1012 if tmpl:
1012 tmpl = templater.parsestring(tmpl)
1013 tmpl = templater.parsestring(tmpl)
1013 else:
1014 else:
1014 style = ui.config('ui', 'style')
1015 style = ui.config('ui', 'style')
1015
1016
1016 if not (tmpl or style):
1017 if not (tmpl or style):
1017 return changeset_printer(ui, repo, patch, opts, buffered)
1018 return changeset_printer(ui, repo, patch, opts, buffered)
1018
1019
1019 mapfile = None
1020 mapfile = None
1020 if style and not tmpl:
1021 if style and not tmpl:
1021 mapfile = style
1022 mapfile = style
1022 if not os.path.split(mapfile)[0]:
1023 if not os.path.split(mapfile)[0]:
1023 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1024 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1024 or templater.templatepath(mapfile))
1025 or templater.templatepath(mapfile))
1025 if mapname: mapfile = mapname
1026 if mapname: mapfile = mapname
1026
1027
1027 try:
1028 try:
1028 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1029 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
1029 except SyntaxError, inst:
1030 except SyntaxError, inst:
1030 raise util.Abort(inst.args[0])
1031 raise util.Abort(inst.args[0])
1031 if tmpl: t.use_template(tmpl)
1032 if tmpl: t.use_template(tmpl)
1032 return t
1033 return t
1033
1034
1034 def finddate(ui, repo, date):
1035 def finddate(ui, repo, date):
1035 """Find the tipmost changeset that matches the given date spec"""
1036 """Find the tipmost changeset that matches the given date spec"""
1036
1037
1037 df = util.matchdate(date)
1038 df = util.matchdate(date)
1038 m = matchall(repo)
1039 m = matchall(repo)
1039 results = {}
1040 results = {}
1040
1041
1041 def prep(ctx, fns):
1042 def prep(ctx, fns):
1042 d = ctx.date()
1043 d = ctx.date()
1043 if df(d[0]):
1044 if df(d[0]):
1044 results[ctx.rev()] = d
1045 results[ctx.rev()] = d
1045
1046
1046 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1047 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1047 rev = ctx.rev()
1048 rev = ctx.rev()
1048 if rev in results:
1049 if rev in results:
1049 ui.status(_("Found revision %s from %s\n") %
1050 ui.status(_("Found revision %s from %s\n") %
1050 (rev, util.datestr(results[rev])))
1051 (rev, util.datestr(results[rev])))
1051 return str(rev)
1052 return str(rev)
1052
1053
1053 raise util.Abort(_("revision matching date not found"))
1054 raise util.Abort(_("revision matching date not found"))
1054
1055
1055 def walkchangerevs(repo, match, opts, prepare):
1056 def walkchangerevs(repo, match, opts, prepare):
1056 '''Iterate over files and the revs in which they changed.
1057 '''Iterate over files and the revs in which they changed.
1057
1058
1058 Callers most commonly need to iterate backwards over the history
1059 Callers most commonly need to iterate backwards over the history
1059 in which they are interested. Doing so has awful (quadratic-looking)
1060 in which they are interested. Doing so has awful (quadratic-looking)
1060 performance, so we use iterators in a "windowed" way.
1061 performance, so we use iterators in a "windowed" way.
1061
1062
1062 We walk a window of revisions in the desired order. Within the
1063 We walk a window of revisions in the desired order. Within the
1063 window, we first walk forwards to gather data, then in the desired
1064 window, we first walk forwards to gather data, then in the desired
1064 order (usually backwards) to display it.
1065 order (usually backwards) to display it.
1065
1066
1066 This function returns an iterator yielding contexts. Before
1067 This function returns an iterator yielding contexts. Before
1067 yielding each context, the iterator will first call the prepare
1068 yielding each context, the iterator will first call the prepare
1068 function on each context in the window in forward order.'''
1069 function on each context in the window in forward order.'''
1069
1070
1070 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1071 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1071 if start < end:
1072 if start < end:
1072 while start < end:
1073 while start < end:
1073 yield start, min(windowsize, end-start)
1074 yield start, min(windowsize, end-start)
1074 start += windowsize
1075 start += windowsize
1075 if windowsize < sizelimit:
1076 if windowsize < sizelimit:
1076 windowsize *= 2
1077 windowsize *= 2
1077 else:
1078 else:
1078 while start > end:
1079 while start > end:
1079 yield start, min(windowsize, start-end-1)
1080 yield start, min(windowsize, start-end-1)
1080 start -= windowsize
1081 start -= windowsize
1081 if windowsize < sizelimit:
1082 if windowsize < sizelimit:
1082 windowsize *= 2
1083 windowsize *= 2
1083
1084
1084 follow = opts.get('follow') or opts.get('follow_first')
1085 follow = opts.get('follow') or opts.get('follow_first')
1085
1086
1086 if not len(repo):
1087 if not len(repo):
1087 return []
1088 return []
1088
1089
1089 if follow:
1090 if follow:
1090 defrange = '%s:0' % repo['.'].rev()
1091 defrange = '%s:0' % repo['.'].rev()
1091 else:
1092 else:
1092 defrange = '-1:0'
1093 defrange = '-1:0'
1093 revs = revrange(repo, opts['rev'] or [defrange])
1094 revs = revrange(repo, opts['rev'] or [defrange])
1094 wanted = set()
1095 wanted = set()
1095 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1096 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1096 fncache = {}
1097 fncache = {}
1097 change = util.cachefunc(repo.changectx)
1098 change = util.cachefunc(repo.changectx)
1098
1099
1099 if not slowpath and not match.files():
1100 if not slowpath and not match.files():
1100 # No files, no patterns. Display all revs.
1101 # No files, no patterns. Display all revs.
1101 wanted = set(revs)
1102 wanted = set(revs)
1102 copies = []
1103 copies = []
1103
1104
1104 if not slowpath:
1105 if not slowpath:
1105 # Only files, no patterns. Check the history of each file.
1106 # Only files, no patterns. Check the history of each file.
1106 def filerevgen(filelog, node):
1107 def filerevgen(filelog, node):
1107 cl_count = len(repo)
1108 cl_count = len(repo)
1108 if node is None:
1109 if node is None:
1109 last = len(filelog) - 1
1110 last = len(filelog) - 1
1110 else:
1111 else:
1111 last = filelog.rev(node)
1112 last = filelog.rev(node)
1112 for i, window in increasing_windows(last, nullrev):
1113 for i, window in increasing_windows(last, nullrev):
1113 revs = []
1114 revs = []
1114 for j in xrange(i - window, i + 1):
1115 for j in xrange(i - window, i + 1):
1115 n = filelog.node(j)
1116 n = filelog.node(j)
1116 revs.append((filelog.linkrev(j),
1117 revs.append((filelog.linkrev(j),
1117 follow and filelog.renamed(n)))
1118 follow and filelog.renamed(n)))
1118 for rev in reversed(revs):
1119 for rev in reversed(revs):
1119 # only yield rev for which we have the changelog, it can
1120 # only yield rev for which we have the changelog, it can
1120 # happen while doing "hg log" during a pull or commit
1121 # happen while doing "hg log" during a pull or commit
1121 if rev[0] < cl_count:
1122 if rev[0] < cl_count:
1122 yield rev
1123 yield rev
1123 def iterfiles():
1124 def iterfiles():
1124 for filename in match.files():
1125 for filename in match.files():
1125 yield filename, None
1126 yield filename, None
1126 for filename_node in copies:
1127 for filename_node in copies:
1127 yield filename_node
1128 yield filename_node
1128 minrev, maxrev = min(revs), max(revs)
1129 minrev, maxrev = min(revs), max(revs)
1129 for file_, node in iterfiles():
1130 for file_, node in iterfiles():
1130 filelog = repo.file(file_)
1131 filelog = repo.file(file_)
1131 if not len(filelog):
1132 if not len(filelog):
1132 if node is None:
1133 if node is None:
1133 # A zero count may be a directory or deleted file, so
1134 # A zero count may be a directory or deleted file, so
1134 # try to find matching entries on the slow path.
1135 # try to find matching entries on the slow path.
1135 if follow:
1136 if follow:
1136 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1137 raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_)
1137 slowpath = True
1138 slowpath = True
1138 break
1139 break
1139 else:
1140 else:
1140 continue
1141 continue
1141 for rev, copied in filerevgen(filelog, node):
1142 for rev, copied in filerevgen(filelog, node):
1142 if rev <= maxrev:
1143 if rev <= maxrev:
1143 if rev < minrev:
1144 if rev < minrev:
1144 break
1145 break
1145 fncache.setdefault(rev, [])
1146 fncache.setdefault(rev, [])
1146 fncache[rev].append(file_)
1147 fncache[rev].append(file_)
1147 wanted.add(rev)
1148 wanted.add(rev)
1148 if follow and copied:
1149 if follow and copied:
1149 copies.append(copied)
1150 copies.append(copied)
1150 if slowpath:
1151 if slowpath:
1151 if follow:
1152 if follow:
1152 raise util.Abort(_('can only follow copies/renames for explicit '
1153 raise util.Abort(_('can only follow copies/renames for explicit '
1153 'filenames'))
1154 'filenames'))
1154
1155
1155 # The slow path checks files modified in every changeset.
1156 # The slow path checks files modified in every changeset.
1156 def changerevgen():
1157 def changerevgen():
1157 for i, window in increasing_windows(len(repo) - 1, nullrev):
1158 for i, window in increasing_windows(len(repo) - 1, nullrev):
1158 for j in xrange(i - window, i + 1):
1159 for j in xrange(i - window, i + 1):
1159 yield change(j)
1160 yield change(j)
1160
1161
1161 for ctx in changerevgen():
1162 for ctx in changerevgen():
1162 matches = filter(match, ctx.files())
1163 matches = filter(match, ctx.files())
1163 if matches:
1164 if matches:
1164 fncache[ctx.rev()] = matches
1165 fncache[ctx.rev()] = matches
1165 wanted.add(ctx.rev())
1166 wanted.add(ctx.rev())
1166
1167
1167 class followfilter(object):
1168 class followfilter(object):
1168 def __init__(self, onlyfirst=False):
1169 def __init__(self, onlyfirst=False):
1169 self.startrev = nullrev
1170 self.startrev = nullrev
1170 self.roots = []
1171 self.roots = []
1171 self.onlyfirst = onlyfirst
1172 self.onlyfirst = onlyfirst
1172
1173
1173 def match(self, rev):
1174 def match(self, rev):
1174 def realparents(rev):
1175 def realparents(rev):
1175 if self.onlyfirst:
1176 if self.onlyfirst:
1176 return repo.changelog.parentrevs(rev)[0:1]
1177 return repo.changelog.parentrevs(rev)[0:1]
1177 else:
1178 else:
1178 return filter(lambda x: x != nullrev,
1179 return filter(lambda x: x != nullrev,
1179 repo.changelog.parentrevs(rev))
1180 repo.changelog.parentrevs(rev))
1180
1181
1181 if self.startrev == nullrev:
1182 if self.startrev == nullrev:
1182 self.startrev = rev
1183 self.startrev = rev
1183 return True
1184 return True
1184
1185
1185 if rev > self.startrev:
1186 if rev > self.startrev:
1186 # forward: all descendants
1187 # forward: all descendants
1187 if not self.roots:
1188 if not self.roots:
1188 self.roots.append(self.startrev)
1189 self.roots.append(self.startrev)
1189 for parent in realparents(rev):
1190 for parent in realparents(rev):
1190 if parent in self.roots:
1191 if parent in self.roots:
1191 self.roots.append(rev)
1192 self.roots.append(rev)
1192 return True
1193 return True
1193 else:
1194 else:
1194 # backwards: all parents
1195 # backwards: all parents
1195 if not self.roots:
1196 if not self.roots:
1196 self.roots.extend(realparents(self.startrev))
1197 self.roots.extend(realparents(self.startrev))
1197 if rev in self.roots:
1198 if rev in self.roots:
1198 self.roots.remove(rev)
1199 self.roots.remove(rev)
1199 self.roots.extend(realparents(rev))
1200 self.roots.extend(realparents(rev))
1200 return True
1201 return True
1201
1202
1202 return False
1203 return False
1203
1204
1204 # it might be worthwhile to do this in the iterator if the rev range
1205 # it might be worthwhile to do this in the iterator if the rev range
1205 # is descending and the prune args are all within that range
1206 # is descending and the prune args are all within that range
1206 for rev in opts.get('prune', ()):
1207 for rev in opts.get('prune', ()):
1207 rev = repo.changelog.rev(repo.lookup(rev))
1208 rev = repo.changelog.rev(repo.lookup(rev))
1208 ff = followfilter()
1209 ff = followfilter()
1209 stop = min(revs[0], revs[-1])
1210 stop = min(revs[0], revs[-1])
1210 for x in xrange(rev, stop-1, -1):
1211 for x in xrange(rev, stop-1, -1):
1211 if ff.match(x):
1212 if ff.match(x):
1212 wanted.discard(x)
1213 wanted.discard(x)
1213
1214
1214 def iterate():
1215 def iterate():
1215 if follow and not match.files():
1216 if follow and not match.files():
1216 ff = followfilter(onlyfirst=opts.get('follow_first'))
1217 ff = followfilter(onlyfirst=opts.get('follow_first'))
1217 def want(rev):
1218 def want(rev):
1218 return ff.match(rev) and rev in wanted
1219 return ff.match(rev) and rev in wanted
1219 else:
1220 else:
1220 def want(rev):
1221 def want(rev):
1221 return rev in wanted
1222 return rev in wanted
1222
1223
1223 for i, window in increasing_windows(0, len(revs)):
1224 for i, window in increasing_windows(0, len(revs)):
1224 change = util.cachefunc(repo.changectx)
1225 change = util.cachefunc(repo.changectx)
1225 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1226 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1226 for rev in sorted(nrevs):
1227 for rev in sorted(nrevs):
1227 fns = fncache.get(rev)
1228 fns = fncache.get(rev)
1228 ctx = change(rev)
1229 ctx = change(rev)
1229 if not fns:
1230 if not fns:
1230 def fns_generator():
1231 def fns_generator():
1231 for f in ctx.files():
1232 for f in ctx.files():
1232 if match(f):
1233 if match(f):
1233 yield f
1234 yield f
1234 fns = fns_generator()
1235 fns = fns_generator()
1235 prepare(ctx, fns)
1236 prepare(ctx, fns)
1236 for rev in nrevs:
1237 for rev in nrevs:
1237 yield change(rev)
1238 yield change(rev)
1238 return iterate()
1239 return iterate()
1239
1240
1240 def commit(ui, repo, commitfunc, pats, opts):
1241 def commit(ui, repo, commitfunc, pats, opts):
1241 '''commit the specified files or all outstanding changes'''
1242 '''commit the specified files or all outstanding changes'''
1242 date = opts.get('date')
1243 date = opts.get('date')
1243 if date:
1244 if date:
1244 opts['date'] = util.parsedate(date)
1245 opts['date'] = util.parsedate(date)
1245 message = logmessage(opts)
1246 message = logmessage(opts)
1246
1247
1247 # extract addremove carefully -- this function can be called from a command
1248 # extract addremove carefully -- this function can be called from a command
1248 # that doesn't support addremove
1249 # that doesn't support addremove
1249 if opts.get('addremove'):
1250 if opts.get('addremove'):
1250 addremove(repo, pats, opts)
1251 addremove(repo, pats, opts)
1251
1252
1252 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1253 return commitfunc(ui, repo, message, match(repo, pats, opts), opts)
1253
1254
1254 def commiteditor(repo, ctx, subs):
1255 def commiteditor(repo, ctx, subs):
1255 if ctx.description():
1256 if ctx.description():
1256 return ctx.description()
1257 return ctx.description()
1257 return commitforceeditor(repo, ctx, subs)
1258 return commitforceeditor(repo, ctx, subs)
1258
1259
1259 def commitforceeditor(repo, ctx, subs):
1260 def commitforceeditor(repo, ctx, subs):
1260 edittext = []
1261 edittext = []
1261 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1262 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1262 if ctx.description():
1263 if ctx.description():
1263 edittext.append(ctx.description())
1264 edittext.append(ctx.description())
1264 edittext.append("")
1265 edittext.append("")
1265 edittext.append("") # Empty line between message and comments.
1266 edittext.append("") # Empty line between message and comments.
1266 edittext.append(_("HG: Enter commit message."
1267 edittext.append(_("HG: Enter commit message."
1267 " Lines beginning with 'HG:' are removed."))
1268 " Lines beginning with 'HG:' are removed."))
1268 edittext.append(_("HG: Leave message empty to abort commit."))
1269 edittext.append(_("HG: Leave message empty to abort commit."))
1269 edittext.append("HG: --")
1270 edittext.append("HG: --")
1270 edittext.append(_("HG: user: %s") % ctx.user())
1271 edittext.append(_("HG: user: %s") % ctx.user())
1271 if ctx.p2():
1272 if ctx.p2():
1272 edittext.append(_("HG: branch merge"))
1273 edittext.append(_("HG: branch merge"))
1273 if ctx.branch():
1274 if ctx.branch():
1274 edittext.append(_("HG: branch '%s'")
1275 edittext.append(_("HG: branch '%s'")
1275 % encoding.tolocal(ctx.branch()))
1276 % encoding.tolocal(ctx.branch()))
1276 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1277 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1277 edittext.extend([_("HG: added %s") % f for f in added])
1278 edittext.extend([_("HG: added %s") % f for f in added])
1278 edittext.extend([_("HG: changed %s") % f for f in modified])
1279 edittext.extend([_("HG: changed %s") % f for f in modified])
1279 edittext.extend([_("HG: removed %s") % f for f in removed])
1280 edittext.extend([_("HG: removed %s") % f for f in removed])
1280 if not added and not modified and not removed:
1281 if not added and not modified and not removed:
1281 edittext.append(_("HG: no files changed"))
1282 edittext.append(_("HG: no files changed"))
1282 edittext.append("")
1283 edittext.append("")
1283 # run editor in the repository root
1284 # run editor in the repository root
1284 olddir = os.getcwd()
1285 olddir = os.getcwd()
1285 os.chdir(repo.root)
1286 os.chdir(repo.root)
1286 text = repo.ui.edit("\n".join(edittext), ctx.user())
1287 text = repo.ui.edit("\n".join(edittext), ctx.user())
1287 text = re.sub("(?m)^HG:.*\n", "", text)
1288 text = re.sub("(?m)^HG:.*\n", "", text)
1288 os.chdir(olddir)
1289 os.chdir(olddir)
1289
1290
1290 if not text.strip():
1291 if not text.strip():
1291 raise util.Abort(_("empty commit message"))
1292 raise util.Abort(_("empty commit message"))
1292
1293
1293 return text
1294 return text
@@ -1,175 +1,175
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5 echo a > a
5 echo a > a
6 hg add a
6 hg add a
7 echo line 1 > b
7 echo line 1 > b
8 echo line 2 >> b
8 echo line 2 >> b
9 hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
9 hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
10 hg add b
10 hg add b
11 echo other 1 > c
11 echo other 1 > c
12 echo other 2 >> c
12 echo other 2 >> c
13 echo >> c
13 echo >> c
14 echo other 3 >> c
14 echo other 3 >> c
15 hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15 hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
16 hg add c
16 hg add c
17 hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 echo c >> c
18 echo c >> c
19 hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20 echo foo > .hg/branch
20 echo foo > .hg/branch
21 hg commit -m 'new branch' -d '1400000 0' -u 'person'
21 hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 hg co -q 3
22 hg co -q 3
23 echo other 4 >> d
23 echo other 4 >> d
24 hg add d
24 hg add d
25 hg commit -m 'new head' -d '1500000 0' -u 'person'
25 hg commit -m 'new head' -d '1500000 0' -u 'person'
26 hg merge -q foo
26 hg merge -q foo
27 hg commit -m 'merge' -d '1500001 0' -u 'person'
27 hg commit -m 'merge' -d '1500001 0' -u 'person'
28 # second branch starting at nullrev
28 # second branch starting at nullrev
29 hg update null
29 hg update null
30 echo second > second
30 echo second > second
31 hg add second
31 hg add second
32 hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
32 hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
33 echo third > third
33 echo third > third
34 hg add third
34 hg add third
35 hg commit -m third -d "2020-01-01 10:01"
35 hg commit -m third -d "2020-01-01 10:01"
36
36
37 # make sure user/global hgrc does not affect tests
37 # make sure user/global hgrc does not affect tests
38 echo '[ui]' > .hg/hgrc
38 echo '[ui]' > .hg/hgrc
39 echo 'logtemplate =' >> .hg/hgrc
39 echo 'logtemplate =' >> .hg/hgrc
40 echo 'style =' >> .hg/hgrc
40 echo 'style =' >> .hg/hgrc
41
41
42 echo '# default style is like normal output'
42 echo '# default style is like normal output'
43 echo '# normal'
43 echo '# normal'
44 hg log > log.out
44 hg log > log.out
45 hg log --style default > style.out
45 hg log --style default > style.out
46 cmp log.out style.out || diff -u log.out style.out
46 cmp log.out style.out || diff -u log.out style.out
47 echo '# verbose'
47 echo '# verbose'
48 hg log -v > log.out
48 hg log -v > log.out
49 hg log -v --style default > style.out
49 hg log -v --style default > style.out
50 cmp log.out style.out || diff -u log.out style.out
50 cmp log.out style.out || diff -u log.out style.out
51 echo '# debug'
51 echo '# debug'
52 hg log --debug > log.out
52 hg log --debug > log.out
53 hg log --debug --style default > style.out
53 hg log --debug --style default > style.out
54 cmp log.out style.out || diff -u log.out style.out
54 cmp log.out style.out || diff -u log.out style.out
55
55
56 echo '# revision with no copies (used to print a traceback)'
56 echo '# revision with no copies (used to print a traceback)'
57 hg tip -v --template '\n'
57 hg tip -v --template '\n'
58
58
59 echo '# compact style works'
59 echo '# compact style works'
60 hg log --style compact
60 hg log --style compact
61 hg log -v --style compact
61 hg log -v --style compact
62 hg log --debug --style compact
62 hg log --debug --style compact
63
63
64 echo '# error if style not readable'
64 echo '# error if style not readable'
65 touch q
65 touch q
66 chmod 0 q
66 chmod 0 q
67 hg log --style ./q
67 hg log --style ./q
68
68
69 echo '# error if no style'
69 echo '# error if no style'
70 hg log --style notexist
70 hg log --style notexist
71
71
72 echo '# error if style missing key'
72 echo '# error if style missing key'
73 echo 'q = q' > t
73 echo 'q = q' > t
74 hg log --style ./t
74 hg log --style ./t
75
75
76 echo '# error if include fails'
76 echo '# error if include fails'
77 echo 'changeset = q' >> t
77 echo 'changeset = q' >> t
78 hg log --style ./t
78 hg log --style ./t
79
79
80 echo '# include works'
80 echo '# include works'
81 rm q
81 rm q
82 echo '{rev}' > q
82 echo '{rev}' > q
83 hg log --style ./t
83 hg log --style ./t
84
84
85 echo '# ui.style works'
85 echo '# ui.style works'
86 echo '[ui]' > .hg/hgrc
86 echo '[ui]' > .hg/hgrc
87 echo 'style = t' >> .hg/hgrc
87 echo 'style = t' >> .hg/hgrc
88 hg log
88 hg log
89
89
90 echo '# issue338'
90 echo '# issue338'
91 hg log --style=changelog > changelog
91 hg log --style=changelog > changelog
92 cat changelog
92 cat changelog
93
93
94 echo "# keys work"
94 echo "# keys work"
95 for key in author branches date desc file_adds file_dels file_mods \
95 for key in author branches date desc file_adds file_dels file_mods \
96 files manifest node parents rev tags diffstat; do
96 files manifest node parents rev tags diffstat extras; do
97 for mode in '' --verbose --debug; do
97 for mode in '' --verbose --debug; do
98 hg log $mode --template "$key$mode: {$key}\n"
98 hg log $mode --template "$key$mode: {$key}\n"
99 done
99 done
100 done
100 done
101
101
102 echo '# filters work'
102 echo '# filters work'
103 hg log --template '{author|domain}\n'
103 hg log --template '{author|domain}\n'
104 hg log --template '{author|person}\n'
104 hg log --template '{author|person}\n'
105 hg log --template '{author|user}\n'
105 hg log --template '{author|user}\n'
106 hg log --template '{date|age}\n' > /dev/null || exit 1
106 hg log --template '{date|age}\n' > /dev/null || exit 1
107 hg log -l1 --template '{date|age}\n'
107 hg log -l1 --template '{date|age}\n'
108 hg log --template '{date|date}\n'
108 hg log --template '{date|date}\n'
109 hg log --template '{date|isodate}\n'
109 hg log --template '{date|isodate}\n'
110 hg log --template '{date|isodatesec}\n'
110 hg log --template '{date|isodatesec}\n'
111 hg log --template '{date|rfc822date}\n'
111 hg log --template '{date|rfc822date}\n'
112 hg log --template '{desc|firstline}\n'
112 hg log --template '{desc|firstline}\n'
113 hg log --template '{node|short}\n'
113 hg log --template '{node|short}\n'
114 hg log --template '<changeset author="{author|xmlescape}"/>\n'
114 hg log --template '<changeset author="{author|xmlescape}"/>\n'
115
115
116 echo '# formatnode filter works'
116 echo '# formatnode filter works'
117 echo '# quiet'
117 echo '# quiet'
118 hg -q log -r 0 --template '{node|formatnode}\n'
118 hg -q log -r 0 --template '{node|formatnode}\n'
119 echo '# normal'
119 echo '# normal'
120 hg log -r 0 --template '{node|formatnode}\n'
120 hg log -r 0 --template '{node|formatnode}\n'
121 echo '# verbose'
121 echo '# verbose'
122 hg -v log -r 0 --template '{node|formatnode}\n'
122 hg -v log -r 0 --template '{node|formatnode}\n'
123 echo '# debug'
123 echo '# debug'
124 hg --debug log -r 0 --template '{node|formatnode}\n'
124 hg --debug log -r 0 --template '{node|formatnode}\n'
125
125
126 echo '# error on syntax'
126 echo '# error on syntax'
127 echo 'x = "f' >> t
127 echo 'x = "f' >> t
128 hg log
128 hg log
129
129
130 cd ..
130 cd ..
131
131
132 echo '# latesttag'
132 echo '# latesttag'
133 hg init latesttag
133 hg init latesttag
134 cd latesttag
134 cd latesttag
135
135
136 echo a > file
136 echo a > file
137 hg ci -Am a -d '0 0'
137 hg ci -Am a -d '0 0'
138
138
139 echo b >> file
139 echo b >> file
140 hg ci -m b -d '1 0'
140 hg ci -m b -d '1 0'
141
141
142 echo c >> head1
142 echo c >> head1
143 hg ci -Am h1c -d '2 0'
143 hg ci -Am h1c -d '2 0'
144
144
145 hg update -q 1
145 hg update -q 1
146 echo d >> head2
146 echo d >> head2
147 hg ci -Am h2d -d '3 0'
147 hg ci -Am h2d -d '3 0'
148
148
149 echo e >> head2
149 echo e >> head2
150 hg ci -m h2e -d '4 0'
150 hg ci -m h2e -d '4 0'
151
151
152 hg merge -q
152 hg merge -q
153 hg ci -m merge -d '5 0'
153 hg ci -m merge -d '5 0'
154
154
155 echo '# No tag set'
155 echo '# No tag set'
156 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
156 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
157
157
158 echo '# one common tag: longuest path wins'
158 echo '# one common tag: longuest path wins'
159 hg tag -r 1 -m t1 -d '6 0' t1
159 hg tag -r 1 -m t1 -d '6 0' t1
160 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
160 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
161
161
162 echo '# one ancestor tag: more recent wins'
162 echo '# one ancestor tag: more recent wins'
163 hg tag -r 2 -m t2 -d '7 0' t2
163 hg tag -r 2 -m t2 -d '7 0' t2
164 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
164 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
165
165
166 echo '# two branch tags: more recent wins'
166 echo '# two branch tags: more recent wins'
167 hg tag -r 3 -m t3 -d '8 0' t3
167 hg tag -r 3 -m t3 -d '8 0' t3
168 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
168 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
169
169
170 echo '# merged tag overrides'
170 echo '# merged tag overrides'
171 hg tag -r 5 -m t5 -d '9 0' t5
171 hg tag -r 5 -m t5 -d '9 0' t5
172 hg tag -r 3 -m at3 -d '10 0' at3
172 hg tag -r 3 -m at3 -d '10 0' at3
173 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
173 hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
174
174
175 echo '# done'
175 echo '# done'
@@ -1,726 +1,753
1 created new head
1 created new head
2 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
2 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
3 created new head
3 created new head
4 # default style is like normal output
4 # default style is like normal output
5 # normal
5 # normal
6 # verbose
6 # verbose
7 # debug
7 # debug
8 # revision with no copies (used to print a traceback)
8 # revision with no copies (used to print a traceback)
9
9
10 # compact style works
10 # compact style works
11 8[tip] 946e2bd9c565 2020-01-01 10:01 +0000 test
11 8[tip] 946e2bd9c565 2020-01-01 10:01 +0000 test
12 third
12 third
13
13
14 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
14 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
15 second
15 second
16
16
17 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
17 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
18 merge
18 merge
19
19
20 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
20 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
21 new head
21 new head
22
22
23 4 32a18f097fcc 1970-01-17 04:53 +0000 person
23 4 32a18f097fcc 1970-01-17 04:53 +0000 person
24 new branch
24 new branch
25
25
26 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
26 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
27 no user, no domain
27 no user, no domain
28
28
29 2 97054abb4ab8 1970-01-14 21:20 +0000 other
29 2 97054abb4ab8 1970-01-14 21:20 +0000 other
30 no person
30 no person
31
31
32 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
32 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
33 other 1
33 other 1
34
34
35 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
35 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
36 line 1
36 line 1
37
37
38 8[tip] 946e2bd9c565 2020-01-01 10:01 +0000 test
38 8[tip] 946e2bd9c565 2020-01-01 10:01 +0000 test
39 third
39 third
40
40
41 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
41 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
42 second
42 second
43
43
44 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
44 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
45 merge
45 merge
46
46
47 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
47 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
48 new head
48 new head
49
49
50 4 32a18f097fcc 1970-01-17 04:53 +0000 person
50 4 32a18f097fcc 1970-01-17 04:53 +0000 person
51 new branch
51 new branch
52
52
53 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
53 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
54 no user, no domain
54 no user, no domain
55
55
56 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
56 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
57 no person
57 no person
58
58
59 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
59 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
60 other 1
60 other 1
61 other 2
61 other 2
62
62
63 other 3
63 other 3
64
64
65 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
65 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
66 line 1
66 line 1
67 line 2
67 line 2
68
68
69 8[tip]:7,-1 946e2bd9c565 2020-01-01 10:01 +0000 test
69 8[tip]:7,-1 946e2bd9c565 2020-01-01 10:01 +0000 test
70 third
70 third
71
71
72 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
72 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
73 second
73 second
74
74
75 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
75 6:5,4 c7b487c6c50e 1970-01-18 08:40 +0000 person
76 merge
76 merge
77
77
78 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
78 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
79 new head
79 new head
80
80
81 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
81 4:3,-1 32a18f097fcc 1970-01-17 04:53 +0000 person
82 new branch
82 new branch
83
83
84 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
84 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
85 no user, no domain
85 no user, no domain
86
86
87 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
87 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
88 no person
88 no person
89
89
90 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
90 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
91 other 1
91 other 1
92 other 2
92 other 2
93
93
94 other 3
94 other 3
95
95
96 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
96 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
97 line 1
97 line 1
98 line 2
98 line 2
99
99
100 # error if style not readable
100 # error if style not readable
101 abort: Permission denied: ./q
101 abort: Permission denied: ./q
102 # error if no style
102 # error if no style
103 abort: style not found: notexist
103 abort: style not found: notexist
104 # error if style missing key
104 # error if style missing key
105 abort: ./t: no key named 'changeset'
105 abort: ./t: no key named 'changeset'
106 # error if include fails
106 # error if include fails
107 abort: template file ./q: Permission denied
107 abort: template file ./q: Permission denied
108 # include works
108 # include works
109 8
109 8
110 7
110 7
111 6
111 6
112 5
112 5
113 4
113 4
114 3
114 3
115 2
115 2
116 1
116 1
117 0
117 0
118 # ui.style works
118 # ui.style works
119 8
119 8
120 7
120 7
121 6
121 6
122 5
122 5
123 4
123 4
124 3
124 3
125 2
125 2
126 1
126 1
127 0
127 0
128 # issue338
128 # issue338
129 2020-01-01 test <test>
129 2020-01-01 test <test>
130
130
131 * third:
131 * third:
132 third
132 third
133 [946e2bd9c565] [tip]
133 [946e2bd9c565] [tip]
134
134
135 1970-01-12 User Name <user@hostname>
135 1970-01-12 User Name <user@hostname>
136
136
137 * second:
137 * second:
138 second
138 second
139 [29114dbae42b]
139 [29114dbae42b]
140
140
141 1970-01-18 person <person>
141 1970-01-18 person <person>
142
142
143 * merge
143 * merge
144 [c7b487c6c50e]
144 [c7b487c6c50e]
145
145
146 * d:
146 * d:
147 new head
147 new head
148 [13207e5a10d9]
148 [13207e5a10d9]
149
149
150 1970-01-17 person <person>
150 1970-01-17 person <person>
151
151
152 * new branch
152 * new branch
153 [32a18f097fcc] <foo>
153 [32a18f097fcc] <foo>
154
154
155 1970-01-16 person <person>
155 1970-01-16 person <person>
156
156
157 * c:
157 * c:
158 no user, no domain
158 no user, no domain
159 [10e46f2dcbf4]
159 [10e46f2dcbf4]
160
160
161 1970-01-14 other <other@place>
161 1970-01-14 other <other@place>
162
162
163 * c:
163 * c:
164 no person
164 no person
165 [97054abb4ab8]
165 [97054abb4ab8]
166
166
167 1970-01-13 A. N. Other <other@place>
167 1970-01-13 A. N. Other <other@place>
168
168
169 * b:
169 * b:
170 other 1 other 2
170 other 1 other 2
171
171
172 other 3
172 other 3
173 [b608e9d1a3f0]
173 [b608e9d1a3f0]
174
174
175 1970-01-12 User Name <user@hostname>
175 1970-01-12 User Name <user@hostname>
176
176
177 * a:
177 * a:
178 line 1 line 2
178 line 1 line 2
179 [1e4e1b8f71e0]
179 [1e4e1b8f71e0]
180
180
181 # keys work
181 # keys work
182 author: test
182 author: test
183 author: User Name <user@hostname>
183 author: User Name <user@hostname>
184 author: person
184 author: person
185 author: person
185 author: person
186 author: person
186 author: person
187 author: person
187 author: person
188 author: other@place
188 author: other@place
189 author: A. N. Other <other@place>
189 author: A. N. Other <other@place>
190 author: User Name <user@hostname>
190 author: User Name <user@hostname>
191 author--verbose: test
191 author--verbose: test
192 author--verbose: User Name <user@hostname>
192 author--verbose: User Name <user@hostname>
193 author--verbose: person
193 author--verbose: person
194 author--verbose: person
194 author--verbose: person
195 author--verbose: person
195 author--verbose: person
196 author--verbose: person
196 author--verbose: person
197 author--verbose: other@place
197 author--verbose: other@place
198 author--verbose: A. N. Other <other@place>
198 author--verbose: A. N. Other <other@place>
199 author--verbose: User Name <user@hostname>
199 author--verbose: User Name <user@hostname>
200 author--debug: test
200 author--debug: test
201 author--debug: User Name <user@hostname>
201 author--debug: User Name <user@hostname>
202 author--debug: person
202 author--debug: person
203 author--debug: person
203 author--debug: person
204 author--debug: person
204 author--debug: person
205 author--debug: person
205 author--debug: person
206 author--debug: other@place
206 author--debug: other@place
207 author--debug: A. N. Other <other@place>
207 author--debug: A. N. Other <other@place>
208 author--debug: User Name <user@hostname>
208 author--debug: User Name <user@hostname>
209 branches:
209 branches:
210 branches:
210 branches:
211 branches:
211 branches:
212 branches:
212 branches:
213 branches: foo
213 branches: foo
214 branches:
214 branches:
215 branches:
215 branches:
216 branches:
216 branches:
217 branches:
217 branches:
218 branches--verbose:
218 branches--verbose:
219 branches--verbose:
219 branches--verbose:
220 branches--verbose:
220 branches--verbose:
221 branches--verbose:
221 branches--verbose:
222 branches--verbose: foo
222 branches--verbose: foo
223 branches--verbose:
223 branches--verbose:
224 branches--verbose:
224 branches--verbose:
225 branches--verbose:
225 branches--verbose:
226 branches--verbose:
226 branches--verbose:
227 branches--debug:
227 branches--debug:
228 branches--debug:
228 branches--debug:
229 branches--debug:
229 branches--debug:
230 branches--debug:
230 branches--debug:
231 branches--debug: foo
231 branches--debug: foo
232 branches--debug:
232 branches--debug:
233 branches--debug:
233 branches--debug:
234 branches--debug:
234 branches--debug:
235 branches--debug:
235 branches--debug:
236 date: 1577872860.00
236 date: 1577872860.00
237 date: 1000000.00
237 date: 1000000.00
238 date: 1500001.00
238 date: 1500001.00
239 date: 1500000.00
239 date: 1500000.00
240 date: 1400000.00
240 date: 1400000.00
241 date: 1300000.00
241 date: 1300000.00
242 date: 1200000.00
242 date: 1200000.00
243 date: 1100000.00
243 date: 1100000.00
244 date: 1000000.00
244 date: 1000000.00
245 date--verbose: 1577872860.00
245 date--verbose: 1577872860.00
246 date--verbose: 1000000.00
246 date--verbose: 1000000.00
247 date--verbose: 1500001.00
247 date--verbose: 1500001.00
248 date--verbose: 1500000.00
248 date--verbose: 1500000.00
249 date--verbose: 1400000.00
249 date--verbose: 1400000.00
250 date--verbose: 1300000.00
250 date--verbose: 1300000.00
251 date--verbose: 1200000.00
251 date--verbose: 1200000.00
252 date--verbose: 1100000.00
252 date--verbose: 1100000.00
253 date--verbose: 1000000.00
253 date--verbose: 1000000.00
254 date--debug: 1577872860.00
254 date--debug: 1577872860.00
255 date--debug: 1000000.00
255 date--debug: 1000000.00
256 date--debug: 1500001.00
256 date--debug: 1500001.00
257 date--debug: 1500000.00
257 date--debug: 1500000.00
258 date--debug: 1400000.00
258 date--debug: 1400000.00
259 date--debug: 1300000.00
259 date--debug: 1300000.00
260 date--debug: 1200000.00
260 date--debug: 1200000.00
261 date--debug: 1100000.00
261 date--debug: 1100000.00
262 date--debug: 1000000.00
262 date--debug: 1000000.00
263 desc: third
263 desc: third
264 desc: second
264 desc: second
265 desc: merge
265 desc: merge
266 desc: new head
266 desc: new head
267 desc: new branch
267 desc: new branch
268 desc: no user, no domain
268 desc: no user, no domain
269 desc: no person
269 desc: no person
270 desc: other 1
270 desc: other 1
271 other 2
271 other 2
272
272
273 other 3
273 other 3
274 desc: line 1
274 desc: line 1
275 line 2
275 line 2
276 desc--verbose: third
276 desc--verbose: third
277 desc--verbose: second
277 desc--verbose: second
278 desc--verbose: merge
278 desc--verbose: merge
279 desc--verbose: new head
279 desc--verbose: new head
280 desc--verbose: new branch
280 desc--verbose: new branch
281 desc--verbose: no user, no domain
281 desc--verbose: no user, no domain
282 desc--verbose: no person
282 desc--verbose: no person
283 desc--verbose: other 1
283 desc--verbose: other 1
284 other 2
284 other 2
285
285
286 other 3
286 other 3
287 desc--verbose: line 1
287 desc--verbose: line 1
288 line 2
288 line 2
289 desc--debug: third
289 desc--debug: third
290 desc--debug: second
290 desc--debug: second
291 desc--debug: merge
291 desc--debug: merge
292 desc--debug: new head
292 desc--debug: new head
293 desc--debug: new branch
293 desc--debug: new branch
294 desc--debug: no user, no domain
294 desc--debug: no user, no domain
295 desc--debug: no person
295 desc--debug: no person
296 desc--debug: other 1
296 desc--debug: other 1
297 other 2
297 other 2
298
298
299 other 3
299 other 3
300 desc--debug: line 1
300 desc--debug: line 1
301 line 2
301 line 2
302 file_adds: third
302 file_adds: third
303 file_adds: second
303 file_adds: second
304 file_adds:
304 file_adds:
305 file_adds: d
305 file_adds: d
306 file_adds:
306 file_adds:
307 file_adds:
307 file_adds:
308 file_adds: c
308 file_adds: c
309 file_adds: b
309 file_adds: b
310 file_adds: a
310 file_adds: a
311 file_adds--verbose: third
311 file_adds--verbose: third
312 file_adds--verbose: second
312 file_adds--verbose: second
313 file_adds--verbose:
313 file_adds--verbose:
314 file_adds--verbose: d
314 file_adds--verbose: d
315 file_adds--verbose:
315 file_adds--verbose:
316 file_adds--verbose:
316 file_adds--verbose:
317 file_adds--verbose: c
317 file_adds--verbose: c
318 file_adds--verbose: b
318 file_adds--verbose: b
319 file_adds--verbose: a
319 file_adds--verbose: a
320 file_adds--debug: third
320 file_adds--debug: third
321 file_adds--debug: second
321 file_adds--debug: second
322 file_adds--debug:
322 file_adds--debug:
323 file_adds--debug: d
323 file_adds--debug: d
324 file_adds--debug:
324 file_adds--debug:
325 file_adds--debug:
325 file_adds--debug:
326 file_adds--debug: c
326 file_adds--debug: c
327 file_adds--debug: b
327 file_adds--debug: b
328 file_adds--debug: a
328 file_adds--debug: a
329 file_dels:
329 file_dels:
330 file_dels:
330 file_dels:
331 file_dels:
331 file_dels:
332 file_dels:
332 file_dels:
333 file_dels:
333 file_dels:
334 file_dels:
334 file_dels:
335 file_dels:
335 file_dels:
336 file_dels:
336 file_dels:
337 file_dels:
337 file_dels:
338 file_dels--verbose:
338 file_dels--verbose:
339 file_dels--verbose:
339 file_dels--verbose:
340 file_dels--verbose:
340 file_dels--verbose:
341 file_dels--verbose:
341 file_dels--verbose:
342 file_dels--verbose:
342 file_dels--verbose:
343 file_dels--verbose:
343 file_dels--verbose:
344 file_dels--verbose:
344 file_dels--verbose:
345 file_dels--verbose:
345 file_dels--verbose:
346 file_dels--verbose:
346 file_dels--verbose:
347 file_dels--debug:
347 file_dels--debug:
348 file_dels--debug:
348 file_dels--debug:
349 file_dels--debug:
349 file_dels--debug:
350 file_dels--debug:
350 file_dels--debug:
351 file_dels--debug:
351 file_dels--debug:
352 file_dels--debug:
352 file_dels--debug:
353 file_dels--debug:
353 file_dels--debug:
354 file_dels--debug:
354 file_dels--debug:
355 file_dels--debug:
355 file_dels--debug:
356 file_mods:
356 file_mods:
357 file_mods:
357 file_mods:
358 file_mods:
358 file_mods:
359 file_mods:
359 file_mods:
360 file_mods:
360 file_mods:
361 file_mods: c
361 file_mods: c
362 file_mods:
362 file_mods:
363 file_mods:
363 file_mods:
364 file_mods:
364 file_mods:
365 file_mods--verbose:
365 file_mods--verbose:
366 file_mods--verbose:
366 file_mods--verbose:
367 file_mods--verbose:
367 file_mods--verbose:
368 file_mods--verbose:
368 file_mods--verbose:
369 file_mods--verbose:
369 file_mods--verbose:
370 file_mods--verbose: c
370 file_mods--verbose: c
371 file_mods--verbose:
371 file_mods--verbose:
372 file_mods--verbose:
372 file_mods--verbose:
373 file_mods--verbose:
373 file_mods--verbose:
374 file_mods--debug:
374 file_mods--debug:
375 file_mods--debug:
375 file_mods--debug:
376 file_mods--debug:
376 file_mods--debug:
377 file_mods--debug:
377 file_mods--debug:
378 file_mods--debug:
378 file_mods--debug:
379 file_mods--debug: c
379 file_mods--debug: c
380 file_mods--debug:
380 file_mods--debug:
381 file_mods--debug:
381 file_mods--debug:
382 file_mods--debug:
382 file_mods--debug:
383 files: third
383 files: third
384 files: second
384 files: second
385 files:
385 files:
386 files: d
386 files: d
387 files:
387 files:
388 files: c
388 files: c
389 files: c
389 files: c
390 files: b
390 files: b
391 files: a
391 files: a
392 files--verbose: third
392 files--verbose: third
393 files--verbose: second
393 files--verbose: second
394 files--verbose:
394 files--verbose:
395 files--verbose: d
395 files--verbose: d
396 files--verbose:
396 files--verbose:
397 files--verbose: c
397 files--verbose: c
398 files--verbose: c
398 files--verbose: c
399 files--verbose: b
399 files--verbose: b
400 files--verbose: a
400 files--verbose: a
401 files--debug: third
401 files--debug: third
402 files--debug: second
402 files--debug: second
403 files--debug:
403 files--debug:
404 files--debug: d
404 files--debug: d
405 files--debug:
405 files--debug:
406 files--debug: c
406 files--debug: c
407 files--debug: c
407 files--debug: c
408 files--debug: b
408 files--debug: b
409 files--debug: a
409 files--debug: a
410 manifest: 8:8a0d8faab8b2
410 manifest: 8:8a0d8faab8b2
411 manifest: 7:f2dbc354b94e
411 manifest: 7:f2dbc354b94e
412 manifest: 6:91015e9dbdd7
412 manifest: 6:91015e9dbdd7
413 manifest: 5:4dc3def4f9b4
413 manifest: 5:4dc3def4f9b4
414 manifest: 4:90ae8dda64e1
414 manifest: 4:90ae8dda64e1
415 manifest: 3:cb5a1327723b
415 manifest: 3:cb5a1327723b
416 manifest: 2:6e0e82995c35
416 manifest: 2:6e0e82995c35
417 manifest: 1:4e8d705b1e53
417 manifest: 1:4e8d705b1e53
418 manifest: 0:a0c8bcbbb45c
418 manifest: 0:a0c8bcbbb45c
419 manifest--verbose: 8:8a0d8faab8b2
419 manifest--verbose: 8:8a0d8faab8b2
420 manifest--verbose: 7:f2dbc354b94e
420 manifest--verbose: 7:f2dbc354b94e
421 manifest--verbose: 6:91015e9dbdd7
421 manifest--verbose: 6:91015e9dbdd7
422 manifest--verbose: 5:4dc3def4f9b4
422 manifest--verbose: 5:4dc3def4f9b4
423 manifest--verbose: 4:90ae8dda64e1
423 manifest--verbose: 4:90ae8dda64e1
424 manifest--verbose: 3:cb5a1327723b
424 manifest--verbose: 3:cb5a1327723b
425 manifest--verbose: 2:6e0e82995c35
425 manifest--verbose: 2:6e0e82995c35
426 manifest--verbose: 1:4e8d705b1e53
426 manifest--verbose: 1:4e8d705b1e53
427 manifest--verbose: 0:a0c8bcbbb45c
427 manifest--verbose: 0:a0c8bcbbb45c
428 manifest--debug: 8:8a0d8faab8b2eee97dcfccabbcb18f413c9d097b
428 manifest--debug: 8:8a0d8faab8b2eee97dcfccabbcb18f413c9d097b
429 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
429 manifest--debug: 7:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
430 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
430 manifest--debug: 6:91015e9dbdd76a6791085d12b0a0ec7fcd22ffbf
431 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
431 manifest--debug: 5:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
432 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
432 manifest--debug: 4:90ae8dda64e1a876c792bccb9af66284f6018363
433 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
433 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
434 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
434 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
435 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
435 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
436 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
436 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
437 node: 946e2bd9c565394777d74d9669045b39e856e3ea
437 node: 946e2bd9c565394777d74d9669045b39e856e3ea
438 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
438 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
439 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
439 node: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
440 node: 13207e5a10d9fd28ec424934298e176197f2c67f
440 node: 13207e5a10d9fd28ec424934298e176197f2c67f
441 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
441 node: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
442 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
442 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
443 node: 97054abb4ab824450e9164180baf491ae0078465
443 node: 97054abb4ab824450e9164180baf491ae0078465
444 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
444 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
445 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
445 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
446 node--verbose: 946e2bd9c565394777d74d9669045b39e856e3ea
446 node--verbose: 946e2bd9c565394777d74d9669045b39e856e3ea
447 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
447 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
448 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
448 node--verbose: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
449 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
449 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
450 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
450 node--verbose: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
451 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
451 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
452 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
452 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
453 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
453 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
454 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
454 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
455 node--debug: 946e2bd9c565394777d74d9669045b39e856e3ea
455 node--debug: 946e2bd9c565394777d74d9669045b39e856e3ea
456 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
456 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
457 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
457 node--debug: c7b487c6c50ef1cf464cafdc4f4f5e615fc5999f
458 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
458 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
459 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
459 node--debug: 32a18f097fcccf76ef282f62f8a85b3adf8d13c4
460 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
460 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
461 node--debug: 97054abb4ab824450e9164180baf491ae0078465
461 node--debug: 97054abb4ab824450e9164180baf491ae0078465
462 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
462 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
463 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
463 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
464 parents:
464 parents:
465 parents: -1:000000000000
465 parents: -1:000000000000
466 parents: 5:13207e5a10d9 4:32a18f097fcc
466 parents: 5:13207e5a10d9 4:32a18f097fcc
467 parents: 3:10e46f2dcbf4
467 parents: 3:10e46f2dcbf4
468 parents:
468 parents:
469 parents:
469 parents:
470 parents:
470 parents:
471 parents:
471 parents:
472 parents:
472 parents:
473 parents--verbose:
473 parents--verbose:
474 parents--verbose: -1:000000000000
474 parents--verbose: -1:000000000000
475 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
475 parents--verbose: 5:13207e5a10d9 4:32a18f097fcc
476 parents--verbose: 3:10e46f2dcbf4
476 parents--verbose: 3:10e46f2dcbf4
477 parents--verbose:
477 parents--verbose:
478 parents--verbose:
478 parents--verbose:
479 parents--verbose:
479 parents--verbose:
480 parents--verbose:
480 parents--verbose:
481 parents--verbose:
481 parents--verbose:
482 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
482 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
483 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
483 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
484 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
484 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:32a18f097fcccf76ef282f62f8a85b3adf8d13c4
485 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
485 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
486 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
486 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
487 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
487 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
488 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
488 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
489 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
489 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
490 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
490 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
491 rev: 8
491 rev: 8
492 rev: 7
492 rev: 7
493 rev: 6
493 rev: 6
494 rev: 5
494 rev: 5
495 rev: 4
495 rev: 4
496 rev: 3
496 rev: 3
497 rev: 2
497 rev: 2
498 rev: 1
498 rev: 1
499 rev: 0
499 rev: 0
500 rev--verbose: 8
500 rev--verbose: 8
501 rev--verbose: 7
501 rev--verbose: 7
502 rev--verbose: 6
502 rev--verbose: 6
503 rev--verbose: 5
503 rev--verbose: 5
504 rev--verbose: 4
504 rev--verbose: 4
505 rev--verbose: 3
505 rev--verbose: 3
506 rev--verbose: 2
506 rev--verbose: 2
507 rev--verbose: 1
507 rev--verbose: 1
508 rev--verbose: 0
508 rev--verbose: 0
509 rev--debug: 8
509 rev--debug: 8
510 rev--debug: 7
510 rev--debug: 7
511 rev--debug: 6
511 rev--debug: 6
512 rev--debug: 5
512 rev--debug: 5
513 rev--debug: 4
513 rev--debug: 4
514 rev--debug: 3
514 rev--debug: 3
515 rev--debug: 2
515 rev--debug: 2
516 rev--debug: 1
516 rev--debug: 1
517 rev--debug: 0
517 rev--debug: 0
518 tags: tip
518 tags: tip
519 tags:
519 tags:
520 tags:
520 tags:
521 tags:
521 tags:
522 tags:
522 tags:
523 tags:
523 tags:
524 tags:
524 tags:
525 tags:
525 tags:
526 tags:
526 tags:
527 tags--verbose: tip
527 tags--verbose: tip
528 tags--verbose:
528 tags--verbose:
529 tags--verbose:
529 tags--verbose:
530 tags--verbose:
530 tags--verbose:
531 tags--verbose:
531 tags--verbose:
532 tags--verbose:
532 tags--verbose:
533 tags--verbose:
533 tags--verbose:
534 tags--verbose:
534 tags--verbose:
535 tags--verbose:
535 tags--verbose:
536 tags--debug: tip
536 tags--debug: tip
537 tags--debug:
537 tags--debug:
538 tags--debug:
538 tags--debug:
539 tags--debug:
539 tags--debug:
540 tags--debug:
540 tags--debug:
541 tags--debug:
541 tags--debug:
542 tags--debug:
542 tags--debug:
543 tags--debug:
543 tags--debug:
544 tags--debug:
544 tags--debug:
545 diffstat: 1: +1/-0
545 diffstat: 1: +1/-0
546 diffstat: 1: +1/-0
546 diffstat: 1: +1/-0
547 diffstat: 0: +0/-0
547 diffstat: 0: +0/-0
548 diffstat: 1: +1/-0
548 diffstat: 1: +1/-0
549 diffstat: 0: +0/-0
549 diffstat: 0: +0/-0
550 diffstat: 1: +1/-0
550 diffstat: 1: +1/-0
551 diffstat: 1: +4/-0
551 diffstat: 1: +4/-0
552 diffstat: 1: +2/-0
552 diffstat: 1: +2/-0
553 diffstat: 1: +1/-0
553 diffstat: 1: +1/-0
554 diffstat--verbose: 1: +1/-0
554 diffstat--verbose: 1: +1/-0
555 diffstat--verbose: 1: +1/-0
555 diffstat--verbose: 1: +1/-0
556 diffstat--verbose: 0: +0/-0
556 diffstat--verbose: 0: +0/-0
557 diffstat--verbose: 1: +1/-0
557 diffstat--verbose: 1: +1/-0
558 diffstat--verbose: 0: +0/-0
558 diffstat--verbose: 0: +0/-0
559 diffstat--verbose: 1: +1/-0
559 diffstat--verbose: 1: +1/-0
560 diffstat--verbose: 1: +4/-0
560 diffstat--verbose: 1: +4/-0
561 diffstat--verbose: 1: +2/-0
561 diffstat--verbose: 1: +2/-0
562 diffstat--verbose: 1: +1/-0
562 diffstat--verbose: 1: +1/-0
563 diffstat--debug: 1: +1/-0
563 diffstat--debug: 1: +1/-0
564 diffstat--debug: 1: +1/-0
564 diffstat--debug: 1: +1/-0
565 diffstat--debug: 0: +0/-0
565 diffstat--debug: 0: +0/-0
566 diffstat--debug: 1: +1/-0
566 diffstat--debug: 1: +1/-0
567 diffstat--debug: 0: +0/-0
567 diffstat--debug: 0: +0/-0
568 diffstat--debug: 1: +1/-0
568 diffstat--debug: 1: +1/-0
569 diffstat--debug: 1: +4/-0
569 diffstat--debug: 1: +4/-0
570 diffstat--debug: 1: +2/-0
570 diffstat--debug: 1: +2/-0
571 diffstat--debug: 1: +1/-0
571 diffstat--debug: 1: +1/-0
572 extras: branch=default
573 extras: branch=default
574 extras: branch=default
575 extras: branch=default
576 extras: branch=foo
577 extras: branch=default
578 extras: branch=default
579 extras: branch=default
580 extras: branch=default
581 extras--verbose: branch=default
582 extras--verbose: branch=default
583 extras--verbose: branch=default
584 extras--verbose: branch=default
585 extras--verbose: branch=foo
586 extras--verbose: branch=default
587 extras--verbose: branch=default
588 extras--verbose: branch=default
589 extras--verbose: branch=default
590 extras--debug: branch=default
591 extras--debug: branch=default
592 extras--debug: branch=default
593 extras--debug: branch=default
594 extras--debug: branch=foo
595 extras--debug: branch=default
596 extras--debug: branch=default
597 extras--debug: branch=default
598 extras--debug: branch=default
572 # filters work
599 # filters work
573
600
574 hostname
601 hostname
575
602
576
603
577
604
578
605
579 place
606 place
580 place
607 place
581 hostname
608 hostname
582 test
609 test
583 User Name
610 User Name
584 person
611 person
585 person
612 person
586 person
613 person
587 person
614 person
588 other
615 other
589 A. N. Other
616 A. N. Other
590 User Name
617 User Name
591 test
618 test
592 user
619 user
593 person
620 person
594 person
621 person
595 person
622 person
596 person
623 person
597 other
624 other
598 other
625 other
599 user
626 user
600 in the future
627 in the future
601 Wed Jan 01 10:01:00 2020 +0000
628 Wed Jan 01 10:01:00 2020 +0000
602 Mon Jan 12 13:46:40 1970 +0000
629 Mon Jan 12 13:46:40 1970 +0000
603 Sun Jan 18 08:40:01 1970 +0000
630 Sun Jan 18 08:40:01 1970 +0000
604 Sun Jan 18 08:40:00 1970 +0000
631 Sun Jan 18 08:40:00 1970 +0000
605 Sat Jan 17 04:53:20 1970 +0000
632 Sat Jan 17 04:53:20 1970 +0000
606 Fri Jan 16 01:06:40 1970 +0000
633 Fri Jan 16 01:06:40 1970 +0000
607 Wed Jan 14 21:20:00 1970 +0000
634 Wed Jan 14 21:20:00 1970 +0000
608 Tue Jan 13 17:33:20 1970 +0000
635 Tue Jan 13 17:33:20 1970 +0000
609 Mon Jan 12 13:46:40 1970 +0000
636 Mon Jan 12 13:46:40 1970 +0000
610 2020-01-01 10:01 +0000
637 2020-01-01 10:01 +0000
611 1970-01-12 13:46 +0000
638 1970-01-12 13:46 +0000
612 1970-01-18 08:40 +0000
639 1970-01-18 08:40 +0000
613 1970-01-18 08:40 +0000
640 1970-01-18 08:40 +0000
614 1970-01-17 04:53 +0000
641 1970-01-17 04:53 +0000
615 1970-01-16 01:06 +0000
642 1970-01-16 01:06 +0000
616 1970-01-14 21:20 +0000
643 1970-01-14 21:20 +0000
617 1970-01-13 17:33 +0000
644 1970-01-13 17:33 +0000
618 1970-01-12 13:46 +0000
645 1970-01-12 13:46 +0000
619 2020-01-01 10:01:00 +0000
646 2020-01-01 10:01:00 +0000
620 1970-01-12 13:46:40 +0000
647 1970-01-12 13:46:40 +0000
621 1970-01-18 08:40:01 +0000
648 1970-01-18 08:40:01 +0000
622 1970-01-18 08:40:00 +0000
649 1970-01-18 08:40:00 +0000
623 1970-01-17 04:53:20 +0000
650 1970-01-17 04:53:20 +0000
624 1970-01-16 01:06:40 +0000
651 1970-01-16 01:06:40 +0000
625 1970-01-14 21:20:00 +0000
652 1970-01-14 21:20:00 +0000
626 1970-01-13 17:33:20 +0000
653 1970-01-13 17:33:20 +0000
627 1970-01-12 13:46:40 +0000
654 1970-01-12 13:46:40 +0000
628 Wed, 01 Jan 2020 10:01:00 +0000
655 Wed, 01 Jan 2020 10:01:00 +0000
629 Mon, 12 Jan 1970 13:46:40 +0000
656 Mon, 12 Jan 1970 13:46:40 +0000
630 Sun, 18 Jan 1970 08:40:01 +0000
657 Sun, 18 Jan 1970 08:40:01 +0000
631 Sun, 18 Jan 1970 08:40:00 +0000
658 Sun, 18 Jan 1970 08:40:00 +0000
632 Sat, 17 Jan 1970 04:53:20 +0000
659 Sat, 17 Jan 1970 04:53:20 +0000
633 Fri, 16 Jan 1970 01:06:40 +0000
660 Fri, 16 Jan 1970 01:06:40 +0000
634 Wed, 14 Jan 1970 21:20:00 +0000
661 Wed, 14 Jan 1970 21:20:00 +0000
635 Tue, 13 Jan 1970 17:33:20 +0000
662 Tue, 13 Jan 1970 17:33:20 +0000
636 Mon, 12 Jan 1970 13:46:40 +0000
663 Mon, 12 Jan 1970 13:46:40 +0000
637 third
664 third
638 second
665 second
639 merge
666 merge
640 new head
667 new head
641 new branch
668 new branch
642 no user, no domain
669 no user, no domain
643 no person
670 no person
644 other 1
671 other 1
645 line 1
672 line 1
646 946e2bd9c565
673 946e2bd9c565
647 29114dbae42b
674 29114dbae42b
648 c7b487c6c50e
675 c7b487c6c50e
649 13207e5a10d9
676 13207e5a10d9
650 32a18f097fcc
677 32a18f097fcc
651 10e46f2dcbf4
678 10e46f2dcbf4
652 97054abb4ab8
679 97054abb4ab8
653 b608e9d1a3f0
680 b608e9d1a3f0
654 1e4e1b8f71e0
681 1e4e1b8f71e0
655 <changeset author="test"/>
682 <changeset author="test"/>
656 <changeset author="User Name &lt;user@hostname&gt;"/>
683 <changeset author="User Name &lt;user@hostname&gt;"/>
657 <changeset author="person"/>
684 <changeset author="person"/>
658 <changeset author="person"/>
685 <changeset author="person"/>
659 <changeset author="person"/>
686 <changeset author="person"/>
660 <changeset author="person"/>
687 <changeset author="person"/>
661 <changeset author="other@place"/>
688 <changeset author="other@place"/>
662 <changeset author="A. N. Other &lt;other@place&gt;"/>
689 <changeset author="A. N. Other &lt;other@place&gt;"/>
663 <changeset author="User Name &lt;user@hostname&gt;"/>
690 <changeset author="User Name &lt;user@hostname&gt;"/>
664 # formatnode filter works
691 # formatnode filter works
665 # quiet
692 # quiet
666 1e4e1b8f71e0
693 1e4e1b8f71e0
667 # normal
694 # normal
668 1e4e1b8f71e0
695 1e4e1b8f71e0
669 # verbose
696 # verbose
670 1e4e1b8f71e0
697 1e4e1b8f71e0
671 # debug
698 # debug
672 1e4e1b8f71e05681d422154f5421e385fec3454f
699 1e4e1b8f71e05681d422154f5421e385fec3454f
673 # error on syntax
700 # error on syntax
674 abort: t:3: unmatched quotes
701 abort: t:3: unmatched quotes
675 # latesttag
702 # latesttag
676 adding file
703 adding file
677 adding head1
704 adding head1
678 adding head2
705 adding head2
679 created new head
706 created new head
680 # No tag set
707 # No tag set
681 5: null+5
708 5: null+5
682 4: null+4
709 4: null+4
683 3: null+3
710 3: null+3
684 2: null+3
711 2: null+3
685 1: null+2
712 1: null+2
686 0: null+1
713 0: null+1
687 # one common tag: longuest path wins
714 # one common tag: longuest path wins
688 6: t1+4
715 6: t1+4
689 5: t1+3
716 5: t1+3
690 4: t1+2
717 4: t1+2
691 3: t1+1
718 3: t1+1
692 2: t1+1
719 2: t1+1
693 1: t1+0
720 1: t1+0
694 0: null+1
721 0: null+1
695 # one ancestor tag: more recent wins
722 # one ancestor tag: more recent wins
696 7: t2+3
723 7: t2+3
697 6: t2+2
724 6: t2+2
698 5: t2+1
725 5: t2+1
699 4: t1+2
726 4: t1+2
700 3: t1+1
727 3: t1+1
701 2: t2+0
728 2: t2+0
702 1: t1+0
729 1: t1+0
703 0: null+1
730 0: null+1
704 # two branch tags: more recent wins
731 # two branch tags: more recent wins
705 8: t3+5
732 8: t3+5
706 7: t3+4
733 7: t3+4
707 6: t3+3
734 6: t3+3
708 5: t3+2
735 5: t3+2
709 4: t3+1
736 4: t3+1
710 3: t3+0
737 3: t3+0
711 2: t2+0
738 2: t2+0
712 1: t1+0
739 1: t1+0
713 0: null+1
740 0: null+1
714 # merged tag overrides
741 # merged tag overrides
715 10: t5+5
742 10: t5+5
716 9: t5+4
743 9: t5+4
717 8: t5+3
744 8: t5+3
718 7: t5+2
745 7: t5+2
719 6: t5+1
746 6: t5+1
720 5: t5+0
747 5: t5+0
721 4: at3:t3+1
748 4: at3:t3+1
722 3: at3:t3+0
749 3: at3:t3+0
723 2: t2+0
750 2: t2+0
724 1: t1+0
751 1: t1+0
725 0: null+1
752 0: null+1
726 # done
753 # done
General Comments 0
You need to be logged in to leave comments. Login now