##// END OF EJS Templates
jsonchangeset: set manifest node to "null" for workingctx...
Yuya Nishihara -
r24603:e74f819e default
parent child Browse files
Show More
@@ -1,3255 +1,3259
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 and rev is not None:
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 rev = ctx.rev()
1263 rev = ctx.rev()
1264 if rev is None:
1264 if rev is None:
1265 jrev = jnode = 'null'
1265 jrev = jnode = 'null'
1266 else:
1266 else:
1267 jrev = str(rev)
1267 jrev = str(rev)
1268 jnode = '"%s"' % hex(ctx.node())
1268 jnode = '"%s"' % hex(ctx.node())
1269 j = encoding.jsonescape
1269 j = encoding.jsonescape
1270
1270
1271 if self._first:
1271 if self._first:
1272 self.ui.write("[\n {")
1272 self.ui.write("[\n {")
1273 self._first = False
1273 self._first = False
1274 else:
1274 else:
1275 self.ui.write(",\n {")
1275 self.ui.write(",\n {")
1276
1276
1277 if self.ui.quiet:
1277 if self.ui.quiet:
1278 self.ui.write('\n "rev": %s' % jrev)
1278 self.ui.write('\n "rev": %s' % jrev)
1279 self.ui.write(',\n "node": %s' % jnode)
1279 self.ui.write(',\n "node": %s' % jnode)
1280 self.ui.write('\n }')
1280 self.ui.write('\n }')
1281 return
1281 return
1282
1282
1283 self.ui.write('\n "rev": %s' % jrev)
1283 self.ui.write('\n "rev": %s' % jrev)
1284 self.ui.write(',\n "node": %s' % jnode)
1284 self.ui.write(',\n "node": %s' % jnode)
1285 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1285 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1286 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1286 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1287 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1287 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1288 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1288 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1289 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1289 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1290
1290
1291 self.ui.write(',\n "bookmarks": [%s]' %
1291 self.ui.write(',\n "bookmarks": [%s]' %
1292 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1292 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1293 self.ui.write(',\n "tags": [%s]' %
1293 self.ui.write(',\n "tags": [%s]' %
1294 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1294 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1295 self.ui.write(',\n "parents": [%s]' %
1295 self.ui.write(',\n "parents": [%s]' %
1296 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1296 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1297
1297
1298 if self.ui.debugflag:
1298 if self.ui.debugflag:
1299 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1299 if rev is None:
1300 jmanifestnode = 'null'
1301 else:
1302 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1303 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1300
1304
1301 self.ui.write(',\n "extra": {%s}' %
1305 self.ui.write(',\n "extra": {%s}' %
1302 ", ".join('"%s": "%s"' % (j(k), j(v))
1306 ", ".join('"%s": "%s"' % (j(k), j(v))
1303 for k, v in ctx.extra().items()))
1307 for k, v in ctx.extra().items()))
1304
1308
1305 files = ctx.p1().status(ctx)
1309 files = ctx.p1().status(ctx)
1306 self.ui.write(',\n "modified": [%s]' %
1310 self.ui.write(',\n "modified": [%s]' %
1307 ", ".join('"%s"' % j(f) for f in files[0]))
1311 ", ".join('"%s"' % j(f) for f in files[0]))
1308 self.ui.write(',\n "added": [%s]' %
1312 self.ui.write(',\n "added": [%s]' %
1309 ", ".join('"%s"' % j(f) for f in files[1]))
1313 ", ".join('"%s"' % j(f) for f in files[1]))
1310 self.ui.write(',\n "removed": [%s]' %
1314 self.ui.write(',\n "removed": [%s]' %
1311 ", ".join('"%s"' % j(f) for f in files[2]))
1315 ", ".join('"%s"' % j(f) for f in files[2]))
1312
1316
1313 elif self.ui.verbose:
1317 elif self.ui.verbose:
1314 self.ui.write(',\n "files": [%s]' %
1318 self.ui.write(',\n "files": [%s]' %
1315 ", ".join('"%s"' % j(f) for f in ctx.files()))
1319 ", ".join('"%s"' % j(f) for f in ctx.files()))
1316
1320
1317 if copies:
1321 if copies:
1318 self.ui.write(',\n "copies": {%s}' %
1322 self.ui.write(',\n "copies": {%s}' %
1319 ", ".join('"%s": "%s"' % (j(k), j(v))
1323 ", ".join('"%s": "%s"' % (j(k), j(v))
1320 for k, v in copies))
1324 for k, v in copies))
1321
1325
1322 matchfn = self.matchfn
1326 matchfn = self.matchfn
1323 if matchfn:
1327 if matchfn:
1324 stat = self.diffopts.get('stat')
1328 stat = self.diffopts.get('stat')
1325 diff = self.diffopts.get('patch')
1329 diff = self.diffopts.get('patch')
1326 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1330 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1327 node, prev = ctx.node(), ctx.p1().node()
1331 node, prev = ctx.node(), ctx.p1().node()
1328 if stat:
1332 if stat:
1329 self.ui.pushbuffer()
1333 self.ui.pushbuffer()
1330 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1334 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1331 match=matchfn, stat=True)
1335 match=matchfn, stat=True)
1332 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1336 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1333 if diff:
1337 if diff:
1334 self.ui.pushbuffer()
1338 self.ui.pushbuffer()
1335 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1339 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1336 match=matchfn, stat=False)
1340 match=matchfn, stat=False)
1337 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1341 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1338
1342
1339 self.ui.write("\n }")
1343 self.ui.write("\n }")
1340
1344
1341 class changeset_templater(changeset_printer):
1345 class changeset_templater(changeset_printer):
1342 '''format changeset information.'''
1346 '''format changeset information.'''
1343
1347
1344 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1348 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1345 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1349 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1346 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1350 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1347 defaulttempl = {
1351 defaulttempl = {
1348 'parent': '{rev}:{node|formatnode} ',
1352 'parent': '{rev}:{node|formatnode} ',
1349 'manifest': '{rev}:{node|formatnode}',
1353 'manifest': '{rev}:{node|formatnode}',
1350 'file_copy': '{name} ({source})',
1354 'file_copy': '{name} ({source})',
1351 'extra': '{key}={value|stringescape}'
1355 'extra': '{key}={value|stringescape}'
1352 }
1356 }
1353 # filecopy is preserved for compatibility reasons
1357 # filecopy is preserved for compatibility reasons
1354 defaulttempl['filecopy'] = defaulttempl['file_copy']
1358 defaulttempl['filecopy'] = defaulttempl['file_copy']
1355 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1359 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1356 cache=defaulttempl)
1360 cache=defaulttempl)
1357 if tmpl:
1361 if tmpl:
1358 self.t.cache['changeset'] = tmpl
1362 self.t.cache['changeset'] = tmpl
1359
1363
1360 self.cache = {}
1364 self.cache = {}
1361
1365
1362 def _show(self, ctx, copies, matchfn, props):
1366 def _show(self, ctx, copies, matchfn, props):
1363 '''show a single changeset or file revision'''
1367 '''show a single changeset or file revision'''
1364
1368
1365 showlist = templatekw.showlist
1369 showlist = templatekw.showlist
1366
1370
1367 # showparents() behaviour depends on ui trace level which
1371 # showparents() behaviour depends on ui trace level which
1368 # causes unexpected behaviours at templating level and makes
1372 # causes unexpected behaviours at templating level and makes
1369 # it harder to extract it in a standalone function. Its
1373 # it harder to extract it in a standalone function. Its
1370 # behaviour cannot be changed so leave it here for now.
1374 # behaviour cannot be changed so leave it here for now.
1371 def showparents(**args):
1375 def showparents(**args):
1372 ctx = args['ctx']
1376 ctx = args['ctx']
1373 parents = [[('rev', p.rev()),
1377 parents = [[('rev', p.rev()),
1374 ('node', p.hex()),
1378 ('node', p.hex()),
1375 ('phase', p.phasestr())]
1379 ('phase', p.phasestr())]
1376 for p in self._meaningful_parentrevs(ctx)]
1380 for p in self._meaningful_parentrevs(ctx)]
1377 return showlist('parent', parents, **args)
1381 return showlist('parent', parents, **args)
1378
1382
1379 props = props.copy()
1383 props = props.copy()
1380 props.update(templatekw.keywords)
1384 props.update(templatekw.keywords)
1381 props['parents'] = showparents
1385 props['parents'] = showparents
1382 props['templ'] = self.t
1386 props['templ'] = self.t
1383 props['ctx'] = ctx
1387 props['ctx'] = ctx
1384 props['repo'] = self.repo
1388 props['repo'] = self.repo
1385 props['revcache'] = {'copies': copies}
1389 props['revcache'] = {'copies': copies}
1386 props['cache'] = self.cache
1390 props['cache'] = self.cache
1387
1391
1388 # find correct templates for current mode
1392 # find correct templates for current mode
1389
1393
1390 tmplmodes = [
1394 tmplmodes = [
1391 (True, None),
1395 (True, None),
1392 (self.ui.verbose, 'verbose'),
1396 (self.ui.verbose, 'verbose'),
1393 (self.ui.quiet, 'quiet'),
1397 (self.ui.quiet, 'quiet'),
1394 (self.ui.debugflag, 'debug'),
1398 (self.ui.debugflag, 'debug'),
1395 ]
1399 ]
1396
1400
1397 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1401 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1398 for mode, postfix in tmplmodes:
1402 for mode, postfix in tmplmodes:
1399 for type in types:
1403 for type in types:
1400 cur = postfix and ('%s_%s' % (type, postfix)) or type
1404 cur = postfix and ('%s_%s' % (type, postfix)) or type
1401 if mode and cur in self.t:
1405 if mode and cur in self.t:
1402 types[type] = cur
1406 types[type] = cur
1403
1407
1404 try:
1408 try:
1405
1409
1406 # write header
1410 # write header
1407 if types['header']:
1411 if types['header']:
1408 h = templater.stringify(self.t(types['header'], **props))
1412 h = templater.stringify(self.t(types['header'], **props))
1409 if self.buffered:
1413 if self.buffered:
1410 self.header[ctx.rev()] = h
1414 self.header[ctx.rev()] = h
1411 else:
1415 else:
1412 if self.lastheader != h:
1416 if self.lastheader != h:
1413 self.lastheader = h
1417 self.lastheader = h
1414 self.ui.write(h)
1418 self.ui.write(h)
1415
1419
1416 # write changeset metadata, then patch if requested
1420 # write changeset metadata, then patch if requested
1417 key = types['changeset']
1421 key = types['changeset']
1418 self.ui.write(templater.stringify(self.t(key, **props)))
1422 self.ui.write(templater.stringify(self.t(key, **props)))
1419 self.showpatch(ctx.node(), matchfn)
1423 self.showpatch(ctx.node(), matchfn)
1420
1424
1421 if types['footer']:
1425 if types['footer']:
1422 if not self.footer:
1426 if not self.footer:
1423 self.footer = templater.stringify(self.t(types['footer'],
1427 self.footer = templater.stringify(self.t(types['footer'],
1424 **props))
1428 **props))
1425
1429
1426 except KeyError, inst:
1430 except KeyError, inst:
1427 msg = _("%s: no key named '%s'")
1431 msg = _("%s: no key named '%s'")
1428 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1432 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1429 except SyntaxError, inst:
1433 except SyntaxError, inst:
1430 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1434 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1431
1435
1432 def gettemplate(ui, tmpl, style):
1436 def gettemplate(ui, tmpl, style):
1433 """
1437 """
1434 Find the template matching the given template spec or style.
1438 Find the template matching the given template spec or style.
1435 """
1439 """
1436
1440
1437 # ui settings
1441 # ui settings
1438 if not tmpl and not style: # template are stronger than style
1442 if not tmpl and not style: # template are stronger than style
1439 tmpl = ui.config('ui', 'logtemplate')
1443 tmpl = ui.config('ui', 'logtemplate')
1440 if tmpl:
1444 if tmpl:
1441 try:
1445 try:
1442 tmpl = templater.parsestring(tmpl)
1446 tmpl = templater.parsestring(tmpl)
1443 except SyntaxError:
1447 except SyntaxError:
1444 tmpl = templater.parsestring(tmpl, quoted=False)
1448 tmpl = templater.parsestring(tmpl, quoted=False)
1445 return tmpl, None
1449 return tmpl, None
1446 else:
1450 else:
1447 style = util.expandpath(ui.config('ui', 'style', ''))
1451 style = util.expandpath(ui.config('ui', 'style', ''))
1448
1452
1449 if not tmpl and style:
1453 if not tmpl and style:
1450 mapfile = style
1454 mapfile = style
1451 if not os.path.split(mapfile)[0]:
1455 if not os.path.split(mapfile)[0]:
1452 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1456 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1453 or templater.templatepath(mapfile))
1457 or templater.templatepath(mapfile))
1454 if mapname:
1458 if mapname:
1455 mapfile = mapname
1459 mapfile = mapname
1456 return None, mapfile
1460 return None, mapfile
1457
1461
1458 if not tmpl:
1462 if not tmpl:
1459 return None, None
1463 return None, None
1460
1464
1461 # looks like a literal template?
1465 # looks like a literal template?
1462 if '{' in tmpl:
1466 if '{' in tmpl:
1463 return tmpl, None
1467 return tmpl, None
1464
1468
1465 # perhaps a stock style?
1469 # perhaps a stock style?
1466 if not os.path.split(tmpl)[0]:
1470 if not os.path.split(tmpl)[0]:
1467 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1471 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1468 or templater.templatepath(tmpl))
1472 or templater.templatepath(tmpl))
1469 if mapname and os.path.isfile(mapname):
1473 if mapname and os.path.isfile(mapname):
1470 return None, mapname
1474 return None, mapname
1471
1475
1472 # perhaps it's a reference to [templates]
1476 # perhaps it's a reference to [templates]
1473 t = ui.config('templates', tmpl)
1477 t = ui.config('templates', tmpl)
1474 if t:
1478 if t:
1475 try:
1479 try:
1476 tmpl = templater.parsestring(t)
1480 tmpl = templater.parsestring(t)
1477 except SyntaxError:
1481 except SyntaxError:
1478 tmpl = templater.parsestring(t, quoted=False)
1482 tmpl = templater.parsestring(t, quoted=False)
1479 return tmpl, None
1483 return tmpl, None
1480
1484
1481 if tmpl == 'list':
1485 if tmpl == 'list':
1482 ui.write(_("available styles: %s\n") % templater.stylelist())
1486 ui.write(_("available styles: %s\n") % templater.stylelist())
1483 raise util.Abort(_("specify a template"))
1487 raise util.Abort(_("specify a template"))
1484
1488
1485 # perhaps it's a path to a map or a template
1489 # perhaps it's a path to a map or a template
1486 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1490 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1487 # is it a mapfile for a style?
1491 # is it a mapfile for a style?
1488 if os.path.basename(tmpl).startswith("map-"):
1492 if os.path.basename(tmpl).startswith("map-"):
1489 return None, os.path.realpath(tmpl)
1493 return None, os.path.realpath(tmpl)
1490 tmpl = open(tmpl).read()
1494 tmpl = open(tmpl).read()
1491 return tmpl, None
1495 return tmpl, None
1492
1496
1493 # constant string?
1497 # constant string?
1494 return tmpl, None
1498 return tmpl, None
1495
1499
1496 def show_changeset(ui, repo, opts, buffered=False):
1500 def show_changeset(ui, repo, opts, buffered=False):
1497 """show one changeset using template or regular display.
1501 """show one changeset using template or regular display.
1498
1502
1499 Display format will be the first non-empty hit of:
1503 Display format will be the first non-empty hit of:
1500 1. option 'template'
1504 1. option 'template'
1501 2. option 'style'
1505 2. option 'style'
1502 3. [ui] setting 'logtemplate'
1506 3. [ui] setting 'logtemplate'
1503 4. [ui] setting 'style'
1507 4. [ui] setting 'style'
1504 If all of these values are either the unset or the empty string,
1508 If all of these values are either the unset or the empty string,
1505 regular display via changeset_printer() is done.
1509 regular display via changeset_printer() is done.
1506 """
1510 """
1507 # options
1511 # options
1508 matchfn = None
1512 matchfn = None
1509 if opts.get('patch') or opts.get('stat'):
1513 if opts.get('patch') or opts.get('stat'):
1510 matchfn = scmutil.matchall(repo)
1514 matchfn = scmutil.matchall(repo)
1511
1515
1512 if opts.get('template') == 'json':
1516 if opts.get('template') == 'json':
1513 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1517 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1514
1518
1515 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1519 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1516
1520
1517 if not tmpl and not mapfile:
1521 if not tmpl and not mapfile:
1518 return changeset_printer(ui, repo, matchfn, opts, buffered)
1522 return changeset_printer(ui, repo, matchfn, opts, buffered)
1519
1523
1520 try:
1524 try:
1521 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1525 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1522 buffered)
1526 buffered)
1523 except SyntaxError, inst:
1527 except SyntaxError, inst:
1524 raise util.Abort(inst.args[0])
1528 raise util.Abort(inst.args[0])
1525 return t
1529 return t
1526
1530
1527 def showmarker(ui, marker):
1531 def showmarker(ui, marker):
1528 """utility function to display obsolescence marker in a readable way
1532 """utility function to display obsolescence marker in a readable way
1529
1533
1530 To be used by debug function."""
1534 To be used by debug function."""
1531 ui.write(hex(marker.precnode()))
1535 ui.write(hex(marker.precnode()))
1532 for repl in marker.succnodes():
1536 for repl in marker.succnodes():
1533 ui.write(' ')
1537 ui.write(' ')
1534 ui.write(hex(repl))
1538 ui.write(hex(repl))
1535 ui.write(' %X ' % marker.flags())
1539 ui.write(' %X ' % marker.flags())
1536 parents = marker.parentnodes()
1540 parents = marker.parentnodes()
1537 if parents is not None:
1541 if parents is not None:
1538 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1542 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1539 ui.write('(%s) ' % util.datestr(marker.date()))
1543 ui.write('(%s) ' % util.datestr(marker.date()))
1540 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1544 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1541 sorted(marker.metadata().items())
1545 sorted(marker.metadata().items())
1542 if t[0] != 'date')))
1546 if t[0] != 'date')))
1543 ui.write('\n')
1547 ui.write('\n')
1544
1548
1545 def finddate(ui, repo, date):
1549 def finddate(ui, repo, date):
1546 """Find the tipmost changeset that matches the given date spec"""
1550 """Find the tipmost changeset that matches the given date spec"""
1547
1551
1548 df = util.matchdate(date)
1552 df = util.matchdate(date)
1549 m = scmutil.matchall(repo)
1553 m = scmutil.matchall(repo)
1550 results = {}
1554 results = {}
1551
1555
1552 def prep(ctx, fns):
1556 def prep(ctx, fns):
1553 d = ctx.date()
1557 d = ctx.date()
1554 if df(d[0]):
1558 if df(d[0]):
1555 results[ctx.rev()] = d
1559 results[ctx.rev()] = d
1556
1560
1557 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1561 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1558 rev = ctx.rev()
1562 rev = ctx.rev()
1559 if rev in results:
1563 if rev in results:
1560 ui.status(_("found revision %s from %s\n") %
1564 ui.status(_("found revision %s from %s\n") %
1561 (rev, util.datestr(results[rev])))
1565 (rev, util.datestr(results[rev])))
1562 return str(rev)
1566 return str(rev)
1563
1567
1564 raise util.Abort(_("revision matching date not found"))
1568 raise util.Abort(_("revision matching date not found"))
1565
1569
1566 def increasingwindows(windowsize=8, sizelimit=512):
1570 def increasingwindows(windowsize=8, sizelimit=512):
1567 while True:
1571 while True:
1568 yield windowsize
1572 yield windowsize
1569 if windowsize < sizelimit:
1573 if windowsize < sizelimit:
1570 windowsize *= 2
1574 windowsize *= 2
1571
1575
1572 class FileWalkError(Exception):
1576 class FileWalkError(Exception):
1573 pass
1577 pass
1574
1578
1575 def walkfilerevs(repo, match, follow, revs, fncache):
1579 def walkfilerevs(repo, match, follow, revs, fncache):
1576 '''Walks the file history for the matched files.
1580 '''Walks the file history for the matched files.
1577
1581
1578 Returns the changeset revs that are involved in the file history.
1582 Returns the changeset revs that are involved in the file history.
1579
1583
1580 Throws FileWalkError if the file history can't be walked using
1584 Throws FileWalkError if the file history can't be walked using
1581 filelogs alone.
1585 filelogs alone.
1582 '''
1586 '''
1583 wanted = set()
1587 wanted = set()
1584 copies = []
1588 copies = []
1585 minrev, maxrev = min(revs), max(revs)
1589 minrev, maxrev = min(revs), max(revs)
1586 def filerevgen(filelog, last):
1590 def filerevgen(filelog, last):
1587 """
1591 """
1588 Only files, no patterns. Check the history of each file.
1592 Only files, no patterns. Check the history of each file.
1589
1593
1590 Examines filelog entries within minrev, maxrev linkrev range
1594 Examines filelog entries within minrev, maxrev linkrev range
1591 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1595 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1592 tuples in backwards order
1596 tuples in backwards order
1593 """
1597 """
1594 cl_count = len(repo)
1598 cl_count = len(repo)
1595 revs = []
1599 revs = []
1596 for j in xrange(0, last + 1):
1600 for j in xrange(0, last + 1):
1597 linkrev = filelog.linkrev(j)
1601 linkrev = filelog.linkrev(j)
1598 if linkrev < minrev:
1602 if linkrev < minrev:
1599 continue
1603 continue
1600 # only yield rev for which we have the changelog, it can
1604 # only yield rev for which we have the changelog, it can
1601 # happen while doing "hg log" during a pull or commit
1605 # happen while doing "hg log" during a pull or commit
1602 if linkrev >= cl_count:
1606 if linkrev >= cl_count:
1603 break
1607 break
1604
1608
1605 parentlinkrevs = []
1609 parentlinkrevs = []
1606 for p in filelog.parentrevs(j):
1610 for p in filelog.parentrevs(j):
1607 if p != nullrev:
1611 if p != nullrev:
1608 parentlinkrevs.append(filelog.linkrev(p))
1612 parentlinkrevs.append(filelog.linkrev(p))
1609 n = filelog.node(j)
1613 n = filelog.node(j)
1610 revs.append((linkrev, parentlinkrevs,
1614 revs.append((linkrev, parentlinkrevs,
1611 follow and filelog.renamed(n)))
1615 follow and filelog.renamed(n)))
1612
1616
1613 return reversed(revs)
1617 return reversed(revs)
1614 def iterfiles():
1618 def iterfiles():
1615 pctx = repo['.']
1619 pctx = repo['.']
1616 for filename in match.files():
1620 for filename in match.files():
1617 if follow:
1621 if follow:
1618 if filename not in pctx:
1622 if filename not in pctx:
1619 raise util.Abort(_('cannot follow file not in parent '
1623 raise util.Abort(_('cannot follow file not in parent '
1620 'revision: "%s"') % filename)
1624 'revision: "%s"') % filename)
1621 yield filename, pctx[filename].filenode()
1625 yield filename, pctx[filename].filenode()
1622 else:
1626 else:
1623 yield filename, None
1627 yield filename, None
1624 for filename_node in copies:
1628 for filename_node in copies:
1625 yield filename_node
1629 yield filename_node
1626
1630
1627 for file_, node in iterfiles():
1631 for file_, node in iterfiles():
1628 filelog = repo.file(file_)
1632 filelog = repo.file(file_)
1629 if not len(filelog):
1633 if not len(filelog):
1630 if node is None:
1634 if node is None:
1631 # A zero count may be a directory or deleted file, so
1635 # A zero count may be a directory or deleted file, so
1632 # try to find matching entries on the slow path.
1636 # try to find matching entries on the slow path.
1633 if follow:
1637 if follow:
1634 raise util.Abort(
1638 raise util.Abort(
1635 _('cannot follow nonexistent file: "%s"') % file_)
1639 _('cannot follow nonexistent file: "%s"') % file_)
1636 raise FileWalkError("Cannot walk via filelog")
1640 raise FileWalkError("Cannot walk via filelog")
1637 else:
1641 else:
1638 continue
1642 continue
1639
1643
1640 if node is None:
1644 if node is None:
1641 last = len(filelog) - 1
1645 last = len(filelog) - 1
1642 else:
1646 else:
1643 last = filelog.rev(node)
1647 last = filelog.rev(node)
1644
1648
1645 # keep track of all ancestors of the file
1649 # keep track of all ancestors of the file
1646 ancestors = set([filelog.linkrev(last)])
1650 ancestors = set([filelog.linkrev(last)])
1647
1651
1648 # iterate from latest to oldest revision
1652 # iterate from latest to oldest revision
1649 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1653 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1650 if not follow:
1654 if not follow:
1651 if rev > maxrev:
1655 if rev > maxrev:
1652 continue
1656 continue
1653 else:
1657 else:
1654 # Note that last might not be the first interesting
1658 # Note that last might not be the first interesting
1655 # rev to us:
1659 # rev to us:
1656 # if the file has been changed after maxrev, we'll
1660 # if the file has been changed after maxrev, we'll
1657 # have linkrev(last) > maxrev, and we still need
1661 # have linkrev(last) > maxrev, and we still need
1658 # to explore the file graph
1662 # to explore the file graph
1659 if rev not in ancestors:
1663 if rev not in ancestors:
1660 continue
1664 continue
1661 # XXX insert 1327 fix here
1665 # XXX insert 1327 fix here
1662 if flparentlinkrevs:
1666 if flparentlinkrevs:
1663 ancestors.update(flparentlinkrevs)
1667 ancestors.update(flparentlinkrevs)
1664
1668
1665 fncache.setdefault(rev, []).append(file_)
1669 fncache.setdefault(rev, []).append(file_)
1666 wanted.add(rev)
1670 wanted.add(rev)
1667 if copied:
1671 if copied:
1668 copies.append(copied)
1672 copies.append(copied)
1669
1673
1670 return wanted
1674 return wanted
1671
1675
1672 class _followfilter(object):
1676 class _followfilter(object):
1673 def __init__(self, repo, onlyfirst=False):
1677 def __init__(self, repo, onlyfirst=False):
1674 self.repo = repo
1678 self.repo = repo
1675 self.startrev = nullrev
1679 self.startrev = nullrev
1676 self.roots = set()
1680 self.roots = set()
1677 self.onlyfirst = onlyfirst
1681 self.onlyfirst = onlyfirst
1678
1682
1679 def match(self, rev):
1683 def match(self, rev):
1680 def realparents(rev):
1684 def realparents(rev):
1681 if self.onlyfirst:
1685 if self.onlyfirst:
1682 return self.repo.changelog.parentrevs(rev)[0:1]
1686 return self.repo.changelog.parentrevs(rev)[0:1]
1683 else:
1687 else:
1684 return filter(lambda x: x != nullrev,
1688 return filter(lambda x: x != nullrev,
1685 self.repo.changelog.parentrevs(rev))
1689 self.repo.changelog.parentrevs(rev))
1686
1690
1687 if self.startrev == nullrev:
1691 if self.startrev == nullrev:
1688 self.startrev = rev
1692 self.startrev = rev
1689 return True
1693 return True
1690
1694
1691 if rev > self.startrev:
1695 if rev > self.startrev:
1692 # forward: all descendants
1696 # forward: all descendants
1693 if not self.roots:
1697 if not self.roots:
1694 self.roots.add(self.startrev)
1698 self.roots.add(self.startrev)
1695 for parent in realparents(rev):
1699 for parent in realparents(rev):
1696 if parent in self.roots:
1700 if parent in self.roots:
1697 self.roots.add(rev)
1701 self.roots.add(rev)
1698 return True
1702 return True
1699 else:
1703 else:
1700 # backwards: all parents
1704 # backwards: all parents
1701 if not self.roots:
1705 if not self.roots:
1702 self.roots.update(realparents(self.startrev))
1706 self.roots.update(realparents(self.startrev))
1703 if rev in self.roots:
1707 if rev in self.roots:
1704 self.roots.remove(rev)
1708 self.roots.remove(rev)
1705 self.roots.update(realparents(rev))
1709 self.roots.update(realparents(rev))
1706 return True
1710 return True
1707
1711
1708 return False
1712 return False
1709
1713
1710 def walkchangerevs(repo, match, opts, prepare):
1714 def walkchangerevs(repo, match, opts, prepare):
1711 '''Iterate over files and the revs in which they changed.
1715 '''Iterate over files and the revs in which they changed.
1712
1716
1713 Callers most commonly need to iterate backwards over the history
1717 Callers most commonly need to iterate backwards over the history
1714 in which they are interested. Doing so has awful (quadratic-looking)
1718 in which they are interested. Doing so has awful (quadratic-looking)
1715 performance, so we use iterators in a "windowed" way.
1719 performance, so we use iterators in a "windowed" way.
1716
1720
1717 We walk a window of revisions in the desired order. Within the
1721 We walk a window of revisions in the desired order. Within the
1718 window, we first walk forwards to gather data, then in the desired
1722 window, we first walk forwards to gather data, then in the desired
1719 order (usually backwards) to display it.
1723 order (usually backwards) to display it.
1720
1724
1721 This function returns an iterator yielding contexts. Before
1725 This function returns an iterator yielding contexts. Before
1722 yielding each context, the iterator will first call the prepare
1726 yielding each context, the iterator will first call the prepare
1723 function on each context in the window in forward order.'''
1727 function on each context in the window in forward order.'''
1724
1728
1725 follow = opts.get('follow') or opts.get('follow_first')
1729 follow = opts.get('follow') or opts.get('follow_first')
1726 revs = _logrevs(repo, opts)
1730 revs = _logrevs(repo, opts)
1727 if not revs:
1731 if not revs:
1728 return []
1732 return []
1729 wanted = set()
1733 wanted = set()
1730 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1734 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1731 fncache = {}
1735 fncache = {}
1732 change = repo.changectx
1736 change = repo.changectx
1733
1737
1734 # First step is to fill wanted, the set of revisions that we want to yield.
1738 # First step is to fill wanted, the set of revisions that we want to yield.
1735 # When it does not induce extra cost, we also fill fncache for revisions in
1739 # When it does not induce extra cost, we also fill fncache for revisions in
1736 # wanted: a cache of filenames that were changed (ctx.files()) and that
1740 # wanted: a cache of filenames that were changed (ctx.files()) and that
1737 # match the file filtering conditions.
1741 # match the file filtering conditions.
1738
1742
1739 if match.always():
1743 if match.always():
1740 # No files, no patterns. Display all revs.
1744 # No files, no patterns. Display all revs.
1741 wanted = revs
1745 wanted = revs
1742
1746
1743 if not slowpath and match.files():
1747 if not slowpath and match.files():
1744 # We only have to read through the filelog to find wanted revisions
1748 # We only have to read through the filelog to find wanted revisions
1745
1749
1746 try:
1750 try:
1747 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1751 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1748 except FileWalkError:
1752 except FileWalkError:
1749 slowpath = True
1753 slowpath = True
1750
1754
1751 # We decided to fall back to the slowpath because at least one
1755 # We decided to fall back to the slowpath because at least one
1752 # of the paths was not a file. Check to see if at least one of them
1756 # of the paths was not a file. Check to see if at least one of them
1753 # existed in history, otherwise simply return
1757 # existed in history, otherwise simply return
1754 for path in match.files():
1758 for path in match.files():
1755 if path == '.' or path in repo.store:
1759 if path == '.' or path in repo.store:
1756 break
1760 break
1757 else:
1761 else:
1758 return []
1762 return []
1759
1763
1760 if slowpath:
1764 if slowpath:
1761 # We have to read the changelog to match filenames against
1765 # We have to read the changelog to match filenames against
1762 # changed files
1766 # changed files
1763
1767
1764 if follow:
1768 if follow:
1765 raise util.Abort(_('can only follow copies/renames for explicit '
1769 raise util.Abort(_('can only follow copies/renames for explicit '
1766 'filenames'))
1770 'filenames'))
1767
1771
1768 # The slow path checks files modified in every changeset.
1772 # The slow path checks files modified in every changeset.
1769 # This is really slow on large repos, so compute the set lazily.
1773 # This is really slow on large repos, so compute the set lazily.
1770 class lazywantedset(object):
1774 class lazywantedset(object):
1771 def __init__(self):
1775 def __init__(self):
1772 self.set = set()
1776 self.set = set()
1773 self.revs = set(revs)
1777 self.revs = set(revs)
1774
1778
1775 # No need to worry about locality here because it will be accessed
1779 # No need to worry about locality here because it will be accessed
1776 # in the same order as the increasing window below.
1780 # in the same order as the increasing window below.
1777 def __contains__(self, value):
1781 def __contains__(self, value):
1778 if value in self.set:
1782 if value in self.set:
1779 return True
1783 return True
1780 elif not value in self.revs:
1784 elif not value in self.revs:
1781 return False
1785 return False
1782 else:
1786 else:
1783 self.revs.discard(value)
1787 self.revs.discard(value)
1784 ctx = change(value)
1788 ctx = change(value)
1785 matches = filter(match, ctx.files())
1789 matches = filter(match, ctx.files())
1786 if matches:
1790 if matches:
1787 fncache[value] = matches
1791 fncache[value] = matches
1788 self.set.add(value)
1792 self.set.add(value)
1789 return True
1793 return True
1790 return False
1794 return False
1791
1795
1792 def discard(self, value):
1796 def discard(self, value):
1793 self.revs.discard(value)
1797 self.revs.discard(value)
1794 self.set.discard(value)
1798 self.set.discard(value)
1795
1799
1796 wanted = lazywantedset()
1800 wanted = lazywantedset()
1797
1801
1798 # it might be worthwhile to do this in the iterator if the rev range
1802 # it might be worthwhile to do this in the iterator if the rev range
1799 # is descending and the prune args are all within that range
1803 # is descending and the prune args are all within that range
1800 for rev in opts.get('prune', ()):
1804 for rev in opts.get('prune', ()):
1801 rev = repo[rev].rev()
1805 rev = repo[rev].rev()
1802 ff = _followfilter(repo)
1806 ff = _followfilter(repo)
1803 stop = min(revs[0], revs[-1])
1807 stop = min(revs[0], revs[-1])
1804 for x in xrange(rev, stop - 1, -1):
1808 for x in xrange(rev, stop - 1, -1):
1805 if ff.match(x):
1809 if ff.match(x):
1806 wanted = wanted - [x]
1810 wanted = wanted - [x]
1807
1811
1808 # Now that wanted is correctly initialized, we can iterate over the
1812 # Now that wanted is correctly initialized, we can iterate over the
1809 # revision range, yielding only revisions in wanted.
1813 # revision range, yielding only revisions in wanted.
1810 def iterate():
1814 def iterate():
1811 if follow and not match.files():
1815 if follow and not match.files():
1812 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1816 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1813 def want(rev):
1817 def want(rev):
1814 return ff.match(rev) and rev in wanted
1818 return ff.match(rev) and rev in wanted
1815 else:
1819 else:
1816 def want(rev):
1820 def want(rev):
1817 return rev in wanted
1821 return rev in wanted
1818
1822
1819 it = iter(revs)
1823 it = iter(revs)
1820 stopiteration = False
1824 stopiteration = False
1821 for windowsize in increasingwindows():
1825 for windowsize in increasingwindows():
1822 nrevs = []
1826 nrevs = []
1823 for i in xrange(windowsize):
1827 for i in xrange(windowsize):
1824 try:
1828 try:
1825 rev = it.next()
1829 rev = it.next()
1826 if want(rev):
1830 if want(rev):
1827 nrevs.append(rev)
1831 nrevs.append(rev)
1828 except (StopIteration):
1832 except (StopIteration):
1829 stopiteration = True
1833 stopiteration = True
1830 break
1834 break
1831 for rev in sorted(nrevs):
1835 for rev in sorted(nrevs):
1832 fns = fncache.get(rev)
1836 fns = fncache.get(rev)
1833 ctx = change(rev)
1837 ctx = change(rev)
1834 if not fns:
1838 if not fns:
1835 def fns_generator():
1839 def fns_generator():
1836 for f in ctx.files():
1840 for f in ctx.files():
1837 if match(f):
1841 if match(f):
1838 yield f
1842 yield f
1839 fns = fns_generator()
1843 fns = fns_generator()
1840 prepare(ctx, fns)
1844 prepare(ctx, fns)
1841 for rev in nrevs:
1845 for rev in nrevs:
1842 yield change(rev)
1846 yield change(rev)
1843
1847
1844 if stopiteration:
1848 if stopiteration:
1845 break
1849 break
1846
1850
1847 return iterate()
1851 return iterate()
1848
1852
1849 def _makefollowlogfilematcher(repo, files, followfirst):
1853 def _makefollowlogfilematcher(repo, files, followfirst):
1850 # When displaying a revision with --patch --follow FILE, we have
1854 # When displaying a revision with --patch --follow FILE, we have
1851 # to know which file of the revision must be diffed. With
1855 # to know which file of the revision must be diffed. With
1852 # --follow, we want the names of the ancestors of FILE in the
1856 # --follow, we want the names of the ancestors of FILE in the
1853 # revision, stored in "fcache". "fcache" is populated by
1857 # revision, stored in "fcache". "fcache" is populated by
1854 # reproducing the graph traversal already done by --follow revset
1858 # reproducing the graph traversal already done by --follow revset
1855 # and relating linkrevs to file names (which is not "correct" but
1859 # and relating linkrevs to file names (which is not "correct" but
1856 # good enough).
1860 # good enough).
1857 fcache = {}
1861 fcache = {}
1858 fcacheready = [False]
1862 fcacheready = [False]
1859 pctx = repo['.']
1863 pctx = repo['.']
1860
1864
1861 def populate():
1865 def populate():
1862 for fn in files:
1866 for fn in files:
1863 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1867 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1864 for c in i:
1868 for c in i:
1865 fcache.setdefault(c.linkrev(), set()).add(c.path())
1869 fcache.setdefault(c.linkrev(), set()).add(c.path())
1866
1870
1867 def filematcher(rev):
1871 def filematcher(rev):
1868 if not fcacheready[0]:
1872 if not fcacheready[0]:
1869 # Lazy initialization
1873 # Lazy initialization
1870 fcacheready[0] = True
1874 fcacheready[0] = True
1871 populate()
1875 populate()
1872 return scmutil.matchfiles(repo, fcache.get(rev, []))
1876 return scmutil.matchfiles(repo, fcache.get(rev, []))
1873
1877
1874 return filematcher
1878 return filematcher
1875
1879
1876 def _makenofollowlogfilematcher(repo, pats, opts):
1880 def _makenofollowlogfilematcher(repo, pats, opts):
1877 '''hook for extensions to override the filematcher for non-follow cases'''
1881 '''hook for extensions to override the filematcher for non-follow cases'''
1878 return None
1882 return None
1879
1883
1880 def _makelogrevset(repo, pats, opts, revs):
1884 def _makelogrevset(repo, pats, opts, revs):
1881 """Return (expr, filematcher) where expr is a revset string built
1885 """Return (expr, filematcher) where expr is a revset string built
1882 from log options and file patterns or None. If --stat or --patch
1886 from log options and file patterns or None. If --stat or --patch
1883 are not passed filematcher is None. Otherwise it is a callable
1887 are not passed filematcher is None. Otherwise it is a callable
1884 taking a revision number and returning a match objects filtering
1888 taking a revision number and returning a match objects filtering
1885 the files to be detailed when displaying the revision.
1889 the files to be detailed when displaying the revision.
1886 """
1890 """
1887 opt2revset = {
1891 opt2revset = {
1888 'no_merges': ('not merge()', None),
1892 'no_merges': ('not merge()', None),
1889 'only_merges': ('merge()', None),
1893 'only_merges': ('merge()', None),
1890 '_ancestors': ('ancestors(%(val)s)', None),
1894 '_ancestors': ('ancestors(%(val)s)', None),
1891 '_fancestors': ('_firstancestors(%(val)s)', None),
1895 '_fancestors': ('_firstancestors(%(val)s)', None),
1892 '_descendants': ('descendants(%(val)s)', None),
1896 '_descendants': ('descendants(%(val)s)', None),
1893 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1897 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1894 '_matchfiles': ('_matchfiles(%(val)s)', None),
1898 '_matchfiles': ('_matchfiles(%(val)s)', None),
1895 'date': ('date(%(val)r)', None),
1899 'date': ('date(%(val)r)', None),
1896 'branch': ('branch(%(val)r)', ' or '),
1900 'branch': ('branch(%(val)r)', ' or '),
1897 '_patslog': ('filelog(%(val)r)', ' or '),
1901 '_patslog': ('filelog(%(val)r)', ' or '),
1898 '_patsfollow': ('follow(%(val)r)', ' or '),
1902 '_patsfollow': ('follow(%(val)r)', ' or '),
1899 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1903 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1900 'keyword': ('keyword(%(val)r)', ' or '),
1904 'keyword': ('keyword(%(val)r)', ' or '),
1901 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1905 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1902 'user': ('user(%(val)r)', ' or '),
1906 'user': ('user(%(val)r)', ' or '),
1903 }
1907 }
1904
1908
1905 opts = dict(opts)
1909 opts = dict(opts)
1906 # follow or not follow?
1910 # follow or not follow?
1907 follow = opts.get('follow') or opts.get('follow_first')
1911 follow = opts.get('follow') or opts.get('follow_first')
1908 if opts.get('follow_first'):
1912 if opts.get('follow_first'):
1909 followfirst = 1
1913 followfirst = 1
1910 else:
1914 else:
1911 followfirst = 0
1915 followfirst = 0
1912 # --follow with FILE behaviour depends on revs...
1916 # --follow with FILE behaviour depends on revs...
1913 it = iter(revs)
1917 it = iter(revs)
1914 startrev = it.next()
1918 startrev = it.next()
1915 try:
1919 try:
1916 followdescendants = startrev < it.next()
1920 followdescendants = startrev < it.next()
1917 except (StopIteration):
1921 except (StopIteration):
1918 followdescendants = False
1922 followdescendants = False
1919
1923
1920 # branch and only_branch are really aliases and must be handled at
1924 # branch and only_branch are really aliases and must be handled at
1921 # the same time
1925 # the same time
1922 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1926 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1923 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1927 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1924 # pats/include/exclude are passed to match.match() directly in
1928 # pats/include/exclude are passed to match.match() directly in
1925 # _matchfiles() revset but walkchangerevs() builds its matcher with
1929 # _matchfiles() revset but walkchangerevs() builds its matcher with
1926 # scmutil.match(). The difference is input pats are globbed on
1930 # scmutil.match(). The difference is input pats are globbed on
1927 # platforms without shell expansion (windows).
1931 # platforms without shell expansion (windows).
1928 wctx = repo[None]
1932 wctx = repo[None]
1929 match, pats = scmutil.matchandpats(wctx, pats, opts)
1933 match, pats = scmutil.matchandpats(wctx, pats, opts)
1930 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1934 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1931 if not slowpath:
1935 if not slowpath:
1932 for f in match.files():
1936 for f in match.files():
1933 if follow and f not in wctx:
1937 if follow and f not in wctx:
1934 # If the file exists, it may be a directory, so let it
1938 # If the file exists, it may be a directory, so let it
1935 # take the slow path.
1939 # take the slow path.
1936 if os.path.exists(repo.wjoin(f)):
1940 if os.path.exists(repo.wjoin(f)):
1937 slowpath = True
1941 slowpath = True
1938 continue
1942 continue
1939 else:
1943 else:
1940 raise util.Abort(_('cannot follow file not in parent '
1944 raise util.Abort(_('cannot follow file not in parent '
1941 'revision: "%s"') % f)
1945 'revision: "%s"') % f)
1942 filelog = repo.file(f)
1946 filelog = repo.file(f)
1943 if not filelog:
1947 if not filelog:
1944 # A zero count may be a directory or deleted file, so
1948 # A zero count may be a directory or deleted file, so
1945 # try to find matching entries on the slow path.
1949 # try to find matching entries on the slow path.
1946 if follow:
1950 if follow:
1947 raise util.Abort(
1951 raise util.Abort(
1948 _('cannot follow nonexistent file: "%s"') % f)
1952 _('cannot follow nonexistent file: "%s"') % f)
1949 slowpath = True
1953 slowpath = True
1950
1954
1951 # We decided to fall back to the slowpath because at least one
1955 # We decided to fall back to the slowpath because at least one
1952 # of the paths was not a file. Check to see if at least one of them
1956 # of the paths was not a file. Check to see if at least one of them
1953 # existed in history - in that case, we'll continue down the
1957 # existed in history - in that case, we'll continue down the
1954 # slowpath; otherwise, we can turn off the slowpath
1958 # slowpath; otherwise, we can turn off the slowpath
1955 if slowpath:
1959 if slowpath:
1956 for path in match.files():
1960 for path in match.files():
1957 if path == '.' or path in repo.store:
1961 if path == '.' or path in repo.store:
1958 break
1962 break
1959 else:
1963 else:
1960 slowpath = False
1964 slowpath = False
1961
1965
1962 fpats = ('_patsfollow', '_patsfollowfirst')
1966 fpats = ('_patsfollow', '_patsfollowfirst')
1963 fnopats = (('_ancestors', '_fancestors'),
1967 fnopats = (('_ancestors', '_fancestors'),
1964 ('_descendants', '_fdescendants'))
1968 ('_descendants', '_fdescendants'))
1965 if slowpath:
1969 if slowpath:
1966 # See walkchangerevs() slow path.
1970 # See walkchangerevs() slow path.
1967 #
1971 #
1968 # pats/include/exclude cannot be represented as separate
1972 # pats/include/exclude cannot be represented as separate
1969 # revset expressions as their filtering logic applies at file
1973 # revset expressions as their filtering logic applies at file
1970 # level. For instance "-I a -X a" matches a revision touching
1974 # level. For instance "-I a -X a" matches a revision touching
1971 # "a" and "b" while "file(a) and not file(b)" does
1975 # "a" and "b" while "file(a) and not file(b)" does
1972 # not. Besides, filesets are evaluated against the working
1976 # not. Besides, filesets are evaluated against the working
1973 # directory.
1977 # directory.
1974 matchargs = ['r:', 'd:relpath']
1978 matchargs = ['r:', 'd:relpath']
1975 for p in pats:
1979 for p in pats:
1976 matchargs.append('p:' + p)
1980 matchargs.append('p:' + p)
1977 for p in opts.get('include', []):
1981 for p in opts.get('include', []):
1978 matchargs.append('i:' + p)
1982 matchargs.append('i:' + p)
1979 for p in opts.get('exclude', []):
1983 for p in opts.get('exclude', []):
1980 matchargs.append('x:' + p)
1984 matchargs.append('x:' + p)
1981 matchargs = ','.join(('%r' % p) for p in matchargs)
1985 matchargs = ','.join(('%r' % p) for p in matchargs)
1982 opts['_matchfiles'] = matchargs
1986 opts['_matchfiles'] = matchargs
1983 if follow:
1987 if follow:
1984 opts[fnopats[0][followfirst]] = '.'
1988 opts[fnopats[0][followfirst]] = '.'
1985 else:
1989 else:
1986 if follow:
1990 if follow:
1987 if pats:
1991 if pats:
1988 # follow() revset interprets its file argument as a
1992 # follow() revset interprets its file argument as a
1989 # manifest entry, so use match.files(), not pats.
1993 # manifest entry, so use match.files(), not pats.
1990 opts[fpats[followfirst]] = list(match.files())
1994 opts[fpats[followfirst]] = list(match.files())
1991 else:
1995 else:
1992 op = fnopats[followdescendants][followfirst]
1996 op = fnopats[followdescendants][followfirst]
1993 opts[op] = 'rev(%d)' % startrev
1997 opts[op] = 'rev(%d)' % startrev
1994 else:
1998 else:
1995 opts['_patslog'] = list(pats)
1999 opts['_patslog'] = list(pats)
1996
2000
1997 filematcher = None
2001 filematcher = None
1998 if opts.get('patch') or opts.get('stat'):
2002 if opts.get('patch') or opts.get('stat'):
1999 # When following files, track renames via a special matcher.
2003 # When following files, track renames via a special matcher.
2000 # If we're forced to take the slowpath it means we're following
2004 # If we're forced to take the slowpath it means we're following
2001 # at least one pattern/directory, so don't bother with rename tracking.
2005 # at least one pattern/directory, so don't bother with rename tracking.
2002 if follow and not match.always() and not slowpath:
2006 if follow and not match.always() and not slowpath:
2003 # _makefollowlogfilematcher expects its files argument to be
2007 # _makefollowlogfilematcher expects its files argument to be
2004 # relative to the repo root, so use match.files(), not pats.
2008 # relative to the repo root, so use match.files(), not pats.
2005 filematcher = _makefollowlogfilematcher(repo, match.files(),
2009 filematcher = _makefollowlogfilematcher(repo, match.files(),
2006 followfirst)
2010 followfirst)
2007 else:
2011 else:
2008 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2012 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2009 if filematcher is None:
2013 if filematcher is None:
2010 filematcher = lambda rev: match
2014 filematcher = lambda rev: match
2011
2015
2012 expr = []
2016 expr = []
2013 for op, val in sorted(opts.iteritems()):
2017 for op, val in sorted(opts.iteritems()):
2014 if not val:
2018 if not val:
2015 continue
2019 continue
2016 if op not in opt2revset:
2020 if op not in opt2revset:
2017 continue
2021 continue
2018 revop, andor = opt2revset[op]
2022 revop, andor = opt2revset[op]
2019 if '%(val)' not in revop:
2023 if '%(val)' not in revop:
2020 expr.append(revop)
2024 expr.append(revop)
2021 else:
2025 else:
2022 if not isinstance(val, list):
2026 if not isinstance(val, list):
2023 e = revop % {'val': val}
2027 e = revop % {'val': val}
2024 else:
2028 else:
2025 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2029 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2026 expr.append(e)
2030 expr.append(e)
2027
2031
2028 if expr:
2032 if expr:
2029 expr = '(' + ' and '.join(expr) + ')'
2033 expr = '(' + ' and '.join(expr) + ')'
2030 else:
2034 else:
2031 expr = None
2035 expr = None
2032 return expr, filematcher
2036 return expr, filematcher
2033
2037
2034 def _logrevs(repo, opts):
2038 def _logrevs(repo, opts):
2035 # Default --rev value depends on --follow but --follow behaviour
2039 # Default --rev value depends on --follow but --follow behaviour
2036 # depends on revisions resolved from --rev...
2040 # depends on revisions resolved from --rev...
2037 follow = opts.get('follow') or opts.get('follow_first')
2041 follow = opts.get('follow') or opts.get('follow_first')
2038 if opts.get('rev'):
2042 if opts.get('rev'):
2039 revs = scmutil.revrange(repo, opts['rev'])
2043 revs = scmutil.revrange(repo, opts['rev'])
2040 elif follow and repo.dirstate.p1() == nullid:
2044 elif follow and repo.dirstate.p1() == nullid:
2041 revs = revset.baseset()
2045 revs = revset.baseset()
2042 elif follow:
2046 elif follow:
2043 revs = repo.revs('reverse(:.)')
2047 revs = repo.revs('reverse(:.)')
2044 else:
2048 else:
2045 revs = revset.spanset(repo)
2049 revs = revset.spanset(repo)
2046 revs.reverse()
2050 revs.reverse()
2047 return revs
2051 return revs
2048
2052
2049 def getgraphlogrevs(repo, pats, opts):
2053 def getgraphlogrevs(repo, pats, opts):
2050 """Return (revs, expr, filematcher) where revs is an iterable of
2054 """Return (revs, expr, filematcher) where revs is an iterable of
2051 revision numbers, expr is a revset string built from log options
2055 revision numbers, expr is a revset string built from log options
2052 and file patterns or None, and used to filter 'revs'. If --stat or
2056 and file patterns or None, and used to filter 'revs'. If --stat or
2053 --patch are not passed filematcher is None. Otherwise it is a
2057 --patch are not passed filematcher is None. Otherwise it is a
2054 callable taking a revision number and returning a match objects
2058 callable taking a revision number and returning a match objects
2055 filtering the files to be detailed when displaying the revision.
2059 filtering the files to be detailed when displaying the revision.
2056 """
2060 """
2057 limit = loglimit(opts)
2061 limit = loglimit(opts)
2058 revs = _logrevs(repo, opts)
2062 revs = _logrevs(repo, opts)
2059 if not revs:
2063 if not revs:
2060 return revset.baseset(), None, None
2064 return revset.baseset(), None, None
2061 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2065 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2062 if opts.get('rev'):
2066 if opts.get('rev'):
2063 # User-specified revs might be unsorted, but don't sort before
2067 # User-specified revs might be unsorted, but don't sort before
2064 # _makelogrevset because it might depend on the order of revs
2068 # _makelogrevset because it might depend on the order of revs
2065 revs.sort(reverse=True)
2069 revs.sort(reverse=True)
2066 if expr:
2070 if expr:
2067 # Revset matchers often operate faster on revisions in changelog
2071 # Revset matchers often operate faster on revisions in changelog
2068 # order, because most filters deal with the changelog.
2072 # order, because most filters deal with the changelog.
2069 revs.reverse()
2073 revs.reverse()
2070 matcher = revset.match(repo.ui, expr)
2074 matcher = revset.match(repo.ui, expr)
2071 # Revset matches can reorder revisions. "A or B" typically returns
2075 # Revset matches can reorder revisions. "A or B" typically returns
2072 # returns the revision matching A then the revision matching B. Sort
2076 # returns the revision matching A then the revision matching B. Sort
2073 # again to fix that.
2077 # again to fix that.
2074 revs = matcher(repo, revs)
2078 revs = matcher(repo, revs)
2075 revs.sort(reverse=True)
2079 revs.sort(reverse=True)
2076 if limit is not None:
2080 if limit is not None:
2077 limitedrevs = []
2081 limitedrevs = []
2078 for idx, rev in enumerate(revs):
2082 for idx, rev in enumerate(revs):
2079 if idx >= limit:
2083 if idx >= limit:
2080 break
2084 break
2081 limitedrevs.append(rev)
2085 limitedrevs.append(rev)
2082 revs = revset.baseset(limitedrevs)
2086 revs = revset.baseset(limitedrevs)
2083
2087
2084 return revs, expr, filematcher
2088 return revs, expr, filematcher
2085
2089
2086 def getlogrevs(repo, pats, opts):
2090 def getlogrevs(repo, pats, opts):
2087 """Return (revs, expr, filematcher) where revs is an iterable of
2091 """Return (revs, expr, filematcher) where revs is an iterable of
2088 revision numbers, expr is a revset string built from log options
2092 revision numbers, expr is a revset string built from log options
2089 and file patterns or None, and used to filter 'revs'. If --stat or
2093 and file patterns or None, and used to filter 'revs'. If --stat or
2090 --patch are not passed filematcher is None. Otherwise it is a
2094 --patch are not passed filematcher is None. Otherwise it is a
2091 callable taking a revision number and returning a match objects
2095 callable taking a revision number and returning a match objects
2092 filtering the files to be detailed when displaying the revision.
2096 filtering the files to be detailed when displaying the revision.
2093 """
2097 """
2094 limit = loglimit(opts)
2098 limit = loglimit(opts)
2095 revs = _logrevs(repo, opts)
2099 revs = _logrevs(repo, opts)
2096 if not revs:
2100 if not revs:
2097 return revset.baseset([]), None, None
2101 return revset.baseset([]), None, None
2098 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2102 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2099 if expr:
2103 if expr:
2100 # Revset matchers often operate faster on revisions in changelog
2104 # Revset matchers often operate faster on revisions in changelog
2101 # order, because most filters deal with the changelog.
2105 # order, because most filters deal with the changelog.
2102 if not opts.get('rev'):
2106 if not opts.get('rev'):
2103 revs.reverse()
2107 revs.reverse()
2104 matcher = revset.match(repo.ui, expr)
2108 matcher = revset.match(repo.ui, expr)
2105 # Revset matches can reorder revisions. "A or B" typically returns
2109 # Revset matches can reorder revisions. "A or B" typically returns
2106 # returns the revision matching A then the revision matching B. Sort
2110 # returns the revision matching A then the revision matching B. Sort
2107 # again to fix that.
2111 # again to fix that.
2108 revs = matcher(repo, revs)
2112 revs = matcher(repo, revs)
2109 if not opts.get('rev'):
2113 if not opts.get('rev'):
2110 revs.sort(reverse=True)
2114 revs.sort(reverse=True)
2111 if limit is not None:
2115 if limit is not None:
2112 count = 0
2116 count = 0
2113 limitedrevs = []
2117 limitedrevs = []
2114 it = iter(revs)
2118 it = iter(revs)
2115 while count < limit:
2119 while count < limit:
2116 try:
2120 try:
2117 limitedrevs.append(it.next())
2121 limitedrevs.append(it.next())
2118 except (StopIteration):
2122 except (StopIteration):
2119 break
2123 break
2120 count += 1
2124 count += 1
2121 revs = revset.baseset(limitedrevs)
2125 revs = revset.baseset(limitedrevs)
2122
2126
2123 return revs, expr, filematcher
2127 return revs, expr, filematcher
2124
2128
2125 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2129 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2126 filematcher=None):
2130 filematcher=None):
2127 seen, state = [], graphmod.asciistate()
2131 seen, state = [], graphmod.asciistate()
2128 for rev, type, ctx, parents in dag:
2132 for rev, type, ctx, parents in dag:
2129 char = 'o'
2133 char = 'o'
2130 if ctx.node() in showparents:
2134 if ctx.node() in showparents:
2131 char = '@'
2135 char = '@'
2132 elif ctx.obsolete():
2136 elif ctx.obsolete():
2133 char = 'x'
2137 char = 'x'
2134 elif ctx.closesbranch():
2138 elif ctx.closesbranch():
2135 char = '_'
2139 char = '_'
2136 copies = None
2140 copies = None
2137 if getrenamed and ctx.rev():
2141 if getrenamed and ctx.rev():
2138 copies = []
2142 copies = []
2139 for fn in ctx.files():
2143 for fn in ctx.files():
2140 rename = getrenamed(fn, ctx.rev())
2144 rename = getrenamed(fn, ctx.rev())
2141 if rename:
2145 if rename:
2142 copies.append((fn, rename[0]))
2146 copies.append((fn, rename[0]))
2143 revmatchfn = None
2147 revmatchfn = None
2144 if filematcher is not None:
2148 if filematcher is not None:
2145 revmatchfn = filematcher(ctx.rev())
2149 revmatchfn = filematcher(ctx.rev())
2146 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2150 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2147 lines = displayer.hunk.pop(rev).split('\n')
2151 lines = displayer.hunk.pop(rev).split('\n')
2148 if not lines[-1]:
2152 if not lines[-1]:
2149 del lines[-1]
2153 del lines[-1]
2150 displayer.flush(rev)
2154 displayer.flush(rev)
2151 edges = edgefn(type, char, lines, seen, rev, parents)
2155 edges = edgefn(type, char, lines, seen, rev, parents)
2152 for type, char, lines, coldata in edges:
2156 for type, char, lines, coldata in edges:
2153 graphmod.ascii(ui, state, type, char, lines, coldata)
2157 graphmod.ascii(ui, state, type, char, lines, coldata)
2154 displayer.close()
2158 displayer.close()
2155
2159
2156 def graphlog(ui, repo, *pats, **opts):
2160 def graphlog(ui, repo, *pats, **opts):
2157 # Parameters are identical to log command ones
2161 # Parameters are identical to log command ones
2158 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2162 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2159 revdag = graphmod.dagwalker(repo, revs)
2163 revdag = graphmod.dagwalker(repo, revs)
2160
2164
2161 getrenamed = None
2165 getrenamed = None
2162 if opts.get('copies'):
2166 if opts.get('copies'):
2163 endrev = None
2167 endrev = None
2164 if opts.get('rev'):
2168 if opts.get('rev'):
2165 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2169 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2166 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2170 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2167 displayer = show_changeset(ui, repo, opts, buffered=True)
2171 displayer = show_changeset(ui, repo, opts, buffered=True)
2168 showparents = [ctx.node() for ctx in repo[None].parents()]
2172 showparents = [ctx.node() for ctx in repo[None].parents()]
2169 displaygraph(ui, revdag, displayer, showparents,
2173 displaygraph(ui, revdag, displayer, showparents,
2170 graphmod.asciiedges, getrenamed, filematcher)
2174 graphmod.asciiedges, getrenamed, filematcher)
2171
2175
2172 def checkunsupportedgraphflags(pats, opts):
2176 def checkunsupportedgraphflags(pats, opts):
2173 for op in ["newest_first"]:
2177 for op in ["newest_first"]:
2174 if op in opts and opts[op]:
2178 if op in opts and opts[op]:
2175 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2179 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2176 % op.replace("_", "-"))
2180 % op.replace("_", "-"))
2177
2181
2178 def graphrevs(repo, nodes, opts):
2182 def graphrevs(repo, nodes, opts):
2179 limit = loglimit(opts)
2183 limit = loglimit(opts)
2180 nodes.reverse()
2184 nodes.reverse()
2181 if limit is not None:
2185 if limit is not None:
2182 nodes = nodes[:limit]
2186 nodes = nodes[:limit]
2183 return graphmod.nodes(repo, nodes)
2187 return graphmod.nodes(repo, nodes)
2184
2188
2185 def add(ui, repo, match, prefix, explicitonly, **opts):
2189 def add(ui, repo, match, prefix, explicitonly, **opts):
2186 join = lambda f: os.path.join(prefix, f)
2190 join = lambda f: os.path.join(prefix, f)
2187 bad = []
2191 bad = []
2188 oldbad = match.bad
2192 oldbad = match.bad
2189 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2193 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2190 names = []
2194 names = []
2191 wctx = repo[None]
2195 wctx = repo[None]
2192 cca = None
2196 cca = None
2193 abort, warn = scmutil.checkportabilityalert(ui)
2197 abort, warn = scmutil.checkportabilityalert(ui)
2194 if abort or warn:
2198 if abort or warn:
2195 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2199 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2196 for f in wctx.walk(match):
2200 for f in wctx.walk(match):
2197 exact = match.exact(f)
2201 exact = match.exact(f)
2198 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2202 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2199 if cca:
2203 if cca:
2200 cca(f)
2204 cca(f)
2201 names.append(f)
2205 names.append(f)
2202 if ui.verbose or not exact:
2206 if ui.verbose or not exact:
2203 ui.status(_('adding %s\n') % match.rel(f))
2207 ui.status(_('adding %s\n') % match.rel(f))
2204
2208
2205 for subpath in sorted(wctx.substate):
2209 for subpath in sorted(wctx.substate):
2206 sub = wctx.sub(subpath)
2210 sub = wctx.sub(subpath)
2207 try:
2211 try:
2208 submatch = matchmod.narrowmatcher(subpath, match)
2212 submatch = matchmod.narrowmatcher(subpath, match)
2209 if opts.get('subrepos'):
2213 if opts.get('subrepos'):
2210 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2214 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2211 else:
2215 else:
2212 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2216 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2213 except error.LookupError:
2217 except error.LookupError:
2214 ui.status(_("skipping missing subrepository: %s\n")
2218 ui.status(_("skipping missing subrepository: %s\n")
2215 % join(subpath))
2219 % join(subpath))
2216
2220
2217 if not opts.get('dry_run'):
2221 if not opts.get('dry_run'):
2218 rejected = wctx.add(names, prefix)
2222 rejected = wctx.add(names, prefix)
2219 bad.extend(f for f in rejected if f in match.files())
2223 bad.extend(f for f in rejected if f in match.files())
2220 return bad
2224 return bad
2221
2225
2222 def forget(ui, repo, match, prefix, explicitonly):
2226 def forget(ui, repo, match, prefix, explicitonly):
2223 join = lambda f: os.path.join(prefix, f)
2227 join = lambda f: os.path.join(prefix, f)
2224 bad = []
2228 bad = []
2225 oldbad = match.bad
2229 oldbad = match.bad
2226 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2230 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2227 wctx = repo[None]
2231 wctx = repo[None]
2228 forgot = []
2232 forgot = []
2229 s = repo.status(match=match, clean=True)
2233 s = repo.status(match=match, clean=True)
2230 forget = sorted(s[0] + s[1] + s[3] + s[6])
2234 forget = sorted(s[0] + s[1] + s[3] + s[6])
2231 if explicitonly:
2235 if explicitonly:
2232 forget = [f for f in forget if match.exact(f)]
2236 forget = [f for f in forget if match.exact(f)]
2233
2237
2234 for subpath in sorted(wctx.substate):
2238 for subpath in sorted(wctx.substate):
2235 sub = wctx.sub(subpath)
2239 sub = wctx.sub(subpath)
2236 try:
2240 try:
2237 submatch = matchmod.narrowmatcher(subpath, match)
2241 submatch = matchmod.narrowmatcher(subpath, match)
2238 subbad, subforgot = sub.forget(submatch, prefix)
2242 subbad, subforgot = sub.forget(submatch, prefix)
2239 bad.extend([subpath + '/' + f for f in subbad])
2243 bad.extend([subpath + '/' + f for f in subbad])
2240 forgot.extend([subpath + '/' + f for f in subforgot])
2244 forgot.extend([subpath + '/' + f for f in subforgot])
2241 except error.LookupError:
2245 except error.LookupError:
2242 ui.status(_("skipping missing subrepository: %s\n")
2246 ui.status(_("skipping missing subrepository: %s\n")
2243 % join(subpath))
2247 % join(subpath))
2244
2248
2245 if not explicitonly:
2249 if not explicitonly:
2246 for f in match.files():
2250 for f in match.files():
2247 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2251 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2248 if f not in forgot:
2252 if f not in forgot:
2249 if repo.wvfs.exists(f):
2253 if repo.wvfs.exists(f):
2250 # Don't complain if the exact case match wasn't given.
2254 # Don't complain if the exact case match wasn't given.
2251 # But don't do this until after checking 'forgot', so
2255 # But don't do this until after checking 'forgot', so
2252 # that subrepo files aren't normalized, and this op is
2256 # that subrepo files aren't normalized, and this op is
2253 # purely from data cached by the status walk above.
2257 # purely from data cached by the status walk above.
2254 if repo.dirstate.normalize(f) in repo.dirstate:
2258 if repo.dirstate.normalize(f) in repo.dirstate:
2255 continue
2259 continue
2256 ui.warn(_('not removing %s: '
2260 ui.warn(_('not removing %s: '
2257 'file is already untracked\n')
2261 'file is already untracked\n')
2258 % match.rel(f))
2262 % match.rel(f))
2259 bad.append(f)
2263 bad.append(f)
2260
2264
2261 for f in forget:
2265 for f in forget:
2262 if ui.verbose or not match.exact(f):
2266 if ui.verbose or not match.exact(f):
2263 ui.status(_('removing %s\n') % match.rel(f))
2267 ui.status(_('removing %s\n') % match.rel(f))
2264
2268
2265 rejected = wctx.forget(forget, prefix)
2269 rejected = wctx.forget(forget, prefix)
2266 bad.extend(f for f in rejected if f in match.files())
2270 bad.extend(f for f in rejected if f in match.files())
2267 forgot.extend(f for f in forget if f not in rejected)
2271 forgot.extend(f for f in forget if f not in rejected)
2268 return bad, forgot
2272 return bad, forgot
2269
2273
2270 def files(ui, ctx, m, fm, fmt, subrepos):
2274 def files(ui, ctx, m, fm, fmt, subrepos):
2271 rev = ctx.rev()
2275 rev = ctx.rev()
2272 ret = 1
2276 ret = 1
2273 ds = ctx.repo().dirstate
2277 ds = ctx.repo().dirstate
2274
2278
2275 for f in ctx.matches(m):
2279 for f in ctx.matches(m):
2276 if rev is None and ds[f] == 'r':
2280 if rev is None and ds[f] == 'r':
2277 continue
2281 continue
2278 fm.startitem()
2282 fm.startitem()
2279 if ui.verbose:
2283 if ui.verbose:
2280 fc = ctx[f]
2284 fc = ctx[f]
2281 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2285 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2282 fm.data(abspath=f)
2286 fm.data(abspath=f)
2283 fm.write('path', fmt, m.rel(f))
2287 fm.write('path', fmt, m.rel(f))
2284 ret = 0
2288 ret = 0
2285
2289
2286 if subrepos:
2290 if subrepos:
2287 for subpath in sorted(ctx.substate):
2291 for subpath in sorted(ctx.substate):
2288 sub = ctx.sub(subpath)
2292 sub = ctx.sub(subpath)
2289 try:
2293 try:
2290 submatch = matchmod.narrowmatcher(subpath, m)
2294 submatch = matchmod.narrowmatcher(subpath, m)
2291 if sub.printfiles(ui, submatch, fm, fmt) == 0:
2295 if sub.printfiles(ui, submatch, fm, fmt) == 0:
2292 ret = 0
2296 ret = 0
2293 except error.LookupError:
2297 except error.LookupError:
2294 ui.status(_("skipping missing subrepository: %s\n")
2298 ui.status(_("skipping missing subrepository: %s\n")
2295 % m.abs(subpath))
2299 % m.abs(subpath))
2296
2300
2297 return ret
2301 return ret
2298
2302
2299 def remove(ui, repo, m, prefix, after, force, subrepos):
2303 def remove(ui, repo, m, prefix, after, force, subrepos):
2300 join = lambda f: os.path.join(prefix, f)
2304 join = lambda f: os.path.join(prefix, f)
2301 ret = 0
2305 ret = 0
2302 s = repo.status(match=m, clean=True)
2306 s = repo.status(match=m, clean=True)
2303 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2307 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2304
2308
2305 wctx = repo[None]
2309 wctx = repo[None]
2306
2310
2307 for subpath in sorted(wctx.substate):
2311 for subpath in sorted(wctx.substate):
2308 def matchessubrepo(matcher, subpath):
2312 def matchessubrepo(matcher, subpath):
2309 if matcher.exact(subpath):
2313 if matcher.exact(subpath):
2310 return True
2314 return True
2311 for f in matcher.files():
2315 for f in matcher.files():
2312 if f.startswith(subpath):
2316 if f.startswith(subpath):
2313 return True
2317 return True
2314 return False
2318 return False
2315
2319
2316 if subrepos or matchessubrepo(m, subpath):
2320 if subrepos or matchessubrepo(m, subpath):
2317 sub = wctx.sub(subpath)
2321 sub = wctx.sub(subpath)
2318 try:
2322 try:
2319 submatch = matchmod.narrowmatcher(subpath, m)
2323 submatch = matchmod.narrowmatcher(subpath, m)
2320 if sub.removefiles(submatch, prefix, after, force, subrepos):
2324 if sub.removefiles(submatch, prefix, after, force, subrepos):
2321 ret = 1
2325 ret = 1
2322 except error.LookupError:
2326 except error.LookupError:
2323 ui.status(_("skipping missing subrepository: %s\n")
2327 ui.status(_("skipping missing subrepository: %s\n")
2324 % join(subpath))
2328 % join(subpath))
2325
2329
2326 # warn about failure to delete explicit files/dirs
2330 # warn about failure to delete explicit files/dirs
2327 deleteddirs = scmutil.dirs(deleted)
2331 deleteddirs = scmutil.dirs(deleted)
2328 for f in m.files():
2332 for f in m.files():
2329 def insubrepo():
2333 def insubrepo():
2330 for subpath in wctx.substate:
2334 for subpath in wctx.substate:
2331 if f.startswith(subpath):
2335 if f.startswith(subpath):
2332 return True
2336 return True
2333 return False
2337 return False
2334
2338
2335 isdir = f in deleteddirs or f in wctx.dirs()
2339 isdir = f in deleteddirs or f in wctx.dirs()
2336 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2340 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2337 continue
2341 continue
2338
2342
2339 if repo.wvfs.exists(f):
2343 if repo.wvfs.exists(f):
2340 if repo.wvfs.isdir(f):
2344 if repo.wvfs.isdir(f):
2341 ui.warn(_('not removing %s: no tracked files\n')
2345 ui.warn(_('not removing %s: no tracked files\n')
2342 % m.rel(f))
2346 % m.rel(f))
2343 else:
2347 else:
2344 ui.warn(_('not removing %s: file is untracked\n')
2348 ui.warn(_('not removing %s: file is untracked\n')
2345 % m.rel(f))
2349 % m.rel(f))
2346 # missing files will generate a warning elsewhere
2350 # missing files will generate a warning elsewhere
2347 ret = 1
2351 ret = 1
2348
2352
2349 if force:
2353 if force:
2350 list = modified + deleted + clean + added
2354 list = modified + deleted + clean + added
2351 elif after:
2355 elif after:
2352 list = deleted
2356 list = deleted
2353 for f in modified + added + clean:
2357 for f in modified + added + clean:
2354 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2358 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2355 ret = 1
2359 ret = 1
2356 else:
2360 else:
2357 list = deleted + clean
2361 list = deleted + clean
2358 for f in modified:
2362 for f in modified:
2359 ui.warn(_('not removing %s: file is modified (use -f'
2363 ui.warn(_('not removing %s: file is modified (use -f'
2360 ' to force removal)\n') % m.rel(f))
2364 ' to force removal)\n') % m.rel(f))
2361 ret = 1
2365 ret = 1
2362 for f in added:
2366 for f in added:
2363 ui.warn(_('not removing %s: file has been marked for add'
2367 ui.warn(_('not removing %s: file has been marked for add'
2364 ' (use forget to undo)\n') % m.rel(f))
2368 ' (use forget to undo)\n') % m.rel(f))
2365 ret = 1
2369 ret = 1
2366
2370
2367 for f in sorted(list):
2371 for f in sorted(list):
2368 if ui.verbose or not m.exact(f):
2372 if ui.verbose or not m.exact(f):
2369 ui.status(_('removing %s\n') % m.rel(f))
2373 ui.status(_('removing %s\n') % m.rel(f))
2370
2374
2371 wlock = repo.wlock()
2375 wlock = repo.wlock()
2372 try:
2376 try:
2373 if not after:
2377 if not after:
2374 for f in list:
2378 for f in list:
2375 if f in added:
2379 if f in added:
2376 continue # we never unlink added files on remove
2380 continue # we never unlink added files on remove
2377 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2381 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2378 repo[None].forget(list)
2382 repo[None].forget(list)
2379 finally:
2383 finally:
2380 wlock.release()
2384 wlock.release()
2381
2385
2382 return ret
2386 return ret
2383
2387
2384 def cat(ui, repo, ctx, matcher, prefix, **opts):
2388 def cat(ui, repo, ctx, matcher, prefix, **opts):
2385 err = 1
2389 err = 1
2386
2390
2387 def write(path):
2391 def write(path):
2388 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2392 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2389 pathname=os.path.join(prefix, path))
2393 pathname=os.path.join(prefix, path))
2390 data = ctx[path].data()
2394 data = ctx[path].data()
2391 if opts.get('decode'):
2395 if opts.get('decode'):
2392 data = repo.wwritedata(path, data)
2396 data = repo.wwritedata(path, data)
2393 fp.write(data)
2397 fp.write(data)
2394 fp.close()
2398 fp.close()
2395
2399
2396 # Automation often uses hg cat on single files, so special case it
2400 # Automation often uses hg cat on single files, so special case it
2397 # for performance to avoid the cost of parsing the manifest.
2401 # for performance to avoid the cost of parsing the manifest.
2398 if len(matcher.files()) == 1 and not matcher.anypats():
2402 if len(matcher.files()) == 1 and not matcher.anypats():
2399 file = matcher.files()[0]
2403 file = matcher.files()[0]
2400 mf = repo.manifest
2404 mf = repo.manifest
2401 mfnode = ctx._changeset[0]
2405 mfnode = ctx._changeset[0]
2402 if mf.find(mfnode, file)[0]:
2406 if mf.find(mfnode, file)[0]:
2403 write(file)
2407 write(file)
2404 return 0
2408 return 0
2405
2409
2406 # Don't warn about "missing" files that are really in subrepos
2410 # Don't warn about "missing" files that are really in subrepos
2407 bad = matcher.bad
2411 bad = matcher.bad
2408
2412
2409 def badfn(path, msg):
2413 def badfn(path, msg):
2410 for subpath in ctx.substate:
2414 for subpath in ctx.substate:
2411 if path.startswith(subpath):
2415 if path.startswith(subpath):
2412 return
2416 return
2413 bad(path, msg)
2417 bad(path, msg)
2414
2418
2415 matcher.bad = badfn
2419 matcher.bad = badfn
2416
2420
2417 for abs in ctx.walk(matcher):
2421 for abs in ctx.walk(matcher):
2418 write(abs)
2422 write(abs)
2419 err = 0
2423 err = 0
2420
2424
2421 matcher.bad = bad
2425 matcher.bad = bad
2422
2426
2423 for subpath in sorted(ctx.substate):
2427 for subpath in sorted(ctx.substate):
2424 sub = ctx.sub(subpath)
2428 sub = ctx.sub(subpath)
2425 try:
2429 try:
2426 submatch = matchmod.narrowmatcher(subpath, matcher)
2430 submatch = matchmod.narrowmatcher(subpath, matcher)
2427
2431
2428 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2432 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2429 **opts):
2433 **opts):
2430 err = 0
2434 err = 0
2431 except error.RepoLookupError:
2435 except error.RepoLookupError:
2432 ui.status(_("skipping missing subrepository: %s\n")
2436 ui.status(_("skipping missing subrepository: %s\n")
2433 % os.path.join(prefix, subpath))
2437 % os.path.join(prefix, subpath))
2434
2438
2435 return err
2439 return err
2436
2440
2437 def commit(ui, repo, commitfunc, pats, opts):
2441 def commit(ui, repo, commitfunc, pats, opts):
2438 '''commit the specified files or all outstanding changes'''
2442 '''commit the specified files or all outstanding changes'''
2439 date = opts.get('date')
2443 date = opts.get('date')
2440 if date:
2444 if date:
2441 opts['date'] = util.parsedate(date)
2445 opts['date'] = util.parsedate(date)
2442 message = logmessage(ui, opts)
2446 message = logmessage(ui, opts)
2443 matcher = scmutil.match(repo[None], pats, opts)
2447 matcher = scmutil.match(repo[None], pats, opts)
2444
2448
2445 # extract addremove carefully -- this function can be called from a command
2449 # extract addremove carefully -- this function can be called from a command
2446 # that doesn't support addremove
2450 # that doesn't support addremove
2447 if opts.get('addremove'):
2451 if opts.get('addremove'):
2448 if scmutil.addremove(repo, matcher, "", opts) != 0:
2452 if scmutil.addremove(repo, matcher, "", opts) != 0:
2449 raise util.Abort(
2453 raise util.Abort(
2450 _("failed to mark all new/missing files as added/removed"))
2454 _("failed to mark all new/missing files as added/removed"))
2451
2455
2452 return commitfunc(ui, repo, message, matcher, opts)
2456 return commitfunc(ui, repo, message, matcher, opts)
2453
2457
2454 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2458 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2455 # amend will reuse the existing user if not specified, but the obsolete
2459 # amend will reuse the existing user if not specified, but the obsolete
2456 # marker creation requires that the current user's name is specified.
2460 # marker creation requires that the current user's name is specified.
2457 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2461 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2458 ui.username() # raise exception if username not set
2462 ui.username() # raise exception if username not set
2459
2463
2460 ui.note(_('amending changeset %s\n') % old)
2464 ui.note(_('amending changeset %s\n') % old)
2461 base = old.p1()
2465 base = old.p1()
2462
2466
2463 wlock = lock = newid = None
2467 wlock = lock = newid = None
2464 try:
2468 try:
2465 wlock = repo.wlock()
2469 wlock = repo.wlock()
2466 lock = repo.lock()
2470 lock = repo.lock()
2467 tr = repo.transaction('amend')
2471 tr = repo.transaction('amend')
2468 try:
2472 try:
2469 # See if we got a message from -m or -l, if not, open the editor
2473 # See if we got a message from -m or -l, if not, open the editor
2470 # with the message of the changeset to amend
2474 # with the message of the changeset to amend
2471 message = logmessage(ui, opts)
2475 message = logmessage(ui, opts)
2472 # ensure logfile does not conflict with later enforcement of the
2476 # ensure logfile does not conflict with later enforcement of the
2473 # message. potential logfile content has been processed by
2477 # message. potential logfile content has been processed by
2474 # `logmessage` anyway.
2478 # `logmessage` anyway.
2475 opts.pop('logfile')
2479 opts.pop('logfile')
2476 # First, do a regular commit to record all changes in the working
2480 # First, do a regular commit to record all changes in the working
2477 # directory (if there are any)
2481 # directory (if there are any)
2478 ui.callhooks = False
2482 ui.callhooks = False
2479 currentbookmark = repo._bookmarkcurrent
2483 currentbookmark = repo._bookmarkcurrent
2480 try:
2484 try:
2481 repo._bookmarkcurrent = None
2485 repo._bookmarkcurrent = None
2482 opts['message'] = 'temporary amend commit for %s' % old
2486 opts['message'] = 'temporary amend commit for %s' % old
2483 node = commit(ui, repo, commitfunc, pats, opts)
2487 node = commit(ui, repo, commitfunc, pats, opts)
2484 finally:
2488 finally:
2485 repo._bookmarkcurrent = currentbookmark
2489 repo._bookmarkcurrent = currentbookmark
2486 ui.callhooks = True
2490 ui.callhooks = True
2487 ctx = repo[node]
2491 ctx = repo[node]
2488
2492
2489 # Participating changesets:
2493 # Participating changesets:
2490 #
2494 #
2491 # node/ctx o - new (intermediate) commit that contains changes
2495 # node/ctx o - new (intermediate) commit that contains changes
2492 # | from working dir to go into amending commit
2496 # | from working dir to go into amending commit
2493 # | (or a workingctx if there were no changes)
2497 # | (or a workingctx if there were no changes)
2494 # |
2498 # |
2495 # old o - changeset to amend
2499 # old o - changeset to amend
2496 # |
2500 # |
2497 # base o - parent of amending changeset
2501 # base o - parent of amending changeset
2498
2502
2499 # Update extra dict from amended commit (e.g. to preserve graft
2503 # Update extra dict from amended commit (e.g. to preserve graft
2500 # source)
2504 # source)
2501 extra.update(old.extra())
2505 extra.update(old.extra())
2502
2506
2503 # Also update it from the intermediate commit or from the wctx
2507 # Also update it from the intermediate commit or from the wctx
2504 extra.update(ctx.extra())
2508 extra.update(ctx.extra())
2505
2509
2506 if len(old.parents()) > 1:
2510 if len(old.parents()) > 1:
2507 # ctx.files() isn't reliable for merges, so fall back to the
2511 # ctx.files() isn't reliable for merges, so fall back to the
2508 # slower repo.status() method
2512 # slower repo.status() method
2509 files = set([fn for st in repo.status(base, old)[:3]
2513 files = set([fn for st in repo.status(base, old)[:3]
2510 for fn in st])
2514 for fn in st])
2511 else:
2515 else:
2512 files = set(old.files())
2516 files = set(old.files())
2513
2517
2514 # Second, we use either the commit we just did, or if there were no
2518 # Second, we use either the commit we just did, or if there were no
2515 # changes the parent of the working directory as the version of the
2519 # changes the parent of the working directory as the version of the
2516 # files in the final amend commit
2520 # files in the final amend commit
2517 if node:
2521 if node:
2518 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2522 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2519
2523
2520 user = ctx.user()
2524 user = ctx.user()
2521 date = ctx.date()
2525 date = ctx.date()
2522 # Recompute copies (avoid recording a -> b -> a)
2526 # Recompute copies (avoid recording a -> b -> a)
2523 copied = copies.pathcopies(base, ctx)
2527 copied = copies.pathcopies(base, ctx)
2524 if old.p2:
2528 if old.p2:
2525 copied.update(copies.pathcopies(old.p2(), ctx))
2529 copied.update(copies.pathcopies(old.p2(), ctx))
2526
2530
2527 # Prune files which were reverted by the updates: if old
2531 # Prune files which were reverted by the updates: if old
2528 # introduced file X and our intermediate commit, node,
2532 # introduced file X and our intermediate commit, node,
2529 # renamed that file, then those two files are the same and
2533 # renamed that file, then those two files are the same and
2530 # we can discard X from our list of files. Likewise if X
2534 # we can discard X from our list of files. Likewise if X
2531 # was deleted, it's no longer relevant
2535 # was deleted, it's no longer relevant
2532 files.update(ctx.files())
2536 files.update(ctx.files())
2533
2537
2534 def samefile(f):
2538 def samefile(f):
2535 if f in ctx.manifest():
2539 if f in ctx.manifest():
2536 a = ctx.filectx(f)
2540 a = ctx.filectx(f)
2537 if f in base.manifest():
2541 if f in base.manifest():
2538 b = base.filectx(f)
2542 b = base.filectx(f)
2539 return (not a.cmp(b)
2543 return (not a.cmp(b)
2540 and a.flags() == b.flags())
2544 and a.flags() == b.flags())
2541 else:
2545 else:
2542 return False
2546 return False
2543 else:
2547 else:
2544 return f not in base.manifest()
2548 return f not in base.manifest()
2545 files = [f for f in files if not samefile(f)]
2549 files = [f for f in files if not samefile(f)]
2546
2550
2547 def filectxfn(repo, ctx_, path):
2551 def filectxfn(repo, ctx_, path):
2548 try:
2552 try:
2549 fctx = ctx[path]
2553 fctx = ctx[path]
2550 flags = fctx.flags()
2554 flags = fctx.flags()
2551 mctx = context.memfilectx(repo,
2555 mctx = context.memfilectx(repo,
2552 fctx.path(), fctx.data(),
2556 fctx.path(), fctx.data(),
2553 islink='l' in flags,
2557 islink='l' in flags,
2554 isexec='x' in flags,
2558 isexec='x' in flags,
2555 copied=copied.get(path))
2559 copied=copied.get(path))
2556 return mctx
2560 return mctx
2557 except KeyError:
2561 except KeyError:
2558 return None
2562 return None
2559 else:
2563 else:
2560 ui.note(_('copying changeset %s to %s\n') % (old, base))
2564 ui.note(_('copying changeset %s to %s\n') % (old, base))
2561
2565
2562 # Use version of files as in the old cset
2566 # Use version of files as in the old cset
2563 def filectxfn(repo, ctx_, path):
2567 def filectxfn(repo, ctx_, path):
2564 try:
2568 try:
2565 return old.filectx(path)
2569 return old.filectx(path)
2566 except KeyError:
2570 except KeyError:
2567 return None
2571 return None
2568
2572
2569 user = opts.get('user') or old.user()
2573 user = opts.get('user') or old.user()
2570 date = opts.get('date') or old.date()
2574 date = opts.get('date') or old.date()
2571 editform = mergeeditform(old, 'commit.amend')
2575 editform = mergeeditform(old, 'commit.amend')
2572 editor = getcommiteditor(editform=editform, **opts)
2576 editor = getcommiteditor(editform=editform, **opts)
2573 if not message:
2577 if not message:
2574 editor = getcommiteditor(edit=True, editform=editform)
2578 editor = getcommiteditor(edit=True, editform=editform)
2575 message = old.description()
2579 message = old.description()
2576
2580
2577 pureextra = extra.copy()
2581 pureextra = extra.copy()
2578 extra['amend_source'] = old.hex()
2582 extra['amend_source'] = old.hex()
2579
2583
2580 new = context.memctx(repo,
2584 new = context.memctx(repo,
2581 parents=[base.node(), old.p2().node()],
2585 parents=[base.node(), old.p2().node()],
2582 text=message,
2586 text=message,
2583 files=files,
2587 files=files,
2584 filectxfn=filectxfn,
2588 filectxfn=filectxfn,
2585 user=user,
2589 user=user,
2586 date=date,
2590 date=date,
2587 extra=extra,
2591 extra=extra,
2588 editor=editor)
2592 editor=editor)
2589
2593
2590 newdesc = changelog.stripdesc(new.description())
2594 newdesc = changelog.stripdesc(new.description())
2591 if ((not node)
2595 if ((not node)
2592 and newdesc == old.description()
2596 and newdesc == old.description()
2593 and user == old.user()
2597 and user == old.user()
2594 and date == old.date()
2598 and date == old.date()
2595 and pureextra == old.extra()):
2599 and pureextra == old.extra()):
2596 # nothing changed. continuing here would create a new node
2600 # nothing changed. continuing here would create a new node
2597 # anyway because of the amend_source noise.
2601 # anyway because of the amend_source noise.
2598 #
2602 #
2599 # This not what we expect from amend.
2603 # This not what we expect from amend.
2600 return old.node()
2604 return old.node()
2601
2605
2602 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2606 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2603 try:
2607 try:
2604 if opts.get('secret'):
2608 if opts.get('secret'):
2605 commitphase = 'secret'
2609 commitphase = 'secret'
2606 else:
2610 else:
2607 commitphase = old.phase()
2611 commitphase = old.phase()
2608 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2612 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2609 newid = repo.commitctx(new)
2613 newid = repo.commitctx(new)
2610 finally:
2614 finally:
2611 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2615 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2612 if newid != old.node():
2616 if newid != old.node():
2613 # Reroute the working copy parent to the new changeset
2617 # Reroute the working copy parent to the new changeset
2614 repo.setparents(newid, nullid)
2618 repo.setparents(newid, nullid)
2615
2619
2616 # Move bookmarks from old parent to amend commit
2620 # Move bookmarks from old parent to amend commit
2617 bms = repo.nodebookmarks(old.node())
2621 bms = repo.nodebookmarks(old.node())
2618 if bms:
2622 if bms:
2619 marks = repo._bookmarks
2623 marks = repo._bookmarks
2620 for bm in bms:
2624 for bm in bms:
2621 marks[bm] = newid
2625 marks[bm] = newid
2622 marks.write()
2626 marks.write()
2623 #commit the whole amend process
2627 #commit the whole amend process
2624 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2628 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2625 if createmarkers and newid != old.node():
2629 if createmarkers and newid != old.node():
2626 # mark the new changeset as successor of the rewritten one
2630 # mark the new changeset as successor of the rewritten one
2627 new = repo[newid]
2631 new = repo[newid]
2628 obs = [(old, (new,))]
2632 obs = [(old, (new,))]
2629 if node:
2633 if node:
2630 obs.append((ctx, ()))
2634 obs.append((ctx, ()))
2631
2635
2632 obsolete.createmarkers(repo, obs)
2636 obsolete.createmarkers(repo, obs)
2633 tr.close()
2637 tr.close()
2634 finally:
2638 finally:
2635 tr.release()
2639 tr.release()
2636 if not createmarkers and newid != old.node():
2640 if not createmarkers and newid != old.node():
2637 # Strip the intermediate commit (if there was one) and the amended
2641 # Strip the intermediate commit (if there was one) and the amended
2638 # commit
2642 # commit
2639 if node:
2643 if node:
2640 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2644 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2641 ui.note(_('stripping amended changeset %s\n') % old)
2645 ui.note(_('stripping amended changeset %s\n') % old)
2642 repair.strip(ui, repo, old.node(), topic='amend-backup')
2646 repair.strip(ui, repo, old.node(), topic='amend-backup')
2643 finally:
2647 finally:
2644 if newid is None:
2648 if newid is None:
2645 repo.dirstate.invalidate()
2649 repo.dirstate.invalidate()
2646 lockmod.release(lock, wlock)
2650 lockmod.release(lock, wlock)
2647 return newid
2651 return newid
2648
2652
2649 def commiteditor(repo, ctx, subs, editform=''):
2653 def commiteditor(repo, ctx, subs, editform=''):
2650 if ctx.description():
2654 if ctx.description():
2651 return ctx.description()
2655 return ctx.description()
2652 return commitforceeditor(repo, ctx, subs, editform=editform)
2656 return commitforceeditor(repo, ctx, subs, editform=editform)
2653
2657
2654 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2658 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2655 editform=''):
2659 editform=''):
2656 if not extramsg:
2660 if not extramsg:
2657 extramsg = _("Leave message empty to abort commit.")
2661 extramsg = _("Leave message empty to abort commit.")
2658
2662
2659 forms = [e for e in editform.split('.') if e]
2663 forms = [e for e in editform.split('.') if e]
2660 forms.insert(0, 'changeset')
2664 forms.insert(0, 'changeset')
2661 while forms:
2665 while forms:
2662 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2666 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2663 if tmpl:
2667 if tmpl:
2664 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2668 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2665 break
2669 break
2666 forms.pop()
2670 forms.pop()
2667 else:
2671 else:
2668 committext = buildcommittext(repo, ctx, subs, extramsg)
2672 committext = buildcommittext(repo, ctx, subs, extramsg)
2669
2673
2670 # run editor in the repository root
2674 # run editor in the repository root
2671 olddir = os.getcwd()
2675 olddir = os.getcwd()
2672 os.chdir(repo.root)
2676 os.chdir(repo.root)
2673 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2677 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2674 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2678 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2675 os.chdir(olddir)
2679 os.chdir(olddir)
2676
2680
2677 if finishdesc:
2681 if finishdesc:
2678 text = finishdesc(text)
2682 text = finishdesc(text)
2679 if not text.strip():
2683 if not text.strip():
2680 raise util.Abort(_("empty commit message"))
2684 raise util.Abort(_("empty commit message"))
2681
2685
2682 return text
2686 return text
2683
2687
2684 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2688 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2685 ui = repo.ui
2689 ui = repo.ui
2686 tmpl, mapfile = gettemplate(ui, tmpl, None)
2690 tmpl, mapfile = gettemplate(ui, tmpl, None)
2687
2691
2688 try:
2692 try:
2689 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2693 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2690 except SyntaxError, inst:
2694 except SyntaxError, inst:
2691 raise util.Abort(inst.args[0])
2695 raise util.Abort(inst.args[0])
2692
2696
2693 for k, v in repo.ui.configitems('committemplate'):
2697 for k, v in repo.ui.configitems('committemplate'):
2694 if k != 'changeset':
2698 if k != 'changeset':
2695 t.t.cache[k] = v
2699 t.t.cache[k] = v
2696
2700
2697 if not extramsg:
2701 if not extramsg:
2698 extramsg = '' # ensure that extramsg is string
2702 extramsg = '' # ensure that extramsg is string
2699
2703
2700 ui.pushbuffer()
2704 ui.pushbuffer()
2701 t.show(ctx, extramsg=extramsg)
2705 t.show(ctx, extramsg=extramsg)
2702 return ui.popbuffer()
2706 return ui.popbuffer()
2703
2707
2704 def buildcommittext(repo, ctx, subs, extramsg):
2708 def buildcommittext(repo, ctx, subs, extramsg):
2705 edittext = []
2709 edittext = []
2706 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2710 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2707 if ctx.description():
2711 if ctx.description():
2708 edittext.append(ctx.description())
2712 edittext.append(ctx.description())
2709 edittext.append("")
2713 edittext.append("")
2710 edittext.append("") # Empty line between message and comments.
2714 edittext.append("") # Empty line between message and comments.
2711 edittext.append(_("HG: Enter commit message."
2715 edittext.append(_("HG: Enter commit message."
2712 " Lines beginning with 'HG:' are removed."))
2716 " Lines beginning with 'HG:' are removed."))
2713 edittext.append("HG: %s" % extramsg)
2717 edittext.append("HG: %s" % extramsg)
2714 edittext.append("HG: --")
2718 edittext.append("HG: --")
2715 edittext.append(_("HG: user: %s") % ctx.user())
2719 edittext.append(_("HG: user: %s") % ctx.user())
2716 if ctx.p2():
2720 if ctx.p2():
2717 edittext.append(_("HG: branch merge"))
2721 edittext.append(_("HG: branch merge"))
2718 if ctx.branch():
2722 if ctx.branch():
2719 edittext.append(_("HG: branch '%s'") % ctx.branch())
2723 edittext.append(_("HG: branch '%s'") % ctx.branch())
2720 if bookmarks.iscurrent(repo):
2724 if bookmarks.iscurrent(repo):
2721 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2725 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2722 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2726 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2723 edittext.extend([_("HG: added %s") % f for f in added])
2727 edittext.extend([_("HG: added %s") % f for f in added])
2724 edittext.extend([_("HG: changed %s") % f for f in modified])
2728 edittext.extend([_("HG: changed %s") % f for f in modified])
2725 edittext.extend([_("HG: removed %s") % f for f in removed])
2729 edittext.extend([_("HG: removed %s") % f for f in removed])
2726 if not added and not modified and not removed:
2730 if not added and not modified and not removed:
2727 edittext.append(_("HG: no files changed"))
2731 edittext.append(_("HG: no files changed"))
2728 edittext.append("")
2732 edittext.append("")
2729
2733
2730 return "\n".join(edittext)
2734 return "\n".join(edittext)
2731
2735
2732 def commitstatus(repo, node, branch, bheads=None, opts={}):
2736 def commitstatus(repo, node, branch, bheads=None, opts={}):
2733 ctx = repo[node]
2737 ctx = repo[node]
2734 parents = ctx.parents()
2738 parents = ctx.parents()
2735
2739
2736 if (not opts.get('amend') and bheads and node not in bheads and not
2740 if (not opts.get('amend') and bheads and node not in bheads and not
2737 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2741 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2738 repo.ui.status(_('created new head\n'))
2742 repo.ui.status(_('created new head\n'))
2739 # The message is not printed for initial roots. For the other
2743 # The message is not printed for initial roots. For the other
2740 # changesets, it is printed in the following situations:
2744 # changesets, it is printed in the following situations:
2741 #
2745 #
2742 # Par column: for the 2 parents with ...
2746 # Par column: for the 2 parents with ...
2743 # N: null or no parent
2747 # N: null or no parent
2744 # B: parent is on another named branch
2748 # B: parent is on another named branch
2745 # C: parent is a regular non head changeset
2749 # C: parent is a regular non head changeset
2746 # H: parent was a branch head of the current branch
2750 # H: parent was a branch head of the current branch
2747 # Msg column: whether we print "created new head" message
2751 # Msg column: whether we print "created new head" message
2748 # In the following, it is assumed that there already exists some
2752 # In the following, it is assumed that there already exists some
2749 # initial branch heads of the current branch, otherwise nothing is
2753 # initial branch heads of the current branch, otherwise nothing is
2750 # printed anyway.
2754 # printed anyway.
2751 #
2755 #
2752 # Par Msg Comment
2756 # Par Msg Comment
2753 # N N y additional topo root
2757 # N N y additional topo root
2754 #
2758 #
2755 # B N y additional branch root
2759 # B N y additional branch root
2756 # C N y additional topo head
2760 # C N y additional topo head
2757 # H N n usual case
2761 # H N n usual case
2758 #
2762 #
2759 # B B y weird additional branch root
2763 # B B y weird additional branch root
2760 # C B y branch merge
2764 # C B y branch merge
2761 # H B n merge with named branch
2765 # H B n merge with named branch
2762 #
2766 #
2763 # C C y additional head from merge
2767 # C C y additional head from merge
2764 # C H n merge with a head
2768 # C H n merge with a head
2765 #
2769 #
2766 # H H n head merge: head count decreases
2770 # H H n head merge: head count decreases
2767
2771
2768 if not opts.get('close_branch'):
2772 if not opts.get('close_branch'):
2769 for r in parents:
2773 for r in parents:
2770 if r.closesbranch() and r.branch() == branch:
2774 if r.closesbranch() and r.branch() == branch:
2771 repo.ui.status(_('reopening closed branch head %d\n') % r)
2775 repo.ui.status(_('reopening closed branch head %d\n') % r)
2772
2776
2773 if repo.ui.debugflag:
2777 if repo.ui.debugflag:
2774 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2778 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2775 elif repo.ui.verbose:
2779 elif repo.ui.verbose:
2776 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2780 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2777
2781
2778 def revert(ui, repo, ctx, parents, *pats, **opts):
2782 def revert(ui, repo, ctx, parents, *pats, **opts):
2779 parent, p2 = parents
2783 parent, p2 = parents
2780 node = ctx.node()
2784 node = ctx.node()
2781
2785
2782 mf = ctx.manifest()
2786 mf = ctx.manifest()
2783 if node == p2:
2787 if node == p2:
2784 parent = p2
2788 parent = p2
2785 if node == parent:
2789 if node == parent:
2786 pmf = mf
2790 pmf = mf
2787 else:
2791 else:
2788 pmf = None
2792 pmf = None
2789
2793
2790 # need all matching names in dirstate and manifest of target rev,
2794 # need all matching names in dirstate and manifest of target rev,
2791 # so have to walk both. do not print errors if files exist in one
2795 # so have to walk both. do not print errors if files exist in one
2792 # but not other. in both cases, filesets should be evaluated against
2796 # but not other. in both cases, filesets should be evaluated against
2793 # workingctx to get consistent result (issue4497). this means 'set:**'
2797 # workingctx to get consistent result (issue4497). this means 'set:**'
2794 # cannot be used to select missing files from target rev.
2798 # cannot be used to select missing files from target rev.
2795
2799
2796 # `names` is a mapping for all elements in working copy and target revision
2800 # `names` is a mapping for all elements in working copy and target revision
2797 # The mapping is in the form:
2801 # The mapping is in the form:
2798 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2802 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2799 names = {}
2803 names = {}
2800
2804
2801 wlock = repo.wlock()
2805 wlock = repo.wlock()
2802 try:
2806 try:
2803 ## filling of the `names` mapping
2807 ## filling of the `names` mapping
2804 # walk dirstate to fill `names`
2808 # walk dirstate to fill `names`
2805
2809
2806 interactive = opts.get('interactive', False)
2810 interactive = opts.get('interactive', False)
2807 wctx = repo[None]
2811 wctx = repo[None]
2808 m = scmutil.match(wctx, pats, opts)
2812 m = scmutil.match(wctx, pats, opts)
2809
2813
2810 # we'll need this later
2814 # we'll need this later
2811 targetsubs = sorted(s for s in wctx.substate if m(s))
2815 targetsubs = sorted(s for s in wctx.substate if m(s))
2812
2816
2813 if not m.always():
2817 if not m.always():
2814 m.bad = lambda x, y: False
2818 m.bad = lambda x, y: False
2815 for abs in repo.walk(m):
2819 for abs in repo.walk(m):
2816 names[abs] = m.rel(abs), m.exact(abs)
2820 names[abs] = m.rel(abs), m.exact(abs)
2817
2821
2818 # walk target manifest to fill `names`
2822 # walk target manifest to fill `names`
2819
2823
2820 def badfn(path, msg):
2824 def badfn(path, msg):
2821 if path in names:
2825 if path in names:
2822 return
2826 return
2823 if path in ctx.substate:
2827 if path in ctx.substate:
2824 return
2828 return
2825 path_ = path + '/'
2829 path_ = path + '/'
2826 for f in names:
2830 for f in names:
2827 if f.startswith(path_):
2831 if f.startswith(path_):
2828 return
2832 return
2829 ui.warn("%s: %s\n" % (m.rel(path), msg))
2833 ui.warn("%s: %s\n" % (m.rel(path), msg))
2830
2834
2831 m.bad = badfn
2835 m.bad = badfn
2832 for abs in ctx.walk(m):
2836 for abs in ctx.walk(m):
2833 if abs not in names:
2837 if abs not in names:
2834 names[abs] = m.rel(abs), m.exact(abs)
2838 names[abs] = m.rel(abs), m.exact(abs)
2835
2839
2836 # Find status of all file in `names`.
2840 # Find status of all file in `names`.
2837 m = scmutil.matchfiles(repo, names)
2841 m = scmutil.matchfiles(repo, names)
2838
2842
2839 changes = repo.status(node1=node, match=m,
2843 changes = repo.status(node1=node, match=m,
2840 unknown=True, ignored=True, clean=True)
2844 unknown=True, ignored=True, clean=True)
2841 else:
2845 else:
2842 changes = repo.status(node1=node, match=m)
2846 changes = repo.status(node1=node, match=m)
2843 for kind in changes:
2847 for kind in changes:
2844 for abs in kind:
2848 for abs in kind:
2845 names[abs] = m.rel(abs), m.exact(abs)
2849 names[abs] = m.rel(abs), m.exact(abs)
2846
2850
2847 m = scmutil.matchfiles(repo, names)
2851 m = scmutil.matchfiles(repo, names)
2848
2852
2849 modified = set(changes.modified)
2853 modified = set(changes.modified)
2850 added = set(changes.added)
2854 added = set(changes.added)
2851 removed = set(changes.removed)
2855 removed = set(changes.removed)
2852 _deleted = set(changes.deleted)
2856 _deleted = set(changes.deleted)
2853 unknown = set(changes.unknown)
2857 unknown = set(changes.unknown)
2854 unknown.update(changes.ignored)
2858 unknown.update(changes.ignored)
2855 clean = set(changes.clean)
2859 clean = set(changes.clean)
2856 modadded = set()
2860 modadded = set()
2857
2861
2858 # split between files known in target manifest and the others
2862 # split between files known in target manifest and the others
2859 smf = set(mf)
2863 smf = set(mf)
2860
2864
2861 # determine the exact nature of the deleted changesets
2865 # determine the exact nature of the deleted changesets
2862 deladded = _deleted - smf
2866 deladded = _deleted - smf
2863 deleted = _deleted - deladded
2867 deleted = _deleted - deladded
2864
2868
2865 # We need to account for the state of the file in the dirstate,
2869 # We need to account for the state of the file in the dirstate,
2866 # even when we revert against something else than parent. This will
2870 # even when we revert against something else than parent. This will
2867 # slightly alter the behavior of revert (doing back up or not, delete
2871 # slightly alter the behavior of revert (doing back up or not, delete
2868 # or just forget etc).
2872 # or just forget etc).
2869 if parent == node:
2873 if parent == node:
2870 dsmodified = modified
2874 dsmodified = modified
2871 dsadded = added
2875 dsadded = added
2872 dsremoved = removed
2876 dsremoved = removed
2873 # store all local modifications, useful later for rename detection
2877 # store all local modifications, useful later for rename detection
2874 localchanges = dsmodified | dsadded
2878 localchanges = dsmodified | dsadded
2875 modified, added, removed = set(), set(), set()
2879 modified, added, removed = set(), set(), set()
2876 else:
2880 else:
2877 changes = repo.status(node1=parent, match=m)
2881 changes = repo.status(node1=parent, match=m)
2878 dsmodified = set(changes.modified)
2882 dsmodified = set(changes.modified)
2879 dsadded = set(changes.added)
2883 dsadded = set(changes.added)
2880 dsremoved = set(changes.removed)
2884 dsremoved = set(changes.removed)
2881 # store all local modifications, useful later for rename detection
2885 # store all local modifications, useful later for rename detection
2882 localchanges = dsmodified | dsadded
2886 localchanges = dsmodified | dsadded
2883
2887
2884 # only take into account for removes between wc and target
2888 # only take into account for removes between wc and target
2885 clean |= dsremoved - removed
2889 clean |= dsremoved - removed
2886 dsremoved &= removed
2890 dsremoved &= removed
2887 # distinct between dirstate remove and other
2891 # distinct between dirstate remove and other
2888 removed -= dsremoved
2892 removed -= dsremoved
2889
2893
2890 modadded = added & dsmodified
2894 modadded = added & dsmodified
2891 added -= modadded
2895 added -= modadded
2892
2896
2893 # tell newly modified apart.
2897 # tell newly modified apart.
2894 dsmodified &= modified
2898 dsmodified &= modified
2895 dsmodified |= modified & dsadded # dirstate added may needs backup
2899 dsmodified |= modified & dsadded # dirstate added may needs backup
2896 modified -= dsmodified
2900 modified -= dsmodified
2897
2901
2898 # We need to wait for some post-processing to update this set
2902 # We need to wait for some post-processing to update this set
2899 # before making the distinction. The dirstate will be used for
2903 # before making the distinction. The dirstate will be used for
2900 # that purpose.
2904 # that purpose.
2901 dsadded = added
2905 dsadded = added
2902
2906
2903 # in case of merge, files that are actually added can be reported as
2907 # in case of merge, files that are actually added can be reported as
2904 # modified, we need to post process the result
2908 # modified, we need to post process the result
2905 if p2 != nullid:
2909 if p2 != nullid:
2906 if pmf is None:
2910 if pmf is None:
2907 # only need parent manifest in the merge case,
2911 # only need parent manifest in the merge case,
2908 # so do not read by default
2912 # so do not read by default
2909 pmf = repo[parent].manifest()
2913 pmf = repo[parent].manifest()
2910 mergeadd = dsmodified - set(pmf)
2914 mergeadd = dsmodified - set(pmf)
2911 dsadded |= mergeadd
2915 dsadded |= mergeadd
2912 dsmodified -= mergeadd
2916 dsmodified -= mergeadd
2913
2917
2914 # if f is a rename, update `names` to also revert the source
2918 # if f is a rename, update `names` to also revert the source
2915 cwd = repo.getcwd()
2919 cwd = repo.getcwd()
2916 for f in localchanges:
2920 for f in localchanges:
2917 src = repo.dirstate.copied(f)
2921 src = repo.dirstate.copied(f)
2918 # XXX should we check for rename down to target node?
2922 # XXX should we check for rename down to target node?
2919 if src and src not in names and repo.dirstate[src] == 'r':
2923 if src and src not in names and repo.dirstate[src] == 'r':
2920 dsremoved.add(src)
2924 dsremoved.add(src)
2921 names[src] = (repo.pathto(src, cwd), True)
2925 names[src] = (repo.pathto(src, cwd), True)
2922
2926
2923 # distinguish between file to forget and the other
2927 # distinguish between file to forget and the other
2924 added = set()
2928 added = set()
2925 for abs in dsadded:
2929 for abs in dsadded:
2926 if repo.dirstate[abs] != 'a':
2930 if repo.dirstate[abs] != 'a':
2927 added.add(abs)
2931 added.add(abs)
2928 dsadded -= added
2932 dsadded -= added
2929
2933
2930 for abs in deladded:
2934 for abs in deladded:
2931 if repo.dirstate[abs] == 'a':
2935 if repo.dirstate[abs] == 'a':
2932 dsadded.add(abs)
2936 dsadded.add(abs)
2933 deladded -= dsadded
2937 deladded -= dsadded
2934
2938
2935 # For files marked as removed, we check if an unknown file is present at
2939 # For files marked as removed, we check if an unknown file is present at
2936 # the same path. If a such file exists it may need to be backed up.
2940 # the same path. If a such file exists it may need to be backed up.
2937 # Making the distinction at this stage helps have simpler backup
2941 # Making the distinction at this stage helps have simpler backup
2938 # logic.
2942 # logic.
2939 removunk = set()
2943 removunk = set()
2940 for abs in removed:
2944 for abs in removed:
2941 target = repo.wjoin(abs)
2945 target = repo.wjoin(abs)
2942 if os.path.lexists(target):
2946 if os.path.lexists(target):
2943 removunk.add(abs)
2947 removunk.add(abs)
2944 removed -= removunk
2948 removed -= removunk
2945
2949
2946 dsremovunk = set()
2950 dsremovunk = set()
2947 for abs in dsremoved:
2951 for abs in dsremoved:
2948 target = repo.wjoin(abs)
2952 target = repo.wjoin(abs)
2949 if os.path.lexists(target):
2953 if os.path.lexists(target):
2950 dsremovunk.add(abs)
2954 dsremovunk.add(abs)
2951 dsremoved -= dsremovunk
2955 dsremoved -= dsremovunk
2952
2956
2953 # action to be actually performed by revert
2957 # action to be actually performed by revert
2954 # (<list of file>, message>) tuple
2958 # (<list of file>, message>) tuple
2955 actions = {'revert': ([], _('reverting %s\n')),
2959 actions = {'revert': ([], _('reverting %s\n')),
2956 'add': ([], _('adding %s\n')),
2960 'add': ([], _('adding %s\n')),
2957 'remove': ([], _('removing %s\n')),
2961 'remove': ([], _('removing %s\n')),
2958 'drop': ([], _('removing %s\n')),
2962 'drop': ([], _('removing %s\n')),
2959 'forget': ([], _('forgetting %s\n')),
2963 'forget': ([], _('forgetting %s\n')),
2960 'undelete': ([], _('undeleting %s\n')),
2964 'undelete': ([], _('undeleting %s\n')),
2961 'noop': (None, _('no changes needed to %s\n')),
2965 'noop': (None, _('no changes needed to %s\n')),
2962 'unknown': (None, _('file not managed: %s\n')),
2966 'unknown': (None, _('file not managed: %s\n')),
2963 }
2967 }
2964
2968
2965 # "constant" that convey the backup strategy.
2969 # "constant" that convey the backup strategy.
2966 # All set to `discard` if `no-backup` is set do avoid checking
2970 # All set to `discard` if `no-backup` is set do avoid checking
2967 # no_backup lower in the code.
2971 # no_backup lower in the code.
2968 # These values are ordered for comparison purposes
2972 # These values are ordered for comparison purposes
2969 backup = 2 # unconditionally do backup
2973 backup = 2 # unconditionally do backup
2970 check = 1 # check if the existing file differs from target
2974 check = 1 # check if the existing file differs from target
2971 discard = 0 # never do backup
2975 discard = 0 # never do backup
2972 if opts.get('no_backup'):
2976 if opts.get('no_backup'):
2973 backup = check = discard
2977 backup = check = discard
2974
2978
2975 backupanddel = actions['remove']
2979 backupanddel = actions['remove']
2976 if not opts.get('no_backup'):
2980 if not opts.get('no_backup'):
2977 backupanddel = actions['drop']
2981 backupanddel = actions['drop']
2978
2982
2979 disptable = (
2983 disptable = (
2980 # dispatch table:
2984 # dispatch table:
2981 # file state
2985 # file state
2982 # action
2986 # action
2983 # make backup
2987 # make backup
2984
2988
2985 ## Sets that results that will change file on disk
2989 ## Sets that results that will change file on disk
2986 # Modified compared to target, no local change
2990 # Modified compared to target, no local change
2987 (modified, actions['revert'], discard),
2991 (modified, actions['revert'], discard),
2988 # Modified compared to target, but local file is deleted
2992 # Modified compared to target, but local file is deleted
2989 (deleted, actions['revert'], discard),
2993 (deleted, actions['revert'], discard),
2990 # Modified compared to target, local change
2994 # Modified compared to target, local change
2991 (dsmodified, actions['revert'], backup),
2995 (dsmodified, actions['revert'], backup),
2992 # Added since target
2996 # Added since target
2993 (added, actions['remove'], discard),
2997 (added, actions['remove'], discard),
2994 # Added in working directory
2998 # Added in working directory
2995 (dsadded, actions['forget'], discard),
2999 (dsadded, actions['forget'], discard),
2996 # Added since target, have local modification
3000 # Added since target, have local modification
2997 (modadded, backupanddel, backup),
3001 (modadded, backupanddel, backup),
2998 # Added since target but file is missing in working directory
3002 # Added since target but file is missing in working directory
2999 (deladded, actions['drop'], discard),
3003 (deladded, actions['drop'], discard),
3000 # Removed since target, before working copy parent
3004 # Removed since target, before working copy parent
3001 (removed, actions['add'], discard),
3005 (removed, actions['add'], discard),
3002 # Same as `removed` but an unknown file exists at the same path
3006 # Same as `removed` but an unknown file exists at the same path
3003 (removunk, actions['add'], check),
3007 (removunk, actions['add'], check),
3004 # Removed since targe, marked as such in working copy parent
3008 # Removed since targe, marked as such in working copy parent
3005 (dsremoved, actions['undelete'], discard),
3009 (dsremoved, actions['undelete'], discard),
3006 # Same as `dsremoved` but an unknown file exists at the same path
3010 # Same as `dsremoved` but an unknown file exists at the same path
3007 (dsremovunk, actions['undelete'], check),
3011 (dsremovunk, actions['undelete'], check),
3008 ## the following sets does not result in any file changes
3012 ## the following sets does not result in any file changes
3009 # File with no modification
3013 # File with no modification
3010 (clean, actions['noop'], discard),
3014 (clean, actions['noop'], discard),
3011 # Existing file, not tracked anywhere
3015 # Existing file, not tracked anywhere
3012 (unknown, actions['unknown'], discard),
3016 (unknown, actions['unknown'], discard),
3013 )
3017 )
3014
3018
3015 for abs, (rel, exact) in sorted(names.items()):
3019 for abs, (rel, exact) in sorted(names.items()):
3016 # target file to be touch on disk (relative to cwd)
3020 # target file to be touch on disk (relative to cwd)
3017 target = repo.wjoin(abs)
3021 target = repo.wjoin(abs)
3018 # search the entry in the dispatch table.
3022 # search the entry in the dispatch table.
3019 # if the file is in any of these sets, it was touched in the working
3023 # if the file is in any of these sets, it was touched in the working
3020 # directory parent and we are sure it needs to be reverted.
3024 # directory parent and we are sure it needs to be reverted.
3021 for table, (xlist, msg), dobackup in disptable:
3025 for table, (xlist, msg), dobackup in disptable:
3022 if abs not in table:
3026 if abs not in table:
3023 continue
3027 continue
3024 if xlist is not None:
3028 if xlist is not None:
3025 xlist.append(abs)
3029 xlist.append(abs)
3026 if dobackup and (backup <= dobackup
3030 if dobackup and (backup <= dobackup
3027 or wctx[abs].cmp(ctx[abs])):
3031 or wctx[abs].cmp(ctx[abs])):
3028 bakname = "%s.orig" % rel
3032 bakname = "%s.orig" % rel
3029 ui.note(_('saving current version of %s as %s\n') %
3033 ui.note(_('saving current version of %s as %s\n') %
3030 (rel, bakname))
3034 (rel, bakname))
3031 if not opts.get('dry_run'):
3035 if not opts.get('dry_run'):
3032 if interactive:
3036 if interactive:
3033 util.copyfile(target, bakname)
3037 util.copyfile(target, bakname)
3034 else:
3038 else:
3035 util.rename(target, bakname)
3039 util.rename(target, bakname)
3036 if ui.verbose or not exact:
3040 if ui.verbose or not exact:
3037 if not isinstance(msg, basestring):
3041 if not isinstance(msg, basestring):
3038 msg = msg(abs)
3042 msg = msg(abs)
3039 ui.status(msg % rel)
3043 ui.status(msg % rel)
3040 elif exact:
3044 elif exact:
3041 ui.warn(msg % rel)
3045 ui.warn(msg % rel)
3042 break
3046 break
3043
3047
3044 if not opts.get('dry_run'):
3048 if not opts.get('dry_run'):
3045 needdata = ('revert', 'add', 'undelete')
3049 needdata = ('revert', 'add', 'undelete')
3046 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3050 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3047 _performrevert(repo, parents, ctx, actions, interactive)
3051 _performrevert(repo, parents, ctx, actions, interactive)
3048
3052
3049 if targetsubs:
3053 if targetsubs:
3050 # Revert the subrepos on the revert list
3054 # Revert the subrepos on the revert list
3051 for sub in targetsubs:
3055 for sub in targetsubs:
3052 try:
3056 try:
3053 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3057 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3054 except KeyError:
3058 except KeyError:
3055 raise util.Abort("subrepository '%s' does not exist in %s!"
3059 raise util.Abort("subrepository '%s' does not exist in %s!"
3056 % (sub, short(ctx.node())))
3060 % (sub, short(ctx.node())))
3057 finally:
3061 finally:
3058 wlock.release()
3062 wlock.release()
3059
3063
3060 def _revertprefetch(repo, ctx, *files):
3064 def _revertprefetch(repo, ctx, *files):
3061 """Let extension changing the storage layer prefetch content"""
3065 """Let extension changing the storage layer prefetch content"""
3062 pass
3066 pass
3063
3067
3064 def _performrevert(repo, parents, ctx, actions, interactive=False):
3068 def _performrevert(repo, parents, ctx, actions, interactive=False):
3065 """function that actually perform all the actions computed for revert
3069 """function that actually perform all the actions computed for revert
3066
3070
3067 This is an independent function to let extension to plug in and react to
3071 This is an independent function to let extension to plug in and react to
3068 the imminent revert.
3072 the imminent revert.
3069
3073
3070 Make sure you have the working directory locked when calling this function.
3074 Make sure you have the working directory locked when calling this function.
3071 """
3075 """
3072 parent, p2 = parents
3076 parent, p2 = parents
3073 node = ctx.node()
3077 node = ctx.node()
3074 def checkout(f):
3078 def checkout(f):
3075 fc = ctx[f]
3079 fc = ctx[f]
3076 repo.wwrite(f, fc.data(), fc.flags())
3080 repo.wwrite(f, fc.data(), fc.flags())
3077
3081
3078 audit_path = pathutil.pathauditor(repo.root)
3082 audit_path = pathutil.pathauditor(repo.root)
3079 for f in actions['forget'][0]:
3083 for f in actions['forget'][0]:
3080 repo.dirstate.drop(f)
3084 repo.dirstate.drop(f)
3081 for f in actions['remove'][0]:
3085 for f in actions['remove'][0]:
3082 audit_path(f)
3086 audit_path(f)
3083 util.unlinkpath(repo.wjoin(f))
3087 util.unlinkpath(repo.wjoin(f))
3084 repo.dirstate.remove(f)
3088 repo.dirstate.remove(f)
3085 for f in actions['drop'][0]:
3089 for f in actions['drop'][0]:
3086 audit_path(f)
3090 audit_path(f)
3087 repo.dirstate.remove(f)
3091 repo.dirstate.remove(f)
3088
3092
3089 normal = None
3093 normal = None
3090 if node == parent:
3094 if node == parent:
3091 # We're reverting to our parent. If possible, we'd like status
3095 # We're reverting to our parent. If possible, we'd like status
3092 # to report the file as clean. We have to use normallookup for
3096 # to report the file as clean. We have to use normallookup for
3093 # merges to avoid losing information about merged/dirty files.
3097 # merges to avoid losing information about merged/dirty files.
3094 if p2 != nullid:
3098 if p2 != nullid:
3095 normal = repo.dirstate.normallookup
3099 normal = repo.dirstate.normallookup
3096 else:
3100 else:
3097 normal = repo.dirstate.normal
3101 normal = repo.dirstate.normal
3098
3102
3099 if interactive:
3103 if interactive:
3100 # Prompt the user for changes to revert
3104 # Prompt the user for changes to revert
3101 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3105 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3102 m = scmutil.match(ctx, torevert, {})
3106 m = scmutil.match(ctx, torevert, {})
3103 diff = patch.diff(repo, None, ctx.node(), m)
3107 diff = patch.diff(repo, None, ctx.node(), m)
3104 originalchunks = patch.parsepatch(diff)
3108 originalchunks = patch.parsepatch(diff)
3105 try:
3109 try:
3106 chunks = recordfilter(repo.ui, originalchunks)
3110 chunks = recordfilter(repo.ui, originalchunks)
3107 except patch.PatchError, err:
3111 except patch.PatchError, err:
3108 raise util.Abort(_('error parsing patch: %s') % err)
3112 raise util.Abort(_('error parsing patch: %s') % err)
3109
3113
3110 # Apply changes
3114 # Apply changes
3111 fp = cStringIO.StringIO()
3115 fp = cStringIO.StringIO()
3112 for c in chunks:
3116 for c in chunks:
3113 c.write(fp)
3117 c.write(fp)
3114 dopatch = fp.tell()
3118 dopatch = fp.tell()
3115 fp.seek(0)
3119 fp.seek(0)
3116 if dopatch:
3120 if dopatch:
3117 try:
3121 try:
3118 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3122 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3119 except patch.PatchError, err:
3123 except patch.PatchError, err:
3120 raise util.Abort(str(err))
3124 raise util.Abort(str(err))
3121 del fp
3125 del fp
3122
3126
3123 for f in actions['revert'][0]:
3127 for f in actions['revert'][0]:
3124 if normal:
3128 if normal:
3125 normal(f)
3129 normal(f)
3126
3130
3127 else:
3131 else:
3128 for f in actions['revert'][0]:
3132 for f in actions['revert'][0]:
3129 checkout(f)
3133 checkout(f)
3130 if normal:
3134 if normal:
3131 normal(f)
3135 normal(f)
3132
3136
3133 for f in actions['add'][0]:
3137 for f in actions['add'][0]:
3134 checkout(f)
3138 checkout(f)
3135 repo.dirstate.add(f)
3139 repo.dirstate.add(f)
3136
3140
3137 normal = repo.dirstate.normallookup
3141 normal = repo.dirstate.normallookup
3138 if node == parent and p2 == nullid:
3142 if node == parent and p2 == nullid:
3139 normal = repo.dirstate.normal
3143 normal = repo.dirstate.normal
3140 for f in actions['undelete'][0]:
3144 for f in actions['undelete'][0]:
3141 checkout(f)
3145 checkout(f)
3142 normal(f)
3146 normal(f)
3143
3147
3144 copied = copies.pathcopies(repo[parent], ctx)
3148 copied = copies.pathcopies(repo[parent], ctx)
3145
3149
3146 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3150 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3147 if f in copied:
3151 if f in copied:
3148 repo.dirstate.copy(copied[f], f)
3152 repo.dirstate.copy(copied[f], f)
3149
3153
3150 def command(table):
3154 def command(table):
3151 """Returns a function object to be used as a decorator for making commands.
3155 """Returns a function object to be used as a decorator for making commands.
3152
3156
3153 This function receives a command table as its argument. The table should
3157 This function receives a command table as its argument. The table should
3154 be a dict.
3158 be a dict.
3155
3159
3156 The returned function can be used as a decorator for adding commands
3160 The returned function can be used as a decorator for adding commands
3157 to that command table. This function accepts multiple arguments to define
3161 to that command table. This function accepts multiple arguments to define
3158 a command.
3162 a command.
3159
3163
3160 The first argument is the command name.
3164 The first argument is the command name.
3161
3165
3162 The options argument is an iterable of tuples defining command arguments.
3166 The options argument is an iterable of tuples defining command arguments.
3163 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3167 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3164
3168
3165 The synopsis argument defines a short, one line summary of how to use the
3169 The synopsis argument defines a short, one line summary of how to use the
3166 command. This shows up in the help output.
3170 command. This shows up in the help output.
3167
3171
3168 The norepo argument defines whether the command does not require a
3172 The norepo argument defines whether the command does not require a
3169 local repository. Most commands operate against a repository, thus the
3173 local repository. Most commands operate against a repository, thus the
3170 default is False.
3174 default is False.
3171
3175
3172 The optionalrepo argument defines whether the command optionally requires
3176 The optionalrepo argument defines whether the command optionally requires
3173 a local repository.
3177 a local repository.
3174
3178
3175 The inferrepo argument defines whether to try to find a repository from the
3179 The inferrepo argument defines whether to try to find a repository from the
3176 command line arguments. If True, arguments will be examined for potential
3180 command line arguments. If True, arguments will be examined for potential
3177 repository locations. See ``findrepo()``. If a repository is found, it
3181 repository locations. See ``findrepo()``. If a repository is found, it
3178 will be used.
3182 will be used.
3179 """
3183 """
3180 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3184 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3181 inferrepo=False):
3185 inferrepo=False):
3182 def decorator(func):
3186 def decorator(func):
3183 if synopsis:
3187 if synopsis:
3184 table[name] = func, list(options), synopsis
3188 table[name] = func, list(options), synopsis
3185 else:
3189 else:
3186 table[name] = func, list(options)
3190 table[name] = func, list(options)
3187
3191
3188 if norepo:
3192 if norepo:
3189 # Avoid import cycle.
3193 # Avoid import cycle.
3190 import commands
3194 import commands
3191 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3195 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3192
3196
3193 if optionalrepo:
3197 if optionalrepo:
3194 import commands
3198 import commands
3195 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3199 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3196
3200
3197 if inferrepo:
3201 if inferrepo:
3198 import commands
3202 import commands
3199 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3203 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3200
3204
3201 return func
3205 return func
3202 return decorator
3206 return decorator
3203
3207
3204 return cmd
3208 return cmd
3205
3209
3206 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3210 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3207 # commands.outgoing. "missing" is "missing" of the result of
3211 # commands.outgoing. "missing" is "missing" of the result of
3208 # "findcommonoutgoing()"
3212 # "findcommonoutgoing()"
3209 outgoinghooks = util.hooks()
3213 outgoinghooks = util.hooks()
3210
3214
3211 # a list of (ui, repo) functions called by commands.summary
3215 # a list of (ui, repo) functions called by commands.summary
3212 summaryhooks = util.hooks()
3216 summaryhooks = util.hooks()
3213
3217
3214 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3218 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3215 #
3219 #
3216 # functions should return tuple of booleans below, if 'changes' is None:
3220 # functions should return tuple of booleans below, if 'changes' is None:
3217 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3221 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3218 #
3222 #
3219 # otherwise, 'changes' is a tuple of tuples below:
3223 # otherwise, 'changes' is a tuple of tuples below:
3220 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3224 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3221 # - (desturl, destbranch, destpeer, outgoing)
3225 # - (desturl, destbranch, destpeer, outgoing)
3222 summaryremotehooks = util.hooks()
3226 summaryremotehooks = util.hooks()
3223
3227
3224 # A list of state files kept by multistep operations like graft.
3228 # A list of state files kept by multistep operations like graft.
3225 # Since graft cannot be aborted, it is considered 'clearable' by update.
3229 # Since graft cannot be aborted, it is considered 'clearable' by update.
3226 # note: bisect is intentionally excluded
3230 # note: bisect is intentionally excluded
3227 # (state file, clearable, allowcommit, error, hint)
3231 # (state file, clearable, allowcommit, error, hint)
3228 unfinishedstates = [
3232 unfinishedstates = [
3229 ('graftstate', True, False, _('graft in progress'),
3233 ('graftstate', True, False, _('graft in progress'),
3230 _("use 'hg graft --continue' or 'hg update' to abort")),
3234 _("use 'hg graft --continue' or 'hg update' to abort")),
3231 ('updatestate', True, False, _('last update was interrupted'),
3235 ('updatestate', True, False, _('last update was interrupted'),
3232 _("use 'hg update' to get a consistent checkout"))
3236 _("use 'hg update' to get a consistent checkout"))
3233 ]
3237 ]
3234
3238
3235 def checkunfinished(repo, commit=False):
3239 def checkunfinished(repo, commit=False):
3236 '''Look for an unfinished multistep operation, like graft, and abort
3240 '''Look for an unfinished multistep operation, like graft, and abort
3237 if found. It's probably good to check this right before
3241 if found. It's probably good to check this right before
3238 bailifchanged().
3242 bailifchanged().
3239 '''
3243 '''
3240 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3244 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3241 if commit and allowcommit:
3245 if commit and allowcommit:
3242 continue
3246 continue
3243 if repo.vfs.exists(f):
3247 if repo.vfs.exists(f):
3244 raise util.Abort(msg, hint=hint)
3248 raise util.Abort(msg, hint=hint)
3245
3249
3246 def clearunfinished(repo):
3250 def clearunfinished(repo):
3247 '''Check for unfinished operations (as above), and clear the ones
3251 '''Check for unfinished operations (as above), and clear the ones
3248 that are clearable.
3252 that are clearable.
3249 '''
3253 '''
3250 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3254 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3251 if not clearable and repo.vfs.exists(f):
3255 if not clearable and repo.vfs.exists(f):
3252 raise util.Abort(msg, hint=hint)
3256 raise util.Abort(msg, hint=hint)
3253 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3257 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3254 if clearable and repo.vfs.exists(f):
3258 if clearable and repo.vfs.exists(f):
3255 util.unlink(repo.join(f))
3259 util.unlink(repo.join(f))
@@ -1,2031 +1,2052
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
1636 $ hg log -r 'wdir()' --debug
1637 changeset: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08+
1637 changeset: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08+
1638 phase: draft
1638 phase: draft
1639 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1639 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1640 parent: -1:0000000000000000000000000000000000000000
1640 parent: -1:0000000000000000000000000000000000000000
1641 user: test
1641 user: test
1642 date: [A-Za-z0-9:+ ]+ (re)
1642 date: [A-Za-z0-9:+ ]+ (re)
1643 extra: branch=default
1643 extra: branch=default
1644
1644
1645 $ hg log -r 'wdir()' -Tjson
1645 $ hg log -r 'wdir()' -Tjson
1646 [
1646 [
1647 {
1647 {
1648 "rev": null,
1648 "rev": null,
1649 "node": null,
1649 "node": null,
1650 "branch": "default",
1650 "branch": "default",
1651 "phase": "draft",
1651 "phase": "draft",
1652 "user": "test",
1652 "user": "test",
1653 "date": [*, 0], (glob)
1653 "date": [*, 0], (glob)
1654 "desc": "",
1654 "desc": "",
1655 "bookmarks": [],
1655 "bookmarks": [],
1656 "tags": ["tip"],
1656 "tags": ["tip"],
1657 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"]
1657 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"]
1658 }
1658 }
1659 ]
1659 ]
1660
1660
1661 $ hg log -r 'wdir()' -Tjson -q
1661 $ hg log -r 'wdir()' -Tjson -q
1662 [
1662 [
1663 {
1663 {
1664 "rev": null,
1664 "rev": null,
1665 "node": null
1665 "node": null
1666 }
1666 }
1667 ]
1667 ]
1668
1668
1669 $ hg log -r 'wdir()' -Tjson --debug
1670 [
1671 {
1672 "rev": null,
1673 "node": null,
1674 "branch": "default",
1675 "phase": "draft",
1676 "user": "test",
1677 "date": [*, 0], (glob)
1678 "desc": "",
1679 "bookmarks": [],
1680 "tags": ["tip"],
1681 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
1682 "manifest": null,
1683 "extra": {"branch": "default"},
1684 "modified": [],
1685 "added": [],
1686 "removed": []
1687 }
1688 ]
1689
1669 Check that adding an arbitrary name shows up in log automatically
1690 Check that adding an arbitrary name shows up in log automatically
1670
1691
1671 $ cat > ../names.py <<EOF
1692 $ cat > ../names.py <<EOF
1672 > """A small extension to test adding arbitrary names to a repo"""
1693 > """A small extension to test adding arbitrary names to a repo"""
1673 > from mercurial.namespaces import namespace
1694 > from mercurial.namespaces import namespace
1674 >
1695 >
1675 > def reposetup(ui, repo):
1696 > def reposetup(ui, repo):
1676 > foo = {'foo': repo[0].node()}
1697 > foo = {'foo': repo[0].node()}
1677 > names = lambda r: foo.keys()
1698 > names = lambda r: foo.keys()
1678 > namemap = lambda r, name: foo.get(name)
1699 > namemap = lambda r, name: foo.get(name)
1679 > nodemap = lambda r, node: [name for name, n in foo.iteritems()
1700 > nodemap = lambda r, node: [name for name, n in foo.iteritems()
1680 > if n == node]
1701 > if n == node]
1681 > ns = namespace("bars", templatename="bar", logname="barlog",
1702 > ns = namespace("bars", templatename="bar", logname="barlog",
1682 > colorname="barcolor", listnames=names, namemap=namemap,
1703 > colorname="barcolor", listnames=names, namemap=namemap,
1683 > nodemap=nodemap)
1704 > nodemap=nodemap)
1684 >
1705 >
1685 > repo.names.addnamespace(ns)
1706 > repo.names.addnamespace(ns)
1686 > EOF
1707 > EOF
1687
1708
1688 $ hg --config extensions.names=../names.py log -r 0
1709 $ hg --config extensions.names=../names.py log -r 0
1689 changeset: 0:65624cd9070a
1710 changeset: 0:65624cd9070a
1690 tag: tip
1711 tag: tip
1691 barlog: foo
1712 barlog: foo
1692 user: test
1713 user: test
1693 date: Thu Jan 01 00:00:00 1970 +0000
1714 date: Thu Jan 01 00:00:00 1970 +0000
1694 summary: a bunch of weird directories
1715 summary: a bunch of weird directories
1695
1716
1696 $ hg --config extensions.names=../names.py \
1717 $ hg --config extensions.names=../names.py \
1697 > --config extensions.color= --config color.log.barcolor=red \
1718 > --config extensions.color= --config color.log.barcolor=red \
1698 > --color=always log -r 0
1719 > --color=always log -r 0
1699 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
1720 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
1700 tag: tip
1721 tag: tip
1701 \x1b[0;31mbarlog: foo\x1b[0m (esc)
1722 \x1b[0;31mbarlog: foo\x1b[0m (esc)
1702 user: test
1723 user: test
1703 date: Thu Jan 01 00:00:00 1970 +0000
1724 date: Thu Jan 01 00:00:00 1970 +0000
1704 summary: a bunch of weird directories
1725 summary: a bunch of weird directories
1705
1726
1706 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
1727 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
1707 foo
1728 foo
1708
1729
1709 $ cd ..
1730 $ cd ..
1710
1731
1711 hg log -f dir across branches
1732 hg log -f dir across branches
1712
1733
1713 $ hg init acrossbranches
1734 $ hg init acrossbranches
1714 $ cd acrossbranches
1735 $ cd acrossbranches
1715 $ mkdir d
1736 $ mkdir d
1716 $ echo a > d/a && hg ci -Aqm a
1737 $ echo a > d/a && hg ci -Aqm a
1717 $ echo b > d/a && hg ci -Aqm b
1738 $ echo b > d/a && hg ci -Aqm b
1718 $ hg up -q 0
1739 $ hg up -q 0
1719 $ echo b > d/a && hg ci -Aqm c
1740 $ echo b > d/a && hg ci -Aqm c
1720 $ hg log -f d -T '{desc}' -G
1741 $ hg log -f d -T '{desc}' -G
1721 @ c
1742 @ c
1722 |
1743 |
1723 o a
1744 o a
1724
1745
1725 Ensure that largefiles doesn't interfere with following a normal file
1746 Ensure that largefiles doesn't interfere with following a normal file
1726 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
1747 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
1727 @ c
1748 @ c
1728 |
1749 |
1729 o a
1750 o a
1730
1751
1731 $ hg log -f d/a -T '{desc}' -G
1752 $ hg log -f d/a -T '{desc}' -G
1732 @ c
1753 @ c
1733 |
1754 |
1734 o a
1755 o a
1735
1756
1736 $ cd ..
1757 $ cd ..
1737
1758
1738 hg log -f with linkrev pointing to another branch
1759 hg log -f with linkrev pointing to another branch
1739 -------------------------------------------------
1760 -------------------------------------------------
1740
1761
1741 create history with a filerev whose linkrev points to another branch
1762 create history with a filerev whose linkrev points to another branch
1742
1763
1743 $ hg init branchedlinkrev
1764 $ hg init branchedlinkrev
1744 $ cd branchedlinkrev
1765 $ cd branchedlinkrev
1745 $ echo 1 > a
1766 $ echo 1 > a
1746 $ hg commit -Am 'content1'
1767 $ hg commit -Am 'content1'
1747 adding a
1768 adding a
1748 $ echo 2 > a
1769 $ echo 2 > a
1749 $ hg commit -m 'content2'
1770 $ hg commit -m 'content2'
1750 $ hg up --rev 'desc(content1)'
1771 $ hg up --rev 'desc(content1)'
1751 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1772 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1752 $ echo unrelated > unrelated
1773 $ echo unrelated > unrelated
1753 $ hg commit -Am 'unrelated'
1774 $ hg commit -Am 'unrelated'
1754 adding unrelated
1775 adding unrelated
1755 created new head
1776 created new head
1756 $ hg graft -r 'desc(content2)'
1777 $ hg graft -r 'desc(content2)'
1757 grafting 1:2294ae80ad84 "content2"
1778 grafting 1:2294ae80ad84 "content2"
1758 $ echo 3 > a
1779 $ echo 3 > a
1759 $ hg commit -m 'content3'
1780 $ hg commit -m 'content3'
1760 $ hg log -G
1781 $ hg log -G
1761 @ changeset: 4:50b9b36e9c5d
1782 @ changeset: 4:50b9b36e9c5d
1762 | tag: tip
1783 | tag: tip
1763 | user: test
1784 | user: test
1764 | date: Thu Jan 01 00:00:00 1970 +0000
1785 | date: Thu Jan 01 00:00:00 1970 +0000
1765 | summary: content3
1786 | summary: content3
1766 |
1787 |
1767 o changeset: 3:15b2327059e5
1788 o changeset: 3:15b2327059e5
1768 | user: test
1789 | user: test
1769 | date: Thu Jan 01 00:00:00 1970 +0000
1790 | date: Thu Jan 01 00:00:00 1970 +0000
1770 | summary: content2
1791 | summary: content2
1771 |
1792 |
1772 o changeset: 2:2029acd1168c
1793 o changeset: 2:2029acd1168c
1773 | parent: 0:ae0a3c9f9e95
1794 | parent: 0:ae0a3c9f9e95
1774 | user: test
1795 | user: test
1775 | date: Thu Jan 01 00:00:00 1970 +0000
1796 | date: Thu Jan 01 00:00:00 1970 +0000
1776 | summary: unrelated
1797 | summary: unrelated
1777 |
1798 |
1778 | o changeset: 1:2294ae80ad84
1799 | o changeset: 1:2294ae80ad84
1779 |/ user: test
1800 |/ user: test
1780 | date: Thu Jan 01 00:00:00 1970 +0000
1801 | date: Thu Jan 01 00:00:00 1970 +0000
1781 | summary: content2
1802 | summary: content2
1782 |
1803 |
1783 o changeset: 0:ae0a3c9f9e95
1804 o changeset: 0:ae0a3c9f9e95
1784 user: test
1805 user: test
1785 date: Thu Jan 01 00:00:00 1970 +0000
1806 date: Thu Jan 01 00:00:00 1970 +0000
1786 summary: content1
1807 summary: content1
1787
1808
1788
1809
1789 log -f on the file should list the graft result.
1810 log -f on the file should list the graft result.
1790
1811
1791 $ hg log -Gf a
1812 $ hg log -Gf a
1792 @ changeset: 4:50b9b36e9c5d
1813 @ changeset: 4:50b9b36e9c5d
1793 | tag: tip
1814 | tag: tip
1794 | user: test
1815 | user: test
1795 | date: Thu Jan 01 00:00:00 1970 +0000
1816 | date: Thu Jan 01 00:00:00 1970 +0000
1796 | summary: content3
1817 | summary: content3
1797 |
1818 |
1798 o changeset: 3:15b2327059e5
1819 o changeset: 3:15b2327059e5
1799 | user: test
1820 | user: test
1800 | date: Thu Jan 01 00:00:00 1970 +0000
1821 | date: Thu Jan 01 00:00:00 1970 +0000
1801 | summary: content2
1822 | summary: content2
1802 |
1823 |
1803 o changeset: 0:ae0a3c9f9e95
1824 o changeset: 0:ae0a3c9f9e95
1804 user: test
1825 user: test
1805 date: Thu Jan 01 00:00:00 1970 +0000
1826 date: Thu Jan 01 00:00:00 1970 +0000
1806 summary: content1
1827 summary: content1
1807
1828
1808
1829
1809 plain log lists the original version
1830 plain log lists the original version
1810 (XXX we should probably list both)
1831 (XXX we should probably list both)
1811
1832
1812 $ hg log -G a
1833 $ hg log -G a
1813 @ changeset: 4:50b9b36e9c5d
1834 @ changeset: 4:50b9b36e9c5d
1814 | tag: tip
1835 | tag: tip
1815 | user: test
1836 | user: test
1816 | date: Thu Jan 01 00:00:00 1970 +0000
1837 | date: Thu Jan 01 00:00:00 1970 +0000
1817 | summary: content3
1838 | summary: content3
1818 |
1839 |
1819 | o changeset: 1:2294ae80ad84
1840 | o changeset: 1:2294ae80ad84
1820 |/ user: test
1841 |/ user: test
1821 | date: Thu Jan 01 00:00:00 1970 +0000
1842 | date: Thu Jan 01 00:00:00 1970 +0000
1822 | summary: content2
1843 | summary: content2
1823 |
1844 |
1824 o changeset: 0:ae0a3c9f9e95
1845 o changeset: 0:ae0a3c9f9e95
1825 user: test
1846 user: test
1826 date: Thu Jan 01 00:00:00 1970 +0000
1847 date: Thu Jan 01 00:00:00 1970 +0000
1827 summary: content1
1848 summary: content1
1828
1849
1829
1850
1830 hg log -f from the grafted changeset
1851 hg log -f from the grafted changeset
1831 (The bootstrap should properly take the topology in account)
1852 (The bootstrap should properly take the topology in account)
1832
1853
1833 $ hg up 'desc(content3)^'
1854 $ hg up 'desc(content3)^'
1834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1855 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1835 $ hg log -Gf a
1856 $ hg log -Gf a
1836 @ changeset: 3:15b2327059e5
1857 @ changeset: 3:15b2327059e5
1837 | user: test
1858 | user: test
1838 | date: Thu Jan 01 00:00:00 1970 +0000
1859 | date: Thu Jan 01 00:00:00 1970 +0000
1839 | summary: content2
1860 | summary: content2
1840 |
1861 |
1841 o changeset: 0:ae0a3c9f9e95
1862 o changeset: 0:ae0a3c9f9e95
1842 user: test
1863 user: test
1843 date: Thu Jan 01 00:00:00 1970 +0000
1864 date: Thu Jan 01 00:00:00 1970 +0000
1844 summary: content1
1865 summary: content1
1845
1866
1846
1867
1847 Test that we use the first non-hidden changeset in that case.
1868 Test that we use the first non-hidden changeset in that case.
1848
1869
1849 (hide the changeset)
1870 (hide the changeset)
1850
1871
1851 $ hg log -T '{node}\n' -r 1
1872 $ hg log -T '{node}\n' -r 1
1852 2294ae80ad8447bc78383182eeac50cb049df623
1873 2294ae80ad8447bc78383182eeac50cb049df623
1853 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
1874 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
1854 $ hg log -G
1875 $ hg log -G
1855 o changeset: 4:50b9b36e9c5d
1876 o changeset: 4:50b9b36e9c5d
1856 | tag: tip
1877 | tag: tip
1857 | user: test
1878 | user: test
1858 | date: Thu Jan 01 00:00:00 1970 +0000
1879 | date: Thu Jan 01 00:00:00 1970 +0000
1859 | summary: content3
1880 | summary: content3
1860 |
1881 |
1861 @ changeset: 3:15b2327059e5
1882 @ changeset: 3:15b2327059e5
1862 | user: test
1883 | user: test
1863 | date: Thu Jan 01 00:00:00 1970 +0000
1884 | date: Thu Jan 01 00:00:00 1970 +0000
1864 | summary: content2
1885 | summary: content2
1865 |
1886 |
1866 o changeset: 2:2029acd1168c
1887 o changeset: 2:2029acd1168c
1867 | parent: 0:ae0a3c9f9e95
1888 | parent: 0:ae0a3c9f9e95
1868 | user: test
1889 | user: test
1869 | date: Thu Jan 01 00:00:00 1970 +0000
1890 | date: Thu Jan 01 00:00:00 1970 +0000
1870 | summary: unrelated
1891 | summary: unrelated
1871 |
1892 |
1872 o changeset: 0:ae0a3c9f9e95
1893 o changeset: 0:ae0a3c9f9e95
1873 user: test
1894 user: test
1874 date: Thu Jan 01 00:00:00 1970 +0000
1895 date: Thu Jan 01 00:00:00 1970 +0000
1875 summary: content1
1896 summary: content1
1876
1897
1877
1898
1878 Check that log on the file does not drop the file revision.
1899 Check that log on the file does not drop the file revision.
1879
1900
1880 $ hg log -G a
1901 $ hg log -G a
1881 o changeset: 4:50b9b36e9c5d
1902 o changeset: 4:50b9b36e9c5d
1882 | tag: tip
1903 | tag: tip
1883 | user: test
1904 | user: test
1884 | date: Thu Jan 01 00:00:00 1970 +0000
1905 | date: Thu Jan 01 00:00:00 1970 +0000
1885 | summary: content3
1906 | summary: content3
1886 |
1907 |
1887 @ changeset: 3:15b2327059e5
1908 @ changeset: 3:15b2327059e5
1888 | user: test
1909 | user: test
1889 | date: Thu Jan 01 00:00:00 1970 +0000
1910 | date: Thu Jan 01 00:00:00 1970 +0000
1890 | summary: content2
1911 | summary: content2
1891 |
1912 |
1892 o changeset: 0:ae0a3c9f9e95
1913 o changeset: 0:ae0a3c9f9e95
1893 user: test
1914 user: test
1894 date: Thu Jan 01 00:00:00 1970 +0000
1915 date: Thu Jan 01 00:00:00 1970 +0000
1895 summary: content1
1916 summary: content1
1896
1917
1897
1918
1898 Even when a head revision is linkrev-shadowed.
1919 Even when a head revision is linkrev-shadowed.
1899
1920
1900 $ hg log -T '{node}\n' -r 4
1921 $ hg log -T '{node}\n' -r 4
1901 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1922 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1902 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1923 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
1903 $ hg log -G a
1924 $ hg log -G a
1904 @ changeset: 3:15b2327059e5
1925 @ changeset: 3:15b2327059e5
1905 | tag: tip
1926 | tag: tip
1906 | user: test
1927 | user: test
1907 | date: Thu Jan 01 00:00:00 1970 +0000
1928 | date: Thu Jan 01 00:00:00 1970 +0000
1908 | summary: content2
1929 | summary: content2
1909 |
1930 |
1910 o changeset: 0:ae0a3c9f9e95
1931 o changeset: 0:ae0a3c9f9e95
1911 user: test
1932 user: test
1912 date: Thu Jan 01 00:00:00 1970 +0000
1933 date: Thu Jan 01 00:00:00 1970 +0000
1913 summary: content1
1934 summary: content1
1914
1935
1915
1936
1916 $ cd ..
1937 $ cd ..
1917
1938
1918 Even when the file revision is missing from some head:
1939 Even when the file revision is missing from some head:
1919
1940
1920 $ hg init issue4490
1941 $ hg init issue4490
1921 $ cd issue4490
1942 $ cd issue4490
1922 $ echo '[experimental]' >> .hg/hgrc
1943 $ echo '[experimental]' >> .hg/hgrc
1923 $ echo 'evolution=createmarkers' >> .hg/hgrc
1944 $ echo 'evolution=createmarkers' >> .hg/hgrc
1924 $ echo a > a
1945 $ echo a > a
1925 $ hg ci -Am0
1946 $ hg ci -Am0
1926 adding a
1947 adding a
1927 $ echo b > b
1948 $ echo b > b
1928 $ hg ci -Am1
1949 $ hg ci -Am1
1929 adding b
1950 adding b
1930 $ echo B > b
1951 $ echo B > b
1931 $ hg ci --amend -m 1
1952 $ hg ci --amend -m 1
1932 $ hg up 0
1953 $ hg up 0
1933 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1954 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1934 $ echo c > c
1955 $ echo c > c
1935 $ hg ci -Am2
1956 $ hg ci -Am2
1936 adding c
1957 adding c
1937 created new head
1958 created new head
1938 $ hg up 'head() and not .'
1959 $ hg up 'head() and not .'
1939 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1960 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1940 $ hg log -G
1961 $ hg log -G
1941 o changeset: 4:db815d6d32e6
1962 o changeset: 4:db815d6d32e6
1942 | tag: tip
1963 | tag: tip
1943 | parent: 0:f7b1eb17ad24
1964 | parent: 0:f7b1eb17ad24
1944 | user: test
1965 | user: test
1945 | date: Thu Jan 01 00:00:00 1970 +0000
1966 | date: Thu Jan 01 00:00:00 1970 +0000
1946 | summary: 2
1967 | summary: 2
1947 |
1968 |
1948 | @ changeset: 3:9bc8ce7f9356
1969 | @ changeset: 3:9bc8ce7f9356
1949 |/ parent: 0:f7b1eb17ad24
1970 |/ parent: 0:f7b1eb17ad24
1950 | user: test
1971 | user: test
1951 | date: Thu Jan 01 00:00:00 1970 +0000
1972 | date: Thu Jan 01 00:00:00 1970 +0000
1952 | summary: 1
1973 | summary: 1
1953 |
1974 |
1954 o changeset: 0:f7b1eb17ad24
1975 o changeset: 0:f7b1eb17ad24
1955 user: test
1976 user: test
1956 date: Thu Jan 01 00:00:00 1970 +0000
1977 date: Thu Jan 01 00:00:00 1970 +0000
1957 summary: 0
1978 summary: 0
1958
1979
1959 $ hg log -f -G b
1980 $ hg log -f -G b
1960 @ changeset: 3:9bc8ce7f9356
1981 @ changeset: 3:9bc8ce7f9356
1961 | parent: 0:f7b1eb17ad24
1982 | parent: 0:f7b1eb17ad24
1962 | user: test
1983 | user: test
1963 | date: Thu Jan 01 00:00:00 1970 +0000
1984 | date: Thu Jan 01 00:00:00 1970 +0000
1964 | summary: 1
1985 | summary: 1
1965 |
1986 |
1966 $ hg log -G b
1987 $ hg log -G b
1967 @ changeset: 3:9bc8ce7f9356
1988 @ changeset: 3:9bc8ce7f9356
1968 | parent: 0:f7b1eb17ad24
1989 | parent: 0:f7b1eb17ad24
1969 | user: test
1990 | user: test
1970 | date: Thu Jan 01 00:00:00 1970 +0000
1991 | date: Thu Jan 01 00:00:00 1970 +0000
1971 | summary: 1
1992 | summary: 1
1972 |
1993 |
1973 $ cd ..
1994 $ cd ..
1974
1995
1975 Check proper report when the manifest changes but not the file issue4499
1996 Check proper report when the manifest changes but not the file issue4499
1976 ------------------------------------------------------------------------
1997 ------------------------------------------------------------------------
1977
1998
1978 $ hg init issue4499
1999 $ hg init issue4499
1979 $ cd issue4499
2000 $ cd issue4499
1980 $ 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
2001 $ 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
1981 > echo 1 > $f;
2002 > echo 1 > $f;
1982 > hg add $f;
2003 > hg add $f;
1983 > done
2004 > done
1984 $ hg commit -m 'A1B1C1'
2005 $ hg commit -m 'A1B1C1'
1985 $ echo 2 > A
2006 $ echo 2 > A
1986 $ echo 2 > B
2007 $ echo 2 > B
1987 $ echo 2 > C
2008 $ echo 2 > C
1988 $ hg commit -m 'A2B2C2'
2009 $ hg commit -m 'A2B2C2'
1989 $ hg up 0
2010 $ hg up 0
1990 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2011 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1991 $ echo 3 > A
2012 $ echo 3 > A
1992 $ echo 2 > B
2013 $ echo 2 > B
1993 $ echo 2 > C
2014 $ echo 2 > C
1994 $ hg commit -m 'A3B2C2'
2015 $ hg commit -m 'A3B2C2'
1995 created new head
2016 created new head
1996
2017
1997 $ hg log -G
2018 $ hg log -G
1998 @ changeset: 2:fe5fc3d0eb17
2019 @ changeset: 2:fe5fc3d0eb17
1999 | tag: tip
2020 | tag: tip
2000 | parent: 0:abf4f0e38563
2021 | parent: 0:abf4f0e38563
2001 | user: test
2022 | user: test
2002 | date: Thu Jan 01 00:00:00 1970 +0000
2023 | date: Thu Jan 01 00:00:00 1970 +0000
2003 | summary: A3B2C2
2024 | summary: A3B2C2
2004 |
2025 |
2005 | o changeset: 1:07dcc6b312c0
2026 | o changeset: 1:07dcc6b312c0
2006 |/ user: test
2027 |/ user: test
2007 | date: Thu Jan 01 00:00:00 1970 +0000
2028 | date: Thu Jan 01 00:00:00 1970 +0000
2008 | summary: A2B2C2
2029 | summary: A2B2C2
2009 |
2030 |
2010 o changeset: 0:abf4f0e38563
2031 o changeset: 0:abf4f0e38563
2011 user: test
2032 user: test
2012 date: Thu Jan 01 00:00:00 1970 +0000
2033 date: Thu Jan 01 00:00:00 1970 +0000
2013 summary: A1B1C1
2034 summary: A1B1C1
2014
2035
2015
2036
2016 Log -f on B should reports current changesets
2037 Log -f on B should reports current changesets
2017
2038
2018 $ hg log -fG B
2039 $ hg log -fG B
2019 @ changeset: 2:fe5fc3d0eb17
2040 @ changeset: 2:fe5fc3d0eb17
2020 | tag: tip
2041 | tag: tip
2021 | parent: 0:abf4f0e38563
2042 | parent: 0:abf4f0e38563
2022 | user: test
2043 | user: test
2023 | date: Thu Jan 01 00:00:00 1970 +0000
2044 | date: Thu Jan 01 00:00:00 1970 +0000
2024 | summary: A3B2C2
2045 | summary: A3B2C2
2025 |
2046 |
2026 o changeset: 0:abf4f0e38563
2047 o changeset: 0:abf4f0e38563
2027 user: test
2048 user: test
2028 date: Thu Jan 01 00:00:00 1970 +0000
2049 date: Thu Jan 01 00:00:00 1970 +0000
2029 summary: A1B1C1
2050 summary: A1B1C1
2030
2051
2031 $ cd ..
2052 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now