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