##// END OF EJS Templates
cmdutil.jsonchangeset: properly compute added and removed files...
Gregory Szorc -
r23734:f4e64759 stable
parent child Browse files
Show More
@@ -1,2888 +1,2888 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 context, repair, graphmod, revset, phases, obsolete, pathutil
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import encoding
16 import encoding
17 import lock as lockmod
17 import lock as lockmod
18
18
19 def parsealiases(cmd):
19 def parsealiases(cmd):
20 return cmd.lstrip("^").split("|")
20 return cmd.lstrip("^").split("|")
21
21
22 def findpossible(cmd, table, strict=False):
22 def findpossible(cmd, table, strict=False):
23 """
23 """
24 Return cmd -> (aliases, command table entry)
24 Return cmd -> (aliases, command table entry)
25 for each matching command.
25 for each matching command.
26 Return debug commands (or their aliases) only if no normal command matches.
26 Return debug commands (or their aliases) only if no normal command matches.
27 """
27 """
28 choice = {}
28 choice = {}
29 debugchoice = {}
29 debugchoice = {}
30
30
31 if cmd in table:
31 if cmd in table:
32 # short-circuit exact matches, "log" alias beats "^log|history"
32 # short-circuit exact matches, "log" alias beats "^log|history"
33 keys = [cmd]
33 keys = [cmd]
34 else:
34 else:
35 keys = table.keys()
35 keys = table.keys()
36
36
37 for e in keys:
37 for e in keys:
38 aliases = parsealiases(e)
38 aliases = parsealiases(e)
39 found = None
39 found = None
40 if cmd in aliases:
40 if cmd in aliases:
41 found = cmd
41 found = cmd
42 elif not strict:
42 elif not strict:
43 for a in aliases:
43 for a in aliases:
44 if a.startswith(cmd):
44 if a.startswith(cmd):
45 found = a
45 found = a
46 break
46 break
47 if found is not None:
47 if found is not None:
48 if aliases[0].startswith("debug") or found.startswith("debug"):
48 if aliases[0].startswith("debug") or found.startswith("debug"):
49 debugchoice[found] = (aliases, table[e])
49 debugchoice[found] = (aliases, table[e])
50 else:
50 else:
51 choice[found] = (aliases, table[e])
51 choice[found] = (aliases, table[e])
52
52
53 if not choice and debugchoice:
53 if not choice and debugchoice:
54 choice = debugchoice
54 choice = debugchoice
55
55
56 return choice
56 return choice
57
57
58 def findcmd(cmd, table, strict=True):
58 def findcmd(cmd, table, strict=True):
59 """Return (aliases, command table entry) for command string."""
59 """Return (aliases, command table entry) for command string."""
60 choice = findpossible(cmd, table, strict)
60 choice = findpossible(cmd, table, strict)
61
61
62 if cmd in choice:
62 if cmd in choice:
63 return choice[cmd]
63 return choice[cmd]
64
64
65 if len(choice) > 1:
65 if len(choice) > 1:
66 clist = choice.keys()
66 clist = choice.keys()
67 clist.sort()
67 clist.sort()
68 raise error.AmbiguousCommand(cmd, clist)
68 raise error.AmbiguousCommand(cmd, clist)
69
69
70 if choice:
70 if choice:
71 return choice.values()[0]
71 return choice.values()[0]
72
72
73 raise error.UnknownCommand(cmd)
73 raise error.UnknownCommand(cmd)
74
74
75 def findrepo(p):
75 def findrepo(p):
76 while not os.path.isdir(os.path.join(p, ".hg")):
76 while not os.path.isdir(os.path.join(p, ".hg")):
77 oldp, p = p, os.path.dirname(p)
77 oldp, p = p, os.path.dirname(p)
78 if p == oldp:
78 if p == oldp:
79 return None
79 return None
80
80
81 return p
81 return p
82
82
83 def bailifchanged(repo):
83 def bailifchanged(repo):
84 if repo.dirstate.p2() != nullid:
84 if repo.dirstate.p2() != nullid:
85 raise util.Abort(_('outstanding uncommitted merge'))
85 raise util.Abort(_('outstanding uncommitted merge'))
86 modified, added, removed, deleted = repo.status()[:4]
86 modified, added, removed, deleted = repo.status()[:4]
87 if modified or added or removed or deleted:
87 if modified or added or removed or deleted:
88 raise util.Abort(_('uncommitted changes'))
88 raise util.Abort(_('uncommitted changes'))
89 ctx = repo[None]
89 ctx = repo[None]
90 for s in sorted(ctx.substate):
90 for s in sorted(ctx.substate):
91 if ctx.sub(s).dirty():
91 if ctx.sub(s).dirty():
92 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
92 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
93
93
94 def logmessage(ui, opts):
94 def logmessage(ui, opts):
95 """ get the log message according to -m and -l option """
95 """ get the log message according to -m and -l option """
96 message = opts.get('message')
96 message = opts.get('message')
97 logfile = opts.get('logfile')
97 logfile = opts.get('logfile')
98
98
99 if message and logfile:
99 if message and logfile:
100 raise util.Abort(_('options --message and --logfile are mutually '
100 raise util.Abort(_('options --message and --logfile are mutually '
101 'exclusive'))
101 'exclusive'))
102 if not message and logfile:
102 if not message and logfile:
103 try:
103 try:
104 if logfile == '-':
104 if logfile == '-':
105 message = ui.fin.read()
105 message = ui.fin.read()
106 else:
106 else:
107 message = '\n'.join(util.readfile(logfile).splitlines())
107 message = '\n'.join(util.readfile(logfile).splitlines())
108 except IOError, inst:
108 except IOError, inst:
109 raise util.Abort(_("can't read commit message '%s': %s") %
109 raise util.Abort(_("can't read commit message '%s': %s") %
110 (logfile, inst.strerror))
110 (logfile, inst.strerror))
111 return message
111 return message
112
112
113 def mergeeditform(ctxorbool, baseform):
113 def mergeeditform(ctxorbool, baseform):
114 """build appropriate editform from ctxorbool and baseform
114 """build appropriate editform from ctxorbool and baseform
115
115
116 'cxtorbool' is one of a ctx to be committed, or a bool whether
116 'cxtorbool' is one of a ctx to be committed, or a bool whether
117 merging is committed.
117 merging is committed.
118
118
119 This returns editform 'baseform' with '.merge' if merging is
119 This returns editform 'baseform' with '.merge' if merging is
120 committed, or one with '.normal' suffix otherwise.
120 committed, or one with '.normal' suffix otherwise.
121 """
121 """
122 if isinstance(ctxorbool, bool):
122 if isinstance(ctxorbool, bool):
123 if ctxorbool:
123 if ctxorbool:
124 return baseform + ".merge"
124 return baseform + ".merge"
125 elif 1 < len(ctxorbool.parents()):
125 elif 1 < len(ctxorbool.parents()):
126 return baseform + ".merge"
126 return baseform + ".merge"
127
127
128 return baseform + ".normal"
128 return baseform + ".normal"
129
129
130 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
130 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
131 editform='', **opts):
131 editform='', **opts):
132 """get appropriate commit message editor according to '--edit' option
132 """get appropriate commit message editor according to '--edit' option
133
133
134 'finishdesc' is a function to be called with edited commit message
134 'finishdesc' is a function to be called with edited commit message
135 (= 'description' of the new changeset) just after editing, but
135 (= 'description' of the new changeset) just after editing, but
136 before checking empty-ness. It should return actual text to be
136 before checking empty-ness. It should return actual text to be
137 stored into history. This allows to change description before
137 stored into history. This allows to change description before
138 storing.
138 storing.
139
139
140 'extramsg' is a extra message to be shown in the editor instead of
140 'extramsg' is a extra message to be shown in the editor instead of
141 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
141 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
142 is automatically added.
142 is automatically added.
143
143
144 'editform' is a dot-separated list of names, to distinguish
144 'editform' is a dot-separated list of names, to distinguish
145 the purpose of commit text editing.
145 the purpose of commit text editing.
146
146
147 'getcommiteditor' returns 'commitforceeditor' regardless of
147 'getcommiteditor' returns 'commitforceeditor' regardless of
148 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
148 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
149 they are specific for usage in MQ.
149 they are specific for usage in MQ.
150 """
150 """
151 if edit or finishdesc or extramsg:
151 if edit or finishdesc or extramsg:
152 return lambda r, c, s: commitforceeditor(r, c, s,
152 return lambda r, c, s: commitforceeditor(r, c, s,
153 finishdesc=finishdesc,
153 finishdesc=finishdesc,
154 extramsg=extramsg,
154 extramsg=extramsg,
155 editform=editform)
155 editform=editform)
156 elif editform:
156 elif editform:
157 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
157 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
158 else:
158 else:
159 return commiteditor
159 return commiteditor
160
160
161 def loglimit(opts):
161 def loglimit(opts):
162 """get the log limit according to option -l/--limit"""
162 """get the log limit according to option -l/--limit"""
163 limit = opts.get('limit')
163 limit = opts.get('limit')
164 if limit:
164 if limit:
165 try:
165 try:
166 limit = int(limit)
166 limit = int(limit)
167 except ValueError:
167 except ValueError:
168 raise util.Abort(_('limit must be a positive integer'))
168 raise util.Abort(_('limit must be a positive integer'))
169 if limit <= 0:
169 if limit <= 0:
170 raise util.Abort(_('limit must be positive'))
170 raise util.Abort(_('limit must be positive'))
171 else:
171 else:
172 limit = None
172 limit = None
173 return limit
173 return limit
174
174
175 def makefilename(repo, pat, node, desc=None,
175 def makefilename(repo, pat, node, desc=None,
176 total=None, seqno=None, revwidth=None, pathname=None):
176 total=None, seqno=None, revwidth=None, pathname=None):
177 node_expander = {
177 node_expander = {
178 'H': lambda: hex(node),
178 'H': lambda: hex(node),
179 'R': lambda: str(repo.changelog.rev(node)),
179 'R': lambda: str(repo.changelog.rev(node)),
180 'h': lambda: short(node),
180 'h': lambda: short(node),
181 'm': lambda: re.sub('[^\w]', '_', str(desc))
181 'm': lambda: re.sub('[^\w]', '_', str(desc))
182 }
182 }
183 expander = {
183 expander = {
184 '%': lambda: '%',
184 '%': lambda: '%',
185 'b': lambda: os.path.basename(repo.root),
185 'b': lambda: os.path.basename(repo.root),
186 }
186 }
187
187
188 try:
188 try:
189 if node:
189 if node:
190 expander.update(node_expander)
190 expander.update(node_expander)
191 if node:
191 if node:
192 expander['r'] = (lambda:
192 expander['r'] = (lambda:
193 str(repo.changelog.rev(node)).zfill(revwidth or 0))
193 str(repo.changelog.rev(node)).zfill(revwidth or 0))
194 if total is not None:
194 if total is not None:
195 expander['N'] = lambda: str(total)
195 expander['N'] = lambda: str(total)
196 if seqno is not None:
196 if seqno is not None:
197 expander['n'] = lambda: str(seqno)
197 expander['n'] = lambda: str(seqno)
198 if total is not None and seqno is not None:
198 if total is not None and seqno is not None:
199 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
199 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
200 if pathname is not None:
200 if pathname is not None:
201 expander['s'] = lambda: os.path.basename(pathname)
201 expander['s'] = lambda: os.path.basename(pathname)
202 expander['d'] = lambda: os.path.dirname(pathname) or '.'
202 expander['d'] = lambda: os.path.dirname(pathname) or '.'
203 expander['p'] = lambda: pathname
203 expander['p'] = lambda: pathname
204
204
205 newname = []
205 newname = []
206 patlen = len(pat)
206 patlen = len(pat)
207 i = 0
207 i = 0
208 while i < patlen:
208 while i < patlen:
209 c = pat[i]
209 c = pat[i]
210 if c == '%':
210 if c == '%':
211 i += 1
211 i += 1
212 c = pat[i]
212 c = pat[i]
213 c = expander[c]()
213 c = expander[c]()
214 newname.append(c)
214 newname.append(c)
215 i += 1
215 i += 1
216 return ''.join(newname)
216 return ''.join(newname)
217 except KeyError, inst:
217 except KeyError, inst:
218 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
218 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
219 inst.args[0])
219 inst.args[0])
220
220
221 def makefileobj(repo, pat, node=None, desc=None, total=None,
221 def makefileobj(repo, pat, node=None, desc=None, total=None,
222 seqno=None, revwidth=None, mode='wb', modemap=None,
222 seqno=None, revwidth=None, mode='wb', modemap=None,
223 pathname=None):
223 pathname=None):
224
224
225 writable = mode not in ('r', 'rb')
225 writable = mode not in ('r', 'rb')
226
226
227 if not pat or pat == '-':
227 if not pat or pat == '-':
228 fp = writable and repo.ui.fout or repo.ui.fin
228 fp = writable and repo.ui.fout or repo.ui.fin
229 if util.safehasattr(fp, 'fileno'):
229 if util.safehasattr(fp, 'fileno'):
230 return os.fdopen(os.dup(fp.fileno()), mode)
230 return os.fdopen(os.dup(fp.fileno()), mode)
231 else:
231 else:
232 # if this fp can't be duped properly, return
232 # if this fp can't be duped properly, return
233 # a dummy object that can be closed
233 # a dummy object that can be closed
234 class wrappedfileobj(object):
234 class wrappedfileobj(object):
235 noop = lambda x: None
235 noop = lambda x: None
236 def __init__(self, f):
236 def __init__(self, f):
237 self.f = f
237 self.f = f
238 def __getattr__(self, attr):
238 def __getattr__(self, attr):
239 if attr == 'close':
239 if attr == 'close':
240 return self.noop
240 return self.noop
241 else:
241 else:
242 return getattr(self.f, attr)
242 return getattr(self.f, attr)
243
243
244 return wrappedfileobj(fp)
244 return wrappedfileobj(fp)
245 if util.safehasattr(pat, 'write') and writable:
245 if util.safehasattr(pat, 'write') and writable:
246 return pat
246 return pat
247 if util.safehasattr(pat, 'read') and 'r' in mode:
247 if util.safehasattr(pat, 'read') and 'r' in mode:
248 return pat
248 return pat
249 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
249 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
250 if modemap is not None:
250 if modemap is not None:
251 mode = modemap.get(fn, mode)
251 mode = modemap.get(fn, mode)
252 if mode == 'wb':
252 if mode == 'wb':
253 modemap[fn] = 'ab'
253 modemap[fn] = 'ab'
254 return open(fn, mode)
254 return open(fn, mode)
255
255
256 def openrevlog(repo, cmd, file_, opts):
256 def openrevlog(repo, cmd, file_, opts):
257 """opens the changelog, manifest, a filelog or a given revlog"""
257 """opens the changelog, manifest, a filelog or a given revlog"""
258 cl = opts['changelog']
258 cl = opts['changelog']
259 mf = opts['manifest']
259 mf = opts['manifest']
260 msg = None
260 msg = None
261 if cl and mf:
261 if cl and mf:
262 msg = _('cannot specify --changelog and --manifest at the same time')
262 msg = _('cannot specify --changelog and --manifest at the same time')
263 elif cl or mf:
263 elif cl or mf:
264 if file_:
264 if file_:
265 msg = _('cannot specify filename with --changelog or --manifest')
265 msg = _('cannot specify filename with --changelog or --manifest')
266 elif not repo:
266 elif not repo:
267 msg = _('cannot specify --changelog or --manifest '
267 msg = _('cannot specify --changelog or --manifest '
268 'without a repository')
268 'without a repository')
269 if msg:
269 if msg:
270 raise util.Abort(msg)
270 raise util.Abort(msg)
271
271
272 r = None
272 r = None
273 if repo:
273 if repo:
274 if cl:
274 if cl:
275 r = repo.unfiltered().changelog
275 r = repo.unfiltered().changelog
276 elif mf:
276 elif mf:
277 r = repo.manifest
277 r = repo.manifest
278 elif file_:
278 elif file_:
279 filelog = repo.file(file_)
279 filelog = repo.file(file_)
280 if len(filelog):
280 if len(filelog):
281 r = filelog
281 r = filelog
282 if not r:
282 if not r:
283 if not file_:
283 if not file_:
284 raise error.CommandError(cmd, _('invalid arguments'))
284 raise error.CommandError(cmd, _('invalid arguments'))
285 if not os.path.isfile(file_):
285 if not os.path.isfile(file_):
286 raise util.Abort(_("revlog '%s' not found") % file_)
286 raise util.Abort(_("revlog '%s' not found") % file_)
287 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
287 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
288 file_[:-2] + ".i")
288 file_[:-2] + ".i")
289 return r
289 return r
290
290
291 def copy(ui, repo, pats, opts, rename=False):
291 def copy(ui, repo, pats, opts, rename=False):
292 # called with the repo lock held
292 # called with the repo lock held
293 #
293 #
294 # hgsep => pathname that uses "/" to separate directories
294 # hgsep => pathname that uses "/" to separate directories
295 # ossep => pathname that uses os.sep to separate directories
295 # ossep => pathname that uses os.sep to separate directories
296 cwd = repo.getcwd()
296 cwd = repo.getcwd()
297 targets = {}
297 targets = {}
298 after = opts.get("after")
298 after = opts.get("after")
299 dryrun = opts.get("dry_run")
299 dryrun = opts.get("dry_run")
300 wctx = repo[None]
300 wctx = repo[None]
301
301
302 def walkpat(pat):
302 def walkpat(pat):
303 srcs = []
303 srcs = []
304 badstates = after and '?' or '?r'
304 badstates = after and '?' or '?r'
305 m = scmutil.match(repo[None], [pat], opts, globbed=True)
305 m = scmutil.match(repo[None], [pat], opts, globbed=True)
306 for abs in repo.walk(m):
306 for abs in repo.walk(m):
307 state = repo.dirstate[abs]
307 state = repo.dirstate[abs]
308 rel = m.rel(abs)
308 rel = m.rel(abs)
309 exact = m.exact(abs)
309 exact = m.exact(abs)
310 if state in badstates:
310 if state in badstates:
311 if exact and state == '?':
311 if exact and state == '?':
312 ui.warn(_('%s: not copying - file is not managed\n') % rel)
312 ui.warn(_('%s: not copying - file is not managed\n') % rel)
313 if exact and state == 'r':
313 if exact and state == 'r':
314 ui.warn(_('%s: not copying - file has been marked for'
314 ui.warn(_('%s: not copying - file has been marked for'
315 ' remove\n') % rel)
315 ' remove\n') % rel)
316 continue
316 continue
317 # abs: hgsep
317 # abs: hgsep
318 # rel: ossep
318 # rel: ossep
319 srcs.append((abs, rel, exact))
319 srcs.append((abs, rel, exact))
320 return srcs
320 return srcs
321
321
322 # abssrc: hgsep
322 # abssrc: hgsep
323 # relsrc: ossep
323 # relsrc: ossep
324 # otarget: ossep
324 # otarget: ossep
325 def copyfile(abssrc, relsrc, otarget, exact):
325 def copyfile(abssrc, relsrc, otarget, exact):
326 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
326 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
327 if '/' in abstarget:
327 if '/' in abstarget:
328 # We cannot normalize abstarget itself, this would prevent
328 # We cannot normalize abstarget itself, this would prevent
329 # case only renames, like a => A.
329 # case only renames, like a => A.
330 abspath, absname = abstarget.rsplit('/', 1)
330 abspath, absname = abstarget.rsplit('/', 1)
331 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
331 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
332 reltarget = repo.pathto(abstarget, cwd)
332 reltarget = repo.pathto(abstarget, cwd)
333 target = repo.wjoin(abstarget)
333 target = repo.wjoin(abstarget)
334 src = repo.wjoin(abssrc)
334 src = repo.wjoin(abssrc)
335 state = repo.dirstate[abstarget]
335 state = repo.dirstate[abstarget]
336
336
337 scmutil.checkportable(ui, abstarget)
337 scmutil.checkportable(ui, abstarget)
338
338
339 # check for collisions
339 # check for collisions
340 prevsrc = targets.get(abstarget)
340 prevsrc = targets.get(abstarget)
341 if prevsrc is not None:
341 if prevsrc is not None:
342 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
342 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
343 (reltarget, repo.pathto(abssrc, cwd),
343 (reltarget, repo.pathto(abssrc, cwd),
344 repo.pathto(prevsrc, cwd)))
344 repo.pathto(prevsrc, cwd)))
345 return
345 return
346
346
347 # check for overwrites
347 # check for overwrites
348 exists = os.path.lexists(target)
348 exists = os.path.lexists(target)
349 samefile = False
349 samefile = False
350 if exists and abssrc != abstarget:
350 if exists and abssrc != abstarget:
351 if (repo.dirstate.normalize(abssrc) ==
351 if (repo.dirstate.normalize(abssrc) ==
352 repo.dirstate.normalize(abstarget)):
352 repo.dirstate.normalize(abstarget)):
353 if not rename:
353 if not rename:
354 ui.warn(_("%s: can't copy - same file\n") % reltarget)
354 ui.warn(_("%s: can't copy - same file\n") % reltarget)
355 return
355 return
356 exists = False
356 exists = False
357 samefile = True
357 samefile = True
358
358
359 if not after and exists or after and state in 'mn':
359 if not after and exists or after and state in 'mn':
360 if not opts['force']:
360 if not opts['force']:
361 ui.warn(_('%s: not overwriting - file exists\n') %
361 ui.warn(_('%s: not overwriting - file exists\n') %
362 reltarget)
362 reltarget)
363 return
363 return
364
364
365 if after:
365 if after:
366 if not exists:
366 if not exists:
367 if rename:
367 if rename:
368 ui.warn(_('%s: not recording move - %s does not exist\n') %
368 ui.warn(_('%s: not recording move - %s does not exist\n') %
369 (relsrc, reltarget))
369 (relsrc, reltarget))
370 else:
370 else:
371 ui.warn(_('%s: not recording copy - %s does not exist\n') %
371 ui.warn(_('%s: not recording copy - %s does not exist\n') %
372 (relsrc, reltarget))
372 (relsrc, reltarget))
373 return
373 return
374 elif not dryrun:
374 elif not dryrun:
375 try:
375 try:
376 if exists:
376 if exists:
377 os.unlink(target)
377 os.unlink(target)
378 targetdir = os.path.dirname(target) or '.'
378 targetdir = os.path.dirname(target) or '.'
379 if not os.path.isdir(targetdir):
379 if not os.path.isdir(targetdir):
380 os.makedirs(targetdir)
380 os.makedirs(targetdir)
381 if samefile:
381 if samefile:
382 tmp = target + "~hgrename"
382 tmp = target + "~hgrename"
383 os.rename(src, tmp)
383 os.rename(src, tmp)
384 os.rename(tmp, target)
384 os.rename(tmp, target)
385 else:
385 else:
386 util.copyfile(src, target)
386 util.copyfile(src, target)
387 srcexists = True
387 srcexists = True
388 except IOError, inst:
388 except IOError, inst:
389 if inst.errno == errno.ENOENT:
389 if inst.errno == errno.ENOENT:
390 ui.warn(_('%s: deleted in working copy\n') % relsrc)
390 ui.warn(_('%s: deleted in working copy\n') % relsrc)
391 srcexists = False
391 srcexists = False
392 else:
392 else:
393 ui.warn(_('%s: cannot copy - %s\n') %
393 ui.warn(_('%s: cannot copy - %s\n') %
394 (relsrc, inst.strerror))
394 (relsrc, inst.strerror))
395 return True # report a failure
395 return True # report a failure
396
396
397 if ui.verbose or not exact:
397 if ui.verbose or not exact:
398 if rename:
398 if rename:
399 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
399 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
400 else:
400 else:
401 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
401 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
402
402
403 targets[abstarget] = abssrc
403 targets[abstarget] = abssrc
404
404
405 # fix up dirstate
405 # fix up dirstate
406 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
406 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
407 dryrun=dryrun, cwd=cwd)
407 dryrun=dryrun, cwd=cwd)
408 if rename and not dryrun:
408 if rename and not dryrun:
409 if not after and srcexists and not samefile:
409 if not after and srcexists and not samefile:
410 util.unlinkpath(repo.wjoin(abssrc))
410 util.unlinkpath(repo.wjoin(abssrc))
411 wctx.forget([abssrc])
411 wctx.forget([abssrc])
412
412
413 # pat: ossep
413 # pat: ossep
414 # dest ossep
414 # dest ossep
415 # srcs: list of (hgsep, hgsep, ossep, bool)
415 # srcs: list of (hgsep, hgsep, ossep, bool)
416 # return: function that takes hgsep and returns ossep
416 # return: function that takes hgsep and returns ossep
417 def targetpathfn(pat, dest, srcs):
417 def targetpathfn(pat, dest, srcs):
418 if os.path.isdir(pat):
418 if os.path.isdir(pat):
419 abspfx = pathutil.canonpath(repo.root, cwd, pat)
419 abspfx = pathutil.canonpath(repo.root, cwd, pat)
420 abspfx = util.localpath(abspfx)
420 abspfx = util.localpath(abspfx)
421 if destdirexists:
421 if destdirexists:
422 striplen = len(os.path.split(abspfx)[0])
422 striplen = len(os.path.split(abspfx)[0])
423 else:
423 else:
424 striplen = len(abspfx)
424 striplen = len(abspfx)
425 if striplen:
425 if striplen:
426 striplen += len(os.sep)
426 striplen += len(os.sep)
427 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
427 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
428 elif destdirexists:
428 elif destdirexists:
429 res = lambda p: os.path.join(dest,
429 res = lambda p: os.path.join(dest,
430 os.path.basename(util.localpath(p)))
430 os.path.basename(util.localpath(p)))
431 else:
431 else:
432 res = lambda p: dest
432 res = lambda p: dest
433 return res
433 return res
434
434
435 # pat: ossep
435 # pat: ossep
436 # dest ossep
436 # dest ossep
437 # srcs: list of (hgsep, hgsep, ossep, bool)
437 # srcs: list of (hgsep, hgsep, ossep, bool)
438 # return: function that takes hgsep and returns ossep
438 # return: function that takes hgsep and returns ossep
439 def targetpathafterfn(pat, dest, srcs):
439 def targetpathafterfn(pat, dest, srcs):
440 if matchmod.patkind(pat):
440 if matchmod.patkind(pat):
441 # a mercurial pattern
441 # a mercurial pattern
442 res = lambda p: os.path.join(dest,
442 res = lambda p: os.path.join(dest,
443 os.path.basename(util.localpath(p)))
443 os.path.basename(util.localpath(p)))
444 else:
444 else:
445 abspfx = pathutil.canonpath(repo.root, cwd, pat)
445 abspfx = pathutil.canonpath(repo.root, cwd, pat)
446 if len(abspfx) < len(srcs[0][0]):
446 if len(abspfx) < len(srcs[0][0]):
447 # A directory. Either the target path contains the last
447 # A directory. Either the target path contains the last
448 # component of the source path or it does not.
448 # component of the source path or it does not.
449 def evalpath(striplen):
449 def evalpath(striplen):
450 score = 0
450 score = 0
451 for s in srcs:
451 for s in srcs:
452 t = os.path.join(dest, util.localpath(s[0])[striplen:])
452 t = os.path.join(dest, util.localpath(s[0])[striplen:])
453 if os.path.lexists(t):
453 if os.path.lexists(t):
454 score += 1
454 score += 1
455 return score
455 return score
456
456
457 abspfx = util.localpath(abspfx)
457 abspfx = util.localpath(abspfx)
458 striplen = len(abspfx)
458 striplen = len(abspfx)
459 if striplen:
459 if striplen:
460 striplen += len(os.sep)
460 striplen += len(os.sep)
461 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
461 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
462 score = evalpath(striplen)
462 score = evalpath(striplen)
463 striplen1 = len(os.path.split(abspfx)[0])
463 striplen1 = len(os.path.split(abspfx)[0])
464 if striplen1:
464 if striplen1:
465 striplen1 += len(os.sep)
465 striplen1 += len(os.sep)
466 if evalpath(striplen1) > score:
466 if evalpath(striplen1) > score:
467 striplen = striplen1
467 striplen = striplen1
468 res = lambda p: os.path.join(dest,
468 res = lambda p: os.path.join(dest,
469 util.localpath(p)[striplen:])
469 util.localpath(p)[striplen:])
470 else:
470 else:
471 # a file
471 # a file
472 if destdirexists:
472 if destdirexists:
473 res = lambda p: os.path.join(dest,
473 res = lambda p: os.path.join(dest,
474 os.path.basename(util.localpath(p)))
474 os.path.basename(util.localpath(p)))
475 else:
475 else:
476 res = lambda p: dest
476 res = lambda p: dest
477 return res
477 return res
478
478
479
479
480 pats = scmutil.expandpats(pats)
480 pats = scmutil.expandpats(pats)
481 if not pats:
481 if not pats:
482 raise util.Abort(_('no source or destination specified'))
482 raise util.Abort(_('no source or destination specified'))
483 if len(pats) == 1:
483 if len(pats) == 1:
484 raise util.Abort(_('no destination specified'))
484 raise util.Abort(_('no destination specified'))
485 dest = pats.pop()
485 dest = pats.pop()
486 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
486 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
487 if not destdirexists:
487 if not destdirexists:
488 if len(pats) > 1 or matchmod.patkind(pats[0]):
488 if len(pats) > 1 or matchmod.patkind(pats[0]):
489 raise util.Abort(_('with multiple sources, destination must be an '
489 raise util.Abort(_('with multiple sources, destination must be an '
490 'existing directory'))
490 'existing directory'))
491 if util.endswithsep(dest):
491 if util.endswithsep(dest):
492 raise util.Abort(_('destination %s is not a directory') % dest)
492 raise util.Abort(_('destination %s is not a directory') % dest)
493
493
494 tfn = targetpathfn
494 tfn = targetpathfn
495 if after:
495 if after:
496 tfn = targetpathafterfn
496 tfn = targetpathafterfn
497 copylist = []
497 copylist = []
498 for pat in pats:
498 for pat in pats:
499 srcs = walkpat(pat)
499 srcs = walkpat(pat)
500 if not srcs:
500 if not srcs:
501 continue
501 continue
502 copylist.append((tfn(pat, dest, srcs), srcs))
502 copylist.append((tfn(pat, dest, srcs), srcs))
503 if not copylist:
503 if not copylist:
504 raise util.Abort(_('no files to copy'))
504 raise util.Abort(_('no files to copy'))
505
505
506 errors = 0
506 errors = 0
507 for targetpath, srcs in copylist:
507 for targetpath, srcs in copylist:
508 for abssrc, relsrc, exact in srcs:
508 for abssrc, relsrc, exact in srcs:
509 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
509 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
510 errors += 1
510 errors += 1
511
511
512 if errors:
512 if errors:
513 ui.warn(_('(consider using --after)\n'))
513 ui.warn(_('(consider using --after)\n'))
514
514
515 return errors != 0
515 return errors != 0
516
516
517 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
517 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
518 runargs=None, appendpid=False):
518 runargs=None, appendpid=False):
519 '''Run a command as a service.'''
519 '''Run a command as a service.'''
520
520
521 def writepid(pid):
521 def writepid(pid):
522 if opts['pid_file']:
522 if opts['pid_file']:
523 mode = appendpid and 'a' or 'w'
523 mode = appendpid and 'a' or 'w'
524 fp = open(opts['pid_file'], mode)
524 fp = open(opts['pid_file'], mode)
525 fp.write(str(pid) + '\n')
525 fp.write(str(pid) + '\n')
526 fp.close()
526 fp.close()
527
527
528 if opts['daemon'] and not opts['daemon_pipefds']:
528 if opts['daemon'] and not opts['daemon_pipefds']:
529 # Signal child process startup with file removal
529 # Signal child process startup with file removal
530 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
530 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
531 os.close(lockfd)
531 os.close(lockfd)
532 try:
532 try:
533 if not runargs:
533 if not runargs:
534 runargs = util.hgcmd() + sys.argv[1:]
534 runargs = util.hgcmd() + sys.argv[1:]
535 runargs.append('--daemon-pipefds=%s' % lockpath)
535 runargs.append('--daemon-pipefds=%s' % lockpath)
536 # Don't pass --cwd to the child process, because we've already
536 # Don't pass --cwd to the child process, because we've already
537 # changed directory.
537 # changed directory.
538 for i in xrange(1, len(runargs)):
538 for i in xrange(1, len(runargs)):
539 if runargs[i].startswith('--cwd='):
539 if runargs[i].startswith('--cwd='):
540 del runargs[i]
540 del runargs[i]
541 break
541 break
542 elif runargs[i].startswith('--cwd'):
542 elif runargs[i].startswith('--cwd'):
543 del runargs[i:i + 2]
543 del runargs[i:i + 2]
544 break
544 break
545 def condfn():
545 def condfn():
546 return not os.path.exists(lockpath)
546 return not os.path.exists(lockpath)
547 pid = util.rundetached(runargs, condfn)
547 pid = util.rundetached(runargs, condfn)
548 if pid < 0:
548 if pid < 0:
549 raise util.Abort(_('child process failed to start'))
549 raise util.Abort(_('child process failed to start'))
550 writepid(pid)
550 writepid(pid)
551 finally:
551 finally:
552 try:
552 try:
553 os.unlink(lockpath)
553 os.unlink(lockpath)
554 except OSError, e:
554 except OSError, e:
555 if e.errno != errno.ENOENT:
555 if e.errno != errno.ENOENT:
556 raise
556 raise
557 if parentfn:
557 if parentfn:
558 return parentfn(pid)
558 return parentfn(pid)
559 else:
559 else:
560 return
560 return
561
561
562 if initfn:
562 if initfn:
563 initfn()
563 initfn()
564
564
565 if not opts['daemon']:
565 if not opts['daemon']:
566 writepid(os.getpid())
566 writepid(os.getpid())
567
567
568 if opts['daemon_pipefds']:
568 if opts['daemon_pipefds']:
569 lockpath = opts['daemon_pipefds']
569 lockpath = opts['daemon_pipefds']
570 try:
570 try:
571 os.setsid()
571 os.setsid()
572 except AttributeError:
572 except AttributeError:
573 pass
573 pass
574 os.unlink(lockpath)
574 os.unlink(lockpath)
575 util.hidewindow()
575 util.hidewindow()
576 sys.stdout.flush()
576 sys.stdout.flush()
577 sys.stderr.flush()
577 sys.stderr.flush()
578
578
579 nullfd = os.open(os.devnull, os.O_RDWR)
579 nullfd = os.open(os.devnull, os.O_RDWR)
580 logfilefd = nullfd
580 logfilefd = nullfd
581 if logfile:
581 if logfile:
582 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
582 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
583 os.dup2(nullfd, 0)
583 os.dup2(nullfd, 0)
584 os.dup2(logfilefd, 1)
584 os.dup2(logfilefd, 1)
585 os.dup2(logfilefd, 2)
585 os.dup2(logfilefd, 2)
586 if nullfd not in (0, 1, 2):
586 if nullfd not in (0, 1, 2):
587 os.close(nullfd)
587 os.close(nullfd)
588 if logfile and logfilefd not in (0, 1, 2):
588 if logfile and logfilefd not in (0, 1, 2):
589 os.close(logfilefd)
589 os.close(logfilefd)
590
590
591 if runfn:
591 if runfn:
592 return runfn()
592 return runfn()
593
593
594 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
594 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
595 """Utility function used by commands.import to import a single patch
595 """Utility function used by commands.import to import a single patch
596
596
597 This function is explicitly defined here to help the evolve extension to
597 This function is explicitly defined here to help the evolve extension to
598 wrap this part of the import logic.
598 wrap this part of the import logic.
599
599
600 The API is currently a bit ugly because it a simple code translation from
600 The API is currently a bit ugly because it a simple code translation from
601 the import command. Feel free to make it better.
601 the import command. Feel free to make it better.
602
602
603 :hunk: a patch (as a binary string)
603 :hunk: a patch (as a binary string)
604 :parents: nodes that will be parent of the created commit
604 :parents: nodes that will be parent of the created commit
605 :opts: the full dict of option passed to the import command
605 :opts: the full dict of option passed to the import command
606 :msgs: list to save commit message to.
606 :msgs: list to save commit message to.
607 (used in case we need to save it when failing)
607 (used in case we need to save it when failing)
608 :updatefunc: a function that update a repo to a given node
608 :updatefunc: a function that update a repo to a given node
609 updatefunc(<repo>, <node>)
609 updatefunc(<repo>, <node>)
610 """
610 """
611 tmpname, message, user, date, branch, nodeid, p1, p2 = \
611 tmpname, message, user, date, branch, nodeid, p1, p2 = \
612 patch.extract(ui, hunk)
612 patch.extract(ui, hunk)
613
613
614 update = not opts.get('bypass')
614 update = not opts.get('bypass')
615 strip = opts["strip"]
615 strip = opts["strip"]
616 sim = float(opts.get('similarity') or 0)
616 sim = float(opts.get('similarity') or 0)
617 if not tmpname:
617 if not tmpname:
618 return (None, None, False)
618 return (None, None, False)
619 msg = _('applied to working directory')
619 msg = _('applied to working directory')
620
620
621 rejects = False
621 rejects = False
622
622
623 try:
623 try:
624 cmdline_message = logmessage(ui, opts)
624 cmdline_message = logmessage(ui, opts)
625 if cmdline_message:
625 if cmdline_message:
626 # pickup the cmdline msg
626 # pickup the cmdline msg
627 message = cmdline_message
627 message = cmdline_message
628 elif message:
628 elif message:
629 # pickup the patch msg
629 # pickup the patch msg
630 message = message.strip()
630 message = message.strip()
631 else:
631 else:
632 # launch the editor
632 # launch the editor
633 message = None
633 message = None
634 ui.debug('message:\n%s\n' % message)
634 ui.debug('message:\n%s\n' % message)
635
635
636 if len(parents) == 1:
636 if len(parents) == 1:
637 parents.append(repo[nullid])
637 parents.append(repo[nullid])
638 if opts.get('exact'):
638 if opts.get('exact'):
639 if not nodeid or not p1:
639 if not nodeid or not p1:
640 raise util.Abort(_('not a Mercurial patch'))
640 raise util.Abort(_('not a Mercurial patch'))
641 p1 = repo[p1]
641 p1 = repo[p1]
642 p2 = repo[p2 or nullid]
642 p2 = repo[p2 or nullid]
643 elif p2:
643 elif p2:
644 try:
644 try:
645 p1 = repo[p1]
645 p1 = repo[p1]
646 p2 = repo[p2]
646 p2 = repo[p2]
647 # Without any options, consider p2 only if the
647 # Without any options, consider p2 only if the
648 # patch is being applied on top of the recorded
648 # patch is being applied on top of the recorded
649 # first parent.
649 # first parent.
650 if p1 != parents[0]:
650 if p1 != parents[0]:
651 p1 = parents[0]
651 p1 = parents[0]
652 p2 = repo[nullid]
652 p2 = repo[nullid]
653 except error.RepoError:
653 except error.RepoError:
654 p1, p2 = parents
654 p1, p2 = parents
655 if p2.node() == nullid:
655 if p2.node() == nullid:
656 ui.warn(_("warning: import the patch as a normal revision\n"
656 ui.warn(_("warning: import the patch as a normal revision\n"
657 "(use --exact to import the patch as a merge)\n"))
657 "(use --exact to import the patch as a merge)\n"))
658 else:
658 else:
659 p1, p2 = parents
659 p1, p2 = parents
660
660
661 n = None
661 n = None
662 if update:
662 if update:
663 repo.dirstate.beginparentchange()
663 repo.dirstate.beginparentchange()
664 if p1 != parents[0]:
664 if p1 != parents[0]:
665 updatefunc(repo, p1.node())
665 updatefunc(repo, p1.node())
666 if p2 != parents[1]:
666 if p2 != parents[1]:
667 repo.setparents(p1.node(), p2.node())
667 repo.setparents(p1.node(), p2.node())
668
668
669 if opts.get('exact') or opts.get('import_branch'):
669 if opts.get('exact') or opts.get('import_branch'):
670 repo.dirstate.setbranch(branch or 'default')
670 repo.dirstate.setbranch(branch or 'default')
671
671
672 partial = opts.get('partial', False)
672 partial = opts.get('partial', False)
673 files = set()
673 files = set()
674 try:
674 try:
675 patch.patch(ui, repo, tmpname, strip=strip, files=files,
675 patch.patch(ui, repo, tmpname, strip=strip, files=files,
676 eolmode=None, similarity=sim / 100.0)
676 eolmode=None, similarity=sim / 100.0)
677 except patch.PatchError, e:
677 except patch.PatchError, e:
678 if not partial:
678 if not partial:
679 raise util.Abort(str(e))
679 raise util.Abort(str(e))
680 if partial:
680 if partial:
681 rejects = True
681 rejects = True
682
682
683 files = list(files)
683 files = list(files)
684 if opts.get('no_commit'):
684 if opts.get('no_commit'):
685 if message:
685 if message:
686 msgs.append(message)
686 msgs.append(message)
687 else:
687 else:
688 if opts.get('exact') or p2:
688 if opts.get('exact') or p2:
689 # If you got here, you either use --force and know what
689 # If you got here, you either use --force and know what
690 # you are doing or used --exact or a merge patch while
690 # you are doing or used --exact or a merge patch while
691 # being updated to its first parent.
691 # being updated to its first parent.
692 m = None
692 m = None
693 else:
693 else:
694 m = scmutil.matchfiles(repo, files or [])
694 m = scmutil.matchfiles(repo, files or [])
695 editform = mergeeditform(repo[None], 'import.normal')
695 editform = mergeeditform(repo[None], 'import.normal')
696 if opts.get('exact'):
696 if opts.get('exact'):
697 editor = None
697 editor = None
698 else:
698 else:
699 editor = getcommiteditor(editform=editform, **opts)
699 editor = getcommiteditor(editform=editform, **opts)
700 n = repo.commit(message, opts.get('user') or user,
700 n = repo.commit(message, opts.get('user') or user,
701 opts.get('date') or date, match=m,
701 opts.get('date') or date, match=m,
702 editor=editor, force=partial)
702 editor=editor, force=partial)
703 repo.dirstate.endparentchange()
703 repo.dirstate.endparentchange()
704 else:
704 else:
705 if opts.get('exact') or opts.get('import_branch'):
705 if opts.get('exact') or opts.get('import_branch'):
706 branch = branch or 'default'
706 branch = branch or 'default'
707 else:
707 else:
708 branch = p1.branch()
708 branch = p1.branch()
709 store = patch.filestore()
709 store = patch.filestore()
710 try:
710 try:
711 files = set()
711 files = set()
712 try:
712 try:
713 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
713 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
714 files, eolmode=None)
714 files, eolmode=None)
715 except patch.PatchError, e:
715 except patch.PatchError, e:
716 raise util.Abort(str(e))
716 raise util.Abort(str(e))
717 if opts.get('exact'):
717 if opts.get('exact'):
718 editor = None
718 editor = None
719 else:
719 else:
720 editor = getcommiteditor(editform='import.bypass')
720 editor = getcommiteditor(editform='import.bypass')
721 memctx = context.makememctx(repo, (p1.node(), p2.node()),
721 memctx = context.makememctx(repo, (p1.node(), p2.node()),
722 message,
722 message,
723 opts.get('user') or user,
723 opts.get('user') or user,
724 opts.get('date') or date,
724 opts.get('date') or date,
725 branch, files, store,
725 branch, files, store,
726 editor=editor)
726 editor=editor)
727 n = memctx.commit()
727 n = memctx.commit()
728 finally:
728 finally:
729 store.close()
729 store.close()
730 if opts.get('exact') and opts.get('no_commit'):
730 if opts.get('exact') and opts.get('no_commit'):
731 # --exact with --no-commit is still useful in that it does merge
731 # --exact with --no-commit is still useful in that it does merge
732 # and branch bits
732 # and branch bits
733 ui.warn(_("warning: can't check exact import with --no-commit\n"))
733 ui.warn(_("warning: can't check exact import with --no-commit\n"))
734 elif opts.get('exact') and hex(n) != nodeid:
734 elif opts.get('exact') and hex(n) != nodeid:
735 raise util.Abort(_('patch is damaged or loses information'))
735 raise util.Abort(_('patch is damaged or loses information'))
736 if n:
736 if n:
737 # i18n: refers to a short changeset id
737 # i18n: refers to a short changeset id
738 msg = _('created %s') % short(n)
738 msg = _('created %s') % short(n)
739 return (msg, n, rejects)
739 return (msg, n, rejects)
740 finally:
740 finally:
741 os.unlink(tmpname)
741 os.unlink(tmpname)
742
742
743 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
743 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
744 opts=None):
744 opts=None):
745 '''export changesets as hg patches.'''
745 '''export changesets as hg patches.'''
746
746
747 total = len(revs)
747 total = len(revs)
748 revwidth = max([len(str(rev)) for rev in revs])
748 revwidth = max([len(str(rev)) for rev in revs])
749 filemode = {}
749 filemode = {}
750
750
751 def single(rev, seqno, fp):
751 def single(rev, seqno, fp):
752 ctx = repo[rev]
752 ctx = repo[rev]
753 node = ctx.node()
753 node = ctx.node()
754 parents = [p.node() for p in ctx.parents() if p]
754 parents = [p.node() for p in ctx.parents() if p]
755 branch = ctx.branch()
755 branch = ctx.branch()
756 if switch_parent:
756 if switch_parent:
757 parents.reverse()
757 parents.reverse()
758 prev = (parents and parents[0]) or nullid
758 prev = (parents and parents[0]) or nullid
759
759
760 shouldclose = False
760 shouldclose = False
761 if not fp and len(template) > 0:
761 if not fp and len(template) > 0:
762 desc_lines = ctx.description().rstrip().split('\n')
762 desc_lines = ctx.description().rstrip().split('\n')
763 desc = desc_lines[0] #Commit always has a first line.
763 desc = desc_lines[0] #Commit always has a first line.
764 fp = makefileobj(repo, template, node, desc=desc, total=total,
764 fp = makefileobj(repo, template, node, desc=desc, total=total,
765 seqno=seqno, revwidth=revwidth, mode='wb',
765 seqno=seqno, revwidth=revwidth, mode='wb',
766 modemap=filemode)
766 modemap=filemode)
767 if fp != template:
767 if fp != template:
768 shouldclose = True
768 shouldclose = True
769 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
769 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
770 repo.ui.note("%s\n" % fp.name)
770 repo.ui.note("%s\n" % fp.name)
771
771
772 if not fp:
772 if not fp:
773 write = repo.ui.write
773 write = repo.ui.write
774 else:
774 else:
775 def write(s, **kw):
775 def write(s, **kw):
776 fp.write(s)
776 fp.write(s)
777
777
778
778
779 write("# HG changeset patch\n")
779 write("# HG changeset patch\n")
780 write("# User %s\n" % ctx.user())
780 write("# User %s\n" % ctx.user())
781 write("# Date %d %d\n" % ctx.date())
781 write("# Date %d %d\n" % ctx.date())
782 write("# %s\n" % util.datestr(ctx.date()))
782 write("# %s\n" % util.datestr(ctx.date()))
783 if branch and branch != 'default':
783 if branch and branch != 'default':
784 write("# Branch %s\n" % branch)
784 write("# Branch %s\n" % branch)
785 write("# Node ID %s\n" % hex(node))
785 write("# Node ID %s\n" % hex(node))
786 write("# Parent %s\n" % hex(prev))
786 write("# Parent %s\n" % hex(prev))
787 if len(parents) > 1:
787 if len(parents) > 1:
788 write("# Parent %s\n" % hex(parents[1]))
788 write("# Parent %s\n" % hex(parents[1]))
789 write(ctx.description().rstrip())
789 write(ctx.description().rstrip())
790 write("\n\n")
790 write("\n\n")
791
791
792 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
792 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
793 write(chunk, label=label)
793 write(chunk, label=label)
794
794
795 if shouldclose:
795 if shouldclose:
796 fp.close()
796 fp.close()
797
797
798 for seqno, rev in enumerate(revs):
798 for seqno, rev in enumerate(revs):
799 single(rev, seqno + 1, fp)
799 single(rev, seqno + 1, fp)
800
800
801 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
801 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
802 changes=None, stat=False, fp=None, prefix='',
802 changes=None, stat=False, fp=None, prefix='',
803 listsubrepos=False):
803 listsubrepos=False):
804 '''show diff or diffstat.'''
804 '''show diff or diffstat.'''
805 if fp is None:
805 if fp is None:
806 write = ui.write
806 write = ui.write
807 else:
807 else:
808 def write(s, **kw):
808 def write(s, **kw):
809 fp.write(s)
809 fp.write(s)
810
810
811 if stat:
811 if stat:
812 diffopts = diffopts.copy(context=0)
812 diffopts = diffopts.copy(context=0)
813 width = 80
813 width = 80
814 if not ui.plain():
814 if not ui.plain():
815 width = ui.termwidth()
815 width = ui.termwidth()
816 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
816 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
817 prefix=prefix)
817 prefix=prefix)
818 for chunk, label in patch.diffstatui(util.iterlines(chunks),
818 for chunk, label in patch.diffstatui(util.iterlines(chunks),
819 width=width,
819 width=width,
820 git=diffopts.git):
820 git=diffopts.git):
821 write(chunk, label=label)
821 write(chunk, label=label)
822 else:
822 else:
823 for chunk, label in patch.diffui(repo, node1, node2, match,
823 for chunk, label in patch.diffui(repo, node1, node2, match,
824 changes, diffopts, prefix=prefix):
824 changes, diffopts, prefix=prefix):
825 write(chunk, label=label)
825 write(chunk, label=label)
826
826
827 if listsubrepos:
827 if listsubrepos:
828 ctx1 = repo[node1]
828 ctx1 = repo[node1]
829 ctx2 = repo[node2]
829 ctx2 = repo[node2]
830 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
830 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
831 tempnode2 = node2
831 tempnode2 = node2
832 try:
832 try:
833 if node2 is not None:
833 if node2 is not None:
834 tempnode2 = ctx2.substate[subpath][1]
834 tempnode2 = ctx2.substate[subpath][1]
835 except KeyError:
835 except KeyError:
836 # A subrepo that existed in node1 was deleted between node1 and
836 # A subrepo that existed in node1 was deleted between node1 and
837 # node2 (inclusive). Thus, ctx2's substate won't contain that
837 # node2 (inclusive). Thus, ctx2's substate won't contain that
838 # subpath. The best we can do is to ignore it.
838 # subpath. The best we can do is to ignore it.
839 tempnode2 = None
839 tempnode2 = None
840 submatch = matchmod.narrowmatcher(subpath, match)
840 submatch = matchmod.narrowmatcher(subpath, match)
841 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
841 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
842 stat=stat, fp=fp, prefix=prefix)
842 stat=stat, fp=fp, prefix=prefix)
843
843
844 class changeset_printer(object):
844 class changeset_printer(object):
845 '''show changeset information when templating not requested.'''
845 '''show changeset information when templating not requested.'''
846
846
847 def __init__(self, ui, repo, matchfn, diffopts, buffered):
847 def __init__(self, ui, repo, matchfn, diffopts, buffered):
848 self.ui = ui
848 self.ui = ui
849 self.repo = repo
849 self.repo = repo
850 self.buffered = buffered
850 self.buffered = buffered
851 self.matchfn = matchfn
851 self.matchfn = matchfn
852 self.diffopts = diffopts
852 self.diffopts = diffopts
853 self.header = {}
853 self.header = {}
854 self.hunk = {}
854 self.hunk = {}
855 self.lastheader = None
855 self.lastheader = None
856 self.footer = None
856 self.footer = None
857
857
858 def flush(self, rev):
858 def flush(self, rev):
859 if rev in self.header:
859 if rev in self.header:
860 h = self.header[rev]
860 h = self.header[rev]
861 if h != self.lastheader:
861 if h != self.lastheader:
862 self.lastheader = h
862 self.lastheader = h
863 self.ui.write(h)
863 self.ui.write(h)
864 del self.header[rev]
864 del self.header[rev]
865 if rev in self.hunk:
865 if rev in self.hunk:
866 self.ui.write(self.hunk[rev])
866 self.ui.write(self.hunk[rev])
867 del self.hunk[rev]
867 del self.hunk[rev]
868 return 1
868 return 1
869 return 0
869 return 0
870
870
871 def close(self):
871 def close(self):
872 if self.footer:
872 if self.footer:
873 self.ui.write(self.footer)
873 self.ui.write(self.footer)
874
874
875 def show(self, ctx, copies=None, matchfn=None, **props):
875 def show(self, ctx, copies=None, matchfn=None, **props):
876 if self.buffered:
876 if self.buffered:
877 self.ui.pushbuffer()
877 self.ui.pushbuffer()
878 self._show(ctx, copies, matchfn, props)
878 self._show(ctx, copies, matchfn, props)
879 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
879 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
880 else:
880 else:
881 self._show(ctx, copies, matchfn, props)
881 self._show(ctx, copies, matchfn, props)
882
882
883 def _show(self, ctx, copies, matchfn, props):
883 def _show(self, ctx, copies, matchfn, props):
884 '''show a single changeset or file revision'''
884 '''show a single changeset or file revision'''
885 changenode = ctx.node()
885 changenode = ctx.node()
886 rev = ctx.rev()
886 rev = ctx.rev()
887
887
888 if self.ui.quiet:
888 if self.ui.quiet:
889 self.ui.write("%d:%s\n" % (rev, short(changenode)),
889 self.ui.write("%d:%s\n" % (rev, short(changenode)),
890 label='log.node')
890 label='log.node')
891 return
891 return
892
892
893 log = self.repo.changelog
893 log = self.repo.changelog
894 date = util.datestr(ctx.date())
894 date = util.datestr(ctx.date())
895
895
896 hexfunc = self.ui.debugflag and hex or short
896 hexfunc = self.ui.debugflag and hex or short
897
897
898 parents = [(p, hexfunc(log.node(p)))
898 parents = [(p, hexfunc(log.node(p)))
899 for p in self._meaningful_parentrevs(log, rev)]
899 for p in self._meaningful_parentrevs(log, rev)]
900
900
901 # i18n: column positioning for "hg log"
901 # i18n: column positioning for "hg log"
902 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
902 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
903 label='log.changeset changeset.%s' % ctx.phasestr())
903 label='log.changeset changeset.%s' % ctx.phasestr())
904
904
905 branch = ctx.branch()
905 branch = ctx.branch()
906 # don't show the default branch name
906 # don't show the default branch name
907 if branch != 'default':
907 if branch != 'default':
908 # i18n: column positioning for "hg log"
908 # i18n: column positioning for "hg log"
909 self.ui.write(_("branch: %s\n") % branch,
909 self.ui.write(_("branch: %s\n") % branch,
910 label='log.branch')
910 label='log.branch')
911 for bookmark in self.repo.nodebookmarks(changenode):
911 for bookmark in self.repo.nodebookmarks(changenode):
912 # i18n: column positioning for "hg log"
912 # i18n: column positioning for "hg log"
913 self.ui.write(_("bookmark: %s\n") % bookmark,
913 self.ui.write(_("bookmark: %s\n") % bookmark,
914 label='log.bookmark')
914 label='log.bookmark')
915 for tag in self.repo.nodetags(changenode):
915 for tag in self.repo.nodetags(changenode):
916 # i18n: column positioning for "hg log"
916 # i18n: column positioning for "hg log"
917 self.ui.write(_("tag: %s\n") % tag,
917 self.ui.write(_("tag: %s\n") % tag,
918 label='log.tag')
918 label='log.tag')
919 if self.ui.debugflag:
919 if self.ui.debugflag:
920 # i18n: column positioning for "hg log"
920 # i18n: column positioning for "hg log"
921 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
921 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
922 label='log.phase')
922 label='log.phase')
923 for parent in parents:
923 for parent in parents:
924 label = 'log.parent changeset.%s' % self.repo[parent[0]].phasestr()
924 label = 'log.parent changeset.%s' % self.repo[parent[0]].phasestr()
925 # i18n: column positioning for "hg log"
925 # i18n: column positioning for "hg log"
926 self.ui.write(_("parent: %d:%s\n") % parent,
926 self.ui.write(_("parent: %d:%s\n") % parent,
927 label=label)
927 label=label)
928
928
929 if self.ui.debugflag:
929 if self.ui.debugflag:
930 mnode = ctx.manifestnode()
930 mnode = ctx.manifestnode()
931 # i18n: column positioning for "hg log"
931 # i18n: column positioning for "hg log"
932 self.ui.write(_("manifest: %d:%s\n") %
932 self.ui.write(_("manifest: %d:%s\n") %
933 (self.repo.manifest.rev(mnode), hex(mnode)),
933 (self.repo.manifest.rev(mnode), hex(mnode)),
934 label='ui.debug log.manifest')
934 label='ui.debug log.manifest')
935 # i18n: column positioning for "hg log"
935 # i18n: column positioning for "hg log"
936 self.ui.write(_("user: %s\n") % ctx.user(),
936 self.ui.write(_("user: %s\n") % ctx.user(),
937 label='log.user')
937 label='log.user')
938 # i18n: column positioning for "hg log"
938 # i18n: column positioning for "hg log"
939 self.ui.write(_("date: %s\n") % date,
939 self.ui.write(_("date: %s\n") % date,
940 label='log.date')
940 label='log.date')
941
941
942 if self.ui.debugflag:
942 if self.ui.debugflag:
943 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
943 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
944 for key, value in zip([# i18n: column positioning for "hg log"
944 for key, value in zip([# i18n: column positioning for "hg log"
945 _("files:"),
945 _("files:"),
946 # i18n: column positioning for "hg log"
946 # i18n: column positioning for "hg log"
947 _("files+:"),
947 _("files+:"),
948 # i18n: column positioning for "hg log"
948 # i18n: column positioning for "hg log"
949 _("files-:")], files):
949 _("files-:")], files):
950 if value:
950 if value:
951 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
951 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
952 label='ui.debug log.files')
952 label='ui.debug log.files')
953 elif ctx.files() and self.ui.verbose:
953 elif ctx.files() and self.ui.verbose:
954 # i18n: column positioning for "hg log"
954 # i18n: column positioning for "hg log"
955 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
955 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
956 label='ui.note log.files')
956 label='ui.note log.files')
957 if copies and self.ui.verbose:
957 if copies and self.ui.verbose:
958 copies = ['%s (%s)' % c for c in copies]
958 copies = ['%s (%s)' % c for c in copies]
959 # i18n: column positioning for "hg log"
959 # i18n: column positioning for "hg log"
960 self.ui.write(_("copies: %s\n") % ' '.join(copies),
960 self.ui.write(_("copies: %s\n") % ' '.join(copies),
961 label='ui.note log.copies')
961 label='ui.note log.copies')
962
962
963 extra = ctx.extra()
963 extra = ctx.extra()
964 if extra and self.ui.debugflag:
964 if extra and self.ui.debugflag:
965 for key, value in sorted(extra.items()):
965 for key, value in sorted(extra.items()):
966 # i18n: column positioning for "hg log"
966 # i18n: column positioning for "hg log"
967 self.ui.write(_("extra: %s=%s\n")
967 self.ui.write(_("extra: %s=%s\n")
968 % (key, value.encode('string_escape')),
968 % (key, value.encode('string_escape')),
969 label='ui.debug log.extra')
969 label='ui.debug log.extra')
970
970
971 description = ctx.description().strip()
971 description = ctx.description().strip()
972 if description:
972 if description:
973 if self.ui.verbose:
973 if self.ui.verbose:
974 self.ui.write(_("description:\n"),
974 self.ui.write(_("description:\n"),
975 label='ui.note log.description')
975 label='ui.note log.description')
976 self.ui.write(description,
976 self.ui.write(description,
977 label='ui.note log.description')
977 label='ui.note log.description')
978 self.ui.write("\n\n")
978 self.ui.write("\n\n")
979 else:
979 else:
980 # i18n: column positioning for "hg log"
980 # i18n: column positioning for "hg log"
981 self.ui.write(_("summary: %s\n") %
981 self.ui.write(_("summary: %s\n") %
982 description.splitlines()[0],
982 description.splitlines()[0],
983 label='log.summary')
983 label='log.summary')
984 self.ui.write("\n")
984 self.ui.write("\n")
985
985
986 self.showpatch(changenode, matchfn)
986 self.showpatch(changenode, matchfn)
987
987
988 def showpatch(self, node, matchfn):
988 def showpatch(self, node, matchfn):
989 if not matchfn:
989 if not matchfn:
990 matchfn = self.matchfn
990 matchfn = self.matchfn
991 if matchfn:
991 if matchfn:
992 stat = self.diffopts.get('stat')
992 stat = self.diffopts.get('stat')
993 diff = self.diffopts.get('patch')
993 diff = self.diffopts.get('patch')
994 diffopts = patch.diffopts(self.ui, self.diffopts)
994 diffopts = patch.diffopts(self.ui, self.diffopts)
995 prev = self.repo.changelog.parents(node)[0]
995 prev = self.repo.changelog.parents(node)[0]
996 if stat:
996 if stat:
997 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
997 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
998 match=matchfn, stat=True)
998 match=matchfn, stat=True)
999 if diff:
999 if diff:
1000 if stat:
1000 if stat:
1001 self.ui.write("\n")
1001 self.ui.write("\n")
1002 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1002 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1003 match=matchfn, stat=False)
1003 match=matchfn, stat=False)
1004 self.ui.write("\n")
1004 self.ui.write("\n")
1005
1005
1006 def _meaningful_parentrevs(self, log, rev):
1006 def _meaningful_parentrevs(self, log, rev):
1007 """Return list of meaningful (or all if debug) parentrevs for rev.
1007 """Return list of meaningful (or all if debug) parentrevs for rev.
1008
1008
1009 For merges (two non-nullrev revisions) both parents are meaningful.
1009 For merges (two non-nullrev revisions) both parents are meaningful.
1010 Otherwise the first parent revision is considered meaningful if it
1010 Otherwise the first parent revision is considered meaningful if it
1011 is not the preceding revision.
1011 is not the preceding revision.
1012 """
1012 """
1013 parents = log.parentrevs(rev)
1013 parents = log.parentrevs(rev)
1014 if not self.ui.debugflag and parents[1] == nullrev:
1014 if not self.ui.debugflag and parents[1] == nullrev:
1015 if parents[0] >= rev - 1:
1015 if parents[0] >= rev - 1:
1016 parents = []
1016 parents = []
1017 else:
1017 else:
1018 parents = [parents[0]]
1018 parents = [parents[0]]
1019 return parents
1019 return parents
1020
1020
1021 class jsonchangeset(changeset_printer):
1021 class jsonchangeset(changeset_printer):
1022 '''format changeset information.'''
1022 '''format changeset information.'''
1023
1023
1024 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1024 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1025 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1025 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1026 self.cache = {}
1026 self.cache = {}
1027 self._first = True
1027 self._first = True
1028
1028
1029 def close(self):
1029 def close(self):
1030 if not self._first:
1030 if not self._first:
1031 self.ui.write("\n]\n")
1031 self.ui.write("\n]\n")
1032 else:
1032 else:
1033 self.ui.write("[]\n")
1033 self.ui.write("[]\n")
1034
1034
1035 def _show(self, ctx, copies, matchfn, props):
1035 def _show(self, ctx, copies, matchfn, props):
1036 '''show a single changeset or file revision'''
1036 '''show a single changeset or file revision'''
1037 hexnode = hex(ctx.node())
1037 hexnode = hex(ctx.node())
1038 rev = ctx.rev()
1038 rev = ctx.rev()
1039 j = encoding.jsonescape
1039 j = encoding.jsonescape
1040
1040
1041 if self._first:
1041 if self._first:
1042 self.ui.write("[\n {")
1042 self.ui.write("[\n {")
1043 self._first = False
1043 self._first = False
1044 else:
1044 else:
1045 self.ui.write(",\n {")
1045 self.ui.write(",\n {")
1046
1046
1047 if self.ui.quiet:
1047 if self.ui.quiet:
1048 self.ui.write('\n "rev": %d' % rev)
1048 self.ui.write('\n "rev": %d' % rev)
1049 self.ui.write(',\n "node": "%s"' % hexnode)
1049 self.ui.write(',\n "node": "%s"' % hexnode)
1050 self.ui.write('\n }')
1050 self.ui.write('\n }')
1051 return
1051 return
1052
1052
1053 self.ui.write('\n "rev": %d' % rev)
1053 self.ui.write('\n "rev": %d' % rev)
1054 self.ui.write(',\n "node": "%s"' % hexnode)
1054 self.ui.write(',\n "node": "%s"' % hexnode)
1055 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1055 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1056 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1056 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1057 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1057 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1058 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1058 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1059 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1059 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1060
1060
1061 self.ui.write(',\n "bookmarks": [%s]' %
1061 self.ui.write(',\n "bookmarks": [%s]' %
1062 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1062 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1063 self.ui.write(',\n "tags": [%s]' %
1063 self.ui.write(',\n "tags": [%s]' %
1064 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1064 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1065 self.ui.write(',\n "parents": [%s]' %
1065 self.ui.write(',\n "parents": [%s]' %
1066 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1066 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1067
1067
1068 if self.ui.debugflag:
1068 if self.ui.debugflag:
1069 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1069 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1070
1070
1071 self.ui.write(',\n "extra": {%s}' %
1071 self.ui.write(',\n "extra": {%s}' %
1072 ", ".join('"%s": "%s"' % (j(k), j(v))
1072 ", ".join('"%s": "%s"' % (j(k), j(v))
1073 for k, v in ctx.extra().items()))
1073 for k, v in ctx.extra().items()))
1074
1074
1075 files = ctx.status(ctx.p1())
1075 files = ctx.p1().status(ctx)
1076 self.ui.write(',\n "modified": [%s]' %
1076 self.ui.write(',\n "modified": [%s]' %
1077 ", ".join('"%s"' % j(f) for f in files[0]))
1077 ", ".join('"%s"' % j(f) for f in files[0]))
1078 self.ui.write(',\n "added": [%s]' %
1078 self.ui.write(',\n "added": [%s]' %
1079 ", ".join('"%s"' % j(f) for f in files[1]))
1079 ", ".join('"%s"' % j(f) for f in files[1]))
1080 self.ui.write(',\n "removed": [%s]' %
1080 self.ui.write(',\n "removed": [%s]' %
1081 ", ".join('"%s"' % j(f) for f in files[2]))
1081 ", ".join('"%s"' % j(f) for f in files[2]))
1082
1082
1083 elif self.ui.verbose:
1083 elif self.ui.verbose:
1084 self.ui.write(',\n "files": [%s]' %
1084 self.ui.write(',\n "files": [%s]' %
1085 ", ".join('"%s"' % j(f) for f in ctx.files()))
1085 ", ".join('"%s"' % j(f) for f in ctx.files()))
1086
1086
1087 if copies:
1087 if copies:
1088 self.ui.write(',\n "copies": {%s}' %
1088 self.ui.write(',\n "copies": {%s}' %
1089 ", ".join('"%s": %s' % (j(k), j(copies[k]))
1089 ", ".join('"%s": %s' % (j(k), j(copies[k]))
1090 for k in copies))
1090 for k in copies))
1091
1091
1092 matchfn = self.matchfn
1092 matchfn = self.matchfn
1093 if matchfn:
1093 if matchfn:
1094 stat = self.diffopts.get('stat')
1094 stat = self.diffopts.get('stat')
1095 diff = self.diffopts.get('patch')
1095 diff = self.diffopts.get('patch')
1096 diffopts = patch.diffopts(self.ui, self.diffopts)
1096 diffopts = patch.diffopts(self.ui, self.diffopts)
1097 node, prev = ctx.node(), ctx.p1().node()
1097 node, prev = ctx.node(), ctx.p1().node()
1098 if stat:
1098 if stat:
1099 self.ui.pushbuffer()
1099 self.ui.pushbuffer()
1100 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1100 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1101 match=matchfn, stat=True)
1101 match=matchfn, stat=True)
1102 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1102 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1103 if diff:
1103 if diff:
1104 self.ui.pushbuffer()
1104 self.ui.pushbuffer()
1105 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1105 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1106 match=matchfn, stat=False)
1106 match=matchfn, stat=False)
1107 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1107 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1108
1108
1109 self.ui.write("\n }")
1109 self.ui.write("\n }")
1110
1110
1111 class changeset_templater(changeset_printer):
1111 class changeset_templater(changeset_printer):
1112 '''format changeset information.'''
1112 '''format changeset information.'''
1113
1113
1114 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1114 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1115 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1115 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1116 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1116 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1117 defaulttempl = {
1117 defaulttempl = {
1118 'parent': '{rev}:{node|formatnode} ',
1118 'parent': '{rev}:{node|formatnode} ',
1119 'manifest': '{rev}:{node|formatnode}',
1119 'manifest': '{rev}:{node|formatnode}',
1120 'file_copy': '{name} ({source})',
1120 'file_copy': '{name} ({source})',
1121 'extra': '{key}={value|stringescape}'
1121 'extra': '{key}={value|stringescape}'
1122 }
1122 }
1123 # filecopy is preserved for compatibility reasons
1123 # filecopy is preserved for compatibility reasons
1124 defaulttempl['filecopy'] = defaulttempl['file_copy']
1124 defaulttempl['filecopy'] = defaulttempl['file_copy']
1125 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1125 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1126 cache=defaulttempl)
1126 cache=defaulttempl)
1127 if tmpl:
1127 if tmpl:
1128 self.t.cache['changeset'] = tmpl
1128 self.t.cache['changeset'] = tmpl
1129
1129
1130 self.cache = {}
1130 self.cache = {}
1131
1131
1132 def _meaningful_parentrevs(self, ctx):
1132 def _meaningful_parentrevs(self, ctx):
1133 """Return list of meaningful (or all if debug) parentrevs for rev.
1133 """Return list of meaningful (or all if debug) parentrevs for rev.
1134 """
1134 """
1135 parents = ctx.parents()
1135 parents = ctx.parents()
1136 if len(parents) > 1:
1136 if len(parents) > 1:
1137 return parents
1137 return parents
1138 if self.ui.debugflag:
1138 if self.ui.debugflag:
1139 return [parents[0], self.repo['null']]
1139 return [parents[0], self.repo['null']]
1140 if parents[0].rev() >= ctx.rev() - 1:
1140 if parents[0].rev() >= ctx.rev() - 1:
1141 return []
1141 return []
1142 return parents
1142 return parents
1143
1143
1144 def _show(self, ctx, copies, matchfn, props):
1144 def _show(self, ctx, copies, matchfn, props):
1145 '''show a single changeset or file revision'''
1145 '''show a single changeset or file revision'''
1146
1146
1147 showlist = templatekw.showlist
1147 showlist = templatekw.showlist
1148
1148
1149 # showparents() behaviour depends on ui trace level which
1149 # showparents() behaviour depends on ui trace level which
1150 # causes unexpected behaviours at templating level and makes
1150 # causes unexpected behaviours at templating level and makes
1151 # it harder to extract it in a standalone function. Its
1151 # it harder to extract it in a standalone function. Its
1152 # behaviour cannot be changed so leave it here for now.
1152 # behaviour cannot be changed so leave it here for now.
1153 def showparents(**args):
1153 def showparents(**args):
1154 ctx = args['ctx']
1154 ctx = args['ctx']
1155 parents = [[('rev', p.rev()),
1155 parents = [[('rev', p.rev()),
1156 ('node', p.hex()),
1156 ('node', p.hex()),
1157 ('phase', p.phasestr())]
1157 ('phase', p.phasestr())]
1158 for p in self._meaningful_parentrevs(ctx)]
1158 for p in self._meaningful_parentrevs(ctx)]
1159 return showlist('parent', parents, **args)
1159 return showlist('parent', parents, **args)
1160
1160
1161 props = props.copy()
1161 props = props.copy()
1162 props.update(templatekw.keywords)
1162 props.update(templatekw.keywords)
1163 props['parents'] = showparents
1163 props['parents'] = showparents
1164 props['templ'] = self.t
1164 props['templ'] = self.t
1165 props['ctx'] = ctx
1165 props['ctx'] = ctx
1166 props['repo'] = self.repo
1166 props['repo'] = self.repo
1167 props['revcache'] = {'copies': copies}
1167 props['revcache'] = {'copies': copies}
1168 props['cache'] = self.cache
1168 props['cache'] = self.cache
1169
1169
1170 # find correct templates for current mode
1170 # find correct templates for current mode
1171
1171
1172 tmplmodes = [
1172 tmplmodes = [
1173 (True, None),
1173 (True, None),
1174 (self.ui.verbose, 'verbose'),
1174 (self.ui.verbose, 'verbose'),
1175 (self.ui.quiet, 'quiet'),
1175 (self.ui.quiet, 'quiet'),
1176 (self.ui.debugflag, 'debug'),
1176 (self.ui.debugflag, 'debug'),
1177 ]
1177 ]
1178
1178
1179 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1179 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1180 for mode, postfix in tmplmodes:
1180 for mode, postfix in tmplmodes:
1181 for type in types:
1181 for type in types:
1182 cur = postfix and ('%s_%s' % (type, postfix)) or type
1182 cur = postfix and ('%s_%s' % (type, postfix)) or type
1183 if mode and cur in self.t:
1183 if mode and cur in self.t:
1184 types[type] = cur
1184 types[type] = cur
1185
1185
1186 try:
1186 try:
1187
1187
1188 # write header
1188 # write header
1189 if types['header']:
1189 if types['header']:
1190 h = templater.stringify(self.t(types['header'], **props))
1190 h = templater.stringify(self.t(types['header'], **props))
1191 if self.buffered:
1191 if self.buffered:
1192 self.header[ctx.rev()] = h
1192 self.header[ctx.rev()] = h
1193 else:
1193 else:
1194 if self.lastheader != h:
1194 if self.lastheader != h:
1195 self.lastheader = h
1195 self.lastheader = h
1196 self.ui.write(h)
1196 self.ui.write(h)
1197
1197
1198 # write changeset metadata, then patch if requested
1198 # write changeset metadata, then patch if requested
1199 key = types['changeset']
1199 key = types['changeset']
1200 self.ui.write(templater.stringify(self.t(key, **props)))
1200 self.ui.write(templater.stringify(self.t(key, **props)))
1201 self.showpatch(ctx.node(), matchfn)
1201 self.showpatch(ctx.node(), matchfn)
1202
1202
1203 if types['footer']:
1203 if types['footer']:
1204 if not self.footer:
1204 if not self.footer:
1205 self.footer = templater.stringify(self.t(types['footer'],
1205 self.footer = templater.stringify(self.t(types['footer'],
1206 **props))
1206 **props))
1207
1207
1208 except KeyError, inst:
1208 except KeyError, inst:
1209 msg = _("%s: no key named '%s'")
1209 msg = _("%s: no key named '%s'")
1210 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1210 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1211 except SyntaxError, inst:
1211 except SyntaxError, inst:
1212 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1212 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1213
1213
1214 def gettemplate(ui, tmpl, style):
1214 def gettemplate(ui, tmpl, style):
1215 """
1215 """
1216 Find the template matching the given template spec or style.
1216 Find the template matching the given template spec or style.
1217 """
1217 """
1218
1218
1219 # ui settings
1219 # ui settings
1220 if not tmpl and not style: # template are stronger than style
1220 if not tmpl and not style: # template are stronger than style
1221 tmpl = ui.config('ui', 'logtemplate')
1221 tmpl = ui.config('ui', 'logtemplate')
1222 if tmpl:
1222 if tmpl:
1223 try:
1223 try:
1224 tmpl = templater.parsestring(tmpl)
1224 tmpl = templater.parsestring(tmpl)
1225 except SyntaxError:
1225 except SyntaxError:
1226 tmpl = templater.parsestring(tmpl, quoted=False)
1226 tmpl = templater.parsestring(tmpl, quoted=False)
1227 return tmpl, None
1227 return tmpl, None
1228 else:
1228 else:
1229 style = util.expandpath(ui.config('ui', 'style', ''))
1229 style = util.expandpath(ui.config('ui', 'style', ''))
1230
1230
1231 if not tmpl and style:
1231 if not tmpl and style:
1232 mapfile = style
1232 mapfile = style
1233 if not os.path.split(mapfile)[0]:
1233 if not os.path.split(mapfile)[0]:
1234 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1234 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1235 or templater.templatepath(mapfile))
1235 or templater.templatepath(mapfile))
1236 if mapname:
1236 if mapname:
1237 mapfile = mapname
1237 mapfile = mapname
1238 return None, mapfile
1238 return None, mapfile
1239
1239
1240 if not tmpl:
1240 if not tmpl:
1241 return None, None
1241 return None, None
1242
1242
1243 # looks like a literal template?
1243 # looks like a literal template?
1244 if '{' in tmpl:
1244 if '{' in tmpl:
1245 return tmpl, None
1245 return tmpl, None
1246
1246
1247 # perhaps a stock style?
1247 # perhaps a stock style?
1248 if not os.path.split(tmpl)[0]:
1248 if not os.path.split(tmpl)[0]:
1249 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1249 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1250 or templater.templatepath(tmpl))
1250 or templater.templatepath(tmpl))
1251 if mapname and os.path.isfile(mapname):
1251 if mapname and os.path.isfile(mapname):
1252 return None, mapname
1252 return None, mapname
1253
1253
1254 # perhaps it's a reference to [templates]
1254 # perhaps it's a reference to [templates]
1255 t = ui.config('templates', tmpl)
1255 t = ui.config('templates', tmpl)
1256 if t:
1256 if t:
1257 try:
1257 try:
1258 tmpl = templater.parsestring(t)
1258 tmpl = templater.parsestring(t)
1259 except SyntaxError:
1259 except SyntaxError:
1260 tmpl = templater.parsestring(t, quoted=False)
1260 tmpl = templater.parsestring(t, quoted=False)
1261 return tmpl, None
1261 return tmpl, None
1262
1262
1263 if tmpl == 'list':
1263 if tmpl == 'list':
1264 ui.write(_("available styles: %s\n") % templater.stylelist())
1264 ui.write(_("available styles: %s\n") % templater.stylelist())
1265 raise util.Abort(_("specify a template"))
1265 raise util.Abort(_("specify a template"))
1266
1266
1267 # perhaps it's a path to a map or a template
1267 # perhaps it's a path to a map or a template
1268 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1268 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1269 # is it a mapfile for a style?
1269 # is it a mapfile for a style?
1270 if os.path.basename(tmpl).startswith("map-"):
1270 if os.path.basename(tmpl).startswith("map-"):
1271 return None, os.path.realpath(tmpl)
1271 return None, os.path.realpath(tmpl)
1272 tmpl = open(tmpl).read()
1272 tmpl = open(tmpl).read()
1273 return tmpl, None
1273 return tmpl, None
1274
1274
1275 # constant string?
1275 # constant string?
1276 return tmpl, None
1276 return tmpl, None
1277
1277
1278 def show_changeset(ui, repo, opts, buffered=False):
1278 def show_changeset(ui, repo, opts, buffered=False):
1279 """show one changeset using template or regular display.
1279 """show one changeset using template or regular display.
1280
1280
1281 Display format will be the first non-empty hit of:
1281 Display format will be the first non-empty hit of:
1282 1. option 'template'
1282 1. option 'template'
1283 2. option 'style'
1283 2. option 'style'
1284 3. [ui] setting 'logtemplate'
1284 3. [ui] setting 'logtemplate'
1285 4. [ui] setting 'style'
1285 4. [ui] setting 'style'
1286 If all of these values are either the unset or the empty string,
1286 If all of these values are either the unset or the empty string,
1287 regular display via changeset_printer() is done.
1287 regular display via changeset_printer() is done.
1288 """
1288 """
1289 # options
1289 # options
1290 matchfn = None
1290 matchfn = None
1291 if opts.get('patch') or opts.get('stat'):
1291 if opts.get('patch') or opts.get('stat'):
1292 matchfn = scmutil.matchall(repo)
1292 matchfn = scmutil.matchall(repo)
1293
1293
1294 if opts.get('template') == 'json':
1294 if opts.get('template') == 'json':
1295 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1295 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1296
1296
1297 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1297 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1298
1298
1299 if not tmpl and not mapfile:
1299 if not tmpl and not mapfile:
1300 return changeset_printer(ui, repo, matchfn, opts, buffered)
1300 return changeset_printer(ui, repo, matchfn, opts, buffered)
1301
1301
1302 try:
1302 try:
1303 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1303 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1304 buffered)
1304 buffered)
1305 except SyntaxError, inst:
1305 except SyntaxError, inst:
1306 raise util.Abort(inst.args[0])
1306 raise util.Abort(inst.args[0])
1307 return t
1307 return t
1308
1308
1309 def showmarker(ui, marker):
1309 def showmarker(ui, marker):
1310 """utility function to display obsolescence marker in a readable way
1310 """utility function to display obsolescence marker in a readable way
1311
1311
1312 To be used by debug function."""
1312 To be used by debug function."""
1313 ui.write(hex(marker.precnode()))
1313 ui.write(hex(marker.precnode()))
1314 for repl in marker.succnodes():
1314 for repl in marker.succnodes():
1315 ui.write(' ')
1315 ui.write(' ')
1316 ui.write(hex(repl))
1316 ui.write(hex(repl))
1317 ui.write(' %X ' % marker.flags())
1317 ui.write(' %X ' % marker.flags())
1318 parents = marker.parentnodes()
1318 parents = marker.parentnodes()
1319 if parents is not None:
1319 if parents is not None:
1320 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1320 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1321 ui.write('(%s) ' % util.datestr(marker.date()))
1321 ui.write('(%s) ' % util.datestr(marker.date()))
1322 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1322 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1323 sorted(marker.metadata().items())
1323 sorted(marker.metadata().items())
1324 if t[0] != 'date')))
1324 if t[0] != 'date')))
1325 ui.write('\n')
1325 ui.write('\n')
1326
1326
1327 def finddate(ui, repo, date):
1327 def finddate(ui, repo, date):
1328 """Find the tipmost changeset that matches the given date spec"""
1328 """Find the tipmost changeset that matches the given date spec"""
1329
1329
1330 df = util.matchdate(date)
1330 df = util.matchdate(date)
1331 m = scmutil.matchall(repo)
1331 m = scmutil.matchall(repo)
1332 results = {}
1332 results = {}
1333
1333
1334 def prep(ctx, fns):
1334 def prep(ctx, fns):
1335 d = ctx.date()
1335 d = ctx.date()
1336 if df(d[0]):
1336 if df(d[0]):
1337 results[ctx.rev()] = d
1337 results[ctx.rev()] = d
1338
1338
1339 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1339 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1340 rev = ctx.rev()
1340 rev = ctx.rev()
1341 if rev in results:
1341 if rev in results:
1342 ui.status(_("found revision %s from %s\n") %
1342 ui.status(_("found revision %s from %s\n") %
1343 (rev, util.datestr(results[rev])))
1343 (rev, util.datestr(results[rev])))
1344 return str(rev)
1344 return str(rev)
1345
1345
1346 raise util.Abort(_("revision matching date not found"))
1346 raise util.Abort(_("revision matching date not found"))
1347
1347
1348 def increasingwindows(windowsize=8, sizelimit=512):
1348 def increasingwindows(windowsize=8, sizelimit=512):
1349 while True:
1349 while True:
1350 yield windowsize
1350 yield windowsize
1351 if windowsize < sizelimit:
1351 if windowsize < sizelimit:
1352 windowsize *= 2
1352 windowsize *= 2
1353
1353
1354 class FileWalkError(Exception):
1354 class FileWalkError(Exception):
1355 pass
1355 pass
1356
1356
1357 def walkfilerevs(repo, match, follow, revs, fncache):
1357 def walkfilerevs(repo, match, follow, revs, fncache):
1358 '''Walks the file history for the matched files.
1358 '''Walks the file history for the matched files.
1359
1359
1360 Returns the changeset revs that are involved in the file history.
1360 Returns the changeset revs that are involved in the file history.
1361
1361
1362 Throws FileWalkError if the file history can't be walked using
1362 Throws FileWalkError if the file history can't be walked using
1363 filelogs alone.
1363 filelogs alone.
1364 '''
1364 '''
1365 wanted = set()
1365 wanted = set()
1366 copies = []
1366 copies = []
1367 minrev, maxrev = min(revs), max(revs)
1367 minrev, maxrev = min(revs), max(revs)
1368 def filerevgen(filelog, last):
1368 def filerevgen(filelog, last):
1369 """
1369 """
1370 Only files, no patterns. Check the history of each file.
1370 Only files, no patterns. Check the history of each file.
1371
1371
1372 Examines filelog entries within minrev, maxrev linkrev range
1372 Examines filelog entries within minrev, maxrev linkrev range
1373 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1373 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1374 tuples in backwards order
1374 tuples in backwards order
1375 """
1375 """
1376 cl_count = len(repo)
1376 cl_count = len(repo)
1377 revs = []
1377 revs = []
1378 for j in xrange(0, last + 1):
1378 for j in xrange(0, last + 1):
1379 linkrev = filelog.linkrev(j)
1379 linkrev = filelog.linkrev(j)
1380 if linkrev < minrev:
1380 if linkrev < minrev:
1381 continue
1381 continue
1382 # only yield rev for which we have the changelog, it can
1382 # only yield rev for which we have the changelog, it can
1383 # happen while doing "hg log" during a pull or commit
1383 # happen while doing "hg log" during a pull or commit
1384 if linkrev >= cl_count:
1384 if linkrev >= cl_count:
1385 break
1385 break
1386
1386
1387 parentlinkrevs = []
1387 parentlinkrevs = []
1388 for p in filelog.parentrevs(j):
1388 for p in filelog.parentrevs(j):
1389 if p != nullrev:
1389 if p != nullrev:
1390 parentlinkrevs.append(filelog.linkrev(p))
1390 parentlinkrevs.append(filelog.linkrev(p))
1391 n = filelog.node(j)
1391 n = filelog.node(j)
1392 revs.append((linkrev, parentlinkrevs,
1392 revs.append((linkrev, parentlinkrevs,
1393 follow and filelog.renamed(n)))
1393 follow and filelog.renamed(n)))
1394
1394
1395 return reversed(revs)
1395 return reversed(revs)
1396 def iterfiles():
1396 def iterfiles():
1397 pctx = repo['.']
1397 pctx = repo['.']
1398 for filename in match.files():
1398 for filename in match.files():
1399 if follow:
1399 if follow:
1400 if filename not in pctx:
1400 if filename not in pctx:
1401 raise util.Abort(_('cannot follow file not in parent '
1401 raise util.Abort(_('cannot follow file not in parent '
1402 'revision: "%s"') % filename)
1402 'revision: "%s"') % filename)
1403 yield filename, pctx[filename].filenode()
1403 yield filename, pctx[filename].filenode()
1404 else:
1404 else:
1405 yield filename, None
1405 yield filename, None
1406 for filename_node in copies:
1406 for filename_node in copies:
1407 yield filename_node
1407 yield filename_node
1408
1408
1409 for file_, node in iterfiles():
1409 for file_, node in iterfiles():
1410 filelog = repo.file(file_)
1410 filelog = repo.file(file_)
1411 if not len(filelog):
1411 if not len(filelog):
1412 if node is None:
1412 if node is None:
1413 # A zero count may be a directory or deleted file, so
1413 # A zero count may be a directory or deleted file, so
1414 # try to find matching entries on the slow path.
1414 # try to find matching entries on the slow path.
1415 if follow:
1415 if follow:
1416 raise util.Abort(
1416 raise util.Abort(
1417 _('cannot follow nonexistent file: "%s"') % file_)
1417 _('cannot follow nonexistent file: "%s"') % file_)
1418 raise FileWalkError("Cannot walk via filelog")
1418 raise FileWalkError("Cannot walk via filelog")
1419 else:
1419 else:
1420 continue
1420 continue
1421
1421
1422 if node is None:
1422 if node is None:
1423 last = len(filelog) - 1
1423 last = len(filelog) - 1
1424 else:
1424 else:
1425 last = filelog.rev(node)
1425 last = filelog.rev(node)
1426
1426
1427
1427
1428 # keep track of all ancestors of the file
1428 # keep track of all ancestors of the file
1429 ancestors = set([filelog.linkrev(last)])
1429 ancestors = set([filelog.linkrev(last)])
1430
1430
1431 # iterate from latest to oldest revision
1431 # iterate from latest to oldest revision
1432 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1432 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1433 if not follow:
1433 if not follow:
1434 if rev > maxrev:
1434 if rev > maxrev:
1435 continue
1435 continue
1436 else:
1436 else:
1437 # Note that last might not be the first interesting
1437 # Note that last might not be the first interesting
1438 # rev to us:
1438 # rev to us:
1439 # if the file has been changed after maxrev, we'll
1439 # if the file has been changed after maxrev, we'll
1440 # have linkrev(last) > maxrev, and we still need
1440 # have linkrev(last) > maxrev, and we still need
1441 # to explore the file graph
1441 # to explore the file graph
1442 if rev not in ancestors:
1442 if rev not in ancestors:
1443 continue
1443 continue
1444 # XXX insert 1327 fix here
1444 # XXX insert 1327 fix here
1445 if flparentlinkrevs:
1445 if flparentlinkrevs:
1446 ancestors.update(flparentlinkrevs)
1446 ancestors.update(flparentlinkrevs)
1447
1447
1448 fncache.setdefault(rev, []).append(file_)
1448 fncache.setdefault(rev, []).append(file_)
1449 wanted.add(rev)
1449 wanted.add(rev)
1450 if copied:
1450 if copied:
1451 copies.append(copied)
1451 copies.append(copied)
1452
1452
1453 return wanted
1453 return wanted
1454
1454
1455 def walkchangerevs(repo, match, opts, prepare):
1455 def walkchangerevs(repo, match, opts, prepare):
1456 '''Iterate over files and the revs in which they changed.
1456 '''Iterate over files and the revs in which they changed.
1457
1457
1458 Callers most commonly need to iterate backwards over the history
1458 Callers most commonly need to iterate backwards over the history
1459 in which they are interested. Doing so has awful (quadratic-looking)
1459 in which they are interested. Doing so has awful (quadratic-looking)
1460 performance, so we use iterators in a "windowed" way.
1460 performance, so we use iterators in a "windowed" way.
1461
1461
1462 We walk a window of revisions in the desired order. Within the
1462 We walk a window of revisions in the desired order. Within the
1463 window, we first walk forwards to gather data, then in the desired
1463 window, we first walk forwards to gather data, then in the desired
1464 order (usually backwards) to display it.
1464 order (usually backwards) to display it.
1465
1465
1466 This function returns an iterator yielding contexts. Before
1466 This function returns an iterator yielding contexts. Before
1467 yielding each context, the iterator will first call the prepare
1467 yielding each context, the iterator will first call the prepare
1468 function on each context in the window in forward order.'''
1468 function on each context in the window in forward order.'''
1469
1469
1470 follow = opts.get('follow') or opts.get('follow_first')
1470 follow = opts.get('follow') or opts.get('follow_first')
1471
1471
1472 if opts.get('rev'):
1472 if opts.get('rev'):
1473 revs = scmutil.revrange(repo, opts.get('rev'))
1473 revs = scmutil.revrange(repo, opts.get('rev'))
1474 elif follow:
1474 elif follow:
1475 revs = repo.revs('reverse(:.)')
1475 revs = repo.revs('reverse(:.)')
1476 else:
1476 else:
1477 revs = revset.spanset(repo)
1477 revs = revset.spanset(repo)
1478 revs.reverse()
1478 revs.reverse()
1479 if not revs:
1479 if not revs:
1480 return []
1480 return []
1481 wanted = set()
1481 wanted = set()
1482 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1482 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1483 fncache = {}
1483 fncache = {}
1484 change = repo.changectx
1484 change = repo.changectx
1485
1485
1486 # First step is to fill wanted, the set of revisions that we want to yield.
1486 # First step is to fill wanted, the set of revisions that we want to yield.
1487 # When it does not induce extra cost, we also fill fncache for revisions in
1487 # When it does not induce extra cost, we also fill fncache for revisions in
1488 # wanted: a cache of filenames that were changed (ctx.files()) and that
1488 # wanted: a cache of filenames that were changed (ctx.files()) and that
1489 # match the file filtering conditions.
1489 # match the file filtering conditions.
1490
1490
1491 if not slowpath and not match.files():
1491 if not slowpath and not match.files():
1492 # No files, no patterns. Display all revs.
1492 # No files, no patterns. Display all revs.
1493 wanted = revs
1493 wanted = revs
1494
1494
1495 if not slowpath and match.files():
1495 if not slowpath and match.files():
1496 # We only have to read through the filelog to find wanted revisions
1496 # We only have to read through the filelog to find wanted revisions
1497
1497
1498 try:
1498 try:
1499 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1499 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1500 except FileWalkError:
1500 except FileWalkError:
1501 slowpath = True
1501 slowpath = True
1502
1502
1503 # We decided to fall back to the slowpath because at least one
1503 # We decided to fall back to the slowpath because at least one
1504 # of the paths was not a file. Check to see if at least one of them
1504 # of the paths was not a file. Check to see if at least one of them
1505 # existed in history, otherwise simply return
1505 # existed in history, otherwise simply return
1506 for path in match.files():
1506 for path in match.files():
1507 if path == '.' or path in repo.store:
1507 if path == '.' or path in repo.store:
1508 break
1508 break
1509 else:
1509 else:
1510 return []
1510 return []
1511
1511
1512 if slowpath:
1512 if slowpath:
1513 # We have to read the changelog to match filenames against
1513 # We have to read the changelog to match filenames against
1514 # changed files
1514 # changed files
1515
1515
1516 if follow:
1516 if follow:
1517 raise util.Abort(_('can only follow copies/renames for explicit '
1517 raise util.Abort(_('can only follow copies/renames for explicit '
1518 'filenames'))
1518 'filenames'))
1519
1519
1520 # The slow path checks files modified in every changeset.
1520 # The slow path checks files modified in every changeset.
1521 # This is really slow on large repos, so compute the set lazily.
1521 # This is really slow on large repos, so compute the set lazily.
1522 class lazywantedset(object):
1522 class lazywantedset(object):
1523 def __init__(self):
1523 def __init__(self):
1524 self.set = set()
1524 self.set = set()
1525 self.revs = set(revs)
1525 self.revs = set(revs)
1526
1526
1527 # No need to worry about locality here because it will be accessed
1527 # No need to worry about locality here because it will be accessed
1528 # in the same order as the increasing window below.
1528 # in the same order as the increasing window below.
1529 def __contains__(self, value):
1529 def __contains__(self, value):
1530 if value in self.set:
1530 if value in self.set:
1531 return True
1531 return True
1532 elif not value in self.revs:
1532 elif not value in self.revs:
1533 return False
1533 return False
1534 else:
1534 else:
1535 self.revs.discard(value)
1535 self.revs.discard(value)
1536 ctx = change(value)
1536 ctx = change(value)
1537 matches = filter(match, ctx.files())
1537 matches = filter(match, ctx.files())
1538 if matches:
1538 if matches:
1539 fncache[value] = matches
1539 fncache[value] = matches
1540 self.set.add(value)
1540 self.set.add(value)
1541 return True
1541 return True
1542 return False
1542 return False
1543
1543
1544 def discard(self, value):
1544 def discard(self, value):
1545 self.revs.discard(value)
1545 self.revs.discard(value)
1546 self.set.discard(value)
1546 self.set.discard(value)
1547
1547
1548 wanted = lazywantedset()
1548 wanted = lazywantedset()
1549
1549
1550 class followfilter(object):
1550 class followfilter(object):
1551 def __init__(self, onlyfirst=False):
1551 def __init__(self, onlyfirst=False):
1552 self.startrev = nullrev
1552 self.startrev = nullrev
1553 self.roots = set()
1553 self.roots = set()
1554 self.onlyfirst = onlyfirst
1554 self.onlyfirst = onlyfirst
1555
1555
1556 def match(self, rev):
1556 def match(self, rev):
1557 def realparents(rev):
1557 def realparents(rev):
1558 if self.onlyfirst:
1558 if self.onlyfirst:
1559 return repo.changelog.parentrevs(rev)[0:1]
1559 return repo.changelog.parentrevs(rev)[0:1]
1560 else:
1560 else:
1561 return filter(lambda x: x != nullrev,
1561 return filter(lambda x: x != nullrev,
1562 repo.changelog.parentrevs(rev))
1562 repo.changelog.parentrevs(rev))
1563
1563
1564 if self.startrev == nullrev:
1564 if self.startrev == nullrev:
1565 self.startrev = rev
1565 self.startrev = rev
1566 return True
1566 return True
1567
1567
1568 if rev > self.startrev:
1568 if rev > self.startrev:
1569 # forward: all descendants
1569 # forward: all descendants
1570 if not self.roots:
1570 if not self.roots:
1571 self.roots.add(self.startrev)
1571 self.roots.add(self.startrev)
1572 for parent in realparents(rev):
1572 for parent in realparents(rev):
1573 if parent in self.roots:
1573 if parent in self.roots:
1574 self.roots.add(rev)
1574 self.roots.add(rev)
1575 return True
1575 return True
1576 else:
1576 else:
1577 # backwards: all parents
1577 # backwards: all parents
1578 if not self.roots:
1578 if not self.roots:
1579 self.roots.update(realparents(self.startrev))
1579 self.roots.update(realparents(self.startrev))
1580 if rev in self.roots:
1580 if rev in self.roots:
1581 self.roots.remove(rev)
1581 self.roots.remove(rev)
1582 self.roots.update(realparents(rev))
1582 self.roots.update(realparents(rev))
1583 return True
1583 return True
1584
1584
1585 return False
1585 return False
1586
1586
1587 # it might be worthwhile to do this in the iterator if the rev range
1587 # it might be worthwhile to do this in the iterator if the rev range
1588 # is descending and the prune args are all within that range
1588 # is descending and the prune args are all within that range
1589 for rev in opts.get('prune', ()):
1589 for rev in opts.get('prune', ()):
1590 rev = repo[rev].rev()
1590 rev = repo[rev].rev()
1591 ff = followfilter()
1591 ff = followfilter()
1592 stop = min(revs[0], revs[-1])
1592 stop = min(revs[0], revs[-1])
1593 for x in xrange(rev, stop - 1, -1):
1593 for x in xrange(rev, stop - 1, -1):
1594 if ff.match(x):
1594 if ff.match(x):
1595 wanted = wanted - [x]
1595 wanted = wanted - [x]
1596
1596
1597 # Now that wanted is correctly initialized, we can iterate over the
1597 # Now that wanted is correctly initialized, we can iterate over the
1598 # revision range, yielding only revisions in wanted.
1598 # revision range, yielding only revisions in wanted.
1599 def iterate():
1599 def iterate():
1600 if follow and not match.files():
1600 if follow and not match.files():
1601 ff = followfilter(onlyfirst=opts.get('follow_first'))
1601 ff = followfilter(onlyfirst=opts.get('follow_first'))
1602 def want(rev):
1602 def want(rev):
1603 return ff.match(rev) and rev in wanted
1603 return ff.match(rev) and rev in wanted
1604 else:
1604 else:
1605 def want(rev):
1605 def want(rev):
1606 return rev in wanted
1606 return rev in wanted
1607
1607
1608 it = iter(revs)
1608 it = iter(revs)
1609 stopiteration = False
1609 stopiteration = False
1610 for windowsize in increasingwindows():
1610 for windowsize in increasingwindows():
1611 nrevs = []
1611 nrevs = []
1612 for i in xrange(windowsize):
1612 for i in xrange(windowsize):
1613 try:
1613 try:
1614 rev = it.next()
1614 rev = it.next()
1615 if want(rev):
1615 if want(rev):
1616 nrevs.append(rev)
1616 nrevs.append(rev)
1617 except (StopIteration):
1617 except (StopIteration):
1618 stopiteration = True
1618 stopiteration = True
1619 break
1619 break
1620 for rev in sorted(nrevs):
1620 for rev in sorted(nrevs):
1621 fns = fncache.get(rev)
1621 fns = fncache.get(rev)
1622 ctx = change(rev)
1622 ctx = change(rev)
1623 if not fns:
1623 if not fns:
1624 def fns_generator():
1624 def fns_generator():
1625 for f in ctx.files():
1625 for f in ctx.files():
1626 if match(f):
1626 if match(f):
1627 yield f
1627 yield f
1628 fns = fns_generator()
1628 fns = fns_generator()
1629 prepare(ctx, fns)
1629 prepare(ctx, fns)
1630 for rev in nrevs:
1630 for rev in nrevs:
1631 yield change(rev)
1631 yield change(rev)
1632
1632
1633 if stopiteration:
1633 if stopiteration:
1634 break
1634 break
1635
1635
1636 return iterate()
1636 return iterate()
1637
1637
1638 def _makefollowlogfilematcher(repo, files, followfirst):
1638 def _makefollowlogfilematcher(repo, files, followfirst):
1639 # When displaying a revision with --patch --follow FILE, we have
1639 # When displaying a revision with --patch --follow FILE, we have
1640 # to know which file of the revision must be diffed. With
1640 # to know which file of the revision must be diffed. With
1641 # --follow, we want the names of the ancestors of FILE in the
1641 # --follow, we want the names of the ancestors of FILE in the
1642 # revision, stored in "fcache". "fcache" is populated by
1642 # revision, stored in "fcache". "fcache" is populated by
1643 # reproducing the graph traversal already done by --follow revset
1643 # reproducing the graph traversal already done by --follow revset
1644 # and relating linkrevs to file names (which is not "correct" but
1644 # and relating linkrevs to file names (which is not "correct" but
1645 # good enough).
1645 # good enough).
1646 fcache = {}
1646 fcache = {}
1647 fcacheready = [False]
1647 fcacheready = [False]
1648 pctx = repo['.']
1648 pctx = repo['.']
1649
1649
1650 def populate():
1650 def populate():
1651 for fn in files:
1651 for fn in files:
1652 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1652 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1653 for c in i:
1653 for c in i:
1654 fcache.setdefault(c.linkrev(), set()).add(c.path())
1654 fcache.setdefault(c.linkrev(), set()).add(c.path())
1655
1655
1656 def filematcher(rev):
1656 def filematcher(rev):
1657 if not fcacheready[0]:
1657 if not fcacheready[0]:
1658 # Lazy initialization
1658 # Lazy initialization
1659 fcacheready[0] = True
1659 fcacheready[0] = True
1660 populate()
1660 populate()
1661 return scmutil.matchfiles(repo, fcache.get(rev, []))
1661 return scmutil.matchfiles(repo, fcache.get(rev, []))
1662
1662
1663 return filematcher
1663 return filematcher
1664
1664
1665 def _makenofollowlogfilematcher(repo, pats, opts):
1665 def _makenofollowlogfilematcher(repo, pats, opts):
1666 '''hook for extensions to override the filematcher for non-follow cases'''
1666 '''hook for extensions to override the filematcher for non-follow cases'''
1667 return None
1667 return None
1668
1668
1669 def _makelogrevset(repo, pats, opts, revs):
1669 def _makelogrevset(repo, pats, opts, revs):
1670 """Return (expr, filematcher) where expr is a revset string built
1670 """Return (expr, filematcher) where expr is a revset string built
1671 from log options and file patterns or None. If --stat or --patch
1671 from log options and file patterns or None. If --stat or --patch
1672 are not passed filematcher is None. Otherwise it is a callable
1672 are not passed filematcher is None. Otherwise it is a callable
1673 taking a revision number and returning a match objects filtering
1673 taking a revision number and returning a match objects filtering
1674 the files to be detailed when displaying the revision.
1674 the files to be detailed when displaying the revision.
1675 """
1675 """
1676 opt2revset = {
1676 opt2revset = {
1677 'no_merges': ('not merge()', None),
1677 'no_merges': ('not merge()', None),
1678 'only_merges': ('merge()', None),
1678 'only_merges': ('merge()', None),
1679 '_ancestors': ('ancestors(%(val)s)', None),
1679 '_ancestors': ('ancestors(%(val)s)', None),
1680 '_fancestors': ('_firstancestors(%(val)s)', None),
1680 '_fancestors': ('_firstancestors(%(val)s)', None),
1681 '_descendants': ('descendants(%(val)s)', None),
1681 '_descendants': ('descendants(%(val)s)', None),
1682 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1682 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1683 '_matchfiles': ('_matchfiles(%(val)s)', None),
1683 '_matchfiles': ('_matchfiles(%(val)s)', None),
1684 'date': ('date(%(val)r)', None),
1684 'date': ('date(%(val)r)', None),
1685 'branch': ('branch(%(val)r)', ' or '),
1685 'branch': ('branch(%(val)r)', ' or '),
1686 '_patslog': ('filelog(%(val)r)', ' or '),
1686 '_patslog': ('filelog(%(val)r)', ' or '),
1687 '_patsfollow': ('follow(%(val)r)', ' or '),
1687 '_patsfollow': ('follow(%(val)r)', ' or '),
1688 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1688 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1689 'keyword': ('keyword(%(val)r)', ' or '),
1689 'keyword': ('keyword(%(val)r)', ' or '),
1690 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1690 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1691 'user': ('user(%(val)r)', ' or '),
1691 'user': ('user(%(val)r)', ' or '),
1692 }
1692 }
1693
1693
1694 opts = dict(opts)
1694 opts = dict(opts)
1695 # follow or not follow?
1695 # follow or not follow?
1696 follow = opts.get('follow') or opts.get('follow_first')
1696 follow = opts.get('follow') or opts.get('follow_first')
1697 followfirst = opts.get('follow_first') and 1 or 0
1697 followfirst = opts.get('follow_first') and 1 or 0
1698 # --follow with FILE behaviour depends on revs...
1698 # --follow with FILE behaviour depends on revs...
1699 it = iter(revs)
1699 it = iter(revs)
1700 startrev = it.next()
1700 startrev = it.next()
1701 try:
1701 try:
1702 followdescendants = startrev < it.next()
1702 followdescendants = startrev < it.next()
1703 except (StopIteration):
1703 except (StopIteration):
1704 followdescendants = False
1704 followdescendants = False
1705
1705
1706 # branch and only_branch are really aliases and must be handled at
1706 # branch and only_branch are really aliases and must be handled at
1707 # the same time
1707 # the same time
1708 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1708 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1709 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1709 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1710 # pats/include/exclude are passed to match.match() directly in
1710 # pats/include/exclude are passed to match.match() directly in
1711 # _matchfiles() revset but walkchangerevs() builds its matcher with
1711 # _matchfiles() revset but walkchangerevs() builds its matcher with
1712 # scmutil.match(). The difference is input pats are globbed on
1712 # scmutil.match(). The difference is input pats are globbed on
1713 # platforms without shell expansion (windows).
1713 # platforms without shell expansion (windows).
1714 pctx = repo[None]
1714 pctx = repo[None]
1715 match, pats = scmutil.matchandpats(pctx, pats, opts)
1715 match, pats = scmutil.matchandpats(pctx, pats, opts)
1716 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1716 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1717 if not slowpath:
1717 if not slowpath:
1718 for f in match.files():
1718 for f in match.files():
1719 if follow and f not in pctx:
1719 if follow and f not in pctx:
1720 # If the file exists, it may be a directory, so let it
1720 # If the file exists, it may be a directory, so let it
1721 # take the slow path.
1721 # take the slow path.
1722 if os.path.exists(repo.wjoin(f)):
1722 if os.path.exists(repo.wjoin(f)):
1723 slowpath = True
1723 slowpath = True
1724 continue
1724 continue
1725 else:
1725 else:
1726 raise util.Abort(_('cannot follow file not in parent '
1726 raise util.Abort(_('cannot follow file not in parent '
1727 'revision: "%s"') % f)
1727 'revision: "%s"') % f)
1728 filelog = repo.file(f)
1728 filelog = repo.file(f)
1729 if not filelog:
1729 if not filelog:
1730 # A zero count may be a directory or deleted file, so
1730 # A zero count may be a directory or deleted file, so
1731 # try to find matching entries on the slow path.
1731 # try to find matching entries on the slow path.
1732 if follow:
1732 if follow:
1733 raise util.Abort(
1733 raise util.Abort(
1734 _('cannot follow nonexistent file: "%s"') % f)
1734 _('cannot follow nonexistent file: "%s"') % f)
1735 slowpath = True
1735 slowpath = True
1736
1736
1737 # We decided to fall back to the slowpath because at least one
1737 # We decided to fall back to the slowpath because at least one
1738 # of the paths was not a file. Check to see if at least one of them
1738 # of the paths was not a file. Check to see if at least one of them
1739 # existed in history - in that case, we'll continue down the
1739 # existed in history - in that case, we'll continue down the
1740 # slowpath; otherwise, we can turn off the slowpath
1740 # slowpath; otherwise, we can turn off the slowpath
1741 if slowpath:
1741 if slowpath:
1742 for path in match.files():
1742 for path in match.files():
1743 if path == '.' or path in repo.store:
1743 if path == '.' or path in repo.store:
1744 break
1744 break
1745 else:
1745 else:
1746 slowpath = False
1746 slowpath = False
1747
1747
1748 fpats = ('_patsfollow', '_patsfollowfirst')
1748 fpats = ('_patsfollow', '_patsfollowfirst')
1749 fnopats = (('_ancestors', '_fancestors'),
1749 fnopats = (('_ancestors', '_fancestors'),
1750 ('_descendants', '_fdescendants'))
1750 ('_descendants', '_fdescendants'))
1751 if slowpath:
1751 if slowpath:
1752 # See walkchangerevs() slow path.
1752 # See walkchangerevs() slow path.
1753 #
1753 #
1754 # pats/include/exclude cannot be represented as separate
1754 # pats/include/exclude cannot be represented as separate
1755 # revset expressions as their filtering logic applies at file
1755 # revset expressions as their filtering logic applies at file
1756 # level. For instance "-I a -X a" matches a revision touching
1756 # level. For instance "-I a -X a" matches a revision touching
1757 # "a" and "b" while "file(a) and not file(b)" does
1757 # "a" and "b" while "file(a) and not file(b)" does
1758 # not. Besides, filesets are evaluated against the working
1758 # not. Besides, filesets are evaluated against the working
1759 # directory.
1759 # directory.
1760 matchargs = ['r:', 'd:relpath']
1760 matchargs = ['r:', 'd:relpath']
1761 for p in pats:
1761 for p in pats:
1762 matchargs.append('p:' + p)
1762 matchargs.append('p:' + p)
1763 for p in opts.get('include', []):
1763 for p in opts.get('include', []):
1764 matchargs.append('i:' + p)
1764 matchargs.append('i:' + p)
1765 for p in opts.get('exclude', []):
1765 for p in opts.get('exclude', []):
1766 matchargs.append('x:' + p)
1766 matchargs.append('x:' + p)
1767 matchargs = ','.join(('%r' % p) for p in matchargs)
1767 matchargs = ','.join(('%r' % p) for p in matchargs)
1768 opts['_matchfiles'] = matchargs
1768 opts['_matchfiles'] = matchargs
1769 if follow:
1769 if follow:
1770 opts[fnopats[0][followfirst]] = '.'
1770 opts[fnopats[0][followfirst]] = '.'
1771 else:
1771 else:
1772 if follow:
1772 if follow:
1773 if pats:
1773 if pats:
1774 # follow() revset interprets its file argument as a
1774 # follow() revset interprets its file argument as a
1775 # manifest entry, so use match.files(), not pats.
1775 # manifest entry, so use match.files(), not pats.
1776 opts[fpats[followfirst]] = list(match.files())
1776 opts[fpats[followfirst]] = list(match.files())
1777 else:
1777 else:
1778 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1778 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1779 else:
1779 else:
1780 opts['_patslog'] = list(pats)
1780 opts['_patslog'] = list(pats)
1781
1781
1782 filematcher = None
1782 filematcher = None
1783 if opts.get('patch') or opts.get('stat'):
1783 if opts.get('patch') or opts.get('stat'):
1784 # When following files, track renames via a special matcher.
1784 # When following files, track renames via a special matcher.
1785 # If we're forced to take the slowpath it means we're following
1785 # If we're forced to take the slowpath it means we're following
1786 # at least one pattern/directory, so don't bother with rename tracking.
1786 # at least one pattern/directory, so don't bother with rename tracking.
1787 if follow and not match.always() and not slowpath:
1787 if follow and not match.always() and not slowpath:
1788 # _makelogfilematcher expects its files argument to be relative to
1788 # _makelogfilematcher expects its files argument to be relative to
1789 # the repo root, so use match.files(), not pats.
1789 # the repo root, so use match.files(), not pats.
1790 filematcher = _makefollowlogfilematcher(repo, match.files(),
1790 filematcher = _makefollowlogfilematcher(repo, match.files(),
1791 followfirst)
1791 followfirst)
1792 else:
1792 else:
1793 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1793 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1794 if filematcher is None:
1794 if filematcher is None:
1795 filematcher = lambda rev: match
1795 filematcher = lambda rev: match
1796
1796
1797 expr = []
1797 expr = []
1798 for op, val in sorted(opts.iteritems()):
1798 for op, val in sorted(opts.iteritems()):
1799 if not val:
1799 if not val:
1800 continue
1800 continue
1801 if op not in opt2revset:
1801 if op not in opt2revset:
1802 continue
1802 continue
1803 revop, andor = opt2revset[op]
1803 revop, andor = opt2revset[op]
1804 if '%(val)' not in revop:
1804 if '%(val)' not in revop:
1805 expr.append(revop)
1805 expr.append(revop)
1806 else:
1806 else:
1807 if not isinstance(val, list):
1807 if not isinstance(val, list):
1808 e = revop % {'val': val}
1808 e = revop % {'val': val}
1809 else:
1809 else:
1810 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1810 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1811 expr.append(e)
1811 expr.append(e)
1812
1812
1813 if expr:
1813 if expr:
1814 expr = '(' + ' and '.join(expr) + ')'
1814 expr = '(' + ' and '.join(expr) + ')'
1815 else:
1815 else:
1816 expr = None
1816 expr = None
1817 return expr, filematcher
1817 return expr, filematcher
1818
1818
1819 def getgraphlogrevs(repo, pats, opts):
1819 def getgraphlogrevs(repo, pats, opts):
1820 """Return (revs, expr, filematcher) where revs is an iterable of
1820 """Return (revs, expr, filematcher) where revs is an iterable of
1821 revision numbers, expr is a revset string built from log options
1821 revision numbers, expr is a revset string built from log options
1822 and file patterns or None, and used to filter 'revs'. If --stat or
1822 and file patterns or None, and used to filter 'revs'. If --stat or
1823 --patch are not passed filematcher is None. Otherwise it is a
1823 --patch are not passed filematcher is None. Otherwise it is a
1824 callable taking a revision number and returning a match objects
1824 callable taking a revision number and returning a match objects
1825 filtering the files to be detailed when displaying the revision.
1825 filtering the files to be detailed when displaying the revision.
1826 """
1826 """
1827 if not len(repo):
1827 if not len(repo):
1828 return [], None, None
1828 return [], None, None
1829 limit = loglimit(opts)
1829 limit = loglimit(opts)
1830 # Default --rev value depends on --follow but --follow behaviour
1830 # Default --rev value depends on --follow but --follow behaviour
1831 # depends on revisions resolved from --rev...
1831 # depends on revisions resolved from --rev...
1832 follow = opts.get('follow') or opts.get('follow_first')
1832 follow = opts.get('follow') or opts.get('follow_first')
1833 possiblyunsorted = False # whether revs might need sorting
1833 possiblyunsorted = False # whether revs might need sorting
1834 if opts.get('rev'):
1834 if opts.get('rev'):
1835 revs = scmutil.revrange(repo, opts['rev'])
1835 revs = scmutil.revrange(repo, opts['rev'])
1836 # Don't sort here because _makelogrevset might depend on the
1836 # Don't sort here because _makelogrevset might depend on the
1837 # order of revs
1837 # order of revs
1838 possiblyunsorted = True
1838 possiblyunsorted = True
1839 else:
1839 else:
1840 if follow and len(repo) > 0:
1840 if follow and len(repo) > 0:
1841 revs = repo.revs('reverse(:.)')
1841 revs = repo.revs('reverse(:.)')
1842 else:
1842 else:
1843 revs = revset.spanset(repo)
1843 revs = revset.spanset(repo)
1844 revs.reverse()
1844 revs.reverse()
1845 if not revs:
1845 if not revs:
1846 return revset.baseset(), None, None
1846 return revset.baseset(), None, None
1847 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1847 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1848 if possiblyunsorted:
1848 if possiblyunsorted:
1849 revs.sort(reverse=True)
1849 revs.sort(reverse=True)
1850 if expr:
1850 if expr:
1851 # Revset matchers often operate faster on revisions in changelog
1851 # Revset matchers often operate faster on revisions in changelog
1852 # order, because most filters deal with the changelog.
1852 # order, because most filters deal with the changelog.
1853 revs.reverse()
1853 revs.reverse()
1854 matcher = revset.match(repo.ui, expr)
1854 matcher = revset.match(repo.ui, expr)
1855 # Revset matches can reorder revisions. "A or B" typically returns
1855 # Revset matches can reorder revisions. "A or B" typically returns
1856 # returns the revision matching A then the revision matching B. Sort
1856 # returns the revision matching A then the revision matching B. Sort
1857 # again to fix that.
1857 # again to fix that.
1858 revs = matcher(repo, revs)
1858 revs = matcher(repo, revs)
1859 revs.sort(reverse=True)
1859 revs.sort(reverse=True)
1860 if limit is not None:
1860 if limit is not None:
1861 limitedrevs = []
1861 limitedrevs = []
1862 for idx, rev in enumerate(revs):
1862 for idx, rev in enumerate(revs):
1863 if idx >= limit:
1863 if idx >= limit:
1864 break
1864 break
1865 limitedrevs.append(rev)
1865 limitedrevs.append(rev)
1866 revs = revset.baseset(limitedrevs)
1866 revs = revset.baseset(limitedrevs)
1867
1867
1868 return revs, expr, filematcher
1868 return revs, expr, filematcher
1869
1869
1870 def getlogrevs(repo, pats, opts):
1870 def getlogrevs(repo, pats, opts):
1871 """Return (revs, expr, filematcher) where revs is an iterable of
1871 """Return (revs, expr, filematcher) where revs is an iterable of
1872 revision numbers, expr is a revset string built from log options
1872 revision numbers, expr is a revset string built from log options
1873 and file patterns or None, and used to filter 'revs'. If --stat or
1873 and file patterns or None, and used to filter 'revs'. If --stat or
1874 --patch are not passed filematcher is None. Otherwise it is a
1874 --patch are not passed filematcher is None. Otherwise it is a
1875 callable taking a revision number and returning a match objects
1875 callable taking a revision number and returning a match objects
1876 filtering the files to be detailed when displaying the revision.
1876 filtering the files to be detailed when displaying the revision.
1877 """
1877 """
1878 limit = loglimit(opts)
1878 limit = loglimit(opts)
1879 # Default --rev value depends on --follow but --follow behaviour
1879 # Default --rev value depends on --follow but --follow behaviour
1880 # depends on revisions resolved from --rev...
1880 # depends on revisions resolved from --rev...
1881 follow = opts.get('follow') or opts.get('follow_first')
1881 follow = opts.get('follow') or opts.get('follow_first')
1882 if opts.get('rev'):
1882 if opts.get('rev'):
1883 revs = scmutil.revrange(repo, opts['rev'])
1883 revs = scmutil.revrange(repo, opts['rev'])
1884 elif follow:
1884 elif follow:
1885 revs = repo.revs('reverse(:.)')
1885 revs = repo.revs('reverse(:.)')
1886 else:
1886 else:
1887 revs = revset.spanset(repo)
1887 revs = revset.spanset(repo)
1888 revs.reverse()
1888 revs.reverse()
1889 if not revs:
1889 if not revs:
1890 return revset.baseset([]), None, None
1890 return revset.baseset([]), None, None
1891 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1891 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1892 if expr:
1892 if expr:
1893 # Revset matchers often operate faster on revisions in changelog
1893 # Revset matchers often operate faster on revisions in changelog
1894 # order, because most filters deal with the changelog.
1894 # order, because most filters deal with the changelog.
1895 if not opts.get('rev'):
1895 if not opts.get('rev'):
1896 revs.reverse()
1896 revs.reverse()
1897 matcher = revset.match(repo.ui, expr)
1897 matcher = revset.match(repo.ui, expr)
1898 # Revset matches can reorder revisions. "A or B" typically returns
1898 # Revset matches can reorder revisions. "A or B" typically returns
1899 # returns the revision matching A then the revision matching B. Sort
1899 # returns the revision matching A then the revision matching B. Sort
1900 # again to fix that.
1900 # again to fix that.
1901 revs = matcher(repo, revs)
1901 revs = matcher(repo, revs)
1902 if not opts.get('rev'):
1902 if not opts.get('rev'):
1903 revs.sort(reverse=True)
1903 revs.sort(reverse=True)
1904 if limit is not None:
1904 if limit is not None:
1905 count = 0
1905 count = 0
1906 limitedrevs = []
1906 limitedrevs = []
1907 it = iter(revs)
1907 it = iter(revs)
1908 while count < limit:
1908 while count < limit:
1909 try:
1909 try:
1910 limitedrevs.append(it.next())
1910 limitedrevs.append(it.next())
1911 except (StopIteration):
1911 except (StopIteration):
1912 break
1912 break
1913 count += 1
1913 count += 1
1914 revs = revset.baseset(limitedrevs)
1914 revs = revset.baseset(limitedrevs)
1915
1915
1916 return revs, expr, filematcher
1916 return revs, expr, filematcher
1917
1917
1918 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1918 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1919 filematcher=None):
1919 filematcher=None):
1920 seen, state = [], graphmod.asciistate()
1920 seen, state = [], graphmod.asciistate()
1921 for rev, type, ctx, parents in dag:
1921 for rev, type, ctx, parents in dag:
1922 char = 'o'
1922 char = 'o'
1923 if ctx.node() in showparents:
1923 if ctx.node() in showparents:
1924 char = '@'
1924 char = '@'
1925 elif ctx.obsolete():
1925 elif ctx.obsolete():
1926 char = 'x'
1926 char = 'x'
1927 copies = None
1927 copies = None
1928 if getrenamed and ctx.rev():
1928 if getrenamed and ctx.rev():
1929 copies = []
1929 copies = []
1930 for fn in ctx.files():
1930 for fn in ctx.files():
1931 rename = getrenamed(fn, ctx.rev())
1931 rename = getrenamed(fn, ctx.rev())
1932 if rename:
1932 if rename:
1933 copies.append((fn, rename[0]))
1933 copies.append((fn, rename[0]))
1934 revmatchfn = None
1934 revmatchfn = None
1935 if filematcher is not None:
1935 if filematcher is not None:
1936 revmatchfn = filematcher(ctx.rev())
1936 revmatchfn = filematcher(ctx.rev())
1937 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1937 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1938 lines = displayer.hunk.pop(rev).split('\n')
1938 lines = displayer.hunk.pop(rev).split('\n')
1939 if not lines[-1]:
1939 if not lines[-1]:
1940 del lines[-1]
1940 del lines[-1]
1941 displayer.flush(rev)
1941 displayer.flush(rev)
1942 edges = edgefn(type, char, lines, seen, rev, parents)
1942 edges = edgefn(type, char, lines, seen, rev, parents)
1943 for type, char, lines, coldata in edges:
1943 for type, char, lines, coldata in edges:
1944 graphmod.ascii(ui, state, type, char, lines, coldata)
1944 graphmod.ascii(ui, state, type, char, lines, coldata)
1945 displayer.close()
1945 displayer.close()
1946
1946
1947 def graphlog(ui, repo, *pats, **opts):
1947 def graphlog(ui, repo, *pats, **opts):
1948 # Parameters are identical to log command ones
1948 # Parameters are identical to log command ones
1949 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1949 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1950 revdag = graphmod.dagwalker(repo, revs)
1950 revdag = graphmod.dagwalker(repo, revs)
1951
1951
1952 getrenamed = None
1952 getrenamed = None
1953 if opts.get('copies'):
1953 if opts.get('copies'):
1954 endrev = None
1954 endrev = None
1955 if opts.get('rev'):
1955 if opts.get('rev'):
1956 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1956 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1957 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1957 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1958 displayer = show_changeset(ui, repo, opts, buffered=True)
1958 displayer = show_changeset(ui, repo, opts, buffered=True)
1959 showparents = [ctx.node() for ctx in repo[None].parents()]
1959 showparents = [ctx.node() for ctx in repo[None].parents()]
1960 displaygraph(ui, revdag, displayer, showparents,
1960 displaygraph(ui, revdag, displayer, showparents,
1961 graphmod.asciiedges, getrenamed, filematcher)
1961 graphmod.asciiedges, getrenamed, filematcher)
1962
1962
1963 def checkunsupportedgraphflags(pats, opts):
1963 def checkunsupportedgraphflags(pats, opts):
1964 for op in ["newest_first"]:
1964 for op in ["newest_first"]:
1965 if op in opts and opts[op]:
1965 if op in opts and opts[op]:
1966 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1966 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1967 % op.replace("_", "-"))
1967 % op.replace("_", "-"))
1968
1968
1969 def graphrevs(repo, nodes, opts):
1969 def graphrevs(repo, nodes, opts):
1970 limit = loglimit(opts)
1970 limit = loglimit(opts)
1971 nodes.reverse()
1971 nodes.reverse()
1972 if limit is not None:
1972 if limit is not None:
1973 nodes = nodes[:limit]
1973 nodes = nodes[:limit]
1974 return graphmod.nodes(repo, nodes)
1974 return graphmod.nodes(repo, nodes)
1975
1975
1976 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1976 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1977 join = lambda f: os.path.join(prefix, f)
1977 join = lambda f: os.path.join(prefix, f)
1978 bad = []
1978 bad = []
1979 oldbad = match.bad
1979 oldbad = match.bad
1980 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1980 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1981 names = []
1981 names = []
1982 wctx = repo[None]
1982 wctx = repo[None]
1983 cca = None
1983 cca = None
1984 abort, warn = scmutil.checkportabilityalert(ui)
1984 abort, warn = scmutil.checkportabilityalert(ui)
1985 if abort or warn:
1985 if abort or warn:
1986 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1986 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1987 for f in repo.walk(match):
1987 for f in repo.walk(match):
1988 exact = match.exact(f)
1988 exact = match.exact(f)
1989 if exact or not explicitonly and f not in repo.dirstate:
1989 if exact or not explicitonly and f not in repo.dirstate:
1990 if cca:
1990 if cca:
1991 cca(f)
1991 cca(f)
1992 names.append(f)
1992 names.append(f)
1993 if ui.verbose or not exact:
1993 if ui.verbose or not exact:
1994 ui.status(_('adding %s\n') % match.rel(join(f)))
1994 ui.status(_('adding %s\n') % match.rel(join(f)))
1995
1995
1996 for subpath in sorted(wctx.substate):
1996 for subpath in sorted(wctx.substate):
1997 sub = wctx.sub(subpath)
1997 sub = wctx.sub(subpath)
1998 try:
1998 try:
1999 submatch = matchmod.narrowmatcher(subpath, match)
1999 submatch = matchmod.narrowmatcher(subpath, match)
2000 if listsubrepos:
2000 if listsubrepos:
2001 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
2001 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
2002 False))
2002 False))
2003 else:
2003 else:
2004 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
2004 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
2005 True))
2005 True))
2006 except error.LookupError:
2006 except error.LookupError:
2007 ui.status(_("skipping missing subrepository: %s\n")
2007 ui.status(_("skipping missing subrepository: %s\n")
2008 % join(subpath))
2008 % join(subpath))
2009
2009
2010 if not dryrun:
2010 if not dryrun:
2011 rejected = wctx.add(names, prefix)
2011 rejected = wctx.add(names, prefix)
2012 bad.extend(f for f in rejected if f in match.files())
2012 bad.extend(f for f in rejected if f in match.files())
2013 return bad
2013 return bad
2014
2014
2015 def forget(ui, repo, match, prefix, explicitonly):
2015 def forget(ui, repo, match, prefix, explicitonly):
2016 join = lambda f: os.path.join(prefix, f)
2016 join = lambda f: os.path.join(prefix, f)
2017 bad = []
2017 bad = []
2018 oldbad = match.bad
2018 oldbad = match.bad
2019 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2019 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2020 wctx = repo[None]
2020 wctx = repo[None]
2021 forgot = []
2021 forgot = []
2022 s = repo.status(match=match, clean=True)
2022 s = repo.status(match=match, clean=True)
2023 forget = sorted(s[0] + s[1] + s[3] + s[6])
2023 forget = sorted(s[0] + s[1] + s[3] + s[6])
2024 if explicitonly:
2024 if explicitonly:
2025 forget = [f for f in forget if match.exact(f)]
2025 forget = [f for f in forget if match.exact(f)]
2026
2026
2027 for subpath in sorted(wctx.substate):
2027 for subpath in sorted(wctx.substate):
2028 sub = wctx.sub(subpath)
2028 sub = wctx.sub(subpath)
2029 try:
2029 try:
2030 submatch = matchmod.narrowmatcher(subpath, match)
2030 submatch = matchmod.narrowmatcher(subpath, match)
2031 subbad, subforgot = sub.forget(ui, submatch, prefix)
2031 subbad, subforgot = sub.forget(ui, submatch, prefix)
2032 bad.extend([subpath + '/' + f for f in subbad])
2032 bad.extend([subpath + '/' + f for f in subbad])
2033 forgot.extend([subpath + '/' + f for f in subforgot])
2033 forgot.extend([subpath + '/' + f for f in subforgot])
2034 except error.LookupError:
2034 except error.LookupError:
2035 ui.status(_("skipping missing subrepository: %s\n")
2035 ui.status(_("skipping missing subrepository: %s\n")
2036 % join(subpath))
2036 % join(subpath))
2037
2037
2038 if not explicitonly:
2038 if not explicitonly:
2039 for f in match.files():
2039 for f in match.files():
2040 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
2040 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
2041 if f not in forgot:
2041 if f not in forgot:
2042 if os.path.exists(match.rel(join(f))):
2042 if os.path.exists(match.rel(join(f))):
2043 ui.warn(_('not removing %s: '
2043 ui.warn(_('not removing %s: '
2044 'file is already untracked\n')
2044 'file is already untracked\n')
2045 % match.rel(join(f)))
2045 % match.rel(join(f)))
2046 bad.append(f)
2046 bad.append(f)
2047
2047
2048 for f in forget:
2048 for f in forget:
2049 if ui.verbose or not match.exact(f):
2049 if ui.verbose or not match.exact(f):
2050 ui.status(_('removing %s\n') % match.rel(join(f)))
2050 ui.status(_('removing %s\n') % match.rel(join(f)))
2051
2051
2052 rejected = wctx.forget(forget, prefix)
2052 rejected = wctx.forget(forget, prefix)
2053 bad.extend(f for f in rejected if f in match.files())
2053 bad.extend(f for f in rejected if f in match.files())
2054 forgot.extend(forget)
2054 forgot.extend(forget)
2055 return bad, forgot
2055 return bad, forgot
2056
2056
2057 def cat(ui, repo, ctx, matcher, prefix, **opts):
2057 def cat(ui, repo, ctx, matcher, prefix, **opts):
2058 err = 1
2058 err = 1
2059
2059
2060 def write(path):
2060 def write(path):
2061 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2061 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2062 pathname=os.path.join(prefix, path))
2062 pathname=os.path.join(prefix, path))
2063 data = ctx[path].data()
2063 data = ctx[path].data()
2064 if opts.get('decode'):
2064 if opts.get('decode'):
2065 data = repo.wwritedata(path, data)
2065 data = repo.wwritedata(path, data)
2066 fp.write(data)
2066 fp.write(data)
2067 fp.close()
2067 fp.close()
2068
2068
2069 # Automation often uses hg cat on single files, so special case it
2069 # Automation often uses hg cat on single files, so special case it
2070 # for performance to avoid the cost of parsing the manifest.
2070 # for performance to avoid the cost of parsing the manifest.
2071 if len(matcher.files()) == 1 and not matcher.anypats():
2071 if len(matcher.files()) == 1 and not matcher.anypats():
2072 file = matcher.files()[0]
2072 file = matcher.files()[0]
2073 mf = repo.manifest
2073 mf = repo.manifest
2074 mfnode = ctx._changeset[0]
2074 mfnode = ctx._changeset[0]
2075 if mf.find(mfnode, file)[0]:
2075 if mf.find(mfnode, file)[0]:
2076 write(file)
2076 write(file)
2077 return 0
2077 return 0
2078
2078
2079 # Don't warn about "missing" files that are really in subrepos
2079 # Don't warn about "missing" files that are really in subrepos
2080 bad = matcher.bad
2080 bad = matcher.bad
2081
2081
2082 def badfn(path, msg):
2082 def badfn(path, msg):
2083 for subpath in ctx.substate:
2083 for subpath in ctx.substate:
2084 if path.startswith(subpath):
2084 if path.startswith(subpath):
2085 return
2085 return
2086 bad(path, msg)
2086 bad(path, msg)
2087
2087
2088 matcher.bad = badfn
2088 matcher.bad = badfn
2089
2089
2090 for abs in ctx.walk(matcher):
2090 for abs in ctx.walk(matcher):
2091 write(abs)
2091 write(abs)
2092 err = 0
2092 err = 0
2093
2093
2094 matcher.bad = bad
2094 matcher.bad = bad
2095
2095
2096 for subpath in sorted(ctx.substate):
2096 for subpath in sorted(ctx.substate):
2097 sub = ctx.sub(subpath)
2097 sub = ctx.sub(subpath)
2098 try:
2098 try:
2099 submatch = matchmod.narrowmatcher(subpath, matcher)
2099 submatch = matchmod.narrowmatcher(subpath, matcher)
2100
2100
2101 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
2101 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
2102 **opts):
2102 **opts):
2103 err = 0
2103 err = 0
2104 except error.RepoLookupError:
2104 except error.RepoLookupError:
2105 ui.status(_("skipping missing subrepository: %s\n")
2105 ui.status(_("skipping missing subrepository: %s\n")
2106 % os.path.join(prefix, subpath))
2106 % os.path.join(prefix, subpath))
2107
2107
2108 return err
2108 return err
2109
2109
2110 def commit(ui, repo, commitfunc, pats, opts):
2110 def commit(ui, repo, commitfunc, pats, opts):
2111 '''commit the specified files or all outstanding changes'''
2111 '''commit the specified files or all outstanding changes'''
2112 date = opts.get('date')
2112 date = opts.get('date')
2113 if date:
2113 if date:
2114 opts['date'] = util.parsedate(date)
2114 opts['date'] = util.parsedate(date)
2115 message = logmessage(ui, opts)
2115 message = logmessage(ui, opts)
2116
2116
2117 # extract addremove carefully -- this function can be called from a command
2117 # extract addremove carefully -- this function can be called from a command
2118 # that doesn't support addremove
2118 # that doesn't support addremove
2119 if opts.get('addremove'):
2119 if opts.get('addremove'):
2120 scmutil.addremove(repo, pats, opts)
2120 scmutil.addremove(repo, pats, opts)
2121
2121
2122 return commitfunc(ui, repo, message,
2122 return commitfunc(ui, repo, message,
2123 scmutil.match(repo[None], pats, opts), opts)
2123 scmutil.match(repo[None], pats, opts), opts)
2124
2124
2125 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2125 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2126 # amend will reuse the existing user if not specified, but the obsolete
2126 # amend will reuse the existing user if not specified, but the obsolete
2127 # marker creation requires that the current user's name is specified.
2127 # marker creation requires that the current user's name is specified.
2128 if obsolete._enabled:
2128 if obsolete._enabled:
2129 ui.username() # raise exception if username not set
2129 ui.username() # raise exception if username not set
2130
2130
2131 ui.note(_('amending changeset %s\n') % old)
2131 ui.note(_('amending changeset %s\n') % old)
2132 base = old.p1()
2132 base = old.p1()
2133
2133
2134 wlock = lock = newid = None
2134 wlock = lock = newid = None
2135 try:
2135 try:
2136 wlock = repo.wlock()
2136 wlock = repo.wlock()
2137 lock = repo.lock()
2137 lock = repo.lock()
2138 tr = repo.transaction('amend')
2138 tr = repo.transaction('amend')
2139 try:
2139 try:
2140 # See if we got a message from -m or -l, if not, open the editor
2140 # See if we got a message from -m or -l, if not, open the editor
2141 # with the message of the changeset to amend
2141 # with the message of the changeset to amend
2142 message = logmessage(ui, opts)
2142 message = logmessage(ui, opts)
2143 # ensure logfile does not conflict with later enforcement of the
2143 # ensure logfile does not conflict with later enforcement of the
2144 # message. potential logfile content has been processed by
2144 # message. potential logfile content has been processed by
2145 # `logmessage` anyway.
2145 # `logmessage` anyway.
2146 opts.pop('logfile')
2146 opts.pop('logfile')
2147 # First, do a regular commit to record all changes in the working
2147 # First, do a regular commit to record all changes in the working
2148 # directory (if there are any)
2148 # directory (if there are any)
2149 ui.callhooks = False
2149 ui.callhooks = False
2150 currentbookmark = repo._bookmarkcurrent
2150 currentbookmark = repo._bookmarkcurrent
2151 try:
2151 try:
2152 repo._bookmarkcurrent = None
2152 repo._bookmarkcurrent = None
2153 opts['message'] = 'temporary amend commit for %s' % old
2153 opts['message'] = 'temporary amend commit for %s' % old
2154 node = commit(ui, repo, commitfunc, pats, opts)
2154 node = commit(ui, repo, commitfunc, pats, opts)
2155 finally:
2155 finally:
2156 repo._bookmarkcurrent = currentbookmark
2156 repo._bookmarkcurrent = currentbookmark
2157 ui.callhooks = True
2157 ui.callhooks = True
2158 ctx = repo[node]
2158 ctx = repo[node]
2159
2159
2160 # Participating changesets:
2160 # Participating changesets:
2161 #
2161 #
2162 # node/ctx o - new (intermediate) commit that contains changes
2162 # node/ctx o - new (intermediate) commit that contains changes
2163 # | from working dir to go into amending commit
2163 # | from working dir to go into amending commit
2164 # | (or a workingctx if there were no changes)
2164 # | (or a workingctx if there were no changes)
2165 # |
2165 # |
2166 # old o - changeset to amend
2166 # old o - changeset to amend
2167 # |
2167 # |
2168 # base o - parent of amending changeset
2168 # base o - parent of amending changeset
2169
2169
2170 # Update extra dict from amended commit (e.g. to preserve graft
2170 # Update extra dict from amended commit (e.g. to preserve graft
2171 # source)
2171 # source)
2172 extra.update(old.extra())
2172 extra.update(old.extra())
2173
2173
2174 # Also update it from the intermediate commit or from the wctx
2174 # Also update it from the intermediate commit or from the wctx
2175 extra.update(ctx.extra())
2175 extra.update(ctx.extra())
2176
2176
2177 if len(old.parents()) > 1:
2177 if len(old.parents()) > 1:
2178 # ctx.files() isn't reliable for merges, so fall back to the
2178 # ctx.files() isn't reliable for merges, so fall back to the
2179 # slower repo.status() method
2179 # slower repo.status() method
2180 files = set([fn for st in repo.status(base, old)[:3]
2180 files = set([fn for st in repo.status(base, old)[:3]
2181 for fn in st])
2181 for fn in st])
2182 else:
2182 else:
2183 files = set(old.files())
2183 files = set(old.files())
2184
2184
2185 # Second, we use either the commit we just did, or if there were no
2185 # Second, we use either the commit we just did, or if there were no
2186 # changes the parent of the working directory as the version of the
2186 # changes the parent of the working directory as the version of the
2187 # files in the final amend commit
2187 # files in the final amend commit
2188 if node:
2188 if node:
2189 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2189 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2190
2190
2191 user = ctx.user()
2191 user = ctx.user()
2192 date = ctx.date()
2192 date = ctx.date()
2193 # Recompute copies (avoid recording a -> b -> a)
2193 # Recompute copies (avoid recording a -> b -> a)
2194 copied = copies.pathcopies(base, ctx)
2194 copied = copies.pathcopies(base, ctx)
2195
2195
2196 # Prune files which were reverted by the updates: if old
2196 # Prune files which were reverted by the updates: if old
2197 # introduced file X and our intermediate commit, node,
2197 # introduced file X and our intermediate commit, node,
2198 # renamed that file, then those two files are the same and
2198 # renamed that file, then those two files are the same and
2199 # we can discard X from our list of files. Likewise if X
2199 # we can discard X from our list of files. Likewise if X
2200 # was deleted, it's no longer relevant
2200 # was deleted, it's no longer relevant
2201 files.update(ctx.files())
2201 files.update(ctx.files())
2202
2202
2203 def samefile(f):
2203 def samefile(f):
2204 if f in ctx.manifest():
2204 if f in ctx.manifest():
2205 a = ctx.filectx(f)
2205 a = ctx.filectx(f)
2206 if f in base.manifest():
2206 if f in base.manifest():
2207 b = base.filectx(f)
2207 b = base.filectx(f)
2208 return (not a.cmp(b)
2208 return (not a.cmp(b)
2209 and a.flags() == b.flags())
2209 and a.flags() == b.flags())
2210 else:
2210 else:
2211 return False
2211 return False
2212 else:
2212 else:
2213 return f not in base.manifest()
2213 return f not in base.manifest()
2214 files = [f for f in files if not samefile(f)]
2214 files = [f for f in files if not samefile(f)]
2215
2215
2216 def filectxfn(repo, ctx_, path):
2216 def filectxfn(repo, ctx_, path):
2217 try:
2217 try:
2218 fctx = ctx[path]
2218 fctx = ctx[path]
2219 flags = fctx.flags()
2219 flags = fctx.flags()
2220 mctx = context.memfilectx(repo,
2220 mctx = context.memfilectx(repo,
2221 fctx.path(), fctx.data(),
2221 fctx.path(), fctx.data(),
2222 islink='l' in flags,
2222 islink='l' in flags,
2223 isexec='x' in flags,
2223 isexec='x' in flags,
2224 copied=copied.get(path))
2224 copied=copied.get(path))
2225 return mctx
2225 return mctx
2226 except KeyError:
2226 except KeyError:
2227 return None
2227 return None
2228 else:
2228 else:
2229 ui.note(_('copying changeset %s to %s\n') % (old, base))
2229 ui.note(_('copying changeset %s to %s\n') % (old, base))
2230
2230
2231 # Use version of files as in the old cset
2231 # Use version of files as in the old cset
2232 def filectxfn(repo, ctx_, path):
2232 def filectxfn(repo, ctx_, path):
2233 try:
2233 try:
2234 return old.filectx(path)
2234 return old.filectx(path)
2235 except KeyError:
2235 except KeyError:
2236 return None
2236 return None
2237
2237
2238 user = opts.get('user') or old.user()
2238 user = opts.get('user') or old.user()
2239 date = opts.get('date') or old.date()
2239 date = opts.get('date') or old.date()
2240 editform = mergeeditform(old, 'commit.amend')
2240 editform = mergeeditform(old, 'commit.amend')
2241 editor = getcommiteditor(editform=editform, **opts)
2241 editor = getcommiteditor(editform=editform, **opts)
2242 if not message:
2242 if not message:
2243 editor = getcommiteditor(edit=True, editform=editform)
2243 editor = getcommiteditor(edit=True, editform=editform)
2244 message = old.description()
2244 message = old.description()
2245
2245
2246 pureextra = extra.copy()
2246 pureextra = extra.copy()
2247 extra['amend_source'] = old.hex()
2247 extra['amend_source'] = old.hex()
2248
2248
2249 new = context.memctx(repo,
2249 new = context.memctx(repo,
2250 parents=[base.node(), old.p2().node()],
2250 parents=[base.node(), old.p2().node()],
2251 text=message,
2251 text=message,
2252 files=files,
2252 files=files,
2253 filectxfn=filectxfn,
2253 filectxfn=filectxfn,
2254 user=user,
2254 user=user,
2255 date=date,
2255 date=date,
2256 extra=extra,
2256 extra=extra,
2257 editor=editor)
2257 editor=editor)
2258
2258
2259 newdesc = changelog.stripdesc(new.description())
2259 newdesc = changelog.stripdesc(new.description())
2260 if ((not node)
2260 if ((not node)
2261 and newdesc == old.description()
2261 and newdesc == old.description()
2262 and user == old.user()
2262 and user == old.user()
2263 and date == old.date()
2263 and date == old.date()
2264 and pureextra == old.extra()):
2264 and pureextra == old.extra()):
2265 # nothing changed. continuing here would create a new node
2265 # nothing changed. continuing here would create a new node
2266 # anyway because of the amend_source noise.
2266 # anyway because of the amend_source noise.
2267 #
2267 #
2268 # This not what we expect from amend.
2268 # This not what we expect from amend.
2269 return old.node()
2269 return old.node()
2270
2270
2271 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2271 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2272 try:
2272 try:
2273 if opts.get('secret'):
2273 if opts.get('secret'):
2274 commitphase = 'secret'
2274 commitphase = 'secret'
2275 else:
2275 else:
2276 commitphase = old.phase()
2276 commitphase = old.phase()
2277 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2277 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2278 newid = repo.commitctx(new)
2278 newid = repo.commitctx(new)
2279 finally:
2279 finally:
2280 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2280 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2281 if newid != old.node():
2281 if newid != old.node():
2282 # Reroute the working copy parent to the new changeset
2282 # Reroute the working copy parent to the new changeset
2283 repo.setparents(newid, nullid)
2283 repo.setparents(newid, nullid)
2284
2284
2285 # Move bookmarks from old parent to amend commit
2285 # Move bookmarks from old parent to amend commit
2286 bms = repo.nodebookmarks(old.node())
2286 bms = repo.nodebookmarks(old.node())
2287 if bms:
2287 if bms:
2288 marks = repo._bookmarks
2288 marks = repo._bookmarks
2289 for bm in bms:
2289 for bm in bms:
2290 marks[bm] = newid
2290 marks[bm] = newid
2291 marks.write()
2291 marks.write()
2292 #commit the whole amend process
2292 #commit the whole amend process
2293 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2293 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2294 if createmarkers and newid != old.node():
2294 if createmarkers and newid != old.node():
2295 # mark the new changeset as successor of the rewritten one
2295 # mark the new changeset as successor of the rewritten one
2296 new = repo[newid]
2296 new = repo[newid]
2297 obs = [(old, (new,))]
2297 obs = [(old, (new,))]
2298 if node:
2298 if node:
2299 obs.append((ctx, ()))
2299 obs.append((ctx, ()))
2300
2300
2301 obsolete.createmarkers(repo, obs)
2301 obsolete.createmarkers(repo, obs)
2302 tr.close()
2302 tr.close()
2303 finally:
2303 finally:
2304 tr.release()
2304 tr.release()
2305 if not createmarkers and newid != old.node():
2305 if not createmarkers and newid != old.node():
2306 # Strip the intermediate commit (if there was one) and the amended
2306 # Strip the intermediate commit (if there was one) and the amended
2307 # commit
2307 # commit
2308 if node:
2308 if node:
2309 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2309 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2310 ui.note(_('stripping amended changeset %s\n') % old)
2310 ui.note(_('stripping amended changeset %s\n') % old)
2311 repair.strip(ui, repo, old.node(), topic='amend-backup')
2311 repair.strip(ui, repo, old.node(), topic='amend-backup')
2312 finally:
2312 finally:
2313 if newid is None:
2313 if newid is None:
2314 repo.dirstate.invalidate()
2314 repo.dirstate.invalidate()
2315 lockmod.release(lock, wlock)
2315 lockmod.release(lock, wlock)
2316 return newid
2316 return newid
2317
2317
2318 def commiteditor(repo, ctx, subs, editform=''):
2318 def commiteditor(repo, ctx, subs, editform=''):
2319 if ctx.description():
2319 if ctx.description():
2320 return ctx.description()
2320 return ctx.description()
2321 return commitforceeditor(repo, ctx, subs, editform=editform)
2321 return commitforceeditor(repo, ctx, subs, editform=editform)
2322
2322
2323 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2323 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2324 editform=''):
2324 editform=''):
2325 if not extramsg:
2325 if not extramsg:
2326 extramsg = _("Leave message empty to abort commit.")
2326 extramsg = _("Leave message empty to abort commit.")
2327
2327
2328 forms = [e for e in editform.split('.') if e]
2328 forms = [e for e in editform.split('.') if e]
2329 forms.insert(0, 'changeset')
2329 forms.insert(0, 'changeset')
2330 while forms:
2330 while forms:
2331 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2331 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2332 if tmpl:
2332 if tmpl:
2333 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2333 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2334 break
2334 break
2335 forms.pop()
2335 forms.pop()
2336 else:
2336 else:
2337 committext = buildcommittext(repo, ctx, subs, extramsg)
2337 committext = buildcommittext(repo, ctx, subs, extramsg)
2338
2338
2339 # run editor in the repository root
2339 # run editor in the repository root
2340 olddir = os.getcwd()
2340 olddir = os.getcwd()
2341 os.chdir(repo.root)
2341 os.chdir(repo.root)
2342 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2342 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2343 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2343 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2344 os.chdir(olddir)
2344 os.chdir(olddir)
2345
2345
2346 if finishdesc:
2346 if finishdesc:
2347 text = finishdesc(text)
2347 text = finishdesc(text)
2348 if not text.strip():
2348 if not text.strip():
2349 raise util.Abort(_("empty commit message"))
2349 raise util.Abort(_("empty commit message"))
2350
2350
2351 return text
2351 return text
2352
2352
2353 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2353 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2354 ui = repo.ui
2354 ui = repo.ui
2355 tmpl, mapfile = gettemplate(ui, tmpl, None)
2355 tmpl, mapfile = gettemplate(ui, tmpl, None)
2356
2356
2357 try:
2357 try:
2358 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2358 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2359 except SyntaxError, inst:
2359 except SyntaxError, inst:
2360 raise util.Abort(inst.args[0])
2360 raise util.Abort(inst.args[0])
2361
2361
2362 for k, v in repo.ui.configitems('committemplate'):
2362 for k, v in repo.ui.configitems('committemplate'):
2363 if k != 'changeset':
2363 if k != 'changeset':
2364 t.t.cache[k] = v
2364 t.t.cache[k] = v
2365
2365
2366 if not extramsg:
2366 if not extramsg:
2367 extramsg = '' # ensure that extramsg is string
2367 extramsg = '' # ensure that extramsg is string
2368
2368
2369 ui.pushbuffer()
2369 ui.pushbuffer()
2370 t.show(ctx, extramsg=extramsg)
2370 t.show(ctx, extramsg=extramsg)
2371 return ui.popbuffer()
2371 return ui.popbuffer()
2372
2372
2373 def buildcommittext(repo, ctx, subs, extramsg):
2373 def buildcommittext(repo, ctx, subs, extramsg):
2374 edittext = []
2374 edittext = []
2375 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2375 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2376 if ctx.description():
2376 if ctx.description():
2377 edittext.append(ctx.description())
2377 edittext.append(ctx.description())
2378 edittext.append("")
2378 edittext.append("")
2379 edittext.append("") # Empty line between message and comments.
2379 edittext.append("") # Empty line between message and comments.
2380 edittext.append(_("HG: Enter commit message."
2380 edittext.append(_("HG: Enter commit message."
2381 " Lines beginning with 'HG:' are removed."))
2381 " Lines beginning with 'HG:' are removed."))
2382 edittext.append("HG: %s" % extramsg)
2382 edittext.append("HG: %s" % extramsg)
2383 edittext.append("HG: --")
2383 edittext.append("HG: --")
2384 edittext.append(_("HG: user: %s") % ctx.user())
2384 edittext.append(_("HG: user: %s") % ctx.user())
2385 if ctx.p2():
2385 if ctx.p2():
2386 edittext.append(_("HG: branch merge"))
2386 edittext.append(_("HG: branch merge"))
2387 if ctx.branch():
2387 if ctx.branch():
2388 edittext.append(_("HG: branch '%s'") % ctx.branch())
2388 edittext.append(_("HG: branch '%s'") % ctx.branch())
2389 if bookmarks.iscurrent(repo):
2389 if bookmarks.iscurrent(repo):
2390 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2390 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2391 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2391 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2392 edittext.extend([_("HG: added %s") % f for f in added])
2392 edittext.extend([_("HG: added %s") % f for f in added])
2393 edittext.extend([_("HG: changed %s") % f for f in modified])
2393 edittext.extend([_("HG: changed %s") % f for f in modified])
2394 edittext.extend([_("HG: removed %s") % f for f in removed])
2394 edittext.extend([_("HG: removed %s") % f for f in removed])
2395 if not added and not modified and not removed:
2395 if not added and not modified and not removed:
2396 edittext.append(_("HG: no files changed"))
2396 edittext.append(_("HG: no files changed"))
2397 edittext.append("")
2397 edittext.append("")
2398
2398
2399 return "\n".join(edittext)
2399 return "\n".join(edittext)
2400
2400
2401 def commitstatus(repo, node, branch, bheads=None, opts={}):
2401 def commitstatus(repo, node, branch, bheads=None, opts={}):
2402 ctx = repo[node]
2402 ctx = repo[node]
2403 parents = ctx.parents()
2403 parents = ctx.parents()
2404
2404
2405 if (not opts.get('amend') and bheads and node not in bheads and not
2405 if (not opts.get('amend') and bheads and node not in bheads and not
2406 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2406 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2407 repo.ui.status(_('created new head\n'))
2407 repo.ui.status(_('created new head\n'))
2408 # The message is not printed for initial roots. For the other
2408 # The message is not printed for initial roots. For the other
2409 # changesets, it is printed in the following situations:
2409 # changesets, it is printed in the following situations:
2410 #
2410 #
2411 # Par column: for the 2 parents with ...
2411 # Par column: for the 2 parents with ...
2412 # N: null or no parent
2412 # N: null or no parent
2413 # B: parent is on another named branch
2413 # B: parent is on another named branch
2414 # C: parent is a regular non head changeset
2414 # C: parent is a regular non head changeset
2415 # H: parent was a branch head of the current branch
2415 # H: parent was a branch head of the current branch
2416 # Msg column: whether we print "created new head" message
2416 # Msg column: whether we print "created new head" message
2417 # In the following, it is assumed that there already exists some
2417 # In the following, it is assumed that there already exists some
2418 # initial branch heads of the current branch, otherwise nothing is
2418 # initial branch heads of the current branch, otherwise nothing is
2419 # printed anyway.
2419 # printed anyway.
2420 #
2420 #
2421 # Par Msg Comment
2421 # Par Msg Comment
2422 # N N y additional topo root
2422 # N N y additional topo root
2423 #
2423 #
2424 # B N y additional branch root
2424 # B N y additional branch root
2425 # C N y additional topo head
2425 # C N y additional topo head
2426 # H N n usual case
2426 # H N n usual case
2427 #
2427 #
2428 # B B y weird additional branch root
2428 # B B y weird additional branch root
2429 # C B y branch merge
2429 # C B y branch merge
2430 # H B n merge with named branch
2430 # H B n merge with named branch
2431 #
2431 #
2432 # C C y additional head from merge
2432 # C C y additional head from merge
2433 # C H n merge with a head
2433 # C H n merge with a head
2434 #
2434 #
2435 # H H n head merge: head count decreases
2435 # H H n head merge: head count decreases
2436
2436
2437 if not opts.get('close_branch'):
2437 if not opts.get('close_branch'):
2438 for r in parents:
2438 for r in parents:
2439 if r.closesbranch() and r.branch() == branch:
2439 if r.closesbranch() and r.branch() == branch:
2440 repo.ui.status(_('reopening closed branch head %d\n') % r)
2440 repo.ui.status(_('reopening closed branch head %d\n') % r)
2441
2441
2442 if repo.ui.debugflag:
2442 if repo.ui.debugflag:
2443 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2443 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2444 elif repo.ui.verbose:
2444 elif repo.ui.verbose:
2445 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2445 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2446
2446
2447 def revert(ui, repo, ctx, parents, *pats, **opts):
2447 def revert(ui, repo, ctx, parents, *pats, **opts):
2448 parent, p2 = parents
2448 parent, p2 = parents
2449 node = ctx.node()
2449 node = ctx.node()
2450
2450
2451 mf = ctx.manifest()
2451 mf = ctx.manifest()
2452 if node == p2:
2452 if node == p2:
2453 parent = p2
2453 parent = p2
2454 if node == parent:
2454 if node == parent:
2455 pmf = mf
2455 pmf = mf
2456 else:
2456 else:
2457 pmf = None
2457 pmf = None
2458
2458
2459 # need all matching names in dirstate and manifest of target rev,
2459 # need all matching names in dirstate and manifest of target rev,
2460 # so have to walk both. do not print errors if files exist in one
2460 # so have to walk both. do not print errors if files exist in one
2461 # but not other.
2461 # but not other.
2462
2462
2463 # `names` is a mapping for all elements in working copy and target revision
2463 # `names` is a mapping for all elements in working copy and target revision
2464 # The mapping is in the form:
2464 # The mapping is in the form:
2465 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2465 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2466 names = {}
2466 names = {}
2467
2467
2468 wlock = repo.wlock()
2468 wlock = repo.wlock()
2469 try:
2469 try:
2470 ## filling of the `names` mapping
2470 ## filling of the `names` mapping
2471 # walk dirstate to fill `names`
2471 # walk dirstate to fill `names`
2472
2472
2473 m = scmutil.match(repo[None], pats, opts)
2473 m = scmutil.match(repo[None], pats, opts)
2474 if not m.always() or node != parent:
2474 if not m.always() or node != parent:
2475 m.bad = lambda x, y: False
2475 m.bad = lambda x, y: False
2476 for abs in repo.walk(m):
2476 for abs in repo.walk(m):
2477 names[abs] = m.rel(abs), m.exact(abs)
2477 names[abs] = m.rel(abs), m.exact(abs)
2478
2478
2479 # walk target manifest to fill `names`
2479 # walk target manifest to fill `names`
2480
2480
2481 def badfn(path, msg):
2481 def badfn(path, msg):
2482 if path in names:
2482 if path in names:
2483 return
2483 return
2484 if path in ctx.substate:
2484 if path in ctx.substate:
2485 return
2485 return
2486 path_ = path + '/'
2486 path_ = path + '/'
2487 for f in names:
2487 for f in names:
2488 if f.startswith(path_):
2488 if f.startswith(path_):
2489 return
2489 return
2490 ui.warn("%s: %s\n" % (m.rel(path), msg))
2490 ui.warn("%s: %s\n" % (m.rel(path), msg))
2491
2491
2492 m = scmutil.match(ctx, pats, opts)
2492 m = scmutil.match(ctx, pats, opts)
2493 m.bad = badfn
2493 m.bad = badfn
2494 for abs in ctx.walk(m):
2494 for abs in ctx.walk(m):
2495 if abs not in names:
2495 if abs not in names:
2496 names[abs] = m.rel(abs), m.exact(abs)
2496 names[abs] = m.rel(abs), m.exact(abs)
2497
2497
2498 # Find status of all file in `names`.
2498 # Find status of all file in `names`.
2499 m = scmutil.matchfiles(repo, names)
2499 m = scmutil.matchfiles(repo, names)
2500
2500
2501 changes = repo.status(node1=node, match=m,
2501 changes = repo.status(node1=node, match=m,
2502 unknown=True, ignored=True, clean=True)
2502 unknown=True, ignored=True, clean=True)
2503 else:
2503 else:
2504 changes = repo.status(match=m)
2504 changes = repo.status(match=m)
2505 for kind in changes:
2505 for kind in changes:
2506 for abs in kind:
2506 for abs in kind:
2507 names[abs] = m.rel(abs), m.exact(abs)
2507 names[abs] = m.rel(abs), m.exact(abs)
2508
2508
2509 m = scmutil.matchfiles(repo, names)
2509 m = scmutil.matchfiles(repo, names)
2510
2510
2511 modified = set(changes[0])
2511 modified = set(changes[0])
2512 added = set(changes[1])
2512 added = set(changes[1])
2513 removed = set(changes[2])
2513 removed = set(changes[2])
2514 _deleted = set(changes[3])
2514 _deleted = set(changes[3])
2515 unknown = set(changes[4])
2515 unknown = set(changes[4])
2516 unknown.update(changes[5])
2516 unknown.update(changes[5])
2517 clean = set(changes[6])
2517 clean = set(changes[6])
2518 modadded = set()
2518 modadded = set()
2519
2519
2520 # split between files known in target manifest and the others
2520 # split between files known in target manifest and the others
2521 smf = set(mf)
2521 smf = set(mf)
2522
2522
2523 # determine the exact nature of the deleted changesets
2523 # determine the exact nature of the deleted changesets
2524 deladded = _deleted - smf
2524 deladded = _deleted - smf
2525 deleted = _deleted - deladded
2525 deleted = _deleted - deladded
2526
2526
2527 # We need to account for the state of file in the dirstate
2527 # We need to account for the state of file in the dirstate
2528 #
2528 #
2529 # Even, when we revert agains something else than parent. this will
2529 # Even, when we revert agains something else than parent. this will
2530 # slightly alter the behavior of revert (doing back up or not, delete
2530 # slightly alter the behavior of revert (doing back up or not, delete
2531 # or just forget etc)
2531 # or just forget etc)
2532 if parent == node:
2532 if parent == node:
2533 dsmodified = modified
2533 dsmodified = modified
2534 dsadded = added
2534 dsadded = added
2535 dsremoved = removed
2535 dsremoved = removed
2536 # store all local modifications, useful later for rename detection
2536 # store all local modifications, useful later for rename detection
2537 localchanges = dsmodified | dsadded
2537 localchanges = dsmodified | dsadded
2538 modified, added, removed = set(), set(), set()
2538 modified, added, removed = set(), set(), set()
2539 else:
2539 else:
2540 changes = repo.status(node1=parent, match=m)
2540 changes = repo.status(node1=parent, match=m)
2541 dsmodified = set(changes[0])
2541 dsmodified = set(changes[0])
2542 dsadded = set(changes[1])
2542 dsadded = set(changes[1])
2543 dsremoved = set(changes[2])
2543 dsremoved = set(changes[2])
2544 # store all local modifications, useful later for rename detection
2544 # store all local modifications, useful later for rename detection
2545 localchanges = dsmodified | dsadded
2545 localchanges = dsmodified | dsadded
2546
2546
2547 # only take into account for removes between wc and target
2547 # only take into account for removes between wc and target
2548 clean |= dsremoved - removed
2548 clean |= dsremoved - removed
2549 dsremoved &= removed
2549 dsremoved &= removed
2550 # distinct between dirstate remove and other
2550 # distinct between dirstate remove and other
2551 removed -= dsremoved
2551 removed -= dsremoved
2552
2552
2553 modadded = added & dsmodified
2553 modadded = added & dsmodified
2554 added -= modadded
2554 added -= modadded
2555
2555
2556 # tell newly modified apart.
2556 # tell newly modified apart.
2557 dsmodified &= modified
2557 dsmodified &= modified
2558 dsmodified |= modified & dsadded # dirstate added may needs backup
2558 dsmodified |= modified & dsadded # dirstate added may needs backup
2559 modified -= dsmodified
2559 modified -= dsmodified
2560
2560
2561 # We need to wait for some post-processing to update this set
2561 # We need to wait for some post-processing to update this set
2562 # before making the distinction. The dirstate will be used for
2562 # before making the distinction. The dirstate will be used for
2563 # that purpose.
2563 # that purpose.
2564 dsadded = added
2564 dsadded = added
2565
2565
2566 # in case of merge, files that are actually added can be reported as
2566 # in case of merge, files that are actually added can be reported as
2567 # modified, we need to post process the result
2567 # modified, we need to post process the result
2568 if p2 != nullid:
2568 if p2 != nullid:
2569 if pmf is None:
2569 if pmf is None:
2570 # only need parent manifest in the merge case,
2570 # only need parent manifest in the merge case,
2571 # so do not read by default
2571 # so do not read by default
2572 pmf = repo[parent].manifest()
2572 pmf = repo[parent].manifest()
2573 mergeadd = dsmodified - set(pmf)
2573 mergeadd = dsmodified - set(pmf)
2574 dsadded |= mergeadd
2574 dsadded |= mergeadd
2575 dsmodified -= mergeadd
2575 dsmodified -= mergeadd
2576
2576
2577 # if f is a rename, update `names` to also revert the source
2577 # if f is a rename, update `names` to also revert the source
2578 cwd = repo.getcwd()
2578 cwd = repo.getcwd()
2579 for f in localchanges:
2579 for f in localchanges:
2580 src = repo.dirstate.copied(f)
2580 src = repo.dirstate.copied(f)
2581 # XXX should we check for rename down to target node?
2581 # XXX should we check for rename down to target node?
2582 if src and src not in names and repo.dirstate[src] == 'r':
2582 if src and src not in names and repo.dirstate[src] == 'r':
2583 dsremoved.add(src)
2583 dsremoved.add(src)
2584 names[src] = (repo.pathto(src, cwd), True)
2584 names[src] = (repo.pathto(src, cwd), True)
2585
2585
2586 # distinguish between file to forget and the other
2586 # distinguish between file to forget and the other
2587 added = set()
2587 added = set()
2588 for abs in dsadded:
2588 for abs in dsadded:
2589 if repo.dirstate[abs] != 'a':
2589 if repo.dirstate[abs] != 'a':
2590 added.add(abs)
2590 added.add(abs)
2591 dsadded -= added
2591 dsadded -= added
2592
2592
2593 for abs in deladded:
2593 for abs in deladded:
2594 if repo.dirstate[abs] == 'a':
2594 if repo.dirstate[abs] == 'a':
2595 dsadded.add(abs)
2595 dsadded.add(abs)
2596 deladded -= dsadded
2596 deladded -= dsadded
2597
2597
2598 # For files marked as removed, we check if an unknown file is present at
2598 # For files marked as removed, we check if an unknown file is present at
2599 # the same path. If a such file exists it may need to be backed up.
2599 # the same path. If a such file exists it may need to be backed up.
2600 # Making the distinction at this stage helps have simpler backup
2600 # Making the distinction at this stage helps have simpler backup
2601 # logic.
2601 # logic.
2602 removunk = set()
2602 removunk = set()
2603 for abs in removed:
2603 for abs in removed:
2604 target = repo.wjoin(abs)
2604 target = repo.wjoin(abs)
2605 if os.path.lexists(target):
2605 if os.path.lexists(target):
2606 removunk.add(abs)
2606 removunk.add(abs)
2607 removed -= removunk
2607 removed -= removunk
2608
2608
2609 dsremovunk = set()
2609 dsremovunk = set()
2610 for abs in dsremoved:
2610 for abs in dsremoved:
2611 target = repo.wjoin(abs)
2611 target = repo.wjoin(abs)
2612 if os.path.lexists(target):
2612 if os.path.lexists(target):
2613 dsremovunk.add(abs)
2613 dsremovunk.add(abs)
2614 dsremoved -= dsremovunk
2614 dsremoved -= dsremovunk
2615
2615
2616 # action to be actually performed by revert
2616 # action to be actually performed by revert
2617 # (<list of file>, message>) tuple
2617 # (<list of file>, message>) tuple
2618 actions = {'revert': ([], _('reverting %s\n')),
2618 actions = {'revert': ([], _('reverting %s\n')),
2619 'add': ([], _('adding %s\n')),
2619 'add': ([], _('adding %s\n')),
2620 'remove': ([], _('removing %s\n')),
2620 'remove': ([], _('removing %s\n')),
2621 'drop': ([], _('removing %s\n')),
2621 'drop': ([], _('removing %s\n')),
2622 'forget': ([], _('forgetting %s\n')),
2622 'forget': ([], _('forgetting %s\n')),
2623 'undelete': ([], _('undeleting %s\n')),
2623 'undelete': ([], _('undeleting %s\n')),
2624 'noop': (None, _('no changes needed to %s\n')),
2624 'noop': (None, _('no changes needed to %s\n')),
2625 'unknown': (None, _('file not managed: %s\n')),
2625 'unknown': (None, _('file not managed: %s\n')),
2626 }
2626 }
2627
2627
2628 # "constant" that convey the backup strategy.
2628 # "constant" that convey the backup strategy.
2629 # All set to `discard` if `no-backup` is set do avoid checking
2629 # All set to `discard` if `no-backup` is set do avoid checking
2630 # no_backup lower in the code.
2630 # no_backup lower in the code.
2631 # These values are ordered for comparison purposes
2631 # These values are ordered for comparison purposes
2632 backup = 2 # unconditionally do backup
2632 backup = 2 # unconditionally do backup
2633 check = 1 # check if the existing file differs from target
2633 check = 1 # check if the existing file differs from target
2634 discard = 0 # never do backup
2634 discard = 0 # never do backup
2635 if opts.get('no_backup'):
2635 if opts.get('no_backup'):
2636 backup = check = discard
2636 backup = check = discard
2637
2637
2638 backupanddel = actions['remove']
2638 backupanddel = actions['remove']
2639 if not opts.get('no_backup'):
2639 if not opts.get('no_backup'):
2640 backupanddel = actions['drop']
2640 backupanddel = actions['drop']
2641
2641
2642 disptable = (
2642 disptable = (
2643 # dispatch table:
2643 # dispatch table:
2644 # file state
2644 # file state
2645 # action
2645 # action
2646 # make backup
2646 # make backup
2647
2647
2648 ## Sets that results that will change file on disk
2648 ## Sets that results that will change file on disk
2649 # Modified compared to target, no local change
2649 # Modified compared to target, no local change
2650 (modified, actions['revert'], discard),
2650 (modified, actions['revert'], discard),
2651 # Modified compared to target, but local file is deleted
2651 # Modified compared to target, but local file is deleted
2652 (deleted, actions['revert'], discard),
2652 (deleted, actions['revert'], discard),
2653 # Modified compared to target, local change
2653 # Modified compared to target, local change
2654 (dsmodified, actions['revert'], backup),
2654 (dsmodified, actions['revert'], backup),
2655 # Added since target
2655 # Added since target
2656 (added, actions['remove'], discard),
2656 (added, actions['remove'], discard),
2657 # Added in working directory
2657 # Added in working directory
2658 (dsadded, actions['forget'], discard),
2658 (dsadded, actions['forget'], discard),
2659 # Added since target, have local modification
2659 # Added since target, have local modification
2660 (modadded, backupanddel, backup),
2660 (modadded, backupanddel, backup),
2661 # Added since target but file is missing in working directory
2661 # Added since target but file is missing in working directory
2662 (deladded, actions['drop'], discard),
2662 (deladded, actions['drop'], discard),
2663 # Removed since target, before working copy parent
2663 # Removed since target, before working copy parent
2664 (removed, actions['add'], discard),
2664 (removed, actions['add'], discard),
2665 # Same as `removed` but an unknown file exists at the same path
2665 # Same as `removed` but an unknown file exists at the same path
2666 (removunk, actions['add'], check),
2666 (removunk, actions['add'], check),
2667 # Removed since targe, marked as such in working copy parent
2667 # Removed since targe, marked as such in working copy parent
2668 (dsremoved, actions['undelete'], discard),
2668 (dsremoved, actions['undelete'], discard),
2669 # Same as `dsremoved` but an unknown file exists at the same path
2669 # Same as `dsremoved` but an unknown file exists at the same path
2670 (dsremovunk, actions['undelete'], check),
2670 (dsremovunk, actions['undelete'], check),
2671 ## the following sets does not result in any file changes
2671 ## the following sets does not result in any file changes
2672 # File with no modification
2672 # File with no modification
2673 (clean, actions['noop'], discard),
2673 (clean, actions['noop'], discard),
2674 # Existing file, not tracked anywhere
2674 # Existing file, not tracked anywhere
2675 (unknown, actions['unknown'], discard),
2675 (unknown, actions['unknown'], discard),
2676 )
2676 )
2677
2677
2678 needdata = ('revert', 'add', 'undelete')
2678 needdata = ('revert', 'add', 'undelete')
2679 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
2679 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
2680
2680
2681 wctx = repo[None]
2681 wctx = repo[None]
2682 for abs, (rel, exact) in sorted(names.items()):
2682 for abs, (rel, exact) in sorted(names.items()):
2683 # target file to be touch on disk (relative to cwd)
2683 # target file to be touch on disk (relative to cwd)
2684 target = repo.wjoin(abs)
2684 target = repo.wjoin(abs)
2685 # search the entry in the dispatch table.
2685 # search the entry in the dispatch table.
2686 # if the file is in any of these sets, it was touched in the working
2686 # if the file is in any of these sets, it was touched in the working
2687 # directory parent and we are sure it needs to be reverted.
2687 # directory parent and we are sure it needs to be reverted.
2688 for table, (xlist, msg), dobackup in disptable:
2688 for table, (xlist, msg), dobackup in disptable:
2689 if abs not in table:
2689 if abs not in table:
2690 continue
2690 continue
2691 if xlist is not None:
2691 if xlist is not None:
2692 xlist.append(abs)
2692 xlist.append(abs)
2693 if dobackup and (backup <= dobackup
2693 if dobackup and (backup <= dobackup
2694 or wctx[abs].cmp(ctx[abs])):
2694 or wctx[abs].cmp(ctx[abs])):
2695 bakname = "%s.orig" % rel
2695 bakname = "%s.orig" % rel
2696 ui.note(_('saving current version of %s as %s\n') %
2696 ui.note(_('saving current version of %s as %s\n') %
2697 (rel, bakname))
2697 (rel, bakname))
2698 if not opts.get('dry_run'):
2698 if not opts.get('dry_run'):
2699 util.rename(target, bakname)
2699 util.rename(target, bakname)
2700 if ui.verbose or not exact:
2700 if ui.verbose or not exact:
2701 if not isinstance(msg, basestring):
2701 if not isinstance(msg, basestring):
2702 msg = msg(abs)
2702 msg = msg(abs)
2703 ui.status(msg % rel)
2703 ui.status(msg % rel)
2704 elif exact:
2704 elif exact:
2705 ui.warn(msg % rel)
2705 ui.warn(msg % rel)
2706 break
2706 break
2707
2707
2708
2708
2709 if not opts.get('dry_run'):
2709 if not opts.get('dry_run'):
2710 _performrevert(repo, parents, ctx, actions)
2710 _performrevert(repo, parents, ctx, actions)
2711
2711
2712 # get the list of subrepos that must be reverted
2712 # get the list of subrepos that must be reverted
2713 subrepomatch = scmutil.match(ctx, pats, opts)
2713 subrepomatch = scmutil.match(ctx, pats, opts)
2714 targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
2714 targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
2715
2715
2716 if targetsubs:
2716 if targetsubs:
2717 # Revert the subrepos on the revert list
2717 # Revert the subrepos on the revert list
2718 for sub in targetsubs:
2718 for sub in targetsubs:
2719 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2719 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2720 finally:
2720 finally:
2721 wlock.release()
2721 wlock.release()
2722
2722
2723 def _revertprefetch(repo, ctx, *files):
2723 def _revertprefetch(repo, ctx, *files):
2724 """Let extension changing the storage layer prefetch content"""
2724 """Let extension changing the storage layer prefetch content"""
2725 pass
2725 pass
2726
2726
2727 def _performrevert(repo, parents, ctx, actions):
2727 def _performrevert(repo, parents, ctx, actions):
2728 """function that actually perform all the actions computed for revert
2728 """function that actually perform all the actions computed for revert
2729
2729
2730 This is an independent function to let extension to plug in and react to
2730 This is an independent function to let extension to plug in and react to
2731 the imminent revert.
2731 the imminent revert.
2732
2732
2733 Make sure you have the working directory locked when calling this function.
2733 Make sure you have the working directory locked when calling this function.
2734 """
2734 """
2735 parent, p2 = parents
2735 parent, p2 = parents
2736 node = ctx.node()
2736 node = ctx.node()
2737 def checkout(f):
2737 def checkout(f):
2738 fc = ctx[f]
2738 fc = ctx[f]
2739 repo.wwrite(f, fc.data(), fc.flags())
2739 repo.wwrite(f, fc.data(), fc.flags())
2740
2740
2741 audit_path = pathutil.pathauditor(repo.root)
2741 audit_path = pathutil.pathauditor(repo.root)
2742 for f in actions['forget'][0]:
2742 for f in actions['forget'][0]:
2743 repo.dirstate.drop(f)
2743 repo.dirstate.drop(f)
2744 for f in actions['remove'][0]:
2744 for f in actions['remove'][0]:
2745 audit_path(f)
2745 audit_path(f)
2746 util.unlinkpath(repo.wjoin(f))
2746 util.unlinkpath(repo.wjoin(f))
2747 repo.dirstate.remove(f)
2747 repo.dirstate.remove(f)
2748 for f in actions['drop'][0]:
2748 for f in actions['drop'][0]:
2749 audit_path(f)
2749 audit_path(f)
2750 repo.dirstate.remove(f)
2750 repo.dirstate.remove(f)
2751
2751
2752 normal = None
2752 normal = None
2753 if node == parent:
2753 if node == parent:
2754 # We're reverting to our parent. If possible, we'd like status
2754 # We're reverting to our parent. If possible, we'd like status
2755 # to report the file as clean. We have to use normallookup for
2755 # to report the file as clean. We have to use normallookup for
2756 # merges to avoid losing information about merged/dirty files.
2756 # merges to avoid losing information about merged/dirty files.
2757 if p2 != nullid:
2757 if p2 != nullid:
2758 normal = repo.dirstate.normallookup
2758 normal = repo.dirstate.normallookup
2759 else:
2759 else:
2760 normal = repo.dirstate.normal
2760 normal = repo.dirstate.normal
2761 for f in actions['revert'][0]:
2761 for f in actions['revert'][0]:
2762 checkout(f)
2762 checkout(f)
2763 if normal:
2763 if normal:
2764 normal(f)
2764 normal(f)
2765
2765
2766 for f in actions['add'][0]:
2766 for f in actions['add'][0]:
2767 checkout(f)
2767 checkout(f)
2768 repo.dirstate.add(f)
2768 repo.dirstate.add(f)
2769
2769
2770 normal = repo.dirstate.normallookup
2770 normal = repo.dirstate.normallookup
2771 if node == parent and p2 == nullid:
2771 if node == parent and p2 == nullid:
2772 normal = repo.dirstate.normal
2772 normal = repo.dirstate.normal
2773 for f in actions['undelete'][0]:
2773 for f in actions['undelete'][0]:
2774 checkout(f)
2774 checkout(f)
2775 normal(f)
2775 normal(f)
2776
2776
2777 copied = copies.pathcopies(repo[parent], ctx)
2777 copied = copies.pathcopies(repo[parent], ctx)
2778
2778
2779 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2779 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2780 if f in copied:
2780 if f in copied:
2781 repo.dirstate.copy(copied[f], f)
2781 repo.dirstate.copy(copied[f], f)
2782
2782
2783 def command(table):
2783 def command(table):
2784 """Returns a function object to be used as a decorator for making commands.
2784 """Returns a function object to be used as a decorator for making commands.
2785
2785
2786 This function receives a command table as its argument. The table should
2786 This function receives a command table as its argument. The table should
2787 be a dict.
2787 be a dict.
2788
2788
2789 The returned function can be used as a decorator for adding commands
2789 The returned function can be used as a decorator for adding commands
2790 to that command table. This function accepts multiple arguments to define
2790 to that command table. This function accepts multiple arguments to define
2791 a command.
2791 a command.
2792
2792
2793 The first argument is the command name.
2793 The first argument is the command name.
2794
2794
2795 The options argument is an iterable of tuples defining command arguments.
2795 The options argument is an iterable of tuples defining command arguments.
2796 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2796 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2797
2797
2798 The synopsis argument defines a short, one line summary of how to use the
2798 The synopsis argument defines a short, one line summary of how to use the
2799 command. This shows up in the help output.
2799 command. This shows up in the help output.
2800
2800
2801 The norepo argument defines whether the command does not require a
2801 The norepo argument defines whether the command does not require a
2802 local repository. Most commands operate against a repository, thus the
2802 local repository. Most commands operate against a repository, thus the
2803 default is False.
2803 default is False.
2804
2804
2805 The optionalrepo argument defines whether the command optionally requires
2805 The optionalrepo argument defines whether the command optionally requires
2806 a local repository.
2806 a local repository.
2807
2807
2808 The inferrepo argument defines whether to try to find a repository from the
2808 The inferrepo argument defines whether to try to find a repository from the
2809 command line arguments. If True, arguments will be examined for potential
2809 command line arguments. If True, arguments will be examined for potential
2810 repository locations. See ``findrepo()``. If a repository is found, it
2810 repository locations. See ``findrepo()``. If a repository is found, it
2811 will be used.
2811 will be used.
2812 """
2812 """
2813 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2813 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2814 inferrepo=False):
2814 inferrepo=False):
2815 def decorator(func):
2815 def decorator(func):
2816 if synopsis:
2816 if synopsis:
2817 table[name] = func, list(options), synopsis
2817 table[name] = func, list(options), synopsis
2818 else:
2818 else:
2819 table[name] = func, list(options)
2819 table[name] = func, list(options)
2820
2820
2821 if norepo:
2821 if norepo:
2822 # Avoid import cycle.
2822 # Avoid import cycle.
2823 import commands
2823 import commands
2824 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2824 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2825
2825
2826 if optionalrepo:
2826 if optionalrepo:
2827 import commands
2827 import commands
2828 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2828 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2829
2829
2830 if inferrepo:
2830 if inferrepo:
2831 import commands
2831 import commands
2832 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2832 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2833
2833
2834 return func
2834 return func
2835 return decorator
2835 return decorator
2836
2836
2837 return cmd
2837 return cmd
2838
2838
2839 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2839 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2840 # commands.outgoing. "missing" is "missing" of the result of
2840 # commands.outgoing. "missing" is "missing" of the result of
2841 # "findcommonoutgoing()"
2841 # "findcommonoutgoing()"
2842 outgoinghooks = util.hooks()
2842 outgoinghooks = util.hooks()
2843
2843
2844 # a list of (ui, repo) functions called by commands.summary
2844 # a list of (ui, repo) functions called by commands.summary
2845 summaryhooks = util.hooks()
2845 summaryhooks = util.hooks()
2846
2846
2847 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2847 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2848 #
2848 #
2849 # functions should return tuple of booleans below, if 'changes' is None:
2849 # functions should return tuple of booleans below, if 'changes' is None:
2850 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2850 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2851 #
2851 #
2852 # otherwise, 'changes' is a tuple of tuples below:
2852 # otherwise, 'changes' is a tuple of tuples below:
2853 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2853 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2854 # - (desturl, destbranch, destpeer, outgoing)
2854 # - (desturl, destbranch, destpeer, outgoing)
2855 summaryremotehooks = util.hooks()
2855 summaryremotehooks = util.hooks()
2856
2856
2857 # A list of state files kept by multistep operations like graft.
2857 # A list of state files kept by multistep operations like graft.
2858 # Since graft cannot be aborted, it is considered 'clearable' by update.
2858 # Since graft cannot be aborted, it is considered 'clearable' by update.
2859 # note: bisect is intentionally excluded
2859 # note: bisect is intentionally excluded
2860 # (state file, clearable, allowcommit, error, hint)
2860 # (state file, clearable, allowcommit, error, hint)
2861 unfinishedstates = [
2861 unfinishedstates = [
2862 ('graftstate', True, False, _('graft in progress'),
2862 ('graftstate', True, False, _('graft in progress'),
2863 _("use 'hg graft --continue' or 'hg update' to abort")),
2863 _("use 'hg graft --continue' or 'hg update' to abort")),
2864 ('updatestate', True, False, _('last update was interrupted'),
2864 ('updatestate', True, False, _('last update was interrupted'),
2865 _("use 'hg update' to get a consistent checkout"))
2865 _("use 'hg update' to get a consistent checkout"))
2866 ]
2866 ]
2867
2867
2868 def checkunfinished(repo, commit=False):
2868 def checkunfinished(repo, commit=False):
2869 '''Look for an unfinished multistep operation, like graft, and abort
2869 '''Look for an unfinished multistep operation, like graft, and abort
2870 if found. It's probably good to check this right before
2870 if found. It's probably good to check this right before
2871 bailifchanged().
2871 bailifchanged().
2872 '''
2872 '''
2873 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2873 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2874 if commit and allowcommit:
2874 if commit and allowcommit:
2875 continue
2875 continue
2876 if repo.vfs.exists(f):
2876 if repo.vfs.exists(f):
2877 raise util.Abort(msg, hint=hint)
2877 raise util.Abort(msg, hint=hint)
2878
2878
2879 def clearunfinished(repo):
2879 def clearunfinished(repo):
2880 '''Check for unfinished operations (as above), and clear the ones
2880 '''Check for unfinished operations (as above), and clear the ones
2881 that are clearable.
2881 that are clearable.
2882 '''
2882 '''
2883 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2883 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2884 if not clearable and repo.vfs.exists(f):
2884 if not clearable and repo.vfs.exists(f):
2885 raise util.Abort(msg, hint=hint)
2885 raise util.Abort(msg, hint=hint)
2886 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2886 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2887 if clearable and repo.vfs.exists(f):
2887 if clearable and repo.vfs.exists(f):
2888 util.unlink(repo.join(f))
2888 util.unlink(repo.join(f))
@@ -1,2489 +1,2489 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add a
4 $ hg add a
5 $ echo line 1 > b
5 $ echo line 1 > b
6 $ echo line 2 >> b
6 $ echo line 2 >> b
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
7 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
8
8
9 $ hg add b
9 $ hg add b
10 $ echo other 1 > c
10 $ echo other 1 > c
11 $ echo other 2 >> c
11 $ echo other 2 >> c
12 $ echo >> c
12 $ echo >> c
13 $ echo other 3 >> c
13 $ echo other 3 >> c
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
14 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
15
15
16 $ hg add c
16 $ hg add c
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
17 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
18 $ echo c >> c
18 $ echo c >> c
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
19 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
20
20
21 $ echo foo > .hg/branch
21 $ echo foo > .hg/branch
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
22 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
23
23
24 $ hg co -q 3
24 $ hg co -q 3
25 $ echo other 4 >> d
25 $ echo other 4 >> d
26 $ hg add d
26 $ hg add d
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
27 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
28
28
29 $ hg merge -q foo
29 $ hg merge -q foo
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
30 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
31
31
32 Second branch starting at nullrev:
32 Second branch starting at nullrev:
33
33
34 $ hg update null
34 $ hg update null
35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
35 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
36 $ echo second > second
36 $ echo second > second
37 $ hg add second
37 $ hg add second
38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
38 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
39 created new head
39 created new head
40
40
41 $ echo third > third
41 $ echo third > third
42 $ hg add third
42 $ hg add third
43 $ hg mv second fourth
43 $ hg mv second fourth
44 $ hg commit -m third -d "2020-01-01 10:01"
44 $ hg commit -m third -d "2020-01-01 10:01"
45
45
46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
46 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
47 fourth (second)
47 fourth (second)
48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
48 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
49 second -> fourth
49 second -> fourth
50
50
51 Quoting for ui.logtemplate
51 Quoting for ui.logtemplate
52
52
53 $ hg tip --config "ui.logtemplate={rev}\n"
53 $ hg tip --config "ui.logtemplate={rev}\n"
54 8
54 8
55 $ hg tip --config "ui.logtemplate='{rev}\n'"
55 $ hg tip --config "ui.logtemplate='{rev}\n'"
56 8
56 8
57 $ hg tip --config 'ui.logtemplate="{rev}\n"'
57 $ hg tip --config 'ui.logtemplate="{rev}\n"'
58 8
58 8
59
59
60 Make sure user/global hgrc does not affect tests
60 Make sure user/global hgrc does not affect tests
61
61
62 $ echo '[ui]' > .hg/hgrc
62 $ echo '[ui]' > .hg/hgrc
63 $ echo 'logtemplate =' >> .hg/hgrc
63 $ echo 'logtemplate =' >> .hg/hgrc
64 $ echo 'style =' >> .hg/hgrc
64 $ echo 'style =' >> .hg/hgrc
65
65
66 Add some simple styles to settings
66 Add some simple styles to settings
67
67
68 $ echo '[templates]' >> .hg/hgrc
68 $ echo '[templates]' >> .hg/hgrc
69 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
69 $ printf 'simple = "{rev}\\n"\n' >> .hg/hgrc
70 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
70 $ printf 'simple2 = {rev}\\n\n' >> .hg/hgrc
71
71
72 $ hg log -l1 -Tsimple
72 $ hg log -l1 -Tsimple
73 8
73 8
74 $ hg log -l1 -Tsimple2
74 $ hg log -l1 -Tsimple2
75 8
75 8
76
76
77 Test templates and style maps in files:
77 Test templates and style maps in files:
78
78
79 $ echo "{rev}" > tmpl
79 $ echo "{rev}" > tmpl
80 $ hg log -l1 -T./tmpl
80 $ hg log -l1 -T./tmpl
81 8
81 8
82 $ hg log -l1 -Tblah/blah
82 $ hg log -l1 -Tblah/blah
83 blah/blah (no-eol)
83 blah/blah (no-eol)
84
84
85 $ printf 'changeset = "{rev}\\n"\n' > map-simple
85 $ printf 'changeset = "{rev}\\n"\n' > map-simple
86 $ hg log -l1 -T./map-simple
86 $ hg log -l1 -T./map-simple
87 8
87 8
88
88
89 Template should precede style option
89 Template should precede style option
90
90
91 $ hg log -l1 --style default -T '{rev}\n'
91 $ hg log -l1 --style default -T '{rev}\n'
92 8
92 8
93
93
94 Default style is like normal output:
94 Default style is like normal output:
95
95
96 $ hg log > log.out
96 $ hg log > log.out
97 $ hg log --style default > style.out
97 $ hg log --style default > style.out
98 $ cmp log.out style.out || diff -u log.out style.out
98 $ cmp log.out style.out || diff -u log.out style.out
99
99
100 $ hg log -v > log.out
100 $ hg log -v > log.out
101 $ hg log -v --style default > style.out
101 $ hg log -v --style default > style.out
102 $ cmp log.out style.out || diff -u log.out style.out
102 $ cmp log.out style.out || diff -u log.out style.out
103
103
104 $ hg log --debug > log.out
104 $ hg log --debug > log.out
105 $ hg log --debug --style default > style.out
105 $ hg log --debug --style default > style.out
106 $ cmp log.out style.out || diff -u log.out style.out
106 $ cmp log.out style.out || diff -u log.out style.out
107
107
108 Default style should also preserve color information (issue2866):
108 Default style should also preserve color information (issue2866):
109
109
110 $ cp $HGRCPATH $HGRCPATH-bak
110 $ cp $HGRCPATH $HGRCPATH-bak
111 $ cat <<EOF >> $HGRCPATH
111 $ cat <<EOF >> $HGRCPATH
112 > [extensions]
112 > [extensions]
113 > color=
113 > color=
114 > EOF
114 > EOF
115
115
116 $ hg --color=debug log > log.out
116 $ hg --color=debug log > log.out
117 $ hg --color=debug log --style default > style.out
117 $ hg --color=debug log --style default > style.out
118 $ cmp log.out style.out || diff -u log.out style.out
118 $ cmp log.out style.out || diff -u log.out style.out
119 $ hg --color=debug -v log > log.out
119 $ hg --color=debug -v log > log.out
120 $ hg --color=debug -v log --style default > style.out
120 $ hg --color=debug -v log --style default > style.out
121 $ cmp log.out style.out || diff -u log.out style.out
121 $ cmp log.out style.out || diff -u log.out style.out
122 $ hg --color=debug --debug log > log.out
122 $ hg --color=debug --debug log > log.out
123 $ hg --color=debug --debug log --style default > style.out
123 $ hg --color=debug --debug log --style default > style.out
124 $ cmp log.out style.out || diff -u log.out style.out
124 $ cmp log.out style.out || diff -u log.out style.out
125
125
126 $ mv $HGRCPATH-bak $HGRCPATH
126 $ mv $HGRCPATH-bak $HGRCPATH
127
127
128 Revision with no copies (used to print a traceback):
128 Revision with no copies (used to print a traceback):
129
129
130 $ hg tip -v --template '\n'
130 $ hg tip -v --template '\n'
131
131
132
132
133 Compact style works:
133 Compact style works:
134
134
135 $ hg log -Tcompact
135 $ hg log -Tcompact
136 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
136 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
137 third
137 third
138
138
139 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
139 7:-1 29114dbae42b 1970-01-12 13:46 +0000 user
140 second
140 second
141
141
142 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
142 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
143 merge
143 merge
144
144
145 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
145 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
146 new head
146 new head
147
147
148 4 bbe44766e73d 1970-01-17 04:53 +0000 person
148 4 bbe44766e73d 1970-01-17 04:53 +0000 person
149 new branch
149 new branch
150
150
151 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
151 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
152 no user, no domain
152 no user, no domain
153
153
154 2 97054abb4ab8 1970-01-14 21:20 +0000 other
154 2 97054abb4ab8 1970-01-14 21:20 +0000 other
155 no person
155 no person
156
156
157 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
157 1 b608e9d1a3f0 1970-01-13 17:33 +0000 other
158 other 1
158 other 1
159
159
160 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
160 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 user
161 line 1
161 line 1
162
162
163
163
164 $ hg log -v --style compact
164 $ hg log -v --style compact
165 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
165 8[tip] 95c24699272e 2020-01-01 10:01 +0000 test
166 third
166 third
167
167
168 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
168 7:-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
169 second
169 second
170
170
171 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
171 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
172 merge
172 merge
173
173
174 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
174 5:3 13207e5a10d9 1970-01-18 08:40 +0000 person
175 new head
175 new head
176
176
177 4 bbe44766e73d 1970-01-17 04:53 +0000 person
177 4 bbe44766e73d 1970-01-17 04:53 +0000 person
178 new branch
178 new branch
179
179
180 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
180 3 10e46f2dcbf4 1970-01-16 01:06 +0000 person
181 no user, no domain
181 no user, no domain
182
182
183 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
183 2 97054abb4ab8 1970-01-14 21:20 +0000 other@place
184 no person
184 no person
185
185
186 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
186 1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
187 other 1
187 other 1
188 other 2
188 other 2
189
189
190 other 3
190 other 3
191
191
192 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
192 0 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
193 line 1
193 line 1
194 line 2
194 line 2
195
195
196
196
197 $ hg log --debug --style compact
197 $ hg log --debug --style compact
198 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
198 8[tip]:7,-1 95c24699272e 2020-01-01 10:01 +0000 test
199 third
199 third
200
200
201 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
201 7:-1,-1 29114dbae42b 1970-01-12 13:46 +0000 User Name <user@hostname>
202 second
202 second
203
203
204 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
204 6:5,4 d41e714fe50d 1970-01-18 08:40 +0000 person
205 merge
205 merge
206
206
207 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
207 5:3,-1 13207e5a10d9 1970-01-18 08:40 +0000 person
208 new head
208 new head
209
209
210 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
210 4:3,-1 bbe44766e73d 1970-01-17 04:53 +0000 person
211 new branch
211 new branch
212
212
213 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
213 3:2,-1 10e46f2dcbf4 1970-01-16 01:06 +0000 person
214 no user, no domain
214 no user, no domain
215
215
216 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
216 2:1,-1 97054abb4ab8 1970-01-14 21:20 +0000 other@place
217 no person
217 no person
218
218
219 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
219 1:0,-1 b608e9d1a3f0 1970-01-13 17:33 +0000 A. N. Other <other@place>
220 other 1
220 other 1
221 other 2
221 other 2
222
222
223 other 3
223 other 3
224
224
225 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
225 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:46 +0000 User Name <user@hostname>
226 line 1
226 line 1
227 line 2
227 line 2
228
228
229
229
230 Test xml styles:
230 Test xml styles:
231
231
232 $ hg log --style xml
232 $ hg log --style xml
233 <?xml version="1.0"?>
233 <?xml version="1.0"?>
234 <log>
234 <log>
235 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
235 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
236 <tag>tip</tag>
236 <tag>tip</tag>
237 <author email="test">test</author>
237 <author email="test">test</author>
238 <date>2020-01-01T10:01:00+00:00</date>
238 <date>2020-01-01T10:01:00+00:00</date>
239 <msg xml:space="preserve">third</msg>
239 <msg xml:space="preserve">third</msg>
240 </logentry>
240 </logentry>
241 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
241 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
242 <parent revision="-1" node="0000000000000000000000000000000000000000" />
242 <parent revision="-1" node="0000000000000000000000000000000000000000" />
243 <author email="user@hostname">User Name</author>
243 <author email="user@hostname">User Name</author>
244 <date>1970-01-12T13:46:40+00:00</date>
244 <date>1970-01-12T13:46:40+00:00</date>
245 <msg xml:space="preserve">second</msg>
245 <msg xml:space="preserve">second</msg>
246 </logentry>
246 </logentry>
247 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
247 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
248 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
248 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
249 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
249 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
250 <author email="person">person</author>
250 <author email="person">person</author>
251 <date>1970-01-18T08:40:01+00:00</date>
251 <date>1970-01-18T08:40:01+00:00</date>
252 <msg xml:space="preserve">merge</msg>
252 <msg xml:space="preserve">merge</msg>
253 </logentry>
253 </logentry>
254 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
254 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
255 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
255 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
256 <author email="person">person</author>
256 <author email="person">person</author>
257 <date>1970-01-18T08:40:00+00:00</date>
257 <date>1970-01-18T08:40:00+00:00</date>
258 <msg xml:space="preserve">new head</msg>
258 <msg xml:space="preserve">new head</msg>
259 </logentry>
259 </logentry>
260 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
260 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
261 <branch>foo</branch>
261 <branch>foo</branch>
262 <author email="person">person</author>
262 <author email="person">person</author>
263 <date>1970-01-17T04:53:20+00:00</date>
263 <date>1970-01-17T04:53:20+00:00</date>
264 <msg xml:space="preserve">new branch</msg>
264 <msg xml:space="preserve">new branch</msg>
265 </logentry>
265 </logentry>
266 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
266 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
267 <author email="person">person</author>
267 <author email="person">person</author>
268 <date>1970-01-16T01:06:40+00:00</date>
268 <date>1970-01-16T01:06:40+00:00</date>
269 <msg xml:space="preserve">no user, no domain</msg>
269 <msg xml:space="preserve">no user, no domain</msg>
270 </logentry>
270 </logentry>
271 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
271 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
272 <author email="other@place">other</author>
272 <author email="other@place">other</author>
273 <date>1970-01-14T21:20:00+00:00</date>
273 <date>1970-01-14T21:20:00+00:00</date>
274 <msg xml:space="preserve">no person</msg>
274 <msg xml:space="preserve">no person</msg>
275 </logentry>
275 </logentry>
276 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
276 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
277 <author email="other@place">A. N. Other</author>
277 <author email="other@place">A. N. Other</author>
278 <date>1970-01-13T17:33:20+00:00</date>
278 <date>1970-01-13T17:33:20+00:00</date>
279 <msg xml:space="preserve">other 1
279 <msg xml:space="preserve">other 1
280 other 2
280 other 2
281
281
282 other 3</msg>
282 other 3</msg>
283 </logentry>
283 </logentry>
284 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
284 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
285 <author email="user@hostname">User Name</author>
285 <author email="user@hostname">User Name</author>
286 <date>1970-01-12T13:46:40+00:00</date>
286 <date>1970-01-12T13:46:40+00:00</date>
287 <msg xml:space="preserve">line 1
287 <msg xml:space="preserve">line 1
288 line 2</msg>
288 line 2</msg>
289 </logentry>
289 </logentry>
290 </log>
290 </log>
291
291
292 $ hg log -v --style xml
292 $ hg log -v --style xml
293 <?xml version="1.0"?>
293 <?xml version="1.0"?>
294 <log>
294 <log>
295 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
295 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
296 <tag>tip</tag>
296 <tag>tip</tag>
297 <author email="test">test</author>
297 <author email="test">test</author>
298 <date>2020-01-01T10:01:00+00:00</date>
298 <date>2020-01-01T10:01:00+00:00</date>
299 <msg xml:space="preserve">third</msg>
299 <msg xml:space="preserve">third</msg>
300 <paths>
300 <paths>
301 <path action="A">fourth</path>
301 <path action="A">fourth</path>
302 <path action="A">third</path>
302 <path action="A">third</path>
303 <path action="R">second</path>
303 <path action="R">second</path>
304 </paths>
304 </paths>
305 <copies>
305 <copies>
306 <copy source="second">fourth</copy>
306 <copy source="second">fourth</copy>
307 </copies>
307 </copies>
308 </logentry>
308 </logentry>
309 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
309 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
310 <parent revision="-1" node="0000000000000000000000000000000000000000" />
310 <parent revision="-1" node="0000000000000000000000000000000000000000" />
311 <author email="user@hostname">User Name</author>
311 <author email="user@hostname">User Name</author>
312 <date>1970-01-12T13:46:40+00:00</date>
312 <date>1970-01-12T13:46:40+00:00</date>
313 <msg xml:space="preserve">second</msg>
313 <msg xml:space="preserve">second</msg>
314 <paths>
314 <paths>
315 <path action="A">second</path>
315 <path action="A">second</path>
316 </paths>
316 </paths>
317 </logentry>
317 </logentry>
318 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
318 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
319 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
319 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
320 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
320 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
321 <author email="person">person</author>
321 <author email="person">person</author>
322 <date>1970-01-18T08:40:01+00:00</date>
322 <date>1970-01-18T08:40:01+00:00</date>
323 <msg xml:space="preserve">merge</msg>
323 <msg xml:space="preserve">merge</msg>
324 <paths>
324 <paths>
325 </paths>
325 </paths>
326 </logentry>
326 </logentry>
327 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
327 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
328 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
328 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
329 <author email="person">person</author>
329 <author email="person">person</author>
330 <date>1970-01-18T08:40:00+00:00</date>
330 <date>1970-01-18T08:40:00+00:00</date>
331 <msg xml:space="preserve">new head</msg>
331 <msg xml:space="preserve">new head</msg>
332 <paths>
332 <paths>
333 <path action="A">d</path>
333 <path action="A">d</path>
334 </paths>
334 </paths>
335 </logentry>
335 </logentry>
336 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
336 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
337 <branch>foo</branch>
337 <branch>foo</branch>
338 <author email="person">person</author>
338 <author email="person">person</author>
339 <date>1970-01-17T04:53:20+00:00</date>
339 <date>1970-01-17T04:53:20+00:00</date>
340 <msg xml:space="preserve">new branch</msg>
340 <msg xml:space="preserve">new branch</msg>
341 <paths>
341 <paths>
342 </paths>
342 </paths>
343 </logentry>
343 </logentry>
344 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
344 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
345 <author email="person">person</author>
345 <author email="person">person</author>
346 <date>1970-01-16T01:06:40+00:00</date>
346 <date>1970-01-16T01:06:40+00:00</date>
347 <msg xml:space="preserve">no user, no domain</msg>
347 <msg xml:space="preserve">no user, no domain</msg>
348 <paths>
348 <paths>
349 <path action="M">c</path>
349 <path action="M">c</path>
350 </paths>
350 </paths>
351 </logentry>
351 </logentry>
352 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
352 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
353 <author email="other@place">other</author>
353 <author email="other@place">other</author>
354 <date>1970-01-14T21:20:00+00:00</date>
354 <date>1970-01-14T21:20:00+00:00</date>
355 <msg xml:space="preserve">no person</msg>
355 <msg xml:space="preserve">no person</msg>
356 <paths>
356 <paths>
357 <path action="A">c</path>
357 <path action="A">c</path>
358 </paths>
358 </paths>
359 </logentry>
359 </logentry>
360 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
360 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
361 <author email="other@place">A. N. Other</author>
361 <author email="other@place">A. N. Other</author>
362 <date>1970-01-13T17:33:20+00:00</date>
362 <date>1970-01-13T17:33:20+00:00</date>
363 <msg xml:space="preserve">other 1
363 <msg xml:space="preserve">other 1
364 other 2
364 other 2
365
365
366 other 3</msg>
366 other 3</msg>
367 <paths>
367 <paths>
368 <path action="A">b</path>
368 <path action="A">b</path>
369 </paths>
369 </paths>
370 </logentry>
370 </logentry>
371 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
371 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
372 <author email="user@hostname">User Name</author>
372 <author email="user@hostname">User Name</author>
373 <date>1970-01-12T13:46:40+00:00</date>
373 <date>1970-01-12T13:46:40+00:00</date>
374 <msg xml:space="preserve">line 1
374 <msg xml:space="preserve">line 1
375 line 2</msg>
375 line 2</msg>
376 <paths>
376 <paths>
377 <path action="A">a</path>
377 <path action="A">a</path>
378 </paths>
378 </paths>
379 </logentry>
379 </logentry>
380 </log>
380 </log>
381
381
382 $ hg log --debug --style xml
382 $ hg log --debug --style xml
383 <?xml version="1.0"?>
383 <?xml version="1.0"?>
384 <log>
384 <log>
385 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
385 <logentry revision="8" node="95c24699272ef57d062b8bccc32c878bf841784a">
386 <tag>tip</tag>
386 <tag>tip</tag>
387 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
387 <parent revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453" />
388 <parent revision="-1" node="0000000000000000000000000000000000000000" />
388 <parent revision="-1" node="0000000000000000000000000000000000000000" />
389 <author email="test">test</author>
389 <author email="test">test</author>
390 <date>2020-01-01T10:01:00+00:00</date>
390 <date>2020-01-01T10:01:00+00:00</date>
391 <msg xml:space="preserve">third</msg>
391 <msg xml:space="preserve">third</msg>
392 <paths>
392 <paths>
393 <path action="A">fourth</path>
393 <path action="A">fourth</path>
394 <path action="A">third</path>
394 <path action="A">third</path>
395 <path action="R">second</path>
395 <path action="R">second</path>
396 </paths>
396 </paths>
397 <copies>
397 <copies>
398 <copy source="second">fourth</copy>
398 <copy source="second">fourth</copy>
399 </copies>
399 </copies>
400 <extra key="branch">default</extra>
400 <extra key="branch">default</extra>
401 </logentry>
401 </logentry>
402 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
402 <logentry revision="7" node="29114dbae42b9f078cf2714dbe3a86bba8ec7453">
403 <parent revision="-1" node="0000000000000000000000000000000000000000" />
403 <parent revision="-1" node="0000000000000000000000000000000000000000" />
404 <parent revision="-1" node="0000000000000000000000000000000000000000" />
404 <parent revision="-1" node="0000000000000000000000000000000000000000" />
405 <author email="user@hostname">User Name</author>
405 <author email="user@hostname">User Name</author>
406 <date>1970-01-12T13:46:40+00:00</date>
406 <date>1970-01-12T13:46:40+00:00</date>
407 <msg xml:space="preserve">second</msg>
407 <msg xml:space="preserve">second</msg>
408 <paths>
408 <paths>
409 <path action="A">second</path>
409 <path action="A">second</path>
410 </paths>
410 </paths>
411 <extra key="branch">default</extra>
411 <extra key="branch">default</extra>
412 </logentry>
412 </logentry>
413 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
413 <logentry revision="6" node="d41e714fe50d9e4a5f11b4d595d543481b5f980b">
414 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
414 <parent revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f" />
415 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
415 <parent revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74" />
416 <author email="person">person</author>
416 <author email="person">person</author>
417 <date>1970-01-18T08:40:01+00:00</date>
417 <date>1970-01-18T08:40:01+00:00</date>
418 <msg xml:space="preserve">merge</msg>
418 <msg xml:space="preserve">merge</msg>
419 <paths>
419 <paths>
420 </paths>
420 </paths>
421 <extra key="branch">default</extra>
421 <extra key="branch">default</extra>
422 </logentry>
422 </logentry>
423 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
423 <logentry revision="5" node="13207e5a10d9fd28ec424934298e176197f2c67f">
424 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
424 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
425 <parent revision="-1" node="0000000000000000000000000000000000000000" />
425 <parent revision="-1" node="0000000000000000000000000000000000000000" />
426 <author email="person">person</author>
426 <author email="person">person</author>
427 <date>1970-01-18T08:40:00+00:00</date>
427 <date>1970-01-18T08:40:00+00:00</date>
428 <msg xml:space="preserve">new head</msg>
428 <msg xml:space="preserve">new head</msg>
429 <paths>
429 <paths>
430 <path action="A">d</path>
430 <path action="A">d</path>
431 </paths>
431 </paths>
432 <extra key="branch">default</extra>
432 <extra key="branch">default</extra>
433 </logentry>
433 </logentry>
434 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
434 <logentry revision="4" node="bbe44766e73d5f11ed2177f1838de10c53ef3e74">
435 <branch>foo</branch>
435 <branch>foo</branch>
436 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
436 <parent revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47" />
437 <parent revision="-1" node="0000000000000000000000000000000000000000" />
437 <parent revision="-1" node="0000000000000000000000000000000000000000" />
438 <author email="person">person</author>
438 <author email="person">person</author>
439 <date>1970-01-17T04:53:20+00:00</date>
439 <date>1970-01-17T04:53:20+00:00</date>
440 <msg xml:space="preserve">new branch</msg>
440 <msg xml:space="preserve">new branch</msg>
441 <paths>
441 <paths>
442 </paths>
442 </paths>
443 <extra key="branch">foo</extra>
443 <extra key="branch">foo</extra>
444 </logentry>
444 </logentry>
445 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
445 <logentry revision="3" node="10e46f2dcbf4823578cf180f33ecf0b957964c47">
446 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
446 <parent revision="2" node="97054abb4ab824450e9164180baf491ae0078465" />
447 <parent revision="-1" node="0000000000000000000000000000000000000000" />
447 <parent revision="-1" node="0000000000000000000000000000000000000000" />
448 <author email="person">person</author>
448 <author email="person">person</author>
449 <date>1970-01-16T01:06:40+00:00</date>
449 <date>1970-01-16T01:06:40+00:00</date>
450 <msg xml:space="preserve">no user, no domain</msg>
450 <msg xml:space="preserve">no user, no domain</msg>
451 <paths>
451 <paths>
452 <path action="M">c</path>
452 <path action="M">c</path>
453 </paths>
453 </paths>
454 <extra key="branch">default</extra>
454 <extra key="branch">default</extra>
455 </logentry>
455 </logentry>
456 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
456 <logentry revision="2" node="97054abb4ab824450e9164180baf491ae0078465">
457 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
457 <parent revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965" />
458 <parent revision="-1" node="0000000000000000000000000000000000000000" />
458 <parent revision="-1" node="0000000000000000000000000000000000000000" />
459 <author email="other@place">other</author>
459 <author email="other@place">other</author>
460 <date>1970-01-14T21:20:00+00:00</date>
460 <date>1970-01-14T21:20:00+00:00</date>
461 <msg xml:space="preserve">no person</msg>
461 <msg xml:space="preserve">no person</msg>
462 <paths>
462 <paths>
463 <path action="A">c</path>
463 <path action="A">c</path>
464 </paths>
464 </paths>
465 <extra key="branch">default</extra>
465 <extra key="branch">default</extra>
466 </logentry>
466 </logentry>
467 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
467 <logentry revision="1" node="b608e9d1a3f0273ccf70fb85fd6866b3482bf965">
468 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
468 <parent revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f" />
469 <parent revision="-1" node="0000000000000000000000000000000000000000" />
469 <parent revision="-1" node="0000000000000000000000000000000000000000" />
470 <author email="other@place">A. N. Other</author>
470 <author email="other@place">A. N. Other</author>
471 <date>1970-01-13T17:33:20+00:00</date>
471 <date>1970-01-13T17:33:20+00:00</date>
472 <msg xml:space="preserve">other 1
472 <msg xml:space="preserve">other 1
473 other 2
473 other 2
474
474
475 other 3</msg>
475 other 3</msg>
476 <paths>
476 <paths>
477 <path action="A">b</path>
477 <path action="A">b</path>
478 </paths>
478 </paths>
479 <extra key="branch">default</extra>
479 <extra key="branch">default</extra>
480 </logentry>
480 </logentry>
481 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
481 <logentry revision="0" node="1e4e1b8f71e05681d422154f5421e385fec3454f">
482 <parent revision="-1" node="0000000000000000000000000000000000000000" />
482 <parent revision="-1" node="0000000000000000000000000000000000000000" />
483 <parent revision="-1" node="0000000000000000000000000000000000000000" />
483 <parent revision="-1" node="0000000000000000000000000000000000000000" />
484 <author email="user@hostname">User Name</author>
484 <author email="user@hostname">User Name</author>
485 <date>1970-01-12T13:46:40+00:00</date>
485 <date>1970-01-12T13:46:40+00:00</date>
486 <msg xml:space="preserve">line 1
486 <msg xml:space="preserve">line 1
487 line 2</msg>
487 line 2</msg>
488 <paths>
488 <paths>
489 <path action="A">a</path>
489 <path action="A">a</path>
490 </paths>
490 </paths>
491 <extra key="branch">default</extra>
491 <extra key="branch">default</extra>
492 </logentry>
492 </logentry>
493 </log>
493 </log>
494
494
495
495
496 Test JSON style:
496 Test JSON style:
497
497
498 $ hg log -k nosuch -Tjson
498 $ hg log -k nosuch -Tjson
499 []
499 []
500
500
501 $ hg log -qr . -Tjson
501 $ hg log -qr . -Tjson
502 [
502 [
503 {
503 {
504 "rev": 8,
504 "rev": 8,
505 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
505 "node": "95c24699272ef57d062b8bccc32c878bf841784a"
506 }
506 }
507 ]
507 ]
508
508
509 $ hg log -vpr . -Tjson --stat
509 $ hg log -vpr . -Tjson --stat
510 [
510 [
511 {
511 {
512 "rev": 8,
512 "rev": 8,
513 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
513 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
514 "branch": "default",
514 "branch": "default",
515 "phase": "draft",
515 "phase": "draft",
516 "user": "test",
516 "user": "test",
517 "date": [1577872860, 0],
517 "date": [1577872860, 0],
518 "desc": "third",
518 "desc": "third",
519 "bookmarks": [],
519 "bookmarks": [],
520 "tags": ["tip"],
520 "tags": ["tip"],
521 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
521 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
522 "files": ["fourth", "second", "third"],
522 "files": ["fourth", "second", "third"],
523 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
523 "diffstat": " fourth | 1 +\n second | 1 -\n third | 1 +\n 3 files changed, 2 insertions(+), 1 deletions(-)\n",
524 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
524 "diff": "diff -r 29114dbae42b -r 95c24699272e fourth\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/fourth\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+second\ndiff -r 29114dbae42b -r 95c24699272e second\n--- a/second\tMon Jan 12 13:46:40 1970 +0000\n+++ /dev/null\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +0,0 @@\n-second\ndiff -r 29114dbae42b -r 95c24699272e third\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/third\tWed Jan 01 10:01:00 2020 +0000\n@@ -0,0 +1,1 @@\n+third\n"
525 }
525 }
526 ]
526 ]
527
527
528 $ hg log -T json
528 $ hg log -T json
529 [
529 [
530 {
530 {
531 "rev": 8,
531 "rev": 8,
532 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
532 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
533 "branch": "default",
533 "branch": "default",
534 "phase": "draft",
534 "phase": "draft",
535 "user": "test",
535 "user": "test",
536 "date": [1577872860, 0],
536 "date": [1577872860, 0],
537 "desc": "third",
537 "desc": "third",
538 "bookmarks": [],
538 "bookmarks": [],
539 "tags": ["tip"],
539 "tags": ["tip"],
540 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
540 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"]
541 },
541 },
542 {
542 {
543 "rev": 7,
543 "rev": 7,
544 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
544 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
545 "branch": "default",
545 "branch": "default",
546 "phase": "draft",
546 "phase": "draft",
547 "user": "User Name <user@hostname>",
547 "user": "User Name <user@hostname>",
548 "date": [1000000, 0],
548 "date": [1000000, 0],
549 "desc": "second",
549 "desc": "second",
550 "bookmarks": [],
550 "bookmarks": [],
551 "tags": [],
551 "tags": [],
552 "parents": ["0000000000000000000000000000000000000000"]
552 "parents": ["0000000000000000000000000000000000000000"]
553 },
553 },
554 {
554 {
555 "rev": 6,
555 "rev": 6,
556 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
556 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
557 "branch": "default",
557 "branch": "default",
558 "phase": "draft",
558 "phase": "draft",
559 "user": "person",
559 "user": "person",
560 "date": [1500001, 0],
560 "date": [1500001, 0],
561 "desc": "merge",
561 "desc": "merge",
562 "bookmarks": [],
562 "bookmarks": [],
563 "tags": [],
563 "tags": [],
564 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
564 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"]
565 },
565 },
566 {
566 {
567 "rev": 5,
567 "rev": 5,
568 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
568 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
569 "branch": "default",
569 "branch": "default",
570 "phase": "draft",
570 "phase": "draft",
571 "user": "person",
571 "user": "person",
572 "date": [1500000, 0],
572 "date": [1500000, 0],
573 "desc": "new head",
573 "desc": "new head",
574 "bookmarks": [],
574 "bookmarks": [],
575 "tags": [],
575 "tags": [],
576 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
576 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
577 },
577 },
578 {
578 {
579 "rev": 4,
579 "rev": 4,
580 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
580 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
581 "branch": "foo",
581 "branch": "foo",
582 "phase": "draft",
582 "phase": "draft",
583 "user": "person",
583 "user": "person",
584 "date": [1400000, 0],
584 "date": [1400000, 0],
585 "desc": "new branch",
585 "desc": "new branch",
586 "bookmarks": [],
586 "bookmarks": [],
587 "tags": [],
587 "tags": [],
588 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
588 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"]
589 },
589 },
590 {
590 {
591 "rev": 3,
591 "rev": 3,
592 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
592 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
593 "branch": "default",
593 "branch": "default",
594 "phase": "draft",
594 "phase": "draft",
595 "user": "person",
595 "user": "person",
596 "date": [1300000, 0],
596 "date": [1300000, 0],
597 "desc": "no user, no domain",
597 "desc": "no user, no domain",
598 "bookmarks": [],
598 "bookmarks": [],
599 "tags": [],
599 "tags": [],
600 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
600 "parents": ["97054abb4ab824450e9164180baf491ae0078465"]
601 },
601 },
602 {
602 {
603 "rev": 2,
603 "rev": 2,
604 "node": "97054abb4ab824450e9164180baf491ae0078465",
604 "node": "97054abb4ab824450e9164180baf491ae0078465",
605 "branch": "default",
605 "branch": "default",
606 "phase": "draft",
606 "phase": "draft",
607 "user": "other@place",
607 "user": "other@place",
608 "date": [1200000, 0],
608 "date": [1200000, 0],
609 "desc": "no person",
609 "desc": "no person",
610 "bookmarks": [],
610 "bookmarks": [],
611 "tags": [],
611 "tags": [],
612 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
612 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"]
613 },
613 },
614 {
614 {
615 "rev": 1,
615 "rev": 1,
616 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
616 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
617 "branch": "default",
617 "branch": "default",
618 "phase": "draft",
618 "phase": "draft",
619 "user": "A. N. Other <other@place>",
619 "user": "A. N. Other <other@place>",
620 "date": [1100000, 0],
620 "date": [1100000, 0],
621 "desc": "other 1\nother 2\n\nother 3",
621 "desc": "other 1\nother 2\n\nother 3",
622 "bookmarks": [],
622 "bookmarks": [],
623 "tags": [],
623 "tags": [],
624 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
624 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"]
625 },
625 },
626 {
626 {
627 "rev": 0,
627 "rev": 0,
628 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
628 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
629 "branch": "default",
629 "branch": "default",
630 "phase": "draft",
630 "phase": "draft",
631 "user": "User Name <user@hostname>",
631 "user": "User Name <user@hostname>",
632 "date": [1000000, 0],
632 "date": [1000000, 0],
633 "desc": "line 1\nline 2",
633 "desc": "line 1\nline 2",
634 "bookmarks": [],
634 "bookmarks": [],
635 "tags": [],
635 "tags": [],
636 "parents": ["0000000000000000000000000000000000000000"]
636 "parents": ["0000000000000000000000000000000000000000"]
637 }
637 }
638 ]
638 ]
639
639
640 $ hg heads -v -Tjson
640 $ hg heads -v -Tjson
641 [
641 [
642 {
642 {
643 "rev": 8,
643 "rev": 8,
644 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
644 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
645 "branch": "default",
645 "branch": "default",
646 "phase": "draft",
646 "phase": "draft",
647 "user": "test",
647 "user": "test",
648 "date": [1577872860, 0],
648 "date": [1577872860, 0],
649 "desc": "third",
649 "desc": "third",
650 "bookmarks": [],
650 "bookmarks": [],
651 "tags": ["tip"],
651 "tags": ["tip"],
652 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
652 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
653 "files": ["fourth", "second", "third"]
653 "files": ["fourth", "second", "third"]
654 },
654 },
655 {
655 {
656 "rev": 6,
656 "rev": 6,
657 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
657 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
658 "branch": "default",
658 "branch": "default",
659 "phase": "draft",
659 "phase": "draft",
660 "user": "person",
660 "user": "person",
661 "date": [1500001, 0],
661 "date": [1500001, 0],
662 "desc": "merge",
662 "desc": "merge",
663 "bookmarks": [],
663 "bookmarks": [],
664 "tags": [],
664 "tags": [],
665 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
665 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
666 "files": []
666 "files": []
667 },
667 },
668 {
668 {
669 "rev": 4,
669 "rev": 4,
670 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
670 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
671 "branch": "foo",
671 "branch": "foo",
672 "phase": "draft",
672 "phase": "draft",
673 "user": "person",
673 "user": "person",
674 "date": [1400000, 0],
674 "date": [1400000, 0],
675 "desc": "new branch",
675 "desc": "new branch",
676 "bookmarks": [],
676 "bookmarks": [],
677 "tags": [],
677 "tags": [],
678 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
678 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
679 "files": []
679 "files": []
680 }
680 }
681 ]
681 ]
682
682
683 $ hg log --debug -Tjson
683 $ hg log --debug -Tjson
684 [
684 [
685 {
685 {
686 "rev": 8,
686 "rev": 8,
687 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
687 "node": "95c24699272ef57d062b8bccc32c878bf841784a",
688 "branch": "default",
688 "branch": "default",
689 "phase": "draft",
689 "phase": "draft",
690 "user": "test",
690 "user": "test",
691 "date": [1577872860, 0],
691 "date": [1577872860, 0],
692 "desc": "third",
692 "desc": "third",
693 "bookmarks": [],
693 "bookmarks": [],
694 "tags": ["tip"],
694 "tags": ["tip"],
695 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
695 "parents": ["29114dbae42b9f078cf2714dbe3a86bba8ec7453"],
696 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
696 "manifest": "94961b75a2da554b4df6fb599e5bfc7d48de0c64",
697 "extra": {"branch": "default"},
697 "extra": {"branch": "default"},
698 "modified": [],
698 "modified": [],
699 "added": ["second"],
699 "added": ["fourth", "third"],
700 "removed": ["fourth", "third"]
700 "removed": ["second"]
701 },
701 },
702 {
702 {
703 "rev": 7,
703 "rev": 7,
704 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
704 "node": "29114dbae42b9f078cf2714dbe3a86bba8ec7453",
705 "branch": "default",
705 "branch": "default",
706 "phase": "draft",
706 "phase": "draft",
707 "user": "User Name <user@hostname>",
707 "user": "User Name <user@hostname>",
708 "date": [1000000, 0],
708 "date": [1000000, 0],
709 "desc": "second",
709 "desc": "second",
710 "bookmarks": [],
710 "bookmarks": [],
711 "tags": [],
711 "tags": [],
712 "parents": ["0000000000000000000000000000000000000000"],
712 "parents": ["0000000000000000000000000000000000000000"],
713 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
713 "manifest": "f2dbc354b94e5ec0b4f10680ee0cee816101d0bf",
714 "extra": {"branch": "default"},
714 "extra": {"branch": "default"},
715 "modified": [],
715 "modified": [],
716 "added": [],
716 "added": ["second"],
717 "removed": ["second"]
717 "removed": []
718 },
718 },
719 {
719 {
720 "rev": 6,
720 "rev": 6,
721 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
721 "node": "d41e714fe50d9e4a5f11b4d595d543481b5f980b",
722 "branch": "default",
722 "branch": "default",
723 "phase": "draft",
723 "phase": "draft",
724 "user": "person",
724 "user": "person",
725 "date": [1500001, 0],
725 "date": [1500001, 0],
726 "desc": "merge",
726 "desc": "merge",
727 "bookmarks": [],
727 "bookmarks": [],
728 "tags": [],
728 "tags": [],
729 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
729 "parents": ["13207e5a10d9fd28ec424934298e176197f2c67f", "bbe44766e73d5f11ed2177f1838de10c53ef3e74"],
730 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
730 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
731 "extra": {"branch": "default"},
731 "extra": {"branch": "default"},
732 "modified": [],
732 "modified": [],
733 "added": [],
733 "added": [],
734 "removed": []
734 "removed": []
735 },
735 },
736 {
736 {
737 "rev": 5,
737 "rev": 5,
738 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
738 "node": "13207e5a10d9fd28ec424934298e176197f2c67f",
739 "branch": "default",
739 "branch": "default",
740 "phase": "draft",
740 "phase": "draft",
741 "user": "person",
741 "user": "person",
742 "date": [1500000, 0],
742 "date": [1500000, 0],
743 "desc": "new head",
743 "desc": "new head",
744 "bookmarks": [],
744 "bookmarks": [],
745 "tags": [],
745 "tags": [],
746 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
746 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
747 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
747 "manifest": "4dc3def4f9b4c6e8de820f6ee74737f91e96a216",
748 "extra": {"branch": "default"},
748 "extra": {"branch": "default"},
749 "modified": [],
749 "modified": [],
750 "added": [],
750 "added": ["d"],
751 "removed": ["d"]
751 "removed": []
752 },
752 },
753 {
753 {
754 "rev": 4,
754 "rev": 4,
755 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
755 "node": "bbe44766e73d5f11ed2177f1838de10c53ef3e74",
756 "branch": "foo",
756 "branch": "foo",
757 "phase": "draft",
757 "phase": "draft",
758 "user": "person",
758 "user": "person",
759 "date": [1400000, 0],
759 "date": [1400000, 0],
760 "desc": "new branch",
760 "desc": "new branch",
761 "bookmarks": [],
761 "bookmarks": [],
762 "tags": [],
762 "tags": [],
763 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
763 "parents": ["10e46f2dcbf4823578cf180f33ecf0b957964c47"],
764 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
764 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
765 "extra": {"branch": "foo"},
765 "extra": {"branch": "foo"},
766 "modified": [],
766 "modified": [],
767 "added": [],
767 "added": [],
768 "removed": []
768 "removed": []
769 },
769 },
770 {
770 {
771 "rev": 3,
771 "rev": 3,
772 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
772 "node": "10e46f2dcbf4823578cf180f33ecf0b957964c47",
773 "branch": "default",
773 "branch": "default",
774 "phase": "draft",
774 "phase": "draft",
775 "user": "person",
775 "user": "person",
776 "date": [1300000, 0],
776 "date": [1300000, 0],
777 "desc": "no user, no domain",
777 "desc": "no user, no domain",
778 "bookmarks": [],
778 "bookmarks": [],
779 "tags": [],
779 "tags": [],
780 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
780 "parents": ["97054abb4ab824450e9164180baf491ae0078465"],
781 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
781 "manifest": "cb5a1327723bada42f117e4c55a303246eaf9ccc",
782 "extra": {"branch": "default"},
782 "extra": {"branch": "default"},
783 "modified": ["c"],
783 "modified": ["c"],
784 "added": [],
784 "added": [],
785 "removed": []
785 "removed": []
786 },
786 },
787 {
787 {
788 "rev": 2,
788 "rev": 2,
789 "node": "97054abb4ab824450e9164180baf491ae0078465",
789 "node": "97054abb4ab824450e9164180baf491ae0078465",
790 "branch": "default",
790 "branch": "default",
791 "phase": "draft",
791 "phase": "draft",
792 "user": "other@place",
792 "user": "other@place",
793 "date": [1200000, 0],
793 "date": [1200000, 0],
794 "desc": "no person",
794 "desc": "no person",
795 "bookmarks": [],
795 "bookmarks": [],
796 "tags": [],
796 "tags": [],
797 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
797 "parents": ["b608e9d1a3f0273ccf70fb85fd6866b3482bf965"],
798 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
798 "manifest": "6e0e82995c35d0d57a52aca8da4e56139e06b4b1",
799 "extra": {"branch": "default"},
799 "extra": {"branch": "default"},
800 "modified": [],
800 "modified": [],
801 "added": [],
801 "added": ["c"],
802 "removed": ["c"]
802 "removed": []
803 },
803 },
804 {
804 {
805 "rev": 1,
805 "rev": 1,
806 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
806 "node": "b608e9d1a3f0273ccf70fb85fd6866b3482bf965",
807 "branch": "default",
807 "branch": "default",
808 "phase": "draft",
808 "phase": "draft",
809 "user": "A. N. Other <other@place>",
809 "user": "A. N. Other <other@place>",
810 "date": [1100000, 0],
810 "date": [1100000, 0],
811 "desc": "other 1\nother 2\n\nother 3",
811 "desc": "other 1\nother 2\n\nother 3",
812 "bookmarks": [],
812 "bookmarks": [],
813 "tags": [],
813 "tags": [],
814 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
814 "parents": ["1e4e1b8f71e05681d422154f5421e385fec3454f"],
815 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
815 "manifest": "4e8d705b1e53e3f9375e0e60dc7b525d8211fe55",
816 "extra": {"branch": "default"},
816 "extra": {"branch": "default"},
817 "modified": [],
817 "modified": [],
818 "added": [],
818 "added": ["b"],
819 "removed": ["b"]
819 "removed": []
820 },
820 },
821 {
821 {
822 "rev": 0,
822 "rev": 0,
823 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
823 "node": "1e4e1b8f71e05681d422154f5421e385fec3454f",
824 "branch": "default",
824 "branch": "default",
825 "phase": "draft",
825 "phase": "draft",
826 "user": "User Name <user@hostname>",
826 "user": "User Name <user@hostname>",
827 "date": [1000000, 0],
827 "date": [1000000, 0],
828 "desc": "line 1\nline 2",
828 "desc": "line 1\nline 2",
829 "bookmarks": [],
829 "bookmarks": [],
830 "tags": [],
830 "tags": [],
831 "parents": ["0000000000000000000000000000000000000000"],
831 "parents": ["0000000000000000000000000000000000000000"],
832 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
832 "manifest": "a0c8bcbbb45c63b90b70ad007bf38961f64f2af0",
833 "extra": {"branch": "default"},
833 "extra": {"branch": "default"},
834 "modified": [],
834 "modified": [],
835 "added": [],
835 "added": ["a"],
836 "removed": ["a"]
836 "removed": []
837 }
837 }
838 ]
838 ]
839
839
840 Error if style not readable:
840 Error if style not readable:
841
841
842 #if unix-permissions no-root
842 #if unix-permissions no-root
843 $ touch q
843 $ touch q
844 $ chmod 0 q
844 $ chmod 0 q
845 $ hg log --style ./q
845 $ hg log --style ./q
846 abort: Permission denied: ./q
846 abort: Permission denied: ./q
847 [255]
847 [255]
848 #endif
848 #endif
849
849
850 Error if no style:
850 Error if no style:
851
851
852 $ hg log --style notexist
852 $ hg log --style notexist
853 abort: style 'notexist' not found
853 abort: style 'notexist' not found
854 (available styles: bisect, changelog, compact, default, phases, xml)
854 (available styles: bisect, changelog, compact, default, phases, xml)
855 [255]
855 [255]
856
856
857 $ hg log -T list
857 $ hg log -T list
858 available styles: bisect, changelog, compact, default, phases, xml
858 available styles: bisect, changelog, compact, default, phases, xml
859 abort: specify a template
859 abort: specify a template
860 [255]
860 [255]
861
861
862 Error if style missing key:
862 Error if style missing key:
863
863
864 $ echo 'q = q' > t
864 $ echo 'q = q' > t
865 $ hg log --style ./t
865 $ hg log --style ./t
866 abort: "changeset" not in template map
866 abort: "changeset" not in template map
867 [255]
867 [255]
868
868
869 Error if style missing value:
869 Error if style missing value:
870
870
871 $ echo 'changeset =' > t
871 $ echo 'changeset =' > t
872 $ hg log --style t
872 $ hg log --style t
873 abort: t:1: missing value
873 abort: t:1: missing value
874 [255]
874 [255]
875
875
876 Error if include fails:
876 Error if include fails:
877
877
878 $ echo 'changeset = q' >> t
878 $ echo 'changeset = q' >> t
879 #if unix-permissions no-root
879 #if unix-permissions no-root
880 $ hg log --style ./t
880 $ hg log --style ./t
881 abort: template file ./q: Permission denied
881 abort: template file ./q: Permission denied
882 [255]
882 [255]
883 $ rm q
883 $ rm q
884 #endif
884 #endif
885
885
886 Include works:
886 Include works:
887
887
888 $ echo '{rev}' > q
888 $ echo '{rev}' > q
889 $ hg log --style ./t
889 $ hg log --style ./t
890 8
890 8
891 7
891 7
892 6
892 6
893 5
893 5
894 4
894 4
895 3
895 3
896 2
896 2
897 1
897 1
898 0
898 0
899
899
900 Check that {phase} works correctly on parents:
900 Check that {phase} works correctly on parents:
901
901
902 $ cat << EOF > parentphase
902 $ cat << EOF > parentphase
903 > changeset_debug = '{rev} ({phase}):{parents}\n'
903 > changeset_debug = '{rev} ({phase}):{parents}\n'
904 > parent = ' {rev} ({phase})'
904 > parent = ' {rev} ({phase})'
905 > EOF
905 > EOF
906 $ hg phase -r 5 --public
906 $ hg phase -r 5 --public
907 $ hg phase -r 7 --secret --force
907 $ hg phase -r 7 --secret --force
908 $ hg log --debug -G --style ./parentphase
908 $ hg log --debug -G --style ./parentphase
909 @ 8 (secret): 7 (secret) -1 (public)
909 @ 8 (secret): 7 (secret) -1 (public)
910 |
910 |
911 o 7 (secret): -1 (public) -1 (public)
911 o 7 (secret): -1 (public) -1 (public)
912
912
913 o 6 (draft): 5 (public) 4 (draft)
913 o 6 (draft): 5 (public) 4 (draft)
914 |\
914 |\
915 | o 5 (public): 3 (public) -1 (public)
915 | o 5 (public): 3 (public) -1 (public)
916 | |
916 | |
917 o | 4 (draft): 3 (public) -1 (public)
917 o | 4 (draft): 3 (public) -1 (public)
918 |/
918 |/
919 o 3 (public): 2 (public) -1 (public)
919 o 3 (public): 2 (public) -1 (public)
920 |
920 |
921 o 2 (public): 1 (public) -1 (public)
921 o 2 (public): 1 (public) -1 (public)
922 |
922 |
923 o 1 (public): 0 (public) -1 (public)
923 o 1 (public): 0 (public) -1 (public)
924 |
924 |
925 o 0 (public): -1 (public) -1 (public)
925 o 0 (public): -1 (public) -1 (public)
926
926
927
927
928 Missing non-standard names give no error (backward compatibility):
928 Missing non-standard names give no error (backward compatibility):
929
929
930 $ echo "changeset = '{c}'" > t
930 $ echo "changeset = '{c}'" > t
931 $ hg log --style ./t
931 $ hg log --style ./t
932
932
933 Defining non-standard name works:
933 Defining non-standard name works:
934
934
935 $ cat <<EOF > t
935 $ cat <<EOF > t
936 > changeset = '{c}'
936 > changeset = '{c}'
937 > c = q
937 > c = q
938 > EOF
938 > EOF
939 $ hg log --style ./t
939 $ hg log --style ./t
940 8
940 8
941 7
941 7
942 6
942 6
943 5
943 5
944 4
944 4
945 3
945 3
946 2
946 2
947 1
947 1
948 0
948 0
949
949
950 ui.style works:
950 ui.style works:
951
951
952 $ echo '[ui]' > .hg/hgrc
952 $ echo '[ui]' > .hg/hgrc
953 $ echo 'style = t' >> .hg/hgrc
953 $ echo 'style = t' >> .hg/hgrc
954 $ hg log
954 $ hg log
955 8
955 8
956 7
956 7
957 6
957 6
958 5
958 5
959 4
959 4
960 3
960 3
961 2
961 2
962 1
962 1
963 0
963 0
964
964
965
965
966 Issue338:
966 Issue338:
967
967
968 $ hg log --style=changelog > changelog
968 $ hg log --style=changelog > changelog
969
969
970 $ cat changelog
970 $ cat changelog
971 2020-01-01 test <test>
971 2020-01-01 test <test>
972
972
973 * fourth, second, third:
973 * fourth, second, third:
974 third
974 third
975 [95c24699272e] [tip]
975 [95c24699272e] [tip]
976
976
977 1970-01-12 User Name <user@hostname>
977 1970-01-12 User Name <user@hostname>
978
978
979 * second:
979 * second:
980 second
980 second
981 [29114dbae42b]
981 [29114dbae42b]
982
982
983 1970-01-18 person <person>
983 1970-01-18 person <person>
984
984
985 * merge
985 * merge
986 [d41e714fe50d]
986 [d41e714fe50d]
987
987
988 * d:
988 * d:
989 new head
989 new head
990 [13207e5a10d9]
990 [13207e5a10d9]
991
991
992 1970-01-17 person <person>
992 1970-01-17 person <person>
993
993
994 * new branch
994 * new branch
995 [bbe44766e73d] <foo>
995 [bbe44766e73d] <foo>
996
996
997 1970-01-16 person <person>
997 1970-01-16 person <person>
998
998
999 * c:
999 * c:
1000 no user, no domain
1000 no user, no domain
1001 [10e46f2dcbf4]
1001 [10e46f2dcbf4]
1002
1002
1003 1970-01-14 other <other@place>
1003 1970-01-14 other <other@place>
1004
1004
1005 * c:
1005 * c:
1006 no person
1006 no person
1007 [97054abb4ab8]
1007 [97054abb4ab8]
1008
1008
1009 1970-01-13 A. N. Other <other@place>
1009 1970-01-13 A. N. Other <other@place>
1010
1010
1011 * b:
1011 * b:
1012 other 1 other 2
1012 other 1 other 2
1013
1013
1014 other 3
1014 other 3
1015 [b608e9d1a3f0]
1015 [b608e9d1a3f0]
1016
1016
1017 1970-01-12 User Name <user@hostname>
1017 1970-01-12 User Name <user@hostname>
1018
1018
1019 * a:
1019 * a:
1020 line 1 line 2
1020 line 1 line 2
1021 [1e4e1b8f71e0]
1021 [1e4e1b8f71e0]
1022
1022
1023
1023
1024 Issue2130: xml output for 'hg heads' is malformed
1024 Issue2130: xml output for 'hg heads' is malformed
1025
1025
1026 $ hg heads --style changelog
1026 $ hg heads --style changelog
1027 2020-01-01 test <test>
1027 2020-01-01 test <test>
1028
1028
1029 * fourth, second, third:
1029 * fourth, second, third:
1030 third
1030 third
1031 [95c24699272e] [tip]
1031 [95c24699272e] [tip]
1032
1032
1033 1970-01-18 person <person>
1033 1970-01-18 person <person>
1034
1034
1035 * merge
1035 * merge
1036 [d41e714fe50d]
1036 [d41e714fe50d]
1037
1037
1038 1970-01-17 person <person>
1038 1970-01-17 person <person>
1039
1039
1040 * new branch
1040 * new branch
1041 [bbe44766e73d] <foo>
1041 [bbe44766e73d] <foo>
1042
1042
1043
1043
1044 Keys work:
1044 Keys work:
1045
1045
1046 $ for key in author branch branches date desc file_adds file_dels file_mods \
1046 $ for key in author branch branches date desc file_adds file_dels file_mods \
1047 > file_copies file_copies_switch files \
1047 > file_copies file_copies_switch files \
1048 > manifest node parents rev tags diffstat extras \
1048 > manifest node parents rev tags diffstat extras \
1049 > p1rev p2rev p1node p2node; do
1049 > p1rev p2rev p1node p2node; do
1050 > for mode in '' --verbose --debug; do
1050 > for mode in '' --verbose --debug; do
1051 > hg log $mode --template "$key$mode: {$key}\n"
1051 > hg log $mode --template "$key$mode: {$key}\n"
1052 > done
1052 > done
1053 > done
1053 > done
1054 author: test
1054 author: test
1055 author: User Name <user@hostname>
1055 author: User Name <user@hostname>
1056 author: person
1056 author: person
1057 author: person
1057 author: person
1058 author: person
1058 author: person
1059 author: person
1059 author: person
1060 author: other@place
1060 author: other@place
1061 author: A. N. Other <other@place>
1061 author: A. N. Other <other@place>
1062 author: User Name <user@hostname>
1062 author: User Name <user@hostname>
1063 author--verbose: test
1063 author--verbose: test
1064 author--verbose: User Name <user@hostname>
1064 author--verbose: User Name <user@hostname>
1065 author--verbose: person
1065 author--verbose: person
1066 author--verbose: person
1066 author--verbose: person
1067 author--verbose: person
1067 author--verbose: person
1068 author--verbose: person
1068 author--verbose: person
1069 author--verbose: other@place
1069 author--verbose: other@place
1070 author--verbose: A. N. Other <other@place>
1070 author--verbose: A. N. Other <other@place>
1071 author--verbose: User Name <user@hostname>
1071 author--verbose: User Name <user@hostname>
1072 author--debug: test
1072 author--debug: test
1073 author--debug: User Name <user@hostname>
1073 author--debug: User Name <user@hostname>
1074 author--debug: person
1074 author--debug: person
1075 author--debug: person
1075 author--debug: person
1076 author--debug: person
1076 author--debug: person
1077 author--debug: person
1077 author--debug: person
1078 author--debug: other@place
1078 author--debug: other@place
1079 author--debug: A. N. Other <other@place>
1079 author--debug: A. N. Other <other@place>
1080 author--debug: User Name <user@hostname>
1080 author--debug: User Name <user@hostname>
1081 branch: default
1081 branch: default
1082 branch: default
1082 branch: default
1083 branch: default
1083 branch: default
1084 branch: default
1084 branch: default
1085 branch: foo
1085 branch: foo
1086 branch: default
1086 branch: default
1087 branch: default
1087 branch: default
1088 branch: default
1088 branch: default
1089 branch: default
1089 branch: default
1090 branch--verbose: default
1090 branch--verbose: default
1091 branch--verbose: default
1091 branch--verbose: default
1092 branch--verbose: default
1092 branch--verbose: default
1093 branch--verbose: default
1093 branch--verbose: default
1094 branch--verbose: foo
1094 branch--verbose: foo
1095 branch--verbose: default
1095 branch--verbose: default
1096 branch--verbose: default
1096 branch--verbose: default
1097 branch--verbose: default
1097 branch--verbose: default
1098 branch--verbose: default
1098 branch--verbose: default
1099 branch--debug: default
1099 branch--debug: default
1100 branch--debug: default
1100 branch--debug: default
1101 branch--debug: default
1101 branch--debug: default
1102 branch--debug: default
1102 branch--debug: default
1103 branch--debug: foo
1103 branch--debug: foo
1104 branch--debug: default
1104 branch--debug: default
1105 branch--debug: default
1105 branch--debug: default
1106 branch--debug: default
1106 branch--debug: default
1107 branch--debug: default
1107 branch--debug: default
1108 branches:
1108 branches:
1109 branches:
1109 branches:
1110 branches:
1110 branches:
1111 branches:
1111 branches:
1112 branches: foo
1112 branches: foo
1113 branches:
1113 branches:
1114 branches:
1114 branches:
1115 branches:
1115 branches:
1116 branches:
1116 branches:
1117 branches--verbose:
1117 branches--verbose:
1118 branches--verbose:
1118 branches--verbose:
1119 branches--verbose:
1119 branches--verbose:
1120 branches--verbose:
1120 branches--verbose:
1121 branches--verbose: foo
1121 branches--verbose: foo
1122 branches--verbose:
1122 branches--verbose:
1123 branches--verbose:
1123 branches--verbose:
1124 branches--verbose:
1124 branches--verbose:
1125 branches--verbose:
1125 branches--verbose:
1126 branches--debug:
1126 branches--debug:
1127 branches--debug:
1127 branches--debug:
1128 branches--debug:
1128 branches--debug:
1129 branches--debug:
1129 branches--debug:
1130 branches--debug: foo
1130 branches--debug: foo
1131 branches--debug:
1131 branches--debug:
1132 branches--debug:
1132 branches--debug:
1133 branches--debug:
1133 branches--debug:
1134 branches--debug:
1134 branches--debug:
1135 date: 1577872860.00
1135 date: 1577872860.00
1136 date: 1000000.00
1136 date: 1000000.00
1137 date: 1500001.00
1137 date: 1500001.00
1138 date: 1500000.00
1138 date: 1500000.00
1139 date: 1400000.00
1139 date: 1400000.00
1140 date: 1300000.00
1140 date: 1300000.00
1141 date: 1200000.00
1141 date: 1200000.00
1142 date: 1100000.00
1142 date: 1100000.00
1143 date: 1000000.00
1143 date: 1000000.00
1144 date--verbose: 1577872860.00
1144 date--verbose: 1577872860.00
1145 date--verbose: 1000000.00
1145 date--verbose: 1000000.00
1146 date--verbose: 1500001.00
1146 date--verbose: 1500001.00
1147 date--verbose: 1500000.00
1147 date--verbose: 1500000.00
1148 date--verbose: 1400000.00
1148 date--verbose: 1400000.00
1149 date--verbose: 1300000.00
1149 date--verbose: 1300000.00
1150 date--verbose: 1200000.00
1150 date--verbose: 1200000.00
1151 date--verbose: 1100000.00
1151 date--verbose: 1100000.00
1152 date--verbose: 1000000.00
1152 date--verbose: 1000000.00
1153 date--debug: 1577872860.00
1153 date--debug: 1577872860.00
1154 date--debug: 1000000.00
1154 date--debug: 1000000.00
1155 date--debug: 1500001.00
1155 date--debug: 1500001.00
1156 date--debug: 1500000.00
1156 date--debug: 1500000.00
1157 date--debug: 1400000.00
1157 date--debug: 1400000.00
1158 date--debug: 1300000.00
1158 date--debug: 1300000.00
1159 date--debug: 1200000.00
1159 date--debug: 1200000.00
1160 date--debug: 1100000.00
1160 date--debug: 1100000.00
1161 date--debug: 1000000.00
1161 date--debug: 1000000.00
1162 desc: third
1162 desc: third
1163 desc: second
1163 desc: second
1164 desc: merge
1164 desc: merge
1165 desc: new head
1165 desc: new head
1166 desc: new branch
1166 desc: new branch
1167 desc: no user, no domain
1167 desc: no user, no domain
1168 desc: no person
1168 desc: no person
1169 desc: other 1
1169 desc: other 1
1170 other 2
1170 other 2
1171
1171
1172 other 3
1172 other 3
1173 desc: line 1
1173 desc: line 1
1174 line 2
1174 line 2
1175 desc--verbose: third
1175 desc--verbose: third
1176 desc--verbose: second
1176 desc--verbose: second
1177 desc--verbose: merge
1177 desc--verbose: merge
1178 desc--verbose: new head
1178 desc--verbose: new head
1179 desc--verbose: new branch
1179 desc--verbose: new branch
1180 desc--verbose: no user, no domain
1180 desc--verbose: no user, no domain
1181 desc--verbose: no person
1181 desc--verbose: no person
1182 desc--verbose: other 1
1182 desc--verbose: other 1
1183 other 2
1183 other 2
1184
1184
1185 other 3
1185 other 3
1186 desc--verbose: line 1
1186 desc--verbose: line 1
1187 line 2
1187 line 2
1188 desc--debug: third
1188 desc--debug: third
1189 desc--debug: second
1189 desc--debug: second
1190 desc--debug: merge
1190 desc--debug: merge
1191 desc--debug: new head
1191 desc--debug: new head
1192 desc--debug: new branch
1192 desc--debug: new branch
1193 desc--debug: no user, no domain
1193 desc--debug: no user, no domain
1194 desc--debug: no person
1194 desc--debug: no person
1195 desc--debug: other 1
1195 desc--debug: other 1
1196 other 2
1196 other 2
1197
1197
1198 other 3
1198 other 3
1199 desc--debug: line 1
1199 desc--debug: line 1
1200 line 2
1200 line 2
1201 file_adds: fourth third
1201 file_adds: fourth third
1202 file_adds: second
1202 file_adds: second
1203 file_adds:
1203 file_adds:
1204 file_adds: d
1204 file_adds: d
1205 file_adds:
1205 file_adds:
1206 file_adds:
1206 file_adds:
1207 file_adds: c
1207 file_adds: c
1208 file_adds: b
1208 file_adds: b
1209 file_adds: a
1209 file_adds: a
1210 file_adds--verbose: fourth third
1210 file_adds--verbose: fourth third
1211 file_adds--verbose: second
1211 file_adds--verbose: second
1212 file_adds--verbose:
1212 file_adds--verbose:
1213 file_adds--verbose: d
1213 file_adds--verbose: d
1214 file_adds--verbose:
1214 file_adds--verbose:
1215 file_adds--verbose:
1215 file_adds--verbose:
1216 file_adds--verbose: c
1216 file_adds--verbose: c
1217 file_adds--verbose: b
1217 file_adds--verbose: b
1218 file_adds--verbose: a
1218 file_adds--verbose: a
1219 file_adds--debug: fourth third
1219 file_adds--debug: fourth third
1220 file_adds--debug: second
1220 file_adds--debug: second
1221 file_adds--debug:
1221 file_adds--debug:
1222 file_adds--debug: d
1222 file_adds--debug: d
1223 file_adds--debug:
1223 file_adds--debug:
1224 file_adds--debug:
1224 file_adds--debug:
1225 file_adds--debug: c
1225 file_adds--debug: c
1226 file_adds--debug: b
1226 file_adds--debug: b
1227 file_adds--debug: a
1227 file_adds--debug: a
1228 file_dels: second
1228 file_dels: second
1229 file_dels:
1229 file_dels:
1230 file_dels:
1230 file_dels:
1231 file_dels:
1231 file_dels:
1232 file_dels:
1232 file_dels:
1233 file_dels:
1233 file_dels:
1234 file_dels:
1234 file_dels:
1235 file_dels:
1235 file_dels:
1236 file_dels:
1236 file_dels:
1237 file_dels--verbose: second
1237 file_dels--verbose: second
1238 file_dels--verbose:
1238 file_dels--verbose:
1239 file_dels--verbose:
1239 file_dels--verbose:
1240 file_dels--verbose:
1240 file_dels--verbose:
1241 file_dels--verbose:
1241 file_dels--verbose:
1242 file_dels--verbose:
1242 file_dels--verbose:
1243 file_dels--verbose:
1243 file_dels--verbose:
1244 file_dels--verbose:
1244 file_dels--verbose:
1245 file_dels--verbose:
1245 file_dels--verbose:
1246 file_dels--debug: second
1246 file_dels--debug: second
1247 file_dels--debug:
1247 file_dels--debug:
1248 file_dels--debug:
1248 file_dels--debug:
1249 file_dels--debug:
1249 file_dels--debug:
1250 file_dels--debug:
1250 file_dels--debug:
1251 file_dels--debug:
1251 file_dels--debug:
1252 file_dels--debug:
1252 file_dels--debug:
1253 file_dels--debug:
1253 file_dels--debug:
1254 file_dels--debug:
1254 file_dels--debug:
1255 file_mods:
1255 file_mods:
1256 file_mods:
1256 file_mods:
1257 file_mods:
1257 file_mods:
1258 file_mods:
1258 file_mods:
1259 file_mods:
1259 file_mods:
1260 file_mods: c
1260 file_mods: c
1261 file_mods:
1261 file_mods:
1262 file_mods:
1262 file_mods:
1263 file_mods:
1263 file_mods:
1264 file_mods--verbose:
1264 file_mods--verbose:
1265 file_mods--verbose:
1265 file_mods--verbose:
1266 file_mods--verbose:
1266 file_mods--verbose:
1267 file_mods--verbose:
1267 file_mods--verbose:
1268 file_mods--verbose:
1268 file_mods--verbose:
1269 file_mods--verbose: c
1269 file_mods--verbose: c
1270 file_mods--verbose:
1270 file_mods--verbose:
1271 file_mods--verbose:
1271 file_mods--verbose:
1272 file_mods--verbose:
1272 file_mods--verbose:
1273 file_mods--debug:
1273 file_mods--debug:
1274 file_mods--debug:
1274 file_mods--debug:
1275 file_mods--debug:
1275 file_mods--debug:
1276 file_mods--debug:
1276 file_mods--debug:
1277 file_mods--debug:
1277 file_mods--debug:
1278 file_mods--debug: c
1278 file_mods--debug: c
1279 file_mods--debug:
1279 file_mods--debug:
1280 file_mods--debug:
1280 file_mods--debug:
1281 file_mods--debug:
1281 file_mods--debug:
1282 file_copies: fourth (second)
1282 file_copies: fourth (second)
1283 file_copies:
1283 file_copies:
1284 file_copies:
1284 file_copies:
1285 file_copies:
1285 file_copies:
1286 file_copies:
1286 file_copies:
1287 file_copies:
1287 file_copies:
1288 file_copies:
1288 file_copies:
1289 file_copies:
1289 file_copies:
1290 file_copies:
1290 file_copies:
1291 file_copies--verbose: fourth (second)
1291 file_copies--verbose: fourth (second)
1292 file_copies--verbose:
1292 file_copies--verbose:
1293 file_copies--verbose:
1293 file_copies--verbose:
1294 file_copies--verbose:
1294 file_copies--verbose:
1295 file_copies--verbose:
1295 file_copies--verbose:
1296 file_copies--verbose:
1296 file_copies--verbose:
1297 file_copies--verbose:
1297 file_copies--verbose:
1298 file_copies--verbose:
1298 file_copies--verbose:
1299 file_copies--verbose:
1299 file_copies--verbose:
1300 file_copies--debug: fourth (second)
1300 file_copies--debug: fourth (second)
1301 file_copies--debug:
1301 file_copies--debug:
1302 file_copies--debug:
1302 file_copies--debug:
1303 file_copies--debug:
1303 file_copies--debug:
1304 file_copies--debug:
1304 file_copies--debug:
1305 file_copies--debug:
1305 file_copies--debug:
1306 file_copies--debug:
1306 file_copies--debug:
1307 file_copies--debug:
1307 file_copies--debug:
1308 file_copies--debug:
1308 file_copies--debug:
1309 file_copies_switch:
1309 file_copies_switch:
1310 file_copies_switch:
1310 file_copies_switch:
1311 file_copies_switch:
1311 file_copies_switch:
1312 file_copies_switch:
1312 file_copies_switch:
1313 file_copies_switch:
1313 file_copies_switch:
1314 file_copies_switch:
1314 file_copies_switch:
1315 file_copies_switch:
1315 file_copies_switch:
1316 file_copies_switch:
1316 file_copies_switch:
1317 file_copies_switch:
1317 file_copies_switch:
1318 file_copies_switch--verbose:
1318 file_copies_switch--verbose:
1319 file_copies_switch--verbose:
1319 file_copies_switch--verbose:
1320 file_copies_switch--verbose:
1320 file_copies_switch--verbose:
1321 file_copies_switch--verbose:
1321 file_copies_switch--verbose:
1322 file_copies_switch--verbose:
1322 file_copies_switch--verbose:
1323 file_copies_switch--verbose:
1323 file_copies_switch--verbose:
1324 file_copies_switch--verbose:
1324 file_copies_switch--verbose:
1325 file_copies_switch--verbose:
1325 file_copies_switch--verbose:
1326 file_copies_switch--verbose:
1326 file_copies_switch--verbose:
1327 file_copies_switch--debug:
1327 file_copies_switch--debug:
1328 file_copies_switch--debug:
1328 file_copies_switch--debug:
1329 file_copies_switch--debug:
1329 file_copies_switch--debug:
1330 file_copies_switch--debug:
1330 file_copies_switch--debug:
1331 file_copies_switch--debug:
1331 file_copies_switch--debug:
1332 file_copies_switch--debug:
1332 file_copies_switch--debug:
1333 file_copies_switch--debug:
1333 file_copies_switch--debug:
1334 file_copies_switch--debug:
1334 file_copies_switch--debug:
1335 file_copies_switch--debug:
1335 file_copies_switch--debug:
1336 files: fourth second third
1336 files: fourth second third
1337 files: second
1337 files: second
1338 files:
1338 files:
1339 files: d
1339 files: d
1340 files:
1340 files:
1341 files: c
1341 files: c
1342 files: c
1342 files: c
1343 files: b
1343 files: b
1344 files: a
1344 files: a
1345 files--verbose: fourth second third
1345 files--verbose: fourth second third
1346 files--verbose: second
1346 files--verbose: second
1347 files--verbose:
1347 files--verbose:
1348 files--verbose: d
1348 files--verbose: d
1349 files--verbose:
1349 files--verbose:
1350 files--verbose: c
1350 files--verbose: c
1351 files--verbose: c
1351 files--verbose: c
1352 files--verbose: b
1352 files--verbose: b
1353 files--verbose: a
1353 files--verbose: a
1354 files--debug: fourth second third
1354 files--debug: fourth second third
1355 files--debug: second
1355 files--debug: second
1356 files--debug:
1356 files--debug:
1357 files--debug: d
1357 files--debug: d
1358 files--debug:
1358 files--debug:
1359 files--debug: c
1359 files--debug: c
1360 files--debug: c
1360 files--debug: c
1361 files--debug: b
1361 files--debug: b
1362 files--debug: a
1362 files--debug: a
1363 manifest: 6:94961b75a2da
1363 manifest: 6:94961b75a2da
1364 manifest: 5:f2dbc354b94e
1364 manifest: 5:f2dbc354b94e
1365 manifest: 4:4dc3def4f9b4
1365 manifest: 4:4dc3def4f9b4
1366 manifest: 4:4dc3def4f9b4
1366 manifest: 4:4dc3def4f9b4
1367 manifest: 3:cb5a1327723b
1367 manifest: 3:cb5a1327723b
1368 manifest: 3:cb5a1327723b
1368 manifest: 3:cb5a1327723b
1369 manifest: 2:6e0e82995c35
1369 manifest: 2:6e0e82995c35
1370 manifest: 1:4e8d705b1e53
1370 manifest: 1:4e8d705b1e53
1371 manifest: 0:a0c8bcbbb45c
1371 manifest: 0:a0c8bcbbb45c
1372 manifest--verbose: 6:94961b75a2da
1372 manifest--verbose: 6:94961b75a2da
1373 manifest--verbose: 5:f2dbc354b94e
1373 manifest--verbose: 5:f2dbc354b94e
1374 manifest--verbose: 4:4dc3def4f9b4
1374 manifest--verbose: 4:4dc3def4f9b4
1375 manifest--verbose: 4:4dc3def4f9b4
1375 manifest--verbose: 4:4dc3def4f9b4
1376 manifest--verbose: 3:cb5a1327723b
1376 manifest--verbose: 3:cb5a1327723b
1377 manifest--verbose: 3:cb5a1327723b
1377 manifest--verbose: 3:cb5a1327723b
1378 manifest--verbose: 2:6e0e82995c35
1378 manifest--verbose: 2:6e0e82995c35
1379 manifest--verbose: 1:4e8d705b1e53
1379 manifest--verbose: 1:4e8d705b1e53
1380 manifest--verbose: 0:a0c8bcbbb45c
1380 manifest--verbose: 0:a0c8bcbbb45c
1381 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1381 manifest--debug: 6:94961b75a2da554b4df6fb599e5bfc7d48de0c64
1382 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1382 manifest--debug: 5:f2dbc354b94e5ec0b4f10680ee0cee816101d0bf
1383 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1383 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1384 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1384 manifest--debug: 4:4dc3def4f9b4c6e8de820f6ee74737f91e96a216
1385 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1385 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1386 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1386 manifest--debug: 3:cb5a1327723bada42f117e4c55a303246eaf9ccc
1387 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1387 manifest--debug: 2:6e0e82995c35d0d57a52aca8da4e56139e06b4b1
1388 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1388 manifest--debug: 1:4e8d705b1e53e3f9375e0e60dc7b525d8211fe55
1389 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1389 manifest--debug: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
1390 node: 95c24699272ef57d062b8bccc32c878bf841784a
1390 node: 95c24699272ef57d062b8bccc32c878bf841784a
1391 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1391 node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1392 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1392 node: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1393 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1393 node: 13207e5a10d9fd28ec424934298e176197f2c67f
1394 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1394 node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1395 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1395 node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1396 node: 97054abb4ab824450e9164180baf491ae0078465
1396 node: 97054abb4ab824450e9164180baf491ae0078465
1397 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1397 node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1398 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1398 node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1399 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1399 node--verbose: 95c24699272ef57d062b8bccc32c878bf841784a
1400 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1400 node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1401 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1401 node--verbose: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1402 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1402 node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1403 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1403 node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1404 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1404 node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1405 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1405 node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1406 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1406 node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1407 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1407 node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1408 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1408 node--debug: 95c24699272ef57d062b8bccc32c878bf841784a
1409 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1409 node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1410 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1410 node--debug: d41e714fe50d9e4a5f11b4d595d543481b5f980b
1411 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1411 node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1412 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1412 node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1413 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1413 node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1414 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1414 node--debug: 97054abb4ab824450e9164180baf491ae0078465
1415 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1415 node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1416 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1416 node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1417 parents:
1417 parents:
1418 parents: -1:000000000000
1418 parents: -1:000000000000
1419 parents: 5:13207e5a10d9 4:bbe44766e73d
1419 parents: 5:13207e5a10d9 4:bbe44766e73d
1420 parents: 3:10e46f2dcbf4
1420 parents: 3:10e46f2dcbf4
1421 parents:
1421 parents:
1422 parents:
1422 parents:
1423 parents:
1423 parents:
1424 parents:
1424 parents:
1425 parents:
1425 parents:
1426 parents--verbose:
1426 parents--verbose:
1427 parents--verbose: -1:000000000000
1427 parents--verbose: -1:000000000000
1428 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1428 parents--verbose: 5:13207e5a10d9 4:bbe44766e73d
1429 parents--verbose: 3:10e46f2dcbf4
1429 parents--verbose: 3:10e46f2dcbf4
1430 parents--verbose:
1430 parents--verbose:
1431 parents--verbose:
1431 parents--verbose:
1432 parents--verbose:
1432 parents--verbose:
1433 parents--verbose:
1433 parents--verbose:
1434 parents--verbose:
1434 parents--verbose:
1435 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1435 parents--debug: 7:29114dbae42b9f078cf2714dbe3a86bba8ec7453 -1:0000000000000000000000000000000000000000
1436 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1436 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1437 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1437 parents--debug: 5:13207e5a10d9fd28ec424934298e176197f2c67f 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74
1438 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1438 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1439 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1439 parents--debug: 3:10e46f2dcbf4823578cf180f33ecf0b957964c47 -1:0000000000000000000000000000000000000000
1440 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1440 parents--debug: 2:97054abb4ab824450e9164180baf491ae0078465 -1:0000000000000000000000000000000000000000
1441 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1441 parents--debug: 1:b608e9d1a3f0273ccf70fb85fd6866b3482bf965 -1:0000000000000000000000000000000000000000
1442 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1442 parents--debug: 0:1e4e1b8f71e05681d422154f5421e385fec3454f -1:0000000000000000000000000000000000000000
1443 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1443 parents--debug: -1:0000000000000000000000000000000000000000 -1:0000000000000000000000000000000000000000
1444 rev: 8
1444 rev: 8
1445 rev: 7
1445 rev: 7
1446 rev: 6
1446 rev: 6
1447 rev: 5
1447 rev: 5
1448 rev: 4
1448 rev: 4
1449 rev: 3
1449 rev: 3
1450 rev: 2
1450 rev: 2
1451 rev: 1
1451 rev: 1
1452 rev: 0
1452 rev: 0
1453 rev--verbose: 8
1453 rev--verbose: 8
1454 rev--verbose: 7
1454 rev--verbose: 7
1455 rev--verbose: 6
1455 rev--verbose: 6
1456 rev--verbose: 5
1456 rev--verbose: 5
1457 rev--verbose: 4
1457 rev--verbose: 4
1458 rev--verbose: 3
1458 rev--verbose: 3
1459 rev--verbose: 2
1459 rev--verbose: 2
1460 rev--verbose: 1
1460 rev--verbose: 1
1461 rev--verbose: 0
1461 rev--verbose: 0
1462 rev--debug: 8
1462 rev--debug: 8
1463 rev--debug: 7
1463 rev--debug: 7
1464 rev--debug: 6
1464 rev--debug: 6
1465 rev--debug: 5
1465 rev--debug: 5
1466 rev--debug: 4
1466 rev--debug: 4
1467 rev--debug: 3
1467 rev--debug: 3
1468 rev--debug: 2
1468 rev--debug: 2
1469 rev--debug: 1
1469 rev--debug: 1
1470 rev--debug: 0
1470 rev--debug: 0
1471 tags: tip
1471 tags: tip
1472 tags:
1472 tags:
1473 tags:
1473 tags:
1474 tags:
1474 tags:
1475 tags:
1475 tags:
1476 tags:
1476 tags:
1477 tags:
1477 tags:
1478 tags:
1478 tags:
1479 tags:
1479 tags:
1480 tags--verbose: tip
1480 tags--verbose: tip
1481 tags--verbose:
1481 tags--verbose:
1482 tags--verbose:
1482 tags--verbose:
1483 tags--verbose:
1483 tags--verbose:
1484 tags--verbose:
1484 tags--verbose:
1485 tags--verbose:
1485 tags--verbose:
1486 tags--verbose:
1486 tags--verbose:
1487 tags--verbose:
1487 tags--verbose:
1488 tags--verbose:
1488 tags--verbose:
1489 tags--debug: tip
1489 tags--debug: tip
1490 tags--debug:
1490 tags--debug:
1491 tags--debug:
1491 tags--debug:
1492 tags--debug:
1492 tags--debug:
1493 tags--debug:
1493 tags--debug:
1494 tags--debug:
1494 tags--debug:
1495 tags--debug:
1495 tags--debug:
1496 tags--debug:
1496 tags--debug:
1497 tags--debug:
1497 tags--debug:
1498 diffstat: 3: +2/-1
1498 diffstat: 3: +2/-1
1499 diffstat: 1: +1/-0
1499 diffstat: 1: +1/-0
1500 diffstat: 0: +0/-0
1500 diffstat: 0: +0/-0
1501 diffstat: 1: +1/-0
1501 diffstat: 1: +1/-0
1502 diffstat: 0: +0/-0
1502 diffstat: 0: +0/-0
1503 diffstat: 1: +1/-0
1503 diffstat: 1: +1/-0
1504 diffstat: 1: +4/-0
1504 diffstat: 1: +4/-0
1505 diffstat: 1: +2/-0
1505 diffstat: 1: +2/-0
1506 diffstat: 1: +1/-0
1506 diffstat: 1: +1/-0
1507 diffstat--verbose: 3: +2/-1
1507 diffstat--verbose: 3: +2/-1
1508 diffstat--verbose: 1: +1/-0
1508 diffstat--verbose: 1: +1/-0
1509 diffstat--verbose: 0: +0/-0
1509 diffstat--verbose: 0: +0/-0
1510 diffstat--verbose: 1: +1/-0
1510 diffstat--verbose: 1: +1/-0
1511 diffstat--verbose: 0: +0/-0
1511 diffstat--verbose: 0: +0/-0
1512 diffstat--verbose: 1: +1/-0
1512 diffstat--verbose: 1: +1/-0
1513 diffstat--verbose: 1: +4/-0
1513 diffstat--verbose: 1: +4/-0
1514 diffstat--verbose: 1: +2/-0
1514 diffstat--verbose: 1: +2/-0
1515 diffstat--verbose: 1: +1/-0
1515 diffstat--verbose: 1: +1/-0
1516 diffstat--debug: 3: +2/-1
1516 diffstat--debug: 3: +2/-1
1517 diffstat--debug: 1: +1/-0
1517 diffstat--debug: 1: +1/-0
1518 diffstat--debug: 0: +0/-0
1518 diffstat--debug: 0: +0/-0
1519 diffstat--debug: 1: +1/-0
1519 diffstat--debug: 1: +1/-0
1520 diffstat--debug: 0: +0/-0
1520 diffstat--debug: 0: +0/-0
1521 diffstat--debug: 1: +1/-0
1521 diffstat--debug: 1: +1/-0
1522 diffstat--debug: 1: +4/-0
1522 diffstat--debug: 1: +4/-0
1523 diffstat--debug: 1: +2/-0
1523 diffstat--debug: 1: +2/-0
1524 diffstat--debug: 1: +1/-0
1524 diffstat--debug: 1: +1/-0
1525 extras: branch=default
1525 extras: branch=default
1526 extras: branch=default
1526 extras: branch=default
1527 extras: branch=default
1527 extras: branch=default
1528 extras: branch=default
1528 extras: branch=default
1529 extras: branch=foo
1529 extras: branch=foo
1530 extras: branch=default
1530 extras: branch=default
1531 extras: branch=default
1531 extras: branch=default
1532 extras: branch=default
1532 extras: branch=default
1533 extras: branch=default
1533 extras: branch=default
1534 extras--verbose: branch=default
1534 extras--verbose: branch=default
1535 extras--verbose: branch=default
1535 extras--verbose: branch=default
1536 extras--verbose: branch=default
1536 extras--verbose: branch=default
1537 extras--verbose: branch=default
1537 extras--verbose: branch=default
1538 extras--verbose: branch=foo
1538 extras--verbose: branch=foo
1539 extras--verbose: branch=default
1539 extras--verbose: branch=default
1540 extras--verbose: branch=default
1540 extras--verbose: branch=default
1541 extras--verbose: branch=default
1541 extras--verbose: branch=default
1542 extras--verbose: branch=default
1542 extras--verbose: branch=default
1543 extras--debug: branch=default
1543 extras--debug: branch=default
1544 extras--debug: branch=default
1544 extras--debug: branch=default
1545 extras--debug: branch=default
1545 extras--debug: branch=default
1546 extras--debug: branch=default
1546 extras--debug: branch=default
1547 extras--debug: branch=foo
1547 extras--debug: branch=foo
1548 extras--debug: branch=default
1548 extras--debug: branch=default
1549 extras--debug: branch=default
1549 extras--debug: branch=default
1550 extras--debug: branch=default
1550 extras--debug: branch=default
1551 extras--debug: branch=default
1551 extras--debug: branch=default
1552 p1rev: 7
1552 p1rev: 7
1553 p1rev: -1
1553 p1rev: -1
1554 p1rev: 5
1554 p1rev: 5
1555 p1rev: 3
1555 p1rev: 3
1556 p1rev: 3
1556 p1rev: 3
1557 p1rev: 2
1557 p1rev: 2
1558 p1rev: 1
1558 p1rev: 1
1559 p1rev: 0
1559 p1rev: 0
1560 p1rev: -1
1560 p1rev: -1
1561 p1rev--verbose: 7
1561 p1rev--verbose: 7
1562 p1rev--verbose: -1
1562 p1rev--verbose: -1
1563 p1rev--verbose: 5
1563 p1rev--verbose: 5
1564 p1rev--verbose: 3
1564 p1rev--verbose: 3
1565 p1rev--verbose: 3
1565 p1rev--verbose: 3
1566 p1rev--verbose: 2
1566 p1rev--verbose: 2
1567 p1rev--verbose: 1
1567 p1rev--verbose: 1
1568 p1rev--verbose: 0
1568 p1rev--verbose: 0
1569 p1rev--verbose: -1
1569 p1rev--verbose: -1
1570 p1rev--debug: 7
1570 p1rev--debug: 7
1571 p1rev--debug: -1
1571 p1rev--debug: -1
1572 p1rev--debug: 5
1572 p1rev--debug: 5
1573 p1rev--debug: 3
1573 p1rev--debug: 3
1574 p1rev--debug: 3
1574 p1rev--debug: 3
1575 p1rev--debug: 2
1575 p1rev--debug: 2
1576 p1rev--debug: 1
1576 p1rev--debug: 1
1577 p1rev--debug: 0
1577 p1rev--debug: 0
1578 p1rev--debug: -1
1578 p1rev--debug: -1
1579 p2rev: -1
1579 p2rev: -1
1580 p2rev: -1
1580 p2rev: -1
1581 p2rev: 4
1581 p2rev: 4
1582 p2rev: -1
1582 p2rev: -1
1583 p2rev: -1
1583 p2rev: -1
1584 p2rev: -1
1584 p2rev: -1
1585 p2rev: -1
1585 p2rev: -1
1586 p2rev: -1
1586 p2rev: -1
1587 p2rev: -1
1587 p2rev: -1
1588 p2rev--verbose: -1
1588 p2rev--verbose: -1
1589 p2rev--verbose: -1
1589 p2rev--verbose: -1
1590 p2rev--verbose: 4
1590 p2rev--verbose: 4
1591 p2rev--verbose: -1
1591 p2rev--verbose: -1
1592 p2rev--verbose: -1
1592 p2rev--verbose: -1
1593 p2rev--verbose: -1
1593 p2rev--verbose: -1
1594 p2rev--verbose: -1
1594 p2rev--verbose: -1
1595 p2rev--verbose: -1
1595 p2rev--verbose: -1
1596 p2rev--verbose: -1
1596 p2rev--verbose: -1
1597 p2rev--debug: -1
1597 p2rev--debug: -1
1598 p2rev--debug: -1
1598 p2rev--debug: -1
1599 p2rev--debug: 4
1599 p2rev--debug: 4
1600 p2rev--debug: -1
1600 p2rev--debug: -1
1601 p2rev--debug: -1
1601 p2rev--debug: -1
1602 p2rev--debug: -1
1602 p2rev--debug: -1
1603 p2rev--debug: -1
1603 p2rev--debug: -1
1604 p2rev--debug: -1
1604 p2rev--debug: -1
1605 p2rev--debug: -1
1605 p2rev--debug: -1
1606 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1606 p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1607 p1node: 0000000000000000000000000000000000000000
1607 p1node: 0000000000000000000000000000000000000000
1608 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1608 p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
1609 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1609 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1610 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1610 p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1611 p1node: 97054abb4ab824450e9164180baf491ae0078465
1611 p1node: 97054abb4ab824450e9164180baf491ae0078465
1612 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1612 p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1613 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1613 p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
1614 p1node: 0000000000000000000000000000000000000000
1614 p1node: 0000000000000000000000000000000000000000
1615 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1615 p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1616 p1node--verbose: 0000000000000000000000000000000000000000
1616 p1node--verbose: 0000000000000000000000000000000000000000
1617 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1617 p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
1618 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1618 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1619 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1619 p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1620 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1620 p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
1621 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1621 p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1622 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1622 p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
1623 p1node--verbose: 0000000000000000000000000000000000000000
1623 p1node--verbose: 0000000000000000000000000000000000000000
1624 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1624 p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
1625 p1node--debug: 0000000000000000000000000000000000000000
1625 p1node--debug: 0000000000000000000000000000000000000000
1626 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1626 p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
1627 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1627 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1628 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1628 p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
1629 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1629 p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
1630 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1630 p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
1631 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1631 p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
1632 p1node--debug: 0000000000000000000000000000000000000000
1632 p1node--debug: 0000000000000000000000000000000000000000
1633 p2node: 0000000000000000000000000000000000000000
1633 p2node: 0000000000000000000000000000000000000000
1634 p2node: 0000000000000000000000000000000000000000
1634 p2node: 0000000000000000000000000000000000000000
1635 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1635 p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1636 p2node: 0000000000000000000000000000000000000000
1636 p2node: 0000000000000000000000000000000000000000
1637 p2node: 0000000000000000000000000000000000000000
1637 p2node: 0000000000000000000000000000000000000000
1638 p2node: 0000000000000000000000000000000000000000
1638 p2node: 0000000000000000000000000000000000000000
1639 p2node: 0000000000000000000000000000000000000000
1639 p2node: 0000000000000000000000000000000000000000
1640 p2node: 0000000000000000000000000000000000000000
1640 p2node: 0000000000000000000000000000000000000000
1641 p2node: 0000000000000000000000000000000000000000
1641 p2node: 0000000000000000000000000000000000000000
1642 p2node--verbose: 0000000000000000000000000000000000000000
1642 p2node--verbose: 0000000000000000000000000000000000000000
1643 p2node--verbose: 0000000000000000000000000000000000000000
1643 p2node--verbose: 0000000000000000000000000000000000000000
1644 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1644 p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1645 p2node--verbose: 0000000000000000000000000000000000000000
1645 p2node--verbose: 0000000000000000000000000000000000000000
1646 p2node--verbose: 0000000000000000000000000000000000000000
1646 p2node--verbose: 0000000000000000000000000000000000000000
1647 p2node--verbose: 0000000000000000000000000000000000000000
1647 p2node--verbose: 0000000000000000000000000000000000000000
1648 p2node--verbose: 0000000000000000000000000000000000000000
1648 p2node--verbose: 0000000000000000000000000000000000000000
1649 p2node--verbose: 0000000000000000000000000000000000000000
1649 p2node--verbose: 0000000000000000000000000000000000000000
1650 p2node--verbose: 0000000000000000000000000000000000000000
1650 p2node--verbose: 0000000000000000000000000000000000000000
1651 p2node--debug: 0000000000000000000000000000000000000000
1651 p2node--debug: 0000000000000000000000000000000000000000
1652 p2node--debug: 0000000000000000000000000000000000000000
1652 p2node--debug: 0000000000000000000000000000000000000000
1653 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1653 p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
1654 p2node--debug: 0000000000000000000000000000000000000000
1654 p2node--debug: 0000000000000000000000000000000000000000
1655 p2node--debug: 0000000000000000000000000000000000000000
1655 p2node--debug: 0000000000000000000000000000000000000000
1656 p2node--debug: 0000000000000000000000000000000000000000
1656 p2node--debug: 0000000000000000000000000000000000000000
1657 p2node--debug: 0000000000000000000000000000000000000000
1657 p2node--debug: 0000000000000000000000000000000000000000
1658 p2node--debug: 0000000000000000000000000000000000000000
1658 p2node--debug: 0000000000000000000000000000000000000000
1659 p2node--debug: 0000000000000000000000000000000000000000
1659 p2node--debug: 0000000000000000000000000000000000000000
1660
1660
1661 Filters work:
1661 Filters work:
1662
1662
1663 $ hg log --template '{author|domain}\n'
1663 $ hg log --template '{author|domain}\n'
1664
1664
1665 hostname
1665 hostname
1666
1666
1667
1667
1668
1668
1669
1669
1670 place
1670 place
1671 place
1671 place
1672 hostname
1672 hostname
1673
1673
1674 $ hg log --template '{author|person}\n'
1674 $ hg log --template '{author|person}\n'
1675 test
1675 test
1676 User Name
1676 User Name
1677 person
1677 person
1678 person
1678 person
1679 person
1679 person
1680 person
1680 person
1681 other
1681 other
1682 A. N. Other
1682 A. N. Other
1683 User Name
1683 User Name
1684
1684
1685 $ hg log --template '{author|user}\n'
1685 $ hg log --template '{author|user}\n'
1686 test
1686 test
1687 user
1687 user
1688 person
1688 person
1689 person
1689 person
1690 person
1690 person
1691 person
1691 person
1692 other
1692 other
1693 other
1693 other
1694 user
1694 user
1695
1695
1696 $ hg log --template '{date|date}\n'
1696 $ hg log --template '{date|date}\n'
1697 Wed Jan 01 10:01:00 2020 +0000
1697 Wed Jan 01 10:01:00 2020 +0000
1698 Mon Jan 12 13:46:40 1970 +0000
1698 Mon Jan 12 13:46:40 1970 +0000
1699 Sun Jan 18 08:40:01 1970 +0000
1699 Sun Jan 18 08:40:01 1970 +0000
1700 Sun Jan 18 08:40:00 1970 +0000
1700 Sun Jan 18 08:40:00 1970 +0000
1701 Sat Jan 17 04:53:20 1970 +0000
1701 Sat Jan 17 04:53:20 1970 +0000
1702 Fri Jan 16 01:06:40 1970 +0000
1702 Fri Jan 16 01:06:40 1970 +0000
1703 Wed Jan 14 21:20:00 1970 +0000
1703 Wed Jan 14 21:20:00 1970 +0000
1704 Tue Jan 13 17:33:20 1970 +0000
1704 Tue Jan 13 17:33:20 1970 +0000
1705 Mon Jan 12 13:46:40 1970 +0000
1705 Mon Jan 12 13:46:40 1970 +0000
1706
1706
1707 $ hg log --template '{date|isodate}\n'
1707 $ hg log --template '{date|isodate}\n'
1708 2020-01-01 10:01 +0000
1708 2020-01-01 10:01 +0000
1709 1970-01-12 13:46 +0000
1709 1970-01-12 13:46 +0000
1710 1970-01-18 08:40 +0000
1710 1970-01-18 08:40 +0000
1711 1970-01-18 08:40 +0000
1711 1970-01-18 08:40 +0000
1712 1970-01-17 04:53 +0000
1712 1970-01-17 04:53 +0000
1713 1970-01-16 01:06 +0000
1713 1970-01-16 01:06 +0000
1714 1970-01-14 21:20 +0000
1714 1970-01-14 21:20 +0000
1715 1970-01-13 17:33 +0000
1715 1970-01-13 17:33 +0000
1716 1970-01-12 13:46 +0000
1716 1970-01-12 13:46 +0000
1717
1717
1718 $ hg log --template '{date|isodatesec}\n'
1718 $ hg log --template '{date|isodatesec}\n'
1719 2020-01-01 10:01:00 +0000
1719 2020-01-01 10:01:00 +0000
1720 1970-01-12 13:46:40 +0000
1720 1970-01-12 13:46:40 +0000
1721 1970-01-18 08:40:01 +0000
1721 1970-01-18 08:40:01 +0000
1722 1970-01-18 08:40:00 +0000
1722 1970-01-18 08:40:00 +0000
1723 1970-01-17 04:53:20 +0000
1723 1970-01-17 04:53:20 +0000
1724 1970-01-16 01:06:40 +0000
1724 1970-01-16 01:06:40 +0000
1725 1970-01-14 21:20:00 +0000
1725 1970-01-14 21:20:00 +0000
1726 1970-01-13 17:33:20 +0000
1726 1970-01-13 17:33:20 +0000
1727 1970-01-12 13:46:40 +0000
1727 1970-01-12 13:46:40 +0000
1728
1728
1729 $ hg log --template '{date|rfc822date}\n'
1729 $ hg log --template '{date|rfc822date}\n'
1730 Wed, 01 Jan 2020 10:01:00 +0000
1730 Wed, 01 Jan 2020 10:01:00 +0000
1731 Mon, 12 Jan 1970 13:46:40 +0000
1731 Mon, 12 Jan 1970 13:46:40 +0000
1732 Sun, 18 Jan 1970 08:40:01 +0000
1732 Sun, 18 Jan 1970 08:40:01 +0000
1733 Sun, 18 Jan 1970 08:40:00 +0000
1733 Sun, 18 Jan 1970 08:40:00 +0000
1734 Sat, 17 Jan 1970 04:53:20 +0000
1734 Sat, 17 Jan 1970 04:53:20 +0000
1735 Fri, 16 Jan 1970 01:06:40 +0000
1735 Fri, 16 Jan 1970 01:06:40 +0000
1736 Wed, 14 Jan 1970 21:20:00 +0000
1736 Wed, 14 Jan 1970 21:20:00 +0000
1737 Tue, 13 Jan 1970 17:33:20 +0000
1737 Tue, 13 Jan 1970 17:33:20 +0000
1738 Mon, 12 Jan 1970 13:46:40 +0000
1738 Mon, 12 Jan 1970 13:46:40 +0000
1739
1739
1740 $ hg log --template '{desc|firstline}\n'
1740 $ hg log --template '{desc|firstline}\n'
1741 third
1741 third
1742 second
1742 second
1743 merge
1743 merge
1744 new head
1744 new head
1745 new branch
1745 new branch
1746 no user, no domain
1746 no user, no domain
1747 no person
1747 no person
1748 other 1
1748 other 1
1749 line 1
1749 line 1
1750
1750
1751 $ hg log --template '{node|short}\n'
1751 $ hg log --template '{node|short}\n'
1752 95c24699272e
1752 95c24699272e
1753 29114dbae42b
1753 29114dbae42b
1754 d41e714fe50d
1754 d41e714fe50d
1755 13207e5a10d9
1755 13207e5a10d9
1756 bbe44766e73d
1756 bbe44766e73d
1757 10e46f2dcbf4
1757 10e46f2dcbf4
1758 97054abb4ab8
1758 97054abb4ab8
1759 b608e9d1a3f0
1759 b608e9d1a3f0
1760 1e4e1b8f71e0
1760 1e4e1b8f71e0
1761
1761
1762 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1762 $ hg log --template '<changeset author="{author|xmlescape}"/>\n'
1763 <changeset author="test"/>
1763 <changeset author="test"/>
1764 <changeset author="User Name &lt;user@hostname&gt;"/>
1764 <changeset author="User Name &lt;user@hostname&gt;"/>
1765 <changeset author="person"/>
1765 <changeset author="person"/>
1766 <changeset author="person"/>
1766 <changeset author="person"/>
1767 <changeset author="person"/>
1767 <changeset author="person"/>
1768 <changeset author="person"/>
1768 <changeset author="person"/>
1769 <changeset author="other@place"/>
1769 <changeset author="other@place"/>
1770 <changeset author="A. N. Other &lt;other@place&gt;"/>
1770 <changeset author="A. N. Other &lt;other@place&gt;"/>
1771 <changeset author="User Name &lt;user@hostname&gt;"/>
1771 <changeset author="User Name &lt;user@hostname&gt;"/>
1772
1772
1773 $ hg log --template '{rev}: {children}\n'
1773 $ hg log --template '{rev}: {children}\n'
1774 8:
1774 8:
1775 7: 8:95c24699272e
1775 7: 8:95c24699272e
1776 6:
1776 6:
1777 5: 6:d41e714fe50d
1777 5: 6:d41e714fe50d
1778 4: 6:d41e714fe50d
1778 4: 6:d41e714fe50d
1779 3: 4:bbe44766e73d 5:13207e5a10d9
1779 3: 4:bbe44766e73d 5:13207e5a10d9
1780 2: 3:10e46f2dcbf4
1780 2: 3:10e46f2dcbf4
1781 1: 2:97054abb4ab8
1781 1: 2:97054abb4ab8
1782 0: 1:b608e9d1a3f0
1782 0: 1:b608e9d1a3f0
1783
1783
1784 Formatnode filter works:
1784 Formatnode filter works:
1785
1785
1786 $ hg -q log -r 0 --template '{node|formatnode}\n'
1786 $ hg -q log -r 0 --template '{node|formatnode}\n'
1787 1e4e1b8f71e0
1787 1e4e1b8f71e0
1788
1788
1789 $ hg log -r 0 --template '{node|formatnode}\n'
1789 $ hg log -r 0 --template '{node|formatnode}\n'
1790 1e4e1b8f71e0
1790 1e4e1b8f71e0
1791
1791
1792 $ hg -v log -r 0 --template '{node|formatnode}\n'
1792 $ hg -v log -r 0 --template '{node|formatnode}\n'
1793 1e4e1b8f71e0
1793 1e4e1b8f71e0
1794
1794
1795 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1795 $ hg --debug log -r 0 --template '{node|formatnode}\n'
1796 1e4e1b8f71e05681d422154f5421e385fec3454f
1796 1e4e1b8f71e05681d422154f5421e385fec3454f
1797
1797
1798 Age filter:
1798 Age filter:
1799
1799
1800 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1800 $ hg log --template '{date|age}\n' > /dev/null || exit 1
1801
1801
1802 >>> from datetime import datetime, timedelta
1802 >>> from datetime import datetime, timedelta
1803 >>> fp = open('a', 'w')
1803 >>> fp = open('a', 'w')
1804 >>> n = datetime.now() + timedelta(366 * 7)
1804 >>> n = datetime.now() + timedelta(366 * 7)
1805 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1805 >>> fp.write('%d-%d-%d 00:00' % (n.year, n.month, n.day))
1806 >>> fp.close()
1806 >>> fp.close()
1807 $ hg add a
1807 $ hg add a
1808 $ hg commit -m future -d "`cat a`"
1808 $ hg commit -m future -d "`cat a`"
1809
1809
1810 $ hg log -l1 --template '{date|age}\n'
1810 $ hg log -l1 --template '{date|age}\n'
1811 7 years from now
1811 7 years from now
1812
1812
1813 Count filter:
1813 Count filter:
1814
1814
1815 $ hg log -l1 --template '{node|count} {node|short|count}\n'
1815 $ hg log -l1 --template '{node|count} {node|short|count}\n'
1816 40 12
1816 40 12
1817
1817
1818 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
1818 $ hg log -l1 --template '{revset("null^")|count} {revset(".")|count} {revset("0::3")|count}\n'
1819 0 1 4
1819 0 1 4
1820
1820
1821 $ hg log -G --template '{rev}: children: {children|count}, \
1821 $ hg log -G --template '{rev}: children: {children|count}, \
1822 > tags: {tags|count}, file_adds: {file_adds|count}, \
1822 > tags: {tags|count}, file_adds: {file_adds|count}, \
1823 > ancestors: {revset("ancestors(%s)", rev)|count}'
1823 > ancestors: {revset("ancestors(%s)", rev)|count}'
1824 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
1824 @ 9: children: 0, tags: 1, file_adds: 1, ancestors: 3
1825 |
1825 |
1826 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
1826 o 8: children: 1, tags: 0, file_adds: 2, ancestors: 2
1827 |
1827 |
1828 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
1828 o 7: children: 1, tags: 0, file_adds: 1, ancestors: 1
1829
1829
1830 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
1830 o 6: children: 0, tags: 0, file_adds: 0, ancestors: 7
1831 |\
1831 |\
1832 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
1832 | o 5: children: 1, tags: 0, file_adds: 1, ancestors: 5
1833 | |
1833 | |
1834 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
1834 o | 4: children: 1, tags: 0, file_adds: 0, ancestors: 5
1835 |/
1835 |/
1836 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
1836 o 3: children: 2, tags: 0, file_adds: 0, ancestors: 4
1837 |
1837 |
1838 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
1838 o 2: children: 1, tags: 0, file_adds: 1, ancestors: 3
1839 |
1839 |
1840 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
1840 o 1: children: 1, tags: 0, file_adds: 1, ancestors: 2
1841 |
1841 |
1842 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
1842 o 0: children: 1, tags: 0, file_adds: 1, ancestors: 1
1843
1843
1844
1844
1845 Error on syntax:
1845 Error on syntax:
1846
1846
1847 $ echo 'x = "f' >> t
1847 $ echo 'x = "f' >> t
1848 $ hg log
1848 $ hg log
1849 abort: t:3: unmatched quotes
1849 abort: t:3: unmatched quotes
1850 [255]
1850 [255]
1851
1851
1852 Behind the scenes, this will throw TypeError
1852 Behind the scenes, this will throw TypeError
1853
1853
1854 $ hg log -l 3 --template '{date|obfuscate}\n'
1854 $ hg log -l 3 --template '{date|obfuscate}\n'
1855 abort: template filter 'obfuscate' is not compatible with keyword 'date'
1855 abort: template filter 'obfuscate' is not compatible with keyword 'date'
1856 [255]
1856 [255]
1857
1857
1858 Behind the scenes, this will throw a ValueError
1858 Behind the scenes, this will throw a ValueError
1859
1859
1860 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1860 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
1861 abort: template filter 'shortdate' is not compatible with keyword 'desc'
1861 abort: template filter 'shortdate' is not compatible with keyword 'desc'
1862 [255]
1862 [255]
1863
1863
1864 Behind the scenes, this will throw AttributeError
1864 Behind the scenes, this will throw AttributeError
1865
1865
1866 $ hg log -l 3 --template 'line: {date|escape}\n'
1866 $ hg log -l 3 --template 'line: {date|escape}\n'
1867 abort: template filter 'escape' is not compatible with keyword 'date'
1867 abort: template filter 'escape' is not compatible with keyword 'date'
1868 [255]
1868 [255]
1869
1869
1870 Behind the scenes, this will throw ValueError
1870 Behind the scenes, this will throw ValueError
1871
1871
1872 $ hg tip --template '{author|email|date}\n'
1872 $ hg tip --template '{author|email|date}\n'
1873 abort: template filter 'datefilter' is not compatible with keyword 'author'
1873 abort: template filter 'datefilter' is not compatible with keyword 'author'
1874 [255]
1874 [255]
1875
1875
1876 Thrown an error if a template function doesn't exist
1876 Thrown an error if a template function doesn't exist
1877
1877
1878 $ hg tip --template '{foo()}\n'
1878 $ hg tip --template '{foo()}\n'
1879 hg: parse error: unknown function 'foo'
1879 hg: parse error: unknown function 'foo'
1880 [255]
1880 [255]
1881
1881
1882 Test diff function:
1882 Test diff function:
1883
1883
1884 $ hg diff -c 8
1884 $ hg diff -c 8
1885 diff -r 29114dbae42b -r 95c24699272e fourth
1885 diff -r 29114dbae42b -r 95c24699272e fourth
1886 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1886 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1887 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1887 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1888 @@ -0,0 +1,1 @@
1888 @@ -0,0 +1,1 @@
1889 +second
1889 +second
1890 diff -r 29114dbae42b -r 95c24699272e second
1890 diff -r 29114dbae42b -r 95c24699272e second
1891 --- a/second Mon Jan 12 13:46:40 1970 +0000
1891 --- a/second Mon Jan 12 13:46:40 1970 +0000
1892 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1892 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1893 @@ -1,1 +0,0 @@
1893 @@ -1,1 +0,0 @@
1894 -second
1894 -second
1895 diff -r 29114dbae42b -r 95c24699272e third
1895 diff -r 29114dbae42b -r 95c24699272e third
1896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1897 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1897 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1898 @@ -0,0 +1,1 @@
1898 @@ -0,0 +1,1 @@
1899 +third
1899 +third
1900
1900
1901 $ hg log -r 8 -T "{diff()}"
1901 $ hg log -r 8 -T "{diff()}"
1902 diff -r 29114dbae42b -r 95c24699272e fourth
1902 diff -r 29114dbae42b -r 95c24699272e fourth
1903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1904 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1904 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1905 @@ -0,0 +1,1 @@
1905 @@ -0,0 +1,1 @@
1906 +second
1906 +second
1907 diff -r 29114dbae42b -r 95c24699272e second
1907 diff -r 29114dbae42b -r 95c24699272e second
1908 --- a/second Mon Jan 12 13:46:40 1970 +0000
1908 --- a/second Mon Jan 12 13:46:40 1970 +0000
1909 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1909 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1910 @@ -1,1 +0,0 @@
1910 @@ -1,1 +0,0 @@
1911 -second
1911 -second
1912 diff -r 29114dbae42b -r 95c24699272e third
1912 diff -r 29114dbae42b -r 95c24699272e third
1913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1914 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1914 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1915 @@ -0,0 +1,1 @@
1915 @@ -0,0 +1,1 @@
1916 +third
1916 +third
1917
1917
1918 $ hg log -r 8 -T "{diff('glob:f*')}"
1918 $ hg log -r 8 -T "{diff('glob:f*')}"
1919 diff -r 29114dbae42b -r 95c24699272e fourth
1919 diff -r 29114dbae42b -r 95c24699272e fourth
1920 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1920 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1921 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1921 +++ b/fourth Wed Jan 01 10:01:00 2020 +0000
1922 @@ -0,0 +1,1 @@
1922 @@ -0,0 +1,1 @@
1923 +second
1923 +second
1924
1924
1925 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
1925 $ hg log -r 8 -T "{diff('', 'glob:f*')}"
1926 diff -r 29114dbae42b -r 95c24699272e second
1926 diff -r 29114dbae42b -r 95c24699272e second
1927 --- a/second Mon Jan 12 13:46:40 1970 +0000
1927 --- a/second Mon Jan 12 13:46:40 1970 +0000
1928 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1928 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1929 @@ -1,1 +0,0 @@
1929 @@ -1,1 +0,0 @@
1930 -second
1930 -second
1931 diff -r 29114dbae42b -r 95c24699272e third
1931 diff -r 29114dbae42b -r 95c24699272e third
1932 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1932 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1933 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1933 +++ b/third Wed Jan 01 10:01:00 2020 +0000
1934 @@ -0,0 +1,1 @@
1934 @@ -0,0 +1,1 @@
1935 +third
1935 +third
1936
1936
1937 $ cd ..
1937 $ cd ..
1938
1938
1939
1939
1940 latesttag:
1940 latesttag:
1941
1941
1942 $ hg init latesttag
1942 $ hg init latesttag
1943 $ cd latesttag
1943 $ cd latesttag
1944
1944
1945 $ echo a > file
1945 $ echo a > file
1946 $ hg ci -Am a -d '0 0'
1946 $ hg ci -Am a -d '0 0'
1947 adding file
1947 adding file
1948
1948
1949 $ echo b >> file
1949 $ echo b >> file
1950 $ hg ci -m b -d '1 0'
1950 $ hg ci -m b -d '1 0'
1951
1951
1952 $ echo c >> head1
1952 $ echo c >> head1
1953 $ hg ci -Am h1c -d '2 0'
1953 $ hg ci -Am h1c -d '2 0'
1954 adding head1
1954 adding head1
1955
1955
1956 $ hg update -q 1
1956 $ hg update -q 1
1957 $ echo d >> head2
1957 $ echo d >> head2
1958 $ hg ci -Am h2d -d '3 0'
1958 $ hg ci -Am h2d -d '3 0'
1959 adding head2
1959 adding head2
1960 created new head
1960 created new head
1961
1961
1962 $ echo e >> head2
1962 $ echo e >> head2
1963 $ hg ci -m h2e -d '4 0'
1963 $ hg ci -m h2e -d '4 0'
1964
1964
1965 $ hg merge -q
1965 $ hg merge -q
1966 $ hg ci -m merge -d '5 -3600'
1966 $ hg ci -m merge -d '5 -3600'
1967
1967
1968 No tag set:
1968 No tag set:
1969
1969
1970 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1970 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1971 5: null+5
1971 5: null+5
1972 4: null+4
1972 4: null+4
1973 3: null+3
1973 3: null+3
1974 2: null+3
1974 2: null+3
1975 1: null+2
1975 1: null+2
1976 0: null+1
1976 0: null+1
1977
1977
1978 One common tag: longest path wins:
1978 One common tag: longest path wins:
1979
1979
1980 $ hg tag -r 1 -m t1 -d '6 0' t1
1980 $ hg tag -r 1 -m t1 -d '6 0' t1
1981 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1981 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1982 6: t1+4
1982 6: t1+4
1983 5: t1+3
1983 5: t1+3
1984 4: t1+2
1984 4: t1+2
1985 3: t1+1
1985 3: t1+1
1986 2: t1+1
1986 2: t1+1
1987 1: t1+0
1987 1: t1+0
1988 0: null+1
1988 0: null+1
1989
1989
1990 One ancestor tag: more recent wins:
1990 One ancestor tag: more recent wins:
1991
1991
1992 $ hg tag -r 2 -m t2 -d '7 0' t2
1992 $ hg tag -r 2 -m t2 -d '7 0' t2
1993 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1993 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
1994 7: t2+3
1994 7: t2+3
1995 6: t2+2
1995 6: t2+2
1996 5: t2+1
1996 5: t2+1
1997 4: t1+2
1997 4: t1+2
1998 3: t1+1
1998 3: t1+1
1999 2: t2+0
1999 2: t2+0
2000 1: t1+0
2000 1: t1+0
2001 0: null+1
2001 0: null+1
2002
2002
2003 Two branch tags: more recent wins:
2003 Two branch tags: more recent wins:
2004
2004
2005 $ hg tag -r 3 -m t3 -d '8 0' t3
2005 $ hg tag -r 3 -m t3 -d '8 0' t3
2006 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2006 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2007 8: t3+5
2007 8: t3+5
2008 7: t3+4
2008 7: t3+4
2009 6: t3+3
2009 6: t3+3
2010 5: t3+2
2010 5: t3+2
2011 4: t3+1
2011 4: t3+1
2012 3: t3+0
2012 3: t3+0
2013 2: t2+0
2013 2: t2+0
2014 1: t1+0
2014 1: t1+0
2015 0: null+1
2015 0: null+1
2016
2016
2017 Merged tag overrides:
2017 Merged tag overrides:
2018
2018
2019 $ hg tag -r 5 -m t5 -d '9 0' t5
2019 $ hg tag -r 5 -m t5 -d '9 0' t5
2020 $ hg tag -r 3 -m at3 -d '10 0' at3
2020 $ hg tag -r 3 -m at3 -d '10 0' at3
2021 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2021 $ hg log --template '{rev}: {latesttag}+{latesttagdistance}\n'
2022 10: t5+5
2022 10: t5+5
2023 9: t5+4
2023 9: t5+4
2024 8: t5+3
2024 8: t5+3
2025 7: t5+2
2025 7: t5+2
2026 6: t5+1
2026 6: t5+1
2027 5: t5+0
2027 5: t5+0
2028 4: at3:t3+1
2028 4: at3:t3+1
2029 3: at3:t3+0
2029 3: at3:t3+0
2030 2: t2+0
2030 2: t2+0
2031 1: t1+0
2031 1: t1+0
2032 0: null+1
2032 0: null+1
2033
2033
2034 $ cd ..
2034 $ cd ..
2035
2035
2036
2036
2037 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2037 Style path expansion: issue1948 - ui.style option doesn't work on OSX
2038 if it is a relative path
2038 if it is a relative path
2039
2039
2040 $ mkdir -p home/styles
2040 $ mkdir -p home/styles
2041
2041
2042 $ cat > home/styles/teststyle <<EOF
2042 $ cat > home/styles/teststyle <<EOF
2043 > changeset = 'test {rev}:{node|short}\n'
2043 > changeset = 'test {rev}:{node|short}\n'
2044 > EOF
2044 > EOF
2045
2045
2046 $ HOME=`pwd`/home; export HOME
2046 $ HOME=`pwd`/home; export HOME
2047
2047
2048 $ cat > latesttag/.hg/hgrc <<EOF
2048 $ cat > latesttag/.hg/hgrc <<EOF
2049 > [ui]
2049 > [ui]
2050 > style = ~/styles/teststyle
2050 > style = ~/styles/teststyle
2051 > EOF
2051 > EOF
2052
2052
2053 $ hg -R latesttag tip
2053 $ hg -R latesttag tip
2054 test 10:9b4a630e5f5f
2054 test 10:9b4a630e5f5f
2055
2055
2056 Test recursive showlist template (issue1989):
2056 Test recursive showlist template (issue1989):
2057
2057
2058 $ cat > style1989 <<EOF
2058 $ cat > style1989 <<EOF
2059 > changeset = '{file_mods}{manifest}{extras}'
2059 > changeset = '{file_mods}{manifest}{extras}'
2060 > file_mod = 'M|{author|person}\n'
2060 > file_mod = 'M|{author|person}\n'
2061 > manifest = '{rev},{author}\n'
2061 > manifest = '{rev},{author}\n'
2062 > extra = '{key}: {author}\n'
2062 > extra = '{key}: {author}\n'
2063 > EOF
2063 > EOF
2064
2064
2065 $ hg -R latesttag log -r tip --style=style1989
2065 $ hg -R latesttag log -r tip --style=style1989
2066 M|test
2066 M|test
2067 10,test
2067 10,test
2068 branch: test
2068 branch: test
2069
2069
2070 Test new-style inline templating:
2070 Test new-style inline templating:
2071
2071
2072 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2072 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
2073 modified files: .hgtags
2073 modified files: .hgtags
2074
2074
2075 Test the sub function of templating for expansion:
2075 Test the sub function of templating for expansion:
2076
2076
2077 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2077 $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'
2078 xx
2078 xx
2079
2079
2080 Test the strip function with chars specified:
2080 Test the strip function with chars specified:
2081
2081
2082 $ hg log -R latesttag --template '{desc}\n'
2082 $ hg log -R latesttag --template '{desc}\n'
2083 at3
2083 at3
2084 t5
2084 t5
2085 t3
2085 t3
2086 t2
2086 t2
2087 t1
2087 t1
2088 merge
2088 merge
2089 h2e
2089 h2e
2090 h2d
2090 h2d
2091 h1c
2091 h1c
2092 b
2092 b
2093 a
2093 a
2094
2094
2095 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2095 $ hg log -R latesttag --template '{strip(desc, "te")}\n'
2096 at3
2096 at3
2097 5
2097 5
2098 3
2098 3
2099 2
2099 2
2100 1
2100 1
2101 merg
2101 merg
2102 h2
2102 h2
2103 h2d
2103 h2d
2104 h1c
2104 h1c
2105 b
2105 b
2106 a
2106 a
2107
2107
2108 Test date format:
2108 Test date format:
2109
2109
2110 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2110 $ hg log -R latesttag --template 'date: {date(date, "%y %m %d %S %z")}\n'
2111 date: 70 01 01 10 +0000
2111 date: 70 01 01 10 +0000
2112 date: 70 01 01 09 +0000
2112 date: 70 01 01 09 +0000
2113 date: 70 01 01 08 +0000
2113 date: 70 01 01 08 +0000
2114 date: 70 01 01 07 +0000
2114 date: 70 01 01 07 +0000
2115 date: 70 01 01 06 +0000
2115 date: 70 01 01 06 +0000
2116 date: 70 01 01 05 +0100
2116 date: 70 01 01 05 +0100
2117 date: 70 01 01 04 +0000
2117 date: 70 01 01 04 +0000
2118 date: 70 01 01 03 +0000
2118 date: 70 01 01 03 +0000
2119 date: 70 01 01 02 +0000
2119 date: 70 01 01 02 +0000
2120 date: 70 01 01 01 +0000
2120 date: 70 01 01 01 +0000
2121 date: 70 01 01 00 +0000
2121 date: 70 01 01 00 +0000
2122
2122
2123 Test string escaping:
2123 Test string escaping:
2124
2124
2125 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2125 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
2126 >
2126 >
2127 <>\n<[>
2127 <>\n<[>
2128 <>\n<]>
2128 <>\n<]>
2129 <>\n<
2129 <>\n<
2130
2130
2131 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
2131 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
2132
2132
2133 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
2133 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
2134 \x6e
2134 \x6e
2135 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
2135 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
2136 \x5c\x786e
2136 \x5c\x786e
2137 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
2137 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
2138 \x6e
2138 \x6e
2139 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
2139 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
2140 \x5c\x786e
2140 \x5c\x786e
2141
2141
2142 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
2142 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
2143 \x6e
2143 \x6e
2144 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
2144 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
2145 \x5c\x786e
2145 \x5c\x786e
2146 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
2146 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
2147 \x6e
2147 \x6e
2148 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
2148 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
2149 \x5c\x786e
2149 \x5c\x786e
2150
2150
2151 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
2151 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
2152 fourth
2152 fourth
2153 second
2153 second
2154 third
2154 third
2155 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
2155 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
2156 fourth\nsecond\nthird
2156 fourth\nsecond\nthird
2157
2157
2158 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
2158 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
2159 <p>
2159 <p>
2160 1st
2160 1st
2161 </p>
2161 </p>
2162 <p>
2162 <p>
2163 2nd
2163 2nd
2164 </p>
2164 </p>
2165 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
2165 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
2166 <p>
2166 <p>
2167 1st\n\n2nd
2167 1st\n\n2nd
2168 </p>
2168 </p>
2169 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
2169 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
2170 1st
2170 1st
2171
2171
2172 2nd
2172 2nd
2173
2173
2174 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
2174 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
2175 o perso
2175 o perso
2176 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
2176 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
2177 no person
2177 no person
2178 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
2178 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
2179 o perso
2179 o perso
2180 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
2180 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
2181 no perso
2181 no perso
2182
2182
2183 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
2183 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
2184 -o perso-
2184 -o perso-
2185 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
2185 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
2186 no person
2186 no person
2187 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
2187 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", desc)}\n'
2188 \x2do perso\x2d
2188 \x2do perso\x2d
2189 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
2189 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
2190 -o perso-
2190 -o perso-
2191 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
2191 $ hg log -R a -r 2 --template '{sub("n", r"\x2d", r"no perso\x6e")}\n'
2192 \x2do perso\x6e
2192 \x2do perso\x6e
2193
2193
2194 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
2194 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
2195 fourth
2195 fourth
2196 second
2196 second
2197 third
2197 third
2198 $ hg log -R a -r 8 --template '{files % r"{file}\n"}\n'
2198 $ hg log -R a -r 8 --template '{files % r"{file}\n"}\n'
2199 fourth\nsecond\nthird\n
2199 fourth\nsecond\nthird\n
2200
2200
2201 Test string escaping in nested expression:
2201 Test string escaping in nested expression:
2202
2202
2203 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
2203 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
2204 fourth\x6esecond\x6ethird
2204 fourth\x6esecond\x6ethird
2205 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
2205 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
2206 fourth\x6esecond\x6ethird
2206 fourth\x6esecond\x6ethird
2207
2207
2208 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
2208 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
2209 fourth\x6esecond\x6ethird
2209 fourth\x6esecond\x6ethird
2210 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
2210 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
2211 fourth\x5c\x786esecond\x5c\x786ethird
2211 fourth\x5c\x786esecond\x5c\x786ethird
2212
2212
2213 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
2213 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
2214 3:\x6eo user, \x6eo domai\x6e
2214 3:\x6eo user, \x6eo domai\x6e
2215 4:\x5c\x786eew bra\x5c\x786ech
2215 4:\x5c\x786eew bra\x5c\x786ech
2216
2216
2217 Test recursive evaluation:
2217 Test recursive evaluation:
2218
2218
2219 $ hg init r
2219 $ hg init r
2220 $ cd r
2220 $ cd r
2221 $ echo a > a
2221 $ echo a > a
2222 $ hg ci -Am '{rev}'
2222 $ hg ci -Am '{rev}'
2223 adding a
2223 adding a
2224 $ hg log -r 0 --template '{if(rev, desc)}\n'
2224 $ hg log -r 0 --template '{if(rev, desc)}\n'
2225 {rev}
2225 {rev}
2226 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
2226 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
2227 test 0
2227 test 0
2228
2228
2229 $ hg branch -q 'text.{rev}'
2229 $ hg branch -q 'text.{rev}'
2230 $ echo aa >> aa
2230 $ echo aa >> aa
2231 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
2231 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
2232
2232
2233 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
2233 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
2234 {node|short}desc to
2234 {node|short}desc to
2235 text.{rev}be wrapped
2235 text.{rev}be wrapped
2236 text.{rev}desc to be
2236 text.{rev}desc to be
2237 text.{rev}wrapped (no-eol)
2237 text.{rev}wrapped (no-eol)
2238 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
2238 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
2239 bcc7ff960b8e:desc to
2239 bcc7ff960b8e:desc to
2240 text.1:be wrapped
2240 text.1:be wrapped
2241 text.1:desc to be
2241 text.1:desc to be
2242 text.1:wrapped (no-eol)
2242 text.1:wrapped (no-eol)
2243
2243
2244 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
2244 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
2245 {node|short} (no-eol)
2245 {node|short} (no-eol)
2246 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
2246 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
2247 bcc-ff---b-e (no-eol)
2247 bcc-ff---b-e (no-eol)
2248
2248
2249 $ cat >> .hg/hgrc <<EOF
2249 $ cat >> .hg/hgrc <<EOF
2250 > [extensions]
2250 > [extensions]
2251 > color=
2251 > color=
2252 > [color]
2252 > [color]
2253 > mode=ansi
2253 > mode=ansi
2254 > text.{rev} = red
2254 > text.{rev} = red
2255 > text.1 = green
2255 > text.1 = green
2256 > EOF
2256 > EOF
2257 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
2257 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
2258 \x1b[0;31mtext\x1b[0m (esc)
2258 \x1b[0;31mtext\x1b[0m (esc)
2259 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
2259 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
2260 \x1b[0;32mtext\x1b[0m (esc)
2260 \x1b[0;32mtext\x1b[0m (esc)
2261
2261
2262 Test branches inside if statement:
2262 Test branches inside if statement:
2263
2263
2264 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
2264 $ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
2265 no
2265 no
2266
2266
2267 Test shortest(node) function:
2267 Test shortest(node) function:
2268
2268
2269 $ echo b > b
2269 $ echo b > b
2270 $ hg ci -qAm b
2270 $ hg ci -qAm b
2271 $ hg log --template '{shortest(node)}\n'
2271 $ hg log --template '{shortest(node)}\n'
2272 e777
2272 e777
2273 bcc7
2273 bcc7
2274 f776
2274 f776
2275 $ hg log --template '{shortest(node, 10)}\n'
2275 $ hg log --template '{shortest(node, 10)}\n'
2276 e777603221
2276 e777603221
2277 bcc7ff960b
2277 bcc7ff960b
2278 f7769ec2ab
2278 f7769ec2ab
2279
2279
2280 Test pad function
2280 Test pad function
2281
2281
2282 $ hg log --template '{pad(rev, 20)} {author|user}\n'
2282 $ hg log --template '{pad(rev, 20)} {author|user}\n'
2283 2 test
2283 2 test
2284 1 {node|short}
2284 1 {node|short}
2285 0 test
2285 0 test
2286
2286
2287 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
2287 $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
2288 2 test
2288 2 test
2289 1 {node|short}
2289 1 {node|short}
2290 0 test
2290 0 test
2291
2291
2292 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
2292 $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
2293 2------------------- test
2293 2------------------- test
2294 1------------------- {node|short}
2294 1------------------- {node|short}
2295 0------------------- test
2295 0------------------- test
2296
2296
2297 Test ifcontains function
2297 Test ifcontains function
2298
2298
2299 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
2299 $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'
2300 2 is in the string
2300 2 is in the string
2301 1 is not
2301 1 is not
2302 0 is in the string
2302 0 is in the string
2303
2303
2304 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
2304 $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
2305 2 did not add a
2305 2 did not add a
2306 1 did not add a
2306 1 did not add a
2307 0 added a
2307 0 added a
2308
2308
2309 Test revset function
2309 Test revset function
2310
2310
2311 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
2311 $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
2312 2 current rev
2312 2 current rev
2313 1 not current rev
2313 1 not current rev
2314 0 not current rev
2314 0 not current rev
2315
2315
2316 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
2316 $ hg log --template '{rev} {ifcontains(rev, revset(". + .^"), "match rev", "not match rev")}\n'
2317 2 match rev
2317 2 match rev
2318 1 match rev
2318 1 match rev
2319 0 not match rev
2319 0 not match rev
2320
2320
2321 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
2321 $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
2322 2 Parents: 1
2322 2 Parents: 1
2323 1 Parents: 0
2323 1 Parents: 0
2324 0 Parents:
2324 0 Parents:
2325
2325
2326 $ cat >> .hg/hgrc <<EOF
2326 $ cat >> .hg/hgrc <<EOF
2327 > [revsetalias]
2327 > [revsetalias]
2328 > myparents(\$1) = parents(\$1)
2328 > myparents(\$1) = parents(\$1)
2329 > EOF
2329 > EOF
2330 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
2330 $ hg log --template '{rev} Parents: {revset("myparents(%s)", rev)}\n'
2331 2 Parents: 1
2331 2 Parents: 1
2332 1 Parents: 0
2332 1 Parents: 0
2333 0 Parents:
2333 0 Parents:
2334
2334
2335 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
2335 $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
2336 Rev: 2
2336 Rev: 2
2337 Ancestor: 0
2337 Ancestor: 0
2338 Ancestor: 1
2338 Ancestor: 1
2339 Ancestor: 2
2339 Ancestor: 2
2340
2340
2341 Rev: 1
2341 Rev: 1
2342 Ancestor: 0
2342 Ancestor: 0
2343 Ancestor: 1
2343 Ancestor: 1
2344
2344
2345 Rev: 0
2345 Rev: 0
2346 Ancestor: 0
2346 Ancestor: 0
2347
2347
2348 Test current bookmark templating
2348 Test current bookmark templating
2349
2349
2350 $ hg book foo
2350 $ hg book foo
2351 $ hg book bar
2351 $ hg book bar
2352 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
2352 $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
2353 2 bar* foo
2353 2 bar* foo
2354 1
2354 1
2355 0
2355 0
2356 $ hg log --template "{rev} {currentbookmark}\n"
2356 $ hg log --template "{rev} {currentbookmark}\n"
2357 2 bar
2357 2 bar
2358 1
2358 1
2359 0
2359 0
2360 $ hg bookmarks --inactive bar
2360 $ hg bookmarks --inactive bar
2361 $ hg log --template "{rev} {currentbookmark}\n"
2361 $ hg log --template "{rev} {currentbookmark}\n"
2362 2
2362 2
2363 1
2363 1
2364 0
2364 0
2365
2365
2366 Test stringify on sub expressions
2366 Test stringify on sub expressions
2367
2367
2368 $ cd ..
2368 $ cd ..
2369 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
2369 $ hg log -R a -r 8 --template '{join(files, if("1", if("1", ", ")))}\n'
2370 fourth, second, third
2370 fourth, second, third
2371 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
2371 $ hg log -R a -r 8 --template '{strip(if("1", if("1", "-abc-")), if("1", if("1", "-")))}\n'
2372 abc
2372 abc
2373
2373
2374 Test splitlines
2374 Test splitlines
2375
2375
2376 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
2376 $ hg log -Gv -R a --template "{splitlines(desc) % 'foo {line}\n'}"
2377 @ foo future
2377 @ foo future
2378 |
2378 |
2379 o foo third
2379 o foo third
2380 |
2380 |
2381 o foo second
2381 o foo second
2382
2382
2383 o foo merge
2383 o foo merge
2384 |\
2384 |\
2385 | o foo new head
2385 | o foo new head
2386 | |
2386 | |
2387 o | foo new branch
2387 o | foo new branch
2388 |/
2388 |/
2389 o foo no user, no domain
2389 o foo no user, no domain
2390 |
2390 |
2391 o foo no person
2391 o foo no person
2392 |
2392 |
2393 o foo other 1
2393 o foo other 1
2394 | foo other 2
2394 | foo other 2
2395 | foo
2395 | foo
2396 | foo other 3
2396 | foo other 3
2397 o foo line 1
2397 o foo line 1
2398 foo line 2
2398 foo line 2
2399
2399
2400 Test startswith
2400 Test startswith
2401 $ hg log -Gv -R a --template "{startswith(desc)}"
2401 $ hg log -Gv -R a --template "{startswith(desc)}"
2402 hg: parse error: startswith expects two arguments
2402 hg: parse error: startswith expects two arguments
2403 [255]
2403 [255]
2404
2404
2405 $ hg log -Gv -R a --template "{startswith('line', desc)}"
2405 $ hg log -Gv -R a --template "{startswith('line', desc)}"
2406 @
2406 @
2407 |
2407 |
2408 o
2408 o
2409 |
2409 |
2410 o
2410 o
2411
2411
2412 o
2412 o
2413 |\
2413 |\
2414 | o
2414 | o
2415 | |
2415 | |
2416 o |
2416 o |
2417 |/
2417 |/
2418 o
2418 o
2419 |
2419 |
2420 o
2420 o
2421 |
2421 |
2422 o
2422 o
2423 |
2423 |
2424 o line 1
2424 o line 1
2425 line 2
2425 line 2
2426
2426
2427 Test bad template with better error message
2427 Test bad template with better error message
2428
2428
2429 $ hg log -Gv -R a --template '{desc|user()}'
2429 $ hg log -Gv -R a --template '{desc|user()}'
2430 hg: parse error: expected a symbol, got 'func'
2430 hg: parse error: expected a symbol, got 'func'
2431 [255]
2431 [255]
2432
2432
2433 Test word function (including index out of bounds graceful failure)
2433 Test word function (including index out of bounds graceful failure)
2434
2434
2435 $ hg log -Gv -R a --template "{word('1', desc)}"
2435 $ hg log -Gv -R a --template "{word('1', desc)}"
2436 @
2436 @
2437 |
2437 |
2438 o
2438 o
2439 |
2439 |
2440 o
2440 o
2441
2441
2442 o
2442 o
2443 |\
2443 |\
2444 | o head
2444 | o head
2445 | |
2445 | |
2446 o | branch
2446 o | branch
2447 |/
2447 |/
2448 o user,
2448 o user,
2449 |
2449 |
2450 o person
2450 o person
2451 |
2451 |
2452 o 1
2452 o 1
2453 |
2453 |
2454 o 1
2454 o 1
2455
2455
2456
2456
2457 Test word third parameter used as splitter
2457 Test word third parameter used as splitter
2458
2458
2459 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
2459 $ hg log -Gv -R a --template "{word('0', desc, 'o')}"
2460 @ future
2460 @ future
2461 |
2461 |
2462 o third
2462 o third
2463 |
2463 |
2464 o sec
2464 o sec
2465
2465
2466 o merge
2466 o merge
2467 |\
2467 |\
2468 | o new head
2468 | o new head
2469 | |
2469 | |
2470 o | new branch
2470 o | new branch
2471 |/
2471 |/
2472 o n
2472 o n
2473 |
2473 |
2474 o n
2474 o n
2475 |
2475 |
2476 o
2476 o
2477 |
2477 |
2478 o line 1
2478 o line 1
2479 line 2
2479 line 2
2480
2480
2481 Test word error messages for not enough and too many arguments
2481 Test word error messages for not enough and too many arguments
2482
2482
2483 $ hg log -Gv -R a --template "{word('0')}"
2483 $ hg log -Gv -R a --template "{word('0')}"
2484 hg: parse error: word expects two or three arguments, got 1
2484 hg: parse error: word expects two or three arguments, got 1
2485 [255]
2485 [255]
2486
2486
2487 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
2487 $ hg log -Gv -R a --template "{word('0', desc, 'o', 'h', 'b', 'o', 'y')}"
2488 hg: parse error: word expects two or three arguments, got 7
2488 hg: parse error: word expects two or three arguments, got 7
2489 [255]
2489 [255]
General Comments 0
You need to be logged in to leave comments. Login now