##// END OF EJS Templates
log: speed up hg log for untracked files (issue1340)...
smuralid -
r17746:6d218e47 default
parent child Browse files
Show More
@@ -1,1958 +1,1981 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
13 import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
14 import lock as lockmod
14 import lock as lockmod
15
15
16 def parsealiases(cmd):
16 def parsealiases(cmd):
17 return cmd.lstrip("^").split("|")
17 return cmd.lstrip("^").split("|")
18
18
19 def findpossible(cmd, table, strict=False):
19 def findpossible(cmd, table, strict=False):
20 """
20 """
21 Return cmd -> (aliases, command table entry)
21 Return cmd -> (aliases, command table entry)
22 for each matching command.
22 for each matching command.
23 Return debug commands (or their aliases) only if no normal command matches.
23 Return debug commands (or their aliases) only if no normal command matches.
24 """
24 """
25 choice = {}
25 choice = {}
26 debugchoice = {}
26 debugchoice = {}
27
27
28 if cmd in table:
28 if cmd in table:
29 # short-circuit exact matches, "log" alias beats "^log|history"
29 # short-circuit exact matches, "log" alias beats "^log|history"
30 keys = [cmd]
30 keys = [cmd]
31 else:
31 else:
32 keys = table.keys()
32 keys = table.keys()
33
33
34 for e in keys:
34 for e in keys:
35 aliases = parsealiases(e)
35 aliases = parsealiases(e)
36 found = None
36 found = None
37 if cmd in aliases:
37 if cmd in aliases:
38 found = cmd
38 found = cmd
39 elif not strict:
39 elif not strict:
40 for a in aliases:
40 for a in aliases:
41 if a.startswith(cmd):
41 if a.startswith(cmd):
42 found = a
42 found = a
43 break
43 break
44 if found is not None:
44 if found is not None:
45 if aliases[0].startswith("debug") or found.startswith("debug"):
45 if aliases[0].startswith("debug") or found.startswith("debug"):
46 debugchoice[found] = (aliases, table[e])
46 debugchoice[found] = (aliases, table[e])
47 else:
47 else:
48 choice[found] = (aliases, table[e])
48 choice[found] = (aliases, table[e])
49
49
50 if not choice and debugchoice:
50 if not choice and debugchoice:
51 choice = debugchoice
51 choice = debugchoice
52
52
53 return choice
53 return choice
54
54
55 def findcmd(cmd, table, strict=True):
55 def findcmd(cmd, table, strict=True):
56 """Return (aliases, command table entry) for command string."""
56 """Return (aliases, command table entry) for command string."""
57 choice = findpossible(cmd, table, strict)
57 choice = findpossible(cmd, table, strict)
58
58
59 if cmd in choice:
59 if cmd in choice:
60 return choice[cmd]
60 return choice[cmd]
61
61
62 if len(choice) > 1:
62 if len(choice) > 1:
63 clist = choice.keys()
63 clist = choice.keys()
64 clist.sort()
64 clist.sort()
65 raise error.AmbiguousCommand(cmd, clist)
65 raise error.AmbiguousCommand(cmd, clist)
66
66
67 if choice:
67 if choice:
68 return choice.values()[0]
68 return choice.values()[0]
69
69
70 raise error.UnknownCommand(cmd)
70 raise error.UnknownCommand(cmd)
71
71
72 def findrepo(p):
72 def findrepo(p):
73 while not os.path.isdir(os.path.join(p, ".hg")):
73 while not os.path.isdir(os.path.join(p, ".hg")):
74 oldp, p = p, os.path.dirname(p)
74 oldp, p = p, os.path.dirname(p)
75 if p == oldp:
75 if p == oldp:
76 return None
76 return None
77
77
78 return p
78 return p
79
79
80 def bailifchanged(repo):
80 def bailifchanged(repo):
81 if repo.dirstate.p2() != nullid:
81 if repo.dirstate.p2() != nullid:
82 raise util.Abort(_('outstanding uncommitted merge'))
82 raise util.Abort(_('outstanding uncommitted merge'))
83 modified, added, removed, deleted = repo.status()[:4]
83 modified, added, removed, deleted = repo.status()[:4]
84 if modified or added or removed or deleted:
84 if modified or added or removed or deleted:
85 raise util.Abort(_("outstanding uncommitted changes"))
85 raise util.Abort(_("outstanding uncommitted changes"))
86 ctx = repo[None]
86 ctx = repo[None]
87 for s in ctx.substate:
87 for s in ctx.substate:
88 if ctx.sub(s).dirty():
88 if ctx.sub(s).dirty():
89 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
90
90
91 def logmessage(ui, opts):
91 def logmessage(ui, opts):
92 """ get the log message according to -m and -l option """
92 """ get the log message according to -m and -l option """
93 message = opts.get('message')
93 message = opts.get('message')
94 logfile = opts.get('logfile')
94 logfile = opts.get('logfile')
95
95
96 if message and logfile:
96 if message and logfile:
97 raise util.Abort(_('options --message and --logfile are mutually '
97 raise util.Abort(_('options --message and --logfile are mutually '
98 'exclusive'))
98 'exclusive'))
99 if not message and logfile:
99 if not message and logfile:
100 try:
100 try:
101 if logfile == '-':
101 if logfile == '-':
102 message = ui.fin.read()
102 message = ui.fin.read()
103 else:
103 else:
104 message = '\n'.join(util.readfile(logfile).splitlines())
104 message = '\n'.join(util.readfile(logfile).splitlines())
105 except IOError, inst:
105 except IOError, inst:
106 raise util.Abort(_("can't read commit message '%s': %s") %
106 raise util.Abort(_("can't read commit message '%s': %s") %
107 (logfile, inst.strerror))
107 (logfile, inst.strerror))
108 return message
108 return message
109
109
110 def loglimit(opts):
110 def loglimit(opts):
111 """get the log limit according to option -l/--limit"""
111 """get the log limit according to option -l/--limit"""
112 limit = opts.get('limit')
112 limit = opts.get('limit')
113 if limit:
113 if limit:
114 try:
114 try:
115 limit = int(limit)
115 limit = int(limit)
116 except ValueError:
116 except ValueError:
117 raise util.Abort(_('limit must be a positive integer'))
117 raise util.Abort(_('limit must be a positive integer'))
118 if limit <= 0:
118 if limit <= 0:
119 raise util.Abort(_('limit must be positive'))
119 raise util.Abort(_('limit must be positive'))
120 else:
120 else:
121 limit = None
121 limit = None
122 return limit
122 return limit
123
123
124 def makefilename(repo, pat, node, desc=None,
124 def makefilename(repo, pat, node, desc=None,
125 total=None, seqno=None, revwidth=None, pathname=None):
125 total=None, seqno=None, revwidth=None, pathname=None):
126 node_expander = {
126 node_expander = {
127 'H': lambda: hex(node),
127 'H': lambda: hex(node),
128 'R': lambda: str(repo.changelog.rev(node)),
128 'R': lambda: str(repo.changelog.rev(node)),
129 'h': lambda: short(node),
129 'h': lambda: short(node),
130 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 'm': lambda: re.sub('[^\w]', '_', str(desc))
131 }
131 }
132 expander = {
132 expander = {
133 '%': lambda: '%',
133 '%': lambda: '%',
134 'b': lambda: os.path.basename(repo.root),
134 'b': lambda: os.path.basename(repo.root),
135 }
135 }
136
136
137 try:
137 try:
138 if node:
138 if node:
139 expander.update(node_expander)
139 expander.update(node_expander)
140 if node:
140 if node:
141 expander['r'] = (lambda:
141 expander['r'] = (lambda:
142 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 str(repo.changelog.rev(node)).zfill(revwidth or 0))
143 if total is not None:
143 if total is not None:
144 expander['N'] = lambda: str(total)
144 expander['N'] = lambda: str(total)
145 if seqno is not None:
145 if seqno is not None:
146 expander['n'] = lambda: str(seqno)
146 expander['n'] = lambda: str(seqno)
147 if total is not None and seqno is not None:
147 if total is not None and seqno is not None:
148 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
149 if pathname is not None:
149 if pathname is not None:
150 expander['s'] = lambda: os.path.basename(pathname)
150 expander['s'] = lambda: os.path.basename(pathname)
151 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 expander['d'] = lambda: os.path.dirname(pathname) or '.'
152 expander['p'] = lambda: pathname
152 expander['p'] = lambda: pathname
153
153
154 newname = []
154 newname = []
155 patlen = len(pat)
155 patlen = len(pat)
156 i = 0
156 i = 0
157 while i < patlen:
157 while i < patlen:
158 c = pat[i]
158 c = pat[i]
159 if c == '%':
159 if c == '%':
160 i += 1
160 i += 1
161 c = pat[i]
161 c = pat[i]
162 c = expander[c]()
162 c = expander[c]()
163 newname.append(c)
163 newname.append(c)
164 i += 1
164 i += 1
165 return ''.join(newname)
165 return ''.join(newname)
166 except KeyError, inst:
166 except KeyError, inst:
167 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
168 inst.args[0])
168 inst.args[0])
169
169
170 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 def makefileobj(repo, pat, node=None, desc=None, total=None,
171 seqno=None, revwidth=None, mode='wb', pathname=None):
171 seqno=None, revwidth=None, mode='wb', pathname=None):
172
172
173 writable = mode not in ('r', 'rb')
173 writable = mode not in ('r', 'rb')
174
174
175 if not pat or pat == '-':
175 if not pat or pat == '-':
176 fp = writable and repo.ui.fout or repo.ui.fin
176 fp = writable and repo.ui.fout or repo.ui.fin
177 if util.safehasattr(fp, 'fileno'):
177 if util.safehasattr(fp, 'fileno'):
178 return os.fdopen(os.dup(fp.fileno()), mode)
178 return os.fdopen(os.dup(fp.fileno()), mode)
179 else:
179 else:
180 # if this fp can't be duped properly, return
180 # if this fp can't be duped properly, return
181 # a dummy object that can be closed
181 # a dummy object that can be closed
182 class wrappedfileobj(object):
182 class wrappedfileobj(object):
183 noop = lambda x: None
183 noop = lambda x: None
184 def __init__(self, f):
184 def __init__(self, f):
185 self.f = f
185 self.f = f
186 def __getattr__(self, attr):
186 def __getattr__(self, attr):
187 if attr == 'close':
187 if attr == 'close':
188 return self.noop
188 return self.noop
189 else:
189 else:
190 return getattr(self.f, attr)
190 return getattr(self.f, attr)
191
191
192 return wrappedfileobj(fp)
192 return wrappedfileobj(fp)
193 if util.safehasattr(pat, 'write') and writable:
193 if util.safehasattr(pat, 'write') and writable:
194 return pat
194 return pat
195 if util.safehasattr(pat, 'read') and 'r' in mode:
195 if util.safehasattr(pat, 'read') and 'r' in mode:
196 return pat
196 return pat
197 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
198 pathname),
198 pathname),
199 mode)
199 mode)
200
200
201 def openrevlog(repo, cmd, file_, opts):
201 def openrevlog(repo, cmd, file_, opts):
202 """opens the changelog, manifest, a filelog or a given revlog"""
202 """opens the changelog, manifest, a filelog or a given revlog"""
203 cl = opts['changelog']
203 cl = opts['changelog']
204 mf = opts['manifest']
204 mf = opts['manifest']
205 msg = None
205 msg = None
206 if cl and mf:
206 if cl and mf:
207 msg = _('cannot specify --changelog and --manifest at the same time')
207 msg = _('cannot specify --changelog and --manifest at the same time')
208 elif cl or mf:
208 elif cl or mf:
209 if file_:
209 if file_:
210 msg = _('cannot specify filename with --changelog or --manifest')
210 msg = _('cannot specify filename with --changelog or --manifest')
211 elif not repo:
211 elif not repo:
212 msg = _('cannot specify --changelog or --manifest '
212 msg = _('cannot specify --changelog or --manifest '
213 'without a repository')
213 'without a repository')
214 if msg:
214 if msg:
215 raise util.Abort(msg)
215 raise util.Abort(msg)
216
216
217 r = None
217 r = None
218 if repo:
218 if repo:
219 if cl:
219 if cl:
220 r = repo.changelog
220 r = repo.changelog
221 elif mf:
221 elif mf:
222 r = repo.manifest
222 r = repo.manifest
223 elif file_:
223 elif file_:
224 filelog = repo.file(file_)
224 filelog = repo.file(file_)
225 if len(filelog):
225 if len(filelog):
226 r = filelog
226 r = filelog
227 if not r:
227 if not r:
228 if not file_:
228 if not file_:
229 raise error.CommandError(cmd, _('invalid arguments'))
229 raise error.CommandError(cmd, _('invalid arguments'))
230 if not os.path.isfile(file_):
230 if not os.path.isfile(file_):
231 raise util.Abort(_("revlog '%s' not found") % file_)
231 raise util.Abort(_("revlog '%s' not found") % file_)
232 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
233 file_[:-2] + ".i")
233 file_[:-2] + ".i")
234 return r
234 return r
235
235
236 def copy(ui, repo, pats, opts, rename=False):
236 def copy(ui, repo, pats, opts, rename=False):
237 # called with the repo lock held
237 # called with the repo lock held
238 #
238 #
239 # hgsep => pathname that uses "/" to separate directories
239 # hgsep => pathname that uses "/" to separate directories
240 # ossep => pathname that uses os.sep to separate directories
240 # ossep => pathname that uses os.sep to separate directories
241 cwd = repo.getcwd()
241 cwd = repo.getcwd()
242 targets = {}
242 targets = {}
243 after = opts.get("after")
243 after = opts.get("after")
244 dryrun = opts.get("dry_run")
244 dryrun = opts.get("dry_run")
245 wctx = repo[None]
245 wctx = repo[None]
246
246
247 def walkpat(pat):
247 def walkpat(pat):
248 srcs = []
248 srcs = []
249 badstates = after and '?' or '?r'
249 badstates = after and '?' or '?r'
250 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 m = scmutil.match(repo[None], [pat], opts, globbed=True)
251 for abs in repo.walk(m):
251 for abs in repo.walk(m):
252 state = repo.dirstate[abs]
252 state = repo.dirstate[abs]
253 rel = m.rel(abs)
253 rel = m.rel(abs)
254 exact = m.exact(abs)
254 exact = m.exact(abs)
255 if state in badstates:
255 if state in badstates:
256 if exact and state == '?':
256 if exact and state == '?':
257 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 ui.warn(_('%s: not copying - file is not managed\n') % rel)
258 if exact and state == 'r':
258 if exact and state == 'r':
259 ui.warn(_('%s: not copying - file has been marked for'
259 ui.warn(_('%s: not copying - file has been marked for'
260 ' remove\n') % rel)
260 ' remove\n') % rel)
261 continue
261 continue
262 # abs: hgsep
262 # abs: hgsep
263 # rel: ossep
263 # rel: ossep
264 srcs.append((abs, rel, exact))
264 srcs.append((abs, rel, exact))
265 return srcs
265 return srcs
266
266
267 # abssrc: hgsep
267 # abssrc: hgsep
268 # relsrc: ossep
268 # relsrc: ossep
269 # otarget: ossep
269 # otarget: ossep
270 def copyfile(abssrc, relsrc, otarget, exact):
270 def copyfile(abssrc, relsrc, otarget, exact):
271 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
272 if '/' in abstarget:
272 if '/' in abstarget:
273 # We cannot normalize abstarget itself, this would prevent
273 # We cannot normalize abstarget itself, this would prevent
274 # case only renames, like a => A.
274 # case only renames, like a => A.
275 abspath, absname = abstarget.rsplit('/', 1)
275 abspath, absname = abstarget.rsplit('/', 1)
276 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
276 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
277 reltarget = repo.pathto(abstarget, cwd)
277 reltarget = repo.pathto(abstarget, cwd)
278 target = repo.wjoin(abstarget)
278 target = repo.wjoin(abstarget)
279 src = repo.wjoin(abssrc)
279 src = repo.wjoin(abssrc)
280 state = repo.dirstate[abstarget]
280 state = repo.dirstate[abstarget]
281
281
282 scmutil.checkportable(ui, abstarget)
282 scmutil.checkportable(ui, abstarget)
283
283
284 # check for collisions
284 # check for collisions
285 prevsrc = targets.get(abstarget)
285 prevsrc = targets.get(abstarget)
286 if prevsrc is not None:
286 if prevsrc is not None:
287 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
287 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
288 (reltarget, repo.pathto(abssrc, cwd),
288 (reltarget, repo.pathto(abssrc, cwd),
289 repo.pathto(prevsrc, cwd)))
289 repo.pathto(prevsrc, cwd)))
290 return
290 return
291
291
292 # check for overwrites
292 # check for overwrites
293 exists = os.path.lexists(target)
293 exists = os.path.lexists(target)
294 samefile = False
294 samefile = False
295 if exists and abssrc != abstarget:
295 if exists and abssrc != abstarget:
296 if (repo.dirstate.normalize(abssrc) ==
296 if (repo.dirstate.normalize(abssrc) ==
297 repo.dirstate.normalize(abstarget)):
297 repo.dirstate.normalize(abstarget)):
298 if not rename:
298 if not rename:
299 ui.warn(_("%s: can't copy - same file\n") % reltarget)
299 ui.warn(_("%s: can't copy - same file\n") % reltarget)
300 return
300 return
301 exists = False
301 exists = False
302 samefile = True
302 samefile = True
303
303
304 if not after and exists or after and state in 'mn':
304 if not after and exists or after and state in 'mn':
305 if not opts['force']:
305 if not opts['force']:
306 ui.warn(_('%s: not overwriting - file exists\n') %
306 ui.warn(_('%s: not overwriting - file exists\n') %
307 reltarget)
307 reltarget)
308 return
308 return
309
309
310 if after:
310 if after:
311 if not exists:
311 if not exists:
312 if rename:
312 if rename:
313 ui.warn(_('%s: not recording move - %s does not exist\n') %
313 ui.warn(_('%s: not recording move - %s does not exist\n') %
314 (relsrc, reltarget))
314 (relsrc, reltarget))
315 else:
315 else:
316 ui.warn(_('%s: not recording copy - %s does not exist\n') %
316 ui.warn(_('%s: not recording copy - %s does not exist\n') %
317 (relsrc, reltarget))
317 (relsrc, reltarget))
318 return
318 return
319 elif not dryrun:
319 elif not dryrun:
320 try:
320 try:
321 if exists:
321 if exists:
322 os.unlink(target)
322 os.unlink(target)
323 targetdir = os.path.dirname(target) or '.'
323 targetdir = os.path.dirname(target) or '.'
324 if not os.path.isdir(targetdir):
324 if not os.path.isdir(targetdir):
325 os.makedirs(targetdir)
325 os.makedirs(targetdir)
326 if samefile:
326 if samefile:
327 tmp = target + "~hgrename"
327 tmp = target + "~hgrename"
328 os.rename(src, tmp)
328 os.rename(src, tmp)
329 os.rename(tmp, target)
329 os.rename(tmp, target)
330 else:
330 else:
331 util.copyfile(src, target)
331 util.copyfile(src, target)
332 srcexists = True
332 srcexists = True
333 except IOError, inst:
333 except IOError, inst:
334 if inst.errno == errno.ENOENT:
334 if inst.errno == errno.ENOENT:
335 ui.warn(_('%s: deleted in working copy\n') % relsrc)
335 ui.warn(_('%s: deleted in working copy\n') % relsrc)
336 srcexists = False
336 srcexists = False
337 else:
337 else:
338 ui.warn(_('%s: cannot copy - %s\n') %
338 ui.warn(_('%s: cannot copy - %s\n') %
339 (relsrc, inst.strerror))
339 (relsrc, inst.strerror))
340 return True # report a failure
340 return True # report a failure
341
341
342 if ui.verbose or not exact:
342 if ui.verbose or not exact:
343 if rename:
343 if rename:
344 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
344 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
345 else:
345 else:
346 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
346 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
347
347
348 targets[abstarget] = abssrc
348 targets[abstarget] = abssrc
349
349
350 # fix up dirstate
350 # fix up dirstate
351 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
351 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
352 dryrun=dryrun, cwd=cwd)
352 dryrun=dryrun, cwd=cwd)
353 if rename and not dryrun:
353 if rename and not dryrun:
354 if not after and srcexists and not samefile:
354 if not after and srcexists and not samefile:
355 util.unlinkpath(repo.wjoin(abssrc))
355 util.unlinkpath(repo.wjoin(abssrc))
356 wctx.forget([abssrc])
356 wctx.forget([abssrc])
357
357
358 # pat: ossep
358 # pat: ossep
359 # dest ossep
359 # dest ossep
360 # srcs: list of (hgsep, hgsep, ossep, bool)
360 # srcs: list of (hgsep, hgsep, ossep, bool)
361 # return: function that takes hgsep and returns ossep
361 # return: function that takes hgsep and returns ossep
362 def targetpathfn(pat, dest, srcs):
362 def targetpathfn(pat, dest, srcs):
363 if os.path.isdir(pat):
363 if os.path.isdir(pat):
364 abspfx = scmutil.canonpath(repo.root, cwd, pat)
364 abspfx = scmutil.canonpath(repo.root, cwd, pat)
365 abspfx = util.localpath(abspfx)
365 abspfx = util.localpath(abspfx)
366 if destdirexists:
366 if destdirexists:
367 striplen = len(os.path.split(abspfx)[0])
367 striplen = len(os.path.split(abspfx)[0])
368 else:
368 else:
369 striplen = len(abspfx)
369 striplen = len(abspfx)
370 if striplen:
370 if striplen:
371 striplen += len(os.sep)
371 striplen += len(os.sep)
372 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
372 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
373 elif destdirexists:
373 elif destdirexists:
374 res = lambda p: os.path.join(dest,
374 res = lambda p: os.path.join(dest,
375 os.path.basename(util.localpath(p)))
375 os.path.basename(util.localpath(p)))
376 else:
376 else:
377 res = lambda p: dest
377 res = lambda p: dest
378 return res
378 return res
379
379
380 # pat: ossep
380 # pat: ossep
381 # dest ossep
381 # dest ossep
382 # srcs: list of (hgsep, hgsep, ossep, bool)
382 # srcs: list of (hgsep, hgsep, ossep, bool)
383 # return: function that takes hgsep and returns ossep
383 # return: function that takes hgsep and returns ossep
384 def targetpathafterfn(pat, dest, srcs):
384 def targetpathafterfn(pat, dest, srcs):
385 if matchmod.patkind(pat):
385 if matchmod.patkind(pat):
386 # a mercurial pattern
386 # a mercurial pattern
387 res = lambda p: os.path.join(dest,
387 res = lambda p: os.path.join(dest,
388 os.path.basename(util.localpath(p)))
388 os.path.basename(util.localpath(p)))
389 else:
389 else:
390 abspfx = scmutil.canonpath(repo.root, cwd, pat)
390 abspfx = scmutil.canonpath(repo.root, cwd, pat)
391 if len(abspfx) < len(srcs[0][0]):
391 if len(abspfx) < len(srcs[0][0]):
392 # A directory. Either the target path contains the last
392 # A directory. Either the target path contains the last
393 # component of the source path or it does not.
393 # component of the source path or it does not.
394 def evalpath(striplen):
394 def evalpath(striplen):
395 score = 0
395 score = 0
396 for s in srcs:
396 for s in srcs:
397 t = os.path.join(dest, util.localpath(s[0])[striplen:])
397 t = os.path.join(dest, util.localpath(s[0])[striplen:])
398 if os.path.lexists(t):
398 if os.path.lexists(t):
399 score += 1
399 score += 1
400 return score
400 return score
401
401
402 abspfx = util.localpath(abspfx)
402 abspfx = util.localpath(abspfx)
403 striplen = len(abspfx)
403 striplen = len(abspfx)
404 if striplen:
404 if striplen:
405 striplen += len(os.sep)
405 striplen += len(os.sep)
406 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
406 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
407 score = evalpath(striplen)
407 score = evalpath(striplen)
408 striplen1 = len(os.path.split(abspfx)[0])
408 striplen1 = len(os.path.split(abspfx)[0])
409 if striplen1:
409 if striplen1:
410 striplen1 += len(os.sep)
410 striplen1 += len(os.sep)
411 if evalpath(striplen1) > score:
411 if evalpath(striplen1) > score:
412 striplen = striplen1
412 striplen = striplen1
413 res = lambda p: os.path.join(dest,
413 res = lambda p: os.path.join(dest,
414 util.localpath(p)[striplen:])
414 util.localpath(p)[striplen:])
415 else:
415 else:
416 # a file
416 # a file
417 if destdirexists:
417 if destdirexists:
418 res = lambda p: os.path.join(dest,
418 res = lambda p: os.path.join(dest,
419 os.path.basename(util.localpath(p)))
419 os.path.basename(util.localpath(p)))
420 else:
420 else:
421 res = lambda p: dest
421 res = lambda p: dest
422 return res
422 return res
423
423
424
424
425 pats = scmutil.expandpats(pats)
425 pats = scmutil.expandpats(pats)
426 if not pats:
426 if not pats:
427 raise util.Abort(_('no source or destination specified'))
427 raise util.Abort(_('no source or destination specified'))
428 if len(pats) == 1:
428 if len(pats) == 1:
429 raise util.Abort(_('no destination specified'))
429 raise util.Abort(_('no destination specified'))
430 dest = pats.pop()
430 dest = pats.pop()
431 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
431 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
432 if not destdirexists:
432 if not destdirexists:
433 if len(pats) > 1 or matchmod.patkind(pats[0]):
433 if len(pats) > 1 or matchmod.patkind(pats[0]):
434 raise util.Abort(_('with multiple sources, destination must be an '
434 raise util.Abort(_('with multiple sources, destination must be an '
435 'existing directory'))
435 'existing directory'))
436 if util.endswithsep(dest):
436 if util.endswithsep(dest):
437 raise util.Abort(_('destination %s is not a directory') % dest)
437 raise util.Abort(_('destination %s is not a directory') % dest)
438
438
439 tfn = targetpathfn
439 tfn = targetpathfn
440 if after:
440 if after:
441 tfn = targetpathafterfn
441 tfn = targetpathafterfn
442 copylist = []
442 copylist = []
443 for pat in pats:
443 for pat in pats:
444 srcs = walkpat(pat)
444 srcs = walkpat(pat)
445 if not srcs:
445 if not srcs:
446 continue
446 continue
447 copylist.append((tfn(pat, dest, srcs), srcs))
447 copylist.append((tfn(pat, dest, srcs), srcs))
448 if not copylist:
448 if not copylist:
449 raise util.Abort(_('no files to copy'))
449 raise util.Abort(_('no files to copy'))
450
450
451 errors = 0
451 errors = 0
452 for targetpath, srcs in copylist:
452 for targetpath, srcs in copylist:
453 for abssrc, relsrc, exact in srcs:
453 for abssrc, relsrc, exact in srcs:
454 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
454 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
455 errors += 1
455 errors += 1
456
456
457 if errors:
457 if errors:
458 ui.warn(_('(consider using --after)\n'))
458 ui.warn(_('(consider using --after)\n'))
459
459
460 return errors != 0
460 return errors != 0
461
461
462 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
462 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
463 runargs=None, appendpid=False):
463 runargs=None, appendpid=False):
464 '''Run a command as a service.'''
464 '''Run a command as a service.'''
465
465
466 if opts['daemon'] and not opts['daemon_pipefds']:
466 if opts['daemon'] and not opts['daemon_pipefds']:
467 # Signal child process startup with file removal
467 # Signal child process startup with file removal
468 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
468 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
469 os.close(lockfd)
469 os.close(lockfd)
470 try:
470 try:
471 if not runargs:
471 if not runargs:
472 runargs = util.hgcmd() + sys.argv[1:]
472 runargs = util.hgcmd() + sys.argv[1:]
473 runargs.append('--daemon-pipefds=%s' % lockpath)
473 runargs.append('--daemon-pipefds=%s' % lockpath)
474 # Don't pass --cwd to the child process, because we've already
474 # Don't pass --cwd to the child process, because we've already
475 # changed directory.
475 # changed directory.
476 for i in xrange(1, len(runargs)):
476 for i in xrange(1, len(runargs)):
477 if runargs[i].startswith('--cwd='):
477 if runargs[i].startswith('--cwd='):
478 del runargs[i]
478 del runargs[i]
479 break
479 break
480 elif runargs[i].startswith('--cwd'):
480 elif runargs[i].startswith('--cwd'):
481 del runargs[i:i + 2]
481 del runargs[i:i + 2]
482 break
482 break
483 def condfn():
483 def condfn():
484 return not os.path.exists(lockpath)
484 return not os.path.exists(lockpath)
485 pid = util.rundetached(runargs, condfn)
485 pid = util.rundetached(runargs, condfn)
486 if pid < 0:
486 if pid < 0:
487 raise util.Abort(_('child process failed to start'))
487 raise util.Abort(_('child process failed to start'))
488 finally:
488 finally:
489 try:
489 try:
490 os.unlink(lockpath)
490 os.unlink(lockpath)
491 except OSError, e:
491 except OSError, e:
492 if e.errno != errno.ENOENT:
492 if e.errno != errno.ENOENT:
493 raise
493 raise
494 if parentfn:
494 if parentfn:
495 return parentfn(pid)
495 return parentfn(pid)
496 else:
496 else:
497 return
497 return
498
498
499 if initfn:
499 if initfn:
500 initfn()
500 initfn()
501
501
502 if opts['pid_file']:
502 if opts['pid_file']:
503 mode = appendpid and 'a' or 'w'
503 mode = appendpid and 'a' or 'w'
504 fp = open(opts['pid_file'], mode)
504 fp = open(opts['pid_file'], mode)
505 fp.write(str(os.getpid()) + '\n')
505 fp.write(str(os.getpid()) + '\n')
506 fp.close()
506 fp.close()
507
507
508 if opts['daemon_pipefds']:
508 if opts['daemon_pipefds']:
509 lockpath = opts['daemon_pipefds']
509 lockpath = opts['daemon_pipefds']
510 try:
510 try:
511 os.setsid()
511 os.setsid()
512 except AttributeError:
512 except AttributeError:
513 pass
513 pass
514 os.unlink(lockpath)
514 os.unlink(lockpath)
515 util.hidewindow()
515 util.hidewindow()
516 sys.stdout.flush()
516 sys.stdout.flush()
517 sys.stderr.flush()
517 sys.stderr.flush()
518
518
519 nullfd = os.open(os.devnull, os.O_RDWR)
519 nullfd = os.open(os.devnull, os.O_RDWR)
520 logfilefd = nullfd
520 logfilefd = nullfd
521 if logfile:
521 if logfile:
522 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
522 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
523 os.dup2(nullfd, 0)
523 os.dup2(nullfd, 0)
524 os.dup2(logfilefd, 1)
524 os.dup2(logfilefd, 1)
525 os.dup2(logfilefd, 2)
525 os.dup2(logfilefd, 2)
526 if nullfd not in (0, 1, 2):
526 if nullfd not in (0, 1, 2):
527 os.close(nullfd)
527 os.close(nullfd)
528 if logfile and logfilefd not in (0, 1, 2):
528 if logfile and logfilefd not in (0, 1, 2):
529 os.close(logfilefd)
529 os.close(logfilefd)
530
530
531 if runfn:
531 if runfn:
532 return runfn()
532 return runfn()
533
533
534 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
534 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
535 opts=None):
535 opts=None):
536 '''export changesets as hg patches.'''
536 '''export changesets as hg patches.'''
537
537
538 total = len(revs)
538 total = len(revs)
539 revwidth = max([len(str(rev)) for rev in revs])
539 revwidth = max([len(str(rev)) for rev in revs])
540
540
541 def single(rev, seqno, fp):
541 def single(rev, seqno, fp):
542 ctx = repo[rev]
542 ctx = repo[rev]
543 node = ctx.node()
543 node = ctx.node()
544 parents = [p.node() for p in ctx.parents() if p]
544 parents = [p.node() for p in ctx.parents() if p]
545 branch = ctx.branch()
545 branch = ctx.branch()
546 if switch_parent:
546 if switch_parent:
547 parents.reverse()
547 parents.reverse()
548 prev = (parents and parents[0]) or nullid
548 prev = (parents and parents[0]) or nullid
549
549
550 shouldclose = False
550 shouldclose = False
551 if not fp and len(template) > 0:
551 if not fp and len(template) > 0:
552 desc_lines = ctx.description().rstrip().split('\n')
552 desc_lines = ctx.description().rstrip().split('\n')
553 desc = desc_lines[0] #Commit always has a first line.
553 desc = desc_lines[0] #Commit always has a first line.
554 fp = makefileobj(repo, template, node, desc=desc, total=total,
554 fp = makefileobj(repo, template, node, desc=desc, total=total,
555 seqno=seqno, revwidth=revwidth, mode='ab')
555 seqno=seqno, revwidth=revwidth, mode='ab')
556 if fp != template:
556 if fp != template:
557 shouldclose = True
557 shouldclose = True
558 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
558 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
559 repo.ui.note("%s\n" % fp.name)
559 repo.ui.note("%s\n" % fp.name)
560
560
561 if not fp:
561 if not fp:
562 write = repo.ui.write
562 write = repo.ui.write
563 else:
563 else:
564 def write(s, **kw):
564 def write(s, **kw):
565 fp.write(s)
565 fp.write(s)
566
566
567
567
568 write("# HG changeset patch\n")
568 write("# HG changeset patch\n")
569 write("# User %s\n" % ctx.user())
569 write("# User %s\n" % ctx.user())
570 write("# Date %d %d\n" % ctx.date())
570 write("# Date %d %d\n" % ctx.date())
571 if branch and branch != 'default':
571 if branch and branch != 'default':
572 write("# Branch %s\n" % branch)
572 write("# Branch %s\n" % branch)
573 write("# Node ID %s\n" % hex(node))
573 write("# Node ID %s\n" % hex(node))
574 write("# Parent %s\n" % hex(prev))
574 write("# Parent %s\n" % hex(prev))
575 if len(parents) > 1:
575 if len(parents) > 1:
576 write("# Parent %s\n" % hex(parents[1]))
576 write("# Parent %s\n" % hex(parents[1]))
577 write(ctx.description().rstrip())
577 write(ctx.description().rstrip())
578 write("\n\n")
578 write("\n\n")
579
579
580 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
580 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
581 write(chunk, label=label)
581 write(chunk, label=label)
582
582
583 if shouldclose:
583 if shouldclose:
584 fp.close()
584 fp.close()
585
585
586 for seqno, rev in enumerate(revs):
586 for seqno, rev in enumerate(revs):
587 single(rev, seqno + 1, fp)
587 single(rev, seqno + 1, fp)
588
588
589 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
589 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
590 changes=None, stat=False, fp=None, prefix='',
590 changes=None, stat=False, fp=None, prefix='',
591 listsubrepos=False):
591 listsubrepos=False):
592 '''show diff or diffstat.'''
592 '''show diff or diffstat.'''
593 if fp is None:
593 if fp is None:
594 write = ui.write
594 write = ui.write
595 else:
595 else:
596 def write(s, **kw):
596 def write(s, **kw):
597 fp.write(s)
597 fp.write(s)
598
598
599 if stat:
599 if stat:
600 diffopts = diffopts.copy(context=0)
600 diffopts = diffopts.copy(context=0)
601 width = 80
601 width = 80
602 if not ui.plain():
602 if not ui.plain():
603 width = ui.termwidth()
603 width = ui.termwidth()
604 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
604 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
605 prefix=prefix)
605 prefix=prefix)
606 for chunk, label in patch.diffstatui(util.iterlines(chunks),
606 for chunk, label in patch.diffstatui(util.iterlines(chunks),
607 width=width,
607 width=width,
608 git=diffopts.git):
608 git=diffopts.git):
609 write(chunk, label=label)
609 write(chunk, label=label)
610 else:
610 else:
611 for chunk, label in patch.diffui(repo, node1, node2, match,
611 for chunk, label in patch.diffui(repo, node1, node2, match,
612 changes, diffopts, prefix=prefix):
612 changes, diffopts, prefix=prefix):
613 write(chunk, label=label)
613 write(chunk, label=label)
614
614
615 if listsubrepos:
615 if listsubrepos:
616 ctx1 = repo[node1]
616 ctx1 = repo[node1]
617 ctx2 = repo[node2]
617 ctx2 = repo[node2]
618 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
618 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
619 tempnode2 = node2
619 tempnode2 = node2
620 try:
620 try:
621 if node2 is not None:
621 if node2 is not None:
622 tempnode2 = ctx2.substate[subpath][1]
622 tempnode2 = ctx2.substate[subpath][1]
623 except KeyError:
623 except KeyError:
624 # A subrepo that existed in node1 was deleted between node1 and
624 # A subrepo that existed in node1 was deleted between node1 and
625 # node2 (inclusive). Thus, ctx2's substate won't contain that
625 # node2 (inclusive). Thus, ctx2's substate won't contain that
626 # subpath. The best we can do is to ignore it.
626 # subpath. The best we can do is to ignore it.
627 tempnode2 = None
627 tempnode2 = None
628 submatch = matchmod.narrowmatcher(subpath, match)
628 submatch = matchmod.narrowmatcher(subpath, match)
629 sub.diff(diffopts, tempnode2, submatch, changes=changes,
629 sub.diff(diffopts, tempnode2, submatch, changes=changes,
630 stat=stat, fp=fp, prefix=prefix)
630 stat=stat, fp=fp, prefix=prefix)
631
631
632 class changeset_printer(object):
632 class changeset_printer(object):
633 '''show changeset information when templating not requested.'''
633 '''show changeset information when templating not requested.'''
634
634
635 def __init__(self, ui, repo, patch, diffopts, buffered):
635 def __init__(self, ui, repo, patch, diffopts, buffered):
636 self.ui = ui
636 self.ui = ui
637 self.repo = repo
637 self.repo = repo
638 self.buffered = buffered
638 self.buffered = buffered
639 self.patch = patch
639 self.patch = patch
640 self.diffopts = diffopts
640 self.diffopts = diffopts
641 self.header = {}
641 self.header = {}
642 self.hunk = {}
642 self.hunk = {}
643 self.lastheader = None
643 self.lastheader = None
644 self.footer = None
644 self.footer = None
645
645
646 def flush(self, rev):
646 def flush(self, rev):
647 if rev in self.header:
647 if rev in self.header:
648 h = self.header[rev]
648 h = self.header[rev]
649 if h != self.lastheader:
649 if h != self.lastheader:
650 self.lastheader = h
650 self.lastheader = h
651 self.ui.write(h)
651 self.ui.write(h)
652 del self.header[rev]
652 del self.header[rev]
653 if rev in self.hunk:
653 if rev in self.hunk:
654 self.ui.write(self.hunk[rev])
654 self.ui.write(self.hunk[rev])
655 del self.hunk[rev]
655 del self.hunk[rev]
656 return 1
656 return 1
657 return 0
657 return 0
658
658
659 def close(self):
659 def close(self):
660 if self.footer:
660 if self.footer:
661 self.ui.write(self.footer)
661 self.ui.write(self.footer)
662
662
663 def show(self, ctx, copies=None, matchfn=None, **props):
663 def show(self, ctx, copies=None, matchfn=None, **props):
664 if self.buffered:
664 if self.buffered:
665 self.ui.pushbuffer()
665 self.ui.pushbuffer()
666 self._show(ctx, copies, matchfn, props)
666 self._show(ctx, copies, matchfn, props)
667 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
667 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
668 else:
668 else:
669 self._show(ctx, copies, matchfn, props)
669 self._show(ctx, copies, matchfn, props)
670
670
671 def _show(self, ctx, copies, matchfn, props):
671 def _show(self, ctx, copies, matchfn, props):
672 '''show a single changeset or file revision'''
672 '''show a single changeset or file revision'''
673 changenode = ctx.node()
673 changenode = ctx.node()
674 rev = ctx.rev()
674 rev = ctx.rev()
675
675
676 if self.ui.quiet:
676 if self.ui.quiet:
677 self.ui.write("%d:%s\n" % (rev, short(changenode)),
677 self.ui.write("%d:%s\n" % (rev, short(changenode)),
678 label='log.node')
678 label='log.node')
679 return
679 return
680
680
681 log = self.repo.changelog
681 log = self.repo.changelog
682 date = util.datestr(ctx.date())
682 date = util.datestr(ctx.date())
683
683
684 hexfunc = self.ui.debugflag and hex or short
684 hexfunc = self.ui.debugflag and hex or short
685
685
686 parents = [(p, hexfunc(log.node(p)))
686 parents = [(p, hexfunc(log.node(p)))
687 for p in self._meaningful_parentrevs(log, rev)]
687 for p in self._meaningful_parentrevs(log, rev)]
688
688
689 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
689 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
690 label='log.changeset')
690 label='log.changeset')
691
691
692 branch = ctx.branch()
692 branch = ctx.branch()
693 # don't show the default branch name
693 # don't show the default branch name
694 if branch != 'default':
694 if branch != 'default':
695 self.ui.write(_("branch: %s\n") % branch,
695 self.ui.write(_("branch: %s\n") % branch,
696 label='log.branch')
696 label='log.branch')
697 for bookmark in self.repo.nodebookmarks(changenode):
697 for bookmark in self.repo.nodebookmarks(changenode):
698 self.ui.write(_("bookmark: %s\n") % bookmark,
698 self.ui.write(_("bookmark: %s\n") % bookmark,
699 label='log.bookmark')
699 label='log.bookmark')
700 for tag in self.repo.nodetags(changenode):
700 for tag in self.repo.nodetags(changenode):
701 self.ui.write(_("tag: %s\n") % tag,
701 self.ui.write(_("tag: %s\n") % tag,
702 label='log.tag')
702 label='log.tag')
703 if self.ui.debugflag and ctx.phase():
703 if self.ui.debugflag and ctx.phase():
704 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
704 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
705 label='log.phase')
705 label='log.phase')
706 for parent in parents:
706 for parent in parents:
707 self.ui.write(_("parent: %d:%s\n") % parent,
707 self.ui.write(_("parent: %d:%s\n") % parent,
708 label='log.parent')
708 label='log.parent')
709
709
710 if self.ui.debugflag:
710 if self.ui.debugflag:
711 mnode = ctx.manifestnode()
711 mnode = ctx.manifestnode()
712 self.ui.write(_("manifest: %d:%s\n") %
712 self.ui.write(_("manifest: %d:%s\n") %
713 (self.repo.manifest.rev(mnode), hex(mnode)),
713 (self.repo.manifest.rev(mnode), hex(mnode)),
714 label='ui.debug log.manifest')
714 label='ui.debug log.manifest')
715 self.ui.write(_("user: %s\n") % ctx.user(),
715 self.ui.write(_("user: %s\n") % ctx.user(),
716 label='log.user')
716 label='log.user')
717 self.ui.write(_("date: %s\n") % date,
717 self.ui.write(_("date: %s\n") % date,
718 label='log.date')
718 label='log.date')
719
719
720 if self.ui.debugflag:
720 if self.ui.debugflag:
721 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
721 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
722 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
722 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
723 files):
723 files):
724 if value:
724 if value:
725 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
725 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
726 label='ui.debug log.files')
726 label='ui.debug log.files')
727 elif ctx.files() and self.ui.verbose:
727 elif ctx.files() and self.ui.verbose:
728 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
728 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
729 label='ui.note log.files')
729 label='ui.note log.files')
730 if copies and self.ui.verbose:
730 if copies and self.ui.verbose:
731 copies = ['%s (%s)' % c for c in copies]
731 copies = ['%s (%s)' % c for c in copies]
732 self.ui.write(_("copies: %s\n") % ' '.join(copies),
732 self.ui.write(_("copies: %s\n") % ' '.join(copies),
733 label='ui.note log.copies')
733 label='ui.note log.copies')
734
734
735 extra = ctx.extra()
735 extra = ctx.extra()
736 if extra and self.ui.debugflag:
736 if extra and self.ui.debugflag:
737 for key, value in sorted(extra.items()):
737 for key, value in sorted(extra.items()):
738 self.ui.write(_("extra: %s=%s\n")
738 self.ui.write(_("extra: %s=%s\n")
739 % (key, value.encode('string_escape')),
739 % (key, value.encode('string_escape')),
740 label='ui.debug log.extra')
740 label='ui.debug log.extra')
741
741
742 description = ctx.description().strip()
742 description = ctx.description().strip()
743 if description:
743 if description:
744 if self.ui.verbose:
744 if self.ui.verbose:
745 self.ui.write(_("description:\n"),
745 self.ui.write(_("description:\n"),
746 label='ui.note log.description')
746 label='ui.note log.description')
747 self.ui.write(description,
747 self.ui.write(description,
748 label='ui.note log.description')
748 label='ui.note log.description')
749 self.ui.write("\n\n")
749 self.ui.write("\n\n")
750 else:
750 else:
751 self.ui.write(_("summary: %s\n") %
751 self.ui.write(_("summary: %s\n") %
752 description.splitlines()[0],
752 description.splitlines()[0],
753 label='log.summary')
753 label='log.summary')
754 self.ui.write("\n")
754 self.ui.write("\n")
755
755
756 self.showpatch(changenode, matchfn)
756 self.showpatch(changenode, matchfn)
757
757
758 def showpatch(self, node, matchfn):
758 def showpatch(self, node, matchfn):
759 if not matchfn:
759 if not matchfn:
760 matchfn = self.patch
760 matchfn = self.patch
761 if matchfn:
761 if matchfn:
762 stat = self.diffopts.get('stat')
762 stat = self.diffopts.get('stat')
763 diff = self.diffopts.get('patch')
763 diff = self.diffopts.get('patch')
764 diffopts = patch.diffopts(self.ui, self.diffopts)
764 diffopts = patch.diffopts(self.ui, self.diffopts)
765 prev = self.repo.changelog.parents(node)[0]
765 prev = self.repo.changelog.parents(node)[0]
766 if stat:
766 if stat:
767 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
767 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
768 match=matchfn, stat=True)
768 match=matchfn, stat=True)
769 if diff:
769 if diff:
770 if stat:
770 if stat:
771 self.ui.write("\n")
771 self.ui.write("\n")
772 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
772 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
773 match=matchfn, stat=False)
773 match=matchfn, stat=False)
774 self.ui.write("\n")
774 self.ui.write("\n")
775
775
776 def _meaningful_parentrevs(self, log, rev):
776 def _meaningful_parentrevs(self, log, rev):
777 """Return list of meaningful (or all if debug) parentrevs for rev.
777 """Return list of meaningful (or all if debug) parentrevs for rev.
778
778
779 For merges (two non-nullrev revisions) both parents are meaningful.
779 For merges (two non-nullrev revisions) both parents are meaningful.
780 Otherwise the first parent revision is considered meaningful if it
780 Otherwise the first parent revision is considered meaningful if it
781 is not the preceding revision.
781 is not the preceding revision.
782 """
782 """
783 parents = log.parentrevs(rev)
783 parents = log.parentrevs(rev)
784 if not self.ui.debugflag and parents[1] == nullrev:
784 if not self.ui.debugflag and parents[1] == nullrev:
785 if parents[0] >= rev - 1:
785 if parents[0] >= rev - 1:
786 parents = []
786 parents = []
787 else:
787 else:
788 parents = [parents[0]]
788 parents = [parents[0]]
789 return parents
789 return parents
790
790
791
791
792 class changeset_templater(changeset_printer):
792 class changeset_templater(changeset_printer):
793 '''format changeset information.'''
793 '''format changeset information.'''
794
794
795 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
795 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
796 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
796 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
797 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
797 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
798 defaulttempl = {
798 defaulttempl = {
799 'parent': '{rev}:{node|formatnode} ',
799 'parent': '{rev}:{node|formatnode} ',
800 'manifest': '{rev}:{node|formatnode}',
800 'manifest': '{rev}:{node|formatnode}',
801 'file_copy': '{name} ({source})',
801 'file_copy': '{name} ({source})',
802 'extra': '{key}={value|stringescape}'
802 'extra': '{key}={value|stringescape}'
803 }
803 }
804 # filecopy is preserved for compatibility reasons
804 # filecopy is preserved for compatibility reasons
805 defaulttempl['filecopy'] = defaulttempl['file_copy']
805 defaulttempl['filecopy'] = defaulttempl['file_copy']
806 self.t = templater.templater(mapfile, {'formatnode': formatnode},
806 self.t = templater.templater(mapfile, {'formatnode': formatnode},
807 cache=defaulttempl)
807 cache=defaulttempl)
808 self.cache = {}
808 self.cache = {}
809
809
810 def use_template(self, t):
810 def use_template(self, t):
811 '''set template string to use'''
811 '''set template string to use'''
812 self.t.cache['changeset'] = t
812 self.t.cache['changeset'] = t
813
813
814 def _meaningful_parentrevs(self, ctx):
814 def _meaningful_parentrevs(self, ctx):
815 """Return list of meaningful (or all if debug) parentrevs for rev.
815 """Return list of meaningful (or all if debug) parentrevs for rev.
816 """
816 """
817 parents = ctx.parents()
817 parents = ctx.parents()
818 if len(parents) > 1:
818 if len(parents) > 1:
819 return parents
819 return parents
820 if self.ui.debugflag:
820 if self.ui.debugflag:
821 return [parents[0], self.repo['null']]
821 return [parents[0], self.repo['null']]
822 if parents[0].rev() >= ctx.rev() - 1:
822 if parents[0].rev() >= ctx.rev() - 1:
823 return []
823 return []
824 return parents
824 return parents
825
825
826 def _show(self, ctx, copies, matchfn, props):
826 def _show(self, ctx, copies, matchfn, props):
827 '''show a single changeset or file revision'''
827 '''show a single changeset or file revision'''
828
828
829 showlist = templatekw.showlist
829 showlist = templatekw.showlist
830
830
831 # showparents() behaviour depends on ui trace level which
831 # showparents() behaviour depends on ui trace level which
832 # causes unexpected behaviours at templating level and makes
832 # causes unexpected behaviours at templating level and makes
833 # it harder to extract it in a standalone function. Its
833 # it harder to extract it in a standalone function. Its
834 # behaviour cannot be changed so leave it here for now.
834 # behaviour cannot be changed so leave it here for now.
835 def showparents(**args):
835 def showparents(**args):
836 ctx = args['ctx']
836 ctx = args['ctx']
837 parents = [[('rev', p.rev()), ('node', p.hex())]
837 parents = [[('rev', p.rev()), ('node', p.hex())]
838 for p in self._meaningful_parentrevs(ctx)]
838 for p in self._meaningful_parentrevs(ctx)]
839 return showlist('parent', parents, **args)
839 return showlist('parent', parents, **args)
840
840
841 props = props.copy()
841 props = props.copy()
842 props.update(templatekw.keywords)
842 props.update(templatekw.keywords)
843 props['parents'] = showparents
843 props['parents'] = showparents
844 props['templ'] = self.t
844 props['templ'] = self.t
845 props['ctx'] = ctx
845 props['ctx'] = ctx
846 props['repo'] = self.repo
846 props['repo'] = self.repo
847 props['revcache'] = {'copies': copies}
847 props['revcache'] = {'copies': copies}
848 props['cache'] = self.cache
848 props['cache'] = self.cache
849
849
850 # find correct templates for current mode
850 # find correct templates for current mode
851
851
852 tmplmodes = [
852 tmplmodes = [
853 (True, None),
853 (True, None),
854 (self.ui.verbose, 'verbose'),
854 (self.ui.verbose, 'verbose'),
855 (self.ui.quiet, 'quiet'),
855 (self.ui.quiet, 'quiet'),
856 (self.ui.debugflag, 'debug'),
856 (self.ui.debugflag, 'debug'),
857 ]
857 ]
858
858
859 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
859 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
860 for mode, postfix in tmplmodes:
860 for mode, postfix in tmplmodes:
861 for type in types:
861 for type in types:
862 cur = postfix and ('%s_%s' % (type, postfix)) or type
862 cur = postfix and ('%s_%s' % (type, postfix)) or type
863 if mode and cur in self.t:
863 if mode and cur in self.t:
864 types[type] = cur
864 types[type] = cur
865
865
866 try:
866 try:
867
867
868 # write header
868 # write header
869 if types['header']:
869 if types['header']:
870 h = templater.stringify(self.t(types['header'], **props))
870 h = templater.stringify(self.t(types['header'], **props))
871 if self.buffered:
871 if self.buffered:
872 self.header[ctx.rev()] = h
872 self.header[ctx.rev()] = h
873 else:
873 else:
874 if self.lastheader != h:
874 if self.lastheader != h:
875 self.lastheader = h
875 self.lastheader = h
876 self.ui.write(h)
876 self.ui.write(h)
877
877
878 # write changeset metadata, then patch if requested
878 # write changeset metadata, then patch if requested
879 key = types['changeset']
879 key = types['changeset']
880 self.ui.write(templater.stringify(self.t(key, **props)))
880 self.ui.write(templater.stringify(self.t(key, **props)))
881 self.showpatch(ctx.node(), matchfn)
881 self.showpatch(ctx.node(), matchfn)
882
882
883 if types['footer']:
883 if types['footer']:
884 if not self.footer:
884 if not self.footer:
885 self.footer = templater.stringify(self.t(types['footer'],
885 self.footer = templater.stringify(self.t(types['footer'],
886 **props))
886 **props))
887
887
888 except KeyError, inst:
888 except KeyError, inst:
889 msg = _("%s: no key named '%s'")
889 msg = _("%s: no key named '%s'")
890 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
890 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
891 except SyntaxError, inst:
891 except SyntaxError, inst:
892 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
892 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
893
893
894 def show_changeset(ui, repo, opts, buffered=False):
894 def show_changeset(ui, repo, opts, buffered=False):
895 """show one changeset using template or regular display.
895 """show one changeset using template or regular display.
896
896
897 Display format will be the first non-empty hit of:
897 Display format will be the first non-empty hit of:
898 1. option 'template'
898 1. option 'template'
899 2. option 'style'
899 2. option 'style'
900 3. [ui] setting 'logtemplate'
900 3. [ui] setting 'logtemplate'
901 4. [ui] setting 'style'
901 4. [ui] setting 'style'
902 If all of these values are either the unset or the empty string,
902 If all of these values are either the unset or the empty string,
903 regular display via changeset_printer() is done.
903 regular display via changeset_printer() is done.
904 """
904 """
905 # options
905 # options
906 patch = False
906 patch = False
907 if opts.get('patch') or opts.get('stat'):
907 if opts.get('patch') or opts.get('stat'):
908 patch = scmutil.matchall(repo)
908 patch = scmutil.matchall(repo)
909
909
910 tmpl = opts.get('template')
910 tmpl = opts.get('template')
911 style = None
911 style = None
912 if tmpl:
912 if tmpl:
913 tmpl = templater.parsestring(tmpl, quoted=False)
913 tmpl = templater.parsestring(tmpl, quoted=False)
914 else:
914 else:
915 style = opts.get('style')
915 style = opts.get('style')
916
916
917 # ui settings
917 # ui settings
918 if not (tmpl or style):
918 if not (tmpl or style):
919 tmpl = ui.config('ui', 'logtemplate')
919 tmpl = ui.config('ui', 'logtemplate')
920 if tmpl:
920 if tmpl:
921 try:
921 try:
922 tmpl = templater.parsestring(tmpl)
922 tmpl = templater.parsestring(tmpl)
923 except SyntaxError:
923 except SyntaxError:
924 tmpl = templater.parsestring(tmpl, quoted=False)
924 tmpl = templater.parsestring(tmpl, quoted=False)
925 else:
925 else:
926 style = util.expandpath(ui.config('ui', 'style', ''))
926 style = util.expandpath(ui.config('ui', 'style', ''))
927
927
928 if not (tmpl or style):
928 if not (tmpl or style):
929 return changeset_printer(ui, repo, patch, opts, buffered)
929 return changeset_printer(ui, repo, patch, opts, buffered)
930
930
931 mapfile = None
931 mapfile = None
932 if style and not tmpl:
932 if style and not tmpl:
933 mapfile = style
933 mapfile = style
934 if not os.path.split(mapfile)[0]:
934 if not os.path.split(mapfile)[0]:
935 mapname = (templater.templatepath('map-cmdline.' + mapfile)
935 mapname = (templater.templatepath('map-cmdline.' + mapfile)
936 or templater.templatepath(mapfile))
936 or templater.templatepath(mapfile))
937 if mapname:
937 if mapname:
938 mapfile = mapname
938 mapfile = mapname
939
939
940 try:
940 try:
941 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
941 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
942 except SyntaxError, inst:
942 except SyntaxError, inst:
943 raise util.Abort(inst.args[0])
943 raise util.Abort(inst.args[0])
944 if tmpl:
944 if tmpl:
945 t.use_template(tmpl)
945 t.use_template(tmpl)
946 return t
946 return t
947
947
948 def finddate(ui, repo, date):
948 def finddate(ui, repo, date):
949 """Find the tipmost changeset that matches the given date spec"""
949 """Find the tipmost changeset that matches the given date spec"""
950
950
951 df = util.matchdate(date)
951 df = util.matchdate(date)
952 m = scmutil.matchall(repo)
952 m = scmutil.matchall(repo)
953 results = {}
953 results = {}
954
954
955 def prep(ctx, fns):
955 def prep(ctx, fns):
956 d = ctx.date()
956 d = ctx.date()
957 if df(d[0]):
957 if df(d[0]):
958 results[ctx.rev()] = d
958 results[ctx.rev()] = d
959
959
960 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
960 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
961 rev = ctx.rev()
961 rev = ctx.rev()
962 if rev in results:
962 if rev in results:
963 ui.status(_("found revision %s from %s\n") %
963 ui.status(_("found revision %s from %s\n") %
964 (rev, util.datestr(results[rev])))
964 (rev, util.datestr(results[rev])))
965 return str(rev)
965 return str(rev)
966
966
967 raise util.Abort(_("revision matching date not found"))
967 raise util.Abort(_("revision matching date not found"))
968
968
969 def increasingwindows(start, end, windowsize=8, sizelimit=512):
969 def increasingwindows(start, end, windowsize=8, sizelimit=512):
970 if start < end:
970 if start < end:
971 while start < end:
971 while start < end:
972 yield start, min(windowsize, end - start)
972 yield start, min(windowsize, end - start)
973 start += windowsize
973 start += windowsize
974 if windowsize < sizelimit:
974 if windowsize < sizelimit:
975 windowsize *= 2
975 windowsize *= 2
976 else:
976 else:
977 while start > end:
977 while start > end:
978 yield start, min(windowsize, start - end - 1)
978 yield start, min(windowsize, start - end - 1)
979 start -= windowsize
979 start -= windowsize
980 if windowsize < sizelimit:
980 if windowsize < sizelimit:
981 windowsize *= 2
981 windowsize *= 2
982
982
983 def walkchangerevs(repo, match, opts, prepare):
983 def walkchangerevs(repo, match, opts, prepare):
984 '''Iterate over files and the revs in which they changed.
984 '''Iterate over files and the revs in which they changed.
985
985
986 Callers most commonly need to iterate backwards over the history
986 Callers most commonly need to iterate backwards over the history
987 in which they are interested. Doing so has awful (quadratic-looking)
987 in which they are interested. Doing so has awful (quadratic-looking)
988 performance, so we use iterators in a "windowed" way.
988 performance, so we use iterators in a "windowed" way.
989
989
990 We walk a window of revisions in the desired order. Within the
990 We walk a window of revisions in the desired order. Within the
991 window, we first walk forwards to gather data, then in the desired
991 window, we first walk forwards to gather data, then in the desired
992 order (usually backwards) to display it.
992 order (usually backwards) to display it.
993
993
994 This function returns an iterator yielding contexts. Before
994 This function returns an iterator yielding contexts. Before
995 yielding each context, the iterator will first call the prepare
995 yielding each context, the iterator will first call the prepare
996 function on each context in the window in forward order.'''
996 function on each context in the window in forward order.'''
997
997
998 follow = opts.get('follow') or opts.get('follow_first')
998 follow = opts.get('follow') or opts.get('follow_first')
999
999
1000 if not len(repo):
1000 if not len(repo):
1001 return []
1001 return []
1002 if opts.get('rev'):
1002 if opts.get('rev'):
1003 revs = scmutil.revrange(repo, opts.get('rev'))
1003 revs = scmutil.revrange(repo, opts.get('rev'))
1004 elif follow:
1004 elif follow:
1005 revs = repo.revs('reverse(:.)')
1005 revs = repo.revs('reverse(:.)')
1006 else:
1006 else:
1007 revs = list(repo)
1007 revs = list(repo)
1008 revs.reverse()
1008 revs.reverse()
1009 if not revs:
1009 if not revs:
1010 return []
1010 return []
1011 wanted = set()
1011 wanted = set()
1012 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1012 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1013 fncache = {}
1013 fncache = {}
1014 change = repo.changectx
1014 change = repo.changectx
1015
1015
1016 # First step is to fill wanted, the set of revisions that we want to yield.
1016 # First step is to fill wanted, the set of revisions that we want to yield.
1017 # When it does not induce extra cost, we also fill fncache for revisions in
1017 # When it does not induce extra cost, we also fill fncache for revisions in
1018 # wanted: a cache of filenames that were changed (ctx.files()) and that
1018 # wanted: a cache of filenames that were changed (ctx.files()) and that
1019 # match the file filtering conditions.
1019 # match the file filtering conditions.
1020
1020
1021 if not slowpath and not match.files():
1021 if not slowpath and not match.files():
1022 # No files, no patterns. Display all revs.
1022 # No files, no patterns. Display all revs.
1023 wanted = set(revs)
1023 wanted = set(revs)
1024 copies = []
1024 copies = []
1025
1025
1026 if not slowpath and match.files():
1026 if not slowpath and match.files():
1027 # We only have to read through the filelog to find wanted revisions
1027 # We only have to read through the filelog to find wanted revisions
1028
1028
1029 minrev, maxrev = min(revs), max(revs)
1029 minrev, maxrev = min(revs), max(revs)
1030 def filerevgen(filelog, last):
1030 def filerevgen(filelog, last):
1031 """
1031 """
1032 Only files, no patterns. Check the history of each file.
1032 Only files, no patterns. Check the history of each file.
1033
1033
1034 Examines filelog entries within minrev, maxrev linkrev range
1034 Examines filelog entries within minrev, maxrev linkrev range
1035 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1035 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1036 tuples in backwards order
1036 tuples in backwards order
1037 """
1037 """
1038 cl_count = len(repo)
1038 cl_count = len(repo)
1039 revs = []
1039 revs = []
1040 for j in xrange(0, last + 1):
1040 for j in xrange(0, last + 1):
1041 linkrev = filelog.linkrev(j)
1041 linkrev = filelog.linkrev(j)
1042 if linkrev < minrev:
1042 if linkrev < minrev:
1043 continue
1043 continue
1044 # only yield rev for which we have the changelog, it can
1044 # only yield rev for which we have the changelog, it can
1045 # happen while doing "hg log" during a pull or commit
1045 # happen while doing "hg log" during a pull or commit
1046 if linkrev >= cl_count:
1046 if linkrev >= cl_count:
1047 break
1047 break
1048
1048
1049 parentlinkrevs = []
1049 parentlinkrevs = []
1050 for p in filelog.parentrevs(j):
1050 for p in filelog.parentrevs(j):
1051 if p != nullrev:
1051 if p != nullrev:
1052 parentlinkrevs.append(filelog.linkrev(p))
1052 parentlinkrevs.append(filelog.linkrev(p))
1053 n = filelog.node(j)
1053 n = filelog.node(j)
1054 revs.append((linkrev, parentlinkrevs,
1054 revs.append((linkrev, parentlinkrevs,
1055 follow and filelog.renamed(n)))
1055 follow and filelog.renamed(n)))
1056
1056
1057 return reversed(revs)
1057 return reversed(revs)
1058 def iterfiles():
1058 def iterfiles():
1059 pctx = repo['.']
1059 pctx = repo['.']
1060 for filename in match.files():
1060 for filename in match.files():
1061 if follow:
1061 if follow:
1062 if filename not in pctx:
1062 if filename not in pctx:
1063 raise util.Abort(_('cannot follow file not in parent '
1063 raise util.Abort(_('cannot follow file not in parent '
1064 'revision: "%s"') % filename)
1064 'revision: "%s"') % filename)
1065 yield filename, pctx[filename].filenode()
1065 yield filename, pctx[filename].filenode()
1066 else:
1066 else:
1067 yield filename, None
1067 yield filename, None
1068 for filename_node in copies:
1068 for filename_node in copies:
1069 yield filename_node
1069 yield filename_node
1070 for file_, node in iterfiles():
1070 for file_, node in iterfiles():
1071 filelog = repo.file(file_)
1071 filelog = repo.file(file_)
1072 if not len(filelog):
1072 if not len(filelog):
1073 if node is None:
1073 if node is None:
1074 # A zero count may be a directory or deleted file, so
1074 # A zero count may be a directory or deleted file, so
1075 # try to find matching entries on the slow path.
1075 # try to find matching entries on the slow path.
1076 if follow:
1076 if follow:
1077 raise util.Abort(
1077 raise util.Abort(
1078 _('cannot follow nonexistent file: "%s"') % file_)
1078 _('cannot follow nonexistent file: "%s"') % file_)
1079 slowpath = True
1079 slowpath = True
1080 break
1080 break
1081 else:
1081 else:
1082 continue
1082 continue
1083
1083
1084 if node is None:
1084 if node is None:
1085 last = len(filelog) - 1
1085 last = len(filelog) - 1
1086 else:
1086 else:
1087 last = filelog.rev(node)
1087 last = filelog.rev(node)
1088
1088
1089
1089
1090 # keep track of all ancestors of the file
1090 # keep track of all ancestors of the file
1091 ancestors = set([filelog.linkrev(last)])
1091 ancestors = set([filelog.linkrev(last)])
1092
1092
1093 # iterate from latest to oldest revision
1093 # iterate from latest to oldest revision
1094 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1094 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1095 if not follow:
1095 if not follow:
1096 if rev > maxrev:
1096 if rev > maxrev:
1097 continue
1097 continue
1098 else:
1098 else:
1099 # Note that last might not be the first interesting
1099 # Note that last might not be the first interesting
1100 # rev to us:
1100 # rev to us:
1101 # if the file has been changed after maxrev, we'll
1101 # if the file has been changed after maxrev, we'll
1102 # have linkrev(last) > maxrev, and we still need
1102 # have linkrev(last) > maxrev, and we still need
1103 # to explore the file graph
1103 # to explore the file graph
1104 if rev not in ancestors:
1104 if rev not in ancestors:
1105 continue
1105 continue
1106 # XXX insert 1327 fix here
1106 # XXX insert 1327 fix here
1107 if flparentlinkrevs:
1107 if flparentlinkrevs:
1108 ancestors.update(flparentlinkrevs)
1108 ancestors.update(flparentlinkrevs)
1109
1109
1110 fncache.setdefault(rev, []).append(file_)
1110 fncache.setdefault(rev, []).append(file_)
1111 wanted.add(rev)
1111 wanted.add(rev)
1112 if copied:
1112 if copied:
1113 copies.append(copied)
1113 copies.append(copied)
1114
1115 # We decided to fall back to the slowpath because at least one
1116 # of the paths was not a file. Check to see if at least one of them
1117 # existed in history, otherwise simply return
1118 if slowpath:
1119 for path in match.files():
1120 if path == '.' or path in repo.store:
1121 break
1122 else:
1123 return []
1124
1114 if slowpath:
1125 if slowpath:
1115 # We have to read the changelog to match filenames against
1126 # We have to read the changelog to match filenames against
1116 # changed files
1127 # changed files
1117
1128
1118 if follow:
1129 if follow:
1119 raise util.Abort(_('can only follow copies/renames for explicit '
1130 raise util.Abort(_('can only follow copies/renames for explicit '
1120 'filenames'))
1131 'filenames'))
1121
1132
1122 # The slow path checks files modified in every changeset.
1133 # The slow path checks files modified in every changeset.
1123 for i in sorted(revs):
1134 for i in sorted(revs):
1124 ctx = change(i)
1135 ctx = change(i)
1125 matches = filter(match, ctx.files())
1136 matches = filter(match, ctx.files())
1126 if matches:
1137 if matches:
1127 fncache[i] = matches
1138 fncache[i] = matches
1128 wanted.add(i)
1139 wanted.add(i)
1129
1140
1130 class followfilter(object):
1141 class followfilter(object):
1131 def __init__(self, onlyfirst=False):
1142 def __init__(self, onlyfirst=False):
1132 self.startrev = nullrev
1143 self.startrev = nullrev
1133 self.roots = set()
1144 self.roots = set()
1134 self.onlyfirst = onlyfirst
1145 self.onlyfirst = onlyfirst
1135
1146
1136 def match(self, rev):
1147 def match(self, rev):
1137 def realparents(rev):
1148 def realparents(rev):
1138 if self.onlyfirst:
1149 if self.onlyfirst:
1139 return repo.changelog.parentrevs(rev)[0:1]
1150 return repo.changelog.parentrevs(rev)[0:1]
1140 else:
1151 else:
1141 return filter(lambda x: x != nullrev,
1152 return filter(lambda x: x != nullrev,
1142 repo.changelog.parentrevs(rev))
1153 repo.changelog.parentrevs(rev))
1143
1154
1144 if self.startrev == nullrev:
1155 if self.startrev == nullrev:
1145 self.startrev = rev
1156 self.startrev = rev
1146 return True
1157 return True
1147
1158
1148 if rev > self.startrev:
1159 if rev > self.startrev:
1149 # forward: all descendants
1160 # forward: all descendants
1150 if not self.roots:
1161 if not self.roots:
1151 self.roots.add(self.startrev)
1162 self.roots.add(self.startrev)
1152 for parent in realparents(rev):
1163 for parent in realparents(rev):
1153 if parent in self.roots:
1164 if parent in self.roots:
1154 self.roots.add(rev)
1165 self.roots.add(rev)
1155 return True
1166 return True
1156 else:
1167 else:
1157 # backwards: all parents
1168 # backwards: all parents
1158 if not self.roots:
1169 if not self.roots:
1159 self.roots.update(realparents(self.startrev))
1170 self.roots.update(realparents(self.startrev))
1160 if rev in self.roots:
1171 if rev in self.roots:
1161 self.roots.remove(rev)
1172 self.roots.remove(rev)
1162 self.roots.update(realparents(rev))
1173 self.roots.update(realparents(rev))
1163 return True
1174 return True
1164
1175
1165 return False
1176 return False
1166
1177
1167 # it might be worthwhile to do this in the iterator if the rev range
1178 # it might be worthwhile to do this in the iterator if the rev range
1168 # is descending and the prune args are all within that range
1179 # is descending and the prune args are all within that range
1169 for rev in opts.get('prune', ()):
1180 for rev in opts.get('prune', ()):
1170 rev = repo[rev].rev()
1181 rev = repo[rev].rev()
1171 ff = followfilter()
1182 ff = followfilter()
1172 stop = min(revs[0], revs[-1])
1183 stop = min(revs[0], revs[-1])
1173 for x in xrange(rev, stop - 1, -1):
1184 for x in xrange(rev, stop - 1, -1):
1174 if ff.match(x):
1185 if ff.match(x):
1175 wanted.discard(x)
1186 wanted.discard(x)
1176
1187
1177 # Now that wanted is correctly initialized, we can iterate over the
1188 # Now that wanted is correctly initialized, we can iterate over the
1178 # revision range, yielding only revisions in wanted.
1189 # revision range, yielding only revisions in wanted.
1179 def iterate():
1190 def iterate():
1180 if follow and not match.files():
1191 if follow and not match.files():
1181 ff = followfilter(onlyfirst=opts.get('follow_first'))
1192 ff = followfilter(onlyfirst=opts.get('follow_first'))
1182 def want(rev):
1193 def want(rev):
1183 return ff.match(rev) and rev in wanted
1194 return ff.match(rev) and rev in wanted
1184 else:
1195 else:
1185 def want(rev):
1196 def want(rev):
1186 return rev in wanted
1197 return rev in wanted
1187
1198
1188 for i, window in increasingwindows(0, len(revs)):
1199 for i, window in increasingwindows(0, len(revs)):
1189 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1200 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1190 for rev in sorted(nrevs):
1201 for rev in sorted(nrevs):
1191 fns = fncache.get(rev)
1202 fns = fncache.get(rev)
1192 ctx = change(rev)
1203 ctx = change(rev)
1193 if not fns:
1204 if not fns:
1194 def fns_generator():
1205 def fns_generator():
1195 for f in ctx.files():
1206 for f in ctx.files():
1196 if match(f):
1207 if match(f):
1197 yield f
1208 yield f
1198 fns = fns_generator()
1209 fns = fns_generator()
1199 prepare(ctx, fns)
1210 prepare(ctx, fns)
1200 for rev in nrevs:
1211 for rev in nrevs:
1201 yield change(rev)
1212 yield change(rev)
1202 return iterate()
1213 return iterate()
1203
1214
1204 def _makegraphfilematcher(repo, pats, followfirst):
1215 def _makegraphfilematcher(repo, pats, followfirst):
1205 # When displaying a revision with --patch --follow FILE, we have
1216 # When displaying a revision with --patch --follow FILE, we have
1206 # to know which file of the revision must be diffed. With
1217 # to know which file of the revision must be diffed. With
1207 # --follow, we want the names of the ancestors of FILE in the
1218 # --follow, we want the names of the ancestors of FILE in the
1208 # revision, stored in "fcache". "fcache" is populated by
1219 # revision, stored in "fcache". "fcache" is populated by
1209 # reproducing the graph traversal already done by --follow revset
1220 # reproducing the graph traversal already done by --follow revset
1210 # and relating linkrevs to file names (which is not "correct" but
1221 # and relating linkrevs to file names (which is not "correct" but
1211 # good enough).
1222 # good enough).
1212 fcache = {}
1223 fcache = {}
1213 fcacheready = [False]
1224 fcacheready = [False]
1214 pctx = repo['.']
1225 pctx = repo['.']
1215 wctx = repo[None]
1226 wctx = repo[None]
1216
1227
1217 def populate():
1228 def populate():
1218 for fn in pats:
1229 for fn in pats:
1219 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1230 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1220 for c in i:
1231 for c in i:
1221 fcache.setdefault(c.linkrev(), set()).add(c.path())
1232 fcache.setdefault(c.linkrev(), set()).add(c.path())
1222
1233
1223 def filematcher(rev):
1234 def filematcher(rev):
1224 if not fcacheready[0]:
1235 if not fcacheready[0]:
1225 # Lazy initialization
1236 # Lazy initialization
1226 fcacheready[0] = True
1237 fcacheready[0] = True
1227 populate()
1238 populate()
1228 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1239 return scmutil.match(wctx, fcache.get(rev, []), default='path')
1229
1240
1230 return filematcher
1241 return filematcher
1231
1242
1232 def _makegraphlogrevset(repo, pats, opts, revs):
1243 def _makegraphlogrevset(repo, pats, opts, revs):
1233 """Return (expr, filematcher) where expr is a revset string built
1244 """Return (expr, filematcher) where expr is a revset string built
1234 from log options and file patterns or None. If --stat or --patch
1245 from log options and file patterns or None. If --stat or --patch
1235 are not passed filematcher is None. Otherwise it is a callable
1246 are not passed filematcher is None. Otherwise it is a callable
1236 taking a revision number and returning a match objects filtering
1247 taking a revision number and returning a match objects filtering
1237 the files to be detailed when displaying the revision.
1248 the files to be detailed when displaying the revision.
1238 """
1249 """
1239 opt2revset = {
1250 opt2revset = {
1240 'no_merges': ('not merge()', None),
1251 'no_merges': ('not merge()', None),
1241 'only_merges': ('merge()', None),
1252 'only_merges': ('merge()', None),
1242 '_ancestors': ('ancestors(%(val)s)', None),
1253 '_ancestors': ('ancestors(%(val)s)', None),
1243 '_fancestors': ('_firstancestors(%(val)s)', None),
1254 '_fancestors': ('_firstancestors(%(val)s)', None),
1244 '_descendants': ('descendants(%(val)s)', None),
1255 '_descendants': ('descendants(%(val)s)', None),
1245 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1256 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1246 '_matchfiles': ('_matchfiles(%(val)s)', None),
1257 '_matchfiles': ('_matchfiles(%(val)s)', None),
1247 'date': ('date(%(val)r)', None),
1258 'date': ('date(%(val)r)', None),
1248 'branch': ('branch(%(val)r)', ' or '),
1259 'branch': ('branch(%(val)r)', ' or '),
1249 '_patslog': ('filelog(%(val)r)', ' or '),
1260 '_patslog': ('filelog(%(val)r)', ' or '),
1250 '_patsfollow': ('follow(%(val)r)', ' or '),
1261 '_patsfollow': ('follow(%(val)r)', ' or '),
1251 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1262 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1252 'keyword': ('keyword(%(val)r)', ' or '),
1263 'keyword': ('keyword(%(val)r)', ' or '),
1253 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1264 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1254 'user': ('user(%(val)r)', ' or '),
1265 'user': ('user(%(val)r)', ' or '),
1255 }
1266 }
1256
1267
1257 opts = dict(opts)
1268 opts = dict(opts)
1258 # follow or not follow?
1269 # follow or not follow?
1259 follow = opts.get('follow') or opts.get('follow_first')
1270 follow = opts.get('follow') or opts.get('follow_first')
1260 followfirst = opts.get('follow_first') and 1 or 0
1271 followfirst = opts.get('follow_first') and 1 or 0
1261 # --follow with FILE behaviour depends on revs...
1272 # --follow with FILE behaviour depends on revs...
1262 startrev = revs[0]
1273 startrev = revs[0]
1263 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1274 followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0
1264
1275
1265 # branch and only_branch are really aliases and must be handled at
1276 # branch and only_branch are really aliases and must be handled at
1266 # the same time
1277 # the same time
1267 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1278 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1268 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1279 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1269 # pats/include/exclude are passed to match.match() directly in
1280 # pats/include/exclude are passed to match.match() directly in
1270 # _matchfiles() revset but walkchangerevs() builds its matcher with
1281 # _matchfiles() revset but walkchangerevs() builds its matcher with
1271 # scmutil.match(). The difference is input pats are globbed on
1282 # scmutil.match(). The difference is input pats are globbed on
1272 # platforms without shell expansion (windows).
1283 # platforms without shell expansion (windows).
1273 pctx = repo[None]
1284 pctx = repo[None]
1274 match, pats = scmutil.matchandpats(pctx, pats, opts)
1285 match, pats = scmutil.matchandpats(pctx, pats, opts)
1275 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1286 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1276 if not slowpath:
1287 if not slowpath:
1277 for f in match.files():
1288 for f in match.files():
1278 if follow and f not in pctx:
1289 if follow and f not in pctx:
1279 raise util.Abort(_('cannot follow file not in parent '
1290 raise util.Abort(_('cannot follow file not in parent '
1280 'revision: "%s"') % f)
1291 'revision: "%s"') % f)
1281 filelog = repo.file(f)
1292 filelog = repo.file(f)
1282 if not len(filelog):
1293 if not len(filelog):
1283 # A zero count may be a directory or deleted file, so
1294 # A zero count may be a directory or deleted file, so
1284 # try to find matching entries on the slow path.
1295 # try to find matching entries on the slow path.
1285 if follow:
1296 if follow:
1286 raise util.Abort(
1297 raise util.Abort(
1287 _('cannot follow nonexistent file: "%s"') % f)
1298 _('cannot follow nonexistent file: "%s"') % f)
1288 slowpath = True
1299 slowpath = True
1300
1301 # We decided to fall back to the slowpath because at least one
1302 # of the paths was not a file. Check to see if at least one of them
1303 # existed in history - in that case, we'll continue down the
1304 # slowpath; otherwise, we can turn off the slowpath
1305 if slowpath:
1306 for path in match.files():
1307 if path == '.' or path in repo.store:
1308 break
1309 else:
1310 slowpath = False
1311
1289 if slowpath:
1312 if slowpath:
1290 # See walkchangerevs() slow path.
1313 # See walkchangerevs() slow path.
1291 #
1314 #
1292 if follow:
1315 if follow:
1293 raise util.Abort(_('can only follow copies/renames for explicit '
1316 raise util.Abort(_('can only follow copies/renames for explicit '
1294 'filenames'))
1317 'filenames'))
1295 # pats/include/exclude cannot be represented as separate
1318 # pats/include/exclude cannot be represented as separate
1296 # revset expressions as their filtering logic applies at file
1319 # revset expressions as their filtering logic applies at file
1297 # level. For instance "-I a -X a" matches a revision touching
1320 # level. For instance "-I a -X a" matches a revision touching
1298 # "a" and "b" while "file(a) and not file(b)" does
1321 # "a" and "b" while "file(a) and not file(b)" does
1299 # not. Besides, filesets are evaluated against the working
1322 # not. Besides, filesets are evaluated against the working
1300 # directory.
1323 # directory.
1301 matchargs = ['r:', 'd:relpath']
1324 matchargs = ['r:', 'd:relpath']
1302 for p in pats:
1325 for p in pats:
1303 matchargs.append('p:' + p)
1326 matchargs.append('p:' + p)
1304 for p in opts.get('include', []):
1327 for p in opts.get('include', []):
1305 matchargs.append('i:' + p)
1328 matchargs.append('i:' + p)
1306 for p in opts.get('exclude', []):
1329 for p in opts.get('exclude', []):
1307 matchargs.append('x:' + p)
1330 matchargs.append('x:' + p)
1308 matchargs = ','.join(('%r' % p) for p in matchargs)
1331 matchargs = ','.join(('%r' % p) for p in matchargs)
1309 opts['_matchfiles'] = matchargs
1332 opts['_matchfiles'] = matchargs
1310 else:
1333 else:
1311 if follow:
1334 if follow:
1312 fpats = ('_patsfollow', '_patsfollowfirst')
1335 fpats = ('_patsfollow', '_patsfollowfirst')
1313 fnopats = (('_ancestors', '_fancestors'),
1336 fnopats = (('_ancestors', '_fancestors'),
1314 ('_descendants', '_fdescendants'))
1337 ('_descendants', '_fdescendants'))
1315 if pats:
1338 if pats:
1316 # follow() revset interprets its file argument as a
1339 # follow() revset interprets its file argument as a
1317 # manifest entry, so use match.files(), not pats.
1340 # manifest entry, so use match.files(), not pats.
1318 opts[fpats[followfirst]] = list(match.files())
1341 opts[fpats[followfirst]] = list(match.files())
1319 else:
1342 else:
1320 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1343 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1321 else:
1344 else:
1322 opts['_patslog'] = list(pats)
1345 opts['_patslog'] = list(pats)
1323
1346
1324 filematcher = None
1347 filematcher = None
1325 if opts.get('patch') or opts.get('stat'):
1348 if opts.get('patch') or opts.get('stat'):
1326 if follow:
1349 if follow:
1327 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1350 filematcher = _makegraphfilematcher(repo, pats, followfirst)
1328 else:
1351 else:
1329 filematcher = lambda rev: match
1352 filematcher = lambda rev: match
1330
1353
1331 expr = []
1354 expr = []
1332 for op, val in opts.iteritems():
1355 for op, val in opts.iteritems():
1333 if not val:
1356 if not val:
1334 continue
1357 continue
1335 if op not in opt2revset:
1358 if op not in opt2revset:
1336 continue
1359 continue
1337 revop, andor = opt2revset[op]
1360 revop, andor = opt2revset[op]
1338 if '%(val)' not in revop:
1361 if '%(val)' not in revop:
1339 expr.append(revop)
1362 expr.append(revop)
1340 else:
1363 else:
1341 if not isinstance(val, list):
1364 if not isinstance(val, list):
1342 e = revop % {'val': val}
1365 e = revop % {'val': val}
1343 else:
1366 else:
1344 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1367 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1345 expr.append(e)
1368 expr.append(e)
1346
1369
1347 if expr:
1370 if expr:
1348 expr = '(' + ' and '.join(expr) + ')'
1371 expr = '(' + ' and '.join(expr) + ')'
1349 else:
1372 else:
1350 expr = None
1373 expr = None
1351 return expr, filematcher
1374 return expr, filematcher
1352
1375
1353 def getgraphlogrevs(repo, pats, opts):
1376 def getgraphlogrevs(repo, pats, opts):
1354 """Return (revs, expr, filematcher) where revs is an iterable of
1377 """Return (revs, expr, filematcher) where revs is an iterable of
1355 revision numbers, expr is a revset string built from log options
1378 revision numbers, expr is a revset string built from log options
1356 and file patterns or None, and used to filter 'revs'. If --stat or
1379 and file patterns or None, and used to filter 'revs'. If --stat or
1357 --patch are not passed filematcher is None. Otherwise it is a
1380 --patch are not passed filematcher is None. Otherwise it is a
1358 callable taking a revision number and returning a match objects
1381 callable taking a revision number and returning a match objects
1359 filtering the files to be detailed when displaying the revision.
1382 filtering the files to be detailed when displaying the revision.
1360 """
1383 """
1361 def increasingrevs(repo, revs, matcher):
1384 def increasingrevs(repo, revs, matcher):
1362 # The sorted input rev sequence is chopped in sub-sequences
1385 # The sorted input rev sequence is chopped in sub-sequences
1363 # which are sorted in ascending order and passed to the
1386 # which are sorted in ascending order and passed to the
1364 # matcher. The filtered revs are sorted again as they were in
1387 # matcher. The filtered revs are sorted again as they were in
1365 # the original sub-sequence. This achieve several things:
1388 # the original sub-sequence. This achieve several things:
1366 #
1389 #
1367 # - getlogrevs() now returns a generator which behaviour is
1390 # - getlogrevs() now returns a generator which behaviour is
1368 # adapted to log need. First results come fast, last ones
1391 # adapted to log need. First results come fast, last ones
1369 # are batched for performances.
1392 # are batched for performances.
1370 #
1393 #
1371 # - revset matchers often operate faster on revision in
1394 # - revset matchers often operate faster on revision in
1372 # changelog order, because most filters deal with the
1395 # changelog order, because most filters deal with the
1373 # changelog.
1396 # changelog.
1374 #
1397 #
1375 # - revset matchers can reorder revisions. "A or B" typically
1398 # - revset matchers can reorder revisions. "A or B" typically
1376 # returns returns the revision matching A then the revision
1399 # returns returns the revision matching A then the revision
1377 # matching B. We want to hide this internal implementation
1400 # matching B. We want to hide this internal implementation
1378 # detail from the caller, and sorting the filtered revision
1401 # detail from the caller, and sorting the filtered revision
1379 # again achieves this.
1402 # again achieves this.
1380 for i, window in increasingwindows(0, len(revs), windowsize=1):
1403 for i, window in increasingwindows(0, len(revs), windowsize=1):
1381 orevs = revs[i:i + window]
1404 orevs = revs[i:i + window]
1382 nrevs = set(matcher(repo, sorted(orevs)))
1405 nrevs = set(matcher(repo, sorted(orevs)))
1383 for rev in orevs:
1406 for rev in orevs:
1384 if rev in nrevs:
1407 if rev in nrevs:
1385 yield rev
1408 yield rev
1386
1409
1387 if not len(repo):
1410 if not len(repo):
1388 return iter([]), None, None
1411 return iter([]), None, None
1389 # Default --rev value depends on --follow but --follow behaviour
1412 # Default --rev value depends on --follow but --follow behaviour
1390 # depends on revisions resolved from --rev...
1413 # depends on revisions resolved from --rev...
1391 follow = opts.get('follow') or opts.get('follow_first')
1414 follow = opts.get('follow') or opts.get('follow_first')
1392 if opts.get('rev'):
1415 if opts.get('rev'):
1393 revs = scmutil.revrange(repo, opts['rev'])
1416 revs = scmutil.revrange(repo, opts['rev'])
1394 else:
1417 else:
1395 if follow and len(repo) > 0:
1418 if follow and len(repo) > 0:
1396 revs = repo.revs('reverse(:.)')
1419 revs = repo.revs('reverse(:.)')
1397 else:
1420 else:
1398 revs = list(repo.changelog)
1421 revs = list(repo.changelog)
1399 revs.reverse()
1422 revs.reverse()
1400 if not revs:
1423 if not revs:
1401 return iter([]), None, None
1424 return iter([]), None, None
1402 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1425 expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
1403 if expr:
1426 if expr:
1404 matcher = revset.match(repo.ui, expr)
1427 matcher = revset.match(repo.ui, expr)
1405 revs = increasingrevs(repo, revs, matcher)
1428 revs = increasingrevs(repo, revs, matcher)
1406 if not opts.get('hidden'):
1429 if not opts.get('hidden'):
1407 # --hidden is still experimental and not worth a dedicated revset
1430 # --hidden is still experimental and not worth a dedicated revset
1408 # yet. Fortunately, filtering revision number is fast.
1431 # yet. Fortunately, filtering revision number is fast.
1409 revs = (r for r in revs if r not in repo.hiddenrevs)
1432 revs = (r for r in revs if r not in repo.hiddenrevs)
1410 else:
1433 else:
1411 revs = iter(revs)
1434 revs = iter(revs)
1412 return revs, expr, filematcher
1435 return revs, expr, filematcher
1413
1436
1414 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1437 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1415 filematcher=None):
1438 filematcher=None):
1416 seen, state = [], graphmod.asciistate()
1439 seen, state = [], graphmod.asciistate()
1417 for rev, type, ctx, parents in dag:
1440 for rev, type, ctx, parents in dag:
1418 char = 'o'
1441 char = 'o'
1419 if ctx.node() in showparents:
1442 if ctx.node() in showparents:
1420 char = '@'
1443 char = '@'
1421 elif ctx.obsolete():
1444 elif ctx.obsolete():
1422 char = 'x'
1445 char = 'x'
1423 copies = None
1446 copies = None
1424 if getrenamed and ctx.rev():
1447 if getrenamed and ctx.rev():
1425 copies = []
1448 copies = []
1426 for fn in ctx.files():
1449 for fn in ctx.files():
1427 rename = getrenamed(fn, ctx.rev())
1450 rename = getrenamed(fn, ctx.rev())
1428 if rename:
1451 if rename:
1429 copies.append((fn, rename[0]))
1452 copies.append((fn, rename[0]))
1430 revmatchfn = None
1453 revmatchfn = None
1431 if filematcher is not None:
1454 if filematcher is not None:
1432 revmatchfn = filematcher(ctx.rev())
1455 revmatchfn = filematcher(ctx.rev())
1433 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1456 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1434 lines = displayer.hunk.pop(rev).split('\n')
1457 lines = displayer.hunk.pop(rev).split('\n')
1435 if not lines[-1]:
1458 if not lines[-1]:
1436 del lines[-1]
1459 del lines[-1]
1437 displayer.flush(rev)
1460 displayer.flush(rev)
1438 edges = edgefn(type, char, lines, seen, rev, parents)
1461 edges = edgefn(type, char, lines, seen, rev, parents)
1439 for type, char, lines, coldata in edges:
1462 for type, char, lines, coldata in edges:
1440 graphmod.ascii(ui, state, type, char, lines, coldata)
1463 graphmod.ascii(ui, state, type, char, lines, coldata)
1441 displayer.close()
1464 displayer.close()
1442
1465
1443 def graphlog(ui, repo, *pats, **opts):
1466 def graphlog(ui, repo, *pats, **opts):
1444 # Parameters are identical to log command ones
1467 # Parameters are identical to log command ones
1445 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1468 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1446 revs = sorted(revs, reverse=1)
1469 revs = sorted(revs, reverse=1)
1447 limit = loglimit(opts)
1470 limit = loglimit(opts)
1448 if limit is not None:
1471 if limit is not None:
1449 revs = revs[:limit]
1472 revs = revs[:limit]
1450 revdag = graphmod.dagwalker(repo, revs)
1473 revdag = graphmod.dagwalker(repo, revs)
1451
1474
1452 getrenamed = None
1475 getrenamed = None
1453 if opts.get('copies'):
1476 if opts.get('copies'):
1454 endrev = None
1477 endrev = None
1455 if opts.get('rev'):
1478 if opts.get('rev'):
1456 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1479 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
1457 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1480 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1458 displayer = show_changeset(ui, repo, opts, buffered=True)
1481 displayer = show_changeset(ui, repo, opts, buffered=True)
1459 showparents = [ctx.node() for ctx in repo[None].parents()]
1482 showparents = [ctx.node() for ctx in repo[None].parents()]
1460 displaygraph(ui, revdag, displayer, showparents,
1483 displaygraph(ui, revdag, displayer, showparents,
1461 graphmod.asciiedges, getrenamed, filematcher)
1484 graphmod.asciiedges, getrenamed, filematcher)
1462
1485
1463 def checkunsupportedgraphflags(pats, opts):
1486 def checkunsupportedgraphflags(pats, opts):
1464 for op in ["newest_first"]:
1487 for op in ["newest_first"]:
1465 if op in opts and opts[op]:
1488 if op in opts and opts[op]:
1466 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1489 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1467 % op.replace("_", "-"))
1490 % op.replace("_", "-"))
1468
1491
1469 def graphrevs(repo, nodes, opts):
1492 def graphrevs(repo, nodes, opts):
1470 limit = loglimit(opts)
1493 limit = loglimit(opts)
1471 nodes.reverse()
1494 nodes.reverse()
1472 if limit is not None:
1495 if limit is not None:
1473 nodes = nodes[:limit]
1496 nodes = nodes[:limit]
1474 return graphmod.nodes(repo, nodes)
1497 return graphmod.nodes(repo, nodes)
1475
1498
1476 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1499 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1477 join = lambda f: os.path.join(prefix, f)
1500 join = lambda f: os.path.join(prefix, f)
1478 bad = []
1501 bad = []
1479 oldbad = match.bad
1502 oldbad = match.bad
1480 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1503 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1481 names = []
1504 names = []
1482 wctx = repo[None]
1505 wctx = repo[None]
1483 cca = None
1506 cca = None
1484 abort, warn = scmutil.checkportabilityalert(ui)
1507 abort, warn = scmutil.checkportabilityalert(ui)
1485 if abort or warn:
1508 if abort or warn:
1486 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1509 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1487 for f in repo.walk(match):
1510 for f in repo.walk(match):
1488 exact = match.exact(f)
1511 exact = match.exact(f)
1489 if exact or not explicitonly and f not in repo.dirstate:
1512 if exact or not explicitonly and f not in repo.dirstate:
1490 if cca:
1513 if cca:
1491 cca(f)
1514 cca(f)
1492 names.append(f)
1515 names.append(f)
1493 if ui.verbose or not exact:
1516 if ui.verbose or not exact:
1494 ui.status(_('adding %s\n') % match.rel(join(f)))
1517 ui.status(_('adding %s\n') % match.rel(join(f)))
1495
1518
1496 for subpath in wctx.substate:
1519 for subpath in wctx.substate:
1497 sub = wctx.sub(subpath)
1520 sub = wctx.sub(subpath)
1498 try:
1521 try:
1499 submatch = matchmod.narrowmatcher(subpath, match)
1522 submatch = matchmod.narrowmatcher(subpath, match)
1500 if listsubrepos:
1523 if listsubrepos:
1501 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1524 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1502 False))
1525 False))
1503 else:
1526 else:
1504 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1527 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1505 True))
1528 True))
1506 except error.LookupError:
1529 except error.LookupError:
1507 ui.status(_("skipping missing subrepository: %s\n")
1530 ui.status(_("skipping missing subrepository: %s\n")
1508 % join(subpath))
1531 % join(subpath))
1509
1532
1510 if not dryrun:
1533 if not dryrun:
1511 rejected = wctx.add(names, prefix)
1534 rejected = wctx.add(names, prefix)
1512 bad.extend(f for f in rejected if f in match.files())
1535 bad.extend(f for f in rejected if f in match.files())
1513 return bad
1536 return bad
1514
1537
1515 def forget(ui, repo, match, prefix, explicitonly):
1538 def forget(ui, repo, match, prefix, explicitonly):
1516 join = lambda f: os.path.join(prefix, f)
1539 join = lambda f: os.path.join(prefix, f)
1517 bad = []
1540 bad = []
1518 oldbad = match.bad
1541 oldbad = match.bad
1519 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1542 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1520 wctx = repo[None]
1543 wctx = repo[None]
1521 forgot = []
1544 forgot = []
1522 s = repo.status(match=match, clean=True)
1545 s = repo.status(match=match, clean=True)
1523 forget = sorted(s[0] + s[1] + s[3] + s[6])
1546 forget = sorted(s[0] + s[1] + s[3] + s[6])
1524 if explicitonly:
1547 if explicitonly:
1525 forget = [f for f in forget if match.exact(f)]
1548 forget = [f for f in forget if match.exact(f)]
1526
1549
1527 for subpath in wctx.substate:
1550 for subpath in wctx.substate:
1528 sub = wctx.sub(subpath)
1551 sub = wctx.sub(subpath)
1529 try:
1552 try:
1530 submatch = matchmod.narrowmatcher(subpath, match)
1553 submatch = matchmod.narrowmatcher(subpath, match)
1531 subbad, subforgot = sub.forget(ui, submatch, prefix)
1554 subbad, subforgot = sub.forget(ui, submatch, prefix)
1532 bad.extend([subpath + '/' + f for f in subbad])
1555 bad.extend([subpath + '/' + f for f in subbad])
1533 forgot.extend([subpath + '/' + f for f in subforgot])
1556 forgot.extend([subpath + '/' + f for f in subforgot])
1534 except error.LookupError:
1557 except error.LookupError:
1535 ui.status(_("skipping missing subrepository: %s\n")
1558 ui.status(_("skipping missing subrepository: %s\n")
1536 % join(subpath))
1559 % join(subpath))
1537
1560
1538 if not explicitonly:
1561 if not explicitonly:
1539 for f in match.files():
1562 for f in match.files():
1540 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1563 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1541 if f not in forgot:
1564 if f not in forgot:
1542 if os.path.exists(match.rel(join(f))):
1565 if os.path.exists(match.rel(join(f))):
1543 ui.warn(_('not removing %s: '
1566 ui.warn(_('not removing %s: '
1544 'file is already untracked\n')
1567 'file is already untracked\n')
1545 % match.rel(join(f)))
1568 % match.rel(join(f)))
1546 bad.append(f)
1569 bad.append(f)
1547
1570
1548 for f in forget:
1571 for f in forget:
1549 if ui.verbose or not match.exact(f):
1572 if ui.verbose or not match.exact(f):
1550 ui.status(_('removing %s\n') % match.rel(join(f)))
1573 ui.status(_('removing %s\n') % match.rel(join(f)))
1551
1574
1552 rejected = wctx.forget(forget, prefix)
1575 rejected = wctx.forget(forget, prefix)
1553 bad.extend(f for f in rejected if f in match.files())
1576 bad.extend(f for f in rejected if f in match.files())
1554 forgot.extend(forget)
1577 forgot.extend(forget)
1555 return bad, forgot
1578 return bad, forgot
1556
1579
1557 def duplicatecopies(repo, rev, p1):
1580 def duplicatecopies(repo, rev, p1):
1558 "Reproduce copies found in the source revision in the dirstate for grafts"
1581 "Reproduce copies found in the source revision in the dirstate for grafts"
1559 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1582 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1560 repo.dirstate.copy(src, dst)
1583 repo.dirstate.copy(src, dst)
1561
1584
1562 def commit(ui, repo, commitfunc, pats, opts):
1585 def commit(ui, repo, commitfunc, pats, opts):
1563 '''commit the specified files or all outstanding changes'''
1586 '''commit the specified files or all outstanding changes'''
1564 date = opts.get('date')
1587 date = opts.get('date')
1565 if date:
1588 if date:
1566 opts['date'] = util.parsedate(date)
1589 opts['date'] = util.parsedate(date)
1567 message = logmessage(ui, opts)
1590 message = logmessage(ui, opts)
1568
1591
1569 # extract addremove carefully -- this function can be called from a command
1592 # extract addremove carefully -- this function can be called from a command
1570 # that doesn't support addremove
1593 # that doesn't support addremove
1571 if opts.get('addremove'):
1594 if opts.get('addremove'):
1572 scmutil.addremove(repo, pats, opts)
1595 scmutil.addremove(repo, pats, opts)
1573
1596
1574 return commitfunc(ui, repo, message,
1597 return commitfunc(ui, repo, message,
1575 scmutil.match(repo[None], pats, opts), opts)
1598 scmutil.match(repo[None], pats, opts), opts)
1576
1599
1577 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1600 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1578 ui.note(_('amending changeset %s\n') % old)
1601 ui.note(_('amending changeset %s\n') % old)
1579 base = old.p1()
1602 base = old.p1()
1580
1603
1581 wlock = lock = None
1604 wlock = lock = None
1582 try:
1605 try:
1583 wlock = repo.wlock()
1606 wlock = repo.wlock()
1584 lock = repo.lock()
1607 lock = repo.lock()
1585 tr = repo.transaction('amend')
1608 tr = repo.transaction('amend')
1586 try:
1609 try:
1587 # See if we got a message from -m or -l, if not, open the editor
1610 # See if we got a message from -m or -l, if not, open the editor
1588 # with the message of the changeset to amend
1611 # with the message of the changeset to amend
1589 message = logmessage(ui, opts)
1612 message = logmessage(ui, opts)
1590 # First, do a regular commit to record all changes in the working
1613 # First, do a regular commit to record all changes in the working
1591 # directory (if there are any)
1614 # directory (if there are any)
1592 ui.callhooks = False
1615 ui.callhooks = False
1593 try:
1616 try:
1594 opts['message'] = 'temporary amend commit for %s' % old
1617 opts['message'] = 'temporary amend commit for %s' % old
1595 node = commit(ui, repo, commitfunc, pats, opts)
1618 node = commit(ui, repo, commitfunc, pats, opts)
1596 finally:
1619 finally:
1597 ui.callhooks = True
1620 ui.callhooks = True
1598 ctx = repo[node]
1621 ctx = repo[node]
1599
1622
1600 # Participating changesets:
1623 # Participating changesets:
1601 #
1624 #
1602 # node/ctx o - new (intermediate) commit that contains changes
1625 # node/ctx o - new (intermediate) commit that contains changes
1603 # | from working dir to go into amending commit
1626 # | from working dir to go into amending commit
1604 # | (or a workingctx if there were no changes)
1627 # | (or a workingctx if there were no changes)
1605 # |
1628 # |
1606 # old o - changeset to amend
1629 # old o - changeset to amend
1607 # |
1630 # |
1608 # base o - parent of amending changeset
1631 # base o - parent of amending changeset
1609
1632
1610 # Update extra dict from amended commit (e.g. to preserve graft
1633 # Update extra dict from amended commit (e.g. to preserve graft
1611 # source)
1634 # source)
1612 extra.update(old.extra())
1635 extra.update(old.extra())
1613
1636
1614 # Also update it from the intermediate commit or from the wctx
1637 # Also update it from the intermediate commit or from the wctx
1615 extra.update(ctx.extra())
1638 extra.update(ctx.extra())
1616
1639
1617 files = set(old.files())
1640 files = set(old.files())
1618
1641
1619 # Second, we use either the commit we just did, or if there were no
1642 # Second, we use either the commit we just did, or if there were no
1620 # changes the parent of the working directory as the version of the
1643 # changes the parent of the working directory as the version of the
1621 # files in the final amend commit
1644 # files in the final amend commit
1622 if node:
1645 if node:
1623 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1646 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1624
1647
1625 user = ctx.user()
1648 user = ctx.user()
1626 date = ctx.date()
1649 date = ctx.date()
1627 # Recompute copies (avoid recording a -> b -> a)
1650 # Recompute copies (avoid recording a -> b -> a)
1628 copied = copies.pathcopies(base, ctx)
1651 copied = copies.pathcopies(base, ctx)
1629
1652
1630 # Prune files which were reverted by the updates: if old
1653 # Prune files which were reverted by the updates: if old
1631 # introduced file X and our intermediate commit, node,
1654 # introduced file X and our intermediate commit, node,
1632 # renamed that file, then those two files are the same and
1655 # renamed that file, then those two files are the same and
1633 # we can discard X from our list of files. Likewise if X
1656 # we can discard X from our list of files. Likewise if X
1634 # was deleted, it's no longer relevant
1657 # was deleted, it's no longer relevant
1635 files.update(ctx.files())
1658 files.update(ctx.files())
1636
1659
1637 def samefile(f):
1660 def samefile(f):
1638 if f in ctx.manifest():
1661 if f in ctx.manifest():
1639 a = ctx.filectx(f)
1662 a = ctx.filectx(f)
1640 if f in base.manifest():
1663 if f in base.manifest():
1641 b = base.filectx(f)
1664 b = base.filectx(f)
1642 return (not a.cmp(b)
1665 return (not a.cmp(b)
1643 and a.flags() == b.flags())
1666 and a.flags() == b.flags())
1644 else:
1667 else:
1645 return False
1668 return False
1646 else:
1669 else:
1647 return f not in base.manifest()
1670 return f not in base.manifest()
1648 files = [f for f in files if not samefile(f)]
1671 files = [f for f in files if not samefile(f)]
1649
1672
1650 def filectxfn(repo, ctx_, path):
1673 def filectxfn(repo, ctx_, path):
1651 try:
1674 try:
1652 fctx = ctx[path]
1675 fctx = ctx[path]
1653 flags = fctx.flags()
1676 flags = fctx.flags()
1654 mctx = context.memfilectx(fctx.path(), fctx.data(),
1677 mctx = context.memfilectx(fctx.path(), fctx.data(),
1655 islink='l' in flags,
1678 islink='l' in flags,
1656 isexec='x' in flags,
1679 isexec='x' in flags,
1657 copied=copied.get(path))
1680 copied=copied.get(path))
1658 return mctx
1681 return mctx
1659 except KeyError:
1682 except KeyError:
1660 raise IOError
1683 raise IOError
1661 else:
1684 else:
1662 ui.note(_('copying changeset %s to %s\n') % (old, base))
1685 ui.note(_('copying changeset %s to %s\n') % (old, base))
1663
1686
1664 # Use version of files as in the old cset
1687 # Use version of files as in the old cset
1665 def filectxfn(repo, ctx_, path):
1688 def filectxfn(repo, ctx_, path):
1666 try:
1689 try:
1667 return old.filectx(path)
1690 return old.filectx(path)
1668 except KeyError:
1691 except KeyError:
1669 raise IOError
1692 raise IOError
1670
1693
1671 user = opts.get('user') or old.user()
1694 user = opts.get('user') or old.user()
1672 date = opts.get('date') or old.date()
1695 date = opts.get('date') or old.date()
1673 if not message:
1696 if not message:
1674 message = old.description()
1697 message = old.description()
1675
1698
1676 new = context.memctx(repo,
1699 new = context.memctx(repo,
1677 parents=[base.node(), nullid],
1700 parents=[base.node(), nullid],
1678 text=message,
1701 text=message,
1679 files=files,
1702 files=files,
1680 filectxfn=filectxfn,
1703 filectxfn=filectxfn,
1681 user=user,
1704 user=user,
1682 date=date,
1705 date=date,
1683 extra=extra)
1706 extra=extra)
1684 new._text = commitforceeditor(repo, new, [])
1707 new._text = commitforceeditor(repo, new, [])
1685 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1708 ph = repo.ui.config('phases', 'new-commit', phases.draft)
1686 try:
1709 try:
1687 repo.ui.setconfig('phases', 'new-commit', old.phase())
1710 repo.ui.setconfig('phases', 'new-commit', old.phase())
1688 newid = repo.commitctx(new)
1711 newid = repo.commitctx(new)
1689 finally:
1712 finally:
1690 repo.ui.setconfig('phases', 'new-commit', ph)
1713 repo.ui.setconfig('phases', 'new-commit', ph)
1691 if newid != old.node():
1714 if newid != old.node():
1692 # Reroute the working copy parent to the new changeset
1715 # Reroute the working copy parent to the new changeset
1693 repo.setparents(newid, nullid)
1716 repo.setparents(newid, nullid)
1694
1717
1695 # Move bookmarks from old parent to amend commit
1718 # Move bookmarks from old parent to amend commit
1696 bms = repo.nodebookmarks(old.node())
1719 bms = repo.nodebookmarks(old.node())
1697 if bms:
1720 if bms:
1698 for bm in bms:
1721 for bm in bms:
1699 repo._bookmarks[bm] = newid
1722 repo._bookmarks[bm] = newid
1700 bookmarks.write(repo)
1723 bookmarks.write(repo)
1701 #commit the whole amend process
1724 #commit the whole amend process
1702 if obsolete._enabled and newid != old.node():
1725 if obsolete._enabled and newid != old.node():
1703 # mark the new changeset as successor of the rewritten one
1726 # mark the new changeset as successor of the rewritten one
1704 new = repo[newid]
1727 new = repo[newid]
1705 obs = [(old, (new,))]
1728 obs = [(old, (new,))]
1706 if node:
1729 if node:
1707 obs.append((ctx, (new,)))
1730 obs.append((ctx, (new,)))
1708
1731
1709 obsolete.createmarkers(repo, obs)
1732 obsolete.createmarkers(repo, obs)
1710 tr.close()
1733 tr.close()
1711 finally:
1734 finally:
1712 tr.release()
1735 tr.release()
1713 if (not obsolete._enabled) and newid != old.node():
1736 if (not obsolete._enabled) and newid != old.node():
1714 # Strip the intermediate commit (if there was one) and the amended
1737 # Strip the intermediate commit (if there was one) and the amended
1715 # commit
1738 # commit
1716 if node:
1739 if node:
1717 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1740 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1718 ui.note(_('stripping amended changeset %s\n') % old)
1741 ui.note(_('stripping amended changeset %s\n') % old)
1719 repair.strip(ui, repo, old.node(), topic='amend-backup')
1742 repair.strip(ui, repo, old.node(), topic='amend-backup')
1720 finally:
1743 finally:
1721 lockmod.release(wlock, lock)
1744 lockmod.release(wlock, lock)
1722 return newid
1745 return newid
1723
1746
1724 def commiteditor(repo, ctx, subs):
1747 def commiteditor(repo, ctx, subs):
1725 if ctx.description():
1748 if ctx.description():
1726 return ctx.description()
1749 return ctx.description()
1727 return commitforceeditor(repo, ctx, subs)
1750 return commitforceeditor(repo, ctx, subs)
1728
1751
1729 def commitforceeditor(repo, ctx, subs):
1752 def commitforceeditor(repo, ctx, subs):
1730 edittext = []
1753 edittext = []
1731 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1754 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1732 if ctx.description():
1755 if ctx.description():
1733 edittext.append(ctx.description())
1756 edittext.append(ctx.description())
1734 edittext.append("")
1757 edittext.append("")
1735 edittext.append("") # Empty line between message and comments.
1758 edittext.append("") # Empty line between message and comments.
1736 edittext.append(_("HG: Enter commit message."
1759 edittext.append(_("HG: Enter commit message."
1737 " Lines beginning with 'HG:' are removed."))
1760 " Lines beginning with 'HG:' are removed."))
1738 edittext.append(_("HG: Leave message empty to abort commit."))
1761 edittext.append(_("HG: Leave message empty to abort commit."))
1739 edittext.append("HG: --")
1762 edittext.append("HG: --")
1740 edittext.append(_("HG: user: %s") % ctx.user())
1763 edittext.append(_("HG: user: %s") % ctx.user())
1741 if ctx.p2():
1764 if ctx.p2():
1742 edittext.append(_("HG: branch merge"))
1765 edittext.append(_("HG: branch merge"))
1743 if ctx.branch():
1766 if ctx.branch():
1744 edittext.append(_("HG: branch '%s'") % ctx.branch())
1767 edittext.append(_("HG: branch '%s'") % ctx.branch())
1745 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1768 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1746 edittext.extend([_("HG: added %s") % f for f in added])
1769 edittext.extend([_("HG: added %s") % f for f in added])
1747 edittext.extend([_("HG: changed %s") % f for f in modified])
1770 edittext.extend([_("HG: changed %s") % f for f in modified])
1748 edittext.extend([_("HG: removed %s") % f for f in removed])
1771 edittext.extend([_("HG: removed %s") % f for f in removed])
1749 if not added and not modified and not removed:
1772 if not added and not modified and not removed:
1750 edittext.append(_("HG: no files changed"))
1773 edittext.append(_("HG: no files changed"))
1751 edittext.append("")
1774 edittext.append("")
1752 # run editor in the repository root
1775 # run editor in the repository root
1753 olddir = os.getcwd()
1776 olddir = os.getcwd()
1754 os.chdir(repo.root)
1777 os.chdir(repo.root)
1755 text = repo.ui.edit("\n".join(edittext), ctx.user())
1778 text = repo.ui.edit("\n".join(edittext), ctx.user())
1756 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1779 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1757 os.chdir(olddir)
1780 os.chdir(olddir)
1758
1781
1759 if not text.strip():
1782 if not text.strip():
1760 raise util.Abort(_("empty commit message"))
1783 raise util.Abort(_("empty commit message"))
1761
1784
1762 return text
1785 return text
1763
1786
1764 def revert(ui, repo, ctx, parents, *pats, **opts):
1787 def revert(ui, repo, ctx, parents, *pats, **opts):
1765 parent, p2 = parents
1788 parent, p2 = parents
1766 node = ctx.node()
1789 node = ctx.node()
1767
1790
1768 mf = ctx.manifest()
1791 mf = ctx.manifest()
1769 if node == parent:
1792 if node == parent:
1770 pmf = mf
1793 pmf = mf
1771 else:
1794 else:
1772 pmf = None
1795 pmf = None
1773
1796
1774 # need all matching names in dirstate and manifest of target rev,
1797 # need all matching names in dirstate and manifest of target rev,
1775 # so have to walk both. do not print errors if files exist in one
1798 # so have to walk both. do not print errors if files exist in one
1776 # but not other.
1799 # but not other.
1777
1800
1778 names = {}
1801 names = {}
1779
1802
1780 wlock = repo.wlock()
1803 wlock = repo.wlock()
1781 try:
1804 try:
1782 # walk dirstate.
1805 # walk dirstate.
1783
1806
1784 m = scmutil.match(repo[None], pats, opts)
1807 m = scmutil.match(repo[None], pats, opts)
1785 m.bad = lambda x, y: False
1808 m.bad = lambda x, y: False
1786 for abs in repo.walk(m):
1809 for abs in repo.walk(m):
1787 names[abs] = m.rel(abs), m.exact(abs)
1810 names[abs] = m.rel(abs), m.exact(abs)
1788
1811
1789 # walk target manifest.
1812 # walk target manifest.
1790
1813
1791 def badfn(path, msg):
1814 def badfn(path, msg):
1792 if path in names:
1815 if path in names:
1793 return
1816 return
1794 if path in ctx.substate:
1817 if path in ctx.substate:
1795 return
1818 return
1796 path_ = path + '/'
1819 path_ = path + '/'
1797 for f in names:
1820 for f in names:
1798 if f.startswith(path_):
1821 if f.startswith(path_):
1799 return
1822 return
1800 ui.warn("%s: %s\n" % (m.rel(path), msg))
1823 ui.warn("%s: %s\n" % (m.rel(path), msg))
1801
1824
1802 m = scmutil.match(ctx, pats, opts)
1825 m = scmutil.match(ctx, pats, opts)
1803 m.bad = badfn
1826 m.bad = badfn
1804 for abs in ctx.walk(m):
1827 for abs in ctx.walk(m):
1805 if abs not in names:
1828 if abs not in names:
1806 names[abs] = m.rel(abs), m.exact(abs)
1829 names[abs] = m.rel(abs), m.exact(abs)
1807
1830
1808 # get the list of subrepos that must be reverted
1831 # get the list of subrepos that must be reverted
1809 targetsubs = [s for s in ctx.substate if m(s)]
1832 targetsubs = [s for s in ctx.substate if m(s)]
1810 m = scmutil.matchfiles(repo, names)
1833 m = scmutil.matchfiles(repo, names)
1811 changes = repo.status(match=m)[:4]
1834 changes = repo.status(match=m)[:4]
1812 modified, added, removed, deleted = map(set, changes)
1835 modified, added, removed, deleted = map(set, changes)
1813
1836
1814 # if f is a rename, also revert the source
1837 # if f is a rename, also revert the source
1815 cwd = repo.getcwd()
1838 cwd = repo.getcwd()
1816 for f in added:
1839 for f in added:
1817 src = repo.dirstate.copied(f)
1840 src = repo.dirstate.copied(f)
1818 if src and src not in names and repo.dirstate[src] == 'r':
1841 if src and src not in names and repo.dirstate[src] == 'r':
1819 removed.add(src)
1842 removed.add(src)
1820 names[src] = (repo.pathto(src, cwd), True)
1843 names[src] = (repo.pathto(src, cwd), True)
1821
1844
1822 def removeforget(abs):
1845 def removeforget(abs):
1823 if repo.dirstate[abs] == 'a':
1846 if repo.dirstate[abs] == 'a':
1824 return _('forgetting %s\n')
1847 return _('forgetting %s\n')
1825 return _('removing %s\n')
1848 return _('removing %s\n')
1826
1849
1827 revert = ([], _('reverting %s\n'))
1850 revert = ([], _('reverting %s\n'))
1828 add = ([], _('adding %s\n'))
1851 add = ([], _('adding %s\n'))
1829 remove = ([], removeforget)
1852 remove = ([], removeforget)
1830 undelete = ([], _('undeleting %s\n'))
1853 undelete = ([], _('undeleting %s\n'))
1831
1854
1832 disptable = (
1855 disptable = (
1833 # dispatch table:
1856 # dispatch table:
1834 # file state
1857 # file state
1835 # action if in target manifest
1858 # action if in target manifest
1836 # action if not in target manifest
1859 # action if not in target manifest
1837 # make backup if in target manifest
1860 # make backup if in target manifest
1838 # make backup if not in target manifest
1861 # make backup if not in target manifest
1839 (modified, revert, remove, True, True),
1862 (modified, revert, remove, True, True),
1840 (added, revert, remove, True, False),
1863 (added, revert, remove, True, False),
1841 (removed, undelete, None, False, False),
1864 (removed, undelete, None, False, False),
1842 (deleted, revert, remove, False, False),
1865 (deleted, revert, remove, False, False),
1843 )
1866 )
1844
1867
1845 for abs, (rel, exact) in sorted(names.items()):
1868 for abs, (rel, exact) in sorted(names.items()):
1846 mfentry = mf.get(abs)
1869 mfentry = mf.get(abs)
1847 target = repo.wjoin(abs)
1870 target = repo.wjoin(abs)
1848 def handle(xlist, dobackup):
1871 def handle(xlist, dobackup):
1849 xlist[0].append(abs)
1872 xlist[0].append(abs)
1850 if (dobackup and not opts.get('no_backup') and
1873 if (dobackup and not opts.get('no_backup') and
1851 os.path.lexists(target)):
1874 os.path.lexists(target)):
1852 bakname = "%s.orig" % rel
1875 bakname = "%s.orig" % rel
1853 ui.note(_('saving current version of %s as %s\n') %
1876 ui.note(_('saving current version of %s as %s\n') %
1854 (rel, bakname))
1877 (rel, bakname))
1855 if not opts.get('dry_run'):
1878 if not opts.get('dry_run'):
1856 util.rename(target, bakname)
1879 util.rename(target, bakname)
1857 if ui.verbose or not exact:
1880 if ui.verbose or not exact:
1858 msg = xlist[1]
1881 msg = xlist[1]
1859 if not isinstance(msg, basestring):
1882 if not isinstance(msg, basestring):
1860 msg = msg(abs)
1883 msg = msg(abs)
1861 ui.status(msg % rel)
1884 ui.status(msg % rel)
1862 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1885 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1863 if abs not in table:
1886 if abs not in table:
1864 continue
1887 continue
1865 # file has changed in dirstate
1888 # file has changed in dirstate
1866 if mfentry:
1889 if mfentry:
1867 handle(hitlist, backuphit)
1890 handle(hitlist, backuphit)
1868 elif misslist is not None:
1891 elif misslist is not None:
1869 handle(misslist, backupmiss)
1892 handle(misslist, backupmiss)
1870 break
1893 break
1871 else:
1894 else:
1872 if abs not in repo.dirstate:
1895 if abs not in repo.dirstate:
1873 if mfentry:
1896 if mfentry:
1874 handle(add, True)
1897 handle(add, True)
1875 elif exact:
1898 elif exact:
1876 ui.warn(_('file not managed: %s\n') % rel)
1899 ui.warn(_('file not managed: %s\n') % rel)
1877 continue
1900 continue
1878 # file has not changed in dirstate
1901 # file has not changed in dirstate
1879 if node == parent:
1902 if node == parent:
1880 if exact:
1903 if exact:
1881 ui.warn(_('no changes needed to %s\n') % rel)
1904 ui.warn(_('no changes needed to %s\n') % rel)
1882 continue
1905 continue
1883 if pmf is None:
1906 if pmf is None:
1884 # only need parent manifest in this unlikely case,
1907 # only need parent manifest in this unlikely case,
1885 # so do not read by default
1908 # so do not read by default
1886 pmf = repo[parent].manifest()
1909 pmf = repo[parent].manifest()
1887 if abs in pmf and mfentry:
1910 if abs in pmf and mfentry:
1888 # if version of file is same in parent and target
1911 # if version of file is same in parent and target
1889 # manifests, do nothing
1912 # manifests, do nothing
1890 if (pmf[abs] != mfentry or
1913 if (pmf[abs] != mfentry or
1891 pmf.flags(abs) != mf.flags(abs)):
1914 pmf.flags(abs) != mf.flags(abs)):
1892 handle(revert, False)
1915 handle(revert, False)
1893 else:
1916 else:
1894 handle(remove, False)
1917 handle(remove, False)
1895
1918
1896 if not opts.get('dry_run'):
1919 if not opts.get('dry_run'):
1897 def checkout(f):
1920 def checkout(f):
1898 fc = ctx[f]
1921 fc = ctx[f]
1899 repo.wwrite(f, fc.data(), fc.flags())
1922 repo.wwrite(f, fc.data(), fc.flags())
1900
1923
1901 audit_path = scmutil.pathauditor(repo.root)
1924 audit_path = scmutil.pathauditor(repo.root)
1902 for f in remove[0]:
1925 for f in remove[0]:
1903 if repo.dirstate[f] == 'a':
1926 if repo.dirstate[f] == 'a':
1904 repo.dirstate.drop(f)
1927 repo.dirstate.drop(f)
1905 continue
1928 continue
1906 audit_path(f)
1929 audit_path(f)
1907 try:
1930 try:
1908 util.unlinkpath(repo.wjoin(f))
1931 util.unlinkpath(repo.wjoin(f))
1909 except OSError:
1932 except OSError:
1910 pass
1933 pass
1911 repo.dirstate.remove(f)
1934 repo.dirstate.remove(f)
1912
1935
1913 normal = None
1936 normal = None
1914 if node == parent:
1937 if node == parent:
1915 # We're reverting to our parent. If possible, we'd like status
1938 # We're reverting to our parent. If possible, we'd like status
1916 # to report the file as clean. We have to use normallookup for
1939 # to report the file as clean. We have to use normallookup for
1917 # merges to avoid losing information about merged/dirty files.
1940 # merges to avoid losing information about merged/dirty files.
1918 if p2 != nullid:
1941 if p2 != nullid:
1919 normal = repo.dirstate.normallookup
1942 normal = repo.dirstate.normallookup
1920 else:
1943 else:
1921 normal = repo.dirstate.normal
1944 normal = repo.dirstate.normal
1922 for f in revert[0]:
1945 for f in revert[0]:
1923 checkout(f)
1946 checkout(f)
1924 if normal:
1947 if normal:
1925 normal(f)
1948 normal(f)
1926
1949
1927 for f in add[0]:
1950 for f in add[0]:
1928 checkout(f)
1951 checkout(f)
1929 repo.dirstate.add(f)
1952 repo.dirstate.add(f)
1930
1953
1931 normal = repo.dirstate.normallookup
1954 normal = repo.dirstate.normallookup
1932 if node == parent and p2 == nullid:
1955 if node == parent and p2 == nullid:
1933 normal = repo.dirstate.normal
1956 normal = repo.dirstate.normal
1934 for f in undelete[0]:
1957 for f in undelete[0]:
1935 checkout(f)
1958 checkout(f)
1936 normal(f)
1959 normal(f)
1937
1960
1938 if targetsubs:
1961 if targetsubs:
1939 # Revert the subrepos on the revert list
1962 # Revert the subrepos on the revert list
1940 for sub in targetsubs:
1963 for sub in targetsubs:
1941 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1964 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1942 finally:
1965 finally:
1943 wlock.release()
1966 wlock.release()
1944
1967
1945 def command(table):
1968 def command(table):
1946 '''returns a function object bound to table which can be used as
1969 '''returns a function object bound to table which can be used as
1947 a decorator for populating table as a command table'''
1970 a decorator for populating table as a command table'''
1948
1971
1949 def cmd(name, options, synopsis=None):
1972 def cmd(name, options, synopsis=None):
1950 def decorator(func):
1973 def decorator(func):
1951 if synopsis:
1974 if synopsis:
1952 table[name] = func, options[:], synopsis
1975 table[name] = func, options[:], synopsis
1953 else:
1976 else:
1954 table[name] = func, options[:]
1977 table[name] = func, options[:]
1955 return func
1978 return func
1956 return decorator
1979 return decorator
1957
1980
1958 return cmd
1981 return cmd
@@ -1,1215 +1,1264 b''
1 The g is crafted to have 2 filelog topological heads in a linear
1 The g is crafted to have 2 filelog topological heads in a linear
2 changeset graph
2 changeset graph
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ echo f > f
7 $ echo f > f
8 $ hg ci -Ama -d '1 0'
8 $ hg ci -Ama -d '1 0'
9 adding a
9 adding a
10 adding f
10 adding f
11
11
12 $ hg cp a b
12 $ hg cp a b
13 $ hg cp f g
13 $ hg cp f g
14 $ hg ci -mb -d '2 0'
14 $ hg ci -mb -d '2 0'
15
15
16 $ mkdir dir
16 $ mkdir dir
17 $ hg mv b dir
17 $ hg mv b dir
18 $ echo g >> g
18 $ echo g >> g
19 $ echo f >> f
19 $ echo f >> f
20 $ hg ci -mc -d '3 0'
20 $ hg ci -mc -d '3 0'
21
21
22 $ hg mv a b
22 $ hg mv a b
23 $ hg cp -f f g
23 $ hg cp -f f g
24 $ echo a > d
24 $ echo a > d
25 $ hg add d
25 $ hg add d
26 $ hg ci -md -d '4 0'
26 $ hg ci -md -d '4 0'
27
27
28 $ hg mv dir/b e
28 $ hg mv dir/b e
29 $ hg ci -me -d '5 0'
29 $ hg ci -me -d '5 0'
30
30
31 $ hg log a
31 $ hg log a
32 changeset: 0:9161b9aeaf16
32 changeset: 0:9161b9aeaf16
33 user: test
33 user: test
34 date: Thu Jan 01 00:00:01 1970 +0000
34 date: Thu Jan 01 00:00:01 1970 +0000
35 summary: a
35 summary: a
36
36
37
37
38 -f, directory
38 -f, directory
39
39
40 $ hg log -f dir
40 $ hg log -f dir
41 abort: cannot follow file not in parent revision: "dir"
41 abort: cannot follow file not in parent revision: "dir"
42 [255]
42 [255]
43
43
44 -f, but no args
44 -f, but no args
45
45
46 $ hg log -f
46 $ hg log -f
47 changeset: 4:7e4639b4691b
47 changeset: 4:7e4639b4691b
48 tag: tip
48 tag: tip
49 user: test
49 user: test
50 date: Thu Jan 01 00:00:05 1970 +0000
50 date: Thu Jan 01 00:00:05 1970 +0000
51 summary: e
51 summary: e
52
52
53 changeset: 3:2ca5ba701980
53 changeset: 3:2ca5ba701980
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:04 1970 +0000
55 date: Thu Jan 01 00:00:04 1970 +0000
56 summary: d
56 summary: d
57
57
58 changeset: 2:f8954cd4dc1f
58 changeset: 2:f8954cd4dc1f
59 user: test
59 user: test
60 date: Thu Jan 01 00:00:03 1970 +0000
60 date: Thu Jan 01 00:00:03 1970 +0000
61 summary: c
61 summary: c
62
62
63 changeset: 1:d89b0a12d229
63 changeset: 1:d89b0a12d229
64 user: test
64 user: test
65 date: Thu Jan 01 00:00:02 1970 +0000
65 date: Thu Jan 01 00:00:02 1970 +0000
66 summary: b
66 summary: b
67
67
68 changeset: 0:9161b9aeaf16
68 changeset: 0:9161b9aeaf16
69 user: test
69 user: test
70 date: Thu Jan 01 00:00:01 1970 +0000
70 date: Thu Jan 01 00:00:01 1970 +0000
71 summary: a
71 summary: a
72
72
73
73
74 one rename
74 one rename
75
75
76 $ hg up -q 2
76 $ hg up -q 2
77 $ hg log -vf a
77 $ hg log -vf a
78 changeset: 0:9161b9aeaf16
78 changeset: 0:9161b9aeaf16
79 user: test
79 user: test
80 date: Thu Jan 01 00:00:01 1970 +0000
80 date: Thu Jan 01 00:00:01 1970 +0000
81 files: a f
81 files: a f
82 description:
82 description:
83 a
83 a
84
84
85
85
86
86
87 many renames
87 many renames
88
88
89 $ hg up -q tip
89 $ hg up -q tip
90 $ hg log -vf e
90 $ hg log -vf e
91 changeset: 4:7e4639b4691b
91 changeset: 4:7e4639b4691b
92 tag: tip
92 tag: tip
93 user: test
93 user: test
94 date: Thu Jan 01 00:00:05 1970 +0000
94 date: Thu Jan 01 00:00:05 1970 +0000
95 files: dir/b e
95 files: dir/b e
96 description:
96 description:
97 e
97 e
98
98
99
99
100 changeset: 2:f8954cd4dc1f
100 changeset: 2:f8954cd4dc1f
101 user: test
101 user: test
102 date: Thu Jan 01 00:00:03 1970 +0000
102 date: Thu Jan 01 00:00:03 1970 +0000
103 files: b dir/b f g
103 files: b dir/b f g
104 description:
104 description:
105 c
105 c
106
106
107
107
108 changeset: 1:d89b0a12d229
108 changeset: 1:d89b0a12d229
109 user: test
109 user: test
110 date: Thu Jan 01 00:00:02 1970 +0000
110 date: Thu Jan 01 00:00:02 1970 +0000
111 files: b g
111 files: b g
112 description:
112 description:
113 b
113 b
114
114
115
115
116 changeset: 0:9161b9aeaf16
116 changeset: 0:9161b9aeaf16
117 user: test
117 user: test
118 date: Thu Jan 01 00:00:01 1970 +0000
118 date: Thu Jan 01 00:00:01 1970 +0000
119 files: a f
119 files: a f
120 description:
120 description:
121 a
121 a
122
122
123
123
124
124
125
125
126 log -pf dir/b
126 log -pf dir/b
127
127
128 $ hg up -q 3
128 $ hg up -q 3
129 $ hg log -pf dir/b
129 $ hg log -pf dir/b
130 changeset: 2:f8954cd4dc1f
130 changeset: 2:f8954cd4dc1f
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:03 1970 +0000
132 date: Thu Jan 01 00:00:03 1970 +0000
133 summary: c
133 summary: c
134
134
135 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
135 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
137 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
137 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
138 @@ -0,0 +1,1 @@
138 @@ -0,0 +1,1 @@
139 +a
139 +a
140
140
141 changeset: 1:d89b0a12d229
141 changeset: 1:d89b0a12d229
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:02 1970 +0000
143 date: Thu Jan 01 00:00:02 1970 +0000
144 summary: b
144 summary: b
145
145
146 diff -r 9161b9aeaf16 -r d89b0a12d229 b
146 diff -r 9161b9aeaf16 -r d89b0a12d229 b
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148 +++ b/b Thu Jan 01 00:00:02 1970 +0000
148 +++ b/b Thu Jan 01 00:00:02 1970 +0000
149 @@ -0,0 +1,1 @@
149 @@ -0,0 +1,1 @@
150 +a
150 +a
151
151
152 changeset: 0:9161b9aeaf16
152 changeset: 0:9161b9aeaf16
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:01 1970 +0000
154 date: Thu Jan 01 00:00:01 1970 +0000
155 summary: a
155 summary: a
156
156
157 diff -r 000000000000 -r 9161b9aeaf16 a
157 diff -r 000000000000 -r 9161b9aeaf16 a
158 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
158 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
159 +++ b/a Thu Jan 01 00:00:01 1970 +0000
159 +++ b/a Thu Jan 01 00:00:01 1970 +0000
160 @@ -0,0 +1,1 @@
160 @@ -0,0 +1,1 @@
161 +a
161 +a
162
162
163
163
164 log -vf dir/b
164 log -vf dir/b
165
165
166 $ hg log -vf dir/b
166 $ hg log -vf dir/b
167 changeset: 2:f8954cd4dc1f
167 changeset: 2:f8954cd4dc1f
168 user: test
168 user: test
169 date: Thu Jan 01 00:00:03 1970 +0000
169 date: Thu Jan 01 00:00:03 1970 +0000
170 files: b dir/b f g
170 files: b dir/b f g
171 description:
171 description:
172 c
172 c
173
173
174
174
175 changeset: 1:d89b0a12d229
175 changeset: 1:d89b0a12d229
176 user: test
176 user: test
177 date: Thu Jan 01 00:00:02 1970 +0000
177 date: Thu Jan 01 00:00:02 1970 +0000
178 files: b g
178 files: b g
179 description:
179 description:
180 b
180 b
181
181
182
182
183 changeset: 0:9161b9aeaf16
183 changeset: 0:9161b9aeaf16
184 user: test
184 user: test
185 date: Thu Jan 01 00:00:01 1970 +0000
185 date: Thu Jan 01 00:00:01 1970 +0000
186 files: a f
186 files: a f
187 description:
187 description:
188 a
188 a
189
189
190
190
191
191
192
192
193 -f and multiple filelog heads
193 -f and multiple filelog heads
194
194
195 $ hg up -q 2
195 $ hg up -q 2
196 $ hg log -f g --template '{rev}\n'
196 $ hg log -f g --template '{rev}\n'
197 2
197 2
198 1
198 1
199 0
199 0
200 $ hg up -q tip
200 $ hg up -q tip
201 $ hg log -f g --template '{rev}\n'
201 $ hg log -f g --template '{rev}\n'
202 3
202 3
203 2
203 2
204 0
204 0
205
205
206
206
207 log copies with --copies
207 log copies with --copies
208
208
209 $ hg log -vC --template '{rev} {file_copies}\n'
209 $ hg log -vC --template '{rev} {file_copies}\n'
210 4 e (dir/b)
210 4 e (dir/b)
211 3 b (a)g (f)
211 3 b (a)g (f)
212 2 dir/b (b)
212 2 dir/b (b)
213 1 b (a)g (f)
213 1 b (a)g (f)
214 0
214 0
215
215
216 log copies switch without --copies, with old filecopy template
216 log copies switch without --copies, with old filecopy template
217
217
218 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
218 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
219 4
219 4
220 3
220 3
221 2
221 2
222 1
222 1
223 0
223 0
224
224
225 log copies switch with --copies
225 log copies switch with --copies
226
226
227 $ hg log -vC --template '{rev} {file_copies_switch}\n'
227 $ hg log -vC --template '{rev} {file_copies_switch}\n'
228 4 e (dir/b)
228 4 e (dir/b)
229 3 b (a)g (f)
229 3 b (a)g (f)
230 2 dir/b (b)
230 2 dir/b (b)
231 1 b (a)g (f)
231 1 b (a)g (f)
232 0
232 0
233
233
234
234
235 log copies with hardcoded style and with --style=default
235 log copies with hardcoded style and with --style=default
236
236
237 $ hg log -vC -r4
237 $ hg log -vC -r4
238 changeset: 4:7e4639b4691b
238 changeset: 4:7e4639b4691b
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:05 1970 +0000
241 date: Thu Jan 01 00:00:05 1970 +0000
242 files: dir/b e
242 files: dir/b e
243 copies: e (dir/b)
243 copies: e (dir/b)
244 description:
244 description:
245 e
245 e
246
246
247
247
248 $ hg log -vC -r4 --style=default
248 $ hg log -vC -r4 --style=default
249 changeset: 4:7e4639b4691b
249 changeset: 4:7e4639b4691b
250 tag: tip
250 tag: tip
251 user: test
251 user: test
252 date: Thu Jan 01 00:00:05 1970 +0000
252 date: Thu Jan 01 00:00:05 1970 +0000
253 files: dir/b e
253 files: dir/b e
254 copies: e (dir/b)
254 copies: e (dir/b)
255 description:
255 description:
256 e
256 e
257
257
258
258
259
259
260
260
261 log copies, non-linear manifest
261 log copies, non-linear manifest
262
262
263 $ hg up -C 3
263 $ hg up -C 3
264 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
264 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
265 $ hg mv dir/b e
265 $ hg mv dir/b e
266 $ echo foo > foo
266 $ echo foo > foo
267 $ hg ci -Ame2 -d '6 0'
267 $ hg ci -Ame2 -d '6 0'
268 adding foo
268 adding foo
269 created new head
269 created new head
270 $ hg log -v --template '{rev} {file_copies}\n' -r 5
270 $ hg log -v --template '{rev} {file_copies}\n' -r 5
271 5 e (dir/b)
271 5 e (dir/b)
272
272
273
273
274 log copies, execute bit set
274 log copies, execute bit set
275
275
276 #if execbit
276 #if execbit
277 $ chmod +x e
277 $ chmod +x e
278 $ hg ci -me3 -d '7 0'
278 $ hg ci -me3 -d '7 0'
279 $ hg log -v --template '{rev} {file_copies}\n' -r 6
279 $ hg log -v --template '{rev} {file_copies}\n' -r 6
280 6
280 6
281 #endif
281 #endif
282
282
283
283
284 log -p d
284 log -p d
285
285
286 $ hg log -pv d
286 $ hg log -pv d
287 changeset: 3:2ca5ba701980
287 changeset: 3:2ca5ba701980
288 user: test
288 user: test
289 date: Thu Jan 01 00:00:04 1970 +0000
289 date: Thu Jan 01 00:00:04 1970 +0000
290 files: a b d g
290 files: a b d g
291 description:
291 description:
292 d
292 d
293
293
294
294
295 diff -r f8954cd4dc1f -r 2ca5ba701980 d
295 diff -r f8954cd4dc1f -r 2ca5ba701980 d
296 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
296 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
297 +++ b/d Thu Jan 01 00:00:04 1970 +0000
297 +++ b/d Thu Jan 01 00:00:04 1970 +0000
298 @@ -0,0 +1,1 @@
298 @@ -0,0 +1,1 @@
299 +a
299 +a
300
300
301
301
302
302
303 log --removed file
303 log --removed file
304
304
305 $ hg log --removed -v a
305 $ hg log --removed -v a
306 changeset: 3:2ca5ba701980
306 changeset: 3:2ca5ba701980
307 user: test
307 user: test
308 date: Thu Jan 01 00:00:04 1970 +0000
308 date: Thu Jan 01 00:00:04 1970 +0000
309 files: a b d g
309 files: a b d g
310 description:
310 description:
311 d
311 d
312
312
313
313
314 changeset: 0:9161b9aeaf16
314 changeset: 0:9161b9aeaf16
315 user: test
315 user: test
316 date: Thu Jan 01 00:00:01 1970 +0000
316 date: Thu Jan 01 00:00:01 1970 +0000
317 files: a f
317 files: a f
318 description:
318 description:
319 a
319 a
320
320
321
321
322
322
323 log --removed revrange file
323 log --removed revrange file
324
324
325 $ hg log --removed -v -r0:2 a
325 $ hg log --removed -v -r0:2 a
326 changeset: 0:9161b9aeaf16
326 changeset: 0:9161b9aeaf16
327 user: test
327 user: test
328 date: Thu Jan 01 00:00:01 1970 +0000
328 date: Thu Jan 01 00:00:01 1970 +0000
329 files: a f
329 files: a f
330 description:
330 description:
331 a
331 a
332
332
333
333
334 $ cd ..
334 $ cd ..
335
335
336 log --follow tests
336 log --follow tests
337
337
338 $ hg init follow
338 $ hg init follow
339 $ cd follow
339 $ cd follow
340
340
341 $ echo base > base
341 $ echo base > base
342 $ hg ci -Ambase -d '1 0'
342 $ hg ci -Ambase -d '1 0'
343 adding base
343 adding base
344
344
345 $ echo r1 >> base
345 $ echo r1 >> base
346 $ hg ci -Amr1 -d '1 0'
346 $ hg ci -Amr1 -d '1 0'
347 $ echo r2 >> base
347 $ echo r2 >> base
348 $ hg ci -Amr2 -d '1 0'
348 $ hg ci -Amr2 -d '1 0'
349
349
350 $ hg up -C 1
350 $ hg up -C 1
351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 $ echo b1 > b1
352 $ echo b1 > b1
353 $ hg ci -Amb1 -d '1 0'
353 $ hg ci -Amb1 -d '1 0'
354 adding b1
354 adding b1
355 created new head
355 created new head
356
356
357
357
358 log -f
358 log -f
359
359
360 $ hg log -f
360 $ hg log -f
361 changeset: 3:e62f78d544b4
361 changeset: 3:e62f78d544b4
362 tag: tip
362 tag: tip
363 parent: 1:3d5bf5654eda
363 parent: 1:3d5bf5654eda
364 user: test
364 user: test
365 date: Thu Jan 01 00:00:01 1970 +0000
365 date: Thu Jan 01 00:00:01 1970 +0000
366 summary: b1
366 summary: b1
367
367
368 changeset: 1:3d5bf5654eda
368 changeset: 1:3d5bf5654eda
369 user: test
369 user: test
370 date: Thu Jan 01 00:00:01 1970 +0000
370 date: Thu Jan 01 00:00:01 1970 +0000
371 summary: r1
371 summary: r1
372
372
373 changeset: 0:67e992f2c4f3
373 changeset: 0:67e992f2c4f3
374 user: test
374 user: test
375 date: Thu Jan 01 00:00:01 1970 +0000
375 date: Thu Jan 01 00:00:01 1970 +0000
376 summary: base
376 summary: base
377
377
378
378
379
379
380 log -f -r 1:tip
380 log -f -r 1:tip
381
381
382 $ hg up -C 0
382 $ hg up -C 0
383 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
383 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
384 $ echo b2 > b2
384 $ echo b2 > b2
385 $ hg ci -Amb2 -d '1 0'
385 $ hg ci -Amb2 -d '1 0'
386 adding b2
386 adding b2
387 created new head
387 created new head
388 $ hg log -f -r 1:tip
388 $ hg log -f -r 1:tip
389 changeset: 1:3d5bf5654eda
389 changeset: 1:3d5bf5654eda
390 user: test
390 user: test
391 date: Thu Jan 01 00:00:01 1970 +0000
391 date: Thu Jan 01 00:00:01 1970 +0000
392 summary: r1
392 summary: r1
393
393
394 changeset: 2:60c670bf5b30
394 changeset: 2:60c670bf5b30
395 user: test
395 user: test
396 date: Thu Jan 01 00:00:01 1970 +0000
396 date: Thu Jan 01 00:00:01 1970 +0000
397 summary: r2
397 summary: r2
398
398
399 changeset: 3:e62f78d544b4
399 changeset: 3:e62f78d544b4
400 parent: 1:3d5bf5654eda
400 parent: 1:3d5bf5654eda
401 user: test
401 user: test
402 date: Thu Jan 01 00:00:01 1970 +0000
402 date: Thu Jan 01 00:00:01 1970 +0000
403 summary: b1
403 summary: b1
404
404
405
405
406
406
407 log -r . with two parents
407 log -r . with two parents
408
408
409 $ hg up -C 3
409 $ hg up -C 3
410 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
410 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
411 $ hg merge tip
411 $ hg merge tip
412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 (branch merge, don't forget to commit)
413 (branch merge, don't forget to commit)
414 $ hg log -r .
414 $ hg log -r .
415 changeset: 3:e62f78d544b4
415 changeset: 3:e62f78d544b4
416 parent: 1:3d5bf5654eda
416 parent: 1:3d5bf5654eda
417 user: test
417 user: test
418 date: Thu Jan 01 00:00:01 1970 +0000
418 date: Thu Jan 01 00:00:01 1970 +0000
419 summary: b1
419 summary: b1
420
420
421
421
422
422
423 log -r . with one parent
423 log -r . with one parent
424
424
425 $ hg ci -mm12 -d '1 0'
425 $ hg ci -mm12 -d '1 0'
426 $ hg log -r .
426 $ hg log -r .
427 changeset: 5:302e9dd6890d
427 changeset: 5:302e9dd6890d
428 tag: tip
428 tag: tip
429 parent: 3:e62f78d544b4
429 parent: 3:e62f78d544b4
430 parent: 4:ddb82e70d1a1
430 parent: 4:ddb82e70d1a1
431 user: test
431 user: test
432 date: Thu Jan 01 00:00:01 1970 +0000
432 date: Thu Jan 01 00:00:01 1970 +0000
433 summary: m12
433 summary: m12
434
434
435
435
436 $ echo postm >> b1
436 $ echo postm >> b1
437 $ hg ci -Amb1.1 -d'1 0'
437 $ hg ci -Amb1.1 -d'1 0'
438
438
439
439
440 log --follow-first
440 log --follow-first
441
441
442 $ hg log --follow-first
442 $ hg log --follow-first
443 changeset: 6:2404bbcab562
443 changeset: 6:2404bbcab562
444 tag: tip
444 tag: tip
445 user: test
445 user: test
446 date: Thu Jan 01 00:00:01 1970 +0000
446 date: Thu Jan 01 00:00:01 1970 +0000
447 summary: b1.1
447 summary: b1.1
448
448
449 changeset: 5:302e9dd6890d
449 changeset: 5:302e9dd6890d
450 parent: 3:e62f78d544b4
450 parent: 3:e62f78d544b4
451 parent: 4:ddb82e70d1a1
451 parent: 4:ddb82e70d1a1
452 user: test
452 user: test
453 date: Thu Jan 01 00:00:01 1970 +0000
453 date: Thu Jan 01 00:00:01 1970 +0000
454 summary: m12
454 summary: m12
455
455
456 changeset: 3:e62f78d544b4
456 changeset: 3:e62f78d544b4
457 parent: 1:3d5bf5654eda
457 parent: 1:3d5bf5654eda
458 user: test
458 user: test
459 date: Thu Jan 01 00:00:01 1970 +0000
459 date: Thu Jan 01 00:00:01 1970 +0000
460 summary: b1
460 summary: b1
461
461
462 changeset: 1:3d5bf5654eda
462 changeset: 1:3d5bf5654eda
463 user: test
463 user: test
464 date: Thu Jan 01 00:00:01 1970 +0000
464 date: Thu Jan 01 00:00:01 1970 +0000
465 summary: r1
465 summary: r1
466
466
467 changeset: 0:67e992f2c4f3
467 changeset: 0:67e992f2c4f3
468 user: test
468 user: test
469 date: Thu Jan 01 00:00:01 1970 +0000
469 date: Thu Jan 01 00:00:01 1970 +0000
470 summary: base
470 summary: base
471
471
472
472
473
473
474 log -P 2
474 log -P 2
475
475
476 $ hg log -P 2
476 $ hg log -P 2
477 changeset: 6:2404bbcab562
477 changeset: 6:2404bbcab562
478 tag: tip
478 tag: tip
479 user: test
479 user: test
480 date: Thu Jan 01 00:00:01 1970 +0000
480 date: Thu Jan 01 00:00:01 1970 +0000
481 summary: b1.1
481 summary: b1.1
482
482
483 changeset: 5:302e9dd6890d
483 changeset: 5:302e9dd6890d
484 parent: 3:e62f78d544b4
484 parent: 3:e62f78d544b4
485 parent: 4:ddb82e70d1a1
485 parent: 4:ddb82e70d1a1
486 user: test
486 user: test
487 date: Thu Jan 01 00:00:01 1970 +0000
487 date: Thu Jan 01 00:00:01 1970 +0000
488 summary: m12
488 summary: m12
489
489
490 changeset: 4:ddb82e70d1a1
490 changeset: 4:ddb82e70d1a1
491 parent: 0:67e992f2c4f3
491 parent: 0:67e992f2c4f3
492 user: test
492 user: test
493 date: Thu Jan 01 00:00:01 1970 +0000
493 date: Thu Jan 01 00:00:01 1970 +0000
494 summary: b2
494 summary: b2
495
495
496 changeset: 3:e62f78d544b4
496 changeset: 3:e62f78d544b4
497 parent: 1:3d5bf5654eda
497 parent: 1:3d5bf5654eda
498 user: test
498 user: test
499 date: Thu Jan 01 00:00:01 1970 +0000
499 date: Thu Jan 01 00:00:01 1970 +0000
500 summary: b1
500 summary: b1
501
501
502
502
503
503
504 log -r tip -p --git
504 log -r tip -p --git
505
505
506 $ hg log -r tip -p --git
506 $ hg log -r tip -p --git
507 changeset: 6:2404bbcab562
507 changeset: 6:2404bbcab562
508 tag: tip
508 tag: tip
509 user: test
509 user: test
510 date: Thu Jan 01 00:00:01 1970 +0000
510 date: Thu Jan 01 00:00:01 1970 +0000
511 summary: b1.1
511 summary: b1.1
512
512
513 diff --git a/b1 b/b1
513 diff --git a/b1 b/b1
514 --- a/b1
514 --- a/b1
515 +++ b/b1
515 +++ b/b1
516 @@ -1,1 +1,2 @@
516 @@ -1,1 +1,2 @@
517 b1
517 b1
518 +postm
518 +postm
519
519
520
520
521
521
522 log -r ""
522 log -r ""
523
523
524 $ hg log -r ''
524 $ hg log -r ''
525 hg: parse error: empty query
525 hg: parse error: empty query
526 [255]
526 [255]
527
527
528 log -r <some unknown node id>
528 log -r <some unknown node id>
529
529
530 $ hg log -r 1000000000000000000000000000000000000000
530 $ hg log -r 1000000000000000000000000000000000000000
531 abort: unknown revision '1000000000000000000000000000000000000000'!
531 abort: unknown revision '1000000000000000000000000000000000000000'!
532 [255]
532 [255]
533
533
534 log -k r1
534 log -k r1
535
535
536 $ hg log -k r1
536 $ hg log -k r1
537 changeset: 1:3d5bf5654eda
537 changeset: 1:3d5bf5654eda
538 user: test
538 user: test
539 date: Thu Jan 01 00:00:01 1970 +0000
539 date: Thu Jan 01 00:00:01 1970 +0000
540 summary: r1
540 summary: r1
541
541
542 log -p -l2 --color=always
542 log -p -l2 --color=always
543
543
544 $ hg --config extensions.color= --config color.mode=ansi \
544 $ hg --config extensions.color= --config color.mode=ansi \
545 > log -p -l2 --color=always
545 > log -p -l2 --color=always
546 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
546 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
547 tag: tip
547 tag: tip
548 user: test
548 user: test
549 date: Thu Jan 01 00:00:01 1970 +0000
549 date: Thu Jan 01 00:00:01 1970 +0000
550 summary: b1.1
550 summary: b1.1
551
551
552 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
552 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
553 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
553 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
554 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
554 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
555 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
555 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
556 b1
556 b1
557 \x1b[0;32m+postm\x1b[0m (esc)
557 \x1b[0;32m+postm\x1b[0m (esc)
558
558
559 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
559 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
560 parent: 3:e62f78d544b4
560 parent: 3:e62f78d544b4
561 parent: 4:ddb82e70d1a1
561 parent: 4:ddb82e70d1a1
562 user: test
562 user: test
563 date: Thu Jan 01 00:00:01 1970 +0000
563 date: Thu Jan 01 00:00:01 1970 +0000
564 summary: m12
564 summary: m12
565
565
566 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
566 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
567 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
567 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
568 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
568 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
569 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
569 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
570 \x1b[0;32m+b2\x1b[0m (esc)
570 \x1b[0;32m+b2\x1b[0m (esc)
571
571
572
572
573
573
574 log -r tip --stat
574 log -r tip --stat
575
575
576 $ hg log -r tip --stat
576 $ hg log -r tip --stat
577 changeset: 6:2404bbcab562
577 changeset: 6:2404bbcab562
578 tag: tip
578 tag: tip
579 user: test
579 user: test
580 date: Thu Jan 01 00:00:01 1970 +0000
580 date: Thu Jan 01 00:00:01 1970 +0000
581 summary: b1.1
581 summary: b1.1
582
582
583 b1 | 1 +
583 b1 | 1 +
584 1 files changed, 1 insertions(+), 0 deletions(-)
584 1 files changed, 1 insertions(+), 0 deletions(-)
585
585
586
586
587 $ cd ..
587 $ cd ..
588
588
589
589
590 User
590 User
591
591
592 $ hg init usertest
592 $ hg init usertest
593 $ cd usertest
593 $ cd usertest
594
594
595 $ echo a > a
595 $ echo a > a
596 $ hg ci -A -m "a" -u "User One <user1@example.org>"
596 $ hg ci -A -m "a" -u "User One <user1@example.org>"
597 adding a
597 adding a
598 $ echo b > b
598 $ echo b > b
599 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
599 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
600 adding b
600 adding b
601
601
602 $ hg log -u "User One <user1@example.org>"
602 $ hg log -u "User One <user1@example.org>"
603 changeset: 0:29a4c94f1924
603 changeset: 0:29a4c94f1924
604 user: User One <user1@example.org>
604 user: User One <user1@example.org>
605 date: Thu Jan 01 00:00:00 1970 +0000
605 date: Thu Jan 01 00:00:00 1970 +0000
606 summary: a
606 summary: a
607
607
608 $ hg log -u "user1" -u "user2"
608 $ hg log -u "user1" -u "user2"
609 changeset: 1:e834b5e69c0e
609 changeset: 1:e834b5e69c0e
610 tag: tip
610 tag: tip
611 user: User Two <user2@example.org>
611 user: User Two <user2@example.org>
612 date: Thu Jan 01 00:00:00 1970 +0000
612 date: Thu Jan 01 00:00:00 1970 +0000
613 summary: b
613 summary: b
614
614
615 changeset: 0:29a4c94f1924
615 changeset: 0:29a4c94f1924
616 user: User One <user1@example.org>
616 user: User One <user1@example.org>
617 date: Thu Jan 01 00:00:00 1970 +0000
617 date: Thu Jan 01 00:00:00 1970 +0000
618 summary: a
618 summary: a
619
619
620 $ hg log -u "user3"
620 $ hg log -u "user3"
621
621
622 $ cd ..
622 $ cd ..
623
623
624 $ hg init branches
624 $ hg init branches
625 $ cd branches
625 $ cd branches
626
626
627 $ echo a > a
627 $ echo a > a
628 $ hg ci -A -m "commit on default"
628 $ hg ci -A -m "commit on default"
629 adding a
629 adding a
630 $ hg branch test
630 $ hg branch test
631 marked working directory as branch test
631 marked working directory as branch test
632 (branches are permanent and global, did you want a bookmark?)
632 (branches are permanent and global, did you want a bookmark?)
633 $ echo b > b
633 $ echo b > b
634 $ hg ci -A -m "commit on test"
634 $ hg ci -A -m "commit on test"
635 adding b
635 adding b
636
636
637 $ hg up default
637 $ hg up default
638 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
638 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
639 $ echo c > c
639 $ echo c > c
640 $ hg ci -A -m "commit on default"
640 $ hg ci -A -m "commit on default"
641 adding c
641 adding c
642 $ hg up test
642 $ hg up test
643 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
643 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
644 $ echo c > c
644 $ echo c > c
645 $ hg ci -A -m "commit on test"
645 $ hg ci -A -m "commit on test"
646 adding c
646 adding c
647
647
648
648
649 log -b default
649 log -b default
650
650
651 $ hg log -b default
651 $ hg log -b default
652 changeset: 2:c3a4f03cc9a7
652 changeset: 2:c3a4f03cc9a7
653 parent: 0:24427303d56f
653 parent: 0:24427303d56f
654 user: test
654 user: test
655 date: Thu Jan 01 00:00:00 1970 +0000
655 date: Thu Jan 01 00:00:00 1970 +0000
656 summary: commit on default
656 summary: commit on default
657
657
658 changeset: 0:24427303d56f
658 changeset: 0:24427303d56f
659 user: test
659 user: test
660 date: Thu Jan 01 00:00:00 1970 +0000
660 date: Thu Jan 01 00:00:00 1970 +0000
661 summary: commit on default
661 summary: commit on default
662
662
663
663
664
664
665 log -b test
665 log -b test
666
666
667 $ hg log -b test
667 $ hg log -b test
668 changeset: 3:f5d8de11c2e2
668 changeset: 3:f5d8de11c2e2
669 branch: test
669 branch: test
670 tag: tip
670 tag: tip
671 parent: 1:d32277701ccb
671 parent: 1:d32277701ccb
672 user: test
672 user: test
673 date: Thu Jan 01 00:00:00 1970 +0000
673 date: Thu Jan 01 00:00:00 1970 +0000
674 summary: commit on test
674 summary: commit on test
675
675
676 changeset: 1:d32277701ccb
676 changeset: 1:d32277701ccb
677 branch: test
677 branch: test
678 user: test
678 user: test
679 date: Thu Jan 01 00:00:00 1970 +0000
679 date: Thu Jan 01 00:00:00 1970 +0000
680 summary: commit on test
680 summary: commit on test
681
681
682
682
683
683
684 log -b dummy
684 log -b dummy
685
685
686 $ hg log -b dummy
686 $ hg log -b dummy
687 abort: unknown revision 'dummy'!
687 abort: unknown revision 'dummy'!
688 [255]
688 [255]
689
689
690
690
691 log -b .
691 log -b .
692
692
693 $ hg log -b .
693 $ hg log -b .
694 changeset: 3:f5d8de11c2e2
694 changeset: 3:f5d8de11c2e2
695 branch: test
695 branch: test
696 tag: tip
696 tag: tip
697 parent: 1:d32277701ccb
697 parent: 1:d32277701ccb
698 user: test
698 user: test
699 date: Thu Jan 01 00:00:00 1970 +0000
699 date: Thu Jan 01 00:00:00 1970 +0000
700 summary: commit on test
700 summary: commit on test
701
701
702 changeset: 1:d32277701ccb
702 changeset: 1:d32277701ccb
703 branch: test
703 branch: test
704 user: test
704 user: test
705 date: Thu Jan 01 00:00:00 1970 +0000
705 date: Thu Jan 01 00:00:00 1970 +0000
706 summary: commit on test
706 summary: commit on test
707
707
708
708
709
709
710 log -b default -b test
710 log -b default -b test
711
711
712 $ hg log -b default -b test
712 $ hg log -b default -b test
713 changeset: 3:f5d8de11c2e2
713 changeset: 3:f5d8de11c2e2
714 branch: test
714 branch: test
715 tag: tip
715 tag: tip
716 parent: 1:d32277701ccb
716 parent: 1:d32277701ccb
717 user: test
717 user: test
718 date: Thu Jan 01 00:00:00 1970 +0000
718 date: Thu Jan 01 00:00:00 1970 +0000
719 summary: commit on test
719 summary: commit on test
720
720
721 changeset: 2:c3a4f03cc9a7
721 changeset: 2:c3a4f03cc9a7
722 parent: 0:24427303d56f
722 parent: 0:24427303d56f
723 user: test
723 user: test
724 date: Thu Jan 01 00:00:00 1970 +0000
724 date: Thu Jan 01 00:00:00 1970 +0000
725 summary: commit on default
725 summary: commit on default
726
726
727 changeset: 1:d32277701ccb
727 changeset: 1:d32277701ccb
728 branch: test
728 branch: test
729 user: test
729 user: test
730 date: Thu Jan 01 00:00:00 1970 +0000
730 date: Thu Jan 01 00:00:00 1970 +0000
731 summary: commit on test
731 summary: commit on test
732
732
733 changeset: 0:24427303d56f
733 changeset: 0:24427303d56f
734 user: test
734 user: test
735 date: Thu Jan 01 00:00:00 1970 +0000
735 date: Thu Jan 01 00:00:00 1970 +0000
736 summary: commit on default
736 summary: commit on default
737
737
738
738
739
739
740 log -b default -b .
740 log -b default -b .
741
741
742 $ hg log -b default -b .
742 $ hg log -b default -b .
743 changeset: 3:f5d8de11c2e2
743 changeset: 3:f5d8de11c2e2
744 branch: test
744 branch: test
745 tag: tip
745 tag: tip
746 parent: 1:d32277701ccb
746 parent: 1:d32277701ccb
747 user: test
747 user: test
748 date: Thu Jan 01 00:00:00 1970 +0000
748 date: Thu Jan 01 00:00:00 1970 +0000
749 summary: commit on test
749 summary: commit on test
750
750
751 changeset: 2:c3a4f03cc9a7
751 changeset: 2:c3a4f03cc9a7
752 parent: 0:24427303d56f
752 parent: 0:24427303d56f
753 user: test
753 user: test
754 date: Thu Jan 01 00:00:00 1970 +0000
754 date: Thu Jan 01 00:00:00 1970 +0000
755 summary: commit on default
755 summary: commit on default
756
756
757 changeset: 1:d32277701ccb
757 changeset: 1:d32277701ccb
758 branch: test
758 branch: test
759 user: test
759 user: test
760 date: Thu Jan 01 00:00:00 1970 +0000
760 date: Thu Jan 01 00:00:00 1970 +0000
761 summary: commit on test
761 summary: commit on test
762
762
763 changeset: 0:24427303d56f
763 changeset: 0:24427303d56f
764 user: test
764 user: test
765 date: Thu Jan 01 00:00:00 1970 +0000
765 date: Thu Jan 01 00:00:00 1970 +0000
766 summary: commit on default
766 summary: commit on default
767
767
768
768
769
769
770 log -b . -b test
770 log -b . -b test
771
771
772 $ hg log -b . -b test
772 $ hg log -b . -b test
773 changeset: 3:f5d8de11c2e2
773 changeset: 3:f5d8de11c2e2
774 branch: test
774 branch: test
775 tag: tip
775 tag: tip
776 parent: 1:d32277701ccb
776 parent: 1:d32277701ccb
777 user: test
777 user: test
778 date: Thu Jan 01 00:00:00 1970 +0000
778 date: Thu Jan 01 00:00:00 1970 +0000
779 summary: commit on test
779 summary: commit on test
780
780
781 changeset: 1:d32277701ccb
781 changeset: 1:d32277701ccb
782 branch: test
782 branch: test
783 user: test
783 user: test
784 date: Thu Jan 01 00:00:00 1970 +0000
784 date: Thu Jan 01 00:00:00 1970 +0000
785 summary: commit on test
785 summary: commit on test
786
786
787
787
788
788
789 log -b 2
789 log -b 2
790
790
791 $ hg log -b 2
791 $ hg log -b 2
792 changeset: 2:c3a4f03cc9a7
792 changeset: 2:c3a4f03cc9a7
793 parent: 0:24427303d56f
793 parent: 0:24427303d56f
794 user: test
794 user: test
795 date: Thu Jan 01 00:00:00 1970 +0000
795 date: Thu Jan 01 00:00:00 1970 +0000
796 summary: commit on default
796 summary: commit on default
797
797
798 changeset: 0:24427303d56f
798 changeset: 0:24427303d56f
799 user: test
799 user: test
800 date: Thu Jan 01 00:00:00 1970 +0000
800 date: Thu Jan 01 00:00:00 1970 +0000
801 summary: commit on default
801 summary: commit on default
802
802
803
803
804
804
805 log -p --cwd dir (in subdir)
805 log -p --cwd dir (in subdir)
806
806
807 $ mkdir dir
807 $ mkdir dir
808 $ hg log -p --cwd dir
808 $ hg log -p --cwd dir
809 changeset: 3:f5d8de11c2e2
809 changeset: 3:f5d8de11c2e2
810 branch: test
810 branch: test
811 tag: tip
811 tag: tip
812 parent: 1:d32277701ccb
812 parent: 1:d32277701ccb
813 user: test
813 user: test
814 date: Thu Jan 01 00:00:00 1970 +0000
814 date: Thu Jan 01 00:00:00 1970 +0000
815 summary: commit on test
815 summary: commit on test
816
816
817 diff -r d32277701ccb -r f5d8de11c2e2 c
817 diff -r d32277701ccb -r f5d8de11c2e2 c
818 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
818 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
819 +++ b/c Thu Jan 01 00:00:00 1970 +0000
819 +++ b/c Thu Jan 01 00:00:00 1970 +0000
820 @@ -0,0 +1,1 @@
820 @@ -0,0 +1,1 @@
821 +c
821 +c
822
822
823 changeset: 2:c3a4f03cc9a7
823 changeset: 2:c3a4f03cc9a7
824 parent: 0:24427303d56f
824 parent: 0:24427303d56f
825 user: test
825 user: test
826 date: Thu Jan 01 00:00:00 1970 +0000
826 date: Thu Jan 01 00:00:00 1970 +0000
827 summary: commit on default
827 summary: commit on default
828
828
829 diff -r 24427303d56f -r c3a4f03cc9a7 c
829 diff -r 24427303d56f -r c3a4f03cc9a7 c
830 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
830 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
831 +++ b/c Thu Jan 01 00:00:00 1970 +0000
831 +++ b/c Thu Jan 01 00:00:00 1970 +0000
832 @@ -0,0 +1,1 @@
832 @@ -0,0 +1,1 @@
833 +c
833 +c
834
834
835 changeset: 1:d32277701ccb
835 changeset: 1:d32277701ccb
836 branch: test
836 branch: test
837 user: test
837 user: test
838 date: Thu Jan 01 00:00:00 1970 +0000
838 date: Thu Jan 01 00:00:00 1970 +0000
839 summary: commit on test
839 summary: commit on test
840
840
841 diff -r 24427303d56f -r d32277701ccb b
841 diff -r 24427303d56f -r d32277701ccb b
842 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
842 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
843 +++ b/b Thu Jan 01 00:00:00 1970 +0000
843 +++ b/b Thu Jan 01 00:00:00 1970 +0000
844 @@ -0,0 +1,1 @@
844 @@ -0,0 +1,1 @@
845 +b
845 +b
846
846
847 changeset: 0:24427303d56f
847 changeset: 0:24427303d56f
848 user: test
848 user: test
849 date: Thu Jan 01 00:00:00 1970 +0000
849 date: Thu Jan 01 00:00:00 1970 +0000
850 summary: commit on default
850 summary: commit on default
851
851
852 diff -r 000000000000 -r 24427303d56f a
852 diff -r 000000000000 -r 24427303d56f a
853 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
853 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
854 +++ b/a Thu Jan 01 00:00:00 1970 +0000
854 +++ b/a Thu Jan 01 00:00:00 1970 +0000
855 @@ -0,0 +1,1 @@
855 @@ -0,0 +1,1 @@
856 +a
856 +a
857
857
858
858
859
859
860 log -p -R repo
860 log -p -R repo
861
861
862 $ cd dir
862 $ cd dir
863 $ hg log -p -R .. ../a
863 $ hg log -p -R .. ../a
864 changeset: 0:24427303d56f
864 changeset: 0:24427303d56f
865 user: test
865 user: test
866 date: Thu Jan 01 00:00:00 1970 +0000
866 date: Thu Jan 01 00:00:00 1970 +0000
867 summary: commit on default
867 summary: commit on default
868
868
869 diff -r 000000000000 -r 24427303d56f a
869 diff -r 000000000000 -r 24427303d56f a
870 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
870 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
871 +++ b/a Thu Jan 01 00:00:00 1970 +0000
871 +++ b/a Thu Jan 01 00:00:00 1970 +0000
872 @@ -0,0 +1,1 @@
872 @@ -0,0 +1,1 @@
873 +a
873 +a
874
874
875
875
876 $ cd ../..
876 $ cd ../..
877
877
878 $ hg init follow2
878 $ hg init follow2
879 $ cd follow2
879 $ cd follow2
880
880
881 # Build the following history:
881 # Build the following history:
882 # tip - o - x - o - x - x
882 # tip - o - x - o - x - x
883 # \ /
883 # \ /
884 # o - o - o - x
884 # o - o - o - x
885 # \ /
885 # \ /
886 # o
886 # o
887 #
887 #
888 # Where "o" is a revision containing "foo" and
888 # Where "o" is a revision containing "foo" and
889 # "x" is a revision without "foo"
889 # "x" is a revision without "foo"
890
890
891 $ touch init
891 $ touch init
892 $ hg ci -A -m "init, unrelated"
892 $ hg ci -A -m "init, unrelated"
893 adding init
893 adding init
894 $ echo 'foo' > init
894 $ echo 'foo' > init
895 $ hg ci -m "change, unrelated"
895 $ hg ci -m "change, unrelated"
896 $ echo 'foo' > foo
896 $ echo 'foo' > foo
897 $ hg ci -A -m "add unrelated old foo"
897 $ hg ci -A -m "add unrelated old foo"
898 adding foo
898 adding foo
899 $ hg rm foo
899 $ hg rm foo
900 $ hg ci -m "delete foo, unrelated"
900 $ hg ci -m "delete foo, unrelated"
901 $ echo 'related' > foo
901 $ echo 'related' > foo
902 $ hg ci -A -m "add foo, related"
902 $ hg ci -A -m "add foo, related"
903 adding foo
903 adding foo
904
904
905 $ hg up 0
905 $ hg up 0
906 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
906 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
907 $ touch branch
907 $ touch branch
908 $ hg ci -A -m "first branch, unrelated"
908 $ hg ci -A -m "first branch, unrelated"
909 adding branch
909 adding branch
910 created new head
910 created new head
911 $ touch foo
911 $ touch foo
912 $ hg ci -A -m "create foo, related"
912 $ hg ci -A -m "create foo, related"
913 adding foo
913 adding foo
914 $ echo 'change' > foo
914 $ echo 'change' > foo
915 $ hg ci -m "change foo, related"
915 $ hg ci -m "change foo, related"
916
916
917 $ hg up 6
917 $ hg up 6
918 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
918 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
919 $ echo 'change foo in branch' > foo
919 $ echo 'change foo in branch' > foo
920 $ hg ci -m "change foo in branch, related"
920 $ hg ci -m "change foo in branch, related"
921 created new head
921 created new head
922 $ hg merge 7
922 $ hg merge 7
923 merging foo
923 merging foo
924 warning: conflicts during merge.
924 warning: conflicts during merge.
925 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
925 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
926 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
926 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
927 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
927 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
928 [1]
928 [1]
929 $ echo 'merge 1' > foo
929 $ echo 'merge 1' > foo
930 $ hg resolve -m foo
930 $ hg resolve -m foo
931 $ hg ci -m "First merge, related"
931 $ hg ci -m "First merge, related"
932
932
933 $ hg merge 4
933 $ hg merge 4
934 merging foo
934 merging foo
935 warning: conflicts during merge.
935 warning: conflicts during merge.
936 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
936 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
937 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
937 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
938 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
938 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
939 [1]
939 [1]
940 $ echo 'merge 2' > foo
940 $ echo 'merge 2' > foo
941 $ hg resolve -m foo
941 $ hg resolve -m foo
942 $ hg ci -m "Last merge, related"
942 $ hg ci -m "Last merge, related"
943
943
944 $ hg log --graph
944 $ hg log --graph
945 @ changeset: 10:4dae8563d2c5
945 @ changeset: 10:4dae8563d2c5
946 |\ tag: tip
946 |\ tag: tip
947 | | parent: 9:7b35701b003e
947 | | parent: 9:7b35701b003e
948 | | parent: 4:88176d361b69
948 | | parent: 4:88176d361b69
949 | | user: test
949 | | user: test
950 | | date: Thu Jan 01 00:00:00 1970 +0000
950 | | date: Thu Jan 01 00:00:00 1970 +0000
951 | | summary: Last merge, related
951 | | summary: Last merge, related
952 | |
952 | |
953 | o changeset: 9:7b35701b003e
953 | o changeset: 9:7b35701b003e
954 | |\ parent: 8:e5416ad8a855
954 | |\ parent: 8:e5416ad8a855
955 | | | parent: 7:87fe3144dcfa
955 | | | parent: 7:87fe3144dcfa
956 | | | user: test
956 | | | user: test
957 | | | date: Thu Jan 01 00:00:00 1970 +0000
957 | | | date: Thu Jan 01 00:00:00 1970 +0000
958 | | | summary: First merge, related
958 | | | summary: First merge, related
959 | | |
959 | | |
960 | | o changeset: 8:e5416ad8a855
960 | | o changeset: 8:e5416ad8a855
961 | | | parent: 6:dc6c325fe5ee
961 | | | parent: 6:dc6c325fe5ee
962 | | | user: test
962 | | | user: test
963 | | | date: Thu Jan 01 00:00:00 1970 +0000
963 | | | date: Thu Jan 01 00:00:00 1970 +0000
964 | | | summary: change foo in branch, related
964 | | | summary: change foo in branch, related
965 | | |
965 | | |
966 | o | changeset: 7:87fe3144dcfa
966 | o | changeset: 7:87fe3144dcfa
967 | |/ user: test
967 | |/ user: test
968 | | date: Thu Jan 01 00:00:00 1970 +0000
968 | | date: Thu Jan 01 00:00:00 1970 +0000
969 | | summary: change foo, related
969 | | summary: change foo, related
970 | |
970 | |
971 | o changeset: 6:dc6c325fe5ee
971 | o changeset: 6:dc6c325fe5ee
972 | | user: test
972 | | user: test
973 | | date: Thu Jan 01 00:00:00 1970 +0000
973 | | date: Thu Jan 01 00:00:00 1970 +0000
974 | | summary: create foo, related
974 | | summary: create foo, related
975 | |
975 | |
976 | o changeset: 5:73db34516eb9
976 | o changeset: 5:73db34516eb9
977 | | parent: 0:e87515fd044a
977 | | parent: 0:e87515fd044a
978 | | user: test
978 | | user: test
979 | | date: Thu Jan 01 00:00:00 1970 +0000
979 | | date: Thu Jan 01 00:00:00 1970 +0000
980 | | summary: first branch, unrelated
980 | | summary: first branch, unrelated
981 | |
981 | |
982 o | changeset: 4:88176d361b69
982 o | changeset: 4:88176d361b69
983 | | user: test
983 | | user: test
984 | | date: Thu Jan 01 00:00:00 1970 +0000
984 | | date: Thu Jan 01 00:00:00 1970 +0000
985 | | summary: add foo, related
985 | | summary: add foo, related
986 | |
986 | |
987 o | changeset: 3:dd78ae4afb56
987 o | changeset: 3:dd78ae4afb56
988 | | user: test
988 | | user: test
989 | | date: Thu Jan 01 00:00:00 1970 +0000
989 | | date: Thu Jan 01 00:00:00 1970 +0000
990 | | summary: delete foo, unrelated
990 | | summary: delete foo, unrelated
991 | |
991 | |
992 o | changeset: 2:c4c64aedf0f7
992 o | changeset: 2:c4c64aedf0f7
993 | | user: test
993 | | user: test
994 | | date: Thu Jan 01 00:00:00 1970 +0000
994 | | date: Thu Jan 01 00:00:00 1970 +0000
995 | | summary: add unrelated old foo
995 | | summary: add unrelated old foo
996 | |
996 | |
997 o | changeset: 1:e5faa7440653
997 o | changeset: 1:e5faa7440653
998 |/ user: test
998 |/ user: test
999 | date: Thu Jan 01 00:00:00 1970 +0000
999 | date: Thu Jan 01 00:00:00 1970 +0000
1000 | summary: change, unrelated
1000 | summary: change, unrelated
1001 |
1001 |
1002 o changeset: 0:e87515fd044a
1002 o changeset: 0:e87515fd044a
1003 user: test
1003 user: test
1004 date: Thu Jan 01 00:00:00 1970 +0000
1004 date: Thu Jan 01 00:00:00 1970 +0000
1005 summary: init, unrelated
1005 summary: init, unrelated
1006
1006
1007
1007
1008 $ hg --traceback log -f foo
1008 $ hg --traceback log -f foo
1009 changeset: 10:4dae8563d2c5
1009 changeset: 10:4dae8563d2c5
1010 tag: tip
1010 tag: tip
1011 parent: 9:7b35701b003e
1011 parent: 9:7b35701b003e
1012 parent: 4:88176d361b69
1012 parent: 4:88176d361b69
1013 user: test
1013 user: test
1014 date: Thu Jan 01 00:00:00 1970 +0000
1014 date: Thu Jan 01 00:00:00 1970 +0000
1015 summary: Last merge, related
1015 summary: Last merge, related
1016
1016
1017 changeset: 9:7b35701b003e
1017 changeset: 9:7b35701b003e
1018 parent: 8:e5416ad8a855
1018 parent: 8:e5416ad8a855
1019 parent: 7:87fe3144dcfa
1019 parent: 7:87fe3144dcfa
1020 user: test
1020 user: test
1021 date: Thu Jan 01 00:00:00 1970 +0000
1021 date: Thu Jan 01 00:00:00 1970 +0000
1022 summary: First merge, related
1022 summary: First merge, related
1023
1023
1024 changeset: 8:e5416ad8a855
1024 changeset: 8:e5416ad8a855
1025 parent: 6:dc6c325fe5ee
1025 parent: 6:dc6c325fe5ee
1026 user: test
1026 user: test
1027 date: Thu Jan 01 00:00:00 1970 +0000
1027 date: Thu Jan 01 00:00:00 1970 +0000
1028 summary: change foo in branch, related
1028 summary: change foo in branch, related
1029
1029
1030 changeset: 7:87fe3144dcfa
1030 changeset: 7:87fe3144dcfa
1031 user: test
1031 user: test
1032 date: Thu Jan 01 00:00:00 1970 +0000
1032 date: Thu Jan 01 00:00:00 1970 +0000
1033 summary: change foo, related
1033 summary: change foo, related
1034
1034
1035 changeset: 6:dc6c325fe5ee
1035 changeset: 6:dc6c325fe5ee
1036 user: test
1036 user: test
1037 date: Thu Jan 01 00:00:00 1970 +0000
1037 date: Thu Jan 01 00:00:00 1970 +0000
1038 summary: create foo, related
1038 summary: create foo, related
1039
1039
1040 changeset: 4:88176d361b69
1040 changeset: 4:88176d361b69
1041 user: test
1041 user: test
1042 date: Thu Jan 01 00:00:00 1970 +0000
1042 date: Thu Jan 01 00:00:00 1970 +0000
1043 summary: add foo, related
1043 summary: add foo, related
1044
1044
1045
1045
1046 Also check when maxrev < lastrevfilelog
1046 Also check when maxrev < lastrevfilelog
1047
1047
1048 $ hg --traceback log -f -r4 foo
1048 $ hg --traceback log -f -r4 foo
1049 changeset: 4:88176d361b69
1049 changeset: 4:88176d361b69
1050 user: test
1050 user: test
1051 date: Thu Jan 01 00:00:00 1970 +0000
1051 date: Thu Jan 01 00:00:00 1970 +0000
1052 summary: add foo, related
1052 summary: add foo, related
1053
1053
1054 $ cd ..
1054 $ cd ..
1055
1055
1056 Issue2383: hg log showing _less_ differences than hg diff
1056 Issue2383: hg log showing _less_ differences than hg diff
1057
1057
1058 $ hg init issue2383
1058 $ hg init issue2383
1059 $ cd issue2383
1059 $ cd issue2383
1060
1060
1061 Create a test repo:
1061 Create a test repo:
1062
1062
1063 $ echo a > a
1063 $ echo a > a
1064 $ hg ci -Am0
1064 $ hg ci -Am0
1065 adding a
1065 adding a
1066 $ echo b > b
1066 $ echo b > b
1067 $ hg ci -Am1
1067 $ hg ci -Am1
1068 adding b
1068 adding b
1069 $ hg co 0
1069 $ hg co 0
1070 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1070 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1071 $ echo b > a
1071 $ echo b > a
1072 $ hg ci -m2
1072 $ hg ci -m2
1073 created new head
1073 created new head
1074
1074
1075 Merge:
1075 Merge:
1076
1076
1077 $ hg merge
1077 $ hg merge
1078 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1078 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1079 (branch merge, don't forget to commit)
1079 (branch merge, don't forget to commit)
1080
1080
1081 Make sure there's a file listed in the merge to trigger the bug:
1081 Make sure there's a file listed in the merge to trigger the bug:
1082
1082
1083 $ echo c > a
1083 $ echo c > a
1084 $ hg ci -m3
1084 $ hg ci -m3
1085
1085
1086 Two files shown here in diff:
1086 Two files shown here in diff:
1087
1087
1088 $ hg diff --rev 2:3
1088 $ hg diff --rev 2:3
1089 diff -r b09be438c43a -r 8e07aafe1edc a
1089 diff -r b09be438c43a -r 8e07aafe1edc a
1090 --- a/a Thu Jan 01 00:00:00 1970 +0000
1090 --- a/a Thu Jan 01 00:00:00 1970 +0000
1091 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1091 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1092 @@ -1,1 +1,1 @@
1092 @@ -1,1 +1,1 @@
1093 -b
1093 -b
1094 +c
1094 +c
1095 diff -r b09be438c43a -r 8e07aafe1edc b
1095 diff -r b09be438c43a -r 8e07aafe1edc b
1096 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1096 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1097 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1097 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1098 @@ -0,0 +1,1 @@
1098 @@ -0,0 +1,1 @@
1099 +b
1099 +b
1100
1100
1101 Diff here should be the same:
1101 Diff here should be the same:
1102
1102
1103 $ hg log -vpr 3
1103 $ hg log -vpr 3
1104 changeset: 3:8e07aafe1edc
1104 changeset: 3:8e07aafe1edc
1105 tag: tip
1105 tag: tip
1106 parent: 2:b09be438c43a
1106 parent: 2:b09be438c43a
1107 parent: 1:925d80f479bb
1107 parent: 1:925d80f479bb
1108 user: test
1108 user: test
1109 date: Thu Jan 01 00:00:00 1970 +0000
1109 date: Thu Jan 01 00:00:00 1970 +0000
1110 files: a
1110 files: a
1111 description:
1111 description:
1112 3
1112 3
1113
1113
1114
1114
1115 diff -r b09be438c43a -r 8e07aafe1edc a
1115 diff -r b09be438c43a -r 8e07aafe1edc a
1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1118 @@ -1,1 +1,1 @@
1118 @@ -1,1 +1,1 @@
1119 -b
1119 -b
1120 +c
1120 +c
1121 diff -r b09be438c43a -r 8e07aafe1edc b
1121 diff -r b09be438c43a -r 8e07aafe1edc b
1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1124 @@ -0,0 +1,1 @@
1124 @@ -0,0 +1,1 @@
1125 +b
1125 +b
1126
1126
1127 $ cd ..
1127 $ cd ..
1128
1128
1129 'hg log -r rev fn' when last(filelog(fn)) != rev
1129 'hg log -r rev fn' when last(filelog(fn)) != rev
1130
1130
1131 $ hg init simplelog
1131 $ hg init simplelog
1132 $ cd simplelog
1132 $ cd simplelog
1133 $ echo f > a
1133 $ echo f > a
1134 $ hg ci -Am'a' -d '0 0'
1134 $ hg ci -Am'a' -d '0 0'
1135 adding a
1135 adding a
1136 $ echo f >> a
1136 $ echo f >> a
1137 $ hg ci -Am'a bis' -d '1 0'
1137 $ hg ci -Am'a bis' -d '1 0'
1138
1138
1139 $ hg log -r0 a
1139 $ hg log -r0 a
1140 changeset: 0:9f758d63dcde
1140 changeset: 0:9f758d63dcde
1141 user: test
1141 user: test
1142 date: Thu Jan 01 00:00:00 1970 +0000
1142 date: Thu Jan 01 00:00:00 1970 +0000
1143 summary: a
1143 summary: a
1144
1144
1145 $ cat > $HGTMP/testhidden.py << EOF
1145 $ cat > $HGTMP/testhidden.py << EOF
1146 > def reposetup(ui, repo):
1146 > def reposetup(ui, repo):
1147 > for line in repo.opener('hidden'):
1147 > for line in repo.opener('hidden'):
1148 > ctx = repo[line.strip()]
1148 > ctx = repo[line.strip()]
1149 > repo.hiddenrevs.add(ctx.rev())
1149 > repo.hiddenrevs.add(ctx.rev())
1150 > EOF
1150 > EOF
1151 $ echo '[extensions]' >> $HGRCPATH
1151 $ echo '[extensions]' >> $HGRCPATH
1152 $ echo "hidden=$HGTMP/testhidden.py" >> $HGRCPATH
1152 $ echo "hidden=$HGTMP/testhidden.py" >> $HGRCPATH
1153 $ touch .hg/hidden
1153 $ touch .hg/hidden
1154 $ hg log --template='{rev}:{node}\n'
1154 $ hg log --template='{rev}:{node}\n'
1155 1:a765632148dc55d38c35c4f247c618701886cb2f
1155 1:a765632148dc55d38c35c4f247c618701886cb2f
1156 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1156 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1157 $ echo a765632148dc55d38c35c4f247c618701886cb2f > .hg/hidden
1157 $ echo a765632148dc55d38c35c4f247c618701886cb2f > .hg/hidden
1158 $ hg log --template='{rev}:{node}\n'
1158 $ hg log --template='{rev}:{node}\n'
1159 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1159 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1160 $ hg log --template='{rev}:{node}\n' --hidden
1160 $ hg log --template='{rev}:{node}\n' --hidden
1161 1:a765632148dc55d38c35c4f247c618701886cb2f
1161 1:a765632148dc55d38c35c4f247c618701886cb2f
1162 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1162 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1163
1163
1164 clear extensions configuration
1164 clear extensions configuration
1165 $ echo '[extensions]' >> $HGRCPATH
1165 $ echo '[extensions]' >> $HGRCPATH
1166 $ echo "hidden=!" >> $HGRCPATH
1166 $ echo "hidden=!" >> $HGRCPATH
1167 $ cd ..
1167 $ cd ..
1168
1168
1169 test -u/-k for problematic encoding
1169 test -u/-k for problematic encoding
1170 # unicode: cp932:
1170 # unicode: cp932:
1171 # u30A2 0x83 0x41(= 'A')
1171 # u30A2 0x83 0x41(= 'A')
1172 # u30C2 0x83 0x61(= 'a')
1172 # u30C2 0x83 0x61(= 'a')
1173
1173
1174 $ hg init problematicencoding
1174 $ hg init problematicencoding
1175 $ cd problematicencoding
1175 $ cd problematicencoding
1176
1176
1177 $ python > setup.sh <<EOF
1177 $ python > setup.sh <<EOF
1178 > print u'''
1178 > print u'''
1179 > echo a > text
1179 > echo a > text
1180 > hg add text
1180 > hg add text
1181 > hg --encoding utf-8 commit -u '\u30A2' -m none
1181 > hg --encoding utf-8 commit -u '\u30A2' -m none
1182 > echo b > text
1182 > echo b > text
1183 > hg --encoding utf-8 commit -u '\u30C2' -m none
1183 > hg --encoding utf-8 commit -u '\u30C2' -m none
1184 > echo c > text
1184 > echo c > text
1185 > hg --encoding utf-8 commit -u none -m '\u30A2'
1185 > hg --encoding utf-8 commit -u none -m '\u30A2'
1186 > echo d > text
1186 > echo d > text
1187 > hg --encoding utf-8 commit -u none -m '\u30C2'
1187 > hg --encoding utf-8 commit -u none -m '\u30C2'
1188 > '''.encode('utf-8')
1188 > '''.encode('utf-8')
1189 > EOF
1189 > EOF
1190 $ sh < setup.sh
1190 $ sh < setup.sh
1191
1191
1192 test in problematic encoding
1192 test in problematic encoding
1193 $ python > test.sh <<EOF
1193 $ python > test.sh <<EOF
1194 > print u'''
1194 > print u'''
1195 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1195 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1196 > echo ====
1196 > echo ====
1197 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1197 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1198 > echo ====
1198 > echo ====
1199 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1199 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1200 > echo ====
1200 > echo ====
1201 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1201 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1202 > '''.encode('cp932')
1202 > '''.encode('cp932')
1203 > EOF
1203 > EOF
1204 $ sh < test.sh
1204 $ sh < test.sh
1205 0
1205 0
1206 ====
1206 ====
1207 1
1207 1
1208 ====
1208 ====
1209 2
1209 2
1210 0
1210 0
1211 ====
1211 ====
1212 3
1212 3
1213 1
1213 1
1214
1214
1215 $ cd ..
1215 $ cd ..
1216
1217 test hg log on non-existent files and on directories
1218 $ hg init issue1340
1219 $ cd issue1340
1220 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1221 $ echo 1 > d1/f1
1222 $ echo 1 > D2/f1
1223 $ echo 1 > D3.i/f1
1224 $ echo 1 > d4.hg/f1
1225 $ echo 1 > d5.d/f1
1226 $ echo 1 > .d6/f1
1227 $ hg add .
1228 adding .d6/f1
1229 adding D2/f1
1230 adding D3.i/f1
1231 adding d1/f1
1232 adding d4.hg/f1
1233 adding d5.d/f1
1234 $ hg commit -m "a bunch of weird directories"
1235 $ hg log -l1 d1/f1 | grep changeset
1236 changeset: 0:65624cd9070a
1237 $ hg log -l1 f1
1238 $ hg log -l1 . | grep changeset
1239 changeset: 0:65624cd9070a
1240 $ hg log -l1 ./ | grep changeset
1241 changeset: 0:65624cd9070a
1242 $ hg log -l1 d1 | grep changeset
1243 changeset: 0:65624cd9070a
1244 $ hg log -l1 D2 | grep changeset
1245 changeset: 0:65624cd9070a
1246 $ hg log -l1 D2/f1 | grep changeset
1247 changeset: 0:65624cd9070a
1248 $ hg log -l1 D3.i | grep changeset
1249 changeset: 0:65624cd9070a
1250 $ hg log -l1 D3.i/f1 | grep changeset
1251 changeset: 0:65624cd9070a
1252 $ hg log -l1 d4.hg | grep changeset
1253 changeset: 0:65624cd9070a
1254 $ hg log -l1 d4.hg/f1 | grep changeset
1255 changeset: 0:65624cd9070a
1256 $ hg log -l1 d5.d | grep changeset
1257 changeset: 0:65624cd9070a
1258 $ hg log -l1 d5.d/f1 | grep changeset
1259 changeset: 0:65624cd9070a
1260 $ hg log -l1 .d6 | grep changeset
1261 changeset: 0:65624cd9070a
1262 $ hg log -l1 .d6/f1 | grep changeset
1263 changeset: 0:65624cd9070a
1264 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now