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