##// END OF EJS Templates
templater: handle SyntaxError when parsing ui.logtemplate...
Martin Geisler -
r16678:48b1674a stable
parent child Browse files
Show More
@@ -1,1653 +1,1656 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
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, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, bookmarks
13 import subrepo, context, repair, bookmarks
14
14
15 def parsealiases(cmd):
15 def parsealiases(cmd):
16 return cmd.lstrip("^").split("|")
16 return cmd.lstrip("^").split("|")
17
17
18 def findpossible(cmd, table, strict=False):
18 def findpossible(cmd, table, strict=False):
19 """
19 """
20 Return cmd -> (aliases, command table entry)
20 Return cmd -> (aliases, command table entry)
21 for each matching command.
21 for each matching command.
22 Return debug commands (or their aliases) only if no normal command matches.
22 Return debug commands (or their aliases) only if no normal command matches.
23 """
23 """
24 choice = {}
24 choice = {}
25 debugchoice = {}
25 debugchoice = {}
26
26
27 if cmd in table:
27 if cmd in table:
28 # short-circuit exact matches, "log" alias beats "^log|history"
28 # short-circuit exact matches, "log" alias beats "^log|history"
29 keys = [cmd]
29 keys = [cmd]
30 else:
30 else:
31 keys = table.keys()
31 keys = table.keys()
32
32
33 for e in keys:
33 for e in keys:
34 aliases = parsealiases(e)
34 aliases = parsealiases(e)
35 found = None
35 found = None
36 if cmd in aliases:
36 if cmd in aliases:
37 found = cmd
37 found = cmd
38 elif not strict:
38 elif not strict:
39 for a in aliases:
39 for a in aliases:
40 if a.startswith(cmd):
40 if a.startswith(cmd):
41 found = a
41 found = a
42 break
42 break
43 if found is not None:
43 if found is not None:
44 if aliases[0].startswith("debug") or found.startswith("debug"):
44 if aliases[0].startswith("debug") or found.startswith("debug"):
45 debugchoice[found] = (aliases, table[e])
45 debugchoice[found] = (aliases, table[e])
46 else:
46 else:
47 choice[found] = (aliases, table[e])
47 choice[found] = (aliases, table[e])
48
48
49 if not choice and debugchoice:
49 if not choice and debugchoice:
50 choice = debugchoice
50 choice = debugchoice
51
51
52 return choice
52 return choice
53
53
54 def findcmd(cmd, table, strict=True):
54 def findcmd(cmd, table, strict=True):
55 """Return (aliases, command table entry) for command string."""
55 """Return (aliases, command table entry) for command string."""
56 choice = findpossible(cmd, table, strict)
56 choice = findpossible(cmd, table, strict)
57
57
58 if cmd in choice:
58 if cmd in choice:
59 return choice[cmd]
59 return choice[cmd]
60
60
61 if len(choice) > 1:
61 if len(choice) > 1:
62 clist = choice.keys()
62 clist = choice.keys()
63 clist.sort()
63 clist.sort()
64 raise error.AmbiguousCommand(cmd, clist)
64 raise error.AmbiguousCommand(cmd, clist)
65
65
66 if choice:
66 if choice:
67 return choice.values()[0]
67 return choice.values()[0]
68
68
69 raise error.UnknownCommand(cmd)
69 raise error.UnknownCommand(cmd)
70
70
71 def findrepo(p):
71 def findrepo(p):
72 while not os.path.isdir(os.path.join(p, ".hg")):
72 while not os.path.isdir(os.path.join(p, ".hg")):
73 oldp, p = p, os.path.dirname(p)
73 oldp, p = p, os.path.dirname(p)
74 if p == oldp:
74 if p == oldp:
75 return None
75 return None
76
76
77 return p
77 return p
78
78
79 def bailifchanged(repo):
79 def bailifchanged(repo):
80 if repo.dirstate.p2() != nullid:
80 if repo.dirstate.p2() != nullid:
81 raise util.Abort(_('outstanding uncommitted merge'))
81 raise util.Abort(_('outstanding uncommitted merge'))
82 modified, added, removed, deleted = repo.status()[:4]
82 modified, added, removed, deleted = repo.status()[:4]
83 if modified or added or removed or deleted:
83 if modified or added or removed or deleted:
84 raise util.Abort(_("outstanding uncommitted changes"))
84 raise util.Abort(_("outstanding uncommitted changes"))
85 ctx = repo[None]
85 ctx = repo[None]
86 for s in ctx.substate:
86 for s in ctx.substate:
87 if ctx.sub(s).dirty():
87 if ctx.sub(s).dirty():
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89
89
90 def logmessage(ui, opts):
90 def logmessage(ui, opts):
91 """ get the log message according to -m and -l option """
91 """ get the log message according to -m and -l option """
92 message = opts.get('message')
92 message = opts.get('message')
93 logfile = opts.get('logfile')
93 logfile = opts.get('logfile')
94
94
95 if message and logfile:
95 if message and logfile:
96 raise util.Abort(_('options --message and --logfile are mutually '
96 raise util.Abort(_('options --message and --logfile are mutually '
97 'exclusive'))
97 'exclusive'))
98 if not message and logfile:
98 if not message and logfile:
99 try:
99 try:
100 if logfile == '-':
100 if logfile == '-':
101 message = ui.fin.read()
101 message = ui.fin.read()
102 else:
102 else:
103 message = '\n'.join(util.readfile(logfile).splitlines())
103 message = '\n'.join(util.readfile(logfile).splitlines())
104 except IOError, inst:
104 except IOError, inst:
105 raise util.Abort(_("can't read commit message '%s': %s") %
105 raise util.Abort(_("can't read commit message '%s': %s") %
106 (logfile, inst.strerror))
106 (logfile, inst.strerror))
107 return message
107 return message
108
108
109 def loglimit(opts):
109 def loglimit(opts):
110 """get the log limit according to option -l/--limit"""
110 """get the log limit according to option -l/--limit"""
111 limit = opts.get('limit')
111 limit = opts.get('limit')
112 if limit:
112 if limit:
113 try:
113 try:
114 limit = int(limit)
114 limit = int(limit)
115 except ValueError:
115 except ValueError:
116 raise util.Abort(_('limit must be a positive integer'))
116 raise util.Abort(_('limit must be a positive integer'))
117 if limit <= 0:
117 if limit <= 0:
118 raise util.Abort(_('limit must be positive'))
118 raise util.Abort(_('limit must be positive'))
119 else:
119 else:
120 limit = None
120 limit = None
121 return limit
121 return limit
122
122
123 def makefilename(repo, pat, node, desc=None,
123 def makefilename(repo, pat, node, desc=None,
124 total=None, seqno=None, revwidth=None, pathname=None):
124 total=None, seqno=None, revwidth=None, pathname=None):
125 node_expander = {
125 node_expander = {
126 'H': lambda: hex(node),
126 'H': lambda: hex(node),
127 'R': lambda: str(repo.changelog.rev(node)),
127 'R': lambda: str(repo.changelog.rev(node)),
128 'h': lambda: short(node),
128 'h': lambda: short(node),
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
129 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 }
130 }
131 expander = {
131 expander = {
132 '%': lambda: '%',
132 '%': lambda: '%',
133 'b': lambda: os.path.basename(repo.root),
133 'b': lambda: os.path.basename(repo.root),
134 }
134 }
135
135
136 try:
136 try:
137 if node:
137 if node:
138 expander.update(node_expander)
138 expander.update(node_expander)
139 if node:
139 if node:
140 expander['r'] = (lambda:
140 expander['r'] = (lambda:
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 if total is not None:
142 if total is not None:
143 expander['N'] = lambda: str(total)
143 expander['N'] = lambda: str(total)
144 if seqno is not None:
144 if seqno is not None:
145 expander['n'] = lambda: str(seqno)
145 expander['n'] = lambda: str(seqno)
146 if total is not None and seqno is not None:
146 if total is not None and seqno is not None:
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 if pathname is not None:
148 if pathname is not None:
149 expander['s'] = lambda: os.path.basename(pathname)
149 expander['s'] = lambda: os.path.basename(pathname)
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 expander['p'] = lambda: pathname
151 expander['p'] = lambda: pathname
152
152
153 newname = []
153 newname = []
154 patlen = len(pat)
154 patlen = len(pat)
155 i = 0
155 i = 0
156 while i < patlen:
156 while i < patlen:
157 c = pat[i]
157 c = pat[i]
158 if c == '%':
158 if c == '%':
159 i += 1
159 i += 1
160 c = pat[i]
160 c = pat[i]
161 c = expander[c]()
161 c = expander[c]()
162 newname.append(c)
162 newname.append(c)
163 i += 1
163 i += 1
164 return ''.join(newname)
164 return ''.join(newname)
165 except KeyError, inst:
165 except KeyError, inst:
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 inst.args[0])
167 inst.args[0])
168
168
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
169 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 seqno=None, revwidth=None, mode='wb', pathname=None):
170 seqno=None, revwidth=None, mode='wb', pathname=None):
171
171
172 writable = mode not in ('r', 'rb')
172 writable = mode not in ('r', 'rb')
173
173
174 if not pat or pat == '-':
174 if not pat or pat == '-':
175 fp = writable and repo.ui.fout or repo.ui.fin
175 fp = writable and repo.ui.fout or repo.ui.fin
176 if util.safehasattr(fp, 'fileno'):
176 if util.safehasattr(fp, 'fileno'):
177 return os.fdopen(os.dup(fp.fileno()), mode)
177 return os.fdopen(os.dup(fp.fileno()), mode)
178 else:
178 else:
179 # if this fp can't be duped properly, return
179 # if this fp can't be duped properly, return
180 # a dummy object that can be closed
180 # a dummy object that can be closed
181 class wrappedfileobj(object):
181 class wrappedfileobj(object):
182 noop = lambda x: None
182 noop = lambda x: None
183 def __init__(self, f):
183 def __init__(self, f):
184 self.f = f
184 self.f = f
185 def __getattr__(self, attr):
185 def __getattr__(self, attr):
186 if attr == 'close':
186 if attr == 'close':
187 return self.noop
187 return self.noop
188 else:
188 else:
189 return getattr(self.f, attr)
189 return getattr(self.f, attr)
190
190
191 return wrappedfileobj(fp)
191 return wrappedfileobj(fp)
192 if util.safehasattr(pat, 'write') and writable:
192 if util.safehasattr(pat, 'write') and writable:
193 return pat
193 return pat
194 if util.safehasattr(pat, 'read') and 'r' in mode:
194 if util.safehasattr(pat, 'read') and 'r' in mode:
195 return pat
195 return pat
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 pathname),
197 pathname),
198 mode)
198 mode)
199
199
200 def openrevlog(repo, cmd, file_, opts):
200 def openrevlog(repo, cmd, file_, opts):
201 """opens the changelog, manifest, a filelog or a given revlog"""
201 """opens the changelog, manifest, a filelog or a given revlog"""
202 cl = opts['changelog']
202 cl = opts['changelog']
203 mf = opts['manifest']
203 mf = opts['manifest']
204 msg = None
204 msg = None
205 if cl and mf:
205 if cl and mf:
206 msg = _('cannot specify --changelog and --manifest at the same time')
206 msg = _('cannot specify --changelog and --manifest at the same time')
207 elif cl or mf:
207 elif cl or mf:
208 if file_:
208 if file_:
209 msg = _('cannot specify filename with --changelog or --manifest')
209 msg = _('cannot specify filename with --changelog or --manifest')
210 elif not repo:
210 elif not repo:
211 msg = _('cannot specify --changelog or --manifest '
211 msg = _('cannot specify --changelog or --manifest '
212 'without a repository')
212 'without a repository')
213 if msg:
213 if msg:
214 raise util.Abort(msg)
214 raise util.Abort(msg)
215
215
216 r = None
216 r = None
217 if repo:
217 if repo:
218 if cl:
218 if cl:
219 r = repo.changelog
219 r = repo.changelog
220 elif mf:
220 elif mf:
221 r = repo.manifest
221 r = repo.manifest
222 elif file_:
222 elif file_:
223 filelog = repo.file(file_)
223 filelog = repo.file(file_)
224 if len(filelog):
224 if len(filelog):
225 r = filelog
225 r = filelog
226 if not r:
226 if not r:
227 if not file_:
227 if not file_:
228 raise error.CommandError(cmd, _('invalid arguments'))
228 raise error.CommandError(cmd, _('invalid arguments'))
229 if not os.path.isfile(file_):
229 if not os.path.isfile(file_):
230 raise util.Abort(_("revlog '%s' not found") % file_)
230 raise util.Abort(_("revlog '%s' not found") % file_)
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 file_[:-2] + ".i")
232 file_[:-2] + ".i")
233 return r
233 return r
234
234
235 def copy(ui, repo, pats, opts, rename=False):
235 def copy(ui, repo, pats, opts, rename=False):
236 # called with the repo lock held
236 # called with the repo lock held
237 #
237 #
238 # hgsep => pathname that uses "/" to separate directories
238 # hgsep => pathname that uses "/" to separate directories
239 # ossep => pathname that uses os.sep to separate directories
239 # ossep => pathname that uses os.sep to separate directories
240 cwd = repo.getcwd()
240 cwd = repo.getcwd()
241 targets = {}
241 targets = {}
242 after = opts.get("after")
242 after = opts.get("after")
243 dryrun = opts.get("dry_run")
243 dryrun = opts.get("dry_run")
244 wctx = repo[None]
244 wctx = repo[None]
245
245
246 def walkpat(pat):
246 def walkpat(pat):
247 srcs = []
247 srcs = []
248 badstates = after and '?' or '?r'
248 badstates = after and '?' or '?r'
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 for abs in repo.walk(m):
250 for abs in repo.walk(m):
251 state = repo.dirstate[abs]
251 state = repo.dirstate[abs]
252 rel = m.rel(abs)
252 rel = m.rel(abs)
253 exact = m.exact(abs)
253 exact = m.exact(abs)
254 if state in badstates:
254 if state in badstates:
255 if exact and state == '?':
255 if exact and state == '?':
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 if exact and state == 'r':
257 if exact and state == 'r':
258 ui.warn(_('%s: not copying - file has been marked for'
258 ui.warn(_('%s: not copying - file has been marked for'
259 ' remove\n') % rel)
259 ' remove\n') % rel)
260 continue
260 continue
261 # abs: hgsep
261 # abs: hgsep
262 # rel: ossep
262 # rel: ossep
263 srcs.append((abs, rel, exact))
263 srcs.append((abs, rel, exact))
264 return srcs
264 return srcs
265
265
266 # abssrc: hgsep
266 # abssrc: hgsep
267 # relsrc: ossep
267 # relsrc: ossep
268 # otarget: ossep
268 # otarget: ossep
269 def copyfile(abssrc, relsrc, otarget, exact):
269 def copyfile(abssrc, relsrc, otarget, exact):
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 if '/' in abstarget:
271 if '/' in abstarget:
272 # We cannot normalize abstarget itself, this would prevent
272 # We cannot normalize abstarget itself, this would prevent
273 # case only renames, like a => A.
273 # case only renames, like a => A.
274 abspath, absname = abstarget.rsplit('/', 1)
274 abspath, absname = abstarget.rsplit('/', 1)
275 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
275 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
276 reltarget = repo.pathto(abstarget, cwd)
276 reltarget = repo.pathto(abstarget, cwd)
277 target = repo.wjoin(abstarget)
277 target = repo.wjoin(abstarget)
278 src = repo.wjoin(abssrc)
278 src = repo.wjoin(abssrc)
279 state = repo.dirstate[abstarget]
279 state = repo.dirstate[abstarget]
280
280
281 scmutil.checkportable(ui, abstarget)
281 scmutil.checkportable(ui, abstarget)
282
282
283 # check for collisions
283 # check for collisions
284 prevsrc = targets.get(abstarget)
284 prevsrc = targets.get(abstarget)
285 if prevsrc is not None:
285 if prevsrc is not None:
286 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
286 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
287 (reltarget, repo.pathto(abssrc, cwd),
287 (reltarget, repo.pathto(abssrc, cwd),
288 repo.pathto(prevsrc, cwd)))
288 repo.pathto(prevsrc, cwd)))
289 return
289 return
290
290
291 # check for overwrites
291 # check for overwrites
292 exists = os.path.lexists(target)
292 exists = os.path.lexists(target)
293 samefile = False
293 samefile = False
294 if exists and abssrc != abstarget:
294 if exists and abssrc != abstarget:
295 if (repo.dirstate.normalize(abssrc) ==
295 if (repo.dirstate.normalize(abssrc) ==
296 repo.dirstate.normalize(abstarget)):
296 repo.dirstate.normalize(abstarget)):
297 if not rename:
297 if not rename:
298 ui.warn(_("%s: can't copy - same file\n") % reltarget)
298 ui.warn(_("%s: can't copy - same file\n") % reltarget)
299 return
299 return
300 exists = False
300 exists = False
301 samefile = True
301 samefile = True
302
302
303 if not after and exists or after and state in 'mn':
303 if not after and exists or after and state in 'mn':
304 if not opts['force']:
304 if not opts['force']:
305 ui.warn(_('%s: not overwriting - file exists\n') %
305 ui.warn(_('%s: not overwriting - file exists\n') %
306 reltarget)
306 reltarget)
307 return
307 return
308
308
309 if after:
309 if after:
310 if not exists:
310 if not exists:
311 if rename:
311 if rename:
312 ui.warn(_('%s: not recording move - %s does not exist\n') %
312 ui.warn(_('%s: not recording move - %s does not exist\n') %
313 (relsrc, reltarget))
313 (relsrc, reltarget))
314 else:
314 else:
315 ui.warn(_('%s: not recording copy - %s does not exist\n') %
315 ui.warn(_('%s: not recording copy - %s does not exist\n') %
316 (relsrc, reltarget))
316 (relsrc, reltarget))
317 return
317 return
318 elif not dryrun:
318 elif not dryrun:
319 try:
319 try:
320 if exists:
320 if exists:
321 os.unlink(target)
321 os.unlink(target)
322 targetdir = os.path.dirname(target) or '.'
322 targetdir = os.path.dirname(target) or '.'
323 if not os.path.isdir(targetdir):
323 if not os.path.isdir(targetdir):
324 os.makedirs(targetdir)
324 os.makedirs(targetdir)
325 if samefile:
325 if samefile:
326 tmp = target + "~hgrename"
326 tmp = target + "~hgrename"
327 os.rename(src, tmp)
327 os.rename(src, tmp)
328 os.rename(tmp, target)
328 os.rename(tmp, target)
329 else:
329 else:
330 util.copyfile(src, target)
330 util.copyfile(src, target)
331 srcexists = True
331 srcexists = True
332 except IOError, inst:
332 except IOError, inst:
333 if inst.errno == errno.ENOENT:
333 if inst.errno == errno.ENOENT:
334 ui.warn(_('%s: deleted in working copy\n') % relsrc)
334 ui.warn(_('%s: deleted in working copy\n') % relsrc)
335 srcexists = False
335 srcexists = False
336 else:
336 else:
337 ui.warn(_('%s: cannot copy - %s\n') %
337 ui.warn(_('%s: cannot copy - %s\n') %
338 (relsrc, inst.strerror))
338 (relsrc, inst.strerror))
339 return True # report a failure
339 return True # report a failure
340
340
341 if ui.verbose or not exact:
341 if ui.verbose or not exact:
342 if rename:
342 if rename:
343 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
343 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
344 else:
344 else:
345 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
345 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
346
346
347 targets[abstarget] = abssrc
347 targets[abstarget] = abssrc
348
348
349 # fix up dirstate
349 # fix up dirstate
350 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
350 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
351 dryrun=dryrun, cwd=cwd)
351 dryrun=dryrun, cwd=cwd)
352 if rename and not dryrun:
352 if rename and not dryrun:
353 if not after and srcexists and not samefile:
353 if not after and srcexists and not samefile:
354 util.unlinkpath(repo.wjoin(abssrc))
354 util.unlinkpath(repo.wjoin(abssrc))
355 wctx.forget([abssrc])
355 wctx.forget([abssrc])
356
356
357 # pat: ossep
357 # pat: ossep
358 # dest ossep
358 # dest ossep
359 # srcs: list of (hgsep, hgsep, ossep, bool)
359 # srcs: list of (hgsep, hgsep, ossep, bool)
360 # return: function that takes hgsep and returns ossep
360 # return: function that takes hgsep and returns ossep
361 def targetpathfn(pat, dest, srcs):
361 def targetpathfn(pat, dest, srcs):
362 if os.path.isdir(pat):
362 if os.path.isdir(pat):
363 abspfx = scmutil.canonpath(repo.root, cwd, pat)
363 abspfx = scmutil.canonpath(repo.root, cwd, pat)
364 abspfx = util.localpath(abspfx)
364 abspfx = util.localpath(abspfx)
365 if destdirexists:
365 if destdirexists:
366 striplen = len(os.path.split(abspfx)[0])
366 striplen = len(os.path.split(abspfx)[0])
367 else:
367 else:
368 striplen = len(abspfx)
368 striplen = len(abspfx)
369 if striplen:
369 if striplen:
370 striplen += len(os.sep)
370 striplen += len(os.sep)
371 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
371 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
372 elif destdirexists:
372 elif destdirexists:
373 res = lambda p: os.path.join(dest,
373 res = lambda p: os.path.join(dest,
374 os.path.basename(util.localpath(p)))
374 os.path.basename(util.localpath(p)))
375 else:
375 else:
376 res = lambda p: dest
376 res = lambda p: dest
377 return res
377 return res
378
378
379 # pat: ossep
379 # pat: ossep
380 # dest ossep
380 # dest ossep
381 # srcs: list of (hgsep, hgsep, ossep, bool)
381 # srcs: list of (hgsep, hgsep, ossep, bool)
382 # return: function that takes hgsep and returns ossep
382 # return: function that takes hgsep and returns ossep
383 def targetpathafterfn(pat, dest, srcs):
383 def targetpathafterfn(pat, dest, srcs):
384 if matchmod.patkind(pat):
384 if matchmod.patkind(pat):
385 # a mercurial pattern
385 # a mercurial pattern
386 res = lambda p: os.path.join(dest,
386 res = lambda p: os.path.join(dest,
387 os.path.basename(util.localpath(p)))
387 os.path.basename(util.localpath(p)))
388 else:
388 else:
389 abspfx = scmutil.canonpath(repo.root, cwd, pat)
389 abspfx = scmutil.canonpath(repo.root, cwd, pat)
390 if len(abspfx) < len(srcs[0][0]):
390 if len(abspfx) < len(srcs[0][0]):
391 # A directory. Either the target path contains the last
391 # A directory. Either the target path contains the last
392 # component of the source path or it does not.
392 # component of the source path or it does not.
393 def evalpath(striplen):
393 def evalpath(striplen):
394 score = 0
394 score = 0
395 for s in srcs:
395 for s in srcs:
396 t = os.path.join(dest, util.localpath(s[0])[striplen:])
396 t = os.path.join(dest, util.localpath(s[0])[striplen:])
397 if os.path.lexists(t):
397 if os.path.lexists(t):
398 score += 1
398 score += 1
399 return score
399 return score
400
400
401 abspfx = util.localpath(abspfx)
401 abspfx = util.localpath(abspfx)
402 striplen = len(abspfx)
402 striplen = len(abspfx)
403 if striplen:
403 if striplen:
404 striplen += len(os.sep)
404 striplen += len(os.sep)
405 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
405 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
406 score = evalpath(striplen)
406 score = evalpath(striplen)
407 striplen1 = len(os.path.split(abspfx)[0])
407 striplen1 = len(os.path.split(abspfx)[0])
408 if striplen1:
408 if striplen1:
409 striplen1 += len(os.sep)
409 striplen1 += len(os.sep)
410 if evalpath(striplen1) > score:
410 if evalpath(striplen1) > score:
411 striplen = striplen1
411 striplen = striplen1
412 res = lambda p: os.path.join(dest,
412 res = lambda p: os.path.join(dest,
413 util.localpath(p)[striplen:])
413 util.localpath(p)[striplen:])
414 else:
414 else:
415 # a file
415 # a file
416 if destdirexists:
416 if destdirexists:
417 res = lambda p: os.path.join(dest,
417 res = lambda p: os.path.join(dest,
418 os.path.basename(util.localpath(p)))
418 os.path.basename(util.localpath(p)))
419 else:
419 else:
420 res = lambda p: dest
420 res = lambda p: dest
421 return res
421 return res
422
422
423
423
424 pats = scmutil.expandpats(pats)
424 pats = scmutil.expandpats(pats)
425 if not pats:
425 if not pats:
426 raise util.Abort(_('no source or destination specified'))
426 raise util.Abort(_('no source or destination specified'))
427 if len(pats) == 1:
427 if len(pats) == 1:
428 raise util.Abort(_('no destination specified'))
428 raise util.Abort(_('no destination specified'))
429 dest = pats.pop()
429 dest = pats.pop()
430 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
430 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
431 if not destdirexists:
431 if not destdirexists:
432 if len(pats) > 1 or matchmod.patkind(pats[0]):
432 if len(pats) > 1 or matchmod.patkind(pats[0]):
433 raise util.Abort(_('with multiple sources, destination must be an '
433 raise util.Abort(_('with multiple sources, destination must be an '
434 'existing directory'))
434 'existing directory'))
435 if util.endswithsep(dest):
435 if util.endswithsep(dest):
436 raise util.Abort(_('destination %s is not a directory') % dest)
436 raise util.Abort(_('destination %s is not a directory') % dest)
437
437
438 tfn = targetpathfn
438 tfn = targetpathfn
439 if after:
439 if after:
440 tfn = targetpathafterfn
440 tfn = targetpathafterfn
441 copylist = []
441 copylist = []
442 for pat in pats:
442 for pat in pats:
443 srcs = walkpat(pat)
443 srcs = walkpat(pat)
444 if not srcs:
444 if not srcs:
445 continue
445 continue
446 copylist.append((tfn(pat, dest, srcs), srcs))
446 copylist.append((tfn(pat, dest, srcs), srcs))
447 if not copylist:
447 if not copylist:
448 raise util.Abort(_('no files to copy'))
448 raise util.Abort(_('no files to copy'))
449
449
450 errors = 0
450 errors = 0
451 for targetpath, srcs in copylist:
451 for targetpath, srcs in copylist:
452 for abssrc, relsrc, exact in srcs:
452 for abssrc, relsrc, exact in srcs:
453 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
453 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
454 errors += 1
454 errors += 1
455
455
456 if errors:
456 if errors:
457 ui.warn(_('(consider using --after)\n'))
457 ui.warn(_('(consider using --after)\n'))
458
458
459 return errors != 0
459 return errors != 0
460
460
461 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
461 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
462 runargs=None, appendpid=False):
462 runargs=None, appendpid=False):
463 '''Run a command as a service.'''
463 '''Run a command as a service.'''
464
464
465 if opts['daemon'] and not opts['daemon_pipefds']:
465 if opts['daemon'] and not opts['daemon_pipefds']:
466 # Signal child process startup with file removal
466 # Signal child process startup with file removal
467 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
467 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
468 os.close(lockfd)
468 os.close(lockfd)
469 try:
469 try:
470 if not runargs:
470 if not runargs:
471 runargs = util.hgcmd() + sys.argv[1:]
471 runargs = util.hgcmd() + sys.argv[1:]
472 runargs.append('--daemon-pipefds=%s' % lockpath)
472 runargs.append('--daemon-pipefds=%s' % lockpath)
473 # Don't pass --cwd to the child process, because we've already
473 # Don't pass --cwd to the child process, because we've already
474 # changed directory.
474 # changed directory.
475 for i in xrange(1, len(runargs)):
475 for i in xrange(1, len(runargs)):
476 if runargs[i].startswith('--cwd='):
476 if runargs[i].startswith('--cwd='):
477 del runargs[i]
477 del runargs[i]
478 break
478 break
479 elif runargs[i].startswith('--cwd'):
479 elif runargs[i].startswith('--cwd'):
480 del runargs[i:i + 2]
480 del runargs[i:i + 2]
481 break
481 break
482 def condfn():
482 def condfn():
483 return not os.path.exists(lockpath)
483 return not os.path.exists(lockpath)
484 pid = util.rundetached(runargs, condfn)
484 pid = util.rundetached(runargs, condfn)
485 if pid < 0:
485 if pid < 0:
486 raise util.Abort(_('child process failed to start'))
486 raise util.Abort(_('child process failed to start'))
487 finally:
487 finally:
488 try:
488 try:
489 os.unlink(lockpath)
489 os.unlink(lockpath)
490 except OSError, e:
490 except OSError, e:
491 if e.errno != errno.ENOENT:
491 if e.errno != errno.ENOENT:
492 raise
492 raise
493 if parentfn:
493 if parentfn:
494 return parentfn(pid)
494 return parentfn(pid)
495 else:
495 else:
496 return
496 return
497
497
498 if initfn:
498 if initfn:
499 initfn()
499 initfn()
500
500
501 if opts['pid_file']:
501 if opts['pid_file']:
502 mode = appendpid and 'a' or 'w'
502 mode = appendpid and 'a' or 'w'
503 fp = open(opts['pid_file'], mode)
503 fp = open(opts['pid_file'], mode)
504 fp.write(str(os.getpid()) + '\n')
504 fp.write(str(os.getpid()) + '\n')
505 fp.close()
505 fp.close()
506
506
507 if opts['daemon_pipefds']:
507 if opts['daemon_pipefds']:
508 lockpath = opts['daemon_pipefds']
508 lockpath = opts['daemon_pipefds']
509 try:
509 try:
510 os.setsid()
510 os.setsid()
511 except AttributeError:
511 except AttributeError:
512 pass
512 pass
513 os.unlink(lockpath)
513 os.unlink(lockpath)
514 util.hidewindow()
514 util.hidewindow()
515 sys.stdout.flush()
515 sys.stdout.flush()
516 sys.stderr.flush()
516 sys.stderr.flush()
517
517
518 nullfd = os.open(util.nulldev, os.O_RDWR)
518 nullfd = os.open(util.nulldev, os.O_RDWR)
519 logfilefd = nullfd
519 logfilefd = nullfd
520 if logfile:
520 if logfile:
521 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
521 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
522 os.dup2(nullfd, 0)
522 os.dup2(nullfd, 0)
523 os.dup2(logfilefd, 1)
523 os.dup2(logfilefd, 1)
524 os.dup2(logfilefd, 2)
524 os.dup2(logfilefd, 2)
525 if nullfd not in (0, 1, 2):
525 if nullfd not in (0, 1, 2):
526 os.close(nullfd)
526 os.close(nullfd)
527 if logfile and logfilefd not in (0, 1, 2):
527 if logfile and logfilefd not in (0, 1, 2):
528 os.close(logfilefd)
528 os.close(logfilefd)
529
529
530 if runfn:
530 if runfn:
531 return runfn()
531 return runfn()
532
532
533 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
533 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
534 opts=None):
534 opts=None):
535 '''export changesets as hg patches.'''
535 '''export changesets as hg patches.'''
536
536
537 total = len(revs)
537 total = len(revs)
538 revwidth = max([len(str(rev)) for rev in revs])
538 revwidth = max([len(str(rev)) for rev in revs])
539
539
540 def single(rev, seqno, fp):
540 def single(rev, seqno, fp):
541 ctx = repo[rev]
541 ctx = repo[rev]
542 node = ctx.node()
542 node = ctx.node()
543 parents = [p.node() for p in ctx.parents() if p]
543 parents = [p.node() for p in ctx.parents() if p]
544 branch = ctx.branch()
544 branch = ctx.branch()
545 if switch_parent:
545 if switch_parent:
546 parents.reverse()
546 parents.reverse()
547 prev = (parents and parents[0]) or nullid
547 prev = (parents and parents[0]) or nullid
548
548
549 shouldclose = False
549 shouldclose = False
550 if not fp:
550 if not fp:
551 desc_lines = ctx.description().rstrip().split('\n')
551 desc_lines = ctx.description().rstrip().split('\n')
552 desc = desc_lines[0] #Commit always has a first line.
552 desc = desc_lines[0] #Commit always has a first line.
553 fp = makefileobj(repo, template, node, desc=desc, total=total,
553 fp = makefileobj(repo, template, node, desc=desc, total=total,
554 seqno=seqno, revwidth=revwidth, mode='ab')
554 seqno=seqno, revwidth=revwidth, mode='ab')
555 if fp != template:
555 if fp != template:
556 shouldclose = True
556 shouldclose = True
557 if fp != sys.stdout and util.safehasattr(fp, 'name'):
557 if fp != sys.stdout and util.safehasattr(fp, 'name'):
558 repo.ui.note("%s\n" % fp.name)
558 repo.ui.note("%s\n" % fp.name)
559
559
560 fp.write("# HG changeset patch\n")
560 fp.write("# HG changeset patch\n")
561 fp.write("# User %s\n" % ctx.user())
561 fp.write("# User %s\n" % ctx.user())
562 fp.write("# Date %d %d\n" % ctx.date())
562 fp.write("# Date %d %d\n" % ctx.date())
563 if branch and branch != 'default':
563 if branch and branch != 'default':
564 fp.write("# Branch %s\n" % branch)
564 fp.write("# Branch %s\n" % branch)
565 fp.write("# Node ID %s\n" % hex(node))
565 fp.write("# Node ID %s\n" % hex(node))
566 fp.write("# Parent %s\n" % hex(prev))
566 fp.write("# Parent %s\n" % hex(prev))
567 if len(parents) > 1:
567 if len(parents) > 1:
568 fp.write("# Parent %s\n" % hex(parents[1]))
568 fp.write("# Parent %s\n" % hex(parents[1]))
569 fp.write(ctx.description().rstrip())
569 fp.write(ctx.description().rstrip())
570 fp.write("\n\n")
570 fp.write("\n\n")
571
571
572 for chunk in patch.diff(repo, prev, node, opts=opts):
572 for chunk in patch.diff(repo, prev, node, opts=opts):
573 fp.write(chunk)
573 fp.write(chunk)
574
574
575 if shouldclose:
575 if shouldclose:
576 fp.close()
576 fp.close()
577
577
578 for seqno, rev in enumerate(revs):
578 for seqno, rev in enumerate(revs):
579 single(rev, seqno + 1, fp)
579 single(rev, seqno + 1, fp)
580
580
581 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
581 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
582 changes=None, stat=False, fp=None, prefix='',
582 changes=None, stat=False, fp=None, prefix='',
583 listsubrepos=False):
583 listsubrepos=False):
584 '''show diff or diffstat.'''
584 '''show diff or diffstat.'''
585 if fp is None:
585 if fp is None:
586 write = ui.write
586 write = ui.write
587 else:
587 else:
588 def write(s, **kw):
588 def write(s, **kw):
589 fp.write(s)
589 fp.write(s)
590
590
591 if stat:
591 if stat:
592 diffopts = diffopts.copy(context=0)
592 diffopts = diffopts.copy(context=0)
593 width = 80
593 width = 80
594 if not ui.plain():
594 if not ui.plain():
595 width = ui.termwidth()
595 width = ui.termwidth()
596 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
596 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
597 prefix=prefix)
597 prefix=prefix)
598 for chunk, label in patch.diffstatui(util.iterlines(chunks),
598 for chunk, label in patch.diffstatui(util.iterlines(chunks),
599 width=width,
599 width=width,
600 git=diffopts.git):
600 git=diffopts.git):
601 write(chunk, label=label)
601 write(chunk, label=label)
602 else:
602 else:
603 for chunk, label in patch.diffui(repo, node1, node2, match,
603 for chunk, label in patch.diffui(repo, node1, node2, match,
604 changes, diffopts, prefix=prefix):
604 changes, diffopts, prefix=prefix):
605 write(chunk, label=label)
605 write(chunk, label=label)
606
606
607 if listsubrepos:
607 if listsubrepos:
608 ctx1 = repo[node1]
608 ctx1 = repo[node1]
609 ctx2 = repo[node2]
609 ctx2 = repo[node2]
610 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
610 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
611 tempnode2 = node2
611 tempnode2 = node2
612 try:
612 try:
613 if node2 is not None:
613 if node2 is not None:
614 tempnode2 = ctx2.substate[subpath][1]
614 tempnode2 = ctx2.substate[subpath][1]
615 except KeyError:
615 except KeyError:
616 # A subrepo that existed in node1 was deleted between node1 and
616 # A subrepo that existed in node1 was deleted between node1 and
617 # node2 (inclusive). Thus, ctx2's substate won't contain that
617 # node2 (inclusive). Thus, ctx2's substate won't contain that
618 # subpath. The best we can do is to ignore it.
618 # subpath. The best we can do is to ignore it.
619 tempnode2 = None
619 tempnode2 = None
620 submatch = matchmod.narrowmatcher(subpath, match)
620 submatch = matchmod.narrowmatcher(subpath, match)
621 sub.diff(diffopts, tempnode2, submatch, changes=changes,
621 sub.diff(diffopts, tempnode2, submatch, changes=changes,
622 stat=stat, fp=fp, prefix=prefix)
622 stat=stat, fp=fp, prefix=prefix)
623
623
624 class changeset_printer(object):
624 class changeset_printer(object):
625 '''show changeset information when templating not requested.'''
625 '''show changeset information when templating not requested.'''
626
626
627 def __init__(self, ui, repo, patch, diffopts, buffered):
627 def __init__(self, ui, repo, patch, diffopts, buffered):
628 self.ui = ui
628 self.ui = ui
629 self.repo = repo
629 self.repo = repo
630 self.buffered = buffered
630 self.buffered = buffered
631 self.patch = patch
631 self.patch = patch
632 self.diffopts = diffopts
632 self.diffopts = diffopts
633 self.header = {}
633 self.header = {}
634 self.hunk = {}
634 self.hunk = {}
635 self.lastheader = None
635 self.lastheader = None
636 self.footer = None
636 self.footer = None
637
637
638 def flush(self, rev):
638 def flush(self, rev):
639 if rev in self.header:
639 if rev in self.header:
640 h = self.header[rev]
640 h = self.header[rev]
641 if h != self.lastheader:
641 if h != self.lastheader:
642 self.lastheader = h
642 self.lastheader = h
643 self.ui.write(h)
643 self.ui.write(h)
644 del self.header[rev]
644 del self.header[rev]
645 if rev in self.hunk:
645 if rev in self.hunk:
646 self.ui.write(self.hunk[rev])
646 self.ui.write(self.hunk[rev])
647 del self.hunk[rev]
647 del self.hunk[rev]
648 return 1
648 return 1
649 return 0
649 return 0
650
650
651 def close(self):
651 def close(self):
652 if self.footer:
652 if self.footer:
653 self.ui.write(self.footer)
653 self.ui.write(self.footer)
654
654
655 def show(self, ctx, copies=None, matchfn=None, **props):
655 def show(self, ctx, copies=None, matchfn=None, **props):
656 if self.buffered:
656 if self.buffered:
657 self.ui.pushbuffer()
657 self.ui.pushbuffer()
658 self._show(ctx, copies, matchfn, props)
658 self._show(ctx, copies, matchfn, props)
659 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
659 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
660 else:
660 else:
661 self._show(ctx, copies, matchfn, props)
661 self._show(ctx, copies, matchfn, props)
662
662
663 def _show(self, ctx, copies, matchfn, props):
663 def _show(self, ctx, copies, matchfn, props):
664 '''show a single changeset or file revision'''
664 '''show a single changeset or file revision'''
665 changenode = ctx.node()
665 changenode = ctx.node()
666 rev = ctx.rev()
666 rev = ctx.rev()
667
667
668 if self.ui.quiet:
668 if self.ui.quiet:
669 self.ui.write("%d:%s\n" % (rev, short(changenode)),
669 self.ui.write("%d:%s\n" % (rev, short(changenode)),
670 label='log.node')
670 label='log.node')
671 return
671 return
672
672
673 log = self.repo.changelog
673 log = self.repo.changelog
674 date = util.datestr(ctx.date())
674 date = util.datestr(ctx.date())
675
675
676 hexfunc = self.ui.debugflag and hex or short
676 hexfunc = self.ui.debugflag and hex or short
677
677
678 parents = [(p, hexfunc(log.node(p)))
678 parents = [(p, hexfunc(log.node(p)))
679 for p in self._meaningful_parentrevs(log, rev)]
679 for p in self._meaningful_parentrevs(log, rev)]
680
680
681 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
681 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
682 label='log.changeset')
682 label='log.changeset')
683
683
684 branch = ctx.branch()
684 branch = ctx.branch()
685 # don't show the default branch name
685 # don't show the default branch name
686 if branch != 'default':
686 if branch != 'default':
687 self.ui.write(_("branch: %s\n") % branch,
687 self.ui.write(_("branch: %s\n") % branch,
688 label='log.branch')
688 label='log.branch')
689 for bookmark in self.repo.nodebookmarks(changenode):
689 for bookmark in self.repo.nodebookmarks(changenode):
690 self.ui.write(_("bookmark: %s\n") % bookmark,
690 self.ui.write(_("bookmark: %s\n") % bookmark,
691 label='log.bookmark')
691 label='log.bookmark')
692 for tag in self.repo.nodetags(changenode):
692 for tag in self.repo.nodetags(changenode):
693 self.ui.write(_("tag: %s\n") % tag,
693 self.ui.write(_("tag: %s\n") % tag,
694 label='log.tag')
694 label='log.tag')
695 if self.ui.debugflag and ctx.phase():
695 if self.ui.debugflag and ctx.phase():
696 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
696 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
697 label='log.phase')
697 label='log.phase')
698 for parent in parents:
698 for parent in parents:
699 self.ui.write(_("parent: %d:%s\n") % parent,
699 self.ui.write(_("parent: %d:%s\n") % parent,
700 label='log.parent')
700 label='log.parent')
701
701
702 if self.ui.debugflag:
702 if self.ui.debugflag:
703 mnode = ctx.manifestnode()
703 mnode = ctx.manifestnode()
704 self.ui.write(_("manifest: %d:%s\n") %
704 self.ui.write(_("manifest: %d:%s\n") %
705 (self.repo.manifest.rev(mnode), hex(mnode)),
705 (self.repo.manifest.rev(mnode), hex(mnode)),
706 label='ui.debug log.manifest')
706 label='ui.debug log.manifest')
707 self.ui.write(_("user: %s\n") % ctx.user(),
707 self.ui.write(_("user: %s\n") % ctx.user(),
708 label='log.user')
708 label='log.user')
709 self.ui.write(_("date: %s\n") % date,
709 self.ui.write(_("date: %s\n") % date,
710 label='log.date')
710 label='log.date')
711
711
712 if self.ui.debugflag:
712 if self.ui.debugflag:
713 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
713 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
714 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
714 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
715 files):
715 files):
716 if value:
716 if value:
717 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
717 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
718 label='ui.debug log.files')
718 label='ui.debug log.files')
719 elif ctx.files() and self.ui.verbose:
719 elif ctx.files() and self.ui.verbose:
720 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
720 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
721 label='ui.note log.files')
721 label='ui.note log.files')
722 if copies and self.ui.verbose:
722 if copies and self.ui.verbose:
723 copies = ['%s (%s)' % c for c in copies]
723 copies = ['%s (%s)' % c for c in copies]
724 self.ui.write(_("copies: %s\n") % ' '.join(copies),
724 self.ui.write(_("copies: %s\n") % ' '.join(copies),
725 label='ui.note log.copies')
725 label='ui.note log.copies')
726
726
727 extra = ctx.extra()
727 extra = ctx.extra()
728 if extra and self.ui.debugflag:
728 if extra and self.ui.debugflag:
729 for key, value in sorted(extra.items()):
729 for key, value in sorted(extra.items()):
730 self.ui.write(_("extra: %s=%s\n")
730 self.ui.write(_("extra: %s=%s\n")
731 % (key, value.encode('string_escape')),
731 % (key, value.encode('string_escape')),
732 label='ui.debug log.extra')
732 label='ui.debug log.extra')
733
733
734 description = ctx.description().strip()
734 description = ctx.description().strip()
735 if description:
735 if description:
736 if self.ui.verbose:
736 if self.ui.verbose:
737 self.ui.write(_("description:\n"),
737 self.ui.write(_("description:\n"),
738 label='ui.note log.description')
738 label='ui.note log.description')
739 self.ui.write(description,
739 self.ui.write(description,
740 label='ui.note log.description')
740 label='ui.note log.description')
741 self.ui.write("\n\n")
741 self.ui.write("\n\n")
742 else:
742 else:
743 self.ui.write(_("summary: %s\n") %
743 self.ui.write(_("summary: %s\n") %
744 description.splitlines()[0],
744 description.splitlines()[0],
745 label='log.summary')
745 label='log.summary')
746 self.ui.write("\n")
746 self.ui.write("\n")
747
747
748 self.showpatch(changenode, matchfn)
748 self.showpatch(changenode, matchfn)
749
749
750 def showpatch(self, node, matchfn):
750 def showpatch(self, node, matchfn):
751 if not matchfn:
751 if not matchfn:
752 matchfn = self.patch
752 matchfn = self.patch
753 if matchfn:
753 if matchfn:
754 stat = self.diffopts.get('stat')
754 stat = self.diffopts.get('stat')
755 diff = self.diffopts.get('patch')
755 diff = self.diffopts.get('patch')
756 diffopts = patch.diffopts(self.ui, self.diffopts)
756 diffopts = patch.diffopts(self.ui, self.diffopts)
757 prev = self.repo.changelog.parents(node)[0]
757 prev = self.repo.changelog.parents(node)[0]
758 if stat:
758 if stat:
759 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
759 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
760 match=matchfn, stat=True)
760 match=matchfn, stat=True)
761 if diff:
761 if diff:
762 if stat:
762 if stat:
763 self.ui.write("\n")
763 self.ui.write("\n")
764 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
764 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
765 match=matchfn, stat=False)
765 match=matchfn, stat=False)
766 self.ui.write("\n")
766 self.ui.write("\n")
767
767
768 def _meaningful_parentrevs(self, log, rev):
768 def _meaningful_parentrevs(self, log, rev):
769 """Return list of meaningful (or all if debug) parentrevs for rev.
769 """Return list of meaningful (or all if debug) parentrevs for rev.
770
770
771 For merges (two non-nullrev revisions) both parents are meaningful.
771 For merges (two non-nullrev revisions) both parents are meaningful.
772 Otherwise the first parent revision is considered meaningful if it
772 Otherwise the first parent revision is considered meaningful if it
773 is not the preceding revision.
773 is not the preceding revision.
774 """
774 """
775 parents = log.parentrevs(rev)
775 parents = log.parentrevs(rev)
776 if not self.ui.debugflag and parents[1] == nullrev:
776 if not self.ui.debugflag and parents[1] == nullrev:
777 if parents[0] >= rev - 1:
777 if parents[0] >= rev - 1:
778 parents = []
778 parents = []
779 else:
779 else:
780 parents = [parents[0]]
780 parents = [parents[0]]
781 return parents
781 return parents
782
782
783
783
784 class changeset_templater(changeset_printer):
784 class changeset_templater(changeset_printer):
785 '''format changeset information.'''
785 '''format changeset information.'''
786
786
787 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
787 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
788 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
788 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
789 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
789 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
790 defaulttempl = {
790 defaulttempl = {
791 'parent': '{rev}:{node|formatnode} ',
791 'parent': '{rev}:{node|formatnode} ',
792 'manifest': '{rev}:{node|formatnode}',
792 'manifest': '{rev}:{node|formatnode}',
793 'file_copy': '{name} ({source})',
793 'file_copy': '{name} ({source})',
794 'extra': '{key}={value|stringescape}'
794 'extra': '{key}={value|stringescape}'
795 }
795 }
796 # filecopy is preserved for compatibility reasons
796 # filecopy is preserved for compatibility reasons
797 defaulttempl['filecopy'] = defaulttempl['file_copy']
797 defaulttempl['filecopy'] = defaulttempl['file_copy']
798 self.t = templater.templater(mapfile, {'formatnode': formatnode},
798 self.t = templater.templater(mapfile, {'formatnode': formatnode},
799 cache=defaulttempl)
799 cache=defaulttempl)
800 self.cache = {}
800 self.cache = {}
801
801
802 def use_template(self, t):
802 def use_template(self, t):
803 '''set template string to use'''
803 '''set template string to use'''
804 self.t.cache['changeset'] = t
804 self.t.cache['changeset'] = t
805
805
806 def _meaningful_parentrevs(self, ctx):
806 def _meaningful_parentrevs(self, ctx):
807 """Return list of meaningful (or all if debug) parentrevs for rev.
807 """Return list of meaningful (or all if debug) parentrevs for rev.
808 """
808 """
809 parents = ctx.parents()
809 parents = ctx.parents()
810 if len(parents) > 1:
810 if len(parents) > 1:
811 return parents
811 return parents
812 if self.ui.debugflag:
812 if self.ui.debugflag:
813 return [parents[0], self.repo['null']]
813 return [parents[0], self.repo['null']]
814 if parents[0].rev() >= ctx.rev() - 1:
814 if parents[0].rev() >= ctx.rev() - 1:
815 return []
815 return []
816 return parents
816 return parents
817
817
818 def _show(self, ctx, copies, matchfn, props):
818 def _show(self, ctx, copies, matchfn, props):
819 '''show a single changeset or file revision'''
819 '''show a single changeset or file revision'''
820
820
821 showlist = templatekw.showlist
821 showlist = templatekw.showlist
822
822
823 # showparents() behaviour depends on ui trace level which
823 # showparents() behaviour depends on ui trace level which
824 # causes unexpected behaviours at templating level and makes
824 # causes unexpected behaviours at templating level and makes
825 # it harder to extract it in a standalone function. Its
825 # it harder to extract it in a standalone function. Its
826 # behaviour cannot be changed so leave it here for now.
826 # behaviour cannot be changed so leave it here for now.
827 def showparents(**args):
827 def showparents(**args):
828 ctx = args['ctx']
828 ctx = args['ctx']
829 parents = [[('rev', p.rev()), ('node', p.hex())]
829 parents = [[('rev', p.rev()), ('node', p.hex())]
830 for p in self._meaningful_parentrevs(ctx)]
830 for p in self._meaningful_parentrevs(ctx)]
831 return showlist('parent', parents, **args)
831 return showlist('parent', parents, **args)
832
832
833 props = props.copy()
833 props = props.copy()
834 props.update(templatekw.keywords)
834 props.update(templatekw.keywords)
835 props['parents'] = showparents
835 props['parents'] = showparents
836 props['templ'] = self.t
836 props['templ'] = self.t
837 props['ctx'] = ctx
837 props['ctx'] = ctx
838 props['repo'] = self.repo
838 props['repo'] = self.repo
839 props['revcache'] = {'copies': copies}
839 props['revcache'] = {'copies': copies}
840 props['cache'] = self.cache
840 props['cache'] = self.cache
841
841
842 # find correct templates for current mode
842 # find correct templates for current mode
843
843
844 tmplmodes = [
844 tmplmodes = [
845 (True, None),
845 (True, None),
846 (self.ui.verbose, 'verbose'),
846 (self.ui.verbose, 'verbose'),
847 (self.ui.quiet, 'quiet'),
847 (self.ui.quiet, 'quiet'),
848 (self.ui.debugflag, 'debug'),
848 (self.ui.debugflag, 'debug'),
849 ]
849 ]
850
850
851 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
851 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
852 for mode, postfix in tmplmodes:
852 for mode, postfix in tmplmodes:
853 for type in types:
853 for type in types:
854 cur = postfix and ('%s_%s' % (type, postfix)) or type
854 cur = postfix and ('%s_%s' % (type, postfix)) or type
855 if mode and cur in self.t:
855 if mode and cur in self.t:
856 types[type] = cur
856 types[type] = cur
857
857
858 try:
858 try:
859
859
860 # write header
860 # write header
861 if types['header']:
861 if types['header']:
862 h = templater.stringify(self.t(types['header'], **props))
862 h = templater.stringify(self.t(types['header'], **props))
863 if self.buffered:
863 if self.buffered:
864 self.header[ctx.rev()] = h
864 self.header[ctx.rev()] = h
865 else:
865 else:
866 if self.lastheader != h:
866 if self.lastheader != h:
867 self.lastheader = h
867 self.lastheader = h
868 self.ui.write(h)
868 self.ui.write(h)
869
869
870 # write changeset metadata, then patch if requested
870 # write changeset metadata, then patch if requested
871 key = types['changeset']
871 key = types['changeset']
872 self.ui.write(templater.stringify(self.t(key, **props)))
872 self.ui.write(templater.stringify(self.t(key, **props)))
873 self.showpatch(ctx.node(), matchfn)
873 self.showpatch(ctx.node(), matchfn)
874
874
875 if types['footer']:
875 if types['footer']:
876 if not self.footer:
876 if not self.footer:
877 self.footer = templater.stringify(self.t(types['footer'],
877 self.footer = templater.stringify(self.t(types['footer'],
878 **props))
878 **props))
879
879
880 except KeyError, inst:
880 except KeyError, inst:
881 msg = _("%s: no key named '%s'")
881 msg = _("%s: no key named '%s'")
882 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
882 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
883 except SyntaxError, inst:
883 except SyntaxError, inst:
884 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
884 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
885
885
886 def show_changeset(ui, repo, opts, buffered=False):
886 def show_changeset(ui, repo, opts, buffered=False):
887 """show one changeset using template or regular display.
887 """show one changeset using template or regular display.
888
888
889 Display format will be the first non-empty hit of:
889 Display format will be the first non-empty hit of:
890 1. option 'template'
890 1. option 'template'
891 2. option 'style'
891 2. option 'style'
892 3. [ui] setting 'logtemplate'
892 3. [ui] setting 'logtemplate'
893 4. [ui] setting 'style'
893 4. [ui] setting 'style'
894 If all of these values are either the unset or the empty string,
894 If all of these values are either the unset or the empty string,
895 regular display via changeset_printer() is done.
895 regular display via changeset_printer() is done.
896 """
896 """
897 # options
897 # options
898 patch = False
898 patch = False
899 if opts.get('patch') or opts.get('stat'):
899 if opts.get('patch') or opts.get('stat'):
900 patch = scmutil.matchall(repo)
900 patch = scmutil.matchall(repo)
901
901
902 tmpl = opts.get('template')
902 tmpl = opts.get('template')
903 style = None
903 style = None
904 if tmpl:
904 if tmpl:
905 tmpl = templater.parsestring(tmpl, quoted=False)
905 tmpl = templater.parsestring(tmpl, quoted=False)
906 else:
906 else:
907 style = opts.get('style')
907 style = opts.get('style')
908
908
909 # ui settings
909 # ui settings
910 if not (tmpl or style):
910 if not (tmpl or style):
911 tmpl = ui.config('ui', 'logtemplate')
911 tmpl = ui.config('ui', 'logtemplate')
912 if tmpl:
912 if tmpl:
913 tmpl = templater.parsestring(tmpl)
913 try:
914 tmpl = templater.parsestring(tmpl)
915 except SyntaxError:
916 tmpl = templater.parsestring(tmpl, quoted=False)
914 else:
917 else:
915 style = util.expandpath(ui.config('ui', 'style', ''))
918 style = util.expandpath(ui.config('ui', 'style', ''))
916
919
917 if not (tmpl or style):
920 if not (tmpl or style):
918 return changeset_printer(ui, repo, patch, opts, buffered)
921 return changeset_printer(ui, repo, patch, opts, buffered)
919
922
920 mapfile = None
923 mapfile = None
921 if style and not tmpl:
924 if style and not tmpl:
922 mapfile = style
925 mapfile = style
923 if not os.path.split(mapfile)[0]:
926 if not os.path.split(mapfile)[0]:
924 mapname = (templater.templatepath('map-cmdline.' + mapfile)
927 mapname = (templater.templatepath('map-cmdline.' + mapfile)
925 or templater.templatepath(mapfile))
928 or templater.templatepath(mapfile))
926 if mapname:
929 if mapname:
927 mapfile = mapname
930 mapfile = mapname
928
931
929 try:
932 try:
930 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
933 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
931 except SyntaxError, inst:
934 except SyntaxError, inst:
932 raise util.Abort(inst.args[0])
935 raise util.Abort(inst.args[0])
933 if tmpl:
936 if tmpl:
934 t.use_template(tmpl)
937 t.use_template(tmpl)
935 return t
938 return t
936
939
937 def finddate(ui, repo, date):
940 def finddate(ui, repo, date):
938 """Find the tipmost changeset that matches the given date spec"""
941 """Find the tipmost changeset that matches the given date spec"""
939
942
940 df = util.matchdate(date)
943 df = util.matchdate(date)
941 m = scmutil.matchall(repo)
944 m = scmutil.matchall(repo)
942 results = {}
945 results = {}
943
946
944 def prep(ctx, fns):
947 def prep(ctx, fns):
945 d = ctx.date()
948 d = ctx.date()
946 if df(d[0]):
949 if df(d[0]):
947 results[ctx.rev()] = d
950 results[ctx.rev()] = d
948
951
949 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
952 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
950 rev = ctx.rev()
953 rev = ctx.rev()
951 if rev in results:
954 if rev in results:
952 ui.status(_("Found revision %s from %s\n") %
955 ui.status(_("Found revision %s from %s\n") %
953 (rev, util.datestr(results[rev])))
956 (rev, util.datestr(results[rev])))
954 return str(rev)
957 return str(rev)
955
958
956 raise util.Abort(_("revision matching date not found"))
959 raise util.Abort(_("revision matching date not found"))
957
960
958 def walkchangerevs(repo, match, opts, prepare):
961 def walkchangerevs(repo, match, opts, prepare):
959 '''Iterate over files and the revs in which they changed.
962 '''Iterate over files and the revs in which they changed.
960
963
961 Callers most commonly need to iterate backwards over the history
964 Callers most commonly need to iterate backwards over the history
962 in which they are interested. Doing so has awful (quadratic-looking)
965 in which they are interested. Doing so has awful (quadratic-looking)
963 performance, so we use iterators in a "windowed" way.
966 performance, so we use iterators in a "windowed" way.
964
967
965 We walk a window of revisions in the desired order. Within the
968 We walk a window of revisions in the desired order. Within the
966 window, we first walk forwards to gather data, then in the desired
969 window, we first walk forwards to gather data, then in the desired
967 order (usually backwards) to display it.
970 order (usually backwards) to display it.
968
971
969 This function returns an iterator yielding contexts. Before
972 This function returns an iterator yielding contexts. Before
970 yielding each context, the iterator will first call the prepare
973 yielding each context, the iterator will first call the prepare
971 function on each context in the window in forward order.'''
974 function on each context in the window in forward order.'''
972
975
973 def increasing_windows(start, end, windowsize=8, sizelimit=512):
976 def increasing_windows(start, end, windowsize=8, sizelimit=512):
974 if start < end:
977 if start < end:
975 while start < end:
978 while start < end:
976 yield start, min(windowsize, end - start)
979 yield start, min(windowsize, end - start)
977 start += windowsize
980 start += windowsize
978 if windowsize < sizelimit:
981 if windowsize < sizelimit:
979 windowsize *= 2
982 windowsize *= 2
980 else:
983 else:
981 while start > end:
984 while start > end:
982 yield start, min(windowsize, start - end - 1)
985 yield start, min(windowsize, start - end - 1)
983 start -= windowsize
986 start -= windowsize
984 if windowsize < sizelimit:
987 if windowsize < sizelimit:
985 windowsize *= 2
988 windowsize *= 2
986
989
987 follow = opts.get('follow') or opts.get('follow_first')
990 follow = opts.get('follow') or opts.get('follow_first')
988
991
989 if not len(repo):
992 if not len(repo):
990 return []
993 return []
991
994
992 if follow:
995 if follow:
993 defrange = '%s:0' % repo['.'].rev()
996 defrange = '%s:0' % repo['.'].rev()
994 else:
997 else:
995 defrange = '-1:0'
998 defrange = '-1:0'
996 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
999 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
997 if not revs:
1000 if not revs:
998 return []
1001 return []
999 wanted = set()
1002 wanted = set()
1000 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1003 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1001 fncache = {}
1004 fncache = {}
1002 change = repo.changectx
1005 change = repo.changectx
1003
1006
1004 # First step is to fill wanted, the set of revisions that we want to yield.
1007 # First step is to fill wanted, the set of revisions that we want to yield.
1005 # When it does not induce extra cost, we also fill fncache for revisions in
1008 # When it does not induce extra cost, we also fill fncache for revisions in
1006 # wanted: a cache of filenames that were changed (ctx.files()) and that
1009 # wanted: a cache of filenames that were changed (ctx.files()) and that
1007 # match the file filtering conditions.
1010 # match the file filtering conditions.
1008
1011
1009 if not slowpath and not match.files():
1012 if not slowpath and not match.files():
1010 # No files, no patterns. Display all revs.
1013 # No files, no patterns. Display all revs.
1011 wanted = set(revs)
1014 wanted = set(revs)
1012 copies = []
1015 copies = []
1013
1016
1014 if not slowpath and match.files():
1017 if not slowpath and match.files():
1015 # We only have to read through the filelog to find wanted revisions
1018 # We only have to read through the filelog to find wanted revisions
1016
1019
1017 minrev, maxrev = min(revs), max(revs)
1020 minrev, maxrev = min(revs), max(revs)
1018 def filerevgen(filelog, last):
1021 def filerevgen(filelog, last):
1019 """
1022 """
1020 Only files, no patterns. Check the history of each file.
1023 Only files, no patterns. Check the history of each file.
1021
1024
1022 Examines filelog entries within minrev, maxrev linkrev range
1025 Examines filelog entries within minrev, maxrev linkrev range
1023 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1026 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1024 tuples in backwards order
1027 tuples in backwards order
1025 """
1028 """
1026 cl_count = len(repo)
1029 cl_count = len(repo)
1027 revs = []
1030 revs = []
1028 for j in xrange(0, last + 1):
1031 for j in xrange(0, last + 1):
1029 linkrev = filelog.linkrev(j)
1032 linkrev = filelog.linkrev(j)
1030 if linkrev < minrev:
1033 if linkrev < minrev:
1031 continue
1034 continue
1032 # only yield rev for which we have the changelog, it can
1035 # only yield rev for which we have the changelog, it can
1033 # happen while doing "hg log" during a pull or commit
1036 # happen while doing "hg log" during a pull or commit
1034 if linkrev >= cl_count:
1037 if linkrev >= cl_count:
1035 break
1038 break
1036
1039
1037 parentlinkrevs = []
1040 parentlinkrevs = []
1038 for p in filelog.parentrevs(j):
1041 for p in filelog.parentrevs(j):
1039 if p != nullrev:
1042 if p != nullrev:
1040 parentlinkrevs.append(filelog.linkrev(p))
1043 parentlinkrevs.append(filelog.linkrev(p))
1041 n = filelog.node(j)
1044 n = filelog.node(j)
1042 revs.append((linkrev, parentlinkrevs,
1045 revs.append((linkrev, parentlinkrevs,
1043 follow and filelog.renamed(n)))
1046 follow and filelog.renamed(n)))
1044
1047
1045 return reversed(revs)
1048 return reversed(revs)
1046 def iterfiles():
1049 def iterfiles():
1047 pctx = repo['.']
1050 pctx = repo['.']
1048 for filename in match.files():
1051 for filename in match.files():
1049 if follow:
1052 if follow:
1050 if filename not in pctx:
1053 if filename not in pctx:
1051 raise util.Abort(_('cannot follow file not in parent '
1054 raise util.Abort(_('cannot follow file not in parent '
1052 'revision: "%s"') % filename)
1055 'revision: "%s"') % filename)
1053 yield filename, pctx[filename].filenode()
1056 yield filename, pctx[filename].filenode()
1054 else:
1057 else:
1055 yield filename, None
1058 yield filename, None
1056 for filename_node in copies:
1059 for filename_node in copies:
1057 yield filename_node
1060 yield filename_node
1058 for file_, node in iterfiles():
1061 for file_, node in iterfiles():
1059 filelog = repo.file(file_)
1062 filelog = repo.file(file_)
1060 if not len(filelog):
1063 if not len(filelog):
1061 if node is None:
1064 if node is None:
1062 # A zero count may be a directory or deleted file, so
1065 # A zero count may be a directory or deleted file, so
1063 # try to find matching entries on the slow path.
1066 # try to find matching entries on the slow path.
1064 if follow:
1067 if follow:
1065 raise util.Abort(
1068 raise util.Abort(
1066 _('cannot follow nonexistent file: "%s"') % file_)
1069 _('cannot follow nonexistent file: "%s"') % file_)
1067 slowpath = True
1070 slowpath = True
1068 break
1071 break
1069 else:
1072 else:
1070 continue
1073 continue
1071
1074
1072 if node is None:
1075 if node is None:
1073 last = len(filelog) - 1
1076 last = len(filelog) - 1
1074 else:
1077 else:
1075 last = filelog.rev(node)
1078 last = filelog.rev(node)
1076
1079
1077
1080
1078 # keep track of all ancestors of the file
1081 # keep track of all ancestors of the file
1079 ancestors = set([filelog.linkrev(last)])
1082 ancestors = set([filelog.linkrev(last)])
1080
1083
1081 # iterate from latest to oldest revision
1084 # iterate from latest to oldest revision
1082 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1085 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1083 if not follow:
1086 if not follow:
1084 if rev > maxrev:
1087 if rev > maxrev:
1085 continue
1088 continue
1086 else:
1089 else:
1087 # Note that last might not be the first interesting
1090 # Note that last might not be the first interesting
1088 # rev to us:
1091 # rev to us:
1089 # if the file has been changed after maxrev, we'll
1092 # if the file has been changed after maxrev, we'll
1090 # have linkrev(last) > maxrev, and we still need
1093 # have linkrev(last) > maxrev, and we still need
1091 # to explore the file graph
1094 # to explore the file graph
1092 if rev not in ancestors:
1095 if rev not in ancestors:
1093 continue
1096 continue
1094 # XXX insert 1327 fix here
1097 # XXX insert 1327 fix here
1095 if flparentlinkrevs:
1098 if flparentlinkrevs:
1096 ancestors.update(flparentlinkrevs)
1099 ancestors.update(flparentlinkrevs)
1097
1100
1098 fncache.setdefault(rev, []).append(file_)
1101 fncache.setdefault(rev, []).append(file_)
1099 wanted.add(rev)
1102 wanted.add(rev)
1100 if copied:
1103 if copied:
1101 copies.append(copied)
1104 copies.append(copied)
1102 if slowpath:
1105 if slowpath:
1103 # We have to read the changelog to match filenames against
1106 # We have to read the changelog to match filenames against
1104 # changed files
1107 # changed files
1105
1108
1106 if follow:
1109 if follow:
1107 raise util.Abort(_('can only follow copies/renames for explicit '
1110 raise util.Abort(_('can only follow copies/renames for explicit '
1108 'filenames'))
1111 'filenames'))
1109
1112
1110 # The slow path checks files modified in every changeset.
1113 # The slow path checks files modified in every changeset.
1111 for i in sorted(revs):
1114 for i in sorted(revs):
1112 ctx = change(i)
1115 ctx = change(i)
1113 matches = filter(match, ctx.files())
1116 matches = filter(match, ctx.files())
1114 if matches:
1117 if matches:
1115 fncache[i] = matches
1118 fncache[i] = matches
1116 wanted.add(i)
1119 wanted.add(i)
1117
1120
1118 class followfilter(object):
1121 class followfilter(object):
1119 def __init__(self, onlyfirst=False):
1122 def __init__(self, onlyfirst=False):
1120 self.startrev = nullrev
1123 self.startrev = nullrev
1121 self.roots = set()
1124 self.roots = set()
1122 self.onlyfirst = onlyfirst
1125 self.onlyfirst = onlyfirst
1123
1126
1124 def match(self, rev):
1127 def match(self, rev):
1125 def realparents(rev):
1128 def realparents(rev):
1126 if self.onlyfirst:
1129 if self.onlyfirst:
1127 return repo.changelog.parentrevs(rev)[0:1]
1130 return repo.changelog.parentrevs(rev)[0:1]
1128 else:
1131 else:
1129 return filter(lambda x: x != nullrev,
1132 return filter(lambda x: x != nullrev,
1130 repo.changelog.parentrevs(rev))
1133 repo.changelog.parentrevs(rev))
1131
1134
1132 if self.startrev == nullrev:
1135 if self.startrev == nullrev:
1133 self.startrev = rev
1136 self.startrev = rev
1134 return True
1137 return True
1135
1138
1136 if rev > self.startrev:
1139 if rev > self.startrev:
1137 # forward: all descendants
1140 # forward: all descendants
1138 if not self.roots:
1141 if not self.roots:
1139 self.roots.add(self.startrev)
1142 self.roots.add(self.startrev)
1140 for parent in realparents(rev):
1143 for parent in realparents(rev):
1141 if parent in self.roots:
1144 if parent in self.roots:
1142 self.roots.add(rev)
1145 self.roots.add(rev)
1143 return True
1146 return True
1144 else:
1147 else:
1145 # backwards: all parents
1148 # backwards: all parents
1146 if not self.roots:
1149 if not self.roots:
1147 self.roots.update(realparents(self.startrev))
1150 self.roots.update(realparents(self.startrev))
1148 if rev in self.roots:
1151 if rev in self.roots:
1149 self.roots.remove(rev)
1152 self.roots.remove(rev)
1150 self.roots.update(realparents(rev))
1153 self.roots.update(realparents(rev))
1151 return True
1154 return True
1152
1155
1153 return False
1156 return False
1154
1157
1155 # it might be worthwhile to do this in the iterator if the rev range
1158 # it might be worthwhile to do this in the iterator if the rev range
1156 # is descending and the prune args are all within that range
1159 # is descending and the prune args are all within that range
1157 for rev in opts.get('prune', ()):
1160 for rev in opts.get('prune', ()):
1158 rev = repo[rev].rev()
1161 rev = repo[rev].rev()
1159 ff = followfilter()
1162 ff = followfilter()
1160 stop = min(revs[0], revs[-1])
1163 stop = min(revs[0], revs[-1])
1161 for x in xrange(rev, stop - 1, -1):
1164 for x in xrange(rev, stop - 1, -1):
1162 if ff.match(x):
1165 if ff.match(x):
1163 wanted.discard(x)
1166 wanted.discard(x)
1164
1167
1165 # Now that wanted is correctly initialized, we can iterate over the
1168 # Now that wanted is correctly initialized, we can iterate over the
1166 # revision range, yielding only revisions in wanted.
1169 # revision range, yielding only revisions in wanted.
1167 def iterate():
1170 def iterate():
1168 if follow and not match.files():
1171 if follow and not match.files():
1169 ff = followfilter(onlyfirst=opts.get('follow_first'))
1172 ff = followfilter(onlyfirst=opts.get('follow_first'))
1170 def want(rev):
1173 def want(rev):
1171 return ff.match(rev) and rev in wanted
1174 return ff.match(rev) and rev in wanted
1172 else:
1175 else:
1173 def want(rev):
1176 def want(rev):
1174 return rev in wanted
1177 return rev in wanted
1175
1178
1176 for i, window in increasing_windows(0, len(revs)):
1179 for i, window in increasing_windows(0, len(revs)):
1177 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1180 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1178 for rev in sorted(nrevs):
1181 for rev in sorted(nrevs):
1179 fns = fncache.get(rev)
1182 fns = fncache.get(rev)
1180 ctx = change(rev)
1183 ctx = change(rev)
1181 if not fns:
1184 if not fns:
1182 def fns_generator():
1185 def fns_generator():
1183 for f in ctx.files():
1186 for f in ctx.files():
1184 if match(f):
1187 if match(f):
1185 yield f
1188 yield f
1186 fns = fns_generator()
1189 fns = fns_generator()
1187 prepare(ctx, fns)
1190 prepare(ctx, fns)
1188 for rev in nrevs:
1191 for rev in nrevs:
1189 yield change(rev)
1192 yield change(rev)
1190 return iterate()
1193 return iterate()
1191
1194
1192 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1195 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1193 join = lambda f: os.path.join(prefix, f)
1196 join = lambda f: os.path.join(prefix, f)
1194 bad = []
1197 bad = []
1195 oldbad = match.bad
1198 oldbad = match.bad
1196 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1199 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1197 names = []
1200 names = []
1198 wctx = repo[None]
1201 wctx = repo[None]
1199 cca = None
1202 cca = None
1200 abort, warn = scmutil.checkportabilityalert(ui)
1203 abort, warn = scmutil.checkportabilityalert(ui)
1201 if abort or warn:
1204 if abort or warn:
1202 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1205 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1203 for f in repo.walk(match):
1206 for f in repo.walk(match):
1204 exact = match.exact(f)
1207 exact = match.exact(f)
1205 if exact or not explicitonly and f not in repo.dirstate:
1208 if exact or not explicitonly and f not in repo.dirstate:
1206 if cca:
1209 if cca:
1207 cca(f)
1210 cca(f)
1208 names.append(f)
1211 names.append(f)
1209 if ui.verbose or not exact:
1212 if ui.verbose or not exact:
1210 ui.status(_('adding %s\n') % match.rel(join(f)))
1213 ui.status(_('adding %s\n') % match.rel(join(f)))
1211
1214
1212 for subpath in wctx.substate:
1215 for subpath in wctx.substate:
1213 sub = wctx.sub(subpath)
1216 sub = wctx.sub(subpath)
1214 try:
1217 try:
1215 submatch = matchmod.narrowmatcher(subpath, match)
1218 submatch = matchmod.narrowmatcher(subpath, match)
1216 if listsubrepos:
1219 if listsubrepos:
1217 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1220 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1218 False))
1221 False))
1219 else:
1222 else:
1220 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1223 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1221 True))
1224 True))
1222 except error.LookupError:
1225 except error.LookupError:
1223 ui.status(_("skipping missing subrepository: %s\n")
1226 ui.status(_("skipping missing subrepository: %s\n")
1224 % join(subpath))
1227 % join(subpath))
1225
1228
1226 if not dryrun:
1229 if not dryrun:
1227 rejected = wctx.add(names, prefix)
1230 rejected = wctx.add(names, prefix)
1228 bad.extend(f for f in rejected if f in match.files())
1231 bad.extend(f for f in rejected if f in match.files())
1229 return bad
1232 return bad
1230
1233
1231 def forget(ui, repo, match, prefix, explicitonly):
1234 def forget(ui, repo, match, prefix, explicitonly):
1232 join = lambda f: os.path.join(prefix, f)
1235 join = lambda f: os.path.join(prefix, f)
1233 bad = []
1236 bad = []
1234 oldbad = match.bad
1237 oldbad = match.bad
1235 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1238 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1236 wctx = repo[None]
1239 wctx = repo[None]
1237 forgot = []
1240 forgot = []
1238 s = repo.status(match=match, clean=True)
1241 s = repo.status(match=match, clean=True)
1239 forget = sorted(s[0] + s[1] + s[3] + s[6])
1242 forget = sorted(s[0] + s[1] + s[3] + s[6])
1240 if explicitonly:
1243 if explicitonly:
1241 forget = [f for f in forget if match.exact(f)]
1244 forget = [f for f in forget if match.exact(f)]
1242
1245
1243 for subpath in wctx.substate:
1246 for subpath in wctx.substate:
1244 sub = wctx.sub(subpath)
1247 sub = wctx.sub(subpath)
1245 try:
1248 try:
1246 submatch = matchmod.narrowmatcher(subpath, match)
1249 submatch = matchmod.narrowmatcher(subpath, match)
1247 subbad, subforgot = sub.forget(ui, submatch, prefix)
1250 subbad, subforgot = sub.forget(ui, submatch, prefix)
1248 bad.extend([subpath + '/' + f for f in subbad])
1251 bad.extend([subpath + '/' + f for f in subbad])
1249 forgot.extend([subpath + '/' + f for f in subforgot])
1252 forgot.extend([subpath + '/' + f for f in subforgot])
1250 except error.LookupError:
1253 except error.LookupError:
1251 ui.status(_("skipping missing subrepository: %s\n")
1254 ui.status(_("skipping missing subrepository: %s\n")
1252 % join(subpath))
1255 % join(subpath))
1253
1256
1254 if not explicitonly:
1257 if not explicitonly:
1255 for f in match.files():
1258 for f in match.files():
1256 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1259 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1257 if f not in forgot:
1260 if f not in forgot:
1258 if os.path.exists(match.rel(join(f))):
1261 if os.path.exists(match.rel(join(f))):
1259 ui.warn(_('not removing %s: '
1262 ui.warn(_('not removing %s: '
1260 'file is already untracked\n')
1263 'file is already untracked\n')
1261 % match.rel(join(f)))
1264 % match.rel(join(f)))
1262 bad.append(f)
1265 bad.append(f)
1263
1266
1264 for f in forget:
1267 for f in forget:
1265 if ui.verbose or not match.exact(f):
1268 if ui.verbose or not match.exact(f):
1266 ui.status(_('removing %s\n') % match.rel(join(f)))
1269 ui.status(_('removing %s\n') % match.rel(join(f)))
1267
1270
1268 rejected = wctx.forget(forget, prefix)
1271 rejected = wctx.forget(forget, prefix)
1269 bad.extend(f for f in rejected if f in match.files())
1272 bad.extend(f for f in rejected if f in match.files())
1270 forgot.extend(forget)
1273 forgot.extend(forget)
1271 return bad, forgot
1274 return bad, forgot
1272
1275
1273 def duplicatecopies(repo, rev, p1):
1276 def duplicatecopies(repo, rev, p1):
1274 "Reproduce copies found in the source revision in the dirstate for grafts"
1277 "Reproduce copies found in the source revision in the dirstate for grafts"
1275 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1278 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1276 repo.dirstate.copy(src, dst)
1279 repo.dirstate.copy(src, dst)
1277
1280
1278 def commit(ui, repo, commitfunc, pats, opts):
1281 def commit(ui, repo, commitfunc, pats, opts):
1279 '''commit the specified files or all outstanding changes'''
1282 '''commit the specified files or all outstanding changes'''
1280 date = opts.get('date')
1283 date = opts.get('date')
1281 if date:
1284 if date:
1282 opts['date'] = util.parsedate(date)
1285 opts['date'] = util.parsedate(date)
1283 message = logmessage(ui, opts)
1286 message = logmessage(ui, opts)
1284
1287
1285 # extract addremove carefully -- this function can be called from a command
1288 # extract addremove carefully -- this function can be called from a command
1286 # that doesn't support addremove
1289 # that doesn't support addremove
1287 if opts.get('addremove'):
1290 if opts.get('addremove'):
1288 scmutil.addremove(repo, pats, opts)
1291 scmutil.addremove(repo, pats, opts)
1289
1292
1290 return commitfunc(ui, repo, message,
1293 return commitfunc(ui, repo, message,
1291 scmutil.match(repo[None], pats, opts), opts)
1294 scmutil.match(repo[None], pats, opts), opts)
1292
1295
1293 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1296 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1294 ui.note(_('amending changeset %s\n') % old)
1297 ui.note(_('amending changeset %s\n') % old)
1295 base = old.p1()
1298 base = old.p1()
1296
1299
1297 wlock = repo.wlock()
1300 wlock = repo.wlock()
1298 try:
1301 try:
1299 # First, do a regular commit to record all changes in the working
1302 # First, do a regular commit to record all changes in the working
1300 # directory (if there are any)
1303 # directory (if there are any)
1301 node = commit(ui, repo, commitfunc, pats, opts)
1304 node = commit(ui, repo, commitfunc, pats, opts)
1302 ctx = repo[node]
1305 ctx = repo[node]
1303
1306
1304 # Participating changesets:
1307 # Participating changesets:
1305 #
1308 #
1306 # node/ctx o - new (intermediate) commit that contains changes from
1309 # node/ctx o - new (intermediate) commit that contains changes from
1307 # | working dir to go into amending commit (or a workingctx
1310 # | working dir to go into amending commit (or a workingctx
1308 # | if there were no changes)
1311 # | if there were no changes)
1309 # |
1312 # |
1310 # old o - changeset to amend
1313 # old o - changeset to amend
1311 # |
1314 # |
1312 # base o - parent of amending changeset
1315 # base o - parent of amending changeset
1313
1316
1314 # Update extra dict from amended commit (e.g. to preserve graft source)
1317 # Update extra dict from amended commit (e.g. to preserve graft source)
1315 extra.update(old.extra())
1318 extra.update(old.extra())
1316
1319
1317 # Also update it from the intermediate commit or from the wctx
1320 # Also update it from the intermediate commit or from the wctx
1318 extra.update(ctx.extra())
1321 extra.update(ctx.extra())
1319
1322
1320 files = set(old.files())
1323 files = set(old.files())
1321
1324
1322 # Second, we use either the commit we just did, or if there were no
1325 # Second, we use either the commit we just did, or if there were no
1323 # changes the parent of the working directory as the version of the
1326 # changes the parent of the working directory as the version of the
1324 # files in the final amend commit
1327 # files in the final amend commit
1325 if node:
1328 if node:
1326 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1329 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1327
1330
1328 user = ctx.user()
1331 user = ctx.user()
1329 date = ctx.date()
1332 date = ctx.date()
1330 message = ctx.description()
1333 message = ctx.description()
1331 # Recompute copies (avoid recording a -> b -> a)
1334 # Recompute copies (avoid recording a -> b -> a)
1332 copied = copies.pathcopies(base, ctx)
1335 copied = copies.pathcopies(base, ctx)
1333
1336
1334 # Prune files which were reverted by the updates: if old introduced
1337 # Prune files which were reverted by the updates: if old introduced
1335 # file X and our intermediate commit, node, renamed that file, then
1338 # file X and our intermediate commit, node, renamed that file, then
1336 # those two files are the same and we can discard X from our list
1339 # those two files are the same and we can discard X from our list
1337 # of files. Likewise if X was deleted, it's no longer relevant
1340 # of files. Likewise if X was deleted, it's no longer relevant
1338 files.update(ctx.files())
1341 files.update(ctx.files())
1339
1342
1340 def samefile(f):
1343 def samefile(f):
1341 if f in ctx.manifest():
1344 if f in ctx.manifest():
1342 a = ctx.filectx(f)
1345 a = ctx.filectx(f)
1343 if f in base.manifest():
1346 if f in base.manifest():
1344 b = base.filectx(f)
1347 b = base.filectx(f)
1345 return (a.data() == b.data()
1348 return (a.data() == b.data()
1346 and a.flags() == b.flags())
1349 and a.flags() == b.flags())
1347 else:
1350 else:
1348 return False
1351 return False
1349 else:
1352 else:
1350 return f not in base.manifest()
1353 return f not in base.manifest()
1351 files = [f for f in files if not samefile(f)]
1354 files = [f for f in files if not samefile(f)]
1352
1355
1353 def filectxfn(repo, ctx_, path):
1356 def filectxfn(repo, ctx_, path):
1354 try:
1357 try:
1355 fctx = ctx[path]
1358 fctx = ctx[path]
1356 flags = fctx.flags()
1359 flags = fctx.flags()
1357 mctx = context.memfilectx(fctx.path(), fctx.data(),
1360 mctx = context.memfilectx(fctx.path(), fctx.data(),
1358 islink='l' in flags,
1361 islink='l' in flags,
1359 isexec='x' in flags,
1362 isexec='x' in flags,
1360 copied=copied.get(path))
1363 copied=copied.get(path))
1361 return mctx
1364 return mctx
1362 except KeyError:
1365 except KeyError:
1363 raise IOError()
1366 raise IOError()
1364 else:
1367 else:
1365 ui.note(_('copying changeset %s to %s\n') % (old, base))
1368 ui.note(_('copying changeset %s to %s\n') % (old, base))
1366
1369
1367 # Use version of files as in the old cset
1370 # Use version of files as in the old cset
1368 def filectxfn(repo, ctx_, path):
1371 def filectxfn(repo, ctx_, path):
1369 try:
1372 try:
1370 return old.filectx(path)
1373 return old.filectx(path)
1371 except KeyError:
1374 except KeyError:
1372 raise IOError()
1375 raise IOError()
1373
1376
1374 # See if we got a message from -m or -l, if not, open the editor
1377 # See if we got a message from -m or -l, if not, open the editor
1375 # with the message of the changeset to amend
1378 # with the message of the changeset to amend
1376 user = opts.get('user') or old.user()
1379 user = opts.get('user') or old.user()
1377 date = opts.get('date') or old.date()
1380 date = opts.get('date') or old.date()
1378 message = logmessage(ui, opts)
1381 message = logmessage(ui, opts)
1379 if not message:
1382 if not message:
1380 cctx = context.workingctx(repo, old.description(), user, date,
1383 cctx = context.workingctx(repo, old.description(), user, date,
1381 extra,
1384 extra,
1382 repo.status(base.node(), old.node()))
1385 repo.status(base.node(), old.node()))
1383 message = commitforceeditor(repo, cctx, [])
1386 message = commitforceeditor(repo, cctx, [])
1384
1387
1385 new = context.memctx(repo,
1388 new = context.memctx(repo,
1386 parents=[base.node(), nullid],
1389 parents=[base.node(), nullid],
1387 text=message,
1390 text=message,
1388 files=files,
1391 files=files,
1389 filectxfn=filectxfn,
1392 filectxfn=filectxfn,
1390 user=user,
1393 user=user,
1391 date=date,
1394 date=date,
1392 extra=extra)
1395 extra=extra)
1393 newid = repo.commitctx(new)
1396 newid = repo.commitctx(new)
1394 if newid != old.node():
1397 if newid != old.node():
1395 # Reroute the working copy parent to the new changeset
1398 # Reroute the working copy parent to the new changeset
1396 repo.setparents(newid, nullid)
1399 repo.setparents(newid, nullid)
1397
1400
1398 # Move bookmarks from old parent to amend commit
1401 # Move bookmarks from old parent to amend commit
1399 bms = repo.nodebookmarks(old.node())
1402 bms = repo.nodebookmarks(old.node())
1400 if bms:
1403 if bms:
1401 for bm in bms:
1404 for bm in bms:
1402 repo._bookmarks[bm] = newid
1405 repo._bookmarks[bm] = newid
1403 bookmarks.write(repo)
1406 bookmarks.write(repo)
1404
1407
1405 # Strip the intermediate commit (if there was one) and the amended
1408 # Strip the intermediate commit (if there was one) and the amended
1406 # commit
1409 # commit
1407 lock = repo.lock()
1410 lock = repo.lock()
1408 try:
1411 try:
1409 if node:
1412 if node:
1410 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1413 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1411 ui.note(_('stripping amended changeset %s\n') % old)
1414 ui.note(_('stripping amended changeset %s\n') % old)
1412 repair.strip(ui, repo, old.node(), topic='amend-backup')
1415 repair.strip(ui, repo, old.node(), topic='amend-backup')
1413 finally:
1416 finally:
1414 lock.release()
1417 lock.release()
1415 finally:
1418 finally:
1416 wlock.release()
1419 wlock.release()
1417 return newid
1420 return newid
1418
1421
1419 def commiteditor(repo, ctx, subs):
1422 def commiteditor(repo, ctx, subs):
1420 if ctx.description():
1423 if ctx.description():
1421 return ctx.description()
1424 return ctx.description()
1422 return commitforceeditor(repo, ctx, subs)
1425 return commitforceeditor(repo, ctx, subs)
1423
1426
1424 def commitforceeditor(repo, ctx, subs):
1427 def commitforceeditor(repo, ctx, subs):
1425 edittext = []
1428 edittext = []
1426 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1429 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1427 if ctx.description():
1430 if ctx.description():
1428 edittext.append(ctx.description())
1431 edittext.append(ctx.description())
1429 edittext.append("")
1432 edittext.append("")
1430 edittext.append("") # Empty line between message and comments.
1433 edittext.append("") # Empty line between message and comments.
1431 edittext.append(_("HG: Enter commit message."
1434 edittext.append(_("HG: Enter commit message."
1432 " Lines beginning with 'HG:' are removed."))
1435 " Lines beginning with 'HG:' are removed."))
1433 edittext.append(_("HG: Leave message empty to abort commit."))
1436 edittext.append(_("HG: Leave message empty to abort commit."))
1434 edittext.append("HG: --")
1437 edittext.append("HG: --")
1435 edittext.append(_("HG: user: %s") % ctx.user())
1438 edittext.append(_("HG: user: %s") % ctx.user())
1436 if ctx.p2():
1439 if ctx.p2():
1437 edittext.append(_("HG: branch merge"))
1440 edittext.append(_("HG: branch merge"))
1438 if ctx.branch():
1441 if ctx.branch():
1439 edittext.append(_("HG: branch '%s'") % ctx.branch())
1442 edittext.append(_("HG: branch '%s'") % ctx.branch())
1440 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1443 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1441 edittext.extend([_("HG: added %s") % f for f in added])
1444 edittext.extend([_("HG: added %s") % f for f in added])
1442 edittext.extend([_("HG: changed %s") % f for f in modified])
1445 edittext.extend([_("HG: changed %s") % f for f in modified])
1443 edittext.extend([_("HG: removed %s") % f for f in removed])
1446 edittext.extend([_("HG: removed %s") % f for f in removed])
1444 if not added and not modified and not removed:
1447 if not added and not modified and not removed:
1445 edittext.append(_("HG: no files changed"))
1448 edittext.append(_("HG: no files changed"))
1446 edittext.append("")
1449 edittext.append("")
1447 # run editor in the repository root
1450 # run editor in the repository root
1448 olddir = os.getcwd()
1451 olddir = os.getcwd()
1449 os.chdir(repo.root)
1452 os.chdir(repo.root)
1450 text = repo.ui.edit("\n".join(edittext), ctx.user())
1453 text = repo.ui.edit("\n".join(edittext), ctx.user())
1451 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1454 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1452 os.chdir(olddir)
1455 os.chdir(olddir)
1453
1456
1454 if not text.strip():
1457 if not text.strip():
1455 raise util.Abort(_("empty commit message"))
1458 raise util.Abort(_("empty commit message"))
1456
1459
1457 return text
1460 return text
1458
1461
1459 def revert(ui, repo, ctx, parents, *pats, **opts):
1462 def revert(ui, repo, ctx, parents, *pats, **opts):
1460 parent, p2 = parents
1463 parent, p2 = parents
1461 node = ctx.node()
1464 node = ctx.node()
1462
1465
1463 mf = ctx.manifest()
1466 mf = ctx.manifest()
1464 if node == parent:
1467 if node == parent:
1465 pmf = mf
1468 pmf = mf
1466 else:
1469 else:
1467 pmf = None
1470 pmf = None
1468
1471
1469 # need all matching names in dirstate and manifest of target rev,
1472 # need all matching names in dirstate and manifest of target rev,
1470 # so have to walk both. do not print errors if files exist in one
1473 # so have to walk both. do not print errors if files exist in one
1471 # but not other.
1474 # but not other.
1472
1475
1473 names = {}
1476 names = {}
1474
1477
1475 wlock = repo.wlock()
1478 wlock = repo.wlock()
1476 try:
1479 try:
1477 # walk dirstate.
1480 # walk dirstate.
1478
1481
1479 m = scmutil.match(repo[None], pats, opts)
1482 m = scmutil.match(repo[None], pats, opts)
1480 m.bad = lambda x, y: False
1483 m.bad = lambda x, y: False
1481 for abs in repo.walk(m):
1484 for abs in repo.walk(m):
1482 names[abs] = m.rel(abs), m.exact(abs)
1485 names[abs] = m.rel(abs), m.exact(abs)
1483
1486
1484 # walk target manifest.
1487 # walk target manifest.
1485
1488
1486 def badfn(path, msg):
1489 def badfn(path, msg):
1487 if path in names:
1490 if path in names:
1488 return
1491 return
1489 if path in repo[node].substate:
1492 if path in repo[node].substate:
1490 return
1493 return
1491 path_ = path + '/'
1494 path_ = path + '/'
1492 for f in names:
1495 for f in names:
1493 if f.startswith(path_):
1496 if f.startswith(path_):
1494 return
1497 return
1495 ui.warn("%s: %s\n" % (m.rel(path), msg))
1498 ui.warn("%s: %s\n" % (m.rel(path), msg))
1496
1499
1497 m = scmutil.match(repo[node], pats, opts)
1500 m = scmutil.match(repo[node], pats, opts)
1498 m.bad = badfn
1501 m.bad = badfn
1499 for abs in repo[node].walk(m):
1502 for abs in repo[node].walk(m):
1500 if abs not in names:
1503 if abs not in names:
1501 names[abs] = m.rel(abs), m.exact(abs)
1504 names[abs] = m.rel(abs), m.exact(abs)
1502
1505
1503 # get the list of subrepos that must be reverted
1506 # get the list of subrepos that must be reverted
1504 targetsubs = [s for s in repo[node].substate if m(s)]
1507 targetsubs = [s for s in repo[node].substate if m(s)]
1505 m = scmutil.matchfiles(repo, names)
1508 m = scmutil.matchfiles(repo, names)
1506 changes = repo.status(match=m)[:4]
1509 changes = repo.status(match=m)[:4]
1507 modified, added, removed, deleted = map(set, changes)
1510 modified, added, removed, deleted = map(set, changes)
1508
1511
1509 # if f is a rename, also revert the source
1512 # if f is a rename, also revert the source
1510 cwd = repo.getcwd()
1513 cwd = repo.getcwd()
1511 for f in added:
1514 for f in added:
1512 src = repo.dirstate.copied(f)
1515 src = repo.dirstate.copied(f)
1513 if src and src not in names and repo.dirstate[src] == 'r':
1516 if src and src not in names and repo.dirstate[src] == 'r':
1514 removed.add(src)
1517 removed.add(src)
1515 names[src] = (repo.pathto(src, cwd), True)
1518 names[src] = (repo.pathto(src, cwd), True)
1516
1519
1517 def removeforget(abs):
1520 def removeforget(abs):
1518 if repo.dirstate[abs] == 'a':
1521 if repo.dirstate[abs] == 'a':
1519 return _('forgetting %s\n')
1522 return _('forgetting %s\n')
1520 return _('removing %s\n')
1523 return _('removing %s\n')
1521
1524
1522 revert = ([], _('reverting %s\n'))
1525 revert = ([], _('reverting %s\n'))
1523 add = ([], _('adding %s\n'))
1526 add = ([], _('adding %s\n'))
1524 remove = ([], removeforget)
1527 remove = ([], removeforget)
1525 undelete = ([], _('undeleting %s\n'))
1528 undelete = ([], _('undeleting %s\n'))
1526
1529
1527 disptable = (
1530 disptable = (
1528 # dispatch table:
1531 # dispatch table:
1529 # file state
1532 # file state
1530 # action if in target manifest
1533 # action if in target manifest
1531 # action if not in target manifest
1534 # action if not in target manifest
1532 # make backup if in target manifest
1535 # make backup if in target manifest
1533 # make backup if not in target manifest
1536 # make backup if not in target manifest
1534 (modified, revert, remove, True, True),
1537 (modified, revert, remove, True, True),
1535 (added, revert, remove, True, False),
1538 (added, revert, remove, True, False),
1536 (removed, undelete, None, False, False),
1539 (removed, undelete, None, False, False),
1537 (deleted, revert, remove, False, False),
1540 (deleted, revert, remove, False, False),
1538 )
1541 )
1539
1542
1540 for abs, (rel, exact) in sorted(names.items()):
1543 for abs, (rel, exact) in sorted(names.items()):
1541 mfentry = mf.get(abs)
1544 mfentry = mf.get(abs)
1542 target = repo.wjoin(abs)
1545 target = repo.wjoin(abs)
1543 def handle(xlist, dobackup):
1546 def handle(xlist, dobackup):
1544 xlist[0].append(abs)
1547 xlist[0].append(abs)
1545 if (dobackup and not opts.get('no_backup') and
1548 if (dobackup and not opts.get('no_backup') and
1546 os.path.lexists(target)):
1549 os.path.lexists(target)):
1547 bakname = "%s.orig" % rel
1550 bakname = "%s.orig" % rel
1548 ui.note(_('saving current version of %s as %s\n') %
1551 ui.note(_('saving current version of %s as %s\n') %
1549 (rel, bakname))
1552 (rel, bakname))
1550 if not opts.get('dry_run'):
1553 if not opts.get('dry_run'):
1551 util.rename(target, bakname)
1554 util.rename(target, bakname)
1552 if ui.verbose or not exact:
1555 if ui.verbose or not exact:
1553 msg = xlist[1]
1556 msg = xlist[1]
1554 if not isinstance(msg, basestring):
1557 if not isinstance(msg, basestring):
1555 msg = msg(abs)
1558 msg = msg(abs)
1556 ui.status(msg % rel)
1559 ui.status(msg % rel)
1557 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1560 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1558 if abs not in table:
1561 if abs not in table:
1559 continue
1562 continue
1560 # file has changed in dirstate
1563 # file has changed in dirstate
1561 if mfentry:
1564 if mfentry:
1562 handle(hitlist, backuphit)
1565 handle(hitlist, backuphit)
1563 elif misslist is not None:
1566 elif misslist is not None:
1564 handle(misslist, backupmiss)
1567 handle(misslist, backupmiss)
1565 break
1568 break
1566 else:
1569 else:
1567 if abs not in repo.dirstate:
1570 if abs not in repo.dirstate:
1568 if mfentry:
1571 if mfentry:
1569 handle(add, True)
1572 handle(add, True)
1570 elif exact:
1573 elif exact:
1571 ui.warn(_('file not managed: %s\n') % rel)
1574 ui.warn(_('file not managed: %s\n') % rel)
1572 continue
1575 continue
1573 # file has not changed in dirstate
1576 # file has not changed in dirstate
1574 if node == parent:
1577 if node == parent:
1575 if exact:
1578 if exact:
1576 ui.warn(_('no changes needed to %s\n') % rel)
1579 ui.warn(_('no changes needed to %s\n') % rel)
1577 continue
1580 continue
1578 if pmf is None:
1581 if pmf is None:
1579 # only need parent manifest in this unlikely case,
1582 # only need parent manifest in this unlikely case,
1580 # so do not read by default
1583 # so do not read by default
1581 pmf = repo[parent].manifest()
1584 pmf = repo[parent].manifest()
1582 if abs in pmf and mfentry:
1585 if abs in pmf and mfentry:
1583 # if version of file is same in parent and target
1586 # if version of file is same in parent and target
1584 # manifests, do nothing
1587 # manifests, do nothing
1585 if (pmf[abs] != mfentry or
1588 if (pmf[abs] != mfentry or
1586 pmf.flags(abs) != mf.flags(abs)):
1589 pmf.flags(abs) != mf.flags(abs)):
1587 handle(revert, False)
1590 handle(revert, False)
1588 else:
1591 else:
1589 handle(remove, False)
1592 handle(remove, False)
1590
1593
1591 if not opts.get('dry_run'):
1594 if not opts.get('dry_run'):
1592 def checkout(f):
1595 def checkout(f):
1593 fc = ctx[f]
1596 fc = ctx[f]
1594 repo.wwrite(f, fc.data(), fc.flags())
1597 repo.wwrite(f, fc.data(), fc.flags())
1595
1598
1596 audit_path = scmutil.pathauditor(repo.root)
1599 audit_path = scmutil.pathauditor(repo.root)
1597 for f in remove[0]:
1600 for f in remove[0]:
1598 if repo.dirstate[f] == 'a':
1601 if repo.dirstate[f] == 'a':
1599 repo.dirstate.drop(f)
1602 repo.dirstate.drop(f)
1600 continue
1603 continue
1601 audit_path(f)
1604 audit_path(f)
1602 try:
1605 try:
1603 util.unlinkpath(repo.wjoin(f))
1606 util.unlinkpath(repo.wjoin(f))
1604 except OSError:
1607 except OSError:
1605 pass
1608 pass
1606 repo.dirstate.remove(f)
1609 repo.dirstate.remove(f)
1607
1610
1608 normal = None
1611 normal = None
1609 if node == parent:
1612 if node == parent:
1610 # We're reverting to our parent. If possible, we'd like status
1613 # We're reverting to our parent. If possible, we'd like status
1611 # to report the file as clean. We have to use normallookup for
1614 # to report the file as clean. We have to use normallookup for
1612 # merges to avoid losing information about merged/dirty files.
1615 # merges to avoid losing information about merged/dirty files.
1613 if p2 != nullid:
1616 if p2 != nullid:
1614 normal = repo.dirstate.normallookup
1617 normal = repo.dirstate.normallookup
1615 else:
1618 else:
1616 normal = repo.dirstate.normal
1619 normal = repo.dirstate.normal
1617 for f in revert[0]:
1620 for f in revert[0]:
1618 checkout(f)
1621 checkout(f)
1619 if normal:
1622 if normal:
1620 normal(f)
1623 normal(f)
1621
1624
1622 for f in add[0]:
1625 for f in add[0]:
1623 checkout(f)
1626 checkout(f)
1624 repo.dirstate.add(f)
1627 repo.dirstate.add(f)
1625
1628
1626 normal = repo.dirstate.normallookup
1629 normal = repo.dirstate.normallookup
1627 if node == parent and p2 == nullid:
1630 if node == parent and p2 == nullid:
1628 normal = repo.dirstate.normal
1631 normal = repo.dirstate.normal
1629 for f in undelete[0]:
1632 for f in undelete[0]:
1630 checkout(f)
1633 checkout(f)
1631 normal(f)
1634 normal(f)
1632
1635
1633 if targetsubs:
1636 if targetsubs:
1634 # Revert the subrepos on the revert list
1637 # Revert the subrepos on the revert list
1635 for sub in targetsubs:
1638 for sub in targetsubs:
1636 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1639 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1637 finally:
1640 finally:
1638 wlock.release()
1641 wlock.release()
1639
1642
1640 def command(table):
1643 def command(table):
1641 '''returns a function object bound to table which can be used as
1644 '''returns a function object bound to table which can be used as
1642 a decorator for populating table as a command table'''
1645 a decorator for populating table as a command table'''
1643
1646
1644 def cmd(name, options, synopsis=None):
1647 def cmd(name, options, synopsis=None):
1645 def decorator(func):
1648 def decorator(func):
1646 if synopsis:
1649 if synopsis:
1647 table[name] = func, options[:], synopsis
1650 table[name] = func, options[:], synopsis
1648 else:
1651 else:
1649 table[name] = func, options[:]
1652 table[name] = func, options[:]
1650 return func
1653 return func
1651 return decorator
1654 return decorator
1652
1655
1653 return cmd
1656 return cmd
@@ -1,1372 +1,1381 b''
1 $ "$TESTDIR/hghave" unix-permissions || exit 80
1 $ "$TESTDIR/hghave" unix-permissions || exit 80
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
10
11 $ hg add b
11 $ hg add b
12 $ echo other 1 > c
12 $ echo other 1 > c
13 $ echo other 2 >> c
13 $ echo other 2 >> c
14 $ echo >> c
14 $ echo >> c
15 $ echo other 3 >> c
15 $ echo other 3 >> c
16 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
16 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
17
17
18 $ hg add c
18 $ hg add c
19 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
19 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
20 $ echo c >> c
20 $ echo c >> c
21 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
21 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
22
22
23 $ echo foo > .hg/branch
23 $ echo foo > .hg/branch
24 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
24 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
25
25
26 $ hg co -q 3
26 $ hg co -q 3
27 $ echo other 4 >> d
27 $ echo other 4 >> d
28 $ hg add d
28 $ hg add d
29 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
29 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
30
30
31 $ hg merge -q foo
31 $ hg merge -q foo
32 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
32 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
33
33
34 Second branch starting at nullrev:
34 Second branch starting at nullrev:
35
35
36 $ hg update null
36 $ hg update null
37 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
38 $ echo second > second
38 $ echo second > second
39 $ hg add second
39 $ hg add second
40 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
40 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
41 created new head
41 created new head
42
42
43 $ echo third > third
43 $ echo third > third
44 $ hg add third
44 $ hg add third
45 $ hg mv second fourth
45 $ hg mv second fourth
46 $ hg commit -m third -d "2020-01-01 10:01"
46 $ hg commit -m third -d "2020-01-01 10:01"
47
47
48 Quoting for ui.logtemplate
49
50 $ hg tip --config "ui.logtemplate={rev}\n"
51 8
52 $ hg tip --config "ui.logtemplate='{rev}\n'"
53 8
54 $ hg tip --config 'ui.logtemplate="{rev}\n"'
55 8
56
48 Make sure user/global hgrc does not affect tests
57 Make sure user/global hgrc does not affect tests
49
58
50 $ echo '[ui]' > .hg/hgrc
59 $ echo '[ui]' > .hg/hgrc
51 $ echo 'logtemplate =' >> .hg/hgrc
60 $ echo 'logtemplate =' >> .hg/hgrc
52 $ echo 'style =' >> .hg/hgrc
61 $ echo 'style =' >> .hg/hgrc
53
62
54 Default style is like normal output:
63 Default style is like normal output:
55
64
56 $ hg log > log.out
65 $ hg log > log.out
57 $ hg log --style default > style.out
66 $ hg log --style default > style.out
58 $ cmp log.out style.out || diff -u log.out style.out
67 $ cmp log.out style.out || diff -u log.out style.out
59
68
60 $ hg log -v > log.out
69 $ hg log -v > log.out
61 $ hg log -v --style default > style.out
70 $ hg log -v --style default > style.out
62 $ cmp log.out style.out || diff -u log.out style.out
71 $ cmp log.out style.out || diff -u log.out style.out
63
72
64 $ hg log --debug > log.out
73 $ hg log --debug > log.out
65 $ hg log --debug --style default > style.out
74 $ hg log --debug --style default > style.out
66 $ cmp log.out style.out || diff -u log.out style.out
75 $ cmp log.out style.out || diff -u log.out style.out
67
76
68 Revision with no copies (used to print a traceback):
77 Revision with no copies (used to print a traceback):
69
78
70 $ hg tip -v --template '\n'
79 $ hg tip -v --template '\n'
71
80
72
81
73 Compact style works:
82 Compact style works:
74
83
75 $ hg log --style compact
84 $ hg log --style compact
76 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
85 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
77 third
86 third
78
87
79 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
88 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
80 second
89 second
81
90
82 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
91 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
83 merge
92 merge
84
93
85 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
94 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
86 new head
95 new head
87
96
88 4 bbe44766e73d 1970-01-17 04:53 +0000 person
97 4 bbe44766e73d 1970-01-17 04:53 +0000 person
89 new branch
98 new branch
90
99
91 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
100 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
92 no user, no domain
101 no user, no domain
93
102
94 2 97054abb4ab8 1970-01-14 21:20 +0000 other
103 2 97054abb4ab8 1970-01-14 21:20 +0000 other
95 no person
104 no person
96
105
97 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
106 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
98 other 1
107 other 1
99
108
100 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
109 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
101 line 1
110 line 1
102
111
103
112
104 $ hg log -v --style compact
113 $ hg log -v --style compact
105 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
114 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
106 third
115 third
107
116
108 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
117 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
109 second
118 second
110
119
111 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
120 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
112 merge
121 merge
113
122
114 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
123 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
115 new head
124 new head
116
125
117 4 bbe44766e73d 1970-01-17 04:53 +0000 person
126 4 bbe44766e73d 1970-01-17 04:53 +0000 person
118 new branch
127 new branch
119
128
120 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
129 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
121 no user, no domain
130 no user, no domain
122
131
123 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
132 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
124 no person
133 no person
125
134
126 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
135 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
127 other 1
136 other 1
128 other 2
137 other 2
129
138
130 other 3
139 other 3
131
140
132 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
141 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
133 line 1
142 line 1
134 line 2
143 line 2
135
144
136
145
137 $ hg log --debug --style compact
146 $ hg log --debug --style compact
138 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
147 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
139 third
148 third
140
149
141 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
150 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
142 second
151 second
143
152
144 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
153 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
145 merge
154 merge
146
155
147 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
156 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
148 new head
157 new head
149
158
150 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
159 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
151 new branch
160 new branch
152
161
153 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
162 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
154 no user, no domain
163 no user, no domain
155
164
156 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
165 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
157 no person
166 no person
158
167
159 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
168 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
160 other 1
169 other 1
161 other 2
170 other 2
162
171
163 other 3
172 other 3
164
173
165 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
174 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
166 line 1
175 line 1
167 line 2
176 line 2
168
177
169
178
170 Test xml styles:
179 Test xml styles:
171
180
172 $ hg log --style xml
181 $ hg log --style xml
173 <?xml version="1.0"?>
182 <?xml version="1.0"?>
174 <log>
183 <log>
175 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
184 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
176 <tag>tip</tag>
185 <tag>tip</tag>
177 <author email="test">test</author>
186 <author email="test">test</author>
178 <date>2020-01-01T10:01:00+00:00</date>
187 <date>2020-01-01T10:01:00+00:00</date>
179 <msg xml:space="preserve">third</msg>
188 <msg xml:space="preserve">third</msg>
180 </logentry>
189 </logentry>
181 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
190 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
182 <parent revision="-1" node="0000000000000000000000000000000000000000" />
191 <parent revision="-1" node="0000000000000000000000000000000000000000" />
183 <author email="user@hostname">User Name</author>
192 <author email="user@hostname">User Name</author>
184 <date>1970-01-12T13:46:40+00:00</date>
193 <date>1970-01-12T13:46:40+00:00</date>
185 <msg xml:space="preserve">second</msg>
194 <msg xml:space="preserve">second</msg>
186 </logentry>
195 </logentry>
187 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
196 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
188 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
197 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
189 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
198 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
190 <author email="person">person</author>
199 <author email="person">person</author>
191 <date>1970-01-18T08:40:01+00:00</date>
200 <date>1970-01-18T08:40:01+00:00</date>
192 <msg xml:space="preserve">merge</msg>
201 <msg xml:space="preserve">merge</msg>
193 </logentry>
202 </logentry>
194 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
203 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
195 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
204 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
196 <author email="person">person</author>
205 <author email="person">person</author>
197 <date>1970-01-18T08:40:00+00:00</date>
206 <date>1970-01-18T08:40:00+00:00</date>
198 <msg xml:space="preserve">new head</msg>
207 <msg xml:space="preserve">new head</msg>
199 </logentry>
208 </logentry>
200 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
209 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
201 <branch>foo</branch>
210 <branch>foo</branch>
202 <author email="person">person</author>
211 <author email="person">person</author>
203 <date>1970-01-17T04:53:20+00:00</date>
212 <date>1970-01-17T04:53:20+00:00</date>
204 <msg xml:space="preserve">new branch</msg>
213 <msg xml:space="preserve">new branch</msg>
205 </logentry>
214 </logentry>
206 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
215 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
207 <author email="person">person</author>
216 <author email="person">person</author>
208 <date>1970-01-16T01:06:40+00:00</date>
217 <date>1970-01-16T01:06:40+00:00</date>
209 <msg xml:space="preserve">no user, no domain</msg>
218 <msg xml:space="preserve">no user, no domain</msg>
210 </logentry>
219 </logentry>
211 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
220 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
212 <author email="other@place">other</author>
221 <author email="other@place">other</author>
213 <date>1970-01-14T21:20:00+00:00</date>
222 <date>1970-01-14T21:20:00+00:00</date>
214 <msg xml:space="preserve">no person</msg>
223 <msg xml:space="preserve">no person</msg>
215 </logentry>
224 </logentry>
216 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
225 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
217 <author email="other@place">A. N. Other</author>
226 <author email="other@place">A. N. Other</author>
218 <date>1970-01-13T17:33:20+00:00</date>
227 <date>1970-01-13T17:33:20+00:00</date>
219 <msg xml:space="preserve">other 1
228 <msg xml:space="preserve">other 1
220 other 2
229 other 2
221
230
222 other 3</msg>
231 other 3</msg>
223 </logentry>
232 </logentry>
224 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
233 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
225 <author email="user@hostname">User Name</author>
234 <author email="user@hostname">User Name</author>
226 <date>1970-01-12T13:46:40+00:00</date>
235 <date>1970-01-12T13:46:40+00:00</date>
227 <msg xml:space="preserve">line 1
236 <msg xml:space="preserve">line 1
228 line 2</msg>
237 line 2</msg>
229 </logentry>
238 </logentry>
230 </log>
239 </log>
231
240
232 $ hg log -v --style xml
241 $ hg log -v --style xml
233 <?xml version="1.0"?>
242 <?xml version="1.0"?>
234 <log>
243 <log>
235 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
244 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
236 <tag>tip</tag>
245 <tag>tip</tag>
237 <author email="test">test</author>
246 <author email="test">test</author>
238 <date>2020-01-01T10:01:00+00:00</date>
247 <date>2020-01-01T10:01:00+00:00</date>
239 <msg xml:space="preserve">third</msg>
248 <msg xml:space="preserve">third</msg>
240 <paths>
249 <paths>
241 <path action="A">fourth</path>
250 <path action="A">fourth</path>
242 <path action="A">third</path>
251 <path action="A">third</path>
243 <path action="R">second</path>
252 <path action="R">second</path>
244 </paths>
253 </paths>
245 <copies>
254 <copies>
246 <copy source="second">fourth</copy>
255 <copy source="second">fourth</copy>
247 </copies>
256 </copies>
248 </logentry>
257 </logentry>
249 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
258 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
250 <parent revision="-1" node="0000000000000000000000000000000000000000" />
259 <parent revision="-1" node="0000000000000000000000000000000000000000" />
251 <author email="user@hostname">User Name</author>
260 <author email="user@hostname">User Name</author>
252 <date>1970-01-12T13:46:40+00:00</date>
261 <date>1970-01-12T13:46:40+00:00</date>
253 <msg xml:space="preserve">second</msg>
262 <msg xml:space="preserve">second</msg>
254 <paths>
263 <paths>
255 <path action="A">second</path>
264 <path action="A">second</path>
256 </paths>
265 </paths>
257 </logentry>
266 </logentry>
258 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
267 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
259 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
268 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
260 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
269 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
261 <author email="person">person</author>
270 <author email="person">person</author>
262 <date>1970-01-18T08:40:01+00:00</date>
271 <date>1970-01-18T08:40:01+00:00</date>
263 <msg xml:space="preserve">merge</msg>
272 <msg xml:space="preserve">merge</msg>
264 <paths>
273 <paths>
265 </paths>
274 </paths>
266 </logentry>
275 </logentry>
267 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
276 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
268 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
277 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
269 <author email="person">person</author>
278 <author email="person">person</author>
270 <date>1970-01-18T08:40:00+00:00</date>
279 <date>1970-01-18T08:40:00+00:00</date>
271 <msg xml:space="preserve">new head</msg>
280 <msg xml:space="preserve">new head</msg>
272 <paths>
281 <paths>
273 <path action="A">d</path>
282 <path action="A">d</path>
274 </paths>
283 </paths>
275 </logentry>
284 </logentry>
276 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
285 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
277 <branch>foo</branch>
286 <branch>foo</branch>
278 <author email="person">person</author>
287 <author email="person">person</author>
279 <date>1970-01-17T04:53:20+00:00</date>
288 <date>1970-01-17T04:53:20+00:00</date>
280 <msg xml:space="preserve">new branch</msg>
289 <msg xml:space="preserve">new branch</msg>
281 <paths>
290 <paths>
282 </paths>
291 </paths>
283 </logentry>
292 </logentry>
284 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
293 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
285 <author email="person">person</author>
294 <author email="person">person</author>
286 <date>1970-01-16T01:06:40+00:00</date>
295 <date>1970-01-16T01:06:40+00:00</date>
287 <msg xml:space="preserve">no user, no domain</msg>
296 <msg xml:space="preserve">no user, no domain</msg>
288 <paths>
297 <paths>
289 <path action="M">c</path>
298 <path action="M">c</path>
290 </paths>
299 </paths>
291 </logentry>
300 </logentry>
292 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
301 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
293 <author email="other@place">other</author>
302 <author email="other@place">other</author>
294 <date>1970-01-14T21:20:00+00:00</date>
303 <date>1970-01-14T21:20:00+00:00</date>
295 <msg xml:space="preserve">no person</msg>
304 <msg xml:space="preserve">no person</msg>
296 <paths>
305 <paths>
297 <path action="A">c</path>
306 <path action="A">c</path>
298 </paths>
307 </paths>
299 </logentry>
308 </logentry>
300 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
309 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
301 <author email="other@place">A. N. Other</author>
310 <author email="other@place">A. N. Other</author>
302 <date>1970-01-13T17:33:20+00:00</date>
311 <date>1970-01-13T17:33:20+00:00</date>
303 <msg xml:space="preserve">other 1
312 <msg xml:space="preserve">other 1
304 other 2
313 other 2
305
314
306 other 3</msg>
315 other 3</msg>
307 <paths>
316 <paths>
308 <path action="A">b</path>
317 <path action="A">b</path>
309 </paths>
318 </paths>
310 </logentry>
319 </logentry>
311 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
320 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
312 <author email="user@hostname">User Name</author>
321 <author email="user@hostname">User Name</author>
313 <date>1970-01-12T13:46:40+00:00</date>
322 <date>1970-01-12T13:46:40+00:00</date>
314 <msg xml:space="preserve">line 1
323 <msg xml:space="preserve">line 1
315 line 2</msg>
324 line 2</msg>
316 <paths>
325 <paths>
317 <path action="A">a</path>
326 <path action="A">a</path>
318 </paths>
327 </paths>
319 </logentry>
328 </logentry>
320 </log>
329 </log>
321
330
322 $ hg log --debug --style xml
331 $ hg log --debug --style xml
323 <?xml version="1.0"?>
332 <?xml version="1.0"?>
324 <log>
333 <log>
325 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
334 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
326 <tag>tip</tag>
335 <tag>tip</tag>
327 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
336 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
328 <parent revision="-1" node="0000000000000000000000000000000000000000" />
337 <parent revision="-1" node="0000000000000000000000000000000000000000" />
329 <author email="test">test</author>
338 <author email="test">test</author>
330 <date>2020-01-01T10:01:00+00:00</date>
339 <date>2020-01-01T10:01:00+00:00</date>
331 <msg xml:space="preserve">third</msg>
340 <msg xml:space="preserve">third</msg>
332 <paths>
341 <paths>
333 <path action="A">fourth</path>
342 <path action="A">fourth</path>
334 <path action="A">third</path>
343 <path action="A">third</path>
335 <path action="R">second</path>
344 <path action="R">second</path>
336 </paths>
345 </paths>
337 <copies>
346 <copies>
338 <copy source="second">fourth</copy>
347 <copy source="second">fourth</copy>
339 </copies>
348 </copies>
340 <extra key="branch">default</extra>
349 <extra key="branch">default</extra>
341 </logentry>
350 </logentry>
342 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
351 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
343 <parent revision="-1" node="0000000000000000000000000000000000000000" />
352 <parent revision="-1" node="0000000000000000000000000000000000000000" />
344 <parent revision="-1" node="0000000000000000000000000000000000000000" />
353 <parent revision="-1" node="0000000000000000000000000000000000000000" />
345 <author email="user@hostname">User Name</author>
354 <author email="user@hostname">User Name</author>
346 <date>1970-01-12T13:46:40+00:00</date>
355 <date>1970-01-12T13:46:40+00:00</date>
347 <msg xml:space="preserve">second</msg>
356 <msg xml:space="preserve">second</msg>
348 <paths>
357 <paths>
349 <path action="A">second</path>
358 <path action="A">second</path>
350 </paths>
359 </paths>
351 <extra key="branch">default</extra>
360 <extra key="branch">default</extra>
352 </logentry>
361 </logentry>
353 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
362 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
354 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
363 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
355 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
364 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
356 <author email="person">person</author>
365 <author email="person">person</author>
357 <date>1970-01-18T08:40:01+00:00</date>
366 <date>1970-01-18T08:40:01+00:00</date>
358 <msg xml:space="preserve">merge</msg>
367 <msg xml:space="preserve">merge</msg>
359 <paths>
368 <paths>
360 </paths>
369 </paths>
361 <extra key="branch">default</extra>
370 <extra key="branch">default</extra>
362 </logentry>
371 </logentry>
363 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
372 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
364 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
373 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
365 <parent revision="-1" node="0000000000000000000000000000000000000000" />
374 <parent revision="-1" node="0000000000000000000000000000000000000000" />
366 <author email="person">person</author>
375 <author email="person">person</author>
367 <date>1970-01-18T08:40:00+00:00</date>
376 <date>1970-01-18T08:40:00+00:00</date>
368 <msg xml:space="preserve">new head</msg>
377 <msg xml:space="preserve">new head</msg>
369 <paths>
378 <paths>
370 <path action="A">d</path>
379 <path action="A">d</path>
371 </paths>
380 </paths>
372 <extra key="branch">default</extra>
381 <extra key="branch">default</extra>
373 </logentry>
382 </logentry>
374 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
383 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
375 <branch>foo</branch>
384 <branch>foo</branch>
376 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
385 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
377 <parent revision="-1" node="0000000000000000000000000000000000000000" />
386 <parent revision="-1" node="0000000000000000000000000000000000000000" />
378 <author email="person">person</author>
387 <author email="person">person</author>
379 <date>1970-01-17T04:53:20+00:00</date>
388 <date>1970-01-17T04:53:20+00:00</date>
380 <msg xml:space="preserve">new branch</msg>
389 <msg xml:space="preserve">new branch</msg>
381 <paths>
390 <paths>
382 </paths>
391 </paths>
383 <extra key="branch">foo</extra>
392 <extra key="branch">foo</extra>
384 </logentry>
393 </logentry>
385 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
394 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
386 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
395 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
387 <parent revision="-1" node="0000000000000000000000000000000000000000" />
396 <parent revision="-1" node="0000000000000000000000000000000000000000" />
388 <author email="person">person</author>
397 <author email="person">person</author>
389 <date>1970-01-16T01:06:40+00:00</date>
398 <date>1970-01-16T01:06:40+00:00</date>
390 <msg xml:space="preserve">no user, no domain</msg>
399 <msg xml:space="preserve">no user, no domain</msg>
391 <paths>
400 <paths>
392 <path action="M">c</path>
401 <path action="M">c</path>
393 </paths>
402 </paths>
394 <extra key="branch">default</extra>
403 <extra key="branch">default</extra>
395 </logentry>
404 </logentry>
396 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
405 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
397 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
406 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
398 <parent revision="-1" node="0000000000000000000000000000000000000000" />
407 <parent revision="-1" node="0000000000000000000000000000000000000000" />
399 <author email="other@place">other</author>
408 <author email="other@place">other</author>
400 <date>1970-01-14T21:20:00+00:00</date>
409 <date>1970-01-14T21:20:00+00:00</date>
401 <msg xml:space="preserve">no person</msg>
410 <msg xml:space="preserve">no person</msg>
402 <paths>
411 <paths>
403 <path action="A">c</path>
412 <path action="A">c</path>
404 </paths>
413 </paths>
405 <extra key="branch">default</extra>
414 <extra key="branch">default</extra>
406 </logentry>
415 </logentry>
407 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
416 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
408 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
417 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
409 <parent revision="-1" node="0000000000000000000000000000000000000000" />
418 <parent revision="-1" node="0000000000000000000000000000000000000000" />
410 <author email="other@place">A. N. Other</author>
419 <author email="other@place">A. N. Other</author>
411 <date>1970-01-13T17:33:20+00:00</date>
420 <date>1970-01-13T17:33:20+00:00</date>
412 <msg xml:space="preserve">other 1
421 <msg xml:space="preserve">other 1
413 other 2
422 other 2
414
423
415 other 3</msg>
424 other 3</msg>
416 <paths>
425 <paths>
417 <path action="A">b</path>
426 <path action="A">b</path>
418 </paths>
427 </paths>
419 <extra key="branch">default</extra>
428 <extra key="branch">default</extra>
420 </logentry>
429 </logentry>
421 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
430 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
422 <parent revision="-1" node="0000000000000000000000000000000000000000" />
431 <parent revision="-1" node="0000000000000000000000000000000000000000" />
423 <parent revision="-1" node="0000000000000000000000000000000000000000" />
432 <parent revision="-1" node="0000000000000000000000000000000000000000" />
424 <author email="user@hostname">User Name</author>
433 <author email="user@hostname">User Name</author>
425 <date>1970-01-12T13:46:40+00:00</date>
434 <date>1970-01-12T13:46:40+00:00</date>
426 <msg xml:space="preserve">line 1
435 <msg xml:space="preserve">line 1
427 line 2</msg>
436 line 2</msg>
428 <paths>
437 <paths>
429 <path action="A">a</path>
438 <path action="A">a</path>
430 </paths>
439 </paths>
431 <extra key="branch">default</extra>
440 <extra key="branch">default</extra>
432 </logentry>
441 </logentry>
433 </log>
442 </log>
434
443
435
444
436 Error if style not readable:
445 Error if style not readable:
437
446
438 $ touch q
447 $ touch q
439 $ chmod 0 q
448 $ chmod 0 q
440 $ hg log --style ./q
449 $ hg log --style ./q
441 abort: Permission denied: ./q
450 abort: Permission denied: ./q
442 [255]
451 [255]
443
452
444 Error if no style:
453 Error if no style:
445
454
446 $ hg log --style notexist
455 $ hg log --style notexist
447 abort: style not found: notexist
456 abort: style not found: notexist
448 [255]
457 [255]
449
458
450 Error if style missing key:
459 Error if style missing key:
451
460
452 $ echo 'q = q' > t
461 $ echo 'q = q' > t
453 $ hg log --style ./t
462 $ hg log --style ./t
454 abort: "changeset" not in template map
463 abort: "changeset" not in template map
455 [255]
464 [255]
456
465
457 Error if include fails:
466 Error if include fails:
458
467
459 $ echo 'changeset = q' >> t
468 $ echo 'changeset = q' >> t
460 $ hg log --style ./t
469 $ hg log --style ./t
461 abort: template file ./q: Permission denied
470 abort: template file ./q: Permission denied
462 [255]
471 [255]
463
472
464 Include works:
473 Include works:
465
474
466 $ rm q
475 $ rm q
467 $ echo '{rev}' > q
476 $ echo '{rev}' > q
468 $ hg log --style ./t
477 $ hg log --style ./t
469 8
478 8
470 7
479 7
471 6
480 6
472 5
481 5
473 4
482 4
474 3
483 3
475 2
484 2
476 1
485 1
477 0
486 0
478
487
479 ui.style works:
488 ui.style works:
480
489
481 $ echo '[ui]' > .hg/hgrc
490 $ echo '[ui]' > .hg/hgrc
482 $ echo 'style = t' >> .hg/hgrc
491 $ echo 'style = t' >> .hg/hgrc
483 $ hg log
492 $ hg log
484 8
493 8
485 7
494 7
486 6
495 6
487 5
496 5
488 4
497 4
489 3
498 3
490 2
499 2
491 1
500 1
492 0
501 0
493
502
494
503
495 Issue338:
504 Issue338:
496
505
497 $ hg log --style=changelog > changelog
506 $ hg log --style=changelog > changelog
498
507
499 $ cat changelog
508 $ cat changelog
500 2020-01-01 test <test>
509 2020-01-01 test <test>
501
510
502 * fourth, second, third:
511 * fourth, second, third:
503 third
512 third
504 [95c24699272e] [tip]
513 [95c24699272e] [tip]
505
514
506 1970-01-12 User Name <user@hostname>
515 1970-01-12 User Name <user@hostname>
507
516
508 * second:
517 * second:
509 second
518 second
510 [29114dbae42b]
519 [29114dbae42b]
511
520
512 1970-01-18 person <person>
521 1970-01-18 person <person>
513
522
514 * merge
523 * merge
515 [d41e714fe50d]
524 [d41e714fe50d]
516
525
517 * d:
526 * d:
518 new head
527 new head
519 [13207e5a10d9]
528 [13207e5a10d9]
520
529
521 1970-01-17 person <person>
530 1970-01-17 person <person>
522
531
523 * new branch
532 * new branch
524 [bbe44766e73d] <foo>
533 [bbe44766e73d] <foo>
525
534
526 1970-01-16 person <person>
535 1970-01-16 person <person>
527
536
528 * c:
537 * c:
529 no user, no domain
538 no user, no domain
530 [10e46f2dcbf4]
539 [10e46f2dcbf4]
531
540
532 1970-01-14 other <other@place>
541 1970-01-14 other <other@place>
533
542
534 * c:
543 * c:
535 no person
544 no person
536 [97054abb4ab8]
545 [97054abb4ab8]
537
546
538 1970-01-13 A. N. Other <other@place>
547 1970-01-13 A. N. Other <other@place>
539
548
540 * b:
549 * b:
541 other 1 other 2
550 other 1 other 2
542
551
543 other 3
552 other 3
544 [b608e9d1a3f0]
553 [b608e9d1a3f0]
545
554
546 1970-01-12 User Name <user@hostname>
555 1970-01-12 User Name <user@hostname>
547
556
548 * a:
557 * a:
549 line 1 line 2
558 line 1 line 2
550 [1e4e1b8f71e0]
559 [1e4e1b8f71e0]
551
560
552
561
553 Issue2130: xml output for 'hg heads' is malformed
562 Issue2130: xml output for 'hg heads' is malformed
554
563
555 $ hg heads --style changelog
564 $ hg heads --style changelog
556 2020-01-01 test <test>
565 2020-01-01 test <test>
557
566
558 * fourth, second, third:
567 * fourth, second, third:
559 third
568 third
560 [95c24699272e] [tip]
569 [95c24699272e] [tip]
561
570
562 1970-01-18 person <person>
571 1970-01-18 person <person>
563
572
564 * merge
573 * merge
565 [d41e714fe50d]
574 [d41e714fe50d]
566
575
567 1970-01-17 person <person>
576 1970-01-17 person <person>
568
577
569 * new branch
578 * new branch
570 [bbe44766e73d] <foo>
579 [bbe44766e73d] <foo>
571
580
572
581
573 Keys work:
582 Keys work:
574
583
575 $ for key in author branch branches date desc file_adds file_dels file_mods \
584 $ for key in author branch branches date desc file_adds file_dels file_mods \
576 > file_copies file_copies_switch files \
585 > file_copies file_copies_switch files \
577 > manifest node parents rev tags diffstat extras; do
586 > manifest node parents rev tags diffstat extras; do
578 > for mode in '' --verbose --debug; do
587 > for mode in '' --verbose --debug; do
579 > hg log $mode --template "$key$mode: {$key}\n"
588 > hg log $mode --template "$key$mode: {$key}\n"
580 > done
589 > done
581 > done
590 > done
582 author: test
591 author: test
583 author: User Name <user@hostname>
592 author: User Name <user@hostname>
584 author: person
593 author: person
585 author: person
594 author: person
586 author: person
595 author: person
587 author: person
596 author: person
588 author: other@place
597 author: other@place
589 author: A. N. Other <other@place>
598 author: A. N. Other <other@place>
590 author: User Name <user@hostname>
599 author: User Name <user@hostname>
591 author--verbose: test
600 author--verbose: test
592 author--verbose: User Name <user@hostname>
601 author--verbose: User Name <user@hostname>
593 author--verbose: person
602 author--verbose: person
594 author--verbose: person
603 author--verbose: person
595 author--verbose: person
604 author--verbose: person
596 author--verbose: person
605 author--verbose: person
597 author--verbose: other@place
606 author--verbose: other@place
598 author--verbose: A. N. Other <other@place>
607 author--verbose: A. N. Other <other@place>
599 author--verbose: User Name <user@hostname>
608 author--verbose: User Name <user@hostname>
600 author--debug: test
609 author--debug: test
601 author--debug: User Name <user@hostname>
610 author--debug: User Name <user@hostname>
602 author--debug: person
611 author--debug: person
603 author--debug: person
612 author--debug: person
604 author--debug: person
613 author--debug: person
605 author--debug: person
614 author--debug: person
606 author--debug: other@place
615 author--debug: other@place
607 author--debug: A. N. Other <other@place>
616 author--debug: A. N. Other <other@place>
608 author--debug: User Name <user@hostname>
617 author--debug: User Name <user@hostname>
609 branch: default
618 branch: default
610 branch: default
619 branch: default
611 branch: default
620 branch: default
612 branch: default
621 branch: default
613 branch: foo
622 branch: foo
614 branch: default
623 branch: default
615 branch: default
624 branch: default
616 branch: default
625 branch: default
617 branch: default
626 branch: default
618 branch--verbose: default
627 branch--verbose: default
619 branch--verbose: default
628 branch--verbose: default
620 branch--verbose: default
629 branch--verbose: default
621 branch--verbose: default
630 branch--verbose: default
622 branch--verbose: foo
631 branch--verbose: foo
623 branch--verbose: default
632 branch--verbose: default
624 branch--verbose: default
633 branch--verbose: default
625 branch--verbose: default
634 branch--verbose: default
626 branch--verbose: default
635 branch--verbose: default
627 branch--debug: default
636 branch--debug: default
628 branch--debug: default
637 branch--debug: default
629 branch--debug: default
638 branch--debug: default
630 branch--debug: default
639 branch--debug: default
631 branch--debug: foo
640 branch--debug: foo
632 branch--debug: default
641 branch--debug: default
633 branch--debug: default
642 branch--debug: default
634 branch--debug: default
643 branch--debug: default
635 branch--debug: default
644 branch--debug: default
636 branches:
645 branches:
637 branches:
646 branches:
638 branches:
647 branches:
639 branches:
648 branches:
640 branches: foo
649 branches: foo
641 branches:
650 branches:
642 branches:
651 branches:
643 branches:
652 branches:
644 branches:
653 branches:
645 branches--verbose:
654 branches--verbose:
646 branches--verbose:
655 branches--verbose:
647 branches--verbose:
656 branches--verbose:
648 branches--verbose:
657 branches--verbose:
649 branches--verbose: foo
658 branches--verbose: foo
650 branches--verbose:
659 branches--verbose:
651 branches--verbose:
660 branches--verbose:
652 branches--verbose:
661 branches--verbose:
653 branches--verbose:
662 branches--verbose:
654 branches--debug:
663 branches--debug:
655 branches--debug:
664 branches--debug:
656 branches--debug:
665 branches--debug:
657 branches--debug:
666 branches--debug:
658 branches--debug: foo
667 branches--debug: foo
659 branches--debug:
668 branches--debug:
660 branches--debug:
669 branches--debug:
661 branches--debug:
670 branches--debug:
662 branches--debug:
671 branches--debug:
663 date: 1577872860.00
672 date: 1577872860.00
664 date: 1000000.00
673 date: 1000000.00
665 date: 1500001.00
674 date: 1500001.00
666 date: 1500000.00
675 date: 1500000.00
667 date: 1400000.00
676 date: 1400000.00
668 date: 1300000.00
677 date: 1300000.00
669 date: 1200000.00
678 date: 1200000.00
670 date: 1100000.00
679 date: 1100000.00
671 date: 1000000.00
680 date: 1000000.00
672 date--verbose: 1577872860.00
681 date--verbose: 1577872860.00
673 date--verbose: 1000000.00
682 date--verbose: 1000000.00
674 date--verbose: 1500001.00
683 date--verbose: 1500001.00
675 date--verbose: 1500000.00
684 date--verbose: 1500000.00
676 date--verbose: 1400000.00
685 date--verbose: 1400000.00
677 date--verbose: 1300000.00
686 date--verbose: 1300000.00
678 date--verbose: 1200000.00
687 date--verbose: 1200000.00
679 date--verbose: 1100000.00
688 date--verbose: 1100000.00
680 date--verbose: 1000000.00
689 date--verbose: 1000000.00
681 date--debug: 1577872860.00
690 date--debug: 1577872860.00
682 date--debug: 1000000.00
691 date--debug: 1000000.00
683 date--debug: 1500001.00
692 date--debug: 1500001.00
684 date--debug: 1500000.00
693 date--debug: 1500000.00
685 date--debug: 1400000.00
694 date--debug: 1400000.00
686 date--debug: 1300000.00
695 date--debug: 1300000.00
687 date--debug: 1200000.00
696 date--debug: 1200000.00
688 date--debug: 1100000.00
697 date--debug: 1100000.00
689 date--debug: 1000000.00
698 date--debug: 1000000.00
690 desc: third
699 desc: third
691 desc: second
700 desc: second
692 desc: merge
701 desc: merge
693 desc: new head
702 desc: new head
694 desc: new branch
703 desc: new branch
695 desc: no user, no domain
704 desc: no user, no domain
696 desc: no person
705 desc: no person
697 desc: other 1
706 desc: other 1
698 other 2
707 other 2
699
708
700 other 3
709 other 3
701 desc: line 1
710 desc: line 1
702 line 2
711 line 2
703 desc--verbose: third
712 desc--verbose: third
704 desc--verbose: second
713 desc--verbose: second
705 desc--verbose: merge
714 desc--verbose: merge
706 desc--verbose: new head
715 desc--verbose: new head
707 desc--verbose: new branch
716 desc--verbose: new branch
708 desc--verbose: no user, no domain
717 desc--verbose: no user, no domain
709 desc--verbose: no person
718 desc--verbose: no person
710 desc--verbose: other 1
719 desc--verbose: other 1
711 other 2
720 other 2
712
721
713 other 3
722 other 3
714 desc--verbose: line 1
723 desc--verbose: line 1
715 line 2
724 line 2
716 desc--debug: third
725 desc--debug: third
717 desc--debug: second
726 desc--debug: second
718 desc--debug: merge
727 desc--debug: merge
719 desc--debug: new head
728 desc--debug: new head
720 desc--debug: new branch
729 desc--debug: new branch
721 desc--debug: no user, no domain
730 desc--debug: no user, no domain
722 desc--debug: no person
731 desc--debug: no person
723 desc--debug: other 1
732 desc--debug: other 1
724 other 2
733 other 2
725
734
726 other 3
735 other 3
727 desc--debug: line 1
736 desc--debug: line 1
728 line 2
737 line 2
729 file_adds: fourth third
738 file_adds: fourth third
730 file_adds: second
739 file_adds: second
731 file_adds:
740 file_adds:
732 file_adds: d
741 file_adds: d
733 file_adds:
742 file_adds:
734 file_adds:
743 file_adds:
735 file_adds: c
744 file_adds: c
736 file_adds: b
745 file_adds: b
737 file_adds: a
746 file_adds: a
738 file_adds--verbose: fourth third
747 file_adds--verbose: fourth third
739 file_adds--verbose: second
748 file_adds--verbose: second
740 file_adds--verbose:
749 file_adds--verbose:
741 file_adds--verbose: d
750 file_adds--verbose: d
742 file_adds--verbose:
751 file_adds--verbose:
743 file_adds--verbose:
752 file_adds--verbose:
744 file_adds--verbose: c
753 file_adds--verbose: c
745 file_adds--verbose: b
754 file_adds--verbose: b
746 file_adds--verbose: a
755 file_adds--verbose: a
747 file_adds--debug: fourth third
756 file_adds--debug: fourth third
748 file_adds--debug: second
757 file_adds--debug: second
749 file_adds--debug:
758 file_adds--debug:
750 file_adds--debug: d
759 file_adds--debug: d
751 file_adds--debug:
760 file_adds--debug:
752 file_adds--debug:
761 file_adds--debug:
753 file_adds--debug: c
762 file_adds--debug: c
754 file_adds--debug: b
763 file_adds--debug: b
755 file_adds--debug: a
764 file_adds--debug: a
756 file_dels: second
765 file_dels: second
757 file_dels:
766 file_dels:
758 file_dels:
767 file_dels:
759 file_dels:
768 file_dels:
760 file_dels:
769 file_dels:
761 file_dels:
770 file_dels:
762 file_dels:
771 file_dels:
763 file_dels:
772 file_dels:
764 file_dels:
773 file_dels:
765 file_dels--verbose: second
774 file_dels--verbose: second
766 file_dels--verbose:
775 file_dels--verbose:
767 file_dels--verbose:
776 file_dels--verbose:
768 file_dels--verbose:
777 file_dels--verbose:
769 file_dels--verbose:
778 file_dels--verbose:
770 file_dels--verbose:
779 file_dels--verbose:
771 file_dels--verbose:
780 file_dels--verbose:
772 file_dels--verbose:
781 file_dels--verbose:
773 file_dels--verbose:
782 file_dels--verbose:
774 file_dels--debug: second
783 file_dels--debug: second
775 file_dels--debug:
784 file_dels--debug:
776 file_dels--debug:
785 file_dels--debug:
777 file_dels--debug:
786 file_dels--debug:
778 file_dels--debug:
787 file_dels--debug:
779 file_dels--debug:
788 file_dels--debug:
780 file_dels--debug:
789 file_dels--debug:
781 file_dels--debug:
790 file_dels--debug:
782 file_dels--debug:
791 file_dels--debug:
783 file_mods:
792 file_mods:
784 file_mods:
793 file_mods:
785 file_mods:
794 file_mods:
786 file_mods:
795 file_mods:
787 file_mods:
796 file_mods:
788 file_mods: c
797 file_mods: c
789 file_mods:
798 file_mods:
790 file_mods:
799 file_mods:
791 file_mods:
800 file_mods:
792 file_mods--verbose:
801 file_mods--verbose:
793 file_mods--verbose:
802 file_mods--verbose:
794 file_mods--verbose:
803 file_mods--verbose:
795 file_mods--verbose:
804 file_mods--verbose:
796 file_mods--verbose:
805 file_mods--verbose:
797 file_mods--verbose: c
806 file_mods--verbose: c
798 file_mods--verbose:
807 file_mods--verbose:
799 file_mods--verbose:
808 file_mods--verbose:
800 file_mods--verbose:
809 file_mods--verbose:
801 file_mods--debug:
810 file_mods--debug:
802 file_mods--debug:
811 file_mods--debug:
803 file_mods--debug:
812 file_mods--debug:
804 file_mods--debug:
813 file_mods--debug:
805 file_mods--debug:
814 file_mods--debug:
806 file_mods--debug: c
815 file_mods--debug: c
807 file_mods--debug:
816 file_mods--debug:
808 file_mods--debug:
817 file_mods--debug:
809 file_mods--debug:
818 file_mods--debug:
810 file_copies: fourth (second)
819 file_copies: fourth (second)
811 file_copies:
820 file_copies:
812 file_copies:
821 file_copies:
813 file_copies:
822 file_copies:
814 file_copies:
823 file_copies:
815 file_copies:
824 file_copies:
816 file_copies:
825 file_copies:
817 file_copies:
826 file_copies:
818 file_copies:
827 file_copies:
819 file_copies--verbose: fourth (second)
828 file_copies--verbose: fourth (second)
820 file_copies--verbose:
829 file_copies--verbose:
821 file_copies--verbose:
830 file_copies--verbose:
822 file_copies--verbose:
831 file_copies--verbose:
823 file_copies--verbose:
832 file_copies--verbose:
824 file_copies--verbose:
833 file_copies--verbose:
825 file_copies--verbose:
834 file_copies--verbose:
826 file_copies--verbose:
835 file_copies--verbose:
827 file_copies--verbose:
836 file_copies--verbose:
828 file_copies--debug: fourth (second)
837 file_copies--debug: fourth (second)
829 file_copies--debug:
838 file_copies--debug:
830 file_copies--debug:
839 file_copies--debug:
831 file_copies--debug:
840 file_copies--debug:
832 file_copies--debug:
841 file_copies--debug:
833 file_copies--debug:
842 file_copies--debug:
834 file_copies--debug:
843 file_copies--debug:
835 file_copies--debug:
844 file_copies--debug:
836 file_copies--debug:
845 file_copies--debug:
837 file_copies_switch:
846 file_copies_switch:
838 file_copies_switch:
847 file_copies_switch:
839 file_copies_switch:
848 file_copies_switch:
840 file_copies_switch:
849 file_copies_switch:
841 file_copies_switch:
850 file_copies_switch:
842 file_copies_switch:
851 file_copies_switch:
843 file_copies_switch:
852 file_copies_switch:
844 file_copies_switch:
853 file_copies_switch:
845 file_copies_switch:
854 file_copies_switch:
846 file_copies_switch--verbose:
855 file_copies_switch--verbose:
847 file_copies_switch--verbose:
856 file_copies_switch--verbose:
848 file_copies_switch--verbose:
857 file_copies_switch--verbose:
849 file_copies_switch--verbose:
858 file_copies_switch--verbose:
850 file_copies_switch--verbose:
859 file_copies_switch--verbose:
851 file_copies_switch--verbose:
860 file_copies_switch--verbose:
852 file_copies_switch--verbose:
861 file_copies_switch--verbose:
853 file_copies_switch--verbose:
862 file_copies_switch--verbose:
854 file_copies_switch--verbose:
863 file_copies_switch--verbose:
855 file_copies_switch--debug:
864 file_copies_switch--debug:
856 file_copies_switch--debug:
865 file_copies_switch--debug:
857 file_copies_switch--debug:
866 file_copies_switch--debug:
858 file_copies_switch--debug:
867 file_copies_switch--debug:
859 file_copies_switch--debug:
868 file_copies_switch--debug:
860 file_copies_switch--debug:
869 file_copies_switch--debug:
861 file_copies_switch--debug:
870 file_copies_switch--debug:
862 file_copies_switch--debug:
871 file_copies_switch--debug:
863 file_copies_switch--debug:
872 file_copies_switch--debug:
864 files: fourth second third
873 files: fourth second third
865 files: second
874 files: second
866 files:
875 files:
867 files: d
876 files: d
868 files:
877 files:
869 files: c
878 files: c
870 files: c
879 files: c
871 files: b
880 files: b
872 files: a
881 files: a
873 files--verbose: fourth second third
882 files--verbose: fourth second third
874 files--verbose: second
883 files--verbose: second
875 files--verbose:
884 files--verbose:
876 files--verbose: d
885 files--verbose: d
877 files--verbose:
886 files--verbose:
878 files--verbose: c
887 files--verbose: c
879 files--verbose: c
888 files--verbose: c
880 files--verbose: b
889 files--verbose: b
881 files--verbose: a
890 files--verbose: a
882 files--debug: fourth second third
891 files--debug: fourth second third
883 files--debug: second
892 files--debug: second
884 files--debug:
893 files--debug:
885 files--debug: d
894 files--debug: d
886 files--debug:
895 files--debug:
887 files--debug: c
896 files--debug: c
888 files--debug: c
897 files--debug: c
889 files--debug: b
898 files--debug: b
890 files--debug: a
899 files--debug: a
891 manifest: 6:94961b75a2da
900 manifest: 6:94961b75a2da
892 manifest: 5:f2dbc354b94e
901 manifest: 5:f2dbc354b94e
893 manifest: 4:4dc3def4f9b4
902 manifest: 4:4dc3def4f9b4
894 manifest: 4:4dc3def4f9b4
903 manifest: 4:4dc3def4f9b4
895 manifest: 3:cb5a1327723b
904 manifest: 3:cb5a1327723b
896 manifest: 3:cb5a1327723b
905 manifest: 3:cb5a1327723b
897 manifest: 2:6e0e82995c35
906 manifest: 2:6e0e82995c35
898 manifest: 1:4e8d705b1e53
907 manifest: 1:4e8d705b1e53
899 manifest: 0:a0c8bcbbb45c
908 manifest: 0:a0c8bcbbb45c
900 manifest--verbose: 6:94961b75a2da
909 manifest--verbose: 6:94961b75a2da
901 manifest--verbose: 5:f2dbc354b94e
910 manifest--verbose: 5:f2dbc354b94e
902 manifest--verbose: 4:4dc3def4f9b4
911 manifest--verbose: 4:4dc3def4f9b4
903 manifest--verbose: 4:4dc3def4f9b4
912 manifest--verbose: 4:4dc3def4f9b4
904 manifest--verbose: 3:cb5a1327723b
913 manifest--verbose: 3:cb5a1327723b
905 manifest--verbose: 3:cb5a1327723b
914 manifest--verbose: 3:cb5a1327723b
906 manifest--verbose: 2:6e0e82995c35
915 manifest--verbose: 2:6e0e82995c35
907 manifest--verbose: 1:4e8d705b1e53
916 manifest--verbose: 1:4e8d705b1e53
908 manifest--verbose: 0:a0c8bcbbb45c
917 manifest--verbose: 0:a0c8bcbbb45c
909 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
918 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
910 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
919 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
911 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
920 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
912 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
921 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
913 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
922 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
914 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
923 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
915 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
924 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
916 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
925 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
917 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
926 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
918 node: 95c24699272ef57d062b8bccc32c878bf841784a
927 node: 95c24699272ef57d062b8bccc32c878bf841784a
919 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
928 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
920 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
929 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
921 node: 13207e5a10d9fd28ec424934298e176197f2c67f
930 node: 13207e5a10d9fd28ec424934298e176197f2c67f
922 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
931 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
923 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
932 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
924 node: 97054abb4ab824450e9164180baf491ae0078465
933 node: 97054abb4ab824450e9164180baf491ae0078465
925 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
934 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
926 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
935 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
927 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
936 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
928 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
937 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
929 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
938 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
930 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
939 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
931 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
940 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
932 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
941 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
933 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
942 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
934 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
943 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
935 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
944 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
936 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
945 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
937 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
946 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
938 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
947 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
939 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
948 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
940 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
949 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
941 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
950 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
942 node--debug: 97054abb4ab824450e9164180baf491ae0078465
951 node--debug: 97054abb4ab824450e9164180baf491ae0078465
943 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
952 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
944 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
953 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
945 parents:
954 parents:
946 parents: -1:000000000000
955 parents: -1:000000000000
947 parents: 5:13207e5a10d9 4:bbe44766e73d
956 parents: 5:13207e5a10d9 4:bbe44766e73d
948 parents: 3:10e46f2dcbf4
957 parents: 3:10e46f2dcbf4
949 parents:
958 parents:
950 parents:
959 parents:
951 parents:
960 parents:
952 parents:
961 parents:
953 parents:
962 parents:
954 parents--verbose:
963 parents--verbose:
955 parents--verbose: -1:000000000000
964 parents--verbose: -1:000000000000
956 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
965 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
957 parents--verbose: 3:10e46f2dcbf4
966 parents--verbose: 3:10e46f2dcbf4
958 parents--verbose:
967 parents--verbose:
959 parents--verbose:
968 parents--verbose:
960 parents--verbose:
969 parents--verbose:
961 parents--verbose:
970 parents--verbose:
962 parents--verbose:
971 parents--verbose:
963 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
972 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
964 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
973 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
965 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
974 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
966 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
975 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
967 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
976 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
968 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
977 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
969 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
978 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
970 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
979 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
971 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
980 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
972 rev: 8
981 rev: 8
973 rev: 7
982 rev: 7
974 rev: 6
983 rev: 6
975 rev: 5
984 rev: 5
976 rev: 4
985 rev: 4
977 rev: 3
986 rev: 3
978 rev: 2
987 rev: 2
979 rev: 1
988 rev: 1
980 rev: 0
989 rev: 0
981 rev--verbose: 8
990 rev--verbose: 8
982 rev--verbose: 7
991 rev--verbose: 7
983 rev--verbose: 6
992 rev--verbose: 6
984 rev--verbose: 5
993 rev--verbose: 5
985 rev--verbose: 4
994 rev--verbose: 4
986 rev--verbose: 3
995 rev--verbose: 3
987 rev--verbose: 2
996 rev--verbose: 2
988 rev--verbose: 1
997 rev--verbose: 1
989 rev--verbose: 0
998 rev--verbose: 0
990 rev--debug: 8
999 rev--debug: 8
991 rev--debug: 7
1000 rev--debug: 7
992 rev--debug: 6
1001 rev--debug: 6
993 rev--debug: 5
1002 rev--debug: 5
994 rev--debug: 4
1003 rev--debug: 4
995 rev--debug: 3
1004 rev--debug: 3
996 rev--debug: 2
1005 rev--debug: 2
997 rev--debug: 1
1006 rev--debug: 1
998 rev--debug: 0
1007 rev--debug: 0
999 tags: tip
1008 tags: tip
1000 tags:
1009 tags:
1001 tags:
1010 tags:
1002 tags:
1011 tags:
1003 tags:
1012 tags:
1004 tags:
1013 tags:
1005 tags:
1014 tags:
1006 tags:
1015 tags:
1007 tags:
1016 tags:
1008 tags--verbose: tip
1017 tags--verbose: tip
1009 tags--verbose:
1018 tags--verbose:
1010 tags--verbose:
1019 tags--verbose:
1011 tags--verbose:
1020 tags--verbose:
1012 tags--verbose:
1021 tags--verbose:
1013 tags--verbose:
1022 tags--verbose:
1014 tags--verbose:
1023 tags--verbose:
1015 tags--verbose:
1024 tags--verbose:
1016 tags--verbose:
1025 tags--verbose:
1017 tags--debug: tip
1026 tags--debug: tip
1018 tags--debug:
1027 tags--debug:
1019 tags--debug:
1028 tags--debug:
1020 tags--debug:
1029 tags--debug:
1021 tags--debug:
1030 tags--debug:
1022 tags--debug:
1031 tags--debug:
1023 tags--debug:
1032 tags--debug:
1024 tags--debug:
1033 tags--debug:
1025 tags--debug:
1034 tags--debug:
1026 diffstat: 3: +2/-1
1035 diffstat: 3: +2/-1
1027 diffstat: 1: +1/-0
1036 diffstat: 1: +1/-0
1028 diffstat: 0: +0/-0
1037 diffstat: 0: +0/-0
1029 diffstat: 1: +1/-0
1038 diffstat: 1: +1/-0
1030 diffstat: 0: +0/-0
1039 diffstat: 0: +0/-0
1031 diffstat: 1: +1/-0
1040 diffstat: 1: +1/-0
1032 diffstat: 1: +4/-0
1041 diffstat: 1: +4/-0
1033 diffstat: 1: +2/-0
1042 diffstat: 1: +2/-0
1034 diffstat: 1: +1/-0
1043 diffstat: 1: +1/-0
1035 diffstat--verbose: 3: +2/-1
1044 diffstat--verbose: 3: +2/-1
1036 diffstat--verbose: 1: +1/-0
1045 diffstat--verbose: 1: +1/-0
1037 diffstat--verbose: 0: +0/-0
1046 diffstat--verbose: 0: +0/-0
1038 diffstat--verbose: 1: +1/-0
1047 diffstat--verbose: 1: +1/-0
1039 diffstat--verbose: 0: +0/-0
1048 diffstat--verbose: 0: +0/-0
1040 diffstat--verbose: 1: +1/-0
1049 diffstat--verbose: 1: +1/-0
1041 diffstat--verbose: 1: +4/-0
1050 diffstat--verbose: 1: +4/-0
1042 diffstat--verbose: 1: +2/-0
1051 diffstat--verbose: 1: +2/-0
1043 diffstat--verbose: 1: +1/-0
1052 diffstat--verbose: 1: +1/-0
1044 diffstat--debug: 3: +2/-1
1053 diffstat--debug: 3: +2/-1
1045 diffstat--debug: 1: +1/-0
1054 diffstat--debug: 1: +1/-0
1046 diffstat--debug: 0: +0/-0
1055 diffstat--debug: 0: +0/-0
1047 diffstat--debug: 1: +1/-0
1056 diffstat--debug: 1: +1/-0
1048 diffstat--debug: 0: +0/-0
1057 diffstat--debug: 0: +0/-0
1049 diffstat--debug: 1: +1/-0
1058 diffstat--debug: 1: +1/-0
1050 diffstat--debug: 1: +4/-0
1059 diffstat--debug: 1: +4/-0
1051 diffstat--debug: 1: +2/-0
1060 diffstat--debug: 1: +2/-0
1052 diffstat--debug: 1: +1/-0
1061 diffstat--debug: 1: +1/-0
1053 extras: branch=default
1062 extras: branch=default
1054 extras: branch=default
1063 extras: branch=default
1055 extras: branch=default
1064 extras: branch=default
1056 extras: branch=default
1065 extras: branch=default
1057 extras: branch=foo
1066 extras: branch=foo
1058 extras: branch=default
1067 extras: branch=default
1059 extras: branch=default
1068 extras: branch=default
1060 extras: branch=default
1069 extras: branch=default
1061 extras: branch=default
1070 extras: branch=default
1062 extras--verbose: branch=default
1071 extras--verbose: branch=default
1063 extras--verbose: branch=default
1072 extras--verbose: branch=default
1064 extras--verbose: branch=default
1073 extras--verbose: branch=default
1065 extras--verbose: branch=default
1074 extras--verbose: branch=default
1066 extras--verbose: branch=foo
1075 extras--verbose: branch=foo
1067 extras--verbose: branch=default
1076 extras--verbose: branch=default
1068 extras--verbose: branch=default
1077 extras--verbose: branch=default
1069 extras--verbose: branch=default
1078 extras--verbose: branch=default
1070 extras--verbose: branch=default
1079 extras--verbose: branch=default
1071 extras--debug: branch=default
1080 extras--debug: branch=default
1072 extras--debug: branch=default
1081 extras--debug: branch=default
1073 extras--debug: branch=default
1082 extras--debug: branch=default
1074 extras--debug: branch=default
1083 extras--debug: branch=default
1075 extras--debug: branch=foo
1084 extras--debug: branch=foo
1076 extras--debug: branch=default
1085 extras--debug: branch=default
1077 extras--debug: branch=default
1086 extras--debug: branch=default
1078 extras--debug: branch=default
1087 extras--debug: branch=default
1079 extras--debug: branch=default
1088 extras--debug: branch=default
1080
1089
1081
1090
1082 Filters work:
1091 Filters work:
1083
1092
1084 $ hg log --template '{author|domain}\n'
1093 $ hg log --template '{author|domain}\n'
1085
1094
1086 hostname
1095 hostname
1087
1096
1088
1097
1089
1098
1090
1099
1091 place
1100 place
1092 place
1101 place
1093 hostname
1102 hostname
1094
1103
1095 $ hg log --template '{author|person}\n'
1104 $ hg log --template '{author|person}\n'
1096 test
1105 test
1097 User Name
1106 User Name
1098 person
1107 person
1099 person
1108 person
1100 person
1109 person
1101 person
1110 person
1102 other
1111 other
1103 A. N. Other
1112 A. N. Other
1104 User Name
1113 User Name
1105
1114
1106 $ hg log --template '{author|user}\n'
1115 $ hg log --template '{author|user}\n'
1107 test
1116 test
1108 user
1117 user
1109 person
1118 person
1110 person
1119 person
1111 person
1120 person
1112 person
1121 person
1113 other
1122 other
1114 other
1123 other
1115 user
1124 user
1116
1125
1117 $ hg log --template '{date|date}\n'
1126 $ hg log --template '{date|date}\n'
1118 Wed Jan 01 10:01:00 2020 +0000
1127 Wed Jan 01 10:01:00 2020 +0000
1119 Mon Jan 12 13:46:40 1970 +0000
1128 Mon Jan 12 13:46:40 1970 +0000
1120 Sun Jan 18 08:40:01 1970 +0000
1129 Sun Jan 18 08:40:01 1970 +0000
1121 Sun Jan 18 08:40:00 1970 +0000
1130 Sun Jan 18 08:40:00 1970 +0000
1122 Sat Jan 17 04:53:20 1970 +0000
1131 Sat Jan 17 04:53:20 1970 +0000
1123 Fri Jan 16 01:06:40 1970 +0000
1132 Fri Jan 16 01:06:40 1970 +0000
1124 Wed Jan 14 21:20:00 1970 +0000
1133 Wed Jan 14 21:20:00 1970 +0000
1125 Tue Jan 13 17:33:20 1970 +0000
1134 Tue Jan 13 17:33:20 1970 +0000
1126 Mon Jan 12 13:46:40 1970 +0000
1135 Mon Jan 12 13:46:40 1970 +0000
1127
1136
1128 $ hg log --template '{date|isodate}\n'
1137 $ hg log --template '{date|isodate}\n'
1129 2020-01-01 10:01 +0000
1138 2020-01-01 10:01 +0000
1130 1970-01-12 13:46 +0000
1139 1970-01-12 13:46 +0000
1131 1970-01-18 08:40 +0000
1140 1970-01-18 08:40 +0000
1132 1970-01-18 08:40 +0000
1141 1970-01-18 08:40 +0000
1133 1970-01-17 04:53 +0000
1142 1970-01-17 04:53 +0000
1134 1970-01-16 01:06 +0000
1143 1970-01-16 01:06 +0000
1135 1970-01-14 21:20 +0000
1144 1970-01-14 21:20 +0000
1136 1970-01-13 17:33 +0000
1145 1970-01-13 17:33 +0000
1137 1970-01-12 13:46 +0000
1146 1970-01-12 13:46 +0000
1138
1147
1139 $ hg log --template '{date|isodatesec}\n'
1148 $ hg log --template '{date|isodatesec}\n'
1140 2020-01-01 10:01:00 +0000
1149 2020-01-01 10:01:00 +0000
1141 1970-01-12 13:46:40 +0000
1150 1970-01-12 13:46:40 +0000
1142 1970-01-18 08:40:01 +0000
1151 1970-01-18 08:40:01 +0000
1143 1970-01-18 08:40:00 +0000
1152 1970-01-18 08:40:00 +0000
1144 1970-01-17 04:53:20 +0000
1153 1970-01-17 04:53:20 +0000
1145 1970-01-16 01:06:40 +0000
1154 1970-01-16 01:06:40 +0000
1146 1970-01-14 21:20:00 +0000
1155 1970-01-14 21:20:00 +0000
1147 1970-01-13 17:33:20 +0000
1156 1970-01-13 17:33:20 +0000
1148 1970-01-12 13:46:40 +0000
1157 1970-01-12 13:46:40 +0000
1149
1158
1150 $ hg log --template '{date|rfc822date}\n'
1159 $ hg log --template '{date|rfc822date}\n'
1151 Wed, 01 Jan 2020 10:01:00 +0000
1160 Wed, 01 Jan 2020 10:01:00 +0000
1152 Mon, 12 Jan 1970 13:46:40 +0000
1161 Mon, 12 Jan 1970 13:46:40 +0000
1153 Sun, 18 Jan 1970 08:40:01 +0000
1162 Sun, 18 Jan 1970 08:40:01 +0000
1154 Sun, 18 Jan 1970 08:40:00 +0000
1163 Sun, 18 Jan 1970 08:40:00 +0000
1155 Sat, 17 Jan 1970 04:53:20 +0000
1164 Sat, 17 Jan 1970 04:53:20 +0000
1156 Fri, 16 Jan 1970 01:06:40 +0000
1165 Fri, 16 Jan 1970 01:06:40 +0000
1157 Wed, 14 Jan 1970 21:20:00 +0000
1166 Wed, 14 Jan 1970 21:20:00 +0000
1158 Tue, 13 Jan 1970 17:33:20 +0000
1167 Tue, 13 Jan 1970 17:33:20 +0000
1159 Mon, 12 Jan 1970 13:46:40 +0000
1168 Mon, 12 Jan 1970 13:46:40 +0000
1160
1169
1161 $ hg log --template '{desc|firstline}\n'
1170 $ hg log --template '{desc|firstline}\n'
1162 third
1171 third
1163 second
1172 second
1164 merge
1173 merge
1165 new head
1174 new head
1166 new branch
1175 new branch
1167 no user, no domain
1176 no user, no domain
1168 no person
1177 no person
1169 other 1
1178 other 1
1170 line 1
1179 line 1
1171
1180
1172 $ hg log --template '{node|short}\n'
1181 $ hg log --template '{node|short}\n'
1173 95c24699272e
1182 95c24699272e
1174 29114dbae42b
1183 29114dbae42b
1175 d41e714fe50d
1184 d41e714fe50d
1176 13207e5a10d9
1185 13207e5a10d9
1177 bbe44766e73d
1186 bbe44766e73d
1178 10e46f2dcbf4
1187 10e46f2dcbf4
1179 97054abb4ab8
1188 97054abb4ab8
1180 b608e9d1a3f0
1189 b608e9d1a3f0
1181 1e4e1b8f71e0
1190 1e4e1b8f71e0
1182
1191
1183 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1192 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1184 <changeset author="test"/>
1193 <changeset author="test"/>
1185 <changeset author="User Name &lt;user@hostname&gt;"/>
1194 <changeset author="User Name &lt;user@hostname&gt;"/>
1186 <changeset author="person"/>
1195 <changeset author="person"/>
1187 <changeset author="person"/>
1196 <changeset author="person"/>
1188 <changeset author="person"/>
1197 <changeset author="person"/>
1189 <changeset author="person"/>
1198 <changeset author="person"/>
1190 <changeset author="other@place"/>
1199 <changeset author="other@place"/>
1191 <changeset author="A. N. Other &lt;other@place&gt;"/>
1200 <changeset author="A. N. Other &lt;other@place&gt;"/>
1192 <changeset author="User Name &lt;user@hostname&gt;"/>
1201 <changeset author="User Name &lt;user@hostname&gt;"/>
1193
1202
1194 $ hg log --template '{rev}: {children}\n'
1203 $ hg log --template '{rev}: {children}\n'
1195 8:
1204 8:
1196 7: 8:95c24699272e
1205 7: 8:95c24699272e
1197 6:
1206 6:
1198 5: 6:d41e714fe50d
1207 5: 6:d41e714fe50d
1199 4: 6:d41e714fe50d
1208 4: 6:d41e714fe50d
1200 3: 4:bbe44766e73d 5:13207e5a10d9
1209 3: 4:bbe44766e73d 5:13207e5a10d9
1201 2: 3:10e46f2dcbf4
1210 2: 3:10e46f2dcbf4
1202 1: 2:97054abb4ab8
1211 1: 2:97054abb4ab8
1203 0: 1:b608e9d1a3f0
1212 0: 1:b608e9d1a3f0
1204
1213
1205 Formatnode filter works:
1214 Formatnode filter works:
1206
1215
1207 $ hg -q log -r 0 --template '{node|formatnode}\n'
1216 $ hg -q log -r 0 --template '{node|formatnode}\n'
1208 1e4e1b8f71e0
1217 1e4e1b8f71e0
1209
1218
1210 $ hg log -r 0 --template '{node|formatnode}\n'
1219 $ hg log -r 0 --template '{node|formatnode}\n'
1211 1e4e1b8f71e0
1220 1e4e1b8f71e0
1212
1221
1213 $ hg -v log -r 0 --template '{node|formatnode}\n'
1222 $ hg -v log -r 0 --template '{node|formatnode}\n'
1214 1e4e1b8f71e0
1223 1e4e1b8f71e0
1215
1224
1216 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1225 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1217 1e4e1b8f71e05681d422154f5421e385fec3454f
1226 1e4e1b8f71e05681d422154f5421e385fec3454f
1218
1227
1219 Age filter:
1228 Age filter:
1220
1229
1221 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1230 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1222
1231
1223 >>> from datetime import datetime
1232 >>> from datetime import datetime
1224 >>> fp = open('a', 'w')
1233 >>> fp = open('a', 'w')
1225 >>> fp.write(str(datetime.now().year + 8) + '-01-01 00:00')
1234 >>> fp.write(str(datetime.now().year + 8) + '-01-01 00:00')
1226 >>> fp.close()
1235 >>> fp.close()
1227 $ hg add a
1236 $ hg add a
1228 $ hg commit -m future -d "`cat a`"
1237 $ hg commit -m future -d "`cat a`"
1229
1238
1230 $ hg log -l1 --template '{date|age}\n'
1239 $ hg log -l1 --template '{date|age}\n'
1231 7 years from now
1240 7 years from now
1232
1241
1233 Error on syntax:
1242 Error on syntax:
1234
1243
1235 $ echo 'x = "f' >> t
1244 $ echo 'x = "f' >> t
1236 $ hg log
1245 $ hg log
1237 abort: t:3: unmatched quotes
1246 abort: t:3: unmatched quotes
1238 [255]
1247 [255]
1239
1248
1240 $ cd ..
1249 $ cd ..
1241
1250
1242
1251
1243 latesttag:
1252 latesttag:
1244
1253
1245 $ hg init latesttag
1254 $ hg init latesttag
1246 $ cd latesttag
1255 $ cd latesttag
1247
1256
1248 $ echo a > file
1257 $ echo a > file
1249 $ hg ci -Am a -d '0 0'
1258 $ hg ci -Am a -d '0 0'
1250 adding file
1259 adding file
1251
1260
1252 $ echo b >> file
1261 $ echo b >> file
1253 $ hg ci -m b -d '1 0'
1262 $ hg ci -m b -d '1 0'
1254
1263
1255 $ echo c >> head1
1264 $ echo c >> head1
1256 $ hg ci -Am h1c -d '2 0'
1265 $ hg ci -Am h1c -d '2 0'
1257 adding head1
1266 adding head1
1258
1267
1259 $ hg update -q 1
1268 $ hg update -q 1
1260 $ echo d >> head2
1269 $ echo d >> head2
1261 $ hg ci -Am h2d -d '3 0'
1270 $ hg ci -Am h2d -d '3 0'
1262 adding head2
1271 adding head2
1263 created new head
1272 created new head
1264
1273
1265 $ echo e >> head2
1274 $ echo e >> head2
1266 $ hg ci -m h2e -d '4 0'
1275 $ hg ci -m h2e -d '4 0'
1267
1276
1268 $ hg merge -q
1277 $ hg merge -q
1269 $ hg ci -m merge -d '5 0'
1278 $ hg ci -m merge -d '5 0'
1270
1279
1271 No tag set:
1280 No tag set:
1272
1281
1273 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1282 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1274 5: null+5
1283 5: null+5
1275 4: null+4
1284 4: null+4
1276 3: null+3
1285 3: null+3
1277 2: null+3
1286 2: null+3
1278 1: null+2
1287 1: null+2
1279 0: null+1
1288 0: null+1
1280
1289
1281 One common tag: longuest path wins:
1290 One common tag: longuest path wins:
1282
1291
1283 $ hg tag -r 1 -m t1 -d '6 0' t1
1292 $ hg tag -r 1 -m t1 -d '6 0' t1
1284 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1293 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1285 6: t1+4
1294 6: t1+4
1286 5: t1+3
1295 5: t1+3
1287 4: t1+2
1296 4: t1+2
1288 3: t1+1
1297 3: t1+1
1289 2: t1+1
1298 2: t1+1
1290 1: t1+0
1299 1: t1+0
1291 0: null+1
1300 0: null+1
1292
1301
1293 One ancestor tag: more recent wins:
1302 One ancestor tag: more recent wins:
1294
1303
1295 $ hg tag -r 2 -m t2 -d '7 0' t2
1304 $ hg tag -r 2 -m t2 -d '7 0' t2
1296 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1305 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1297 7: t2+3
1306 7: t2+3
1298 6: t2+2
1307 6: t2+2
1299 5: t2+1
1308 5: t2+1
1300 4: t1+2
1309 4: t1+2
1301 3: t1+1
1310 3: t1+1
1302 2: t2+0
1311 2: t2+0
1303 1: t1+0
1312 1: t1+0
1304 0: null+1
1313 0: null+1
1305
1314
1306 Two branch tags: more recent wins:
1315 Two branch tags: more recent wins:
1307
1316
1308 $ hg tag -r 3 -m t3 -d '8 0' t3
1317 $ hg tag -r 3 -m t3 -d '8 0' t3
1309 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1318 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1310 8: t3+5
1319 8: t3+5
1311 7: t3+4
1320 7: t3+4
1312 6: t3+3
1321 6: t3+3
1313 5: t3+2
1322 5: t3+2
1314 4: t3+1
1323 4: t3+1
1315 3: t3+0
1324 3: t3+0
1316 2: t2+0
1325 2: t2+0
1317 1: t1+0
1326 1: t1+0
1318 0: null+1
1327 0: null+1
1319
1328
1320 Merged tag overrides:
1329 Merged tag overrides:
1321
1330
1322 $ hg tag -r 5 -m t5 -d '9 0' t5
1331 $ hg tag -r 5 -m t5 -d '9 0' t5
1323 $ hg tag -r 3 -m at3 -d '10 0' at3
1332 $ hg tag -r 3 -m at3 -d '10 0' at3
1324 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1333 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1325 10: t5+5
1334 10: t5+5
1326 9: t5+4
1335 9: t5+4
1327 8: t5+3
1336 8: t5+3
1328 7: t5+2
1337 7: t5+2
1329 6: t5+1
1338 6: t5+1
1330 5: t5+0
1339 5: t5+0
1331 4: at3:t3+1
1340 4: at3:t3+1
1332 3: at3:t3+0
1341 3: at3:t3+0
1333 2: t2+0
1342 2: t2+0
1334 1: t1+0
1343 1: t1+0
1335 0: null+1
1344 0: null+1
1336
1345
1337 $ cd ..
1346 $ cd ..
1338
1347
1339
1348
1340 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1349 Style path expansion: issue1948 - ui.style option doesn't work on OSX
1341 if it is a relative path
1350 if it is a relative path
1342
1351
1343 $ mkdir -p home/styles
1352 $ mkdir -p home/styles
1344
1353
1345 $ cat > home/styles/teststyle <<EOF
1354 $ cat > home/styles/teststyle <<EOF
1346 > changeset = 'test {rev}:{node|short}\n'
1355 > changeset = 'test {rev}:{node|short}\n'
1347 > EOF
1356 > EOF
1348
1357
1349 $ HOME=`pwd`/home; export HOME
1358 $ HOME=`pwd`/home; export HOME
1350
1359
1351 $ cat > latesttag/.hg/hgrc <<EOF
1360 $ cat > latesttag/.hg/hgrc <<EOF
1352 > [ui]
1361 > [ui]
1353 > style = ~/styles/teststyle
1362 > style = ~/styles/teststyle
1354 > EOF
1363 > EOF
1355
1364
1356 $ hg -R latesttag tip
1365 $ hg -R latesttag tip
1357 test 10:dee8f28249af
1366 test 10:dee8f28249af
1358
1367
1359 Test recursive showlist template (issue1989):
1368 Test recursive showlist template (issue1989):
1360
1369
1361 $ cat > style1989 <<EOF
1370 $ cat > style1989 <<EOF
1362 > changeset = '{file_mods}{manifest}{extras}'
1371 > changeset = '{file_mods}{manifest}{extras}'
1363 > file_mod = 'M|{author|person}\n'
1372 > file_mod = 'M|{author|person}\n'
1364 > manifest = '{rev},{author}\n'
1373 > manifest = '{rev},{author}\n'
1365 > extra = '{key}: {author}\n'
1374 > extra = '{key}: {author}\n'
1366 > EOF
1375 > EOF
1367
1376
1368 $ hg -R latesttag log -r tip --style=style1989
1377 $ hg -R latesttag log -r tip --style=style1989
1369 M|test
1378 M|test
1370 10,test
1379 10,test
1371 branch: test
1380 branch: test
1372
1381
General Comments 0
You need to be logged in to leave comments. Login now