##// END OF EJS Templates
graphlog: bring back color to node symbol template...
Yuya Nishihara -
r28428:6a4a4ca2 default
parent child Browse files
Show More
@@ -1,3473 +1,3474
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 cStringIO
10 import cStringIO
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15 import tempfile
15 import tempfile
16
16
17 from .i18n import _
17 from .i18n import _
18 from .node import (
18 from .node import (
19 bin,
19 bin,
20 hex,
20 hex,
21 nullid,
21 nullid,
22 nullrev,
22 nullrev,
23 short,
23 short,
24 )
24 )
25
25
26 from . import (
26 from . import (
27 bookmarks,
27 bookmarks,
28 changelog,
28 changelog,
29 copies,
29 copies,
30 crecord as crecordmod,
30 crecord as crecordmod,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 graphmod,
34 graphmod,
35 lock as lockmod,
35 lock as lockmod,
36 match as matchmod,
36 match as matchmod,
37 obsolete,
37 obsolete,
38 patch,
38 patch,
39 pathutil,
39 pathutil,
40 phases,
40 phases,
41 repair,
41 repair,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 scmutil,
44 scmutil,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 )
48 )
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, operation)
86 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
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 ui purposes to indicate the user
94 *operation* is used for ui purposes to indicate the user
95 what kind of filtering they are doing: reverting, committing, shelving, etc.
95 what kind of filtering they are doing: reverting, committing, shelving, etc.
96 *operation* has to be a translated string.
96 *operation* has to be a translated string.
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 merge = len(repo[None].parents()) > 1
138 merge = len(repo[None].parents()) > 1
139 if merge:
139 if merge:
140 raise error.Abort(_('cannot partially commit a merge '
140 raise error.Abort(_('cannot partially commit a merge '
141 '(use "hg commit" instead)'))
141 '(use "hg commit" instead)'))
142
142
143 status = repo.status(match=match)
143 status = repo.status(match=match)
144 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
144 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
145 diffopts.nodates = True
145 diffopts.nodates = True
146 diffopts.git = True
146 diffopts.git = True
147 diffopts.showfunc = True
147 diffopts.showfunc = True
148 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
148 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
149 originalchunks = patch.parsepatch(originaldiff)
149 originalchunks = patch.parsepatch(originaldiff)
150
150
151 # 1. filter patch, so we have intending-to apply subset of it
151 # 1. filter patch, so we have intending-to apply subset of it
152 try:
152 try:
153 chunks, newopts = filterfn(ui, originalchunks)
153 chunks, newopts = filterfn(ui, originalchunks)
154 except patch.PatchError as err:
154 except patch.PatchError as err:
155 raise error.Abort(_('error parsing patch: %s') % err)
155 raise error.Abort(_('error parsing patch: %s') % err)
156 opts.update(newopts)
156 opts.update(newopts)
157
157
158 # We need to keep a backup of files that have been newly added and
158 # We need to keep a backup of files that have been newly added and
159 # modified during the recording process because there is a previous
159 # modified during the recording process because there is a previous
160 # version without the edit in the workdir
160 # version without the edit in the workdir
161 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
161 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
162 contenders = set()
162 contenders = set()
163 for h in chunks:
163 for h in chunks:
164 try:
164 try:
165 contenders.update(set(h.files()))
165 contenders.update(set(h.files()))
166 except AttributeError:
166 except AttributeError:
167 pass
167 pass
168
168
169 changed = status.modified + status.added + status.removed
169 changed = status.modified + status.added + status.removed
170 newfiles = [f for f in changed if f in contenders]
170 newfiles = [f for f in changed if f in contenders]
171 if not newfiles:
171 if not newfiles:
172 ui.status(_('no changes to record\n'))
172 ui.status(_('no changes to record\n'))
173 return 0
173 return 0
174
174
175 modified = set(status.modified)
175 modified = set(status.modified)
176
176
177 # 2. backup changed files, so we can restore them in the end
177 # 2. backup changed files, so we can restore them in the end
178
178
179 if backupall:
179 if backupall:
180 tobackup = changed
180 tobackup = changed
181 else:
181 else:
182 tobackup = [f for f in newfiles if f in modified or f in \
182 tobackup = [f for f in newfiles if f in modified or f in \
183 newlyaddedandmodifiedfiles]
183 newlyaddedandmodifiedfiles]
184 backups = {}
184 backups = {}
185 if tobackup:
185 if tobackup:
186 backupdir = repo.join('record-backups')
186 backupdir = repo.join('record-backups')
187 try:
187 try:
188 os.mkdir(backupdir)
188 os.mkdir(backupdir)
189 except OSError as err:
189 except OSError as err:
190 if err.errno != errno.EEXIST:
190 if err.errno != errno.EEXIST:
191 raise
191 raise
192 try:
192 try:
193 # backup continues
193 # backup continues
194 for f in tobackup:
194 for f in tobackup:
195 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
195 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
196 dir=backupdir)
196 dir=backupdir)
197 os.close(fd)
197 os.close(fd)
198 ui.debug('backup %r as %r\n' % (f, tmpname))
198 ui.debug('backup %r as %r\n' % (f, tmpname))
199 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
199 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
200 backups[f] = tmpname
200 backups[f] = tmpname
201
201
202 fp = cStringIO.StringIO()
202 fp = cStringIO.StringIO()
203 for c in chunks:
203 for c in chunks:
204 fname = c.filename()
204 fname = c.filename()
205 if fname in backups:
205 if fname in backups:
206 c.write(fp)
206 c.write(fp)
207 dopatch = fp.tell()
207 dopatch = fp.tell()
208 fp.seek(0)
208 fp.seek(0)
209
209
210 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
210 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
211 # 3a. apply filtered patch to clean repo (clean)
211 # 3a. apply filtered patch to clean repo (clean)
212 if backups:
212 if backups:
213 # Equivalent to hg.revert
213 # Equivalent to hg.revert
214 m = scmutil.matchfiles(repo, backups.keys())
214 m = scmutil.matchfiles(repo, backups.keys())
215 mergemod.update(repo, repo.dirstate.p1(),
215 mergemod.update(repo, repo.dirstate.p1(),
216 False, True, matcher=m)
216 False, True, matcher=m)
217
217
218 # 3b. (apply)
218 # 3b. (apply)
219 if dopatch:
219 if dopatch:
220 try:
220 try:
221 ui.debug('applying patch\n')
221 ui.debug('applying patch\n')
222 ui.debug(fp.getvalue())
222 ui.debug(fp.getvalue())
223 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
223 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
224 except patch.PatchError as err:
224 except patch.PatchError as err:
225 raise error.Abort(str(err))
225 raise error.Abort(str(err))
226 del fp
226 del fp
227
227
228 # 4. We prepared working directory according to filtered
228 # 4. We prepared working directory according to filtered
229 # patch. Now is the time to delegate the job to
229 # patch. Now is the time to delegate the job to
230 # commit/qrefresh or the like!
230 # commit/qrefresh or the like!
231
231
232 # Make all of the pathnames absolute.
232 # Make all of the pathnames absolute.
233 newfiles = [repo.wjoin(nf) for nf in newfiles]
233 newfiles = [repo.wjoin(nf) for nf in newfiles]
234 return commitfunc(ui, repo, *newfiles, **opts)
234 return commitfunc(ui, repo, *newfiles, **opts)
235 finally:
235 finally:
236 # 5. finally restore backed-up files
236 # 5. finally restore backed-up files
237 try:
237 try:
238 dirstate = repo.dirstate
238 dirstate = repo.dirstate
239 for realname, tmpname in backups.iteritems():
239 for realname, tmpname in backups.iteritems():
240 ui.debug('restoring %r to %r\n' % (tmpname, realname))
240 ui.debug('restoring %r to %r\n' % (tmpname, realname))
241
241
242 if dirstate[realname] == 'n':
242 if dirstate[realname] == 'n':
243 # without normallookup, restoring timestamp
243 # without normallookup, restoring timestamp
244 # may cause partially committed files
244 # may cause partially committed files
245 # to be treated as unmodified
245 # to be treated as unmodified
246 dirstate.normallookup(realname)
246 dirstate.normallookup(realname)
247
247
248 # copystat=True here and above are a hack to trick any
248 # copystat=True here and above are a hack to trick any
249 # editors that have f open that we haven't modified them.
249 # editors that have f open that we haven't modified them.
250 #
250 #
251 # Also note that this racy as an editor could notice the
251 # Also note that this racy as an editor could notice the
252 # file's mtime before we've finished writing it.
252 # file's mtime before we've finished writing it.
253 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
253 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
254 os.unlink(tmpname)
254 os.unlink(tmpname)
255 if tobackup:
255 if tobackup:
256 os.rmdir(backupdir)
256 os.rmdir(backupdir)
257 except OSError:
257 except OSError:
258 pass
258 pass
259
259
260 def recordinwlock(ui, repo, message, match, opts):
260 def recordinwlock(ui, repo, message, match, opts):
261 with repo.wlock():
261 with repo.wlock():
262 return recordfunc(ui, repo, message, match, opts)
262 return recordfunc(ui, repo, message, match, opts)
263
263
264 return commit(ui, repo, recordinwlock, pats, opts)
264 return commit(ui, repo, recordinwlock, pats, opts)
265
265
266 def findpossible(cmd, table, strict=False):
266 def findpossible(cmd, table, strict=False):
267 """
267 """
268 Return cmd -> (aliases, command table entry)
268 Return cmd -> (aliases, command table entry)
269 for each matching command.
269 for each matching command.
270 Return debug commands (or their aliases) only if no normal command matches.
270 Return debug commands (or their aliases) only if no normal command matches.
271 """
271 """
272 choice = {}
272 choice = {}
273 debugchoice = {}
273 debugchoice = {}
274
274
275 if cmd in table:
275 if cmd in table:
276 # short-circuit exact matches, "log" alias beats "^log|history"
276 # short-circuit exact matches, "log" alias beats "^log|history"
277 keys = [cmd]
277 keys = [cmd]
278 else:
278 else:
279 keys = table.keys()
279 keys = table.keys()
280
280
281 allcmds = []
281 allcmds = []
282 for e in keys:
282 for e in keys:
283 aliases = parsealiases(e)
283 aliases = parsealiases(e)
284 allcmds.extend(aliases)
284 allcmds.extend(aliases)
285 found = None
285 found = None
286 if cmd in aliases:
286 if cmd in aliases:
287 found = cmd
287 found = cmd
288 elif not strict:
288 elif not strict:
289 for a in aliases:
289 for a in aliases:
290 if a.startswith(cmd):
290 if a.startswith(cmd):
291 found = a
291 found = a
292 break
292 break
293 if found is not None:
293 if found is not None:
294 if aliases[0].startswith("debug") or found.startswith("debug"):
294 if aliases[0].startswith("debug") or found.startswith("debug"):
295 debugchoice[found] = (aliases, table[e])
295 debugchoice[found] = (aliases, table[e])
296 else:
296 else:
297 choice[found] = (aliases, table[e])
297 choice[found] = (aliases, table[e])
298
298
299 if not choice and debugchoice:
299 if not choice and debugchoice:
300 choice = debugchoice
300 choice = debugchoice
301
301
302 return choice, allcmds
302 return choice, allcmds
303
303
304 def findcmd(cmd, table, strict=True):
304 def findcmd(cmd, table, strict=True):
305 """Return (aliases, command table entry) for command string."""
305 """Return (aliases, command table entry) for command string."""
306 choice, allcmds = findpossible(cmd, table, strict)
306 choice, allcmds = findpossible(cmd, table, strict)
307
307
308 if cmd in choice:
308 if cmd in choice:
309 return choice[cmd]
309 return choice[cmd]
310
310
311 if len(choice) > 1:
311 if len(choice) > 1:
312 clist = choice.keys()
312 clist = choice.keys()
313 clist.sort()
313 clist.sort()
314 raise error.AmbiguousCommand(cmd, clist)
314 raise error.AmbiguousCommand(cmd, clist)
315
315
316 if choice:
316 if choice:
317 return choice.values()[0]
317 return choice.values()[0]
318
318
319 raise error.UnknownCommand(cmd, allcmds)
319 raise error.UnknownCommand(cmd, allcmds)
320
320
321 def findrepo(p):
321 def findrepo(p):
322 while not os.path.isdir(os.path.join(p, ".hg")):
322 while not os.path.isdir(os.path.join(p, ".hg")):
323 oldp, p = p, os.path.dirname(p)
323 oldp, p = p, os.path.dirname(p)
324 if p == oldp:
324 if p == oldp:
325 return None
325 return None
326
326
327 return p
327 return p
328
328
329 def bailifchanged(repo, merge=True):
329 def bailifchanged(repo, merge=True):
330 if merge and repo.dirstate.p2() != nullid:
330 if merge and repo.dirstate.p2() != nullid:
331 raise error.Abort(_('outstanding uncommitted merge'))
331 raise error.Abort(_('outstanding uncommitted merge'))
332 modified, added, removed, deleted = repo.status()[:4]
332 modified, added, removed, deleted = repo.status()[:4]
333 if modified or added or removed or deleted:
333 if modified or added or removed or deleted:
334 raise error.Abort(_('uncommitted changes'))
334 raise error.Abort(_('uncommitted changes'))
335 ctx = repo[None]
335 ctx = repo[None]
336 for s in sorted(ctx.substate):
336 for s in sorted(ctx.substate):
337 ctx.sub(s).bailifchanged()
337 ctx.sub(s).bailifchanged()
338
338
339 def logmessage(ui, opts):
339 def logmessage(ui, opts):
340 """ get the log message according to -m and -l option """
340 """ get the log message according to -m and -l option """
341 message = opts.get('message')
341 message = opts.get('message')
342 logfile = opts.get('logfile')
342 logfile = opts.get('logfile')
343
343
344 if message and logfile:
344 if message and logfile:
345 raise error.Abort(_('options --message and --logfile are mutually '
345 raise error.Abort(_('options --message and --logfile are mutually '
346 'exclusive'))
346 'exclusive'))
347 if not message and logfile:
347 if not message and logfile:
348 try:
348 try:
349 if logfile == '-':
349 if logfile == '-':
350 message = ui.fin.read()
350 message = ui.fin.read()
351 else:
351 else:
352 message = '\n'.join(util.readfile(logfile).splitlines())
352 message = '\n'.join(util.readfile(logfile).splitlines())
353 except IOError as inst:
353 except IOError as inst:
354 raise error.Abort(_("can't read commit message '%s': %s") %
354 raise error.Abort(_("can't read commit message '%s': %s") %
355 (logfile, inst.strerror))
355 (logfile, inst.strerror))
356 return message
356 return message
357
357
358 def mergeeditform(ctxorbool, baseformname):
358 def mergeeditform(ctxorbool, baseformname):
359 """return appropriate editform name (referencing a committemplate)
359 """return appropriate editform name (referencing a committemplate)
360
360
361 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
361 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
362 merging is committed.
362 merging is committed.
363
363
364 This returns baseformname with '.merge' appended if it is a merge,
364 This returns baseformname with '.merge' appended if it is a merge,
365 otherwise '.normal' is appended.
365 otherwise '.normal' is appended.
366 """
366 """
367 if isinstance(ctxorbool, bool):
367 if isinstance(ctxorbool, bool):
368 if ctxorbool:
368 if ctxorbool:
369 return baseformname + ".merge"
369 return baseformname + ".merge"
370 elif 1 < len(ctxorbool.parents()):
370 elif 1 < len(ctxorbool.parents()):
371 return baseformname + ".merge"
371 return baseformname + ".merge"
372
372
373 return baseformname + ".normal"
373 return baseformname + ".normal"
374
374
375 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
375 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
376 editform='', **opts):
376 editform='', **opts):
377 """get appropriate commit message editor according to '--edit' option
377 """get appropriate commit message editor according to '--edit' option
378
378
379 'finishdesc' is a function to be called with edited commit message
379 'finishdesc' is a function to be called with edited commit message
380 (= 'description' of the new changeset) just after editing, but
380 (= 'description' of the new changeset) just after editing, but
381 before checking empty-ness. It should return actual text to be
381 before checking empty-ness. It should return actual text to be
382 stored into history. This allows to change description before
382 stored into history. This allows to change description before
383 storing.
383 storing.
384
384
385 'extramsg' is a extra message to be shown in the editor instead of
385 'extramsg' is a extra message to be shown in the editor instead of
386 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
386 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
387 is automatically added.
387 is automatically added.
388
388
389 'editform' is a dot-separated list of names, to distinguish
389 'editform' is a dot-separated list of names, to distinguish
390 the purpose of commit text editing.
390 the purpose of commit text editing.
391
391
392 'getcommiteditor' returns 'commitforceeditor' regardless of
392 'getcommiteditor' returns 'commitforceeditor' regardless of
393 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
393 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
394 they are specific for usage in MQ.
394 they are specific for usage in MQ.
395 """
395 """
396 if edit or finishdesc or extramsg:
396 if edit or finishdesc or extramsg:
397 return lambda r, c, s: commitforceeditor(r, c, s,
397 return lambda r, c, s: commitforceeditor(r, c, s,
398 finishdesc=finishdesc,
398 finishdesc=finishdesc,
399 extramsg=extramsg,
399 extramsg=extramsg,
400 editform=editform)
400 editform=editform)
401 elif editform:
401 elif editform:
402 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
402 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
403 else:
403 else:
404 return commiteditor
404 return commiteditor
405
405
406 def loglimit(opts):
406 def loglimit(opts):
407 """get the log limit according to option -l/--limit"""
407 """get the log limit according to option -l/--limit"""
408 limit = opts.get('limit')
408 limit = opts.get('limit')
409 if limit:
409 if limit:
410 try:
410 try:
411 limit = int(limit)
411 limit = int(limit)
412 except ValueError:
412 except ValueError:
413 raise error.Abort(_('limit must be a positive integer'))
413 raise error.Abort(_('limit must be a positive integer'))
414 if limit <= 0:
414 if limit <= 0:
415 raise error.Abort(_('limit must be positive'))
415 raise error.Abort(_('limit must be positive'))
416 else:
416 else:
417 limit = None
417 limit = None
418 return limit
418 return limit
419
419
420 def makefilename(repo, pat, node, desc=None,
420 def makefilename(repo, pat, node, desc=None,
421 total=None, seqno=None, revwidth=None, pathname=None):
421 total=None, seqno=None, revwidth=None, pathname=None):
422 node_expander = {
422 node_expander = {
423 'H': lambda: hex(node),
423 'H': lambda: hex(node),
424 'R': lambda: str(repo.changelog.rev(node)),
424 'R': lambda: str(repo.changelog.rev(node)),
425 'h': lambda: short(node),
425 'h': lambda: short(node),
426 'm': lambda: re.sub('[^\w]', '_', str(desc))
426 'm': lambda: re.sub('[^\w]', '_', str(desc))
427 }
427 }
428 expander = {
428 expander = {
429 '%': lambda: '%',
429 '%': lambda: '%',
430 'b': lambda: os.path.basename(repo.root),
430 'b': lambda: os.path.basename(repo.root),
431 }
431 }
432
432
433 try:
433 try:
434 if node:
434 if node:
435 expander.update(node_expander)
435 expander.update(node_expander)
436 if node:
436 if node:
437 expander['r'] = (lambda:
437 expander['r'] = (lambda:
438 str(repo.changelog.rev(node)).zfill(revwidth or 0))
438 str(repo.changelog.rev(node)).zfill(revwidth or 0))
439 if total is not None:
439 if total is not None:
440 expander['N'] = lambda: str(total)
440 expander['N'] = lambda: str(total)
441 if seqno is not None:
441 if seqno is not None:
442 expander['n'] = lambda: str(seqno)
442 expander['n'] = lambda: str(seqno)
443 if total is not None and seqno is not None:
443 if total is not None and seqno is not None:
444 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
444 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
445 if pathname is not None:
445 if pathname is not None:
446 expander['s'] = lambda: os.path.basename(pathname)
446 expander['s'] = lambda: os.path.basename(pathname)
447 expander['d'] = lambda: os.path.dirname(pathname) or '.'
447 expander['d'] = lambda: os.path.dirname(pathname) or '.'
448 expander['p'] = lambda: pathname
448 expander['p'] = lambda: pathname
449
449
450 newname = []
450 newname = []
451 patlen = len(pat)
451 patlen = len(pat)
452 i = 0
452 i = 0
453 while i < patlen:
453 while i < patlen:
454 c = pat[i]
454 c = pat[i]
455 if c == '%':
455 if c == '%':
456 i += 1
456 i += 1
457 c = pat[i]
457 c = pat[i]
458 c = expander[c]()
458 c = expander[c]()
459 newname.append(c)
459 newname.append(c)
460 i += 1
460 i += 1
461 return ''.join(newname)
461 return ''.join(newname)
462 except KeyError as inst:
462 except KeyError as inst:
463 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
463 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
464 inst.args[0])
464 inst.args[0])
465
465
466 class _unclosablefile(object):
466 class _unclosablefile(object):
467 def __init__(self, fp):
467 def __init__(self, fp):
468 self._fp = fp
468 self._fp = fp
469
469
470 def close(self):
470 def close(self):
471 pass
471 pass
472
472
473 def __iter__(self):
473 def __iter__(self):
474 return iter(self._fp)
474 return iter(self._fp)
475
475
476 def __getattr__(self, attr):
476 def __getattr__(self, attr):
477 return getattr(self._fp, attr)
477 return getattr(self._fp, attr)
478
478
479 def makefileobj(repo, pat, node=None, desc=None, total=None,
479 def makefileobj(repo, pat, node=None, desc=None, total=None,
480 seqno=None, revwidth=None, mode='wb', modemap=None,
480 seqno=None, revwidth=None, mode='wb', modemap=None,
481 pathname=None):
481 pathname=None):
482
482
483 writable = mode not in ('r', 'rb')
483 writable = mode not in ('r', 'rb')
484
484
485 if not pat or pat == '-':
485 if not pat or pat == '-':
486 if writable:
486 if writable:
487 fp = repo.ui.fout
487 fp = repo.ui.fout
488 else:
488 else:
489 fp = repo.ui.fin
489 fp = repo.ui.fin
490 return _unclosablefile(fp)
490 return _unclosablefile(fp)
491 if util.safehasattr(pat, 'write') and writable:
491 if util.safehasattr(pat, 'write') and writable:
492 return pat
492 return pat
493 if util.safehasattr(pat, 'read') and 'r' in mode:
493 if util.safehasattr(pat, 'read') and 'r' in mode:
494 return pat
494 return pat
495 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
495 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
496 if modemap is not None:
496 if modemap is not None:
497 mode = modemap.get(fn, mode)
497 mode = modemap.get(fn, mode)
498 if mode == 'wb':
498 if mode == 'wb':
499 modemap[fn] = 'ab'
499 modemap[fn] = 'ab'
500 return open(fn, mode)
500 return open(fn, mode)
501
501
502 def openrevlog(repo, cmd, file_, opts):
502 def openrevlog(repo, cmd, file_, opts):
503 """opens the changelog, manifest, a filelog or a given revlog"""
503 """opens the changelog, manifest, a filelog or a given revlog"""
504 cl = opts['changelog']
504 cl = opts['changelog']
505 mf = opts['manifest']
505 mf = opts['manifest']
506 dir = opts['dir']
506 dir = opts['dir']
507 msg = None
507 msg = None
508 if cl and mf:
508 if cl and mf:
509 msg = _('cannot specify --changelog and --manifest at the same time')
509 msg = _('cannot specify --changelog and --manifest at the same time')
510 elif cl and dir:
510 elif cl and dir:
511 msg = _('cannot specify --changelog and --dir at the same time')
511 msg = _('cannot specify --changelog and --dir at the same time')
512 elif cl or mf:
512 elif cl or mf:
513 if file_:
513 if file_:
514 msg = _('cannot specify filename with --changelog or --manifest')
514 msg = _('cannot specify filename with --changelog or --manifest')
515 elif not repo:
515 elif not repo:
516 msg = _('cannot specify --changelog or --manifest or --dir '
516 msg = _('cannot specify --changelog or --manifest or --dir '
517 'without a repository')
517 'without a repository')
518 if msg:
518 if msg:
519 raise error.Abort(msg)
519 raise error.Abort(msg)
520
520
521 r = None
521 r = None
522 if repo:
522 if repo:
523 if cl:
523 if cl:
524 r = repo.unfiltered().changelog
524 r = repo.unfiltered().changelog
525 elif dir:
525 elif dir:
526 if 'treemanifest' not in repo.requirements:
526 if 'treemanifest' not in repo.requirements:
527 raise error.Abort(_("--dir can only be used on repos with "
527 raise error.Abort(_("--dir can only be used on repos with "
528 "treemanifest enabled"))
528 "treemanifest enabled"))
529 dirlog = repo.dirlog(file_)
529 dirlog = repo.dirlog(file_)
530 if len(dirlog):
530 if len(dirlog):
531 r = dirlog
531 r = dirlog
532 elif mf:
532 elif mf:
533 r = repo.manifest
533 r = repo.manifest
534 elif file_:
534 elif file_:
535 filelog = repo.file(file_)
535 filelog = repo.file(file_)
536 if len(filelog):
536 if len(filelog):
537 r = filelog
537 r = filelog
538 if not r:
538 if not r:
539 if not file_:
539 if not file_:
540 raise error.CommandError(cmd, _('invalid arguments'))
540 raise error.CommandError(cmd, _('invalid arguments'))
541 if not os.path.isfile(file_):
541 if not os.path.isfile(file_):
542 raise error.Abort(_("revlog '%s' not found") % file_)
542 raise error.Abort(_("revlog '%s' not found") % file_)
543 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
543 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
544 file_[:-2] + ".i")
544 file_[:-2] + ".i")
545 return r
545 return r
546
546
547 def copy(ui, repo, pats, opts, rename=False):
547 def copy(ui, repo, pats, opts, rename=False):
548 # called with the repo lock held
548 # called with the repo lock held
549 #
549 #
550 # hgsep => pathname that uses "/" to separate directories
550 # hgsep => pathname that uses "/" to separate directories
551 # ossep => pathname that uses os.sep to separate directories
551 # ossep => pathname that uses os.sep to separate directories
552 cwd = repo.getcwd()
552 cwd = repo.getcwd()
553 targets = {}
553 targets = {}
554 after = opts.get("after")
554 after = opts.get("after")
555 dryrun = opts.get("dry_run")
555 dryrun = opts.get("dry_run")
556 wctx = repo[None]
556 wctx = repo[None]
557
557
558 def walkpat(pat):
558 def walkpat(pat):
559 srcs = []
559 srcs = []
560 if after:
560 if after:
561 badstates = '?'
561 badstates = '?'
562 else:
562 else:
563 badstates = '?r'
563 badstates = '?r'
564 m = scmutil.match(repo[None], [pat], opts, globbed=True)
564 m = scmutil.match(repo[None], [pat], opts, globbed=True)
565 for abs in repo.walk(m):
565 for abs in repo.walk(m):
566 state = repo.dirstate[abs]
566 state = repo.dirstate[abs]
567 rel = m.rel(abs)
567 rel = m.rel(abs)
568 exact = m.exact(abs)
568 exact = m.exact(abs)
569 if state in badstates:
569 if state in badstates:
570 if exact and state == '?':
570 if exact and state == '?':
571 ui.warn(_('%s: not copying - file is not managed\n') % rel)
571 ui.warn(_('%s: not copying - file is not managed\n') % rel)
572 if exact and state == 'r':
572 if exact and state == 'r':
573 ui.warn(_('%s: not copying - file has been marked for'
573 ui.warn(_('%s: not copying - file has been marked for'
574 ' remove\n') % rel)
574 ' remove\n') % rel)
575 continue
575 continue
576 # abs: hgsep
576 # abs: hgsep
577 # rel: ossep
577 # rel: ossep
578 srcs.append((abs, rel, exact))
578 srcs.append((abs, rel, exact))
579 return srcs
579 return srcs
580
580
581 # abssrc: hgsep
581 # abssrc: hgsep
582 # relsrc: ossep
582 # relsrc: ossep
583 # otarget: ossep
583 # otarget: ossep
584 def copyfile(abssrc, relsrc, otarget, exact):
584 def copyfile(abssrc, relsrc, otarget, exact):
585 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
585 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
586 if '/' in abstarget:
586 if '/' in abstarget:
587 # We cannot normalize abstarget itself, this would prevent
587 # We cannot normalize abstarget itself, this would prevent
588 # case only renames, like a => A.
588 # case only renames, like a => A.
589 abspath, absname = abstarget.rsplit('/', 1)
589 abspath, absname = abstarget.rsplit('/', 1)
590 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
590 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
591 reltarget = repo.pathto(abstarget, cwd)
591 reltarget = repo.pathto(abstarget, cwd)
592 target = repo.wjoin(abstarget)
592 target = repo.wjoin(abstarget)
593 src = repo.wjoin(abssrc)
593 src = repo.wjoin(abssrc)
594 state = repo.dirstate[abstarget]
594 state = repo.dirstate[abstarget]
595
595
596 scmutil.checkportable(ui, abstarget)
596 scmutil.checkportable(ui, abstarget)
597
597
598 # check for collisions
598 # check for collisions
599 prevsrc = targets.get(abstarget)
599 prevsrc = targets.get(abstarget)
600 if prevsrc is not None:
600 if prevsrc is not None:
601 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
601 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
602 (reltarget, repo.pathto(abssrc, cwd),
602 (reltarget, repo.pathto(abssrc, cwd),
603 repo.pathto(prevsrc, cwd)))
603 repo.pathto(prevsrc, cwd)))
604 return
604 return
605
605
606 # check for overwrites
606 # check for overwrites
607 exists = os.path.lexists(target)
607 exists = os.path.lexists(target)
608 samefile = False
608 samefile = False
609 if exists and abssrc != abstarget:
609 if exists and abssrc != abstarget:
610 if (repo.dirstate.normalize(abssrc) ==
610 if (repo.dirstate.normalize(abssrc) ==
611 repo.dirstate.normalize(abstarget)):
611 repo.dirstate.normalize(abstarget)):
612 if not rename:
612 if not rename:
613 ui.warn(_("%s: can't copy - same file\n") % reltarget)
613 ui.warn(_("%s: can't copy - same file\n") % reltarget)
614 return
614 return
615 exists = False
615 exists = False
616 samefile = True
616 samefile = True
617
617
618 if not after and exists or after and state in 'mn':
618 if not after and exists or after and state in 'mn':
619 if not opts['force']:
619 if not opts['force']:
620 ui.warn(_('%s: not overwriting - file exists\n') %
620 ui.warn(_('%s: not overwriting - file exists\n') %
621 reltarget)
621 reltarget)
622 return
622 return
623
623
624 if after:
624 if after:
625 if not exists:
625 if not exists:
626 if rename:
626 if rename:
627 ui.warn(_('%s: not recording move - %s does not exist\n') %
627 ui.warn(_('%s: not recording move - %s does not exist\n') %
628 (relsrc, reltarget))
628 (relsrc, reltarget))
629 else:
629 else:
630 ui.warn(_('%s: not recording copy - %s does not exist\n') %
630 ui.warn(_('%s: not recording copy - %s does not exist\n') %
631 (relsrc, reltarget))
631 (relsrc, reltarget))
632 return
632 return
633 elif not dryrun:
633 elif not dryrun:
634 try:
634 try:
635 if exists:
635 if exists:
636 os.unlink(target)
636 os.unlink(target)
637 targetdir = os.path.dirname(target) or '.'
637 targetdir = os.path.dirname(target) or '.'
638 if not os.path.isdir(targetdir):
638 if not os.path.isdir(targetdir):
639 os.makedirs(targetdir)
639 os.makedirs(targetdir)
640 if samefile:
640 if samefile:
641 tmp = target + "~hgrename"
641 tmp = target + "~hgrename"
642 os.rename(src, tmp)
642 os.rename(src, tmp)
643 os.rename(tmp, target)
643 os.rename(tmp, target)
644 else:
644 else:
645 util.copyfile(src, target)
645 util.copyfile(src, target)
646 srcexists = True
646 srcexists = True
647 except IOError as inst:
647 except IOError as inst:
648 if inst.errno == errno.ENOENT:
648 if inst.errno == errno.ENOENT:
649 ui.warn(_('%s: deleted in working directory\n') % relsrc)
649 ui.warn(_('%s: deleted in working directory\n') % relsrc)
650 srcexists = False
650 srcexists = False
651 else:
651 else:
652 ui.warn(_('%s: cannot copy - %s\n') %
652 ui.warn(_('%s: cannot copy - %s\n') %
653 (relsrc, inst.strerror))
653 (relsrc, inst.strerror))
654 return True # report a failure
654 return True # report a failure
655
655
656 if ui.verbose or not exact:
656 if ui.verbose or not exact:
657 if rename:
657 if rename:
658 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
658 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
659 else:
659 else:
660 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
660 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
661
661
662 targets[abstarget] = abssrc
662 targets[abstarget] = abssrc
663
663
664 # fix up dirstate
664 # fix up dirstate
665 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
665 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
666 dryrun=dryrun, cwd=cwd)
666 dryrun=dryrun, cwd=cwd)
667 if rename and not dryrun:
667 if rename and not dryrun:
668 if not after and srcexists and not samefile:
668 if not after and srcexists and not samefile:
669 util.unlinkpath(repo.wjoin(abssrc))
669 util.unlinkpath(repo.wjoin(abssrc))
670 wctx.forget([abssrc])
670 wctx.forget([abssrc])
671
671
672 # pat: ossep
672 # pat: ossep
673 # dest ossep
673 # dest ossep
674 # srcs: list of (hgsep, hgsep, ossep, bool)
674 # srcs: list of (hgsep, hgsep, ossep, bool)
675 # return: function that takes hgsep and returns ossep
675 # return: function that takes hgsep and returns ossep
676 def targetpathfn(pat, dest, srcs):
676 def targetpathfn(pat, dest, srcs):
677 if os.path.isdir(pat):
677 if os.path.isdir(pat):
678 abspfx = pathutil.canonpath(repo.root, cwd, pat)
678 abspfx = pathutil.canonpath(repo.root, cwd, pat)
679 abspfx = util.localpath(abspfx)
679 abspfx = util.localpath(abspfx)
680 if destdirexists:
680 if destdirexists:
681 striplen = len(os.path.split(abspfx)[0])
681 striplen = len(os.path.split(abspfx)[0])
682 else:
682 else:
683 striplen = len(abspfx)
683 striplen = len(abspfx)
684 if striplen:
684 if striplen:
685 striplen += len(os.sep)
685 striplen += len(os.sep)
686 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
686 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
687 elif destdirexists:
687 elif destdirexists:
688 res = lambda p: os.path.join(dest,
688 res = lambda p: os.path.join(dest,
689 os.path.basename(util.localpath(p)))
689 os.path.basename(util.localpath(p)))
690 else:
690 else:
691 res = lambda p: dest
691 res = lambda p: dest
692 return res
692 return res
693
693
694 # pat: ossep
694 # pat: ossep
695 # dest ossep
695 # dest ossep
696 # srcs: list of (hgsep, hgsep, ossep, bool)
696 # srcs: list of (hgsep, hgsep, ossep, bool)
697 # return: function that takes hgsep and returns ossep
697 # return: function that takes hgsep and returns ossep
698 def targetpathafterfn(pat, dest, srcs):
698 def targetpathafterfn(pat, dest, srcs):
699 if matchmod.patkind(pat):
699 if matchmod.patkind(pat):
700 # a mercurial pattern
700 # a mercurial pattern
701 res = lambda p: os.path.join(dest,
701 res = lambda p: os.path.join(dest,
702 os.path.basename(util.localpath(p)))
702 os.path.basename(util.localpath(p)))
703 else:
703 else:
704 abspfx = pathutil.canonpath(repo.root, cwd, pat)
704 abspfx = pathutil.canonpath(repo.root, cwd, pat)
705 if len(abspfx) < len(srcs[0][0]):
705 if len(abspfx) < len(srcs[0][0]):
706 # A directory. Either the target path contains the last
706 # A directory. Either the target path contains the last
707 # component of the source path or it does not.
707 # component of the source path or it does not.
708 def evalpath(striplen):
708 def evalpath(striplen):
709 score = 0
709 score = 0
710 for s in srcs:
710 for s in srcs:
711 t = os.path.join(dest, util.localpath(s[0])[striplen:])
711 t = os.path.join(dest, util.localpath(s[0])[striplen:])
712 if os.path.lexists(t):
712 if os.path.lexists(t):
713 score += 1
713 score += 1
714 return score
714 return score
715
715
716 abspfx = util.localpath(abspfx)
716 abspfx = util.localpath(abspfx)
717 striplen = len(abspfx)
717 striplen = len(abspfx)
718 if striplen:
718 if striplen:
719 striplen += len(os.sep)
719 striplen += len(os.sep)
720 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
720 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
721 score = evalpath(striplen)
721 score = evalpath(striplen)
722 striplen1 = len(os.path.split(abspfx)[0])
722 striplen1 = len(os.path.split(abspfx)[0])
723 if striplen1:
723 if striplen1:
724 striplen1 += len(os.sep)
724 striplen1 += len(os.sep)
725 if evalpath(striplen1) > score:
725 if evalpath(striplen1) > score:
726 striplen = striplen1
726 striplen = striplen1
727 res = lambda p: os.path.join(dest,
727 res = lambda p: os.path.join(dest,
728 util.localpath(p)[striplen:])
728 util.localpath(p)[striplen:])
729 else:
729 else:
730 # a file
730 # a file
731 if destdirexists:
731 if destdirexists:
732 res = lambda p: os.path.join(dest,
732 res = lambda p: os.path.join(dest,
733 os.path.basename(util.localpath(p)))
733 os.path.basename(util.localpath(p)))
734 else:
734 else:
735 res = lambda p: dest
735 res = lambda p: dest
736 return res
736 return res
737
737
738 pats = scmutil.expandpats(pats)
738 pats = scmutil.expandpats(pats)
739 if not pats:
739 if not pats:
740 raise error.Abort(_('no source or destination specified'))
740 raise error.Abort(_('no source or destination specified'))
741 if len(pats) == 1:
741 if len(pats) == 1:
742 raise error.Abort(_('no destination specified'))
742 raise error.Abort(_('no destination specified'))
743 dest = pats.pop()
743 dest = pats.pop()
744 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
744 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
745 if not destdirexists:
745 if not destdirexists:
746 if len(pats) > 1 or matchmod.patkind(pats[0]):
746 if len(pats) > 1 or matchmod.patkind(pats[0]):
747 raise error.Abort(_('with multiple sources, destination must be an '
747 raise error.Abort(_('with multiple sources, destination must be an '
748 'existing directory'))
748 'existing directory'))
749 if util.endswithsep(dest):
749 if util.endswithsep(dest):
750 raise error.Abort(_('destination %s is not a directory') % dest)
750 raise error.Abort(_('destination %s is not a directory') % dest)
751
751
752 tfn = targetpathfn
752 tfn = targetpathfn
753 if after:
753 if after:
754 tfn = targetpathafterfn
754 tfn = targetpathafterfn
755 copylist = []
755 copylist = []
756 for pat in pats:
756 for pat in pats:
757 srcs = walkpat(pat)
757 srcs = walkpat(pat)
758 if not srcs:
758 if not srcs:
759 continue
759 continue
760 copylist.append((tfn(pat, dest, srcs), srcs))
760 copylist.append((tfn(pat, dest, srcs), srcs))
761 if not copylist:
761 if not copylist:
762 raise error.Abort(_('no files to copy'))
762 raise error.Abort(_('no files to copy'))
763
763
764 errors = 0
764 errors = 0
765 for targetpath, srcs in copylist:
765 for targetpath, srcs in copylist:
766 for abssrc, relsrc, exact in srcs:
766 for abssrc, relsrc, exact in srcs:
767 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
767 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
768 errors += 1
768 errors += 1
769
769
770 if errors:
770 if errors:
771 ui.warn(_('(consider using --after)\n'))
771 ui.warn(_('(consider using --after)\n'))
772
772
773 return errors != 0
773 return errors != 0
774
774
775 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
775 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
776 runargs=None, appendpid=False):
776 runargs=None, appendpid=False):
777 '''Run a command as a service.'''
777 '''Run a command as a service.'''
778
778
779 def writepid(pid):
779 def writepid(pid):
780 if opts['pid_file']:
780 if opts['pid_file']:
781 if appendpid:
781 if appendpid:
782 mode = 'a'
782 mode = 'a'
783 else:
783 else:
784 mode = 'w'
784 mode = 'w'
785 fp = open(opts['pid_file'], mode)
785 fp = open(opts['pid_file'], mode)
786 fp.write(str(pid) + '\n')
786 fp.write(str(pid) + '\n')
787 fp.close()
787 fp.close()
788
788
789 if opts['daemon'] and not opts['daemon_postexec']:
789 if opts['daemon'] and not opts['daemon_postexec']:
790 # Signal child process startup with file removal
790 # Signal child process startup with file removal
791 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
791 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
792 os.close(lockfd)
792 os.close(lockfd)
793 try:
793 try:
794 if not runargs:
794 if not runargs:
795 runargs = util.hgcmd() + sys.argv[1:]
795 runargs = util.hgcmd() + sys.argv[1:]
796 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
796 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
797 # Don't pass --cwd to the child process, because we've already
797 # Don't pass --cwd to the child process, because we've already
798 # changed directory.
798 # changed directory.
799 for i in xrange(1, len(runargs)):
799 for i in xrange(1, len(runargs)):
800 if runargs[i].startswith('--cwd='):
800 if runargs[i].startswith('--cwd='):
801 del runargs[i]
801 del runargs[i]
802 break
802 break
803 elif runargs[i].startswith('--cwd'):
803 elif runargs[i].startswith('--cwd'):
804 del runargs[i:i + 2]
804 del runargs[i:i + 2]
805 break
805 break
806 def condfn():
806 def condfn():
807 return not os.path.exists(lockpath)
807 return not os.path.exists(lockpath)
808 pid = util.rundetached(runargs, condfn)
808 pid = util.rundetached(runargs, condfn)
809 if pid < 0:
809 if pid < 0:
810 raise error.Abort(_('child process failed to start'))
810 raise error.Abort(_('child process failed to start'))
811 writepid(pid)
811 writepid(pid)
812 finally:
812 finally:
813 try:
813 try:
814 os.unlink(lockpath)
814 os.unlink(lockpath)
815 except OSError as e:
815 except OSError as e:
816 if e.errno != errno.ENOENT:
816 if e.errno != errno.ENOENT:
817 raise
817 raise
818 if parentfn:
818 if parentfn:
819 return parentfn(pid)
819 return parentfn(pid)
820 else:
820 else:
821 return
821 return
822
822
823 if initfn:
823 if initfn:
824 initfn()
824 initfn()
825
825
826 if not opts['daemon']:
826 if not opts['daemon']:
827 writepid(util.getpid())
827 writepid(util.getpid())
828
828
829 if opts['daemon_postexec']:
829 if opts['daemon_postexec']:
830 inst = opts['daemon_postexec']
830 inst = opts['daemon_postexec']
831 try:
831 try:
832 os.setsid()
832 os.setsid()
833 except AttributeError:
833 except AttributeError:
834 pass
834 pass
835 if inst.startswith('unlink:'):
835 if inst.startswith('unlink:'):
836 lockpath = inst[7:]
836 lockpath = inst[7:]
837 os.unlink(lockpath)
837 os.unlink(lockpath)
838 elif inst != 'none':
838 elif inst != 'none':
839 raise error.Abort(_('invalid value for --daemon-postexec'))
839 raise error.Abort(_('invalid value for --daemon-postexec'))
840 util.hidewindow()
840 util.hidewindow()
841 sys.stdout.flush()
841 sys.stdout.flush()
842 sys.stderr.flush()
842 sys.stderr.flush()
843
843
844 nullfd = os.open(os.devnull, os.O_RDWR)
844 nullfd = os.open(os.devnull, os.O_RDWR)
845 logfilefd = nullfd
845 logfilefd = nullfd
846 if logfile:
846 if logfile:
847 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
847 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
848 os.dup2(nullfd, 0)
848 os.dup2(nullfd, 0)
849 os.dup2(logfilefd, 1)
849 os.dup2(logfilefd, 1)
850 os.dup2(logfilefd, 2)
850 os.dup2(logfilefd, 2)
851 if nullfd not in (0, 1, 2):
851 if nullfd not in (0, 1, 2):
852 os.close(nullfd)
852 os.close(nullfd)
853 if logfile and logfilefd not in (0, 1, 2):
853 if logfile and logfilefd not in (0, 1, 2):
854 os.close(logfilefd)
854 os.close(logfilefd)
855
855
856 if runfn:
856 if runfn:
857 return runfn()
857 return runfn()
858
858
859 ## facility to let extension process additional data into an import patch
859 ## facility to let extension process additional data into an import patch
860 # list of identifier to be executed in order
860 # list of identifier to be executed in order
861 extrapreimport = [] # run before commit
861 extrapreimport = [] # run before commit
862 extrapostimport = [] # run after commit
862 extrapostimport = [] # run after commit
863 # mapping from identifier to actual import function
863 # mapping from identifier to actual import function
864 #
864 #
865 # 'preimport' are run before the commit is made and are provided the following
865 # 'preimport' are run before the commit is made and are provided the following
866 # arguments:
866 # arguments:
867 # - repo: the localrepository instance,
867 # - repo: the localrepository instance,
868 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
868 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
869 # - extra: the future extra dictionary of the changeset, please mutate it,
869 # - extra: the future extra dictionary of the changeset, please mutate it,
870 # - opts: the import options.
870 # - opts: the import options.
871 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
871 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
872 # mutation of in memory commit and more. Feel free to rework the code to get
872 # mutation of in memory commit and more. Feel free to rework the code to get
873 # there.
873 # there.
874 extrapreimportmap = {}
874 extrapreimportmap = {}
875 # 'postimport' are run after the commit is made and are provided the following
875 # 'postimport' are run after the commit is made and are provided the following
876 # argument:
876 # argument:
877 # - ctx: the changectx created by import.
877 # - ctx: the changectx created by import.
878 extrapostimportmap = {}
878 extrapostimportmap = {}
879
879
880 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
880 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
881 """Utility function used by commands.import to import a single patch
881 """Utility function used by commands.import to import a single patch
882
882
883 This function is explicitly defined here to help the evolve extension to
883 This function is explicitly defined here to help the evolve extension to
884 wrap this part of the import logic.
884 wrap this part of the import logic.
885
885
886 The API is currently a bit ugly because it a simple code translation from
886 The API is currently a bit ugly because it a simple code translation from
887 the import command. Feel free to make it better.
887 the import command. Feel free to make it better.
888
888
889 :hunk: a patch (as a binary string)
889 :hunk: a patch (as a binary string)
890 :parents: nodes that will be parent of the created commit
890 :parents: nodes that will be parent of the created commit
891 :opts: the full dict of option passed to the import command
891 :opts: the full dict of option passed to the import command
892 :msgs: list to save commit message to.
892 :msgs: list to save commit message to.
893 (used in case we need to save it when failing)
893 (used in case we need to save it when failing)
894 :updatefunc: a function that update a repo to a given node
894 :updatefunc: a function that update a repo to a given node
895 updatefunc(<repo>, <node>)
895 updatefunc(<repo>, <node>)
896 """
896 """
897 # avoid cycle context -> subrepo -> cmdutil
897 # avoid cycle context -> subrepo -> cmdutil
898 from . import context
898 from . import context
899 extractdata = patch.extract(ui, hunk)
899 extractdata = patch.extract(ui, hunk)
900 tmpname = extractdata.get('filename')
900 tmpname = extractdata.get('filename')
901 message = extractdata.get('message')
901 message = extractdata.get('message')
902 user = opts.get('user') or extractdata.get('user')
902 user = opts.get('user') or extractdata.get('user')
903 date = opts.get('date') or extractdata.get('date')
903 date = opts.get('date') or extractdata.get('date')
904 branch = extractdata.get('branch')
904 branch = extractdata.get('branch')
905 nodeid = extractdata.get('nodeid')
905 nodeid = extractdata.get('nodeid')
906 p1 = extractdata.get('p1')
906 p1 = extractdata.get('p1')
907 p2 = extractdata.get('p2')
907 p2 = extractdata.get('p2')
908
908
909 nocommit = opts.get('no_commit')
909 nocommit = opts.get('no_commit')
910 importbranch = opts.get('import_branch')
910 importbranch = opts.get('import_branch')
911 update = not opts.get('bypass')
911 update = not opts.get('bypass')
912 strip = opts["strip"]
912 strip = opts["strip"]
913 prefix = opts["prefix"]
913 prefix = opts["prefix"]
914 sim = float(opts.get('similarity') or 0)
914 sim = float(opts.get('similarity') or 0)
915 if not tmpname:
915 if not tmpname:
916 return (None, None, False)
916 return (None, None, False)
917
917
918 rejects = False
918 rejects = False
919
919
920 try:
920 try:
921 cmdline_message = logmessage(ui, opts)
921 cmdline_message = logmessage(ui, opts)
922 if cmdline_message:
922 if cmdline_message:
923 # pickup the cmdline msg
923 # pickup the cmdline msg
924 message = cmdline_message
924 message = cmdline_message
925 elif message:
925 elif message:
926 # pickup the patch msg
926 # pickup the patch msg
927 message = message.strip()
927 message = message.strip()
928 else:
928 else:
929 # launch the editor
929 # launch the editor
930 message = None
930 message = None
931 ui.debug('message:\n%s\n' % message)
931 ui.debug('message:\n%s\n' % message)
932
932
933 if len(parents) == 1:
933 if len(parents) == 1:
934 parents.append(repo[nullid])
934 parents.append(repo[nullid])
935 if opts.get('exact'):
935 if opts.get('exact'):
936 if not nodeid or not p1:
936 if not nodeid or not p1:
937 raise error.Abort(_('not a Mercurial patch'))
937 raise error.Abort(_('not a Mercurial patch'))
938 p1 = repo[p1]
938 p1 = repo[p1]
939 p2 = repo[p2 or nullid]
939 p2 = repo[p2 or nullid]
940 elif p2:
940 elif p2:
941 try:
941 try:
942 p1 = repo[p1]
942 p1 = repo[p1]
943 p2 = repo[p2]
943 p2 = repo[p2]
944 # Without any options, consider p2 only if the
944 # Without any options, consider p2 only if the
945 # patch is being applied on top of the recorded
945 # patch is being applied on top of the recorded
946 # first parent.
946 # first parent.
947 if p1 != parents[0]:
947 if p1 != parents[0]:
948 p1 = parents[0]
948 p1 = parents[0]
949 p2 = repo[nullid]
949 p2 = repo[nullid]
950 except error.RepoError:
950 except error.RepoError:
951 p1, p2 = parents
951 p1, p2 = parents
952 if p2.node() == nullid:
952 if p2.node() == nullid:
953 ui.warn(_("warning: import the patch as a normal revision\n"
953 ui.warn(_("warning: import the patch as a normal revision\n"
954 "(use --exact to import the patch as a merge)\n"))
954 "(use --exact to import the patch as a merge)\n"))
955 else:
955 else:
956 p1, p2 = parents
956 p1, p2 = parents
957
957
958 n = None
958 n = None
959 if update:
959 if update:
960 if p1 != parents[0]:
960 if p1 != parents[0]:
961 updatefunc(repo, p1.node())
961 updatefunc(repo, p1.node())
962 if p2 != parents[1]:
962 if p2 != parents[1]:
963 repo.setparents(p1.node(), p2.node())
963 repo.setparents(p1.node(), p2.node())
964
964
965 if opts.get('exact') or importbranch:
965 if opts.get('exact') or importbranch:
966 repo.dirstate.setbranch(branch or 'default')
966 repo.dirstate.setbranch(branch or 'default')
967
967
968 partial = opts.get('partial', False)
968 partial = opts.get('partial', False)
969 files = set()
969 files = set()
970 try:
970 try:
971 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
971 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
972 files=files, eolmode=None, similarity=sim / 100.0)
972 files=files, eolmode=None, similarity=sim / 100.0)
973 except patch.PatchError as e:
973 except patch.PatchError as e:
974 if not partial:
974 if not partial:
975 raise error.Abort(str(e))
975 raise error.Abort(str(e))
976 if partial:
976 if partial:
977 rejects = True
977 rejects = True
978
978
979 files = list(files)
979 files = list(files)
980 if nocommit:
980 if nocommit:
981 if message:
981 if message:
982 msgs.append(message)
982 msgs.append(message)
983 else:
983 else:
984 if opts.get('exact') or p2:
984 if opts.get('exact') or p2:
985 # If you got here, you either use --force and know what
985 # If you got here, you either use --force and know what
986 # you are doing or used --exact or a merge patch while
986 # you are doing or used --exact or a merge patch while
987 # being updated to its first parent.
987 # being updated to its first parent.
988 m = None
988 m = None
989 else:
989 else:
990 m = scmutil.matchfiles(repo, files or [])
990 m = scmutil.matchfiles(repo, files or [])
991 editform = mergeeditform(repo[None], 'import.normal')
991 editform = mergeeditform(repo[None], 'import.normal')
992 if opts.get('exact'):
992 if opts.get('exact'):
993 editor = None
993 editor = None
994 else:
994 else:
995 editor = getcommiteditor(editform=editform, **opts)
995 editor = getcommiteditor(editform=editform, **opts)
996 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
996 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
997 extra = {}
997 extra = {}
998 for idfunc in extrapreimport:
998 for idfunc in extrapreimport:
999 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
999 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1000 try:
1000 try:
1001 if partial:
1001 if partial:
1002 repo.ui.setconfig('ui', 'allowemptycommit', True)
1002 repo.ui.setconfig('ui', 'allowemptycommit', True)
1003 n = repo.commit(message, user,
1003 n = repo.commit(message, user,
1004 date, match=m,
1004 date, match=m,
1005 editor=editor, extra=extra)
1005 editor=editor, extra=extra)
1006 for idfunc in extrapostimport:
1006 for idfunc in extrapostimport:
1007 extrapostimportmap[idfunc](repo[n])
1007 extrapostimportmap[idfunc](repo[n])
1008 finally:
1008 finally:
1009 repo.ui.restoreconfig(allowemptyback)
1009 repo.ui.restoreconfig(allowemptyback)
1010 else:
1010 else:
1011 if opts.get('exact') or importbranch:
1011 if opts.get('exact') or importbranch:
1012 branch = branch or 'default'
1012 branch = branch or 'default'
1013 else:
1013 else:
1014 branch = p1.branch()
1014 branch = p1.branch()
1015 store = patch.filestore()
1015 store = patch.filestore()
1016 try:
1016 try:
1017 files = set()
1017 files = set()
1018 try:
1018 try:
1019 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1019 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1020 files, eolmode=None)
1020 files, eolmode=None)
1021 except patch.PatchError as e:
1021 except patch.PatchError as e:
1022 raise error.Abort(str(e))
1022 raise error.Abort(str(e))
1023 if opts.get('exact'):
1023 if opts.get('exact'):
1024 editor = None
1024 editor = None
1025 else:
1025 else:
1026 editor = getcommiteditor(editform='import.bypass')
1026 editor = getcommiteditor(editform='import.bypass')
1027 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1027 memctx = context.makememctx(repo, (p1.node(), p2.node()),
1028 message,
1028 message,
1029 user,
1029 user,
1030 date,
1030 date,
1031 branch, files, store,
1031 branch, files, store,
1032 editor=editor)
1032 editor=editor)
1033 n = memctx.commit()
1033 n = memctx.commit()
1034 finally:
1034 finally:
1035 store.close()
1035 store.close()
1036 if opts.get('exact') and nocommit:
1036 if opts.get('exact') and nocommit:
1037 # --exact with --no-commit is still useful in that it does merge
1037 # --exact with --no-commit is still useful in that it does merge
1038 # and branch bits
1038 # and branch bits
1039 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1039 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1040 elif opts.get('exact') and hex(n) != nodeid:
1040 elif opts.get('exact') and hex(n) != nodeid:
1041 raise error.Abort(_('patch is damaged or loses information'))
1041 raise error.Abort(_('patch is damaged or loses information'))
1042 msg = _('applied to working directory')
1042 msg = _('applied to working directory')
1043 if n:
1043 if n:
1044 # i18n: refers to a short changeset id
1044 # i18n: refers to a short changeset id
1045 msg = _('created %s') % short(n)
1045 msg = _('created %s') % short(n)
1046 return (msg, n, rejects)
1046 return (msg, n, rejects)
1047 finally:
1047 finally:
1048 os.unlink(tmpname)
1048 os.unlink(tmpname)
1049
1049
1050 # facility to let extensions include additional data in an exported patch
1050 # facility to let extensions include additional data in an exported patch
1051 # list of identifiers to be executed in order
1051 # list of identifiers to be executed in order
1052 extraexport = []
1052 extraexport = []
1053 # mapping from identifier to actual export function
1053 # mapping from identifier to actual export function
1054 # function as to return a string to be added to the header or None
1054 # function as to return a string to be added to the header or None
1055 # it is given two arguments (sequencenumber, changectx)
1055 # it is given two arguments (sequencenumber, changectx)
1056 extraexportmap = {}
1056 extraexportmap = {}
1057
1057
1058 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1058 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1059 opts=None, match=None):
1059 opts=None, match=None):
1060 '''export changesets as hg patches.'''
1060 '''export changesets as hg patches.'''
1061
1061
1062 total = len(revs)
1062 total = len(revs)
1063 revwidth = max([len(str(rev)) for rev in revs])
1063 revwidth = max([len(str(rev)) for rev in revs])
1064 filemode = {}
1064 filemode = {}
1065
1065
1066 def single(rev, seqno, fp):
1066 def single(rev, seqno, fp):
1067 ctx = repo[rev]
1067 ctx = repo[rev]
1068 node = ctx.node()
1068 node = ctx.node()
1069 parents = [p.node() for p in ctx.parents() if p]
1069 parents = [p.node() for p in ctx.parents() if p]
1070 branch = ctx.branch()
1070 branch = ctx.branch()
1071 if switch_parent:
1071 if switch_parent:
1072 parents.reverse()
1072 parents.reverse()
1073
1073
1074 if parents:
1074 if parents:
1075 prev = parents[0]
1075 prev = parents[0]
1076 else:
1076 else:
1077 prev = nullid
1077 prev = nullid
1078
1078
1079 shouldclose = False
1079 shouldclose = False
1080 if not fp and len(template) > 0:
1080 if not fp and len(template) > 0:
1081 desc_lines = ctx.description().rstrip().split('\n')
1081 desc_lines = ctx.description().rstrip().split('\n')
1082 desc = desc_lines[0] #Commit always has a first line.
1082 desc = desc_lines[0] #Commit always has a first line.
1083 fp = makefileobj(repo, template, node, desc=desc, total=total,
1083 fp = makefileobj(repo, template, node, desc=desc, total=total,
1084 seqno=seqno, revwidth=revwidth, mode='wb',
1084 seqno=seqno, revwidth=revwidth, mode='wb',
1085 modemap=filemode)
1085 modemap=filemode)
1086 shouldclose = True
1086 shouldclose = True
1087 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1087 if fp and not getattr(fp, 'name', '<unnamed>').startswith('<'):
1088 repo.ui.note("%s\n" % fp.name)
1088 repo.ui.note("%s\n" % fp.name)
1089
1089
1090 if not fp:
1090 if not fp:
1091 write = repo.ui.write
1091 write = repo.ui.write
1092 else:
1092 else:
1093 def write(s, **kw):
1093 def write(s, **kw):
1094 fp.write(s)
1094 fp.write(s)
1095
1095
1096 write("# HG changeset patch\n")
1096 write("# HG changeset patch\n")
1097 write("# User %s\n" % ctx.user())
1097 write("# User %s\n" % ctx.user())
1098 write("# Date %d %d\n" % ctx.date())
1098 write("# Date %d %d\n" % ctx.date())
1099 write("# %s\n" % util.datestr(ctx.date()))
1099 write("# %s\n" % util.datestr(ctx.date()))
1100 if branch and branch != 'default':
1100 if branch and branch != 'default':
1101 write("# Branch %s\n" % branch)
1101 write("# Branch %s\n" % branch)
1102 write("# Node ID %s\n" % hex(node))
1102 write("# Node ID %s\n" % hex(node))
1103 write("# Parent %s\n" % hex(prev))
1103 write("# Parent %s\n" % hex(prev))
1104 if len(parents) > 1:
1104 if len(parents) > 1:
1105 write("# Parent %s\n" % hex(parents[1]))
1105 write("# Parent %s\n" % hex(parents[1]))
1106
1106
1107 for headerid in extraexport:
1107 for headerid in extraexport:
1108 header = extraexportmap[headerid](seqno, ctx)
1108 header = extraexportmap[headerid](seqno, ctx)
1109 if header is not None:
1109 if header is not None:
1110 write('# %s\n' % header)
1110 write('# %s\n' % header)
1111 write(ctx.description().rstrip())
1111 write(ctx.description().rstrip())
1112 write("\n\n")
1112 write("\n\n")
1113
1113
1114 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1114 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1115 write(chunk, label=label)
1115 write(chunk, label=label)
1116
1116
1117 if shouldclose:
1117 if shouldclose:
1118 fp.close()
1118 fp.close()
1119
1119
1120 for seqno, rev in enumerate(revs):
1120 for seqno, rev in enumerate(revs):
1121 single(rev, seqno + 1, fp)
1121 single(rev, seqno + 1, fp)
1122
1122
1123 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1123 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1124 changes=None, stat=False, fp=None, prefix='',
1124 changes=None, stat=False, fp=None, prefix='',
1125 root='', listsubrepos=False):
1125 root='', listsubrepos=False):
1126 '''show diff or diffstat.'''
1126 '''show diff or diffstat.'''
1127 if fp is None:
1127 if fp is None:
1128 write = ui.write
1128 write = ui.write
1129 else:
1129 else:
1130 def write(s, **kw):
1130 def write(s, **kw):
1131 fp.write(s)
1131 fp.write(s)
1132
1132
1133 if root:
1133 if root:
1134 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1134 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1135 else:
1135 else:
1136 relroot = ''
1136 relroot = ''
1137 if relroot != '':
1137 if relroot != '':
1138 # XXX relative roots currently don't work if the root is within a
1138 # XXX relative roots currently don't work if the root is within a
1139 # subrepo
1139 # subrepo
1140 uirelroot = match.uipath(relroot)
1140 uirelroot = match.uipath(relroot)
1141 relroot += '/'
1141 relroot += '/'
1142 for matchroot in match.files():
1142 for matchroot in match.files():
1143 if not matchroot.startswith(relroot):
1143 if not matchroot.startswith(relroot):
1144 ui.warn(_('warning: %s not inside relative root %s\n') % (
1144 ui.warn(_('warning: %s not inside relative root %s\n') % (
1145 match.uipath(matchroot), uirelroot))
1145 match.uipath(matchroot), uirelroot))
1146
1146
1147 if stat:
1147 if stat:
1148 diffopts = diffopts.copy(context=0)
1148 diffopts = diffopts.copy(context=0)
1149 width = 80
1149 width = 80
1150 if not ui.plain():
1150 if not ui.plain():
1151 width = ui.termwidth()
1151 width = ui.termwidth()
1152 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1152 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1153 prefix=prefix, relroot=relroot)
1153 prefix=prefix, relroot=relroot)
1154 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1154 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1155 width=width,
1155 width=width,
1156 git=diffopts.git):
1156 git=diffopts.git):
1157 write(chunk, label=label)
1157 write(chunk, label=label)
1158 else:
1158 else:
1159 for chunk, label in patch.diffui(repo, node1, node2, match,
1159 for chunk, label in patch.diffui(repo, node1, node2, match,
1160 changes, diffopts, prefix=prefix,
1160 changes, diffopts, prefix=prefix,
1161 relroot=relroot):
1161 relroot=relroot):
1162 write(chunk, label=label)
1162 write(chunk, label=label)
1163
1163
1164 if listsubrepos:
1164 if listsubrepos:
1165 ctx1 = repo[node1]
1165 ctx1 = repo[node1]
1166 ctx2 = repo[node2]
1166 ctx2 = repo[node2]
1167 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1167 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1168 tempnode2 = node2
1168 tempnode2 = node2
1169 try:
1169 try:
1170 if node2 is not None:
1170 if node2 is not None:
1171 tempnode2 = ctx2.substate[subpath][1]
1171 tempnode2 = ctx2.substate[subpath][1]
1172 except KeyError:
1172 except KeyError:
1173 # A subrepo that existed in node1 was deleted between node1 and
1173 # A subrepo that existed in node1 was deleted between node1 and
1174 # node2 (inclusive). Thus, ctx2's substate won't contain that
1174 # node2 (inclusive). Thus, ctx2's substate won't contain that
1175 # subpath. The best we can do is to ignore it.
1175 # subpath. The best we can do is to ignore it.
1176 tempnode2 = None
1176 tempnode2 = None
1177 submatch = matchmod.subdirmatcher(subpath, match)
1177 submatch = matchmod.subdirmatcher(subpath, match)
1178 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1178 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1179 stat=stat, fp=fp, prefix=prefix)
1179 stat=stat, fp=fp, prefix=prefix)
1180
1180
1181 class changeset_printer(object):
1181 class changeset_printer(object):
1182 '''show changeset information when templating not requested.'''
1182 '''show changeset information when templating not requested.'''
1183
1183
1184 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1184 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1185 self.ui = ui
1185 self.ui = ui
1186 self.repo = repo
1186 self.repo = repo
1187 self.buffered = buffered
1187 self.buffered = buffered
1188 self.matchfn = matchfn
1188 self.matchfn = matchfn
1189 self.diffopts = diffopts
1189 self.diffopts = diffopts
1190 self.header = {}
1190 self.header = {}
1191 self.hunk = {}
1191 self.hunk = {}
1192 self.lastheader = None
1192 self.lastheader = None
1193 self.footer = None
1193 self.footer = None
1194
1194
1195 def flush(self, ctx):
1195 def flush(self, ctx):
1196 rev = ctx.rev()
1196 rev = ctx.rev()
1197 if rev in self.header:
1197 if rev in self.header:
1198 h = self.header[rev]
1198 h = self.header[rev]
1199 if h != self.lastheader:
1199 if h != self.lastheader:
1200 self.lastheader = h
1200 self.lastheader = h
1201 self.ui.write(h)
1201 self.ui.write(h)
1202 del self.header[rev]
1202 del self.header[rev]
1203 if rev in self.hunk:
1203 if rev in self.hunk:
1204 self.ui.write(self.hunk[rev])
1204 self.ui.write(self.hunk[rev])
1205 del self.hunk[rev]
1205 del self.hunk[rev]
1206 return 1
1206 return 1
1207 return 0
1207 return 0
1208
1208
1209 def close(self):
1209 def close(self):
1210 if self.footer:
1210 if self.footer:
1211 self.ui.write(self.footer)
1211 self.ui.write(self.footer)
1212
1212
1213 def show(self, ctx, copies=None, matchfn=None, **props):
1213 def show(self, ctx, copies=None, matchfn=None, **props):
1214 if self.buffered:
1214 if self.buffered:
1215 self.ui.pushbuffer(labeled=True)
1215 self.ui.pushbuffer(labeled=True)
1216 self._show(ctx, copies, matchfn, props)
1216 self._show(ctx, copies, matchfn, props)
1217 self.hunk[ctx.rev()] = self.ui.popbuffer()
1217 self.hunk[ctx.rev()] = self.ui.popbuffer()
1218 else:
1218 else:
1219 self._show(ctx, copies, matchfn, props)
1219 self._show(ctx, copies, matchfn, props)
1220
1220
1221 def _show(self, ctx, copies, matchfn, props):
1221 def _show(self, ctx, copies, matchfn, props):
1222 '''show a single changeset or file revision'''
1222 '''show a single changeset or file revision'''
1223 changenode = ctx.node()
1223 changenode = ctx.node()
1224 rev = ctx.rev()
1224 rev = ctx.rev()
1225 if self.ui.debugflag:
1225 if self.ui.debugflag:
1226 hexfunc = hex
1226 hexfunc = hex
1227 else:
1227 else:
1228 hexfunc = short
1228 hexfunc = short
1229 # as of now, wctx.node() and wctx.rev() return None, but we want to
1229 # as of now, wctx.node() and wctx.rev() return None, but we want to
1230 # show the same values as {node} and {rev} templatekw
1230 # show the same values as {node} and {rev} templatekw
1231 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1231 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1232
1232
1233 if self.ui.quiet:
1233 if self.ui.quiet:
1234 self.ui.write("%d:%s\n" % revnode, label='log.node')
1234 self.ui.write("%d:%s\n" % revnode, label='log.node')
1235 return
1235 return
1236
1236
1237 date = util.datestr(ctx.date())
1237 date = util.datestr(ctx.date())
1238
1238
1239 # i18n: column positioning for "hg log"
1239 # i18n: column positioning for "hg log"
1240 self.ui.write(_("changeset: %d:%s\n") % revnode,
1240 self.ui.write(_("changeset: %d:%s\n") % revnode,
1241 label='log.changeset changeset.%s' % ctx.phasestr())
1241 label='log.changeset changeset.%s' % ctx.phasestr())
1242
1242
1243 # branches are shown first before any other names due to backwards
1243 # branches are shown first before any other names due to backwards
1244 # compatibility
1244 # compatibility
1245 branch = ctx.branch()
1245 branch = ctx.branch()
1246 # don't show the default branch name
1246 # don't show the default branch name
1247 if branch != 'default':
1247 if branch != 'default':
1248 # i18n: column positioning for "hg log"
1248 # i18n: column positioning for "hg log"
1249 self.ui.write(_("branch: %s\n") % branch,
1249 self.ui.write(_("branch: %s\n") % branch,
1250 label='log.branch')
1250 label='log.branch')
1251
1251
1252 for name, ns in self.repo.names.iteritems():
1252 for name, ns in self.repo.names.iteritems():
1253 # branches has special logic already handled above, so here we just
1253 # branches has special logic already handled above, so here we just
1254 # skip it
1254 # skip it
1255 if name == 'branches':
1255 if name == 'branches':
1256 continue
1256 continue
1257 # we will use the templatename as the color name since those two
1257 # we will use the templatename as the color name since those two
1258 # should be the same
1258 # should be the same
1259 for name in ns.names(self.repo, changenode):
1259 for name in ns.names(self.repo, changenode):
1260 self.ui.write(ns.logfmt % name,
1260 self.ui.write(ns.logfmt % name,
1261 label='log.%s' % ns.colorname)
1261 label='log.%s' % ns.colorname)
1262 if self.ui.debugflag:
1262 if self.ui.debugflag:
1263 # i18n: column positioning for "hg log"
1263 # i18n: column positioning for "hg log"
1264 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1264 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1265 label='log.phase')
1265 label='log.phase')
1266 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1266 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1267 label = 'log.parent changeset.%s' % pctx.phasestr()
1267 label = 'log.parent changeset.%s' % pctx.phasestr()
1268 # i18n: column positioning for "hg log"
1268 # i18n: column positioning for "hg log"
1269 self.ui.write(_("parent: %d:%s\n")
1269 self.ui.write(_("parent: %d:%s\n")
1270 % (pctx.rev(), hexfunc(pctx.node())),
1270 % (pctx.rev(), hexfunc(pctx.node())),
1271 label=label)
1271 label=label)
1272
1272
1273 if self.ui.debugflag and rev is not None:
1273 if self.ui.debugflag and rev is not None:
1274 mnode = ctx.manifestnode()
1274 mnode = ctx.manifestnode()
1275 # i18n: column positioning for "hg log"
1275 # i18n: column positioning for "hg log"
1276 self.ui.write(_("manifest: %d:%s\n") %
1276 self.ui.write(_("manifest: %d:%s\n") %
1277 (self.repo.manifest.rev(mnode), hex(mnode)),
1277 (self.repo.manifest.rev(mnode), hex(mnode)),
1278 label='ui.debug log.manifest')
1278 label='ui.debug log.manifest')
1279 # i18n: column positioning for "hg log"
1279 # i18n: column positioning for "hg log"
1280 self.ui.write(_("user: %s\n") % ctx.user(),
1280 self.ui.write(_("user: %s\n") % ctx.user(),
1281 label='log.user')
1281 label='log.user')
1282 # i18n: column positioning for "hg log"
1282 # i18n: column positioning for "hg log"
1283 self.ui.write(_("date: %s\n") % date,
1283 self.ui.write(_("date: %s\n") % date,
1284 label='log.date')
1284 label='log.date')
1285
1285
1286 if self.ui.debugflag:
1286 if self.ui.debugflag:
1287 files = ctx.p1().status(ctx)[:3]
1287 files = ctx.p1().status(ctx)[:3]
1288 for key, value in zip([# i18n: column positioning for "hg log"
1288 for key, value in zip([# i18n: column positioning for "hg log"
1289 _("files:"),
1289 _("files:"),
1290 # i18n: column positioning for "hg log"
1290 # i18n: column positioning for "hg log"
1291 _("files+:"),
1291 _("files+:"),
1292 # i18n: column positioning for "hg log"
1292 # i18n: column positioning for "hg log"
1293 _("files-:")], files):
1293 _("files-:")], files):
1294 if value:
1294 if value:
1295 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1295 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1296 label='ui.debug log.files')
1296 label='ui.debug log.files')
1297 elif ctx.files() and self.ui.verbose:
1297 elif ctx.files() and self.ui.verbose:
1298 # i18n: column positioning for "hg log"
1298 # i18n: column positioning for "hg log"
1299 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1299 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1300 label='ui.note log.files')
1300 label='ui.note log.files')
1301 if copies and self.ui.verbose:
1301 if copies and self.ui.verbose:
1302 copies = ['%s (%s)' % c for c in copies]
1302 copies = ['%s (%s)' % c for c in copies]
1303 # i18n: column positioning for "hg log"
1303 # i18n: column positioning for "hg log"
1304 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1304 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1305 label='ui.note log.copies')
1305 label='ui.note log.copies')
1306
1306
1307 extra = ctx.extra()
1307 extra = ctx.extra()
1308 if extra and self.ui.debugflag:
1308 if extra and self.ui.debugflag:
1309 for key, value in sorted(extra.items()):
1309 for key, value in sorted(extra.items()):
1310 # i18n: column positioning for "hg log"
1310 # i18n: column positioning for "hg log"
1311 self.ui.write(_("extra: %s=%s\n")
1311 self.ui.write(_("extra: %s=%s\n")
1312 % (key, value.encode('string_escape')),
1312 % (key, value.encode('string_escape')),
1313 label='ui.debug log.extra')
1313 label='ui.debug log.extra')
1314
1314
1315 description = ctx.description().strip()
1315 description = ctx.description().strip()
1316 if description:
1316 if description:
1317 if self.ui.verbose:
1317 if self.ui.verbose:
1318 self.ui.write(_("description:\n"),
1318 self.ui.write(_("description:\n"),
1319 label='ui.note log.description')
1319 label='ui.note log.description')
1320 self.ui.write(description,
1320 self.ui.write(description,
1321 label='ui.note log.description')
1321 label='ui.note log.description')
1322 self.ui.write("\n\n")
1322 self.ui.write("\n\n")
1323 else:
1323 else:
1324 # i18n: column positioning for "hg log"
1324 # i18n: column positioning for "hg log"
1325 self.ui.write(_("summary: %s\n") %
1325 self.ui.write(_("summary: %s\n") %
1326 description.splitlines()[0],
1326 description.splitlines()[0],
1327 label='log.summary')
1327 label='log.summary')
1328 self.ui.write("\n")
1328 self.ui.write("\n")
1329
1329
1330 self.showpatch(ctx, matchfn)
1330 self.showpatch(ctx, matchfn)
1331
1331
1332 def showpatch(self, ctx, matchfn):
1332 def showpatch(self, ctx, matchfn):
1333 if not matchfn:
1333 if not matchfn:
1334 matchfn = self.matchfn
1334 matchfn = self.matchfn
1335 if matchfn:
1335 if matchfn:
1336 stat = self.diffopts.get('stat')
1336 stat = self.diffopts.get('stat')
1337 diff = self.diffopts.get('patch')
1337 diff = self.diffopts.get('patch')
1338 diffopts = patch.diffallopts(self.ui, self.diffopts)
1338 diffopts = patch.diffallopts(self.ui, self.diffopts)
1339 node = ctx.node()
1339 node = ctx.node()
1340 prev = ctx.p1().node()
1340 prev = ctx.p1().node()
1341 if stat:
1341 if stat:
1342 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1342 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1343 match=matchfn, stat=True)
1343 match=matchfn, stat=True)
1344 if diff:
1344 if diff:
1345 if stat:
1345 if stat:
1346 self.ui.write("\n")
1346 self.ui.write("\n")
1347 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1347 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1348 match=matchfn, stat=False)
1348 match=matchfn, stat=False)
1349 self.ui.write("\n")
1349 self.ui.write("\n")
1350
1350
1351 class jsonchangeset(changeset_printer):
1351 class jsonchangeset(changeset_printer):
1352 '''format changeset information.'''
1352 '''format changeset information.'''
1353
1353
1354 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1354 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1355 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1355 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1356 self.cache = {}
1356 self.cache = {}
1357 self._first = True
1357 self._first = True
1358
1358
1359 def close(self):
1359 def close(self):
1360 if not self._first:
1360 if not self._first:
1361 self.ui.write("\n]\n")
1361 self.ui.write("\n]\n")
1362 else:
1362 else:
1363 self.ui.write("[]\n")
1363 self.ui.write("[]\n")
1364
1364
1365 def _show(self, ctx, copies, matchfn, props):
1365 def _show(self, ctx, copies, matchfn, props):
1366 '''show a single changeset or file revision'''
1366 '''show a single changeset or file revision'''
1367 rev = ctx.rev()
1367 rev = ctx.rev()
1368 if rev is None:
1368 if rev is None:
1369 jrev = jnode = 'null'
1369 jrev = jnode = 'null'
1370 else:
1370 else:
1371 jrev = str(rev)
1371 jrev = str(rev)
1372 jnode = '"%s"' % hex(ctx.node())
1372 jnode = '"%s"' % hex(ctx.node())
1373 j = encoding.jsonescape
1373 j = encoding.jsonescape
1374
1374
1375 if self._first:
1375 if self._first:
1376 self.ui.write("[\n {")
1376 self.ui.write("[\n {")
1377 self._first = False
1377 self._first = False
1378 else:
1378 else:
1379 self.ui.write(",\n {")
1379 self.ui.write(",\n {")
1380
1380
1381 if self.ui.quiet:
1381 if self.ui.quiet:
1382 self.ui.write('\n "rev": %s' % jrev)
1382 self.ui.write('\n "rev": %s' % jrev)
1383 self.ui.write(',\n "node": %s' % jnode)
1383 self.ui.write(',\n "node": %s' % jnode)
1384 self.ui.write('\n }')
1384 self.ui.write('\n }')
1385 return
1385 return
1386
1386
1387 self.ui.write('\n "rev": %s' % jrev)
1387 self.ui.write('\n "rev": %s' % jrev)
1388 self.ui.write(',\n "node": %s' % jnode)
1388 self.ui.write(',\n "node": %s' % jnode)
1389 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1389 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1390 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1390 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1391 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1391 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1392 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1392 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1393 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1393 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1394
1394
1395 self.ui.write(',\n "bookmarks": [%s]' %
1395 self.ui.write(',\n "bookmarks": [%s]' %
1396 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1396 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1397 self.ui.write(',\n "tags": [%s]' %
1397 self.ui.write(',\n "tags": [%s]' %
1398 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1398 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1399 self.ui.write(',\n "parents": [%s]' %
1399 self.ui.write(',\n "parents": [%s]' %
1400 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1400 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1401
1401
1402 if self.ui.debugflag:
1402 if self.ui.debugflag:
1403 if rev is None:
1403 if rev is None:
1404 jmanifestnode = 'null'
1404 jmanifestnode = 'null'
1405 else:
1405 else:
1406 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1406 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1407 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1407 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1408
1408
1409 self.ui.write(',\n "extra": {%s}' %
1409 self.ui.write(',\n "extra": {%s}' %
1410 ", ".join('"%s": "%s"' % (j(k), j(v))
1410 ", ".join('"%s": "%s"' % (j(k), j(v))
1411 for k, v in ctx.extra().items()))
1411 for k, v in ctx.extra().items()))
1412
1412
1413 files = ctx.p1().status(ctx)
1413 files = ctx.p1().status(ctx)
1414 self.ui.write(',\n "modified": [%s]' %
1414 self.ui.write(',\n "modified": [%s]' %
1415 ", ".join('"%s"' % j(f) for f in files[0]))
1415 ", ".join('"%s"' % j(f) for f in files[0]))
1416 self.ui.write(',\n "added": [%s]' %
1416 self.ui.write(',\n "added": [%s]' %
1417 ", ".join('"%s"' % j(f) for f in files[1]))
1417 ", ".join('"%s"' % j(f) for f in files[1]))
1418 self.ui.write(',\n "removed": [%s]' %
1418 self.ui.write(',\n "removed": [%s]' %
1419 ", ".join('"%s"' % j(f) for f in files[2]))
1419 ", ".join('"%s"' % j(f) for f in files[2]))
1420
1420
1421 elif self.ui.verbose:
1421 elif self.ui.verbose:
1422 self.ui.write(',\n "files": [%s]' %
1422 self.ui.write(',\n "files": [%s]' %
1423 ", ".join('"%s"' % j(f) for f in ctx.files()))
1423 ", ".join('"%s"' % j(f) for f in ctx.files()))
1424
1424
1425 if copies:
1425 if copies:
1426 self.ui.write(',\n "copies": {%s}' %
1426 self.ui.write(',\n "copies": {%s}' %
1427 ", ".join('"%s": "%s"' % (j(k), j(v))
1427 ", ".join('"%s": "%s"' % (j(k), j(v))
1428 for k, v in copies))
1428 for k, v in copies))
1429
1429
1430 matchfn = self.matchfn
1430 matchfn = self.matchfn
1431 if matchfn:
1431 if matchfn:
1432 stat = self.diffopts.get('stat')
1432 stat = self.diffopts.get('stat')
1433 diff = self.diffopts.get('patch')
1433 diff = self.diffopts.get('patch')
1434 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1434 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1435 node, prev = ctx.node(), ctx.p1().node()
1435 node, prev = ctx.node(), ctx.p1().node()
1436 if stat:
1436 if stat:
1437 self.ui.pushbuffer()
1437 self.ui.pushbuffer()
1438 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1438 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1439 match=matchfn, stat=True)
1439 match=matchfn, stat=True)
1440 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1440 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1441 if diff:
1441 if diff:
1442 self.ui.pushbuffer()
1442 self.ui.pushbuffer()
1443 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1443 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1444 match=matchfn, stat=False)
1444 match=matchfn, stat=False)
1445 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1445 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1446
1446
1447 self.ui.write("\n }")
1447 self.ui.write("\n }")
1448
1448
1449 class changeset_templater(changeset_printer):
1449 class changeset_templater(changeset_printer):
1450 '''format changeset information.'''
1450 '''format changeset information.'''
1451
1451
1452 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1452 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1453 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1453 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1454 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1454 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1455 defaulttempl = {
1455 defaulttempl = {
1456 'parent': '{rev}:{node|formatnode} ',
1456 'parent': '{rev}:{node|formatnode} ',
1457 'manifest': '{rev}:{node|formatnode}',
1457 'manifest': '{rev}:{node|formatnode}',
1458 'file_copy': '{name} ({source})',
1458 'file_copy': '{name} ({source})',
1459 'extra': '{key}={value|stringescape}'
1459 'extra': '{key}={value|stringescape}'
1460 }
1460 }
1461 # filecopy is preserved for compatibility reasons
1461 # filecopy is preserved for compatibility reasons
1462 defaulttempl['filecopy'] = defaulttempl['file_copy']
1462 defaulttempl['filecopy'] = defaulttempl['file_copy']
1463 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1463 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1464 cache=defaulttempl)
1464 cache=defaulttempl)
1465 if tmpl:
1465 if tmpl:
1466 self.t.cache['changeset'] = tmpl
1466 self.t.cache['changeset'] = tmpl
1467
1467
1468 self.cache = {}
1468 self.cache = {}
1469
1469
1470 # find correct templates for current mode
1470 # find correct templates for current mode
1471 tmplmodes = [
1471 tmplmodes = [
1472 (True, None),
1472 (True, None),
1473 (self.ui.verbose, 'verbose'),
1473 (self.ui.verbose, 'verbose'),
1474 (self.ui.quiet, 'quiet'),
1474 (self.ui.quiet, 'quiet'),
1475 (self.ui.debugflag, 'debug'),
1475 (self.ui.debugflag, 'debug'),
1476 ]
1476 ]
1477
1477
1478 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1478 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1479 'docheader': '', 'docfooter': ''}
1479 'docheader': '', 'docfooter': ''}
1480 for mode, postfix in tmplmodes:
1480 for mode, postfix in tmplmodes:
1481 for t in self._parts:
1481 for t in self._parts:
1482 cur = t
1482 cur = t
1483 if postfix:
1483 if postfix:
1484 cur += "_" + postfix
1484 cur += "_" + postfix
1485 if mode and cur in self.t:
1485 if mode and cur in self.t:
1486 self._parts[t] = cur
1486 self._parts[t] = cur
1487
1487
1488 if self._parts['docheader']:
1488 if self._parts['docheader']:
1489 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1489 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1490
1490
1491 def close(self):
1491 def close(self):
1492 if self._parts['docfooter']:
1492 if self._parts['docfooter']:
1493 if not self.footer:
1493 if not self.footer:
1494 self.footer = ""
1494 self.footer = ""
1495 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1495 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1496 return super(changeset_templater, self).close()
1496 return super(changeset_templater, self).close()
1497
1497
1498 def _show(self, ctx, copies, matchfn, props):
1498 def _show(self, ctx, copies, matchfn, props):
1499 '''show a single changeset or file revision'''
1499 '''show a single changeset or file revision'''
1500 props = props.copy()
1500 props = props.copy()
1501 props.update(templatekw.keywords)
1501 props.update(templatekw.keywords)
1502 props['templ'] = self.t
1502 props['templ'] = self.t
1503 props['ctx'] = ctx
1503 props['ctx'] = ctx
1504 props['repo'] = self.repo
1504 props['repo'] = self.repo
1505 props['ui'] = self.repo.ui
1505 props['ui'] = self.repo.ui
1506 props['revcache'] = {'copies': copies}
1506 props['revcache'] = {'copies': copies}
1507 props['cache'] = self.cache
1507 props['cache'] = self.cache
1508
1508
1509 try:
1509 try:
1510 # write header
1510 # write header
1511 if self._parts['header']:
1511 if self._parts['header']:
1512 h = templater.stringify(self.t(self._parts['header'], **props))
1512 h = templater.stringify(self.t(self._parts['header'], **props))
1513 if self.buffered:
1513 if self.buffered:
1514 self.header[ctx.rev()] = h
1514 self.header[ctx.rev()] = h
1515 else:
1515 else:
1516 if self.lastheader != h:
1516 if self.lastheader != h:
1517 self.lastheader = h
1517 self.lastheader = h
1518 self.ui.write(h)
1518 self.ui.write(h)
1519
1519
1520 # write changeset metadata, then patch if requested
1520 # write changeset metadata, then patch if requested
1521 key = self._parts['changeset']
1521 key = self._parts['changeset']
1522 self.ui.write(templater.stringify(self.t(key, **props)))
1522 self.ui.write(templater.stringify(self.t(key, **props)))
1523 self.showpatch(ctx, matchfn)
1523 self.showpatch(ctx, matchfn)
1524
1524
1525 if self._parts['footer']:
1525 if self._parts['footer']:
1526 if not self.footer:
1526 if not self.footer:
1527 self.footer = templater.stringify(
1527 self.footer = templater.stringify(
1528 self.t(self._parts['footer'], **props))
1528 self.t(self._parts['footer'], **props))
1529 except KeyError as inst:
1529 except KeyError as inst:
1530 msg = _("%s: no key named '%s'")
1530 msg = _("%s: no key named '%s'")
1531 raise error.Abort(msg % (self.t.mapfile, inst.args[0]))
1531 raise error.Abort(msg % (self.t.mapfile, inst.args[0]))
1532 except SyntaxError as inst:
1532 except SyntaxError as inst:
1533 raise error.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1533 raise error.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1534
1534
1535 def gettemplate(ui, tmpl, style):
1535 def gettemplate(ui, tmpl, style):
1536 """
1536 """
1537 Find the template matching the given template spec or style.
1537 Find the template matching the given template spec or style.
1538 """
1538 """
1539
1539
1540 # ui settings
1540 # ui settings
1541 if not tmpl and not style: # template are stronger than style
1541 if not tmpl and not style: # template are stronger than style
1542 tmpl = ui.config('ui', 'logtemplate')
1542 tmpl = ui.config('ui', 'logtemplate')
1543 if tmpl:
1543 if tmpl:
1544 try:
1544 try:
1545 tmpl = templater.unquotestring(tmpl)
1545 tmpl = templater.unquotestring(tmpl)
1546 except SyntaxError:
1546 except SyntaxError:
1547 pass
1547 pass
1548 return tmpl, None
1548 return tmpl, None
1549 else:
1549 else:
1550 style = util.expandpath(ui.config('ui', 'style', ''))
1550 style = util.expandpath(ui.config('ui', 'style', ''))
1551
1551
1552 if not tmpl and style:
1552 if not tmpl and style:
1553 mapfile = style
1553 mapfile = style
1554 if not os.path.split(mapfile)[0]:
1554 if not os.path.split(mapfile)[0]:
1555 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1555 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1556 or templater.templatepath(mapfile))
1556 or templater.templatepath(mapfile))
1557 if mapname:
1557 if mapname:
1558 mapfile = mapname
1558 mapfile = mapname
1559 return None, mapfile
1559 return None, mapfile
1560
1560
1561 if not tmpl:
1561 if not tmpl:
1562 return None, None
1562 return None, None
1563
1563
1564 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1564 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1565
1565
1566 def show_changeset(ui, repo, opts, buffered=False):
1566 def show_changeset(ui, repo, opts, buffered=False):
1567 """show one changeset using template or regular display.
1567 """show one changeset using template or regular display.
1568
1568
1569 Display format will be the first non-empty hit of:
1569 Display format will be the first non-empty hit of:
1570 1. option 'template'
1570 1. option 'template'
1571 2. option 'style'
1571 2. option 'style'
1572 3. [ui] setting 'logtemplate'
1572 3. [ui] setting 'logtemplate'
1573 4. [ui] setting 'style'
1573 4. [ui] setting 'style'
1574 If all of these values are either the unset or the empty string,
1574 If all of these values are either the unset or the empty string,
1575 regular display via changeset_printer() is done.
1575 regular display via changeset_printer() is done.
1576 """
1576 """
1577 # options
1577 # options
1578 matchfn = None
1578 matchfn = None
1579 if opts.get('patch') or opts.get('stat'):
1579 if opts.get('patch') or opts.get('stat'):
1580 matchfn = scmutil.matchall(repo)
1580 matchfn = scmutil.matchall(repo)
1581
1581
1582 if opts.get('template') == 'json':
1582 if opts.get('template') == 'json':
1583 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1583 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1584
1584
1585 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1585 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1586
1586
1587 if not tmpl and not mapfile:
1587 if not tmpl and not mapfile:
1588 return changeset_printer(ui, repo, matchfn, opts, buffered)
1588 return changeset_printer(ui, repo, matchfn, opts, buffered)
1589
1589
1590 try:
1590 try:
1591 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1591 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1592 buffered)
1592 buffered)
1593 except SyntaxError as inst:
1593 except SyntaxError as inst:
1594 raise error.Abort(inst.args[0])
1594 raise error.Abort(inst.args[0])
1595 return t
1595 return t
1596
1596
1597 def showmarker(ui, marker):
1597 def showmarker(ui, marker):
1598 """utility function to display obsolescence marker in a readable way
1598 """utility function to display obsolescence marker in a readable way
1599
1599
1600 To be used by debug function."""
1600 To be used by debug function."""
1601 ui.write(hex(marker.precnode()))
1601 ui.write(hex(marker.precnode()))
1602 for repl in marker.succnodes():
1602 for repl in marker.succnodes():
1603 ui.write(' ')
1603 ui.write(' ')
1604 ui.write(hex(repl))
1604 ui.write(hex(repl))
1605 ui.write(' %X ' % marker.flags())
1605 ui.write(' %X ' % marker.flags())
1606 parents = marker.parentnodes()
1606 parents = marker.parentnodes()
1607 if parents is not None:
1607 if parents is not None:
1608 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1608 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1609 ui.write('(%s) ' % util.datestr(marker.date()))
1609 ui.write('(%s) ' % util.datestr(marker.date()))
1610 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1610 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1611 sorted(marker.metadata().items())
1611 sorted(marker.metadata().items())
1612 if t[0] != 'date')))
1612 if t[0] != 'date')))
1613 ui.write('\n')
1613 ui.write('\n')
1614
1614
1615 def finddate(ui, repo, date):
1615 def finddate(ui, repo, date):
1616 """Find the tipmost changeset that matches the given date spec"""
1616 """Find the tipmost changeset that matches the given date spec"""
1617
1617
1618 df = util.matchdate(date)
1618 df = util.matchdate(date)
1619 m = scmutil.matchall(repo)
1619 m = scmutil.matchall(repo)
1620 results = {}
1620 results = {}
1621
1621
1622 def prep(ctx, fns):
1622 def prep(ctx, fns):
1623 d = ctx.date()
1623 d = ctx.date()
1624 if df(d[0]):
1624 if df(d[0]):
1625 results[ctx.rev()] = d
1625 results[ctx.rev()] = d
1626
1626
1627 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1627 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1628 rev = ctx.rev()
1628 rev = ctx.rev()
1629 if rev in results:
1629 if rev in results:
1630 ui.status(_("found revision %s from %s\n") %
1630 ui.status(_("found revision %s from %s\n") %
1631 (rev, util.datestr(results[rev])))
1631 (rev, util.datestr(results[rev])))
1632 return str(rev)
1632 return str(rev)
1633
1633
1634 raise error.Abort(_("revision matching date not found"))
1634 raise error.Abort(_("revision matching date not found"))
1635
1635
1636 def increasingwindows(windowsize=8, sizelimit=512):
1636 def increasingwindows(windowsize=8, sizelimit=512):
1637 while True:
1637 while True:
1638 yield windowsize
1638 yield windowsize
1639 if windowsize < sizelimit:
1639 if windowsize < sizelimit:
1640 windowsize *= 2
1640 windowsize *= 2
1641
1641
1642 class FileWalkError(Exception):
1642 class FileWalkError(Exception):
1643 pass
1643 pass
1644
1644
1645 def walkfilerevs(repo, match, follow, revs, fncache):
1645 def walkfilerevs(repo, match, follow, revs, fncache):
1646 '''Walks the file history for the matched files.
1646 '''Walks the file history for the matched files.
1647
1647
1648 Returns the changeset revs that are involved in the file history.
1648 Returns the changeset revs that are involved in the file history.
1649
1649
1650 Throws FileWalkError if the file history can't be walked using
1650 Throws FileWalkError if the file history can't be walked using
1651 filelogs alone.
1651 filelogs alone.
1652 '''
1652 '''
1653 wanted = set()
1653 wanted = set()
1654 copies = []
1654 copies = []
1655 minrev, maxrev = min(revs), max(revs)
1655 minrev, maxrev = min(revs), max(revs)
1656 def filerevgen(filelog, last):
1656 def filerevgen(filelog, last):
1657 """
1657 """
1658 Only files, no patterns. Check the history of each file.
1658 Only files, no patterns. Check the history of each file.
1659
1659
1660 Examines filelog entries within minrev, maxrev linkrev range
1660 Examines filelog entries within minrev, maxrev linkrev range
1661 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1661 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1662 tuples in backwards order
1662 tuples in backwards order
1663 """
1663 """
1664 cl_count = len(repo)
1664 cl_count = len(repo)
1665 revs = []
1665 revs = []
1666 for j in xrange(0, last + 1):
1666 for j in xrange(0, last + 1):
1667 linkrev = filelog.linkrev(j)
1667 linkrev = filelog.linkrev(j)
1668 if linkrev < minrev:
1668 if linkrev < minrev:
1669 continue
1669 continue
1670 # only yield rev for which we have the changelog, it can
1670 # only yield rev for which we have the changelog, it can
1671 # happen while doing "hg log" during a pull or commit
1671 # happen while doing "hg log" during a pull or commit
1672 if linkrev >= cl_count:
1672 if linkrev >= cl_count:
1673 break
1673 break
1674
1674
1675 parentlinkrevs = []
1675 parentlinkrevs = []
1676 for p in filelog.parentrevs(j):
1676 for p in filelog.parentrevs(j):
1677 if p != nullrev:
1677 if p != nullrev:
1678 parentlinkrevs.append(filelog.linkrev(p))
1678 parentlinkrevs.append(filelog.linkrev(p))
1679 n = filelog.node(j)
1679 n = filelog.node(j)
1680 revs.append((linkrev, parentlinkrevs,
1680 revs.append((linkrev, parentlinkrevs,
1681 follow and filelog.renamed(n)))
1681 follow and filelog.renamed(n)))
1682
1682
1683 return reversed(revs)
1683 return reversed(revs)
1684 def iterfiles():
1684 def iterfiles():
1685 pctx = repo['.']
1685 pctx = repo['.']
1686 for filename in match.files():
1686 for filename in match.files():
1687 if follow:
1687 if follow:
1688 if filename not in pctx:
1688 if filename not in pctx:
1689 raise error.Abort(_('cannot follow file not in parent '
1689 raise error.Abort(_('cannot follow file not in parent '
1690 'revision: "%s"') % filename)
1690 'revision: "%s"') % filename)
1691 yield filename, pctx[filename].filenode()
1691 yield filename, pctx[filename].filenode()
1692 else:
1692 else:
1693 yield filename, None
1693 yield filename, None
1694 for filename_node in copies:
1694 for filename_node in copies:
1695 yield filename_node
1695 yield filename_node
1696
1696
1697 for file_, node in iterfiles():
1697 for file_, node in iterfiles():
1698 filelog = repo.file(file_)
1698 filelog = repo.file(file_)
1699 if not len(filelog):
1699 if not len(filelog):
1700 if node is None:
1700 if node is None:
1701 # A zero count may be a directory or deleted file, so
1701 # A zero count may be a directory or deleted file, so
1702 # try to find matching entries on the slow path.
1702 # try to find matching entries on the slow path.
1703 if follow:
1703 if follow:
1704 raise error.Abort(
1704 raise error.Abort(
1705 _('cannot follow nonexistent file: "%s"') % file_)
1705 _('cannot follow nonexistent file: "%s"') % file_)
1706 raise FileWalkError("Cannot walk via filelog")
1706 raise FileWalkError("Cannot walk via filelog")
1707 else:
1707 else:
1708 continue
1708 continue
1709
1709
1710 if node is None:
1710 if node is None:
1711 last = len(filelog) - 1
1711 last = len(filelog) - 1
1712 else:
1712 else:
1713 last = filelog.rev(node)
1713 last = filelog.rev(node)
1714
1714
1715 # keep track of all ancestors of the file
1715 # keep track of all ancestors of the file
1716 ancestors = set([filelog.linkrev(last)])
1716 ancestors = set([filelog.linkrev(last)])
1717
1717
1718 # iterate from latest to oldest revision
1718 # iterate from latest to oldest revision
1719 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1719 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1720 if not follow:
1720 if not follow:
1721 if rev > maxrev:
1721 if rev > maxrev:
1722 continue
1722 continue
1723 else:
1723 else:
1724 # Note that last might not be the first interesting
1724 # Note that last might not be the first interesting
1725 # rev to us:
1725 # rev to us:
1726 # if the file has been changed after maxrev, we'll
1726 # if the file has been changed after maxrev, we'll
1727 # have linkrev(last) > maxrev, and we still need
1727 # have linkrev(last) > maxrev, and we still need
1728 # to explore the file graph
1728 # to explore the file graph
1729 if rev not in ancestors:
1729 if rev not in ancestors:
1730 continue
1730 continue
1731 # XXX insert 1327 fix here
1731 # XXX insert 1327 fix here
1732 if flparentlinkrevs:
1732 if flparentlinkrevs:
1733 ancestors.update(flparentlinkrevs)
1733 ancestors.update(flparentlinkrevs)
1734
1734
1735 fncache.setdefault(rev, []).append(file_)
1735 fncache.setdefault(rev, []).append(file_)
1736 wanted.add(rev)
1736 wanted.add(rev)
1737 if copied:
1737 if copied:
1738 copies.append(copied)
1738 copies.append(copied)
1739
1739
1740 return wanted
1740 return wanted
1741
1741
1742 class _followfilter(object):
1742 class _followfilter(object):
1743 def __init__(self, repo, onlyfirst=False):
1743 def __init__(self, repo, onlyfirst=False):
1744 self.repo = repo
1744 self.repo = repo
1745 self.startrev = nullrev
1745 self.startrev = nullrev
1746 self.roots = set()
1746 self.roots = set()
1747 self.onlyfirst = onlyfirst
1747 self.onlyfirst = onlyfirst
1748
1748
1749 def match(self, rev):
1749 def match(self, rev):
1750 def realparents(rev):
1750 def realparents(rev):
1751 if self.onlyfirst:
1751 if self.onlyfirst:
1752 return self.repo.changelog.parentrevs(rev)[0:1]
1752 return self.repo.changelog.parentrevs(rev)[0:1]
1753 else:
1753 else:
1754 return filter(lambda x: x != nullrev,
1754 return filter(lambda x: x != nullrev,
1755 self.repo.changelog.parentrevs(rev))
1755 self.repo.changelog.parentrevs(rev))
1756
1756
1757 if self.startrev == nullrev:
1757 if self.startrev == nullrev:
1758 self.startrev = rev
1758 self.startrev = rev
1759 return True
1759 return True
1760
1760
1761 if rev > self.startrev:
1761 if rev > self.startrev:
1762 # forward: all descendants
1762 # forward: all descendants
1763 if not self.roots:
1763 if not self.roots:
1764 self.roots.add(self.startrev)
1764 self.roots.add(self.startrev)
1765 for parent in realparents(rev):
1765 for parent in realparents(rev):
1766 if parent in self.roots:
1766 if parent in self.roots:
1767 self.roots.add(rev)
1767 self.roots.add(rev)
1768 return True
1768 return True
1769 else:
1769 else:
1770 # backwards: all parents
1770 # backwards: all parents
1771 if not self.roots:
1771 if not self.roots:
1772 self.roots.update(realparents(self.startrev))
1772 self.roots.update(realparents(self.startrev))
1773 if rev in self.roots:
1773 if rev in self.roots:
1774 self.roots.remove(rev)
1774 self.roots.remove(rev)
1775 self.roots.update(realparents(rev))
1775 self.roots.update(realparents(rev))
1776 return True
1776 return True
1777
1777
1778 return False
1778 return False
1779
1779
1780 def walkchangerevs(repo, match, opts, prepare):
1780 def walkchangerevs(repo, match, opts, prepare):
1781 '''Iterate over files and the revs in which they changed.
1781 '''Iterate over files and the revs in which they changed.
1782
1782
1783 Callers most commonly need to iterate backwards over the history
1783 Callers most commonly need to iterate backwards over the history
1784 in which they are interested. Doing so has awful (quadratic-looking)
1784 in which they are interested. Doing so has awful (quadratic-looking)
1785 performance, so we use iterators in a "windowed" way.
1785 performance, so we use iterators in a "windowed" way.
1786
1786
1787 We walk a window of revisions in the desired order. Within the
1787 We walk a window of revisions in the desired order. Within the
1788 window, we first walk forwards to gather data, then in the desired
1788 window, we first walk forwards to gather data, then in the desired
1789 order (usually backwards) to display it.
1789 order (usually backwards) to display it.
1790
1790
1791 This function returns an iterator yielding contexts. Before
1791 This function returns an iterator yielding contexts. Before
1792 yielding each context, the iterator will first call the prepare
1792 yielding each context, the iterator will first call the prepare
1793 function on each context in the window in forward order.'''
1793 function on each context in the window in forward order.'''
1794
1794
1795 follow = opts.get('follow') or opts.get('follow_first')
1795 follow = opts.get('follow') or opts.get('follow_first')
1796 revs = _logrevs(repo, opts)
1796 revs = _logrevs(repo, opts)
1797 if not revs:
1797 if not revs:
1798 return []
1798 return []
1799 wanted = set()
1799 wanted = set()
1800 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1800 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1801 opts.get('removed'))
1801 opts.get('removed'))
1802 fncache = {}
1802 fncache = {}
1803 change = repo.changectx
1803 change = repo.changectx
1804
1804
1805 # First step is to fill wanted, the set of revisions that we want to yield.
1805 # First step is to fill wanted, the set of revisions that we want to yield.
1806 # When it does not induce extra cost, we also fill fncache for revisions in
1806 # When it does not induce extra cost, we also fill fncache for revisions in
1807 # wanted: a cache of filenames that were changed (ctx.files()) and that
1807 # wanted: a cache of filenames that were changed (ctx.files()) and that
1808 # match the file filtering conditions.
1808 # match the file filtering conditions.
1809
1809
1810 if match.always():
1810 if match.always():
1811 # No files, no patterns. Display all revs.
1811 # No files, no patterns. Display all revs.
1812 wanted = revs
1812 wanted = revs
1813 elif not slowpath:
1813 elif not slowpath:
1814 # We only have to read through the filelog to find wanted revisions
1814 # We only have to read through the filelog to find wanted revisions
1815
1815
1816 try:
1816 try:
1817 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1817 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1818 except FileWalkError:
1818 except FileWalkError:
1819 slowpath = True
1819 slowpath = True
1820
1820
1821 # We decided to fall back to the slowpath because at least one
1821 # We decided to fall back to the slowpath because at least one
1822 # of the paths was not a file. Check to see if at least one of them
1822 # of the paths was not a file. Check to see if at least one of them
1823 # existed in history, otherwise simply return
1823 # existed in history, otherwise simply return
1824 for path in match.files():
1824 for path in match.files():
1825 if path == '.' or path in repo.store:
1825 if path == '.' or path in repo.store:
1826 break
1826 break
1827 else:
1827 else:
1828 return []
1828 return []
1829
1829
1830 if slowpath:
1830 if slowpath:
1831 # We have to read the changelog to match filenames against
1831 # We have to read the changelog to match filenames against
1832 # changed files
1832 # changed files
1833
1833
1834 if follow:
1834 if follow:
1835 raise error.Abort(_('can only follow copies/renames for explicit '
1835 raise error.Abort(_('can only follow copies/renames for explicit '
1836 'filenames'))
1836 'filenames'))
1837
1837
1838 # The slow path checks files modified in every changeset.
1838 # The slow path checks files modified in every changeset.
1839 # This is really slow on large repos, so compute the set lazily.
1839 # This is really slow on large repos, so compute the set lazily.
1840 class lazywantedset(object):
1840 class lazywantedset(object):
1841 def __init__(self):
1841 def __init__(self):
1842 self.set = set()
1842 self.set = set()
1843 self.revs = set(revs)
1843 self.revs = set(revs)
1844
1844
1845 # No need to worry about locality here because it will be accessed
1845 # No need to worry about locality here because it will be accessed
1846 # in the same order as the increasing window below.
1846 # in the same order as the increasing window below.
1847 def __contains__(self, value):
1847 def __contains__(self, value):
1848 if value in self.set:
1848 if value in self.set:
1849 return True
1849 return True
1850 elif not value in self.revs:
1850 elif not value in self.revs:
1851 return False
1851 return False
1852 else:
1852 else:
1853 self.revs.discard(value)
1853 self.revs.discard(value)
1854 ctx = change(value)
1854 ctx = change(value)
1855 matches = filter(match, ctx.files())
1855 matches = filter(match, ctx.files())
1856 if matches:
1856 if matches:
1857 fncache[value] = matches
1857 fncache[value] = matches
1858 self.set.add(value)
1858 self.set.add(value)
1859 return True
1859 return True
1860 return False
1860 return False
1861
1861
1862 def discard(self, value):
1862 def discard(self, value):
1863 self.revs.discard(value)
1863 self.revs.discard(value)
1864 self.set.discard(value)
1864 self.set.discard(value)
1865
1865
1866 wanted = lazywantedset()
1866 wanted = lazywantedset()
1867
1867
1868 # it might be worthwhile to do this in the iterator if the rev range
1868 # it might be worthwhile to do this in the iterator if the rev range
1869 # is descending and the prune args are all within that range
1869 # is descending and the prune args are all within that range
1870 for rev in opts.get('prune', ()):
1870 for rev in opts.get('prune', ()):
1871 rev = repo[rev].rev()
1871 rev = repo[rev].rev()
1872 ff = _followfilter(repo)
1872 ff = _followfilter(repo)
1873 stop = min(revs[0], revs[-1])
1873 stop = min(revs[0], revs[-1])
1874 for x in xrange(rev, stop - 1, -1):
1874 for x in xrange(rev, stop - 1, -1):
1875 if ff.match(x):
1875 if ff.match(x):
1876 wanted = wanted - [x]
1876 wanted = wanted - [x]
1877
1877
1878 # Now that wanted is correctly initialized, we can iterate over the
1878 # Now that wanted is correctly initialized, we can iterate over the
1879 # revision range, yielding only revisions in wanted.
1879 # revision range, yielding only revisions in wanted.
1880 def iterate():
1880 def iterate():
1881 if follow and match.always():
1881 if follow and match.always():
1882 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1882 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1883 def want(rev):
1883 def want(rev):
1884 return ff.match(rev) and rev in wanted
1884 return ff.match(rev) and rev in wanted
1885 else:
1885 else:
1886 def want(rev):
1886 def want(rev):
1887 return rev in wanted
1887 return rev in wanted
1888
1888
1889 it = iter(revs)
1889 it = iter(revs)
1890 stopiteration = False
1890 stopiteration = False
1891 for windowsize in increasingwindows():
1891 for windowsize in increasingwindows():
1892 nrevs = []
1892 nrevs = []
1893 for i in xrange(windowsize):
1893 for i in xrange(windowsize):
1894 rev = next(it, None)
1894 rev = next(it, None)
1895 if rev is None:
1895 if rev is None:
1896 stopiteration = True
1896 stopiteration = True
1897 break
1897 break
1898 elif want(rev):
1898 elif want(rev):
1899 nrevs.append(rev)
1899 nrevs.append(rev)
1900 for rev in sorted(nrevs):
1900 for rev in sorted(nrevs):
1901 fns = fncache.get(rev)
1901 fns = fncache.get(rev)
1902 ctx = change(rev)
1902 ctx = change(rev)
1903 if not fns:
1903 if not fns:
1904 def fns_generator():
1904 def fns_generator():
1905 for f in ctx.files():
1905 for f in ctx.files():
1906 if match(f):
1906 if match(f):
1907 yield f
1907 yield f
1908 fns = fns_generator()
1908 fns = fns_generator()
1909 prepare(ctx, fns)
1909 prepare(ctx, fns)
1910 for rev in nrevs:
1910 for rev in nrevs:
1911 yield change(rev)
1911 yield change(rev)
1912
1912
1913 if stopiteration:
1913 if stopiteration:
1914 break
1914 break
1915
1915
1916 return iterate()
1916 return iterate()
1917
1917
1918 def _makefollowlogfilematcher(repo, files, followfirst):
1918 def _makefollowlogfilematcher(repo, files, followfirst):
1919 # When displaying a revision with --patch --follow FILE, we have
1919 # When displaying a revision with --patch --follow FILE, we have
1920 # to know which file of the revision must be diffed. With
1920 # to know which file of the revision must be diffed. With
1921 # --follow, we want the names of the ancestors of FILE in the
1921 # --follow, we want the names of the ancestors of FILE in the
1922 # revision, stored in "fcache". "fcache" is populated by
1922 # revision, stored in "fcache". "fcache" is populated by
1923 # reproducing the graph traversal already done by --follow revset
1923 # reproducing the graph traversal already done by --follow revset
1924 # and relating linkrevs to file names (which is not "correct" but
1924 # and relating linkrevs to file names (which is not "correct" but
1925 # good enough).
1925 # good enough).
1926 fcache = {}
1926 fcache = {}
1927 fcacheready = [False]
1927 fcacheready = [False]
1928 pctx = repo['.']
1928 pctx = repo['.']
1929
1929
1930 def populate():
1930 def populate():
1931 for fn in files:
1931 for fn in files:
1932 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1932 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1933 for c in i:
1933 for c in i:
1934 fcache.setdefault(c.linkrev(), set()).add(c.path())
1934 fcache.setdefault(c.linkrev(), set()).add(c.path())
1935
1935
1936 def filematcher(rev):
1936 def filematcher(rev):
1937 if not fcacheready[0]:
1937 if not fcacheready[0]:
1938 # Lazy initialization
1938 # Lazy initialization
1939 fcacheready[0] = True
1939 fcacheready[0] = True
1940 populate()
1940 populate()
1941 return scmutil.matchfiles(repo, fcache.get(rev, []))
1941 return scmutil.matchfiles(repo, fcache.get(rev, []))
1942
1942
1943 return filematcher
1943 return filematcher
1944
1944
1945 def _makenofollowlogfilematcher(repo, pats, opts):
1945 def _makenofollowlogfilematcher(repo, pats, opts):
1946 '''hook for extensions to override the filematcher for non-follow cases'''
1946 '''hook for extensions to override the filematcher for non-follow cases'''
1947 return None
1947 return None
1948
1948
1949 def _makelogrevset(repo, pats, opts, revs):
1949 def _makelogrevset(repo, pats, opts, revs):
1950 """Return (expr, filematcher) where expr is a revset string built
1950 """Return (expr, filematcher) where expr is a revset string built
1951 from log options and file patterns or None. If --stat or --patch
1951 from log options and file patterns or None. If --stat or --patch
1952 are not passed filematcher is None. Otherwise it is a callable
1952 are not passed filematcher is None. Otherwise it is a callable
1953 taking a revision number and returning a match objects filtering
1953 taking a revision number and returning a match objects filtering
1954 the files to be detailed when displaying the revision.
1954 the files to be detailed when displaying the revision.
1955 """
1955 """
1956 opt2revset = {
1956 opt2revset = {
1957 'no_merges': ('not merge()', None),
1957 'no_merges': ('not merge()', None),
1958 'only_merges': ('merge()', None),
1958 'only_merges': ('merge()', None),
1959 '_ancestors': ('ancestors(%(val)s)', None),
1959 '_ancestors': ('ancestors(%(val)s)', None),
1960 '_fancestors': ('_firstancestors(%(val)s)', None),
1960 '_fancestors': ('_firstancestors(%(val)s)', None),
1961 '_descendants': ('descendants(%(val)s)', None),
1961 '_descendants': ('descendants(%(val)s)', None),
1962 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1962 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1963 '_matchfiles': ('_matchfiles(%(val)s)', None),
1963 '_matchfiles': ('_matchfiles(%(val)s)', None),
1964 'date': ('date(%(val)r)', None),
1964 'date': ('date(%(val)r)', None),
1965 'branch': ('branch(%(val)r)', ' or '),
1965 'branch': ('branch(%(val)r)', ' or '),
1966 '_patslog': ('filelog(%(val)r)', ' or '),
1966 '_patslog': ('filelog(%(val)r)', ' or '),
1967 '_patsfollow': ('follow(%(val)r)', ' or '),
1967 '_patsfollow': ('follow(%(val)r)', ' or '),
1968 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1968 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1969 'keyword': ('keyword(%(val)r)', ' or '),
1969 'keyword': ('keyword(%(val)r)', ' or '),
1970 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1970 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1971 'user': ('user(%(val)r)', ' or '),
1971 'user': ('user(%(val)r)', ' or '),
1972 }
1972 }
1973
1973
1974 opts = dict(opts)
1974 opts = dict(opts)
1975 # follow or not follow?
1975 # follow or not follow?
1976 follow = opts.get('follow') or opts.get('follow_first')
1976 follow = opts.get('follow') or opts.get('follow_first')
1977 if opts.get('follow_first'):
1977 if opts.get('follow_first'):
1978 followfirst = 1
1978 followfirst = 1
1979 else:
1979 else:
1980 followfirst = 0
1980 followfirst = 0
1981 # --follow with FILE behavior depends on revs...
1981 # --follow with FILE behavior depends on revs...
1982 it = iter(revs)
1982 it = iter(revs)
1983 startrev = it.next()
1983 startrev = it.next()
1984 followdescendants = startrev < next(it, startrev)
1984 followdescendants = startrev < next(it, startrev)
1985
1985
1986 # branch and only_branch are really aliases and must be handled at
1986 # branch and only_branch are really aliases and must be handled at
1987 # the same time
1987 # the same time
1988 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1988 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1989 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1989 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1990 # pats/include/exclude are passed to match.match() directly in
1990 # pats/include/exclude are passed to match.match() directly in
1991 # _matchfiles() revset but walkchangerevs() builds its matcher with
1991 # _matchfiles() revset but walkchangerevs() builds its matcher with
1992 # scmutil.match(). The difference is input pats are globbed on
1992 # scmutil.match(). The difference is input pats are globbed on
1993 # platforms without shell expansion (windows).
1993 # platforms without shell expansion (windows).
1994 wctx = repo[None]
1994 wctx = repo[None]
1995 match, pats = scmutil.matchandpats(wctx, pats, opts)
1995 match, pats = scmutil.matchandpats(wctx, pats, opts)
1996 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1996 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1997 opts.get('removed'))
1997 opts.get('removed'))
1998 if not slowpath:
1998 if not slowpath:
1999 for f in match.files():
1999 for f in match.files():
2000 if follow and f not in wctx:
2000 if follow and f not in wctx:
2001 # If the file exists, it may be a directory, so let it
2001 # If the file exists, it may be a directory, so let it
2002 # take the slow path.
2002 # take the slow path.
2003 if os.path.exists(repo.wjoin(f)):
2003 if os.path.exists(repo.wjoin(f)):
2004 slowpath = True
2004 slowpath = True
2005 continue
2005 continue
2006 else:
2006 else:
2007 raise error.Abort(_('cannot follow file not in parent '
2007 raise error.Abort(_('cannot follow file not in parent '
2008 'revision: "%s"') % f)
2008 'revision: "%s"') % f)
2009 filelog = repo.file(f)
2009 filelog = repo.file(f)
2010 if not filelog:
2010 if not filelog:
2011 # A zero count may be a directory or deleted file, so
2011 # A zero count may be a directory or deleted file, so
2012 # try to find matching entries on the slow path.
2012 # try to find matching entries on the slow path.
2013 if follow:
2013 if follow:
2014 raise error.Abort(
2014 raise error.Abort(
2015 _('cannot follow nonexistent file: "%s"') % f)
2015 _('cannot follow nonexistent file: "%s"') % f)
2016 slowpath = True
2016 slowpath = True
2017
2017
2018 # We decided to fall back to the slowpath because at least one
2018 # We decided to fall back to the slowpath because at least one
2019 # of the paths was not a file. Check to see if at least one of them
2019 # of the paths was not a file. Check to see if at least one of them
2020 # existed in history - in that case, we'll continue down the
2020 # existed in history - in that case, we'll continue down the
2021 # slowpath; otherwise, we can turn off the slowpath
2021 # slowpath; otherwise, we can turn off the slowpath
2022 if slowpath:
2022 if slowpath:
2023 for path in match.files():
2023 for path in match.files():
2024 if path == '.' or path in repo.store:
2024 if path == '.' or path in repo.store:
2025 break
2025 break
2026 else:
2026 else:
2027 slowpath = False
2027 slowpath = False
2028
2028
2029 fpats = ('_patsfollow', '_patsfollowfirst')
2029 fpats = ('_patsfollow', '_patsfollowfirst')
2030 fnopats = (('_ancestors', '_fancestors'),
2030 fnopats = (('_ancestors', '_fancestors'),
2031 ('_descendants', '_fdescendants'))
2031 ('_descendants', '_fdescendants'))
2032 if slowpath:
2032 if slowpath:
2033 # See walkchangerevs() slow path.
2033 # See walkchangerevs() slow path.
2034 #
2034 #
2035 # pats/include/exclude cannot be represented as separate
2035 # pats/include/exclude cannot be represented as separate
2036 # revset expressions as their filtering logic applies at file
2036 # revset expressions as their filtering logic applies at file
2037 # level. For instance "-I a -X a" matches a revision touching
2037 # level. For instance "-I a -X a" matches a revision touching
2038 # "a" and "b" while "file(a) and not file(b)" does
2038 # "a" and "b" while "file(a) and not file(b)" does
2039 # not. Besides, filesets are evaluated against the working
2039 # not. Besides, filesets are evaluated against the working
2040 # directory.
2040 # directory.
2041 matchargs = ['r:', 'd:relpath']
2041 matchargs = ['r:', 'd:relpath']
2042 for p in pats:
2042 for p in pats:
2043 matchargs.append('p:' + p)
2043 matchargs.append('p:' + p)
2044 for p in opts.get('include', []):
2044 for p in opts.get('include', []):
2045 matchargs.append('i:' + p)
2045 matchargs.append('i:' + p)
2046 for p in opts.get('exclude', []):
2046 for p in opts.get('exclude', []):
2047 matchargs.append('x:' + p)
2047 matchargs.append('x:' + p)
2048 matchargs = ','.join(('%r' % p) for p in matchargs)
2048 matchargs = ','.join(('%r' % p) for p in matchargs)
2049 opts['_matchfiles'] = matchargs
2049 opts['_matchfiles'] = matchargs
2050 if follow:
2050 if follow:
2051 opts[fnopats[0][followfirst]] = '.'
2051 opts[fnopats[0][followfirst]] = '.'
2052 else:
2052 else:
2053 if follow:
2053 if follow:
2054 if pats:
2054 if pats:
2055 # follow() revset interprets its file argument as a
2055 # follow() revset interprets its file argument as a
2056 # manifest entry, so use match.files(), not pats.
2056 # manifest entry, so use match.files(), not pats.
2057 opts[fpats[followfirst]] = list(match.files())
2057 opts[fpats[followfirst]] = list(match.files())
2058 else:
2058 else:
2059 op = fnopats[followdescendants][followfirst]
2059 op = fnopats[followdescendants][followfirst]
2060 opts[op] = 'rev(%d)' % startrev
2060 opts[op] = 'rev(%d)' % startrev
2061 else:
2061 else:
2062 opts['_patslog'] = list(pats)
2062 opts['_patslog'] = list(pats)
2063
2063
2064 filematcher = None
2064 filematcher = None
2065 if opts.get('patch') or opts.get('stat'):
2065 if opts.get('patch') or opts.get('stat'):
2066 # When following files, track renames via a special matcher.
2066 # When following files, track renames via a special matcher.
2067 # If we're forced to take the slowpath it means we're following
2067 # If we're forced to take the slowpath it means we're following
2068 # at least one pattern/directory, so don't bother with rename tracking.
2068 # at least one pattern/directory, so don't bother with rename tracking.
2069 if follow and not match.always() and not slowpath:
2069 if follow and not match.always() and not slowpath:
2070 # _makefollowlogfilematcher expects its files argument to be
2070 # _makefollowlogfilematcher expects its files argument to be
2071 # relative to the repo root, so use match.files(), not pats.
2071 # relative to the repo root, so use match.files(), not pats.
2072 filematcher = _makefollowlogfilematcher(repo, match.files(),
2072 filematcher = _makefollowlogfilematcher(repo, match.files(),
2073 followfirst)
2073 followfirst)
2074 else:
2074 else:
2075 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2075 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2076 if filematcher is None:
2076 if filematcher is None:
2077 filematcher = lambda rev: match
2077 filematcher = lambda rev: match
2078
2078
2079 expr = []
2079 expr = []
2080 for op, val in sorted(opts.iteritems()):
2080 for op, val in sorted(opts.iteritems()):
2081 if not val:
2081 if not val:
2082 continue
2082 continue
2083 if op not in opt2revset:
2083 if op not in opt2revset:
2084 continue
2084 continue
2085 revop, andor = opt2revset[op]
2085 revop, andor = opt2revset[op]
2086 if '%(val)' not in revop:
2086 if '%(val)' not in revop:
2087 expr.append(revop)
2087 expr.append(revop)
2088 else:
2088 else:
2089 if not isinstance(val, list):
2089 if not isinstance(val, list):
2090 e = revop % {'val': val}
2090 e = revop % {'val': val}
2091 else:
2091 else:
2092 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2092 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2093 expr.append(e)
2093 expr.append(e)
2094
2094
2095 if expr:
2095 if expr:
2096 expr = '(' + ' and '.join(expr) + ')'
2096 expr = '(' + ' and '.join(expr) + ')'
2097 else:
2097 else:
2098 expr = None
2098 expr = None
2099 return expr, filematcher
2099 return expr, filematcher
2100
2100
2101 def _logrevs(repo, opts):
2101 def _logrevs(repo, opts):
2102 # Default --rev value depends on --follow but --follow behavior
2102 # Default --rev value depends on --follow but --follow behavior
2103 # depends on revisions resolved from --rev...
2103 # depends on revisions resolved from --rev...
2104 follow = opts.get('follow') or opts.get('follow_first')
2104 follow = opts.get('follow') or opts.get('follow_first')
2105 if opts.get('rev'):
2105 if opts.get('rev'):
2106 revs = scmutil.revrange(repo, opts['rev'])
2106 revs = scmutil.revrange(repo, opts['rev'])
2107 elif follow and repo.dirstate.p1() == nullid:
2107 elif follow and repo.dirstate.p1() == nullid:
2108 revs = revset.baseset()
2108 revs = revset.baseset()
2109 elif follow:
2109 elif follow:
2110 revs = repo.revs('reverse(:.)')
2110 revs = repo.revs('reverse(:.)')
2111 else:
2111 else:
2112 revs = revset.spanset(repo)
2112 revs = revset.spanset(repo)
2113 revs.reverse()
2113 revs.reverse()
2114 return revs
2114 return revs
2115
2115
2116 def getgraphlogrevs(repo, pats, opts):
2116 def getgraphlogrevs(repo, pats, opts):
2117 """Return (revs, expr, filematcher) where revs is an iterable of
2117 """Return (revs, expr, filematcher) where revs is an iterable of
2118 revision numbers, expr is a revset string built from log options
2118 revision numbers, expr is a revset string built from log options
2119 and file patterns or None, and used to filter 'revs'. If --stat or
2119 and file patterns or None, and used to filter 'revs'. If --stat or
2120 --patch are not passed filematcher is None. Otherwise it is a
2120 --patch are not passed filematcher is None. Otherwise it is a
2121 callable taking a revision number and returning a match objects
2121 callable taking a revision number and returning a match objects
2122 filtering the files to be detailed when displaying the revision.
2122 filtering the files to be detailed when displaying the revision.
2123 """
2123 """
2124 limit = loglimit(opts)
2124 limit = loglimit(opts)
2125 revs = _logrevs(repo, opts)
2125 revs = _logrevs(repo, opts)
2126 if not revs:
2126 if not revs:
2127 return revset.baseset(), None, None
2127 return revset.baseset(), None, None
2128 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2128 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2129 if opts.get('rev'):
2129 if opts.get('rev'):
2130 # User-specified revs might be unsorted, but don't sort before
2130 # User-specified revs might be unsorted, but don't sort before
2131 # _makelogrevset because it might depend on the order of revs
2131 # _makelogrevset because it might depend on the order of revs
2132 revs.sort(reverse=True)
2132 revs.sort(reverse=True)
2133 if expr:
2133 if expr:
2134 # Revset matchers often operate faster on revisions in changelog
2134 # Revset matchers often operate faster on revisions in changelog
2135 # order, because most filters deal with the changelog.
2135 # order, because most filters deal with the changelog.
2136 revs.reverse()
2136 revs.reverse()
2137 matcher = revset.match(repo.ui, expr)
2137 matcher = revset.match(repo.ui, expr)
2138 # Revset matches can reorder revisions. "A or B" typically returns
2138 # Revset matches can reorder revisions. "A or B" typically returns
2139 # returns the revision matching A then the revision matching B. Sort
2139 # returns the revision matching A then the revision matching B. Sort
2140 # again to fix that.
2140 # again to fix that.
2141 revs = matcher(repo, revs)
2141 revs = matcher(repo, revs)
2142 revs.sort(reverse=True)
2142 revs.sort(reverse=True)
2143 if limit is not None:
2143 if limit is not None:
2144 limitedrevs = []
2144 limitedrevs = []
2145 for idx, rev in enumerate(revs):
2145 for idx, rev in enumerate(revs):
2146 if idx >= limit:
2146 if idx >= limit:
2147 break
2147 break
2148 limitedrevs.append(rev)
2148 limitedrevs.append(rev)
2149 revs = revset.baseset(limitedrevs)
2149 revs = revset.baseset(limitedrevs)
2150
2150
2151 return revs, expr, filematcher
2151 return revs, expr, filematcher
2152
2152
2153 def getlogrevs(repo, pats, opts):
2153 def getlogrevs(repo, pats, opts):
2154 """Return (revs, expr, filematcher) where revs is an iterable of
2154 """Return (revs, expr, filematcher) where revs is an iterable of
2155 revision numbers, expr is a revset string built from log options
2155 revision numbers, expr is a revset string built from log options
2156 and file patterns or None, and used to filter 'revs'. If --stat or
2156 and file patterns or None, and used to filter 'revs'. If --stat or
2157 --patch are not passed filematcher is None. Otherwise it is a
2157 --patch are not passed filematcher is None. Otherwise it is a
2158 callable taking a revision number and returning a match objects
2158 callable taking a revision number and returning a match objects
2159 filtering the files to be detailed when displaying the revision.
2159 filtering the files to be detailed when displaying the revision.
2160 """
2160 """
2161 limit = loglimit(opts)
2161 limit = loglimit(opts)
2162 revs = _logrevs(repo, opts)
2162 revs = _logrevs(repo, opts)
2163 if not revs:
2163 if not revs:
2164 return revset.baseset([]), None, None
2164 return revset.baseset([]), None, None
2165 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2165 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2166 if expr:
2166 if expr:
2167 # Revset matchers often operate faster on revisions in changelog
2167 # Revset matchers often operate faster on revisions in changelog
2168 # order, because most filters deal with the changelog.
2168 # order, because most filters deal with the changelog.
2169 if not opts.get('rev'):
2169 if not opts.get('rev'):
2170 revs.reverse()
2170 revs.reverse()
2171 matcher = revset.match(repo.ui, expr)
2171 matcher = revset.match(repo.ui, expr)
2172 # Revset matches can reorder revisions. "A or B" typically returns
2172 # Revset matches can reorder revisions. "A or B" typically returns
2173 # returns the revision matching A then the revision matching B. Sort
2173 # returns the revision matching A then the revision matching B. Sort
2174 # again to fix that.
2174 # again to fix that.
2175 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2175 fixopts = ['branch', 'only_branch', 'keyword', 'user']
2176 oldrevs = revs
2176 oldrevs = revs
2177 revs = matcher(repo, revs)
2177 revs = matcher(repo, revs)
2178 if not opts.get('rev'):
2178 if not opts.get('rev'):
2179 revs.sort(reverse=True)
2179 revs.sort(reverse=True)
2180 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2180 elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts):
2181 # XXX "A or B" is known to change the order; fix it by filtering
2181 # XXX "A or B" is known to change the order; fix it by filtering
2182 # matched set again (issue5100)
2182 # matched set again (issue5100)
2183 revs = oldrevs & revs
2183 revs = oldrevs & 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['revcache'] = {}
2210 props['revcache'] = {}
2210 return templater.stringify(templ('graphnode', **props))
2211 return templater.stringify(templ('graphnode', **props))
2211 return formatnode
2212 return formatnode
2212
2213
2213 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2214 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2214 filematcher=None):
2215 filematcher=None):
2215 formatnode = _graphnodeformatter(ui, displayer)
2216 formatnode = _graphnodeformatter(ui, displayer)
2216 state = graphmod.asciistate()
2217 state = graphmod.asciistate()
2217 for rev, type, ctx, parents in dag:
2218 for rev, type, ctx, parents in dag:
2218 char = formatnode(repo, ctx)
2219 char = formatnode(repo, ctx)
2219 copies = None
2220 copies = None
2220 if getrenamed and ctx.rev():
2221 if getrenamed and ctx.rev():
2221 copies = []
2222 copies = []
2222 for fn in ctx.files():
2223 for fn in ctx.files():
2223 rename = getrenamed(fn, ctx.rev())
2224 rename = getrenamed(fn, ctx.rev())
2224 if rename:
2225 if rename:
2225 copies.append((fn, rename[0]))
2226 copies.append((fn, rename[0]))
2226 revmatchfn = None
2227 revmatchfn = None
2227 if filematcher is not None:
2228 if filematcher is not None:
2228 revmatchfn = filematcher(ctx.rev())
2229 revmatchfn = filematcher(ctx.rev())
2229 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2230 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2230 lines = displayer.hunk.pop(rev).split('\n')
2231 lines = displayer.hunk.pop(rev).split('\n')
2231 if not lines[-1]:
2232 if not lines[-1]:
2232 del lines[-1]
2233 del lines[-1]
2233 displayer.flush(ctx)
2234 displayer.flush(ctx)
2234 edges = edgefn(type, char, lines, state, rev, parents)
2235 edges = edgefn(type, char, lines, state, rev, parents)
2235 for type, char, lines, coldata in edges:
2236 for type, char, lines, coldata in edges:
2236 graphmod.ascii(ui, state, type, char, lines, coldata)
2237 graphmod.ascii(ui, state, type, char, lines, coldata)
2237 displayer.close()
2238 displayer.close()
2238
2239
2239 def graphlog(ui, repo, *pats, **opts):
2240 def graphlog(ui, repo, *pats, **opts):
2240 # Parameters are identical to log command ones
2241 # Parameters are identical to log command ones
2241 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2242 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2242 revdag = graphmod.dagwalker(repo, revs)
2243 revdag = graphmod.dagwalker(repo, revs)
2243
2244
2244 getrenamed = None
2245 getrenamed = None
2245 if opts.get('copies'):
2246 if opts.get('copies'):
2246 endrev = None
2247 endrev = None
2247 if opts.get('rev'):
2248 if opts.get('rev'):
2248 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2249 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2249 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2250 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2250 displayer = show_changeset(ui, repo, opts, buffered=True)
2251 displayer = show_changeset(ui, repo, opts, buffered=True)
2251 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2252 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2252 filematcher)
2253 filematcher)
2253
2254
2254 def checkunsupportedgraphflags(pats, opts):
2255 def checkunsupportedgraphflags(pats, opts):
2255 for op in ["newest_first"]:
2256 for op in ["newest_first"]:
2256 if op in opts and opts[op]:
2257 if op in opts and opts[op]:
2257 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2258 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2258 % op.replace("_", "-"))
2259 % op.replace("_", "-"))
2259
2260
2260 def graphrevs(repo, nodes, opts):
2261 def graphrevs(repo, nodes, opts):
2261 limit = loglimit(opts)
2262 limit = loglimit(opts)
2262 nodes.reverse()
2263 nodes.reverse()
2263 if limit is not None:
2264 if limit is not None:
2264 nodes = nodes[:limit]
2265 nodes = nodes[:limit]
2265 return graphmod.nodes(repo, nodes)
2266 return graphmod.nodes(repo, nodes)
2266
2267
2267 def add(ui, repo, match, prefix, explicitonly, **opts):
2268 def add(ui, repo, match, prefix, explicitonly, **opts):
2268 join = lambda f: os.path.join(prefix, f)
2269 join = lambda f: os.path.join(prefix, f)
2269 bad = []
2270 bad = []
2270
2271
2271 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2272 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2272 names = []
2273 names = []
2273 wctx = repo[None]
2274 wctx = repo[None]
2274 cca = None
2275 cca = None
2275 abort, warn = scmutil.checkportabilityalert(ui)
2276 abort, warn = scmutil.checkportabilityalert(ui)
2276 if abort or warn:
2277 if abort or warn:
2277 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2278 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2278
2279
2279 badmatch = matchmod.badmatch(match, badfn)
2280 badmatch = matchmod.badmatch(match, badfn)
2280 dirstate = repo.dirstate
2281 dirstate = repo.dirstate
2281 # We don't want to just call wctx.walk here, since it would return a lot of
2282 # We don't want to just call wctx.walk here, since it would return a lot of
2282 # clean files, which we aren't interested in and takes time.
2283 # clean files, which we aren't interested in and takes time.
2283 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2284 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2284 True, False, full=False)):
2285 True, False, full=False)):
2285 exact = match.exact(f)
2286 exact = match.exact(f)
2286 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2287 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2287 if cca:
2288 if cca:
2288 cca(f)
2289 cca(f)
2289 names.append(f)
2290 names.append(f)
2290 if ui.verbose or not exact:
2291 if ui.verbose or not exact:
2291 ui.status(_('adding %s\n') % match.rel(f))
2292 ui.status(_('adding %s\n') % match.rel(f))
2292
2293
2293 for subpath in sorted(wctx.substate):
2294 for subpath in sorted(wctx.substate):
2294 sub = wctx.sub(subpath)
2295 sub = wctx.sub(subpath)
2295 try:
2296 try:
2296 submatch = matchmod.subdirmatcher(subpath, match)
2297 submatch = matchmod.subdirmatcher(subpath, match)
2297 if opts.get('subrepos'):
2298 if opts.get('subrepos'):
2298 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2299 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2299 else:
2300 else:
2300 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2301 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2301 except error.LookupError:
2302 except error.LookupError:
2302 ui.status(_("skipping missing subrepository: %s\n")
2303 ui.status(_("skipping missing subrepository: %s\n")
2303 % join(subpath))
2304 % join(subpath))
2304
2305
2305 if not opts.get('dry_run'):
2306 if not opts.get('dry_run'):
2306 rejected = wctx.add(names, prefix)
2307 rejected = wctx.add(names, prefix)
2307 bad.extend(f for f in rejected if f in match.files())
2308 bad.extend(f for f in rejected if f in match.files())
2308 return bad
2309 return bad
2309
2310
2310 def forget(ui, repo, match, prefix, explicitonly):
2311 def forget(ui, repo, match, prefix, explicitonly):
2311 join = lambda f: os.path.join(prefix, f)
2312 join = lambda f: os.path.join(prefix, f)
2312 bad = []
2313 bad = []
2313 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2314 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2314 wctx = repo[None]
2315 wctx = repo[None]
2315 forgot = []
2316 forgot = []
2316
2317
2317 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2318 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2318 forget = sorted(s[0] + s[1] + s[3] + s[6])
2319 forget = sorted(s[0] + s[1] + s[3] + s[6])
2319 if explicitonly:
2320 if explicitonly:
2320 forget = [f for f in forget if match.exact(f)]
2321 forget = [f for f in forget if match.exact(f)]
2321
2322
2322 for subpath in sorted(wctx.substate):
2323 for subpath in sorted(wctx.substate):
2323 sub = wctx.sub(subpath)
2324 sub = wctx.sub(subpath)
2324 try:
2325 try:
2325 submatch = matchmod.subdirmatcher(subpath, match)
2326 submatch = matchmod.subdirmatcher(subpath, match)
2326 subbad, subforgot = sub.forget(submatch, prefix)
2327 subbad, subforgot = sub.forget(submatch, prefix)
2327 bad.extend([subpath + '/' + f for f in subbad])
2328 bad.extend([subpath + '/' + f for f in subbad])
2328 forgot.extend([subpath + '/' + f for f in subforgot])
2329 forgot.extend([subpath + '/' + f for f in subforgot])
2329 except error.LookupError:
2330 except error.LookupError:
2330 ui.status(_("skipping missing subrepository: %s\n")
2331 ui.status(_("skipping missing subrepository: %s\n")
2331 % join(subpath))
2332 % join(subpath))
2332
2333
2333 if not explicitonly:
2334 if not explicitonly:
2334 for f in match.files():
2335 for f in match.files():
2335 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2336 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2336 if f not in forgot:
2337 if f not in forgot:
2337 if repo.wvfs.exists(f):
2338 if repo.wvfs.exists(f):
2338 # Don't complain if the exact case match wasn't given.
2339 # Don't complain if the exact case match wasn't given.
2339 # But don't do this until after checking 'forgot', so
2340 # But don't do this until after checking 'forgot', so
2340 # that subrepo files aren't normalized, and this op is
2341 # that subrepo files aren't normalized, and this op is
2341 # purely from data cached by the status walk above.
2342 # purely from data cached by the status walk above.
2342 if repo.dirstate.normalize(f) in repo.dirstate:
2343 if repo.dirstate.normalize(f) in repo.dirstate:
2343 continue
2344 continue
2344 ui.warn(_('not removing %s: '
2345 ui.warn(_('not removing %s: '
2345 'file is already untracked\n')
2346 'file is already untracked\n')
2346 % match.rel(f))
2347 % match.rel(f))
2347 bad.append(f)
2348 bad.append(f)
2348
2349
2349 for f in forget:
2350 for f in forget:
2350 if ui.verbose or not match.exact(f):
2351 if ui.verbose or not match.exact(f):
2351 ui.status(_('removing %s\n') % match.rel(f))
2352 ui.status(_('removing %s\n') % match.rel(f))
2352
2353
2353 rejected = wctx.forget(forget, prefix)
2354 rejected = wctx.forget(forget, prefix)
2354 bad.extend(f for f in rejected if f in match.files())
2355 bad.extend(f for f in rejected if f in match.files())
2355 forgot.extend(f for f in forget if f not in rejected)
2356 forgot.extend(f for f in forget if f not in rejected)
2356 return bad, forgot
2357 return bad, forgot
2357
2358
2358 def files(ui, ctx, m, fm, fmt, subrepos):
2359 def files(ui, ctx, m, fm, fmt, subrepos):
2359 rev = ctx.rev()
2360 rev = ctx.rev()
2360 ret = 1
2361 ret = 1
2361 ds = ctx.repo().dirstate
2362 ds = ctx.repo().dirstate
2362
2363
2363 for f in ctx.matches(m):
2364 for f in ctx.matches(m):
2364 if rev is None and ds[f] == 'r':
2365 if rev is None and ds[f] == 'r':
2365 continue
2366 continue
2366 fm.startitem()
2367 fm.startitem()
2367 if ui.verbose:
2368 if ui.verbose:
2368 fc = ctx[f]
2369 fc = ctx[f]
2369 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2370 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2370 fm.data(abspath=f)
2371 fm.data(abspath=f)
2371 fm.write('path', fmt, m.rel(f))
2372 fm.write('path', fmt, m.rel(f))
2372 ret = 0
2373 ret = 0
2373
2374
2374 for subpath in sorted(ctx.substate):
2375 for subpath in sorted(ctx.substate):
2375 def matchessubrepo(subpath):
2376 def matchessubrepo(subpath):
2376 return (m.always() or m.exact(subpath)
2377 return (m.always() or m.exact(subpath)
2377 or any(f.startswith(subpath + '/') for f in m.files()))
2378 or any(f.startswith(subpath + '/') for f in m.files()))
2378
2379
2379 if subrepos or matchessubrepo(subpath):
2380 if subrepos or matchessubrepo(subpath):
2380 sub = ctx.sub(subpath)
2381 sub = ctx.sub(subpath)
2381 try:
2382 try:
2382 submatch = matchmod.subdirmatcher(subpath, m)
2383 submatch = matchmod.subdirmatcher(subpath, m)
2383 if sub.printfiles(ui, submatch, fm, fmt, subrepos) == 0:
2384 if sub.printfiles(ui, submatch, fm, fmt, subrepos) == 0:
2384 ret = 0
2385 ret = 0
2385 except error.LookupError:
2386 except error.LookupError:
2386 ui.status(_("skipping missing subrepository: %s\n")
2387 ui.status(_("skipping missing subrepository: %s\n")
2387 % m.abs(subpath))
2388 % m.abs(subpath))
2388
2389
2389 return ret
2390 return ret
2390
2391
2391 def remove(ui, repo, m, prefix, after, force, subrepos):
2392 def remove(ui, repo, m, prefix, after, force, subrepos):
2392 join = lambda f: os.path.join(prefix, f)
2393 join = lambda f: os.path.join(prefix, f)
2393 ret = 0
2394 ret = 0
2394 s = repo.status(match=m, clean=True)
2395 s = repo.status(match=m, clean=True)
2395 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2396 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2396
2397
2397 wctx = repo[None]
2398 wctx = repo[None]
2398
2399
2399 for subpath in sorted(wctx.substate):
2400 for subpath in sorted(wctx.substate):
2400 def matchessubrepo(matcher, subpath):
2401 def matchessubrepo(matcher, subpath):
2401 if matcher.exact(subpath):
2402 if matcher.exact(subpath):
2402 return True
2403 return True
2403 for f in matcher.files():
2404 for f in matcher.files():
2404 if f.startswith(subpath):
2405 if f.startswith(subpath):
2405 return True
2406 return True
2406 return False
2407 return False
2407
2408
2408 if subrepos or matchessubrepo(m, subpath):
2409 if subrepos or matchessubrepo(m, subpath):
2409 sub = wctx.sub(subpath)
2410 sub = wctx.sub(subpath)
2410 try:
2411 try:
2411 submatch = matchmod.subdirmatcher(subpath, m)
2412 submatch = matchmod.subdirmatcher(subpath, m)
2412 if sub.removefiles(submatch, prefix, after, force, subrepos):
2413 if sub.removefiles(submatch, prefix, after, force, subrepos):
2413 ret = 1
2414 ret = 1
2414 except error.LookupError:
2415 except error.LookupError:
2415 ui.status(_("skipping missing subrepository: %s\n")
2416 ui.status(_("skipping missing subrepository: %s\n")
2416 % join(subpath))
2417 % join(subpath))
2417
2418
2418 # warn about failure to delete explicit files/dirs
2419 # warn about failure to delete explicit files/dirs
2419 deleteddirs = util.dirs(deleted)
2420 deleteddirs = util.dirs(deleted)
2420 for f in m.files():
2421 for f in m.files():
2421 def insubrepo():
2422 def insubrepo():
2422 for subpath in wctx.substate:
2423 for subpath in wctx.substate:
2423 if f.startswith(subpath):
2424 if f.startswith(subpath):
2424 return True
2425 return True
2425 return False
2426 return False
2426
2427
2427 isdir = f in deleteddirs or wctx.hasdir(f)
2428 isdir = f in deleteddirs or wctx.hasdir(f)
2428 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2429 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2429 continue
2430 continue
2430
2431
2431 if repo.wvfs.exists(f):
2432 if repo.wvfs.exists(f):
2432 if repo.wvfs.isdir(f):
2433 if repo.wvfs.isdir(f):
2433 ui.warn(_('not removing %s: no tracked files\n')
2434 ui.warn(_('not removing %s: no tracked files\n')
2434 % m.rel(f))
2435 % m.rel(f))
2435 else:
2436 else:
2436 ui.warn(_('not removing %s: file is untracked\n')
2437 ui.warn(_('not removing %s: file is untracked\n')
2437 % m.rel(f))
2438 % m.rel(f))
2438 # missing files will generate a warning elsewhere
2439 # missing files will generate a warning elsewhere
2439 ret = 1
2440 ret = 1
2440
2441
2441 if force:
2442 if force:
2442 list = modified + deleted + clean + added
2443 list = modified + deleted + clean + added
2443 elif after:
2444 elif after:
2444 list = deleted
2445 list = deleted
2445 for f in modified + added + clean:
2446 for f in modified + added + clean:
2446 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2447 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2447 ret = 1
2448 ret = 1
2448 else:
2449 else:
2449 list = deleted + clean
2450 list = deleted + clean
2450 for f in modified:
2451 for f in modified:
2451 ui.warn(_('not removing %s: file is modified (use -f'
2452 ui.warn(_('not removing %s: file is modified (use -f'
2452 ' to force removal)\n') % m.rel(f))
2453 ' to force removal)\n') % m.rel(f))
2453 ret = 1
2454 ret = 1
2454 for f in added:
2455 for f in added:
2455 ui.warn(_('not removing %s: file has been marked for add'
2456 ui.warn(_('not removing %s: file has been marked for add'
2456 ' (use forget to undo)\n') % m.rel(f))
2457 ' (use forget to undo)\n') % m.rel(f))
2457 ret = 1
2458 ret = 1
2458
2459
2459 for f in sorted(list):
2460 for f in sorted(list):
2460 if ui.verbose or not m.exact(f):
2461 if ui.verbose or not m.exact(f):
2461 ui.status(_('removing %s\n') % m.rel(f))
2462 ui.status(_('removing %s\n') % m.rel(f))
2462
2463
2463 with repo.wlock():
2464 with repo.wlock():
2464 if not after:
2465 if not after:
2465 for f in list:
2466 for f in list:
2466 if f in added:
2467 if f in added:
2467 continue # we never unlink added files on remove
2468 continue # we never unlink added files on remove
2468 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2469 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2469 repo[None].forget(list)
2470 repo[None].forget(list)
2470
2471
2471 return ret
2472 return ret
2472
2473
2473 def cat(ui, repo, ctx, matcher, prefix, **opts):
2474 def cat(ui, repo, ctx, matcher, prefix, **opts):
2474 err = 1
2475 err = 1
2475
2476
2476 def write(path):
2477 def write(path):
2477 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2478 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2478 pathname=os.path.join(prefix, path))
2479 pathname=os.path.join(prefix, path))
2479 data = ctx[path].data()
2480 data = ctx[path].data()
2480 if opts.get('decode'):
2481 if opts.get('decode'):
2481 data = repo.wwritedata(path, data)
2482 data = repo.wwritedata(path, data)
2482 fp.write(data)
2483 fp.write(data)
2483 fp.close()
2484 fp.close()
2484
2485
2485 # Automation often uses hg cat on single files, so special case it
2486 # Automation often uses hg cat on single files, so special case it
2486 # for performance to avoid the cost of parsing the manifest.
2487 # for performance to avoid the cost of parsing the manifest.
2487 if len(matcher.files()) == 1 and not matcher.anypats():
2488 if len(matcher.files()) == 1 and not matcher.anypats():
2488 file = matcher.files()[0]
2489 file = matcher.files()[0]
2489 mf = repo.manifest
2490 mf = repo.manifest
2490 mfnode = ctx.manifestnode()
2491 mfnode = ctx.manifestnode()
2491 if mfnode and mf.find(mfnode, file)[0]:
2492 if mfnode and mf.find(mfnode, file)[0]:
2492 write(file)
2493 write(file)
2493 return 0
2494 return 0
2494
2495
2495 # Don't warn about "missing" files that are really in subrepos
2496 # Don't warn about "missing" files that are really in subrepos
2496 def badfn(path, msg):
2497 def badfn(path, msg):
2497 for subpath in ctx.substate:
2498 for subpath in ctx.substate:
2498 if path.startswith(subpath):
2499 if path.startswith(subpath):
2499 return
2500 return
2500 matcher.bad(path, msg)
2501 matcher.bad(path, msg)
2501
2502
2502 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2503 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2503 write(abs)
2504 write(abs)
2504 err = 0
2505 err = 0
2505
2506
2506 for subpath in sorted(ctx.substate):
2507 for subpath in sorted(ctx.substate):
2507 sub = ctx.sub(subpath)
2508 sub = ctx.sub(subpath)
2508 try:
2509 try:
2509 submatch = matchmod.subdirmatcher(subpath, matcher)
2510 submatch = matchmod.subdirmatcher(subpath, matcher)
2510
2511
2511 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2512 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2512 **opts):
2513 **opts):
2513 err = 0
2514 err = 0
2514 except error.RepoLookupError:
2515 except error.RepoLookupError:
2515 ui.status(_("skipping missing subrepository: %s\n")
2516 ui.status(_("skipping missing subrepository: %s\n")
2516 % os.path.join(prefix, subpath))
2517 % os.path.join(prefix, subpath))
2517
2518
2518 return err
2519 return err
2519
2520
2520 def commit(ui, repo, commitfunc, pats, opts):
2521 def commit(ui, repo, commitfunc, pats, opts):
2521 '''commit the specified files or all outstanding changes'''
2522 '''commit the specified files or all outstanding changes'''
2522 date = opts.get('date')
2523 date = opts.get('date')
2523 if date:
2524 if date:
2524 opts['date'] = util.parsedate(date)
2525 opts['date'] = util.parsedate(date)
2525 message = logmessage(ui, opts)
2526 message = logmessage(ui, opts)
2526 matcher = scmutil.match(repo[None], pats, opts)
2527 matcher = scmutil.match(repo[None], pats, opts)
2527
2528
2528 # extract addremove carefully -- this function can be called from a command
2529 # extract addremove carefully -- this function can be called from a command
2529 # that doesn't support addremove
2530 # that doesn't support addremove
2530 if opts.get('addremove'):
2531 if opts.get('addremove'):
2531 if scmutil.addremove(repo, matcher, "", opts) != 0:
2532 if scmutil.addremove(repo, matcher, "", opts) != 0:
2532 raise error.Abort(
2533 raise error.Abort(
2533 _("failed to mark all new/missing files as added/removed"))
2534 _("failed to mark all new/missing files as added/removed"))
2534
2535
2535 return commitfunc(ui, repo, message, matcher, opts)
2536 return commitfunc(ui, repo, message, matcher, opts)
2536
2537
2537 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2538 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2538 # avoid cycle context -> subrepo -> cmdutil
2539 # avoid cycle context -> subrepo -> cmdutil
2539 from . import context
2540 from . import context
2540
2541
2541 # amend will reuse the existing user if not specified, but the obsolete
2542 # amend will reuse the existing user if not specified, but the obsolete
2542 # marker creation requires that the current user's name is specified.
2543 # marker creation requires that the current user's name is specified.
2543 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2544 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2544 ui.username() # raise exception if username not set
2545 ui.username() # raise exception if username not set
2545
2546
2546 ui.note(_('amending changeset %s\n') % old)
2547 ui.note(_('amending changeset %s\n') % old)
2547 base = old.p1()
2548 base = old.p1()
2548 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2549 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2549
2550
2550 wlock = lock = newid = None
2551 wlock = lock = newid = None
2551 try:
2552 try:
2552 wlock = repo.wlock()
2553 wlock = repo.wlock()
2553 lock = repo.lock()
2554 lock = repo.lock()
2554 with repo.transaction('amend') as tr:
2555 with repo.transaction('amend') as tr:
2555 # See if we got a message from -m or -l, if not, open the editor
2556 # See if we got a message from -m or -l, if not, open the editor
2556 # with the message of the changeset to amend
2557 # with the message of the changeset to amend
2557 message = logmessage(ui, opts)
2558 message = logmessage(ui, opts)
2558 # ensure logfile does not conflict with later enforcement of the
2559 # ensure logfile does not conflict with later enforcement of the
2559 # message. potential logfile content has been processed by
2560 # message. potential logfile content has been processed by
2560 # `logmessage` anyway.
2561 # `logmessage` anyway.
2561 opts.pop('logfile')
2562 opts.pop('logfile')
2562 # First, do a regular commit to record all changes in the working
2563 # First, do a regular commit to record all changes in the working
2563 # directory (if there are any)
2564 # directory (if there are any)
2564 ui.callhooks = False
2565 ui.callhooks = False
2565 activebookmark = repo._bookmarks.active
2566 activebookmark = repo._bookmarks.active
2566 try:
2567 try:
2567 repo._bookmarks.active = None
2568 repo._bookmarks.active = None
2568 opts['message'] = 'temporary amend commit for %s' % old
2569 opts['message'] = 'temporary amend commit for %s' % old
2569 node = commit(ui, repo, commitfunc, pats, opts)
2570 node = commit(ui, repo, commitfunc, pats, opts)
2570 finally:
2571 finally:
2571 repo._bookmarks.active = activebookmark
2572 repo._bookmarks.active = activebookmark
2572 repo._bookmarks.recordchange(tr)
2573 repo._bookmarks.recordchange(tr)
2573 ui.callhooks = True
2574 ui.callhooks = True
2574 ctx = repo[node]
2575 ctx = repo[node]
2575
2576
2576 # Participating changesets:
2577 # Participating changesets:
2577 #
2578 #
2578 # node/ctx o - new (intermediate) commit that contains changes
2579 # node/ctx o - new (intermediate) commit that contains changes
2579 # | from working dir to go into amending commit
2580 # | from working dir to go into amending commit
2580 # | (or a workingctx if there were no changes)
2581 # | (or a workingctx if there were no changes)
2581 # |
2582 # |
2582 # old o - changeset to amend
2583 # old o - changeset to amend
2583 # |
2584 # |
2584 # base o - parent of amending changeset
2585 # base o - parent of amending changeset
2585
2586
2586 # Update extra dict from amended commit (e.g. to preserve graft
2587 # Update extra dict from amended commit (e.g. to preserve graft
2587 # source)
2588 # source)
2588 extra.update(old.extra())
2589 extra.update(old.extra())
2589
2590
2590 # Also update it from the intermediate commit or from the wctx
2591 # Also update it from the intermediate commit or from the wctx
2591 extra.update(ctx.extra())
2592 extra.update(ctx.extra())
2592
2593
2593 if len(old.parents()) > 1:
2594 if len(old.parents()) > 1:
2594 # ctx.files() isn't reliable for merges, so fall back to the
2595 # ctx.files() isn't reliable for merges, so fall back to the
2595 # slower repo.status() method
2596 # slower repo.status() method
2596 files = set([fn for st in repo.status(base, old)[:3]
2597 files = set([fn for st in repo.status(base, old)[:3]
2597 for fn in st])
2598 for fn in st])
2598 else:
2599 else:
2599 files = set(old.files())
2600 files = set(old.files())
2600
2601
2601 # Second, we use either the commit we just did, or if there were no
2602 # Second, we use either the commit we just did, or if there were no
2602 # changes the parent of the working directory as the version of the
2603 # changes the parent of the working directory as the version of the
2603 # files in the final amend commit
2604 # files in the final amend commit
2604 if node:
2605 if node:
2605 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2606 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2606
2607
2607 user = ctx.user()
2608 user = ctx.user()
2608 date = ctx.date()
2609 date = ctx.date()
2609 # Recompute copies (avoid recording a -> b -> a)
2610 # Recompute copies (avoid recording a -> b -> a)
2610 copied = copies.pathcopies(base, ctx)
2611 copied = copies.pathcopies(base, ctx)
2611 if old.p2:
2612 if old.p2:
2612 copied.update(copies.pathcopies(old.p2(), ctx))
2613 copied.update(copies.pathcopies(old.p2(), ctx))
2613
2614
2614 # Prune files which were reverted by the updates: if old
2615 # Prune files which were reverted by the updates: if old
2615 # introduced file X and our intermediate commit, node,
2616 # introduced file X and our intermediate commit, node,
2616 # renamed that file, then those two files are the same and
2617 # renamed that file, then those two files are the same and
2617 # we can discard X from our list of files. Likewise if X
2618 # we can discard X from our list of files. Likewise if X
2618 # was deleted, it's no longer relevant
2619 # was deleted, it's no longer relevant
2619 files.update(ctx.files())
2620 files.update(ctx.files())
2620
2621
2621 def samefile(f):
2622 def samefile(f):
2622 if f in ctx.manifest():
2623 if f in ctx.manifest():
2623 a = ctx.filectx(f)
2624 a = ctx.filectx(f)
2624 if f in base.manifest():
2625 if f in base.manifest():
2625 b = base.filectx(f)
2626 b = base.filectx(f)
2626 return (not a.cmp(b)
2627 return (not a.cmp(b)
2627 and a.flags() == b.flags())
2628 and a.flags() == b.flags())
2628 else:
2629 else:
2629 return False
2630 return False
2630 else:
2631 else:
2631 return f not in base.manifest()
2632 return f not in base.manifest()
2632 files = [f for f in files if not samefile(f)]
2633 files = [f for f in files if not samefile(f)]
2633
2634
2634 def filectxfn(repo, ctx_, path):
2635 def filectxfn(repo, ctx_, path):
2635 try:
2636 try:
2636 fctx = ctx[path]
2637 fctx = ctx[path]
2637 flags = fctx.flags()
2638 flags = fctx.flags()
2638 mctx = context.memfilectx(repo,
2639 mctx = context.memfilectx(repo,
2639 fctx.path(), fctx.data(),
2640 fctx.path(), fctx.data(),
2640 islink='l' in flags,
2641 islink='l' in flags,
2641 isexec='x' in flags,
2642 isexec='x' in flags,
2642 copied=copied.get(path))
2643 copied=copied.get(path))
2643 return mctx
2644 return mctx
2644 except KeyError:
2645 except KeyError:
2645 return None
2646 return None
2646 else:
2647 else:
2647 ui.note(_('copying changeset %s to %s\n') % (old, base))
2648 ui.note(_('copying changeset %s to %s\n') % (old, base))
2648
2649
2649 # Use version of files as in the old cset
2650 # Use version of files as in the old cset
2650 def filectxfn(repo, ctx_, path):
2651 def filectxfn(repo, ctx_, path):
2651 try:
2652 try:
2652 return old.filectx(path)
2653 return old.filectx(path)
2653 except KeyError:
2654 except KeyError:
2654 return None
2655 return None
2655
2656
2656 user = opts.get('user') or old.user()
2657 user = opts.get('user') or old.user()
2657 date = opts.get('date') or old.date()
2658 date = opts.get('date') or old.date()
2658 editform = mergeeditform(old, 'commit.amend')
2659 editform = mergeeditform(old, 'commit.amend')
2659 editor = getcommiteditor(editform=editform, **opts)
2660 editor = getcommiteditor(editform=editform, **opts)
2660 if not message:
2661 if not message:
2661 editor = getcommiteditor(edit=True, editform=editform)
2662 editor = getcommiteditor(edit=True, editform=editform)
2662 message = old.description()
2663 message = old.description()
2663
2664
2664 pureextra = extra.copy()
2665 pureextra = extra.copy()
2665 extra['amend_source'] = old.hex()
2666 extra['amend_source'] = old.hex()
2666
2667
2667 new = context.memctx(repo,
2668 new = context.memctx(repo,
2668 parents=[base.node(), old.p2().node()],
2669 parents=[base.node(), old.p2().node()],
2669 text=message,
2670 text=message,
2670 files=files,
2671 files=files,
2671 filectxfn=filectxfn,
2672 filectxfn=filectxfn,
2672 user=user,
2673 user=user,
2673 date=date,
2674 date=date,
2674 extra=extra,
2675 extra=extra,
2675 editor=editor)
2676 editor=editor)
2676
2677
2677 newdesc = changelog.stripdesc(new.description())
2678 newdesc = changelog.stripdesc(new.description())
2678 if ((not node)
2679 if ((not node)
2679 and newdesc == old.description()
2680 and newdesc == old.description()
2680 and user == old.user()
2681 and user == old.user()
2681 and date == old.date()
2682 and date == old.date()
2682 and pureextra == old.extra()):
2683 and pureextra == old.extra()):
2683 # nothing changed. continuing here would create a new node
2684 # nothing changed. continuing here would create a new node
2684 # anyway because of the amend_source noise.
2685 # anyway because of the amend_source noise.
2685 #
2686 #
2686 # This not what we expect from amend.
2687 # This not what we expect from amend.
2687 return old.node()
2688 return old.node()
2688
2689
2689 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2690 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2690 try:
2691 try:
2691 if opts.get('secret'):
2692 if opts.get('secret'):
2692 commitphase = 'secret'
2693 commitphase = 'secret'
2693 else:
2694 else:
2694 commitphase = old.phase()
2695 commitphase = old.phase()
2695 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2696 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2696 newid = repo.commitctx(new)
2697 newid = repo.commitctx(new)
2697 finally:
2698 finally:
2698 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2699 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2699 if newid != old.node():
2700 if newid != old.node():
2700 # Reroute the working copy parent to the new changeset
2701 # Reroute the working copy parent to the new changeset
2701 repo.setparents(newid, nullid)
2702 repo.setparents(newid, nullid)
2702
2703
2703 # Move bookmarks from old parent to amend commit
2704 # Move bookmarks from old parent to amend commit
2704 bms = repo.nodebookmarks(old.node())
2705 bms = repo.nodebookmarks(old.node())
2705 if bms:
2706 if bms:
2706 marks = repo._bookmarks
2707 marks = repo._bookmarks
2707 for bm in bms:
2708 for bm in bms:
2708 ui.debug('moving bookmarks %r from %s to %s\n' %
2709 ui.debug('moving bookmarks %r from %s to %s\n' %
2709 (marks, old.hex(), hex(newid)))
2710 (marks, old.hex(), hex(newid)))
2710 marks[bm] = newid
2711 marks[bm] = newid
2711 marks.recordchange(tr)
2712 marks.recordchange(tr)
2712 #commit the whole amend process
2713 #commit the whole amend process
2713 if createmarkers:
2714 if createmarkers:
2714 # mark the new changeset as successor of the rewritten one
2715 # mark the new changeset as successor of the rewritten one
2715 new = repo[newid]
2716 new = repo[newid]
2716 obs = [(old, (new,))]
2717 obs = [(old, (new,))]
2717 if node:
2718 if node:
2718 obs.append((ctx, ()))
2719 obs.append((ctx, ()))
2719
2720
2720 obsolete.createmarkers(repo, obs)
2721 obsolete.createmarkers(repo, obs)
2721 if not createmarkers and newid != old.node():
2722 if not createmarkers and newid != old.node():
2722 # Strip the intermediate commit (if there was one) and the amended
2723 # Strip the intermediate commit (if there was one) and the amended
2723 # commit
2724 # commit
2724 if node:
2725 if node:
2725 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2726 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2726 ui.note(_('stripping amended changeset %s\n') % old)
2727 ui.note(_('stripping amended changeset %s\n') % old)
2727 repair.strip(ui, repo, old.node(), topic='amend-backup')
2728 repair.strip(ui, repo, old.node(), topic='amend-backup')
2728 finally:
2729 finally:
2729 lockmod.release(lock, wlock)
2730 lockmod.release(lock, wlock)
2730 return newid
2731 return newid
2731
2732
2732 def commiteditor(repo, ctx, subs, editform=''):
2733 def commiteditor(repo, ctx, subs, editform=''):
2733 if ctx.description():
2734 if ctx.description():
2734 return ctx.description()
2735 return ctx.description()
2735 return commitforceeditor(repo, ctx, subs, editform=editform,
2736 return commitforceeditor(repo, ctx, subs, editform=editform,
2736 unchangedmessagedetection=True)
2737 unchangedmessagedetection=True)
2737
2738
2738 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2739 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2739 editform='', unchangedmessagedetection=False):
2740 editform='', unchangedmessagedetection=False):
2740 if not extramsg:
2741 if not extramsg:
2741 extramsg = _("Leave message empty to abort commit.")
2742 extramsg = _("Leave message empty to abort commit.")
2742
2743
2743 forms = [e for e in editform.split('.') if e]
2744 forms = [e for e in editform.split('.') if e]
2744 forms.insert(0, 'changeset')
2745 forms.insert(0, 'changeset')
2745 templatetext = None
2746 templatetext = None
2746 while forms:
2747 while forms:
2747 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2748 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2748 if tmpl:
2749 if tmpl:
2749 templatetext = committext = buildcommittemplate(
2750 templatetext = committext = buildcommittemplate(
2750 repo, ctx, subs, extramsg, tmpl)
2751 repo, ctx, subs, extramsg, tmpl)
2751 break
2752 break
2752 forms.pop()
2753 forms.pop()
2753 else:
2754 else:
2754 committext = buildcommittext(repo, ctx, subs, extramsg)
2755 committext = buildcommittext(repo, ctx, subs, extramsg)
2755
2756
2756 # run editor in the repository root
2757 # run editor in the repository root
2757 olddir = os.getcwd()
2758 olddir = os.getcwd()
2758 os.chdir(repo.root)
2759 os.chdir(repo.root)
2759
2760
2760 # make in-memory changes visible to external process
2761 # make in-memory changes visible to external process
2761 tr = repo.currenttransaction()
2762 tr = repo.currenttransaction()
2762 repo.dirstate.write(tr)
2763 repo.dirstate.write(tr)
2763 pending = tr and tr.writepending() and repo.root
2764 pending = tr and tr.writepending() and repo.root
2764
2765
2765 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2766 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2766 editform=editform, pending=pending)
2767 editform=editform, pending=pending)
2767 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2768 text = re.sub("(?m)^HG:.*(\n|$)", "", editortext)
2768 os.chdir(olddir)
2769 os.chdir(olddir)
2769
2770
2770 if finishdesc:
2771 if finishdesc:
2771 text = finishdesc(text)
2772 text = finishdesc(text)
2772 if not text.strip():
2773 if not text.strip():
2773 raise error.Abort(_("empty commit message"))
2774 raise error.Abort(_("empty commit message"))
2774 if unchangedmessagedetection and editortext == templatetext:
2775 if unchangedmessagedetection and editortext == templatetext:
2775 raise error.Abort(_("commit message unchanged"))
2776 raise error.Abort(_("commit message unchanged"))
2776
2777
2777 return text
2778 return text
2778
2779
2779 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2780 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2780 ui = repo.ui
2781 ui = repo.ui
2781 tmpl, mapfile = gettemplate(ui, tmpl, None)
2782 tmpl, mapfile = gettemplate(ui, tmpl, None)
2782
2783
2783 try:
2784 try:
2784 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2785 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2785 except SyntaxError as inst:
2786 except SyntaxError as inst:
2786 raise error.Abort(inst.args[0])
2787 raise error.Abort(inst.args[0])
2787
2788
2788 for k, v in repo.ui.configitems('committemplate'):
2789 for k, v in repo.ui.configitems('committemplate'):
2789 if k != 'changeset':
2790 if k != 'changeset':
2790 t.t.cache[k] = v
2791 t.t.cache[k] = v
2791
2792
2792 if not extramsg:
2793 if not extramsg:
2793 extramsg = '' # ensure that extramsg is string
2794 extramsg = '' # ensure that extramsg is string
2794
2795
2795 ui.pushbuffer()
2796 ui.pushbuffer()
2796 t.show(ctx, extramsg=extramsg)
2797 t.show(ctx, extramsg=extramsg)
2797 return ui.popbuffer()
2798 return ui.popbuffer()
2798
2799
2799 def hgprefix(msg):
2800 def hgprefix(msg):
2800 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2801 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2801
2802
2802 def buildcommittext(repo, ctx, subs, extramsg):
2803 def buildcommittext(repo, ctx, subs, extramsg):
2803 edittext = []
2804 edittext = []
2804 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2805 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2805 if ctx.description():
2806 if ctx.description():
2806 edittext.append(ctx.description())
2807 edittext.append(ctx.description())
2807 edittext.append("")
2808 edittext.append("")
2808 edittext.append("") # Empty line between message and comments.
2809 edittext.append("") # Empty line between message and comments.
2809 edittext.append(hgprefix(_("Enter commit message."
2810 edittext.append(hgprefix(_("Enter commit message."
2810 " Lines beginning with 'HG:' are removed.")))
2811 " Lines beginning with 'HG:' are removed.")))
2811 edittext.append(hgprefix(extramsg))
2812 edittext.append(hgprefix(extramsg))
2812 edittext.append("HG: --")
2813 edittext.append("HG: --")
2813 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2814 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2814 if ctx.p2():
2815 if ctx.p2():
2815 edittext.append(hgprefix(_("branch merge")))
2816 edittext.append(hgprefix(_("branch merge")))
2816 if ctx.branch():
2817 if ctx.branch():
2817 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2818 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2818 if bookmarks.isactivewdirparent(repo):
2819 if bookmarks.isactivewdirparent(repo):
2819 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2820 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2820 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2821 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2821 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2822 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2822 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2823 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2823 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2824 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2824 if not added and not modified and not removed:
2825 if not added and not modified and not removed:
2825 edittext.append(hgprefix(_("no files changed")))
2826 edittext.append(hgprefix(_("no files changed")))
2826 edittext.append("")
2827 edittext.append("")
2827
2828
2828 return "\n".join(edittext)
2829 return "\n".join(edittext)
2829
2830
2830 def commitstatus(repo, node, branch, bheads=None, opts=None):
2831 def commitstatus(repo, node, branch, bheads=None, opts=None):
2831 if opts is None:
2832 if opts is None:
2832 opts = {}
2833 opts = {}
2833 ctx = repo[node]
2834 ctx = repo[node]
2834 parents = ctx.parents()
2835 parents = ctx.parents()
2835
2836
2836 if (not opts.get('amend') and bheads and node not in bheads and not
2837 if (not opts.get('amend') and bheads and node not in bheads and not
2837 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2838 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2838 repo.ui.status(_('created new head\n'))
2839 repo.ui.status(_('created new head\n'))
2839 # The message is not printed for initial roots. For the other
2840 # The message is not printed for initial roots. For the other
2840 # changesets, it is printed in the following situations:
2841 # changesets, it is printed in the following situations:
2841 #
2842 #
2842 # Par column: for the 2 parents with ...
2843 # Par column: for the 2 parents with ...
2843 # N: null or no parent
2844 # N: null or no parent
2844 # B: parent is on another named branch
2845 # B: parent is on another named branch
2845 # C: parent is a regular non head changeset
2846 # C: parent is a regular non head changeset
2846 # H: parent was a branch head of the current branch
2847 # H: parent was a branch head of the current branch
2847 # Msg column: whether we print "created new head" message
2848 # Msg column: whether we print "created new head" message
2848 # In the following, it is assumed that there already exists some
2849 # In the following, it is assumed that there already exists some
2849 # initial branch heads of the current branch, otherwise nothing is
2850 # initial branch heads of the current branch, otherwise nothing is
2850 # printed anyway.
2851 # printed anyway.
2851 #
2852 #
2852 # Par Msg Comment
2853 # Par Msg Comment
2853 # N N y additional topo root
2854 # N N y additional topo root
2854 #
2855 #
2855 # B N y additional branch root
2856 # B N y additional branch root
2856 # C N y additional topo head
2857 # C N y additional topo head
2857 # H N n usual case
2858 # H N n usual case
2858 #
2859 #
2859 # B B y weird additional branch root
2860 # B B y weird additional branch root
2860 # C B y branch merge
2861 # C B y branch merge
2861 # H B n merge with named branch
2862 # H B n merge with named branch
2862 #
2863 #
2863 # C C y additional head from merge
2864 # C C y additional head from merge
2864 # C H n merge with a head
2865 # C H n merge with a head
2865 #
2866 #
2866 # H H n head merge: head count decreases
2867 # H H n head merge: head count decreases
2867
2868
2868 if not opts.get('close_branch'):
2869 if not opts.get('close_branch'):
2869 for r in parents:
2870 for r in parents:
2870 if r.closesbranch() and r.branch() == branch:
2871 if r.closesbranch() and r.branch() == branch:
2871 repo.ui.status(_('reopening closed branch head %d\n') % r)
2872 repo.ui.status(_('reopening closed branch head %d\n') % r)
2872
2873
2873 if repo.ui.debugflag:
2874 if repo.ui.debugflag:
2874 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2875 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2875 elif repo.ui.verbose:
2876 elif repo.ui.verbose:
2876 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2877 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2877
2878
2878 def postcommitstatus(repo, pats, opts):
2879 def postcommitstatus(repo, pats, opts):
2879 return repo.status(match=scmutil.match(repo[None], pats, opts))
2880 return repo.status(match=scmutil.match(repo[None], pats, opts))
2880
2881
2881 def revert(ui, repo, ctx, parents, *pats, **opts):
2882 def revert(ui, repo, ctx, parents, *pats, **opts):
2882 parent, p2 = parents
2883 parent, p2 = parents
2883 node = ctx.node()
2884 node = ctx.node()
2884
2885
2885 mf = ctx.manifest()
2886 mf = ctx.manifest()
2886 if node == p2:
2887 if node == p2:
2887 parent = p2
2888 parent = p2
2888
2889
2889 # need all matching names in dirstate and manifest of target rev,
2890 # need all matching names in dirstate and manifest of target rev,
2890 # so have to walk both. do not print errors if files exist in one
2891 # so have to walk both. do not print errors if files exist in one
2891 # but not other. in both cases, filesets should be evaluated against
2892 # but not other. in both cases, filesets should be evaluated against
2892 # workingctx to get consistent result (issue4497). this means 'set:**'
2893 # workingctx to get consistent result (issue4497). this means 'set:**'
2893 # cannot be used to select missing files from target rev.
2894 # cannot be used to select missing files from target rev.
2894
2895
2895 # `names` is a mapping for all elements in working copy and target revision
2896 # `names` is a mapping for all elements in working copy and target revision
2896 # The mapping is in the form:
2897 # The mapping is in the form:
2897 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2898 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2898 names = {}
2899 names = {}
2899
2900
2900 with repo.wlock():
2901 with repo.wlock():
2901 ## filling of the `names` mapping
2902 ## filling of the `names` mapping
2902 # walk dirstate to fill `names`
2903 # walk dirstate to fill `names`
2903
2904
2904 interactive = opts.get('interactive', False)
2905 interactive = opts.get('interactive', False)
2905 wctx = repo[None]
2906 wctx = repo[None]
2906 m = scmutil.match(wctx, pats, opts)
2907 m = scmutil.match(wctx, pats, opts)
2907
2908
2908 # we'll need this later
2909 # we'll need this later
2909 targetsubs = sorted(s for s in wctx.substate if m(s))
2910 targetsubs = sorted(s for s in wctx.substate if m(s))
2910
2911
2911 if not m.always():
2912 if not m.always():
2912 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2913 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2913 names[abs] = m.rel(abs), m.exact(abs)
2914 names[abs] = m.rel(abs), m.exact(abs)
2914
2915
2915 # walk target manifest to fill `names`
2916 # walk target manifest to fill `names`
2916
2917
2917 def badfn(path, msg):
2918 def badfn(path, msg):
2918 if path in names:
2919 if path in names:
2919 return
2920 return
2920 if path in ctx.substate:
2921 if path in ctx.substate:
2921 return
2922 return
2922 path_ = path + '/'
2923 path_ = path + '/'
2923 for f in names:
2924 for f in names:
2924 if f.startswith(path_):
2925 if f.startswith(path_):
2925 return
2926 return
2926 ui.warn("%s: %s\n" % (m.rel(path), msg))
2927 ui.warn("%s: %s\n" % (m.rel(path), msg))
2927
2928
2928 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2929 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2929 if abs not in names:
2930 if abs not in names:
2930 names[abs] = m.rel(abs), m.exact(abs)
2931 names[abs] = m.rel(abs), m.exact(abs)
2931
2932
2932 # Find status of all file in `names`.
2933 # Find status of all file in `names`.
2933 m = scmutil.matchfiles(repo, names)
2934 m = scmutil.matchfiles(repo, names)
2934
2935
2935 changes = repo.status(node1=node, match=m,
2936 changes = repo.status(node1=node, match=m,
2936 unknown=True, ignored=True, clean=True)
2937 unknown=True, ignored=True, clean=True)
2937 else:
2938 else:
2938 changes = repo.status(node1=node, match=m)
2939 changes = repo.status(node1=node, match=m)
2939 for kind in changes:
2940 for kind in changes:
2940 for abs in kind:
2941 for abs in kind:
2941 names[abs] = m.rel(abs), m.exact(abs)
2942 names[abs] = m.rel(abs), m.exact(abs)
2942
2943
2943 m = scmutil.matchfiles(repo, names)
2944 m = scmutil.matchfiles(repo, names)
2944
2945
2945 modified = set(changes.modified)
2946 modified = set(changes.modified)
2946 added = set(changes.added)
2947 added = set(changes.added)
2947 removed = set(changes.removed)
2948 removed = set(changes.removed)
2948 _deleted = set(changes.deleted)
2949 _deleted = set(changes.deleted)
2949 unknown = set(changes.unknown)
2950 unknown = set(changes.unknown)
2950 unknown.update(changes.ignored)
2951 unknown.update(changes.ignored)
2951 clean = set(changes.clean)
2952 clean = set(changes.clean)
2952 modadded = set()
2953 modadded = set()
2953
2954
2954 # split between files known in target manifest and the others
2955 # split between files known in target manifest and the others
2955 smf = set(mf)
2956 smf = set(mf)
2956
2957
2957 # determine the exact nature of the deleted changesets
2958 # determine the exact nature of the deleted changesets
2958 deladded = _deleted - smf
2959 deladded = _deleted - smf
2959 deleted = _deleted - deladded
2960 deleted = _deleted - deladded
2960
2961
2961 # We need to account for the state of the file in the dirstate,
2962 # We need to account for the state of the file in the dirstate,
2962 # even when we revert against something else than parent. This will
2963 # even when we revert against something else than parent. This will
2963 # slightly alter the behavior of revert (doing back up or not, delete
2964 # slightly alter the behavior of revert (doing back up or not, delete
2964 # or just forget etc).
2965 # or just forget etc).
2965 if parent == node:
2966 if parent == node:
2966 dsmodified = modified
2967 dsmodified = modified
2967 dsadded = added
2968 dsadded = added
2968 dsremoved = removed
2969 dsremoved = removed
2969 # store all local modifications, useful later for rename detection
2970 # store all local modifications, useful later for rename detection
2970 localchanges = dsmodified | dsadded
2971 localchanges = dsmodified | dsadded
2971 modified, added, removed = set(), set(), set()
2972 modified, added, removed = set(), set(), set()
2972 else:
2973 else:
2973 changes = repo.status(node1=parent, match=m)
2974 changes = repo.status(node1=parent, match=m)
2974 dsmodified = set(changes.modified)
2975 dsmodified = set(changes.modified)
2975 dsadded = set(changes.added)
2976 dsadded = set(changes.added)
2976 dsremoved = set(changes.removed)
2977 dsremoved = set(changes.removed)
2977 # store all local modifications, useful later for rename detection
2978 # store all local modifications, useful later for rename detection
2978 localchanges = dsmodified | dsadded
2979 localchanges = dsmodified | dsadded
2979
2980
2980 # only take into account for removes between wc and target
2981 # only take into account for removes between wc and target
2981 clean |= dsremoved - removed
2982 clean |= dsremoved - removed
2982 dsremoved &= removed
2983 dsremoved &= removed
2983 # distinct between dirstate remove and other
2984 # distinct between dirstate remove and other
2984 removed -= dsremoved
2985 removed -= dsremoved
2985
2986
2986 modadded = added & dsmodified
2987 modadded = added & dsmodified
2987 added -= modadded
2988 added -= modadded
2988
2989
2989 # tell newly modified apart.
2990 # tell newly modified apart.
2990 dsmodified &= modified
2991 dsmodified &= modified
2991 dsmodified |= modified & dsadded # dirstate added may needs backup
2992 dsmodified |= modified & dsadded # dirstate added may needs backup
2992 modified -= dsmodified
2993 modified -= dsmodified
2993
2994
2994 # We need to wait for some post-processing to update this set
2995 # We need to wait for some post-processing to update this set
2995 # before making the distinction. The dirstate will be used for
2996 # before making the distinction. The dirstate will be used for
2996 # that purpose.
2997 # that purpose.
2997 dsadded = added
2998 dsadded = added
2998
2999
2999 # in case of merge, files that are actually added can be reported as
3000 # in case of merge, files that are actually added can be reported as
3000 # modified, we need to post process the result
3001 # modified, we need to post process the result
3001 if p2 != nullid:
3002 if p2 != nullid:
3002 mergeadd = dsmodified - smf
3003 mergeadd = dsmodified - smf
3003 dsadded |= mergeadd
3004 dsadded |= mergeadd
3004 dsmodified -= mergeadd
3005 dsmodified -= mergeadd
3005
3006
3006 # if f is a rename, update `names` to also revert the source
3007 # if f is a rename, update `names` to also revert the source
3007 cwd = repo.getcwd()
3008 cwd = repo.getcwd()
3008 for f in localchanges:
3009 for f in localchanges:
3009 src = repo.dirstate.copied(f)
3010 src = repo.dirstate.copied(f)
3010 # XXX should we check for rename down to target node?
3011 # XXX should we check for rename down to target node?
3011 if src and src not in names and repo.dirstate[src] == 'r':
3012 if src and src not in names and repo.dirstate[src] == 'r':
3012 dsremoved.add(src)
3013 dsremoved.add(src)
3013 names[src] = (repo.pathto(src, cwd), True)
3014 names[src] = (repo.pathto(src, cwd), True)
3014
3015
3015 # distinguish between file to forget and the other
3016 # distinguish between file to forget and the other
3016 added = set()
3017 added = set()
3017 for abs in dsadded:
3018 for abs in dsadded:
3018 if repo.dirstate[abs] != 'a':
3019 if repo.dirstate[abs] != 'a':
3019 added.add(abs)
3020 added.add(abs)
3020 dsadded -= added
3021 dsadded -= added
3021
3022
3022 for abs in deladded:
3023 for abs in deladded:
3023 if repo.dirstate[abs] == 'a':
3024 if repo.dirstate[abs] == 'a':
3024 dsadded.add(abs)
3025 dsadded.add(abs)
3025 deladded -= dsadded
3026 deladded -= dsadded
3026
3027
3027 # For files marked as removed, we check if an unknown file is present at
3028 # For files marked as removed, we check if an unknown file is present at
3028 # the same path. If a such file exists it may need to be backed up.
3029 # the same path. If a such file exists it may need to be backed up.
3029 # Making the distinction at this stage helps have simpler backup
3030 # Making the distinction at this stage helps have simpler backup
3030 # logic.
3031 # logic.
3031 removunk = set()
3032 removunk = set()
3032 for abs in removed:
3033 for abs in removed:
3033 target = repo.wjoin(abs)
3034 target = repo.wjoin(abs)
3034 if os.path.lexists(target):
3035 if os.path.lexists(target):
3035 removunk.add(abs)
3036 removunk.add(abs)
3036 removed -= removunk
3037 removed -= removunk
3037
3038
3038 dsremovunk = set()
3039 dsremovunk = set()
3039 for abs in dsremoved:
3040 for abs in dsremoved:
3040 target = repo.wjoin(abs)
3041 target = repo.wjoin(abs)
3041 if os.path.lexists(target):
3042 if os.path.lexists(target):
3042 dsremovunk.add(abs)
3043 dsremovunk.add(abs)
3043 dsremoved -= dsremovunk
3044 dsremoved -= dsremovunk
3044
3045
3045 # action to be actually performed by revert
3046 # action to be actually performed by revert
3046 # (<list of file>, message>) tuple
3047 # (<list of file>, message>) tuple
3047 actions = {'revert': ([], _('reverting %s\n')),
3048 actions = {'revert': ([], _('reverting %s\n')),
3048 'add': ([], _('adding %s\n')),
3049 'add': ([], _('adding %s\n')),
3049 'remove': ([], _('removing %s\n')),
3050 'remove': ([], _('removing %s\n')),
3050 'drop': ([], _('removing %s\n')),
3051 'drop': ([], _('removing %s\n')),
3051 'forget': ([], _('forgetting %s\n')),
3052 'forget': ([], _('forgetting %s\n')),
3052 'undelete': ([], _('undeleting %s\n')),
3053 'undelete': ([], _('undeleting %s\n')),
3053 'noop': (None, _('no changes needed to %s\n')),
3054 'noop': (None, _('no changes needed to %s\n')),
3054 'unknown': (None, _('file not managed: %s\n')),
3055 'unknown': (None, _('file not managed: %s\n')),
3055 }
3056 }
3056
3057
3057 # "constant" that convey the backup strategy.
3058 # "constant" that convey the backup strategy.
3058 # All set to `discard` if `no-backup` is set do avoid checking
3059 # All set to `discard` if `no-backup` is set do avoid checking
3059 # no_backup lower in the code.
3060 # no_backup lower in the code.
3060 # These values are ordered for comparison purposes
3061 # These values are ordered for comparison purposes
3061 backup = 2 # unconditionally do backup
3062 backup = 2 # unconditionally do backup
3062 check = 1 # check if the existing file differs from target
3063 check = 1 # check if the existing file differs from target
3063 discard = 0 # never do backup
3064 discard = 0 # never do backup
3064 if opts.get('no_backup'):
3065 if opts.get('no_backup'):
3065 backup = check = discard
3066 backup = check = discard
3066
3067
3067 backupanddel = actions['remove']
3068 backupanddel = actions['remove']
3068 if not opts.get('no_backup'):
3069 if not opts.get('no_backup'):
3069 backupanddel = actions['drop']
3070 backupanddel = actions['drop']
3070
3071
3071 disptable = (
3072 disptable = (
3072 # dispatch table:
3073 # dispatch table:
3073 # file state
3074 # file state
3074 # action
3075 # action
3075 # make backup
3076 # make backup
3076
3077
3077 ## Sets that results that will change file on disk
3078 ## Sets that results that will change file on disk
3078 # Modified compared to target, no local change
3079 # Modified compared to target, no local change
3079 (modified, actions['revert'], discard),
3080 (modified, actions['revert'], discard),
3080 # Modified compared to target, but local file is deleted
3081 # Modified compared to target, but local file is deleted
3081 (deleted, actions['revert'], discard),
3082 (deleted, actions['revert'], discard),
3082 # Modified compared to target, local change
3083 # Modified compared to target, local change
3083 (dsmodified, actions['revert'], backup),
3084 (dsmodified, actions['revert'], backup),
3084 # Added since target
3085 # Added since target
3085 (added, actions['remove'], discard),
3086 (added, actions['remove'], discard),
3086 # Added in working directory
3087 # Added in working directory
3087 (dsadded, actions['forget'], discard),
3088 (dsadded, actions['forget'], discard),
3088 # Added since target, have local modification
3089 # Added since target, have local modification
3089 (modadded, backupanddel, backup),
3090 (modadded, backupanddel, backup),
3090 # Added since target but file is missing in working directory
3091 # Added since target but file is missing in working directory
3091 (deladded, actions['drop'], discard),
3092 (deladded, actions['drop'], discard),
3092 # Removed since target, before working copy parent
3093 # Removed since target, before working copy parent
3093 (removed, actions['add'], discard),
3094 (removed, actions['add'], discard),
3094 # Same as `removed` but an unknown file exists at the same path
3095 # Same as `removed` but an unknown file exists at the same path
3095 (removunk, actions['add'], check),
3096 (removunk, actions['add'], check),
3096 # Removed since targe, marked as such in working copy parent
3097 # Removed since targe, marked as such in working copy parent
3097 (dsremoved, actions['undelete'], discard),
3098 (dsremoved, actions['undelete'], discard),
3098 # Same as `dsremoved` but an unknown file exists at the same path
3099 # Same as `dsremoved` but an unknown file exists at the same path
3099 (dsremovunk, actions['undelete'], check),
3100 (dsremovunk, actions['undelete'], check),
3100 ## the following sets does not result in any file changes
3101 ## the following sets does not result in any file changes
3101 # File with no modification
3102 # File with no modification
3102 (clean, actions['noop'], discard),
3103 (clean, actions['noop'], discard),
3103 # Existing file, not tracked anywhere
3104 # Existing file, not tracked anywhere
3104 (unknown, actions['unknown'], discard),
3105 (unknown, actions['unknown'], discard),
3105 )
3106 )
3106
3107
3107 for abs, (rel, exact) in sorted(names.items()):
3108 for abs, (rel, exact) in sorted(names.items()):
3108 # target file to be touch on disk (relative to cwd)
3109 # target file to be touch on disk (relative to cwd)
3109 target = repo.wjoin(abs)
3110 target = repo.wjoin(abs)
3110 # search the entry in the dispatch table.
3111 # search the entry in the dispatch table.
3111 # if the file is in any of these sets, it was touched in the working
3112 # if the file is in any of these sets, it was touched in the working
3112 # directory parent and we are sure it needs to be reverted.
3113 # directory parent and we are sure it needs to be reverted.
3113 for table, (xlist, msg), dobackup in disptable:
3114 for table, (xlist, msg), dobackup in disptable:
3114 if abs not in table:
3115 if abs not in table:
3115 continue
3116 continue
3116 if xlist is not None:
3117 if xlist is not None:
3117 xlist.append(abs)
3118 xlist.append(abs)
3118 if dobackup and (backup <= dobackup
3119 if dobackup and (backup <= dobackup
3119 or wctx[abs].cmp(ctx[abs])):
3120 or wctx[abs].cmp(ctx[abs])):
3120 bakname = scmutil.origpath(ui, repo, rel)
3121 bakname = scmutil.origpath(ui, repo, rel)
3121 ui.note(_('saving current version of %s as %s\n') %
3122 ui.note(_('saving current version of %s as %s\n') %
3122 (rel, bakname))
3123 (rel, bakname))
3123 if not opts.get('dry_run'):
3124 if not opts.get('dry_run'):
3124 if interactive:
3125 if interactive:
3125 util.copyfile(target, bakname)
3126 util.copyfile(target, bakname)
3126 else:
3127 else:
3127 util.rename(target, bakname)
3128 util.rename(target, bakname)
3128 if ui.verbose or not exact:
3129 if ui.verbose or not exact:
3129 if not isinstance(msg, basestring):
3130 if not isinstance(msg, basestring):
3130 msg = msg(abs)
3131 msg = msg(abs)
3131 ui.status(msg % rel)
3132 ui.status(msg % rel)
3132 elif exact:
3133 elif exact:
3133 ui.warn(msg % rel)
3134 ui.warn(msg % rel)
3134 break
3135 break
3135
3136
3136 if not opts.get('dry_run'):
3137 if not opts.get('dry_run'):
3137 needdata = ('revert', 'add', 'undelete')
3138 needdata = ('revert', 'add', 'undelete')
3138 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3139 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3139 _performrevert(repo, parents, ctx, actions, interactive)
3140 _performrevert(repo, parents, ctx, actions, interactive)
3140
3141
3141 if targetsubs:
3142 if targetsubs:
3142 # Revert the subrepos on the revert list
3143 # Revert the subrepos on the revert list
3143 for sub in targetsubs:
3144 for sub in targetsubs:
3144 try:
3145 try:
3145 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3146 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3146 except KeyError:
3147 except KeyError:
3147 raise error.Abort("subrepository '%s' does not exist in %s!"
3148 raise error.Abort("subrepository '%s' does not exist in %s!"
3148 % (sub, short(ctx.node())))
3149 % (sub, short(ctx.node())))
3149
3150
3150 def _revertprefetch(repo, ctx, *files):
3151 def _revertprefetch(repo, ctx, *files):
3151 """Let extension changing the storage layer prefetch content"""
3152 """Let extension changing the storage layer prefetch content"""
3152 pass
3153 pass
3153
3154
3154 def _performrevert(repo, parents, ctx, actions, interactive=False):
3155 def _performrevert(repo, parents, ctx, actions, interactive=False):
3155 """function that actually perform all the actions computed for revert
3156 """function that actually perform all the actions computed for revert
3156
3157
3157 This is an independent function to let extension to plug in and react to
3158 This is an independent function to let extension to plug in and react to
3158 the imminent revert.
3159 the imminent revert.
3159
3160
3160 Make sure you have the working directory locked when calling this function.
3161 Make sure you have the working directory locked when calling this function.
3161 """
3162 """
3162 parent, p2 = parents
3163 parent, p2 = parents
3163 node = ctx.node()
3164 node = ctx.node()
3164 excluded_files = []
3165 excluded_files = []
3165 matcher_opts = {"exclude": excluded_files}
3166 matcher_opts = {"exclude": excluded_files}
3166
3167
3167 def checkout(f):
3168 def checkout(f):
3168 fc = ctx[f]
3169 fc = ctx[f]
3169 repo.wwrite(f, fc.data(), fc.flags())
3170 repo.wwrite(f, fc.data(), fc.flags())
3170
3171
3171 audit_path = pathutil.pathauditor(repo.root)
3172 audit_path = pathutil.pathauditor(repo.root)
3172 for f in actions['forget'][0]:
3173 for f in actions['forget'][0]:
3173 if interactive:
3174 if interactive:
3174 choice = \
3175 choice = \
3175 repo.ui.promptchoice(
3176 repo.ui.promptchoice(
3176 _("forget added file %s (yn)?$$ &Yes $$ &No")
3177 _("forget added file %s (yn)?$$ &Yes $$ &No")
3177 % f)
3178 % f)
3178 if choice == 0:
3179 if choice == 0:
3179 repo.dirstate.drop(f)
3180 repo.dirstate.drop(f)
3180 else:
3181 else:
3181 excluded_files.append(repo.wjoin(f))
3182 excluded_files.append(repo.wjoin(f))
3182 else:
3183 else:
3183 repo.dirstate.drop(f)
3184 repo.dirstate.drop(f)
3184 for f in actions['remove'][0]:
3185 for f in actions['remove'][0]:
3185 audit_path(f)
3186 audit_path(f)
3186 try:
3187 try:
3187 util.unlinkpath(repo.wjoin(f))
3188 util.unlinkpath(repo.wjoin(f))
3188 except OSError:
3189 except OSError:
3189 pass
3190 pass
3190 repo.dirstate.remove(f)
3191 repo.dirstate.remove(f)
3191 for f in actions['drop'][0]:
3192 for f in actions['drop'][0]:
3192 audit_path(f)
3193 audit_path(f)
3193 repo.dirstate.remove(f)
3194 repo.dirstate.remove(f)
3194
3195
3195 normal = None
3196 normal = None
3196 if node == parent:
3197 if node == parent:
3197 # We're reverting to our parent. If possible, we'd like status
3198 # We're reverting to our parent. If possible, we'd like status
3198 # to report the file as clean. We have to use normallookup for
3199 # to report the file as clean. We have to use normallookup for
3199 # merges to avoid losing information about merged/dirty files.
3200 # merges to avoid losing information about merged/dirty files.
3200 if p2 != nullid:
3201 if p2 != nullid:
3201 normal = repo.dirstate.normallookup
3202 normal = repo.dirstate.normallookup
3202 else:
3203 else:
3203 normal = repo.dirstate.normal
3204 normal = repo.dirstate.normal
3204
3205
3205 newlyaddedandmodifiedfiles = set()
3206 newlyaddedandmodifiedfiles = set()
3206 if interactive:
3207 if interactive:
3207 # Prompt the user for changes to revert
3208 # Prompt the user for changes to revert
3208 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3209 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3209 m = scmutil.match(ctx, torevert, matcher_opts)
3210 m = scmutil.match(ctx, torevert, matcher_opts)
3210 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3211 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3211 diffopts.nodates = True
3212 diffopts.nodates = True
3212 diffopts.git = True
3213 diffopts.git = True
3213 reversehunks = repo.ui.configbool('experimental',
3214 reversehunks = repo.ui.configbool('experimental',
3214 'revertalternateinteractivemode',
3215 'revertalternateinteractivemode',
3215 True)
3216 True)
3216 if reversehunks:
3217 if reversehunks:
3217 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3218 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3218 else:
3219 else:
3219 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3220 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3220 originalchunks = patch.parsepatch(diff)
3221 originalchunks = patch.parsepatch(diff)
3221
3222
3222 try:
3223 try:
3223
3224
3224 chunks, opts = recordfilter(repo.ui, originalchunks)
3225 chunks, opts = recordfilter(repo.ui, originalchunks)
3225 if reversehunks:
3226 if reversehunks:
3226 chunks = patch.reversehunks(chunks)
3227 chunks = patch.reversehunks(chunks)
3227
3228
3228 except patch.PatchError as err:
3229 except patch.PatchError as err:
3229 raise error.Abort(_('error parsing patch: %s') % err)
3230 raise error.Abort(_('error parsing patch: %s') % err)
3230
3231
3231 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3232 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3232 # Apply changes
3233 # Apply changes
3233 fp = cStringIO.StringIO()
3234 fp = cStringIO.StringIO()
3234 for c in chunks:
3235 for c in chunks:
3235 c.write(fp)
3236 c.write(fp)
3236 dopatch = fp.tell()
3237 dopatch = fp.tell()
3237 fp.seek(0)
3238 fp.seek(0)
3238 if dopatch:
3239 if dopatch:
3239 try:
3240 try:
3240 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3241 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3241 except patch.PatchError as err:
3242 except patch.PatchError as err:
3242 raise error.Abort(str(err))
3243 raise error.Abort(str(err))
3243 del fp
3244 del fp
3244 else:
3245 else:
3245 for f in actions['revert'][0]:
3246 for f in actions['revert'][0]:
3246 checkout(f)
3247 checkout(f)
3247 if normal:
3248 if normal:
3248 normal(f)
3249 normal(f)
3249
3250
3250 for f in actions['add'][0]:
3251 for f in actions['add'][0]:
3251 # Don't checkout modified files, they are already created by the diff
3252 # Don't checkout modified files, they are already created by the diff
3252 if f not in newlyaddedandmodifiedfiles:
3253 if f not in newlyaddedandmodifiedfiles:
3253 checkout(f)
3254 checkout(f)
3254 repo.dirstate.add(f)
3255 repo.dirstate.add(f)
3255
3256
3256 normal = repo.dirstate.normallookup
3257 normal = repo.dirstate.normallookup
3257 if node == parent and p2 == nullid:
3258 if node == parent and p2 == nullid:
3258 normal = repo.dirstate.normal
3259 normal = repo.dirstate.normal
3259 for f in actions['undelete'][0]:
3260 for f in actions['undelete'][0]:
3260 checkout(f)
3261 checkout(f)
3261 normal(f)
3262 normal(f)
3262
3263
3263 copied = copies.pathcopies(repo[parent], ctx)
3264 copied = copies.pathcopies(repo[parent], ctx)
3264
3265
3265 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3266 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3266 if f in copied:
3267 if f in copied:
3267 repo.dirstate.copy(copied[f], f)
3268 repo.dirstate.copy(copied[f], f)
3268
3269
3269 def command(table):
3270 def command(table):
3270 """Returns a function object to be used as a decorator for making commands.
3271 """Returns a function object to be used as a decorator for making commands.
3271
3272
3272 This function receives a command table as its argument. The table should
3273 This function receives a command table as its argument. The table should
3273 be a dict.
3274 be a dict.
3274
3275
3275 The returned function can be used as a decorator for adding commands
3276 The returned function can be used as a decorator for adding commands
3276 to that command table. This function accepts multiple arguments to define
3277 to that command table. This function accepts multiple arguments to define
3277 a command.
3278 a command.
3278
3279
3279 The first argument is the command name.
3280 The first argument is the command name.
3280
3281
3281 The options argument is an iterable of tuples defining command arguments.
3282 The options argument is an iterable of tuples defining command arguments.
3282 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3283 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3283
3284
3284 The synopsis argument defines a short, one line summary of how to use the
3285 The synopsis argument defines a short, one line summary of how to use the
3285 command. This shows up in the help output.
3286 command. This shows up in the help output.
3286
3287
3287 The norepo argument defines whether the command does not require a
3288 The norepo argument defines whether the command does not require a
3288 local repository. Most commands operate against a repository, thus the
3289 local repository. Most commands operate against a repository, thus the
3289 default is False.
3290 default is False.
3290
3291
3291 The optionalrepo argument defines whether the command optionally requires
3292 The optionalrepo argument defines whether the command optionally requires
3292 a local repository.
3293 a local repository.
3293
3294
3294 The inferrepo argument defines whether to try to find a repository from the
3295 The inferrepo argument defines whether to try to find a repository from the
3295 command line arguments. If True, arguments will be examined for potential
3296 command line arguments. If True, arguments will be examined for potential
3296 repository locations. See ``findrepo()``. If a repository is found, it
3297 repository locations. See ``findrepo()``. If a repository is found, it
3297 will be used.
3298 will be used.
3298 """
3299 """
3299 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3300 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3300 inferrepo=False):
3301 inferrepo=False):
3301 def decorator(func):
3302 def decorator(func):
3302 func.norepo = norepo
3303 func.norepo = norepo
3303 func.optionalrepo = optionalrepo
3304 func.optionalrepo = optionalrepo
3304 func.inferrepo = inferrepo
3305 func.inferrepo = inferrepo
3305 if synopsis:
3306 if synopsis:
3306 table[name] = func, list(options), synopsis
3307 table[name] = func, list(options), synopsis
3307 else:
3308 else:
3308 table[name] = func, list(options)
3309 table[name] = func, list(options)
3309 return func
3310 return func
3310 return decorator
3311 return decorator
3311
3312
3312 return cmd
3313 return cmd
3313
3314
3314 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3315 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3315 # commands.outgoing. "missing" is "missing" of the result of
3316 # commands.outgoing. "missing" is "missing" of the result of
3316 # "findcommonoutgoing()"
3317 # "findcommonoutgoing()"
3317 outgoinghooks = util.hooks()
3318 outgoinghooks = util.hooks()
3318
3319
3319 # a list of (ui, repo) functions called by commands.summary
3320 # a list of (ui, repo) functions called by commands.summary
3320 summaryhooks = util.hooks()
3321 summaryhooks = util.hooks()
3321
3322
3322 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3323 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3323 #
3324 #
3324 # functions should return tuple of booleans below, if 'changes' is None:
3325 # functions should return tuple of booleans below, if 'changes' is None:
3325 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3326 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3326 #
3327 #
3327 # otherwise, 'changes' is a tuple of tuples below:
3328 # otherwise, 'changes' is a tuple of tuples below:
3328 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3329 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3329 # - (desturl, destbranch, destpeer, outgoing)
3330 # - (desturl, destbranch, destpeer, outgoing)
3330 summaryremotehooks = util.hooks()
3331 summaryremotehooks = util.hooks()
3331
3332
3332 # A list of state files kept by multistep operations like graft.
3333 # A list of state files kept by multistep operations like graft.
3333 # Since graft cannot be aborted, it is considered 'clearable' by update.
3334 # Since graft cannot be aborted, it is considered 'clearable' by update.
3334 # note: bisect is intentionally excluded
3335 # note: bisect is intentionally excluded
3335 # (state file, clearable, allowcommit, error, hint)
3336 # (state file, clearable, allowcommit, error, hint)
3336 unfinishedstates = [
3337 unfinishedstates = [
3337 ('graftstate', True, False, _('graft in progress'),
3338 ('graftstate', True, False, _('graft in progress'),
3338 _("use 'hg graft --continue' or 'hg update' to abort")),
3339 _("use 'hg graft --continue' or 'hg update' to abort")),
3339 ('updatestate', True, False, _('last update was interrupted'),
3340 ('updatestate', True, False, _('last update was interrupted'),
3340 _("use 'hg update' to get a consistent checkout"))
3341 _("use 'hg update' to get a consistent checkout"))
3341 ]
3342 ]
3342
3343
3343 def checkunfinished(repo, commit=False):
3344 def checkunfinished(repo, commit=False):
3344 '''Look for an unfinished multistep operation, like graft, and abort
3345 '''Look for an unfinished multistep operation, like graft, and abort
3345 if found. It's probably good to check this right before
3346 if found. It's probably good to check this right before
3346 bailifchanged().
3347 bailifchanged().
3347 '''
3348 '''
3348 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3349 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3349 if commit and allowcommit:
3350 if commit and allowcommit:
3350 continue
3351 continue
3351 if repo.vfs.exists(f):
3352 if repo.vfs.exists(f):
3352 raise error.Abort(msg, hint=hint)
3353 raise error.Abort(msg, hint=hint)
3353
3354
3354 def clearunfinished(repo):
3355 def clearunfinished(repo):
3355 '''Check for unfinished operations (as above), and clear the ones
3356 '''Check for unfinished operations (as above), and clear the ones
3356 that are clearable.
3357 that are clearable.
3357 '''
3358 '''
3358 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3359 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3359 if not clearable and repo.vfs.exists(f):
3360 if not clearable and repo.vfs.exists(f):
3360 raise error.Abort(msg, hint=hint)
3361 raise error.Abort(msg, hint=hint)
3361 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3362 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3362 if clearable and repo.vfs.exists(f):
3363 if clearable and repo.vfs.exists(f):
3363 util.unlink(repo.join(f))
3364 util.unlink(repo.join(f))
3364
3365
3365 afterresolvedstates = [
3366 afterresolvedstates = [
3366 ('graftstate',
3367 ('graftstate',
3367 _('hg graft --continue')),
3368 _('hg graft --continue')),
3368 ]
3369 ]
3369
3370
3370 def howtocontinue(repo):
3371 def howtocontinue(repo):
3371 '''Check for an unfinished operation and return the command to finish
3372 '''Check for an unfinished operation and return the command to finish
3372 it.
3373 it.
3373
3374
3374 afterresolvedstates tupples define a .hg/{file} and the corresponding
3375 afterresolvedstates tupples define a .hg/{file} and the corresponding
3375 command needed to finish it.
3376 command needed to finish it.
3376
3377
3377 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3378 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3378 a boolean.
3379 a boolean.
3379 '''
3380 '''
3380 contmsg = _("continue: %s")
3381 contmsg = _("continue: %s")
3381 for f, msg in afterresolvedstates:
3382 for f, msg in afterresolvedstates:
3382 if repo.vfs.exists(f):
3383 if repo.vfs.exists(f):
3383 return contmsg % msg, True
3384 return contmsg % msg, True
3384 workingctx = repo[None]
3385 workingctx = repo[None]
3385 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3386 dirty = any(repo.status()) or any(workingctx.sub(s).dirty()
3386 for s in workingctx.substate)
3387 for s in workingctx.substate)
3387 if dirty:
3388 if dirty:
3388 return contmsg % _("hg commit"), False
3389 return contmsg % _("hg commit"), False
3389 return None, None
3390 return None, None
3390
3391
3391 def checkafterresolved(repo):
3392 def checkafterresolved(repo):
3392 '''Inform the user about the next action after completing hg resolve
3393 '''Inform the user about the next action after completing hg resolve
3393
3394
3394 If there's a matching afterresolvedstates, howtocontinue will yield
3395 If there's a matching afterresolvedstates, howtocontinue will yield
3395 repo.ui.warn as the reporter.
3396 repo.ui.warn as the reporter.
3396
3397
3397 Otherwise, it will yield repo.ui.note.
3398 Otherwise, it will yield repo.ui.note.
3398 '''
3399 '''
3399 msg, warning = howtocontinue(repo)
3400 msg, warning = howtocontinue(repo)
3400 if msg is not None:
3401 if msg is not None:
3401 if warning:
3402 if warning:
3402 repo.ui.warn("%s\n" % msg)
3403 repo.ui.warn("%s\n" % msg)
3403 else:
3404 else:
3404 repo.ui.note("%s\n" % msg)
3405 repo.ui.note("%s\n" % msg)
3405
3406
3406 def wrongtooltocontinue(repo, task):
3407 def wrongtooltocontinue(repo, task):
3407 '''Raise an abort suggesting how to properly continue if there is an
3408 '''Raise an abort suggesting how to properly continue if there is an
3408 active task.
3409 active task.
3409
3410
3410 Uses howtocontinue() to find the active task.
3411 Uses howtocontinue() to find the active task.
3411
3412
3412 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3413 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3413 a hint.
3414 a hint.
3414 '''
3415 '''
3415 after = howtocontinue(repo)
3416 after = howtocontinue(repo)
3416 hint = None
3417 hint = None
3417 if after[1]:
3418 if after[1]:
3418 hint = after[0]
3419 hint = after[0]
3419 raise error.Abort(_('no %s in progress') % task, hint=hint)
3420 raise error.Abort(_('no %s in progress') % task, hint=hint)
3420
3421
3421 class dirstateguard(object):
3422 class dirstateguard(object):
3422 '''Restore dirstate at unexpected failure.
3423 '''Restore dirstate at unexpected failure.
3423
3424
3424 At the construction, this class does:
3425 At the construction, this class does:
3425
3426
3426 - write current ``repo.dirstate`` out, and
3427 - write current ``repo.dirstate`` out, and
3427 - save ``.hg/dirstate`` into the backup file
3428 - save ``.hg/dirstate`` into the backup file
3428
3429
3429 This restores ``.hg/dirstate`` from backup file, if ``release()``
3430 This restores ``.hg/dirstate`` from backup file, if ``release()``
3430 is invoked before ``close()``.
3431 is invoked before ``close()``.
3431
3432
3432 This just removes the backup file at ``close()`` before ``release()``.
3433 This just removes the backup file at ``close()`` before ``release()``.
3433 '''
3434 '''
3434
3435
3435 def __init__(self, repo, name):
3436 def __init__(self, repo, name):
3436 self._repo = repo
3437 self._repo = repo
3437 self._suffix = '.backup.%s.%d' % (name, id(self))
3438 self._suffix = '.backup.%s.%d' % (name, id(self))
3438 repo.dirstate._savebackup(repo.currenttransaction(), self._suffix)
3439 repo.dirstate._savebackup(repo.currenttransaction(), self._suffix)
3439 self._active = True
3440 self._active = True
3440 self._closed = False
3441 self._closed = False
3441
3442
3442 def __del__(self):
3443 def __del__(self):
3443 if self._active: # still active
3444 if self._active: # still active
3444 # this may occur, even if this class is used correctly:
3445 # this may occur, even if this class is used correctly:
3445 # for example, releasing other resources like transaction
3446 # for example, releasing other resources like transaction
3446 # may raise exception before ``dirstateguard.release`` in
3447 # may raise exception before ``dirstateguard.release`` in
3447 # ``release(tr, ....)``.
3448 # ``release(tr, ....)``.
3448 self._abort()
3449 self._abort()
3449
3450
3450 def close(self):
3451 def close(self):
3451 if not self._active: # already inactivated
3452 if not self._active: # already inactivated
3452 msg = (_("can't close already inactivated backup: dirstate%s")
3453 msg = (_("can't close already inactivated backup: dirstate%s")
3453 % self._suffix)
3454 % self._suffix)
3454 raise error.Abort(msg)
3455 raise error.Abort(msg)
3455
3456
3456 self._repo.dirstate._clearbackup(self._repo.currenttransaction(),
3457 self._repo.dirstate._clearbackup(self._repo.currenttransaction(),
3457 self._suffix)
3458 self._suffix)
3458 self._active = False
3459 self._active = False
3459 self._closed = True
3460 self._closed = True
3460
3461
3461 def _abort(self):
3462 def _abort(self):
3462 self._repo.dirstate._restorebackup(self._repo.currenttransaction(),
3463 self._repo.dirstate._restorebackup(self._repo.currenttransaction(),
3463 self._suffix)
3464 self._suffix)
3464 self._active = False
3465 self._active = False
3465
3466
3466 def release(self):
3467 def release(self):
3467 if not self._closed:
3468 if not self._closed:
3468 if not self._active: # already inactivated
3469 if not self._active: # already inactivated
3469 msg = (_("can't release already inactivated backup:"
3470 msg = (_("can't release already inactivated backup:"
3470 " dirstate%s")
3471 " dirstate%s")
3471 % self._suffix)
3472 % self._suffix)
3472 raise error.Abort(msg)
3473 raise error.Abort(msg)
3473 self._abort()
3474 self._abort()
@@ -1,2410 +1,2417
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71
71
72 $ commit()
72 $ commit()
73 > {
73 > {
74 > rev=$1
74 > rev=$1
75 > msg=$2
75 > msg=$2
76 > shift 2
76 > shift 2
77 > if [ "$#" -gt 0 ]; then
77 > if [ "$#" -gt 0 ]; then
78 > hg debugsetparents "$@"
78 > hg debugsetparents "$@"
79 > fi
79 > fi
80 > echo $rev > a
80 > echo $rev > a
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 > }
82 > }
83
83
84 $ cat > printrevset.py <<EOF
84 $ cat > printrevset.py <<EOF
85 > from mercurial import extensions, revset, commands, cmdutil
85 > from mercurial import extensions, revset, commands, cmdutil
86 >
86 >
87 > def uisetup(ui):
87 > def uisetup(ui):
88 > def printrevset(orig, ui, repo, *pats, **opts):
88 > def printrevset(orig, ui, repo, *pats, **opts):
89 > if opts.get('print_revset'):
89 > if opts.get('print_revset'):
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 > if expr:
91 > if expr:
92 > tree = revset.parse(expr)
92 > tree = revset.parse(expr)
93 > else:
93 > else:
94 > tree = []
94 > tree = []
95 > ui.write('%r\n' % (opts.get('rev', []),))
95 > ui.write('%r\n' % (opts.get('rev', []),))
96 > ui.write(revset.prettyformat(tree) + '\n')
96 > ui.write(revset.prettyformat(tree) + '\n')
97 > return 0
97 > return 0
98 > return orig(ui, repo, *pats, **opts)
98 > return orig(ui, repo, *pats, **opts)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 > entry[1].append(('', 'print-revset', False,
100 > entry[1].append(('', 'print-revset', False,
101 > 'print generated revset and exit (DEPRECATED)'))
101 > 'print generated revset and exit (DEPRECATED)'))
102 > EOF
102 > EOF
103
103
104 $ echo "[extensions]" >> $HGRCPATH
104 $ echo "[extensions]" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106
106
107 $ hg init repo
107 $ hg init repo
108 $ cd repo
108 $ cd repo
109
109
110 Empty repo:
110 Empty repo:
111
111
112 $ hg log -G
112 $ hg log -G
113
113
114
114
115 Building DAG:
115 Building DAG:
116
116
117 $ commit 0 "root"
117 $ commit 0 "root"
118 $ commit 1 "collapse" 0
118 $ commit 1 "collapse" 0
119 $ commit 2 "collapse" 1
119 $ commit 2 "collapse" 1
120 $ commit 3 "collapse" 2
120 $ commit 3 "collapse" 2
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 $ commit 5 "expand" 3 4
122 $ commit 5 "expand" 3 4
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 $ commit 7 "expand" 2 5
124 $ commit 7 "expand" 2 5
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 $ commit 9 "expand" 7 8
126 $ commit 9 "expand" 7 8
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 $ commit 11 "expand" 6 10
128 $ commit 11 "expand" 6 10
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 $ commit 13 "expand" 9 11
130 $ commit 13 "expand" 9 11
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 $ commit 15 "expand" 13 14
132 $ commit 15 "expand" 13 14
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 $ commit 17 "expand" 12 16
134 $ commit 17 "expand" 12 16
135 $ commit 18 "merge two known; two far left" 1 15
135 $ commit 18 "merge two known; two far left" 1 15
136 $ commit 19 "expand" 15 17
136 $ commit 19 "expand" 15 17
137 $ commit 20 "merge two known; two far right" 0 18
137 $ commit 20 "merge two known; two far right" 0 18
138 $ commit 21 "expand" 19 20
138 $ commit 21 "expand" 19 20
139 $ commit 22 "merge two known; one far left, one far right" 18 21
139 $ commit 22 "merge two known; one far left, one far right" 18 21
140 $ commit 23 "merge one known; immediate left" 1 22
140 $ commit 23 "merge one known; immediate left" 1 22
141 $ commit 24 "merge one known; immediate right" 0 23
141 $ commit 24 "merge one known; immediate right" 0 23
142 $ commit 25 "merge one known; far left" 21 24
142 $ commit 25 "merge one known; far left" 21 24
143 $ commit 26 "merge one known; far right" 18 25
143 $ commit 26 "merge one known; far right" 18 25
144 $ commit 27 "collapse" 21
144 $ commit 27 "collapse" 21
145 $ commit 28 "merge zero known" 1 26
145 $ commit 28 "merge zero known" 1 26
146 $ commit 29 "regular commit" 0
146 $ commit 29 "regular commit" 0
147 $ commit 30 "expand" 28 29
147 $ commit 30 "expand" 28 29
148 $ commit 31 "expand" 21 30
148 $ commit 31 "expand" 21 30
149 $ commit 32 "expand" 27 31
149 $ commit 32 "expand" 27 31
150 $ commit 33 "head" 18
150 $ commit 33 "head" 18
151 $ commit 34 "head" 32
151 $ commit 34 "head" 32
152
152
153
153
154 $ hg log -G -q
154 $ hg log -G -q
155 @ 34:fea3ac5810e0
155 @ 34:fea3ac5810e0
156 |
156 |
157 | o 33:68608f5145f9
157 | o 33:68608f5145f9
158 | |
158 | |
159 o | 32:d06dffa21a31
159 o | 32:d06dffa21a31
160 |\ \
160 |\ \
161 | o \ 31:621d83e11f67
161 | o \ 31:621d83e11f67
162 | |\ \
162 | |\ \
163 | | o \ 30:6e11cd4b648f
163 | | o \ 30:6e11cd4b648f
164 | | |\ \
164 | | |\ \
165 | | | o | 29:cd9bb2be7593
165 | | | o | 29:cd9bb2be7593
166 | | | | |
166 | | | | |
167 | | o | | 28:44ecd0b9ae99
167 | | o | | 28:44ecd0b9ae99
168 | | |\ \ \
168 | | |\ \ \
169 o | | | | | 27:886ed638191b
169 o | | | | | 27:886ed638191b
170 |/ / / / /
170 |/ / / / /
171 | | o---+ 26:7f25b6c2f0b9
171 | | o---+ 26:7f25b6c2f0b9
172 | | | | |
172 | | | | |
173 +---o | | 25:91da8ed57247
173 +---o | | 25:91da8ed57247
174 | | | | |
174 | | | | |
175 | | o | | 24:a9c19a3d96b7
175 | | o | | 24:a9c19a3d96b7
176 | | |\| |
176 | | |\| |
177 | | o | | 23:a01cddf0766d
177 | | o | | 23:a01cddf0766d
178 | |/| | |
178 | |/| | |
179 +---o---+ 22:e0d9cccacb5d
179 +---o---+ 22:e0d9cccacb5d
180 | | / /
180 | | / /
181 o | | | 21:d42a756af44d
181 o | | | 21:d42a756af44d
182 |\ \ \ \
182 |\ \ \ \
183 | o---+-+ 20:d30ed6450e32
183 | o---+-+ 20:d30ed6450e32
184 | / / /
184 | / / /
185 o | | | 19:31ddc2c1573b
185 o | | | 19:31ddc2c1573b
186 |\ \ \ \
186 |\ \ \ \
187 +---+---o 18:1aa84d96232a
187 +---+---o 18:1aa84d96232a
188 | | | |
188 | | | |
189 | o | | 17:44765d7c06e0
189 | o | | 17:44765d7c06e0
190 | |\ \ \
190 | |\ \ \
191 | | o---+ 16:3677d192927d
191 | | o---+ 16:3677d192927d
192 | | |/ /
192 | | |/ /
193 o | | | 15:1dda3f72782d
193 o | | | 15:1dda3f72782d
194 |\ \ \ \
194 |\ \ \ \
195 | o-----+ 14:8eac370358ef
195 | o-----+ 14:8eac370358ef
196 | |/ / /
196 | |/ / /
197 o | | | 13:22d8966a97e3
197 o | | | 13:22d8966a97e3
198 |\ \ \ \
198 |\ \ \ \
199 +---o | | 12:86b91144a6e9
199 +---o | | 12:86b91144a6e9
200 | | |/ /
200 | | |/ /
201 | o | | 11:832d76e6bdf2
201 | o | | 11:832d76e6bdf2
202 | |\ \ \
202 | |\ \ \
203 | | o---+ 10:74c64d036d72
203 | | o---+ 10:74c64d036d72
204 | |/ / /
204 | |/ / /
205 o | | | 9:7010c0af0a35
205 o | | | 9:7010c0af0a35
206 |\ \ \ \
206 |\ \ \ \
207 | o-----+ 8:7a0b11f71937
207 | o-----+ 8:7a0b11f71937
208 |/ / / /
208 |/ / / /
209 o | | | 7:b632bb1b1224
209 o | | | 7:b632bb1b1224
210 |\ \ \ \
210 |\ \ \ \
211 +---o | | 6:b105a072e251
211 +---o | | 6:b105a072e251
212 | |/ / /
212 | |/ / /
213 | o | | 5:4409d547b708
213 | o | | 5:4409d547b708
214 | |\ \ \
214 | |\ \ \
215 | | o | | 4:26a8bac39d9f
215 | | o | | 4:26a8bac39d9f
216 | |/|/ /
216 | |/|/ /
217 | o / / 3:27eef8ed80b4
217 | o / / 3:27eef8ed80b4
218 |/ / /
218 |/ / /
219 o / / 2:3d9a33b8d1e1
219 o / / 2:3d9a33b8d1e1
220 |/ /
220 |/ /
221 o / 1:6db2ef61d156
221 o / 1:6db2ef61d156
222 |/
222 |/
223 o 0:e6eb3150255d
223 o 0:e6eb3150255d
224
224
225
225
226 $ hg log -G
226 $ hg log -G
227 @ changeset: 34:fea3ac5810e0
227 @ changeset: 34:fea3ac5810e0
228 | tag: tip
228 | tag: tip
229 | parent: 32:d06dffa21a31
229 | parent: 32:d06dffa21a31
230 | user: test
230 | user: test
231 | date: Thu Jan 01 00:00:34 1970 +0000
231 | date: Thu Jan 01 00:00:34 1970 +0000
232 | summary: (34) head
232 | summary: (34) head
233 |
233 |
234 | o changeset: 33:68608f5145f9
234 | o changeset: 33:68608f5145f9
235 | | parent: 18:1aa84d96232a
235 | | parent: 18:1aa84d96232a
236 | | user: test
236 | | user: test
237 | | date: Thu Jan 01 00:00:33 1970 +0000
237 | | date: Thu Jan 01 00:00:33 1970 +0000
238 | | summary: (33) head
238 | | summary: (33) head
239 | |
239 | |
240 o | changeset: 32:d06dffa21a31
240 o | changeset: 32:d06dffa21a31
241 |\ \ parent: 27:886ed638191b
241 |\ \ parent: 27:886ed638191b
242 | | | parent: 31:621d83e11f67
242 | | | parent: 31:621d83e11f67
243 | | | user: test
243 | | | user: test
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
244 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 | | | summary: (32) expand
245 | | | summary: (32) expand
246 | | |
246 | | |
247 | o | changeset: 31:621d83e11f67
247 | o | changeset: 31:621d83e11f67
248 | |\ \ parent: 21:d42a756af44d
248 | |\ \ parent: 21:d42a756af44d
249 | | | | parent: 30:6e11cd4b648f
249 | | | | parent: 30:6e11cd4b648f
250 | | | | user: test
250 | | | | user: test
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 | | | | summary: (31) expand
252 | | | | summary: (31) expand
253 | | | |
253 | | | |
254 | | o | changeset: 30:6e11cd4b648f
254 | | o | changeset: 30:6e11cd4b648f
255 | | |\ \ parent: 28:44ecd0b9ae99
255 | | |\ \ parent: 28:44ecd0b9ae99
256 | | | | | parent: 29:cd9bb2be7593
256 | | | | | parent: 29:cd9bb2be7593
257 | | | | | user: test
257 | | | | | user: test
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 | | | | | summary: (30) expand
259 | | | | | summary: (30) expand
260 | | | | |
260 | | | | |
261 | | | o | changeset: 29:cd9bb2be7593
261 | | | o | changeset: 29:cd9bb2be7593
262 | | | | | parent: 0:e6eb3150255d
262 | | | | | parent: 0:e6eb3150255d
263 | | | | | user: test
263 | | | | | user: test
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 | | | | | summary: (29) regular commit
265 | | | | | summary: (29) regular commit
266 | | | | |
266 | | | | |
267 | | o | | changeset: 28:44ecd0b9ae99
267 | | o | | changeset: 28:44ecd0b9ae99
268 | | |\ \ \ parent: 1:6db2ef61d156
268 | | |\ \ \ parent: 1:6db2ef61d156
269 | | | | | | parent: 26:7f25b6c2f0b9
269 | | | | | | parent: 26:7f25b6c2f0b9
270 | | | | | | user: test
270 | | | | | | user: test
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 | | | | | | summary: (28) merge zero known
272 | | | | | | summary: (28) merge zero known
273 | | | | | |
273 | | | | | |
274 o | | | | | changeset: 27:886ed638191b
274 o | | | | | changeset: 27:886ed638191b
275 |/ / / / / parent: 21:d42a756af44d
275 |/ / / / / parent: 21:d42a756af44d
276 | | | | | user: test
276 | | | | | user: test
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 | | | | | summary: (27) collapse
278 | | | | | summary: (27) collapse
279 | | | | |
279 | | | | |
280 | | o---+ changeset: 26:7f25b6c2f0b9
280 | | o---+ changeset: 26:7f25b6c2f0b9
281 | | | | | parent: 18:1aa84d96232a
281 | | | | | parent: 18:1aa84d96232a
282 | | | | | parent: 25:91da8ed57247
282 | | | | | parent: 25:91da8ed57247
283 | | | | | user: test
283 | | | | | user: test
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 | | | | | summary: (26) merge one known; far right
285 | | | | | summary: (26) merge one known; far right
286 | | | | |
286 | | | | |
287 +---o | | changeset: 25:91da8ed57247
287 +---o | | changeset: 25:91da8ed57247
288 | | | | | parent: 21:d42a756af44d
288 | | | | | parent: 21:d42a756af44d
289 | | | | | parent: 24:a9c19a3d96b7
289 | | | | | parent: 24:a9c19a3d96b7
290 | | | | | user: test
290 | | | | | user: test
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 | | | | | summary: (25) merge one known; far left
292 | | | | | summary: (25) merge one known; far left
293 | | | | |
293 | | | | |
294 | | o | | changeset: 24:a9c19a3d96b7
294 | | o | | changeset: 24:a9c19a3d96b7
295 | | |\| | parent: 0:e6eb3150255d
295 | | |\| | parent: 0:e6eb3150255d
296 | | | | | parent: 23:a01cddf0766d
296 | | | | | parent: 23:a01cddf0766d
297 | | | | | user: test
297 | | | | | user: test
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 | | | | | summary: (24) merge one known; immediate right
299 | | | | | summary: (24) merge one known; immediate right
300 | | | | |
300 | | | | |
301 | | o | | changeset: 23:a01cddf0766d
301 | | o | | changeset: 23:a01cddf0766d
302 | |/| | | parent: 1:6db2ef61d156
302 | |/| | | parent: 1:6db2ef61d156
303 | | | | | parent: 22:e0d9cccacb5d
303 | | | | | parent: 22:e0d9cccacb5d
304 | | | | | user: test
304 | | | | | user: test
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 | | | | | summary: (23) merge one known; immediate left
306 | | | | | summary: (23) merge one known; immediate left
307 | | | | |
307 | | | | |
308 +---o---+ changeset: 22:e0d9cccacb5d
308 +---o---+ changeset: 22:e0d9cccacb5d
309 | | | | parent: 18:1aa84d96232a
309 | | | | parent: 18:1aa84d96232a
310 | | / / parent: 21:d42a756af44d
310 | | / / parent: 21:d42a756af44d
311 | | | | user: test
311 | | | | user: test
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 | | | | summary: (22) merge two known; one far left, one far right
313 | | | | summary: (22) merge two known; one far left, one far right
314 | | | |
314 | | | |
315 o | | | changeset: 21:d42a756af44d
315 o | | | changeset: 21:d42a756af44d
316 |\ \ \ \ parent: 19:31ddc2c1573b
316 |\ \ \ \ parent: 19:31ddc2c1573b
317 | | | | | parent: 20:d30ed6450e32
317 | | | | | parent: 20:d30ed6450e32
318 | | | | | user: test
318 | | | | | user: test
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 | | | | | summary: (21) expand
320 | | | | | summary: (21) expand
321 | | | | |
321 | | | | |
322 | o---+-+ changeset: 20:d30ed6450e32
322 | o---+-+ changeset: 20:d30ed6450e32
323 | | | | parent: 0:e6eb3150255d
323 | | | | parent: 0:e6eb3150255d
324 | / / / parent: 18:1aa84d96232a
324 | / / / parent: 18:1aa84d96232a
325 | | | | user: test
325 | | | | user: test
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 | | | | summary: (20) merge two known; two far right
327 | | | | summary: (20) merge two known; two far right
328 | | | |
328 | | | |
329 o | | | changeset: 19:31ddc2c1573b
329 o | | | changeset: 19:31ddc2c1573b
330 |\ \ \ \ parent: 15:1dda3f72782d
330 |\ \ \ \ parent: 15:1dda3f72782d
331 | | | | | parent: 17:44765d7c06e0
331 | | | | | parent: 17:44765d7c06e0
332 | | | | | user: test
332 | | | | | user: test
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 | | | | | summary: (19) expand
334 | | | | | summary: (19) expand
335 | | | | |
335 | | | | |
336 +---+---o changeset: 18:1aa84d96232a
336 +---+---o changeset: 18:1aa84d96232a
337 | | | | parent: 1:6db2ef61d156
337 | | | | parent: 1:6db2ef61d156
338 | | | | parent: 15:1dda3f72782d
338 | | | | parent: 15:1dda3f72782d
339 | | | | user: test
339 | | | | user: test
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 | | | | summary: (18) merge two known; two far left
341 | | | | summary: (18) merge two known; two far left
342 | | | |
342 | | | |
343 | o | | changeset: 17:44765d7c06e0
343 | o | | changeset: 17:44765d7c06e0
344 | |\ \ \ parent: 12:86b91144a6e9
344 | |\ \ \ parent: 12:86b91144a6e9
345 | | | | | parent: 16:3677d192927d
345 | | | | | parent: 16:3677d192927d
346 | | | | | user: test
346 | | | | | user: test
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 | | | | | summary: (17) expand
348 | | | | | summary: (17) expand
349 | | | | |
349 | | | | |
350 | | o---+ changeset: 16:3677d192927d
350 | | o---+ changeset: 16:3677d192927d
351 | | | | | parent: 0:e6eb3150255d
351 | | | | | parent: 0:e6eb3150255d
352 | | |/ / parent: 1:6db2ef61d156
352 | | |/ / parent: 1:6db2ef61d156
353 | | | | user: test
353 | | | | user: test
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 | | | | summary: (16) merge two known; one immediate right, one near right
355 | | | | summary: (16) merge two known; one immediate right, one near right
356 | | | |
356 | | | |
357 o | | | changeset: 15:1dda3f72782d
357 o | | | changeset: 15:1dda3f72782d
358 |\ \ \ \ parent: 13:22d8966a97e3
358 |\ \ \ \ parent: 13:22d8966a97e3
359 | | | | | parent: 14:8eac370358ef
359 | | | | | parent: 14:8eac370358ef
360 | | | | | user: test
360 | | | | | user: test
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 | | | | | summary: (15) expand
362 | | | | | summary: (15) expand
363 | | | | |
363 | | | | |
364 | o-----+ changeset: 14:8eac370358ef
364 | o-----+ changeset: 14:8eac370358ef
365 | | | | | parent: 0:e6eb3150255d
365 | | | | | parent: 0:e6eb3150255d
366 | |/ / / parent: 12:86b91144a6e9
366 | |/ / / parent: 12:86b91144a6e9
367 | | | | user: test
367 | | | | user: test
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 | | | | summary: (14) merge two known; one immediate right, one far right
369 | | | | summary: (14) merge two known; one immediate right, one far right
370 | | | |
370 | | | |
371 o | | | changeset: 13:22d8966a97e3
371 o | | | changeset: 13:22d8966a97e3
372 |\ \ \ \ parent: 9:7010c0af0a35
372 |\ \ \ \ parent: 9:7010c0af0a35
373 | | | | | parent: 11:832d76e6bdf2
373 | | | | | parent: 11:832d76e6bdf2
374 | | | | | user: test
374 | | | | | user: test
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 | | | | | summary: (13) expand
376 | | | | | summary: (13) expand
377 | | | | |
377 | | | | |
378 +---o | | changeset: 12:86b91144a6e9
378 +---o | | changeset: 12:86b91144a6e9
379 | | |/ / parent: 1:6db2ef61d156
379 | | |/ / parent: 1:6db2ef61d156
380 | | | | parent: 9:7010c0af0a35
380 | | | | parent: 9:7010c0af0a35
381 | | | | user: test
381 | | | | user: test
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 | | | | summary: (12) merge two known; one immediate right, one far left
383 | | | | summary: (12) merge two known; one immediate right, one far left
384 | | | |
384 | | | |
385 | o | | changeset: 11:832d76e6bdf2
385 | o | | changeset: 11:832d76e6bdf2
386 | |\ \ \ parent: 6:b105a072e251
386 | |\ \ \ parent: 6:b105a072e251
387 | | | | | parent: 10:74c64d036d72
387 | | | | | parent: 10:74c64d036d72
388 | | | | | user: test
388 | | | | | user: test
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 | | | | | summary: (11) expand
390 | | | | | summary: (11) expand
391 | | | | |
391 | | | | |
392 | | o---+ changeset: 10:74c64d036d72
392 | | o---+ changeset: 10:74c64d036d72
393 | | | | | parent: 0:e6eb3150255d
393 | | | | | parent: 0:e6eb3150255d
394 | |/ / / parent: 6:b105a072e251
394 | |/ / / parent: 6:b105a072e251
395 | | | | user: test
395 | | | | user: test
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 | | | | summary: (10) merge two known; one immediate left, one near right
397 | | | | summary: (10) merge two known; one immediate left, one near right
398 | | | |
398 | | | |
399 o | | | changeset: 9:7010c0af0a35
399 o | | | changeset: 9:7010c0af0a35
400 |\ \ \ \ parent: 7:b632bb1b1224
400 |\ \ \ \ parent: 7:b632bb1b1224
401 | | | | | parent: 8:7a0b11f71937
401 | | | | | parent: 8:7a0b11f71937
402 | | | | | user: test
402 | | | | | user: test
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 | | | | | summary: (9) expand
404 | | | | | summary: (9) expand
405 | | | | |
405 | | | | |
406 | o-----+ changeset: 8:7a0b11f71937
406 | o-----+ changeset: 8:7a0b11f71937
407 | | | | | parent: 0:e6eb3150255d
407 | | | | | parent: 0:e6eb3150255d
408 |/ / / / parent: 7:b632bb1b1224
408 |/ / / / parent: 7:b632bb1b1224
409 | | | | user: test
409 | | | | user: test
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 | | | | summary: (8) merge two known; one immediate left, one far right
411 | | | | summary: (8) merge two known; one immediate left, one far right
412 | | | |
412 | | | |
413 o | | | changeset: 7:b632bb1b1224
413 o | | | changeset: 7:b632bb1b1224
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
414 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 | | | | | parent: 5:4409d547b708
415 | | | | | parent: 5:4409d547b708
416 | | | | | user: test
416 | | | | | user: test
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 | | | | | summary: (7) expand
418 | | | | | summary: (7) expand
419 | | | | |
419 | | | | |
420 +---o | | changeset: 6:b105a072e251
420 +---o | | changeset: 6:b105a072e251
421 | |/ / / parent: 2:3d9a33b8d1e1
421 | |/ / / parent: 2:3d9a33b8d1e1
422 | | | | parent: 5:4409d547b708
422 | | | | parent: 5:4409d547b708
423 | | | | user: test
423 | | | | user: test
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 | | | | summary: (6) merge two known; one immediate left, one far left
425 | | | | summary: (6) merge two known; one immediate left, one far left
426 | | | |
426 | | | |
427 | o | | changeset: 5:4409d547b708
427 | o | | changeset: 5:4409d547b708
428 | |\ \ \ parent: 3:27eef8ed80b4
428 | |\ \ \ parent: 3:27eef8ed80b4
429 | | | | | parent: 4:26a8bac39d9f
429 | | | | | parent: 4:26a8bac39d9f
430 | | | | | user: test
430 | | | | | user: test
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 | | | | | summary: (5) expand
432 | | | | | summary: (5) expand
433 | | | | |
433 | | | | |
434 | | o | | changeset: 4:26a8bac39d9f
434 | | o | | changeset: 4:26a8bac39d9f
435 | |/|/ / parent: 1:6db2ef61d156
435 | |/|/ / parent: 1:6db2ef61d156
436 | | | | parent: 3:27eef8ed80b4
436 | | | | parent: 3:27eef8ed80b4
437 | | | | user: test
437 | | | | user: test
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
439 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 | | | |
440 | | | |
441 | o | | changeset: 3:27eef8ed80b4
441 | o | | changeset: 3:27eef8ed80b4
442 |/ / / user: test
442 |/ / / user: test
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
443 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 | | | summary: (3) collapse
444 | | | summary: (3) collapse
445 | | |
445 | | |
446 o | | changeset: 2:3d9a33b8d1e1
446 o | | changeset: 2:3d9a33b8d1e1
447 |/ / user: test
447 |/ / user: test
448 | | date: Thu Jan 01 00:00:02 1970 +0000
448 | | date: Thu Jan 01 00:00:02 1970 +0000
449 | | summary: (2) collapse
449 | | summary: (2) collapse
450 | |
450 | |
451 o | changeset: 1:6db2ef61d156
451 o | changeset: 1:6db2ef61d156
452 |/ user: test
452 |/ user: test
453 | date: Thu Jan 01 00:00:01 1970 +0000
453 | date: Thu Jan 01 00:00:01 1970 +0000
454 | summary: (1) collapse
454 | summary: (1) collapse
455 |
455 |
456 o changeset: 0:e6eb3150255d
456 o changeset: 0:e6eb3150255d
457 user: test
457 user: test
458 date: Thu Jan 01 00:00:00 1970 +0000
458 date: Thu Jan 01 00:00:00 1970 +0000
459 summary: (0) root
459 summary: (0) root
460
460
461
461
462 File glog:
462 File glog:
463 $ hg log -G a
463 $ hg log -G a
464 @ changeset: 34:fea3ac5810e0
464 @ changeset: 34:fea3ac5810e0
465 | tag: tip
465 | tag: tip
466 | parent: 32:d06dffa21a31
466 | parent: 32:d06dffa21a31
467 | user: test
467 | user: test
468 | date: Thu Jan 01 00:00:34 1970 +0000
468 | date: Thu Jan 01 00:00:34 1970 +0000
469 | summary: (34) head
469 | summary: (34) head
470 |
470 |
471 | o changeset: 33:68608f5145f9
471 | o changeset: 33:68608f5145f9
472 | | parent: 18:1aa84d96232a
472 | | parent: 18:1aa84d96232a
473 | | user: test
473 | | user: test
474 | | date: Thu Jan 01 00:00:33 1970 +0000
474 | | date: Thu Jan 01 00:00:33 1970 +0000
475 | | summary: (33) head
475 | | summary: (33) head
476 | |
476 | |
477 o | changeset: 32:d06dffa21a31
477 o | changeset: 32:d06dffa21a31
478 |\ \ parent: 27:886ed638191b
478 |\ \ parent: 27:886ed638191b
479 | | | parent: 31:621d83e11f67
479 | | | parent: 31:621d83e11f67
480 | | | user: test
480 | | | user: test
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
481 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 | | | summary: (32) expand
482 | | | summary: (32) expand
483 | | |
483 | | |
484 | o | changeset: 31:621d83e11f67
484 | o | changeset: 31:621d83e11f67
485 | |\ \ parent: 21:d42a756af44d
485 | |\ \ parent: 21:d42a756af44d
486 | | | | parent: 30:6e11cd4b648f
486 | | | | parent: 30:6e11cd4b648f
487 | | | | user: test
487 | | | | user: test
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 | | | | summary: (31) expand
489 | | | | summary: (31) expand
490 | | | |
490 | | | |
491 | | o | changeset: 30:6e11cd4b648f
491 | | o | changeset: 30:6e11cd4b648f
492 | | |\ \ parent: 28:44ecd0b9ae99
492 | | |\ \ parent: 28:44ecd0b9ae99
493 | | | | | parent: 29:cd9bb2be7593
493 | | | | | parent: 29:cd9bb2be7593
494 | | | | | user: test
494 | | | | | user: test
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 | | | | | summary: (30) expand
496 | | | | | summary: (30) expand
497 | | | | |
497 | | | | |
498 | | | o | changeset: 29:cd9bb2be7593
498 | | | o | changeset: 29:cd9bb2be7593
499 | | | | | parent: 0:e6eb3150255d
499 | | | | | parent: 0:e6eb3150255d
500 | | | | | user: test
500 | | | | | user: test
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 | | | | | summary: (29) regular commit
502 | | | | | summary: (29) regular commit
503 | | | | |
503 | | | | |
504 | | o | | changeset: 28:44ecd0b9ae99
504 | | o | | changeset: 28:44ecd0b9ae99
505 | | |\ \ \ parent: 1:6db2ef61d156
505 | | |\ \ \ parent: 1:6db2ef61d156
506 | | | | | | parent: 26:7f25b6c2f0b9
506 | | | | | | parent: 26:7f25b6c2f0b9
507 | | | | | | user: test
507 | | | | | | user: test
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 | | | | | | summary: (28) merge zero known
509 | | | | | | summary: (28) merge zero known
510 | | | | | |
510 | | | | | |
511 o | | | | | changeset: 27:886ed638191b
511 o | | | | | changeset: 27:886ed638191b
512 |/ / / / / parent: 21:d42a756af44d
512 |/ / / / / parent: 21:d42a756af44d
513 | | | | | user: test
513 | | | | | user: test
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 | | | | | summary: (27) collapse
515 | | | | | summary: (27) collapse
516 | | | | |
516 | | | | |
517 | | o---+ changeset: 26:7f25b6c2f0b9
517 | | o---+ changeset: 26:7f25b6c2f0b9
518 | | | | | parent: 18:1aa84d96232a
518 | | | | | parent: 18:1aa84d96232a
519 | | | | | parent: 25:91da8ed57247
519 | | | | | parent: 25:91da8ed57247
520 | | | | | user: test
520 | | | | | user: test
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 | | | | | summary: (26) merge one known; far right
522 | | | | | summary: (26) merge one known; far right
523 | | | | |
523 | | | | |
524 +---o | | changeset: 25:91da8ed57247
524 +---o | | changeset: 25:91da8ed57247
525 | | | | | parent: 21:d42a756af44d
525 | | | | | parent: 21:d42a756af44d
526 | | | | | parent: 24:a9c19a3d96b7
526 | | | | | parent: 24:a9c19a3d96b7
527 | | | | | user: test
527 | | | | | user: test
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 | | | | | summary: (25) merge one known; far left
529 | | | | | summary: (25) merge one known; far left
530 | | | | |
530 | | | | |
531 | | o | | changeset: 24:a9c19a3d96b7
531 | | o | | changeset: 24:a9c19a3d96b7
532 | | |\| | parent: 0:e6eb3150255d
532 | | |\| | parent: 0:e6eb3150255d
533 | | | | | parent: 23:a01cddf0766d
533 | | | | | parent: 23:a01cddf0766d
534 | | | | | user: test
534 | | | | | user: test
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 | | | | | summary: (24) merge one known; immediate right
536 | | | | | summary: (24) merge one known; immediate right
537 | | | | |
537 | | | | |
538 | | o | | changeset: 23:a01cddf0766d
538 | | o | | changeset: 23:a01cddf0766d
539 | |/| | | parent: 1:6db2ef61d156
539 | |/| | | parent: 1:6db2ef61d156
540 | | | | | parent: 22:e0d9cccacb5d
540 | | | | | parent: 22:e0d9cccacb5d
541 | | | | | user: test
541 | | | | | user: test
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 | | | | | summary: (23) merge one known; immediate left
543 | | | | | summary: (23) merge one known; immediate left
544 | | | | |
544 | | | | |
545 +---o---+ changeset: 22:e0d9cccacb5d
545 +---o---+ changeset: 22:e0d9cccacb5d
546 | | | | parent: 18:1aa84d96232a
546 | | | | parent: 18:1aa84d96232a
547 | | / / parent: 21:d42a756af44d
547 | | / / parent: 21:d42a756af44d
548 | | | | user: test
548 | | | | user: test
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 | | | | summary: (22) merge two known; one far left, one far right
550 | | | | summary: (22) merge two known; one far left, one far right
551 | | | |
551 | | | |
552 o | | | changeset: 21:d42a756af44d
552 o | | | changeset: 21:d42a756af44d
553 |\ \ \ \ parent: 19:31ddc2c1573b
553 |\ \ \ \ parent: 19:31ddc2c1573b
554 | | | | | parent: 20:d30ed6450e32
554 | | | | | parent: 20:d30ed6450e32
555 | | | | | user: test
555 | | | | | user: test
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 | | | | | summary: (21) expand
557 | | | | | summary: (21) expand
558 | | | | |
558 | | | | |
559 | o---+-+ changeset: 20:d30ed6450e32
559 | o---+-+ changeset: 20:d30ed6450e32
560 | | | | parent: 0:e6eb3150255d
560 | | | | parent: 0:e6eb3150255d
561 | / / / parent: 18:1aa84d96232a
561 | / / / parent: 18:1aa84d96232a
562 | | | | user: test
562 | | | | user: test
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 | | | | summary: (20) merge two known; two far right
564 | | | | summary: (20) merge two known; two far right
565 | | | |
565 | | | |
566 o | | | changeset: 19:31ddc2c1573b
566 o | | | changeset: 19:31ddc2c1573b
567 |\ \ \ \ parent: 15:1dda3f72782d
567 |\ \ \ \ parent: 15:1dda3f72782d
568 | | | | | parent: 17:44765d7c06e0
568 | | | | | parent: 17:44765d7c06e0
569 | | | | | user: test
569 | | | | | user: test
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 | | | | | summary: (19) expand
571 | | | | | summary: (19) expand
572 | | | | |
572 | | | | |
573 +---+---o changeset: 18:1aa84d96232a
573 +---+---o changeset: 18:1aa84d96232a
574 | | | | parent: 1:6db2ef61d156
574 | | | | parent: 1:6db2ef61d156
575 | | | | parent: 15:1dda3f72782d
575 | | | | parent: 15:1dda3f72782d
576 | | | | user: test
576 | | | | user: test
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 | | | | summary: (18) merge two known; two far left
578 | | | | summary: (18) merge two known; two far left
579 | | | |
579 | | | |
580 | o | | changeset: 17:44765d7c06e0
580 | o | | changeset: 17:44765d7c06e0
581 | |\ \ \ parent: 12:86b91144a6e9
581 | |\ \ \ parent: 12:86b91144a6e9
582 | | | | | parent: 16:3677d192927d
582 | | | | | parent: 16:3677d192927d
583 | | | | | user: test
583 | | | | | user: test
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 | | | | | summary: (17) expand
585 | | | | | summary: (17) expand
586 | | | | |
586 | | | | |
587 | | o---+ changeset: 16:3677d192927d
587 | | o---+ changeset: 16:3677d192927d
588 | | | | | parent: 0:e6eb3150255d
588 | | | | | parent: 0:e6eb3150255d
589 | | |/ / parent: 1:6db2ef61d156
589 | | |/ / parent: 1:6db2ef61d156
590 | | | | user: test
590 | | | | user: test
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 | | | | summary: (16) merge two known; one immediate right, one near right
592 | | | | summary: (16) merge two known; one immediate right, one near right
593 | | | |
593 | | | |
594 o | | | changeset: 15:1dda3f72782d
594 o | | | changeset: 15:1dda3f72782d
595 |\ \ \ \ parent: 13:22d8966a97e3
595 |\ \ \ \ parent: 13:22d8966a97e3
596 | | | | | parent: 14:8eac370358ef
596 | | | | | parent: 14:8eac370358ef
597 | | | | | user: test
597 | | | | | user: test
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 | | | | | summary: (15) expand
599 | | | | | summary: (15) expand
600 | | | | |
600 | | | | |
601 | o-----+ changeset: 14:8eac370358ef
601 | o-----+ changeset: 14:8eac370358ef
602 | | | | | parent: 0:e6eb3150255d
602 | | | | | parent: 0:e6eb3150255d
603 | |/ / / parent: 12:86b91144a6e9
603 | |/ / / parent: 12:86b91144a6e9
604 | | | | user: test
604 | | | | user: test
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 | | | | summary: (14) merge two known; one immediate right, one far right
606 | | | | summary: (14) merge two known; one immediate right, one far right
607 | | | |
607 | | | |
608 o | | | changeset: 13:22d8966a97e3
608 o | | | changeset: 13:22d8966a97e3
609 |\ \ \ \ parent: 9:7010c0af0a35
609 |\ \ \ \ parent: 9:7010c0af0a35
610 | | | | | parent: 11:832d76e6bdf2
610 | | | | | parent: 11:832d76e6bdf2
611 | | | | | user: test
611 | | | | | user: test
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 | | | | | summary: (13) expand
613 | | | | | summary: (13) expand
614 | | | | |
614 | | | | |
615 +---o | | changeset: 12:86b91144a6e9
615 +---o | | changeset: 12:86b91144a6e9
616 | | |/ / parent: 1:6db2ef61d156
616 | | |/ / parent: 1:6db2ef61d156
617 | | | | parent: 9:7010c0af0a35
617 | | | | parent: 9:7010c0af0a35
618 | | | | user: test
618 | | | | user: test
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 | | | | summary: (12) merge two known; one immediate right, one far left
620 | | | | summary: (12) merge two known; one immediate right, one far left
621 | | | |
621 | | | |
622 | o | | changeset: 11:832d76e6bdf2
622 | o | | changeset: 11:832d76e6bdf2
623 | |\ \ \ parent: 6:b105a072e251
623 | |\ \ \ parent: 6:b105a072e251
624 | | | | | parent: 10:74c64d036d72
624 | | | | | parent: 10:74c64d036d72
625 | | | | | user: test
625 | | | | | user: test
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 | | | | | summary: (11) expand
627 | | | | | summary: (11) expand
628 | | | | |
628 | | | | |
629 | | o---+ changeset: 10:74c64d036d72
629 | | o---+ changeset: 10:74c64d036d72
630 | | | | | parent: 0:e6eb3150255d
630 | | | | | parent: 0:e6eb3150255d
631 | |/ / / parent: 6:b105a072e251
631 | |/ / / parent: 6:b105a072e251
632 | | | | user: test
632 | | | | user: test
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 | | | | summary: (10) merge two known; one immediate left, one near right
634 | | | | summary: (10) merge two known; one immediate left, one near right
635 | | | |
635 | | | |
636 o | | | changeset: 9:7010c0af0a35
636 o | | | changeset: 9:7010c0af0a35
637 |\ \ \ \ parent: 7:b632bb1b1224
637 |\ \ \ \ parent: 7:b632bb1b1224
638 | | | | | parent: 8:7a0b11f71937
638 | | | | | parent: 8:7a0b11f71937
639 | | | | | user: test
639 | | | | | user: test
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 | | | | | summary: (9) expand
641 | | | | | summary: (9) expand
642 | | | | |
642 | | | | |
643 | o-----+ changeset: 8:7a0b11f71937
643 | o-----+ changeset: 8:7a0b11f71937
644 | | | | | parent: 0:e6eb3150255d
644 | | | | | parent: 0:e6eb3150255d
645 |/ / / / parent: 7:b632bb1b1224
645 |/ / / / parent: 7:b632bb1b1224
646 | | | | user: test
646 | | | | user: test
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 | | | | summary: (8) merge two known; one immediate left, one far right
648 | | | | summary: (8) merge two known; one immediate left, one far right
649 | | | |
649 | | | |
650 o | | | changeset: 7:b632bb1b1224
650 o | | | changeset: 7:b632bb1b1224
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
651 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 | | | | | parent: 5:4409d547b708
652 | | | | | parent: 5:4409d547b708
653 | | | | | user: test
653 | | | | | user: test
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 | | | | | summary: (7) expand
655 | | | | | summary: (7) expand
656 | | | | |
656 | | | | |
657 +---o | | changeset: 6:b105a072e251
657 +---o | | changeset: 6:b105a072e251
658 | |/ / / parent: 2:3d9a33b8d1e1
658 | |/ / / parent: 2:3d9a33b8d1e1
659 | | | | parent: 5:4409d547b708
659 | | | | parent: 5:4409d547b708
660 | | | | user: test
660 | | | | user: test
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 | | | | summary: (6) merge two known; one immediate left, one far left
662 | | | | summary: (6) merge two known; one immediate left, one far left
663 | | | |
663 | | | |
664 | o | | changeset: 5:4409d547b708
664 | o | | changeset: 5:4409d547b708
665 | |\ \ \ parent: 3:27eef8ed80b4
665 | |\ \ \ parent: 3:27eef8ed80b4
666 | | | | | parent: 4:26a8bac39d9f
666 | | | | | parent: 4:26a8bac39d9f
667 | | | | | user: test
667 | | | | | user: test
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 | | | | | summary: (5) expand
669 | | | | | summary: (5) expand
670 | | | | |
670 | | | | |
671 | | o | | changeset: 4:26a8bac39d9f
671 | | o | | changeset: 4:26a8bac39d9f
672 | |/|/ / parent: 1:6db2ef61d156
672 | |/|/ / parent: 1:6db2ef61d156
673 | | | | parent: 3:27eef8ed80b4
673 | | | | parent: 3:27eef8ed80b4
674 | | | | user: test
674 | | | | user: test
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
676 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 | | | |
677 | | | |
678 | o | | changeset: 3:27eef8ed80b4
678 | o | | changeset: 3:27eef8ed80b4
679 |/ / / user: test
679 |/ / / user: test
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
680 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 | | | summary: (3) collapse
681 | | | summary: (3) collapse
682 | | |
682 | | |
683 o | | changeset: 2:3d9a33b8d1e1
683 o | | changeset: 2:3d9a33b8d1e1
684 |/ / user: test
684 |/ / user: test
685 | | date: Thu Jan 01 00:00:02 1970 +0000
685 | | date: Thu Jan 01 00:00:02 1970 +0000
686 | | summary: (2) collapse
686 | | summary: (2) collapse
687 | |
687 | |
688 o | changeset: 1:6db2ef61d156
688 o | changeset: 1:6db2ef61d156
689 |/ user: test
689 |/ user: test
690 | date: Thu Jan 01 00:00:01 1970 +0000
690 | date: Thu Jan 01 00:00:01 1970 +0000
691 | summary: (1) collapse
691 | summary: (1) collapse
692 |
692 |
693 o changeset: 0:e6eb3150255d
693 o changeset: 0:e6eb3150255d
694 user: test
694 user: test
695 date: Thu Jan 01 00:00:00 1970 +0000
695 date: Thu Jan 01 00:00:00 1970 +0000
696 summary: (0) root
696 summary: (0) root
697
697
698
698
699 File glog per revset:
699 File glog per revset:
700
700
701 $ hg log -G -r 'file("a")'
701 $ hg log -G -r 'file("a")'
702 @ changeset: 34:fea3ac5810e0
702 @ changeset: 34:fea3ac5810e0
703 | tag: tip
703 | tag: tip
704 | parent: 32:d06dffa21a31
704 | parent: 32:d06dffa21a31
705 | user: test
705 | user: test
706 | date: Thu Jan 01 00:00:34 1970 +0000
706 | date: Thu Jan 01 00:00:34 1970 +0000
707 | summary: (34) head
707 | summary: (34) head
708 |
708 |
709 | o changeset: 33:68608f5145f9
709 | o changeset: 33:68608f5145f9
710 | | parent: 18:1aa84d96232a
710 | | parent: 18:1aa84d96232a
711 | | user: test
711 | | user: test
712 | | date: Thu Jan 01 00:00:33 1970 +0000
712 | | date: Thu Jan 01 00:00:33 1970 +0000
713 | | summary: (33) head
713 | | summary: (33) head
714 | |
714 | |
715 o | changeset: 32:d06dffa21a31
715 o | changeset: 32:d06dffa21a31
716 |\ \ parent: 27:886ed638191b
716 |\ \ parent: 27:886ed638191b
717 | | | parent: 31:621d83e11f67
717 | | | parent: 31:621d83e11f67
718 | | | user: test
718 | | | user: test
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
719 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 | | | summary: (32) expand
720 | | | summary: (32) expand
721 | | |
721 | | |
722 | o | changeset: 31:621d83e11f67
722 | o | changeset: 31:621d83e11f67
723 | |\ \ parent: 21:d42a756af44d
723 | |\ \ parent: 21:d42a756af44d
724 | | | | parent: 30:6e11cd4b648f
724 | | | | parent: 30:6e11cd4b648f
725 | | | | user: test
725 | | | | user: test
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 | | | | summary: (31) expand
727 | | | | summary: (31) expand
728 | | | |
728 | | | |
729 | | o | changeset: 30:6e11cd4b648f
729 | | o | changeset: 30:6e11cd4b648f
730 | | |\ \ parent: 28:44ecd0b9ae99
730 | | |\ \ parent: 28:44ecd0b9ae99
731 | | | | | parent: 29:cd9bb2be7593
731 | | | | | parent: 29:cd9bb2be7593
732 | | | | | user: test
732 | | | | | user: test
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 | | | | | summary: (30) expand
734 | | | | | summary: (30) expand
735 | | | | |
735 | | | | |
736 | | | o | changeset: 29:cd9bb2be7593
736 | | | o | changeset: 29:cd9bb2be7593
737 | | | | | parent: 0:e6eb3150255d
737 | | | | | parent: 0:e6eb3150255d
738 | | | | | user: test
738 | | | | | user: test
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 | | | | | summary: (29) regular commit
740 | | | | | summary: (29) regular commit
741 | | | | |
741 | | | | |
742 | | o | | changeset: 28:44ecd0b9ae99
742 | | o | | changeset: 28:44ecd0b9ae99
743 | | |\ \ \ parent: 1:6db2ef61d156
743 | | |\ \ \ parent: 1:6db2ef61d156
744 | | | | | | parent: 26:7f25b6c2f0b9
744 | | | | | | parent: 26:7f25b6c2f0b9
745 | | | | | | user: test
745 | | | | | | user: test
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 | | | | | | summary: (28) merge zero known
747 | | | | | | summary: (28) merge zero known
748 | | | | | |
748 | | | | | |
749 o | | | | | changeset: 27:886ed638191b
749 o | | | | | changeset: 27:886ed638191b
750 |/ / / / / parent: 21:d42a756af44d
750 |/ / / / / parent: 21:d42a756af44d
751 | | | | | user: test
751 | | | | | user: test
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 | | | | | summary: (27) collapse
753 | | | | | summary: (27) collapse
754 | | | | |
754 | | | | |
755 | | o---+ changeset: 26:7f25b6c2f0b9
755 | | o---+ changeset: 26:7f25b6c2f0b9
756 | | | | | parent: 18:1aa84d96232a
756 | | | | | parent: 18:1aa84d96232a
757 | | | | | parent: 25:91da8ed57247
757 | | | | | parent: 25:91da8ed57247
758 | | | | | user: test
758 | | | | | user: test
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 | | | | | summary: (26) merge one known; far right
760 | | | | | summary: (26) merge one known; far right
761 | | | | |
761 | | | | |
762 +---o | | changeset: 25:91da8ed57247
762 +---o | | changeset: 25:91da8ed57247
763 | | | | | parent: 21:d42a756af44d
763 | | | | | parent: 21:d42a756af44d
764 | | | | | parent: 24:a9c19a3d96b7
764 | | | | | parent: 24:a9c19a3d96b7
765 | | | | | user: test
765 | | | | | user: test
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 | | | | | summary: (25) merge one known; far left
767 | | | | | summary: (25) merge one known; far left
768 | | | | |
768 | | | | |
769 | | o | | changeset: 24:a9c19a3d96b7
769 | | o | | changeset: 24:a9c19a3d96b7
770 | | |\| | parent: 0:e6eb3150255d
770 | | |\| | parent: 0:e6eb3150255d
771 | | | | | parent: 23:a01cddf0766d
771 | | | | | parent: 23:a01cddf0766d
772 | | | | | user: test
772 | | | | | user: test
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 | | | | | summary: (24) merge one known; immediate right
774 | | | | | summary: (24) merge one known; immediate right
775 | | | | |
775 | | | | |
776 | | o | | changeset: 23:a01cddf0766d
776 | | o | | changeset: 23:a01cddf0766d
777 | |/| | | parent: 1:6db2ef61d156
777 | |/| | | parent: 1:6db2ef61d156
778 | | | | | parent: 22:e0d9cccacb5d
778 | | | | | parent: 22:e0d9cccacb5d
779 | | | | | user: test
779 | | | | | user: test
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 | | | | | summary: (23) merge one known; immediate left
781 | | | | | summary: (23) merge one known; immediate left
782 | | | | |
782 | | | | |
783 +---o---+ changeset: 22:e0d9cccacb5d
783 +---o---+ changeset: 22:e0d9cccacb5d
784 | | | | parent: 18:1aa84d96232a
784 | | | | parent: 18:1aa84d96232a
785 | | / / parent: 21:d42a756af44d
785 | | / / parent: 21:d42a756af44d
786 | | | | user: test
786 | | | | user: test
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 | | | | summary: (22) merge two known; one far left, one far right
788 | | | | summary: (22) merge two known; one far left, one far right
789 | | | |
789 | | | |
790 o | | | changeset: 21:d42a756af44d
790 o | | | changeset: 21:d42a756af44d
791 |\ \ \ \ parent: 19:31ddc2c1573b
791 |\ \ \ \ parent: 19:31ddc2c1573b
792 | | | | | parent: 20:d30ed6450e32
792 | | | | | parent: 20:d30ed6450e32
793 | | | | | user: test
793 | | | | | user: test
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 | | | | | summary: (21) expand
795 | | | | | summary: (21) expand
796 | | | | |
796 | | | | |
797 | o---+-+ changeset: 20:d30ed6450e32
797 | o---+-+ changeset: 20:d30ed6450e32
798 | | | | parent: 0:e6eb3150255d
798 | | | | parent: 0:e6eb3150255d
799 | / / / parent: 18:1aa84d96232a
799 | / / / parent: 18:1aa84d96232a
800 | | | | user: test
800 | | | | user: test
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 | | | | summary: (20) merge two known; two far right
802 | | | | summary: (20) merge two known; two far right
803 | | | |
803 | | | |
804 o | | | changeset: 19:31ddc2c1573b
804 o | | | changeset: 19:31ddc2c1573b
805 |\ \ \ \ parent: 15:1dda3f72782d
805 |\ \ \ \ parent: 15:1dda3f72782d
806 | | | | | parent: 17:44765d7c06e0
806 | | | | | parent: 17:44765d7c06e0
807 | | | | | user: test
807 | | | | | user: test
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 | | | | | summary: (19) expand
809 | | | | | summary: (19) expand
810 | | | | |
810 | | | | |
811 +---+---o changeset: 18:1aa84d96232a
811 +---+---o changeset: 18:1aa84d96232a
812 | | | | parent: 1:6db2ef61d156
812 | | | | parent: 1:6db2ef61d156
813 | | | | parent: 15:1dda3f72782d
813 | | | | parent: 15:1dda3f72782d
814 | | | | user: test
814 | | | | user: test
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 | | | | summary: (18) merge two known; two far left
816 | | | | summary: (18) merge two known; two far left
817 | | | |
817 | | | |
818 | o | | changeset: 17:44765d7c06e0
818 | o | | changeset: 17:44765d7c06e0
819 | |\ \ \ parent: 12:86b91144a6e9
819 | |\ \ \ parent: 12:86b91144a6e9
820 | | | | | parent: 16:3677d192927d
820 | | | | | parent: 16:3677d192927d
821 | | | | | user: test
821 | | | | | user: test
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 | | | | | summary: (17) expand
823 | | | | | summary: (17) expand
824 | | | | |
824 | | | | |
825 | | o---+ changeset: 16:3677d192927d
825 | | o---+ changeset: 16:3677d192927d
826 | | | | | parent: 0:e6eb3150255d
826 | | | | | parent: 0:e6eb3150255d
827 | | |/ / parent: 1:6db2ef61d156
827 | | |/ / parent: 1:6db2ef61d156
828 | | | | user: test
828 | | | | user: test
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 | | | | summary: (16) merge two known; one immediate right, one near right
830 | | | | summary: (16) merge two known; one immediate right, one near right
831 | | | |
831 | | | |
832 o | | | changeset: 15:1dda3f72782d
832 o | | | changeset: 15:1dda3f72782d
833 |\ \ \ \ parent: 13:22d8966a97e3
833 |\ \ \ \ parent: 13:22d8966a97e3
834 | | | | | parent: 14:8eac370358ef
834 | | | | | parent: 14:8eac370358ef
835 | | | | | user: test
835 | | | | | user: test
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 | | | | | summary: (15) expand
837 | | | | | summary: (15) expand
838 | | | | |
838 | | | | |
839 | o-----+ changeset: 14:8eac370358ef
839 | o-----+ changeset: 14:8eac370358ef
840 | | | | | parent: 0:e6eb3150255d
840 | | | | | parent: 0:e6eb3150255d
841 | |/ / / parent: 12:86b91144a6e9
841 | |/ / / parent: 12:86b91144a6e9
842 | | | | user: test
842 | | | | user: test
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 | | | | summary: (14) merge two known; one immediate right, one far right
844 | | | | summary: (14) merge two known; one immediate right, one far right
845 | | | |
845 | | | |
846 o | | | changeset: 13:22d8966a97e3
846 o | | | changeset: 13:22d8966a97e3
847 |\ \ \ \ parent: 9:7010c0af0a35
847 |\ \ \ \ parent: 9:7010c0af0a35
848 | | | | | parent: 11:832d76e6bdf2
848 | | | | | parent: 11:832d76e6bdf2
849 | | | | | user: test
849 | | | | | user: test
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 | | | | | summary: (13) expand
851 | | | | | summary: (13) expand
852 | | | | |
852 | | | | |
853 +---o | | changeset: 12:86b91144a6e9
853 +---o | | changeset: 12:86b91144a6e9
854 | | |/ / parent: 1:6db2ef61d156
854 | | |/ / parent: 1:6db2ef61d156
855 | | | | parent: 9:7010c0af0a35
855 | | | | parent: 9:7010c0af0a35
856 | | | | user: test
856 | | | | user: test
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 | | | | summary: (12) merge two known; one immediate right, one far left
858 | | | | summary: (12) merge two known; one immediate right, one far left
859 | | | |
859 | | | |
860 | o | | changeset: 11:832d76e6bdf2
860 | o | | changeset: 11:832d76e6bdf2
861 | |\ \ \ parent: 6:b105a072e251
861 | |\ \ \ parent: 6:b105a072e251
862 | | | | | parent: 10:74c64d036d72
862 | | | | | parent: 10:74c64d036d72
863 | | | | | user: test
863 | | | | | user: test
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 | | | | | summary: (11) expand
865 | | | | | summary: (11) expand
866 | | | | |
866 | | | | |
867 | | o---+ changeset: 10:74c64d036d72
867 | | o---+ changeset: 10:74c64d036d72
868 | | | | | parent: 0:e6eb3150255d
868 | | | | | parent: 0:e6eb3150255d
869 | |/ / / parent: 6:b105a072e251
869 | |/ / / parent: 6:b105a072e251
870 | | | | user: test
870 | | | | user: test
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 | | | | summary: (10) merge two known; one immediate left, one near right
872 | | | | summary: (10) merge two known; one immediate left, one near right
873 | | | |
873 | | | |
874 o | | | changeset: 9:7010c0af0a35
874 o | | | changeset: 9:7010c0af0a35
875 |\ \ \ \ parent: 7:b632bb1b1224
875 |\ \ \ \ parent: 7:b632bb1b1224
876 | | | | | parent: 8:7a0b11f71937
876 | | | | | parent: 8:7a0b11f71937
877 | | | | | user: test
877 | | | | | user: test
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 | | | | | summary: (9) expand
879 | | | | | summary: (9) expand
880 | | | | |
880 | | | | |
881 | o-----+ changeset: 8:7a0b11f71937
881 | o-----+ changeset: 8:7a0b11f71937
882 | | | | | parent: 0:e6eb3150255d
882 | | | | | parent: 0:e6eb3150255d
883 |/ / / / parent: 7:b632bb1b1224
883 |/ / / / parent: 7:b632bb1b1224
884 | | | | user: test
884 | | | | user: test
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 | | | | summary: (8) merge two known; one immediate left, one far right
886 | | | | summary: (8) merge two known; one immediate left, one far right
887 | | | |
887 | | | |
888 o | | | changeset: 7:b632bb1b1224
888 o | | | changeset: 7:b632bb1b1224
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
889 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 | | | | | parent: 5:4409d547b708
890 | | | | | parent: 5:4409d547b708
891 | | | | | user: test
891 | | | | | user: test
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 | | | | | summary: (7) expand
893 | | | | | summary: (7) expand
894 | | | | |
894 | | | | |
895 +---o | | changeset: 6:b105a072e251
895 +---o | | changeset: 6:b105a072e251
896 | |/ / / parent: 2:3d9a33b8d1e1
896 | |/ / / parent: 2:3d9a33b8d1e1
897 | | | | parent: 5:4409d547b708
897 | | | | parent: 5:4409d547b708
898 | | | | user: test
898 | | | | user: test
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 | | | | summary: (6) merge two known; one immediate left, one far left
900 | | | | summary: (6) merge two known; one immediate left, one far left
901 | | | |
901 | | | |
902 | o | | changeset: 5:4409d547b708
902 | o | | changeset: 5:4409d547b708
903 | |\ \ \ parent: 3:27eef8ed80b4
903 | |\ \ \ parent: 3:27eef8ed80b4
904 | | | | | parent: 4:26a8bac39d9f
904 | | | | | parent: 4:26a8bac39d9f
905 | | | | | user: test
905 | | | | | user: test
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 | | | | | summary: (5) expand
907 | | | | | summary: (5) expand
908 | | | | |
908 | | | | |
909 | | o | | changeset: 4:26a8bac39d9f
909 | | o | | changeset: 4:26a8bac39d9f
910 | |/|/ / parent: 1:6db2ef61d156
910 | |/|/ / parent: 1:6db2ef61d156
911 | | | | parent: 3:27eef8ed80b4
911 | | | | parent: 3:27eef8ed80b4
912 | | | | user: test
912 | | | | user: test
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
914 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 | | | |
915 | | | |
916 | o | | changeset: 3:27eef8ed80b4
916 | o | | changeset: 3:27eef8ed80b4
917 |/ / / user: test
917 |/ / / user: test
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
918 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 | | | summary: (3) collapse
919 | | | summary: (3) collapse
920 | | |
920 | | |
921 o | | changeset: 2:3d9a33b8d1e1
921 o | | changeset: 2:3d9a33b8d1e1
922 |/ / user: test
922 |/ / user: test
923 | | date: Thu Jan 01 00:00:02 1970 +0000
923 | | date: Thu Jan 01 00:00:02 1970 +0000
924 | | summary: (2) collapse
924 | | summary: (2) collapse
925 | |
925 | |
926 o | changeset: 1:6db2ef61d156
926 o | changeset: 1:6db2ef61d156
927 |/ user: test
927 |/ user: test
928 | date: Thu Jan 01 00:00:01 1970 +0000
928 | date: Thu Jan 01 00:00:01 1970 +0000
929 | summary: (1) collapse
929 | summary: (1) collapse
930 |
930 |
931 o changeset: 0:e6eb3150255d
931 o changeset: 0:e6eb3150255d
932 user: test
932 user: test
933 date: Thu Jan 01 00:00:00 1970 +0000
933 date: Thu Jan 01 00:00:00 1970 +0000
934 summary: (0) root
934 summary: (0) root
935
935
936
936
937
937
938 File glog per revset (only merges):
938 File glog per revset (only merges):
939
939
940 $ hg log -G -r 'file("a")' -m
940 $ hg log -G -r 'file("a")' -m
941 o changeset: 32:d06dffa21a31
941 o changeset: 32:d06dffa21a31
942 |\ parent: 27:886ed638191b
942 |\ parent: 27:886ed638191b
943 | | parent: 31:621d83e11f67
943 | | parent: 31:621d83e11f67
944 | | user: test
944 | | user: test
945 | | date: Thu Jan 01 00:00:32 1970 +0000
945 | | date: Thu Jan 01 00:00:32 1970 +0000
946 | | summary: (32) expand
946 | | summary: (32) expand
947 | |
947 | |
948 o | changeset: 31:621d83e11f67
948 o | changeset: 31:621d83e11f67
949 |\| parent: 21:d42a756af44d
949 |\| parent: 21:d42a756af44d
950 | | parent: 30:6e11cd4b648f
950 | | parent: 30:6e11cd4b648f
951 | | user: test
951 | | user: test
952 | | date: Thu Jan 01 00:00:31 1970 +0000
952 | | date: Thu Jan 01 00:00:31 1970 +0000
953 | | summary: (31) expand
953 | | summary: (31) expand
954 | |
954 | |
955 o | changeset: 30:6e11cd4b648f
955 o | changeset: 30:6e11cd4b648f
956 |\ \ parent: 28:44ecd0b9ae99
956 |\ \ parent: 28:44ecd0b9ae99
957 | | | parent: 29:cd9bb2be7593
957 | | | parent: 29:cd9bb2be7593
958 | | | user: test
958 | | | user: test
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
959 | | | date: Thu Jan 01 00:00:30 1970 +0000
960 | | | summary: (30) expand
960 | | | summary: (30) expand
961 | | |
961 | | |
962 o | | changeset: 28:44ecd0b9ae99
962 o | | changeset: 28:44ecd0b9ae99
963 |\ \ \ parent: 1:6db2ef61d156
963 |\ \ \ parent: 1:6db2ef61d156
964 | | | | parent: 26:7f25b6c2f0b9
964 | | | | parent: 26:7f25b6c2f0b9
965 | | | | user: test
965 | | | | user: test
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
966 | | | | date: Thu Jan 01 00:00:28 1970 +0000
967 | | | | summary: (28) merge zero known
967 | | | | summary: (28) merge zero known
968 | | | |
968 | | | |
969 o | | | changeset: 26:7f25b6c2f0b9
969 o | | | changeset: 26:7f25b6c2f0b9
970 |\ \ \ \ parent: 18:1aa84d96232a
970 |\ \ \ \ parent: 18:1aa84d96232a
971 | | | | | parent: 25:91da8ed57247
971 | | | | | parent: 25:91da8ed57247
972 | | | | | user: test
972 | | | | | user: test
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
973 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
974 | | | | | summary: (26) merge one known; far right
974 | | | | | summary: (26) merge one known; far right
975 | | | | |
975 | | | | |
976 | o-----+ changeset: 25:91da8ed57247
976 | o-----+ changeset: 25:91da8ed57247
977 | | | | | parent: 21:d42a756af44d
977 | | | | | parent: 21:d42a756af44d
978 | | | | | parent: 24:a9c19a3d96b7
978 | | | | | parent: 24:a9c19a3d96b7
979 | | | | | user: test
979 | | | | | user: test
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
980 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
981 | | | | | summary: (25) merge one known; far left
981 | | | | | summary: (25) merge one known; far left
982 | | | | |
982 | | | | |
983 | o | | | changeset: 24:a9c19a3d96b7
983 | o | | | changeset: 24:a9c19a3d96b7
984 | |\ \ \ \ parent: 0:e6eb3150255d
984 | |\ \ \ \ parent: 0:e6eb3150255d
985 | | | | | | parent: 23:a01cddf0766d
985 | | | | | | parent: 23:a01cddf0766d
986 | | | | | | user: test
986 | | | | | | user: test
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
987 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
988 | | | | | | summary: (24) merge one known; immediate right
988 | | | | | | summary: (24) merge one known; immediate right
989 | | | | | |
989 | | | | | |
990 | o---+ | | changeset: 23:a01cddf0766d
990 | o---+ | | changeset: 23:a01cddf0766d
991 | | | | | | parent: 1:6db2ef61d156
991 | | | | | | parent: 1:6db2ef61d156
992 | | | | | | parent: 22:e0d9cccacb5d
992 | | | | | | parent: 22:e0d9cccacb5d
993 | | | | | | user: test
993 | | | | | | user: test
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
994 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
995 | | | | | | summary: (23) merge one known; immediate left
995 | | | | | | summary: (23) merge one known; immediate left
996 | | | | | |
996 | | | | | |
997 | o-------+ changeset: 22:e0d9cccacb5d
997 | o-------+ changeset: 22:e0d9cccacb5d
998 | | | | | | parent: 18:1aa84d96232a
998 | | | | | | parent: 18:1aa84d96232a
999 |/ / / / / parent: 21:d42a756af44d
999 |/ / / / / parent: 21:d42a756af44d
1000 | | | | | user: test
1000 | | | | | user: test
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1001 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
1002 | | | | | summary: (22) merge two known; one far left, one far right
1002 | | | | | summary: (22) merge two known; one far left, one far right
1003 | | | | |
1003 | | | | |
1004 | | | | o changeset: 21:d42a756af44d
1004 | | | | o changeset: 21:d42a756af44d
1005 | | | | |\ parent: 19:31ddc2c1573b
1005 | | | | |\ parent: 19:31ddc2c1573b
1006 | | | | | | parent: 20:d30ed6450e32
1006 | | | | | | parent: 20:d30ed6450e32
1007 | | | | | | user: test
1007 | | | | | | user: test
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1008 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 | | | | | | summary: (21) expand
1009 | | | | | | summary: (21) expand
1010 | | | | | |
1010 | | | | | |
1011 +-+-------o changeset: 20:d30ed6450e32
1011 +-+-------o changeset: 20:d30ed6450e32
1012 | | | | | parent: 0:e6eb3150255d
1012 | | | | | parent: 0:e6eb3150255d
1013 | | | | | parent: 18:1aa84d96232a
1013 | | | | | parent: 18:1aa84d96232a
1014 | | | | | user: test
1014 | | | | | user: test
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1015 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
1016 | | | | | summary: (20) merge two known; two far right
1016 | | | | | summary: (20) merge two known; two far right
1017 | | | | |
1017 | | | | |
1018 | | | | o changeset: 19:31ddc2c1573b
1018 | | | | o changeset: 19:31ddc2c1573b
1019 | | | | |\ parent: 15:1dda3f72782d
1019 | | | | |\ parent: 15:1dda3f72782d
1020 | | | | | | parent: 17:44765d7c06e0
1020 | | | | | | parent: 17:44765d7c06e0
1021 | | | | | | user: test
1021 | | | | | | user: test
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1022 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 | | | | | | summary: (19) expand
1023 | | | | | | summary: (19) expand
1024 | | | | | |
1024 | | | | | |
1025 o---+---+ | changeset: 18:1aa84d96232a
1025 o---+---+ | changeset: 18:1aa84d96232a
1026 | | | | | parent: 1:6db2ef61d156
1026 | | | | | parent: 1:6db2ef61d156
1027 / / / / / parent: 15:1dda3f72782d
1027 / / / / / parent: 15:1dda3f72782d
1028 | | | | | user: test
1028 | | | | | user: test
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1029 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
1030 | | | | | summary: (18) merge two known; two far left
1030 | | | | | summary: (18) merge two known; two far left
1031 | | | | |
1031 | | | | |
1032 | | | | o changeset: 17:44765d7c06e0
1032 | | | | o changeset: 17:44765d7c06e0
1033 | | | | |\ parent: 12:86b91144a6e9
1033 | | | | |\ parent: 12:86b91144a6e9
1034 | | | | | | parent: 16:3677d192927d
1034 | | | | | | parent: 16:3677d192927d
1035 | | | | | | user: test
1035 | | | | | | user: test
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1036 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 | | | | | | summary: (17) expand
1037 | | | | | | summary: (17) expand
1038 | | | | | |
1038 | | | | | |
1039 +-+-------o changeset: 16:3677d192927d
1039 +-+-------o changeset: 16:3677d192927d
1040 | | | | | parent: 0:e6eb3150255d
1040 | | | | | parent: 0:e6eb3150255d
1041 | | | | | parent: 1:6db2ef61d156
1041 | | | | | parent: 1:6db2ef61d156
1042 | | | | | user: test
1042 | | | | | user: test
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1043 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1044 | | | | | summary: (16) merge two known; one immediate right, one near right
1045 | | | | |
1045 | | | | |
1046 | | | o | changeset: 15:1dda3f72782d
1046 | | | o | changeset: 15:1dda3f72782d
1047 | | | |\ \ parent: 13:22d8966a97e3
1047 | | | |\ \ parent: 13:22d8966a97e3
1048 | | | | | | parent: 14:8eac370358ef
1048 | | | | | | parent: 14:8eac370358ef
1049 | | | | | | user: test
1049 | | | | | | user: test
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1050 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 | | | | | | summary: (15) expand
1051 | | | | | | summary: (15) expand
1052 | | | | | |
1052 | | | | | |
1053 +-------o | changeset: 14:8eac370358ef
1053 +-------o | changeset: 14:8eac370358ef
1054 | | | | |/ parent: 0:e6eb3150255d
1054 | | | | |/ parent: 0:e6eb3150255d
1055 | | | | | parent: 12:86b91144a6e9
1055 | | | | | parent: 12:86b91144a6e9
1056 | | | | | user: test
1056 | | | | | user: test
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1057 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1058 | | | | | summary: (14) merge two known; one immediate right, one far right
1059 | | | | |
1059 | | | | |
1060 | | | o | changeset: 13:22d8966a97e3
1060 | | | o | changeset: 13:22d8966a97e3
1061 | | | |\ \ parent: 9:7010c0af0a35
1061 | | | |\ \ parent: 9:7010c0af0a35
1062 | | | | | | parent: 11:832d76e6bdf2
1062 | | | | | | parent: 11:832d76e6bdf2
1063 | | | | | | user: test
1063 | | | | | | user: test
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1064 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 | | | | | | summary: (13) expand
1065 | | | | | | summary: (13) expand
1066 | | | | | |
1066 | | | | | |
1067 | +---+---o changeset: 12:86b91144a6e9
1067 | +---+---o changeset: 12:86b91144a6e9
1068 | | | | | parent: 1:6db2ef61d156
1068 | | | | | parent: 1:6db2ef61d156
1069 | | | | | parent: 9:7010c0af0a35
1069 | | | | | parent: 9:7010c0af0a35
1070 | | | | | user: test
1070 | | | | | user: test
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1071 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1072 | | | | | summary: (12) merge two known; one immediate right, one far left
1073 | | | | |
1073 | | | | |
1074 | | | | o changeset: 11:832d76e6bdf2
1074 | | | | o changeset: 11:832d76e6bdf2
1075 | | | | |\ parent: 6:b105a072e251
1075 | | | | |\ parent: 6:b105a072e251
1076 | | | | | | parent: 10:74c64d036d72
1076 | | | | | | parent: 10:74c64d036d72
1077 | | | | | | user: test
1077 | | | | | | user: test
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1078 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 | | | | | | summary: (11) expand
1079 | | | | | | summary: (11) expand
1080 | | | | | |
1080 | | | | | |
1081 +---------o changeset: 10:74c64d036d72
1081 +---------o changeset: 10:74c64d036d72
1082 | | | | |/ parent: 0:e6eb3150255d
1082 | | | | |/ parent: 0:e6eb3150255d
1083 | | | | | parent: 6:b105a072e251
1083 | | | | | parent: 6:b105a072e251
1084 | | | | | user: test
1084 | | | | | user: test
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1085 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1086 | | | | | summary: (10) merge two known; one immediate left, one near right
1087 | | | | |
1087 | | | | |
1088 | | | o | changeset: 9:7010c0af0a35
1088 | | | o | changeset: 9:7010c0af0a35
1089 | | | |\ \ parent: 7:b632bb1b1224
1089 | | | |\ \ parent: 7:b632bb1b1224
1090 | | | | | | parent: 8:7a0b11f71937
1090 | | | | | | parent: 8:7a0b11f71937
1091 | | | | | | user: test
1091 | | | | | | user: test
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1092 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 | | | | | | summary: (9) expand
1093 | | | | | | summary: (9) expand
1094 | | | | | |
1094 | | | | | |
1095 +-------o | changeset: 8:7a0b11f71937
1095 +-------o | changeset: 8:7a0b11f71937
1096 | | | |/ / parent: 0:e6eb3150255d
1096 | | | |/ / parent: 0:e6eb3150255d
1097 | | | | | parent: 7:b632bb1b1224
1097 | | | | | parent: 7:b632bb1b1224
1098 | | | | | user: test
1098 | | | | | user: test
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1099 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1100 | | | | | summary: (8) merge two known; one immediate left, one far right
1101 | | | | |
1101 | | | | |
1102 | | | o | changeset: 7:b632bb1b1224
1102 | | | o | changeset: 7:b632bb1b1224
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1103 | | | |\ \ parent: 2:3d9a33b8d1e1
1104 | | | | | | parent: 5:4409d547b708
1104 | | | | | | parent: 5:4409d547b708
1105 | | | | | | user: test
1105 | | | | | | user: test
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1106 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
1107 | | | | | | summary: (7) expand
1107 | | | | | | summary: (7) expand
1108 | | | | | |
1108 | | | | | |
1109 | | | +---o changeset: 6:b105a072e251
1109 | | | +---o changeset: 6:b105a072e251
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1110 | | | | |/ parent: 2:3d9a33b8d1e1
1111 | | | | | parent: 5:4409d547b708
1111 | | | | | parent: 5:4409d547b708
1112 | | | | | user: test
1112 | | | | | user: test
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1113 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1114 | | | | | summary: (6) merge two known; one immediate left, one far left
1115 | | | | |
1115 | | | | |
1116 | | | o | changeset: 5:4409d547b708
1116 | | | o | changeset: 5:4409d547b708
1117 | | | |\ \ parent: 3:27eef8ed80b4
1117 | | | |\ \ parent: 3:27eef8ed80b4
1118 | | | | | | parent: 4:26a8bac39d9f
1118 | | | | | | parent: 4:26a8bac39d9f
1119 | | | | | | user: test
1119 | | | | | | user: test
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1120 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
1121 | | | | | | summary: (5) expand
1121 | | | | | | summary: (5) expand
1122 | | | | | |
1122 | | | | | |
1123 | +---o | | changeset: 4:26a8bac39d9f
1123 | +---o | | changeset: 4:26a8bac39d9f
1124 | | | |/ / parent: 1:6db2ef61d156
1124 | | | |/ / parent: 1:6db2ef61d156
1125 | | | | | parent: 3:27eef8ed80b4
1125 | | | | | parent: 3:27eef8ed80b4
1126 | | | | | user: test
1126 | | | | | user: test
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1127 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1128 | | | | | summary: (4) merge two known; one immediate left, one immediate right
1129 | | | | |
1129 | | | | |
1130
1130
1131
1131
1132 Empty revision range - display nothing:
1132 Empty revision range - display nothing:
1133 $ hg log -G -r 1..0
1133 $ hg log -G -r 1..0
1134
1134
1135 $ cd ..
1135 $ cd ..
1136
1136
1137 #if no-outer-repo
1137 #if no-outer-repo
1138
1138
1139 From outer space:
1139 From outer space:
1140 $ hg log -G -l1 repo
1140 $ hg log -G -l1 repo
1141 @ changeset: 34:fea3ac5810e0
1141 @ changeset: 34:fea3ac5810e0
1142 | tag: tip
1142 | tag: tip
1143 | parent: 32:d06dffa21a31
1143 | parent: 32:d06dffa21a31
1144 | user: test
1144 | user: test
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1145 | date: Thu Jan 01 00:00:34 1970 +0000
1146 | summary: (34) head
1146 | summary: (34) head
1147 |
1147 |
1148 $ hg log -G -l1 repo/a
1148 $ hg log -G -l1 repo/a
1149 @ changeset: 34:fea3ac5810e0
1149 @ changeset: 34:fea3ac5810e0
1150 | tag: tip
1150 | tag: tip
1151 | parent: 32:d06dffa21a31
1151 | parent: 32:d06dffa21a31
1152 | user: test
1152 | user: test
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1153 | date: Thu Jan 01 00:00:34 1970 +0000
1154 | summary: (34) head
1154 | summary: (34) head
1155 |
1155 |
1156 $ hg log -G -l1 repo/missing
1156 $ hg log -G -l1 repo/missing
1157
1157
1158 #endif
1158 #endif
1159
1159
1160 File log with revs != cset revs:
1160 File log with revs != cset revs:
1161 $ hg init flog
1161 $ hg init flog
1162 $ cd flog
1162 $ cd flog
1163 $ echo one >one
1163 $ echo one >one
1164 $ hg add one
1164 $ hg add one
1165 $ hg commit -mone
1165 $ hg commit -mone
1166 $ echo two >two
1166 $ echo two >two
1167 $ hg add two
1167 $ hg add two
1168 $ hg commit -mtwo
1168 $ hg commit -mtwo
1169 $ echo more >two
1169 $ echo more >two
1170 $ hg commit -mmore
1170 $ hg commit -mmore
1171 $ hg log -G two
1171 $ hg log -G two
1172 @ changeset: 2:12c28321755b
1172 @ changeset: 2:12c28321755b
1173 | tag: tip
1173 | tag: tip
1174 | user: test
1174 | user: test
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1175 | date: Thu Jan 01 00:00:00 1970 +0000
1176 | summary: more
1176 | summary: more
1177 |
1177 |
1178 o changeset: 1:5ac72c0599bf
1178 o changeset: 1:5ac72c0599bf
1179 | user: test
1179 | user: test
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1180 | date: Thu Jan 01 00:00:00 1970 +0000
1181 | summary: two
1181 | summary: two
1182 |
1182 |
1183
1183
1184 Issue1896: File log with explicit style
1184 Issue1896: File log with explicit style
1185 $ hg log -G --style=default one
1185 $ hg log -G --style=default one
1186 o changeset: 0:3d578b4a1f53
1186 o changeset: 0:3d578b4a1f53
1187 user: test
1187 user: test
1188 date: Thu Jan 01 00:00:00 1970 +0000
1188 date: Thu Jan 01 00:00:00 1970 +0000
1189 summary: one
1189 summary: one
1190
1190
1191 Issue2395: glog --style header and footer
1191 Issue2395: glog --style header and footer
1192 $ hg log -G --style=xml one
1192 $ hg log -G --style=xml one
1193 <?xml version="1.0"?>
1193 <?xml version="1.0"?>
1194 <log>
1194 <log>
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 <author email="test">test</author>
1196 <author email="test">test</author>
1197 <date>1970-01-01T00:00:00+00:00</date>
1197 <date>1970-01-01T00:00:00+00:00</date>
1198 <msg xml:space="preserve">one</msg>
1198 <msg xml:space="preserve">one</msg>
1199 </logentry>
1199 </logentry>
1200 </log>
1200 </log>
1201
1201
1202 $ cd ..
1202 $ cd ..
1203
1203
1204 Incoming and outgoing:
1204 Incoming and outgoing:
1205
1205
1206 $ hg clone -U -r31 repo repo2
1206 $ hg clone -U -r31 repo repo2
1207 adding changesets
1207 adding changesets
1208 adding manifests
1208 adding manifests
1209 adding file changes
1209 adding file changes
1210 added 31 changesets with 31 changes to 1 files
1210 added 31 changesets with 31 changes to 1 files
1211 $ cd repo2
1211 $ cd repo2
1212
1212
1213 $ hg incoming --graph ../repo
1213 $ hg incoming --graph ../repo
1214 comparing with ../repo
1214 comparing with ../repo
1215 searching for changes
1215 searching for changes
1216 o changeset: 34:fea3ac5810e0
1216 o changeset: 34:fea3ac5810e0
1217 | tag: tip
1217 | tag: tip
1218 | parent: 32:d06dffa21a31
1218 | parent: 32:d06dffa21a31
1219 | user: test
1219 | user: test
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1220 | date: Thu Jan 01 00:00:34 1970 +0000
1221 | summary: (34) head
1221 | summary: (34) head
1222 |
1222 |
1223 | o changeset: 33:68608f5145f9
1223 | o changeset: 33:68608f5145f9
1224 | parent: 18:1aa84d96232a
1224 | parent: 18:1aa84d96232a
1225 | user: test
1225 | user: test
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1226 | date: Thu Jan 01 00:00:33 1970 +0000
1227 | summary: (33) head
1227 | summary: (33) head
1228 |
1228 |
1229 o changeset: 32:d06dffa21a31
1229 o changeset: 32:d06dffa21a31
1230 | parent: 27:886ed638191b
1230 | parent: 27:886ed638191b
1231 | parent: 31:621d83e11f67
1231 | parent: 31:621d83e11f67
1232 | user: test
1232 | user: test
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1233 | date: Thu Jan 01 00:00:32 1970 +0000
1234 | summary: (32) expand
1234 | summary: (32) expand
1235 |
1235 |
1236 o changeset: 27:886ed638191b
1236 o changeset: 27:886ed638191b
1237 parent: 21:d42a756af44d
1237 parent: 21:d42a756af44d
1238 user: test
1238 user: test
1239 date: Thu Jan 01 00:00:27 1970 +0000
1239 date: Thu Jan 01 00:00:27 1970 +0000
1240 summary: (27) collapse
1240 summary: (27) collapse
1241
1241
1242 $ cd ..
1242 $ cd ..
1243
1243
1244 $ hg -R repo outgoing --graph repo2
1244 $ hg -R repo outgoing --graph repo2
1245 comparing with repo2
1245 comparing with repo2
1246 searching for changes
1246 searching for changes
1247 @ changeset: 34:fea3ac5810e0
1247 @ changeset: 34:fea3ac5810e0
1248 | tag: tip
1248 | tag: tip
1249 | parent: 32:d06dffa21a31
1249 | parent: 32:d06dffa21a31
1250 | user: test
1250 | user: test
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1251 | date: Thu Jan 01 00:00:34 1970 +0000
1252 | summary: (34) head
1252 | summary: (34) head
1253 |
1253 |
1254 | o changeset: 33:68608f5145f9
1254 | o changeset: 33:68608f5145f9
1255 | parent: 18:1aa84d96232a
1255 | parent: 18:1aa84d96232a
1256 | user: test
1256 | user: test
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1257 | date: Thu Jan 01 00:00:33 1970 +0000
1258 | summary: (33) head
1258 | summary: (33) head
1259 |
1259 |
1260 o changeset: 32:d06dffa21a31
1260 o changeset: 32:d06dffa21a31
1261 | parent: 27:886ed638191b
1261 | parent: 27:886ed638191b
1262 | parent: 31:621d83e11f67
1262 | parent: 31:621d83e11f67
1263 | user: test
1263 | user: test
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1264 | date: Thu Jan 01 00:00:32 1970 +0000
1265 | summary: (32) expand
1265 | summary: (32) expand
1266 |
1266 |
1267 o changeset: 27:886ed638191b
1267 o changeset: 27:886ed638191b
1268 parent: 21:d42a756af44d
1268 parent: 21:d42a756af44d
1269 user: test
1269 user: test
1270 date: Thu Jan 01 00:00:27 1970 +0000
1270 date: Thu Jan 01 00:00:27 1970 +0000
1271 summary: (27) collapse
1271 summary: (27) collapse
1272
1272
1273
1273
1274 File + limit with revs != cset revs:
1274 File + limit with revs != cset revs:
1275 $ cd repo
1275 $ cd repo
1276 $ touch b
1276 $ touch b
1277 $ hg ci -Aqm0
1277 $ hg ci -Aqm0
1278 $ hg log -G -l2 a
1278 $ hg log -G -l2 a
1279 o changeset: 34:fea3ac5810e0
1279 o changeset: 34:fea3ac5810e0
1280 | parent: 32:d06dffa21a31
1280 | parent: 32:d06dffa21a31
1281 | user: test
1281 | user: test
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1282 | date: Thu Jan 01 00:00:34 1970 +0000
1283 | summary: (34) head
1283 | summary: (34) head
1284 |
1284 |
1285 | o changeset: 33:68608f5145f9
1285 | o changeset: 33:68608f5145f9
1286 | | parent: 18:1aa84d96232a
1286 | | parent: 18:1aa84d96232a
1287 | | user: test
1287 | | user: test
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1288 | | date: Thu Jan 01 00:00:33 1970 +0000
1289 | | summary: (33) head
1289 | | summary: (33) head
1290 | |
1290 | |
1291
1291
1292 File + limit + -ra:b, (b - a) < limit:
1292 File + limit + -ra:b, (b - a) < limit:
1293 $ hg log -G -l3000 -r32:tip a
1293 $ hg log -G -l3000 -r32:tip a
1294 o changeset: 34:fea3ac5810e0
1294 o changeset: 34:fea3ac5810e0
1295 | parent: 32:d06dffa21a31
1295 | parent: 32:d06dffa21a31
1296 | user: test
1296 | user: test
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1297 | date: Thu Jan 01 00:00:34 1970 +0000
1298 | summary: (34) head
1298 | summary: (34) head
1299 |
1299 |
1300 | o changeset: 33:68608f5145f9
1300 | o changeset: 33:68608f5145f9
1301 | | parent: 18:1aa84d96232a
1301 | | parent: 18:1aa84d96232a
1302 | | user: test
1302 | | user: test
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1303 | | date: Thu Jan 01 00:00:33 1970 +0000
1304 | | summary: (33) head
1304 | | summary: (33) head
1305 | |
1305 | |
1306 o | changeset: 32:d06dffa21a31
1306 o | changeset: 32:d06dffa21a31
1307 |\ \ parent: 27:886ed638191b
1307 |\ \ parent: 27:886ed638191b
1308 | | | parent: 31:621d83e11f67
1308 | | | parent: 31:621d83e11f67
1309 | | | user: test
1309 | | | user: test
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1310 | | | date: Thu Jan 01 00:00:32 1970 +0000
1311 | | | summary: (32) expand
1311 | | | summary: (32) expand
1312 | | |
1312 | | |
1313
1313
1314 Point out a common and an uncommon unshown parent
1314 Point out a common and an uncommon unshown parent
1315
1315
1316 $ hg log -G -r 'rev(8) or rev(9)'
1316 $ hg log -G -r 'rev(8) or rev(9)'
1317 o changeset: 9:7010c0af0a35
1317 o changeset: 9:7010c0af0a35
1318 |\ parent: 7:b632bb1b1224
1318 |\ parent: 7:b632bb1b1224
1319 | | parent: 8:7a0b11f71937
1319 | | parent: 8:7a0b11f71937
1320 | | user: test
1320 | | user: test
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1321 | | date: Thu Jan 01 00:00:09 1970 +0000
1322 | | summary: (9) expand
1322 | | summary: (9) expand
1323 | |
1323 | |
1324 o | changeset: 8:7a0b11f71937
1324 o | changeset: 8:7a0b11f71937
1325 |\| parent: 0:e6eb3150255d
1325 |\| parent: 0:e6eb3150255d
1326 | | parent: 7:b632bb1b1224
1326 | | parent: 7:b632bb1b1224
1327 | | user: test
1327 | | user: test
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1328 | | date: Thu Jan 01 00:00:08 1970 +0000
1329 | | summary: (8) merge two known; one immediate left, one far right
1329 | | summary: (8) merge two known; one immediate left, one far right
1330 | |
1330 | |
1331
1331
1332 File + limit + -ra:b, b < tip:
1332 File + limit + -ra:b, b < tip:
1333
1333
1334 $ hg log -G -l1 -r32:34 a
1334 $ hg log -G -l1 -r32:34 a
1335 o changeset: 34:fea3ac5810e0
1335 o changeset: 34:fea3ac5810e0
1336 | parent: 32:d06dffa21a31
1336 | parent: 32:d06dffa21a31
1337 | user: test
1337 | user: test
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1338 | date: Thu Jan 01 00:00:34 1970 +0000
1339 | summary: (34) head
1339 | summary: (34) head
1340 |
1340 |
1341
1341
1342 file(File) + limit + -ra:b, b < tip:
1342 file(File) + limit + -ra:b, b < tip:
1343
1343
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1345 o changeset: 34:fea3ac5810e0
1345 o changeset: 34:fea3ac5810e0
1346 | parent: 32:d06dffa21a31
1346 | parent: 32:d06dffa21a31
1347 | user: test
1347 | user: test
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1348 | date: Thu Jan 01 00:00:34 1970 +0000
1349 | summary: (34) head
1349 | summary: (34) head
1350 |
1350 |
1351
1351
1352 limit(file(File) and a::b), b < tip:
1352 limit(file(File) and a::b), b < tip:
1353
1353
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1355 o changeset: 32:d06dffa21a31
1355 o changeset: 32:d06dffa21a31
1356 |\ parent: 27:886ed638191b
1356 |\ parent: 27:886ed638191b
1357 | | parent: 31:621d83e11f67
1357 | | parent: 31:621d83e11f67
1358 | | user: test
1358 | | user: test
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1359 | | date: Thu Jan 01 00:00:32 1970 +0000
1360 | | summary: (32) expand
1360 | | summary: (32) expand
1361 | |
1361 | |
1362
1362
1363 File + limit + -ra:b, b < tip:
1363 File + limit + -ra:b, b < tip:
1364
1364
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1366
1366
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1368
1368
1369 $ hg log -G -l10 -r33:34 a
1369 $ hg log -G -l10 -r33:34 a
1370 o changeset: 34:fea3ac5810e0
1370 o changeset: 34:fea3ac5810e0
1371 | parent: 32:d06dffa21a31
1371 | parent: 32:d06dffa21a31
1372 | user: test
1372 | user: test
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1373 | date: Thu Jan 01 00:00:34 1970 +0000
1374 | summary: (34) head
1374 | summary: (34) head
1375 |
1375 |
1376 | o changeset: 33:68608f5145f9
1376 | o changeset: 33:68608f5145f9
1377 | | parent: 18:1aa84d96232a
1377 | | parent: 18:1aa84d96232a
1378 | | user: test
1378 | | user: test
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1379 | | date: Thu Jan 01 00:00:33 1970 +0000
1380 | | summary: (33) head
1380 | | summary: (33) head
1381 | |
1381 | |
1382
1382
1383 Do not crash or produce strange graphs if history is buggy
1383 Do not crash or produce strange graphs if history is buggy
1384
1384
1385 $ hg branch branch
1385 $ hg branch branch
1386 marked working directory as branch branch
1386 marked working directory as branch branch
1387 (branches are permanent and global, did you want a bookmark?)
1387 (branches are permanent and global, did you want a bookmark?)
1388 $ commit 36 "buggy merge: identical parents" 35 35
1388 $ commit 36 "buggy merge: identical parents" 35 35
1389 $ hg log -G -l5
1389 $ hg log -G -l5
1390 @ changeset: 36:08a19a744424
1390 @ changeset: 36:08a19a744424
1391 | branch: branch
1391 | branch: branch
1392 | tag: tip
1392 | tag: tip
1393 | parent: 35:9159c3644c5e
1393 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1394 | parent: 35:9159c3644c5e
1395 | user: test
1395 | user: test
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1396 | date: Thu Jan 01 00:00:36 1970 +0000
1397 | summary: (36) buggy merge: identical parents
1397 | summary: (36) buggy merge: identical parents
1398 |
1398 |
1399 o changeset: 35:9159c3644c5e
1399 o changeset: 35:9159c3644c5e
1400 | user: test
1400 | user: test
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1401 | date: Thu Jan 01 00:00:00 1970 +0000
1402 | summary: 0
1402 | summary: 0
1403 |
1403 |
1404 o changeset: 34:fea3ac5810e0
1404 o changeset: 34:fea3ac5810e0
1405 | parent: 32:d06dffa21a31
1405 | parent: 32:d06dffa21a31
1406 | user: test
1406 | user: test
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1407 | date: Thu Jan 01 00:00:34 1970 +0000
1408 | summary: (34) head
1408 | summary: (34) head
1409 |
1409 |
1410 | o changeset: 33:68608f5145f9
1410 | o changeset: 33:68608f5145f9
1411 | | parent: 18:1aa84d96232a
1411 | | parent: 18:1aa84d96232a
1412 | | user: test
1412 | | user: test
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1413 | | date: Thu Jan 01 00:00:33 1970 +0000
1414 | | summary: (33) head
1414 | | summary: (33) head
1415 | |
1415 | |
1416 o | changeset: 32:d06dffa21a31
1416 o | changeset: 32:d06dffa21a31
1417 |\ \ parent: 27:886ed638191b
1417 |\ \ parent: 27:886ed638191b
1418 | | | parent: 31:621d83e11f67
1418 | | | parent: 31:621d83e11f67
1419 | | | user: test
1419 | | | user: test
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1420 | | | date: Thu Jan 01 00:00:32 1970 +0000
1421 | | | summary: (32) expand
1421 | | | summary: (32) expand
1422 | | |
1422 | | |
1423
1423
1424 Test log -G options
1424 Test log -G options
1425
1425
1426 $ testlog() {
1426 $ testlog() {
1427 > hg log -G --print-revset "$@"
1427 > hg log -G --print-revset "$@"
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1432 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1433 > | grep '^[-+@ ]' || :
1433 > | grep '^[-+@ ]' || :
1434 > }
1434 > }
1435
1435
1436 glog always reorders nodes which explains the difference with log
1436 glog always reorders nodes which explains the difference with log
1437
1437
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1439 ['27', '25', '21', '34', '32', '31']
1439 ['27', '25', '21', '34', '32', '31']
1440 []
1440 []
1441 --- log.nodes * (glob)
1441 --- log.nodes * (glob)
1442 +++ glog.nodes * (glob)
1442 +++ glog.nodes * (glob)
1443 @@ -1,6 +1,6 @@
1443 @@ -1,6 +1,6 @@
1444 -nodetag 27
1444 -nodetag 27
1445 -nodetag 25
1445 -nodetag 25
1446 -nodetag 21
1446 -nodetag 21
1447 nodetag 34
1447 nodetag 34
1448 nodetag 32
1448 nodetag 32
1449 nodetag 31
1449 nodetag 31
1450 +nodetag 27
1450 +nodetag 27
1451 +nodetag 25
1451 +nodetag 25
1452 +nodetag 21
1452 +nodetag 21
1453 $ testlog -u test -u not-a-user
1453 $ testlog -u test -u not-a-user
1454 []
1454 []
1455 (group
1455 (group
1456 (group
1456 (group
1457 (or
1457 (or
1458 (func
1458 (func
1459 ('symbol', 'user')
1459 ('symbol', 'user')
1460 ('string', 'test'))
1460 ('string', 'test'))
1461 (func
1461 (func
1462 ('symbol', 'user')
1462 ('symbol', 'user')
1463 ('string', 'not-a-user')))))
1463 ('string', 'not-a-user')))))
1464 $ testlog -b not-a-branch
1464 $ testlog -b not-a-branch
1465 abort: unknown revision 'not-a-branch'!
1465 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1466 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1467 abort: unknown revision 'not-a-branch'!
1468 $ testlog -b 35 -b 36 --only-branch branch
1468 $ testlog -b 35 -b 36 --only-branch branch
1469 []
1469 []
1470 (group
1470 (group
1471 (group
1471 (group
1472 (or
1472 (or
1473 (func
1473 (func
1474 ('symbol', 'branch')
1474 ('symbol', 'branch')
1475 ('string', 'default'))
1475 ('string', 'default'))
1476 (func
1476 (func
1477 ('symbol', 'branch')
1477 ('symbol', 'branch')
1478 ('string', 'branch'))
1478 ('string', 'branch'))
1479 (func
1479 (func
1480 ('symbol', 'branch')
1480 ('symbol', 'branch')
1481 ('string', 'branch')))))
1481 ('string', 'branch')))))
1482 $ testlog -k expand -k merge
1482 $ testlog -k expand -k merge
1483 []
1483 []
1484 (group
1484 (group
1485 (group
1485 (group
1486 (or
1486 (or
1487 (func
1487 (func
1488 ('symbol', 'keyword')
1488 ('symbol', 'keyword')
1489 ('string', 'expand'))
1489 ('string', 'expand'))
1490 (func
1490 (func
1491 ('symbol', 'keyword')
1491 ('symbol', 'keyword')
1492 ('string', 'merge')))))
1492 ('string', 'merge')))))
1493 $ testlog --only-merges
1493 $ testlog --only-merges
1494 []
1494 []
1495 (group
1495 (group
1496 (func
1496 (func
1497 ('symbol', 'merge')
1497 ('symbol', 'merge')
1498 None))
1498 None))
1499 $ testlog --no-merges
1499 $ testlog --no-merges
1500 []
1500 []
1501 (group
1501 (group
1502 (not
1502 (not
1503 (func
1503 (func
1504 ('symbol', 'merge')
1504 ('symbol', 'merge')
1505 None)))
1505 None)))
1506 $ testlog --date '2 0 to 4 0'
1506 $ testlog --date '2 0 to 4 0'
1507 []
1507 []
1508 (group
1508 (group
1509 (func
1509 (func
1510 ('symbol', 'date')
1510 ('symbol', 'date')
1511 ('string', '2 0 to 4 0')))
1511 ('string', '2 0 to 4 0')))
1512 $ hg log -G -d 'brace ) in a date'
1512 $ hg log -G -d 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1513 abort: invalid date: 'brace ) in a date'
1514 [255]
1514 [255]
1515 $ testlog --prune 31 --prune 32
1515 $ testlog --prune 31 --prune 32
1516 []
1516 []
1517 (group
1517 (group
1518 (group
1518 (group
1519 (and
1519 (and
1520 (not
1520 (not
1521 (group
1521 (group
1522 (or
1522 (or
1523 ('string', '31')
1523 ('string', '31')
1524 (func
1524 (func
1525 ('symbol', 'ancestors')
1525 ('symbol', 'ancestors')
1526 ('string', '31')))))
1526 ('string', '31')))))
1527 (not
1527 (not
1528 (group
1528 (group
1529 (or
1529 (or
1530 ('string', '32')
1530 ('string', '32')
1531 (func
1531 (func
1532 ('symbol', 'ancestors')
1532 ('symbol', 'ancestors')
1533 ('string', '32'))))))))
1533 ('string', '32'))))))))
1534
1534
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1535 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 have 2 filelog topological heads in a linear changeset graph.
1536 have 2 filelog topological heads in a linear changeset graph.
1537
1537
1538 $ cd ..
1538 $ cd ..
1539 $ hg init follow
1539 $ hg init follow
1540 $ cd follow
1540 $ cd follow
1541 $ testlog --follow
1541 $ testlog --follow
1542 []
1542 []
1543 []
1543 []
1544 $ testlog -rnull
1544 $ testlog -rnull
1545 ['null']
1545 ['null']
1546 []
1546 []
1547 $ echo a > a
1547 $ echo a > a
1548 $ echo aa > aa
1548 $ echo aa > aa
1549 $ echo f > f
1549 $ echo f > f
1550 $ hg ci -Am "add a" a aa f
1550 $ hg ci -Am "add a" a aa f
1551 $ hg cp a b
1551 $ hg cp a b
1552 $ hg cp f g
1552 $ hg cp f g
1553 $ hg ci -m "copy a b"
1553 $ hg ci -m "copy a b"
1554 $ mkdir dir
1554 $ mkdir dir
1555 $ hg mv b dir
1555 $ hg mv b dir
1556 $ echo g >> g
1556 $ echo g >> g
1557 $ echo f >> f
1557 $ echo f >> f
1558 $ hg ci -m "mv b dir/b"
1558 $ hg ci -m "mv b dir/b"
1559 $ hg mv a b
1559 $ hg mv a b
1560 $ hg cp -f f g
1560 $ hg cp -f f g
1561 $ echo a > d
1561 $ echo a > d
1562 $ hg add d
1562 $ hg add d
1563 $ hg ci -m "mv a b; add d"
1563 $ hg ci -m "mv a b; add d"
1564 $ hg mv dir/b e
1564 $ hg mv dir/b e
1565 $ hg ci -m "mv dir/b e"
1565 $ hg ci -m "mv dir/b e"
1566 $ hg log -G --template '({rev}) {desc|firstline}\n'
1566 $ hg log -G --template '({rev}) {desc|firstline}\n'
1567 @ (4) mv dir/b e
1567 @ (4) mv dir/b e
1568 |
1568 |
1569 o (3) mv a b; add d
1569 o (3) mv a b; add d
1570 |
1570 |
1571 o (2) mv b dir/b
1571 o (2) mv b dir/b
1572 |
1572 |
1573 o (1) copy a b
1573 o (1) copy a b
1574 |
1574 |
1575 o (0) add a
1575 o (0) add a
1576
1576
1577
1577
1578 $ testlog a
1578 $ testlog a
1579 []
1579 []
1580 (group
1580 (group
1581 (group
1581 (group
1582 (func
1582 (func
1583 ('symbol', 'filelog')
1583 ('symbol', 'filelog')
1584 ('string', 'a'))))
1584 ('string', 'a'))))
1585 $ testlog a b
1585 $ testlog a b
1586 []
1586 []
1587 (group
1587 (group
1588 (group
1588 (group
1589 (or
1589 (or
1590 (func
1590 (func
1591 ('symbol', 'filelog')
1591 ('symbol', 'filelog')
1592 ('string', 'a'))
1592 ('string', 'a'))
1593 (func
1593 (func
1594 ('symbol', 'filelog')
1594 ('symbol', 'filelog')
1595 ('string', 'b')))))
1595 ('string', 'b')))))
1596
1596
1597 Test falling back to slow path for non-existing files
1597 Test falling back to slow path for non-existing files
1598
1598
1599 $ testlog a c
1599 $ testlog a c
1600 []
1600 []
1601 (group
1601 (group
1602 (func
1602 (func
1603 ('symbol', '_matchfiles')
1603 ('symbol', '_matchfiles')
1604 (list
1604 (list
1605 ('string', 'r:')
1605 ('string', 'r:')
1606 ('string', 'd:relpath')
1606 ('string', 'd:relpath')
1607 ('string', 'p:a')
1607 ('string', 'p:a')
1608 ('string', 'p:c'))))
1608 ('string', 'p:c'))))
1609
1609
1610 Test multiple --include/--exclude/paths
1610 Test multiple --include/--exclude/paths
1611
1611
1612 $ testlog --include a --include e --exclude b --exclude e a e
1612 $ testlog --include a --include e --exclude b --exclude e a e
1613 []
1613 []
1614 (group
1614 (group
1615 (func
1615 (func
1616 ('symbol', '_matchfiles')
1616 ('symbol', '_matchfiles')
1617 (list
1617 (list
1618 ('string', 'r:')
1618 ('string', 'r:')
1619 ('string', 'd:relpath')
1619 ('string', 'd:relpath')
1620 ('string', 'p:a')
1620 ('string', 'p:a')
1621 ('string', 'p:e')
1621 ('string', 'p:e')
1622 ('string', 'i:a')
1622 ('string', 'i:a')
1623 ('string', 'i:e')
1623 ('string', 'i:e')
1624 ('string', 'x:b')
1624 ('string', 'x:b')
1625 ('string', 'x:e'))))
1625 ('string', 'x:e'))))
1626
1626
1627 Test glob expansion of pats
1627 Test glob expansion of pats
1628
1628
1629 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1629 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1630 > print mercurial.util.expandglobs and 'true' or 'false'"`
1630 > print mercurial.util.expandglobs and 'true' or 'false'"`
1631 $ if [ $expandglobs = "true" ]; then
1631 $ if [ $expandglobs = "true" ]; then
1632 > testlog 'a*';
1632 > testlog 'a*';
1633 > else
1633 > else
1634 > testlog a*;
1634 > testlog a*;
1635 > fi;
1635 > fi;
1636 []
1636 []
1637 (group
1637 (group
1638 (group
1638 (group
1639 (func
1639 (func
1640 ('symbol', 'filelog')
1640 ('symbol', 'filelog')
1641 ('string', 'aa'))))
1641 ('string', 'aa'))))
1642
1642
1643 Test --follow on a non-existent directory
1643 Test --follow on a non-existent directory
1644
1644
1645 $ testlog -f dir
1645 $ testlog -f dir
1646 abort: cannot follow file not in parent revision: "dir"
1646 abort: cannot follow file not in parent revision: "dir"
1647 abort: cannot follow file not in parent revision: "dir"
1647 abort: cannot follow file not in parent revision: "dir"
1648 abort: cannot follow file not in parent revision: "dir"
1648 abort: cannot follow file not in parent revision: "dir"
1649
1649
1650 Test --follow on a directory
1650 Test --follow on a directory
1651
1651
1652 $ hg up -q '.^'
1652 $ hg up -q '.^'
1653 $ testlog -f dir
1653 $ testlog -f dir
1654 []
1654 []
1655 (group
1655 (group
1656 (and
1656 (and
1657 (func
1657 (func
1658 ('symbol', 'ancestors')
1658 ('symbol', 'ancestors')
1659 ('symbol', '.'))
1659 ('symbol', '.'))
1660 (func
1660 (func
1661 ('symbol', '_matchfiles')
1661 ('symbol', '_matchfiles')
1662 (list
1662 (list
1663 ('string', 'r:')
1663 ('string', 'r:')
1664 ('string', 'd:relpath')
1664 ('string', 'd:relpath')
1665 ('string', 'p:dir')))))
1665 ('string', 'p:dir')))))
1666 $ hg up -q tip
1666 $ hg up -q tip
1667
1667
1668 Test --follow on file not in parent revision
1668 Test --follow on file not in parent revision
1669
1669
1670 $ testlog -f a
1670 $ testlog -f a
1671 abort: cannot follow file not in parent revision: "a"
1671 abort: cannot follow file not in parent revision: "a"
1672 abort: cannot follow file not in parent revision: "a"
1672 abort: cannot follow file not in parent revision: "a"
1673 abort: cannot follow file not in parent revision: "a"
1673 abort: cannot follow file not in parent revision: "a"
1674
1674
1675 Test --follow and patterns
1675 Test --follow and patterns
1676
1676
1677 $ testlog -f 'glob:*'
1677 $ testlog -f 'glob:*'
1678 []
1678 []
1679 (group
1679 (group
1680 (and
1680 (and
1681 (func
1681 (func
1682 ('symbol', 'ancestors')
1682 ('symbol', 'ancestors')
1683 ('symbol', '.'))
1683 ('symbol', '.'))
1684 (func
1684 (func
1685 ('symbol', '_matchfiles')
1685 ('symbol', '_matchfiles')
1686 (list
1686 (list
1687 ('string', 'r:')
1687 ('string', 'r:')
1688 ('string', 'd:relpath')
1688 ('string', 'd:relpath')
1689 ('string', 'p:glob:*')))))
1689 ('string', 'p:glob:*')))))
1690
1690
1691 Test --follow on a single rename
1691 Test --follow on a single rename
1692
1692
1693 $ hg up -q 2
1693 $ hg up -q 2
1694 $ testlog -f a
1694 $ testlog -f a
1695 []
1695 []
1696 (group
1696 (group
1697 (group
1697 (group
1698 (func
1698 (func
1699 ('symbol', 'follow')
1699 ('symbol', 'follow')
1700 ('string', 'a'))))
1700 ('string', 'a'))))
1701
1701
1702 Test --follow and multiple renames
1702 Test --follow and multiple renames
1703
1703
1704 $ hg up -q tip
1704 $ hg up -q tip
1705 $ testlog -f e
1705 $ testlog -f e
1706 []
1706 []
1707 (group
1707 (group
1708 (group
1708 (group
1709 (func
1709 (func
1710 ('symbol', 'follow')
1710 ('symbol', 'follow')
1711 ('string', 'e'))))
1711 ('string', 'e'))))
1712
1712
1713 Test --follow and multiple filelog heads
1713 Test --follow and multiple filelog heads
1714
1714
1715 $ hg up -q 2
1715 $ hg up -q 2
1716 $ testlog -f g
1716 $ testlog -f g
1717 []
1717 []
1718 (group
1718 (group
1719 (group
1719 (group
1720 (func
1720 (func
1721 ('symbol', 'follow')
1721 ('symbol', 'follow')
1722 ('string', 'g'))))
1722 ('string', 'g'))))
1723 $ cat log.nodes
1723 $ cat log.nodes
1724 nodetag 2
1724 nodetag 2
1725 nodetag 1
1725 nodetag 1
1726 nodetag 0
1726 nodetag 0
1727 $ hg up -q tip
1727 $ hg up -q tip
1728 $ testlog -f g
1728 $ testlog -f g
1729 []
1729 []
1730 (group
1730 (group
1731 (group
1731 (group
1732 (func
1732 (func
1733 ('symbol', 'follow')
1733 ('symbol', 'follow')
1734 ('string', 'g'))))
1734 ('string', 'g'))))
1735 $ cat log.nodes
1735 $ cat log.nodes
1736 nodetag 3
1736 nodetag 3
1737 nodetag 2
1737 nodetag 2
1738 nodetag 0
1738 nodetag 0
1739
1739
1740 Test --follow and multiple files
1740 Test --follow and multiple files
1741
1741
1742 $ testlog -f g e
1742 $ testlog -f g e
1743 []
1743 []
1744 (group
1744 (group
1745 (group
1745 (group
1746 (or
1746 (or
1747 (func
1747 (func
1748 ('symbol', 'follow')
1748 ('symbol', 'follow')
1749 ('string', 'g'))
1749 ('string', 'g'))
1750 (func
1750 (func
1751 ('symbol', 'follow')
1751 ('symbol', 'follow')
1752 ('string', 'e')))))
1752 ('string', 'e')))))
1753 $ cat log.nodes
1753 $ cat log.nodes
1754 nodetag 4
1754 nodetag 4
1755 nodetag 3
1755 nodetag 3
1756 nodetag 2
1756 nodetag 2
1757 nodetag 1
1757 nodetag 1
1758 nodetag 0
1758 nodetag 0
1759
1759
1760 Test --follow null parent
1760 Test --follow null parent
1761
1761
1762 $ hg up -q null
1762 $ hg up -q null
1763 $ testlog -f
1763 $ testlog -f
1764 []
1764 []
1765 []
1765 []
1766
1766
1767 Test --follow-first
1767 Test --follow-first
1768
1768
1769 $ hg up -q 3
1769 $ hg up -q 3
1770 $ echo ee > e
1770 $ echo ee > e
1771 $ hg ci -Am "add another e" e
1771 $ hg ci -Am "add another e" e
1772 created new head
1772 created new head
1773 $ hg merge --tool internal:other 4
1773 $ hg merge --tool internal:other 4
1774 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1774 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1775 (branch merge, don't forget to commit)
1775 (branch merge, don't forget to commit)
1776 $ echo merge > e
1776 $ echo merge > e
1777 $ hg ci -m "merge 5 and 4"
1777 $ hg ci -m "merge 5 and 4"
1778 $ testlog --follow-first
1778 $ testlog --follow-first
1779 []
1779 []
1780 (group
1780 (group
1781 (func
1781 (func
1782 ('symbol', '_firstancestors')
1782 ('symbol', '_firstancestors')
1783 (func
1783 (func
1784 ('symbol', 'rev')
1784 ('symbol', 'rev')
1785 ('symbol', '6'))))
1785 ('symbol', '6'))))
1786
1786
1787 Cannot compare with log --follow-first FILE as it never worked
1787 Cannot compare with log --follow-first FILE as it never worked
1788
1788
1789 $ hg log -G --print-revset --follow-first e
1789 $ hg log -G --print-revset --follow-first e
1790 []
1790 []
1791 (group
1791 (group
1792 (group
1792 (group
1793 (func
1793 (func
1794 ('symbol', '_followfirst')
1794 ('symbol', '_followfirst')
1795 ('string', 'e'))))
1795 ('string', 'e'))))
1796 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1796 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1797 @ 6 merge 5 and 4
1797 @ 6 merge 5 and 4
1798 |\
1798 |\
1799 o | 5 add another e
1799 o | 5 add another e
1800 | |
1800 | |
1801
1801
1802 Test --copies
1802 Test --copies
1803
1803
1804 $ hg log -G --copies --template "{rev} {desc|firstline} \
1804 $ hg log -G --copies --template "{rev} {desc|firstline} \
1805 > copies: {file_copies_switch}\n"
1805 > copies: {file_copies_switch}\n"
1806 @ 6 merge 5 and 4 copies:
1806 @ 6 merge 5 and 4 copies:
1807 |\
1807 |\
1808 | o 5 add another e copies:
1808 | o 5 add another e copies:
1809 | |
1809 | |
1810 o | 4 mv dir/b e copies: e (dir/b)
1810 o | 4 mv dir/b e copies: e (dir/b)
1811 |/
1811 |/
1812 o 3 mv a b; add d copies: b (a)g (f)
1812 o 3 mv a b; add d copies: b (a)g (f)
1813 |
1813 |
1814 o 2 mv b dir/b copies: dir/b (b)
1814 o 2 mv b dir/b copies: dir/b (b)
1815 |
1815 |
1816 o 1 copy a b copies: b (a)g (f)
1816 o 1 copy a b copies: b (a)g (f)
1817 |
1817 |
1818 o 0 add a copies:
1818 o 0 add a copies:
1819
1819
1820 Test "set:..." and parent revision
1820 Test "set:..." and parent revision
1821
1821
1822 $ hg up -q 4
1822 $ hg up -q 4
1823 $ testlog "set:copied()"
1823 $ testlog "set:copied()"
1824 []
1824 []
1825 (group
1825 (group
1826 (func
1826 (func
1827 ('symbol', '_matchfiles')
1827 ('symbol', '_matchfiles')
1828 (list
1828 (list
1829 ('string', 'r:')
1829 ('string', 'r:')
1830 ('string', 'd:relpath')
1830 ('string', 'd:relpath')
1831 ('string', 'p:set:copied()'))))
1831 ('string', 'p:set:copied()'))))
1832 $ testlog --include "set:copied()"
1832 $ testlog --include "set:copied()"
1833 []
1833 []
1834 (group
1834 (group
1835 (func
1835 (func
1836 ('symbol', '_matchfiles')
1836 ('symbol', '_matchfiles')
1837 (list
1837 (list
1838 ('string', 'r:')
1838 ('string', 'r:')
1839 ('string', 'd:relpath')
1839 ('string', 'd:relpath')
1840 ('string', 'i:set:copied()'))))
1840 ('string', 'i:set:copied()'))))
1841 $ testlog -r "sort(file('set:copied()'), -rev)"
1841 $ testlog -r "sort(file('set:copied()'), -rev)"
1842 ["sort(file('set:copied()'), -rev)"]
1842 ["sort(file('set:copied()'), -rev)"]
1843 []
1843 []
1844
1844
1845 Test --removed
1845 Test --removed
1846
1846
1847 $ testlog --removed
1847 $ testlog --removed
1848 []
1848 []
1849 []
1849 []
1850 $ testlog --removed a
1850 $ testlog --removed a
1851 []
1851 []
1852 (group
1852 (group
1853 (func
1853 (func
1854 ('symbol', '_matchfiles')
1854 ('symbol', '_matchfiles')
1855 (list
1855 (list
1856 ('string', 'r:')
1856 ('string', 'r:')
1857 ('string', 'd:relpath')
1857 ('string', 'd:relpath')
1858 ('string', 'p:a'))))
1858 ('string', 'p:a'))))
1859 $ testlog --removed --follow a
1859 $ testlog --removed --follow a
1860 []
1860 []
1861 (group
1861 (group
1862 (and
1862 (and
1863 (func
1863 (func
1864 ('symbol', 'ancestors')
1864 ('symbol', 'ancestors')
1865 ('symbol', '.'))
1865 ('symbol', '.'))
1866 (func
1866 (func
1867 ('symbol', '_matchfiles')
1867 ('symbol', '_matchfiles')
1868 (list
1868 (list
1869 ('string', 'r:')
1869 ('string', 'r:')
1870 ('string', 'd:relpath')
1870 ('string', 'd:relpath')
1871 ('string', 'p:a')))))
1871 ('string', 'p:a')))))
1872
1872
1873 Test --patch and --stat with --follow and --follow-first
1873 Test --patch and --stat with --follow and --follow-first
1874
1874
1875 $ hg up -q 3
1875 $ hg up -q 3
1876 $ hg log -G --git --patch b
1876 $ hg log -G --git --patch b
1877 o changeset: 1:216d4c92cf98
1877 o changeset: 1:216d4c92cf98
1878 | user: test
1878 | user: test
1879 | date: Thu Jan 01 00:00:00 1970 +0000
1879 | date: Thu Jan 01 00:00:00 1970 +0000
1880 | summary: copy a b
1880 | summary: copy a b
1881 |
1881 |
1882 | diff --git a/a b/b
1882 | diff --git a/a b/b
1883 | copy from a
1883 | copy from a
1884 | copy to b
1884 | copy to b
1885 |
1885 |
1886
1886
1887 $ hg log -G --git --stat b
1887 $ hg log -G --git --stat b
1888 o changeset: 1:216d4c92cf98
1888 o changeset: 1:216d4c92cf98
1889 | user: test
1889 | user: test
1890 | date: Thu Jan 01 00:00:00 1970 +0000
1890 | date: Thu Jan 01 00:00:00 1970 +0000
1891 | summary: copy a b
1891 | summary: copy a b
1892 |
1892 |
1893 | b | 0
1893 | b | 0
1894 | 1 files changed, 0 insertions(+), 0 deletions(-)
1894 | 1 files changed, 0 insertions(+), 0 deletions(-)
1895 |
1895 |
1896
1896
1897 $ hg log -G --git --patch --follow b
1897 $ hg log -G --git --patch --follow b
1898 o changeset: 1:216d4c92cf98
1898 o changeset: 1:216d4c92cf98
1899 | user: test
1899 | user: test
1900 | date: Thu Jan 01 00:00:00 1970 +0000
1900 | date: Thu Jan 01 00:00:00 1970 +0000
1901 | summary: copy a b
1901 | summary: copy a b
1902 |
1902 |
1903 | diff --git a/a b/b
1903 | diff --git a/a b/b
1904 | copy from a
1904 | copy from a
1905 | copy to b
1905 | copy to b
1906 |
1906 |
1907 o changeset: 0:f8035bb17114
1907 o changeset: 0:f8035bb17114
1908 user: test
1908 user: test
1909 date: Thu Jan 01 00:00:00 1970 +0000
1909 date: Thu Jan 01 00:00:00 1970 +0000
1910 summary: add a
1910 summary: add a
1911
1911
1912 diff --git a/a b/a
1912 diff --git a/a b/a
1913 new file mode 100644
1913 new file mode 100644
1914 --- /dev/null
1914 --- /dev/null
1915 +++ b/a
1915 +++ b/a
1916 @@ -0,0 +1,1 @@
1916 @@ -0,0 +1,1 @@
1917 +a
1917 +a
1918
1918
1919
1919
1920 $ hg log -G --git --stat --follow b
1920 $ hg log -G --git --stat --follow b
1921 o changeset: 1:216d4c92cf98
1921 o changeset: 1:216d4c92cf98
1922 | user: test
1922 | user: test
1923 | date: Thu Jan 01 00:00:00 1970 +0000
1923 | date: Thu Jan 01 00:00:00 1970 +0000
1924 | summary: copy a b
1924 | summary: copy a b
1925 |
1925 |
1926 | b | 0
1926 | b | 0
1927 | 1 files changed, 0 insertions(+), 0 deletions(-)
1927 | 1 files changed, 0 insertions(+), 0 deletions(-)
1928 |
1928 |
1929 o changeset: 0:f8035bb17114
1929 o changeset: 0:f8035bb17114
1930 user: test
1930 user: test
1931 date: Thu Jan 01 00:00:00 1970 +0000
1931 date: Thu Jan 01 00:00:00 1970 +0000
1932 summary: add a
1932 summary: add a
1933
1933
1934 a | 1 +
1934 a | 1 +
1935 1 files changed, 1 insertions(+), 0 deletions(-)
1935 1 files changed, 1 insertions(+), 0 deletions(-)
1936
1936
1937
1937
1938 $ hg up -q 6
1938 $ hg up -q 6
1939 $ hg log -G --git --patch --follow-first e
1939 $ hg log -G --git --patch --follow-first e
1940 @ changeset: 6:fc281d8ff18d
1940 @ changeset: 6:fc281d8ff18d
1941 |\ tag: tip
1941 |\ tag: tip
1942 | | parent: 5:99b31f1c2782
1942 | | parent: 5:99b31f1c2782
1943 | | parent: 4:17d952250a9d
1943 | | parent: 4:17d952250a9d
1944 | | user: test
1944 | | user: test
1945 | | date: Thu Jan 01 00:00:00 1970 +0000
1945 | | date: Thu Jan 01 00:00:00 1970 +0000
1946 | | summary: merge 5 and 4
1946 | | summary: merge 5 and 4
1947 | |
1947 | |
1948 | | diff --git a/e b/e
1948 | | diff --git a/e b/e
1949 | | --- a/e
1949 | | --- a/e
1950 | | +++ b/e
1950 | | +++ b/e
1951 | | @@ -1,1 +1,1 @@
1951 | | @@ -1,1 +1,1 @@
1952 | | -ee
1952 | | -ee
1953 | | +merge
1953 | | +merge
1954 | |
1954 | |
1955 o | changeset: 5:99b31f1c2782
1955 o | changeset: 5:99b31f1c2782
1956 | | parent: 3:5918b8d165d1
1956 | | parent: 3:5918b8d165d1
1957 | | user: test
1957 | | user: test
1958 | | date: Thu Jan 01 00:00:00 1970 +0000
1958 | | date: Thu Jan 01 00:00:00 1970 +0000
1959 | | summary: add another e
1959 | | summary: add another e
1960 | |
1960 | |
1961 | | diff --git a/e b/e
1961 | | diff --git a/e b/e
1962 | | new file mode 100644
1962 | | new file mode 100644
1963 | | --- /dev/null
1963 | | --- /dev/null
1964 | | +++ b/e
1964 | | +++ b/e
1965 | | @@ -0,0 +1,1 @@
1965 | | @@ -0,0 +1,1 @@
1966 | | +ee
1966 | | +ee
1967 | |
1967 | |
1968
1968
1969 Test old-style --rev
1969 Test old-style --rev
1970
1970
1971 $ hg tag 'foo-bar'
1971 $ hg tag 'foo-bar'
1972 $ testlog -r 'foo-bar'
1972 $ testlog -r 'foo-bar'
1973 ['foo-bar']
1973 ['foo-bar']
1974 []
1974 []
1975
1975
1976 Test --follow and forward --rev
1976 Test --follow and forward --rev
1977
1977
1978 $ hg up -q 6
1978 $ hg up -q 6
1979 $ echo g > g
1979 $ echo g > g
1980 $ hg ci -Am 'add g' g
1980 $ hg ci -Am 'add g' g
1981 created new head
1981 created new head
1982 $ hg up -q 2
1982 $ hg up -q 2
1983 $ hg log -G --template "{rev} {desc|firstline}\n"
1983 $ hg log -G --template "{rev} {desc|firstline}\n"
1984 o 8 add g
1984 o 8 add g
1985 |
1985 |
1986 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1986 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1987 |/
1987 |/
1988 o 6 merge 5 and 4
1988 o 6 merge 5 and 4
1989 |\
1989 |\
1990 | o 5 add another e
1990 | o 5 add another e
1991 | |
1991 | |
1992 o | 4 mv dir/b e
1992 o | 4 mv dir/b e
1993 |/
1993 |/
1994 o 3 mv a b; add d
1994 o 3 mv a b; add d
1995 |
1995 |
1996 @ 2 mv b dir/b
1996 @ 2 mv b dir/b
1997 |
1997 |
1998 o 1 copy a b
1998 o 1 copy a b
1999 |
1999 |
2000 o 0 add a
2000 o 0 add a
2001
2001
2002 $ hg archive -r 7 archive
2002 $ hg archive -r 7 archive
2003 $ grep changessincelatesttag archive/.hg_archival.txt
2003 $ grep changessincelatesttag archive/.hg_archival.txt
2004 changessincelatesttag: 1
2004 changessincelatesttag: 1
2005 $ rm -r archive
2005 $ rm -r archive
2006
2006
2007 changessincelatesttag with no prior tag
2007 changessincelatesttag with no prior tag
2008 $ hg archive -r 4 archive
2008 $ hg archive -r 4 archive
2009 $ grep changessincelatesttag archive/.hg_archival.txt
2009 $ grep changessincelatesttag archive/.hg_archival.txt
2010 changessincelatesttag: 5
2010 changessincelatesttag: 5
2011
2011
2012 $ hg export 'all()'
2012 $ hg export 'all()'
2013 # HG changeset patch
2013 # HG changeset patch
2014 # User test
2014 # User test
2015 # Date 0 0
2015 # Date 0 0
2016 # Thu Jan 01 00:00:00 1970 +0000
2016 # Thu Jan 01 00:00:00 1970 +0000
2017 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2017 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2018 # Parent 0000000000000000000000000000000000000000
2018 # Parent 0000000000000000000000000000000000000000
2019 add a
2019 add a
2020
2020
2021 diff -r 000000000000 -r f8035bb17114 a
2021 diff -r 000000000000 -r f8035bb17114 a
2022 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2022 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2023 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2023 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2024 @@ -0,0 +1,1 @@
2024 @@ -0,0 +1,1 @@
2025 +a
2025 +a
2026 diff -r 000000000000 -r f8035bb17114 aa
2026 diff -r 000000000000 -r f8035bb17114 aa
2027 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2027 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2028 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2028 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2029 @@ -0,0 +1,1 @@
2029 @@ -0,0 +1,1 @@
2030 +aa
2030 +aa
2031 diff -r 000000000000 -r f8035bb17114 f
2031 diff -r 000000000000 -r f8035bb17114 f
2032 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2032 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2033 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2033 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2034 @@ -0,0 +1,1 @@
2034 @@ -0,0 +1,1 @@
2035 +f
2035 +f
2036 # HG changeset patch
2036 # HG changeset patch
2037 # User test
2037 # User test
2038 # Date 0 0
2038 # Date 0 0
2039 # Thu Jan 01 00:00:00 1970 +0000
2039 # Thu Jan 01 00:00:00 1970 +0000
2040 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2040 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2041 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2041 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2042 copy a b
2042 copy a b
2043
2043
2044 diff -r f8035bb17114 -r 216d4c92cf98 b
2044 diff -r f8035bb17114 -r 216d4c92cf98 b
2045 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2045 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2046 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2046 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2047 @@ -0,0 +1,1 @@
2047 @@ -0,0 +1,1 @@
2048 +a
2048 +a
2049 diff -r f8035bb17114 -r 216d4c92cf98 g
2049 diff -r f8035bb17114 -r 216d4c92cf98 g
2050 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2050 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2051 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2051 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2052 @@ -0,0 +1,1 @@
2052 @@ -0,0 +1,1 @@
2053 +f
2053 +f
2054 # HG changeset patch
2054 # HG changeset patch
2055 # User test
2055 # User test
2056 # Date 0 0
2056 # Date 0 0
2057 # Thu Jan 01 00:00:00 1970 +0000
2057 # Thu Jan 01 00:00:00 1970 +0000
2058 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2058 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2059 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2059 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2060 mv b dir/b
2060 mv b dir/b
2061
2061
2062 diff -r 216d4c92cf98 -r bb573313a9e8 b
2062 diff -r 216d4c92cf98 -r bb573313a9e8 b
2063 --- a/b Thu Jan 01 00:00:00 1970 +0000
2063 --- a/b Thu Jan 01 00:00:00 1970 +0000
2064 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2064 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2065 @@ -1,1 +0,0 @@
2065 @@ -1,1 +0,0 @@
2066 -a
2066 -a
2067 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2067 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2068 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2068 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2069 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2069 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2070 @@ -0,0 +1,1 @@
2070 @@ -0,0 +1,1 @@
2071 +a
2071 +a
2072 diff -r 216d4c92cf98 -r bb573313a9e8 f
2072 diff -r 216d4c92cf98 -r bb573313a9e8 f
2073 --- a/f Thu Jan 01 00:00:00 1970 +0000
2073 --- a/f Thu Jan 01 00:00:00 1970 +0000
2074 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2074 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2075 @@ -1,1 +1,2 @@
2075 @@ -1,1 +1,2 @@
2076 f
2076 f
2077 +f
2077 +f
2078 diff -r 216d4c92cf98 -r bb573313a9e8 g
2078 diff -r 216d4c92cf98 -r bb573313a9e8 g
2079 --- a/g Thu Jan 01 00:00:00 1970 +0000
2079 --- a/g Thu Jan 01 00:00:00 1970 +0000
2080 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2080 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2081 @@ -1,1 +1,2 @@
2081 @@ -1,1 +1,2 @@
2082 f
2082 f
2083 +g
2083 +g
2084 # HG changeset patch
2084 # HG changeset patch
2085 # User test
2085 # User test
2086 # Date 0 0
2086 # Date 0 0
2087 # Thu Jan 01 00:00:00 1970 +0000
2087 # Thu Jan 01 00:00:00 1970 +0000
2088 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2088 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2089 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2089 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2090 mv a b; add d
2090 mv a b; add d
2091
2091
2092 diff -r bb573313a9e8 -r 5918b8d165d1 a
2092 diff -r bb573313a9e8 -r 5918b8d165d1 a
2093 --- a/a Thu Jan 01 00:00:00 1970 +0000
2093 --- a/a Thu Jan 01 00:00:00 1970 +0000
2094 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2094 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2095 @@ -1,1 +0,0 @@
2095 @@ -1,1 +0,0 @@
2096 -a
2096 -a
2097 diff -r bb573313a9e8 -r 5918b8d165d1 b
2097 diff -r bb573313a9e8 -r 5918b8d165d1 b
2098 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2098 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2099 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2099 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2100 @@ -0,0 +1,1 @@
2100 @@ -0,0 +1,1 @@
2101 +a
2101 +a
2102 diff -r bb573313a9e8 -r 5918b8d165d1 d
2102 diff -r bb573313a9e8 -r 5918b8d165d1 d
2103 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2103 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2104 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2104 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2105 @@ -0,0 +1,1 @@
2105 @@ -0,0 +1,1 @@
2106 +a
2106 +a
2107 diff -r bb573313a9e8 -r 5918b8d165d1 g
2107 diff -r bb573313a9e8 -r 5918b8d165d1 g
2108 --- a/g Thu Jan 01 00:00:00 1970 +0000
2108 --- a/g Thu Jan 01 00:00:00 1970 +0000
2109 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2109 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2110 @@ -1,2 +1,2 @@
2110 @@ -1,2 +1,2 @@
2111 f
2111 f
2112 -g
2112 -g
2113 +f
2113 +f
2114 # HG changeset patch
2114 # HG changeset patch
2115 # User test
2115 # User test
2116 # Date 0 0
2116 # Date 0 0
2117 # Thu Jan 01 00:00:00 1970 +0000
2117 # Thu Jan 01 00:00:00 1970 +0000
2118 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2118 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2119 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2119 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2120 mv dir/b e
2120 mv dir/b e
2121
2121
2122 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2122 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2123 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2123 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2124 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2124 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2125 @@ -1,1 +0,0 @@
2125 @@ -1,1 +0,0 @@
2126 -a
2126 -a
2127 diff -r 5918b8d165d1 -r 17d952250a9d e
2127 diff -r 5918b8d165d1 -r 17d952250a9d e
2128 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2128 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2129 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2129 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2130 @@ -0,0 +1,1 @@
2130 @@ -0,0 +1,1 @@
2131 +a
2131 +a
2132 # HG changeset patch
2132 # HG changeset patch
2133 # User test
2133 # User test
2134 # Date 0 0
2134 # Date 0 0
2135 # Thu Jan 01 00:00:00 1970 +0000
2135 # Thu Jan 01 00:00:00 1970 +0000
2136 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2136 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2137 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2137 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2138 add another e
2138 add another e
2139
2139
2140 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2140 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2141 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2141 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2142 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2142 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2143 @@ -0,0 +1,1 @@
2143 @@ -0,0 +1,1 @@
2144 +ee
2144 +ee
2145 # HG changeset patch
2145 # HG changeset patch
2146 # User test
2146 # User test
2147 # Date 0 0
2147 # Date 0 0
2148 # Thu Jan 01 00:00:00 1970 +0000
2148 # Thu Jan 01 00:00:00 1970 +0000
2149 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2149 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2150 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2150 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2151 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2151 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2152 merge 5 and 4
2152 merge 5 and 4
2153
2153
2154 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2154 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2155 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2155 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2156 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2156 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2157 @@ -1,1 +0,0 @@
2157 @@ -1,1 +0,0 @@
2158 -a
2158 -a
2159 diff -r 99b31f1c2782 -r fc281d8ff18d e
2159 diff -r 99b31f1c2782 -r fc281d8ff18d e
2160 --- a/e Thu Jan 01 00:00:00 1970 +0000
2160 --- a/e Thu Jan 01 00:00:00 1970 +0000
2161 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2161 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2162 @@ -1,1 +1,1 @@
2162 @@ -1,1 +1,1 @@
2163 -ee
2163 -ee
2164 +merge
2164 +merge
2165 # HG changeset patch
2165 # HG changeset patch
2166 # User test
2166 # User test
2167 # Date 0 0
2167 # Date 0 0
2168 # Thu Jan 01 00:00:00 1970 +0000
2168 # Thu Jan 01 00:00:00 1970 +0000
2169 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2169 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2170 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2170 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2171 Added tag foo-bar for changeset fc281d8ff18d
2171 Added tag foo-bar for changeset fc281d8ff18d
2172
2172
2173 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2173 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2174 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2174 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2175 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2175 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2176 @@ -0,0 +1,1 @@
2176 @@ -0,0 +1,1 @@
2177 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2177 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2178 # HG changeset patch
2178 # HG changeset patch
2179 # User test
2179 # User test
2180 # Date 0 0
2180 # Date 0 0
2181 # Thu Jan 01 00:00:00 1970 +0000
2181 # Thu Jan 01 00:00:00 1970 +0000
2182 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2182 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2183 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2183 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2184 add g
2184 add g
2185
2185
2186 diff -r fc281d8ff18d -r 24c2e826ddeb g
2186 diff -r fc281d8ff18d -r 24c2e826ddeb g
2187 --- a/g Thu Jan 01 00:00:00 1970 +0000
2187 --- a/g Thu Jan 01 00:00:00 1970 +0000
2188 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2188 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2189 @@ -1,2 +1,1 @@
2189 @@ -1,2 +1,1 @@
2190 -f
2190 -f
2191 -f
2191 -f
2192 +g
2192 +g
2193 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2193 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2194 ['6', '8', '5', '7', '4']
2194 ['6', '8', '5', '7', '4']
2195 (group
2195 (group
2196 (func
2196 (func
2197 ('symbol', 'descendants')
2197 ('symbol', 'descendants')
2198 (func
2198 (func
2199 ('symbol', 'rev')
2199 ('symbol', 'rev')
2200 ('symbol', '6'))))
2200 ('symbol', '6'))))
2201
2201
2202 Test --follow-first and forward --rev
2202 Test --follow-first and forward --rev
2203
2203
2204 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2204 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2205 ['6', '8', '5', '7', '4']
2205 ['6', '8', '5', '7', '4']
2206 (group
2206 (group
2207 (func
2207 (func
2208 ('symbol', '_firstdescendants')
2208 ('symbol', '_firstdescendants')
2209 (func
2209 (func
2210 ('symbol', 'rev')
2210 ('symbol', 'rev')
2211 ('symbol', '6'))))
2211 ('symbol', '6'))))
2212 --- log.nodes * (glob)
2212 --- log.nodes * (glob)
2213 +++ glog.nodes * (glob)
2213 +++ glog.nodes * (glob)
2214 @@ -1,3 +1,3 @@
2214 @@ -1,3 +1,3 @@
2215 -nodetag 6
2215 -nodetag 6
2216 nodetag 8
2216 nodetag 8
2217 nodetag 7
2217 nodetag 7
2218 +nodetag 6
2218 +nodetag 6
2219
2219
2220 Test --follow and backward --rev
2220 Test --follow and backward --rev
2221
2221
2222 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2222 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2223 ['6', '5', '7', '8', '4']
2223 ['6', '5', '7', '8', '4']
2224 (group
2224 (group
2225 (func
2225 (func
2226 ('symbol', 'ancestors')
2226 ('symbol', 'ancestors')
2227 (func
2227 (func
2228 ('symbol', 'rev')
2228 ('symbol', 'rev')
2229 ('symbol', '6'))))
2229 ('symbol', '6'))))
2230
2230
2231 Test --follow-first and backward --rev
2231 Test --follow-first and backward --rev
2232
2232
2233 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2233 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2234 ['6', '5', '7', '8', '4']
2234 ['6', '5', '7', '8', '4']
2235 (group
2235 (group
2236 (func
2236 (func
2237 ('symbol', '_firstancestors')
2237 ('symbol', '_firstancestors')
2238 (func
2238 (func
2239 ('symbol', 'rev')
2239 ('symbol', 'rev')
2240 ('symbol', '6'))))
2240 ('symbol', '6'))))
2241
2241
2242 Test --follow with --rev of graphlog extension
2242 Test --follow with --rev of graphlog extension
2243
2243
2244 $ hg --config extensions.graphlog= glog -qfr1
2244 $ hg --config extensions.graphlog= glog -qfr1
2245 o 1:216d4c92cf98
2245 o 1:216d4c92cf98
2246 |
2246 |
2247 o 0:f8035bb17114
2247 o 0:f8035bb17114
2248
2248
2249
2249
2250 Test subdir
2250 Test subdir
2251
2251
2252 $ hg up -q 3
2252 $ hg up -q 3
2253 $ cd dir
2253 $ cd dir
2254 $ testlog .
2254 $ testlog .
2255 []
2255 []
2256 (group
2256 (group
2257 (func
2257 (func
2258 ('symbol', '_matchfiles')
2258 ('symbol', '_matchfiles')
2259 (list
2259 (list
2260 ('string', 'r:')
2260 ('string', 'r:')
2261 ('string', 'd:relpath')
2261 ('string', 'd:relpath')
2262 ('string', 'p:.'))))
2262 ('string', 'p:.'))))
2263 $ testlog ../b
2263 $ testlog ../b
2264 []
2264 []
2265 (group
2265 (group
2266 (group
2266 (group
2267 (func
2267 (func
2268 ('symbol', 'filelog')
2268 ('symbol', 'filelog')
2269 ('string', '../b'))))
2269 ('string', '../b'))))
2270 $ testlog -f ../b
2270 $ testlog -f ../b
2271 []
2271 []
2272 (group
2272 (group
2273 (group
2273 (group
2274 (func
2274 (func
2275 ('symbol', 'follow')
2275 ('symbol', 'follow')
2276 ('string', 'b'))))
2276 ('string', 'b'))))
2277 $ cd ..
2277 $ cd ..
2278
2278
2279 Test --hidden
2279 Test --hidden
2280 (enable obsolete)
2280 (enable obsolete)
2281
2281
2282 $ cat >> $HGRCPATH << EOF
2282 $ cat >> $HGRCPATH << EOF
2283 > [experimental]
2283 > [experimental]
2284 > evolution=createmarkers
2284 > evolution=createmarkers
2285 > EOF
2285 > EOF
2286
2286
2287 $ hg debugobsolete `hg id --debug -i -r 8`
2287 $ hg debugobsolete `hg id --debug -i -r 8`
2288 $ testlog
2288 $ testlog
2289 []
2289 []
2290 []
2290 []
2291 $ testlog --hidden
2291 $ testlog --hidden
2292 []
2292 []
2293 []
2293 []
2294 $ hg log -G --template '{rev} {desc}\n'
2294 $ hg log -G --template '{rev} {desc}\n'
2295 o 7 Added tag foo-bar for changeset fc281d8ff18d
2295 o 7 Added tag foo-bar for changeset fc281d8ff18d
2296 |
2296 |
2297 o 6 merge 5 and 4
2297 o 6 merge 5 and 4
2298 |\
2298 |\
2299 | o 5 add another e
2299 | o 5 add another e
2300 | |
2300 | |
2301 o | 4 mv dir/b e
2301 o | 4 mv dir/b e
2302 |/
2302 |/
2303 @ 3 mv a b; add d
2303 @ 3 mv a b; add d
2304 |
2304 |
2305 o 2 mv b dir/b
2305 o 2 mv b dir/b
2306 |
2306 |
2307 o 1 copy a b
2307 o 1 copy a b
2308 |
2308 |
2309 o 0 add a
2309 o 0 add a
2310
2310
2311
2311
2312 A template without trailing newline should do something sane
2312 A template without trailing newline should do something sane
2313
2313
2314 $ hg log -G -r ::2 --template '{rev} {desc}'
2314 $ hg log -G -r ::2 --template '{rev} {desc}'
2315 o 2 mv b dir/b
2315 o 2 mv b dir/b
2316 |
2316 |
2317 o 1 copy a b
2317 o 1 copy a b
2318 |
2318 |
2319 o 0 add a
2319 o 0 add a
2320
2320
2321
2321
2322 Extra newlines must be preserved
2322 Extra newlines must be preserved
2323
2323
2324 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2324 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2325 o
2325 o
2326 | 2 mv b dir/b
2326 | 2 mv b dir/b
2327 |
2327 |
2328 o
2328 o
2329 | 1 copy a b
2329 | 1 copy a b
2330 |
2330 |
2331 o
2331 o
2332 0 add a
2332 0 add a
2333
2333
2334
2334
2335 The almost-empty template should do something sane too ...
2335 The almost-empty template should do something sane too ...
2336
2336
2337 $ hg log -G -r ::2 --template '\n'
2337 $ hg log -G -r ::2 --template '\n'
2338 o
2338 o
2339 |
2339 |
2340 o
2340 o
2341 |
2341 |
2342 o
2342 o
2343
2343
2344
2344
2345 issue3772
2345 issue3772
2346
2346
2347 $ hg log -G -r :null
2347 $ hg log -G -r :null
2348 o changeset: 0:f8035bb17114
2348 o changeset: 0:f8035bb17114
2349 | user: test
2349 | user: test
2350 | date: Thu Jan 01 00:00:00 1970 +0000
2350 | date: Thu Jan 01 00:00:00 1970 +0000
2351 | summary: add a
2351 | summary: add a
2352 |
2352 |
2353 o changeset: -1:000000000000
2353 o changeset: -1:000000000000
2354 user:
2354 user:
2355 date: Thu Jan 01 00:00:00 1970 +0000
2355 date: Thu Jan 01 00:00:00 1970 +0000
2356
2356
2357 $ hg log -G -r null:null
2357 $ hg log -G -r null:null
2358 o changeset: -1:000000000000
2358 o changeset: -1:000000000000
2359 user:
2359 user:
2360 date: Thu Jan 01 00:00:00 1970 +0000
2360 date: Thu Jan 01 00:00:00 1970 +0000
2361
2361
2362
2362
2363 should not draw line down to null due to the magic of fullreposet
2363 should not draw line down to null due to the magic of fullreposet
2364
2364
2365 $ hg log -G -r 'all()' | tail -6
2365 $ hg log -G -r 'all()' | tail -6
2366 |
2366 |
2367 o changeset: 0:f8035bb17114
2367 o changeset: 0:f8035bb17114
2368 user: test
2368 user: test
2369 date: Thu Jan 01 00:00:00 1970 +0000
2369 date: Thu Jan 01 00:00:00 1970 +0000
2370 summary: add a
2370 summary: add a
2371
2371
2372
2372
2373 $ hg log -G -r 'branch(default)' | tail -6
2373 $ hg log -G -r 'branch(default)' | tail -6
2374 |
2374 |
2375 o changeset: 0:f8035bb17114
2375 o changeset: 0:f8035bb17114
2376 user: test
2376 user: test
2377 date: Thu Jan 01 00:00:00 1970 +0000
2377 date: Thu Jan 01 00:00:00 1970 +0000
2378 summary: add a
2378 summary: add a
2379
2379
2380
2380
2381 working-directory revision
2381 working-directory revision
2382
2382
2383 $ hg log -G -qr '. + wdir()'
2383 $ hg log -G -qr '. + wdir()'
2384 o 2147483647:ffffffffffff
2384 o 2147483647:ffffffffffff
2385 |
2385 |
2386 @ 3:5918b8d165d1
2386 @ 3:5918b8d165d1
2387 |
2387 |
2388
2388
2389 node template with changeset_printer:
2389 node template with changeset_printer:
2390
2390
2391 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='{rev}'
2391 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='{rev}'
2392 7 7:02dbb8e276b8
2392 7 7:02dbb8e276b8
2393 |
2393 |
2394 6 6:fc281d8ff18d
2394 6 6:fc281d8ff18d
2395 |\
2395 |\
2396 5 | 5:99b31f1c2782
2396 5 | 5:99b31f1c2782
2397 | |
2397 | |
2398
2398
2399 node template with changeset_templater (shared cache variable):
2399 node template with changeset_templater (shared cache variable):
2400
2400
2401 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2401 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2402 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2402 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2403 o 7 foo-bar+1
2403 o 7 foo-bar+1
2404 |
2404 |
2405 # 6 foo-bar+0
2405 # 6 foo-bar+0
2406 |\
2406 |\
2407 o | 5 null+5
2407 o | 5 null+5
2408 | |
2408 | |
2409
2409
2410 label() should just work in node template:
2411
2412 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2413 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2414 [branch.default|7] [log.node|7:02dbb8e276b8]
2415 |
2416
2410 $ cd ..
2417 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now