##// END OF EJS Templates
log: fix behavior with empty repositories (issue3497)...
Alexander Plavin -
r18991:c1af1fb3 default
parent child Browse files
Show More
@@ -1,2080 +1,2078 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, graphmod, revset, phases, obsolete
13 import subrepo, context, repair, graphmod, revset, phases, obsolete
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import lock as lockmod
16 import lock as lockmod
17
17
18 def parsealiases(cmd):
18 def parsealiases(cmd):
19 return cmd.lstrip("^").split("|")
19 return cmd.lstrip("^").split("|")
20
20
21 def findpossible(cmd, table, strict=False):
21 def findpossible(cmd, table, strict=False):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29
29
30 if cmd in table:
30 if cmd in table:
31 # short-circuit exact matches, "log" alias beats "^log|history"
31 # short-circuit exact matches, "log" alias beats "^log|history"
32 keys = [cmd]
32 keys = [cmd]
33 else:
33 else:
34 keys = table.keys()
34 keys = table.keys()
35
35
36 for e in keys:
36 for e in keys:
37 aliases = parsealiases(e)
37 aliases = parsealiases(e)
38 found = None
38 found = None
39 if cmd in aliases:
39 if cmd in aliases:
40 found = cmd
40 found = cmd
41 elif not strict:
41 elif not strict:
42 for a in aliases:
42 for a in aliases:
43 if a.startswith(cmd):
43 if a.startswith(cmd):
44 found = a
44 found = a
45 break
45 break
46 if found is not None:
46 if found is not None:
47 if aliases[0].startswith("debug") or found.startswith("debug"):
47 if aliases[0].startswith("debug") or found.startswith("debug"):
48 debugchoice[found] = (aliases, table[e])
48 debugchoice[found] = (aliases, table[e])
49 else:
49 else:
50 choice[found] = (aliases, table[e])
50 choice[found] = (aliases, table[e])
51
51
52 if not choice and debugchoice:
52 if not choice and debugchoice:
53 choice = debugchoice
53 choice = debugchoice
54
54
55 return choice
55 return choice
56
56
57 def findcmd(cmd, table, strict=True):
57 def findcmd(cmd, table, strict=True):
58 """Return (aliases, command table entry) for command string."""
58 """Return (aliases, command table entry) for command string."""
59 choice = findpossible(cmd, table, strict)
59 choice = findpossible(cmd, table, strict)
60
60
61 if cmd in choice:
61 if cmd in choice:
62 return choice[cmd]
62 return choice[cmd]
63
63
64 if len(choice) > 1:
64 if len(choice) > 1:
65 clist = choice.keys()
65 clist = choice.keys()
66 clist.sort()
66 clist.sort()
67 raise error.AmbiguousCommand(cmd, clist)
67 raise error.AmbiguousCommand(cmd, clist)
68
68
69 if choice:
69 if choice:
70 return choice.values()[0]
70 return choice.values()[0]
71
71
72 raise error.UnknownCommand(cmd)
72 raise error.UnknownCommand(cmd)
73
73
74 def findrepo(p):
74 def findrepo(p):
75 while not os.path.isdir(os.path.join(p, ".hg")):
75 while not os.path.isdir(os.path.join(p, ".hg")):
76 oldp, p = p, os.path.dirname(p)
76 oldp, p = p, os.path.dirname(p)
77 if p == oldp:
77 if p == oldp:
78 return None
78 return None
79
79
80 return p
80 return p
81
81
82 def bailifchanged(repo):
82 def bailifchanged(repo):
83 if repo.dirstate.p2() != nullid:
83 if repo.dirstate.p2() != nullid:
84 raise util.Abort(_('outstanding uncommitted merge'))
84 raise util.Abort(_('outstanding uncommitted merge'))
85 modified, added, removed, deleted = repo.status()[:4]
85 modified, added, removed, deleted = repo.status()[:4]
86 if modified or added or removed or deleted:
86 if modified or added or removed or deleted:
87 raise util.Abort(_("outstanding uncommitted changes"))
87 raise util.Abort(_("outstanding uncommitted changes"))
88 ctx = repo[None]
88 ctx = repo[None]
89 for s in sorted(ctx.substate):
89 for s in sorted(ctx.substate):
90 if ctx.sub(s).dirty():
90 if ctx.sub(s).dirty():
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
92
92
93 def logmessage(ui, opts):
93 def logmessage(ui, opts):
94 """ get the log message according to -m and -l option """
94 """ get the log message according to -m and -l option """
95 message = opts.get('message')
95 message = opts.get('message')
96 logfile = opts.get('logfile')
96 logfile = opts.get('logfile')
97
97
98 if message and logfile:
98 if message and logfile:
99 raise util.Abort(_('options --message and --logfile are mutually '
99 raise util.Abort(_('options --message and --logfile are mutually '
100 'exclusive'))
100 'exclusive'))
101 if not message and logfile:
101 if not message and logfile:
102 try:
102 try:
103 if logfile == '-':
103 if logfile == '-':
104 message = ui.fin.read()
104 message = ui.fin.read()
105 else:
105 else:
106 message = '\n'.join(util.readfile(logfile).splitlines())
106 message = '\n'.join(util.readfile(logfile).splitlines())
107 except IOError, inst:
107 except IOError, inst:
108 raise util.Abort(_("can't read commit message '%s': %s") %
108 raise util.Abort(_("can't read commit message '%s': %s") %
109 (logfile, inst.strerror))
109 (logfile, inst.strerror))
110 return message
110 return message
111
111
112 def loglimit(opts):
112 def loglimit(opts):
113 """get the log limit according to option -l/--limit"""
113 """get the log limit according to option -l/--limit"""
114 limit = opts.get('limit')
114 limit = opts.get('limit')
115 if limit:
115 if limit:
116 try:
116 try:
117 limit = int(limit)
117 limit = int(limit)
118 except ValueError:
118 except ValueError:
119 raise util.Abort(_('limit must be a positive integer'))
119 raise util.Abort(_('limit must be a positive integer'))
120 if limit <= 0:
120 if limit <= 0:
121 raise util.Abort(_('limit must be positive'))
121 raise util.Abort(_('limit must be positive'))
122 else:
122 else:
123 limit = None
123 limit = None
124 return limit
124 return limit
125
125
126 def makefilename(repo, pat, node, desc=None,
126 def makefilename(repo, pat, node, desc=None,
127 total=None, seqno=None, revwidth=None, pathname=None):
127 total=None, seqno=None, revwidth=None, pathname=None):
128 node_expander = {
128 node_expander = {
129 'H': lambda: hex(node),
129 'H': lambda: hex(node),
130 'R': lambda: str(repo.changelog.rev(node)),
130 'R': lambda: str(repo.changelog.rev(node)),
131 'h': lambda: short(node),
131 'h': lambda: short(node),
132 'm': lambda: re.sub('[^\w]', '_', str(desc))
132 'm': lambda: re.sub('[^\w]', '_', str(desc))
133 }
133 }
134 expander = {
134 expander = {
135 '%': lambda: '%',
135 '%': lambda: '%',
136 'b': lambda: os.path.basename(repo.root),
136 'b': lambda: os.path.basename(repo.root),
137 }
137 }
138
138
139 try:
139 try:
140 if node:
140 if node:
141 expander.update(node_expander)
141 expander.update(node_expander)
142 if node:
142 if node:
143 expander['r'] = (lambda:
143 expander['r'] = (lambda:
144 str(repo.changelog.rev(node)).zfill(revwidth or 0))
144 str(repo.changelog.rev(node)).zfill(revwidth or 0))
145 if total is not None:
145 if total is not None:
146 expander['N'] = lambda: str(total)
146 expander['N'] = lambda: str(total)
147 if seqno is not None:
147 if seqno is not None:
148 expander['n'] = lambda: str(seqno)
148 expander['n'] = lambda: str(seqno)
149 if total is not None and seqno is not None:
149 if total is not None and seqno is not None:
150 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
150 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
151 if pathname is not None:
151 if pathname is not None:
152 expander['s'] = lambda: os.path.basename(pathname)
152 expander['s'] = lambda: os.path.basename(pathname)
153 expander['d'] = lambda: os.path.dirname(pathname) or '.'
153 expander['d'] = lambda: os.path.dirname(pathname) or '.'
154 expander['p'] = lambda: pathname
154 expander['p'] = lambda: pathname
155
155
156 newname = []
156 newname = []
157 patlen = len(pat)
157 patlen = len(pat)
158 i = 0
158 i = 0
159 while i < patlen:
159 while i < patlen:
160 c = pat[i]
160 c = pat[i]
161 if c == '%':
161 if c == '%':
162 i += 1
162 i += 1
163 c = pat[i]
163 c = pat[i]
164 c = expander[c]()
164 c = expander[c]()
165 newname.append(c)
165 newname.append(c)
166 i += 1
166 i += 1
167 return ''.join(newname)
167 return ''.join(newname)
168 except KeyError, inst:
168 except KeyError, inst:
169 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
169 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
170 inst.args[0])
170 inst.args[0])
171
171
172 def makefileobj(repo, pat, node=None, desc=None, total=None,
172 def makefileobj(repo, pat, node=None, desc=None, total=None,
173 seqno=None, revwidth=None, mode='wb', modemap={},
173 seqno=None, revwidth=None, mode='wb', modemap={},
174 pathname=None):
174 pathname=None):
175
175
176 writable = mode not in ('r', 'rb')
176 writable = mode not in ('r', 'rb')
177
177
178 if not pat or pat == '-':
178 if not pat or pat == '-':
179 fp = writable and repo.ui.fout or repo.ui.fin
179 fp = writable and repo.ui.fout or repo.ui.fin
180 if util.safehasattr(fp, 'fileno'):
180 if util.safehasattr(fp, 'fileno'):
181 return os.fdopen(os.dup(fp.fileno()), mode)
181 return os.fdopen(os.dup(fp.fileno()), mode)
182 else:
182 else:
183 # if this fp can't be duped properly, return
183 # if this fp can't be duped properly, return
184 # a dummy object that can be closed
184 # a dummy object that can be closed
185 class wrappedfileobj(object):
185 class wrappedfileobj(object):
186 noop = lambda x: None
186 noop = lambda x: None
187 def __init__(self, f):
187 def __init__(self, f):
188 self.f = f
188 self.f = f
189 def __getattr__(self, attr):
189 def __getattr__(self, attr):
190 if attr == 'close':
190 if attr == 'close':
191 return self.noop
191 return self.noop
192 else:
192 else:
193 return getattr(self.f, attr)
193 return getattr(self.f, attr)
194
194
195 return wrappedfileobj(fp)
195 return wrappedfileobj(fp)
196 if util.safehasattr(pat, 'write') and writable:
196 if util.safehasattr(pat, 'write') and writable:
197 return pat
197 return pat
198 if util.safehasattr(pat, 'read') and 'r' in mode:
198 if util.safehasattr(pat, 'read') and 'r' in mode:
199 return pat
199 return pat
200 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
200 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
201 mode = modemap.get(fn, mode)
201 mode = modemap.get(fn, mode)
202 if mode == 'wb':
202 if mode == 'wb':
203 modemap[fn] = 'ab'
203 modemap[fn] = 'ab'
204 return open(fn, mode)
204 return open(fn, mode)
205
205
206 def openrevlog(repo, cmd, file_, opts):
206 def openrevlog(repo, cmd, file_, opts):
207 """opens the changelog, manifest, a filelog or a given revlog"""
207 """opens the changelog, manifest, a filelog or a given revlog"""
208 cl = opts['changelog']
208 cl = opts['changelog']
209 mf = opts['manifest']
209 mf = opts['manifest']
210 msg = None
210 msg = None
211 if cl and mf:
211 if cl and mf:
212 msg = _('cannot specify --changelog and --manifest at the same time')
212 msg = _('cannot specify --changelog and --manifest at the same time')
213 elif cl or mf:
213 elif cl or mf:
214 if file_:
214 if file_:
215 msg = _('cannot specify filename with --changelog or --manifest')
215 msg = _('cannot specify filename with --changelog or --manifest')
216 elif not repo:
216 elif not repo:
217 msg = _('cannot specify --changelog or --manifest '
217 msg = _('cannot specify --changelog or --manifest '
218 'without a repository')
218 'without a repository')
219 if msg:
219 if msg:
220 raise util.Abort(msg)
220 raise util.Abort(msg)
221
221
222 r = None
222 r = None
223 if repo:
223 if repo:
224 if cl:
224 if cl:
225 r = repo.changelog
225 r = repo.changelog
226 elif mf:
226 elif mf:
227 r = repo.manifest
227 r = repo.manifest
228 elif file_:
228 elif file_:
229 filelog = repo.file(file_)
229 filelog = repo.file(file_)
230 if len(filelog):
230 if len(filelog):
231 r = filelog
231 r = filelog
232 if not r:
232 if not r:
233 if not file_:
233 if not file_:
234 raise error.CommandError(cmd, _('invalid arguments'))
234 raise error.CommandError(cmd, _('invalid arguments'))
235 if not os.path.isfile(file_):
235 if not os.path.isfile(file_):
236 raise util.Abort(_("revlog '%s' not found") % file_)
236 raise util.Abort(_("revlog '%s' not found") % file_)
237 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
237 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
238 file_[:-2] + ".i")
238 file_[:-2] + ".i")
239 return r
239 return r
240
240
241 def copy(ui, repo, pats, opts, rename=False):
241 def copy(ui, repo, pats, opts, rename=False):
242 # called with the repo lock held
242 # called with the repo lock held
243 #
243 #
244 # hgsep => pathname that uses "/" to separate directories
244 # hgsep => pathname that uses "/" to separate directories
245 # ossep => pathname that uses os.sep to separate directories
245 # ossep => pathname that uses os.sep to separate directories
246 cwd = repo.getcwd()
246 cwd = repo.getcwd()
247 targets = {}
247 targets = {}
248 after = opts.get("after")
248 after = opts.get("after")
249 dryrun = opts.get("dry_run")
249 dryrun = opts.get("dry_run")
250 wctx = repo[None]
250 wctx = repo[None]
251
251
252 def walkpat(pat):
252 def walkpat(pat):
253 srcs = []
253 srcs = []
254 badstates = after and '?' or '?r'
254 badstates = after and '?' or '?r'
255 m = scmutil.match(repo[None], [pat], opts, globbed=True)
255 m = scmutil.match(repo[None], [pat], opts, globbed=True)
256 for abs in repo.walk(m):
256 for abs in repo.walk(m):
257 state = repo.dirstate[abs]
257 state = repo.dirstate[abs]
258 rel = m.rel(abs)
258 rel = m.rel(abs)
259 exact = m.exact(abs)
259 exact = m.exact(abs)
260 if state in badstates:
260 if state in badstates:
261 if exact and state == '?':
261 if exact and state == '?':
262 ui.warn(_('%s: not copying - file is not managed\n') % rel)
262 ui.warn(_('%s: not copying - file is not managed\n') % rel)
263 if exact and state == 'r':
263 if exact and state == 'r':
264 ui.warn(_('%s: not copying - file has been marked for'
264 ui.warn(_('%s: not copying - file has been marked for'
265 ' remove\n') % rel)
265 ' remove\n') % rel)
266 continue
266 continue
267 # abs: hgsep
267 # abs: hgsep
268 # rel: ossep
268 # rel: ossep
269 srcs.append((abs, rel, exact))
269 srcs.append((abs, rel, exact))
270 return srcs
270 return srcs
271
271
272 # abssrc: hgsep
272 # abssrc: hgsep
273 # relsrc: ossep
273 # relsrc: ossep
274 # otarget: ossep
274 # otarget: ossep
275 def copyfile(abssrc, relsrc, otarget, exact):
275 def copyfile(abssrc, relsrc, otarget, exact):
276 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
276 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
277 if '/' in abstarget:
277 if '/' in abstarget:
278 # We cannot normalize abstarget itself, this would prevent
278 # We cannot normalize abstarget itself, this would prevent
279 # case only renames, like a => A.
279 # case only renames, like a => A.
280 abspath, absname = abstarget.rsplit('/', 1)
280 abspath, absname = abstarget.rsplit('/', 1)
281 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
281 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
282 reltarget = repo.pathto(abstarget, cwd)
282 reltarget = repo.pathto(abstarget, cwd)
283 target = repo.wjoin(abstarget)
283 target = repo.wjoin(abstarget)
284 src = repo.wjoin(abssrc)
284 src = repo.wjoin(abssrc)
285 state = repo.dirstate[abstarget]
285 state = repo.dirstate[abstarget]
286
286
287 scmutil.checkportable(ui, abstarget)
287 scmutil.checkportable(ui, abstarget)
288
288
289 # check for collisions
289 # check for collisions
290 prevsrc = targets.get(abstarget)
290 prevsrc = targets.get(abstarget)
291 if prevsrc is not None:
291 if prevsrc is not None:
292 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
292 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
293 (reltarget, repo.pathto(abssrc, cwd),
293 (reltarget, repo.pathto(abssrc, cwd),
294 repo.pathto(prevsrc, cwd)))
294 repo.pathto(prevsrc, cwd)))
295 return
295 return
296
296
297 # check for overwrites
297 # check for overwrites
298 exists = os.path.lexists(target)
298 exists = os.path.lexists(target)
299 samefile = False
299 samefile = False
300 if exists and abssrc != abstarget:
300 if exists and abssrc != abstarget:
301 if (repo.dirstate.normalize(abssrc) ==
301 if (repo.dirstate.normalize(abssrc) ==
302 repo.dirstate.normalize(abstarget)):
302 repo.dirstate.normalize(abstarget)):
303 if not rename:
303 if not rename:
304 ui.warn(_("%s: can't copy - same file\n") % reltarget)
304 ui.warn(_("%s: can't copy - same file\n") % reltarget)
305 return
305 return
306 exists = False
306 exists = False
307 samefile = True
307 samefile = True
308
308
309 if not after and exists or after and state in 'mn':
309 if not after and exists or after and state in 'mn':
310 if not opts['force']:
310 if not opts['force']:
311 ui.warn(_('%s: not overwriting - file exists\n') %
311 ui.warn(_('%s: not overwriting - file exists\n') %
312 reltarget)
312 reltarget)
313 return
313 return
314
314
315 if after:
315 if after:
316 if not exists:
316 if not exists:
317 if rename:
317 if rename:
318 ui.warn(_('%s: not recording move - %s does not exist\n') %
318 ui.warn(_('%s: not recording move - %s does not exist\n') %
319 (relsrc, reltarget))
319 (relsrc, reltarget))
320 else:
320 else:
321 ui.warn(_('%s: not recording copy - %s does not exist\n') %
321 ui.warn(_('%s: not recording copy - %s does not exist\n') %
322 (relsrc, reltarget))
322 (relsrc, reltarget))
323 return
323 return
324 elif not dryrun:
324 elif not dryrun:
325 try:
325 try:
326 if exists:
326 if exists:
327 os.unlink(target)
327 os.unlink(target)
328 targetdir = os.path.dirname(target) or '.'
328 targetdir = os.path.dirname(target) or '.'
329 if not os.path.isdir(targetdir):
329 if not os.path.isdir(targetdir):
330 os.makedirs(targetdir)
330 os.makedirs(targetdir)
331 if samefile:
331 if samefile:
332 tmp = target + "~hgrename"
332 tmp = target + "~hgrename"
333 os.rename(src, tmp)
333 os.rename(src, tmp)
334 os.rename(tmp, target)
334 os.rename(tmp, target)
335 else:
335 else:
336 util.copyfile(src, target)
336 util.copyfile(src, target)
337 srcexists = True
337 srcexists = True
338 except IOError, inst:
338 except IOError, inst:
339 if inst.errno == errno.ENOENT:
339 if inst.errno == errno.ENOENT:
340 ui.warn(_('%s: deleted in working copy\n') % relsrc)
340 ui.warn(_('%s: deleted in working copy\n') % relsrc)
341 srcexists = False
341 srcexists = False
342 else:
342 else:
343 ui.warn(_('%s: cannot copy - %s\n') %
343 ui.warn(_('%s: cannot copy - %s\n') %
344 (relsrc, inst.strerror))
344 (relsrc, inst.strerror))
345 return True # report a failure
345 return True # report a failure
346
346
347 if ui.verbose or not exact:
347 if ui.verbose or not exact:
348 if rename:
348 if rename:
349 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
349 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
350 else:
350 else:
351 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
351 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
352
352
353 targets[abstarget] = abssrc
353 targets[abstarget] = abssrc
354
354
355 # fix up dirstate
355 # fix up dirstate
356 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
356 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
357 dryrun=dryrun, cwd=cwd)
357 dryrun=dryrun, cwd=cwd)
358 if rename and not dryrun:
358 if rename and not dryrun:
359 if not after and srcexists and not samefile:
359 if not after and srcexists and not samefile:
360 util.unlinkpath(repo.wjoin(abssrc))
360 util.unlinkpath(repo.wjoin(abssrc))
361 wctx.forget([abssrc])
361 wctx.forget([abssrc])
362
362
363 # pat: ossep
363 # pat: ossep
364 # dest ossep
364 # dest ossep
365 # srcs: list of (hgsep, hgsep, ossep, bool)
365 # srcs: list of (hgsep, hgsep, ossep, bool)
366 # return: function that takes hgsep and returns ossep
366 # return: function that takes hgsep and returns ossep
367 def targetpathfn(pat, dest, srcs):
367 def targetpathfn(pat, dest, srcs):
368 if os.path.isdir(pat):
368 if os.path.isdir(pat):
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
369 abspfx = scmutil.canonpath(repo.root, cwd, pat)
370 abspfx = util.localpath(abspfx)
370 abspfx = util.localpath(abspfx)
371 if destdirexists:
371 if destdirexists:
372 striplen = len(os.path.split(abspfx)[0])
372 striplen = len(os.path.split(abspfx)[0])
373 else:
373 else:
374 striplen = len(abspfx)
374 striplen = len(abspfx)
375 if striplen:
375 if striplen:
376 striplen += len(os.sep)
376 striplen += len(os.sep)
377 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
377 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
378 elif destdirexists:
378 elif destdirexists:
379 res = lambda p: os.path.join(dest,
379 res = lambda p: os.path.join(dest,
380 os.path.basename(util.localpath(p)))
380 os.path.basename(util.localpath(p)))
381 else:
381 else:
382 res = lambda p: dest
382 res = lambda p: dest
383 return res
383 return res
384
384
385 # pat: ossep
385 # pat: ossep
386 # dest ossep
386 # dest ossep
387 # srcs: list of (hgsep, hgsep, ossep, bool)
387 # srcs: list of (hgsep, hgsep, ossep, bool)
388 # return: function that takes hgsep and returns ossep
388 # return: function that takes hgsep and returns ossep
389 def targetpathafterfn(pat, dest, srcs):
389 def targetpathafterfn(pat, dest, srcs):
390 if matchmod.patkind(pat):
390 if matchmod.patkind(pat):
391 # a mercurial pattern
391 # a mercurial pattern
392 res = lambda p: os.path.join(dest,
392 res = lambda p: os.path.join(dest,
393 os.path.basename(util.localpath(p)))
393 os.path.basename(util.localpath(p)))
394 else:
394 else:
395 abspfx = scmutil.canonpath(repo.root, cwd, pat)
395 abspfx = scmutil.canonpath(repo.root, cwd, pat)
396 if len(abspfx) < len(srcs[0][0]):
396 if len(abspfx) < len(srcs[0][0]):
397 # A directory. Either the target path contains the last
397 # A directory. Either the target path contains the last
398 # component of the source path or it does not.
398 # component of the source path or it does not.
399 def evalpath(striplen):
399 def evalpath(striplen):
400 score = 0
400 score = 0
401 for s in srcs:
401 for s in srcs:
402 t = os.path.join(dest, util.localpath(s[0])[striplen:])
402 t = os.path.join(dest, util.localpath(s[0])[striplen:])
403 if os.path.lexists(t):
403 if os.path.lexists(t):
404 score += 1
404 score += 1
405 return score
405 return score
406
406
407 abspfx = util.localpath(abspfx)
407 abspfx = util.localpath(abspfx)
408 striplen = len(abspfx)
408 striplen = len(abspfx)
409 if striplen:
409 if striplen:
410 striplen += len(os.sep)
410 striplen += len(os.sep)
411 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
411 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
412 score = evalpath(striplen)
412 score = evalpath(striplen)
413 striplen1 = len(os.path.split(abspfx)[0])
413 striplen1 = len(os.path.split(abspfx)[0])
414 if striplen1:
414 if striplen1:
415 striplen1 += len(os.sep)
415 striplen1 += len(os.sep)
416 if evalpath(striplen1) > score:
416 if evalpath(striplen1) > score:
417 striplen = striplen1
417 striplen = striplen1
418 res = lambda p: os.path.join(dest,
418 res = lambda p: os.path.join(dest,
419 util.localpath(p)[striplen:])
419 util.localpath(p)[striplen:])
420 else:
420 else:
421 # a file
421 # a file
422 if destdirexists:
422 if destdirexists:
423 res = lambda p: os.path.join(dest,
423 res = lambda p: os.path.join(dest,
424 os.path.basename(util.localpath(p)))
424 os.path.basename(util.localpath(p)))
425 else:
425 else:
426 res = lambda p: dest
426 res = lambda p: dest
427 return res
427 return res
428
428
429
429
430 pats = scmutil.expandpats(pats)
430 pats = scmutil.expandpats(pats)
431 if not pats:
431 if not pats:
432 raise util.Abort(_('no source or destination specified'))
432 raise util.Abort(_('no source or destination specified'))
433 if len(pats) == 1:
433 if len(pats) == 1:
434 raise util.Abort(_('no destination specified'))
434 raise util.Abort(_('no destination specified'))
435 dest = pats.pop()
435 dest = pats.pop()
436 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
436 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
437 if not destdirexists:
437 if not destdirexists:
438 if len(pats) > 1 or matchmod.patkind(pats[0]):
438 if len(pats) > 1 or matchmod.patkind(pats[0]):
439 raise util.Abort(_('with multiple sources, destination must be an '
439 raise util.Abort(_('with multiple sources, destination must be an '
440 'existing directory'))
440 'existing directory'))
441 if util.endswithsep(dest):
441 if util.endswithsep(dest):
442 raise util.Abort(_('destination %s is not a directory') % dest)
442 raise util.Abort(_('destination %s is not a directory') % dest)
443
443
444 tfn = targetpathfn
444 tfn = targetpathfn
445 if after:
445 if after:
446 tfn = targetpathafterfn
446 tfn = targetpathafterfn
447 copylist = []
447 copylist = []
448 for pat in pats:
448 for pat in pats:
449 srcs = walkpat(pat)
449 srcs = walkpat(pat)
450 if not srcs:
450 if not srcs:
451 continue
451 continue
452 copylist.append((tfn(pat, dest, srcs), srcs))
452 copylist.append((tfn(pat, dest, srcs), srcs))
453 if not copylist:
453 if not copylist:
454 raise util.Abort(_('no files to copy'))
454 raise util.Abort(_('no files to copy'))
455
455
456 errors = 0
456 errors = 0
457 for targetpath, srcs in copylist:
457 for targetpath, srcs in copylist:
458 for abssrc, relsrc, exact in srcs:
458 for abssrc, relsrc, exact in srcs:
459 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
459 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
460 errors += 1
460 errors += 1
461
461
462 if errors:
462 if errors:
463 ui.warn(_('(consider using --after)\n'))
463 ui.warn(_('(consider using --after)\n'))
464
464
465 return errors != 0
465 return errors != 0
466
466
467 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
467 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
468 runargs=None, appendpid=False):
468 runargs=None, appendpid=False):
469 '''Run a command as a service.'''
469 '''Run a command as a service.'''
470
470
471 if opts['daemon'] and not opts['daemon_pipefds']:
471 if opts['daemon'] and not opts['daemon_pipefds']:
472 # Signal child process startup with file removal
472 # Signal child process startup with file removal
473 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
473 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
474 os.close(lockfd)
474 os.close(lockfd)
475 try:
475 try:
476 if not runargs:
476 if not runargs:
477 runargs = util.hgcmd() + sys.argv[1:]
477 runargs = util.hgcmd() + sys.argv[1:]
478 runargs.append('--daemon-pipefds=%s' % lockpath)
478 runargs.append('--daemon-pipefds=%s' % lockpath)
479 # Don't pass --cwd to the child process, because we've already
479 # Don't pass --cwd to the child process, because we've already
480 # changed directory.
480 # changed directory.
481 for i in xrange(1, len(runargs)):
481 for i in xrange(1, len(runargs)):
482 if runargs[i].startswith('--cwd='):
482 if runargs[i].startswith('--cwd='):
483 del runargs[i]
483 del runargs[i]
484 break
484 break
485 elif runargs[i].startswith('--cwd'):
485 elif runargs[i].startswith('--cwd'):
486 del runargs[i:i + 2]
486 del runargs[i:i + 2]
487 break
487 break
488 def condfn():
488 def condfn():
489 return not os.path.exists(lockpath)
489 return not os.path.exists(lockpath)
490 pid = util.rundetached(runargs, condfn)
490 pid = util.rundetached(runargs, condfn)
491 if pid < 0:
491 if pid < 0:
492 raise util.Abort(_('child process failed to start'))
492 raise util.Abort(_('child process failed to start'))
493 finally:
493 finally:
494 try:
494 try:
495 os.unlink(lockpath)
495 os.unlink(lockpath)
496 except OSError, e:
496 except OSError, e:
497 if e.errno != errno.ENOENT:
497 if e.errno != errno.ENOENT:
498 raise
498 raise
499 if parentfn:
499 if parentfn:
500 return parentfn(pid)
500 return parentfn(pid)
501 else:
501 else:
502 return
502 return
503
503
504 if initfn:
504 if initfn:
505 initfn()
505 initfn()
506
506
507 if opts['pid_file']:
507 if opts['pid_file']:
508 mode = appendpid and 'a' or 'w'
508 mode = appendpid and 'a' or 'w'
509 fp = open(opts['pid_file'], mode)
509 fp = open(opts['pid_file'], mode)
510 fp.write(str(os.getpid()) + '\n')
510 fp.write(str(os.getpid()) + '\n')
511 fp.close()
511 fp.close()
512
512
513 if opts['daemon_pipefds']:
513 if opts['daemon_pipefds']:
514 lockpath = opts['daemon_pipefds']
514 lockpath = opts['daemon_pipefds']
515 try:
515 try:
516 os.setsid()
516 os.setsid()
517 except AttributeError:
517 except AttributeError:
518 pass
518 pass
519 os.unlink(lockpath)
519 os.unlink(lockpath)
520 util.hidewindow()
520 util.hidewindow()
521 sys.stdout.flush()
521 sys.stdout.flush()
522 sys.stderr.flush()
522 sys.stderr.flush()
523
523
524 nullfd = os.open(os.devnull, os.O_RDWR)
524 nullfd = os.open(os.devnull, os.O_RDWR)
525 logfilefd = nullfd
525 logfilefd = nullfd
526 if logfile:
526 if logfile:
527 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
527 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
528 os.dup2(nullfd, 0)
528 os.dup2(nullfd, 0)
529 os.dup2(logfilefd, 1)
529 os.dup2(logfilefd, 1)
530 os.dup2(logfilefd, 2)
530 os.dup2(logfilefd, 2)
531 if nullfd not in (0, 1, 2):
531 if nullfd not in (0, 1, 2):
532 os.close(nullfd)
532 os.close(nullfd)
533 if logfile and logfilefd not in (0, 1, 2):
533 if logfile and logfilefd not in (0, 1, 2):
534 os.close(logfilefd)
534 os.close(logfilefd)
535
535
536 if runfn:
536 if runfn:
537 return runfn()
537 return runfn()
538
538
539 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
539 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
540 opts=None):
540 opts=None):
541 '''export changesets as hg patches.'''
541 '''export changesets as hg patches.'''
542
542
543 total = len(revs)
543 total = len(revs)
544 revwidth = max([len(str(rev)) for rev in revs])
544 revwidth = max([len(str(rev)) for rev in revs])
545 filemode = {}
545 filemode = {}
546
546
547 def single(rev, seqno, fp):
547 def single(rev, seqno, fp):
548 ctx = repo[rev]
548 ctx = repo[rev]
549 node = ctx.node()
549 node = ctx.node()
550 parents = [p.node() for p in ctx.parents() if p]
550 parents = [p.node() for p in ctx.parents() if p]
551 branch = ctx.branch()
551 branch = ctx.branch()
552 if switch_parent:
552 if switch_parent:
553 parents.reverse()
553 parents.reverse()
554 prev = (parents and parents[0]) or nullid
554 prev = (parents and parents[0]) or nullid
555
555
556 shouldclose = False
556 shouldclose = False
557 if not fp and len(template) > 0:
557 if not fp and len(template) > 0:
558 desc_lines = ctx.description().rstrip().split('\n')
558 desc_lines = ctx.description().rstrip().split('\n')
559 desc = desc_lines[0] #Commit always has a first line.
559 desc = desc_lines[0] #Commit always has a first line.
560 fp = makefileobj(repo, template, node, desc=desc, total=total,
560 fp = makefileobj(repo, template, node, desc=desc, total=total,
561 seqno=seqno, revwidth=revwidth, mode='wb',
561 seqno=seqno, revwidth=revwidth, mode='wb',
562 modemap=filemode)
562 modemap=filemode)
563 if fp != template:
563 if fp != template:
564 shouldclose = True
564 shouldclose = True
565 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
565 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
566 repo.ui.note("%s\n" % fp.name)
566 repo.ui.note("%s\n" % fp.name)
567
567
568 if not fp:
568 if not fp:
569 write = repo.ui.write
569 write = repo.ui.write
570 else:
570 else:
571 def write(s, **kw):
571 def write(s, **kw):
572 fp.write(s)
572 fp.write(s)
573
573
574
574
575 write("# HG changeset patch\n")
575 write("# HG changeset patch\n")
576 write("# User %s\n" % ctx.user())
576 write("# User %s\n" % ctx.user())
577 write("# Date %d %d\n" % ctx.date())
577 write("# Date %d %d\n" % ctx.date())
578 write("# %s\n" % util.datestr(ctx.date()))
578 write("# %s\n" % util.datestr(ctx.date()))
579 if branch and branch != 'default':
579 if branch and branch != 'default':
580 write("# Branch %s\n" % branch)
580 write("# Branch %s\n" % branch)
581 write("# Node ID %s\n" % hex(node))
581 write("# Node ID %s\n" % hex(node))
582 write("# Parent %s\n" % hex(prev))
582 write("# Parent %s\n" % hex(prev))
583 if len(parents) > 1:
583 if len(parents) > 1:
584 write("# Parent %s\n" % hex(parents[1]))
584 write("# Parent %s\n" % hex(parents[1]))
585 write(ctx.description().rstrip())
585 write(ctx.description().rstrip())
586 write("\n\n")
586 write("\n\n")
587
587
588 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
588 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
589 write(chunk, label=label)
589 write(chunk, label=label)
590
590
591 if shouldclose:
591 if shouldclose:
592 fp.close()
592 fp.close()
593
593
594 for seqno, rev in enumerate(revs):
594 for seqno, rev in enumerate(revs):
595 single(rev, seqno + 1, fp)
595 single(rev, seqno + 1, fp)
596
596
597 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
597 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
598 changes=None, stat=False, fp=None, prefix='',
598 changes=None, stat=False, fp=None, prefix='',
599 listsubrepos=False):
599 listsubrepos=False):
600 '''show diff or diffstat.'''
600 '''show diff or diffstat.'''
601 if fp is None:
601 if fp is None:
602 write = ui.write
602 write = ui.write
603 else:
603 else:
604 def write(s, **kw):
604 def write(s, **kw):
605 fp.write(s)
605 fp.write(s)
606
606
607 if stat:
607 if stat:
608 diffopts = diffopts.copy(context=0)
608 diffopts = diffopts.copy(context=0)
609 width = 80
609 width = 80
610 if not ui.plain():
610 if not ui.plain():
611 width = ui.termwidth()
611 width = ui.termwidth()
612 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
612 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
613 prefix=prefix)
613 prefix=prefix)
614 for chunk, label in patch.diffstatui(util.iterlines(chunks),
614 for chunk, label in patch.diffstatui(util.iterlines(chunks),
615 width=width,
615 width=width,
616 git=diffopts.git):
616 git=diffopts.git):
617 write(chunk, label=label)
617 write(chunk, label=label)
618 else:
618 else:
619 for chunk, label in patch.diffui(repo, node1, node2, match,
619 for chunk, label in patch.diffui(repo, node1, node2, match,
620 changes, diffopts, prefix=prefix):
620 changes, diffopts, prefix=prefix):
621 write(chunk, label=label)
621 write(chunk, label=label)
622
622
623 if listsubrepos:
623 if listsubrepos:
624 ctx1 = repo[node1]
624 ctx1 = repo[node1]
625 ctx2 = repo[node2]
625 ctx2 = repo[node2]
626 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
626 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
627 tempnode2 = node2
627 tempnode2 = node2
628 try:
628 try:
629 if node2 is not None:
629 if node2 is not None:
630 tempnode2 = ctx2.substate[subpath][1]
630 tempnode2 = ctx2.substate[subpath][1]
631 except KeyError:
631 except KeyError:
632 # A subrepo that existed in node1 was deleted between node1 and
632 # A subrepo that existed in node1 was deleted between node1 and
633 # node2 (inclusive). Thus, ctx2's substate won't contain that
633 # node2 (inclusive). Thus, ctx2's substate won't contain that
634 # subpath. The best we can do is to ignore it.
634 # subpath. The best we can do is to ignore it.
635 tempnode2 = None
635 tempnode2 = None
636 submatch = matchmod.narrowmatcher(subpath, match)
636 submatch = matchmod.narrowmatcher(subpath, match)
637 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
637 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
638 stat=stat, fp=fp, prefix=prefix)
638 stat=stat, fp=fp, prefix=prefix)
639
639
640 class changeset_printer(object):
640 class changeset_printer(object):
641 '''show changeset information when templating not requested.'''
641 '''show changeset information when templating not requested.'''
642
642
643 def __init__(self, ui, repo, patch, diffopts, buffered):
643 def __init__(self, ui, repo, patch, diffopts, buffered):
644 self.ui = ui
644 self.ui = ui
645 self.repo = repo
645 self.repo = repo
646 self.buffered = buffered
646 self.buffered = buffered
647 self.patch = patch
647 self.patch = patch
648 self.diffopts = diffopts
648 self.diffopts = diffopts
649 self.header = {}
649 self.header = {}
650 self.hunk = {}
650 self.hunk = {}
651 self.lastheader = None
651 self.lastheader = None
652 self.footer = None
652 self.footer = None
653
653
654 def flush(self, rev):
654 def flush(self, rev):
655 if rev in self.header:
655 if rev in self.header:
656 h = self.header[rev]
656 h = self.header[rev]
657 if h != self.lastheader:
657 if h != self.lastheader:
658 self.lastheader = h
658 self.lastheader = h
659 self.ui.write(h)
659 self.ui.write(h)
660 del self.header[rev]
660 del self.header[rev]
661 if rev in self.hunk:
661 if rev in self.hunk:
662 self.ui.write(self.hunk[rev])
662 self.ui.write(self.hunk[rev])
663 del self.hunk[rev]
663 del self.hunk[rev]
664 return 1
664 return 1
665 return 0
665 return 0
666
666
667 def close(self):
667 def close(self):
668 if self.footer:
668 if self.footer:
669 self.ui.write(self.footer)
669 self.ui.write(self.footer)
670
670
671 def show(self, ctx, copies=None, matchfn=None, **props):
671 def show(self, ctx, copies=None, matchfn=None, **props):
672 if self.buffered:
672 if self.buffered:
673 self.ui.pushbuffer()
673 self.ui.pushbuffer()
674 self._show(ctx, copies, matchfn, props)
674 self._show(ctx, copies, matchfn, props)
675 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
675 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
676 else:
676 else:
677 self._show(ctx, copies, matchfn, props)
677 self._show(ctx, copies, matchfn, props)
678
678
679 def _show(self, ctx, copies, matchfn, props):
679 def _show(self, ctx, copies, matchfn, props):
680 '''show a single changeset or file revision'''
680 '''show a single changeset or file revision'''
681 changenode = ctx.node()
681 changenode = ctx.node()
682 rev = ctx.rev()
682 rev = ctx.rev()
683
683
684 if self.ui.quiet:
684 if self.ui.quiet:
685 self.ui.write("%d:%s\n" % (rev, short(changenode)),
685 self.ui.write("%d:%s\n" % (rev, short(changenode)),
686 label='log.node')
686 label='log.node')
687 return
687 return
688
688
689 log = self.repo.changelog
689 log = self.repo.changelog
690 date = util.datestr(ctx.date())
690 date = util.datestr(ctx.date())
691
691
692 hexfunc = self.ui.debugflag and hex or short
692 hexfunc = self.ui.debugflag and hex or short
693
693
694 parents = [(p, hexfunc(log.node(p)))
694 parents = [(p, hexfunc(log.node(p)))
695 for p in self._meaningful_parentrevs(log, rev)]
695 for p in self._meaningful_parentrevs(log, rev)]
696
696
697 # i18n: column positioning for "hg log"
697 # i18n: column positioning for "hg log"
698 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
698 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
699 label='log.changeset changeset.%s' % ctx.phasestr())
699 label='log.changeset changeset.%s' % ctx.phasestr())
700
700
701 branch = ctx.branch()
701 branch = ctx.branch()
702 # don't show the default branch name
702 # don't show the default branch name
703 if branch != 'default':
703 if branch != 'default':
704 # i18n: column positioning for "hg log"
704 # i18n: column positioning for "hg log"
705 self.ui.write(_("branch: %s\n") % branch,
705 self.ui.write(_("branch: %s\n") % branch,
706 label='log.branch')
706 label='log.branch')
707 for bookmark in self.repo.nodebookmarks(changenode):
707 for bookmark in self.repo.nodebookmarks(changenode):
708 # i18n: column positioning for "hg log"
708 # i18n: column positioning for "hg log"
709 self.ui.write(_("bookmark: %s\n") % bookmark,
709 self.ui.write(_("bookmark: %s\n") % bookmark,
710 label='log.bookmark')
710 label='log.bookmark')
711 for tag in self.repo.nodetags(changenode):
711 for tag in self.repo.nodetags(changenode):
712 # i18n: column positioning for "hg log"
712 # i18n: column positioning for "hg log"
713 self.ui.write(_("tag: %s\n") % tag,
713 self.ui.write(_("tag: %s\n") % tag,
714 label='log.tag')
714 label='log.tag')
715 if self.ui.debugflag and ctx.phase():
715 if self.ui.debugflag and ctx.phase():
716 # i18n: column positioning for "hg log"
716 # i18n: column positioning for "hg log"
717 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
717 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
718 label='log.phase')
718 label='log.phase')
719 for parent in parents:
719 for parent in parents:
720 # i18n: column positioning for "hg log"
720 # i18n: column positioning for "hg log"
721 self.ui.write(_("parent: %d:%s\n") % parent,
721 self.ui.write(_("parent: %d:%s\n") % parent,
722 label='log.parent changeset.%s' % ctx.phasestr())
722 label='log.parent changeset.%s' % ctx.phasestr())
723
723
724 if self.ui.debugflag:
724 if self.ui.debugflag:
725 mnode = ctx.manifestnode()
725 mnode = ctx.manifestnode()
726 # i18n: column positioning for "hg log"
726 # i18n: column positioning for "hg log"
727 self.ui.write(_("manifest: %d:%s\n") %
727 self.ui.write(_("manifest: %d:%s\n") %
728 (self.repo.manifest.rev(mnode), hex(mnode)),
728 (self.repo.manifest.rev(mnode), hex(mnode)),
729 label='ui.debug log.manifest')
729 label='ui.debug log.manifest')
730 # i18n: column positioning for "hg log"
730 # i18n: column positioning for "hg log"
731 self.ui.write(_("user: %s\n") % ctx.user(),
731 self.ui.write(_("user: %s\n") % ctx.user(),
732 label='log.user')
732 label='log.user')
733 # i18n: column positioning for "hg log"
733 # i18n: column positioning for "hg log"
734 self.ui.write(_("date: %s\n") % date,
734 self.ui.write(_("date: %s\n") % date,
735 label='log.date')
735 label='log.date')
736
736
737 if self.ui.debugflag:
737 if self.ui.debugflag:
738 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
738 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
739 for key, value in zip([# i18n: column positioning for "hg log"
739 for key, value in zip([# i18n: column positioning for "hg log"
740 _("files:"),
740 _("files:"),
741 # i18n: column positioning for "hg log"
741 # i18n: column positioning for "hg log"
742 _("files+:"),
742 _("files+:"),
743 # i18n: column positioning for "hg log"
743 # i18n: column positioning for "hg log"
744 _("files-:")], files):
744 _("files-:")], files):
745 if value:
745 if value:
746 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
746 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
747 label='ui.debug log.files')
747 label='ui.debug log.files')
748 elif ctx.files() and self.ui.verbose:
748 elif ctx.files() and self.ui.verbose:
749 # i18n: column positioning for "hg log"
749 # i18n: column positioning for "hg log"
750 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
750 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
751 label='ui.note log.files')
751 label='ui.note log.files')
752 if copies and self.ui.verbose:
752 if copies and self.ui.verbose:
753 copies = ['%s (%s)' % c for c in copies]
753 copies = ['%s (%s)' % c for c in copies]
754 # i18n: column positioning for "hg log"
754 # i18n: column positioning for "hg log"
755 self.ui.write(_("copies: %s\n") % ' '.join(copies),
755 self.ui.write(_("copies: %s\n") % ' '.join(copies),
756 label='ui.note log.copies')
756 label='ui.note log.copies')
757
757
758 extra = ctx.extra()
758 extra = ctx.extra()
759 if extra and self.ui.debugflag:
759 if extra and self.ui.debugflag:
760 for key, value in sorted(extra.items()):
760 for key, value in sorted(extra.items()):
761 # i18n: column positioning for "hg log"
761 # i18n: column positioning for "hg log"
762 self.ui.write(_("extra: %s=%s\n")
762 self.ui.write(_("extra: %s=%s\n")
763 % (key, value.encode('string_escape')),
763 % (key, value.encode('string_escape')),
764 label='ui.debug log.extra')
764 label='ui.debug log.extra')
765
765
766 description = ctx.description().strip()
766 description = ctx.description().strip()
767 if description:
767 if description:
768 if self.ui.verbose:
768 if self.ui.verbose:
769 self.ui.write(_("description:\n"),
769 self.ui.write(_("description:\n"),
770 label='ui.note log.description')
770 label='ui.note log.description')
771 self.ui.write(description,
771 self.ui.write(description,
772 label='ui.note log.description')
772 label='ui.note log.description')
773 self.ui.write("\n\n")
773 self.ui.write("\n\n")
774 else:
774 else:
775 # i18n: column positioning for "hg log"
775 # i18n: column positioning for "hg log"
776 self.ui.write(_("summary: %s\n") %
776 self.ui.write(_("summary: %s\n") %
777 description.splitlines()[0],
777 description.splitlines()[0],
778 label='log.summary')
778 label='log.summary')
779 self.ui.write("\n")
779 self.ui.write("\n")
780
780
781 self.showpatch(changenode, matchfn)
781 self.showpatch(changenode, matchfn)
782
782
783 def showpatch(self, node, matchfn):
783 def showpatch(self, node, matchfn):
784 if not matchfn:
784 if not matchfn:
785 matchfn = self.patch
785 matchfn = self.patch
786 if matchfn:
786 if matchfn:
787 stat = self.diffopts.get('stat')
787 stat = self.diffopts.get('stat')
788 diff = self.diffopts.get('patch')
788 diff = self.diffopts.get('patch')
789 diffopts = patch.diffopts(self.ui, self.diffopts)
789 diffopts = patch.diffopts(self.ui, self.diffopts)
790 prev = self.repo.changelog.parents(node)[0]
790 prev = self.repo.changelog.parents(node)[0]
791 if stat:
791 if stat:
792 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
792 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
793 match=matchfn, stat=True)
793 match=matchfn, stat=True)
794 if diff:
794 if diff:
795 if stat:
795 if stat:
796 self.ui.write("\n")
796 self.ui.write("\n")
797 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
797 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
798 match=matchfn, stat=False)
798 match=matchfn, stat=False)
799 self.ui.write("\n")
799 self.ui.write("\n")
800
800
801 def _meaningful_parentrevs(self, log, rev):
801 def _meaningful_parentrevs(self, log, rev):
802 """Return list of meaningful (or all if debug) parentrevs for rev.
802 """Return list of meaningful (or all if debug) parentrevs for rev.
803
803
804 For merges (two non-nullrev revisions) both parents are meaningful.
804 For merges (two non-nullrev revisions) both parents are meaningful.
805 Otherwise the first parent revision is considered meaningful if it
805 Otherwise the first parent revision is considered meaningful if it
806 is not the preceding revision.
806 is not the preceding revision.
807 """
807 """
808 parents = log.parentrevs(rev)
808 parents = log.parentrevs(rev)
809 if not self.ui.debugflag and parents[1] == nullrev:
809 if not self.ui.debugflag and parents[1] == nullrev:
810 if parents[0] >= rev - 1:
810 if parents[0] >= rev - 1:
811 parents = []
811 parents = []
812 else:
812 else:
813 parents = [parents[0]]
813 parents = [parents[0]]
814 return parents
814 return parents
815
815
816
816
817 class changeset_templater(changeset_printer):
817 class changeset_templater(changeset_printer):
818 '''format changeset information.'''
818 '''format changeset information.'''
819
819
820 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
820 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
821 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
821 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
822 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
822 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
823 defaulttempl = {
823 defaulttempl = {
824 'parent': '{rev}:{node|formatnode} ',
824 'parent': '{rev}:{node|formatnode} ',
825 'manifest': '{rev}:{node|formatnode}',
825 'manifest': '{rev}:{node|formatnode}',
826 'file_copy': '{name} ({source})',
826 'file_copy': '{name} ({source})',
827 'extra': '{key}={value|stringescape}'
827 'extra': '{key}={value|stringescape}'
828 }
828 }
829 # filecopy is preserved for compatibility reasons
829 # filecopy is preserved for compatibility reasons
830 defaulttempl['filecopy'] = defaulttempl['file_copy']
830 defaulttempl['filecopy'] = defaulttempl['file_copy']
831 self.t = templater.templater(mapfile, {'formatnode': formatnode},
831 self.t = templater.templater(mapfile, {'formatnode': formatnode},
832 cache=defaulttempl)
832 cache=defaulttempl)
833 self.cache = {}
833 self.cache = {}
834
834
835 def use_template(self, t):
835 def use_template(self, t):
836 '''set template string to use'''
836 '''set template string to use'''
837 self.t.cache['changeset'] = t
837 self.t.cache['changeset'] = t
838
838
839 def _meaningful_parentrevs(self, ctx):
839 def _meaningful_parentrevs(self, ctx):
840 """Return list of meaningful (or all if debug) parentrevs for rev.
840 """Return list of meaningful (or all if debug) parentrevs for rev.
841 """
841 """
842 parents = ctx.parents()
842 parents = ctx.parents()
843 if len(parents) > 1:
843 if len(parents) > 1:
844 return parents
844 return parents
845 if self.ui.debugflag:
845 if self.ui.debugflag:
846 return [parents[0], self.repo['null']]
846 return [parents[0], self.repo['null']]
847 if parents[0].rev() >= ctx.rev() - 1:
847 if parents[0].rev() >= ctx.rev() - 1:
848 return []
848 return []
849 return parents
849 return parents
850
850
851 def _show(self, ctx, copies, matchfn, props):
851 def _show(self, ctx, copies, matchfn, props):
852 '''show a single changeset or file revision'''
852 '''show a single changeset or file revision'''
853
853
854 showlist = templatekw.showlist
854 showlist = templatekw.showlist
855
855
856 # showparents() behaviour depends on ui trace level which
856 # showparents() behaviour depends on ui trace level which
857 # causes unexpected behaviours at templating level and makes
857 # causes unexpected behaviours at templating level and makes
858 # it harder to extract it in a standalone function. Its
858 # it harder to extract it in a standalone function. Its
859 # behaviour cannot be changed so leave it here for now.
859 # behaviour cannot be changed so leave it here for now.
860 def showparents(**args):
860 def showparents(**args):
861 ctx = args['ctx']
861 ctx = args['ctx']
862 parents = [[('rev', p.rev()), ('node', p.hex())]
862 parents = [[('rev', p.rev()), ('node', p.hex())]
863 for p in self._meaningful_parentrevs(ctx)]
863 for p in self._meaningful_parentrevs(ctx)]
864 return showlist('parent', parents, **args)
864 return showlist('parent', parents, **args)
865
865
866 props = props.copy()
866 props = props.copy()
867 props.update(templatekw.keywords)
867 props.update(templatekw.keywords)
868 props['parents'] = showparents
868 props['parents'] = showparents
869 props['templ'] = self.t
869 props['templ'] = self.t
870 props['ctx'] = ctx
870 props['ctx'] = ctx
871 props['repo'] = self.repo
871 props['repo'] = self.repo
872 props['revcache'] = {'copies': copies}
872 props['revcache'] = {'copies': copies}
873 props['cache'] = self.cache
873 props['cache'] = self.cache
874
874
875 # find correct templates for current mode
875 # find correct templates for current mode
876
876
877 tmplmodes = [
877 tmplmodes = [
878 (True, None),
878 (True, None),
879 (self.ui.verbose, 'verbose'),
879 (self.ui.verbose, 'verbose'),
880 (self.ui.quiet, 'quiet'),
880 (self.ui.quiet, 'quiet'),
881 (self.ui.debugflag, 'debug'),
881 (self.ui.debugflag, 'debug'),
882 ]
882 ]
883
883
884 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
884 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
885 for mode, postfix in tmplmodes:
885 for mode, postfix in tmplmodes:
886 for type in types:
886 for type in types:
887 cur = postfix and ('%s_%s' % (type, postfix)) or type
887 cur = postfix and ('%s_%s' % (type, postfix)) or type
888 if mode and cur in self.t:
888 if mode and cur in self.t:
889 types[type] = cur
889 types[type] = cur
890
890
891 try:
891 try:
892
892
893 # write header
893 # write header
894 if types['header']:
894 if types['header']:
895 h = templater.stringify(self.t(types['header'], **props))
895 h = templater.stringify(self.t(types['header'], **props))
896 if self.buffered:
896 if self.buffered:
897 self.header[ctx.rev()] = h
897 self.header[ctx.rev()] = h
898 else:
898 else:
899 if self.lastheader != h:
899 if self.lastheader != h:
900 self.lastheader = h
900 self.lastheader = h
901 self.ui.write(h)
901 self.ui.write(h)
902
902
903 # write changeset metadata, then patch if requested
903 # write changeset metadata, then patch if requested
904 key = types['changeset']
904 key = types['changeset']
905 self.ui.write(templater.stringify(self.t(key, **props)))
905 self.ui.write(templater.stringify(self.t(key, **props)))
906 self.showpatch(ctx.node(), matchfn)
906 self.showpatch(ctx.node(), matchfn)
907
907
908 if types['footer']:
908 if types['footer']:
909 if not self.footer:
909 if not self.footer:
910 self.footer = templater.stringify(self.t(types['footer'],
910 self.footer = templater.stringify(self.t(types['footer'],
911 **props))
911 **props))
912
912
913 except KeyError, inst:
913 except KeyError, inst:
914 msg = _("%s: no key named '%s'")
914 msg = _("%s: no key named '%s'")
915 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
915 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
916 except SyntaxError, inst:
916 except SyntaxError, inst:
917 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
917 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
918
918
919 def show_changeset(ui, repo, opts, buffered=False):
919 def show_changeset(ui, repo, opts, buffered=False):
920 """show one changeset using template or regular display.
920 """show one changeset using template or regular display.
921
921
922 Display format will be the first non-empty hit of:
922 Display format will be the first non-empty hit of:
923 1. option 'template'
923 1. option 'template'
924 2. option 'style'
924 2. option 'style'
925 3. [ui] setting 'logtemplate'
925 3. [ui] setting 'logtemplate'
926 4. [ui] setting 'style'
926 4. [ui] setting 'style'
927 If all of these values are either the unset or the empty string,
927 If all of these values are either the unset or the empty string,
928 regular display via changeset_printer() is done.
928 regular display via changeset_printer() is done.
929 """
929 """
930 # options
930 # options
931 patch = False
931 patch = False
932 if opts.get('patch') or opts.get('stat'):
932 if opts.get('patch') or opts.get('stat'):
933 patch = scmutil.matchall(repo)
933 patch = scmutil.matchall(repo)
934
934
935 tmpl = opts.get('template')
935 tmpl = opts.get('template')
936 style = None
936 style = None
937 if tmpl:
937 if tmpl:
938 tmpl = templater.parsestring(tmpl, quoted=False)
938 tmpl = templater.parsestring(tmpl, quoted=False)
939 else:
939 else:
940 style = opts.get('style')
940 style = opts.get('style')
941
941
942 # ui settings
942 # ui settings
943 if not (tmpl or style):
943 if not (tmpl or style):
944 tmpl = ui.config('ui', 'logtemplate')
944 tmpl = ui.config('ui', 'logtemplate')
945 if tmpl:
945 if tmpl:
946 try:
946 try:
947 tmpl = templater.parsestring(tmpl)
947 tmpl = templater.parsestring(tmpl)
948 except SyntaxError:
948 except SyntaxError:
949 tmpl = templater.parsestring(tmpl, quoted=False)
949 tmpl = templater.parsestring(tmpl, quoted=False)
950 else:
950 else:
951 style = util.expandpath(ui.config('ui', 'style', ''))
951 style = util.expandpath(ui.config('ui', 'style', ''))
952
952
953 if not (tmpl or style):
953 if not (tmpl or style):
954 return changeset_printer(ui, repo, patch, opts, buffered)
954 return changeset_printer(ui, repo, patch, opts, buffered)
955
955
956 mapfile = None
956 mapfile = None
957 if style and not tmpl:
957 if style and not tmpl:
958 mapfile = style
958 mapfile = style
959 if not os.path.split(mapfile)[0]:
959 if not os.path.split(mapfile)[0]:
960 mapname = (templater.templatepath('map-cmdline.' + mapfile)
960 mapname = (templater.templatepath('map-cmdline.' + mapfile)
961 or templater.templatepath(mapfile))
961 or templater.templatepath(mapfile))
962 if mapname:
962 if mapname:
963 mapfile = mapname
963 mapfile = mapname
964
964
965 try:
965 try:
966 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
966 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
967 except SyntaxError, inst:
967 except SyntaxError, inst:
968 raise util.Abort(inst.args[0])
968 raise util.Abort(inst.args[0])
969 if tmpl:
969 if tmpl:
970 t.use_template(tmpl)
970 t.use_template(tmpl)
971 return t
971 return t
972
972
973 def finddate(ui, repo, date):
973 def finddate(ui, repo, date):
974 """Find the tipmost changeset that matches the given date spec"""
974 """Find the tipmost changeset that matches the given date spec"""
975
975
976 df = util.matchdate(date)
976 df = util.matchdate(date)
977 m = scmutil.matchall(repo)
977 m = scmutil.matchall(repo)
978 results = {}
978 results = {}
979
979
980 def prep(ctx, fns):
980 def prep(ctx, fns):
981 d = ctx.date()
981 d = ctx.date()
982 if df(d[0]):
982 if df(d[0]):
983 results[ctx.rev()] = d
983 results[ctx.rev()] = d
984
984
985 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
985 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
986 rev = ctx.rev()
986 rev = ctx.rev()
987 if rev in results:
987 if rev in results:
988 ui.status(_("found revision %s from %s\n") %
988 ui.status(_("found revision %s from %s\n") %
989 (rev, util.datestr(results[rev])))
989 (rev, util.datestr(results[rev])))
990 return str(rev)
990 return str(rev)
991
991
992 raise util.Abort(_("revision matching date not found"))
992 raise util.Abort(_("revision matching date not found"))
993
993
994 def increasingwindows(start, end, windowsize=8, sizelimit=512):
994 def increasingwindows(start, end, windowsize=8, sizelimit=512):
995 if start < end:
995 if start < end:
996 while start < end:
996 while start < end:
997 yield start, min(windowsize, end - start)
997 yield start, min(windowsize, end - start)
998 start += windowsize
998 start += windowsize
999 if windowsize < sizelimit:
999 if windowsize < sizelimit:
1000 windowsize *= 2
1000 windowsize *= 2
1001 else:
1001 else:
1002 while start > end:
1002 while start > end:
1003 yield start, min(windowsize, start - end - 1)
1003 yield start, min(windowsize, start - end - 1)
1004 start -= windowsize
1004 start -= windowsize
1005 if windowsize < sizelimit:
1005 if windowsize < sizelimit:
1006 windowsize *= 2
1006 windowsize *= 2
1007
1007
1008 def walkchangerevs(repo, match, opts, prepare):
1008 def walkchangerevs(repo, match, opts, prepare):
1009 '''Iterate over files and the revs in which they changed.
1009 '''Iterate over files and the revs in which they changed.
1010
1010
1011 Callers most commonly need to iterate backwards over the history
1011 Callers most commonly need to iterate backwards over the history
1012 in which they are interested. Doing so has awful (quadratic-looking)
1012 in which they are interested. Doing so has awful (quadratic-looking)
1013 performance, so we use iterators in a "windowed" way.
1013 performance, so we use iterators in a "windowed" way.
1014
1014
1015 We walk a window of revisions in the desired order. Within the
1015 We walk a window of revisions in the desired order. Within the
1016 window, we first walk forwards to gather data, then in the desired
1016 window, we first walk forwards to gather data, then in the desired
1017 order (usually backwards) to display it.
1017 order (usually backwards) to display it.
1018
1018
1019 This function returns an iterator yielding contexts. Before
1019 This function returns an iterator yielding contexts. Before
1020 yielding each context, the iterator will first call the prepare
1020 yielding each context, the iterator will first call the prepare
1021 function on each context in the window in forward order.'''
1021 function on each context in the window in forward order.'''
1022
1022
1023 follow = opts.get('follow') or opts.get('follow_first')
1023 follow = opts.get('follow') or opts.get('follow_first')
1024
1024
1025 if not len(repo):
1026 return []
1027 if opts.get('rev'):
1025 if opts.get('rev'):
1028 revs = scmutil.revrange(repo, opts.get('rev'))
1026 revs = scmutil.revrange(repo, opts.get('rev'))
1029 elif follow:
1027 elif follow:
1030 revs = repo.revs('reverse(:.)')
1028 revs = repo.revs('reverse(:.)')
1031 else:
1029 else:
1032 revs = list(repo)
1030 revs = list(repo)
1033 revs.reverse()
1031 revs.reverse()
1034 if not revs:
1032 if not revs:
1035 return []
1033 return []
1036 wanted = set()
1034 wanted = set()
1037 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1035 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1038 fncache = {}
1036 fncache = {}
1039 change = repo.changectx
1037 change = repo.changectx
1040
1038
1041 # First step is to fill wanted, the set of revisions that we want to yield.
1039 # First step is to fill wanted, the set of revisions that we want to yield.
1042 # When it does not induce extra cost, we also fill fncache for revisions in
1040 # When it does not induce extra cost, we also fill fncache for revisions in
1043 # wanted: a cache of filenames that were changed (ctx.files()) and that
1041 # wanted: a cache of filenames that were changed (ctx.files()) and that
1044 # match the file filtering conditions.
1042 # match the file filtering conditions.
1045
1043
1046 if not slowpath and not match.files():
1044 if not slowpath and not match.files():
1047 # No files, no patterns. Display all revs.
1045 # No files, no patterns. Display all revs.
1048 wanted = set(revs)
1046 wanted = set(revs)
1049 copies = []
1047 copies = []
1050
1048
1051 if not slowpath and match.files():
1049 if not slowpath and match.files():
1052 # We only have to read through the filelog to find wanted revisions
1050 # We only have to read through the filelog to find wanted revisions
1053
1051
1054 minrev, maxrev = min(revs), max(revs)
1052 minrev, maxrev = min(revs), max(revs)
1055 def filerevgen(filelog, last):
1053 def filerevgen(filelog, last):
1056 """
1054 """
1057 Only files, no patterns. Check the history of each file.
1055 Only files, no patterns. Check the history of each file.
1058
1056
1059 Examines filelog entries within minrev, maxrev linkrev range
1057 Examines filelog entries within minrev, maxrev linkrev range
1060 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1058 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1061 tuples in backwards order
1059 tuples in backwards order
1062 """
1060 """
1063 cl_count = len(repo)
1061 cl_count = len(repo)
1064 revs = []
1062 revs = []
1065 for j in xrange(0, last + 1):
1063 for j in xrange(0, last + 1):
1066 linkrev = filelog.linkrev(j)
1064 linkrev = filelog.linkrev(j)
1067 if linkrev < minrev:
1065 if linkrev < minrev:
1068 continue
1066 continue
1069 # only yield rev for which we have the changelog, it can
1067 # only yield rev for which we have the changelog, it can
1070 # happen while doing "hg log" during a pull or commit
1068 # happen while doing "hg log" during a pull or commit
1071 if linkrev >= cl_count:
1069 if linkrev >= cl_count:
1072 break
1070 break
1073
1071
1074 parentlinkrevs = []
1072 parentlinkrevs = []
1075 for p in filelog.parentrevs(j):
1073 for p in filelog.parentrevs(j):
1076 if p != nullrev:
1074 if p != nullrev:
1077 parentlinkrevs.append(filelog.linkrev(p))
1075 parentlinkrevs.append(filelog.linkrev(p))
1078 n = filelog.node(j)
1076 n = filelog.node(j)
1079 revs.append((linkrev, parentlinkrevs,
1077 revs.append((linkrev, parentlinkrevs,
1080 follow and filelog.renamed(n)))
1078 follow and filelog.renamed(n)))
1081
1079
1082 return reversed(revs)
1080 return reversed(revs)
1083 def iterfiles():
1081 def iterfiles():
1084 pctx = repo['.']
1082 pctx = repo['.']
1085 for filename in match.files():
1083 for filename in match.files():
1086 if follow:
1084 if follow:
1087 if filename not in pctx:
1085 if filename not in pctx:
1088 raise util.Abort(_('cannot follow file not in parent '
1086 raise util.Abort(_('cannot follow file not in parent '
1089 'revision: "%s"') % filename)
1087 'revision: "%s"') % filename)
1090 yield filename, pctx[filename].filenode()
1088 yield filename, pctx[filename].filenode()
1091 else:
1089 else:
1092 yield filename, None
1090 yield filename, None
1093 for filename_node in copies:
1091 for filename_node in copies:
1094 yield filename_node
1092 yield filename_node
1095 for file_, node in iterfiles():
1093 for file_, node in iterfiles():
1096 filelog = repo.file(file_)
1094 filelog = repo.file(file_)
1097 if not len(filelog):
1095 if not len(filelog):
1098 if node is None:
1096 if node is None:
1099 # A zero count may be a directory or deleted file, so
1097 # A zero count may be a directory or deleted file, so
1100 # try to find matching entries on the slow path.
1098 # try to find matching entries on the slow path.
1101 if follow:
1099 if follow:
1102 raise util.Abort(
1100 raise util.Abort(
1103 _('cannot follow nonexistent file: "%s"') % file_)
1101 _('cannot follow nonexistent file: "%s"') % file_)
1104 slowpath = True
1102 slowpath = True
1105 break
1103 break
1106 else:
1104 else:
1107 continue
1105 continue
1108
1106
1109 if node is None:
1107 if node is None:
1110 last = len(filelog) - 1
1108 last = len(filelog) - 1
1111 else:
1109 else:
1112 last = filelog.rev(node)
1110 last = filelog.rev(node)
1113
1111
1114
1112
1115 # keep track of all ancestors of the file
1113 # keep track of all ancestors of the file
1116 ancestors = set([filelog.linkrev(last)])
1114 ancestors = set([filelog.linkrev(last)])
1117
1115
1118 # iterate from latest to oldest revision
1116 # iterate from latest to oldest revision
1119 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1117 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1120 if not follow:
1118 if not follow:
1121 if rev > maxrev:
1119 if rev > maxrev:
1122 continue
1120 continue
1123 else:
1121 else:
1124 # Note that last might not be the first interesting
1122 # Note that last might not be the first interesting
1125 # rev to us:
1123 # rev to us:
1126 # if the file has been changed after maxrev, we'll
1124 # if the file has been changed after maxrev, we'll
1127 # have linkrev(last) > maxrev, and we still need
1125 # have linkrev(last) > maxrev, and we still need
1128 # to explore the file graph
1126 # to explore the file graph
1129 if rev not in ancestors:
1127 if rev not in ancestors:
1130 continue
1128 continue
1131 # XXX insert 1327 fix here
1129 # XXX insert 1327 fix here
1132 if flparentlinkrevs:
1130 if flparentlinkrevs:
1133 ancestors.update(flparentlinkrevs)
1131 ancestors.update(flparentlinkrevs)
1134
1132
1135 fncache.setdefault(rev, []).append(file_)
1133 fncache.setdefault(rev, []).append(file_)
1136 wanted.add(rev)
1134 wanted.add(rev)
1137 if copied:
1135 if copied:
1138 copies.append(copied)
1136 copies.append(copied)
1139
1137
1140 # We decided to fall back to the slowpath because at least one
1138 # We decided to fall back to the slowpath because at least one
1141 # of the paths was not a file. Check to see if at least one of them
1139 # of the paths was not a file. Check to see if at least one of them
1142 # existed in history, otherwise simply return
1140 # existed in history, otherwise simply return
1143 if slowpath:
1141 if slowpath:
1144 for path in match.files():
1142 for path in match.files():
1145 if path == '.' or path in repo.store:
1143 if path == '.' or path in repo.store:
1146 break
1144 break
1147 else:
1145 else:
1148 return []
1146 return []
1149
1147
1150 if slowpath:
1148 if slowpath:
1151 # We have to read the changelog to match filenames against
1149 # We have to read the changelog to match filenames against
1152 # changed files
1150 # changed files
1153
1151
1154 if follow:
1152 if follow:
1155 raise util.Abort(_('can only follow copies/renames for explicit '
1153 raise util.Abort(_('can only follow copies/renames for explicit '
1156 'filenames'))
1154 'filenames'))
1157
1155
1158 # The slow path checks files modified in every changeset.
1156 # The slow path checks files modified in every changeset.
1159 for i in sorted(revs):
1157 for i in sorted(revs):
1160 ctx = change(i)
1158 ctx = change(i)
1161 matches = filter(match, ctx.files())
1159 matches = filter(match, ctx.files())
1162 if matches:
1160 if matches:
1163 fncache[i] = matches
1161 fncache[i] = matches
1164 wanted.add(i)
1162 wanted.add(i)
1165
1163
1166 class followfilter(object):
1164 class followfilter(object):
1167 def __init__(self, onlyfirst=False):
1165 def __init__(self, onlyfirst=False):
1168 self.startrev = nullrev
1166 self.startrev = nullrev
1169 self.roots = set()
1167 self.roots = set()
1170 self.onlyfirst = onlyfirst
1168 self.onlyfirst = onlyfirst
1171
1169
1172 def match(self, rev):
1170 def match(self, rev):
1173 def realparents(rev):
1171 def realparents(rev):
1174 if self.onlyfirst:
1172 if self.onlyfirst:
1175 return repo.changelog.parentrevs(rev)[0:1]
1173 return repo.changelog.parentrevs(rev)[0:1]
1176 else:
1174 else:
1177 return filter(lambda x: x != nullrev,
1175 return filter(lambda x: x != nullrev,
1178 repo.changelog.parentrevs(rev))
1176 repo.changelog.parentrevs(rev))
1179
1177
1180 if self.startrev == nullrev:
1178 if self.startrev == nullrev:
1181 self.startrev = rev
1179 self.startrev = rev
1182 return True
1180 return True
1183
1181
1184 if rev > self.startrev:
1182 if rev > self.startrev:
1185 # forward: all descendants
1183 # forward: all descendants
1186 if not self.roots:
1184 if not self.roots:
1187 self.roots.add(self.startrev)
1185 self.roots.add(self.startrev)
1188 for parent in realparents(rev):
1186 for parent in realparents(rev):
1189 if parent in self.roots:
1187 if parent in self.roots:
1190 self.roots.add(rev)
1188 self.roots.add(rev)
1191 return True
1189 return True
1192 else:
1190 else:
1193 # backwards: all parents
1191 # backwards: all parents
1194 if not self.roots:
1192 if not self.roots:
1195 self.roots.update(realparents(self.startrev))
1193 self.roots.update(realparents(self.startrev))
1196 if rev in self.roots:
1194 if rev in self.roots:
1197 self.roots.remove(rev)
1195 self.roots.remove(rev)
1198 self.roots.update(realparents(rev))
1196 self.roots.update(realparents(rev))
1199 return True
1197 return True
1200
1198
1201 return False
1199 return False
1202
1200
1203 # it might be worthwhile to do this in the iterator if the rev range
1201 # it might be worthwhile to do this in the iterator if the rev range
1204 # is descending and the prune args are all within that range
1202 # is descending and the prune args are all within that range
1205 for rev in opts.get('prune', ()):
1203 for rev in opts.get('prune', ()):
1206 rev = repo[rev].rev()
1204 rev = repo[rev].rev()
1207 ff = followfilter()
1205 ff = followfilter()
1208 stop = min(revs[0], revs[-1])
1206 stop = min(revs[0], revs[-1])
1209 for x in xrange(rev, stop - 1, -1):
1207 for x in xrange(rev, stop - 1, -1):
1210 if ff.match(x):
1208 if ff.match(x):
1211 wanted.discard(x)
1209 wanted.discard(x)
1212
1210
1213 # Choose a small initial window if we will probably only visit a
1211 # Choose a small initial window if we will probably only visit a
1214 # few commits.
1212 # few commits.
1215 limit = loglimit(opts)
1213 limit = loglimit(opts)
1216 windowsize = 8
1214 windowsize = 8
1217 if limit:
1215 if limit:
1218 windowsize = min(limit, windowsize)
1216 windowsize = min(limit, windowsize)
1219
1217
1220 # Now that wanted is correctly initialized, we can iterate over the
1218 # Now that wanted is correctly initialized, we can iterate over the
1221 # revision range, yielding only revisions in wanted.
1219 # revision range, yielding only revisions in wanted.
1222 def iterate():
1220 def iterate():
1223 if follow and not match.files():
1221 if follow and not match.files():
1224 ff = followfilter(onlyfirst=opts.get('follow_first'))
1222 ff = followfilter(onlyfirst=opts.get('follow_first'))
1225 def want(rev):
1223 def want(rev):
1226 return ff.match(rev) and rev in wanted
1224 return ff.match(rev) and rev in wanted
1227 else:
1225 else:
1228 def want(rev):
1226 def want(rev):
1229 return rev in wanted
1227 return rev in wanted
1230
1228
1231 for i, window in increasingwindows(0, len(revs), windowsize):
1229 for i, window in increasingwindows(0, len(revs), windowsize):
1232 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1230 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1233 for rev in sorted(nrevs):
1231 for rev in sorted(nrevs):
1234 fns = fncache.get(rev)
1232 fns = fncache.get(rev)
1235 ctx = change(rev)
1233 ctx = change(rev)
1236 if not fns:
1234 if not fns:
1237 def fns_generator():
1235 def fns_generator():
1238 for f in ctx.files():
1236 for f in ctx.files():
1239 if match(f):
1237 if match(f):
1240 yield f
1238 yield f
1241 fns = fns_generator()
1239 fns = fns_generator()
1242 prepare(ctx, fns)
1240 prepare(ctx, fns)
1243 for rev in nrevs:
1241 for rev in nrevs:
1244 yield change(rev)
1242 yield change(rev)
1245 return iterate()
1243 return iterate()
1246
1244
1247 def _makegraphfilematcher(repo, pats, followfirst):
1245 def _makegraphfilematcher(repo, pats, followfirst):
1248 # When displaying a revision with --patch --follow FILE, we have
1246 # When displaying a revision with --patch --follow FILE, we have
1249 # to know which file of the revision must be diffed. With
1247 # to know which file of the revision must be diffed. With
1250 # --follow, we want the names of the ancestors of FILE in the
1248 # --follow, we want the names of the ancestors of FILE in the
1251 # revision, stored in "fcache". "fcache" is populated by
1249 # revision, stored in "fcache". "fcache" is populated by
1252 # reproducing the graph traversal already done by --follow revset
1250 # reproducing the graph traversal already done by --follow revset
1253 # and relating linkrevs to file names (which is not "correct" but
1251 # and relating linkrevs to file names (which is not "correct" but
1254 # good enough).
1252 # good enough).
1255 fcache = {}
1253 fcache = {}
1256 fcacheready = [False]
1254 fcacheready = [False]
1257 pctx = repo['.']
1255 pctx = repo['.']
1258 wctx = repo[None]
1256 wctx = repo[None]
1259
1257
1260 def populate():
1258 def populate():
1261 for fn in pats:
1259 for fn in pats:
1262 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1260 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1263 for c in i:
1261 for c in i:
1264 fcache.setdefault(c.linkrev(), set()).add(c.path())
1262 fcache.setdefault(c.linkrev(), set()).add(c.path())
1265
1263
1266 def filematcher(rev):
1264 def filematcher(rev):
1267 if not fcacheready[0]:
1265 if not fcacheready[0]:
1268 # Lazy initialization
1266 # Lazy initialization
1269 fcacheready[0] = True
1267 fcacheready[0] = True
1270 populate()
1268 populate()
1271 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1269 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1272
1270
1273 return filematcher
1271 return filematcher
1274
1272
1275 def _makegraphlogrevset(repo, pats, opts, revs):
1273 def _makegraphlogrevset(repo, pats, opts, revs):
1276 """Return (expr, filematcher) where expr is a revset string built
1274 """Return (expr, filematcher) where expr is a revset string built
1277 from log options and file patterns or None. If --stat or --patch
1275 from log options and file patterns or None. If --stat or --patch
1278 are not passed filematcher is None. Otherwise it is a callable
1276 are not passed filematcher is None. Otherwise it is a callable
1279 taking a revision number and returning a match objects filtering
1277 taking a revision number and returning a match objects filtering
1280 the files to be detailed when displaying the revision.
1278 the files to be detailed when displaying the revision.
1281 """
1279 """
1282 opt2revset = {
1280 opt2revset = {
1283 'no_merges': ('not merge()', None),
1281 'no_merges': ('not merge()', None),
1284 'only_merges': ('merge()', None),
1282 'only_merges': ('merge()', None),
1285 '_ancestors': ('ancestors(%(val)s)', None),
1283 '_ancestors': ('ancestors(%(val)s)', None),
1286 '_fancestors': ('_firstancestors(%(val)s)', None),
1284 '_fancestors': ('_firstancestors(%(val)s)', None),
1287 '_descendants': ('descendants(%(val)s)', None),
1285 '_descendants': ('descendants(%(val)s)', None),
1288 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1286 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1289 '_matchfiles': ('_matchfiles(%(val)s)', None),
1287 '_matchfiles': ('_matchfiles(%(val)s)', None),
1290 'date': ('date(%(val)r)', None),
1288 'date': ('date(%(val)r)', None),
1291 'branch': ('branch(%(val)r)', ' or '),
1289 'branch': ('branch(%(val)r)', ' or '),
1292 '_patslog': ('filelog(%(val)r)', ' or '),
1290 '_patslog': ('filelog(%(val)r)', ' or '),
1293 '_patsfollow': ('follow(%(val)r)', ' or '),
1291 '_patsfollow': ('follow(%(val)r)', ' or '),
1294 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1292 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1295 'keyword': ('keyword(%(val)r)', ' or '),
1293 'keyword': ('keyword(%(val)r)', ' or '),
1296 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1294 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1297 'user': ('user(%(val)r)', ' or '),
1295 'user': ('user(%(val)r)', ' or '),
1298 }
1296 }
1299
1297
1300 opts = dict(opts)
1298 opts = dict(opts)
1301 # follow or not follow?
1299 # follow or not follow?
1302 follow = opts.get('follow') or opts.get('follow_first')
1300 follow = opts.get('follow') or opts.get('follow_first')
1303 followfirst = opts.get('follow_first') and 1 or 0
1301 followfirst = opts.get('follow_first') and 1 or 0
1304 # --follow with FILE behaviour depends on revs...
1302 # --follow with FILE behaviour depends on revs...
1305 startrev = revs[0]
1303 startrev = revs[0]
1306 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1304 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1307
1305
1308 # branch and only_branch are really aliases and must be handled at
1306 # branch and only_branch are really aliases and must be handled at
1309 # the same time
1307 # the same time
1310 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1308 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1311 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1309 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1312 # pats/include/exclude are passed to match.match() directly in
1310 # pats/include/exclude are passed to match.match() directly in
1313 # _matchfiles() revset but walkchangerevs() builds its matcher with
1311 # _matchfiles() revset but walkchangerevs() builds its matcher with
1314 # scmutil.match(). The difference is input pats are globbed on
1312 # scmutil.match(). The difference is input pats are globbed on
1315 # platforms without shell expansion (windows).
1313 # platforms without shell expansion (windows).
1316 pctx = repo[None]
1314 pctx = repo[None]
1317 match, pats = scmutil.matchandpats(pctx, pats, opts)
1315 match, pats = scmutil.matchandpats(pctx, pats, opts)
1318 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1316 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1319 if not slowpath:
1317 if not slowpath:
1320 for f in match.files():
1318 for f in match.files():
1321 if follow and f not in pctx:
1319 if follow and f not in pctx:
1322 raise util.Abort(_('cannot follow file not in parent '
1320 raise util.Abort(_('cannot follow file not in parent '
1323 'revision: "%s"') % f)
1321 'revision: "%s"') % f)
1324 filelog = repo.file(f)
1322 filelog = repo.file(f)
1325 if not len(filelog):
1323 if not len(filelog):
1326 # A zero count may be a directory or deleted file, so
1324 # A zero count may be a directory or deleted file, so
1327 # try to find matching entries on the slow path.
1325 # try to find matching entries on the slow path.
1328 if follow:
1326 if follow:
1329 raise util.Abort(
1327 raise util.Abort(
1330 _('cannot follow nonexistent file: "%s"') % f)
1328 _('cannot follow nonexistent file: "%s"') % f)
1331 slowpath = True
1329 slowpath = True
1332
1330
1333 # We decided to fall back to the slowpath because at least one
1331 # We decided to fall back to the slowpath because at least one
1334 # of the paths was not a file. Check to see if at least one of them
1332 # of the paths was not a file. Check to see if at least one of them
1335 # existed in history - in that case, we'll continue down the
1333 # existed in history - in that case, we'll continue down the
1336 # slowpath; otherwise, we can turn off the slowpath
1334 # slowpath; otherwise, we can turn off the slowpath
1337 if slowpath:
1335 if slowpath:
1338 for path in match.files():
1336 for path in match.files():
1339 if path == '.' or path in repo.store:
1337 if path == '.' or path in repo.store:
1340 break
1338 break
1341 else:
1339 else:
1342 slowpath = False
1340 slowpath = False
1343
1341
1344 if slowpath:
1342 if slowpath:
1345 # See walkchangerevs() slow path.
1343 # See walkchangerevs() slow path.
1346 #
1344 #
1347 if follow:
1345 if follow:
1348 raise util.Abort(_('can only follow copies/renames for explicit '
1346 raise util.Abort(_('can only follow copies/renames for explicit '
1349 'filenames'))
1347 'filenames'))
1350 # pats/include/exclude cannot be represented as separate
1348 # pats/include/exclude cannot be represented as separate
1351 # revset expressions as their filtering logic applies at file
1349 # revset expressions as their filtering logic applies at file
1352 # level. For instance "-I a -X a" matches a revision touching
1350 # level. For instance "-I a -X a" matches a revision touching
1353 # "a" and "b" while "file(a) and not file(b)" does
1351 # "a" and "b" while "file(a) and not file(b)" does
1354 # not. Besides, filesets are evaluated against the working
1352 # not. Besides, filesets are evaluated against the working
1355 # directory.
1353 # directory.
1356 matchargs = ['r:', 'd:relpath']
1354 matchargs = ['r:', 'd:relpath']
1357 for p in pats:
1355 for p in pats:
1358 matchargs.append('p:' + p)
1356 matchargs.append('p:' + p)
1359 for p in opts.get('include', []):
1357 for p in opts.get('include', []):
1360 matchargs.append('i:' + p)
1358 matchargs.append('i:' + p)
1361 for p in opts.get('exclude', []):
1359 for p in opts.get('exclude', []):
1362 matchargs.append('x:' + p)
1360 matchargs.append('x:' + p)
1363 matchargs = ','.join(('%r' % p) for p in matchargs)
1361 matchargs = ','.join(('%r' % p) for p in matchargs)
1364 opts['_matchfiles'] = matchargs
1362 opts['_matchfiles'] = matchargs
1365 else:
1363 else:
1366 if follow:
1364 if follow:
1367 fpats = ('_patsfollow', '_patsfollowfirst')
1365 fpats = ('_patsfollow', '_patsfollowfirst')
1368 fnopats = (('_ancestors', '_fancestors'),
1366 fnopats = (('_ancestors', '_fancestors'),
1369 ('_descendants', '_fdescendants'))
1367 ('_descendants', '_fdescendants'))
1370 if pats:
1368 if pats:
1371 # follow() revset interprets its file argument as a
1369 # follow() revset interprets its file argument as a
1372 # manifest entry, so use match.files(), not pats.
1370 # manifest entry, so use match.files(), not pats.
1373 opts[fpats[followfirst]] = list(match.files())
1371 opts[fpats[followfirst]] = list(match.files())
1374 else:
1372 else:
1375 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1373 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1376 else:
1374 else:
1377 opts['_patslog'] = list(pats)
1375 opts['_patslog'] = list(pats)
1378
1376
1379 filematcher = None
1377 filematcher = None
1380 if opts.get('patch') or opts.get('stat'):
1378 if opts.get('patch') or opts.get('stat'):
1381 if follow:
1379 if follow:
1382 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1380 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1383 else:
1381 else:
1384 filematcher = lambda rev: match
1382 filematcher = lambda rev: match
1385
1383
1386 expr = []
1384 expr = []
1387 for op, val in opts.iteritems():
1385 for op, val in opts.iteritems():
1388 if not val:
1386 if not val:
1389 continue
1387 continue
1390 if op not in opt2revset:
1388 if op not in opt2revset:
1391 continue
1389 continue
1392 revop, andor = opt2revset[op]
1390 revop, andor = opt2revset[op]
1393 if '%(val)' not in revop:
1391 if '%(val)' not in revop:
1394 expr.append(revop)
1392 expr.append(revop)
1395 else:
1393 else:
1396 if not isinstance(val, list):
1394 if not isinstance(val, list):
1397 e = revop % {'val': val}
1395 e = revop % {'val': val}
1398 else:
1396 else:
1399 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1397 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1400 expr.append(e)
1398 expr.append(e)
1401
1399
1402 if expr:
1400 if expr:
1403 expr = '(' + ' and '.join(expr) + ')'
1401 expr = '(' + ' and '.join(expr) + ')'
1404 else:
1402 else:
1405 expr = None
1403 expr = None
1406 return expr, filematcher
1404 return expr, filematcher
1407
1405
1408 def getgraphlogrevs(repo, pats, opts):
1406 def getgraphlogrevs(repo, pats, opts):
1409 """Return (revs, expr, filematcher) where revs is an iterable of
1407 """Return (revs, expr, filematcher) where revs is an iterable of
1410 revision numbers, expr is a revset string built from log options
1408 revision numbers, expr is a revset string built from log options
1411 and file patterns or None, and used to filter 'revs'. If --stat or
1409 and file patterns or None, and used to filter 'revs'. If --stat or
1412 --patch are not passed filematcher is None. Otherwise it is a
1410 --patch are not passed filematcher is None. Otherwise it is a
1413 callable taking a revision number and returning a match objects
1411 callable taking a revision number and returning a match objects
1414 filtering the files to be detailed when displaying the revision.
1412 filtering the files to be detailed when displaying the revision.
1415 """
1413 """
1416 if not len(repo):
1414 if not len(repo):
1417 return [], None, None
1415 return [], None, None
1418 limit = loglimit(opts)
1416 limit = loglimit(opts)
1419 # Default --rev value depends on --follow but --follow behaviour
1417 # Default --rev value depends on --follow but --follow behaviour
1420 # depends on revisions resolved from --rev...
1418 # depends on revisions resolved from --rev...
1421 follow = opts.get('follow') or opts.get('follow_first')
1419 follow = opts.get('follow') or opts.get('follow_first')
1422 possiblyunsorted = False # whether revs might need sorting
1420 possiblyunsorted = False # whether revs might need sorting
1423 if opts.get('rev'):
1421 if opts.get('rev'):
1424 revs = scmutil.revrange(repo, opts['rev'])
1422 revs = scmutil.revrange(repo, opts['rev'])
1425 # Don't sort here because _makegraphlogrevset might depend on the
1423 # Don't sort here because _makegraphlogrevset might depend on the
1426 # order of revs
1424 # order of revs
1427 possiblyunsorted = True
1425 possiblyunsorted = True
1428 else:
1426 else:
1429 if follow and len(repo) > 0:
1427 if follow and len(repo) > 0:
1430 revs = repo.revs('reverse(:.)')
1428 revs = repo.revs('reverse(:.)')
1431 else:
1429 else:
1432 revs = list(repo.changelog)
1430 revs = list(repo.changelog)
1433 revs.reverse()
1431 revs.reverse()
1434 if not revs:
1432 if not revs:
1435 return [], None, None
1433 return [], None, None
1436 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1434 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1437 if possiblyunsorted:
1435 if possiblyunsorted:
1438 revs.sort(reverse=True)
1436 revs.sort(reverse=True)
1439 if expr:
1437 if expr:
1440 # Revset matchers often operate faster on revisions in changelog
1438 # Revset matchers often operate faster on revisions in changelog
1441 # order, because most filters deal with the changelog.
1439 # order, because most filters deal with the changelog.
1442 revs.reverse()
1440 revs.reverse()
1443 matcher = revset.match(repo.ui, expr)
1441 matcher = revset.match(repo.ui, expr)
1444 # Revset matches can reorder revisions. "A or B" typically returns
1442 # Revset matches can reorder revisions. "A or B" typically returns
1445 # returns the revision matching A then the revision matching B. Sort
1443 # returns the revision matching A then the revision matching B. Sort
1446 # again to fix that.
1444 # again to fix that.
1447 revs = matcher(repo, revs)
1445 revs = matcher(repo, revs)
1448 revs.sort(reverse=True)
1446 revs.sort(reverse=True)
1449 if limit is not None:
1447 if limit is not None:
1450 revs = revs[:limit]
1448 revs = revs[:limit]
1451
1449
1452 return revs, expr, filematcher
1450 return revs, expr, filematcher
1453
1451
1454 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1452 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1455 filematcher=None):
1453 filematcher=None):
1456 seen, state = [], graphmod.asciistate()
1454 seen, state = [], graphmod.asciistate()
1457 for rev, type, ctx, parents in dag:
1455 for rev, type, ctx, parents in dag:
1458 char = 'o'
1456 char = 'o'
1459 if ctx.node() in showparents:
1457 if ctx.node() in showparents:
1460 char = '@'
1458 char = '@'
1461 elif ctx.obsolete():
1459 elif ctx.obsolete():
1462 char = 'x'
1460 char = 'x'
1463 copies = None
1461 copies = None
1464 if getrenamed and ctx.rev():
1462 if getrenamed and ctx.rev():
1465 copies = []
1463 copies = []
1466 for fn in ctx.files():
1464 for fn in ctx.files():
1467 rename = getrenamed(fn, ctx.rev())
1465 rename = getrenamed(fn, ctx.rev())
1468 if rename:
1466 if rename:
1469 copies.append((fn, rename[0]))
1467 copies.append((fn, rename[0]))
1470 revmatchfn = None
1468 revmatchfn = None
1471 if filematcher is not None:
1469 if filematcher is not None:
1472 revmatchfn = filematcher(ctx.rev())
1470 revmatchfn = filematcher(ctx.rev())
1473 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1471 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1474 lines = displayer.hunk.pop(rev).split('\n')
1472 lines = displayer.hunk.pop(rev).split('\n')
1475 if not lines[-1]:
1473 if not lines[-1]:
1476 del lines[-1]
1474 del lines[-1]
1477 displayer.flush(rev)
1475 displayer.flush(rev)
1478 edges = edgefn(type, char, lines, seen, rev, parents)
1476 edges = edgefn(type, char, lines, seen, rev, parents)
1479 for type, char, lines, coldata in edges:
1477 for type, char, lines, coldata in edges:
1480 graphmod.ascii(ui, state, type, char, lines, coldata)
1478 graphmod.ascii(ui, state, type, char, lines, coldata)
1481 displayer.close()
1479 displayer.close()
1482
1480
1483 def graphlog(ui, repo, *pats, **opts):
1481 def graphlog(ui, repo, *pats, **opts):
1484 # Parameters are identical to log command ones
1482 # Parameters are identical to log command ones
1485 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1483 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1486 revdag = graphmod.dagwalker(repo, revs)
1484 revdag = graphmod.dagwalker(repo, revs)
1487
1485
1488 getrenamed = None
1486 getrenamed = None
1489 if opts.get('copies'):
1487 if opts.get('copies'):
1490 endrev = None
1488 endrev = None
1491 if opts.get('rev'):
1489 if opts.get('rev'):
1492 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1490 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1493 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1491 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1494 displayer = show_changeset(ui, repo, opts, buffered=True)
1492 displayer = show_changeset(ui, repo, opts, buffered=True)
1495 showparents = [ctx.node() for ctx in repo[None].parents()]
1493 showparents = [ctx.node() for ctx in repo[None].parents()]
1496 displaygraph(ui, revdag, displayer, showparents,
1494 displaygraph(ui, revdag, displayer, showparents,
1497 graphmod.asciiedges, getrenamed, filematcher)
1495 graphmod.asciiedges, getrenamed, filematcher)
1498
1496
1499 def checkunsupportedgraphflags(pats, opts):
1497 def checkunsupportedgraphflags(pats, opts):
1500 for op in ["newest_first"]:
1498 for op in ["newest_first"]:
1501 if op in opts and opts[op]:
1499 if op in opts and opts[op]:
1502 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1500 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1503 % op.replace("_", "-"))
1501 % op.replace("_", "-"))
1504
1502
1505 def graphrevs(repo, nodes, opts):
1503 def graphrevs(repo, nodes, opts):
1506 limit = loglimit(opts)
1504 limit = loglimit(opts)
1507 nodes.reverse()
1505 nodes.reverse()
1508 if limit is not None:
1506 if limit is not None:
1509 nodes = nodes[:limit]
1507 nodes = nodes[:limit]
1510 return graphmod.nodes(repo, nodes)
1508 return graphmod.nodes(repo, nodes)
1511
1509
1512 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1510 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1513 join = lambda f: os.path.join(prefix, f)
1511 join = lambda f: os.path.join(prefix, f)
1514 bad = []
1512 bad = []
1515 oldbad = match.bad
1513 oldbad = match.bad
1516 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1514 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1517 names = []
1515 names = []
1518 wctx = repo[None]
1516 wctx = repo[None]
1519 cca = None
1517 cca = None
1520 abort, warn = scmutil.checkportabilityalert(ui)
1518 abort, warn = scmutil.checkportabilityalert(ui)
1521 if abort or warn:
1519 if abort or warn:
1522 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1520 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1523 for f in repo.walk(match):
1521 for f in repo.walk(match):
1524 exact = match.exact(f)
1522 exact = match.exact(f)
1525 if exact or not explicitonly and f not in repo.dirstate:
1523 if exact or not explicitonly and f not in repo.dirstate:
1526 if cca:
1524 if cca:
1527 cca(f)
1525 cca(f)
1528 names.append(f)
1526 names.append(f)
1529 if ui.verbose or not exact:
1527 if ui.verbose or not exact:
1530 ui.status(_('adding %s\n') % match.rel(join(f)))
1528 ui.status(_('adding %s\n') % match.rel(join(f)))
1531
1529
1532 for subpath in sorted(wctx.substate):
1530 for subpath in sorted(wctx.substate):
1533 sub = wctx.sub(subpath)
1531 sub = wctx.sub(subpath)
1534 try:
1532 try:
1535 submatch = matchmod.narrowmatcher(subpath, match)
1533 submatch = matchmod.narrowmatcher(subpath, match)
1536 if listsubrepos:
1534 if listsubrepos:
1537 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1535 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1538 False))
1536 False))
1539 else:
1537 else:
1540 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1538 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1541 True))
1539 True))
1542 except error.LookupError:
1540 except error.LookupError:
1543 ui.status(_("skipping missing subrepository: %s\n")
1541 ui.status(_("skipping missing subrepository: %s\n")
1544 % join(subpath))
1542 % join(subpath))
1545
1543
1546 if not dryrun:
1544 if not dryrun:
1547 rejected = wctx.add(names, prefix)
1545 rejected = wctx.add(names, prefix)
1548 bad.extend(f for f in rejected if f in match.files())
1546 bad.extend(f for f in rejected if f in match.files())
1549 return bad
1547 return bad
1550
1548
1551 def forget(ui, repo, match, prefix, explicitonly):
1549 def forget(ui, repo, match, prefix, explicitonly):
1552 join = lambda f: os.path.join(prefix, f)
1550 join = lambda f: os.path.join(prefix, f)
1553 bad = []
1551 bad = []
1554 oldbad = match.bad
1552 oldbad = match.bad
1555 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1553 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1556 wctx = repo[None]
1554 wctx = repo[None]
1557 forgot = []
1555 forgot = []
1558 s = repo.status(match=match, clean=True)
1556 s = repo.status(match=match, clean=True)
1559 forget = sorted(s[0] + s[1] + s[3] + s[6])
1557 forget = sorted(s[0] + s[1] + s[3] + s[6])
1560 if explicitonly:
1558 if explicitonly:
1561 forget = [f for f in forget if match.exact(f)]
1559 forget = [f for f in forget if match.exact(f)]
1562
1560
1563 for subpath in sorted(wctx.substate):
1561 for subpath in sorted(wctx.substate):
1564 sub = wctx.sub(subpath)
1562 sub = wctx.sub(subpath)
1565 try:
1563 try:
1566 submatch = matchmod.narrowmatcher(subpath, match)
1564 submatch = matchmod.narrowmatcher(subpath, match)
1567 subbad, subforgot = sub.forget(ui, submatch, prefix)
1565 subbad, subforgot = sub.forget(ui, submatch, prefix)
1568 bad.extend([subpath + '/' + f for f in subbad])
1566 bad.extend([subpath + '/' + f for f in subbad])
1569 forgot.extend([subpath + '/' + f for f in subforgot])
1567 forgot.extend([subpath + '/' + f for f in subforgot])
1570 except error.LookupError:
1568 except error.LookupError:
1571 ui.status(_("skipping missing subrepository: %s\n")
1569 ui.status(_("skipping missing subrepository: %s\n")
1572 % join(subpath))
1570 % join(subpath))
1573
1571
1574 if not explicitonly:
1572 if not explicitonly:
1575 for f in match.files():
1573 for f in match.files():
1576 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1574 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1577 if f not in forgot:
1575 if f not in forgot:
1578 if os.path.exists(match.rel(join(f))):
1576 if os.path.exists(match.rel(join(f))):
1579 ui.warn(_('not removing %s: '
1577 ui.warn(_('not removing %s: '
1580 'file is already untracked\n')
1578 'file is already untracked\n')
1581 % match.rel(join(f)))
1579 % match.rel(join(f)))
1582 bad.append(f)
1580 bad.append(f)
1583
1581
1584 for f in forget:
1582 for f in forget:
1585 if ui.verbose or not match.exact(f):
1583 if ui.verbose or not match.exact(f):
1586 ui.status(_('removing %s\n') % match.rel(join(f)))
1584 ui.status(_('removing %s\n') % match.rel(join(f)))
1587
1585
1588 rejected = wctx.forget(forget, prefix)
1586 rejected = wctx.forget(forget, prefix)
1589 bad.extend(f for f in rejected if f in match.files())
1587 bad.extend(f for f in rejected if f in match.files())
1590 forgot.extend(forget)
1588 forgot.extend(forget)
1591 return bad, forgot
1589 return bad, forgot
1592
1590
1593 def duplicatecopies(repo, rev, fromrev):
1591 def duplicatecopies(repo, rev, fromrev):
1594 '''reproduce copies from fromrev to rev in the dirstate'''
1592 '''reproduce copies from fromrev to rev in the dirstate'''
1595 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
1593 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
1596 # copies.pathcopies returns backward renames, so dst might not
1594 # copies.pathcopies returns backward renames, so dst might not
1597 # actually be in the dirstate
1595 # actually be in the dirstate
1598 if repo.dirstate[dst] in "nma":
1596 if repo.dirstate[dst] in "nma":
1599 repo.dirstate.copy(src, dst)
1597 repo.dirstate.copy(src, dst)
1600
1598
1601 def commit(ui, repo, commitfunc, pats, opts):
1599 def commit(ui, repo, commitfunc, pats, opts):
1602 '''commit the specified files or all outstanding changes'''
1600 '''commit the specified files or all outstanding changes'''
1603 date = opts.get('date')
1601 date = opts.get('date')
1604 if date:
1602 if date:
1605 opts['date'] = util.parsedate(date)
1603 opts['date'] = util.parsedate(date)
1606 message = logmessage(ui, opts)
1604 message = logmessage(ui, opts)
1607
1605
1608 # extract addremove carefully -- this function can be called from a command
1606 # extract addremove carefully -- this function can be called from a command
1609 # that doesn't support addremove
1607 # that doesn't support addremove
1610 if opts.get('addremove'):
1608 if opts.get('addremove'):
1611 scmutil.addremove(repo, pats, opts)
1609 scmutil.addremove(repo, pats, opts)
1612
1610
1613 return commitfunc(ui, repo, message,
1611 return commitfunc(ui, repo, message,
1614 scmutil.match(repo[None], pats, opts), opts)
1612 scmutil.match(repo[None], pats, opts), opts)
1615
1613
1616 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1614 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1617 ui.note(_('amending changeset %s\n') % old)
1615 ui.note(_('amending changeset %s\n') % old)
1618 base = old.p1()
1616 base = old.p1()
1619
1617
1620 wlock = lock = newid = None
1618 wlock = lock = newid = None
1621 try:
1619 try:
1622 wlock = repo.wlock()
1620 wlock = repo.wlock()
1623 lock = repo.lock()
1621 lock = repo.lock()
1624 tr = repo.transaction('amend')
1622 tr = repo.transaction('amend')
1625 try:
1623 try:
1626 # See if we got a message from -m or -l, if not, open the editor
1624 # See if we got a message from -m or -l, if not, open the editor
1627 # with the message of the changeset to amend
1625 # with the message of the changeset to amend
1628 message = logmessage(ui, opts)
1626 message = logmessage(ui, opts)
1629 # ensure logfile does not conflict with later enforcement of the
1627 # ensure logfile does not conflict with later enforcement of the
1630 # message. potential logfile content has been processed by
1628 # message. potential logfile content has been processed by
1631 # `logmessage` anyway.
1629 # `logmessage` anyway.
1632 opts.pop('logfile')
1630 opts.pop('logfile')
1633 # First, do a regular commit to record all changes in the working
1631 # First, do a regular commit to record all changes in the working
1634 # directory (if there are any)
1632 # directory (if there are any)
1635 ui.callhooks = False
1633 ui.callhooks = False
1636 currentbookmark = repo._bookmarkcurrent
1634 currentbookmark = repo._bookmarkcurrent
1637 try:
1635 try:
1638 repo._bookmarkcurrent = None
1636 repo._bookmarkcurrent = None
1639 opts['message'] = 'temporary amend commit for %s' % old
1637 opts['message'] = 'temporary amend commit for %s' % old
1640 node = commit(ui, repo, commitfunc, pats, opts)
1638 node = commit(ui, repo, commitfunc, pats, opts)
1641 finally:
1639 finally:
1642 repo._bookmarkcurrent = currentbookmark
1640 repo._bookmarkcurrent = currentbookmark
1643 ui.callhooks = True
1641 ui.callhooks = True
1644 ctx = repo[node]
1642 ctx = repo[node]
1645
1643
1646 # Participating changesets:
1644 # Participating changesets:
1647 #
1645 #
1648 # node/ctx o - new (intermediate) commit that contains changes
1646 # node/ctx o - new (intermediate) commit that contains changes
1649 # | from working dir to go into amending commit
1647 # | from working dir to go into amending commit
1650 # | (or a workingctx if there were no changes)
1648 # | (or a workingctx if there were no changes)
1651 # |
1649 # |
1652 # old o - changeset to amend
1650 # old o - changeset to amend
1653 # |
1651 # |
1654 # base o - parent of amending changeset
1652 # base o - parent of amending changeset
1655
1653
1656 # Update extra dict from amended commit (e.g. to preserve graft
1654 # Update extra dict from amended commit (e.g. to preserve graft
1657 # source)
1655 # source)
1658 extra.update(old.extra())
1656 extra.update(old.extra())
1659
1657
1660 # Also update it from the intermediate commit or from the wctx
1658 # Also update it from the intermediate commit or from the wctx
1661 extra.update(ctx.extra())
1659 extra.update(ctx.extra())
1662
1660
1663 if len(old.parents()) > 1:
1661 if len(old.parents()) > 1:
1664 # ctx.files() isn't reliable for merges, so fall back to the
1662 # ctx.files() isn't reliable for merges, so fall back to the
1665 # slower repo.status() method
1663 # slower repo.status() method
1666 files = set([fn for st in repo.status(base, old)[:3]
1664 files = set([fn for st in repo.status(base, old)[:3]
1667 for fn in st])
1665 for fn in st])
1668 else:
1666 else:
1669 files = set(old.files())
1667 files = set(old.files())
1670
1668
1671 # Second, we use either the commit we just did, or if there were no
1669 # Second, we use either the commit we just did, or if there were no
1672 # changes the parent of the working directory as the version of the
1670 # changes the parent of the working directory as the version of the
1673 # files in the final amend commit
1671 # files in the final amend commit
1674 if node:
1672 if node:
1675 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1673 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1676
1674
1677 user = ctx.user()
1675 user = ctx.user()
1678 date = ctx.date()
1676 date = ctx.date()
1679 # Recompute copies (avoid recording a -> b -> a)
1677 # Recompute copies (avoid recording a -> b -> a)
1680 copied = copies.pathcopies(base, ctx)
1678 copied = copies.pathcopies(base, ctx)
1681
1679
1682 # Prune files which were reverted by the updates: if old
1680 # Prune files which were reverted by the updates: if old
1683 # introduced file X and our intermediate commit, node,
1681 # introduced file X and our intermediate commit, node,
1684 # renamed that file, then those two files are the same and
1682 # renamed that file, then those two files are the same and
1685 # we can discard X from our list of files. Likewise if X
1683 # we can discard X from our list of files. Likewise if X
1686 # was deleted, it's no longer relevant
1684 # was deleted, it's no longer relevant
1687 files.update(ctx.files())
1685 files.update(ctx.files())
1688
1686
1689 def samefile(f):
1687 def samefile(f):
1690 if f in ctx.manifest():
1688 if f in ctx.manifest():
1691 a = ctx.filectx(f)
1689 a = ctx.filectx(f)
1692 if f in base.manifest():
1690 if f in base.manifest():
1693 b = base.filectx(f)
1691 b = base.filectx(f)
1694 return (not a.cmp(b)
1692 return (not a.cmp(b)
1695 and a.flags() == b.flags())
1693 and a.flags() == b.flags())
1696 else:
1694 else:
1697 return False
1695 return False
1698 else:
1696 else:
1699 return f not in base.manifest()
1697 return f not in base.manifest()
1700 files = [f for f in files if not samefile(f)]
1698 files = [f for f in files if not samefile(f)]
1701
1699
1702 def filectxfn(repo, ctx_, path):
1700 def filectxfn(repo, ctx_, path):
1703 try:
1701 try:
1704 fctx = ctx[path]
1702 fctx = ctx[path]
1705 flags = fctx.flags()
1703 flags = fctx.flags()
1706 mctx = context.memfilectx(fctx.path(), fctx.data(),
1704 mctx = context.memfilectx(fctx.path(), fctx.data(),
1707 islink='l' in flags,
1705 islink='l' in flags,
1708 isexec='x' in flags,
1706 isexec='x' in flags,
1709 copied=copied.get(path))
1707 copied=copied.get(path))
1710 return mctx
1708 return mctx
1711 except KeyError:
1709 except KeyError:
1712 raise IOError
1710 raise IOError
1713 else:
1711 else:
1714 ui.note(_('copying changeset %s to %s\n') % (old, base))
1712 ui.note(_('copying changeset %s to %s\n') % (old, base))
1715
1713
1716 # Use version of files as in the old cset
1714 # Use version of files as in the old cset
1717 def filectxfn(repo, ctx_, path):
1715 def filectxfn(repo, ctx_, path):
1718 try:
1716 try:
1719 return old.filectx(path)
1717 return old.filectx(path)
1720 except KeyError:
1718 except KeyError:
1721 raise IOError
1719 raise IOError
1722
1720
1723 user = opts.get('user') or old.user()
1721 user = opts.get('user') or old.user()
1724 date = opts.get('date') or old.date()
1722 date = opts.get('date') or old.date()
1725 editmsg = False
1723 editmsg = False
1726 if not message:
1724 if not message:
1727 editmsg = True
1725 editmsg = True
1728 message = old.description()
1726 message = old.description()
1729
1727
1730 pureextra = extra.copy()
1728 pureextra = extra.copy()
1731 extra['amend_source'] = old.hex()
1729 extra['amend_source'] = old.hex()
1732
1730
1733 new = context.memctx(repo,
1731 new = context.memctx(repo,
1734 parents=[base.node(), old.p2().node()],
1732 parents=[base.node(), old.p2().node()],
1735 text=message,
1733 text=message,
1736 files=files,
1734 files=files,
1737 filectxfn=filectxfn,
1735 filectxfn=filectxfn,
1738 user=user,
1736 user=user,
1739 date=date,
1737 date=date,
1740 extra=extra)
1738 extra=extra)
1741 if editmsg:
1739 if editmsg:
1742 new._text = commitforceeditor(repo, new, [])
1740 new._text = commitforceeditor(repo, new, [])
1743
1741
1744 newdesc = changelog.stripdesc(new.description())
1742 newdesc = changelog.stripdesc(new.description())
1745 if ((not node)
1743 if ((not node)
1746 and newdesc == old.description()
1744 and newdesc == old.description()
1747 and user == old.user()
1745 and user == old.user()
1748 and date == old.date()
1746 and date == old.date()
1749 and pureextra == old.extra()):
1747 and pureextra == old.extra()):
1750 # nothing changed. continuing here would create a new node
1748 # nothing changed. continuing here would create a new node
1751 # anyway because of the amend_source noise.
1749 # anyway because of the amend_source noise.
1752 #
1750 #
1753 # This not what we expect from amend.
1751 # This not what we expect from amend.
1754 return old.node()
1752 return old.node()
1755
1753
1756 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1754 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1757 try:
1755 try:
1758 repo.ui.setconfig('phases', 'new-commit', old.phase())
1756 repo.ui.setconfig('phases', 'new-commit', old.phase())
1759 newid = repo.commitctx(new)
1757 newid = repo.commitctx(new)
1760 finally:
1758 finally:
1761 repo.ui.setconfig('phases', 'new-commit', ph)
1759 repo.ui.setconfig('phases', 'new-commit', ph)
1762 if newid != old.node():
1760 if newid != old.node():
1763 # Reroute the working copy parent to the new changeset
1761 # Reroute the working copy parent to the new changeset
1764 repo.setparents(newid, nullid)
1762 repo.setparents(newid, nullid)
1765
1763
1766 # Move bookmarks from old parent to amend commit
1764 # Move bookmarks from old parent to amend commit
1767 bms = repo.nodebookmarks(old.node())
1765 bms = repo.nodebookmarks(old.node())
1768 if bms:
1766 if bms:
1769 marks = repo._bookmarks
1767 marks = repo._bookmarks
1770 for bm in bms:
1768 for bm in bms:
1771 marks[bm] = newid
1769 marks[bm] = newid
1772 marks.write()
1770 marks.write()
1773 #commit the whole amend process
1771 #commit the whole amend process
1774 if obsolete._enabled and newid != old.node():
1772 if obsolete._enabled and newid != old.node():
1775 # mark the new changeset as successor of the rewritten one
1773 # mark the new changeset as successor of the rewritten one
1776 new = repo[newid]
1774 new = repo[newid]
1777 obs = [(old, (new,))]
1775 obs = [(old, (new,))]
1778 if node:
1776 if node:
1779 obs.append((ctx, ()))
1777 obs.append((ctx, ()))
1780
1778
1781 obsolete.createmarkers(repo, obs)
1779 obsolete.createmarkers(repo, obs)
1782 tr.close()
1780 tr.close()
1783 finally:
1781 finally:
1784 tr.release()
1782 tr.release()
1785 if (not obsolete._enabled) and newid != old.node():
1783 if (not obsolete._enabled) and newid != old.node():
1786 # Strip the intermediate commit (if there was one) and the amended
1784 # Strip the intermediate commit (if there was one) and the amended
1787 # commit
1785 # commit
1788 if node:
1786 if node:
1789 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1787 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1790 ui.note(_('stripping amended changeset %s\n') % old)
1788 ui.note(_('stripping amended changeset %s\n') % old)
1791 repair.strip(ui, repo, old.node(), topic='amend-backup')
1789 repair.strip(ui, repo, old.node(), topic='amend-backup')
1792 finally:
1790 finally:
1793 if newid is None:
1791 if newid is None:
1794 repo.dirstate.invalidate()
1792 repo.dirstate.invalidate()
1795 lockmod.release(wlock, lock)
1793 lockmod.release(wlock, lock)
1796 return newid
1794 return newid
1797
1795
1798 def commiteditor(repo, ctx, subs):
1796 def commiteditor(repo, ctx, subs):
1799 if ctx.description():
1797 if ctx.description():
1800 return ctx.description()
1798 return ctx.description()
1801 return commitforceeditor(repo, ctx, subs)
1799 return commitforceeditor(repo, ctx, subs)
1802
1800
1803 def commitforceeditor(repo, ctx, subs):
1801 def commitforceeditor(repo, ctx, subs):
1804 edittext = []
1802 edittext = []
1805 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1803 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1806 if ctx.description():
1804 if ctx.description():
1807 edittext.append(ctx.description())
1805 edittext.append(ctx.description())
1808 edittext.append("")
1806 edittext.append("")
1809 edittext.append("") # Empty line between message and comments.
1807 edittext.append("") # Empty line between message and comments.
1810 edittext.append(_("HG: Enter commit message."
1808 edittext.append(_("HG: Enter commit message."
1811 " Lines beginning with 'HG:' are removed."))
1809 " Lines beginning with 'HG:' are removed."))
1812 edittext.append(_("HG: Leave message empty to abort commit."))
1810 edittext.append(_("HG: Leave message empty to abort commit."))
1813 edittext.append("HG: --")
1811 edittext.append("HG: --")
1814 edittext.append(_("HG: user: %s") % ctx.user())
1812 edittext.append(_("HG: user: %s") % ctx.user())
1815 if ctx.p2():
1813 if ctx.p2():
1816 edittext.append(_("HG: branch merge"))
1814 edittext.append(_("HG: branch merge"))
1817 if ctx.branch():
1815 if ctx.branch():
1818 edittext.append(_("HG: branch '%s'") % ctx.branch())
1816 edittext.append(_("HG: branch '%s'") % ctx.branch())
1819 if bookmarks.iscurrent(repo):
1817 if bookmarks.iscurrent(repo):
1820 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
1818 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
1821 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1819 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1822 edittext.extend([_("HG: added %s") % f for f in added])
1820 edittext.extend([_("HG: added %s") % f for f in added])
1823 edittext.extend([_("HG: changed %s") % f for f in modified])
1821 edittext.extend([_("HG: changed %s") % f for f in modified])
1824 edittext.extend([_("HG: removed %s") % f for f in removed])
1822 edittext.extend([_("HG: removed %s") % f for f in removed])
1825 if not added and not modified and not removed:
1823 if not added and not modified and not removed:
1826 edittext.append(_("HG: no files changed"))
1824 edittext.append(_("HG: no files changed"))
1827 edittext.append("")
1825 edittext.append("")
1828 # run editor in the repository root
1826 # run editor in the repository root
1829 olddir = os.getcwd()
1827 olddir = os.getcwd()
1830 os.chdir(repo.root)
1828 os.chdir(repo.root)
1831 text = repo.ui.edit("\n".join(edittext), ctx.user())
1829 text = repo.ui.edit("\n".join(edittext), ctx.user())
1832 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1830 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1833 os.chdir(olddir)
1831 os.chdir(olddir)
1834
1832
1835 if not text.strip():
1833 if not text.strip():
1836 raise util.Abort(_("empty commit message"))
1834 raise util.Abort(_("empty commit message"))
1837
1835
1838 return text
1836 return text
1839
1837
1840 def commitstatus(repo, node, branch, bheads=None, opts={}):
1838 def commitstatus(repo, node, branch, bheads=None, opts={}):
1841 ctx = repo[node]
1839 ctx = repo[node]
1842 parents = ctx.parents()
1840 parents = ctx.parents()
1843
1841
1844 if (not opts.get('amend') and bheads and node not in bheads and not
1842 if (not opts.get('amend') and bheads and node not in bheads and not
1845 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1843 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1846 repo.ui.status(_('created new head\n'))
1844 repo.ui.status(_('created new head\n'))
1847 # The message is not printed for initial roots. For the other
1845 # The message is not printed for initial roots. For the other
1848 # changesets, it is printed in the following situations:
1846 # changesets, it is printed in the following situations:
1849 #
1847 #
1850 # Par column: for the 2 parents with ...
1848 # Par column: for the 2 parents with ...
1851 # N: null or no parent
1849 # N: null or no parent
1852 # B: parent is on another named branch
1850 # B: parent is on another named branch
1853 # C: parent is a regular non head changeset
1851 # C: parent is a regular non head changeset
1854 # H: parent was a branch head of the current branch
1852 # H: parent was a branch head of the current branch
1855 # Msg column: whether we print "created new head" message
1853 # Msg column: whether we print "created new head" message
1856 # In the following, it is assumed that there already exists some
1854 # In the following, it is assumed that there already exists some
1857 # initial branch heads of the current branch, otherwise nothing is
1855 # initial branch heads of the current branch, otherwise nothing is
1858 # printed anyway.
1856 # printed anyway.
1859 #
1857 #
1860 # Par Msg Comment
1858 # Par Msg Comment
1861 # N N y additional topo root
1859 # N N y additional topo root
1862 #
1860 #
1863 # B N y additional branch root
1861 # B N y additional branch root
1864 # C N y additional topo head
1862 # C N y additional topo head
1865 # H N n usual case
1863 # H N n usual case
1866 #
1864 #
1867 # B B y weird additional branch root
1865 # B B y weird additional branch root
1868 # C B y branch merge
1866 # C B y branch merge
1869 # H B n merge with named branch
1867 # H B n merge with named branch
1870 #
1868 #
1871 # C C y additional head from merge
1869 # C C y additional head from merge
1872 # C H n merge with a head
1870 # C H n merge with a head
1873 #
1871 #
1874 # H H n head merge: head count decreases
1872 # H H n head merge: head count decreases
1875
1873
1876 if not opts.get('close_branch'):
1874 if not opts.get('close_branch'):
1877 for r in parents:
1875 for r in parents:
1878 if r.closesbranch() and r.branch() == branch:
1876 if r.closesbranch() and r.branch() == branch:
1879 repo.ui.status(_('reopening closed branch head %d\n') % r)
1877 repo.ui.status(_('reopening closed branch head %d\n') % r)
1880
1878
1881 if repo.ui.debugflag:
1879 if repo.ui.debugflag:
1882 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1880 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1883 elif repo.ui.verbose:
1881 elif repo.ui.verbose:
1884 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1882 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1885
1883
1886 def revert(ui, repo, ctx, parents, *pats, **opts):
1884 def revert(ui, repo, ctx, parents, *pats, **opts):
1887 parent, p2 = parents
1885 parent, p2 = parents
1888 node = ctx.node()
1886 node = ctx.node()
1889
1887
1890 mf = ctx.manifest()
1888 mf = ctx.manifest()
1891 if node == parent:
1889 if node == parent:
1892 pmf = mf
1890 pmf = mf
1893 else:
1891 else:
1894 pmf = None
1892 pmf = None
1895
1893
1896 # need all matching names in dirstate and manifest of target rev,
1894 # need all matching names in dirstate and manifest of target rev,
1897 # so have to walk both. do not print errors if files exist in one
1895 # so have to walk both. do not print errors if files exist in one
1898 # but not other.
1896 # but not other.
1899
1897
1900 names = {}
1898 names = {}
1901
1899
1902 wlock = repo.wlock()
1900 wlock = repo.wlock()
1903 try:
1901 try:
1904 # walk dirstate.
1902 # walk dirstate.
1905
1903
1906 m = scmutil.match(repo[None], pats, opts)
1904 m = scmutil.match(repo[None], pats, opts)
1907 m.bad = lambda x, y: False
1905 m.bad = lambda x, y: False
1908 for abs in repo.walk(m):
1906 for abs in repo.walk(m):
1909 names[abs] = m.rel(abs), m.exact(abs)
1907 names[abs] = m.rel(abs), m.exact(abs)
1910
1908
1911 # walk target manifest.
1909 # walk target manifest.
1912
1910
1913 def badfn(path, msg):
1911 def badfn(path, msg):
1914 if path in names:
1912 if path in names:
1915 return
1913 return
1916 if path in ctx.substate:
1914 if path in ctx.substate:
1917 return
1915 return
1918 path_ = path + '/'
1916 path_ = path + '/'
1919 for f in names:
1917 for f in names:
1920 if f.startswith(path_):
1918 if f.startswith(path_):
1921 return
1919 return
1922 ui.warn("%s: %s\n" % (m.rel(path), msg))
1920 ui.warn("%s: %s\n" % (m.rel(path), msg))
1923
1921
1924 m = scmutil.match(ctx, pats, opts)
1922 m = scmutil.match(ctx, pats, opts)
1925 m.bad = badfn
1923 m.bad = badfn
1926 for abs in ctx.walk(m):
1924 for abs in ctx.walk(m):
1927 if abs not in names:
1925 if abs not in names:
1928 names[abs] = m.rel(abs), m.exact(abs)
1926 names[abs] = m.rel(abs), m.exact(abs)
1929
1927
1930 # get the list of subrepos that must be reverted
1928 # get the list of subrepos that must be reverted
1931 targetsubs = sorted(s for s in ctx.substate if m(s))
1929 targetsubs = sorted(s for s in ctx.substate if m(s))
1932 m = scmutil.matchfiles(repo, names)
1930 m = scmutil.matchfiles(repo, names)
1933 changes = repo.status(match=m)[:4]
1931 changes = repo.status(match=m)[:4]
1934 modified, added, removed, deleted = map(set, changes)
1932 modified, added, removed, deleted = map(set, changes)
1935
1933
1936 # if f is a rename, also revert the source
1934 # if f is a rename, also revert the source
1937 cwd = repo.getcwd()
1935 cwd = repo.getcwd()
1938 for f in added:
1936 for f in added:
1939 src = repo.dirstate.copied(f)
1937 src = repo.dirstate.copied(f)
1940 if src and src not in names and repo.dirstate[src] == 'r':
1938 if src and src not in names and repo.dirstate[src] == 'r':
1941 removed.add(src)
1939 removed.add(src)
1942 names[src] = (repo.pathto(src, cwd), True)
1940 names[src] = (repo.pathto(src, cwd), True)
1943
1941
1944 def removeforget(abs):
1942 def removeforget(abs):
1945 if repo.dirstate[abs] == 'a':
1943 if repo.dirstate[abs] == 'a':
1946 return _('forgetting %s\n')
1944 return _('forgetting %s\n')
1947 return _('removing %s\n')
1945 return _('removing %s\n')
1948
1946
1949 revert = ([], _('reverting %s\n'))
1947 revert = ([], _('reverting %s\n'))
1950 add = ([], _('adding %s\n'))
1948 add = ([], _('adding %s\n'))
1951 remove = ([], removeforget)
1949 remove = ([], removeforget)
1952 undelete = ([], _('undeleting %s\n'))
1950 undelete = ([], _('undeleting %s\n'))
1953
1951
1954 disptable = (
1952 disptable = (
1955 # dispatch table:
1953 # dispatch table:
1956 # file state
1954 # file state
1957 # action if in target manifest
1955 # action if in target manifest
1958 # action if not in target manifest
1956 # action if not in target manifest
1959 # make backup if in target manifest
1957 # make backup if in target manifest
1960 # make backup if not in target manifest
1958 # make backup if not in target manifest
1961 (modified, revert, remove, True, True),
1959 (modified, revert, remove, True, True),
1962 (added, revert, remove, True, False),
1960 (added, revert, remove, True, False),
1963 (removed, undelete, None, False, False),
1961 (removed, undelete, None, False, False),
1964 (deleted, revert, remove, False, False),
1962 (deleted, revert, remove, False, False),
1965 )
1963 )
1966
1964
1967 for abs, (rel, exact) in sorted(names.items()):
1965 for abs, (rel, exact) in sorted(names.items()):
1968 mfentry = mf.get(abs)
1966 mfentry = mf.get(abs)
1969 target = repo.wjoin(abs)
1967 target = repo.wjoin(abs)
1970 def handle(xlist, dobackup):
1968 def handle(xlist, dobackup):
1971 xlist[0].append(abs)
1969 xlist[0].append(abs)
1972 if (dobackup and not opts.get('no_backup') and
1970 if (dobackup and not opts.get('no_backup') and
1973 os.path.lexists(target)):
1971 os.path.lexists(target)):
1974 bakname = "%s.orig" % rel
1972 bakname = "%s.orig" % rel
1975 ui.note(_('saving current version of %s as %s\n') %
1973 ui.note(_('saving current version of %s as %s\n') %
1976 (rel, bakname))
1974 (rel, bakname))
1977 if not opts.get('dry_run'):
1975 if not opts.get('dry_run'):
1978 util.rename(target, bakname)
1976 util.rename(target, bakname)
1979 if ui.verbose or not exact:
1977 if ui.verbose or not exact:
1980 msg = xlist[1]
1978 msg = xlist[1]
1981 if not isinstance(msg, basestring):
1979 if not isinstance(msg, basestring):
1982 msg = msg(abs)
1980 msg = msg(abs)
1983 ui.status(msg % rel)
1981 ui.status(msg % rel)
1984 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1982 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1985 if abs not in table:
1983 if abs not in table:
1986 continue
1984 continue
1987 # file has changed in dirstate
1985 # file has changed in dirstate
1988 if mfentry:
1986 if mfentry:
1989 handle(hitlist, backuphit)
1987 handle(hitlist, backuphit)
1990 elif misslist is not None:
1988 elif misslist is not None:
1991 handle(misslist, backupmiss)
1989 handle(misslist, backupmiss)
1992 break
1990 break
1993 else:
1991 else:
1994 if abs not in repo.dirstate:
1992 if abs not in repo.dirstate:
1995 if mfentry:
1993 if mfentry:
1996 handle(add, True)
1994 handle(add, True)
1997 elif exact:
1995 elif exact:
1998 ui.warn(_('file not managed: %s\n') % rel)
1996 ui.warn(_('file not managed: %s\n') % rel)
1999 continue
1997 continue
2000 # file has not changed in dirstate
1998 # file has not changed in dirstate
2001 if node == parent:
1999 if node == parent:
2002 if exact:
2000 if exact:
2003 ui.warn(_('no changes needed to %s\n') % rel)
2001 ui.warn(_('no changes needed to %s\n') % rel)
2004 continue
2002 continue
2005 if pmf is None:
2003 if pmf is None:
2006 # only need parent manifest in this unlikely case,
2004 # only need parent manifest in this unlikely case,
2007 # so do not read by default
2005 # so do not read by default
2008 pmf = repo[parent].manifest()
2006 pmf = repo[parent].manifest()
2009 if abs in pmf and mfentry:
2007 if abs in pmf and mfentry:
2010 # if version of file is same in parent and target
2008 # if version of file is same in parent and target
2011 # manifests, do nothing
2009 # manifests, do nothing
2012 if (pmf[abs] != mfentry or
2010 if (pmf[abs] != mfentry or
2013 pmf.flags(abs) != mf.flags(abs)):
2011 pmf.flags(abs) != mf.flags(abs)):
2014 handle(revert, False)
2012 handle(revert, False)
2015 else:
2013 else:
2016 handle(remove, False)
2014 handle(remove, False)
2017
2015
2018 if not opts.get('dry_run'):
2016 if not opts.get('dry_run'):
2019 def checkout(f):
2017 def checkout(f):
2020 fc = ctx[f]
2018 fc = ctx[f]
2021 repo.wwrite(f, fc.data(), fc.flags())
2019 repo.wwrite(f, fc.data(), fc.flags())
2022
2020
2023 audit_path = scmutil.pathauditor(repo.root)
2021 audit_path = scmutil.pathauditor(repo.root)
2024 for f in remove[0]:
2022 for f in remove[0]:
2025 if repo.dirstate[f] == 'a':
2023 if repo.dirstate[f] == 'a':
2026 repo.dirstate.drop(f)
2024 repo.dirstate.drop(f)
2027 continue
2025 continue
2028 audit_path(f)
2026 audit_path(f)
2029 try:
2027 try:
2030 util.unlinkpath(repo.wjoin(f))
2028 util.unlinkpath(repo.wjoin(f))
2031 except OSError:
2029 except OSError:
2032 pass
2030 pass
2033 repo.dirstate.remove(f)
2031 repo.dirstate.remove(f)
2034
2032
2035 normal = None
2033 normal = None
2036 if node == parent:
2034 if node == parent:
2037 # We're reverting to our parent. If possible, we'd like status
2035 # We're reverting to our parent. If possible, we'd like status
2038 # to report the file as clean. We have to use normallookup for
2036 # to report the file as clean. We have to use normallookup for
2039 # merges to avoid losing information about merged/dirty files.
2037 # merges to avoid losing information about merged/dirty files.
2040 if p2 != nullid:
2038 if p2 != nullid:
2041 normal = repo.dirstate.normallookup
2039 normal = repo.dirstate.normallookup
2042 else:
2040 else:
2043 normal = repo.dirstate.normal
2041 normal = repo.dirstate.normal
2044 for f in revert[0]:
2042 for f in revert[0]:
2045 checkout(f)
2043 checkout(f)
2046 if normal:
2044 if normal:
2047 normal(f)
2045 normal(f)
2048
2046
2049 for f in add[0]:
2047 for f in add[0]:
2050 checkout(f)
2048 checkout(f)
2051 repo.dirstate.add(f)
2049 repo.dirstate.add(f)
2052
2050
2053 normal = repo.dirstate.normallookup
2051 normal = repo.dirstate.normallookup
2054 if node == parent and p2 == nullid:
2052 if node == parent and p2 == nullid:
2055 normal = repo.dirstate.normal
2053 normal = repo.dirstate.normal
2056 for f in undelete[0]:
2054 for f in undelete[0]:
2057 checkout(f)
2055 checkout(f)
2058 normal(f)
2056 normal(f)
2059
2057
2060 if targetsubs:
2058 if targetsubs:
2061 # Revert the subrepos on the revert list
2059 # Revert the subrepos on the revert list
2062 for sub in targetsubs:
2060 for sub in targetsubs:
2063 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2061 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2064 finally:
2062 finally:
2065 wlock.release()
2063 wlock.release()
2066
2064
2067 def command(table):
2065 def command(table):
2068 '''returns a function object bound to table which can be used as
2066 '''returns a function object bound to table which can be used as
2069 a decorator for populating table as a command table'''
2067 a decorator for populating table as a command table'''
2070
2068
2071 def cmd(name, options=(), synopsis=None):
2069 def cmd(name, options=(), synopsis=None):
2072 def decorator(func):
2070 def decorator(func):
2073 if synopsis:
2071 if synopsis:
2074 table[name] = func, list(options), synopsis
2072 table[name] = func, list(options), synopsis
2075 else:
2073 else:
2076 table[name] = func, list(options)
2074 table[name] = func, list(options)
2077 return func
2075 return func
2078 return decorator
2076 return decorator
2079
2077
2080 return cmd
2078 return cmd
@@ -1,1340 +1,1344 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for 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 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 # import stuff from node for others to import from revlog
14 # import stuff from node for others to import from revlog
15 from node import bin, hex, nullid, nullrev
15 from node import bin, hex, nullid, nullrev
16 from i18n import _
16 from i18n import _
17 import ancestor, mdiff, parsers, error, util, dagutil
17 import ancestor, mdiff, parsers, error, util, dagutil
18 import struct, zlib, errno
18 import struct, zlib, errno
19
19
20 _pack = struct.pack
20 _pack = struct.pack
21 _unpack = struct.unpack
21 _unpack = struct.unpack
22 _compress = zlib.compress
22 _compress = zlib.compress
23 _decompress = zlib.decompress
23 _decompress = zlib.decompress
24 _sha = util.sha1
24 _sha = util.sha1
25
25
26 # revlog header flags
26 # revlog header flags
27 REVLOGV0 = 0
27 REVLOGV0 = 0
28 REVLOGNG = 1
28 REVLOGNG = 1
29 REVLOGNGINLINEDATA = (1 << 16)
29 REVLOGNGINLINEDATA = (1 << 16)
30 REVLOGGENERALDELTA = (1 << 17)
30 REVLOGGENERALDELTA = (1 << 17)
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
35
35
36 # revlog index flags
36 # revlog index flags
37 REVIDX_KNOWN_FLAGS = 0
37 REVIDX_KNOWN_FLAGS = 0
38
38
39 # max size of revlog with inline data
39 # max size of revlog with inline data
40 _maxinline = 131072
40 _maxinline = 131072
41 _chunksize = 1048576
41 _chunksize = 1048576
42
42
43 RevlogError = error.RevlogError
43 RevlogError = error.RevlogError
44 LookupError = error.LookupError
44 LookupError = error.LookupError
45
45
46 def getoffset(q):
46 def getoffset(q):
47 return int(q >> 16)
47 return int(q >> 16)
48
48
49 def gettype(q):
49 def gettype(q):
50 return int(q & 0xFFFF)
50 return int(q & 0xFFFF)
51
51
52 def offset_type(offset, type):
52 def offset_type(offset, type):
53 return long(long(offset) << 16 | type)
53 return long(long(offset) << 16 | type)
54
54
55 nullhash = _sha(nullid)
55 nullhash = _sha(nullid)
56
56
57 def hash(text, p1, p2):
57 def hash(text, p1, p2):
58 """generate a hash from the given text and its parent hashes
58 """generate a hash from the given text and its parent hashes
59
59
60 This hash combines both the current file contents and its history
60 This hash combines both the current file contents and its history
61 in a manner that makes it easy to distinguish nodes with the same
61 in a manner that makes it easy to distinguish nodes with the same
62 content in the revision graph.
62 content in the revision graph.
63 """
63 """
64 # As of now, if one of the parent node is null, p2 is null
64 # As of now, if one of the parent node is null, p2 is null
65 if p2 == nullid:
65 if p2 == nullid:
66 # deep copy of a hash is faster than creating one
66 # deep copy of a hash is faster than creating one
67 s = nullhash.copy()
67 s = nullhash.copy()
68 s.update(p1)
68 s.update(p1)
69 else:
69 else:
70 # none of the parent nodes are nullid
70 # none of the parent nodes are nullid
71 l = [p1, p2]
71 l = [p1, p2]
72 l.sort()
72 l.sort()
73 s = _sha(l[0])
73 s = _sha(l[0])
74 s.update(l[1])
74 s.update(l[1])
75 s.update(text)
75 s.update(text)
76 return s.digest()
76 return s.digest()
77
77
78 def decompress(bin):
78 def decompress(bin):
79 """ decompress the given input """
79 """ decompress the given input """
80 if not bin:
80 if not bin:
81 return bin
81 return bin
82 t = bin[0]
82 t = bin[0]
83 if t == '\0':
83 if t == '\0':
84 return bin
84 return bin
85 if t == 'x':
85 if t == 'x':
86 try:
86 try:
87 return _decompress(bin)
87 return _decompress(bin)
88 except zlib.error, e:
88 except zlib.error, e:
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
90 if t == 'u':
90 if t == 'u':
91 return bin[1:]
91 return bin[1:]
92 raise RevlogError(_("unknown compression type %r") % t)
92 raise RevlogError(_("unknown compression type %r") % t)
93
93
94 # index v0:
94 # index v0:
95 # 4 bytes: offset
95 # 4 bytes: offset
96 # 4 bytes: compressed length
96 # 4 bytes: compressed length
97 # 4 bytes: base rev
97 # 4 bytes: base rev
98 # 4 bytes: link rev
98 # 4 bytes: link rev
99 # 32 bytes: parent 1 nodeid
99 # 32 bytes: parent 1 nodeid
100 # 32 bytes: parent 2 nodeid
100 # 32 bytes: parent 2 nodeid
101 # 32 bytes: nodeid
101 # 32 bytes: nodeid
102 indexformatv0 = ">4l20s20s20s"
102 indexformatv0 = ">4l20s20s20s"
103 v0shaoffset = 56
103 v0shaoffset = 56
104
104
105 class revlogoldio(object):
105 class revlogoldio(object):
106 def __init__(self):
106 def __init__(self):
107 self.size = struct.calcsize(indexformatv0)
107 self.size = struct.calcsize(indexformatv0)
108
108
109 def parseindex(self, data, inline):
109 def parseindex(self, data, inline):
110 s = self.size
110 s = self.size
111 index = []
111 index = []
112 nodemap = {nullid: nullrev}
112 nodemap = {nullid: nullrev}
113 n = off = 0
113 n = off = 0
114 l = len(data)
114 l = len(data)
115 while off + s <= l:
115 while off + s <= l:
116 cur = data[off:off + s]
116 cur = data[off:off + s]
117 off += s
117 off += s
118 e = _unpack(indexformatv0, cur)
118 e = _unpack(indexformatv0, cur)
119 # transform to revlogv1 format
119 # transform to revlogv1 format
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
122 index.append(e2)
122 index.append(e2)
123 nodemap[e[6]] = n
123 nodemap[e[6]] = n
124 n += 1
124 n += 1
125
125
126 # add the magic null revision at -1
126 # add the magic null revision at -1
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
128
128
129 return index, nodemap, None
129 return index, nodemap, None
130
130
131 def packentry(self, entry, node, version, rev):
131 def packentry(self, entry, node, version, rev):
132 if gettype(entry[0]):
132 if gettype(entry[0]):
133 raise RevlogError(_("index entry flags need RevlogNG"))
133 raise RevlogError(_("index entry flags need RevlogNG"))
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
135 node(entry[5]), node(entry[6]), entry[7])
135 node(entry[5]), node(entry[6]), entry[7])
136 return _pack(indexformatv0, *e2)
136 return _pack(indexformatv0, *e2)
137
137
138 # index ng:
138 # index ng:
139 # 6 bytes: offset
139 # 6 bytes: offset
140 # 2 bytes: flags
140 # 2 bytes: flags
141 # 4 bytes: compressed length
141 # 4 bytes: compressed length
142 # 4 bytes: uncompressed length
142 # 4 bytes: uncompressed length
143 # 4 bytes: base rev
143 # 4 bytes: base rev
144 # 4 bytes: link rev
144 # 4 bytes: link rev
145 # 4 bytes: parent 1 rev
145 # 4 bytes: parent 1 rev
146 # 4 bytes: parent 2 rev
146 # 4 bytes: parent 2 rev
147 # 32 bytes: nodeid
147 # 32 bytes: nodeid
148 indexformatng = ">Qiiiiii20s12x"
148 indexformatng = ">Qiiiiii20s12x"
149 ngshaoffset = 32
149 ngshaoffset = 32
150 versionformat = ">I"
150 versionformat = ">I"
151
151
152 class revlogio(object):
152 class revlogio(object):
153 def __init__(self):
153 def __init__(self):
154 self.size = struct.calcsize(indexformatng)
154 self.size = struct.calcsize(indexformatng)
155
155
156 def parseindex(self, data, inline):
156 def parseindex(self, data, inline):
157 # call the C implementation to parse the index data
157 # call the C implementation to parse the index data
158 index, cache = parsers.parse_index2(data, inline)
158 index, cache = parsers.parse_index2(data, inline)
159 return index, getattr(index, 'nodemap', None), cache
159 return index, getattr(index, 'nodemap', None), cache
160
160
161 def packentry(self, entry, node, version, rev):
161 def packentry(self, entry, node, version, rev):
162 p = _pack(indexformatng, *entry)
162 p = _pack(indexformatng, *entry)
163 if rev == 0:
163 if rev == 0:
164 p = _pack(versionformat, version) + p[4:]
164 p = _pack(versionformat, version) + p[4:]
165 return p
165 return p
166
166
167 class revlog(object):
167 class revlog(object):
168 """
168 """
169 the underlying revision storage object
169 the underlying revision storage object
170
170
171 A revlog consists of two parts, an index and the revision data.
171 A revlog consists of two parts, an index and the revision data.
172
172
173 The index is a file with a fixed record size containing
173 The index is a file with a fixed record size containing
174 information on each revision, including its nodeid (hash), the
174 information on each revision, including its nodeid (hash), the
175 nodeids of its parents, the position and offset of its data within
175 nodeids of its parents, the position and offset of its data within
176 the data file, and the revision it's based on. Finally, each entry
176 the data file, and the revision it's based on. Finally, each entry
177 contains a linkrev entry that can serve as a pointer to external
177 contains a linkrev entry that can serve as a pointer to external
178 data.
178 data.
179
179
180 The revision data itself is a linear collection of data chunks.
180 The revision data itself is a linear collection of data chunks.
181 Each chunk represents a revision and is usually represented as a
181 Each chunk represents a revision and is usually represented as a
182 delta against the previous chunk. To bound lookup time, runs of
182 delta against the previous chunk. To bound lookup time, runs of
183 deltas are limited to about 2 times the length of the original
183 deltas are limited to about 2 times the length of the original
184 version data. This makes retrieval of a version proportional to
184 version data. This makes retrieval of a version proportional to
185 its size, or O(1) relative to the number of revisions.
185 its size, or O(1) relative to the number of revisions.
186
186
187 Both pieces of the revlog are written to in an append-only
187 Both pieces of the revlog are written to in an append-only
188 fashion, which means we never need to rewrite a file to insert or
188 fashion, which means we never need to rewrite a file to insert or
189 remove data, and can use some simple techniques to avoid the need
189 remove data, and can use some simple techniques to avoid the need
190 for locking while reading.
190 for locking while reading.
191 """
191 """
192 def __init__(self, opener, indexfile):
192 def __init__(self, opener, indexfile):
193 """
193 """
194 create a revlog object
194 create a revlog object
195
195
196 opener is a function that abstracts the file opening operation
196 opener is a function that abstracts the file opening operation
197 and can be used to implement COW semantics or the like.
197 and can be used to implement COW semantics or the like.
198 """
198 """
199 self.indexfile = indexfile
199 self.indexfile = indexfile
200 self.datafile = indexfile[:-2] + ".d"
200 self.datafile = indexfile[:-2] + ".d"
201 self.opener = opener
201 self.opener = opener
202 self._cache = None
202 self._cache = None
203 self._basecache = (0, 0)
203 self._basecache = (0, 0)
204 self._chunkcache = (0, '')
204 self._chunkcache = (0, '')
205 self.index = []
205 self.index = []
206 self._pcache = {}
206 self._pcache = {}
207 self._nodecache = {nullid: nullrev}
207 self._nodecache = {nullid: nullrev}
208 self._nodepos = None
208 self._nodepos = None
209
209
210 v = REVLOG_DEFAULT_VERSION
210 v = REVLOG_DEFAULT_VERSION
211 opts = getattr(opener, 'options', None)
211 opts = getattr(opener, 'options', None)
212 if opts is not None:
212 if opts is not None:
213 if 'revlogv1' in opts:
213 if 'revlogv1' in opts:
214 if 'generaldelta' in opts:
214 if 'generaldelta' in opts:
215 v |= REVLOGGENERALDELTA
215 v |= REVLOGGENERALDELTA
216 else:
216 else:
217 v = 0
217 v = 0
218
218
219 i = ''
219 i = ''
220 self._initempty = True
220 self._initempty = True
221 try:
221 try:
222 f = self.opener(self.indexfile)
222 f = self.opener(self.indexfile)
223 i = f.read()
223 i = f.read()
224 f.close()
224 f.close()
225 if len(i) > 0:
225 if len(i) > 0:
226 v = struct.unpack(versionformat, i[:4])[0]
226 v = struct.unpack(versionformat, i[:4])[0]
227 self._initempty = False
227 self._initempty = False
228 except IOError, inst:
228 except IOError, inst:
229 if inst.errno != errno.ENOENT:
229 if inst.errno != errno.ENOENT:
230 raise
230 raise
231
231
232 self.version = v
232 self.version = v
233 self._inline = v & REVLOGNGINLINEDATA
233 self._inline = v & REVLOGNGINLINEDATA
234 self._generaldelta = v & REVLOGGENERALDELTA
234 self._generaldelta = v & REVLOGGENERALDELTA
235 flags = v & ~0xFFFF
235 flags = v & ~0xFFFF
236 fmt = v & 0xFFFF
236 fmt = v & 0xFFFF
237 if fmt == REVLOGV0 and flags:
237 if fmt == REVLOGV0 and flags:
238 raise RevlogError(_("index %s unknown flags %#04x for format v0")
238 raise RevlogError(_("index %s unknown flags %#04x for format v0")
239 % (self.indexfile, flags >> 16))
239 % (self.indexfile, flags >> 16))
240 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
240 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
241 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
241 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
242 % (self.indexfile, flags >> 16))
242 % (self.indexfile, flags >> 16))
243 elif fmt > REVLOGNG:
243 elif fmt > REVLOGNG:
244 raise RevlogError(_("index %s unknown format %d")
244 raise RevlogError(_("index %s unknown format %d")
245 % (self.indexfile, fmt))
245 % (self.indexfile, fmt))
246
246
247 self._io = revlogio()
247 self._io = revlogio()
248 if self.version == REVLOGV0:
248 if self.version == REVLOGV0:
249 self._io = revlogoldio()
249 self._io = revlogoldio()
250 try:
250 try:
251 d = self._io.parseindex(i, self._inline)
251 d = self._io.parseindex(i, self._inline)
252 except (ValueError, IndexError):
252 except (ValueError, IndexError):
253 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
253 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
254 self.index, nodemap, self._chunkcache = d
254 self.index, nodemap, self._chunkcache = d
255 if nodemap is not None:
255 if nodemap is not None:
256 self.nodemap = self._nodecache = nodemap
256 self.nodemap = self._nodecache = nodemap
257 if not self._chunkcache:
257 if not self._chunkcache:
258 self._chunkclear()
258 self._chunkclear()
259
259
260 def tip(self):
260 def tip(self):
261 return self.node(len(self.index) - 2)
261 return self.node(len(self.index) - 2)
262 def __len__(self):
262 def __len__(self):
263 return len(self.index) - 1
263 return len(self.index) - 1
264 def __iter__(self):
264 def __iter__(self):
265 return iter(xrange(len(self)))
265 return iter(xrange(len(self)))
266 def revs(self, start=0, stop=None):
266 def revs(self, start=0, stop=None):
267 """iterate over all rev in this revlog (from start to stop)"""
267 """iterate over all rev in this revlog (from start to stop)"""
268 step = 1
268 step = 1
269 if stop is not None:
269 if stop is not None:
270 if start > stop:
270 if start > stop:
271 step = -1
271 step = -1
272 stop += step
272 stop += step
273 else:
273 else:
274 stop = len(self)
274 stop = len(self)
275 return xrange(start, stop, step)
275 return xrange(start, stop, step)
276
276
277 @util.propertycache
277 @util.propertycache
278 def nodemap(self):
278 def nodemap(self):
279 self.rev(self.node(0))
279 self.rev(self.node(0))
280 return self._nodecache
280 return self._nodecache
281
281
282 def hasnode(self, node):
282 def hasnode(self, node):
283 try:
283 try:
284 self.rev(node)
284 self.rev(node)
285 return True
285 return True
286 except KeyError:
286 except KeyError:
287 return False
287 return False
288
288
289 def clearcaches(self):
289 def clearcaches(self):
290 try:
290 try:
291 self._nodecache.clearcaches()
291 self._nodecache.clearcaches()
292 except AttributeError:
292 except AttributeError:
293 self._nodecache = {nullid: nullrev}
293 self._nodecache = {nullid: nullrev}
294 self._nodepos = None
294 self._nodepos = None
295
295
296 def rev(self, node):
296 def rev(self, node):
297 try:
297 try:
298 return self._nodecache[node]
298 return self._nodecache[node]
299 except RevlogError:
299 except RevlogError:
300 # parsers.c radix tree lookup failed
300 # parsers.c radix tree lookup failed
301 raise LookupError(node, self.indexfile, _('no node'))
301 raise LookupError(node, self.indexfile, _('no node'))
302 except KeyError:
302 except KeyError:
303 # pure python cache lookup failed
303 # pure python cache lookup failed
304 n = self._nodecache
304 n = self._nodecache
305 i = self.index
305 i = self.index
306 p = self._nodepos
306 p = self._nodepos
307 if p is None:
307 if p is None:
308 p = len(i) - 2
308 p = len(i) - 2
309 for r in xrange(p, -1, -1):
309 for r in xrange(p, -1, -1):
310 v = i[r][7]
310 v = i[r][7]
311 n[v] = r
311 n[v] = r
312 if v == node:
312 if v == node:
313 self._nodepos = r - 1
313 self._nodepos = r - 1
314 return r
314 return r
315 raise LookupError(node, self.indexfile, _('no node'))
315 raise LookupError(node, self.indexfile, _('no node'))
316
316
317 def node(self, rev):
317 def node(self, rev):
318 return self.index[rev][7]
318 return self.index[rev][7]
319 def linkrev(self, rev):
319 def linkrev(self, rev):
320 return self.index[rev][4]
320 return self.index[rev][4]
321 def parents(self, node):
321 def parents(self, node):
322 i = self.index
322 i = self.index
323 d = i[self.rev(node)]
323 d = i[self.rev(node)]
324 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
324 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
325 def parentrevs(self, rev):
325 def parentrevs(self, rev):
326 return self.index[rev][5:7]
326 return self.index[rev][5:7]
327 def start(self, rev):
327 def start(self, rev):
328 return int(self.index[rev][0] >> 16)
328 return int(self.index[rev][0] >> 16)
329 def end(self, rev):
329 def end(self, rev):
330 return self.start(rev) + self.length(rev)
330 return self.start(rev) + self.length(rev)
331 def length(self, rev):
331 def length(self, rev):
332 return self.index[rev][1]
332 return self.index[rev][1]
333 def chainbase(self, rev):
333 def chainbase(self, rev):
334 index = self.index
334 index = self.index
335 base = index[rev][3]
335 base = index[rev][3]
336 while base != rev:
336 while base != rev:
337 rev = base
337 rev = base
338 base = index[rev][3]
338 base = index[rev][3]
339 return base
339 return base
340 def flags(self, rev):
340 def flags(self, rev):
341 return self.index[rev][0] & 0xFFFF
341 return self.index[rev][0] & 0xFFFF
342 def rawsize(self, rev):
342 def rawsize(self, rev):
343 """return the length of the uncompressed text for a given revision"""
343 """return the length of the uncompressed text for a given revision"""
344 l = self.index[rev][2]
344 l = self.index[rev][2]
345 if l >= 0:
345 if l >= 0:
346 return l
346 return l
347
347
348 t = self.revision(self.node(rev))
348 t = self.revision(self.node(rev))
349 return len(t)
349 return len(t)
350 size = rawsize
350 size = rawsize
351
351
352 def ancestors(self, revs, stoprev=0, inclusive=False):
352 def ancestors(self, revs, stoprev=0, inclusive=False):
353 """Generate the ancestors of 'revs' in reverse topological order.
353 """Generate the ancestors of 'revs' in reverse topological order.
354 Does not generate revs lower than stoprev.
354 Does not generate revs lower than stoprev.
355
355
356 See the documentation for ancestor.lazyancestors for more details."""
356 See the documentation for ancestor.lazyancestors for more details."""
357
357
358 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
358 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
359 inclusive=inclusive)
359 inclusive=inclusive)
360
360
361 def descendants(self, revs):
361 def descendants(self, revs):
362 """Generate the descendants of 'revs' in revision order.
362 """Generate the descendants of 'revs' in revision order.
363
363
364 Yield a sequence of revision numbers starting with a child of
364 Yield a sequence of revision numbers starting with a child of
365 some rev in revs, i.e., each revision is *not* considered a
365 some rev in revs, i.e., each revision is *not* considered a
366 descendant of itself. Results are ordered by revision number (a
366 descendant of itself. Results are ordered by revision number (a
367 topological sort)."""
367 topological sort)."""
368 first = min(revs)
368 first = min(revs)
369 if first == nullrev:
369 if first == nullrev:
370 for i in self:
370 for i in self:
371 yield i
371 yield i
372 return
372 return
373
373
374 seen = set(revs)
374 seen = set(revs)
375 for i in self.revs(start=first + 1):
375 for i in self.revs(start=first + 1):
376 for x in self.parentrevs(i):
376 for x in self.parentrevs(i):
377 if x != nullrev and x in seen:
377 if x != nullrev and x in seen:
378 seen.add(i)
378 seen.add(i)
379 yield i
379 yield i
380 break
380 break
381
381
382 def findcommonmissing(self, common=None, heads=None):
382 def findcommonmissing(self, common=None, heads=None):
383 """Return a tuple of the ancestors of common and the ancestors of heads
383 """Return a tuple of the ancestors of common and the ancestors of heads
384 that are not ancestors of common. In revset terminology, we return the
384 that are not ancestors of common. In revset terminology, we return the
385 tuple:
385 tuple:
386
386
387 ::common, (::heads) - (::common)
387 ::common, (::heads) - (::common)
388
388
389 The list is sorted by revision number, meaning it is
389 The list is sorted by revision number, meaning it is
390 topologically sorted.
390 topologically sorted.
391
391
392 'heads' and 'common' are both lists of node IDs. If heads is
392 'heads' and 'common' are both lists of node IDs. If heads is
393 not supplied, uses all of the revlog's heads. If common is not
393 not supplied, uses all of the revlog's heads. If common is not
394 supplied, uses nullid."""
394 supplied, uses nullid."""
395 if common is None:
395 if common is None:
396 common = [nullid]
396 common = [nullid]
397 if heads is None:
397 if heads is None:
398 heads = self.heads()
398 heads = self.heads()
399
399
400 common = [self.rev(n) for n in common]
400 common = [self.rev(n) for n in common]
401 heads = [self.rev(n) for n in heads]
401 heads = [self.rev(n) for n in heads]
402
402
403 # we want the ancestors, but inclusive
403 # we want the ancestors, but inclusive
404 has = set(self.ancestors(common))
404 has = set(self.ancestors(common))
405 has.add(nullrev)
405 has.add(nullrev)
406 has.update(common)
406 has.update(common)
407
407
408 # take all ancestors from heads that aren't in has
408 # take all ancestors from heads that aren't in has
409 missing = set()
409 missing = set()
410 visit = util.deque(r for r in heads if r not in has)
410 visit = util.deque(r for r in heads if r not in has)
411 while visit:
411 while visit:
412 r = visit.popleft()
412 r = visit.popleft()
413 if r in missing:
413 if r in missing:
414 continue
414 continue
415 else:
415 else:
416 missing.add(r)
416 missing.add(r)
417 for p in self.parentrevs(r):
417 for p in self.parentrevs(r):
418 if p not in has:
418 if p not in has:
419 visit.append(p)
419 visit.append(p)
420 missing = list(missing)
420 missing = list(missing)
421 missing.sort()
421 missing.sort()
422 return has, [self.node(r) for r in missing]
422 return has, [self.node(r) for r in missing]
423
423
424 def findmissingrevs(self, common=None, heads=None):
424 def findmissingrevs(self, common=None, heads=None):
425 """Return the revision numbers of the ancestors of heads that
425 """Return the revision numbers of the ancestors of heads that
426 are not ancestors of common.
426 are not ancestors of common.
427
427
428 More specifically, return a list of revision numbers corresponding to
428 More specifically, return a list of revision numbers corresponding to
429 nodes N such that every N satisfies the following constraints:
429 nodes N such that every N satisfies the following constraints:
430
430
431 1. N is an ancestor of some node in 'heads'
431 1. N is an ancestor of some node in 'heads'
432 2. N is not an ancestor of any node in 'common'
432 2. N is not an ancestor of any node in 'common'
433
433
434 The list is sorted by revision number, meaning it is
434 The list is sorted by revision number, meaning it is
435 topologically sorted.
435 topologically sorted.
436
436
437 'heads' and 'common' are both lists of revision numbers. If heads is
437 'heads' and 'common' are both lists of revision numbers. If heads is
438 not supplied, uses all of the revlog's heads. If common is not
438 not supplied, uses all of the revlog's heads. If common is not
439 supplied, uses nullid."""
439 supplied, uses nullid."""
440 if common is None:
440 if common is None:
441 common = [nullrev]
441 common = [nullrev]
442 if heads is None:
442 if heads is None:
443 heads = self.headrevs()
443 heads = self.headrevs()
444
444
445 return ancestor.missingancestors(heads, common, self.parentrevs)
445 return ancestor.missingancestors(heads, common, self.parentrevs)
446
446
447 def findmissing(self, common=None, heads=None):
447 def findmissing(self, common=None, heads=None):
448 """Return the ancestors of heads that are not ancestors of common.
448 """Return the ancestors of heads that are not ancestors of common.
449
449
450 More specifically, return a list of nodes N such that every N
450 More specifically, return a list of nodes N such that every N
451 satisfies the following constraints:
451 satisfies the following constraints:
452
452
453 1. N is an ancestor of some node in 'heads'
453 1. N is an ancestor of some node in 'heads'
454 2. N is not an ancestor of any node in 'common'
454 2. N is not an ancestor of any node in 'common'
455
455
456 The list is sorted by revision number, meaning it is
456 The list is sorted by revision number, meaning it is
457 topologically sorted.
457 topologically sorted.
458
458
459 'heads' and 'common' are both lists of node IDs. If heads is
459 'heads' and 'common' are both lists of node IDs. If heads is
460 not supplied, uses all of the revlog's heads. If common is not
460 not supplied, uses all of the revlog's heads. If common is not
461 supplied, uses nullid."""
461 supplied, uses nullid."""
462 if common is None:
462 if common is None:
463 common = [nullid]
463 common = [nullid]
464 if heads is None:
464 if heads is None:
465 heads = self.heads()
465 heads = self.heads()
466
466
467 common = [self.rev(n) for n in common]
467 common = [self.rev(n) for n in common]
468 heads = [self.rev(n) for n in heads]
468 heads = [self.rev(n) for n in heads]
469
469
470 return [self.node(r) for r in
470 return [self.node(r) for r in
471 ancestor.missingancestors(heads, common, self.parentrevs)]
471 ancestor.missingancestors(heads, common, self.parentrevs)]
472
472
473 def nodesbetween(self, roots=None, heads=None):
473 def nodesbetween(self, roots=None, heads=None):
474 """Return a topological path from 'roots' to 'heads'.
474 """Return a topological path from 'roots' to 'heads'.
475
475
476 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
476 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
477 topologically sorted list of all nodes N that satisfy both of
477 topologically sorted list of all nodes N that satisfy both of
478 these constraints:
478 these constraints:
479
479
480 1. N is a descendant of some node in 'roots'
480 1. N is a descendant of some node in 'roots'
481 2. N is an ancestor of some node in 'heads'
481 2. N is an ancestor of some node in 'heads'
482
482
483 Every node is considered to be both a descendant and an ancestor
483 Every node is considered to be both a descendant and an ancestor
484 of itself, so every reachable node in 'roots' and 'heads' will be
484 of itself, so every reachable node in 'roots' and 'heads' will be
485 included in 'nodes'.
485 included in 'nodes'.
486
486
487 'outroots' is the list of reachable nodes in 'roots', i.e., the
487 'outroots' is the list of reachable nodes in 'roots', i.e., the
488 subset of 'roots' that is returned in 'nodes'. Likewise,
488 subset of 'roots' that is returned in 'nodes'. Likewise,
489 'outheads' is the subset of 'heads' that is also in 'nodes'.
489 'outheads' is the subset of 'heads' that is also in 'nodes'.
490
490
491 'roots' and 'heads' are both lists of node IDs. If 'roots' is
491 'roots' and 'heads' are both lists of node IDs. If 'roots' is
492 unspecified, uses nullid as the only root. If 'heads' is
492 unspecified, uses nullid as the only root. If 'heads' is
493 unspecified, uses list of all of the revlog's heads."""
493 unspecified, uses list of all of the revlog's heads."""
494 nonodes = ([], [], [])
494 nonodes = ([], [], [])
495 if roots is not None:
495 if roots is not None:
496 roots = list(roots)
496 roots = list(roots)
497 if not roots:
497 if not roots:
498 return nonodes
498 return nonodes
499 lowestrev = min([self.rev(n) for n in roots])
499 lowestrev = min([self.rev(n) for n in roots])
500 else:
500 else:
501 roots = [nullid] # Everybody's a descendant of nullid
501 roots = [nullid] # Everybody's a descendant of nullid
502 lowestrev = nullrev
502 lowestrev = nullrev
503 if (lowestrev == nullrev) and (heads is None):
503 if (lowestrev == nullrev) and (heads is None):
504 # We want _all_ the nodes!
504 # We want _all_ the nodes!
505 return ([self.node(r) for r in self], [nullid], list(self.heads()))
505 return ([self.node(r) for r in self], [nullid], list(self.heads()))
506 if heads is None:
506 if heads is None:
507 # All nodes are ancestors, so the latest ancestor is the last
507 # All nodes are ancestors, so the latest ancestor is the last
508 # node.
508 # node.
509 highestrev = len(self) - 1
509 highestrev = len(self) - 1
510 # Set ancestors to None to signal that every node is an ancestor.
510 # Set ancestors to None to signal that every node is an ancestor.
511 ancestors = None
511 ancestors = None
512 # Set heads to an empty dictionary for later discovery of heads
512 # Set heads to an empty dictionary for later discovery of heads
513 heads = {}
513 heads = {}
514 else:
514 else:
515 heads = list(heads)
515 heads = list(heads)
516 if not heads:
516 if not heads:
517 return nonodes
517 return nonodes
518 ancestors = set()
518 ancestors = set()
519 # Turn heads into a dictionary so we can remove 'fake' heads.
519 # Turn heads into a dictionary so we can remove 'fake' heads.
520 # Also, later we will be using it to filter out the heads we can't
520 # Also, later we will be using it to filter out the heads we can't
521 # find from roots.
521 # find from roots.
522 heads = dict.fromkeys(heads, False)
522 heads = dict.fromkeys(heads, False)
523 # Start at the top and keep marking parents until we're done.
523 # Start at the top and keep marking parents until we're done.
524 nodestotag = set(heads)
524 nodestotag = set(heads)
525 # Remember where the top was so we can use it as a limit later.
525 # Remember where the top was so we can use it as a limit later.
526 highestrev = max([self.rev(n) for n in nodestotag])
526 highestrev = max([self.rev(n) for n in nodestotag])
527 while nodestotag:
527 while nodestotag:
528 # grab a node to tag
528 # grab a node to tag
529 n = nodestotag.pop()
529 n = nodestotag.pop()
530 # Never tag nullid
530 # Never tag nullid
531 if n == nullid:
531 if n == nullid:
532 continue
532 continue
533 # A node's revision number represents its place in a
533 # A node's revision number represents its place in a
534 # topologically sorted list of nodes.
534 # topologically sorted list of nodes.
535 r = self.rev(n)
535 r = self.rev(n)
536 if r >= lowestrev:
536 if r >= lowestrev:
537 if n not in ancestors:
537 if n not in ancestors:
538 # If we are possibly a descendant of one of the roots
538 # If we are possibly a descendant of one of the roots
539 # and we haven't already been marked as an ancestor
539 # and we haven't already been marked as an ancestor
540 ancestors.add(n) # Mark as ancestor
540 ancestors.add(n) # Mark as ancestor
541 # Add non-nullid parents to list of nodes to tag.
541 # Add non-nullid parents to list of nodes to tag.
542 nodestotag.update([p for p in self.parents(n) if
542 nodestotag.update([p for p in self.parents(n) if
543 p != nullid])
543 p != nullid])
544 elif n in heads: # We've seen it before, is it a fake head?
544 elif n in heads: # We've seen it before, is it a fake head?
545 # So it is, real heads should not be the ancestors of
545 # So it is, real heads should not be the ancestors of
546 # any other heads.
546 # any other heads.
547 heads.pop(n)
547 heads.pop(n)
548 if not ancestors:
548 if not ancestors:
549 return nonodes
549 return nonodes
550 # Now that we have our set of ancestors, we want to remove any
550 # Now that we have our set of ancestors, we want to remove any
551 # roots that are not ancestors.
551 # roots that are not ancestors.
552
552
553 # If one of the roots was nullid, everything is included anyway.
553 # If one of the roots was nullid, everything is included anyway.
554 if lowestrev > nullrev:
554 if lowestrev > nullrev:
555 # But, since we weren't, let's recompute the lowest rev to not
555 # But, since we weren't, let's recompute the lowest rev to not
556 # include roots that aren't ancestors.
556 # include roots that aren't ancestors.
557
557
558 # Filter out roots that aren't ancestors of heads
558 # Filter out roots that aren't ancestors of heads
559 roots = [n for n in roots if n in ancestors]
559 roots = [n for n in roots if n in ancestors]
560 # Recompute the lowest revision
560 # Recompute the lowest revision
561 if roots:
561 if roots:
562 lowestrev = min([self.rev(n) for n in roots])
562 lowestrev = min([self.rev(n) for n in roots])
563 else:
563 else:
564 # No more roots? Return empty list
564 # No more roots? Return empty list
565 return nonodes
565 return nonodes
566 else:
566 else:
567 # We are descending from nullid, and don't need to care about
567 # We are descending from nullid, and don't need to care about
568 # any other roots.
568 # any other roots.
569 lowestrev = nullrev
569 lowestrev = nullrev
570 roots = [nullid]
570 roots = [nullid]
571 # Transform our roots list into a set.
571 # Transform our roots list into a set.
572 descendants = set(roots)
572 descendants = set(roots)
573 # Also, keep the original roots so we can filter out roots that aren't
573 # Also, keep the original roots so we can filter out roots that aren't
574 # 'real' roots (i.e. are descended from other roots).
574 # 'real' roots (i.e. are descended from other roots).
575 roots = descendants.copy()
575 roots = descendants.copy()
576 # Our topologically sorted list of output nodes.
576 # Our topologically sorted list of output nodes.
577 orderedout = []
577 orderedout = []
578 # Don't start at nullid since we don't want nullid in our output list,
578 # Don't start at nullid since we don't want nullid in our output list,
579 # and if nullid shows up in descendants, empty parents will look like
579 # and if nullid shows up in descendants, empty parents will look like
580 # they're descendants.
580 # they're descendants.
581 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
581 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
582 n = self.node(r)
582 n = self.node(r)
583 isdescendant = False
583 isdescendant = False
584 if lowestrev == nullrev: # Everybody is a descendant of nullid
584 if lowestrev == nullrev: # Everybody is a descendant of nullid
585 isdescendant = True
585 isdescendant = True
586 elif n in descendants:
586 elif n in descendants:
587 # n is already a descendant
587 # n is already a descendant
588 isdescendant = True
588 isdescendant = True
589 # This check only needs to be done here because all the roots
589 # This check only needs to be done here because all the roots
590 # will start being marked is descendants before the loop.
590 # will start being marked is descendants before the loop.
591 if n in roots:
591 if n in roots:
592 # If n was a root, check if it's a 'real' root.
592 # If n was a root, check if it's a 'real' root.
593 p = tuple(self.parents(n))
593 p = tuple(self.parents(n))
594 # If any of its parents are descendants, it's not a root.
594 # If any of its parents are descendants, it's not a root.
595 if (p[0] in descendants) or (p[1] in descendants):
595 if (p[0] in descendants) or (p[1] in descendants):
596 roots.remove(n)
596 roots.remove(n)
597 else:
597 else:
598 p = tuple(self.parents(n))
598 p = tuple(self.parents(n))
599 # A node is a descendant if either of its parents are
599 # A node is a descendant if either of its parents are
600 # descendants. (We seeded the dependents list with the roots
600 # descendants. (We seeded the dependents list with the roots
601 # up there, remember?)
601 # up there, remember?)
602 if (p[0] in descendants) or (p[1] in descendants):
602 if (p[0] in descendants) or (p[1] in descendants):
603 descendants.add(n)
603 descendants.add(n)
604 isdescendant = True
604 isdescendant = True
605 if isdescendant and ((ancestors is None) or (n in ancestors)):
605 if isdescendant and ((ancestors is None) or (n in ancestors)):
606 # Only include nodes that are both descendants and ancestors.
606 # Only include nodes that are both descendants and ancestors.
607 orderedout.append(n)
607 orderedout.append(n)
608 if (ancestors is not None) and (n in heads):
608 if (ancestors is not None) and (n in heads):
609 # We're trying to figure out which heads are reachable
609 # We're trying to figure out which heads are reachable
610 # from roots.
610 # from roots.
611 # Mark this head as having been reached
611 # Mark this head as having been reached
612 heads[n] = True
612 heads[n] = True
613 elif ancestors is None:
613 elif ancestors is None:
614 # Otherwise, we're trying to discover the heads.
614 # Otherwise, we're trying to discover the heads.
615 # Assume this is a head because if it isn't, the next step
615 # Assume this is a head because if it isn't, the next step
616 # will eventually remove it.
616 # will eventually remove it.
617 heads[n] = True
617 heads[n] = True
618 # But, obviously its parents aren't.
618 # But, obviously its parents aren't.
619 for p in self.parents(n):
619 for p in self.parents(n):
620 heads.pop(p, None)
620 heads.pop(p, None)
621 heads = [n for n, flag in heads.iteritems() if flag]
621 heads = [n for n, flag in heads.iteritems() if flag]
622 roots = list(roots)
622 roots = list(roots)
623 assert orderedout
623 assert orderedout
624 assert roots
624 assert roots
625 assert heads
625 assert heads
626 return (orderedout, roots, heads)
626 return (orderedout, roots, heads)
627
627
628 def headrevs(self):
628 def headrevs(self):
629 try:
629 try:
630 return self.index.headrevs()
630 return self.index.headrevs()
631 except AttributeError:
631 except AttributeError:
632 return self._headrevs()
632 return self._headrevs()
633
633
634 def _headrevs(self):
634 def _headrevs(self):
635 count = len(self)
635 count = len(self)
636 if not count:
636 if not count:
637 return [nullrev]
637 return [nullrev]
638 # we won't iter over filtered rev so nobody is a head at start
638 # we won't iter over filtered rev so nobody is a head at start
639 ishead = [0] * (count + 1)
639 ishead = [0] * (count + 1)
640 index = self.index
640 index = self.index
641 for r in self:
641 for r in self:
642 ishead[r] = 1 # I may be an head
642 ishead[r] = 1 # I may be an head
643 e = index[r]
643 e = index[r]
644 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
644 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
645 return [r for r, val in enumerate(ishead) if val]
645 return [r for r, val in enumerate(ishead) if val]
646
646
647 def heads(self, start=None, stop=None):
647 def heads(self, start=None, stop=None):
648 """return the list of all nodes that have no children
648 """return the list of all nodes that have no children
649
649
650 if start is specified, only heads that are descendants of
650 if start is specified, only heads that are descendants of
651 start will be returned
651 start will be returned
652 if stop is specified, it will consider all the revs from stop
652 if stop is specified, it will consider all the revs from stop
653 as if they had no children
653 as if they had no children
654 """
654 """
655 if start is None and stop is None:
655 if start is None and stop is None:
656 if not len(self):
656 if not len(self):
657 return [nullid]
657 return [nullid]
658 return [self.node(r) for r in self.headrevs()]
658 return [self.node(r) for r in self.headrevs()]
659
659
660 if start is None:
660 if start is None:
661 start = nullid
661 start = nullid
662 if stop is None:
662 if stop is None:
663 stop = []
663 stop = []
664 stoprevs = set([self.rev(n) for n in stop])
664 stoprevs = set([self.rev(n) for n in stop])
665 startrev = self.rev(start)
665 startrev = self.rev(start)
666 reachable = set((startrev,))
666 reachable = set((startrev,))
667 heads = set((startrev,))
667 heads = set((startrev,))
668
668
669 parentrevs = self.parentrevs
669 parentrevs = self.parentrevs
670 for r in self.revs(start=startrev + 1):
670 for r in self.revs(start=startrev + 1):
671 for p in parentrevs(r):
671 for p in parentrevs(r):
672 if p in reachable:
672 if p in reachable:
673 if r not in stoprevs:
673 if r not in stoprevs:
674 reachable.add(r)
674 reachable.add(r)
675 heads.add(r)
675 heads.add(r)
676 if p in heads and p not in stoprevs:
676 if p in heads and p not in stoprevs:
677 heads.remove(p)
677 heads.remove(p)
678
678
679 return [self.node(r) for r in heads]
679 return [self.node(r) for r in heads]
680
680
681 def children(self, node):
681 def children(self, node):
682 """find the children of a given node"""
682 """find the children of a given node"""
683 c = []
683 c = []
684 p = self.rev(node)
684 p = self.rev(node)
685 for r in self.revs(start=p + 1):
685 for r in self.revs(start=p + 1):
686 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
686 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
687 if prevs:
687 if prevs:
688 for pr in prevs:
688 for pr in prevs:
689 if pr == p:
689 if pr == p:
690 c.append(self.node(r))
690 c.append(self.node(r))
691 elif p == nullrev:
691 elif p == nullrev:
692 c.append(self.node(r))
692 c.append(self.node(r))
693 return c
693 return c
694
694
695 def descendant(self, start, end):
695 def descendant(self, start, end):
696 if start == nullrev:
696 if start == nullrev:
697 return True
697 return True
698 for i in self.descendants([start]):
698 for i in self.descendants([start]):
699 if i == end:
699 if i == end:
700 return True
700 return True
701 elif i > end:
701 elif i > end:
702 break
702 break
703 return False
703 return False
704
704
705 def ancestor(self, a, b):
705 def ancestor(self, a, b):
706 """calculate the least common ancestor of nodes a and b"""
706 """calculate the least common ancestor of nodes a and b"""
707
707
708 a, b = self.rev(a), self.rev(b)
708 a, b = self.rev(a), self.rev(b)
709 try:
709 try:
710 ancs = self.index.ancestors(a, b)
710 ancs = self.index.ancestors(a, b)
711 except (AttributeError, OverflowError):
711 except (AttributeError, OverflowError):
712 ancs = ancestor.ancestors(self.parentrevs, a, b)
712 ancs = ancestor.ancestors(self.parentrevs, a, b)
713 if ancs:
713 if ancs:
714 # choose a consistent winner when there's a tie
714 # choose a consistent winner when there's a tie
715 return min(map(self.node, ancs))
715 return min(map(self.node, ancs))
716 return nullid
716 return nullid
717
717
718 def _match(self, id):
718 def _match(self, id):
719 if isinstance(id, int):
719 if isinstance(id, int):
720 # rev
720 # rev
721 return self.node(id)
721 return self.node(id)
722 if len(id) == 20:
722 if len(id) == 20:
723 # possibly a binary node
723 # possibly a binary node
724 # odds of a binary node being all hex in ASCII are 1 in 10**25
724 # odds of a binary node being all hex in ASCII are 1 in 10**25
725 try:
725 try:
726 node = id
726 node = id
727 self.rev(node) # quick search the index
727 self.rev(node) # quick search the index
728 return node
728 return node
729 except LookupError:
729 except LookupError:
730 pass # may be partial hex id
730 pass # may be partial hex id
731 try:
731 try:
732 # str(rev)
732 # str(rev)
733 rev = int(id)
733 rev = int(id)
734 if str(rev) != id:
734 if str(rev) != id:
735 raise ValueError
735 raise ValueError
736 if rev < 0:
736 if rev < 0:
737 rev = len(self) + rev
737 rev = len(self) + rev
738 if rev < 0 or rev >= len(self):
738 if rev < 0 or rev >= len(self):
739 raise ValueError
739 raise ValueError
740 return self.node(rev)
740 return self.node(rev)
741 except (ValueError, OverflowError):
741 except (ValueError, OverflowError):
742 pass
742 pass
743 if len(id) == 40:
743 if len(id) == 40:
744 try:
744 try:
745 # a full hex nodeid?
745 # a full hex nodeid?
746 node = bin(id)
746 node = bin(id)
747 self.rev(node)
747 self.rev(node)
748 return node
748 return node
749 except (TypeError, LookupError):
749 except (TypeError, LookupError):
750 pass
750 pass
751
751
752 def _partialmatch(self, id):
752 def _partialmatch(self, id):
753 try:
753 try:
754 return self.index.partialmatch(id)
754 return self.index.partialmatch(id)
755 except RevlogError:
755 except RevlogError:
756 # parsers.c radix tree lookup gave multiple matches
756 # parsers.c radix tree lookup gave multiple matches
757 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
757 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
758 except (AttributeError, ValueError):
758 except (AttributeError, ValueError):
759 # we are pure python, or key was too short to search radix tree
759 # we are pure python, or key was too short to search radix tree
760 pass
760 pass
761
761
762 if id in self._pcache:
762 if id in self._pcache:
763 return self._pcache[id]
763 return self._pcache[id]
764
764
765 if len(id) < 40:
765 if len(id) < 40:
766 try:
766 try:
767 # hex(node)[:...]
767 # hex(node)[:...]
768 l = len(id) // 2 # grab an even number of digits
768 l = len(id) // 2 # grab an even number of digits
769 prefix = bin(id[:l * 2])
769 prefix = bin(id[:l * 2])
770 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
770 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
771 nl = [n for n in nl if hex(n).startswith(id)]
771 nl = [n for n in nl if hex(n).startswith(id)]
772 if len(nl) > 0:
772 if len(nl) > 0:
773 if len(nl) == 1:
773 if len(nl) == 1:
774 if nl[0] == nullid:
775 # dummy null revision always exists,
776 # it shouldn't be returned here
777 return None
774 self._pcache[id] = nl[0]
778 self._pcache[id] = nl[0]
775 return nl[0]
779 return nl[0]
776 raise LookupError(id, self.indexfile,
780 raise LookupError(id, self.indexfile,
777 _('ambiguous identifier'))
781 _('ambiguous identifier'))
778 return None
782 return None
779 except TypeError:
783 except TypeError:
780 pass
784 pass
781
785
782 def lookup(self, id):
786 def lookup(self, id):
783 """locate a node based on:
787 """locate a node based on:
784 - revision number or str(revision number)
788 - revision number or str(revision number)
785 - nodeid or subset of hex nodeid
789 - nodeid or subset of hex nodeid
786 """
790 """
787 n = self._match(id)
791 n = self._match(id)
788 if n is not None:
792 if n is not None:
789 return n
793 return n
790 n = self._partialmatch(id)
794 n = self._partialmatch(id)
791 if n:
795 if n:
792 return n
796 return n
793
797
794 raise LookupError(id, self.indexfile, _('no match found'))
798 raise LookupError(id, self.indexfile, _('no match found'))
795
799
796 def cmp(self, node, text):
800 def cmp(self, node, text):
797 """compare text with a given file revision
801 """compare text with a given file revision
798
802
799 returns True if text is different than what is stored.
803 returns True if text is different than what is stored.
800 """
804 """
801 p1, p2 = self.parents(node)
805 p1, p2 = self.parents(node)
802 return hash(text, p1, p2) != node
806 return hash(text, p1, p2) != node
803
807
804 def _addchunk(self, offset, data):
808 def _addchunk(self, offset, data):
805 o, d = self._chunkcache
809 o, d = self._chunkcache
806 # try to add to existing cache
810 # try to add to existing cache
807 if o + len(d) == offset and len(d) + len(data) < _chunksize:
811 if o + len(d) == offset and len(d) + len(data) < _chunksize:
808 self._chunkcache = o, d + data
812 self._chunkcache = o, d + data
809 else:
813 else:
810 self._chunkcache = offset, data
814 self._chunkcache = offset, data
811
815
812 def _loadchunk(self, offset, length):
816 def _loadchunk(self, offset, length):
813 if self._inline:
817 if self._inline:
814 df = self.opener(self.indexfile)
818 df = self.opener(self.indexfile)
815 else:
819 else:
816 df = self.opener(self.datafile)
820 df = self.opener(self.datafile)
817
821
818 readahead = max(65536, length)
822 readahead = max(65536, length)
819 df.seek(offset)
823 df.seek(offset)
820 d = df.read(readahead)
824 d = df.read(readahead)
821 df.close()
825 df.close()
822 self._addchunk(offset, d)
826 self._addchunk(offset, d)
823 if readahead > length:
827 if readahead > length:
824 return util.buffer(d, 0, length)
828 return util.buffer(d, 0, length)
825 return d
829 return d
826
830
827 def _getchunk(self, offset, length):
831 def _getchunk(self, offset, length):
828 o, d = self._chunkcache
832 o, d = self._chunkcache
829 l = len(d)
833 l = len(d)
830
834
831 # is it in the cache?
835 # is it in the cache?
832 cachestart = offset - o
836 cachestart = offset - o
833 cacheend = cachestart + length
837 cacheend = cachestart + length
834 if cachestart >= 0 and cacheend <= l:
838 if cachestart >= 0 and cacheend <= l:
835 if cachestart == 0 and cacheend == l:
839 if cachestart == 0 and cacheend == l:
836 return d # avoid a copy
840 return d # avoid a copy
837 return util.buffer(d, cachestart, cacheend - cachestart)
841 return util.buffer(d, cachestart, cacheend - cachestart)
838
842
839 return self._loadchunk(offset, length)
843 return self._loadchunk(offset, length)
840
844
841 def _chunkraw(self, startrev, endrev):
845 def _chunkraw(self, startrev, endrev):
842 start = self.start(startrev)
846 start = self.start(startrev)
843 length = self.end(endrev) - start
847 length = self.end(endrev) - start
844 if self._inline:
848 if self._inline:
845 start += (startrev + 1) * self._io.size
849 start += (startrev + 1) * self._io.size
846 return self._getchunk(start, length)
850 return self._getchunk(start, length)
847
851
848 def _chunk(self, rev):
852 def _chunk(self, rev):
849 return decompress(self._chunkraw(rev, rev))
853 return decompress(self._chunkraw(rev, rev))
850
854
851 def _chunkbase(self, rev):
855 def _chunkbase(self, rev):
852 return self._chunk(rev)
856 return self._chunk(rev)
853
857
854 def _chunkclear(self):
858 def _chunkclear(self):
855 self._chunkcache = (0, '')
859 self._chunkcache = (0, '')
856
860
857 def deltaparent(self, rev):
861 def deltaparent(self, rev):
858 """return deltaparent of the given revision"""
862 """return deltaparent of the given revision"""
859 base = self.index[rev][3]
863 base = self.index[rev][3]
860 if base == rev:
864 if base == rev:
861 return nullrev
865 return nullrev
862 elif self._generaldelta:
866 elif self._generaldelta:
863 return base
867 return base
864 else:
868 else:
865 return rev - 1
869 return rev - 1
866
870
867 def revdiff(self, rev1, rev2):
871 def revdiff(self, rev1, rev2):
868 """return or calculate a delta between two revisions"""
872 """return or calculate a delta between two revisions"""
869 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
873 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
870 return str(self._chunk(rev2))
874 return str(self._chunk(rev2))
871
875
872 return mdiff.textdiff(self.revision(rev1),
876 return mdiff.textdiff(self.revision(rev1),
873 self.revision(rev2))
877 self.revision(rev2))
874
878
875 def revision(self, nodeorrev):
879 def revision(self, nodeorrev):
876 """return an uncompressed revision of a given node or revision
880 """return an uncompressed revision of a given node or revision
877 number.
881 number.
878 """
882 """
879 if isinstance(nodeorrev, int):
883 if isinstance(nodeorrev, int):
880 rev = nodeorrev
884 rev = nodeorrev
881 node = self.node(rev)
885 node = self.node(rev)
882 else:
886 else:
883 node = nodeorrev
887 node = nodeorrev
884 rev = None
888 rev = None
885
889
886 cachedrev = None
890 cachedrev = None
887 if node == nullid:
891 if node == nullid:
888 return ""
892 return ""
889 if self._cache:
893 if self._cache:
890 if self._cache[0] == node:
894 if self._cache[0] == node:
891 return self._cache[2]
895 return self._cache[2]
892 cachedrev = self._cache[1]
896 cachedrev = self._cache[1]
893
897
894 # look up what we need to read
898 # look up what we need to read
895 text = None
899 text = None
896 if rev is None:
900 if rev is None:
897 rev = self.rev(node)
901 rev = self.rev(node)
898
902
899 # check rev flags
903 # check rev flags
900 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
904 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
901 raise RevlogError(_('incompatible revision flag %x') %
905 raise RevlogError(_('incompatible revision flag %x') %
902 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
906 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
903
907
904 # build delta chain
908 # build delta chain
905 chain = []
909 chain = []
906 index = self.index # for performance
910 index = self.index # for performance
907 generaldelta = self._generaldelta
911 generaldelta = self._generaldelta
908 iterrev = rev
912 iterrev = rev
909 e = index[iterrev]
913 e = index[iterrev]
910 while iterrev != e[3] and iterrev != cachedrev:
914 while iterrev != e[3] and iterrev != cachedrev:
911 chain.append(iterrev)
915 chain.append(iterrev)
912 if generaldelta:
916 if generaldelta:
913 iterrev = e[3]
917 iterrev = e[3]
914 else:
918 else:
915 iterrev -= 1
919 iterrev -= 1
916 e = index[iterrev]
920 e = index[iterrev]
917 chain.reverse()
921 chain.reverse()
918 base = iterrev
922 base = iterrev
919
923
920 if iterrev == cachedrev:
924 if iterrev == cachedrev:
921 # cache hit
925 # cache hit
922 text = self._cache[2]
926 text = self._cache[2]
923
927
924 # drop cache to save memory
928 # drop cache to save memory
925 self._cache = None
929 self._cache = None
926
930
927 self._chunkraw(base, rev)
931 self._chunkraw(base, rev)
928 if text is None:
932 if text is None:
929 text = str(self._chunkbase(base))
933 text = str(self._chunkbase(base))
930
934
931 bins = [self._chunk(r) for r in chain]
935 bins = [self._chunk(r) for r in chain]
932 text = mdiff.patches(text, bins)
936 text = mdiff.patches(text, bins)
933
937
934 text = self._checkhash(text, node, rev)
938 text = self._checkhash(text, node, rev)
935
939
936 self._cache = (node, rev, text)
940 self._cache = (node, rev, text)
937 return text
941 return text
938
942
939 def _checkhash(self, text, node, rev):
943 def _checkhash(self, text, node, rev):
940 p1, p2 = self.parents(node)
944 p1, p2 = self.parents(node)
941 if node != hash(text, p1, p2):
945 if node != hash(text, p1, p2):
942 raise RevlogError(_("integrity check failed on %s:%d")
946 raise RevlogError(_("integrity check failed on %s:%d")
943 % (self.indexfile, rev))
947 % (self.indexfile, rev))
944 return text
948 return text
945
949
946 def checkinlinesize(self, tr, fp=None):
950 def checkinlinesize(self, tr, fp=None):
947 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
951 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
948 return
952 return
949
953
950 trinfo = tr.find(self.indexfile)
954 trinfo = tr.find(self.indexfile)
951 if trinfo is None:
955 if trinfo is None:
952 raise RevlogError(_("%s not found in the transaction")
956 raise RevlogError(_("%s not found in the transaction")
953 % self.indexfile)
957 % self.indexfile)
954
958
955 trindex = trinfo[2]
959 trindex = trinfo[2]
956 dataoff = self.start(trindex)
960 dataoff = self.start(trindex)
957
961
958 tr.add(self.datafile, dataoff)
962 tr.add(self.datafile, dataoff)
959
963
960 if fp:
964 if fp:
961 fp.flush()
965 fp.flush()
962 fp.close()
966 fp.close()
963
967
964 df = self.opener(self.datafile, 'w')
968 df = self.opener(self.datafile, 'w')
965 try:
969 try:
966 for r in self:
970 for r in self:
967 df.write(self._chunkraw(r, r))
971 df.write(self._chunkraw(r, r))
968 finally:
972 finally:
969 df.close()
973 df.close()
970
974
971 fp = self.opener(self.indexfile, 'w', atomictemp=True)
975 fp = self.opener(self.indexfile, 'w', atomictemp=True)
972 self.version &= ~(REVLOGNGINLINEDATA)
976 self.version &= ~(REVLOGNGINLINEDATA)
973 self._inline = False
977 self._inline = False
974 for i in self:
978 for i in self:
975 e = self._io.packentry(self.index[i], self.node, self.version, i)
979 e = self._io.packentry(self.index[i], self.node, self.version, i)
976 fp.write(e)
980 fp.write(e)
977
981
978 # if we don't call close, the temp file will never replace the
982 # if we don't call close, the temp file will never replace the
979 # real index
983 # real index
980 fp.close()
984 fp.close()
981
985
982 tr.replace(self.indexfile, trindex * self._io.size)
986 tr.replace(self.indexfile, trindex * self._io.size)
983 self._chunkclear()
987 self._chunkclear()
984
988
985 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
989 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
986 """add a revision to the log
990 """add a revision to the log
987
991
988 text - the revision data to add
992 text - the revision data to add
989 transaction - the transaction object used for rollback
993 transaction - the transaction object used for rollback
990 link - the linkrev data to add
994 link - the linkrev data to add
991 p1, p2 - the parent nodeids of the revision
995 p1, p2 - the parent nodeids of the revision
992 cachedelta - an optional precomputed delta
996 cachedelta - an optional precomputed delta
993 """
997 """
994 node = hash(text, p1, p2)
998 node = hash(text, p1, p2)
995 if node in self.nodemap:
999 if node in self.nodemap:
996 return node
1000 return node
997
1001
998 dfh = None
1002 dfh = None
999 if not self._inline:
1003 if not self._inline:
1000 dfh = self.opener(self.datafile, "a")
1004 dfh = self.opener(self.datafile, "a")
1001 ifh = self.opener(self.indexfile, "a+")
1005 ifh = self.opener(self.indexfile, "a+")
1002 try:
1006 try:
1003 return self._addrevision(node, text, transaction, link, p1, p2,
1007 return self._addrevision(node, text, transaction, link, p1, p2,
1004 cachedelta, ifh, dfh)
1008 cachedelta, ifh, dfh)
1005 finally:
1009 finally:
1006 if dfh:
1010 if dfh:
1007 dfh.close()
1011 dfh.close()
1008 ifh.close()
1012 ifh.close()
1009
1013
1010 def compress(self, text):
1014 def compress(self, text):
1011 """ generate a possibly-compressed representation of text """
1015 """ generate a possibly-compressed representation of text """
1012 if not text:
1016 if not text:
1013 return ("", text)
1017 return ("", text)
1014 l = len(text)
1018 l = len(text)
1015 bin = None
1019 bin = None
1016 if l < 44:
1020 if l < 44:
1017 pass
1021 pass
1018 elif l > 1000000:
1022 elif l > 1000000:
1019 # zlib makes an internal copy, thus doubling memory usage for
1023 # zlib makes an internal copy, thus doubling memory usage for
1020 # large files, so lets do this in pieces
1024 # large files, so lets do this in pieces
1021 z = zlib.compressobj()
1025 z = zlib.compressobj()
1022 p = []
1026 p = []
1023 pos = 0
1027 pos = 0
1024 while pos < l:
1028 while pos < l:
1025 pos2 = pos + 2**20
1029 pos2 = pos + 2**20
1026 p.append(z.compress(text[pos:pos2]))
1030 p.append(z.compress(text[pos:pos2]))
1027 pos = pos2
1031 pos = pos2
1028 p.append(z.flush())
1032 p.append(z.flush())
1029 if sum(map(len, p)) < l:
1033 if sum(map(len, p)) < l:
1030 bin = "".join(p)
1034 bin = "".join(p)
1031 else:
1035 else:
1032 bin = _compress(text)
1036 bin = _compress(text)
1033 if bin is None or len(bin) > l:
1037 if bin is None or len(bin) > l:
1034 if text[0] == '\0':
1038 if text[0] == '\0':
1035 return ("", text)
1039 return ("", text)
1036 return ('u', text)
1040 return ('u', text)
1037 return ("", bin)
1041 return ("", bin)
1038
1042
1039 def _addrevision(self, node, text, transaction, link, p1, p2,
1043 def _addrevision(self, node, text, transaction, link, p1, p2,
1040 cachedelta, ifh, dfh):
1044 cachedelta, ifh, dfh):
1041 """internal function to add revisions to the log
1045 """internal function to add revisions to the log
1042
1046
1043 see addrevision for argument descriptions.
1047 see addrevision for argument descriptions.
1044 invariants:
1048 invariants:
1045 - text is optional (can be None); if not set, cachedelta must be set.
1049 - text is optional (can be None); if not set, cachedelta must be set.
1046 if both are set, they must correspond to each other.
1050 if both are set, they must correspond to each other.
1047 """
1051 """
1048 btext = [text]
1052 btext = [text]
1049 def buildtext():
1053 def buildtext():
1050 if btext[0] is not None:
1054 if btext[0] is not None:
1051 return btext[0]
1055 return btext[0]
1052 # flush any pending writes here so we can read it in revision
1056 # flush any pending writes here so we can read it in revision
1053 if dfh:
1057 if dfh:
1054 dfh.flush()
1058 dfh.flush()
1055 ifh.flush()
1059 ifh.flush()
1056 basetext = self.revision(self.node(cachedelta[0]))
1060 basetext = self.revision(self.node(cachedelta[0]))
1057 btext[0] = mdiff.patch(basetext, cachedelta[1])
1061 btext[0] = mdiff.patch(basetext, cachedelta[1])
1058 chk = hash(btext[0], p1, p2)
1062 chk = hash(btext[0], p1, p2)
1059 if chk != node:
1063 if chk != node:
1060 raise RevlogError(_("consistency error in delta"))
1064 raise RevlogError(_("consistency error in delta"))
1061 return btext[0]
1065 return btext[0]
1062
1066
1063 def builddelta(rev):
1067 def builddelta(rev):
1064 # can we use the cached delta?
1068 # can we use the cached delta?
1065 if cachedelta and cachedelta[0] == rev:
1069 if cachedelta and cachedelta[0] == rev:
1066 delta = cachedelta[1]
1070 delta = cachedelta[1]
1067 else:
1071 else:
1068 t = buildtext()
1072 t = buildtext()
1069 ptext = self.revision(self.node(rev))
1073 ptext = self.revision(self.node(rev))
1070 delta = mdiff.textdiff(ptext, t)
1074 delta = mdiff.textdiff(ptext, t)
1071 data = self.compress(delta)
1075 data = self.compress(delta)
1072 l = len(data[1]) + len(data[0])
1076 l = len(data[1]) + len(data[0])
1073 if basecache[0] == rev:
1077 if basecache[0] == rev:
1074 chainbase = basecache[1]
1078 chainbase = basecache[1]
1075 else:
1079 else:
1076 chainbase = self.chainbase(rev)
1080 chainbase = self.chainbase(rev)
1077 dist = l + offset - self.start(chainbase)
1081 dist = l + offset - self.start(chainbase)
1078 if self._generaldelta:
1082 if self._generaldelta:
1079 base = rev
1083 base = rev
1080 else:
1084 else:
1081 base = chainbase
1085 base = chainbase
1082 return dist, l, data, base, chainbase
1086 return dist, l, data, base, chainbase
1083
1087
1084 curr = len(self)
1088 curr = len(self)
1085 prev = curr - 1
1089 prev = curr - 1
1086 base = chainbase = curr
1090 base = chainbase = curr
1087 offset = self.end(prev)
1091 offset = self.end(prev)
1088 flags = 0
1092 flags = 0
1089 d = None
1093 d = None
1090 basecache = self._basecache
1094 basecache = self._basecache
1091 p1r, p2r = self.rev(p1), self.rev(p2)
1095 p1r, p2r = self.rev(p1), self.rev(p2)
1092
1096
1093 # should we try to build a delta?
1097 # should we try to build a delta?
1094 if prev != nullrev:
1098 if prev != nullrev:
1095 if self._generaldelta:
1099 if self._generaldelta:
1096 if p1r >= basecache[1]:
1100 if p1r >= basecache[1]:
1097 d = builddelta(p1r)
1101 d = builddelta(p1r)
1098 elif p2r >= basecache[1]:
1102 elif p2r >= basecache[1]:
1099 d = builddelta(p2r)
1103 d = builddelta(p2r)
1100 else:
1104 else:
1101 d = builddelta(prev)
1105 d = builddelta(prev)
1102 else:
1106 else:
1103 d = builddelta(prev)
1107 d = builddelta(prev)
1104 dist, l, data, base, chainbase = d
1108 dist, l, data, base, chainbase = d
1105
1109
1106 # full versions are inserted when the needed deltas
1110 # full versions are inserted when the needed deltas
1107 # become comparable to the uncompressed text
1111 # become comparable to the uncompressed text
1108 if text is None:
1112 if text is None:
1109 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1113 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1110 cachedelta[1])
1114 cachedelta[1])
1111 else:
1115 else:
1112 textlen = len(text)
1116 textlen = len(text)
1113 if d is None or dist > textlen * 2:
1117 if d is None or dist > textlen * 2:
1114 text = buildtext()
1118 text = buildtext()
1115 data = self.compress(text)
1119 data = self.compress(text)
1116 l = len(data[1]) + len(data[0])
1120 l = len(data[1]) + len(data[0])
1117 base = chainbase = curr
1121 base = chainbase = curr
1118
1122
1119 e = (offset_type(offset, flags), l, textlen,
1123 e = (offset_type(offset, flags), l, textlen,
1120 base, link, p1r, p2r, node)
1124 base, link, p1r, p2r, node)
1121 self.index.insert(-1, e)
1125 self.index.insert(-1, e)
1122 self.nodemap[node] = curr
1126 self.nodemap[node] = curr
1123
1127
1124 entry = self._io.packentry(e, self.node, self.version, curr)
1128 entry = self._io.packentry(e, self.node, self.version, curr)
1125 if not self._inline:
1129 if not self._inline:
1126 transaction.add(self.datafile, offset)
1130 transaction.add(self.datafile, offset)
1127 transaction.add(self.indexfile, curr * len(entry))
1131 transaction.add(self.indexfile, curr * len(entry))
1128 if data[0]:
1132 if data[0]:
1129 dfh.write(data[0])
1133 dfh.write(data[0])
1130 dfh.write(data[1])
1134 dfh.write(data[1])
1131 dfh.flush()
1135 dfh.flush()
1132 ifh.write(entry)
1136 ifh.write(entry)
1133 else:
1137 else:
1134 offset += curr * self._io.size
1138 offset += curr * self._io.size
1135 transaction.add(self.indexfile, offset, curr)
1139 transaction.add(self.indexfile, offset, curr)
1136 ifh.write(entry)
1140 ifh.write(entry)
1137 ifh.write(data[0])
1141 ifh.write(data[0])
1138 ifh.write(data[1])
1142 ifh.write(data[1])
1139 self.checkinlinesize(transaction, ifh)
1143 self.checkinlinesize(transaction, ifh)
1140
1144
1141 if type(text) == str: # only accept immutable objects
1145 if type(text) == str: # only accept immutable objects
1142 self._cache = (node, curr, text)
1146 self._cache = (node, curr, text)
1143 self._basecache = (curr, chainbase)
1147 self._basecache = (curr, chainbase)
1144 return node
1148 return node
1145
1149
1146 def group(self, nodelist, bundler, reorder=None):
1150 def group(self, nodelist, bundler, reorder=None):
1147 """Calculate a delta group, yielding a sequence of changegroup chunks
1151 """Calculate a delta group, yielding a sequence of changegroup chunks
1148 (strings).
1152 (strings).
1149
1153
1150 Given a list of changeset revs, return a set of deltas and
1154 Given a list of changeset revs, return a set of deltas and
1151 metadata corresponding to nodes. The first delta is
1155 metadata corresponding to nodes. The first delta is
1152 first parent(nodelist[0]) -> nodelist[0], the receiver is
1156 first parent(nodelist[0]) -> nodelist[0], the receiver is
1153 guaranteed to have this parent as it has all history before
1157 guaranteed to have this parent as it has all history before
1154 these changesets. In the case firstparent is nullrev the
1158 these changesets. In the case firstparent is nullrev the
1155 changegroup starts with a full revision.
1159 changegroup starts with a full revision.
1156 """
1160 """
1157
1161
1158 # if we don't have any revisions touched by these changesets, bail
1162 # if we don't have any revisions touched by these changesets, bail
1159 if len(nodelist) == 0:
1163 if len(nodelist) == 0:
1160 yield bundler.close()
1164 yield bundler.close()
1161 return
1165 return
1162
1166
1163 # for generaldelta revlogs, we linearize the revs; this will both be
1167 # for generaldelta revlogs, we linearize the revs; this will both be
1164 # much quicker and generate a much smaller bundle
1168 # much quicker and generate a much smaller bundle
1165 if (self._generaldelta and reorder is not False) or reorder:
1169 if (self._generaldelta and reorder is not False) or reorder:
1166 dag = dagutil.revlogdag(self)
1170 dag = dagutil.revlogdag(self)
1167 revs = set(self.rev(n) for n in nodelist)
1171 revs = set(self.rev(n) for n in nodelist)
1168 revs = dag.linearize(revs)
1172 revs = dag.linearize(revs)
1169 else:
1173 else:
1170 revs = sorted([self.rev(n) for n in nodelist])
1174 revs = sorted([self.rev(n) for n in nodelist])
1171
1175
1172 # add the parent of the first rev
1176 # add the parent of the first rev
1173 p = self.parentrevs(revs[0])[0]
1177 p = self.parentrevs(revs[0])[0]
1174 revs.insert(0, p)
1178 revs.insert(0, p)
1175
1179
1176 # build deltas
1180 # build deltas
1177 for r in xrange(len(revs) - 1):
1181 for r in xrange(len(revs) - 1):
1178 prev, curr = revs[r], revs[r + 1]
1182 prev, curr = revs[r], revs[r + 1]
1179 for c in bundler.revchunk(self, curr, prev):
1183 for c in bundler.revchunk(self, curr, prev):
1180 yield c
1184 yield c
1181
1185
1182 yield bundler.close()
1186 yield bundler.close()
1183
1187
1184 def addgroup(self, bundle, linkmapper, transaction):
1188 def addgroup(self, bundle, linkmapper, transaction):
1185 """
1189 """
1186 add a delta group
1190 add a delta group
1187
1191
1188 given a set of deltas, add them to the revision log. the
1192 given a set of deltas, add them to the revision log. the
1189 first delta is against its parent, which should be in our
1193 first delta is against its parent, which should be in our
1190 log, the rest are against the previous delta.
1194 log, the rest are against the previous delta.
1191 """
1195 """
1192
1196
1193 # track the base of the current delta log
1197 # track the base of the current delta log
1194 content = []
1198 content = []
1195 node = None
1199 node = None
1196
1200
1197 r = len(self)
1201 r = len(self)
1198 end = 0
1202 end = 0
1199 if r:
1203 if r:
1200 end = self.end(r - 1)
1204 end = self.end(r - 1)
1201 ifh = self.opener(self.indexfile, "a+")
1205 ifh = self.opener(self.indexfile, "a+")
1202 isize = r * self._io.size
1206 isize = r * self._io.size
1203 if self._inline:
1207 if self._inline:
1204 transaction.add(self.indexfile, end + isize, r)
1208 transaction.add(self.indexfile, end + isize, r)
1205 dfh = None
1209 dfh = None
1206 else:
1210 else:
1207 transaction.add(self.indexfile, isize, r)
1211 transaction.add(self.indexfile, isize, r)
1208 transaction.add(self.datafile, end)
1212 transaction.add(self.datafile, end)
1209 dfh = self.opener(self.datafile, "a")
1213 dfh = self.opener(self.datafile, "a")
1210
1214
1211 try:
1215 try:
1212 # loop through our set of deltas
1216 # loop through our set of deltas
1213 chain = None
1217 chain = None
1214 while True:
1218 while True:
1215 chunkdata = bundle.deltachunk(chain)
1219 chunkdata = bundle.deltachunk(chain)
1216 if not chunkdata:
1220 if not chunkdata:
1217 break
1221 break
1218 node = chunkdata['node']
1222 node = chunkdata['node']
1219 p1 = chunkdata['p1']
1223 p1 = chunkdata['p1']
1220 p2 = chunkdata['p2']
1224 p2 = chunkdata['p2']
1221 cs = chunkdata['cs']
1225 cs = chunkdata['cs']
1222 deltabase = chunkdata['deltabase']
1226 deltabase = chunkdata['deltabase']
1223 delta = chunkdata['delta']
1227 delta = chunkdata['delta']
1224
1228
1225 content.append(node)
1229 content.append(node)
1226
1230
1227 link = linkmapper(cs)
1231 link = linkmapper(cs)
1228 if node in self.nodemap:
1232 if node in self.nodemap:
1229 # this can happen if two branches make the same change
1233 # this can happen if two branches make the same change
1230 chain = node
1234 chain = node
1231 continue
1235 continue
1232
1236
1233 for p in (p1, p2):
1237 for p in (p1, p2):
1234 if p not in self.nodemap:
1238 if p not in self.nodemap:
1235 raise LookupError(p, self.indexfile,
1239 raise LookupError(p, self.indexfile,
1236 _('unknown parent'))
1240 _('unknown parent'))
1237
1241
1238 if deltabase not in self.nodemap:
1242 if deltabase not in self.nodemap:
1239 raise LookupError(deltabase, self.indexfile,
1243 raise LookupError(deltabase, self.indexfile,
1240 _('unknown delta base'))
1244 _('unknown delta base'))
1241
1245
1242 baserev = self.rev(deltabase)
1246 baserev = self.rev(deltabase)
1243 chain = self._addrevision(node, None, transaction, link,
1247 chain = self._addrevision(node, None, transaction, link,
1244 p1, p2, (baserev, delta), ifh, dfh)
1248 p1, p2, (baserev, delta), ifh, dfh)
1245 if not dfh and not self._inline:
1249 if not dfh and not self._inline:
1246 # addrevision switched from inline to conventional
1250 # addrevision switched from inline to conventional
1247 # reopen the index
1251 # reopen the index
1248 ifh.close()
1252 ifh.close()
1249 dfh = self.opener(self.datafile, "a")
1253 dfh = self.opener(self.datafile, "a")
1250 ifh = self.opener(self.indexfile, "a")
1254 ifh = self.opener(self.indexfile, "a")
1251 finally:
1255 finally:
1252 if dfh:
1256 if dfh:
1253 dfh.close()
1257 dfh.close()
1254 ifh.close()
1258 ifh.close()
1255
1259
1256 return content
1260 return content
1257
1261
1258 def strip(self, minlink, transaction):
1262 def strip(self, minlink, transaction):
1259 """truncate the revlog on the first revision with a linkrev >= minlink
1263 """truncate the revlog on the first revision with a linkrev >= minlink
1260
1264
1261 This function is called when we're stripping revision minlink and
1265 This function is called when we're stripping revision minlink and
1262 its descendants from the repository.
1266 its descendants from the repository.
1263
1267
1264 We have to remove all revisions with linkrev >= minlink, because
1268 We have to remove all revisions with linkrev >= minlink, because
1265 the equivalent changelog revisions will be renumbered after the
1269 the equivalent changelog revisions will be renumbered after the
1266 strip.
1270 strip.
1267
1271
1268 So we truncate the revlog on the first of these revisions, and
1272 So we truncate the revlog on the first of these revisions, and
1269 trust that the caller has saved the revisions that shouldn't be
1273 trust that the caller has saved the revisions that shouldn't be
1270 removed and that it'll re-add them after this truncation.
1274 removed and that it'll re-add them after this truncation.
1271 """
1275 """
1272 if len(self) == 0:
1276 if len(self) == 0:
1273 return
1277 return
1274
1278
1275 for rev in self:
1279 for rev in self:
1276 if self.index[rev][4] >= minlink:
1280 if self.index[rev][4] >= minlink:
1277 break
1281 break
1278 else:
1282 else:
1279 return
1283 return
1280
1284
1281 # first truncate the files on disk
1285 # first truncate the files on disk
1282 end = self.start(rev)
1286 end = self.start(rev)
1283 if not self._inline:
1287 if not self._inline:
1284 transaction.add(self.datafile, end)
1288 transaction.add(self.datafile, end)
1285 end = rev * self._io.size
1289 end = rev * self._io.size
1286 else:
1290 else:
1287 end += rev * self._io.size
1291 end += rev * self._io.size
1288
1292
1289 transaction.add(self.indexfile, end)
1293 transaction.add(self.indexfile, end)
1290
1294
1291 # then reset internal state in memory to forget those revisions
1295 # then reset internal state in memory to forget those revisions
1292 self._cache = None
1296 self._cache = None
1293 self._chunkclear()
1297 self._chunkclear()
1294 for x in xrange(rev, len(self)):
1298 for x in xrange(rev, len(self)):
1295 del self.nodemap[self.node(x)]
1299 del self.nodemap[self.node(x)]
1296
1300
1297 del self.index[rev:-1]
1301 del self.index[rev:-1]
1298
1302
1299 def checksize(self):
1303 def checksize(self):
1300 expected = 0
1304 expected = 0
1301 if len(self):
1305 if len(self):
1302 expected = max(0, self.end(len(self) - 1))
1306 expected = max(0, self.end(len(self) - 1))
1303
1307
1304 try:
1308 try:
1305 f = self.opener(self.datafile)
1309 f = self.opener(self.datafile)
1306 f.seek(0, 2)
1310 f.seek(0, 2)
1307 actual = f.tell()
1311 actual = f.tell()
1308 f.close()
1312 f.close()
1309 dd = actual - expected
1313 dd = actual - expected
1310 except IOError, inst:
1314 except IOError, inst:
1311 if inst.errno != errno.ENOENT:
1315 if inst.errno != errno.ENOENT:
1312 raise
1316 raise
1313 dd = 0
1317 dd = 0
1314
1318
1315 try:
1319 try:
1316 f = self.opener(self.indexfile)
1320 f = self.opener(self.indexfile)
1317 f.seek(0, 2)
1321 f.seek(0, 2)
1318 actual = f.tell()
1322 actual = f.tell()
1319 f.close()
1323 f.close()
1320 s = self._io.size
1324 s = self._io.size
1321 i = max(0, actual // s)
1325 i = max(0, actual // s)
1322 di = actual - (i * s)
1326 di = actual - (i * s)
1323 if self._inline:
1327 if self._inline:
1324 databytes = 0
1328 databytes = 0
1325 for r in self:
1329 for r in self:
1326 databytes += max(0, self.length(r))
1330 databytes += max(0, self.length(r))
1327 dd = 0
1331 dd = 0
1328 di = actual - len(self) * s - databytes
1332 di = actual - len(self) * s - databytes
1329 except IOError, inst:
1333 except IOError, inst:
1330 if inst.errno != errno.ENOENT:
1334 if inst.errno != errno.ENOENT:
1331 raise
1335 raise
1332 di = 0
1336 di = 0
1333
1337
1334 return (dd, di)
1338 return (dd, di)
1335
1339
1336 def files(self):
1340 def files(self):
1337 res = [self.indexfile]
1341 res = [self.indexfile]
1338 if not self._inline:
1342 if not self._inline:
1339 res.append(self.datafile)
1343 res.append(self.datafile)
1340 return res
1344 return res
@@ -1,1941 +1,1939 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13 import encoding
13 import encoding
14 import obsolete as obsmod
14 import obsolete as obsmod
15 import repoview
15 import repoview
16
16
17 def _revancestors(repo, revs, followfirst):
17 def _revancestors(repo, revs, followfirst):
18 """Like revlog.ancestors(), but supports followfirst."""
18 """Like revlog.ancestors(), but supports followfirst."""
19 cut = followfirst and 1 or None
19 cut = followfirst and 1 or None
20 cl = repo.changelog
20 cl = repo.changelog
21 visit = util.deque(revs)
21 visit = util.deque(revs)
22 seen = set([node.nullrev])
22 seen = set([node.nullrev])
23 while visit:
23 while visit:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
25 if parent not in seen:
25 if parent not in seen:
26 visit.append(parent)
26 visit.append(parent)
27 seen.add(parent)
27 seen.add(parent)
28 yield parent
28 yield parent
29
29
30 def _revdescendants(repo, revs, followfirst):
30 def _revdescendants(repo, revs, followfirst):
31 """Like revlog.descendants() but supports followfirst."""
31 """Like revlog.descendants() but supports followfirst."""
32 cut = followfirst and 1 or None
32 cut = followfirst and 1 or None
33 cl = repo.changelog
33 cl = repo.changelog
34 first = min(revs)
34 first = min(revs)
35 nullrev = node.nullrev
35 nullrev = node.nullrev
36 if first == nullrev:
36 if first == nullrev:
37 # Are there nodes with a null first parent and a non-null
37 # Are there nodes with a null first parent and a non-null
38 # second one? Maybe. Do we care? Probably not.
38 # second one? Maybe. Do we care? Probably not.
39 for i in cl:
39 for i in cl:
40 yield i
40 yield i
41 return
41 return
42
42
43 seen = set(revs)
43 seen = set(revs)
44 for i in cl.revs(first + 1):
44 for i in cl.revs(first + 1):
45 for x in cl.parentrevs(i)[:cut]:
45 for x in cl.parentrevs(i)[:cut]:
46 if x != nullrev and x in seen:
46 if x != nullrev and x in seen:
47 seen.add(i)
47 seen.add(i)
48 yield i
48 yield i
49 break
49 break
50
50
51 def _revsbetween(repo, roots, heads):
51 def _revsbetween(repo, roots, heads):
52 """Return all paths between roots and heads, inclusive of both endpoint
52 """Return all paths between roots and heads, inclusive of both endpoint
53 sets."""
53 sets."""
54 if not roots:
54 if not roots:
55 return []
55 return []
56 parentrevs = repo.changelog.parentrevs
56 parentrevs = repo.changelog.parentrevs
57 visit = heads[:]
57 visit = heads[:]
58 reachable = set()
58 reachable = set()
59 seen = {}
59 seen = {}
60 minroot = min(roots)
60 minroot = min(roots)
61 roots = set(roots)
61 roots = set(roots)
62 # open-code the post-order traversal due to the tiny size of
62 # open-code the post-order traversal due to the tiny size of
63 # sys.getrecursionlimit()
63 # sys.getrecursionlimit()
64 while visit:
64 while visit:
65 rev = visit.pop()
65 rev = visit.pop()
66 if rev in roots:
66 if rev in roots:
67 reachable.add(rev)
67 reachable.add(rev)
68 parents = parentrevs(rev)
68 parents = parentrevs(rev)
69 seen[rev] = parents
69 seen[rev] = parents
70 for parent in parents:
70 for parent in parents:
71 if parent >= minroot and parent not in seen:
71 if parent >= minroot and parent not in seen:
72 visit.append(parent)
72 visit.append(parent)
73 if not reachable:
73 if not reachable:
74 return []
74 return []
75 for rev in sorted(seen):
75 for rev in sorted(seen):
76 for parent in seen[rev]:
76 for parent in seen[rev]:
77 if parent in reachable:
77 if parent in reachable:
78 reachable.add(rev)
78 reachable.add(rev)
79 return sorted(reachable)
79 return sorted(reachable)
80
80
81 elements = {
81 elements = {
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
83 "~": (18, None, ("ancestor", 18)),
83 "~": (18, None, ("ancestor", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
85 "-": (5, ("negate", 19), ("minus", 5)),
85 "-": (5, ("negate", 19), ("minus", 5)),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
87 ("dagrangepost", 17)),
87 ("dagrangepost", 17)),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
89 ("dagrangepost", 17)),
89 ("dagrangepost", 17)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
91 "not": (10, ("not", 10)),
91 "not": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
93 "and": (5, None, ("and", 5)),
93 "and": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
95 "or": (4, None, ("or", 4)),
95 "or": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
98 ",": (2, None, ("list", 2)),
98 ",": (2, None, ("list", 2)),
99 ")": (0, None, None),
99 ")": (0, None, None),
100 "symbol": (0, ("symbol",), None),
100 "symbol": (0, ("symbol",), None),
101 "string": (0, ("string",), None),
101 "string": (0, ("string",), None),
102 "end": (0, None, None),
102 "end": (0, None, None),
103 }
103 }
104
104
105 keywords = set(['and', 'or', 'not'])
105 keywords = set(['and', 'or', 'not'])
106
106
107 def tokenize(program):
107 def tokenize(program):
108 '''
108 '''
109 Parse a revset statement into a stream of tokens
109 Parse a revset statement into a stream of tokens
110
110
111 Check that @ is a valid unquoted token character (issue3686):
111 Check that @ is a valid unquoted token character (issue3686):
112 >>> list(tokenize("@::"))
112 >>> list(tokenize("@::"))
113 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
113 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
114
114
115 '''
115 '''
116
116
117 pos, l = 0, len(program)
117 pos, l = 0, len(program)
118 while pos < l:
118 while pos < l:
119 c = program[pos]
119 c = program[pos]
120 if c.isspace(): # skip inter-token whitespace
120 if c.isspace(): # skip inter-token whitespace
121 pass
121 pass
122 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
122 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
123 yield ('::', None, pos)
123 yield ('::', None, pos)
124 pos += 1 # skip ahead
124 pos += 1 # skip ahead
125 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
125 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
126 yield ('..', None, pos)
126 yield ('..', None, pos)
127 pos += 1 # skip ahead
127 pos += 1 # skip ahead
128 elif c in "():,-|&+!~^": # handle simple operators
128 elif c in "():,-|&+!~^": # handle simple operators
129 yield (c, None, pos)
129 yield (c, None, pos)
130 elif (c in '"\'' or c == 'r' and
130 elif (c in '"\'' or c == 'r' and
131 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
131 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
132 if c == 'r':
132 if c == 'r':
133 pos += 1
133 pos += 1
134 c = program[pos]
134 c = program[pos]
135 decode = lambda x: x
135 decode = lambda x: x
136 else:
136 else:
137 decode = lambda x: x.decode('string-escape')
137 decode = lambda x: x.decode('string-escape')
138 pos += 1
138 pos += 1
139 s = pos
139 s = pos
140 while pos < l: # find closing quote
140 while pos < l: # find closing quote
141 d = program[pos]
141 d = program[pos]
142 if d == '\\': # skip over escaped characters
142 if d == '\\': # skip over escaped characters
143 pos += 2
143 pos += 2
144 continue
144 continue
145 if d == c:
145 if d == c:
146 yield ('string', decode(program[s:pos]), s)
146 yield ('string', decode(program[s:pos]), s)
147 break
147 break
148 pos += 1
148 pos += 1
149 else:
149 else:
150 raise error.ParseError(_("unterminated string"), s)
150 raise error.ParseError(_("unterminated string"), s)
151 # gather up a symbol/keyword
151 # gather up a symbol/keyword
152 elif c.isalnum() or c in '._@' or ord(c) > 127:
152 elif c.isalnum() or c in '._@' or ord(c) > 127:
153 s = pos
153 s = pos
154 pos += 1
154 pos += 1
155 while pos < l: # find end of symbol
155 while pos < l: # find end of symbol
156 d = program[pos]
156 d = program[pos]
157 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
157 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
158 break
158 break
159 if d == '.' and program[pos - 1] == '.': # special case for ..
159 if d == '.' and program[pos - 1] == '.': # special case for ..
160 pos -= 1
160 pos -= 1
161 break
161 break
162 pos += 1
162 pos += 1
163 sym = program[s:pos]
163 sym = program[s:pos]
164 if sym in keywords: # operator keywords
164 if sym in keywords: # operator keywords
165 yield (sym, None, s)
165 yield (sym, None, s)
166 else:
166 else:
167 yield ('symbol', sym, s)
167 yield ('symbol', sym, s)
168 pos -= 1
168 pos -= 1
169 else:
169 else:
170 raise error.ParseError(_("syntax error"), pos)
170 raise error.ParseError(_("syntax error"), pos)
171 pos += 1
171 pos += 1
172 yield ('end', None, pos)
172 yield ('end', None, pos)
173
173
174 # helpers
174 # helpers
175
175
176 def getstring(x, err):
176 def getstring(x, err):
177 if x and (x[0] == 'string' or x[0] == 'symbol'):
177 if x and (x[0] == 'string' or x[0] == 'symbol'):
178 return x[1]
178 return x[1]
179 raise error.ParseError(err)
179 raise error.ParseError(err)
180
180
181 def getlist(x):
181 def getlist(x):
182 if not x:
182 if not x:
183 return []
183 return []
184 if x[0] == 'list':
184 if x[0] == 'list':
185 return getlist(x[1]) + [x[2]]
185 return getlist(x[1]) + [x[2]]
186 return [x]
186 return [x]
187
187
188 def getargs(x, min, max, err):
188 def getargs(x, min, max, err):
189 l = getlist(x)
189 l = getlist(x)
190 if len(l) < min or (max >= 0 and len(l) > max):
190 if len(l) < min or (max >= 0 and len(l) > max):
191 raise error.ParseError(err)
191 raise error.ParseError(err)
192 return l
192 return l
193
193
194 def getset(repo, subset, x):
194 def getset(repo, subset, x):
195 if not x:
195 if not x:
196 raise error.ParseError(_("missing argument"))
196 raise error.ParseError(_("missing argument"))
197 return methods[x[0]](repo, subset, *x[1:])
197 return methods[x[0]](repo, subset, *x[1:])
198
198
199 def _getrevsource(repo, r):
199 def _getrevsource(repo, r):
200 extra = repo[r].extra()
200 extra = repo[r].extra()
201 for label in ('source', 'transplant_source', 'rebase_source'):
201 for label in ('source', 'transplant_source', 'rebase_source'):
202 if label in extra:
202 if label in extra:
203 try:
203 try:
204 return repo[extra[label]].rev()
204 return repo[extra[label]].rev()
205 except error.RepoLookupError:
205 except error.RepoLookupError:
206 pass
206 pass
207 return None
207 return None
208
208
209 # operator methods
209 # operator methods
210
210
211 def stringset(repo, subset, x):
211 def stringset(repo, subset, x):
212 x = repo[x].rev()
212 x = repo[x].rev()
213 if x == -1 and len(subset) == len(repo):
213 if x == -1 and len(subset) == len(repo):
214 return [-1]
214 return [-1]
215 if len(subset) == len(repo) or x in subset:
215 if len(subset) == len(repo) or x in subset:
216 return [x]
216 return [x]
217 return []
217 return []
218
218
219 def symbolset(repo, subset, x):
219 def symbolset(repo, subset, x):
220 if x in symbols:
220 if x in symbols:
221 raise error.ParseError(_("can't use %s here") % x)
221 raise error.ParseError(_("can't use %s here") % x)
222 return stringset(repo, subset, x)
222 return stringset(repo, subset, x)
223
223
224 def rangeset(repo, subset, x, y):
224 def rangeset(repo, subset, x, y):
225 cl = repo.changelog
225 cl = repo.changelog
226 m = getset(repo, cl, x)
226 m = getset(repo, cl, x)
227 n = getset(repo, cl, y)
227 n = getset(repo, cl, y)
228
228
229 if not m or not n:
229 if not m or not n:
230 return []
230 return []
231 m, n = m[0], n[-1]
231 m, n = m[0], n[-1]
232
232
233 if m < n:
233 if m < n:
234 r = range(m, n + 1)
234 r = range(m, n + 1)
235 else:
235 else:
236 r = range(m, n - 1, -1)
236 r = range(m, n - 1, -1)
237 s = set(subset)
237 s = set(subset)
238 return [x for x in r if x in s]
238 return [x for x in r if x in s]
239
239
240 def dagrange(repo, subset, x, y):
240 def dagrange(repo, subset, x, y):
241 if subset:
241 r = list(repo)
242 r = list(repo)
242 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 s = set(subset)
244 s = set(subset)
244 return [r for r in xs if r in s]
245 return [r for r in xs if r in s]
246 return []
247
245
248 def andset(repo, subset, x, y):
246 def andset(repo, subset, x, y):
249 return getset(repo, getset(repo, subset, x), y)
247 return getset(repo, getset(repo, subset, x), y)
250
248
251 def orset(repo, subset, x, y):
249 def orset(repo, subset, x, y):
252 xl = getset(repo, subset, x)
250 xl = getset(repo, subset, x)
253 s = set(xl)
251 s = set(xl)
254 yl = getset(repo, [r for r in subset if r not in s], y)
252 yl = getset(repo, [r for r in subset if r not in s], y)
255 return xl + yl
253 return xl + yl
256
254
257 def notset(repo, subset, x):
255 def notset(repo, subset, x):
258 s = set(getset(repo, subset, x))
256 s = set(getset(repo, subset, x))
259 return [r for r in subset if r not in s]
257 return [r for r in subset if r not in s]
260
258
261 def listset(repo, subset, a, b):
259 def listset(repo, subset, a, b):
262 raise error.ParseError(_("can't use a list in this context"))
260 raise error.ParseError(_("can't use a list in this context"))
263
261
264 def func(repo, subset, a, b):
262 def func(repo, subset, a, b):
265 if a[0] == 'symbol' and a[1] in symbols:
263 if a[0] == 'symbol' and a[1] in symbols:
266 return symbols[a[1]](repo, subset, b)
264 return symbols[a[1]](repo, subset, b)
267 raise error.ParseError(_("not a function: %s") % a[1])
265 raise error.ParseError(_("not a function: %s") % a[1])
268
266
269 # functions
267 # functions
270
268
271 def adds(repo, subset, x):
269 def adds(repo, subset, x):
272 """``adds(pattern)``
270 """``adds(pattern)``
273 Changesets that add a file matching pattern.
271 Changesets that add a file matching pattern.
274 """
272 """
275 # i18n: "adds" is a keyword
273 # i18n: "adds" is a keyword
276 pat = getstring(x, _("adds requires a pattern"))
274 pat = getstring(x, _("adds requires a pattern"))
277 return checkstatus(repo, subset, pat, 1)
275 return checkstatus(repo, subset, pat, 1)
278
276
279 def ancestor(repo, subset, x):
277 def ancestor(repo, subset, x):
280 """``ancestor(*changeset)``
278 """``ancestor(*changeset)``
281 Greatest common ancestor of the changesets.
279 Greatest common ancestor of the changesets.
282
280
283 Accepts 0 or more changesets.
281 Accepts 0 or more changesets.
284 Will return empty list when passed no args.
282 Will return empty list when passed no args.
285 Greatest common ancestor of a single changeset is that changeset.
283 Greatest common ancestor of a single changeset is that changeset.
286 """
284 """
287 # i18n: "ancestor" is a keyword
285 # i18n: "ancestor" is a keyword
288 l = getlist(x)
286 l = getlist(x)
289 rl = list(repo)
287 rl = list(repo)
290 anc = None
288 anc = None
291
289
292 # (getset(repo, rl, i) for i in l) generates a list of lists
290 # (getset(repo, rl, i) for i in l) generates a list of lists
293 rev = repo.changelog.rev
291 rev = repo.changelog.rev
294 ancestor = repo.changelog.ancestor
292 ancestor = repo.changelog.ancestor
295 node = repo.changelog.node
293 node = repo.changelog.node
296 for revs in (getset(repo, rl, i) for i in l):
294 for revs in (getset(repo, rl, i) for i in l):
297 for r in revs:
295 for r in revs:
298 if anc is None:
296 if anc is None:
299 anc = r
297 anc = r
300 else:
298 else:
301 anc = rev(ancestor(node(anc), node(r)))
299 anc = rev(ancestor(node(anc), node(r)))
302
300
303 if anc is not None and anc in subset:
301 if anc is not None and anc in subset:
304 return [anc]
302 return [anc]
305 return []
303 return []
306
304
307 def _ancestors(repo, subset, x, followfirst=False):
305 def _ancestors(repo, subset, x, followfirst=False):
308 args = getset(repo, list(repo), x)
306 args = getset(repo, list(repo), x)
309 if not args:
307 if not args:
310 return []
308 return []
311 s = set(_revancestors(repo, args, followfirst)) | set(args)
309 s = set(_revancestors(repo, args, followfirst)) | set(args)
312 return [r for r in subset if r in s]
310 return [r for r in subset if r in s]
313
311
314 def ancestors(repo, subset, x):
312 def ancestors(repo, subset, x):
315 """``ancestors(set)``
313 """``ancestors(set)``
316 Changesets that are ancestors of a changeset in set.
314 Changesets that are ancestors of a changeset in set.
317 """
315 """
318 return _ancestors(repo, subset, x)
316 return _ancestors(repo, subset, x)
319
317
320 def _firstancestors(repo, subset, x):
318 def _firstancestors(repo, subset, x):
321 # ``_firstancestors(set)``
319 # ``_firstancestors(set)``
322 # Like ``ancestors(set)`` but follows only the first parents.
320 # Like ``ancestors(set)`` but follows only the first parents.
323 return _ancestors(repo, subset, x, followfirst=True)
321 return _ancestors(repo, subset, x, followfirst=True)
324
322
325 def ancestorspec(repo, subset, x, n):
323 def ancestorspec(repo, subset, x, n):
326 """``set~n``
324 """``set~n``
327 Changesets that are the Nth ancestor (first parents only) of a changeset
325 Changesets that are the Nth ancestor (first parents only) of a changeset
328 in set.
326 in set.
329 """
327 """
330 try:
328 try:
331 n = int(n[1])
329 n = int(n[1])
332 except (TypeError, ValueError):
330 except (TypeError, ValueError):
333 raise error.ParseError(_("~ expects a number"))
331 raise error.ParseError(_("~ expects a number"))
334 ps = set()
332 ps = set()
335 cl = repo.changelog
333 cl = repo.changelog
336 for r in getset(repo, cl, x):
334 for r in getset(repo, cl, x):
337 for i in range(n):
335 for i in range(n):
338 r = cl.parentrevs(r)[0]
336 r = cl.parentrevs(r)[0]
339 ps.add(r)
337 ps.add(r)
340 return [r for r in subset if r in ps]
338 return [r for r in subset if r in ps]
341
339
342 def author(repo, subset, x):
340 def author(repo, subset, x):
343 """``author(string)``
341 """``author(string)``
344 Alias for ``user(string)``.
342 Alias for ``user(string)``.
345 """
343 """
346 # i18n: "author" is a keyword
344 # i18n: "author" is a keyword
347 n = encoding.lower(getstring(x, _("author requires a string")))
345 n = encoding.lower(getstring(x, _("author requires a string")))
348 kind, pattern, matcher = _substringmatcher(n)
346 kind, pattern, matcher = _substringmatcher(n)
349 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
347 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
350
348
351 def bisect(repo, subset, x):
349 def bisect(repo, subset, x):
352 """``bisect(string)``
350 """``bisect(string)``
353 Changesets marked in the specified bisect status:
351 Changesets marked in the specified bisect status:
354
352
355 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
353 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
356 - ``goods``, ``bads`` : csets topologically good/bad
354 - ``goods``, ``bads`` : csets topologically good/bad
357 - ``range`` : csets taking part in the bisection
355 - ``range`` : csets taking part in the bisection
358 - ``pruned`` : csets that are goods, bads or skipped
356 - ``pruned`` : csets that are goods, bads or skipped
359 - ``untested`` : csets whose fate is yet unknown
357 - ``untested`` : csets whose fate is yet unknown
360 - ``ignored`` : csets ignored due to DAG topology
358 - ``ignored`` : csets ignored due to DAG topology
361 - ``current`` : the cset currently being bisected
359 - ``current`` : the cset currently being bisected
362 """
360 """
363 # i18n: "bisect" is a keyword
361 # i18n: "bisect" is a keyword
364 status = getstring(x, _("bisect requires a string")).lower()
362 status = getstring(x, _("bisect requires a string")).lower()
365 state = set(hbisect.get(repo, status))
363 state = set(hbisect.get(repo, status))
366 return [r for r in subset if r in state]
364 return [r for r in subset if r in state]
367
365
368 # Backward-compatibility
366 # Backward-compatibility
369 # - no help entry so that we do not advertise it any more
367 # - no help entry so that we do not advertise it any more
370 def bisected(repo, subset, x):
368 def bisected(repo, subset, x):
371 return bisect(repo, subset, x)
369 return bisect(repo, subset, x)
372
370
373 def bookmark(repo, subset, x):
371 def bookmark(repo, subset, x):
374 """``bookmark([name])``
372 """``bookmark([name])``
375 The named bookmark or all bookmarks.
373 The named bookmark or all bookmarks.
376
374
377 If `name` starts with `re:`, the remainder of the name is treated as
375 If `name` starts with `re:`, the remainder of the name is treated as
378 a regular expression. To match a bookmark that actually starts with `re:`,
376 a regular expression. To match a bookmark that actually starts with `re:`,
379 use the prefix `literal:`.
377 use the prefix `literal:`.
380 """
378 """
381 # i18n: "bookmark" is a keyword
379 # i18n: "bookmark" is a keyword
382 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
380 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
383 if args:
381 if args:
384 bm = getstring(args[0],
382 bm = getstring(args[0],
385 # i18n: "bookmark" is a keyword
383 # i18n: "bookmark" is a keyword
386 _('the argument to bookmark must be a string'))
384 _('the argument to bookmark must be a string'))
387 kind, pattern, matcher = _stringmatcher(bm)
385 kind, pattern, matcher = _stringmatcher(bm)
388 if kind == 'literal':
386 if kind == 'literal':
389 bmrev = repo._bookmarks.get(bm, None)
387 bmrev = repo._bookmarks.get(bm, None)
390 if not bmrev:
388 if not bmrev:
391 raise util.Abort(_("bookmark '%s' does not exist") % bm)
389 raise util.Abort(_("bookmark '%s' does not exist") % bm)
392 bmrev = repo[bmrev].rev()
390 bmrev = repo[bmrev].rev()
393 return [r for r in subset if r == bmrev]
391 return [r for r in subset if r == bmrev]
394 else:
392 else:
395 matchrevs = set()
393 matchrevs = set()
396 for name, bmrev in repo._bookmarks.iteritems():
394 for name, bmrev in repo._bookmarks.iteritems():
397 if matcher(name):
395 if matcher(name):
398 matchrevs.add(bmrev)
396 matchrevs.add(bmrev)
399 if not matchrevs:
397 if not matchrevs:
400 raise util.Abort(_("no bookmarks exist that match '%s'")
398 raise util.Abort(_("no bookmarks exist that match '%s'")
401 % pattern)
399 % pattern)
402 bmrevs = set()
400 bmrevs = set()
403 for bmrev in matchrevs:
401 for bmrev in matchrevs:
404 bmrevs.add(repo[bmrev].rev())
402 bmrevs.add(repo[bmrev].rev())
405 return [r for r in subset if r in bmrevs]
403 return [r for r in subset if r in bmrevs]
406
404
407 bms = set([repo[r].rev()
405 bms = set([repo[r].rev()
408 for r in repo._bookmarks.values()])
406 for r in repo._bookmarks.values()])
409 return [r for r in subset if r in bms]
407 return [r for r in subset if r in bms]
410
408
411 def branch(repo, subset, x):
409 def branch(repo, subset, x):
412 """``branch(string or set)``
410 """``branch(string or set)``
413 All changesets belonging to the given branch or the branches of the given
411 All changesets belonging to the given branch or the branches of the given
414 changesets.
412 changesets.
415
413
416 If `string` starts with `re:`, the remainder of the name is treated as
414 If `string` starts with `re:`, the remainder of the name is treated as
417 a regular expression. To match a branch that actually starts with `re:`,
415 a regular expression. To match a branch that actually starts with `re:`,
418 use the prefix `literal:`.
416 use the prefix `literal:`.
419 """
417 """
420 try:
418 try:
421 b = getstring(x, '')
419 b = getstring(x, '')
422 except error.ParseError:
420 except error.ParseError:
423 # not a string, but another revspec, e.g. tip()
421 # not a string, but another revspec, e.g. tip()
424 pass
422 pass
425 else:
423 else:
426 kind, pattern, matcher = _stringmatcher(b)
424 kind, pattern, matcher = _stringmatcher(b)
427 if kind == 'literal':
425 if kind == 'literal':
428 # note: falls through to the revspec case if no branch with
426 # note: falls through to the revspec case if no branch with
429 # this name exists
427 # this name exists
430 if pattern in repo.branchmap():
428 if pattern in repo.branchmap():
431 return [r for r in subset if matcher(repo[r].branch())]
429 return [r for r in subset if matcher(repo[r].branch())]
432 else:
430 else:
433 return [r for r in subset if matcher(repo[r].branch())]
431 return [r for r in subset if matcher(repo[r].branch())]
434
432
435 s = getset(repo, list(repo), x)
433 s = getset(repo, list(repo), x)
436 b = set()
434 b = set()
437 for r in s:
435 for r in s:
438 b.add(repo[r].branch())
436 b.add(repo[r].branch())
439 s = set(s)
437 s = set(s)
440 return [r for r in subset if r in s or repo[r].branch() in b]
438 return [r for r in subset if r in s or repo[r].branch() in b]
441
439
442 def bumped(repo, subset, x):
440 def bumped(repo, subset, x):
443 """``bumped()``
441 """``bumped()``
444 Mutable changesets marked as successors of public changesets.
442 Mutable changesets marked as successors of public changesets.
445
443
446 Only non-public and non-obsolete changesets can be `bumped`.
444 Only non-public and non-obsolete changesets can be `bumped`.
447 """
445 """
448 # i18n: "bumped" is a keyword
446 # i18n: "bumped" is a keyword
449 getargs(x, 0, 0, _("bumped takes no arguments"))
447 getargs(x, 0, 0, _("bumped takes no arguments"))
450 bumped = obsmod.getrevs(repo, 'bumped')
448 bumped = obsmod.getrevs(repo, 'bumped')
451 return [r for r in subset if r in bumped]
449 return [r for r in subset if r in bumped]
452
450
453 def bundle(repo, subset, x):
451 def bundle(repo, subset, x):
454 """``bundle()``
452 """``bundle()``
455 Changesets in the bundle.
453 Changesets in the bundle.
456
454
457 Bundle must be specified by the -R option."""
455 Bundle must be specified by the -R option."""
458
456
459 try:
457 try:
460 bundlerevs = repo.changelog.bundlerevs
458 bundlerevs = repo.changelog.bundlerevs
461 except AttributeError:
459 except AttributeError:
462 raise util.Abort(_("no bundle provided - specify with -R"))
460 raise util.Abort(_("no bundle provided - specify with -R"))
463 return [r for r in subset if r in bundlerevs]
461 return [r for r in subset if r in bundlerevs]
464
462
465 def checkstatus(repo, subset, pat, field):
463 def checkstatus(repo, subset, pat, field):
466 m = None
464 m = None
467 s = []
465 s = []
468 hasset = matchmod.patkind(pat) == 'set'
466 hasset = matchmod.patkind(pat) == 'set'
469 fname = None
467 fname = None
470 for r in subset:
468 for r in subset:
471 c = repo[r]
469 c = repo[r]
472 if not m or hasset:
470 if not m or hasset:
473 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
471 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
474 if not m.anypats() and len(m.files()) == 1:
472 if not m.anypats() and len(m.files()) == 1:
475 fname = m.files()[0]
473 fname = m.files()[0]
476 if fname is not None:
474 if fname is not None:
477 if fname not in c.files():
475 if fname not in c.files():
478 continue
476 continue
479 else:
477 else:
480 for f in c.files():
478 for f in c.files():
481 if m(f):
479 if m(f):
482 break
480 break
483 else:
481 else:
484 continue
482 continue
485 files = repo.status(c.p1().node(), c.node())[field]
483 files = repo.status(c.p1().node(), c.node())[field]
486 if fname is not None:
484 if fname is not None:
487 if fname in files:
485 if fname in files:
488 s.append(r)
486 s.append(r)
489 else:
487 else:
490 for f in files:
488 for f in files:
491 if m(f):
489 if m(f):
492 s.append(r)
490 s.append(r)
493 break
491 break
494 return s
492 return s
495
493
496 def _children(repo, narrow, parentset):
494 def _children(repo, narrow, parentset):
497 cs = set()
495 cs = set()
498 if not parentset:
496 if not parentset:
499 return cs
497 return cs
500 pr = repo.changelog.parentrevs
498 pr = repo.changelog.parentrevs
501 minrev = min(parentset)
499 minrev = min(parentset)
502 for r in narrow:
500 for r in narrow:
503 if r <= minrev:
501 if r <= minrev:
504 continue
502 continue
505 for p in pr(r):
503 for p in pr(r):
506 if p in parentset:
504 if p in parentset:
507 cs.add(r)
505 cs.add(r)
508 return cs
506 return cs
509
507
510 def children(repo, subset, x):
508 def children(repo, subset, x):
511 """``children(set)``
509 """``children(set)``
512 Child changesets of changesets in set.
510 Child changesets of changesets in set.
513 """
511 """
514 s = set(getset(repo, list(repo), x))
512 s = set(getset(repo, list(repo), x))
515 cs = _children(repo, subset, s)
513 cs = _children(repo, subset, s)
516 return [r for r in subset if r in cs]
514 return [r for r in subset if r in cs]
517
515
518 def closed(repo, subset, x):
516 def closed(repo, subset, x):
519 """``closed()``
517 """``closed()``
520 Changeset is closed.
518 Changeset is closed.
521 """
519 """
522 # i18n: "closed" is a keyword
520 # i18n: "closed" is a keyword
523 getargs(x, 0, 0, _("closed takes no arguments"))
521 getargs(x, 0, 0, _("closed takes no arguments"))
524 return [r for r in subset if repo[r].closesbranch()]
522 return [r for r in subset if repo[r].closesbranch()]
525
523
526 def contains(repo, subset, x):
524 def contains(repo, subset, x):
527 """``contains(pattern)``
525 """``contains(pattern)``
528 Revision contains a file matching pattern. See :hg:`help patterns`
526 Revision contains a file matching pattern. See :hg:`help patterns`
529 for information about file patterns.
527 for information about file patterns.
530 """
528 """
531 # i18n: "contains" is a keyword
529 # i18n: "contains" is a keyword
532 pat = getstring(x, _("contains requires a pattern"))
530 pat = getstring(x, _("contains requires a pattern"))
533 m = None
531 m = None
534 s = []
532 s = []
535 if not matchmod.patkind(pat):
533 if not matchmod.patkind(pat):
536 for r in subset:
534 for r in subset:
537 if pat in repo[r]:
535 if pat in repo[r]:
538 s.append(r)
536 s.append(r)
539 else:
537 else:
540 for r in subset:
538 for r in subset:
541 c = repo[r]
539 c = repo[r]
542 if not m or matchmod.patkind(pat) == 'set':
540 if not m or matchmod.patkind(pat) == 'set':
543 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
541 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
544 for f in c.manifest():
542 for f in c.manifest():
545 if m(f):
543 if m(f):
546 s.append(r)
544 s.append(r)
547 break
545 break
548 return s
546 return s
549
547
550 def converted(repo, subset, x):
548 def converted(repo, subset, x):
551 """``converted([id])``
549 """``converted([id])``
552 Changesets converted from the given identifier in the old repository if
550 Changesets converted from the given identifier in the old repository if
553 present, or all converted changesets if no identifier is specified.
551 present, or all converted changesets if no identifier is specified.
554 """
552 """
555
553
556 # There is exactly no chance of resolving the revision, so do a simple
554 # There is exactly no chance of resolving the revision, so do a simple
557 # string compare and hope for the best
555 # string compare and hope for the best
558
556
559 rev = None
557 rev = None
560 # i18n: "converted" is a keyword
558 # i18n: "converted" is a keyword
561 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
559 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
562 if l:
560 if l:
563 # i18n: "converted" is a keyword
561 # i18n: "converted" is a keyword
564 rev = getstring(l[0], _('converted requires a revision'))
562 rev = getstring(l[0], _('converted requires a revision'))
565
563
566 def _matchvalue(r):
564 def _matchvalue(r):
567 source = repo[r].extra().get('convert_revision', None)
565 source = repo[r].extra().get('convert_revision', None)
568 return source is not None and (rev is None or source.startswith(rev))
566 return source is not None and (rev is None or source.startswith(rev))
569
567
570 return [r for r in subset if _matchvalue(r)]
568 return [r for r in subset if _matchvalue(r)]
571
569
572 def date(repo, subset, x):
570 def date(repo, subset, x):
573 """``date(interval)``
571 """``date(interval)``
574 Changesets within the interval, see :hg:`help dates`.
572 Changesets within the interval, see :hg:`help dates`.
575 """
573 """
576 # i18n: "date" is a keyword
574 # i18n: "date" is a keyword
577 ds = getstring(x, _("date requires a string"))
575 ds = getstring(x, _("date requires a string"))
578 dm = util.matchdate(ds)
576 dm = util.matchdate(ds)
579 return [r for r in subset if dm(repo[r].date()[0])]
577 return [r for r in subset if dm(repo[r].date()[0])]
580
578
581 def desc(repo, subset, x):
579 def desc(repo, subset, x):
582 """``desc(string)``
580 """``desc(string)``
583 Search commit message for string. The match is case-insensitive.
581 Search commit message for string. The match is case-insensitive.
584 """
582 """
585 # i18n: "desc" is a keyword
583 # i18n: "desc" is a keyword
586 ds = encoding.lower(getstring(x, _("desc requires a string")))
584 ds = encoding.lower(getstring(x, _("desc requires a string")))
587 l = []
585 l = []
588 for r in subset:
586 for r in subset:
589 c = repo[r]
587 c = repo[r]
590 if ds in encoding.lower(c.description()):
588 if ds in encoding.lower(c.description()):
591 l.append(r)
589 l.append(r)
592 return l
590 return l
593
591
594 def _descendants(repo, subset, x, followfirst=False):
592 def _descendants(repo, subset, x, followfirst=False):
595 args = getset(repo, list(repo), x)
593 args = getset(repo, list(repo), x)
596 if not args:
594 if not args:
597 return []
595 return []
598 s = set(_revdescendants(repo, args, followfirst)) | set(args)
596 s = set(_revdescendants(repo, args, followfirst)) | set(args)
599 return [r for r in subset if r in s]
597 return [r for r in subset if r in s]
600
598
601 def descendants(repo, subset, x):
599 def descendants(repo, subset, x):
602 """``descendants(set)``
600 """``descendants(set)``
603 Changesets which are descendants of changesets in set.
601 Changesets which are descendants of changesets in set.
604 """
602 """
605 return _descendants(repo, subset, x)
603 return _descendants(repo, subset, x)
606
604
607 def _firstdescendants(repo, subset, x):
605 def _firstdescendants(repo, subset, x):
608 # ``_firstdescendants(set)``
606 # ``_firstdescendants(set)``
609 # Like ``descendants(set)`` but follows only the first parents.
607 # Like ``descendants(set)`` but follows only the first parents.
610 return _descendants(repo, subset, x, followfirst=True)
608 return _descendants(repo, subset, x, followfirst=True)
611
609
612 def destination(repo, subset, x):
610 def destination(repo, subset, x):
613 """``destination([set])``
611 """``destination([set])``
614 Changesets that were created by a graft, transplant or rebase operation,
612 Changesets that were created by a graft, transplant or rebase operation,
615 with the given revisions specified as the source. Omitting the optional set
613 with the given revisions specified as the source. Omitting the optional set
616 is the same as passing all().
614 is the same as passing all().
617 """
615 """
618 if x is not None:
616 if x is not None:
619 args = set(getset(repo, list(repo), x))
617 args = set(getset(repo, list(repo), x))
620 else:
618 else:
621 args = set(getall(repo, list(repo), x))
619 args = set(getall(repo, list(repo), x))
622
620
623 dests = set()
621 dests = set()
624
622
625 # subset contains all of the possible destinations that can be returned, so
623 # subset contains all of the possible destinations that can be returned, so
626 # iterate over them and see if their source(s) were provided in the args.
624 # iterate over them and see if their source(s) were provided in the args.
627 # Even if the immediate src of r is not in the args, src's source (or
625 # Even if the immediate src of r is not in the args, src's source (or
628 # further back) may be. Scanning back further than the immediate src allows
626 # further back) may be. Scanning back further than the immediate src allows
629 # transitive transplants and rebases to yield the same results as transitive
627 # transitive transplants and rebases to yield the same results as transitive
630 # grafts.
628 # grafts.
631 for r in subset:
629 for r in subset:
632 src = _getrevsource(repo, r)
630 src = _getrevsource(repo, r)
633 lineage = None
631 lineage = None
634
632
635 while src is not None:
633 while src is not None:
636 if lineage is None:
634 if lineage is None:
637 lineage = list()
635 lineage = list()
638
636
639 lineage.append(r)
637 lineage.append(r)
640
638
641 # The visited lineage is a match if the current source is in the arg
639 # The visited lineage is a match if the current source is in the arg
642 # set. Since every candidate dest is visited by way of iterating
640 # set. Since every candidate dest is visited by way of iterating
643 # subset, any dests further back in the lineage will be tested by a
641 # subset, any dests further back in the lineage will be tested by a
644 # different iteration over subset. Likewise, if the src was already
642 # different iteration over subset. Likewise, if the src was already
645 # selected, the current lineage can be selected without going back
643 # selected, the current lineage can be selected without going back
646 # further.
644 # further.
647 if src in args or src in dests:
645 if src in args or src in dests:
648 dests.update(lineage)
646 dests.update(lineage)
649 break
647 break
650
648
651 r = src
649 r = src
652 src = _getrevsource(repo, r)
650 src = _getrevsource(repo, r)
653
651
654 return [r for r in subset if r in dests]
652 return [r for r in subset if r in dests]
655
653
656 def divergent(repo, subset, x):
654 def divergent(repo, subset, x):
657 """``divergent()``
655 """``divergent()``
658 Final successors of changesets with an alternative set of final successors.
656 Final successors of changesets with an alternative set of final successors.
659 """
657 """
660 # i18n: "divergent" is a keyword
658 # i18n: "divergent" is a keyword
661 getargs(x, 0, 0, _("divergent takes no arguments"))
659 getargs(x, 0, 0, _("divergent takes no arguments"))
662 divergent = obsmod.getrevs(repo, 'divergent')
660 divergent = obsmod.getrevs(repo, 'divergent')
663 return [r for r in subset if r in divergent]
661 return [r for r in subset if r in divergent]
664
662
665 def draft(repo, subset, x):
663 def draft(repo, subset, x):
666 """``draft()``
664 """``draft()``
667 Changeset in draft phase."""
665 Changeset in draft phase."""
668 # i18n: "draft" is a keyword
666 # i18n: "draft" is a keyword
669 getargs(x, 0, 0, _("draft takes no arguments"))
667 getargs(x, 0, 0, _("draft takes no arguments"))
670 pc = repo._phasecache
668 pc = repo._phasecache
671 return [r for r in subset if pc.phase(repo, r) == phases.draft]
669 return [r for r in subset if pc.phase(repo, r) == phases.draft]
672
670
673 def extinct(repo, subset, x):
671 def extinct(repo, subset, x):
674 """``extinct()``
672 """``extinct()``
675 Obsolete changesets with obsolete descendants only.
673 Obsolete changesets with obsolete descendants only.
676 """
674 """
677 # i18n: "extinct" is a keyword
675 # i18n: "extinct" is a keyword
678 getargs(x, 0, 0, _("extinct takes no arguments"))
676 getargs(x, 0, 0, _("extinct takes no arguments"))
679 extincts = obsmod.getrevs(repo, 'extinct')
677 extincts = obsmod.getrevs(repo, 'extinct')
680 return [r for r in subset if r in extincts]
678 return [r for r in subset if r in extincts]
681
679
682 def extra(repo, subset, x):
680 def extra(repo, subset, x):
683 """``extra(label, [value])``
681 """``extra(label, [value])``
684 Changesets with the given label in the extra metadata, with the given
682 Changesets with the given label in the extra metadata, with the given
685 optional value.
683 optional value.
686
684
687 If `value` starts with `re:`, the remainder of the value is treated as
685 If `value` starts with `re:`, the remainder of the value is treated as
688 a regular expression. To match a value that actually starts with `re:`,
686 a regular expression. To match a value that actually starts with `re:`,
689 use the prefix `literal:`.
687 use the prefix `literal:`.
690 """
688 """
691
689
692 # i18n: "extra" is a keyword
690 # i18n: "extra" is a keyword
693 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
691 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
694 # i18n: "extra" is a keyword
692 # i18n: "extra" is a keyword
695 label = getstring(l[0], _('first argument to extra must be a string'))
693 label = getstring(l[0], _('first argument to extra must be a string'))
696 value = None
694 value = None
697
695
698 if len(l) > 1:
696 if len(l) > 1:
699 # i18n: "extra" is a keyword
697 # i18n: "extra" is a keyword
700 value = getstring(l[1], _('second argument to extra must be a string'))
698 value = getstring(l[1], _('second argument to extra must be a string'))
701 kind, value, matcher = _stringmatcher(value)
699 kind, value, matcher = _stringmatcher(value)
702
700
703 def _matchvalue(r):
701 def _matchvalue(r):
704 extra = repo[r].extra()
702 extra = repo[r].extra()
705 return label in extra and (value is None or matcher(extra[label]))
703 return label in extra and (value is None or matcher(extra[label]))
706
704
707 return [r for r in subset if _matchvalue(r)]
705 return [r for r in subset if _matchvalue(r)]
708
706
709 def filelog(repo, subset, x):
707 def filelog(repo, subset, x):
710 """``filelog(pattern)``
708 """``filelog(pattern)``
711 Changesets connected to the specified filelog.
709 Changesets connected to the specified filelog.
712
710
713 For performance reasons, ``filelog()`` does not show every changeset
711 For performance reasons, ``filelog()`` does not show every changeset
714 that affects the requested file(s). See :hg:`help log` for details. For
712 that affects the requested file(s). See :hg:`help log` for details. For
715 a slower, more accurate result, use ``file()``.
713 a slower, more accurate result, use ``file()``.
716 """
714 """
717
715
718 # i18n: "filelog" is a keyword
716 # i18n: "filelog" is a keyword
719 pat = getstring(x, _("filelog requires a pattern"))
717 pat = getstring(x, _("filelog requires a pattern"))
720 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
718 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
721 ctx=repo[None])
719 ctx=repo[None])
722 s = set()
720 s = set()
723
721
724 if not matchmod.patkind(pat):
722 if not matchmod.patkind(pat):
725 for f in m.files():
723 for f in m.files():
726 fl = repo.file(f)
724 fl = repo.file(f)
727 for fr in fl:
725 for fr in fl:
728 s.add(fl.linkrev(fr))
726 s.add(fl.linkrev(fr))
729 else:
727 else:
730 for f in repo[None]:
728 for f in repo[None]:
731 if m(f):
729 if m(f):
732 fl = repo.file(f)
730 fl = repo.file(f)
733 for fr in fl:
731 for fr in fl:
734 s.add(fl.linkrev(fr))
732 s.add(fl.linkrev(fr))
735
733
736 return [r for r in subset if r in s]
734 return [r for r in subset if r in s]
737
735
738 def first(repo, subset, x):
736 def first(repo, subset, x):
739 """``first(set, [n])``
737 """``first(set, [n])``
740 An alias for limit().
738 An alias for limit().
741 """
739 """
742 return limit(repo, subset, x)
740 return limit(repo, subset, x)
743
741
744 def _follow(repo, subset, x, name, followfirst=False):
742 def _follow(repo, subset, x, name, followfirst=False):
745 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
743 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
746 c = repo['.']
744 c = repo['.']
747 if l:
745 if l:
748 x = getstring(l[0], _("%s expected a filename") % name)
746 x = getstring(l[0], _("%s expected a filename") % name)
749 if x in c:
747 if x in c:
750 cx = c[x]
748 cx = c[x]
751 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
749 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
752 # include the revision responsible for the most recent version
750 # include the revision responsible for the most recent version
753 s.add(cx.linkrev())
751 s.add(cx.linkrev())
754 else:
752 else:
755 return []
753 return []
756 else:
754 else:
757 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
755 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
758
756
759 return [r for r in subset if r in s]
757 return [r for r in subset if r in s]
760
758
761 def follow(repo, subset, x):
759 def follow(repo, subset, x):
762 """``follow([file])``
760 """``follow([file])``
763 An alias for ``::.`` (ancestors of the working copy's first parent).
761 An alias for ``::.`` (ancestors of the working copy's first parent).
764 If a filename is specified, the history of the given file is followed,
762 If a filename is specified, the history of the given file is followed,
765 including copies.
763 including copies.
766 """
764 """
767 return _follow(repo, subset, x, 'follow')
765 return _follow(repo, subset, x, 'follow')
768
766
769 def _followfirst(repo, subset, x):
767 def _followfirst(repo, subset, x):
770 # ``followfirst([file])``
768 # ``followfirst([file])``
771 # Like ``follow([file])`` but follows only the first parent of
769 # Like ``follow([file])`` but follows only the first parent of
772 # every revision or file revision.
770 # every revision or file revision.
773 return _follow(repo, subset, x, '_followfirst', followfirst=True)
771 return _follow(repo, subset, x, '_followfirst', followfirst=True)
774
772
775 def getall(repo, subset, x):
773 def getall(repo, subset, x):
776 """``all()``
774 """``all()``
777 All changesets, the same as ``0:tip``.
775 All changesets, the same as ``0:tip``.
778 """
776 """
779 # i18n: "all" is a keyword
777 # i18n: "all" is a keyword
780 getargs(x, 0, 0, _("all takes no arguments"))
778 getargs(x, 0, 0, _("all takes no arguments"))
781 return subset
779 return subset
782
780
783 def grep(repo, subset, x):
781 def grep(repo, subset, x):
784 """``grep(regex)``
782 """``grep(regex)``
785 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
783 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
786 to ensure special escape characters are handled correctly. Unlike
784 to ensure special escape characters are handled correctly. Unlike
787 ``keyword(string)``, the match is case-sensitive.
785 ``keyword(string)``, the match is case-sensitive.
788 """
786 """
789 try:
787 try:
790 # i18n: "grep" is a keyword
788 # i18n: "grep" is a keyword
791 gr = re.compile(getstring(x, _("grep requires a string")))
789 gr = re.compile(getstring(x, _("grep requires a string")))
792 except re.error, e:
790 except re.error, e:
793 raise error.ParseError(_('invalid match pattern: %s') % e)
791 raise error.ParseError(_('invalid match pattern: %s') % e)
794 l = []
792 l = []
795 for r in subset:
793 for r in subset:
796 c = repo[r]
794 c = repo[r]
797 for e in c.files() + [c.user(), c.description()]:
795 for e in c.files() + [c.user(), c.description()]:
798 if gr.search(e):
796 if gr.search(e):
799 l.append(r)
797 l.append(r)
800 break
798 break
801 return l
799 return l
802
800
803 def _matchfiles(repo, subset, x):
801 def _matchfiles(repo, subset, x):
804 # _matchfiles takes a revset list of prefixed arguments:
802 # _matchfiles takes a revset list of prefixed arguments:
805 #
803 #
806 # [p:foo, i:bar, x:baz]
804 # [p:foo, i:bar, x:baz]
807 #
805 #
808 # builds a match object from them and filters subset. Allowed
806 # builds a match object from them and filters subset. Allowed
809 # prefixes are 'p:' for regular patterns, 'i:' for include
807 # prefixes are 'p:' for regular patterns, 'i:' for include
810 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
808 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
811 # a revision identifier, or the empty string to reference the
809 # a revision identifier, or the empty string to reference the
812 # working directory, from which the match object is
810 # working directory, from which the match object is
813 # initialized. Use 'd:' to set the default matching mode, default
811 # initialized. Use 'd:' to set the default matching mode, default
814 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
812 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
815
813
816 # i18n: "_matchfiles" is a keyword
814 # i18n: "_matchfiles" is a keyword
817 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
815 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
818 pats, inc, exc = [], [], []
816 pats, inc, exc = [], [], []
819 hasset = False
817 hasset = False
820 rev, default = None, None
818 rev, default = None, None
821 for arg in l:
819 for arg in l:
822 # i18n: "_matchfiles" is a keyword
820 # i18n: "_matchfiles" is a keyword
823 s = getstring(arg, _("_matchfiles requires string arguments"))
821 s = getstring(arg, _("_matchfiles requires string arguments"))
824 prefix, value = s[:2], s[2:]
822 prefix, value = s[:2], s[2:]
825 if prefix == 'p:':
823 if prefix == 'p:':
826 pats.append(value)
824 pats.append(value)
827 elif prefix == 'i:':
825 elif prefix == 'i:':
828 inc.append(value)
826 inc.append(value)
829 elif prefix == 'x:':
827 elif prefix == 'x:':
830 exc.append(value)
828 exc.append(value)
831 elif prefix == 'r:':
829 elif prefix == 'r:':
832 if rev is not None:
830 if rev is not None:
833 # i18n: "_matchfiles" is a keyword
831 # i18n: "_matchfiles" is a keyword
834 raise error.ParseError(_('_matchfiles expected at most one '
832 raise error.ParseError(_('_matchfiles expected at most one '
835 'revision'))
833 'revision'))
836 rev = value
834 rev = value
837 elif prefix == 'd:':
835 elif prefix == 'd:':
838 if default is not None:
836 if default is not None:
839 # i18n: "_matchfiles" is a keyword
837 # i18n: "_matchfiles" is a keyword
840 raise error.ParseError(_('_matchfiles expected at most one '
838 raise error.ParseError(_('_matchfiles expected at most one '
841 'default mode'))
839 'default mode'))
842 default = value
840 default = value
843 else:
841 else:
844 # i18n: "_matchfiles" is a keyword
842 # i18n: "_matchfiles" is a keyword
845 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
843 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
846 if not hasset and matchmod.patkind(value) == 'set':
844 if not hasset and matchmod.patkind(value) == 'set':
847 hasset = True
845 hasset = True
848 if not default:
846 if not default:
849 default = 'glob'
847 default = 'glob'
850 m = None
848 m = None
851 s = []
849 s = []
852 for r in subset:
850 for r in subset:
853 c = repo[r]
851 c = repo[r]
854 if not m or (hasset and rev is None):
852 if not m or (hasset and rev is None):
855 ctx = c
853 ctx = c
856 if rev is not None:
854 if rev is not None:
857 ctx = repo[rev or None]
855 ctx = repo[rev or None]
858 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
856 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
859 exclude=exc, ctx=ctx, default=default)
857 exclude=exc, ctx=ctx, default=default)
860 for f in c.files():
858 for f in c.files():
861 if m(f):
859 if m(f):
862 s.append(r)
860 s.append(r)
863 break
861 break
864 return s
862 return s
865
863
866 def hasfile(repo, subset, x):
864 def hasfile(repo, subset, x):
867 """``file(pattern)``
865 """``file(pattern)``
868 Changesets affecting files matched by pattern.
866 Changesets affecting files matched by pattern.
869
867
870 For a faster but less accurate result, consider using ``filelog()``
868 For a faster but less accurate result, consider using ``filelog()``
871 instead.
869 instead.
872 """
870 """
873 # i18n: "file" is a keyword
871 # i18n: "file" is a keyword
874 pat = getstring(x, _("file requires a pattern"))
872 pat = getstring(x, _("file requires a pattern"))
875 return _matchfiles(repo, subset, ('string', 'p:' + pat))
873 return _matchfiles(repo, subset, ('string', 'p:' + pat))
876
874
877 def head(repo, subset, x):
875 def head(repo, subset, x):
878 """``head()``
876 """``head()``
879 Changeset is a named branch head.
877 Changeset is a named branch head.
880 """
878 """
881 # i18n: "head" is a keyword
879 # i18n: "head" is a keyword
882 getargs(x, 0, 0, _("head takes no arguments"))
880 getargs(x, 0, 0, _("head takes no arguments"))
883 hs = set()
881 hs = set()
884 for b, ls in repo.branchmap().iteritems():
882 for b, ls in repo.branchmap().iteritems():
885 hs.update(repo[h].rev() for h in ls)
883 hs.update(repo[h].rev() for h in ls)
886 return [r for r in subset if r in hs]
884 return [r for r in subset if r in hs]
887
885
888 def heads(repo, subset, x):
886 def heads(repo, subset, x):
889 """``heads(set)``
887 """``heads(set)``
890 Members of set with no children in set.
888 Members of set with no children in set.
891 """
889 """
892 s = getset(repo, subset, x)
890 s = getset(repo, subset, x)
893 ps = set(parents(repo, subset, x))
891 ps = set(parents(repo, subset, x))
894 return [r for r in s if r not in ps]
892 return [r for r in s if r not in ps]
895
893
896 def hidden(repo, subset, x):
894 def hidden(repo, subset, x):
897 """``hidden()``
895 """``hidden()``
898 Hidden changesets.
896 Hidden changesets.
899 """
897 """
900 # i18n: "hidden" is a keyword
898 # i18n: "hidden" is a keyword
901 getargs(x, 0, 0, _("hidden takes no arguments"))
899 getargs(x, 0, 0, _("hidden takes no arguments"))
902 hiddenrevs = repoview.filterrevs(repo, 'visible')
900 hiddenrevs = repoview.filterrevs(repo, 'visible')
903 return [r for r in subset if r in hiddenrevs]
901 return [r for r in subset if r in hiddenrevs]
904
902
905 def keyword(repo, subset, x):
903 def keyword(repo, subset, x):
906 """``keyword(string)``
904 """``keyword(string)``
907 Search commit message, user name, and names of changed files for
905 Search commit message, user name, and names of changed files for
908 string. The match is case-insensitive.
906 string. The match is case-insensitive.
909 """
907 """
910 # i18n: "keyword" is a keyword
908 # i18n: "keyword" is a keyword
911 kw = encoding.lower(getstring(x, _("keyword requires a string")))
909 kw = encoding.lower(getstring(x, _("keyword requires a string")))
912 l = []
910 l = []
913 for r in subset:
911 for r in subset:
914 c = repo[r]
912 c = repo[r]
915 t = " ".join(c.files() + [c.user(), c.description()])
913 t = " ".join(c.files() + [c.user(), c.description()])
916 if kw in encoding.lower(t):
914 if kw in encoding.lower(t):
917 l.append(r)
915 l.append(r)
918 return l
916 return l
919
917
920 def limit(repo, subset, x):
918 def limit(repo, subset, x):
921 """``limit(set, [n])``
919 """``limit(set, [n])``
922 First n members of set, defaulting to 1.
920 First n members of set, defaulting to 1.
923 """
921 """
924 # i18n: "limit" is a keyword
922 # i18n: "limit" is a keyword
925 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
923 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
926 try:
924 try:
927 lim = 1
925 lim = 1
928 if len(l) == 2:
926 if len(l) == 2:
929 # i18n: "limit" is a keyword
927 # i18n: "limit" is a keyword
930 lim = int(getstring(l[1], _("limit requires a number")))
928 lim = int(getstring(l[1], _("limit requires a number")))
931 except (TypeError, ValueError):
929 except (TypeError, ValueError):
932 # i18n: "limit" is a keyword
930 # i18n: "limit" is a keyword
933 raise error.ParseError(_("limit expects a number"))
931 raise error.ParseError(_("limit expects a number"))
934 ss = set(subset)
932 ss = set(subset)
935 os = getset(repo, list(repo), l[0])[:lim]
933 os = getset(repo, list(repo), l[0])[:lim]
936 return [r for r in os if r in ss]
934 return [r for r in os if r in ss]
937
935
938 def last(repo, subset, x):
936 def last(repo, subset, x):
939 """``last(set, [n])``
937 """``last(set, [n])``
940 Last n members of set, defaulting to 1.
938 Last n members of set, defaulting to 1.
941 """
939 """
942 # i18n: "last" is a keyword
940 # i18n: "last" is a keyword
943 l = getargs(x, 1, 2, _("last requires one or two arguments"))
941 l = getargs(x, 1, 2, _("last requires one or two arguments"))
944 try:
942 try:
945 lim = 1
943 lim = 1
946 if len(l) == 2:
944 if len(l) == 2:
947 # i18n: "last" is a keyword
945 # i18n: "last" is a keyword
948 lim = int(getstring(l[1], _("last requires a number")))
946 lim = int(getstring(l[1], _("last requires a number")))
949 except (TypeError, ValueError):
947 except (TypeError, ValueError):
950 # i18n: "last" is a keyword
948 # i18n: "last" is a keyword
951 raise error.ParseError(_("last expects a number"))
949 raise error.ParseError(_("last expects a number"))
952 ss = set(subset)
950 ss = set(subset)
953 os = getset(repo, list(repo), l[0])[-lim:]
951 os = getset(repo, list(repo), l[0])[-lim:]
954 return [r for r in os if r in ss]
952 return [r for r in os if r in ss]
955
953
956 def maxrev(repo, subset, x):
954 def maxrev(repo, subset, x):
957 """``max(set)``
955 """``max(set)``
958 Changeset with highest revision number in set.
956 Changeset with highest revision number in set.
959 """
957 """
960 os = getset(repo, list(repo), x)
958 os = getset(repo, list(repo), x)
961 if os:
959 if os:
962 m = max(os)
960 m = max(os)
963 if m in subset:
961 if m in subset:
964 return [m]
962 return [m]
965 return []
963 return []
966
964
967 def merge(repo, subset, x):
965 def merge(repo, subset, x):
968 """``merge()``
966 """``merge()``
969 Changeset is a merge changeset.
967 Changeset is a merge changeset.
970 """
968 """
971 # i18n: "merge" is a keyword
969 # i18n: "merge" is a keyword
972 getargs(x, 0, 0, _("merge takes no arguments"))
970 getargs(x, 0, 0, _("merge takes no arguments"))
973 cl = repo.changelog
971 cl = repo.changelog
974 return [r for r in subset if cl.parentrevs(r)[1] != -1]
972 return [r for r in subset if cl.parentrevs(r)[1] != -1]
975
973
976 def branchpoint(repo, subset, x):
974 def branchpoint(repo, subset, x):
977 """``branchpoint()``
975 """``branchpoint()``
978 Changesets with more than one child.
976 Changesets with more than one child.
979 """
977 """
980 # i18n: "branchpoint" is a keyword
978 # i18n: "branchpoint" is a keyword
981 getargs(x, 0, 0, _("branchpoint takes no arguments"))
979 getargs(x, 0, 0, _("branchpoint takes no arguments"))
982 cl = repo.changelog
980 cl = repo.changelog
983 if not subset:
981 if not subset:
984 return []
982 return []
985 baserev = min(subset)
983 baserev = min(subset)
986 parentscount = [0]*(len(repo) - baserev)
984 parentscount = [0]*(len(repo) - baserev)
987 for r in cl.revs(start=baserev + 1):
985 for r in cl.revs(start=baserev + 1):
988 for p in cl.parentrevs(r):
986 for p in cl.parentrevs(r):
989 if p >= baserev:
987 if p >= baserev:
990 parentscount[p - baserev] += 1
988 parentscount[p - baserev] += 1
991 return [r for r in subset if (parentscount[r - baserev] > 1)]
989 return [r for r in subset if (parentscount[r - baserev] > 1)]
992
990
993 def minrev(repo, subset, x):
991 def minrev(repo, subset, x):
994 """``min(set)``
992 """``min(set)``
995 Changeset with lowest revision number in set.
993 Changeset with lowest revision number in set.
996 """
994 """
997 os = getset(repo, list(repo), x)
995 os = getset(repo, list(repo), x)
998 if os:
996 if os:
999 m = min(os)
997 m = min(os)
1000 if m in subset:
998 if m in subset:
1001 return [m]
999 return [m]
1002 return []
1000 return []
1003
1001
1004 def modifies(repo, subset, x):
1002 def modifies(repo, subset, x):
1005 """``modifies(pattern)``
1003 """``modifies(pattern)``
1006 Changesets modifying files matched by pattern.
1004 Changesets modifying files matched by pattern.
1007 """
1005 """
1008 # i18n: "modifies" is a keyword
1006 # i18n: "modifies" is a keyword
1009 pat = getstring(x, _("modifies requires a pattern"))
1007 pat = getstring(x, _("modifies requires a pattern"))
1010 return checkstatus(repo, subset, pat, 0)
1008 return checkstatus(repo, subset, pat, 0)
1011
1009
1012 def node_(repo, subset, x):
1010 def node_(repo, subset, x):
1013 """``id(string)``
1011 """``id(string)``
1014 Revision non-ambiguously specified by the given hex string prefix.
1012 Revision non-ambiguously specified by the given hex string prefix.
1015 """
1013 """
1016 # i18n: "id" is a keyword
1014 # i18n: "id" is a keyword
1017 l = getargs(x, 1, 1, _("id requires one argument"))
1015 l = getargs(x, 1, 1, _("id requires one argument"))
1018 # i18n: "id" is a keyword
1016 # i18n: "id" is a keyword
1019 n = getstring(l[0], _("id requires a string"))
1017 n = getstring(l[0], _("id requires a string"))
1020 if len(n) == 40:
1018 if len(n) == 40:
1021 rn = repo[n].rev()
1019 rn = repo[n].rev()
1022 else:
1020 else:
1023 rn = None
1021 rn = None
1024 pm = repo.changelog._partialmatch(n)
1022 pm = repo.changelog._partialmatch(n)
1025 if pm is not None:
1023 if pm is not None:
1026 rn = repo.changelog.rev(pm)
1024 rn = repo.changelog.rev(pm)
1027
1025
1028 return [r for r in subset if r == rn]
1026 return [r for r in subset if r == rn]
1029
1027
1030 def obsolete(repo, subset, x):
1028 def obsolete(repo, subset, x):
1031 """``obsolete()``
1029 """``obsolete()``
1032 Mutable changeset with a newer version."""
1030 Mutable changeset with a newer version."""
1033 # i18n: "obsolete" is a keyword
1031 # i18n: "obsolete" is a keyword
1034 getargs(x, 0, 0, _("obsolete takes no arguments"))
1032 getargs(x, 0, 0, _("obsolete takes no arguments"))
1035 obsoletes = obsmod.getrevs(repo, 'obsolete')
1033 obsoletes = obsmod.getrevs(repo, 'obsolete')
1036 return [r for r in subset if r in obsoletes]
1034 return [r for r in subset if r in obsoletes]
1037
1035
1038 def origin(repo, subset, x):
1036 def origin(repo, subset, x):
1039 """``origin([set])``
1037 """``origin([set])``
1040 Changesets that were specified as a source for the grafts, transplants or
1038 Changesets that were specified as a source for the grafts, transplants or
1041 rebases that created the given revisions. Omitting the optional set is the
1039 rebases that created the given revisions. Omitting the optional set is the
1042 same as passing all(). If a changeset created by these operations is itself
1040 same as passing all(). If a changeset created by these operations is itself
1043 specified as a source for one of these operations, only the source changeset
1041 specified as a source for one of these operations, only the source changeset
1044 for the first operation is selected.
1042 for the first operation is selected.
1045 """
1043 """
1046 if x is not None:
1044 if x is not None:
1047 args = set(getset(repo, list(repo), x))
1045 args = set(getset(repo, list(repo), x))
1048 else:
1046 else:
1049 args = set(getall(repo, list(repo), x))
1047 args = set(getall(repo, list(repo), x))
1050
1048
1051 def _firstsrc(rev):
1049 def _firstsrc(rev):
1052 src = _getrevsource(repo, rev)
1050 src = _getrevsource(repo, rev)
1053 if src is None:
1051 if src is None:
1054 return None
1052 return None
1055
1053
1056 while True:
1054 while True:
1057 prev = _getrevsource(repo, src)
1055 prev = _getrevsource(repo, src)
1058
1056
1059 if prev is None:
1057 if prev is None:
1060 return src
1058 return src
1061 src = prev
1059 src = prev
1062
1060
1063 o = set([_firstsrc(r) for r in args])
1061 o = set([_firstsrc(r) for r in args])
1064 return [r for r in subset if r in o]
1062 return [r for r in subset if r in o]
1065
1063
1066 def outgoing(repo, subset, x):
1064 def outgoing(repo, subset, x):
1067 """``outgoing([path])``
1065 """``outgoing([path])``
1068 Changesets not found in the specified destination repository, or the
1066 Changesets not found in the specified destination repository, or the
1069 default push location.
1067 default push location.
1070 """
1068 """
1071 import hg # avoid start-up nasties
1069 import hg # avoid start-up nasties
1072 # i18n: "outgoing" is a keyword
1070 # i18n: "outgoing" is a keyword
1073 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1071 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1074 # i18n: "outgoing" is a keyword
1072 # i18n: "outgoing" is a keyword
1075 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1073 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1076 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1074 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1077 dest, branches = hg.parseurl(dest)
1075 dest, branches = hg.parseurl(dest)
1078 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1076 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1079 if revs:
1077 if revs:
1080 revs = [repo.lookup(rev) for rev in revs]
1078 revs = [repo.lookup(rev) for rev in revs]
1081 other = hg.peer(repo, {}, dest)
1079 other = hg.peer(repo, {}, dest)
1082 repo.ui.pushbuffer()
1080 repo.ui.pushbuffer()
1083 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1081 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1084 repo.ui.popbuffer()
1082 repo.ui.popbuffer()
1085 cl = repo.changelog
1083 cl = repo.changelog
1086 o = set([cl.rev(r) for r in outgoing.missing])
1084 o = set([cl.rev(r) for r in outgoing.missing])
1087 return [r for r in subset if r in o]
1085 return [r for r in subset if r in o]
1088
1086
1089 def p1(repo, subset, x):
1087 def p1(repo, subset, x):
1090 """``p1([set])``
1088 """``p1([set])``
1091 First parent of changesets in set, or the working directory.
1089 First parent of changesets in set, or the working directory.
1092 """
1090 """
1093 if x is None:
1091 if x is None:
1094 p = repo[x].p1().rev()
1092 p = repo[x].p1().rev()
1095 return [r for r in subset if r == p]
1093 return [r for r in subset if r == p]
1096
1094
1097 ps = set()
1095 ps = set()
1098 cl = repo.changelog
1096 cl = repo.changelog
1099 for r in getset(repo, list(repo), x):
1097 for r in getset(repo, list(repo), x):
1100 ps.add(cl.parentrevs(r)[0])
1098 ps.add(cl.parentrevs(r)[0])
1101 return [r for r in subset if r in ps]
1099 return [r for r in subset if r in ps]
1102
1100
1103 def p2(repo, subset, x):
1101 def p2(repo, subset, x):
1104 """``p2([set])``
1102 """``p2([set])``
1105 Second parent of changesets in set, or the working directory.
1103 Second parent of changesets in set, or the working directory.
1106 """
1104 """
1107 if x is None:
1105 if x is None:
1108 ps = repo[x].parents()
1106 ps = repo[x].parents()
1109 try:
1107 try:
1110 p = ps[1].rev()
1108 p = ps[1].rev()
1111 return [r for r in subset if r == p]
1109 return [r for r in subset if r == p]
1112 except IndexError:
1110 except IndexError:
1113 return []
1111 return []
1114
1112
1115 ps = set()
1113 ps = set()
1116 cl = repo.changelog
1114 cl = repo.changelog
1117 for r in getset(repo, list(repo), x):
1115 for r in getset(repo, list(repo), x):
1118 ps.add(cl.parentrevs(r)[1])
1116 ps.add(cl.parentrevs(r)[1])
1119 return [r for r in subset if r in ps]
1117 return [r for r in subset if r in ps]
1120
1118
1121 def parents(repo, subset, x):
1119 def parents(repo, subset, x):
1122 """``parents([set])``
1120 """``parents([set])``
1123 The set of all parents for all changesets in set, or the working directory.
1121 The set of all parents for all changesets in set, or the working directory.
1124 """
1122 """
1125 if x is None:
1123 if x is None:
1126 ps = tuple(p.rev() for p in repo[x].parents())
1124 ps = tuple(p.rev() for p in repo[x].parents())
1127 return [r for r in subset if r in ps]
1125 return [r for r in subset if r in ps]
1128
1126
1129 ps = set()
1127 ps = set()
1130 cl = repo.changelog
1128 cl = repo.changelog
1131 for r in getset(repo, list(repo), x):
1129 for r in getset(repo, list(repo), x):
1132 ps.update(cl.parentrevs(r))
1130 ps.update(cl.parentrevs(r))
1133 return [r for r in subset if r in ps]
1131 return [r for r in subset if r in ps]
1134
1132
1135 def parentspec(repo, subset, x, n):
1133 def parentspec(repo, subset, x, n):
1136 """``set^0``
1134 """``set^0``
1137 The set.
1135 The set.
1138 ``set^1`` (or ``set^``), ``set^2``
1136 ``set^1`` (or ``set^``), ``set^2``
1139 First or second parent, respectively, of all changesets in set.
1137 First or second parent, respectively, of all changesets in set.
1140 """
1138 """
1141 try:
1139 try:
1142 n = int(n[1])
1140 n = int(n[1])
1143 if n not in (0, 1, 2):
1141 if n not in (0, 1, 2):
1144 raise ValueError
1142 raise ValueError
1145 except (TypeError, ValueError):
1143 except (TypeError, ValueError):
1146 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1144 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1147 ps = set()
1145 ps = set()
1148 cl = repo.changelog
1146 cl = repo.changelog
1149 for r in getset(repo, cl, x):
1147 for r in getset(repo, cl, x):
1150 if n == 0:
1148 if n == 0:
1151 ps.add(r)
1149 ps.add(r)
1152 elif n == 1:
1150 elif n == 1:
1153 ps.add(cl.parentrevs(r)[0])
1151 ps.add(cl.parentrevs(r)[0])
1154 elif n == 2:
1152 elif n == 2:
1155 parents = cl.parentrevs(r)
1153 parents = cl.parentrevs(r)
1156 if len(parents) > 1:
1154 if len(parents) > 1:
1157 ps.add(parents[1])
1155 ps.add(parents[1])
1158 return [r for r in subset if r in ps]
1156 return [r for r in subset if r in ps]
1159
1157
1160 def present(repo, subset, x):
1158 def present(repo, subset, x):
1161 """``present(set)``
1159 """``present(set)``
1162 An empty set, if any revision in set isn't found; otherwise,
1160 An empty set, if any revision in set isn't found; otherwise,
1163 all revisions in set.
1161 all revisions in set.
1164
1162
1165 If any of specified revisions is not present in the local repository,
1163 If any of specified revisions is not present in the local repository,
1166 the query is normally aborted. But this predicate allows the query
1164 the query is normally aborted. But this predicate allows the query
1167 to continue even in such cases.
1165 to continue even in such cases.
1168 """
1166 """
1169 try:
1167 try:
1170 return getset(repo, subset, x)
1168 return getset(repo, subset, x)
1171 except error.RepoLookupError:
1169 except error.RepoLookupError:
1172 return []
1170 return []
1173
1171
1174 def public(repo, subset, x):
1172 def public(repo, subset, x):
1175 """``public()``
1173 """``public()``
1176 Changeset in public phase."""
1174 Changeset in public phase."""
1177 # i18n: "public" is a keyword
1175 # i18n: "public" is a keyword
1178 getargs(x, 0, 0, _("public takes no arguments"))
1176 getargs(x, 0, 0, _("public takes no arguments"))
1179 pc = repo._phasecache
1177 pc = repo._phasecache
1180 return [r for r in subset if pc.phase(repo, r) == phases.public]
1178 return [r for r in subset if pc.phase(repo, r) == phases.public]
1181
1179
1182 def remote(repo, subset, x):
1180 def remote(repo, subset, x):
1183 """``remote([id [,path]])``
1181 """``remote([id [,path]])``
1184 Local revision that corresponds to the given identifier in a
1182 Local revision that corresponds to the given identifier in a
1185 remote repository, if present. Here, the '.' identifier is a
1183 remote repository, if present. Here, the '.' identifier is a
1186 synonym for the current local branch.
1184 synonym for the current local branch.
1187 """
1185 """
1188
1186
1189 import hg # avoid start-up nasties
1187 import hg # avoid start-up nasties
1190 # i18n: "remote" is a keyword
1188 # i18n: "remote" is a keyword
1191 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1189 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1192
1190
1193 q = '.'
1191 q = '.'
1194 if len(l) > 0:
1192 if len(l) > 0:
1195 # i18n: "remote" is a keyword
1193 # i18n: "remote" is a keyword
1196 q = getstring(l[0], _("remote requires a string id"))
1194 q = getstring(l[0], _("remote requires a string id"))
1197 if q == '.':
1195 if q == '.':
1198 q = repo['.'].branch()
1196 q = repo['.'].branch()
1199
1197
1200 dest = ''
1198 dest = ''
1201 if len(l) > 1:
1199 if len(l) > 1:
1202 # i18n: "remote" is a keyword
1200 # i18n: "remote" is a keyword
1203 dest = getstring(l[1], _("remote requires a repository path"))
1201 dest = getstring(l[1], _("remote requires a repository path"))
1204 dest = repo.ui.expandpath(dest or 'default')
1202 dest = repo.ui.expandpath(dest or 'default')
1205 dest, branches = hg.parseurl(dest)
1203 dest, branches = hg.parseurl(dest)
1206 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1204 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1207 if revs:
1205 if revs:
1208 revs = [repo.lookup(rev) for rev in revs]
1206 revs = [repo.lookup(rev) for rev in revs]
1209 other = hg.peer(repo, {}, dest)
1207 other = hg.peer(repo, {}, dest)
1210 n = other.lookup(q)
1208 n = other.lookup(q)
1211 if n in repo:
1209 if n in repo:
1212 r = repo[n].rev()
1210 r = repo[n].rev()
1213 if r in subset:
1211 if r in subset:
1214 return [r]
1212 return [r]
1215 return []
1213 return []
1216
1214
1217 def removes(repo, subset, x):
1215 def removes(repo, subset, x):
1218 """``removes(pattern)``
1216 """``removes(pattern)``
1219 Changesets which remove files matching pattern.
1217 Changesets which remove files matching pattern.
1220 """
1218 """
1221 # i18n: "removes" is a keyword
1219 # i18n: "removes" is a keyword
1222 pat = getstring(x, _("removes requires a pattern"))
1220 pat = getstring(x, _("removes requires a pattern"))
1223 return checkstatus(repo, subset, pat, 2)
1221 return checkstatus(repo, subset, pat, 2)
1224
1222
1225 def rev(repo, subset, x):
1223 def rev(repo, subset, x):
1226 """``rev(number)``
1224 """``rev(number)``
1227 Revision with the given numeric identifier.
1225 Revision with the given numeric identifier.
1228 """
1226 """
1229 # i18n: "rev" is a keyword
1227 # i18n: "rev" is a keyword
1230 l = getargs(x, 1, 1, _("rev requires one argument"))
1228 l = getargs(x, 1, 1, _("rev requires one argument"))
1231 try:
1229 try:
1232 # i18n: "rev" is a keyword
1230 # i18n: "rev" is a keyword
1233 l = int(getstring(l[0], _("rev requires a number")))
1231 l = int(getstring(l[0], _("rev requires a number")))
1234 except (TypeError, ValueError):
1232 except (TypeError, ValueError):
1235 # i18n: "rev" is a keyword
1233 # i18n: "rev" is a keyword
1236 raise error.ParseError(_("rev expects a number"))
1234 raise error.ParseError(_("rev expects a number"))
1237 return [r for r in subset if r == l]
1235 return [r for r in subset if r == l]
1238
1236
1239 def matching(repo, subset, x):
1237 def matching(repo, subset, x):
1240 """``matching(revision [, field])``
1238 """``matching(revision [, field])``
1241 Changesets in which a given set of fields match the set of fields in the
1239 Changesets in which a given set of fields match the set of fields in the
1242 selected revision or set.
1240 selected revision or set.
1243
1241
1244 To match more than one field pass the list of fields to match separated
1242 To match more than one field pass the list of fields to match separated
1245 by spaces (e.g. ``author description``).
1243 by spaces (e.g. ``author description``).
1246
1244
1247 Valid fields are most regular revision fields and some special fields.
1245 Valid fields are most regular revision fields and some special fields.
1248
1246
1249 Regular revision fields are ``description``, ``author``, ``branch``,
1247 Regular revision fields are ``description``, ``author``, ``branch``,
1250 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1248 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1251 and ``diff``.
1249 and ``diff``.
1252 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1250 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1253 contents of the revision. Two revisions matching their ``diff`` will
1251 contents of the revision. Two revisions matching their ``diff`` will
1254 also match their ``files``.
1252 also match their ``files``.
1255
1253
1256 Special fields are ``summary`` and ``metadata``:
1254 Special fields are ``summary`` and ``metadata``:
1257 ``summary`` matches the first line of the description.
1255 ``summary`` matches the first line of the description.
1258 ``metadata`` is equivalent to matching ``description user date``
1256 ``metadata`` is equivalent to matching ``description user date``
1259 (i.e. it matches the main metadata fields).
1257 (i.e. it matches the main metadata fields).
1260
1258
1261 ``metadata`` is the default field which is used when no fields are
1259 ``metadata`` is the default field which is used when no fields are
1262 specified. You can match more than one field at a time.
1260 specified. You can match more than one field at a time.
1263 """
1261 """
1264 # i18n: "matching" is a keyword
1262 # i18n: "matching" is a keyword
1265 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1263 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1266
1264
1267 revs = getset(repo, repo.changelog, l[0])
1265 revs = getset(repo, repo.changelog, l[0])
1268
1266
1269 fieldlist = ['metadata']
1267 fieldlist = ['metadata']
1270 if len(l) > 1:
1268 if len(l) > 1:
1271 fieldlist = getstring(l[1],
1269 fieldlist = getstring(l[1],
1272 # i18n: "matching" is a keyword
1270 # i18n: "matching" is a keyword
1273 _("matching requires a string "
1271 _("matching requires a string "
1274 "as its second argument")).split()
1272 "as its second argument")).split()
1275
1273
1276 # Make sure that there are no repeated fields,
1274 # Make sure that there are no repeated fields,
1277 # expand the 'special' 'metadata' field type
1275 # expand the 'special' 'metadata' field type
1278 # and check the 'files' whenever we check the 'diff'
1276 # and check the 'files' whenever we check the 'diff'
1279 fields = []
1277 fields = []
1280 for field in fieldlist:
1278 for field in fieldlist:
1281 if field == 'metadata':
1279 if field == 'metadata':
1282 fields += ['user', 'description', 'date']
1280 fields += ['user', 'description', 'date']
1283 elif field == 'diff':
1281 elif field == 'diff':
1284 # a revision matching the diff must also match the files
1282 # a revision matching the diff must also match the files
1285 # since matching the diff is very costly, make sure to
1283 # since matching the diff is very costly, make sure to
1286 # also match the files first
1284 # also match the files first
1287 fields += ['files', 'diff']
1285 fields += ['files', 'diff']
1288 else:
1286 else:
1289 if field == 'author':
1287 if field == 'author':
1290 field = 'user'
1288 field = 'user'
1291 fields.append(field)
1289 fields.append(field)
1292 fields = set(fields)
1290 fields = set(fields)
1293 if 'summary' in fields and 'description' in fields:
1291 if 'summary' in fields and 'description' in fields:
1294 # If a revision matches its description it also matches its summary
1292 # If a revision matches its description it also matches its summary
1295 fields.discard('summary')
1293 fields.discard('summary')
1296
1294
1297 # We may want to match more than one field
1295 # We may want to match more than one field
1298 # Not all fields take the same amount of time to be matched
1296 # Not all fields take the same amount of time to be matched
1299 # Sort the selected fields in order of increasing matching cost
1297 # Sort the selected fields in order of increasing matching cost
1300 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1298 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1301 'files', 'description', 'substate', 'diff']
1299 'files', 'description', 'substate', 'diff']
1302 def fieldkeyfunc(f):
1300 def fieldkeyfunc(f):
1303 try:
1301 try:
1304 return fieldorder.index(f)
1302 return fieldorder.index(f)
1305 except ValueError:
1303 except ValueError:
1306 # assume an unknown field is very costly
1304 # assume an unknown field is very costly
1307 return len(fieldorder)
1305 return len(fieldorder)
1308 fields = list(fields)
1306 fields = list(fields)
1309 fields.sort(key=fieldkeyfunc)
1307 fields.sort(key=fieldkeyfunc)
1310
1308
1311 # Each field will be matched with its own "getfield" function
1309 # Each field will be matched with its own "getfield" function
1312 # which will be added to the getfieldfuncs array of functions
1310 # which will be added to the getfieldfuncs array of functions
1313 getfieldfuncs = []
1311 getfieldfuncs = []
1314 _funcs = {
1312 _funcs = {
1315 'user': lambda r: repo[r].user(),
1313 'user': lambda r: repo[r].user(),
1316 'branch': lambda r: repo[r].branch(),
1314 'branch': lambda r: repo[r].branch(),
1317 'date': lambda r: repo[r].date(),
1315 'date': lambda r: repo[r].date(),
1318 'description': lambda r: repo[r].description(),
1316 'description': lambda r: repo[r].description(),
1319 'files': lambda r: repo[r].files(),
1317 'files': lambda r: repo[r].files(),
1320 'parents': lambda r: repo[r].parents(),
1318 'parents': lambda r: repo[r].parents(),
1321 'phase': lambda r: repo[r].phase(),
1319 'phase': lambda r: repo[r].phase(),
1322 'substate': lambda r: repo[r].substate,
1320 'substate': lambda r: repo[r].substate,
1323 'summary': lambda r: repo[r].description().splitlines()[0],
1321 'summary': lambda r: repo[r].description().splitlines()[0],
1324 'diff': lambda r: list(repo[r].diff(git=True),)
1322 'diff': lambda r: list(repo[r].diff(git=True),)
1325 }
1323 }
1326 for info in fields:
1324 for info in fields:
1327 getfield = _funcs.get(info, None)
1325 getfield = _funcs.get(info, None)
1328 if getfield is None:
1326 if getfield is None:
1329 raise error.ParseError(
1327 raise error.ParseError(
1330 # i18n: "matching" is a keyword
1328 # i18n: "matching" is a keyword
1331 _("unexpected field name passed to matching: %s") % info)
1329 _("unexpected field name passed to matching: %s") % info)
1332 getfieldfuncs.append(getfield)
1330 getfieldfuncs.append(getfield)
1333 # convert the getfield array of functions into a "getinfo" function
1331 # convert the getfield array of functions into a "getinfo" function
1334 # which returns an array of field values (or a single value if there
1332 # which returns an array of field values (or a single value if there
1335 # is only one field to match)
1333 # is only one field to match)
1336 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1334 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1337
1335
1338 matches = set()
1336 matches = set()
1339 for rev in revs:
1337 for rev in revs:
1340 target = getinfo(rev)
1338 target = getinfo(rev)
1341 for r in subset:
1339 for r in subset:
1342 match = True
1340 match = True
1343 for n, f in enumerate(getfieldfuncs):
1341 for n, f in enumerate(getfieldfuncs):
1344 if target[n] != f(r):
1342 if target[n] != f(r):
1345 match = False
1343 match = False
1346 break
1344 break
1347 if match:
1345 if match:
1348 matches.add(r)
1346 matches.add(r)
1349 return [r for r in subset if r in matches]
1347 return [r for r in subset if r in matches]
1350
1348
1351 def reverse(repo, subset, x):
1349 def reverse(repo, subset, x):
1352 """``reverse(set)``
1350 """``reverse(set)``
1353 Reverse order of set.
1351 Reverse order of set.
1354 """
1352 """
1355 l = getset(repo, subset, x)
1353 l = getset(repo, subset, x)
1356 if not isinstance(l, list):
1354 if not isinstance(l, list):
1357 l = list(l)
1355 l = list(l)
1358 l.reverse()
1356 l.reverse()
1359 return l
1357 return l
1360
1358
1361 def roots(repo, subset, x):
1359 def roots(repo, subset, x):
1362 """``roots(set)``
1360 """``roots(set)``
1363 Changesets in set with no parent changeset in set.
1361 Changesets in set with no parent changeset in set.
1364 """
1362 """
1365 s = set(getset(repo, repo.changelog, x))
1363 s = set(getset(repo, repo.changelog, x))
1366 subset = [r for r in subset if r in s]
1364 subset = [r for r in subset if r in s]
1367 cs = _children(repo, subset, s)
1365 cs = _children(repo, subset, s)
1368 return [r for r in subset if r not in cs]
1366 return [r for r in subset if r not in cs]
1369
1367
1370 def secret(repo, subset, x):
1368 def secret(repo, subset, x):
1371 """``secret()``
1369 """``secret()``
1372 Changeset in secret phase."""
1370 Changeset in secret phase."""
1373 # i18n: "secret" is a keyword
1371 # i18n: "secret" is a keyword
1374 getargs(x, 0, 0, _("secret takes no arguments"))
1372 getargs(x, 0, 0, _("secret takes no arguments"))
1375 pc = repo._phasecache
1373 pc = repo._phasecache
1376 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1374 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1377
1375
1378 def sort(repo, subset, x):
1376 def sort(repo, subset, x):
1379 """``sort(set[, [-]key...])``
1377 """``sort(set[, [-]key...])``
1380 Sort set by keys. The default sort order is ascending, specify a key
1378 Sort set by keys. The default sort order is ascending, specify a key
1381 as ``-key`` to sort in descending order.
1379 as ``-key`` to sort in descending order.
1382
1380
1383 The keys can be:
1381 The keys can be:
1384
1382
1385 - ``rev`` for the revision number,
1383 - ``rev`` for the revision number,
1386 - ``branch`` for the branch name,
1384 - ``branch`` for the branch name,
1387 - ``desc`` for the commit message (description),
1385 - ``desc`` for the commit message (description),
1388 - ``user`` for user name (``author`` can be used as an alias),
1386 - ``user`` for user name (``author`` can be used as an alias),
1389 - ``date`` for the commit date
1387 - ``date`` for the commit date
1390 """
1388 """
1391 # i18n: "sort" is a keyword
1389 # i18n: "sort" is a keyword
1392 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1390 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1393 keys = "rev"
1391 keys = "rev"
1394 if len(l) == 2:
1392 if len(l) == 2:
1395 # i18n: "sort" is a keyword
1393 # i18n: "sort" is a keyword
1396 keys = getstring(l[1], _("sort spec must be a string"))
1394 keys = getstring(l[1], _("sort spec must be a string"))
1397
1395
1398 s = l[0]
1396 s = l[0]
1399 keys = keys.split()
1397 keys = keys.split()
1400 l = []
1398 l = []
1401 def invert(s):
1399 def invert(s):
1402 return "".join(chr(255 - ord(c)) for c in s)
1400 return "".join(chr(255 - ord(c)) for c in s)
1403 for r in getset(repo, subset, s):
1401 for r in getset(repo, subset, s):
1404 c = repo[r]
1402 c = repo[r]
1405 e = []
1403 e = []
1406 for k in keys:
1404 for k in keys:
1407 if k == 'rev':
1405 if k == 'rev':
1408 e.append(r)
1406 e.append(r)
1409 elif k == '-rev':
1407 elif k == '-rev':
1410 e.append(-r)
1408 e.append(-r)
1411 elif k == 'branch':
1409 elif k == 'branch':
1412 e.append(c.branch())
1410 e.append(c.branch())
1413 elif k == '-branch':
1411 elif k == '-branch':
1414 e.append(invert(c.branch()))
1412 e.append(invert(c.branch()))
1415 elif k == 'desc':
1413 elif k == 'desc':
1416 e.append(c.description())
1414 e.append(c.description())
1417 elif k == '-desc':
1415 elif k == '-desc':
1418 e.append(invert(c.description()))
1416 e.append(invert(c.description()))
1419 elif k in 'user author':
1417 elif k in 'user author':
1420 e.append(c.user())
1418 e.append(c.user())
1421 elif k in '-user -author':
1419 elif k in '-user -author':
1422 e.append(invert(c.user()))
1420 e.append(invert(c.user()))
1423 elif k == 'date':
1421 elif k == 'date':
1424 e.append(c.date()[0])
1422 e.append(c.date()[0])
1425 elif k == '-date':
1423 elif k == '-date':
1426 e.append(-c.date()[0])
1424 e.append(-c.date()[0])
1427 else:
1425 else:
1428 raise error.ParseError(_("unknown sort key %r") % k)
1426 raise error.ParseError(_("unknown sort key %r") % k)
1429 e.append(r)
1427 e.append(r)
1430 l.append(e)
1428 l.append(e)
1431 l.sort()
1429 l.sort()
1432 return [e[-1] for e in l]
1430 return [e[-1] for e in l]
1433
1431
1434 def _stringmatcher(pattern):
1432 def _stringmatcher(pattern):
1435 """
1433 """
1436 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1434 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1437 returns the matcher name, pattern, and matcher function.
1435 returns the matcher name, pattern, and matcher function.
1438 missing or unknown prefixes are treated as literal matches.
1436 missing or unknown prefixes are treated as literal matches.
1439
1437
1440 helper for tests:
1438 helper for tests:
1441 >>> def test(pattern, *tests):
1439 >>> def test(pattern, *tests):
1442 ... kind, pattern, matcher = _stringmatcher(pattern)
1440 ... kind, pattern, matcher = _stringmatcher(pattern)
1443 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1441 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1444
1442
1445 exact matching (no prefix):
1443 exact matching (no prefix):
1446 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1444 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1447 ('literal', 'abcdefg', [False, False, True])
1445 ('literal', 'abcdefg', [False, False, True])
1448
1446
1449 regex matching ('re:' prefix)
1447 regex matching ('re:' prefix)
1450 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1448 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1451 ('re', 'a.+b', [False, False, True])
1449 ('re', 'a.+b', [False, False, True])
1452
1450
1453 force exact matches ('literal:' prefix)
1451 force exact matches ('literal:' prefix)
1454 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1452 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1455 ('literal', 're:foobar', [False, True])
1453 ('literal', 're:foobar', [False, True])
1456
1454
1457 unknown prefixes are ignored and treated as literals
1455 unknown prefixes are ignored and treated as literals
1458 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1456 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1459 ('literal', 'foo:bar', [False, False, True])
1457 ('literal', 'foo:bar', [False, False, True])
1460 """
1458 """
1461 if pattern.startswith('re:'):
1459 if pattern.startswith('re:'):
1462 pattern = pattern[3:]
1460 pattern = pattern[3:]
1463 try:
1461 try:
1464 regex = re.compile(pattern)
1462 regex = re.compile(pattern)
1465 except re.error, e:
1463 except re.error, e:
1466 raise error.ParseError(_('invalid regular expression: %s')
1464 raise error.ParseError(_('invalid regular expression: %s')
1467 % e)
1465 % e)
1468 return 're', pattern, regex.search
1466 return 're', pattern, regex.search
1469 elif pattern.startswith('literal:'):
1467 elif pattern.startswith('literal:'):
1470 pattern = pattern[8:]
1468 pattern = pattern[8:]
1471 return 'literal', pattern, pattern.__eq__
1469 return 'literal', pattern, pattern.__eq__
1472
1470
1473 def _substringmatcher(pattern):
1471 def _substringmatcher(pattern):
1474 kind, pattern, matcher = _stringmatcher(pattern)
1472 kind, pattern, matcher = _stringmatcher(pattern)
1475 if kind == 'literal':
1473 if kind == 'literal':
1476 matcher = lambda s: pattern in s
1474 matcher = lambda s: pattern in s
1477 return kind, pattern, matcher
1475 return kind, pattern, matcher
1478
1476
1479 def tag(repo, subset, x):
1477 def tag(repo, subset, x):
1480 """``tag([name])``
1478 """``tag([name])``
1481 The specified tag by name, or all tagged revisions if no name is given.
1479 The specified tag by name, or all tagged revisions if no name is given.
1482 """
1480 """
1483 # i18n: "tag" is a keyword
1481 # i18n: "tag" is a keyword
1484 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1482 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1485 cl = repo.changelog
1483 cl = repo.changelog
1486 if args:
1484 if args:
1487 pattern = getstring(args[0],
1485 pattern = getstring(args[0],
1488 # i18n: "tag" is a keyword
1486 # i18n: "tag" is a keyword
1489 _('the argument to tag must be a string'))
1487 _('the argument to tag must be a string'))
1490 kind, pattern, matcher = _stringmatcher(pattern)
1488 kind, pattern, matcher = _stringmatcher(pattern)
1491 if kind == 'literal':
1489 if kind == 'literal':
1492 # avoid resolving all tags
1490 # avoid resolving all tags
1493 tn = repo._tagscache.tags.get(pattern, None)
1491 tn = repo._tagscache.tags.get(pattern, None)
1494 if tn is None:
1492 if tn is None:
1495 raise util.Abort(_("tag '%s' does not exist") % pattern)
1493 raise util.Abort(_("tag '%s' does not exist") % pattern)
1496 s = set([repo[tn].rev()])
1494 s = set([repo[tn].rev()])
1497 else:
1495 else:
1498 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1496 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1499 else:
1497 else:
1500 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1498 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1501 return [r for r in subset if r in s]
1499 return [r for r in subset if r in s]
1502
1500
1503 def tagged(repo, subset, x):
1501 def tagged(repo, subset, x):
1504 return tag(repo, subset, x)
1502 return tag(repo, subset, x)
1505
1503
1506 def unstable(repo, subset, x):
1504 def unstable(repo, subset, x):
1507 """``unstable()``
1505 """``unstable()``
1508 Non-obsolete changesets with obsolete ancestors.
1506 Non-obsolete changesets with obsolete ancestors.
1509 """
1507 """
1510 # i18n: "unstable" is a keyword
1508 # i18n: "unstable" is a keyword
1511 getargs(x, 0, 0, _("unstable takes no arguments"))
1509 getargs(x, 0, 0, _("unstable takes no arguments"))
1512 unstables = obsmod.getrevs(repo, 'unstable')
1510 unstables = obsmod.getrevs(repo, 'unstable')
1513 return [r for r in subset if r in unstables]
1511 return [r for r in subset if r in unstables]
1514
1512
1515
1513
1516 def user(repo, subset, x):
1514 def user(repo, subset, x):
1517 """``user(string)``
1515 """``user(string)``
1518 User name contains string. The match is case-insensitive.
1516 User name contains string. The match is case-insensitive.
1519
1517
1520 If `string` starts with `re:`, the remainder of the string is treated as
1518 If `string` starts with `re:`, the remainder of the string is treated as
1521 a regular expression. To match a user that actually contains `re:`, use
1519 a regular expression. To match a user that actually contains `re:`, use
1522 the prefix `literal:`.
1520 the prefix `literal:`.
1523 """
1521 """
1524 return author(repo, subset, x)
1522 return author(repo, subset, x)
1525
1523
1526 # for internal use
1524 # for internal use
1527 def _list(repo, subset, x):
1525 def _list(repo, subset, x):
1528 s = getstring(x, "internal error")
1526 s = getstring(x, "internal error")
1529 if not s:
1527 if not s:
1530 return []
1528 return []
1531 if not isinstance(subset, set):
1529 if not isinstance(subset, set):
1532 subset = set(subset)
1530 subset = set(subset)
1533 ls = [repo[r].rev() for r in s.split('\0')]
1531 ls = [repo[r].rev() for r in s.split('\0')]
1534 return [r for r in ls if r in subset]
1532 return [r for r in ls if r in subset]
1535
1533
1536 symbols = {
1534 symbols = {
1537 "adds": adds,
1535 "adds": adds,
1538 "all": getall,
1536 "all": getall,
1539 "ancestor": ancestor,
1537 "ancestor": ancestor,
1540 "ancestors": ancestors,
1538 "ancestors": ancestors,
1541 "_firstancestors": _firstancestors,
1539 "_firstancestors": _firstancestors,
1542 "author": author,
1540 "author": author,
1543 "bisect": bisect,
1541 "bisect": bisect,
1544 "bisected": bisected,
1542 "bisected": bisected,
1545 "bookmark": bookmark,
1543 "bookmark": bookmark,
1546 "branch": branch,
1544 "branch": branch,
1547 "branchpoint": branchpoint,
1545 "branchpoint": branchpoint,
1548 "bumped": bumped,
1546 "bumped": bumped,
1549 "bundle": bundle,
1547 "bundle": bundle,
1550 "children": children,
1548 "children": children,
1551 "closed": closed,
1549 "closed": closed,
1552 "contains": contains,
1550 "contains": contains,
1553 "converted": converted,
1551 "converted": converted,
1554 "date": date,
1552 "date": date,
1555 "desc": desc,
1553 "desc": desc,
1556 "descendants": descendants,
1554 "descendants": descendants,
1557 "_firstdescendants": _firstdescendants,
1555 "_firstdescendants": _firstdescendants,
1558 "destination": destination,
1556 "destination": destination,
1559 "divergent": divergent,
1557 "divergent": divergent,
1560 "draft": draft,
1558 "draft": draft,
1561 "extinct": extinct,
1559 "extinct": extinct,
1562 "extra": extra,
1560 "extra": extra,
1563 "file": hasfile,
1561 "file": hasfile,
1564 "filelog": filelog,
1562 "filelog": filelog,
1565 "first": first,
1563 "first": first,
1566 "follow": follow,
1564 "follow": follow,
1567 "_followfirst": _followfirst,
1565 "_followfirst": _followfirst,
1568 "grep": grep,
1566 "grep": grep,
1569 "head": head,
1567 "head": head,
1570 "heads": heads,
1568 "heads": heads,
1571 "hidden": hidden,
1569 "hidden": hidden,
1572 "id": node_,
1570 "id": node_,
1573 "keyword": keyword,
1571 "keyword": keyword,
1574 "last": last,
1572 "last": last,
1575 "limit": limit,
1573 "limit": limit,
1576 "_matchfiles": _matchfiles,
1574 "_matchfiles": _matchfiles,
1577 "max": maxrev,
1575 "max": maxrev,
1578 "merge": merge,
1576 "merge": merge,
1579 "min": minrev,
1577 "min": minrev,
1580 "modifies": modifies,
1578 "modifies": modifies,
1581 "obsolete": obsolete,
1579 "obsolete": obsolete,
1582 "origin": origin,
1580 "origin": origin,
1583 "outgoing": outgoing,
1581 "outgoing": outgoing,
1584 "p1": p1,
1582 "p1": p1,
1585 "p2": p2,
1583 "p2": p2,
1586 "parents": parents,
1584 "parents": parents,
1587 "present": present,
1585 "present": present,
1588 "public": public,
1586 "public": public,
1589 "remote": remote,
1587 "remote": remote,
1590 "removes": removes,
1588 "removes": removes,
1591 "rev": rev,
1589 "rev": rev,
1592 "reverse": reverse,
1590 "reverse": reverse,
1593 "roots": roots,
1591 "roots": roots,
1594 "sort": sort,
1592 "sort": sort,
1595 "secret": secret,
1593 "secret": secret,
1596 "matching": matching,
1594 "matching": matching,
1597 "tag": tag,
1595 "tag": tag,
1598 "tagged": tagged,
1596 "tagged": tagged,
1599 "user": user,
1597 "user": user,
1600 "unstable": unstable,
1598 "unstable": unstable,
1601 "_list": _list,
1599 "_list": _list,
1602 }
1600 }
1603
1601
1604 methods = {
1602 methods = {
1605 "range": rangeset,
1603 "range": rangeset,
1606 "dagrange": dagrange,
1604 "dagrange": dagrange,
1607 "string": stringset,
1605 "string": stringset,
1608 "symbol": symbolset,
1606 "symbol": symbolset,
1609 "and": andset,
1607 "and": andset,
1610 "or": orset,
1608 "or": orset,
1611 "not": notset,
1609 "not": notset,
1612 "list": listset,
1610 "list": listset,
1613 "func": func,
1611 "func": func,
1614 "ancestor": ancestorspec,
1612 "ancestor": ancestorspec,
1615 "parent": parentspec,
1613 "parent": parentspec,
1616 "parentpost": p1,
1614 "parentpost": p1,
1617 }
1615 }
1618
1616
1619 def optimize(x, small):
1617 def optimize(x, small):
1620 if x is None:
1618 if x is None:
1621 return 0, x
1619 return 0, x
1622
1620
1623 smallbonus = 1
1621 smallbonus = 1
1624 if small:
1622 if small:
1625 smallbonus = .5
1623 smallbonus = .5
1626
1624
1627 op = x[0]
1625 op = x[0]
1628 if op == 'minus':
1626 if op == 'minus':
1629 return optimize(('and', x[1], ('not', x[2])), small)
1627 return optimize(('and', x[1], ('not', x[2])), small)
1630 elif op == 'dagrangepre':
1628 elif op == 'dagrangepre':
1631 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1629 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1632 elif op == 'dagrangepost':
1630 elif op == 'dagrangepost':
1633 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1631 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1634 elif op == 'rangepre':
1632 elif op == 'rangepre':
1635 return optimize(('range', ('string', '0'), x[1]), small)
1633 return optimize(('range', ('string', '0'), x[1]), small)
1636 elif op == 'rangepost':
1634 elif op == 'rangepost':
1637 return optimize(('range', x[1], ('string', 'tip')), small)
1635 return optimize(('range', x[1], ('string', 'tip')), small)
1638 elif op == 'negate':
1636 elif op == 'negate':
1639 return optimize(('string',
1637 return optimize(('string',
1640 '-' + getstring(x[1], _("can't negate that"))), small)
1638 '-' + getstring(x[1], _("can't negate that"))), small)
1641 elif op in 'string symbol negate':
1639 elif op in 'string symbol negate':
1642 return smallbonus, x # single revisions are small
1640 return smallbonus, x # single revisions are small
1643 elif op == 'and':
1641 elif op == 'and':
1644 wa, ta = optimize(x[1], True)
1642 wa, ta = optimize(x[1], True)
1645 wb, tb = optimize(x[2], True)
1643 wb, tb = optimize(x[2], True)
1646 w = min(wa, wb)
1644 w = min(wa, wb)
1647 if wa > wb:
1645 if wa > wb:
1648 return w, (op, tb, ta)
1646 return w, (op, tb, ta)
1649 return w, (op, ta, tb)
1647 return w, (op, ta, tb)
1650 elif op == 'or':
1648 elif op == 'or':
1651 wa, ta = optimize(x[1], False)
1649 wa, ta = optimize(x[1], False)
1652 wb, tb = optimize(x[2], False)
1650 wb, tb = optimize(x[2], False)
1653 if wb < wa:
1651 if wb < wa:
1654 wb, wa = wa, wb
1652 wb, wa = wa, wb
1655 return max(wa, wb), (op, ta, tb)
1653 return max(wa, wb), (op, ta, tb)
1656 elif op == 'not':
1654 elif op == 'not':
1657 o = optimize(x[1], not small)
1655 o = optimize(x[1], not small)
1658 return o[0], (op, o[1])
1656 return o[0], (op, o[1])
1659 elif op == 'parentpost':
1657 elif op == 'parentpost':
1660 o = optimize(x[1], small)
1658 o = optimize(x[1], small)
1661 return o[0], (op, o[1])
1659 return o[0], (op, o[1])
1662 elif op == 'group':
1660 elif op == 'group':
1663 return optimize(x[1], small)
1661 return optimize(x[1], small)
1664 elif op in 'dagrange range list parent ancestorspec':
1662 elif op in 'dagrange range list parent ancestorspec':
1665 if op == 'parent':
1663 if op == 'parent':
1666 # x^:y means (x^) : y, not x ^ (:y)
1664 # x^:y means (x^) : y, not x ^ (:y)
1667 post = ('parentpost', x[1])
1665 post = ('parentpost', x[1])
1668 if x[2][0] == 'dagrangepre':
1666 if x[2][0] == 'dagrangepre':
1669 return optimize(('dagrange', post, x[2][1]), small)
1667 return optimize(('dagrange', post, x[2][1]), small)
1670 elif x[2][0] == 'rangepre':
1668 elif x[2][0] == 'rangepre':
1671 return optimize(('range', post, x[2][1]), small)
1669 return optimize(('range', post, x[2][1]), small)
1672
1670
1673 wa, ta = optimize(x[1], small)
1671 wa, ta = optimize(x[1], small)
1674 wb, tb = optimize(x[2], small)
1672 wb, tb = optimize(x[2], small)
1675 return wa + wb, (op, ta, tb)
1673 return wa + wb, (op, ta, tb)
1676 elif op == 'func':
1674 elif op == 'func':
1677 f = getstring(x[1], _("not a symbol"))
1675 f = getstring(x[1], _("not a symbol"))
1678 wa, ta = optimize(x[2], small)
1676 wa, ta = optimize(x[2], small)
1679 if f in ("author branch closed date desc file grep keyword "
1677 if f in ("author branch closed date desc file grep keyword "
1680 "outgoing user"):
1678 "outgoing user"):
1681 w = 10 # slow
1679 w = 10 # slow
1682 elif f in "modifies adds removes":
1680 elif f in "modifies adds removes":
1683 w = 30 # slower
1681 w = 30 # slower
1684 elif f == "contains":
1682 elif f == "contains":
1685 w = 100 # very slow
1683 w = 100 # very slow
1686 elif f == "ancestor":
1684 elif f == "ancestor":
1687 w = 1 * smallbonus
1685 w = 1 * smallbonus
1688 elif f in "reverse limit first":
1686 elif f in "reverse limit first":
1689 w = 0
1687 w = 0
1690 elif f in "sort":
1688 elif f in "sort":
1691 w = 10 # assume most sorts look at changelog
1689 w = 10 # assume most sorts look at changelog
1692 else:
1690 else:
1693 w = 1
1691 w = 1
1694 return w + wa, (op, x[1], ta)
1692 return w + wa, (op, x[1], ta)
1695 return 1, x
1693 return 1, x
1696
1694
1697 _aliasarg = ('func', ('symbol', '_aliasarg'))
1695 _aliasarg = ('func', ('symbol', '_aliasarg'))
1698 def _getaliasarg(tree):
1696 def _getaliasarg(tree):
1699 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1697 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1700 return X, None otherwise.
1698 return X, None otherwise.
1701 """
1699 """
1702 if (len(tree) == 3 and tree[:2] == _aliasarg
1700 if (len(tree) == 3 and tree[:2] == _aliasarg
1703 and tree[2][0] == 'string'):
1701 and tree[2][0] == 'string'):
1704 return tree[2][1]
1702 return tree[2][1]
1705 return None
1703 return None
1706
1704
1707 def _checkaliasarg(tree, known=None):
1705 def _checkaliasarg(tree, known=None):
1708 """Check tree contains no _aliasarg construct or only ones which
1706 """Check tree contains no _aliasarg construct or only ones which
1709 value is in known. Used to avoid alias placeholders injection.
1707 value is in known. Used to avoid alias placeholders injection.
1710 """
1708 """
1711 if isinstance(tree, tuple):
1709 if isinstance(tree, tuple):
1712 arg = _getaliasarg(tree)
1710 arg = _getaliasarg(tree)
1713 if arg is not None and (not known or arg not in known):
1711 if arg is not None and (not known or arg not in known):
1714 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1712 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1715 for t in tree:
1713 for t in tree:
1716 _checkaliasarg(t, known)
1714 _checkaliasarg(t, known)
1717
1715
1718 class revsetalias(object):
1716 class revsetalias(object):
1719 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1717 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1720 args = None
1718 args = None
1721
1719
1722 def __init__(self, name, value):
1720 def __init__(self, name, value):
1723 '''Aliases like:
1721 '''Aliases like:
1724
1722
1725 h = heads(default)
1723 h = heads(default)
1726 b($1) = ancestors($1) - ancestors(default)
1724 b($1) = ancestors($1) - ancestors(default)
1727 '''
1725 '''
1728 m = self.funcre.search(name)
1726 m = self.funcre.search(name)
1729 if m:
1727 if m:
1730 self.name = m.group(1)
1728 self.name = m.group(1)
1731 self.tree = ('func', ('symbol', m.group(1)))
1729 self.tree = ('func', ('symbol', m.group(1)))
1732 self.args = [x.strip() for x in m.group(2).split(',')]
1730 self.args = [x.strip() for x in m.group(2).split(',')]
1733 for arg in self.args:
1731 for arg in self.args:
1734 # _aliasarg() is an unknown symbol only used separate
1732 # _aliasarg() is an unknown symbol only used separate
1735 # alias argument placeholders from regular strings.
1733 # alias argument placeholders from regular strings.
1736 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1734 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1737 else:
1735 else:
1738 self.name = name
1736 self.name = name
1739 self.tree = ('symbol', name)
1737 self.tree = ('symbol', name)
1740
1738
1741 self.replacement, pos = parse(value)
1739 self.replacement, pos = parse(value)
1742 if pos != len(value):
1740 if pos != len(value):
1743 raise error.ParseError(_('invalid token'), pos)
1741 raise error.ParseError(_('invalid token'), pos)
1744 # Check for placeholder injection
1742 # Check for placeholder injection
1745 _checkaliasarg(self.replacement, self.args)
1743 _checkaliasarg(self.replacement, self.args)
1746
1744
1747 def _getalias(aliases, tree):
1745 def _getalias(aliases, tree):
1748 """If tree looks like an unexpanded alias, return it. Return None
1746 """If tree looks like an unexpanded alias, return it. Return None
1749 otherwise.
1747 otherwise.
1750 """
1748 """
1751 if isinstance(tree, tuple) and tree:
1749 if isinstance(tree, tuple) and tree:
1752 if tree[0] == 'symbol' and len(tree) == 2:
1750 if tree[0] == 'symbol' and len(tree) == 2:
1753 name = tree[1]
1751 name = tree[1]
1754 alias = aliases.get(name)
1752 alias = aliases.get(name)
1755 if alias and alias.args is None and alias.tree == tree:
1753 if alias and alias.args is None and alias.tree == tree:
1756 return alias
1754 return alias
1757 if tree[0] == 'func' and len(tree) > 1:
1755 if tree[0] == 'func' and len(tree) > 1:
1758 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1756 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1759 name = tree[1][1]
1757 name = tree[1][1]
1760 alias = aliases.get(name)
1758 alias = aliases.get(name)
1761 if alias and alias.args is not None and alias.tree == tree[:2]:
1759 if alias and alias.args is not None and alias.tree == tree[:2]:
1762 return alias
1760 return alias
1763 return None
1761 return None
1764
1762
1765 def _expandargs(tree, args):
1763 def _expandargs(tree, args):
1766 """Replace _aliasarg instances with the substitution value of the
1764 """Replace _aliasarg instances with the substitution value of the
1767 same name in args, recursively.
1765 same name in args, recursively.
1768 """
1766 """
1769 if not tree or not isinstance(tree, tuple):
1767 if not tree or not isinstance(tree, tuple):
1770 return tree
1768 return tree
1771 arg = _getaliasarg(tree)
1769 arg = _getaliasarg(tree)
1772 if arg is not None:
1770 if arg is not None:
1773 return args[arg]
1771 return args[arg]
1774 return tuple(_expandargs(t, args) for t in tree)
1772 return tuple(_expandargs(t, args) for t in tree)
1775
1773
1776 def _expandaliases(aliases, tree, expanding, cache):
1774 def _expandaliases(aliases, tree, expanding, cache):
1777 """Expand aliases in tree, recursively.
1775 """Expand aliases in tree, recursively.
1778
1776
1779 'aliases' is a dictionary mapping user defined aliases to
1777 'aliases' is a dictionary mapping user defined aliases to
1780 revsetalias objects.
1778 revsetalias objects.
1781 """
1779 """
1782 if not isinstance(tree, tuple):
1780 if not isinstance(tree, tuple):
1783 # Do not expand raw strings
1781 # Do not expand raw strings
1784 return tree
1782 return tree
1785 alias = _getalias(aliases, tree)
1783 alias = _getalias(aliases, tree)
1786 if alias is not None:
1784 if alias is not None:
1787 if alias in expanding:
1785 if alias in expanding:
1788 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1786 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1789 'detected') % alias.name)
1787 'detected') % alias.name)
1790 expanding.append(alias)
1788 expanding.append(alias)
1791 if alias.name not in cache:
1789 if alias.name not in cache:
1792 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1790 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1793 expanding, cache)
1791 expanding, cache)
1794 result = cache[alias.name]
1792 result = cache[alias.name]
1795 expanding.pop()
1793 expanding.pop()
1796 if alias.args is not None:
1794 if alias.args is not None:
1797 l = getlist(tree[2])
1795 l = getlist(tree[2])
1798 if len(l) != len(alias.args):
1796 if len(l) != len(alias.args):
1799 raise error.ParseError(
1797 raise error.ParseError(
1800 _('invalid number of arguments: %s') % len(l))
1798 _('invalid number of arguments: %s') % len(l))
1801 l = [_expandaliases(aliases, a, [], cache) for a in l]
1799 l = [_expandaliases(aliases, a, [], cache) for a in l]
1802 result = _expandargs(result, dict(zip(alias.args, l)))
1800 result = _expandargs(result, dict(zip(alias.args, l)))
1803 else:
1801 else:
1804 result = tuple(_expandaliases(aliases, t, expanding, cache)
1802 result = tuple(_expandaliases(aliases, t, expanding, cache)
1805 for t in tree)
1803 for t in tree)
1806 return result
1804 return result
1807
1805
1808 def findaliases(ui, tree):
1806 def findaliases(ui, tree):
1809 _checkaliasarg(tree)
1807 _checkaliasarg(tree)
1810 aliases = {}
1808 aliases = {}
1811 for k, v in ui.configitems('revsetalias'):
1809 for k, v in ui.configitems('revsetalias'):
1812 alias = revsetalias(k, v)
1810 alias = revsetalias(k, v)
1813 aliases[alias.name] = alias
1811 aliases[alias.name] = alias
1814 return _expandaliases(aliases, tree, [], {})
1812 return _expandaliases(aliases, tree, [], {})
1815
1813
1816 parse = parser.parser(tokenize, elements).parse
1814 parse = parser.parser(tokenize, elements).parse
1817
1815
1818 def match(ui, spec):
1816 def match(ui, spec):
1819 if not spec:
1817 if not spec:
1820 raise error.ParseError(_("empty query"))
1818 raise error.ParseError(_("empty query"))
1821 tree, pos = parse(spec)
1819 tree, pos = parse(spec)
1822 if (pos != len(spec)):
1820 if (pos != len(spec)):
1823 raise error.ParseError(_("invalid token"), pos)
1821 raise error.ParseError(_("invalid token"), pos)
1824 if ui:
1822 if ui:
1825 tree = findaliases(ui, tree)
1823 tree = findaliases(ui, tree)
1826 weight, tree = optimize(tree, True)
1824 weight, tree = optimize(tree, True)
1827 def mfunc(repo, subset):
1825 def mfunc(repo, subset):
1828 return getset(repo, subset, tree)
1826 return getset(repo, subset, tree)
1829 return mfunc
1827 return mfunc
1830
1828
1831 def formatspec(expr, *args):
1829 def formatspec(expr, *args):
1832 '''
1830 '''
1833 This is a convenience function for using revsets internally, and
1831 This is a convenience function for using revsets internally, and
1834 escapes arguments appropriately. Aliases are intentionally ignored
1832 escapes arguments appropriately. Aliases are intentionally ignored
1835 so that intended expression behavior isn't accidentally subverted.
1833 so that intended expression behavior isn't accidentally subverted.
1836
1834
1837 Supported arguments:
1835 Supported arguments:
1838
1836
1839 %r = revset expression, parenthesized
1837 %r = revset expression, parenthesized
1840 %d = int(arg), no quoting
1838 %d = int(arg), no quoting
1841 %s = string(arg), escaped and single-quoted
1839 %s = string(arg), escaped and single-quoted
1842 %b = arg.branch(), escaped and single-quoted
1840 %b = arg.branch(), escaped and single-quoted
1843 %n = hex(arg), single-quoted
1841 %n = hex(arg), single-quoted
1844 %% = a literal '%'
1842 %% = a literal '%'
1845
1843
1846 Prefixing the type with 'l' specifies a parenthesized list of that type.
1844 Prefixing the type with 'l' specifies a parenthesized list of that type.
1847
1845
1848 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1846 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1849 '(10 or 11):: and ((this()) or (that()))'
1847 '(10 or 11):: and ((this()) or (that()))'
1850 >>> formatspec('%d:: and not %d::', 10, 20)
1848 >>> formatspec('%d:: and not %d::', 10, 20)
1851 '10:: and not 20::'
1849 '10:: and not 20::'
1852 >>> formatspec('%ld or %ld', [], [1])
1850 >>> formatspec('%ld or %ld', [], [1])
1853 "_list('') or 1"
1851 "_list('') or 1"
1854 >>> formatspec('keyword(%s)', 'foo\\xe9')
1852 >>> formatspec('keyword(%s)', 'foo\\xe9')
1855 "keyword('foo\\\\xe9')"
1853 "keyword('foo\\\\xe9')"
1856 >>> b = lambda: 'default'
1854 >>> b = lambda: 'default'
1857 >>> b.branch = b
1855 >>> b.branch = b
1858 >>> formatspec('branch(%b)', b)
1856 >>> formatspec('branch(%b)', b)
1859 "branch('default')"
1857 "branch('default')"
1860 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1858 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1861 "root(_list('a\\x00b\\x00c\\x00d'))"
1859 "root(_list('a\\x00b\\x00c\\x00d'))"
1862 '''
1860 '''
1863
1861
1864 def quote(s):
1862 def quote(s):
1865 return repr(str(s))
1863 return repr(str(s))
1866
1864
1867 def argtype(c, arg):
1865 def argtype(c, arg):
1868 if c == 'd':
1866 if c == 'd':
1869 return str(int(arg))
1867 return str(int(arg))
1870 elif c == 's':
1868 elif c == 's':
1871 return quote(arg)
1869 return quote(arg)
1872 elif c == 'r':
1870 elif c == 'r':
1873 parse(arg) # make sure syntax errors are confined
1871 parse(arg) # make sure syntax errors are confined
1874 return '(%s)' % arg
1872 return '(%s)' % arg
1875 elif c == 'n':
1873 elif c == 'n':
1876 return quote(node.hex(arg))
1874 return quote(node.hex(arg))
1877 elif c == 'b':
1875 elif c == 'b':
1878 return quote(arg.branch())
1876 return quote(arg.branch())
1879
1877
1880 def listexp(s, t):
1878 def listexp(s, t):
1881 l = len(s)
1879 l = len(s)
1882 if l == 0:
1880 if l == 0:
1883 return "_list('')"
1881 return "_list('')"
1884 elif l == 1:
1882 elif l == 1:
1885 return argtype(t, s[0])
1883 return argtype(t, s[0])
1886 elif t == 'd':
1884 elif t == 'd':
1887 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1885 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1888 elif t == 's':
1886 elif t == 's':
1889 return "_list('%s')" % "\0".join(s)
1887 return "_list('%s')" % "\0".join(s)
1890 elif t == 'n':
1888 elif t == 'n':
1891 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1889 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1892 elif t == 'b':
1890 elif t == 'b':
1893 return "_list('%s')" % "\0".join(a.branch() for a in s)
1891 return "_list('%s')" % "\0".join(a.branch() for a in s)
1894
1892
1895 m = l // 2
1893 m = l // 2
1896 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1894 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1897
1895
1898 ret = ''
1896 ret = ''
1899 pos = 0
1897 pos = 0
1900 arg = 0
1898 arg = 0
1901 while pos < len(expr):
1899 while pos < len(expr):
1902 c = expr[pos]
1900 c = expr[pos]
1903 if c == '%':
1901 if c == '%':
1904 pos += 1
1902 pos += 1
1905 d = expr[pos]
1903 d = expr[pos]
1906 if d == '%':
1904 if d == '%':
1907 ret += d
1905 ret += d
1908 elif d in 'dsnbr':
1906 elif d in 'dsnbr':
1909 ret += argtype(d, args[arg])
1907 ret += argtype(d, args[arg])
1910 arg += 1
1908 arg += 1
1911 elif d == 'l':
1909 elif d == 'l':
1912 # a list of some type
1910 # a list of some type
1913 pos += 1
1911 pos += 1
1914 d = expr[pos]
1912 d = expr[pos]
1915 ret += listexp(list(args[arg]), d)
1913 ret += listexp(list(args[arg]), d)
1916 arg += 1
1914 arg += 1
1917 else:
1915 else:
1918 raise util.Abort('unexpected revspec format character %s' % d)
1916 raise util.Abort('unexpected revspec format character %s' % d)
1919 else:
1917 else:
1920 ret += c
1918 ret += c
1921 pos += 1
1919 pos += 1
1922
1920
1923 return ret
1921 return ret
1924
1922
1925 def prettyformat(tree):
1923 def prettyformat(tree):
1926 def _prettyformat(tree, level, lines):
1924 def _prettyformat(tree, level, lines):
1927 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1925 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1928 lines.append((level, str(tree)))
1926 lines.append((level, str(tree)))
1929 else:
1927 else:
1930 lines.append((level, '(%s' % tree[0]))
1928 lines.append((level, '(%s' % tree[0]))
1931 for s in tree[1:]:
1929 for s in tree[1:]:
1932 _prettyformat(s, level + 1, lines)
1930 _prettyformat(s, level + 1, lines)
1933 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1931 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1934
1932
1935 lines = []
1933 lines = []
1936 _prettyformat(tree, 0, lines)
1934 _prettyformat(tree, 0, lines)
1937 output = '\n'.join((' '*l + s) for l, s in lines)
1935 output = '\n'.join((' '*l + s) for l, s in lines)
1938 return output
1936 return output
1939
1937
1940 # tell hggettext to extract docstrings from these functions:
1938 # tell hggettext to extract docstrings from these functions:
1941 i18nfunctions = symbols.values()
1939 i18nfunctions = symbols.values()
@@ -1,429 +1,432 b''
1 $ HGFOO=BAR; export HGFOO
1 $ HGFOO=BAR; export HGFOO
2 $ cat >> $HGRCPATH <<EOF
2 $ cat >> $HGRCPATH <<EOF
3 > [extensions]
3 > [extensions]
4 > graphlog=
4 > graphlog=
5 >
5 >
6 > [alias]
6 > [alias]
7 > # should clobber ci but not commit (issue2993)
7 > # should clobber ci but not commit (issue2993)
8 > ci = version
8 > ci = version
9 > myinit = init
9 > myinit = init
10 > optionalrepo = showconfig alias.myinit
10 > optionalrepo = showconfig alias.myinit
11 > cleanstatus = status -c
11 > cleanstatus = status -c
12 > unknown = bargle
12 > unknown = bargle
13 > ambiguous = s
13 > ambiguous = s
14 > recursive = recursive
14 > recursive = recursive
15 > nodefinition =
15 > nodefinition =
16 > no--cwd = status --cwd elsewhere
16 > no--cwd = status --cwd elsewhere
17 > no-R = status -R elsewhere
17 > no-R = status -R elsewhere
18 > no--repo = status --repo elsewhere
18 > no--repo = status --repo elsewhere
19 > no--repository = status --repository elsewhere
19 > no--repository = status --repository elsewhere
20 > no--config = status --config a.config=1
20 > no--config = status --config a.config=1
21 > mylog = log
21 > mylog = log
22 > lognull = log -r null
22 > lognull = log -r null
23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
25 > dln = lognull --debug
25 > dln = lognull --debug
26 > nousage = rollback
26 > nousage = rollback
27 > put = export -r 0 -o "\$FOO/%R.diff"
27 > put = export -r 0 -o "\$FOO/%R.diff"
28 > blank = !printf '\n'
28 > blank = !printf '\n'
29 > self = !printf '\$0\n'
29 > self = !printf '\$0\n'
30 > echoall = !printf '\$@\n'
30 > echoall = !printf '\$@\n'
31 > echo1 = !printf '\$1\n'
31 > echo1 = !printf '\$1\n'
32 > echo2 = !printf '\$2\n'
32 > echo2 = !printf '\$2\n'
33 > echo13 = !printf '\$1 \$3\n'
33 > echo13 = !printf '\$1 \$3\n'
34 > count = !hg log -r "\$@" --template=. | wc -c | sed -e 's/ //g'
34 > count = !hg log -r "\$@" --template=. | wc -c | sed -e 's/ //g'
35 > mcount = !hg log \$@ --template=. | wc -c | sed -e 's/ //g'
35 > mcount = !hg log \$@ --template=. | wc -c | sed -e 's/ //g'
36 > rt = root
36 > rt = root
37 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
37 > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n"
38 > idalias = id
38 > idalias = id
39 > idaliaslong = id
39 > idaliaslong = id
40 > idaliasshell = !echo test
40 > idaliasshell = !echo test
41 > parentsshell1 = !echo one
41 > parentsshell1 = !echo one
42 > parentsshell2 = !echo two
42 > parentsshell2 = !echo two
43 > escaped1 = !printf 'test\$\$test\n'
43 > escaped1 = !printf 'test\$\$test\n'
44 > escaped2 = !sh -c 'echo "HGFOO is \$\$HGFOO"'
44 > escaped2 = !sh -c 'echo "HGFOO is \$\$HGFOO"'
45 > escaped3 = !sh -c 'echo "\$1 is \$\$\$1"'
45 > escaped3 = !sh -c 'echo "\$1 is \$\$\$1"'
46 > escaped4 = !printf '\$\$0 \$\$@\n'
46 > escaped4 = !printf '\$\$0 \$\$@\n'
47 >
47 >
48 > [defaults]
48 > [defaults]
49 > mylog = -q
49 > mylog = -q
50 > lognull = -q
50 > lognull = -q
51 > log = -v
51 > log = -v
52 > EOF
52 > EOF
53
53
54
54
55 basic
55 basic
56
56
57 $ hg myinit alias
57 $ hg myinit alias
58
58
59
59
60 unknown
60 unknown
61
61
62 $ hg unknown
62 $ hg unknown
63 alias 'unknown' resolves to unknown command 'bargle'
63 alias 'unknown' resolves to unknown command 'bargle'
64 $ hg help unknown
64 $ hg help unknown
65 alias 'unknown' resolves to unknown command 'bargle'
65 alias 'unknown' resolves to unknown command 'bargle'
66
66
67
67
68 ambiguous
68 ambiguous
69
69
70 $ hg ambiguous
70 $ hg ambiguous
71 alias 'ambiguous' resolves to ambiguous command 's'
71 alias 'ambiguous' resolves to ambiguous command 's'
72 $ hg help ambiguous
72 $ hg help ambiguous
73 alias 'ambiguous' resolves to ambiguous command 's'
73 alias 'ambiguous' resolves to ambiguous command 's'
74
74
75
75
76 recursive
76 recursive
77
77
78 $ hg recursive
78 $ hg recursive
79 alias 'recursive' resolves to unknown command 'recursive'
79 alias 'recursive' resolves to unknown command 'recursive'
80 $ hg help recursive
80 $ hg help recursive
81 alias 'recursive' resolves to unknown command 'recursive'
81 alias 'recursive' resolves to unknown command 'recursive'
82
82
83
83
84 no definition
84 no definition
85
85
86 $ hg nodef
86 $ hg nodef
87 no definition for alias 'nodefinition'
87 no definition for alias 'nodefinition'
88 $ hg help nodef
88 $ hg help nodef
89 no definition for alias 'nodefinition'
89 no definition for alias 'nodefinition'
90
90
91
91
92 invalid options
92 invalid options
93
93
94 $ hg no--cwd
94 $ hg no--cwd
95 error in definition for alias 'no--cwd': --cwd may only be given on the command line
95 error in definition for alias 'no--cwd': --cwd may only be given on the command line
96 $ hg help no--cwd
96 $ hg help no--cwd
97 error in definition for alias 'no--cwd': --cwd may only be given on the command line
97 error in definition for alias 'no--cwd': --cwd may only be given on the command line
98 $ hg no-R
98 $ hg no-R
99 error in definition for alias 'no-R': -R may only be given on the command line
99 error in definition for alias 'no-R': -R may only be given on the command line
100 $ hg help no-R
100 $ hg help no-R
101 error in definition for alias 'no-R': -R may only be given on the command line
101 error in definition for alias 'no-R': -R may only be given on the command line
102 $ hg no--repo
102 $ hg no--repo
103 error in definition for alias 'no--repo': --repo may only be given on the command line
103 error in definition for alias 'no--repo': --repo may only be given on the command line
104 $ hg help no--repo
104 $ hg help no--repo
105 error in definition for alias 'no--repo': --repo may only be given on the command line
105 error in definition for alias 'no--repo': --repo may only be given on the command line
106 $ hg no--repository
106 $ hg no--repository
107 error in definition for alias 'no--repository': --repository may only be given on the command line
107 error in definition for alias 'no--repository': --repository may only be given on the command line
108 $ hg help no--repository
108 $ hg help no--repository
109 error in definition for alias 'no--repository': --repository may only be given on the command line
109 error in definition for alias 'no--repository': --repository may only be given on the command line
110 $ hg no--config
110 $ hg no--config
111 error in definition for alias 'no--config': --config may only be given on the command line
111 error in definition for alias 'no--config': --config may only be given on the command line
112
112
113 optional repository
113 optional repository
114
114
115 #if no-outer-repo
115 #if no-outer-repo
116 $ hg optionalrepo
116 $ hg optionalrepo
117 init
117 init
118 #endif
118 #endif
119 $ cd alias
119 $ cd alias
120 $ cat > .hg/hgrc <<EOF
120 $ cat > .hg/hgrc <<EOF
121 > [alias]
121 > [alias]
122 > myinit = init -q
122 > myinit = init -q
123 > EOF
123 > EOF
124 $ hg optionalrepo
124 $ hg optionalrepo
125 init -q
125 init -q
126
126
127 no usage
127 no usage
128
128
129 $ hg nousage
129 $ hg nousage
130 no rollback information available
130 no rollback information available
131
131
132 $ echo foo > foo
132 $ echo foo > foo
133 $ hg commit -Amfoo
133 $ hg commit -Amfoo
134 adding foo
134 adding foo
135
135
136
136
137 with opts
137 with opts
138
138
139 $ hg cleanst
139 $ hg cleanst
140 C foo
140 C foo
141
141
142
142
143 with opts and whitespace
143 with opts and whitespace
144
144
145 $ hg shortlog
145 $ hg shortlog
146 0 e63c23eaa88a | 1970-01-01 00:00 +0000
146 0 e63c23eaa88a | 1970-01-01 00:00 +0000
147
147
148 positional arguments
148 positional arguments
149
149
150 $ hg positional
150 $ hg positional
151 abort: too few arguments for command alias
151 abort: too few arguments for command alias
152 [255]
152 [255]
153 $ hg positional a
153 $ hg positional a
154 abort: too few arguments for command alias
154 abort: too few arguments for command alias
155 [255]
155 [255]
156 $ hg positional 'node|short' rev
156 $ hg positional 'node|short' rev
157 0 e63c23eaa88a | 1970-01-01 00:00 +0000
157 0 e63c23eaa88a | 1970-01-01 00:00 +0000
158
158
159 interaction with defaults
159 interaction with defaults
160
160
161 $ hg mylog
161 $ hg mylog
162 0:e63c23eaa88a
162 0:e63c23eaa88a
163 $ hg lognull
163 $ hg lognull
164 -1:000000000000
164 -1:000000000000
165
165
166
166
167 properly recursive
167 properly recursive
168
168
169 $ hg dln
169 $ hg dln
170 changeset: -1:0000000000000000000000000000000000000000
170 changeset: -1:0000000000000000000000000000000000000000
171 parent: -1:0000000000000000000000000000000000000000
171 parent: -1:0000000000000000000000000000000000000000
172 parent: -1:0000000000000000000000000000000000000000
172 parent: -1:0000000000000000000000000000000000000000
173 manifest: -1:0000000000000000000000000000000000000000
173 manifest: -1:0000000000000000000000000000000000000000
174 user:
174 user:
175 date: Thu Jan 01 00:00:00 1970 +0000
175 date: Thu Jan 01 00:00:00 1970 +0000
176 extra: branch=default
176 extra: branch=default
177
177
178
178
179
179
180 path expanding
180 path expanding
181
181
182 $ FOO=`pwd` hg put
182 $ FOO=`pwd` hg put
183 $ cat 0.diff
183 $ cat 0.diff
184 # HG changeset patch
184 # HG changeset patch
185 # User test
185 # User test
186 # Date 0 0
186 # Date 0 0
187 # Thu Jan 01 00:00:00 1970 +0000
187 # Thu Jan 01 00:00:00 1970 +0000
188 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
188 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
189 # Parent 0000000000000000000000000000000000000000
189 # Parent 0000000000000000000000000000000000000000
190 foo
190 foo
191
191
192 diff -r 000000000000 -r e63c23eaa88a foo
192 diff -r 000000000000 -r e63c23eaa88a foo
193 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
193 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
194 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
194 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
195 @@ -0,0 +1,1 @@
195 @@ -0,0 +1,1 @@
196 +foo
196 +foo
197
197
198
198
199 simple shell aliases
199 simple shell aliases
200
200
201 $ hg blank
201 $ hg blank
202
202
203 $ hg blank foo
203 $ hg blank foo
204
204
205 $ hg self
205 $ hg self
206 self
206 self
207 $ hg echoall
207 $ hg echoall
208
208
209 $ hg echoall foo
209 $ hg echoall foo
210 foo
210 foo
211 $ hg echoall 'test $2' foo
211 $ hg echoall 'test $2' foo
212 test $2 foo
212 test $2 foo
213 $ hg echo1 foo bar baz
213 $ hg echo1 foo bar baz
214 foo
214 foo
215 $ hg echo2 foo bar baz
215 $ hg echo2 foo bar baz
216 bar
216 bar
217 $ hg echo13 foo bar baz test
217 $ hg echo13 foo bar baz test
218 foo baz
218 foo baz
219 $ hg echo2 foo
219 $ hg echo2 foo
220
220
221 $ echo bar > bar
221 $ echo bar > bar
222 $ hg commit -qA -m bar
222 $ hg commit -qA -m bar
223 $ hg count .
223 $ hg count .
224 1
224 1
225 $ hg count 'branch(default)'
225 $ hg count 'branch(default)'
226 2
226 2
227 $ hg mcount -r '"branch(default)"'
227 $ hg mcount -r '"branch(default)"'
228 2
228 2
229
229
230 $ hg tglog
230 $ hg tglog
231 @ 1:042423737847: 'bar'
231 @ 1:042423737847: 'bar'
232 |
232 |
233 o 0:e63c23eaa88a: 'foo'
233 o 0:e63c23eaa88a: 'foo'
234
234
235
235
236
236
237 shadowing
237 shadowing
238
238
239 $ hg i
239 $ hg i
240 hg: command 'i' is ambiguous:
240 hg: command 'i' is ambiguous:
241 idalias idaliaslong idaliasshell identify import incoming init
241 idalias idaliaslong idaliasshell identify import incoming init
242 [255]
242 [255]
243 $ hg id
243 $ hg id
244 042423737847 tip
244 042423737847 tip
245 $ hg ida
245 $ hg ida
246 hg: command 'ida' is ambiguous:
246 hg: command 'ida' is ambiguous:
247 idalias idaliaslong idaliasshell
247 idalias idaliaslong idaliasshell
248 [255]
248 [255]
249 $ hg idalias
249 $ hg idalias
250 042423737847 tip
250 042423737847 tip
251 $ hg idaliasl
251 $ hg idaliasl
252 042423737847 tip
252 042423737847 tip
253 $ hg idaliass
253 $ hg idaliass
254 test
254 test
255 $ hg parentsshell
255 $ hg parentsshell
256 hg: command 'parentsshell' is ambiguous:
256 hg: command 'parentsshell' is ambiguous:
257 parentsshell1 parentsshell2
257 parentsshell1 parentsshell2
258 [255]
258 [255]
259 $ hg parentsshell1
259 $ hg parentsshell1
260 one
260 one
261 $ hg parentsshell2
261 $ hg parentsshell2
262 two
262 two
263
263
264
264
265 shell aliases with global options
265 shell aliases with global options
266
266
267 $ hg init sub
267 $ hg init sub
268 $ cd sub
268 $ cd sub
269 $ hg count 'branch(default)'
269 $ hg count 'branch(default)'
270 abort: unknown revision 'default'!
270 0
271 0
271 $ hg -v count 'branch(default)'
272 $ hg -v count 'branch(default)'
273 abort: unknown revision 'default'!
272 0
274 0
273 $ hg -R .. count 'branch(default)'
275 $ hg -R .. count 'branch(default)'
276 abort: unknown revision 'default'!
274 0
277 0
275 $ hg --cwd .. count 'branch(default)'
278 $ hg --cwd .. count 'branch(default)'
276 2
279 2
277 $ hg echoall --cwd ..
280 $ hg echoall --cwd ..
278
281
279
282
280
283
281 repo specific shell aliases
284 repo specific shell aliases
282
285
283 $ cat >> .hg/hgrc <<EOF
286 $ cat >> .hg/hgrc <<EOF
284 > [alias]
287 > [alias]
285 > subalias = !echo sub
288 > subalias = !echo sub
286 > EOF
289 > EOF
287 $ cat >> ../.hg/hgrc <<EOF
290 $ cat >> ../.hg/hgrc <<EOF
288 > [alias]
291 > [alias]
289 > mainalias = !echo main
292 > mainalias = !echo main
290 > EOF
293 > EOF
291
294
292
295
293 shell alias defined in current repo
296 shell alias defined in current repo
294
297
295 $ hg subalias
298 $ hg subalias
296 sub
299 sub
297 $ hg --cwd .. subalias > /dev/null
300 $ hg --cwd .. subalias > /dev/null
298 hg: unknown command 'subalias'
301 hg: unknown command 'subalias'
299 [255]
302 [255]
300 $ hg -R .. subalias > /dev/null
303 $ hg -R .. subalias > /dev/null
301 hg: unknown command 'subalias'
304 hg: unknown command 'subalias'
302 [255]
305 [255]
303
306
304
307
305 shell alias defined in other repo
308 shell alias defined in other repo
306
309
307 $ hg mainalias > /dev/null
310 $ hg mainalias > /dev/null
308 hg: unknown command 'mainalias'
311 hg: unknown command 'mainalias'
309 [255]
312 [255]
310 $ hg -R .. mainalias
313 $ hg -R .. mainalias
311 main
314 main
312 $ hg --cwd .. mainalias
315 $ hg --cwd .. mainalias
313 main
316 main
314
317
315
318
316 shell aliases with escaped $ chars
319 shell aliases with escaped $ chars
317
320
318 $ hg escaped1
321 $ hg escaped1
319 test$test
322 test$test
320 $ hg escaped2
323 $ hg escaped2
321 HGFOO is BAR
324 HGFOO is BAR
322 $ hg escaped3 HGFOO
325 $ hg escaped3 HGFOO
323 HGFOO is BAR
326 HGFOO is BAR
324 $ hg escaped4 test
327 $ hg escaped4 test
325 $0 $@
328 $0 $@
326
329
327
330
328 invalid arguments
331 invalid arguments
329
332
330 $ hg rt foo
333 $ hg rt foo
331 hg rt: invalid arguments
334 hg rt: invalid arguments
332 hg rt
335 hg rt
333
336
334 alias for: hg root
337 alias for: hg root
335
338
336 use "hg help rt" to show the full help text
339 use "hg help rt" to show the full help text
337 [255]
340 [255]
338
341
339 invalid global arguments for normal commands, aliases, and shell aliases
342 invalid global arguments for normal commands, aliases, and shell aliases
340
343
341 $ hg --invalid root
344 $ hg --invalid root
342 hg: option --invalid not recognized
345 hg: option --invalid not recognized
343 Mercurial Distributed SCM
346 Mercurial Distributed SCM
344
347
345 basic commands:
348 basic commands:
346
349
347 add add the specified files on the next commit
350 add add the specified files on the next commit
348 annotate show changeset information by line for each file
351 annotate show changeset information by line for each file
349 clone make a copy of an existing repository
352 clone make a copy of an existing repository
350 commit commit the specified files or all outstanding changes
353 commit commit the specified files or all outstanding changes
351 diff diff repository (or selected files)
354 diff diff repository (or selected files)
352 export dump the header and diffs for one or more changesets
355 export dump the header and diffs for one or more changesets
353 forget forget the specified files on the next commit
356 forget forget the specified files on the next commit
354 init create a new repository in the given directory
357 init create a new repository in the given directory
355 log show revision history of entire repository or files
358 log show revision history of entire repository or files
356 merge merge working directory with another revision
359 merge merge working directory with another revision
357 pull pull changes from the specified source
360 pull pull changes from the specified source
358 push push changes to the specified destination
361 push push changes to the specified destination
359 remove remove the specified files on the next commit
362 remove remove the specified files on the next commit
360 serve start stand-alone webserver
363 serve start stand-alone webserver
361 status show changed files in the working directory
364 status show changed files in the working directory
362 summary summarize working directory state
365 summary summarize working directory state
363 update update working directory (or switch revisions)
366 update update working directory (or switch revisions)
364
367
365 use "hg help" for the full list of commands or "hg -v" for details
368 use "hg help" for the full list of commands or "hg -v" for details
366 [255]
369 [255]
367 $ hg --invalid mylog
370 $ hg --invalid mylog
368 hg: option --invalid not recognized
371 hg: option --invalid not recognized
369 Mercurial Distributed SCM
372 Mercurial Distributed SCM
370
373
371 basic commands:
374 basic commands:
372
375
373 add add the specified files on the next commit
376 add add the specified files on the next commit
374 annotate show changeset information by line for each file
377 annotate show changeset information by line for each file
375 clone make a copy of an existing repository
378 clone make a copy of an existing repository
376 commit commit the specified files or all outstanding changes
379 commit commit the specified files or all outstanding changes
377 diff diff repository (or selected files)
380 diff diff repository (or selected files)
378 export dump the header and diffs for one or more changesets
381 export dump the header and diffs for one or more changesets
379 forget forget the specified files on the next commit
382 forget forget the specified files on the next commit
380 init create a new repository in the given directory
383 init create a new repository in the given directory
381 log show revision history of entire repository or files
384 log show revision history of entire repository or files
382 merge merge working directory with another revision
385 merge merge working directory with another revision
383 pull pull changes from the specified source
386 pull pull changes from the specified source
384 push push changes to the specified destination
387 push push changes to the specified destination
385 remove remove the specified files on the next commit
388 remove remove the specified files on the next commit
386 serve start stand-alone webserver
389 serve start stand-alone webserver
387 status show changed files in the working directory
390 status show changed files in the working directory
388 summary summarize working directory state
391 summary summarize working directory state
389 update update working directory (or switch revisions)
392 update update working directory (or switch revisions)
390
393
391 use "hg help" for the full list of commands or "hg -v" for details
394 use "hg help" for the full list of commands or "hg -v" for details
392 [255]
395 [255]
393 $ hg --invalid blank
396 $ hg --invalid blank
394 hg: option --invalid not recognized
397 hg: option --invalid not recognized
395 Mercurial Distributed SCM
398 Mercurial Distributed SCM
396
399
397 basic commands:
400 basic commands:
398
401
399 add add the specified files on the next commit
402 add add the specified files on the next commit
400 annotate show changeset information by line for each file
403 annotate show changeset information by line for each file
401 clone make a copy of an existing repository
404 clone make a copy of an existing repository
402 commit commit the specified files or all outstanding changes
405 commit commit the specified files or all outstanding changes
403 diff diff repository (or selected files)
406 diff diff repository (or selected files)
404 export dump the header and diffs for one or more changesets
407 export dump the header and diffs for one or more changesets
405 forget forget the specified files on the next commit
408 forget forget the specified files on the next commit
406 init create a new repository in the given directory
409 init create a new repository in the given directory
407 log show revision history of entire repository or files
410 log show revision history of entire repository or files
408 merge merge working directory with another revision
411 merge merge working directory with another revision
409 pull pull changes from the specified source
412 pull pull changes from the specified source
410 push push changes to the specified destination
413 push push changes to the specified destination
411 remove remove the specified files on the next commit
414 remove remove the specified files on the next commit
412 serve start stand-alone webserver
415 serve start stand-alone webserver
413 status show changed files in the working directory
416 status show changed files in the working directory
414 summary summarize working directory state
417 summary summarize working directory state
415 update update working directory (or switch revisions)
418 update update working directory (or switch revisions)
416
419
417 use "hg help" for the full list of commands or "hg -v" for details
420 use "hg help" for the full list of commands or "hg -v" for details
418 [255]
421 [255]
419
422
420 This should show id:
423 This should show id:
421
424
422 $ hg --config alias.log='id' log
425 $ hg --config alias.log='id' log
423 000000000000 tip
426 000000000000 tip
424
427
425 This shouldn't:
428 This shouldn't:
426
429
427 $ hg --config alias.log='id' history
430 $ hg --config alias.log='id' history
428
431
429 $ cd ../..
432 $ cd ../..
@@ -1,2125 +1,2126 b''
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71
71
72 $ commit()
72 $ commit()
73 > {
73 > {
74 > rev=$1
74 > rev=$1
75 > msg=$2
75 > msg=$2
76 > shift 2
76 > shift 2
77 > if [ "$#" -gt 0 ]; then
77 > if [ "$#" -gt 0 ]; then
78 > hg debugsetparents "$@"
78 > hg debugsetparents "$@"
79 > fi
79 > fi
80 > echo $rev > a
80 > echo $rev > a
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revset, commands, cmdutil
85 > from mercurial import extensions, revset, commands, cmdutil
86 >
86 >
87 > def uisetup(ui):
87 > def uisetup(ui):
88 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
89 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 > if expr:
91 > if expr:
92 > tree = revset.parse(expr)[0]
92 > tree = revset.parse(expr)[0]
93 > else:
93 > else:
94 > tree = []
94 > tree = []
95 > ui.write('%r\n' % (opts.get('rev', []),))
95 > ui.write('%r\n' % (opts.get('rev', []),))
96 > ui.write(revset.prettyformat(tree) + '\n')
96 > ui.write(revset.prettyformat(tree) + '\n')
97 > return 0
97 > return 0
98 > return orig(ui, repo, *pats, **opts)
98 > return orig(ui, repo, *pats, **opts)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 > entry[1].append(('', 'print-revset', False,
100 > entry[1].append(('', 'print-revset', False,
101 > 'print generated revset and exit (DEPRECATED)'))
101 > 'print generated revset and exit (DEPRECATED)'))
102 > EOF
102 > EOF
103
103
104 $ echo "[extensions]" >> $HGRCPATH
104 $ echo "[extensions]" >> $HGRCPATH
105 $ echo "graphlog=" >> $HGRCPATH
105 $ echo "graphlog=" >> $HGRCPATH
106 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
107
107
108 $ hg init repo
108 $ hg init repo
109 $ cd repo
109 $ cd repo
110
110
111 Empty repo:
111 Empty repo:
112
112
113 $ hg glog
113 $ hg glog
114
114
115
115
116 Building DAG:
116 Building DAG:
117
117
118 $ commit 0 "root"
118 $ commit 0 "root"
119 $ commit 1 "collapse" 0
119 $ commit 1 "collapse" 0
120 $ commit 2 "collapse" 1
120 $ commit 2 "collapse" 1
121 $ commit 3 "collapse" 2
121 $ commit 3 "collapse" 2
122 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
123 $ commit 5 "expand" 3 4
123 $ commit 5 "expand" 3 4
124 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 $ commit 6 "merge two known; one immediate left, one far left" 2 5
125 $ commit 7 "expand" 2 5
125 $ commit 7 "expand" 2 5
126 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 $ commit 8 "merge two known; one immediate left, one far right" 0 7
127 $ commit 9 "expand" 7 8
127 $ commit 9 "expand" 7 8
128 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 $ commit 10 "merge two known; one immediate left, one near right" 0 6
129 $ commit 11 "expand" 6 10
129 $ commit 11 "expand" 6 10
130 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 $ commit 12 "merge two known; one immediate right, one far left" 1 9
131 $ commit 13 "expand" 9 11
131 $ commit 13 "expand" 9 11
132 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 $ commit 14 "merge two known; one immediate right, one far right" 0 12
133 $ commit 15 "expand" 13 14
133 $ commit 15 "expand" 13 14
134 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 $ commit 16 "merge two known; one immediate right, one near right" 0 1
135 $ commit 17 "expand" 12 16
135 $ commit 17 "expand" 12 16
136 $ commit 18 "merge two known; two far left" 1 15
136 $ commit 18 "merge two known; two far left" 1 15
137 $ commit 19 "expand" 15 17
137 $ commit 19 "expand" 15 17
138 $ commit 20 "merge two known; two far right" 0 18
138 $ commit 20 "merge two known; two far right" 0 18
139 $ commit 21 "expand" 19 20
139 $ commit 21 "expand" 19 20
140 $ commit 22 "merge two known; one far left, one far right" 18 21
140 $ commit 22 "merge two known; one far left, one far right" 18 21
141 $ commit 23 "merge one known; immediate left" 1 22
141 $ commit 23 "merge one known; immediate left" 1 22
142 $ commit 24 "merge one known; immediate right" 0 23
142 $ commit 24 "merge one known; immediate right" 0 23
143 $ commit 25 "merge one known; far left" 21 24
143 $ commit 25 "merge one known; far left" 21 24
144 $ commit 26 "merge one known; far right" 18 25
144 $ commit 26 "merge one known; far right" 18 25
145 $ commit 27 "collapse" 21
145 $ commit 27 "collapse" 21
146 $ commit 28 "merge zero known" 1 26
146 $ commit 28 "merge zero known" 1 26
147 $ commit 29 "regular commit" 0
147 $ commit 29 "regular commit" 0
148 $ commit 30 "expand" 28 29
148 $ commit 30 "expand" 28 29
149 $ commit 31 "expand" 21 30
149 $ commit 31 "expand" 21 30
150 $ commit 32 "expand" 27 31
150 $ commit 32 "expand" 27 31
151 $ commit 33 "head" 18
151 $ commit 33 "head" 18
152 $ commit 34 "head" 32
152 $ commit 34 "head" 32
153
153
154
154
155 $ hg glog -q
155 $ hg glog -q
156 @ 34:fea3ac5810e0
156 @ 34:fea3ac5810e0
157 |
157 |
158 | o 33:68608f5145f9
158 | o 33:68608f5145f9
159 | |
159 | |
160 o | 32:d06dffa21a31
160 o | 32:d06dffa21a31
161 |\ \
161 |\ \
162 | o \ 31:621d83e11f67
162 | o \ 31:621d83e11f67
163 | |\ \
163 | |\ \
164 | | o \ 30:6e11cd4b648f
164 | | o \ 30:6e11cd4b648f
165 | | |\ \
165 | | |\ \
166 | | | o | 29:cd9bb2be7593
166 | | | o | 29:cd9bb2be7593
167 | | | | |
167 | | | | |
168 | | o | | 28:44ecd0b9ae99
168 | | o | | 28:44ecd0b9ae99
169 | | |\ \ \
169 | | |\ \ \
170 o | | | | | 27:886ed638191b
170 o | | | | | 27:886ed638191b
171 |/ / / / /
171 |/ / / / /
172 | | o---+ 26:7f25b6c2f0b9
172 | | o---+ 26:7f25b6c2f0b9
173 | | | | |
173 | | | | |
174 +---o | | 25:91da8ed57247
174 +---o | | 25:91da8ed57247
175 | | | | |
175 | | | | |
176 | | o | | 24:a9c19a3d96b7
176 | | o | | 24:a9c19a3d96b7
177 | | |\| |
177 | | |\| |
178 | | o | | 23:a01cddf0766d
178 | | o | | 23:a01cddf0766d
179 | |/| | |
179 | |/| | |
180 +---o---+ 22:e0d9cccacb5d
180 +---o---+ 22:e0d9cccacb5d
181 | | / /
181 | | / /
182 o | | | 21:d42a756af44d
182 o | | | 21:d42a756af44d
183 |\ \ \ \
183 |\ \ \ \
184 | o---+-+ 20:d30ed6450e32
184 | o---+-+ 20:d30ed6450e32
185 | / / /
185 | / / /
186 o | | | 19:31ddc2c1573b
186 o | | | 19:31ddc2c1573b
187 |\ \ \ \
187 |\ \ \ \
188 +---+---o 18:1aa84d96232a
188 +---+---o 18:1aa84d96232a
189 | | | |
189 | | | |
190 | o | | 17:44765d7c06e0
190 | o | | 17:44765d7c06e0
191 | |\ \ \
191 | |\ \ \
192 | | o---+ 16:3677d192927d
192 | | o---+ 16:3677d192927d
193 | | |/ /
193 | | |/ /
194 o | | | 15:1dda3f72782d
194 o | | | 15:1dda3f72782d
195 |\ \ \ \
195 |\ \ \ \
196 | o-----+ 14:8eac370358ef
196 | o-----+ 14:8eac370358ef
197 | |/ / /
197 | |/ / /
198 o | | | 13:22d8966a97e3
198 o | | | 13:22d8966a97e3
199 |\ \ \ \
199 |\ \ \ \
200 +---o | | 12:86b91144a6e9
200 +---o | | 12:86b91144a6e9
201 | | |/ /
201 | | |/ /
202 | o | | 11:832d76e6bdf2
202 | o | | 11:832d76e6bdf2
203 | |\ \ \
203 | |\ \ \
204 | | o---+ 10:74c64d036d72
204 | | o---+ 10:74c64d036d72
205 | |/ / /
205 | |/ / /
206 o | | | 9:7010c0af0a35
206 o | | | 9:7010c0af0a35
207 |\ \ \ \
207 |\ \ \ \
208 | o-----+ 8:7a0b11f71937
208 | o-----+ 8:7a0b11f71937
209 |/ / / /
209 |/ / / /
210 o | | | 7:b632bb1b1224
210 o | | | 7:b632bb1b1224
211 |\ \ \ \
211 |\ \ \ \
212 +---o | | 6:b105a072e251
212 +---o | | 6:b105a072e251
213 | |/ / /
213 | |/ / /
214 | o | | 5:4409d547b708
214 | o | | 5:4409d547b708
215 | |\ \ \
215 | |\ \ \
216 | | o | | 4:26a8bac39d9f
216 | | o | | 4:26a8bac39d9f
217 | |/|/ /
217 | |/|/ /
218 | o / / 3:27eef8ed80b4
218 | o / / 3:27eef8ed80b4
219 |/ / /
219 |/ / /
220 o / / 2:3d9a33b8d1e1
220 o / / 2:3d9a33b8d1e1
221 |/ /
221 |/ /
222 o / 1:6db2ef61d156
222 o / 1:6db2ef61d156
223 |/
223 |/
224 o 0:e6eb3150255d
224 o 0:e6eb3150255d
225
225
226
226
227 $ hg glog
227 $ hg glog
228 @ changeset: 34:fea3ac5810e0
228 @ changeset: 34:fea3ac5810e0
229 | tag: tip
229 | tag: tip
230 | parent: 32:d06dffa21a31
230 | parent: 32:d06dffa21a31
231 | user: test
231 | user: test
232 | date: Thu Jan 01 00:00:34 1970 +0000
232 | date: Thu Jan 01 00:00:34 1970 +0000
233 | summary: (34) head
233 | summary: (34) head
234 |
234 |
235 | o changeset: 33:68608f5145f9
235 | o changeset: 33:68608f5145f9
236 | | parent: 18:1aa84d96232a
236 | | parent: 18:1aa84d96232a
237 | | user: test
237 | | user: test
238 | | date: Thu Jan 01 00:00:33 1970 +0000
238 | | date: Thu Jan 01 00:00:33 1970 +0000
239 | | summary: (33) head
239 | | summary: (33) head
240 | |
240 | |
241 o | changeset: 32:d06dffa21a31
241 o | changeset: 32:d06dffa21a31
242 |\ \ parent: 27:886ed638191b
242 |\ \ parent: 27:886ed638191b
243 | | | parent: 31:621d83e11f67
243 | | | parent: 31:621d83e11f67
244 | | | user: test
244 | | | user: test
245 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 | | | date: Thu Jan 01 00:00:32 1970 +0000
246 | | | summary: (32) expand
246 | | | summary: (32) expand
247 | | |
247 | | |
248 | o | changeset: 31:621d83e11f67
248 | o | changeset: 31:621d83e11f67
249 | |\ \ parent: 21:d42a756af44d
249 | |\ \ parent: 21:d42a756af44d
250 | | | | parent: 30:6e11cd4b648f
250 | | | | parent: 30:6e11cd4b648f
251 | | | | user: test
251 | | | | user: test
252 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 | | | | date: Thu Jan 01 00:00:31 1970 +0000
253 | | | | summary: (31) expand
253 | | | | summary: (31) expand
254 | | | |
254 | | | |
255 | | o | changeset: 30:6e11cd4b648f
255 | | o | changeset: 30:6e11cd4b648f
256 | | |\ \ parent: 28:44ecd0b9ae99
256 | | |\ \ parent: 28:44ecd0b9ae99
257 | | | | | parent: 29:cd9bb2be7593
257 | | | | | parent: 29:cd9bb2be7593
258 | | | | | user: test
258 | | | | | user: test
259 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
260 | | | | | summary: (30) expand
260 | | | | | summary: (30) expand
261 | | | | |
261 | | | | |
262 | | | o | changeset: 29:cd9bb2be7593
262 | | | o | changeset: 29:cd9bb2be7593
263 | | | | | parent: 0:e6eb3150255d
263 | | | | | parent: 0:e6eb3150255d
264 | | | | | user: test
264 | | | | | user: test
265 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
266 | | | | | summary: (29) regular commit
266 | | | | | summary: (29) regular commit
267 | | | | |
267 | | | | |
268 | | o | | changeset: 28:44ecd0b9ae99
268 | | o | | changeset: 28:44ecd0b9ae99
269 | | |\ \ \ parent: 1:6db2ef61d156
269 | | |\ \ \ parent: 1:6db2ef61d156
270 | | | | | | parent: 26:7f25b6c2f0b9
270 | | | | | | parent: 26:7f25b6c2f0b9
271 | | | | | | user: test
271 | | | | | | user: test
272 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
273 | | | | | | summary: (28) merge zero known
273 | | | | | | summary: (28) merge zero known
274 | | | | | |
274 | | | | | |
275 o | | | | | changeset: 27:886ed638191b
275 o | | | | | changeset: 27:886ed638191b
276 |/ / / / / parent: 21:d42a756af44d
276 |/ / / / / parent: 21:d42a756af44d
277 | | | | | user: test
277 | | | | | user: test
278 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
279 | | | | | summary: (27) collapse
279 | | | | | summary: (27) collapse
280 | | | | |
280 | | | | |
281 | | o---+ changeset: 26:7f25b6c2f0b9
281 | | o---+ changeset: 26:7f25b6c2f0b9
282 | | | | | parent: 18:1aa84d96232a
282 | | | | | parent: 18:1aa84d96232a
283 | | | | | parent: 25:91da8ed57247
283 | | | | | parent: 25:91da8ed57247
284 | | | | | user: test
284 | | | | | user: test
285 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
286 | | | | | summary: (26) merge one known; far right
286 | | | | | summary: (26) merge one known; far right
287 | | | | |
287 | | | | |
288 +---o | | changeset: 25:91da8ed57247
288 +---o | | changeset: 25:91da8ed57247
289 | | | | | parent: 21:d42a756af44d
289 | | | | | parent: 21:d42a756af44d
290 | | | | | parent: 24:a9c19a3d96b7
290 | | | | | parent: 24:a9c19a3d96b7
291 | | | | | user: test
291 | | | | | user: test
292 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
293 | | | | | summary: (25) merge one known; far left
293 | | | | | summary: (25) merge one known; far left
294 | | | | |
294 | | | | |
295 | | o | | changeset: 24:a9c19a3d96b7
295 | | o | | changeset: 24:a9c19a3d96b7
296 | | |\| | parent: 0:e6eb3150255d
296 | | |\| | parent: 0:e6eb3150255d
297 | | | | | parent: 23:a01cddf0766d
297 | | | | | parent: 23:a01cddf0766d
298 | | | | | user: test
298 | | | | | user: test
299 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
300 | | | | | summary: (24) merge one known; immediate right
300 | | | | | summary: (24) merge one known; immediate right
301 | | | | |
301 | | | | |
302 | | o | | changeset: 23:a01cddf0766d
302 | | o | | changeset: 23:a01cddf0766d
303 | |/| | | parent: 1:6db2ef61d156
303 | |/| | | parent: 1:6db2ef61d156
304 | | | | | parent: 22:e0d9cccacb5d
304 | | | | | parent: 22:e0d9cccacb5d
305 | | | | | user: test
305 | | | | | user: test
306 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
307 | | | | | summary: (23) merge one known; immediate left
307 | | | | | summary: (23) merge one known; immediate left
308 | | | | |
308 | | | | |
309 +---o---+ changeset: 22:e0d9cccacb5d
309 +---o---+ changeset: 22:e0d9cccacb5d
310 | | | | parent: 18:1aa84d96232a
310 | | | | parent: 18:1aa84d96232a
311 | | / / parent: 21:d42a756af44d
311 | | / / parent: 21:d42a756af44d
312 | | | | user: test
312 | | | | user: test
313 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 | | | | date: Thu Jan 01 00:00:22 1970 +0000
314 | | | | summary: (22) merge two known; one far left, one far right
314 | | | | summary: (22) merge two known; one far left, one far right
315 | | | |
315 | | | |
316 o | | | changeset: 21:d42a756af44d
316 o | | | changeset: 21:d42a756af44d
317 |\ \ \ \ parent: 19:31ddc2c1573b
317 |\ \ \ \ parent: 19:31ddc2c1573b
318 | | | | | parent: 20:d30ed6450e32
318 | | | | | parent: 20:d30ed6450e32
319 | | | | | user: test
319 | | | | | user: test
320 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
321 | | | | | summary: (21) expand
321 | | | | | summary: (21) expand
322 | | | | |
322 | | | | |
323 | o---+-+ changeset: 20:d30ed6450e32
323 | o---+-+ changeset: 20:d30ed6450e32
324 | | | | parent: 0:e6eb3150255d
324 | | | | parent: 0:e6eb3150255d
325 | / / / parent: 18:1aa84d96232a
325 | / / / parent: 18:1aa84d96232a
326 | | | | user: test
326 | | | | user: test
327 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 | | | | date: Thu Jan 01 00:00:20 1970 +0000
328 | | | | summary: (20) merge two known; two far right
328 | | | | summary: (20) merge two known; two far right
329 | | | |
329 | | | |
330 o | | | changeset: 19:31ddc2c1573b
330 o | | | changeset: 19:31ddc2c1573b
331 |\ \ \ \ parent: 15:1dda3f72782d
331 |\ \ \ \ parent: 15:1dda3f72782d
332 | | | | | parent: 17:44765d7c06e0
332 | | | | | parent: 17:44765d7c06e0
333 | | | | | user: test
333 | | | | | user: test
334 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
335 | | | | | summary: (19) expand
335 | | | | | summary: (19) expand
336 | | | | |
336 | | | | |
337 +---+---o changeset: 18:1aa84d96232a
337 +---+---o changeset: 18:1aa84d96232a
338 | | | | parent: 1:6db2ef61d156
338 | | | | parent: 1:6db2ef61d156
339 | | | | parent: 15:1dda3f72782d
339 | | | | parent: 15:1dda3f72782d
340 | | | | user: test
340 | | | | user: test
341 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 | | | | date: Thu Jan 01 00:00:18 1970 +0000
342 | | | | summary: (18) merge two known; two far left
342 | | | | summary: (18) merge two known; two far left
343 | | | |
343 | | | |
344 | o | | changeset: 17:44765d7c06e0
344 | o | | changeset: 17:44765d7c06e0
345 | |\ \ \ parent: 12:86b91144a6e9
345 | |\ \ \ parent: 12:86b91144a6e9
346 | | | | | parent: 16:3677d192927d
346 | | | | | parent: 16:3677d192927d
347 | | | | | user: test
347 | | | | | user: test
348 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
349 | | | | | summary: (17) expand
349 | | | | | summary: (17) expand
350 | | | | |
350 | | | | |
351 | | o---+ changeset: 16:3677d192927d
351 | | o---+ changeset: 16:3677d192927d
352 | | | | | parent: 0:e6eb3150255d
352 | | | | | parent: 0:e6eb3150255d
353 | | |/ / parent: 1:6db2ef61d156
353 | | |/ / parent: 1:6db2ef61d156
354 | | | | user: test
354 | | | | user: test
355 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 | | | | date: Thu Jan 01 00:00:16 1970 +0000
356 | | | | summary: (16) merge two known; one immediate right, one near right
356 | | | | summary: (16) merge two known; one immediate right, one near right
357 | | | |
357 | | | |
358 o | | | changeset: 15:1dda3f72782d
358 o | | | changeset: 15:1dda3f72782d
359 |\ \ \ \ parent: 13:22d8966a97e3
359 |\ \ \ \ parent: 13:22d8966a97e3
360 | | | | | parent: 14:8eac370358ef
360 | | | | | parent: 14:8eac370358ef
361 | | | | | user: test
361 | | | | | user: test
362 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
363 | | | | | summary: (15) expand
363 | | | | | summary: (15) expand
364 | | | | |
364 | | | | |
365 | o-----+ changeset: 14:8eac370358ef
365 | o-----+ changeset: 14:8eac370358ef
366 | | | | | parent: 0:e6eb3150255d
366 | | | | | parent: 0:e6eb3150255d
367 | |/ / / parent: 12:86b91144a6e9
367 | |/ / / parent: 12:86b91144a6e9
368 | | | | user: test
368 | | | | user: test
369 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 | | | | date: Thu Jan 01 00:00:14 1970 +0000
370 | | | | summary: (14) merge two known; one immediate right, one far right
370 | | | | summary: (14) merge two known; one immediate right, one far right
371 | | | |
371 | | | |
372 o | | | changeset: 13:22d8966a97e3
372 o | | | changeset: 13:22d8966a97e3
373 |\ \ \ \ parent: 9:7010c0af0a35
373 |\ \ \ \ parent: 9:7010c0af0a35
374 | | | | | parent: 11:832d76e6bdf2
374 | | | | | parent: 11:832d76e6bdf2
375 | | | | | user: test
375 | | | | | user: test
376 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
377 | | | | | summary: (13) expand
377 | | | | | summary: (13) expand
378 | | | | |
378 | | | | |
379 +---o | | changeset: 12:86b91144a6e9
379 +---o | | changeset: 12:86b91144a6e9
380 | | |/ / parent: 1:6db2ef61d156
380 | | |/ / parent: 1:6db2ef61d156
381 | | | | parent: 9:7010c0af0a35
381 | | | | parent: 9:7010c0af0a35
382 | | | | user: test
382 | | | | user: test
383 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 | | | | date: Thu Jan 01 00:00:12 1970 +0000
384 | | | | summary: (12) merge two known; one immediate right, one far left
384 | | | | summary: (12) merge two known; one immediate right, one far left
385 | | | |
385 | | | |
386 | o | | changeset: 11:832d76e6bdf2
386 | o | | changeset: 11:832d76e6bdf2
387 | |\ \ \ parent: 6:b105a072e251
387 | |\ \ \ parent: 6:b105a072e251
388 | | | | | parent: 10:74c64d036d72
388 | | | | | parent: 10:74c64d036d72
389 | | | | | user: test
389 | | | | | user: test
390 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
391 | | | | | summary: (11) expand
391 | | | | | summary: (11) expand
392 | | | | |
392 | | | | |
393 | | o---+ changeset: 10:74c64d036d72
393 | | o---+ changeset: 10:74c64d036d72
394 | | | | | parent: 0:e6eb3150255d
394 | | | | | parent: 0:e6eb3150255d
395 | |/ / / parent: 6:b105a072e251
395 | |/ / / parent: 6:b105a072e251
396 | | | | user: test
396 | | | | user: test
397 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 | | | | date: Thu Jan 01 00:00:10 1970 +0000
398 | | | | summary: (10) merge two known; one immediate left, one near right
398 | | | | summary: (10) merge two known; one immediate left, one near right
399 | | | |
399 | | | |
400 o | | | changeset: 9:7010c0af0a35
400 o | | | changeset: 9:7010c0af0a35
401 |\ \ \ \ parent: 7:b632bb1b1224
401 |\ \ \ \ parent: 7:b632bb1b1224
402 | | | | | parent: 8:7a0b11f71937
402 | | | | | parent: 8:7a0b11f71937
403 | | | | | user: test
403 | | | | | user: test
404 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
405 | | | | | summary: (9) expand
405 | | | | | summary: (9) expand
406 | | | | |
406 | | | | |
407 | o-----+ changeset: 8:7a0b11f71937
407 | o-----+ changeset: 8:7a0b11f71937
408 | | | | | parent: 0:e6eb3150255d
408 | | | | | parent: 0:e6eb3150255d
409 |/ / / / parent: 7:b632bb1b1224
409 |/ / / / parent: 7:b632bb1b1224
410 | | | | user: test
410 | | | | user: test
411 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 | | | | date: Thu Jan 01 00:00:08 1970 +0000
412 | | | | summary: (8) merge two known; one immediate left, one far right
412 | | | | summary: (8) merge two known; one immediate left, one far right
413 | | | |
413 | | | |
414 o | | | changeset: 7:b632bb1b1224
414 o | | | changeset: 7:b632bb1b1224
415 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 |\ \ \ \ parent: 2:3d9a33b8d1e1
416 | | | | | parent: 5:4409d547b708
416 | | | | | parent: 5:4409d547b708
417 | | | | | user: test
417 | | | | | user: test
418 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
419 | | | | | summary: (7) expand
419 | | | | | summary: (7) expand
420 | | | | |
420 | | | | |
421 +---o | | changeset: 6:b105a072e251
421 +---o | | changeset: 6:b105a072e251
422 | |/ / / parent: 2:3d9a33b8d1e1
422 | |/ / / parent: 2:3d9a33b8d1e1
423 | | | | parent: 5:4409d547b708
423 | | | | parent: 5:4409d547b708
424 | | | | user: test
424 | | | | user: test
425 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 | | | | date: Thu Jan 01 00:00:06 1970 +0000
426 | | | | summary: (6) merge two known; one immediate left, one far left
426 | | | | summary: (6) merge two known; one immediate left, one far left
427 | | | |
427 | | | |
428 | o | | changeset: 5:4409d547b708
428 | o | | changeset: 5:4409d547b708
429 | |\ \ \ parent: 3:27eef8ed80b4
429 | |\ \ \ parent: 3:27eef8ed80b4
430 | | | | | parent: 4:26a8bac39d9f
430 | | | | | parent: 4:26a8bac39d9f
431 | | | | | user: test
431 | | | | | user: test
432 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
433 | | | | | summary: (5) expand
433 | | | | | summary: (5) expand
434 | | | | |
434 | | | | |
435 | | o | | changeset: 4:26a8bac39d9f
435 | | o | | changeset: 4:26a8bac39d9f
436 | |/|/ / parent: 1:6db2ef61d156
436 | |/|/ / parent: 1:6db2ef61d156
437 | | | | parent: 3:27eef8ed80b4
437 | | | | parent: 3:27eef8ed80b4
438 | | | | user: test
438 | | | | user: test
439 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 | | | | date: Thu Jan 01 00:00:04 1970 +0000
440 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 | | | | summary: (4) merge two known; one immediate left, one immediate right
441 | | | |
441 | | | |
442 | o | | changeset: 3:27eef8ed80b4
442 | o | | changeset: 3:27eef8ed80b4
443 |/ / / user: test
443 |/ / / user: test
444 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 | | | date: Thu Jan 01 00:00:03 1970 +0000
445 | | | summary: (3) collapse
445 | | | summary: (3) collapse
446 | | |
446 | | |
447 o | | changeset: 2:3d9a33b8d1e1
447 o | | changeset: 2:3d9a33b8d1e1
448 |/ / user: test
448 |/ / user: test
449 | | date: Thu Jan 01 00:00:02 1970 +0000
449 | | date: Thu Jan 01 00:00:02 1970 +0000
450 | | summary: (2) collapse
450 | | summary: (2) collapse
451 | |
451 | |
452 o | changeset: 1:6db2ef61d156
452 o | changeset: 1:6db2ef61d156
453 |/ user: test
453 |/ user: test
454 | date: Thu Jan 01 00:00:01 1970 +0000
454 | date: Thu Jan 01 00:00:01 1970 +0000
455 | summary: (1) collapse
455 | summary: (1) collapse
456 |
456 |
457 o changeset: 0:e6eb3150255d
457 o changeset: 0:e6eb3150255d
458 user: test
458 user: test
459 date: Thu Jan 01 00:00:00 1970 +0000
459 date: Thu Jan 01 00:00:00 1970 +0000
460 summary: (0) root
460 summary: (0) root
461
461
462
462
463 File glog:
463 File glog:
464 $ hg glog a
464 $ hg glog a
465 @ changeset: 34:fea3ac5810e0
465 @ changeset: 34:fea3ac5810e0
466 | tag: tip
466 | tag: tip
467 | parent: 32:d06dffa21a31
467 | parent: 32:d06dffa21a31
468 | user: test
468 | user: test
469 | date: Thu Jan 01 00:00:34 1970 +0000
469 | date: Thu Jan 01 00:00:34 1970 +0000
470 | summary: (34) head
470 | summary: (34) head
471 |
471 |
472 | o changeset: 33:68608f5145f9
472 | o changeset: 33:68608f5145f9
473 | | parent: 18:1aa84d96232a
473 | | parent: 18:1aa84d96232a
474 | | user: test
474 | | user: test
475 | | date: Thu Jan 01 00:00:33 1970 +0000
475 | | date: Thu Jan 01 00:00:33 1970 +0000
476 | | summary: (33) head
476 | | summary: (33) head
477 | |
477 | |
478 o | changeset: 32:d06dffa21a31
478 o | changeset: 32:d06dffa21a31
479 |\ \ parent: 27:886ed638191b
479 |\ \ parent: 27:886ed638191b
480 | | | parent: 31:621d83e11f67
480 | | | parent: 31:621d83e11f67
481 | | | user: test
481 | | | user: test
482 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 | | | date: Thu Jan 01 00:00:32 1970 +0000
483 | | | summary: (32) expand
483 | | | summary: (32) expand
484 | | |
484 | | |
485 | o | changeset: 31:621d83e11f67
485 | o | changeset: 31:621d83e11f67
486 | |\ \ parent: 21:d42a756af44d
486 | |\ \ parent: 21:d42a756af44d
487 | | | | parent: 30:6e11cd4b648f
487 | | | | parent: 30:6e11cd4b648f
488 | | | | user: test
488 | | | | user: test
489 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 | | | | date: Thu Jan 01 00:00:31 1970 +0000
490 | | | | summary: (31) expand
490 | | | | summary: (31) expand
491 | | | |
491 | | | |
492 | | o | changeset: 30:6e11cd4b648f
492 | | o | changeset: 30:6e11cd4b648f
493 | | |\ \ parent: 28:44ecd0b9ae99
493 | | |\ \ parent: 28:44ecd0b9ae99
494 | | | | | parent: 29:cd9bb2be7593
494 | | | | | parent: 29:cd9bb2be7593
495 | | | | | user: test
495 | | | | | user: test
496 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
497 | | | | | summary: (30) expand
497 | | | | | summary: (30) expand
498 | | | | |
498 | | | | |
499 | | | o | changeset: 29:cd9bb2be7593
499 | | | o | changeset: 29:cd9bb2be7593
500 | | | | | parent: 0:e6eb3150255d
500 | | | | | parent: 0:e6eb3150255d
501 | | | | | user: test
501 | | | | | user: test
502 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
503 | | | | | summary: (29) regular commit
503 | | | | | summary: (29) regular commit
504 | | | | |
504 | | | | |
505 | | o | | changeset: 28:44ecd0b9ae99
505 | | o | | changeset: 28:44ecd0b9ae99
506 | | |\ \ \ parent: 1:6db2ef61d156
506 | | |\ \ \ parent: 1:6db2ef61d156
507 | | | | | | parent: 26:7f25b6c2f0b9
507 | | | | | | parent: 26:7f25b6c2f0b9
508 | | | | | | user: test
508 | | | | | | user: test
509 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
510 | | | | | | summary: (28) merge zero known
510 | | | | | | summary: (28) merge zero known
511 | | | | | |
511 | | | | | |
512 o | | | | | changeset: 27:886ed638191b
512 o | | | | | changeset: 27:886ed638191b
513 |/ / / / / parent: 21:d42a756af44d
513 |/ / / / / parent: 21:d42a756af44d
514 | | | | | user: test
514 | | | | | user: test
515 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
516 | | | | | summary: (27) collapse
516 | | | | | summary: (27) collapse
517 | | | | |
517 | | | | |
518 | | o---+ changeset: 26:7f25b6c2f0b9
518 | | o---+ changeset: 26:7f25b6c2f0b9
519 | | | | | parent: 18:1aa84d96232a
519 | | | | | parent: 18:1aa84d96232a
520 | | | | | parent: 25:91da8ed57247
520 | | | | | parent: 25:91da8ed57247
521 | | | | | user: test
521 | | | | | user: test
522 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
523 | | | | | summary: (26) merge one known; far right
523 | | | | | summary: (26) merge one known; far right
524 | | | | |
524 | | | | |
525 +---o | | changeset: 25:91da8ed57247
525 +---o | | changeset: 25:91da8ed57247
526 | | | | | parent: 21:d42a756af44d
526 | | | | | parent: 21:d42a756af44d
527 | | | | | parent: 24:a9c19a3d96b7
527 | | | | | parent: 24:a9c19a3d96b7
528 | | | | | user: test
528 | | | | | user: test
529 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
530 | | | | | summary: (25) merge one known; far left
530 | | | | | summary: (25) merge one known; far left
531 | | | | |
531 | | | | |
532 | | o | | changeset: 24:a9c19a3d96b7
532 | | o | | changeset: 24:a9c19a3d96b7
533 | | |\| | parent: 0:e6eb3150255d
533 | | |\| | parent: 0:e6eb3150255d
534 | | | | | parent: 23:a01cddf0766d
534 | | | | | parent: 23:a01cddf0766d
535 | | | | | user: test
535 | | | | | user: test
536 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
537 | | | | | summary: (24) merge one known; immediate right
537 | | | | | summary: (24) merge one known; immediate right
538 | | | | |
538 | | | | |
539 | | o | | changeset: 23:a01cddf0766d
539 | | o | | changeset: 23:a01cddf0766d
540 | |/| | | parent: 1:6db2ef61d156
540 | |/| | | parent: 1:6db2ef61d156
541 | | | | | parent: 22:e0d9cccacb5d
541 | | | | | parent: 22:e0d9cccacb5d
542 | | | | | user: test
542 | | | | | user: test
543 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
544 | | | | | summary: (23) merge one known; immediate left
544 | | | | | summary: (23) merge one known; immediate left
545 | | | | |
545 | | | | |
546 +---o---+ changeset: 22:e0d9cccacb5d
546 +---o---+ changeset: 22:e0d9cccacb5d
547 | | | | parent: 18:1aa84d96232a
547 | | | | parent: 18:1aa84d96232a
548 | | / / parent: 21:d42a756af44d
548 | | / / parent: 21:d42a756af44d
549 | | | | user: test
549 | | | | user: test
550 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 | | | | date: Thu Jan 01 00:00:22 1970 +0000
551 | | | | summary: (22) merge two known; one far left, one far right
551 | | | | summary: (22) merge two known; one far left, one far right
552 | | | |
552 | | | |
553 o | | | changeset: 21:d42a756af44d
553 o | | | changeset: 21:d42a756af44d
554 |\ \ \ \ parent: 19:31ddc2c1573b
554 |\ \ \ \ parent: 19:31ddc2c1573b
555 | | | | | parent: 20:d30ed6450e32
555 | | | | | parent: 20:d30ed6450e32
556 | | | | | user: test
556 | | | | | user: test
557 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
558 | | | | | summary: (21) expand
558 | | | | | summary: (21) expand
559 | | | | |
559 | | | | |
560 | o---+-+ changeset: 20:d30ed6450e32
560 | o---+-+ changeset: 20:d30ed6450e32
561 | | | | parent: 0:e6eb3150255d
561 | | | | parent: 0:e6eb3150255d
562 | / / / parent: 18:1aa84d96232a
562 | / / / parent: 18:1aa84d96232a
563 | | | | user: test
563 | | | | user: test
564 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 | | | | date: Thu Jan 01 00:00:20 1970 +0000
565 | | | | summary: (20) merge two known; two far right
565 | | | | summary: (20) merge two known; two far right
566 | | | |
566 | | | |
567 o | | | changeset: 19:31ddc2c1573b
567 o | | | changeset: 19:31ddc2c1573b
568 |\ \ \ \ parent: 15:1dda3f72782d
568 |\ \ \ \ parent: 15:1dda3f72782d
569 | | | | | parent: 17:44765d7c06e0
569 | | | | | parent: 17:44765d7c06e0
570 | | | | | user: test
570 | | | | | user: test
571 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
572 | | | | | summary: (19) expand
572 | | | | | summary: (19) expand
573 | | | | |
573 | | | | |
574 +---+---o changeset: 18:1aa84d96232a
574 +---+---o changeset: 18:1aa84d96232a
575 | | | | parent: 1:6db2ef61d156
575 | | | | parent: 1:6db2ef61d156
576 | | | | parent: 15:1dda3f72782d
576 | | | | parent: 15:1dda3f72782d
577 | | | | user: test
577 | | | | user: test
578 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 | | | | date: Thu Jan 01 00:00:18 1970 +0000
579 | | | | summary: (18) merge two known; two far left
579 | | | | summary: (18) merge two known; two far left
580 | | | |
580 | | | |
581 | o | | changeset: 17:44765d7c06e0
581 | o | | changeset: 17:44765d7c06e0
582 | |\ \ \ parent: 12:86b91144a6e9
582 | |\ \ \ parent: 12:86b91144a6e9
583 | | | | | parent: 16:3677d192927d
583 | | | | | parent: 16:3677d192927d
584 | | | | | user: test
584 | | | | | user: test
585 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
586 | | | | | summary: (17) expand
586 | | | | | summary: (17) expand
587 | | | | |
587 | | | | |
588 | | o---+ changeset: 16:3677d192927d
588 | | o---+ changeset: 16:3677d192927d
589 | | | | | parent: 0:e6eb3150255d
589 | | | | | parent: 0:e6eb3150255d
590 | | |/ / parent: 1:6db2ef61d156
590 | | |/ / parent: 1:6db2ef61d156
591 | | | | user: test
591 | | | | user: test
592 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 | | | | date: Thu Jan 01 00:00:16 1970 +0000
593 | | | | summary: (16) merge two known; one immediate right, one near right
593 | | | | summary: (16) merge two known; one immediate right, one near right
594 | | | |
594 | | | |
595 o | | | changeset: 15:1dda3f72782d
595 o | | | changeset: 15:1dda3f72782d
596 |\ \ \ \ parent: 13:22d8966a97e3
596 |\ \ \ \ parent: 13:22d8966a97e3
597 | | | | | parent: 14:8eac370358ef
597 | | | | | parent: 14:8eac370358ef
598 | | | | | user: test
598 | | | | | user: test
599 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
600 | | | | | summary: (15) expand
600 | | | | | summary: (15) expand
601 | | | | |
601 | | | | |
602 | o-----+ changeset: 14:8eac370358ef
602 | o-----+ changeset: 14:8eac370358ef
603 | | | | | parent: 0:e6eb3150255d
603 | | | | | parent: 0:e6eb3150255d
604 | |/ / / parent: 12:86b91144a6e9
604 | |/ / / parent: 12:86b91144a6e9
605 | | | | user: test
605 | | | | user: test
606 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 | | | | date: Thu Jan 01 00:00:14 1970 +0000
607 | | | | summary: (14) merge two known; one immediate right, one far right
607 | | | | summary: (14) merge two known; one immediate right, one far right
608 | | | |
608 | | | |
609 o | | | changeset: 13:22d8966a97e3
609 o | | | changeset: 13:22d8966a97e3
610 |\ \ \ \ parent: 9:7010c0af0a35
610 |\ \ \ \ parent: 9:7010c0af0a35
611 | | | | | parent: 11:832d76e6bdf2
611 | | | | | parent: 11:832d76e6bdf2
612 | | | | | user: test
612 | | | | | user: test
613 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
614 | | | | | summary: (13) expand
614 | | | | | summary: (13) expand
615 | | | | |
615 | | | | |
616 +---o | | changeset: 12:86b91144a6e9
616 +---o | | changeset: 12:86b91144a6e9
617 | | |/ / parent: 1:6db2ef61d156
617 | | |/ / parent: 1:6db2ef61d156
618 | | | | parent: 9:7010c0af0a35
618 | | | | parent: 9:7010c0af0a35
619 | | | | user: test
619 | | | | user: test
620 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 | | | | date: Thu Jan 01 00:00:12 1970 +0000
621 | | | | summary: (12) merge two known; one immediate right, one far left
621 | | | | summary: (12) merge two known; one immediate right, one far left
622 | | | |
622 | | | |
623 | o | | changeset: 11:832d76e6bdf2
623 | o | | changeset: 11:832d76e6bdf2
624 | |\ \ \ parent: 6:b105a072e251
624 | |\ \ \ parent: 6:b105a072e251
625 | | | | | parent: 10:74c64d036d72
625 | | | | | parent: 10:74c64d036d72
626 | | | | | user: test
626 | | | | | user: test
627 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
628 | | | | | summary: (11) expand
628 | | | | | summary: (11) expand
629 | | | | |
629 | | | | |
630 | | o---+ changeset: 10:74c64d036d72
630 | | o---+ changeset: 10:74c64d036d72
631 | | | | | parent: 0:e6eb3150255d
631 | | | | | parent: 0:e6eb3150255d
632 | |/ / / parent: 6:b105a072e251
632 | |/ / / parent: 6:b105a072e251
633 | | | | user: test
633 | | | | user: test
634 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 | | | | date: Thu Jan 01 00:00:10 1970 +0000
635 | | | | summary: (10) merge two known; one immediate left, one near right
635 | | | | summary: (10) merge two known; one immediate left, one near right
636 | | | |
636 | | | |
637 o | | | changeset: 9:7010c0af0a35
637 o | | | changeset: 9:7010c0af0a35
638 |\ \ \ \ parent: 7:b632bb1b1224
638 |\ \ \ \ parent: 7:b632bb1b1224
639 | | | | | parent: 8:7a0b11f71937
639 | | | | | parent: 8:7a0b11f71937
640 | | | | | user: test
640 | | | | | user: test
641 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
642 | | | | | summary: (9) expand
642 | | | | | summary: (9) expand
643 | | | | |
643 | | | | |
644 | o-----+ changeset: 8:7a0b11f71937
644 | o-----+ changeset: 8:7a0b11f71937
645 | | | | | parent: 0:e6eb3150255d
645 | | | | | parent: 0:e6eb3150255d
646 |/ / / / parent: 7:b632bb1b1224
646 |/ / / / parent: 7:b632bb1b1224
647 | | | | user: test
647 | | | | user: test
648 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 | | | | date: Thu Jan 01 00:00:08 1970 +0000
649 | | | | summary: (8) merge two known; one immediate left, one far right
649 | | | | summary: (8) merge two known; one immediate left, one far right
650 | | | |
650 | | | |
651 o | | | changeset: 7:b632bb1b1224
651 o | | | changeset: 7:b632bb1b1224
652 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 |\ \ \ \ parent: 2:3d9a33b8d1e1
653 | | | | | parent: 5:4409d547b708
653 | | | | | parent: 5:4409d547b708
654 | | | | | user: test
654 | | | | | user: test
655 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
656 | | | | | summary: (7) expand
656 | | | | | summary: (7) expand
657 | | | | |
657 | | | | |
658 +---o | | changeset: 6:b105a072e251
658 +---o | | changeset: 6:b105a072e251
659 | |/ / / parent: 2:3d9a33b8d1e1
659 | |/ / / parent: 2:3d9a33b8d1e1
660 | | | | parent: 5:4409d547b708
660 | | | | parent: 5:4409d547b708
661 | | | | user: test
661 | | | | user: test
662 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 | | | | date: Thu Jan 01 00:00:06 1970 +0000
663 | | | | summary: (6) merge two known; one immediate left, one far left
663 | | | | summary: (6) merge two known; one immediate left, one far left
664 | | | |
664 | | | |
665 | o | | changeset: 5:4409d547b708
665 | o | | changeset: 5:4409d547b708
666 | |\ \ \ parent: 3:27eef8ed80b4
666 | |\ \ \ parent: 3:27eef8ed80b4
667 | | | | | parent: 4:26a8bac39d9f
667 | | | | | parent: 4:26a8bac39d9f
668 | | | | | user: test
668 | | | | | user: test
669 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
670 | | | | | summary: (5) expand
670 | | | | | summary: (5) expand
671 | | | | |
671 | | | | |
672 | | o | | changeset: 4:26a8bac39d9f
672 | | o | | changeset: 4:26a8bac39d9f
673 | |/|/ / parent: 1:6db2ef61d156
673 | |/|/ / parent: 1:6db2ef61d156
674 | | | | parent: 3:27eef8ed80b4
674 | | | | parent: 3:27eef8ed80b4
675 | | | | user: test
675 | | | | user: test
676 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 | | | | date: Thu Jan 01 00:00:04 1970 +0000
677 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 | | | | summary: (4) merge two known; one immediate left, one immediate right
678 | | | |
678 | | | |
679 | o | | changeset: 3:27eef8ed80b4
679 | o | | changeset: 3:27eef8ed80b4
680 |/ / / user: test
680 |/ / / user: test
681 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 | | | date: Thu Jan 01 00:00:03 1970 +0000
682 | | | summary: (3) collapse
682 | | | summary: (3) collapse
683 | | |
683 | | |
684 o | | changeset: 2:3d9a33b8d1e1
684 o | | changeset: 2:3d9a33b8d1e1
685 |/ / user: test
685 |/ / user: test
686 | | date: Thu Jan 01 00:00:02 1970 +0000
686 | | date: Thu Jan 01 00:00:02 1970 +0000
687 | | summary: (2) collapse
687 | | summary: (2) collapse
688 | |
688 | |
689 o | changeset: 1:6db2ef61d156
689 o | changeset: 1:6db2ef61d156
690 |/ user: test
690 |/ user: test
691 | date: Thu Jan 01 00:00:01 1970 +0000
691 | date: Thu Jan 01 00:00:01 1970 +0000
692 | summary: (1) collapse
692 | summary: (1) collapse
693 |
693 |
694 o changeset: 0:e6eb3150255d
694 o changeset: 0:e6eb3150255d
695 user: test
695 user: test
696 date: Thu Jan 01 00:00:00 1970 +0000
696 date: Thu Jan 01 00:00:00 1970 +0000
697 summary: (0) root
697 summary: (0) root
698
698
699
699
700 File glog per revset:
700 File glog per revset:
701
701
702 $ hg glog -r 'file("a")'
702 $ hg glog -r 'file("a")'
703 @ changeset: 34:fea3ac5810e0
703 @ changeset: 34:fea3ac5810e0
704 | tag: tip
704 | tag: tip
705 | parent: 32:d06dffa21a31
705 | parent: 32:d06dffa21a31
706 | user: test
706 | user: test
707 | date: Thu Jan 01 00:00:34 1970 +0000
707 | date: Thu Jan 01 00:00:34 1970 +0000
708 | summary: (34) head
708 | summary: (34) head
709 |
709 |
710 | o changeset: 33:68608f5145f9
710 | o changeset: 33:68608f5145f9
711 | | parent: 18:1aa84d96232a
711 | | parent: 18:1aa84d96232a
712 | | user: test
712 | | user: test
713 | | date: Thu Jan 01 00:00:33 1970 +0000
713 | | date: Thu Jan 01 00:00:33 1970 +0000
714 | | summary: (33) head
714 | | summary: (33) head
715 | |
715 | |
716 o | changeset: 32:d06dffa21a31
716 o | changeset: 32:d06dffa21a31
717 |\ \ parent: 27:886ed638191b
717 |\ \ parent: 27:886ed638191b
718 | | | parent: 31:621d83e11f67
718 | | | parent: 31:621d83e11f67
719 | | | user: test
719 | | | user: test
720 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 | | | date: Thu Jan 01 00:00:32 1970 +0000
721 | | | summary: (32) expand
721 | | | summary: (32) expand
722 | | |
722 | | |
723 | o | changeset: 31:621d83e11f67
723 | o | changeset: 31:621d83e11f67
724 | |\ \ parent: 21:d42a756af44d
724 | |\ \ parent: 21:d42a756af44d
725 | | | | parent: 30:6e11cd4b648f
725 | | | | parent: 30:6e11cd4b648f
726 | | | | user: test
726 | | | | user: test
727 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 | | | | date: Thu Jan 01 00:00:31 1970 +0000
728 | | | | summary: (31) expand
728 | | | | summary: (31) expand
729 | | | |
729 | | | |
730 | | o | changeset: 30:6e11cd4b648f
730 | | o | changeset: 30:6e11cd4b648f
731 | | |\ \ parent: 28:44ecd0b9ae99
731 | | |\ \ parent: 28:44ecd0b9ae99
732 | | | | | parent: 29:cd9bb2be7593
732 | | | | | parent: 29:cd9bb2be7593
733 | | | | | user: test
733 | | | | | user: test
734 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
735 | | | | | summary: (30) expand
735 | | | | | summary: (30) expand
736 | | | | |
736 | | | | |
737 | | | o | changeset: 29:cd9bb2be7593
737 | | | o | changeset: 29:cd9bb2be7593
738 | | | | | parent: 0:e6eb3150255d
738 | | | | | parent: 0:e6eb3150255d
739 | | | | | user: test
739 | | | | | user: test
740 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
741 | | | | | summary: (29) regular commit
741 | | | | | summary: (29) regular commit
742 | | | | |
742 | | | | |
743 | | o | | changeset: 28:44ecd0b9ae99
743 | | o | | changeset: 28:44ecd0b9ae99
744 | | |\ \ \ parent: 1:6db2ef61d156
744 | | |\ \ \ parent: 1:6db2ef61d156
745 | | | | | | parent: 26:7f25b6c2f0b9
745 | | | | | | parent: 26:7f25b6c2f0b9
746 | | | | | | user: test
746 | | | | | | user: test
747 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
748 | | | | | | summary: (28) merge zero known
748 | | | | | | summary: (28) merge zero known
749 | | | | | |
749 | | | | | |
750 o | | | | | changeset: 27:886ed638191b
750 o | | | | | changeset: 27:886ed638191b
751 |/ / / / / parent: 21:d42a756af44d
751 |/ / / / / parent: 21:d42a756af44d
752 | | | | | user: test
752 | | | | | user: test
753 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
754 | | | | | summary: (27) collapse
754 | | | | | summary: (27) collapse
755 | | | | |
755 | | | | |
756 | | o---+ changeset: 26:7f25b6c2f0b9
756 | | o---+ changeset: 26:7f25b6c2f0b9
757 | | | | | parent: 18:1aa84d96232a
757 | | | | | parent: 18:1aa84d96232a
758 | | | | | parent: 25:91da8ed57247
758 | | | | | parent: 25:91da8ed57247
759 | | | | | user: test
759 | | | | | user: test
760 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
761 | | | | | summary: (26) merge one known; far right
761 | | | | | summary: (26) merge one known; far right
762 | | | | |
762 | | | | |
763 +---o | | changeset: 25:91da8ed57247
763 +---o | | changeset: 25:91da8ed57247
764 | | | | | parent: 21:d42a756af44d
764 | | | | | parent: 21:d42a756af44d
765 | | | | | parent: 24:a9c19a3d96b7
765 | | | | | parent: 24:a9c19a3d96b7
766 | | | | | user: test
766 | | | | | user: test
767 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
768 | | | | | summary: (25) merge one known; far left
768 | | | | | summary: (25) merge one known; far left
769 | | | | |
769 | | | | |
770 | | o | | changeset: 24:a9c19a3d96b7
770 | | o | | changeset: 24:a9c19a3d96b7
771 | | |\| | parent: 0:e6eb3150255d
771 | | |\| | parent: 0:e6eb3150255d
772 | | | | | parent: 23:a01cddf0766d
772 | | | | | parent: 23:a01cddf0766d
773 | | | | | user: test
773 | | | | | user: test
774 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
775 | | | | | summary: (24) merge one known; immediate right
775 | | | | | summary: (24) merge one known; immediate right
776 | | | | |
776 | | | | |
777 | | o | | changeset: 23:a01cddf0766d
777 | | o | | changeset: 23:a01cddf0766d
778 | |/| | | parent: 1:6db2ef61d156
778 | |/| | | parent: 1:6db2ef61d156
779 | | | | | parent: 22:e0d9cccacb5d
779 | | | | | parent: 22:e0d9cccacb5d
780 | | | | | user: test
780 | | | | | user: test
781 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
782 | | | | | summary: (23) merge one known; immediate left
782 | | | | | summary: (23) merge one known; immediate left
783 | | | | |
783 | | | | |
784 +---o---+ changeset: 22:e0d9cccacb5d
784 +---o---+ changeset: 22:e0d9cccacb5d
785 | | | | parent: 18:1aa84d96232a
785 | | | | parent: 18:1aa84d96232a
786 | | / / parent: 21:d42a756af44d
786 | | / / parent: 21:d42a756af44d
787 | | | | user: test
787 | | | | user: test
788 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 | | | | date: Thu Jan 01 00:00:22 1970 +0000
789 | | | | summary: (22) merge two known; one far left, one far right
789 | | | | summary: (22) merge two known; one far left, one far right
790 | | | |
790 | | | |
791 o | | | changeset: 21:d42a756af44d
791 o | | | changeset: 21:d42a756af44d
792 |\ \ \ \ parent: 19:31ddc2c1573b
792 |\ \ \ \ parent: 19:31ddc2c1573b
793 | | | | | parent: 20:d30ed6450e32
793 | | | | | parent: 20:d30ed6450e32
794 | | | | | user: test
794 | | | | | user: test
795 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
796 | | | | | summary: (21) expand
796 | | | | | summary: (21) expand
797 | | | | |
797 | | | | |
798 | o---+-+ changeset: 20:d30ed6450e32
798 | o---+-+ changeset: 20:d30ed6450e32
799 | | | | parent: 0:e6eb3150255d
799 | | | | parent: 0:e6eb3150255d
800 | / / / parent: 18:1aa84d96232a
800 | / / / parent: 18:1aa84d96232a
801 | | | | user: test
801 | | | | user: test
802 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 | | | | date: Thu Jan 01 00:00:20 1970 +0000
803 | | | | summary: (20) merge two known; two far right
803 | | | | summary: (20) merge two known; two far right
804 | | | |
804 | | | |
805 o | | | changeset: 19:31ddc2c1573b
805 o | | | changeset: 19:31ddc2c1573b
806 |\ \ \ \ parent: 15:1dda3f72782d
806 |\ \ \ \ parent: 15:1dda3f72782d
807 | | | | | parent: 17:44765d7c06e0
807 | | | | | parent: 17:44765d7c06e0
808 | | | | | user: test
808 | | | | | user: test
809 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
810 | | | | | summary: (19) expand
810 | | | | | summary: (19) expand
811 | | | | |
811 | | | | |
812 +---+---o changeset: 18:1aa84d96232a
812 +---+---o changeset: 18:1aa84d96232a
813 | | | | parent: 1:6db2ef61d156
813 | | | | parent: 1:6db2ef61d156
814 | | | | parent: 15:1dda3f72782d
814 | | | | parent: 15:1dda3f72782d
815 | | | | user: test
815 | | | | user: test
816 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 | | | | date: Thu Jan 01 00:00:18 1970 +0000
817 | | | | summary: (18) merge two known; two far left
817 | | | | summary: (18) merge two known; two far left
818 | | | |
818 | | | |
819 | o | | changeset: 17:44765d7c06e0
819 | o | | changeset: 17:44765d7c06e0
820 | |\ \ \ parent: 12:86b91144a6e9
820 | |\ \ \ parent: 12:86b91144a6e9
821 | | | | | parent: 16:3677d192927d
821 | | | | | parent: 16:3677d192927d
822 | | | | | user: test
822 | | | | | user: test
823 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
824 | | | | | summary: (17) expand
824 | | | | | summary: (17) expand
825 | | | | |
825 | | | | |
826 | | o---+ changeset: 16:3677d192927d
826 | | o---+ changeset: 16:3677d192927d
827 | | | | | parent: 0:e6eb3150255d
827 | | | | | parent: 0:e6eb3150255d
828 | | |/ / parent: 1:6db2ef61d156
828 | | |/ / parent: 1:6db2ef61d156
829 | | | | user: test
829 | | | | user: test
830 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 | | | | date: Thu Jan 01 00:00:16 1970 +0000
831 | | | | summary: (16) merge two known; one immediate right, one near right
831 | | | | summary: (16) merge two known; one immediate right, one near right
832 | | | |
832 | | | |
833 o | | | changeset: 15:1dda3f72782d
833 o | | | changeset: 15:1dda3f72782d
834 |\ \ \ \ parent: 13:22d8966a97e3
834 |\ \ \ \ parent: 13:22d8966a97e3
835 | | | | | parent: 14:8eac370358ef
835 | | | | | parent: 14:8eac370358ef
836 | | | | | user: test
836 | | | | | user: test
837 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
838 | | | | | summary: (15) expand
838 | | | | | summary: (15) expand
839 | | | | |
839 | | | | |
840 | o-----+ changeset: 14:8eac370358ef
840 | o-----+ changeset: 14:8eac370358ef
841 | | | | | parent: 0:e6eb3150255d
841 | | | | | parent: 0:e6eb3150255d
842 | |/ / / parent: 12:86b91144a6e9
842 | |/ / / parent: 12:86b91144a6e9
843 | | | | user: test
843 | | | | user: test
844 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 | | | | date: Thu Jan 01 00:00:14 1970 +0000
845 | | | | summary: (14) merge two known; one immediate right, one far right
845 | | | | summary: (14) merge two known; one immediate right, one far right
846 | | | |
846 | | | |
847 o | | | changeset: 13:22d8966a97e3
847 o | | | changeset: 13:22d8966a97e3
848 |\ \ \ \ parent: 9:7010c0af0a35
848 |\ \ \ \ parent: 9:7010c0af0a35
849 | | | | | parent: 11:832d76e6bdf2
849 | | | | | parent: 11:832d76e6bdf2
850 | | | | | user: test
850 | | | | | user: test
851 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
852 | | | | | summary: (13) expand
852 | | | | | summary: (13) expand
853 | | | | |
853 | | | | |
854 +---o | | changeset: 12:86b91144a6e9
854 +---o | | changeset: 12:86b91144a6e9
855 | | |/ / parent: 1:6db2ef61d156
855 | | |/ / parent: 1:6db2ef61d156
856 | | | | parent: 9:7010c0af0a35
856 | | | | parent: 9:7010c0af0a35
857 | | | | user: test
857 | | | | user: test
858 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 | | | | date: Thu Jan 01 00:00:12 1970 +0000
859 | | | | summary: (12) merge two known; one immediate right, one far left
859 | | | | summary: (12) merge two known; one immediate right, one far left
860 | | | |
860 | | | |
861 | o | | changeset: 11:832d76e6bdf2
861 | o | | changeset: 11:832d76e6bdf2
862 | |\ \ \ parent: 6:b105a072e251
862 | |\ \ \ parent: 6:b105a072e251
863 | | | | | parent: 10:74c64d036d72
863 | | | | | parent: 10:74c64d036d72
864 | | | | | user: test
864 | | | | | user: test
865 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
866 | | | | | summary: (11) expand
866 | | | | | summary: (11) expand
867 | | | | |
867 | | | | |
868 | | o---+ changeset: 10:74c64d036d72
868 | | o---+ changeset: 10:74c64d036d72
869 | | | | | parent: 0:e6eb3150255d
869 | | | | | parent: 0:e6eb3150255d
870 | |/ / / parent: 6:b105a072e251
870 | |/ / / parent: 6:b105a072e251
871 | | | | user: test
871 | | | | user: test
872 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 | | | | date: Thu Jan 01 00:00:10 1970 +0000
873 | | | | summary: (10) merge two known; one immediate left, one near right
873 | | | | summary: (10) merge two known; one immediate left, one near right
874 | | | |
874 | | | |
875 o | | | changeset: 9:7010c0af0a35
875 o | | | changeset: 9:7010c0af0a35
876 |\ \ \ \ parent: 7:b632bb1b1224
876 |\ \ \ \ parent: 7:b632bb1b1224
877 | | | | | parent: 8:7a0b11f71937
877 | | | | | parent: 8:7a0b11f71937
878 | | | | | user: test
878 | | | | | user: test
879 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
880 | | | | | summary: (9) expand
880 | | | | | summary: (9) expand
881 | | | | |
881 | | | | |
882 | o-----+ changeset: 8:7a0b11f71937
882 | o-----+ changeset: 8:7a0b11f71937
883 | | | | | parent: 0:e6eb3150255d
883 | | | | | parent: 0:e6eb3150255d
884 |/ / / / parent: 7:b632bb1b1224
884 |/ / / / parent: 7:b632bb1b1224
885 | | | | user: test
885 | | | | user: test
886 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 | | | | date: Thu Jan 01 00:00:08 1970 +0000
887 | | | | summary: (8) merge two known; one immediate left, one far right
887 | | | | summary: (8) merge two known; one immediate left, one far right
888 | | | |
888 | | | |
889 o | | | changeset: 7:b632bb1b1224
889 o | | | changeset: 7:b632bb1b1224
890 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 |\ \ \ \ parent: 2:3d9a33b8d1e1
891 | | | | | parent: 5:4409d547b708
891 | | | | | parent: 5:4409d547b708
892 | | | | | user: test
892 | | | | | user: test
893 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
894 | | | | | summary: (7) expand
894 | | | | | summary: (7) expand
895 | | | | |
895 | | | | |
896 +---o | | changeset: 6:b105a072e251
896 +---o | | changeset: 6:b105a072e251
897 | |/ / / parent: 2:3d9a33b8d1e1
897 | |/ / / parent: 2:3d9a33b8d1e1
898 | | | | parent: 5:4409d547b708
898 | | | | parent: 5:4409d547b708
899 | | | | user: test
899 | | | | user: test
900 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 | | | | date: Thu Jan 01 00:00:06 1970 +0000
901 | | | | summary: (6) merge two known; one immediate left, one far left
901 | | | | summary: (6) merge two known; one immediate left, one far left
902 | | | |
902 | | | |
903 | o | | changeset: 5:4409d547b708
903 | o | | changeset: 5:4409d547b708
904 | |\ \ \ parent: 3:27eef8ed80b4
904 | |\ \ \ parent: 3:27eef8ed80b4
905 | | | | | parent: 4:26a8bac39d9f
905 | | | | | parent: 4:26a8bac39d9f
906 | | | | | user: test
906 | | | | | user: test
907 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
908 | | | | | summary: (5) expand
908 | | | | | summary: (5) expand
909 | | | | |
909 | | | | |
910 | | o | | changeset: 4:26a8bac39d9f
910 | | o | | changeset: 4:26a8bac39d9f
911 | |/|/ / parent: 1:6db2ef61d156
911 | |/|/ / parent: 1:6db2ef61d156
912 | | | | parent: 3:27eef8ed80b4
912 | | | | parent: 3:27eef8ed80b4
913 | | | | user: test
913 | | | | user: test
914 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 | | | | date: Thu Jan 01 00:00:04 1970 +0000
915 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 | | | | summary: (4) merge two known; one immediate left, one immediate right
916 | | | |
916 | | | |
917 | o | | changeset: 3:27eef8ed80b4
917 | o | | changeset: 3:27eef8ed80b4
918 |/ / / user: test
918 |/ / / user: test
919 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 | | | date: Thu Jan 01 00:00:03 1970 +0000
920 | | | summary: (3) collapse
920 | | | summary: (3) collapse
921 | | |
921 | | |
922 o | | changeset: 2:3d9a33b8d1e1
922 o | | changeset: 2:3d9a33b8d1e1
923 |/ / user: test
923 |/ / user: test
924 | | date: Thu Jan 01 00:00:02 1970 +0000
924 | | date: Thu Jan 01 00:00:02 1970 +0000
925 | | summary: (2) collapse
925 | | summary: (2) collapse
926 | |
926 | |
927 o | changeset: 1:6db2ef61d156
927 o | changeset: 1:6db2ef61d156
928 |/ user: test
928 |/ user: test
929 | date: Thu Jan 01 00:00:01 1970 +0000
929 | date: Thu Jan 01 00:00:01 1970 +0000
930 | summary: (1) collapse
930 | summary: (1) collapse
931 |
931 |
932 o changeset: 0:e6eb3150255d
932 o changeset: 0:e6eb3150255d
933 user: test
933 user: test
934 date: Thu Jan 01 00:00:00 1970 +0000
934 date: Thu Jan 01 00:00:00 1970 +0000
935 summary: (0) root
935 summary: (0) root
936
936
937
937
938
938
939 File glog per revset (only merges):
939 File glog per revset (only merges):
940
940
941 $ hg log -G -r 'file("a")' -m
941 $ hg log -G -r 'file("a")' -m
942 o changeset: 32:d06dffa21a31
942 o changeset: 32:d06dffa21a31
943 |\ parent: 27:886ed638191b
943 |\ parent: 27:886ed638191b
944 | | parent: 31:621d83e11f67
944 | | parent: 31:621d83e11f67
945 | | user: test
945 | | user: test
946 | | date: Thu Jan 01 00:00:32 1970 +0000
946 | | date: Thu Jan 01 00:00:32 1970 +0000
947 | | summary: (32) expand
947 | | summary: (32) expand
948 | |
948 | |
949 o | changeset: 31:621d83e11f67
949 o | changeset: 31:621d83e11f67
950 |\| parent: 21:d42a756af44d
950 |\| parent: 21:d42a756af44d
951 | | parent: 30:6e11cd4b648f
951 | | parent: 30:6e11cd4b648f
952 | | user: test
952 | | user: test
953 | | date: Thu Jan 01 00:00:31 1970 +0000
953 | | date: Thu Jan 01 00:00:31 1970 +0000
954 | | summary: (31) expand
954 | | summary: (31) expand
955 | |
955 | |
956 o | changeset: 30:6e11cd4b648f
956 o | changeset: 30:6e11cd4b648f
957 |\ \ parent: 28:44ecd0b9ae99
957 |\ \ parent: 28:44ecd0b9ae99
958 | | | parent: 29:cd9bb2be7593
958 | | | parent: 29:cd9bb2be7593
959 | | | user: test
959 | | | user: test
960 | | | date: Thu Jan 01 00:00:30 1970 +0000
960 | | | date: Thu Jan 01 00:00:30 1970 +0000
961 | | | summary: (30) expand
961 | | | summary: (30) expand
962 | | |
962 | | |
963 o | | changeset: 28:44ecd0b9ae99
963 o | | changeset: 28:44ecd0b9ae99
964 |\ \ \ parent: 1:6db2ef61d156
964 |\ \ \ parent: 1:6db2ef61d156
965 | | | | parent: 26:7f25b6c2f0b9
965 | | | | parent: 26:7f25b6c2f0b9
966 | | | | user: test
966 | | | | user: test
967 | | | | date: Thu Jan 01 00:00:28 1970 +0000
967 | | | | date: Thu Jan 01 00:00:28 1970 +0000
968 | | | | summary: (28) merge zero known
968 | | | | summary: (28) merge zero known
969 | | | |
969 | | | |
970 o | | | changeset: 26:7f25b6c2f0b9
970 o | | | changeset: 26:7f25b6c2f0b9
971 |\ \ \ \ parent: 18:1aa84d96232a
971 |\ \ \ \ parent: 18:1aa84d96232a
972 | | | | | parent: 25:91da8ed57247
972 | | | | | parent: 25:91da8ed57247
973 | | | | | user: test
973 | | | | | user: test
974 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
974 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
975 | | | | | summary: (26) merge one known; far right
975 | | | | | summary: (26) merge one known; far right
976 | | | | |
976 | | | | |
977 | o-----+ changeset: 25:91da8ed57247
977 | o-----+ changeset: 25:91da8ed57247
978 | | | | | parent: 21:d42a756af44d
978 | | | | | parent: 21:d42a756af44d
979 | | | | | parent: 24:a9c19a3d96b7
979 | | | | | parent: 24:a9c19a3d96b7
980 | | | | | user: test
980 | | | | | user: test
981 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
981 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
982 | | | | | summary: (25) merge one known; far left
982 | | | | | summary: (25) merge one known; far left
983 | | | | |
983 | | | | |
984 | o | | | changeset: 24:a9c19a3d96b7
984 | o | | | changeset: 24:a9c19a3d96b7
985 | |\ \ \ \ parent: 0:e6eb3150255d
985 | |\ \ \ \ parent: 0:e6eb3150255d
986 | | | | | | parent: 23:a01cddf0766d
986 | | | | | | parent: 23:a01cddf0766d
987 | | | | | | user: test
987 | | | | | | user: test
988 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
988 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
989 | | | | | | summary: (24) merge one known; immediate right
989 | | | | | | summary: (24) merge one known; immediate right
990 | | | | | |
990 | | | | | |
991 | o---+ | | changeset: 23:a01cddf0766d
991 | o---+ | | changeset: 23:a01cddf0766d
992 | | | | | | parent: 1:6db2ef61d156
992 | | | | | | parent: 1:6db2ef61d156
993 | | | | | | parent: 22:e0d9cccacb5d
993 | | | | | | parent: 22:e0d9cccacb5d
994 | | | | | | user: test
994 | | | | | | user: test
995 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
995 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
996 | | | | | | summary: (23) merge one known; immediate left
996 | | | | | | summary: (23) merge one known; immediate left
997 | | | | | |
997 | | | | | |
998 | o-------+ changeset: 22:e0d9cccacb5d
998 | o-------+ changeset: 22:e0d9cccacb5d
999 | | | | | | parent: 18:1aa84d96232a
999 | | | | | | parent: 18:1aa84d96232a
1000 |/ / / / / parent: 21:d42a756af44d
1000 |/ / / / / parent: 21:d42a756af44d
1001 | | | | | user: test
1001 | | | | | user: test
1002 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1002 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1003 | | | | | summary: (22) merge two known; one far left, one far right
1003 | | | | | summary: (22) merge two known; one far left, one far right
1004 | | | | |
1004 | | | | |
1005 | | | | o changeset: 21:d42a756af44d
1005 | | | | o changeset: 21:d42a756af44d
1006 | | | | |\ parent: 19:31ddc2c1573b
1006 | | | | |\ parent: 19:31ddc2c1573b
1007 | | | | | | parent: 20:d30ed6450e32
1007 | | | | | | parent: 20:d30ed6450e32
1008 | | | | | | user: test
1008 | | | | | | user: test
1009 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1010 | | | | | | summary: (21) expand
1010 | | | | | | summary: (21) expand
1011 | | | | | |
1011 | | | | | |
1012 +-+-------o changeset: 20:d30ed6450e32
1012 +-+-------o changeset: 20:d30ed6450e32
1013 | | | | | parent: 0:e6eb3150255d
1013 | | | | | parent: 0:e6eb3150255d
1014 | | | | | parent: 18:1aa84d96232a
1014 | | | | | parent: 18:1aa84d96232a
1015 | | | | | user: test
1015 | | | | | user: test
1016 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1016 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1017 | | | | | summary: (20) merge two known; two far right
1017 | | | | | summary: (20) merge two known; two far right
1018 | | | | |
1018 | | | | |
1019 | | | | o changeset: 19:31ddc2c1573b
1019 | | | | o changeset: 19:31ddc2c1573b
1020 | | | | |\ parent: 15:1dda3f72782d
1020 | | | | |\ parent: 15:1dda3f72782d
1021 | | | | | | parent: 17:44765d7c06e0
1021 | | | | | | parent: 17:44765d7c06e0
1022 | | | | | | user: test
1022 | | | | | | user: test
1023 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1024 | | | | | | summary: (19) expand
1024 | | | | | | summary: (19) expand
1025 | | | | | |
1025 | | | | | |
1026 o---+---+ | changeset: 18:1aa84d96232a
1026 o---+---+ | changeset: 18:1aa84d96232a
1027 | | | | | parent: 1:6db2ef61d156
1027 | | | | | parent: 1:6db2ef61d156
1028 / / / / / parent: 15:1dda3f72782d
1028 / / / / / parent: 15:1dda3f72782d
1029 | | | | | user: test
1029 | | | | | user: test
1030 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1030 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1031 | | | | | summary: (18) merge two known; two far left
1031 | | | | | summary: (18) merge two known; two far left
1032 | | | | |
1032 | | | | |
1033 | | | | o changeset: 17:44765d7c06e0
1033 | | | | o changeset: 17:44765d7c06e0
1034 | | | | |\ parent: 12:86b91144a6e9
1034 | | | | |\ parent: 12:86b91144a6e9
1035 | | | | | | parent: 16:3677d192927d
1035 | | | | | | parent: 16:3677d192927d
1036 | | | | | | user: test
1036 | | | | | | user: test
1037 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1038 | | | | | | summary: (17) expand
1038 | | | | | | summary: (17) expand
1039 | | | | | |
1039 | | | | | |
1040 +-+-------o changeset: 16:3677d192927d
1040 +-+-------o changeset: 16:3677d192927d
1041 | | | | | parent: 0:e6eb3150255d
1041 | | | | | parent: 0:e6eb3150255d
1042 | | | | | parent: 1:6db2ef61d156
1042 | | | | | parent: 1:6db2ef61d156
1043 | | | | | user: test
1043 | | | | | user: test
1044 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1044 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1045 | | | | | summary: (16) merge two known; one immediate right, one near right
1045 | | | | | summary: (16) merge two known; one immediate right, one near right
1046 | | | | |
1046 | | | | |
1047 | | | o | changeset: 15:1dda3f72782d
1047 | | | o | changeset: 15:1dda3f72782d
1048 | | | |\ \ parent: 13:22d8966a97e3
1048 | | | |\ \ parent: 13:22d8966a97e3
1049 | | | | | | parent: 14:8eac370358ef
1049 | | | | | | parent: 14:8eac370358ef
1050 | | | | | | user: test
1050 | | | | | | user: test
1051 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1052 | | | | | | summary: (15) expand
1052 | | | | | | summary: (15) expand
1053 | | | | | |
1053 | | | | | |
1054 +-------o | changeset: 14:8eac370358ef
1054 +-------o | changeset: 14:8eac370358ef
1055 | | | | |/ parent: 0:e6eb3150255d
1055 | | | | |/ parent: 0:e6eb3150255d
1056 | | | | | parent: 12:86b91144a6e9
1056 | | | | | parent: 12:86b91144a6e9
1057 | | | | | user: test
1057 | | | | | user: test
1058 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1058 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1059 | | | | | summary: (14) merge two known; one immediate right, one far right
1059 | | | | | summary: (14) merge two known; one immediate right, one far right
1060 | | | | |
1060 | | | | |
1061 | | | o | changeset: 13:22d8966a97e3
1061 | | | o | changeset: 13:22d8966a97e3
1062 | | | |\ \ parent: 9:7010c0af0a35
1062 | | | |\ \ parent: 9:7010c0af0a35
1063 | | | | | | parent: 11:832d76e6bdf2
1063 | | | | | | parent: 11:832d76e6bdf2
1064 | | | | | | user: test
1064 | | | | | | user: test
1065 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1066 | | | | | | summary: (13) expand
1066 | | | | | | summary: (13) expand
1067 | | | | | |
1067 | | | | | |
1068 | +---+---o changeset: 12:86b91144a6e9
1068 | +---+---o changeset: 12:86b91144a6e9
1069 | | | | | parent: 1:6db2ef61d156
1069 | | | | | parent: 1:6db2ef61d156
1070 | | | | | parent: 9:7010c0af0a35
1070 | | | | | parent: 9:7010c0af0a35
1071 | | | | | user: test
1071 | | | | | user: test
1072 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1072 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1073 | | | | | summary: (12) merge two known; one immediate right, one far left
1073 | | | | | summary: (12) merge two known; one immediate right, one far left
1074 | | | | |
1074 | | | | |
1075 | | | | o changeset: 11:832d76e6bdf2
1075 | | | | o changeset: 11:832d76e6bdf2
1076 | | | | |\ parent: 6:b105a072e251
1076 | | | | |\ parent: 6:b105a072e251
1077 | | | | | | parent: 10:74c64d036d72
1077 | | | | | | parent: 10:74c64d036d72
1078 | | | | | | user: test
1078 | | | | | | user: test
1079 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1080 | | | | | | summary: (11) expand
1080 | | | | | | summary: (11) expand
1081 | | | | | |
1081 | | | | | |
1082 +---------o changeset: 10:74c64d036d72
1082 +---------o changeset: 10:74c64d036d72
1083 | | | | |/ parent: 0:e6eb3150255d
1083 | | | | |/ parent: 0:e6eb3150255d
1084 | | | | | parent: 6:b105a072e251
1084 | | | | | parent: 6:b105a072e251
1085 | | | | | user: test
1085 | | | | | user: test
1086 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1086 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1087 | | | | | summary: (10) merge two known; one immediate left, one near right
1087 | | | | | summary: (10) merge two known; one immediate left, one near right
1088 | | | | |
1088 | | | | |
1089 | | | o | changeset: 9:7010c0af0a35
1089 | | | o | changeset: 9:7010c0af0a35
1090 | | | |\ \ parent: 7:b632bb1b1224
1090 | | | |\ \ parent: 7:b632bb1b1224
1091 | | | | | | parent: 8:7a0b11f71937
1091 | | | | | | parent: 8:7a0b11f71937
1092 | | | | | | user: test
1092 | | | | | | user: test
1093 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1094 | | | | | | summary: (9) expand
1094 | | | | | | summary: (9) expand
1095 | | | | | |
1095 | | | | | |
1096 +-------o | changeset: 8:7a0b11f71937
1096 +-------o | changeset: 8:7a0b11f71937
1097 | | | |/ / parent: 0:e6eb3150255d
1097 | | | |/ / parent: 0:e6eb3150255d
1098 | | | | | parent: 7:b632bb1b1224
1098 | | | | | parent: 7:b632bb1b1224
1099 | | | | | user: test
1099 | | | | | user: test
1100 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1100 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1101 | | | | | summary: (8) merge two known; one immediate left, one far right
1101 | | | | | summary: (8) merge two known; one immediate left, one far right
1102 | | | | |
1102 | | | | |
1103 | | | o | changeset: 7:b632bb1b1224
1103 | | | o | changeset: 7:b632bb1b1224
1104 | | | |\ \ parent: 2:3d9a33b8d1e1
1104 | | | |\ \ parent: 2:3d9a33b8d1e1
1105 | | | | | | parent: 5:4409d547b708
1105 | | | | | | parent: 5:4409d547b708
1106 | | | | | | user: test
1106 | | | | | | user: test
1107 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1107 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1108 | | | | | | summary: (7) expand
1108 | | | | | | summary: (7) expand
1109 | | | | | |
1109 | | | | | |
1110 | | | +---o changeset: 6:b105a072e251
1110 | | | +---o changeset: 6:b105a072e251
1111 | | | | |/ parent: 2:3d9a33b8d1e1
1111 | | | | |/ parent: 2:3d9a33b8d1e1
1112 | | | | | parent: 5:4409d547b708
1112 | | | | | parent: 5:4409d547b708
1113 | | | | | user: test
1113 | | | | | user: test
1114 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1114 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1115 | | | | | summary: (6) merge two known; one immediate left, one far left
1115 | | | | | summary: (6) merge two known; one immediate left, one far left
1116 | | | | |
1116 | | | | |
1117 | | | o | changeset: 5:4409d547b708
1117 | | | o | changeset: 5:4409d547b708
1118 | | | |\ \ parent: 3:27eef8ed80b4
1118 | | | |\ \ parent: 3:27eef8ed80b4
1119 | | | | | | parent: 4:26a8bac39d9f
1119 | | | | | | parent: 4:26a8bac39d9f
1120 | | | | | | user: test
1120 | | | | | | user: test
1121 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1121 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1122 | | | | | | summary: (5) expand
1122 | | | | | | summary: (5) expand
1123 | | | | | |
1123 | | | | | |
1124 | +---o | | changeset: 4:26a8bac39d9f
1124 | +---o | | changeset: 4:26a8bac39d9f
1125 | | | |/ / parent: 1:6db2ef61d156
1125 | | | |/ / parent: 1:6db2ef61d156
1126 | | | | | parent: 3:27eef8ed80b4
1126 | | | | | parent: 3:27eef8ed80b4
1127 | | | | | user: test
1127 | | | | | user: test
1128 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1128 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1129 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1129 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1130 | | | | |
1130 | | | | |
1131
1131
1132
1132
1133 Empty revision range - display nothing:
1133 Empty revision range - display nothing:
1134 $ hg glog -r 1..0
1134 $ hg glog -r 1..0
1135
1135
1136 $ cd ..
1136 $ cd ..
1137
1137
1138 #if no-outer-repo
1138 #if no-outer-repo
1139
1139
1140 From outer space:
1140 From outer space:
1141 $ hg glog -l1 repo
1141 $ hg glog -l1 repo
1142 @ changeset: 34:fea3ac5810e0
1142 @ changeset: 34:fea3ac5810e0
1143 | tag: tip
1143 | tag: tip
1144 | parent: 32:d06dffa21a31
1144 | parent: 32:d06dffa21a31
1145 | user: test
1145 | user: test
1146 | date: Thu Jan 01 00:00:34 1970 +0000
1146 | date: Thu Jan 01 00:00:34 1970 +0000
1147 | summary: (34) head
1147 | summary: (34) head
1148 |
1148 |
1149 $ hg glog -l1 repo/a
1149 $ hg glog -l1 repo/a
1150 @ changeset: 34:fea3ac5810e0
1150 @ changeset: 34:fea3ac5810e0
1151 | tag: tip
1151 | tag: tip
1152 | parent: 32:d06dffa21a31
1152 | parent: 32:d06dffa21a31
1153 | user: test
1153 | user: test
1154 | date: Thu Jan 01 00:00:34 1970 +0000
1154 | date: Thu Jan 01 00:00:34 1970 +0000
1155 | summary: (34) head
1155 | summary: (34) head
1156 |
1156 |
1157 $ hg glog -l1 repo/missing
1157 $ hg glog -l1 repo/missing
1158
1158
1159 #endif
1159 #endif
1160
1160
1161 File log with revs != cset revs:
1161 File log with revs != cset revs:
1162 $ hg init flog
1162 $ hg init flog
1163 $ cd flog
1163 $ cd flog
1164 $ echo one >one
1164 $ echo one >one
1165 $ hg add one
1165 $ hg add one
1166 $ hg commit -mone
1166 $ hg commit -mone
1167 $ echo two >two
1167 $ echo two >two
1168 $ hg add two
1168 $ hg add two
1169 $ hg commit -mtwo
1169 $ hg commit -mtwo
1170 $ echo more >two
1170 $ echo more >two
1171 $ hg commit -mmore
1171 $ hg commit -mmore
1172 $ hg glog two
1172 $ hg glog two
1173 @ changeset: 2:12c28321755b
1173 @ changeset: 2:12c28321755b
1174 | tag: tip
1174 | tag: tip
1175 | user: test
1175 | user: test
1176 | date: Thu Jan 01 00:00:00 1970 +0000
1176 | date: Thu Jan 01 00:00:00 1970 +0000
1177 | summary: more
1177 | summary: more
1178 |
1178 |
1179 o changeset: 1:5ac72c0599bf
1179 o changeset: 1:5ac72c0599bf
1180 | user: test
1180 | user: test
1181 | date: Thu Jan 01 00:00:00 1970 +0000
1181 | date: Thu Jan 01 00:00:00 1970 +0000
1182 | summary: two
1182 | summary: two
1183 |
1183 |
1184
1184
1185 Issue1896: File log with explicit style
1185 Issue1896: File log with explicit style
1186 $ hg glog --style=default one
1186 $ hg glog --style=default one
1187 o changeset: 0:3d578b4a1f53
1187 o changeset: 0:3d578b4a1f53
1188 user: test
1188 user: test
1189 date: Thu Jan 01 00:00:00 1970 +0000
1189 date: Thu Jan 01 00:00:00 1970 +0000
1190 summary: one
1190 summary: one
1191
1191
1192 Issue2395: glog --style header and footer
1192 Issue2395: glog --style header and footer
1193 $ hg glog --style=xml one
1193 $ hg glog --style=xml one
1194 <?xml version="1.0"?>
1194 <?xml version="1.0"?>
1195 <log>
1195 <log>
1196 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1197 <author email="test">test</author>
1197 <author email="test">test</author>
1198 <date>1970-01-01T00:00:00+00:00</date>
1198 <date>1970-01-01T00:00:00+00:00</date>
1199 <msg xml:space="preserve">one</msg>
1199 <msg xml:space="preserve">one</msg>
1200 </logentry>
1200 </logentry>
1201 </log>
1201 </log>
1202
1202
1203 $ cd ..
1203 $ cd ..
1204
1204
1205 Incoming and outgoing:
1205 Incoming and outgoing:
1206
1206
1207 $ hg clone -U -r31 repo repo2
1207 $ hg clone -U -r31 repo repo2
1208 adding changesets
1208 adding changesets
1209 adding manifests
1209 adding manifests
1210 adding file changes
1210 adding file changes
1211 added 31 changesets with 31 changes to 1 files
1211 added 31 changesets with 31 changes to 1 files
1212 $ cd repo2
1212 $ cd repo2
1213
1213
1214 $ hg incoming --graph ../repo
1214 $ hg incoming --graph ../repo
1215 comparing with ../repo
1215 comparing with ../repo
1216 searching for changes
1216 searching for changes
1217 o changeset: 34:fea3ac5810e0
1217 o changeset: 34:fea3ac5810e0
1218 | tag: tip
1218 | tag: tip
1219 | parent: 32:d06dffa21a31
1219 | parent: 32:d06dffa21a31
1220 | user: test
1220 | user: test
1221 | date: Thu Jan 01 00:00:34 1970 +0000
1221 | date: Thu Jan 01 00:00:34 1970 +0000
1222 | summary: (34) head
1222 | summary: (34) head
1223 |
1223 |
1224 | o changeset: 33:68608f5145f9
1224 | o changeset: 33:68608f5145f9
1225 | parent: 18:1aa84d96232a
1225 | parent: 18:1aa84d96232a
1226 | user: test
1226 | user: test
1227 | date: Thu Jan 01 00:00:33 1970 +0000
1227 | date: Thu Jan 01 00:00:33 1970 +0000
1228 | summary: (33) head
1228 | summary: (33) head
1229 |
1229 |
1230 o changeset: 32:d06dffa21a31
1230 o changeset: 32:d06dffa21a31
1231 | parent: 27:886ed638191b
1231 | parent: 27:886ed638191b
1232 | parent: 31:621d83e11f67
1232 | parent: 31:621d83e11f67
1233 | user: test
1233 | user: test
1234 | date: Thu Jan 01 00:00:32 1970 +0000
1234 | date: Thu Jan 01 00:00:32 1970 +0000
1235 | summary: (32) expand
1235 | summary: (32) expand
1236 |
1236 |
1237 o changeset: 27:886ed638191b
1237 o changeset: 27:886ed638191b
1238 parent: 21:d42a756af44d
1238 parent: 21:d42a756af44d
1239 user: test
1239 user: test
1240 date: Thu Jan 01 00:00:27 1970 +0000
1240 date: Thu Jan 01 00:00:27 1970 +0000
1241 summary: (27) collapse
1241 summary: (27) collapse
1242
1242
1243 $ cd ..
1243 $ cd ..
1244
1244
1245 $ hg -R repo outgoing --graph repo2
1245 $ hg -R repo outgoing --graph repo2
1246 comparing with repo2
1246 comparing with repo2
1247 searching for changes
1247 searching for changes
1248 @ changeset: 34:fea3ac5810e0
1248 @ changeset: 34:fea3ac5810e0
1249 | tag: tip
1249 | tag: tip
1250 | parent: 32:d06dffa21a31
1250 | parent: 32:d06dffa21a31
1251 | user: test
1251 | user: test
1252 | date: Thu Jan 01 00:00:34 1970 +0000
1252 | date: Thu Jan 01 00:00:34 1970 +0000
1253 | summary: (34) head
1253 | summary: (34) head
1254 |
1254 |
1255 | o changeset: 33:68608f5145f9
1255 | o changeset: 33:68608f5145f9
1256 | parent: 18:1aa84d96232a
1256 | parent: 18:1aa84d96232a
1257 | user: test
1257 | user: test
1258 | date: Thu Jan 01 00:00:33 1970 +0000
1258 | date: Thu Jan 01 00:00:33 1970 +0000
1259 | summary: (33) head
1259 | summary: (33) head
1260 |
1260 |
1261 o changeset: 32:d06dffa21a31
1261 o changeset: 32:d06dffa21a31
1262 | parent: 27:886ed638191b
1262 | parent: 27:886ed638191b
1263 | parent: 31:621d83e11f67
1263 | parent: 31:621d83e11f67
1264 | user: test
1264 | user: test
1265 | date: Thu Jan 01 00:00:32 1970 +0000
1265 | date: Thu Jan 01 00:00:32 1970 +0000
1266 | summary: (32) expand
1266 | summary: (32) expand
1267 |
1267 |
1268 o changeset: 27:886ed638191b
1268 o changeset: 27:886ed638191b
1269 parent: 21:d42a756af44d
1269 parent: 21:d42a756af44d
1270 user: test
1270 user: test
1271 date: Thu Jan 01 00:00:27 1970 +0000
1271 date: Thu Jan 01 00:00:27 1970 +0000
1272 summary: (27) collapse
1272 summary: (27) collapse
1273
1273
1274
1274
1275 File + limit with revs != cset revs:
1275 File + limit with revs != cset revs:
1276 $ cd repo
1276 $ cd repo
1277 $ touch b
1277 $ touch b
1278 $ hg ci -Aqm0
1278 $ hg ci -Aqm0
1279 $ hg glog -l2 a
1279 $ hg glog -l2 a
1280 o changeset: 34:fea3ac5810e0
1280 o changeset: 34:fea3ac5810e0
1281 | parent: 32:d06dffa21a31
1281 | parent: 32:d06dffa21a31
1282 | user: test
1282 | user: test
1283 | date: Thu Jan 01 00:00:34 1970 +0000
1283 | date: Thu Jan 01 00:00:34 1970 +0000
1284 | summary: (34) head
1284 | summary: (34) head
1285 |
1285 |
1286 | o changeset: 33:68608f5145f9
1286 | o changeset: 33:68608f5145f9
1287 | | parent: 18:1aa84d96232a
1287 | | parent: 18:1aa84d96232a
1288 | | user: test
1288 | | user: test
1289 | | date: Thu Jan 01 00:00:33 1970 +0000
1289 | | date: Thu Jan 01 00:00:33 1970 +0000
1290 | | summary: (33) head
1290 | | summary: (33) head
1291 | |
1291 | |
1292
1292
1293 File + limit + -ra:b, (b - a) < limit:
1293 File + limit + -ra:b, (b - a) < limit:
1294 $ hg glog -l3000 -r32:tip a
1294 $ hg glog -l3000 -r32:tip a
1295 o changeset: 34:fea3ac5810e0
1295 o changeset: 34:fea3ac5810e0
1296 | parent: 32:d06dffa21a31
1296 | parent: 32:d06dffa21a31
1297 | user: test
1297 | user: test
1298 | date: Thu Jan 01 00:00:34 1970 +0000
1298 | date: Thu Jan 01 00:00:34 1970 +0000
1299 | summary: (34) head
1299 | summary: (34) head
1300 |
1300 |
1301 | o changeset: 33:68608f5145f9
1301 | o changeset: 33:68608f5145f9
1302 | | parent: 18:1aa84d96232a
1302 | | parent: 18:1aa84d96232a
1303 | | user: test
1303 | | user: test
1304 | | date: Thu Jan 01 00:00:33 1970 +0000
1304 | | date: Thu Jan 01 00:00:33 1970 +0000
1305 | | summary: (33) head
1305 | | summary: (33) head
1306 | |
1306 | |
1307 o | changeset: 32:d06dffa21a31
1307 o | changeset: 32:d06dffa21a31
1308 |\ \ parent: 27:886ed638191b
1308 |\ \ parent: 27:886ed638191b
1309 | | | parent: 31:621d83e11f67
1309 | | | parent: 31:621d83e11f67
1310 | | | user: test
1310 | | | user: test
1311 | | | date: Thu Jan 01 00:00:32 1970 +0000
1311 | | | date: Thu Jan 01 00:00:32 1970 +0000
1312 | | | summary: (32) expand
1312 | | | summary: (32) expand
1313 | | |
1313 | | |
1314
1314
1315 Point out a common and an uncommon unshown parent
1315 Point out a common and an uncommon unshown parent
1316
1316
1317 $ hg glog -r 'rev(8) or rev(9)'
1317 $ hg glog -r 'rev(8) or rev(9)'
1318 o changeset: 9:7010c0af0a35
1318 o changeset: 9:7010c0af0a35
1319 |\ parent: 7:b632bb1b1224
1319 |\ parent: 7:b632bb1b1224
1320 | | parent: 8:7a0b11f71937
1320 | | parent: 8:7a0b11f71937
1321 | | user: test
1321 | | user: test
1322 | | date: Thu Jan 01 00:00:09 1970 +0000
1322 | | date: Thu Jan 01 00:00:09 1970 +0000
1323 | | summary: (9) expand
1323 | | summary: (9) expand
1324 | |
1324 | |
1325 o | changeset: 8:7a0b11f71937
1325 o | changeset: 8:7a0b11f71937
1326 |\| parent: 0:e6eb3150255d
1326 |\| parent: 0:e6eb3150255d
1327 | | parent: 7:b632bb1b1224
1327 | | parent: 7:b632bb1b1224
1328 | | user: test
1328 | | user: test
1329 | | date: Thu Jan 01 00:00:08 1970 +0000
1329 | | date: Thu Jan 01 00:00:08 1970 +0000
1330 | | summary: (8) merge two known; one immediate left, one far right
1330 | | summary: (8) merge two known; one immediate left, one far right
1331 | |
1331 | |
1332
1332
1333 File + limit + -ra:b, b < tip:
1333 File + limit + -ra:b, b < tip:
1334
1334
1335 $ hg glog -l1 -r32:34 a
1335 $ hg glog -l1 -r32:34 a
1336 o changeset: 34:fea3ac5810e0
1336 o changeset: 34:fea3ac5810e0
1337 | parent: 32:d06dffa21a31
1337 | parent: 32:d06dffa21a31
1338 | user: test
1338 | user: test
1339 | date: Thu Jan 01 00:00:34 1970 +0000
1339 | date: Thu Jan 01 00:00:34 1970 +0000
1340 | summary: (34) head
1340 | summary: (34) head
1341 |
1341 |
1342
1342
1343 file(File) + limit + -ra:b, b < tip:
1343 file(File) + limit + -ra:b, b < tip:
1344
1344
1345 $ hg glog -l1 -r32:34 -r 'file("a")'
1345 $ hg glog -l1 -r32:34 -r 'file("a")'
1346 o changeset: 34:fea3ac5810e0
1346 o changeset: 34:fea3ac5810e0
1347 | parent: 32:d06dffa21a31
1347 | parent: 32:d06dffa21a31
1348 | user: test
1348 | user: test
1349 | date: Thu Jan 01 00:00:34 1970 +0000
1349 | date: Thu Jan 01 00:00:34 1970 +0000
1350 | summary: (34) head
1350 | summary: (34) head
1351 |
1351 |
1352
1352
1353 limit(file(File) and a::b), b < tip:
1353 limit(file(File) and a::b), b < tip:
1354
1354
1355 $ hg glog -r 'limit(file("a") and 32::34, 1)'
1355 $ hg glog -r 'limit(file("a") and 32::34, 1)'
1356 o changeset: 32:d06dffa21a31
1356 o changeset: 32:d06dffa21a31
1357 |\ parent: 27:886ed638191b
1357 |\ parent: 27:886ed638191b
1358 | | parent: 31:621d83e11f67
1358 | | parent: 31:621d83e11f67
1359 | | user: test
1359 | | user: test
1360 | | date: Thu Jan 01 00:00:32 1970 +0000
1360 | | date: Thu Jan 01 00:00:32 1970 +0000
1361 | | summary: (32) expand
1361 | | summary: (32) expand
1362 | |
1362 | |
1363
1363
1364 File + limit + -ra:b, b < tip:
1364 File + limit + -ra:b, b < tip:
1365
1365
1366 $ hg glog -r 'limit(file("a") and 34::32, 1)'
1366 $ hg glog -r 'limit(file("a") and 34::32, 1)'
1367
1367
1368 File + limit + -ra:b, b < tip, (b - a) < limit:
1368 File + limit + -ra:b, b < tip, (b - a) < limit:
1369
1369
1370 $ hg glog -l10 -r33:34 a
1370 $ hg glog -l10 -r33:34 a
1371 o changeset: 34:fea3ac5810e0
1371 o changeset: 34:fea3ac5810e0
1372 | parent: 32:d06dffa21a31
1372 | parent: 32:d06dffa21a31
1373 | user: test
1373 | user: test
1374 | date: Thu Jan 01 00:00:34 1970 +0000
1374 | date: Thu Jan 01 00:00:34 1970 +0000
1375 | summary: (34) head
1375 | summary: (34) head
1376 |
1376 |
1377 | o changeset: 33:68608f5145f9
1377 | o changeset: 33:68608f5145f9
1378 | | parent: 18:1aa84d96232a
1378 | | parent: 18:1aa84d96232a
1379 | | user: test
1379 | | user: test
1380 | | date: Thu Jan 01 00:00:33 1970 +0000
1380 | | date: Thu Jan 01 00:00:33 1970 +0000
1381 | | summary: (33) head
1381 | | summary: (33) head
1382 | |
1382 | |
1383
1383
1384 Do not crash or produce strange graphs if history is buggy
1384 Do not crash or produce strange graphs if history is buggy
1385
1385
1386 $ hg branch branch
1386 $ hg branch branch
1387 marked working directory as branch branch
1387 marked working directory as branch branch
1388 (branches are permanent and global, did you want a bookmark?)
1388 (branches are permanent and global, did you want a bookmark?)
1389 $ commit 36 "buggy merge: identical parents" 35 35
1389 $ commit 36 "buggy merge: identical parents" 35 35
1390 $ hg glog -l5
1390 $ hg glog -l5
1391 @ changeset: 36:08a19a744424
1391 @ changeset: 36:08a19a744424
1392 | branch: branch
1392 | branch: branch
1393 | tag: tip
1393 | tag: tip
1394 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1395 | parent: 35:9159c3644c5e
1395 | parent: 35:9159c3644c5e
1396 | user: test
1396 | user: test
1397 | date: Thu Jan 01 00:00:36 1970 +0000
1397 | date: Thu Jan 01 00:00:36 1970 +0000
1398 | summary: (36) buggy merge: identical parents
1398 | summary: (36) buggy merge: identical parents
1399 |
1399 |
1400 o changeset: 35:9159c3644c5e
1400 o changeset: 35:9159c3644c5e
1401 | user: test
1401 | user: test
1402 | date: Thu Jan 01 00:00:00 1970 +0000
1402 | date: Thu Jan 01 00:00:00 1970 +0000
1403 | summary: 0
1403 | summary: 0
1404 |
1404 |
1405 o changeset: 34:fea3ac5810e0
1405 o changeset: 34:fea3ac5810e0
1406 | parent: 32:d06dffa21a31
1406 | parent: 32:d06dffa21a31
1407 | user: test
1407 | user: test
1408 | date: Thu Jan 01 00:00:34 1970 +0000
1408 | date: Thu Jan 01 00:00:34 1970 +0000
1409 | summary: (34) head
1409 | summary: (34) head
1410 |
1410 |
1411 | o changeset: 33:68608f5145f9
1411 | o changeset: 33:68608f5145f9
1412 | | parent: 18:1aa84d96232a
1412 | | parent: 18:1aa84d96232a
1413 | | user: test
1413 | | user: test
1414 | | date: Thu Jan 01 00:00:33 1970 +0000
1414 | | date: Thu Jan 01 00:00:33 1970 +0000
1415 | | summary: (33) head
1415 | | summary: (33) head
1416 | |
1416 | |
1417 o | changeset: 32:d06dffa21a31
1417 o | changeset: 32:d06dffa21a31
1418 |\ \ parent: 27:886ed638191b
1418 |\ \ parent: 27:886ed638191b
1419 | | | parent: 31:621d83e11f67
1419 | | | parent: 31:621d83e11f67
1420 | | | user: test
1420 | | | user: test
1421 | | | date: Thu Jan 01 00:00:32 1970 +0000
1421 | | | date: Thu Jan 01 00:00:32 1970 +0000
1422 | | | summary: (32) expand
1422 | | | summary: (32) expand
1423 | | |
1423 | | |
1424
1424
1425 Test log -G options
1425 Test log -G options
1426
1426
1427 $ testlog() {
1427 $ testlog() {
1428 > hg log -G --print-revset "$@"
1428 > hg log -G --print-revset "$@"
1429 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 > | sed 's/.*nodetag/nodetag/' > log.nodes
1431 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1432 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1433 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1433 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1434 > }
1434 > }
1435
1435
1436 glog always reorders nodes which explains the difference with log
1436 glog always reorders nodes which explains the difference with log
1437
1437
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1439 ['27', '25', '21', '34', '32', '31']
1439 ['27', '25', '21', '34', '32', '31']
1440 []
1440 []
1441 --- log.nodes * (glob)
1441 --- log.nodes * (glob)
1442 +++ glog.nodes * (glob)
1442 +++ glog.nodes * (glob)
1443 @@ -1,6 +1,6 @@
1443 @@ -1,6 +1,6 @@
1444 -nodetag 27
1444 -nodetag 27
1445 -nodetag 25
1445 -nodetag 25
1446 -nodetag 21
1446 -nodetag 21
1447 nodetag 34
1447 nodetag 34
1448 nodetag 32
1448 nodetag 32
1449 nodetag 31
1449 nodetag 31
1450 +nodetag 27
1450 +nodetag 27
1451 +nodetag 25
1451 +nodetag 25
1452 +nodetag 21
1452 +nodetag 21
1453 $ testlog -u test -u not-a-user
1453 $ testlog -u test -u not-a-user
1454 []
1454 []
1455 (group
1455 (group
1456 (group
1456 (group
1457 (or
1457 (or
1458 (func
1458 (func
1459 ('symbol', 'user')
1459 ('symbol', 'user')
1460 ('string', 'test'))
1460 ('string', 'test'))
1461 (func
1461 (func
1462 ('symbol', 'user')
1462 ('symbol', 'user')
1463 ('string', 'not-a-user')))))
1463 ('string', 'not-a-user')))))
1464 $ testlog -b not-a-branch
1464 $ testlog -b not-a-branch
1465 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1468 $ testlog -b 35 -b 36 --only-branch branch
1468 $ testlog -b 35 -b 36 --only-branch branch
1469 []
1469 []
1470 (group
1470 (group
1471 (group
1471 (group
1472 (or
1472 (or
1473 (or
1473 (or
1474 (func
1474 (func
1475 ('symbol', 'branch')
1475 ('symbol', 'branch')
1476 ('string', 'default'))
1476 ('string', 'default'))
1477 (func
1477 (func
1478 ('symbol', 'branch')
1478 ('symbol', 'branch')
1479 ('string', 'branch')))
1479 ('string', 'branch')))
1480 (func
1480 (func
1481 ('symbol', 'branch')
1481 ('symbol', 'branch')
1482 ('string', 'branch')))))
1482 ('string', 'branch')))))
1483 $ testlog -k expand -k merge
1483 $ testlog -k expand -k merge
1484 []
1484 []
1485 (group
1485 (group
1486 (group
1486 (group
1487 (or
1487 (or
1488 (func
1488 (func
1489 ('symbol', 'keyword')
1489 ('symbol', 'keyword')
1490 ('string', 'expand'))
1490 ('string', 'expand'))
1491 (func
1491 (func
1492 ('symbol', 'keyword')
1492 ('symbol', 'keyword')
1493 ('string', 'merge')))))
1493 ('string', 'merge')))))
1494 $ testlog --only-merges
1494 $ testlog --only-merges
1495 []
1495 []
1496 (group
1496 (group
1497 (func
1497 (func
1498 ('symbol', 'merge')
1498 ('symbol', 'merge')
1499 None))
1499 None))
1500 $ testlog --no-merges
1500 $ testlog --no-merges
1501 []
1501 []
1502 (group
1502 (group
1503 (not
1503 (not
1504 (func
1504 (func
1505 ('symbol', 'merge')
1505 ('symbol', 'merge')
1506 None)))
1506 None)))
1507 $ testlog --date '2 0 to 4 0'
1507 $ testlog --date '2 0 to 4 0'
1508 []
1508 []
1509 (group
1509 (group
1510 (func
1510 (func
1511 ('symbol', 'date')
1511 ('symbol', 'date')
1512 ('string', '2 0 to 4 0')))
1512 ('string', '2 0 to 4 0')))
1513 $ hg log -G -d 'brace ) in a date'
1513 $ hg log -G -d 'brace ) in a date'
1514 abort: invalid date: 'brace ) in a date'
1514 abort: invalid date: 'brace ) in a date'
1515 [255]
1515 [255]
1516 $ testlog --prune 31 --prune 32
1516 $ testlog --prune 31 --prune 32
1517 []
1517 []
1518 (group
1518 (group
1519 (group
1519 (group
1520 (and
1520 (and
1521 (not
1521 (not
1522 (group
1522 (group
1523 (or
1523 (or
1524 ('string', '31')
1524 ('string', '31')
1525 (func
1525 (func
1526 ('symbol', 'ancestors')
1526 ('symbol', 'ancestors')
1527 ('string', '31')))))
1527 ('string', '31')))))
1528 (not
1528 (not
1529 (group
1529 (group
1530 (or
1530 (or
1531 ('string', '32')
1531 ('string', '32')
1532 (func
1532 (func
1533 ('symbol', 'ancestors')
1533 ('symbol', 'ancestors')
1534 ('string', '32'))))))))
1534 ('string', '32'))))))))
1535
1535
1536 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 Dedicated repo for --follow and paths filtering. The g is crafted to
1537 have 2 filelog topological heads in a linear changeset graph.
1537 have 2 filelog topological heads in a linear changeset graph.
1538
1538
1539 $ cd ..
1539 $ cd ..
1540 $ hg init follow
1540 $ hg init follow
1541 $ cd follow
1541 $ cd follow
1542 $ testlog --follow
1542 $ testlog --follow
1543 []
1543 []
1544 []
1544 []
1545 abort: unknown revision '0'!
1545 $ echo a > a
1546 $ echo a > a
1546 $ echo aa > aa
1547 $ echo aa > aa
1547 $ echo f > f
1548 $ echo f > f
1548 $ hg ci -Am "add a" a aa f
1549 $ hg ci -Am "add a" a aa f
1549 $ hg cp a b
1550 $ hg cp a b
1550 $ hg cp f g
1551 $ hg cp f g
1551 $ hg ci -m "copy a b"
1552 $ hg ci -m "copy a b"
1552 $ mkdir dir
1553 $ mkdir dir
1553 $ hg mv b dir
1554 $ hg mv b dir
1554 $ echo g >> g
1555 $ echo g >> g
1555 $ echo f >> f
1556 $ echo f >> f
1556 $ hg ci -m "mv b dir/b"
1557 $ hg ci -m "mv b dir/b"
1557 $ hg mv a b
1558 $ hg mv a b
1558 $ hg cp -f f g
1559 $ hg cp -f f g
1559 $ echo a > d
1560 $ echo a > d
1560 $ hg add d
1561 $ hg add d
1561 $ hg ci -m "mv a b; add d"
1562 $ hg ci -m "mv a b; add d"
1562 $ hg mv dir/b e
1563 $ hg mv dir/b e
1563 $ hg ci -m "mv dir/b e"
1564 $ hg ci -m "mv dir/b e"
1564 $ hg glog --template '({rev}) {desc|firstline}\n'
1565 $ hg glog --template '({rev}) {desc|firstline}\n'
1565 @ (4) mv dir/b e
1566 @ (4) mv dir/b e
1566 |
1567 |
1567 o (3) mv a b; add d
1568 o (3) mv a b; add d
1568 |
1569 |
1569 o (2) mv b dir/b
1570 o (2) mv b dir/b
1570 |
1571 |
1571 o (1) copy a b
1572 o (1) copy a b
1572 |
1573 |
1573 o (0) add a
1574 o (0) add a
1574
1575
1575
1576
1576 $ testlog a
1577 $ testlog a
1577 []
1578 []
1578 (group
1579 (group
1579 (group
1580 (group
1580 (func
1581 (func
1581 ('symbol', 'filelog')
1582 ('symbol', 'filelog')
1582 ('string', 'a'))))
1583 ('string', 'a'))))
1583 $ testlog a b
1584 $ testlog a b
1584 []
1585 []
1585 (group
1586 (group
1586 (group
1587 (group
1587 (or
1588 (or
1588 (func
1589 (func
1589 ('symbol', 'filelog')
1590 ('symbol', 'filelog')
1590 ('string', 'a'))
1591 ('string', 'a'))
1591 (func
1592 (func
1592 ('symbol', 'filelog')
1593 ('symbol', 'filelog')
1593 ('string', 'b')))))
1594 ('string', 'b')))))
1594
1595
1595 Test falling back to slow path for non-existing files
1596 Test falling back to slow path for non-existing files
1596
1597
1597 $ testlog a c
1598 $ testlog a c
1598 []
1599 []
1599 (group
1600 (group
1600 (func
1601 (func
1601 ('symbol', '_matchfiles')
1602 ('symbol', '_matchfiles')
1602 (list
1603 (list
1603 (list
1604 (list
1604 (list
1605 (list
1605 ('string', 'r:')
1606 ('string', 'r:')
1606 ('string', 'd:relpath'))
1607 ('string', 'd:relpath'))
1607 ('string', 'p:a'))
1608 ('string', 'p:a'))
1608 ('string', 'p:c'))))
1609 ('string', 'p:c'))))
1609
1610
1610 Test multiple --include/--exclude/paths
1611 Test multiple --include/--exclude/paths
1611
1612
1612 $ testlog --include a --include e --exclude b --exclude e a e
1613 $ testlog --include a --include e --exclude b --exclude e a e
1613 []
1614 []
1614 (group
1615 (group
1615 (func
1616 (func
1616 ('symbol', '_matchfiles')
1617 ('symbol', '_matchfiles')
1617 (list
1618 (list
1618 (list
1619 (list
1619 (list
1620 (list
1620 (list
1621 (list
1621 (list
1622 (list
1622 (list
1623 (list
1623 (list
1624 (list
1624 ('string', 'r:')
1625 ('string', 'r:')
1625 ('string', 'd:relpath'))
1626 ('string', 'd:relpath'))
1626 ('string', 'p:a'))
1627 ('string', 'p:a'))
1627 ('string', 'p:e'))
1628 ('string', 'p:e'))
1628 ('string', 'i:a'))
1629 ('string', 'i:a'))
1629 ('string', 'i:e'))
1630 ('string', 'i:e'))
1630 ('string', 'x:b'))
1631 ('string', 'x:b'))
1631 ('string', 'x:e'))))
1632 ('string', 'x:e'))))
1632
1633
1633 Test glob expansion of pats
1634 Test glob expansion of pats
1634
1635
1635 $ expandglobs=`python -c "import mercurial.util; \
1636 $ expandglobs=`python -c "import mercurial.util; \
1636 > print mercurial.util.expandglobs and 'true' or 'false'"`
1637 > print mercurial.util.expandglobs and 'true' or 'false'"`
1637 $ if [ $expandglobs = "true" ]; then
1638 $ if [ $expandglobs = "true" ]; then
1638 > testlog 'a*';
1639 > testlog 'a*';
1639 > else
1640 > else
1640 > testlog a*;
1641 > testlog a*;
1641 > fi;
1642 > fi;
1642 []
1643 []
1643 (group
1644 (group
1644 (group
1645 (group
1645 (func
1646 (func
1646 ('symbol', 'filelog')
1647 ('symbol', 'filelog')
1647 ('string', 'aa'))))
1648 ('string', 'aa'))))
1648
1649
1649 Test --follow on a directory
1650 Test --follow on a directory
1650
1651
1651 $ testlog -f dir
1652 $ testlog -f dir
1652 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1653 abort: cannot follow file not in parent revision: "dir"
1654 abort: cannot follow file not in parent revision: "dir"
1654 abort: cannot follow file not in parent revision: "dir"
1655 abort: cannot follow file not in parent revision: "dir"
1655
1656
1656 Test --follow on file not in parent revision
1657 Test --follow on file not in parent revision
1657
1658
1658 $ testlog -f a
1659 $ testlog -f a
1659 abort: cannot follow file not in parent revision: "a"
1660 abort: cannot follow file not in parent revision: "a"
1660 abort: cannot follow file not in parent revision: "a"
1661 abort: cannot follow file not in parent revision: "a"
1661 abort: cannot follow file not in parent revision: "a"
1662 abort: cannot follow file not in parent revision: "a"
1662
1663
1663 Test --follow and patterns
1664 Test --follow and patterns
1664
1665
1665 $ testlog -f 'glob:*'
1666 $ testlog -f 'glob:*'
1666 abort: can only follow copies/renames for explicit filenames
1667 abort: can only follow copies/renames for explicit filenames
1667 abort: can only follow copies/renames for explicit filenames
1668 abort: can only follow copies/renames for explicit filenames
1668 abort: can only follow copies/renames for explicit filenames
1669 abort: can only follow copies/renames for explicit filenames
1669
1670
1670 Test --follow on a single rename
1671 Test --follow on a single rename
1671
1672
1672 $ hg up -q 2
1673 $ hg up -q 2
1673 $ testlog -f a
1674 $ testlog -f a
1674 []
1675 []
1675 (group
1676 (group
1676 (group
1677 (group
1677 (func
1678 (func
1678 ('symbol', 'follow')
1679 ('symbol', 'follow')
1679 ('string', 'a'))))
1680 ('string', 'a'))))
1680
1681
1681 Test --follow and multiple renames
1682 Test --follow and multiple renames
1682
1683
1683 $ hg up -q tip
1684 $ hg up -q tip
1684 $ testlog -f e
1685 $ testlog -f e
1685 []
1686 []
1686 (group
1687 (group
1687 (group
1688 (group
1688 (func
1689 (func
1689 ('symbol', 'follow')
1690 ('symbol', 'follow')
1690 ('string', 'e'))))
1691 ('string', 'e'))))
1691
1692
1692 Test --follow and multiple filelog heads
1693 Test --follow and multiple filelog heads
1693
1694
1694 $ hg up -q 2
1695 $ hg up -q 2
1695 $ testlog -f g
1696 $ testlog -f g
1696 []
1697 []
1697 (group
1698 (group
1698 (group
1699 (group
1699 (func
1700 (func
1700 ('symbol', 'follow')
1701 ('symbol', 'follow')
1701 ('string', 'g'))))
1702 ('string', 'g'))))
1702 $ cat log.nodes
1703 $ cat log.nodes
1703 nodetag 2
1704 nodetag 2
1704 nodetag 1
1705 nodetag 1
1705 nodetag 0
1706 nodetag 0
1706 $ hg up -q tip
1707 $ hg up -q tip
1707 $ testlog -f g
1708 $ testlog -f g
1708 []
1709 []
1709 (group
1710 (group
1710 (group
1711 (group
1711 (func
1712 (func
1712 ('symbol', 'follow')
1713 ('symbol', 'follow')
1713 ('string', 'g'))))
1714 ('string', 'g'))))
1714 $ cat log.nodes
1715 $ cat log.nodes
1715 nodetag 3
1716 nodetag 3
1716 nodetag 2
1717 nodetag 2
1717 nodetag 0
1718 nodetag 0
1718
1719
1719 Test --follow and multiple files
1720 Test --follow and multiple files
1720
1721
1721 $ testlog -f g e
1722 $ testlog -f g e
1722 []
1723 []
1723 (group
1724 (group
1724 (group
1725 (group
1725 (or
1726 (or
1726 (func
1727 (func
1727 ('symbol', 'follow')
1728 ('symbol', 'follow')
1728 ('string', 'g'))
1729 ('string', 'g'))
1729 (func
1730 (func
1730 ('symbol', 'follow')
1731 ('symbol', 'follow')
1731 ('string', 'e')))))
1732 ('string', 'e')))))
1732 $ cat log.nodes
1733 $ cat log.nodes
1733 nodetag 4
1734 nodetag 4
1734 nodetag 3
1735 nodetag 3
1735 nodetag 2
1736 nodetag 2
1736 nodetag 1
1737 nodetag 1
1737 nodetag 0
1738 nodetag 0
1738
1739
1739 Test --follow-first
1740 Test --follow-first
1740
1741
1741 $ hg up -q 3
1742 $ hg up -q 3
1742 $ echo ee > e
1743 $ echo ee > e
1743 $ hg ci -Am "add another e" e
1744 $ hg ci -Am "add another e" e
1744 created new head
1745 created new head
1745 $ hg merge --tool internal:other 4
1746 $ hg merge --tool internal:other 4
1746 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1747 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1747 (branch merge, don't forget to commit)
1748 (branch merge, don't forget to commit)
1748 $ echo merge > e
1749 $ echo merge > e
1749 $ hg ci -m "merge 5 and 4"
1750 $ hg ci -m "merge 5 and 4"
1750 $ testlog --follow-first
1751 $ testlog --follow-first
1751 []
1752 []
1752 (group
1753 (group
1753 (func
1754 (func
1754 ('symbol', '_firstancestors')
1755 ('symbol', '_firstancestors')
1755 ('symbol', '6')))
1756 ('symbol', '6')))
1756
1757
1757 Cannot compare with log --follow-first FILE as it never worked
1758 Cannot compare with log --follow-first FILE as it never worked
1758
1759
1759 $ hg log -G --print-revset --follow-first e
1760 $ hg log -G --print-revset --follow-first e
1760 []
1761 []
1761 (group
1762 (group
1762 (group
1763 (group
1763 (func
1764 (func
1764 ('symbol', '_followfirst')
1765 ('symbol', '_followfirst')
1765 ('string', 'e'))))
1766 ('string', 'e'))))
1766 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1767 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1767 @ 6 merge 5 and 4
1768 @ 6 merge 5 and 4
1768 |\
1769 |\
1769 o | 5 add another e
1770 o | 5 add another e
1770 | |
1771 | |
1771
1772
1772 Test --copies
1773 Test --copies
1773
1774
1774 $ hg log -G --copies --template "{rev} {desc|firstline} \
1775 $ hg log -G --copies --template "{rev} {desc|firstline} \
1775 > copies: {file_copies_switch}\n"
1776 > copies: {file_copies_switch}\n"
1776 @ 6 merge 5 and 4 copies:
1777 @ 6 merge 5 and 4 copies:
1777 |\
1778 |\
1778 | o 5 add another e copies:
1779 | o 5 add another e copies:
1779 | |
1780 | |
1780 o | 4 mv dir/b e copies: e (dir/b)
1781 o | 4 mv dir/b e copies: e (dir/b)
1781 |/
1782 |/
1782 o 3 mv a b; add d copies: b (a)g (f)
1783 o 3 mv a b; add d copies: b (a)g (f)
1783 |
1784 |
1784 o 2 mv b dir/b copies: dir/b (b)
1785 o 2 mv b dir/b copies: dir/b (b)
1785 |
1786 |
1786 o 1 copy a b copies: b (a)g (f)
1787 o 1 copy a b copies: b (a)g (f)
1787 |
1788 |
1788 o 0 add a copies:
1789 o 0 add a copies:
1789
1790
1790 Test "set:..." and parent revision
1791 Test "set:..." and parent revision
1791
1792
1792 $ hg up -q 4
1793 $ hg up -q 4
1793 $ testlog "set:copied()"
1794 $ testlog "set:copied()"
1794 []
1795 []
1795 (group
1796 (group
1796 (func
1797 (func
1797 ('symbol', '_matchfiles')
1798 ('symbol', '_matchfiles')
1798 (list
1799 (list
1799 (list
1800 (list
1800 ('string', 'r:')
1801 ('string', 'r:')
1801 ('string', 'd:relpath'))
1802 ('string', 'd:relpath'))
1802 ('string', 'p:set:copied()'))))
1803 ('string', 'p:set:copied()'))))
1803 $ testlog --include "set:copied()"
1804 $ testlog --include "set:copied()"
1804 []
1805 []
1805 (group
1806 (group
1806 (func
1807 (func
1807 ('symbol', '_matchfiles')
1808 ('symbol', '_matchfiles')
1808 (list
1809 (list
1809 (list
1810 (list
1810 ('string', 'r:')
1811 ('string', 'r:')
1811 ('string', 'd:relpath'))
1812 ('string', 'd:relpath'))
1812 ('string', 'i:set:copied()'))))
1813 ('string', 'i:set:copied()'))))
1813 $ testlog -r "sort(file('set:copied()'), -rev)"
1814 $ testlog -r "sort(file('set:copied()'), -rev)"
1814 ["sort(file('set:copied()'), -rev)"]
1815 ["sort(file('set:copied()'), -rev)"]
1815 []
1816 []
1816
1817
1817 Test --removed
1818 Test --removed
1818
1819
1819 $ testlog --removed
1820 $ testlog --removed
1820 []
1821 []
1821 []
1822 []
1822 $ testlog --removed a
1823 $ testlog --removed a
1823 []
1824 []
1824 (group
1825 (group
1825 (func
1826 (func
1826 ('symbol', '_matchfiles')
1827 ('symbol', '_matchfiles')
1827 (list
1828 (list
1828 (list
1829 (list
1829 ('string', 'r:')
1830 ('string', 'r:')
1830 ('string', 'd:relpath'))
1831 ('string', 'd:relpath'))
1831 ('string', 'p:a'))))
1832 ('string', 'p:a'))))
1832 $ testlog --removed --follow a
1833 $ testlog --removed --follow a
1833 abort: can only follow copies/renames for explicit filenames
1834 abort: can only follow copies/renames for explicit filenames
1834 abort: can only follow copies/renames for explicit filenames
1835 abort: can only follow copies/renames for explicit filenames
1835 abort: can only follow copies/renames for explicit filenames
1836 abort: can only follow copies/renames for explicit filenames
1836
1837
1837 Test --patch and --stat with --follow and --follow-first
1838 Test --patch and --stat with --follow and --follow-first
1838
1839
1839 $ hg up -q 3
1840 $ hg up -q 3
1840 $ hg log -G --git --patch b
1841 $ hg log -G --git --patch b
1841 o changeset: 1:216d4c92cf98
1842 o changeset: 1:216d4c92cf98
1842 | user: test
1843 | user: test
1843 | date: Thu Jan 01 00:00:00 1970 +0000
1844 | date: Thu Jan 01 00:00:00 1970 +0000
1844 | summary: copy a b
1845 | summary: copy a b
1845 |
1846 |
1846 | diff --git a/a b/b
1847 | diff --git a/a b/b
1847 | copy from a
1848 | copy from a
1848 | copy to b
1849 | copy to b
1849 |
1850 |
1850
1851
1851 $ hg log -G --git --stat b
1852 $ hg log -G --git --stat b
1852 o changeset: 1:216d4c92cf98
1853 o changeset: 1:216d4c92cf98
1853 | user: test
1854 | user: test
1854 | date: Thu Jan 01 00:00:00 1970 +0000
1855 | date: Thu Jan 01 00:00:00 1970 +0000
1855 | summary: copy a b
1856 | summary: copy a b
1856 |
1857 |
1857 | a | 0
1858 | a | 0
1858 | 1 files changed, 0 insertions(+), 0 deletions(-)
1859 | 1 files changed, 0 insertions(+), 0 deletions(-)
1859 |
1860 |
1860
1861
1861 $ hg log -G --git --patch --follow b
1862 $ hg log -G --git --patch --follow b
1862 o changeset: 1:216d4c92cf98
1863 o changeset: 1:216d4c92cf98
1863 | user: test
1864 | user: test
1864 | date: Thu Jan 01 00:00:00 1970 +0000
1865 | date: Thu Jan 01 00:00:00 1970 +0000
1865 | summary: copy a b
1866 | summary: copy a b
1866 |
1867 |
1867 | diff --git a/a b/b
1868 | diff --git a/a b/b
1868 | copy from a
1869 | copy from a
1869 | copy to b
1870 | copy to b
1870 |
1871 |
1871 o changeset: 0:f8035bb17114
1872 o changeset: 0:f8035bb17114
1872 user: test
1873 user: test
1873 date: Thu Jan 01 00:00:00 1970 +0000
1874 date: Thu Jan 01 00:00:00 1970 +0000
1874 summary: add a
1875 summary: add a
1875
1876
1876 diff --git a/a b/a
1877 diff --git a/a b/a
1877 new file mode 100644
1878 new file mode 100644
1878 --- /dev/null
1879 --- /dev/null
1879 +++ b/a
1880 +++ b/a
1880 @@ -0,0 +1,1 @@
1881 @@ -0,0 +1,1 @@
1881 +a
1882 +a
1882
1883
1883
1884
1884 $ hg log -G --git --stat --follow b
1885 $ hg log -G --git --stat --follow b
1885 o changeset: 1:216d4c92cf98
1886 o changeset: 1:216d4c92cf98
1886 | user: test
1887 | user: test
1887 | date: Thu Jan 01 00:00:00 1970 +0000
1888 | date: Thu Jan 01 00:00:00 1970 +0000
1888 | summary: copy a b
1889 | summary: copy a b
1889 |
1890 |
1890 | a | 0
1891 | a | 0
1891 | 1 files changed, 0 insertions(+), 0 deletions(-)
1892 | 1 files changed, 0 insertions(+), 0 deletions(-)
1892 |
1893 |
1893 o changeset: 0:f8035bb17114
1894 o changeset: 0:f8035bb17114
1894 user: test
1895 user: test
1895 date: Thu Jan 01 00:00:00 1970 +0000
1896 date: Thu Jan 01 00:00:00 1970 +0000
1896 summary: add a
1897 summary: add a
1897
1898
1898 a | 1 +
1899 a | 1 +
1899 1 files changed, 1 insertions(+), 0 deletions(-)
1900 1 files changed, 1 insertions(+), 0 deletions(-)
1900
1901
1901
1902
1902 $ hg up -q 6
1903 $ hg up -q 6
1903 $ hg log -G --git --patch --follow-first e
1904 $ hg log -G --git --patch --follow-first e
1904 @ changeset: 6:fc281d8ff18d
1905 @ changeset: 6:fc281d8ff18d
1905 |\ tag: tip
1906 |\ tag: tip
1906 | | parent: 5:99b31f1c2782
1907 | | parent: 5:99b31f1c2782
1907 | | parent: 4:17d952250a9d
1908 | | parent: 4:17d952250a9d
1908 | | user: test
1909 | | user: test
1909 | | date: Thu Jan 01 00:00:00 1970 +0000
1910 | | date: Thu Jan 01 00:00:00 1970 +0000
1910 | | summary: merge 5 and 4
1911 | | summary: merge 5 and 4
1911 | |
1912 | |
1912 | | diff --git a/e b/e
1913 | | diff --git a/e b/e
1913 | | --- a/e
1914 | | --- a/e
1914 | | +++ b/e
1915 | | +++ b/e
1915 | | @@ -1,1 +1,1 @@
1916 | | @@ -1,1 +1,1 @@
1916 | | -ee
1917 | | -ee
1917 | | +merge
1918 | | +merge
1918 | |
1919 | |
1919 o | changeset: 5:99b31f1c2782
1920 o | changeset: 5:99b31f1c2782
1920 | | parent: 3:5918b8d165d1
1921 | | parent: 3:5918b8d165d1
1921 | | user: test
1922 | | user: test
1922 | | date: Thu Jan 01 00:00:00 1970 +0000
1923 | | date: Thu Jan 01 00:00:00 1970 +0000
1923 | | summary: add another e
1924 | | summary: add another e
1924 | |
1925 | |
1925 | | diff --git a/e b/e
1926 | | diff --git a/e b/e
1926 | | new file mode 100644
1927 | | new file mode 100644
1927 | | --- /dev/null
1928 | | --- /dev/null
1928 | | +++ b/e
1929 | | +++ b/e
1929 | | @@ -0,0 +1,1 @@
1930 | | @@ -0,0 +1,1 @@
1930 | | +ee
1931 | | +ee
1931 | |
1932 | |
1932
1933
1933 Test old-style --rev
1934 Test old-style --rev
1934
1935
1935 $ hg tag 'foo-bar'
1936 $ hg tag 'foo-bar'
1936 $ testlog -r 'foo-bar'
1937 $ testlog -r 'foo-bar'
1937 ['foo-bar']
1938 ['foo-bar']
1938 []
1939 []
1939
1940
1940 Test --follow and forward --rev
1941 Test --follow and forward --rev
1941
1942
1942 $ hg up -q 6
1943 $ hg up -q 6
1943 $ echo g > g
1944 $ echo g > g
1944 $ hg ci -Am 'add g' g
1945 $ hg ci -Am 'add g' g
1945 created new head
1946 created new head
1946 $ hg up -q 2
1947 $ hg up -q 2
1947 $ hg log -G --template "{rev} {desc|firstline}\n"
1948 $ hg log -G --template "{rev} {desc|firstline}\n"
1948 o 8 add g
1949 o 8 add g
1949 |
1950 |
1950 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1951 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1951 |/
1952 |/
1952 o 6 merge 5 and 4
1953 o 6 merge 5 and 4
1953 |\
1954 |\
1954 | o 5 add another e
1955 | o 5 add another e
1955 | |
1956 | |
1956 o | 4 mv dir/b e
1957 o | 4 mv dir/b e
1957 |/
1958 |/
1958 o 3 mv a b; add d
1959 o 3 mv a b; add d
1959 |
1960 |
1960 @ 2 mv b dir/b
1961 @ 2 mv b dir/b
1961 |
1962 |
1962 o 1 copy a b
1963 o 1 copy a b
1963 |
1964 |
1964 o 0 add a
1965 o 0 add a
1965
1966
1966 $ testlog --follow -r6 -r8 -r5 -r7 -r4
1967 $ testlog --follow -r6 -r8 -r5 -r7 -r4
1967 ['6', '8', '5', '7', '4']
1968 ['6', '8', '5', '7', '4']
1968 (group
1969 (group
1969 (func
1970 (func
1970 ('symbol', 'descendants')
1971 ('symbol', 'descendants')
1971 ('symbol', '6')))
1972 ('symbol', '6')))
1972 --- log.nodes * (glob)
1973 --- log.nodes * (glob)
1973 +++ glog.nodes * (glob)
1974 +++ glog.nodes * (glob)
1974 @@ -1,3 +1,3 @@
1975 @@ -1,3 +1,3 @@
1975 -nodetag 6
1976 -nodetag 6
1976 nodetag 8
1977 nodetag 8
1977 nodetag 7
1978 nodetag 7
1978 +nodetag 6
1979 +nodetag 6
1979
1980
1980 Test --follow-first and forward --rev
1981 Test --follow-first and forward --rev
1981
1982
1982 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
1983 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
1983 ['6', '8', '5', '7', '4']
1984 ['6', '8', '5', '7', '4']
1984 (group
1985 (group
1985 (func
1986 (func
1986 ('symbol', '_firstdescendants')
1987 ('symbol', '_firstdescendants')
1987 ('symbol', '6')))
1988 ('symbol', '6')))
1988 --- log.nodes * (glob)
1989 --- log.nodes * (glob)
1989 +++ glog.nodes * (glob)
1990 +++ glog.nodes * (glob)
1990 @@ -1,3 +1,3 @@
1991 @@ -1,3 +1,3 @@
1991 -nodetag 6
1992 -nodetag 6
1992 nodetag 8
1993 nodetag 8
1993 nodetag 7
1994 nodetag 7
1994 +nodetag 6
1995 +nodetag 6
1995
1996
1996 Test --follow and backward --rev
1997 Test --follow and backward --rev
1997
1998
1998 $ testlog --follow -r6 -r5 -r7 -r8 -r4
1999 $ testlog --follow -r6 -r5 -r7 -r8 -r4
1999 ['6', '5', '7', '8', '4']
2000 ['6', '5', '7', '8', '4']
2000 (group
2001 (group
2001 (func
2002 (func
2002 ('symbol', 'ancestors')
2003 ('symbol', 'ancestors')
2003 ('symbol', '6')))
2004 ('symbol', '6')))
2004
2005
2005 Test --follow-first and backward --rev
2006 Test --follow-first and backward --rev
2006
2007
2007 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2008 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2008 ['6', '5', '7', '8', '4']
2009 ['6', '5', '7', '8', '4']
2009 (group
2010 (group
2010 (func
2011 (func
2011 ('symbol', '_firstancestors')
2012 ('symbol', '_firstancestors')
2012 ('symbol', '6')))
2013 ('symbol', '6')))
2013
2014
2014 Test subdir
2015 Test subdir
2015
2016
2016 $ hg up -q 3
2017 $ hg up -q 3
2017 $ cd dir
2018 $ cd dir
2018 $ testlog .
2019 $ testlog .
2019 []
2020 []
2020 (group
2021 (group
2021 (func
2022 (func
2022 ('symbol', '_matchfiles')
2023 ('symbol', '_matchfiles')
2023 (list
2024 (list
2024 (list
2025 (list
2025 ('string', 'r:')
2026 ('string', 'r:')
2026 ('string', 'd:relpath'))
2027 ('string', 'd:relpath'))
2027 ('string', 'p:.'))))
2028 ('string', 'p:.'))))
2028 $ testlog ../b
2029 $ testlog ../b
2029 []
2030 []
2030 (group
2031 (group
2031 (group
2032 (group
2032 (func
2033 (func
2033 ('symbol', 'filelog')
2034 ('symbol', 'filelog')
2034 ('string', '../b'))))
2035 ('string', '../b'))))
2035 $ testlog -f ../b
2036 $ testlog -f ../b
2036 []
2037 []
2037 (group
2038 (group
2038 (group
2039 (group
2039 (func
2040 (func
2040 ('symbol', 'follow')
2041 ('symbol', 'follow')
2041 ('string', 'b'))))
2042 ('string', 'b'))))
2042 $ cd ..
2043 $ cd ..
2043
2044
2044 Test --hidden
2045 Test --hidden
2045 (enable obsolete)
2046 (enable obsolete)
2046
2047
2047 $ cat > ${TESTTMP}/obs.py << EOF
2048 $ cat > ${TESTTMP}/obs.py << EOF
2048 > import mercurial.obsolete
2049 > import mercurial.obsolete
2049 > mercurial.obsolete._enabled = True
2050 > mercurial.obsolete._enabled = True
2050 > EOF
2051 > EOF
2051 $ echo '[extensions]' >> $HGRCPATH
2052 $ echo '[extensions]' >> $HGRCPATH
2052 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
2053 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
2053
2054
2054 $ hg debugobsolete `hg id --debug -i -r 8`
2055 $ hg debugobsolete `hg id --debug -i -r 8`
2055 $ testlog
2056 $ testlog
2056 []
2057 []
2057 []
2058 []
2058 $ testlog --hidden
2059 $ testlog --hidden
2059 []
2060 []
2060 []
2061 []
2061 $ hg glog --template '{rev} {desc}\n'
2062 $ hg glog --template '{rev} {desc}\n'
2062 o 7 Added tag foo-bar for changeset fc281d8ff18d
2063 o 7 Added tag foo-bar for changeset fc281d8ff18d
2063 |
2064 |
2064 o 6 merge 5 and 4
2065 o 6 merge 5 and 4
2065 |\
2066 |\
2066 | o 5 add another e
2067 | o 5 add another e
2067 | |
2068 | |
2068 o | 4 mv dir/b e
2069 o | 4 mv dir/b e
2069 |/
2070 |/
2070 @ 3 mv a b; add d
2071 @ 3 mv a b; add d
2071 |
2072 |
2072 o 2 mv b dir/b
2073 o 2 mv b dir/b
2073 |
2074 |
2074 o 1 copy a b
2075 o 1 copy a b
2075 |
2076 |
2076 o 0 add a
2077 o 0 add a
2077
2078
2078
2079
2079 A template without trailing newline should do something sane
2080 A template without trailing newline should do something sane
2080
2081
2081 $ hg glog -r ::2 --template '{rev} {desc}'
2082 $ hg glog -r ::2 --template '{rev} {desc}'
2082 o 2 mv b dir/b
2083 o 2 mv b dir/b
2083 |
2084 |
2084 o 1 copy a b
2085 o 1 copy a b
2085 |
2086 |
2086 o 0 add a
2087 o 0 add a
2087
2088
2088
2089
2089 Extra newlines must be preserved
2090 Extra newlines must be preserved
2090
2091
2091 $ hg glog -r ::2 --template '\n{rev} {desc}\n\n'
2092 $ hg glog -r ::2 --template '\n{rev} {desc}\n\n'
2092 o
2093 o
2093 | 2 mv b dir/b
2094 | 2 mv b dir/b
2094 |
2095 |
2095 o
2096 o
2096 | 1 copy a b
2097 | 1 copy a b
2097 |
2098 |
2098 o
2099 o
2099 0 add a
2100 0 add a
2100
2101
2101
2102
2102 The almost-empty template should do something sane too ...
2103 The almost-empty template should do something sane too ...
2103
2104
2104 $ hg glog -r ::2 --template '\n'
2105 $ hg glog -r ::2 --template '\n'
2105 o
2106 o
2106 |
2107 |
2107 o
2108 o
2108 |
2109 |
2109 o
2110 o
2110
2111
2111
2112
2112 issue3772
2113 issue3772
2113
2114
2114 $ hg glog -r :null
2115 $ hg glog -r :null
2115 o changeset: -1:000000000000
2116 o changeset: -1:000000000000
2116 user:
2117 user:
2117 date: Thu Jan 01 00:00:00 1970 +0000
2118 date: Thu Jan 01 00:00:00 1970 +0000
2118
2119
2119 $ hg glog -r null:null
2120 $ hg glog -r null:null
2120 o changeset: -1:000000000000
2121 o changeset: -1:000000000000
2121 user:
2122 user:
2122 date: Thu Jan 01 00:00:00 1970 +0000
2123 date: Thu Jan 01 00:00:00 1970 +0000
2123
2124
2124
2125
2125 $ cd ..
2126 $ cd ..
@@ -1,1330 +1,1347 b''
1 Log on empty repository: checking consistency
2
3 $ hg init empty
4 $ cd empty
5 $ hg log
6 $ hg log -r 1
7 abort: unknown revision '1'!
8 [255]
9 $ hg log -r -1:0
10 abort: unknown revision '-1'!
11 [255]
12 $ hg log -r 'branch(name)'
13 abort: unknown revision 'name'!
14 [255]
15 $ hg log -r null -q
16 -1:000000000000
17
1 The g is crafted to have 2 filelog topological heads in a linear
18 The g is crafted to have 2 filelog topological heads in a linear
2 changeset graph
19 changeset graph
3
20
4 $ hg init a
21 $ hg init a
5 $ cd a
22 $ cd a
6 $ echo a > a
23 $ echo a > a
7 $ echo f > f
24 $ echo f > f
8 $ hg ci -Ama -d '1 0'
25 $ hg ci -Ama -d '1 0'
9 adding a
26 adding a
10 adding f
27 adding f
11
28
12 $ hg cp a b
29 $ hg cp a b
13 $ hg cp f g
30 $ hg cp f g
14 $ hg ci -mb -d '2 0'
31 $ hg ci -mb -d '2 0'
15
32
16 $ mkdir dir
33 $ mkdir dir
17 $ hg mv b dir
34 $ hg mv b dir
18 $ echo g >> g
35 $ echo g >> g
19 $ echo f >> f
36 $ echo f >> f
20 $ hg ci -mc -d '3 0'
37 $ hg ci -mc -d '3 0'
21
38
22 $ hg mv a b
39 $ hg mv a b
23 $ hg cp -f f g
40 $ hg cp -f f g
24 $ echo a > d
41 $ echo a > d
25 $ hg add d
42 $ hg add d
26 $ hg ci -md -d '4 0'
43 $ hg ci -md -d '4 0'
27
44
28 $ hg mv dir/b e
45 $ hg mv dir/b e
29 $ hg ci -me -d '5 0'
46 $ hg ci -me -d '5 0'
30
47
31 $ hg log a
48 $ hg log a
32 changeset: 0:9161b9aeaf16
49 changeset: 0:9161b9aeaf16
33 user: test
50 user: test
34 date: Thu Jan 01 00:00:01 1970 +0000
51 date: Thu Jan 01 00:00:01 1970 +0000
35 summary: a
52 summary: a
36
53
37 log on directory
54 log on directory
38
55
39 $ hg log dir
56 $ hg log dir
40 changeset: 4:7e4639b4691b
57 changeset: 4:7e4639b4691b
41 tag: tip
58 tag: tip
42 user: test
59 user: test
43 date: Thu Jan 01 00:00:05 1970 +0000
60 date: Thu Jan 01 00:00:05 1970 +0000
44 summary: e
61 summary: e
45
62
46 changeset: 2:f8954cd4dc1f
63 changeset: 2:f8954cd4dc1f
47 user: test
64 user: test
48 date: Thu Jan 01 00:00:03 1970 +0000
65 date: Thu Jan 01 00:00:03 1970 +0000
49 summary: c
66 summary: c
50
67
51 $ hg log somethingthatdoesntexist dir
68 $ hg log somethingthatdoesntexist dir
52 changeset: 4:7e4639b4691b
69 changeset: 4:7e4639b4691b
53 tag: tip
70 tag: tip
54 user: test
71 user: test
55 date: Thu Jan 01 00:00:05 1970 +0000
72 date: Thu Jan 01 00:00:05 1970 +0000
56 summary: e
73 summary: e
57
74
58 changeset: 2:f8954cd4dc1f
75 changeset: 2:f8954cd4dc1f
59 user: test
76 user: test
60 date: Thu Jan 01 00:00:03 1970 +0000
77 date: Thu Jan 01 00:00:03 1970 +0000
61 summary: c
78 summary: c
62
79
63
80
64 -f, directory
81 -f, directory
65
82
66 $ hg log -f dir
83 $ hg log -f dir
67 abort: cannot follow file not in parent revision: "dir"
84 abort: cannot follow file not in parent revision: "dir"
68 [255]
85 [255]
69
86
70 -f, but no args
87 -f, but no args
71
88
72 $ hg log -f
89 $ hg log -f
73 changeset: 4:7e4639b4691b
90 changeset: 4:7e4639b4691b
74 tag: tip
91 tag: tip
75 user: test
92 user: test
76 date: Thu Jan 01 00:00:05 1970 +0000
93 date: Thu Jan 01 00:00:05 1970 +0000
77 summary: e
94 summary: e
78
95
79 changeset: 3:2ca5ba701980
96 changeset: 3:2ca5ba701980
80 user: test
97 user: test
81 date: Thu Jan 01 00:00:04 1970 +0000
98 date: Thu Jan 01 00:00:04 1970 +0000
82 summary: d
99 summary: d
83
100
84 changeset: 2:f8954cd4dc1f
101 changeset: 2:f8954cd4dc1f
85 user: test
102 user: test
86 date: Thu Jan 01 00:00:03 1970 +0000
103 date: Thu Jan 01 00:00:03 1970 +0000
87 summary: c
104 summary: c
88
105
89 changeset: 1:d89b0a12d229
106 changeset: 1:d89b0a12d229
90 user: test
107 user: test
91 date: Thu Jan 01 00:00:02 1970 +0000
108 date: Thu Jan 01 00:00:02 1970 +0000
92 summary: b
109 summary: b
93
110
94 changeset: 0:9161b9aeaf16
111 changeset: 0:9161b9aeaf16
95 user: test
112 user: test
96 date: Thu Jan 01 00:00:01 1970 +0000
113 date: Thu Jan 01 00:00:01 1970 +0000
97 summary: a
114 summary: a
98
115
99
116
100 one rename
117 one rename
101
118
102 $ hg up -q 2
119 $ hg up -q 2
103 $ hg log -vf a
120 $ hg log -vf a
104 changeset: 0:9161b9aeaf16
121 changeset: 0:9161b9aeaf16
105 user: test
122 user: test
106 date: Thu Jan 01 00:00:01 1970 +0000
123 date: Thu Jan 01 00:00:01 1970 +0000
107 files: a f
124 files: a f
108 description:
125 description:
109 a
126 a
110
127
111
128
112
129
113 many renames
130 many renames
114
131
115 $ hg up -q tip
132 $ hg up -q tip
116 $ hg log -vf e
133 $ hg log -vf e
117 changeset: 4:7e4639b4691b
134 changeset: 4:7e4639b4691b
118 tag: tip
135 tag: tip
119 user: test
136 user: test
120 date: Thu Jan 01 00:00:05 1970 +0000
137 date: Thu Jan 01 00:00:05 1970 +0000
121 files: dir/b e
138 files: dir/b e
122 description:
139 description:
123 e
140 e
124
141
125
142
126 changeset: 2:f8954cd4dc1f
143 changeset: 2:f8954cd4dc1f
127 user: test
144 user: test
128 date: Thu Jan 01 00:00:03 1970 +0000
145 date: Thu Jan 01 00:00:03 1970 +0000
129 files: b dir/b f g
146 files: b dir/b f g
130 description:
147 description:
131 c
148 c
132
149
133
150
134 changeset: 1:d89b0a12d229
151 changeset: 1:d89b0a12d229
135 user: test
152 user: test
136 date: Thu Jan 01 00:00:02 1970 +0000
153 date: Thu Jan 01 00:00:02 1970 +0000
137 files: b g
154 files: b g
138 description:
155 description:
139 b
156 b
140
157
141
158
142 changeset: 0:9161b9aeaf16
159 changeset: 0:9161b9aeaf16
143 user: test
160 user: test
144 date: Thu Jan 01 00:00:01 1970 +0000
161 date: Thu Jan 01 00:00:01 1970 +0000
145 files: a f
162 files: a f
146 description:
163 description:
147 a
164 a
148
165
149
166
150
167
151
168
152 log -pf dir/b
169 log -pf dir/b
153
170
154 $ hg up -q 3
171 $ hg up -q 3
155 $ hg log -pf dir/b
172 $ hg log -pf dir/b
156 changeset: 2:f8954cd4dc1f
173 changeset: 2:f8954cd4dc1f
157 user: test
174 user: test
158 date: Thu Jan 01 00:00:03 1970 +0000
175 date: Thu Jan 01 00:00:03 1970 +0000
159 summary: c
176 summary: c
160
177
161 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
178 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
179 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
180 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
164 @@ -0,0 +1,1 @@
181 @@ -0,0 +1,1 @@
165 +a
182 +a
166
183
167 changeset: 1:d89b0a12d229
184 changeset: 1:d89b0a12d229
168 user: test
185 user: test
169 date: Thu Jan 01 00:00:02 1970 +0000
186 date: Thu Jan 01 00:00:02 1970 +0000
170 summary: b
187 summary: b
171
188
172 diff -r 9161b9aeaf16 -r d89b0a12d229 b
189 diff -r 9161b9aeaf16 -r d89b0a12d229 b
173 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
190 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
174 +++ b/b Thu Jan 01 00:00:02 1970 +0000
191 +++ b/b Thu Jan 01 00:00:02 1970 +0000
175 @@ -0,0 +1,1 @@
192 @@ -0,0 +1,1 @@
176 +a
193 +a
177
194
178 changeset: 0:9161b9aeaf16
195 changeset: 0:9161b9aeaf16
179 user: test
196 user: test
180 date: Thu Jan 01 00:00:01 1970 +0000
197 date: Thu Jan 01 00:00:01 1970 +0000
181 summary: a
198 summary: a
182
199
183 diff -r 000000000000 -r 9161b9aeaf16 a
200 diff -r 000000000000 -r 9161b9aeaf16 a
184 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
201 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
185 +++ b/a Thu Jan 01 00:00:01 1970 +0000
202 +++ b/a Thu Jan 01 00:00:01 1970 +0000
186 @@ -0,0 +1,1 @@
203 @@ -0,0 +1,1 @@
187 +a
204 +a
188
205
189
206
190 log -vf dir/b
207 log -vf dir/b
191
208
192 $ hg log -vf dir/b
209 $ hg log -vf dir/b
193 changeset: 2:f8954cd4dc1f
210 changeset: 2:f8954cd4dc1f
194 user: test
211 user: test
195 date: Thu Jan 01 00:00:03 1970 +0000
212 date: Thu Jan 01 00:00:03 1970 +0000
196 files: b dir/b f g
213 files: b dir/b f g
197 description:
214 description:
198 c
215 c
199
216
200
217
201 changeset: 1:d89b0a12d229
218 changeset: 1:d89b0a12d229
202 user: test
219 user: test
203 date: Thu Jan 01 00:00:02 1970 +0000
220 date: Thu Jan 01 00:00:02 1970 +0000
204 files: b g
221 files: b g
205 description:
222 description:
206 b
223 b
207
224
208
225
209 changeset: 0:9161b9aeaf16
226 changeset: 0:9161b9aeaf16
210 user: test
227 user: test
211 date: Thu Jan 01 00:00:01 1970 +0000
228 date: Thu Jan 01 00:00:01 1970 +0000
212 files: a f
229 files: a f
213 description:
230 description:
214 a
231 a
215
232
216
233
217
234
218
235
219 -f and multiple filelog heads
236 -f and multiple filelog heads
220
237
221 $ hg up -q 2
238 $ hg up -q 2
222 $ hg log -f g --template '{rev}\n'
239 $ hg log -f g --template '{rev}\n'
223 2
240 2
224 1
241 1
225 0
242 0
226 $ hg up -q tip
243 $ hg up -q tip
227 $ hg log -f g --template '{rev}\n'
244 $ hg log -f g --template '{rev}\n'
228 3
245 3
229 2
246 2
230 0
247 0
231
248
232
249
233 log copies with --copies
250 log copies with --copies
234
251
235 $ hg log -vC --template '{rev} {file_copies}\n'
252 $ hg log -vC --template '{rev} {file_copies}\n'
236 4 e (dir/b)
253 4 e (dir/b)
237 3 b (a)g (f)
254 3 b (a)g (f)
238 2 dir/b (b)
255 2 dir/b (b)
239 1 b (a)g (f)
256 1 b (a)g (f)
240 0
257 0
241
258
242 log copies switch without --copies, with old filecopy template
259 log copies switch without --copies, with old filecopy template
243
260
244 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
261 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
245 4
262 4
246 3
263 3
247 2
264 2
248 1
265 1
249 0
266 0
250
267
251 log copies switch with --copies
268 log copies switch with --copies
252
269
253 $ hg log -vC --template '{rev} {file_copies_switch}\n'
270 $ hg log -vC --template '{rev} {file_copies_switch}\n'
254 4 e (dir/b)
271 4 e (dir/b)
255 3 b (a)g (f)
272 3 b (a)g (f)
256 2 dir/b (b)
273 2 dir/b (b)
257 1 b (a)g (f)
274 1 b (a)g (f)
258 0
275 0
259
276
260
277
261 log copies with hardcoded style and with --style=default
278 log copies with hardcoded style and with --style=default
262
279
263 $ hg log -vC -r4
280 $ hg log -vC -r4
264 changeset: 4:7e4639b4691b
281 changeset: 4:7e4639b4691b
265 tag: tip
282 tag: tip
266 user: test
283 user: test
267 date: Thu Jan 01 00:00:05 1970 +0000
284 date: Thu Jan 01 00:00:05 1970 +0000
268 files: dir/b e
285 files: dir/b e
269 copies: e (dir/b)
286 copies: e (dir/b)
270 description:
287 description:
271 e
288 e
272
289
273
290
274 $ hg log -vC -r4 --style=default
291 $ hg log -vC -r4 --style=default
275 changeset: 4:7e4639b4691b
292 changeset: 4:7e4639b4691b
276 tag: tip
293 tag: tip
277 user: test
294 user: test
278 date: Thu Jan 01 00:00:05 1970 +0000
295 date: Thu Jan 01 00:00:05 1970 +0000
279 files: dir/b e
296 files: dir/b e
280 copies: e (dir/b)
297 copies: e (dir/b)
281 description:
298 description:
282 e
299 e
283
300
284
301
285
302
286
303
287 log copies, non-linear manifest
304 log copies, non-linear manifest
288
305
289 $ hg up -C 3
306 $ hg up -C 3
290 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
307 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
291 $ hg mv dir/b e
308 $ hg mv dir/b e
292 $ echo foo > foo
309 $ echo foo > foo
293 $ hg ci -Ame2 -d '6 0'
310 $ hg ci -Ame2 -d '6 0'
294 adding foo
311 adding foo
295 created new head
312 created new head
296 $ hg log -v --template '{rev} {file_copies}\n' -r 5
313 $ hg log -v --template '{rev} {file_copies}\n' -r 5
297 5 e (dir/b)
314 5 e (dir/b)
298
315
299
316
300 log copies, execute bit set
317 log copies, execute bit set
301
318
302 #if execbit
319 #if execbit
303 $ chmod +x e
320 $ chmod +x e
304 $ hg ci -me3 -d '7 0'
321 $ hg ci -me3 -d '7 0'
305 $ hg log -v --template '{rev} {file_copies}\n' -r 6
322 $ hg log -v --template '{rev} {file_copies}\n' -r 6
306 6
323 6
307 #endif
324 #endif
308
325
309
326
310 log -p d
327 log -p d
311
328
312 $ hg log -pv d
329 $ hg log -pv d
313 changeset: 3:2ca5ba701980
330 changeset: 3:2ca5ba701980
314 user: test
331 user: test
315 date: Thu Jan 01 00:00:04 1970 +0000
332 date: Thu Jan 01 00:00:04 1970 +0000
316 files: a b d g
333 files: a b d g
317 description:
334 description:
318 d
335 d
319
336
320
337
321 diff -r f8954cd4dc1f -r 2ca5ba701980 d
338 diff -r f8954cd4dc1f -r 2ca5ba701980 d
322 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
339 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
323 +++ b/d Thu Jan 01 00:00:04 1970 +0000
340 +++ b/d Thu Jan 01 00:00:04 1970 +0000
324 @@ -0,0 +1,1 @@
341 @@ -0,0 +1,1 @@
325 +a
342 +a
326
343
327
344
328
345
329 log --removed file
346 log --removed file
330
347
331 $ hg log --removed -v a
348 $ hg log --removed -v a
332 changeset: 3:2ca5ba701980
349 changeset: 3:2ca5ba701980
333 user: test
350 user: test
334 date: Thu Jan 01 00:00:04 1970 +0000
351 date: Thu Jan 01 00:00:04 1970 +0000
335 files: a b d g
352 files: a b d g
336 description:
353 description:
337 d
354 d
338
355
339
356
340 changeset: 0:9161b9aeaf16
357 changeset: 0:9161b9aeaf16
341 user: test
358 user: test
342 date: Thu Jan 01 00:00:01 1970 +0000
359 date: Thu Jan 01 00:00:01 1970 +0000
343 files: a f
360 files: a f
344 description:
361 description:
345 a
362 a
346
363
347
364
348
365
349 log --removed revrange file
366 log --removed revrange file
350
367
351 $ hg log --removed -v -r0:2 a
368 $ hg log --removed -v -r0:2 a
352 changeset: 0:9161b9aeaf16
369 changeset: 0:9161b9aeaf16
353 user: test
370 user: test
354 date: Thu Jan 01 00:00:01 1970 +0000
371 date: Thu Jan 01 00:00:01 1970 +0000
355 files: a f
372 files: a f
356 description:
373 description:
357 a
374 a
358
375
359
376
360 $ cd ..
377 $ cd ..
361
378
362 log --follow tests
379 log --follow tests
363
380
364 $ hg init follow
381 $ hg init follow
365 $ cd follow
382 $ cd follow
366
383
367 $ echo base > base
384 $ echo base > base
368 $ hg ci -Ambase -d '1 0'
385 $ hg ci -Ambase -d '1 0'
369 adding base
386 adding base
370
387
371 $ echo r1 >> base
388 $ echo r1 >> base
372 $ hg ci -Amr1 -d '1 0'
389 $ hg ci -Amr1 -d '1 0'
373 $ echo r2 >> base
390 $ echo r2 >> base
374 $ hg ci -Amr2 -d '1 0'
391 $ hg ci -Amr2 -d '1 0'
375
392
376 $ hg up -C 1
393 $ hg up -C 1
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 $ echo b1 > b1
395 $ echo b1 > b1
379 $ hg ci -Amb1 -d '1 0'
396 $ hg ci -Amb1 -d '1 0'
380 adding b1
397 adding b1
381 created new head
398 created new head
382
399
383
400
384 log -f
401 log -f
385
402
386 $ hg log -f
403 $ hg log -f
387 changeset: 3:e62f78d544b4
404 changeset: 3:e62f78d544b4
388 tag: tip
405 tag: tip
389 parent: 1:3d5bf5654eda
406 parent: 1:3d5bf5654eda
390 user: test
407 user: test
391 date: Thu Jan 01 00:00:01 1970 +0000
408 date: Thu Jan 01 00:00:01 1970 +0000
392 summary: b1
409 summary: b1
393
410
394 changeset: 1:3d5bf5654eda
411 changeset: 1:3d5bf5654eda
395 user: test
412 user: test
396 date: Thu Jan 01 00:00:01 1970 +0000
413 date: Thu Jan 01 00:00:01 1970 +0000
397 summary: r1
414 summary: r1
398
415
399 changeset: 0:67e992f2c4f3
416 changeset: 0:67e992f2c4f3
400 user: test
417 user: test
401 date: Thu Jan 01 00:00:01 1970 +0000
418 date: Thu Jan 01 00:00:01 1970 +0000
402 summary: base
419 summary: base
403
420
404
421
405
422
406 log -f -r 1:tip
423 log -f -r 1:tip
407
424
408 $ hg up -C 0
425 $ hg up -C 0
409 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
426 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
410 $ echo b2 > b2
427 $ echo b2 > b2
411 $ hg ci -Amb2 -d '1 0'
428 $ hg ci -Amb2 -d '1 0'
412 adding b2
429 adding b2
413 created new head
430 created new head
414 $ hg log -f -r 1:tip
431 $ hg log -f -r 1:tip
415 changeset: 1:3d5bf5654eda
432 changeset: 1:3d5bf5654eda
416 user: test
433 user: test
417 date: Thu Jan 01 00:00:01 1970 +0000
434 date: Thu Jan 01 00:00:01 1970 +0000
418 summary: r1
435 summary: r1
419
436
420 changeset: 2:60c670bf5b30
437 changeset: 2:60c670bf5b30
421 user: test
438 user: test
422 date: Thu Jan 01 00:00:01 1970 +0000
439 date: Thu Jan 01 00:00:01 1970 +0000
423 summary: r2
440 summary: r2
424
441
425 changeset: 3:e62f78d544b4
442 changeset: 3:e62f78d544b4
426 parent: 1:3d5bf5654eda
443 parent: 1:3d5bf5654eda
427 user: test
444 user: test
428 date: Thu Jan 01 00:00:01 1970 +0000
445 date: Thu Jan 01 00:00:01 1970 +0000
429 summary: b1
446 summary: b1
430
447
431
448
432
449
433 log -r . with two parents
450 log -r . with two parents
434
451
435 $ hg up -C 3
452 $ hg up -C 3
436 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
453 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
437 $ hg merge tip
454 $ hg merge tip
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
455 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 (branch merge, don't forget to commit)
456 (branch merge, don't forget to commit)
440 $ hg log -r .
457 $ hg log -r .
441 changeset: 3:e62f78d544b4
458 changeset: 3:e62f78d544b4
442 parent: 1:3d5bf5654eda
459 parent: 1:3d5bf5654eda
443 user: test
460 user: test
444 date: Thu Jan 01 00:00:01 1970 +0000
461 date: Thu Jan 01 00:00:01 1970 +0000
445 summary: b1
462 summary: b1
446
463
447
464
448
465
449 log -r . with one parent
466 log -r . with one parent
450
467
451 $ hg ci -mm12 -d '1 0'
468 $ hg ci -mm12 -d '1 0'
452 $ hg log -r .
469 $ hg log -r .
453 changeset: 5:302e9dd6890d
470 changeset: 5:302e9dd6890d
454 tag: tip
471 tag: tip
455 parent: 3:e62f78d544b4
472 parent: 3:e62f78d544b4
456 parent: 4:ddb82e70d1a1
473 parent: 4:ddb82e70d1a1
457 user: test
474 user: test
458 date: Thu Jan 01 00:00:01 1970 +0000
475 date: Thu Jan 01 00:00:01 1970 +0000
459 summary: m12
476 summary: m12
460
477
461
478
462 $ echo postm >> b1
479 $ echo postm >> b1
463 $ hg ci -Amb1.1 -d'1 0'
480 $ hg ci -Amb1.1 -d'1 0'
464
481
465
482
466 log --follow-first
483 log --follow-first
467
484
468 $ hg log --follow-first
485 $ hg log --follow-first
469 changeset: 6:2404bbcab562
486 changeset: 6:2404bbcab562
470 tag: tip
487 tag: tip
471 user: test
488 user: test
472 date: Thu Jan 01 00:00:01 1970 +0000
489 date: Thu Jan 01 00:00:01 1970 +0000
473 summary: b1.1
490 summary: b1.1
474
491
475 changeset: 5:302e9dd6890d
492 changeset: 5:302e9dd6890d
476 parent: 3:e62f78d544b4
493 parent: 3:e62f78d544b4
477 parent: 4:ddb82e70d1a1
494 parent: 4:ddb82e70d1a1
478 user: test
495 user: test
479 date: Thu Jan 01 00:00:01 1970 +0000
496 date: Thu Jan 01 00:00:01 1970 +0000
480 summary: m12
497 summary: m12
481
498
482 changeset: 3:e62f78d544b4
499 changeset: 3:e62f78d544b4
483 parent: 1:3d5bf5654eda
500 parent: 1:3d5bf5654eda
484 user: test
501 user: test
485 date: Thu Jan 01 00:00:01 1970 +0000
502 date: Thu Jan 01 00:00:01 1970 +0000
486 summary: b1
503 summary: b1
487
504
488 changeset: 1:3d5bf5654eda
505 changeset: 1:3d5bf5654eda
489 user: test
506 user: test
490 date: Thu Jan 01 00:00:01 1970 +0000
507 date: Thu Jan 01 00:00:01 1970 +0000
491 summary: r1
508 summary: r1
492
509
493 changeset: 0:67e992f2c4f3
510 changeset: 0:67e992f2c4f3
494 user: test
511 user: test
495 date: Thu Jan 01 00:00:01 1970 +0000
512 date: Thu Jan 01 00:00:01 1970 +0000
496 summary: base
513 summary: base
497
514
498
515
499
516
500 log -P 2
517 log -P 2
501
518
502 $ hg log -P 2
519 $ hg log -P 2
503 changeset: 6:2404bbcab562
520 changeset: 6:2404bbcab562
504 tag: tip
521 tag: tip
505 user: test
522 user: test
506 date: Thu Jan 01 00:00:01 1970 +0000
523 date: Thu Jan 01 00:00:01 1970 +0000
507 summary: b1.1
524 summary: b1.1
508
525
509 changeset: 5:302e9dd6890d
526 changeset: 5:302e9dd6890d
510 parent: 3:e62f78d544b4
527 parent: 3:e62f78d544b4
511 parent: 4:ddb82e70d1a1
528 parent: 4:ddb82e70d1a1
512 user: test
529 user: test
513 date: Thu Jan 01 00:00:01 1970 +0000
530 date: Thu Jan 01 00:00:01 1970 +0000
514 summary: m12
531 summary: m12
515
532
516 changeset: 4:ddb82e70d1a1
533 changeset: 4:ddb82e70d1a1
517 parent: 0:67e992f2c4f3
534 parent: 0:67e992f2c4f3
518 user: test
535 user: test
519 date: Thu Jan 01 00:00:01 1970 +0000
536 date: Thu Jan 01 00:00:01 1970 +0000
520 summary: b2
537 summary: b2
521
538
522 changeset: 3:e62f78d544b4
539 changeset: 3:e62f78d544b4
523 parent: 1:3d5bf5654eda
540 parent: 1:3d5bf5654eda
524 user: test
541 user: test
525 date: Thu Jan 01 00:00:01 1970 +0000
542 date: Thu Jan 01 00:00:01 1970 +0000
526 summary: b1
543 summary: b1
527
544
528
545
529
546
530 log -r tip -p --git
547 log -r tip -p --git
531
548
532 $ hg log -r tip -p --git
549 $ hg log -r tip -p --git
533 changeset: 6:2404bbcab562
550 changeset: 6:2404bbcab562
534 tag: tip
551 tag: tip
535 user: test
552 user: test
536 date: Thu Jan 01 00:00:01 1970 +0000
553 date: Thu Jan 01 00:00:01 1970 +0000
537 summary: b1.1
554 summary: b1.1
538
555
539 diff --git a/b1 b/b1
556 diff --git a/b1 b/b1
540 --- a/b1
557 --- a/b1
541 +++ b/b1
558 +++ b/b1
542 @@ -1,1 +1,2 @@
559 @@ -1,1 +1,2 @@
543 b1
560 b1
544 +postm
561 +postm
545
562
546
563
547
564
548 log -r ""
565 log -r ""
549
566
550 $ hg log -r ''
567 $ hg log -r ''
551 hg: parse error: empty query
568 hg: parse error: empty query
552 [255]
569 [255]
553
570
554 log -r <some unknown node id>
571 log -r <some unknown node id>
555
572
556 $ hg log -r 1000000000000000000000000000000000000000
573 $ hg log -r 1000000000000000000000000000000000000000
557 abort: unknown revision '1000000000000000000000000000000000000000'!
574 abort: unknown revision '1000000000000000000000000000000000000000'!
558 [255]
575 [255]
559
576
560 log -k r1
577 log -k r1
561
578
562 $ hg log -k r1
579 $ hg log -k r1
563 changeset: 1:3d5bf5654eda
580 changeset: 1:3d5bf5654eda
564 user: test
581 user: test
565 date: Thu Jan 01 00:00:01 1970 +0000
582 date: Thu Jan 01 00:00:01 1970 +0000
566 summary: r1
583 summary: r1
567
584
568 log -p -l2 --color=always
585 log -p -l2 --color=always
569
586
570 $ hg --config extensions.color= --config color.mode=ansi \
587 $ hg --config extensions.color= --config color.mode=ansi \
571 > log -p -l2 --color=always
588 > log -p -l2 --color=always
572 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
589 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
573 tag: tip
590 tag: tip
574 user: test
591 user: test
575 date: Thu Jan 01 00:00:01 1970 +0000
592 date: Thu Jan 01 00:00:01 1970 +0000
576 summary: b1.1
593 summary: b1.1
577
594
578 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
595 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
579 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
596 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
580 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
597 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
581 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
598 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
582 b1
599 b1
583 \x1b[0;32m+postm\x1b[0m (esc)
600 \x1b[0;32m+postm\x1b[0m (esc)
584
601
585 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
602 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
586 parent: 3:e62f78d544b4
603 parent: 3:e62f78d544b4
587 parent: 4:ddb82e70d1a1
604 parent: 4:ddb82e70d1a1
588 user: test
605 user: test
589 date: Thu Jan 01 00:00:01 1970 +0000
606 date: Thu Jan 01 00:00:01 1970 +0000
590 summary: m12
607 summary: m12
591
608
592 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
609 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
593 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
610 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
594 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
611 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
595 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
612 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
596 \x1b[0;32m+b2\x1b[0m (esc)
613 \x1b[0;32m+b2\x1b[0m (esc)
597
614
598
615
599
616
600 log -r tip --stat
617 log -r tip --stat
601
618
602 $ hg log -r tip --stat
619 $ hg log -r tip --stat
603 changeset: 6:2404bbcab562
620 changeset: 6:2404bbcab562
604 tag: tip
621 tag: tip
605 user: test
622 user: test
606 date: Thu Jan 01 00:00:01 1970 +0000
623 date: Thu Jan 01 00:00:01 1970 +0000
607 summary: b1.1
624 summary: b1.1
608
625
609 b1 | 1 +
626 b1 | 1 +
610 1 files changed, 1 insertions(+), 0 deletions(-)
627 1 files changed, 1 insertions(+), 0 deletions(-)
611
628
612
629
613 $ cd ..
630 $ cd ..
614
631
615
632
616 User
633 User
617
634
618 $ hg init usertest
635 $ hg init usertest
619 $ cd usertest
636 $ cd usertest
620
637
621 $ echo a > a
638 $ echo a > a
622 $ hg ci -A -m "a" -u "User One <user1@example.org>"
639 $ hg ci -A -m "a" -u "User One <user1@example.org>"
623 adding a
640 adding a
624 $ echo b > b
641 $ echo b > b
625 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
642 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
626 adding b
643 adding b
627
644
628 $ hg log -u "User One <user1@example.org>"
645 $ hg log -u "User One <user1@example.org>"
629 changeset: 0:29a4c94f1924
646 changeset: 0:29a4c94f1924
630 user: User One <user1@example.org>
647 user: User One <user1@example.org>
631 date: Thu Jan 01 00:00:00 1970 +0000
648 date: Thu Jan 01 00:00:00 1970 +0000
632 summary: a
649 summary: a
633
650
634 $ hg log -u "user1" -u "user2"
651 $ hg log -u "user1" -u "user2"
635 changeset: 1:e834b5e69c0e
652 changeset: 1:e834b5e69c0e
636 tag: tip
653 tag: tip
637 user: User Two <user2@example.org>
654 user: User Two <user2@example.org>
638 date: Thu Jan 01 00:00:00 1970 +0000
655 date: Thu Jan 01 00:00:00 1970 +0000
639 summary: b
656 summary: b
640
657
641 changeset: 0:29a4c94f1924
658 changeset: 0:29a4c94f1924
642 user: User One <user1@example.org>
659 user: User One <user1@example.org>
643 date: Thu Jan 01 00:00:00 1970 +0000
660 date: Thu Jan 01 00:00:00 1970 +0000
644 summary: a
661 summary: a
645
662
646 $ hg log -u "user3"
663 $ hg log -u "user3"
647
664
648 $ cd ..
665 $ cd ..
649
666
650 $ hg init branches
667 $ hg init branches
651 $ cd branches
668 $ cd branches
652
669
653 $ echo a > a
670 $ echo a > a
654 $ hg ci -A -m "commit on default"
671 $ hg ci -A -m "commit on default"
655 adding a
672 adding a
656 $ hg branch test
673 $ hg branch test
657 marked working directory as branch test
674 marked working directory as branch test
658 (branches are permanent and global, did you want a bookmark?)
675 (branches are permanent and global, did you want a bookmark?)
659 $ echo b > b
676 $ echo b > b
660 $ hg ci -A -m "commit on test"
677 $ hg ci -A -m "commit on test"
661 adding b
678 adding b
662
679
663 $ hg up default
680 $ hg up default
664 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
681 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
665 $ echo c > c
682 $ echo c > c
666 $ hg ci -A -m "commit on default"
683 $ hg ci -A -m "commit on default"
667 adding c
684 adding c
668 $ hg up test
685 $ hg up test
669 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
686 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
670 $ echo c > c
687 $ echo c > c
671 $ hg ci -A -m "commit on test"
688 $ hg ci -A -m "commit on test"
672 adding c
689 adding c
673
690
674
691
675 log -b default
692 log -b default
676
693
677 $ hg log -b default
694 $ hg log -b default
678 changeset: 2:c3a4f03cc9a7
695 changeset: 2:c3a4f03cc9a7
679 parent: 0:24427303d56f
696 parent: 0:24427303d56f
680 user: test
697 user: test
681 date: Thu Jan 01 00:00:00 1970 +0000
698 date: Thu Jan 01 00:00:00 1970 +0000
682 summary: commit on default
699 summary: commit on default
683
700
684 changeset: 0:24427303d56f
701 changeset: 0:24427303d56f
685 user: test
702 user: test
686 date: Thu Jan 01 00:00:00 1970 +0000
703 date: Thu Jan 01 00:00:00 1970 +0000
687 summary: commit on default
704 summary: commit on default
688
705
689
706
690
707
691 log -b test
708 log -b test
692
709
693 $ hg log -b test
710 $ hg log -b test
694 changeset: 3:f5d8de11c2e2
711 changeset: 3:f5d8de11c2e2
695 branch: test
712 branch: test
696 tag: tip
713 tag: tip
697 parent: 1:d32277701ccb
714 parent: 1:d32277701ccb
698 user: test
715 user: test
699 date: Thu Jan 01 00:00:00 1970 +0000
716 date: Thu Jan 01 00:00:00 1970 +0000
700 summary: commit on test
717 summary: commit on test
701
718
702 changeset: 1:d32277701ccb
719 changeset: 1:d32277701ccb
703 branch: test
720 branch: test
704 user: test
721 user: test
705 date: Thu Jan 01 00:00:00 1970 +0000
722 date: Thu Jan 01 00:00:00 1970 +0000
706 summary: commit on test
723 summary: commit on test
707
724
708
725
709
726
710 log -b dummy
727 log -b dummy
711
728
712 $ hg log -b dummy
729 $ hg log -b dummy
713 abort: unknown revision 'dummy'!
730 abort: unknown revision 'dummy'!
714 [255]
731 [255]
715
732
716
733
717 log -b .
734 log -b .
718
735
719 $ hg log -b .
736 $ hg log -b .
720 changeset: 3:f5d8de11c2e2
737 changeset: 3:f5d8de11c2e2
721 branch: test
738 branch: test
722 tag: tip
739 tag: tip
723 parent: 1:d32277701ccb
740 parent: 1:d32277701ccb
724 user: test
741 user: test
725 date: Thu Jan 01 00:00:00 1970 +0000
742 date: Thu Jan 01 00:00:00 1970 +0000
726 summary: commit on test
743 summary: commit on test
727
744
728 changeset: 1:d32277701ccb
745 changeset: 1:d32277701ccb
729 branch: test
746 branch: test
730 user: test
747 user: test
731 date: Thu Jan 01 00:00:00 1970 +0000
748 date: Thu Jan 01 00:00:00 1970 +0000
732 summary: commit on test
749 summary: commit on test
733
750
734
751
735
752
736 log -b default -b test
753 log -b default -b test
737
754
738 $ hg log -b default -b test
755 $ hg log -b default -b test
739 changeset: 3:f5d8de11c2e2
756 changeset: 3:f5d8de11c2e2
740 branch: test
757 branch: test
741 tag: tip
758 tag: tip
742 parent: 1:d32277701ccb
759 parent: 1:d32277701ccb
743 user: test
760 user: test
744 date: Thu Jan 01 00:00:00 1970 +0000
761 date: Thu Jan 01 00:00:00 1970 +0000
745 summary: commit on test
762 summary: commit on test
746
763
747 changeset: 2:c3a4f03cc9a7
764 changeset: 2:c3a4f03cc9a7
748 parent: 0:24427303d56f
765 parent: 0:24427303d56f
749 user: test
766 user: test
750 date: Thu Jan 01 00:00:00 1970 +0000
767 date: Thu Jan 01 00:00:00 1970 +0000
751 summary: commit on default
768 summary: commit on default
752
769
753 changeset: 1:d32277701ccb
770 changeset: 1:d32277701ccb
754 branch: test
771 branch: test
755 user: test
772 user: test
756 date: Thu Jan 01 00:00:00 1970 +0000
773 date: Thu Jan 01 00:00:00 1970 +0000
757 summary: commit on test
774 summary: commit on test
758
775
759 changeset: 0:24427303d56f
776 changeset: 0:24427303d56f
760 user: test
777 user: test
761 date: Thu Jan 01 00:00:00 1970 +0000
778 date: Thu Jan 01 00:00:00 1970 +0000
762 summary: commit on default
779 summary: commit on default
763
780
764
781
765
782
766 log -b default -b .
783 log -b default -b .
767
784
768 $ hg log -b default -b .
785 $ hg log -b default -b .
769 changeset: 3:f5d8de11c2e2
786 changeset: 3:f5d8de11c2e2
770 branch: test
787 branch: test
771 tag: tip
788 tag: tip
772 parent: 1:d32277701ccb
789 parent: 1:d32277701ccb
773 user: test
790 user: test
774 date: Thu Jan 01 00:00:00 1970 +0000
791 date: Thu Jan 01 00:00:00 1970 +0000
775 summary: commit on test
792 summary: commit on test
776
793
777 changeset: 2:c3a4f03cc9a7
794 changeset: 2:c3a4f03cc9a7
778 parent: 0:24427303d56f
795 parent: 0:24427303d56f
779 user: test
796 user: test
780 date: Thu Jan 01 00:00:00 1970 +0000
797 date: Thu Jan 01 00:00:00 1970 +0000
781 summary: commit on default
798 summary: commit on default
782
799
783 changeset: 1:d32277701ccb
800 changeset: 1:d32277701ccb
784 branch: test
801 branch: test
785 user: test
802 user: test
786 date: Thu Jan 01 00:00:00 1970 +0000
803 date: Thu Jan 01 00:00:00 1970 +0000
787 summary: commit on test
804 summary: commit on test
788
805
789 changeset: 0:24427303d56f
806 changeset: 0:24427303d56f
790 user: test
807 user: test
791 date: Thu Jan 01 00:00:00 1970 +0000
808 date: Thu Jan 01 00:00:00 1970 +0000
792 summary: commit on default
809 summary: commit on default
793
810
794
811
795
812
796 log -b . -b test
813 log -b . -b test
797
814
798 $ hg log -b . -b test
815 $ hg log -b . -b test
799 changeset: 3:f5d8de11c2e2
816 changeset: 3:f5d8de11c2e2
800 branch: test
817 branch: test
801 tag: tip
818 tag: tip
802 parent: 1:d32277701ccb
819 parent: 1:d32277701ccb
803 user: test
820 user: test
804 date: Thu Jan 01 00:00:00 1970 +0000
821 date: Thu Jan 01 00:00:00 1970 +0000
805 summary: commit on test
822 summary: commit on test
806
823
807 changeset: 1:d32277701ccb
824 changeset: 1:d32277701ccb
808 branch: test
825 branch: test
809 user: test
826 user: test
810 date: Thu Jan 01 00:00:00 1970 +0000
827 date: Thu Jan 01 00:00:00 1970 +0000
811 summary: commit on test
828 summary: commit on test
812
829
813
830
814
831
815 log -b 2
832 log -b 2
816
833
817 $ hg log -b 2
834 $ hg log -b 2
818 changeset: 2:c3a4f03cc9a7
835 changeset: 2:c3a4f03cc9a7
819 parent: 0:24427303d56f
836 parent: 0:24427303d56f
820 user: test
837 user: test
821 date: Thu Jan 01 00:00:00 1970 +0000
838 date: Thu Jan 01 00:00:00 1970 +0000
822 summary: commit on default
839 summary: commit on default
823
840
824 changeset: 0:24427303d56f
841 changeset: 0:24427303d56f
825 user: test
842 user: test
826 date: Thu Jan 01 00:00:00 1970 +0000
843 date: Thu Jan 01 00:00:00 1970 +0000
827 summary: commit on default
844 summary: commit on default
828
845
829
846
830
847
831 log -p --cwd dir (in subdir)
848 log -p --cwd dir (in subdir)
832
849
833 $ mkdir dir
850 $ mkdir dir
834 $ hg log -p --cwd dir
851 $ hg log -p --cwd dir
835 changeset: 3:f5d8de11c2e2
852 changeset: 3:f5d8de11c2e2
836 branch: test
853 branch: test
837 tag: tip
854 tag: tip
838 parent: 1:d32277701ccb
855 parent: 1:d32277701ccb
839 user: test
856 user: test
840 date: Thu Jan 01 00:00:00 1970 +0000
857 date: Thu Jan 01 00:00:00 1970 +0000
841 summary: commit on test
858 summary: commit on test
842
859
843 diff -r d32277701ccb -r f5d8de11c2e2 c
860 diff -r d32277701ccb -r f5d8de11c2e2 c
844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
861 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
845 +++ b/c Thu Jan 01 00:00:00 1970 +0000
862 +++ b/c Thu Jan 01 00:00:00 1970 +0000
846 @@ -0,0 +1,1 @@
863 @@ -0,0 +1,1 @@
847 +c
864 +c
848
865
849 changeset: 2:c3a4f03cc9a7
866 changeset: 2:c3a4f03cc9a7
850 parent: 0:24427303d56f
867 parent: 0:24427303d56f
851 user: test
868 user: test
852 date: Thu Jan 01 00:00:00 1970 +0000
869 date: Thu Jan 01 00:00:00 1970 +0000
853 summary: commit on default
870 summary: commit on default
854
871
855 diff -r 24427303d56f -r c3a4f03cc9a7 c
872 diff -r 24427303d56f -r c3a4f03cc9a7 c
856 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
873 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
857 +++ b/c Thu Jan 01 00:00:00 1970 +0000
874 +++ b/c Thu Jan 01 00:00:00 1970 +0000
858 @@ -0,0 +1,1 @@
875 @@ -0,0 +1,1 @@
859 +c
876 +c
860
877
861 changeset: 1:d32277701ccb
878 changeset: 1:d32277701ccb
862 branch: test
879 branch: test
863 user: test
880 user: test
864 date: Thu Jan 01 00:00:00 1970 +0000
881 date: Thu Jan 01 00:00:00 1970 +0000
865 summary: commit on test
882 summary: commit on test
866
883
867 diff -r 24427303d56f -r d32277701ccb b
884 diff -r 24427303d56f -r d32277701ccb b
868 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
885 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
869 +++ b/b Thu Jan 01 00:00:00 1970 +0000
886 +++ b/b Thu Jan 01 00:00:00 1970 +0000
870 @@ -0,0 +1,1 @@
887 @@ -0,0 +1,1 @@
871 +b
888 +b
872
889
873 changeset: 0:24427303d56f
890 changeset: 0:24427303d56f
874 user: test
891 user: test
875 date: Thu Jan 01 00:00:00 1970 +0000
892 date: Thu Jan 01 00:00:00 1970 +0000
876 summary: commit on default
893 summary: commit on default
877
894
878 diff -r 000000000000 -r 24427303d56f a
895 diff -r 000000000000 -r 24427303d56f a
879 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
880 +++ b/a Thu Jan 01 00:00:00 1970 +0000
897 +++ b/a Thu Jan 01 00:00:00 1970 +0000
881 @@ -0,0 +1,1 @@
898 @@ -0,0 +1,1 @@
882 +a
899 +a
883
900
884
901
885
902
886 log -p -R repo
903 log -p -R repo
887
904
888 $ cd dir
905 $ cd dir
889 $ hg log -p -R .. ../a
906 $ hg log -p -R .. ../a
890 changeset: 0:24427303d56f
907 changeset: 0:24427303d56f
891 user: test
908 user: test
892 date: Thu Jan 01 00:00:00 1970 +0000
909 date: Thu Jan 01 00:00:00 1970 +0000
893 summary: commit on default
910 summary: commit on default
894
911
895 diff -r 000000000000 -r 24427303d56f a
912 diff -r 000000000000 -r 24427303d56f a
896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
913 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
897 +++ b/a Thu Jan 01 00:00:00 1970 +0000
914 +++ b/a Thu Jan 01 00:00:00 1970 +0000
898 @@ -0,0 +1,1 @@
915 @@ -0,0 +1,1 @@
899 +a
916 +a
900
917
901
918
902 $ cd ../..
919 $ cd ../..
903
920
904 $ hg init follow2
921 $ hg init follow2
905 $ cd follow2
922 $ cd follow2
906
923
907 # Build the following history:
924 # Build the following history:
908 # tip - o - x - o - x - x
925 # tip - o - x - o - x - x
909 # \ /
926 # \ /
910 # o - o - o - x
927 # o - o - o - x
911 # \ /
928 # \ /
912 # o
929 # o
913 #
930 #
914 # Where "o" is a revision containing "foo" and
931 # Where "o" is a revision containing "foo" and
915 # "x" is a revision without "foo"
932 # "x" is a revision without "foo"
916
933
917 $ touch init
934 $ touch init
918 $ hg ci -A -m "init, unrelated"
935 $ hg ci -A -m "init, unrelated"
919 adding init
936 adding init
920 $ echo 'foo' > init
937 $ echo 'foo' > init
921 $ hg ci -m "change, unrelated"
938 $ hg ci -m "change, unrelated"
922 $ echo 'foo' > foo
939 $ echo 'foo' > foo
923 $ hg ci -A -m "add unrelated old foo"
940 $ hg ci -A -m "add unrelated old foo"
924 adding foo
941 adding foo
925 $ hg rm foo
942 $ hg rm foo
926 $ hg ci -m "delete foo, unrelated"
943 $ hg ci -m "delete foo, unrelated"
927 $ echo 'related' > foo
944 $ echo 'related' > foo
928 $ hg ci -A -m "add foo, related"
945 $ hg ci -A -m "add foo, related"
929 adding foo
946 adding foo
930
947
931 $ hg up 0
948 $ hg up 0
932 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
949 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
933 $ touch branch
950 $ touch branch
934 $ hg ci -A -m "first branch, unrelated"
951 $ hg ci -A -m "first branch, unrelated"
935 adding branch
952 adding branch
936 created new head
953 created new head
937 $ touch foo
954 $ touch foo
938 $ hg ci -A -m "create foo, related"
955 $ hg ci -A -m "create foo, related"
939 adding foo
956 adding foo
940 $ echo 'change' > foo
957 $ echo 'change' > foo
941 $ hg ci -m "change foo, related"
958 $ hg ci -m "change foo, related"
942
959
943 $ hg up 6
960 $ hg up 6
944 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
961 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 $ echo 'change foo in branch' > foo
962 $ echo 'change foo in branch' > foo
946 $ hg ci -m "change foo in branch, related"
963 $ hg ci -m "change foo in branch, related"
947 created new head
964 created new head
948 $ hg merge 7
965 $ hg merge 7
949 merging foo
966 merging foo
950 warning: conflicts during merge.
967 warning: conflicts during merge.
951 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
968 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
952 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
969 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
953 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
970 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
954 [1]
971 [1]
955 $ echo 'merge 1' > foo
972 $ echo 'merge 1' > foo
956 $ hg resolve -m foo
973 $ hg resolve -m foo
957 $ hg ci -m "First merge, related"
974 $ hg ci -m "First merge, related"
958
975
959 $ hg merge 4
976 $ hg merge 4
960 merging foo
977 merging foo
961 warning: conflicts during merge.
978 warning: conflicts during merge.
962 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
979 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
963 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
980 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
964 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
981 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
965 [1]
982 [1]
966 $ echo 'merge 2' > foo
983 $ echo 'merge 2' > foo
967 $ hg resolve -m foo
984 $ hg resolve -m foo
968 $ hg ci -m "Last merge, related"
985 $ hg ci -m "Last merge, related"
969
986
970 $ hg log --graph
987 $ hg log --graph
971 @ changeset: 10:4dae8563d2c5
988 @ changeset: 10:4dae8563d2c5
972 |\ tag: tip
989 |\ tag: tip
973 | | parent: 9:7b35701b003e
990 | | parent: 9:7b35701b003e
974 | | parent: 4:88176d361b69
991 | | parent: 4:88176d361b69
975 | | user: test
992 | | user: test
976 | | date: Thu Jan 01 00:00:00 1970 +0000
993 | | date: Thu Jan 01 00:00:00 1970 +0000
977 | | summary: Last merge, related
994 | | summary: Last merge, related
978 | |
995 | |
979 | o changeset: 9:7b35701b003e
996 | o changeset: 9:7b35701b003e
980 | |\ parent: 8:e5416ad8a855
997 | |\ parent: 8:e5416ad8a855
981 | | | parent: 7:87fe3144dcfa
998 | | | parent: 7:87fe3144dcfa
982 | | | user: test
999 | | | user: test
983 | | | date: Thu Jan 01 00:00:00 1970 +0000
1000 | | | date: Thu Jan 01 00:00:00 1970 +0000
984 | | | summary: First merge, related
1001 | | | summary: First merge, related
985 | | |
1002 | | |
986 | | o changeset: 8:e5416ad8a855
1003 | | o changeset: 8:e5416ad8a855
987 | | | parent: 6:dc6c325fe5ee
1004 | | | parent: 6:dc6c325fe5ee
988 | | | user: test
1005 | | | user: test
989 | | | date: Thu Jan 01 00:00:00 1970 +0000
1006 | | | date: Thu Jan 01 00:00:00 1970 +0000
990 | | | summary: change foo in branch, related
1007 | | | summary: change foo in branch, related
991 | | |
1008 | | |
992 | o | changeset: 7:87fe3144dcfa
1009 | o | changeset: 7:87fe3144dcfa
993 | |/ user: test
1010 | |/ user: test
994 | | date: Thu Jan 01 00:00:00 1970 +0000
1011 | | date: Thu Jan 01 00:00:00 1970 +0000
995 | | summary: change foo, related
1012 | | summary: change foo, related
996 | |
1013 | |
997 | o changeset: 6:dc6c325fe5ee
1014 | o changeset: 6:dc6c325fe5ee
998 | | user: test
1015 | | user: test
999 | | date: Thu Jan 01 00:00:00 1970 +0000
1016 | | date: Thu Jan 01 00:00:00 1970 +0000
1000 | | summary: create foo, related
1017 | | summary: create foo, related
1001 | |
1018 | |
1002 | o changeset: 5:73db34516eb9
1019 | o changeset: 5:73db34516eb9
1003 | | parent: 0:e87515fd044a
1020 | | parent: 0:e87515fd044a
1004 | | user: test
1021 | | user: test
1005 | | date: Thu Jan 01 00:00:00 1970 +0000
1022 | | date: Thu Jan 01 00:00:00 1970 +0000
1006 | | summary: first branch, unrelated
1023 | | summary: first branch, unrelated
1007 | |
1024 | |
1008 o | changeset: 4:88176d361b69
1025 o | changeset: 4:88176d361b69
1009 | | user: test
1026 | | user: test
1010 | | date: Thu Jan 01 00:00:00 1970 +0000
1027 | | date: Thu Jan 01 00:00:00 1970 +0000
1011 | | summary: add foo, related
1028 | | summary: add foo, related
1012 | |
1029 | |
1013 o | changeset: 3:dd78ae4afb56
1030 o | changeset: 3:dd78ae4afb56
1014 | | user: test
1031 | | user: test
1015 | | date: Thu Jan 01 00:00:00 1970 +0000
1032 | | date: Thu Jan 01 00:00:00 1970 +0000
1016 | | summary: delete foo, unrelated
1033 | | summary: delete foo, unrelated
1017 | |
1034 | |
1018 o | changeset: 2:c4c64aedf0f7
1035 o | changeset: 2:c4c64aedf0f7
1019 | | user: test
1036 | | user: test
1020 | | date: Thu Jan 01 00:00:00 1970 +0000
1037 | | date: Thu Jan 01 00:00:00 1970 +0000
1021 | | summary: add unrelated old foo
1038 | | summary: add unrelated old foo
1022 | |
1039 | |
1023 o | changeset: 1:e5faa7440653
1040 o | changeset: 1:e5faa7440653
1024 |/ user: test
1041 |/ user: test
1025 | date: Thu Jan 01 00:00:00 1970 +0000
1042 | date: Thu Jan 01 00:00:00 1970 +0000
1026 | summary: change, unrelated
1043 | summary: change, unrelated
1027 |
1044 |
1028 o changeset: 0:e87515fd044a
1045 o changeset: 0:e87515fd044a
1029 user: test
1046 user: test
1030 date: Thu Jan 01 00:00:00 1970 +0000
1047 date: Thu Jan 01 00:00:00 1970 +0000
1031 summary: init, unrelated
1048 summary: init, unrelated
1032
1049
1033
1050
1034 $ hg --traceback log -f foo
1051 $ hg --traceback log -f foo
1035 changeset: 10:4dae8563d2c5
1052 changeset: 10:4dae8563d2c5
1036 tag: tip
1053 tag: tip
1037 parent: 9:7b35701b003e
1054 parent: 9:7b35701b003e
1038 parent: 4:88176d361b69
1055 parent: 4:88176d361b69
1039 user: test
1056 user: test
1040 date: Thu Jan 01 00:00:00 1970 +0000
1057 date: Thu Jan 01 00:00:00 1970 +0000
1041 summary: Last merge, related
1058 summary: Last merge, related
1042
1059
1043 changeset: 9:7b35701b003e
1060 changeset: 9:7b35701b003e
1044 parent: 8:e5416ad8a855
1061 parent: 8:e5416ad8a855
1045 parent: 7:87fe3144dcfa
1062 parent: 7:87fe3144dcfa
1046 user: test
1063 user: test
1047 date: Thu Jan 01 00:00:00 1970 +0000
1064 date: Thu Jan 01 00:00:00 1970 +0000
1048 summary: First merge, related
1065 summary: First merge, related
1049
1066
1050 changeset: 8:e5416ad8a855
1067 changeset: 8:e5416ad8a855
1051 parent: 6:dc6c325fe5ee
1068 parent: 6:dc6c325fe5ee
1052 user: test
1069 user: test
1053 date: Thu Jan 01 00:00:00 1970 +0000
1070 date: Thu Jan 01 00:00:00 1970 +0000
1054 summary: change foo in branch, related
1071 summary: change foo in branch, related
1055
1072
1056 changeset: 7:87fe3144dcfa
1073 changeset: 7:87fe3144dcfa
1057 user: test
1074 user: test
1058 date: Thu Jan 01 00:00:00 1970 +0000
1075 date: Thu Jan 01 00:00:00 1970 +0000
1059 summary: change foo, related
1076 summary: change foo, related
1060
1077
1061 changeset: 6:dc6c325fe5ee
1078 changeset: 6:dc6c325fe5ee
1062 user: test
1079 user: test
1063 date: Thu Jan 01 00:00:00 1970 +0000
1080 date: Thu Jan 01 00:00:00 1970 +0000
1064 summary: create foo, related
1081 summary: create foo, related
1065
1082
1066 changeset: 4:88176d361b69
1083 changeset: 4:88176d361b69
1067 user: test
1084 user: test
1068 date: Thu Jan 01 00:00:00 1970 +0000
1085 date: Thu Jan 01 00:00:00 1970 +0000
1069 summary: add foo, related
1086 summary: add foo, related
1070
1087
1071
1088
1072 Also check when maxrev < lastrevfilelog
1089 Also check when maxrev < lastrevfilelog
1073
1090
1074 $ hg --traceback log -f -r4 foo
1091 $ hg --traceback log -f -r4 foo
1075 changeset: 4:88176d361b69
1092 changeset: 4:88176d361b69
1076 user: test
1093 user: test
1077 date: Thu Jan 01 00:00:00 1970 +0000
1094 date: Thu Jan 01 00:00:00 1970 +0000
1078 summary: add foo, related
1095 summary: add foo, related
1079
1096
1080 $ cd ..
1097 $ cd ..
1081
1098
1082 Issue2383: hg log showing _less_ differences than hg diff
1099 Issue2383: hg log showing _less_ differences than hg diff
1083
1100
1084 $ hg init issue2383
1101 $ hg init issue2383
1085 $ cd issue2383
1102 $ cd issue2383
1086
1103
1087 Create a test repo:
1104 Create a test repo:
1088
1105
1089 $ echo a > a
1106 $ echo a > a
1090 $ hg ci -Am0
1107 $ hg ci -Am0
1091 adding a
1108 adding a
1092 $ echo b > b
1109 $ echo b > b
1093 $ hg ci -Am1
1110 $ hg ci -Am1
1094 adding b
1111 adding b
1095 $ hg co 0
1112 $ hg co 0
1096 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1097 $ echo b > a
1114 $ echo b > a
1098 $ hg ci -m2
1115 $ hg ci -m2
1099 created new head
1116 created new head
1100
1117
1101 Merge:
1118 Merge:
1102
1119
1103 $ hg merge
1120 $ hg merge
1104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1105 (branch merge, don't forget to commit)
1122 (branch merge, don't forget to commit)
1106
1123
1107 Make sure there's a file listed in the merge to trigger the bug:
1124 Make sure there's a file listed in the merge to trigger the bug:
1108
1125
1109 $ echo c > a
1126 $ echo c > a
1110 $ hg ci -m3
1127 $ hg ci -m3
1111
1128
1112 Two files shown here in diff:
1129 Two files shown here in diff:
1113
1130
1114 $ hg diff --rev 2:3
1131 $ hg diff --rev 2:3
1115 diff -r b09be438c43a -r 8e07aafe1edc a
1132 diff -r b09be438c43a -r 8e07aafe1edc a
1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1133 --- a/a Thu Jan 01 00:00:00 1970 +0000
1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1134 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1118 @@ -1,1 +1,1 @@
1135 @@ -1,1 +1,1 @@
1119 -b
1136 -b
1120 +c
1137 +c
1121 diff -r b09be438c43a -r 8e07aafe1edc b
1138 diff -r b09be438c43a -r 8e07aafe1edc b
1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1139 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1140 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1124 @@ -0,0 +1,1 @@
1141 @@ -0,0 +1,1 @@
1125 +b
1142 +b
1126
1143
1127 Diff here should be the same:
1144 Diff here should be the same:
1128
1145
1129 $ hg log -vpr 3
1146 $ hg log -vpr 3
1130 changeset: 3:8e07aafe1edc
1147 changeset: 3:8e07aafe1edc
1131 tag: tip
1148 tag: tip
1132 parent: 2:b09be438c43a
1149 parent: 2:b09be438c43a
1133 parent: 1:925d80f479bb
1150 parent: 1:925d80f479bb
1134 user: test
1151 user: test
1135 date: Thu Jan 01 00:00:00 1970 +0000
1152 date: Thu Jan 01 00:00:00 1970 +0000
1136 files: a
1153 files: a
1137 description:
1154 description:
1138 3
1155 3
1139
1156
1140
1157
1141 diff -r b09be438c43a -r 8e07aafe1edc a
1158 diff -r b09be438c43a -r 8e07aafe1edc a
1142 --- a/a Thu Jan 01 00:00:00 1970 +0000
1159 --- a/a Thu Jan 01 00:00:00 1970 +0000
1143 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1160 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1144 @@ -1,1 +1,1 @@
1161 @@ -1,1 +1,1 @@
1145 -b
1162 -b
1146 +c
1163 +c
1147 diff -r b09be438c43a -r 8e07aafe1edc b
1164 diff -r b09be438c43a -r 8e07aafe1edc b
1148 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1165 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1149 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1166 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1150 @@ -0,0 +1,1 @@
1167 @@ -0,0 +1,1 @@
1151 +b
1168 +b
1152
1169
1153 $ cd ..
1170 $ cd ..
1154
1171
1155 'hg log -r rev fn' when last(filelog(fn)) != rev
1172 'hg log -r rev fn' when last(filelog(fn)) != rev
1156
1173
1157 $ hg init simplelog
1174 $ hg init simplelog
1158 $ cd simplelog
1175 $ cd simplelog
1159 $ echo f > a
1176 $ echo f > a
1160 $ hg ci -Am'a' -d '0 0'
1177 $ hg ci -Am'a' -d '0 0'
1161 adding a
1178 adding a
1162 $ echo f >> a
1179 $ echo f >> a
1163 $ hg ci -Am'a bis' -d '1 0'
1180 $ hg ci -Am'a bis' -d '1 0'
1164
1181
1165 $ hg log -r0 a
1182 $ hg log -r0 a
1166 changeset: 0:9f758d63dcde
1183 changeset: 0:9f758d63dcde
1167 user: test
1184 user: test
1168 date: Thu Jan 01 00:00:00 1970 +0000
1185 date: Thu Jan 01 00:00:00 1970 +0000
1169 summary: a
1186 summary: a
1170
1187
1171 enable obsolete to test hidden feature
1188 enable obsolete to test hidden feature
1172
1189
1173 $ cat > ${TESTTMP}/obs.py << EOF
1190 $ cat > ${TESTTMP}/obs.py << EOF
1174 > import mercurial.obsolete
1191 > import mercurial.obsolete
1175 > mercurial.obsolete._enabled = True
1192 > mercurial.obsolete._enabled = True
1176 > EOF
1193 > EOF
1177 $ echo '[extensions]' >> $HGRCPATH
1194 $ echo '[extensions]' >> $HGRCPATH
1178 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
1195 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
1179
1196
1180 $ hg log --template='{rev}:{node}\n'
1197 $ hg log --template='{rev}:{node}\n'
1181 1:a765632148dc55d38c35c4f247c618701886cb2f
1198 1:a765632148dc55d38c35c4f247c618701886cb2f
1182 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1199 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1183 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1200 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1184 $ hg up null -q
1201 $ hg up null -q
1185 $ hg log --template='{rev}:{node}\n'
1202 $ hg log --template='{rev}:{node}\n'
1186 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1203 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1187 $ hg log --template='{rev}:{node}\n' --hidden
1204 $ hg log --template='{rev}:{node}\n' --hidden
1188 1:a765632148dc55d38c35c4f247c618701886cb2f
1205 1:a765632148dc55d38c35c4f247c618701886cb2f
1189 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1206 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1190
1207
1191 test that parent prevent a changeset to be hidden
1208 test that parent prevent a changeset to be hidden
1192
1209
1193 $ hg up 1 -q --hidden
1210 $ hg up 1 -q --hidden
1194 $ hg log --template='{rev}:{node}\n'
1211 $ hg log --template='{rev}:{node}\n'
1195 1:a765632148dc55d38c35c4f247c618701886cb2f
1212 1:a765632148dc55d38c35c4f247c618701886cb2f
1196 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1213 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1197
1214
1198 test that second parent prevent a changeset to be hidden too
1215 test that second parent prevent a changeset to be hidden too
1199
1216
1200 $ hg debugsetparents 0 1 # nothing suitable to merge here
1217 $ hg debugsetparents 0 1 # nothing suitable to merge here
1201 $ hg log --template='{rev}:{node}\n'
1218 $ hg log --template='{rev}:{node}\n'
1202 1:a765632148dc55d38c35c4f247c618701886cb2f
1219 1:a765632148dc55d38c35c4f247c618701886cb2f
1203 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1220 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1204 $ hg debugsetparents 1
1221 $ hg debugsetparents 1
1205 $ hg up -q null
1222 $ hg up -q null
1206
1223
1207 bookmarks prevent a changeset being hidden
1224 bookmarks prevent a changeset being hidden
1208
1225
1209 $ hg bookmark --hidden -r 1 X
1226 $ hg bookmark --hidden -r 1 X
1210 $ hg log --template '{rev}:{node}\n'
1227 $ hg log --template '{rev}:{node}\n'
1211 1:a765632148dc55d38c35c4f247c618701886cb2f
1228 1:a765632148dc55d38c35c4f247c618701886cb2f
1212 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1229 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1213 $ hg bookmark -d X
1230 $ hg bookmark -d X
1214
1231
1215 divergent bookmarks are not hidden
1232 divergent bookmarks are not hidden
1216
1233
1217 $ hg bookmark --hidden -r 1 X@foo
1234 $ hg bookmark --hidden -r 1 X@foo
1218 $ hg log --template '{rev}:{node}\n'
1235 $ hg log --template '{rev}:{node}\n'
1219 1:a765632148dc55d38c35c4f247c618701886cb2f
1236 1:a765632148dc55d38c35c4f247c618701886cb2f
1220 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1237 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1221
1238
1222 clear extensions configuration
1239 clear extensions configuration
1223 $ echo '[extensions]' >> $HGRCPATH
1240 $ echo '[extensions]' >> $HGRCPATH
1224 $ echo "obs=!" >> $HGRCPATH
1241 $ echo "obs=!" >> $HGRCPATH
1225 $ cd ..
1242 $ cd ..
1226
1243
1227 test -u/-k for problematic encoding
1244 test -u/-k for problematic encoding
1228 # unicode: cp932:
1245 # unicode: cp932:
1229 # u30A2 0x83 0x41(= 'A')
1246 # u30A2 0x83 0x41(= 'A')
1230 # u30C2 0x83 0x61(= 'a')
1247 # u30C2 0x83 0x61(= 'a')
1231
1248
1232 $ hg init problematicencoding
1249 $ hg init problematicencoding
1233 $ cd problematicencoding
1250 $ cd problematicencoding
1234
1251
1235 $ python > setup.sh <<EOF
1252 $ python > setup.sh <<EOF
1236 > print u'''
1253 > print u'''
1237 > echo a > text
1254 > echo a > text
1238 > hg add text
1255 > hg add text
1239 > hg --encoding utf-8 commit -u '\u30A2' -m none
1256 > hg --encoding utf-8 commit -u '\u30A2' -m none
1240 > echo b > text
1257 > echo b > text
1241 > hg --encoding utf-8 commit -u '\u30C2' -m none
1258 > hg --encoding utf-8 commit -u '\u30C2' -m none
1242 > echo c > text
1259 > echo c > text
1243 > hg --encoding utf-8 commit -u none -m '\u30A2'
1260 > hg --encoding utf-8 commit -u none -m '\u30A2'
1244 > echo d > text
1261 > echo d > text
1245 > hg --encoding utf-8 commit -u none -m '\u30C2'
1262 > hg --encoding utf-8 commit -u none -m '\u30C2'
1246 > '''.encode('utf-8')
1263 > '''.encode('utf-8')
1247 > EOF
1264 > EOF
1248 $ sh < setup.sh
1265 $ sh < setup.sh
1249
1266
1250 test in problematic encoding
1267 test in problematic encoding
1251 $ python > test.sh <<EOF
1268 $ python > test.sh <<EOF
1252 > print u'''
1269 > print u'''
1253 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1270 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1254 > echo ====
1271 > echo ====
1255 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1272 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1256 > echo ====
1273 > echo ====
1257 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1274 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1258 > echo ====
1275 > echo ====
1259 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1276 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1260 > '''.encode('cp932')
1277 > '''.encode('cp932')
1261 > EOF
1278 > EOF
1262 $ sh < test.sh
1279 $ sh < test.sh
1263 0
1280 0
1264 ====
1281 ====
1265 1
1282 1
1266 ====
1283 ====
1267 2
1284 2
1268 0
1285 0
1269 ====
1286 ====
1270 3
1287 3
1271 1
1288 1
1272
1289
1273 $ cd ..
1290 $ cd ..
1274
1291
1275 test hg log on non-existent files and on directories
1292 test hg log on non-existent files and on directories
1276 $ hg init issue1340
1293 $ hg init issue1340
1277 $ cd issue1340
1294 $ cd issue1340
1278 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1295 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1279 $ echo 1 > d1/f1
1296 $ echo 1 > d1/f1
1280 $ echo 1 > D2/f1
1297 $ echo 1 > D2/f1
1281 $ echo 1 > D3.i/f1
1298 $ echo 1 > D3.i/f1
1282 $ echo 1 > d4.hg/f1
1299 $ echo 1 > d4.hg/f1
1283 $ echo 1 > d5.d/f1
1300 $ echo 1 > d5.d/f1
1284 $ echo 1 > .d6/f1
1301 $ echo 1 > .d6/f1
1285 $ hg -q add .
1302 $ hg -q add .
1286 $ hg commit -m "a bunch of weird directories"
1303 $ hg commit -m "a bunch of weird directories"
1287 $ hg log -l1 d1/f1 | grep changeset
1304 $ hg log -l1 d1/f1 | grep changeset
1288 changeset: 0:65624cd9070a
1305 changeset: 0:65624cd9070a
1289 $ hg log -l1 f1
1306 $ hg log -l1 f1
1290 $ hg log -l1 . | grep changeset
1307 $ hg log -l1 . | grep changeset
1291 changeset: 0:65624cd9070a
1308 changeset: 0:65624cd9070a
1292 $ hg log -l1 ./ | grep changeset
1309 $ hg log -l1 ./ | grep changeset
1293 changeset: 0:65624cd9070a
1310 changeset: 0:65624cd9070a
1294 $ hg log -l1 d1 | grep changeset
1311 $ hg log -l1 d1 | grep changeset
1295 changeset: 0:65624cd9070a
1312 changeset: 0:65624cd9070a
1296 $ hg log -l1 D2 | grep changeset
1313 $ hg log -l1 D2 | grep changeset
1297 changeset: 0:65624cd9070a
1314 changeset: 0:65624cd9070a
1298 $ hg log -l1 D2/f1 | grep changeset
1315 $ hg log -l1 D2/f1 | grep changeset
1299 changeset: 0:65624cd9070a
1316 changeset: 0:65624cd9070a
1300 $ hg log -l1 D3.i | grep changeset
1317 $ hg log -l1 D3.i | grep changeset
1301 changeset: 0:65624cd9070a
1318 changeset: 0:65624cd9070a
1302 $ hg log -l1 D3.i/f1 | grep changeset
1319 $ hg log -l1 D3.i/f1 | grep changeset
1303 changeset: 0:65624cd9070a
1320 changeset: 0:65624cd9070a
1304 $ hg log -l1 d4.hg | grep changeset
1321 $ hg log -l1 d4.hg | grep changeset
1305 changeset: 0:65624cd9070a
1322 changeset: 0:65624cd9070a
1306 $ hg log -l1 d4.hg/f1 | grep changeset
1323 $ hg log -l1 d4.hg/f1 | grep changeset
1307 changeset: 0:65624cd9070a
1324 changeset: 0:65624cd9070a
1308 $ hg log -l1 d5.d | grep changeset
1325 $ hg log -l1 d5.d | grep changeset
1309 changeset: 0:65624cd9070a
1326 changeset: 0:65624cd9070a
1310 $ hg log -l1 d5.d/f1 | grep changeset
1327 $ hg log -l1 d5.d/f1 | grep changeset
1311 changeset: 0:65624cd9070a
1328 changeset: 0:65624cd9070a
1312 $ hg log -l1 .d6 | grep changeset
1329 $ hg log -l1 .d6 | grep changeset
1313 changeset: 0:65624cd9070a
1330 changeset: 0:65624cd9070a
1314 $ hg log -l1 .d6/f1 | grep changeset
1331 $ hg log -l1 .d6/f1 | grep changeset
1315 changeset: 0:65624cd9070a
1332 changeset: 0:65624cd9070a
1316
1333
1317 issue3772: hg log -r :null showing revision 0 as well
1334 issue3772: hg log -r :null showing revision 0 as well
1318
1335
1319 $ hg log -r :null
1336 $ hg log -r :null
1320 changeset: -1:000000000000
1337 changeset: -1:000000000000
1321 user:
1338 user:
1322 date: Thu Jan 01 00:00:00 1970 +0000
1339 date: Thu Jan 01 00:00:00 1970 +0000
1323
1340
1324 $ hg log -r null:null
1341 $ hg log -r null:null
1325 changeset: -1:000000000000
1342 changeset: -1:000000000000
1326 user:
1343 user:
1327 date: Thu Jan 01 00:00:00 1970 +0000
1344 date: Thu Jan 01 00:00:00 1970 +0000
1328
1345
1329
1346
1330 $ cd ..
1347 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now