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