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