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