##// END OF EJS Templates
log: resolve --follow with -rREV in cmdutil.getlogrevs()...
Yuya Nishihara -
r35702:1c929b49 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,3944 +1,3953 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23
23
24 from . import (
24 from . import (
25 bookmarks,
25 bookmarks,
26 changelog,
26 changelog,
27 copies,
27 copies,
28 crecord as crecordmod,
28 crecord as crecordmod,
29 dagop,
29 dagop,
30 dirstateguard,
30 dirstateguard,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 graphmod,
34 graphmod,
35 match as matchmod,
35 match as matchmod,
36 mdiff,
36 mdiff,
37 obsolete,
37 obsolete,
38 patch,
38 patch,
39 pathutil,
39 pathutil,
40 pycompat,
40 pycompat,
41 registrar,
41 registrar,
42 revlog,
42 revlog,
43 revset,
43 revset,
44 revsetlang,
44 revsetlang,
45 scmutil,
45 scmutil,
46 smartset,
46 smartset,
47 templatekw,
47 templatekw,
48 templater,
48 templater,
49 util,
49 util,
50 vfs as vfsmod,
50 vfs as vfsmod,
51 )
51 )
52 stringio = util.stringio
52 stringio = util.stringio
53
53
54 # templates of common command options
54 # templates of common command options
55
55
56 dryrunopts = [
56 dryrunopts = [
57 ('n', 'dry-run', None,
57 ('n', 'dry-run', None,
58 _('do not perform actions, just print output')),
58 _('do not perform actions, just print output')),
59 ]
59 ]
60
60
61 remoteopts = [
61 remoteopts = [
62 ('e', 'ssh', '',
62 ('e', 'ssh', '',
63 _('specify ssh command to use'), _('CMD')),
63 _('specify ssh command to use'), _('CMD')),
64 ('', 'remotecmd', '',
64 ('', 'remotecmd', '',
65 _('specify hg command to run on the remote side'), _('CMD')),
65 _('specify hg command to run on the remote side'), _('CMD')),
66 ('', 'insecure', None,
66 ('', 'insecure', None,
67 _('do not verify server certificate (ignoring web.cacerts config)')),
67 _('do not verify server certificate (ignoring web.cacerts config)')),
68 ]
68 ]
69
69
70 walkopts = [
70 walkopts = [
71 ('I', 'include', [],
71 ('I', 'include', [],
72 _('include names matching the given patterns'), _('PATTERN')),
72 _('include names matching the given patterns'), _('PATTERN')),
73 ('X', 'exclude', [],
73 ('X', 'exclude', [],
74 _('exclude names matching the given patterns'), _('PATTERN')),
74 _('exclude names matching the given patterns'), _('PATTERN')),
75 ]
75 ]
76
76
77 commitopts = [
77 commitopts = [
78 ('m', 'message', '',
78 ('m', 'message', '',
79 _('use text as commit message'), _('TEXT')),
79 _('use text as commit message'), _('TEXT')),
80 ('l', 'logfile', '',
80 ('l', 'logfile', '',
81 _('read commit message from file'), _('FILE')),
81 _('read commit message from file'), _('FILE')),
82 ]
82 ]
83
83
84 commitopts2 = [
84 commitopts2 = [
85 ('d', 'date', '',
85 ('d', 'date', '',
86 _('record the specified date as commit date'), _('DATE')),
86 _('record the specified date as commit date'), _('DATE')),
87 ('u', 'user', '',
87 ('u', 'user', '',
88 _('record the specified user as committer'), _('USER')),
88 _('record the specified user as committer'), _('USER')),
89 ]
89 ]
90
90
91 # hidden for now
91 # hidden for now
92 formatteropts = [
92 formatteropts = [
93 ('T', 'template', '',
93 ('T', 'template', '',
94 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
94 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
95 ]
95 ]
96
96
97 templateopts = [
97 templateopts = [
98 ('', 'style', '',
98 ('', 'style', '',
99 _('display using template map file (DEPRECATED)'), _('STYLE')),
99 _('display using template map file (DEPRECATED)'), _('STYLE')),
100 ('T', 'template', '',
100 ('T', 'template', '',
101 _('display with template'), _('TEMPLATE')),
101 _('display with template'), _('TEMPLATE')),
102 ]
102 ]
103
103
104 logopts = [
104 logopts = [
105 ('p', 'patch', None, _('show patch')),
105 ('p', 'patch', None, _('show patch')),
106 ('g', 'git', None, _('use git extended diff format')),
106 ('g', 'git', None, _('use git extended diff format')),
107 ('l', 'limit', '',
107 ('l', 'limit', '',
108 _('limit number of changes displayed'), _('NUM')),
108 _('limit number of changes displayed'), _('NUM')),
109 ('M', 'no-merges', None, _('do not show merges')),
109 ('M', 'no-merges', None, _('do not show merges')),
110 ('', 'stat', None, _('output diffstat-style summary of changes')),
110 ('', 'stat', None, _('output diffstat-style summary of changes')),
111 ('G', 'graph', None, _("show the revision DAG")),
111 ('G', 'graph', None, _("show the revision DAG")),
112 ] + templateopts
112 ] + templateopts
113
113
114 diffopts = [
114 diffopts = [
115 ('a', 'text', None, _('treat all files as text')),
115 ('a', 'text', None, _('treat all files as text')),
116 ('g', 'git', None, _('use git extended diff format')),
116 ('g', 'git', None, _('use git extended diff format')),
117 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
117 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
118 ('', 'nodates', None, _('omit dates from diff headers'))
118 ('', 'nodates', None, _('omit dates from diff headers'))
119 ]
119 ]
120
120
121 diffwsopts = [
121 diffwsopts = [
122 ('w', 'ignore-all-space', None,
122 ('w', 'ignore-all-space', None,
123 _('ignore white space when comparing lines')),
123 _('ignore white space when comparing lines')),
124 ('b', 'ignore-space-change', None,
124 ('b', 'ignore-space-change', None,
125 _('ignore changes in the amount of white space')),
125 _('ignore changes in the amount of white space')),
126 ('B', 'ignore-blank-lines', None,
126 ('B', 'ignore-blank-lines', None,
127 _('ignore changes whose lines are all blank')),
127 _('ignore changes whose lines are all blank')),
128 ('Z', 'ignore-space-at-eol', None,
128 ('Z', 'ignore-space-at-eol', None,
129 _('ignore changes in whitespace at EOL')),
129 _('ignore changes in whitespace at EOL')),
130 ]
130 ]
131
131
132 diffopts2 = [
132 diffopts2 = [
133 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
133 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
134 ('p', 'show-function', None, _('show which function each change is in')),
134 ('p', 'show-function', None, _('show which function each change is in')),
135 ('', 'reverse', None, _('produce a diff that undoes the changes')),
135 ('', 'reverse', None, _('produce a diff that undoes the changes')),
136 ] + diffwsopts + [
136 ] + diffwsopts + [
137 ('U', 'unified', '',
137 ('U', 'unified', '',
138 _('number of lines of context to show'), _('NUM')),
138 _('number of lines of context to show'), _('NUM')),
139 ('', 'stat', None, _('output diffstat-style summary of changes')),
139 ('', 'stat', None, _('output diffstat-style summary of changes')),
140 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
140 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
141 ]
141 ]
142
142
143 mergetoolopts = [
143 mergetoolopts = [
144 ('t', 'tool', '', _('specify merge tool')),
144 ('t', 'tool', '', _('specify merge tool')),
145 ]
145 ]
146
146
147 similarityopts = [
147 similarityopts = [
148 ('s', 'similarity', '',
148 ('s', 'similarity', '',
149 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
149 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
150 ]
150 ]
151
151
152 subrepoopts = [
152 subrepoopts = [
153 ('S', 'subrepos', None,
153 ('S', 'subrepos', None,
154 _('recurse into subrepositories'))
154 _('recurse into subrepositories'))
155 ]
155 ]
156
156
157 debugrevlogopts = [
157 debugrevlogopts = [
158 ('c', 'changelog', False, _('open changelog')),
158 ('c', 'changelog', False, _('open changelog')),
159 ('m', 'manifest', False, _('open manifest')),
159 ('m', 'manifest', False, _('open manifest')),
160 ('', 'dir', '', _('open directory manifest')),
160 ('', 'dir', '', _('open directory manifest')),
161 ]
161 ]
162
162
163 # special string such that everything below this line will be ingored in the
163 # special string such that everything below this line will be ingored in the
164 # editor text
164 # editor text
165 _linebelow = "^HG: ------------------------ >8 ------------------------$"
165 _linebelow = "^HG: ------------------------ >8 ------------------------$"
166
166
167 def ishunk(x):
167 def ishunk(x):
168 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
168 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
169 return isinstance(x, hunkclasses)
169 return isinstance(x, hunkclasses)
170
170
171 def newandmodified(chunks, originalchunks):
171 def newandmodified(chunks, originalchunks):
172 newlyaddedandmodifiedfiles = set()
172 newlyaddedandmodifiedfiles = set()
173 for chunk in chunks:
173 for chunk in chunks:
174 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
174 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
175 originalchunks:
175 originalchunks:
176 newlyaddedandmodifiedfiles.add(chunk.header.filename())
176 newlyaddedandmodifiedfiles.add(chunk.header.filename())
177 return newlyaddedandmodifiedfiles
177 return newlyaddedandmodifiedfiles
178
178
179 def parsealiases(cmd):
179 def parsealiases(cmd):
180 return cmd.lstrip("^").split("|")
180 return cmd.lstrip("^").split("|")
181
181
182 def setupwrapcolorwrite(ui):
182 def setupwrapcolorwrite(ui):
183 # wrap ui.write so diff output can be labeled/colorized
183 # wrap ui.write so diff output can be labeled/colorized
184 def wrapwrite(orig, *args, **kw):
184 def wrapwrite(orig, *args, **kw):
185 label = kw.pop(r'label', '')
185 label = kw.pop(r'label', '')
186 for chunk, l in patch.difflabel(lambda: args):
186 for chunk, l in patch.difflabel(lambda: args):
187 orig(chunk, label=label + l)
187 orig(chunk, label=label + l)
188
188
189 oldwrite = ui.write
189 oldwrite = ui.write
190 def wrap(*args, **kwargs):
190 def wrap(*args, **kwargs):
191 return wrapwrite(oldwrite, *args, **kwargs)
191 return wrapwrite(oldwrite, *args, **kwargs)
192 setattr(ui, 'write', wrap)
192 setattr(ui, 'write', wrap)
193 return oldwrite
193 return oldwrite
194
194
195 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
195 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
196 if usecurses:
196 if usecurses:
197 if testfile:
197 if testfile:
198 recordfn = crecordmod.testdecorator(testfile,
198 recordfn = crecordmod.testdecorator(testfile,
199 crecordmod.testchunkselector)
199 crecordmod.testchunkselector)
200 else:
200 else:
201 recordfn = crecordmod.chunkselector
201 recordfn = crecordmod.chunkselector
202
202
203 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
203 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
204
204
205 else:
205 else:
206 return patch.filterpatch(ui, originalhunks, operation)
206 return patch.filterpatch(ui, originalhunks, operation)
207
207
208 def recordfilter(ui, originalhunks, operation=None):
208 def recordfilter(ui, originalhunks, operation=None):
209 """ Prompts the user to filter the originalhunks and return a list of
209 """ Prompts the user to filter the originalhunks and return a list of
210 selected hunks.
210 selected hunks.
211 *operation* is used for to build ui messages to indicate the user what
211 *operation* is used for to build ui messages to indicate the user what
212 kind of filtering they are doing: reverting, committing, shelving, etc.
212 kind of filtering they are doing: reverting, committing, shelving, etc.
213 (see patch.filterpatch).
213 (see patch.filterpatch).
214 """
214 """
215 usecurses = crecordmod.checkcurses(ui)
215 usecurses = crecordmod.checkcurses(ui)
216 testfile = ui.config('experimental', 'crecordtest')
216 testfile = ui.config('experimental', 'crecordtest')
217 oldwrite = setupwrapcolorwrite(ui)
217 oldwrite = setupwrapcolorwrite(ui)
218 try:
218 try:
219 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
219 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
220 testfile, operation)
220 testfile, operation)
221 finally:
221 finally:
222 ui.write = oldwrite
222 ui.write = oldwrite
223 return newchunks, newopts
223 return newchunks, newopts
224
224
225 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
225 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
226 filterfn, *pats, **opts):
226 filterfn, *pats, **opts):
227 from . import merge as mergemod
227 from . import merge as mergemod
228 opts = pycompat.byteskwargs(opts)
228 opts = pycompat.byteskwargs(opts)
229 if not ui.interactive():
229 if not ui.interactive():
230 if cmdsuggest:
230 if cmdsuggest:
231 msg = _('running non-interactively, use %s instead') % cmdsuggest
231 msg = _('running non-interactively, use %s instead') % cmdsuggest
232 else:
232 else:
233 msg = _('running non-interactively')
233 msg = _('running non-interactively')
234 raise error.Abort(msg)
234 raise error.Abort(msg)
235
235
236 # make sure username is set before going interactive
236 # make sure username is set before going interactive
237 if not opts.get('user'):
237 if not opts.get('user'):
238 ui.username() # raise exception, username not provided
238 ui.username() # raise exception, username not provided
239
239
240 def recordfunc(ui, repo, message, match, opts):
240 def recordfunc(ui, repo, message, match, opts):
241 """This is generic record driver.
241 """This is generic record driver.
242
242
243 Its job is to interactively filter local changes, and
243 Its job is to interactively filter local changes, and
244 accordingly prepare working directory into a state in which the
244 accordingly prepare working directory into a state in which the
245 job can be delegated to a non-interactive commit command such as
245 job can be delegated to a non-interactive commit command such as
246 'commit' or 'qrefresh'.
246 'commit' or 'qrefresh'.
247
247
248 After the actual job is done by non-interactive command, the
248 After the actual job is done by non-interactive command, the
249 working directory is restored to its original state.
249 working directory is restored to its original state.
250
250
251 In the end we'll record interesting changes, and everything else
251 In the end we'll record interesting changes, and everything else
252 will be left in place, so the user can continue working.
252 will be left in place, so the user can continue working.
253 """
253 """
254
254
255 checkunfinished(repo, commit=True)
255 checkunfinished(repo, commit=True)
256 wctx = repo[None]
256 wctx = repo[None]
257 merge = len(wctx.parents()) > 1
257 merge = len(wctx.parents()) > 1
258 if merge:
258 if merge:
259 raise error.Abort(_('cannot partially commit a merge '
259 raise error.Abort(_('cannot partially commit a merge '
260 '(use "hg commit" instead)'))
260 '(use "hg commit" instead)'))
261
261
262 def fail(f, msg):
262 def fail(f, msg):
263 raise error.Abort('%s: %s' % (f, msg))
263 raise error.Abort('%s: %s' % (f, msg))
264
264
265 force = opts.get('force')
265 force = opts.get('force')
266 if not force:
266 if not force:
267 vdirs = []
267 vdirs = []
268 match.explicitdir = vdirs.append
268 match.explicitdir = vdirs.append
269 match.bad = fail
269 match.bad = fail
270
270
271 status = repo.status(match=match)
271 status = repo.status(match=match)
272 if not force:
272 if not force:
273 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
273 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
274 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
274 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
275 diffopts.nodates = True
275 diffopts.nodates = True
276 diffopts.git = True
276 diffopts.git = True
277 diffopts.showfunc = True
277 diffopts.showfunc = True
278 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
278 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
279 originalchunks = patch.parsepatch(originaldiff)
279 originalchunks = patch.parsepatch(originaldiff)
280
280
281 # 1. filter patch, since we are intending to apply subset of it
281 # 1. filter patch, since we are intending to apply subset of it
282 try:
282 try:
283 chunks, newopts = filterfn(ui, originalchunks)
283 chunks, newopts = filterfn(ui, originalchunks)
284 except error.PatchError as err:
284 except error.PatchError as err:
285 raise error.Abort(_('error parsing patch: %s') % err)
285 raise error.Abort(_('error parsing patch: %s') % err)
286 opts.update(newopts)
286 opts.update(newopts)
287
287
288 # We need to keep a backup of files that have been newly added and
288 # We need to keep a backup of files that have been newly added and
289 # modified during the recording process because there is a previous
289 # modified during the recording process because there is a previous
290 # version without the edit in the workdir
290 # version without the edit in the workdir
291 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
291 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
292 contenders = set()
292 contenders = set()
293 for h in chunks:
293 for h in chunks:
294 try:
294 try:
295 contenders.update(set(h.files()))
295 contenders.update(set(h.files()))
296 except AttributeError:
296 except AttributeError:
297 pass
297 pass
298
298
299 changed = status.modified + status.added + status.removed
299 changed = status.modified + status.added + status.removed
300 newfiles = [f for f in changed if f in contenders]
300 newfiles = [f for f in changed if f in contenders]
301 if not newfiles:
301 if not newfiles:
302 ui.status(_('no changes to record\n'))
302 ui.status(_('no changes to record\n'))
303 return 0
303 return 0
304
304
305 modified = set(status.modified)
305 modified = set(status.modified)
306
306
307 # 2. backup changed files, so we can restore them in the end
307 # 2. backup changed files, so we can restore them in the end
308
308
309 if backupall:
309 if backupall:
310 tobackup = changed
310 tobackup = changed
311 else:
311 else:
312 tobackup = [f for f in newfiles if f in modified or f in \
312 tobackup = [f for f in newfiles if f in modified or f in \
313 newlyaddedandmodifiedfiles]
313 newlyaddedandmodifiedfiles]
314 backups = {}
314 backups = {}
315 if tobackup:
315 if tobackup:
316 backupdir = repo.vfs.join('record-backups')
316 backupdir = repo.vfs.join('record-backups')
317 try:
317 try:
318 os.mkdir(backupdir)
318 os.mkdir(backupdir)
319 except OSError as err:
319 except OSError as err:
320 if err.errno != errno.EEXIST:
320 if err.errno != errno.EEXIST:
321 raise
321 raise
322 try:
322 try:
323 # backup continues
323 # backup continues
324 for f in tobackup:
324 for f in tobackup:
325 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
325 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
326 dir=backupdir)
326 dir=backupdir)
327 os.close(fd)
327 os.close(fd)
328 ui.debug('backup %r as %r\n' % (f, tmpname))
328 ui.debug('backup %r as %r\n' % (f, tmpname))
329 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
329 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
330 backups[f] = tmpname
330 backups[f] = tmpname
331
331
332 fp = stringio()
332 fp = stringio()
333 for c in chunks:
333 for c in chunks:
334 fname = c.filename()
334 fname = c.filename()
335 if fname in backups:
335 if fname in backups:
336 c.write(fp)
336 c.write(fp)
337 dopatch = fp.tell()
337 dopatch = fp.tell()
338 fp.seek(0)
338 fp.seek(0)
339
339
340 # 2.5 optionally review / modify patch in text editor
340 # 2.5 optionally review / modify patch in text editor
341 if opts.get('review', False):
341 if opts.get('review', False):
342 patchtext = (crecordmod.diffhelptext
342 patchtext = (crecordmod.diffhelptext
343 + crecordmod.patchhelptext
343 + crecordmod.patchhelptext
344 + fp.read())
344 + fp.read())
345 reviewedpatch = ui.edit(patchtext, "",
345 reviewedpatch = ui.edit(patchtext, "",
346 action="diff",
346 action="diff",
347 repopath=repo.path)
347 repopath=repo.path)
348 fp.truncate(0)
348 fp.truncate(0)
349 fp.write(reviewedpatch)
349 fp.write(reviewedpatch)
350 fp.seek(0)
350 fp.seek(0)
351
351
352 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
352 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
353 # 3a. apply filtered patch to clean repo (clean)
353 # 3a. apply filtered patch to clean repo (clean)
354 if backups:
354 if backups:
355 # Equivalent to hg.revert
355 # Equivalent to hg.revert
356 m = scmutil.matchfiles(repo, backups.keys())
356 m = scmutil.matchfiles(repo, backups.keys())
357 mergemod.update(repo, repo.dirstate.p1(),
357 mergemod.update(repo, repo.dirstate.p1(),
358 False, True, matcher=m)
358 False, True, matcher=m)
359
359
360 # 3b. (apply)
360 # 3b. (apply)
361 if dopatch:
361 if dopatch:
362 try:
362 try:
363 ui.debug('applying patch\n')
363 ui.debug('applying patch\n')
364 ui.debug(fp.getvalue())
364 ui.debug(fp.getvalue())
365 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
365 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
366 except error.PatchError as err:
366 except error.PatchError as err:
367 raise error.Abort(str(err))
367 raise error.Abort(str(err))
368 del fp
368 del fp
369
369
370 # 4. We prepared working directory according to filtered
370 # 4. We prepared working directory according to filtered
371 # patch. Now is the time to delegate the job to
371 # patch. Now is the time to delegate the job to
372 # commit/qrefresh or the like!
372 # commit/qrefresh or the like!
373
373
374 # Make all of the pathnames absolute.
374 # Make all of the pathnames absolute.
375 newfiles = [repo.wjoin(nf) for nf in newfiles]
375 newfiles = [repo.wjoin(nf) for nf in newfiles]
376 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
376 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
377 finally:
377 finally:
378 # 5. finally restore backed-up files
378 # 5. finally restore backed-up files
379 try:
379 try:
380 dirstate = repo.dirstate
380 dirstate = repo.dirstate
381 for realname, tmpname in backups.iteritems():
381 for realname, tmpname in backups.iteritems():
382 ui.debug('restoring %r to %r\n' % (tmpname, realname))
382 ui.debug('restoring %r to %r\n' % (tmpname, realname))
383
383
384 if dirstate[realname] == 'n':
384 if dirstate[realname] == 'n':
385 # without normallookup, restoring timestamp
385 # without normallookup, restoring timestamp
386 # may cause partially committed files
386 # may cause partially committed files
387 # to be treated as unmodified
387 # to be treated as unmodified
388 dirstate.normallookup(realname)
388 dirstate.normallookup(realname)
389
389
390 # copystat=True here and above are a hack to trick any
390 # copystat=True here and above are a hack to trick any
391 # editors that have f open that we haven't modified them.
391 # editors that have f open that we haven't modified them.
392 #
392 #
393 # Also note that this racy as an editor could notice the
393 # Also note that this racy as an editor could notice the
394 # file's mtime before we've finished writing it.
394 # file's mtime before we've finished writing it.
395 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
395 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
396 os.unlink(tmpname)
396 os.unlink(tmpname)
397 if tobackup:
397 if tobackup:
398 os.rmdir(backupdir)
398 os.rmdir(backupdir)
399 except OSError:
399 except OSError:
400 pass
400 pass
401
401
402 def recordinwlock(ui, repo, message, match, opts):
402 def recordinwlock(ui, repo, message, match, opts):
403 with repo.wlock():
403 with repo.wlock():
404 return recordfunc(ui, repo, message, match, opts)
404 return recordfunc(ui, repo, message, match, opts)
405
405
406 return commit(ui, repo, recordinwlock, pats, opts)
406 return commit(ui, repo, recordinwlock, pats, opts)
407
407
408 class dirnode(object):
408 class dirnode(object):
409 """
409 """
410 Represent a directory in user working copy with information required for
410 Represent a directory in user working copy with information required for
411 the purpose of tersing its status.
411 the purpose of tersing its status.
412
412
413 path is the path to the directory
413 path is the path to the directory
414
414
415 statuses is a set of statuses of all files in this directory (this includes
415 statuses is a set of statuses of all files in this directory (this includes
416 all the files in all the subdirectories too)
416 all the files in all the subdirectories too)
417
417
418 files is a list of files which are direct child of this directory
418 files is a list of files which are direct child of this directory
419
419
420 subdirs is a dictionary of sub-directory name as the key and it's own
420 subdirs is a dictionary of sub-directory name as the key and it's own
421 dirnode object as the value
421 dirnode object as the value
422 """
422 """
423
423
424 def __init__(self, dirpath):
424 def __init__(self, dirpath):
425 self.path = dirpath
425 self.path = dirpath
426 self.statuses = set([])
426 self.statuses = set([])
427 self.files = []
427 self.files = []
428 self.subdirs = {}
428 self.subdirs = {}
429
429
430 def _addfileindir(self, filename, status):
430 def _addfileindir(self, filename, status):
431 """Add a file in this directory as a direct child."""
431 """Add a file in this directory as a direct child."""
432 self.files.append((filename, status))
432 self.files.append((filename, status))
433
433
434 def addfile(self, filename, status):
434 def addfile(self, filename, status):
435 """
435 """
436 Add a file to this directory or to its direct parent directory.
436 Add a file to this directory or to its direct parent directory.
437
437
438 If the file is not direct child of this directory, we traverse to the
438 If the file is not direct child of this directory, we traverse to the
439 directory of which this file is a direct child of and add the file
439 directory of which this file is a direct child of and add the file
440 there.
440 there.
441 """
441 """
442
442
443 # the filename contains a path separator, it means it's not the direct
443 # the filename contains a path separator, it means it's not the direct
444 # child of this directory
444 # child of this directory
445 if '/' in filename:
445 if '/' in filename:
446 subdir, filep = filename.split('/', 1)
446 subdir, filep = filename.split('/', 1)
447
447
448 # does the dirnode object for subdir exists
448 # does the dirnode object for subdir exists
449 if subdir not in self.subdirs:
449 if subdir not in self.subdirs:
450 subdirpath = os.path.join(self.path, subdir)
450 subdirpath = os.path.join(self.path, subdir)
451 self.subdirs[subdir] = dirnode(subdirpath)
451 self.subdirs[subdir] = dirnode(subdirpath)
452
452
453 # try adding the file in subdir
453 # try adding the file in subdir
454 self.subdirs[subdir].addfile(filep, status)
454 self.subdirs[subdir].addfile(filep, status)
455
455
456 else:
456 else:
457 self._addfileindir(filename, status)
457 self._addfileindir(filename, status)
458
458
459 if status not in self.statuses:
459 if status not in self.statuses:
460 self.statuses.add(status)
460 self.statuses.add(status)
461
461
462 def iterfilepaths(self):
462 def iterfilepaths(self):
463 """Yield (status, path) for files directly under this directory."""
463 """Yield (status, path) for files directly under this directory."""
464 for f, st in self.files:
464 for f, st in self.files:
465 yield st, os.path.join(self.path, f)
465 yield st, os.path.join(self.path, f)
466
466
467 def tersewalk(self, terseargs):
467 def tersewalk(self, terseargs):
468 """
468 """
469 Yield (status, path) obtained by processing the status of this
469 Yield (status, path) obtained by processing the status of this
470 dirnode.
470 dirnode.
471
471
472 terseargs is the string of arguments passed by the user with `--terse`
472 terseargs is the string of arguments passed by the user with `--terse`
473 flag.
473 flag.
474
474
475 Following are the cases which can happen:
475 Following are the cases which can happen:
476
476
477 1) All the files in the directory (including all the files in its
477 1) All the files in the directory (including all the files in its
478 subdirectories) share the same status and the user has asked us to terse
478 subdirectories) share the same status and the user has asked us to terse
479 that status. -> yield (status, dirpath)
479 that status. -> yield (status, dirpath)
480
480
481 2) Otherwise, we do following:
481 2) Otherwise, we do following:
482
482
483 a) Yield (status, filepath) for all the files which are in this
483 a) Yield (status, filepath) for all the files which are in this
484 directory (only the ones in this directory, not the subdirs)
484 directory (only the ones in this directory, not the subdirs)
485
485
486 b) Recurse the function on all the subdirectories of this
486 b) Recurse the function on all the subdirectories of this
487 directory
487 directory
488 """
488 """
489
489
490 if len(self.statuses) == 1:
490 if len(self.statuses) == 1:
491 onlyst = self.statuses.pop()
491 onlyst = self.statuses.pop()
492
492
493 # Making sure we terse only when the status abbreviation is
493 # Making sure we terse only when the status abbreviation is
494 # passed as terse argument
494 # passed as terse argument
495 if onlyst in terseargs:
495 if onlyst in terseargs:
496 yield onlyst, self.path + pycompat.ossep
496 yield onlyst, self.path + pycompat.ossep
497 return
497 return
498
498
499 # add the files to status list
499 # add the files to status list
500 for st, fpath in self.iterfilepaths():
500 for st, fpath in self.iterfilepaths():
501 yield st, fpath
501 yield st, fpath
502
502
503 #recurse on the subdirs
503 #recurse on the subdirs
504 for dirobj in self.subdirs.values():
504 for dirobj in self.subdirs.values():
505 for st, fpath in dirobj.tersewalk(terseargs):
505 for st, fpath in dirobj.tersewalk(terseargs):
506 yield st, fpath
506 yield st, fpath
507
507
508 def tersedir(statuslist, terseargs):
508 def tersedir(statuslist, terseargs):
509 """
509 """
510 Terse the status if all the files in a directory shares the same status.
510 Terse the status if all the files in a directory shares the same status.
511
511
512 statuslist is scmutil.status() object which contains a list of files for
512 statuslist is scmutil.status() object which contains a list of files for
513 each status.
513 each status.
514 terseargs is string which is passed by the user as the argument to `--terse`
514 terseargs is string which is passed by the user as the argument to `--terse`
515 flag.
515 flag.
516
516
517 The function makes a tree of objects of dirnode class, and at each node it
517 The function makes a tree of objects of dirnode class, and at each node it
518 stores the information required to know whether we can terse a certain
518 stores the information required to know whether we can terse a certain
519 directory or not.
519 directory or not.
520 """
520 """
521 # the order matters here as that is used to produce final list
521 # the order matters here as that is used to produce final list
522 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
522 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
523
523
524 # checking the argument validity
524 # checking the argument validity
525 for s in pycompat.bytestr(terseargs):
525 for s in pycompat.bytestr(terseargs):
526 if s not in allst:
526 if s not in allst:
527 raise error.Abort(_("'%s' not recognized") % s)
527 raise error.Abort(_("'%s' not recognized") % s)
528
528
529 # creating a dirnode object for the root of the repo
529 # creating a dirnode object for the root of the repo
530 rootobj = dirnode('')
530 rootobj = dirnode('')
531 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
531 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
532 'ignored', 'removed')
532 'ignored', 'removed')
533
533
534 tersedict = {}
534 tersedict = {}
535 for attrname in pstatus:
535 for attrname in pstatus:
536 statuschar = attrname[0:1]
536 statuschar = attrname[0:1]
537 for f in getattr(statuslist, attrname):
537 for f in getattr(statuslist, attrname):
538 rootobj.addfile(f, statuschar)
538 rootobj.addfile(f, statuschar)
539 tersedict[statuschar] = []
539 tersedict[statuschar] = []
540
540
541 # we won't be tersing the root dir, so add files in it
541 # we won't be tersing the root dir, so add files in it
542 for st, fpath in rootobj.iterfilepaths():
542 for st, fpath in rootobj.iterfilepaths():
543 tersedict[st].append(fpath)
543 tersedict[st].append(fpath)
544
544
545 # process each sub-directory and build tersedict
545 # process each sub-directory and build tersedict
546 for subdir in rootobj.subdirs.values():
546 for subdir in rootobj.subdirs.values():
547 for st, f in subdir.tersewalk(terseargs):
547 for st, f in subdir.tersewalk(terseargs):
548 tersedict[st].append(f)
548 tersedict[st].append(f)
549
549
550 tersedlist = []
550 tersedlist = []
551 for st in allst:
551 for st in allst:
552 tersedict[st].sort()
552 tersedict[st].sort()
553 tersedlist.append(tersedict[st])
553 tersedlist.append(tersedict[st])
554
554
555 return tersedlist
555 return tersedlist
556
556
557 def _commentlines(raw):
557 def _commentlines(raw):
558 '''Surround lineswith a comment char and a new line'''
558 '''Surround lineswith a comment char and a new line'''
559 lines = raw.splitlines()
559 lines = raw.splitlines()
560 commentedlines = ['# %s' % line for line in lines]
560 commentedlines = ['# %s' % line for line in lines]
561 return '\n'.join(commentedlines) + '\n'
561 return '\n'.join(commentedlines) + '\n'
562
562
563 def _conflictsmsg(repo):
563 def _conflictsmsg(repo):
564 # avoid merge cycle
564 # avoid merge cycle
565 from . import merge as mergemod
565 from . import merge as mergemod
566 mergestate = mergemod.mergestate.read(repo)
566 mergestate = mergemod.mergestate.read(repo)
567 if not mergestate.active():
567 if not mergestate.active():
568 return
568 return
569
569
570 m = scmutil.match(repo[None])
570 m = scmutil.match(repo[None])
571 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
571 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
572 if unresolvedlist:
572 if unresolvedlist:
573 mergeliststr = '\n'.join(
573 mergeliststr = '\n'.join(
574 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
574 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
575 for path in unresolvedlist])
575 for path in unresolvedlist])
576 msg = _('''Unresolved merge conflicts:
576 msg = _('''Unresolved merge conflicts:
577
577
578 %s
578 %s
579
579
580 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
580 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
581 else:
581 else:
582 msg = _('No unresolved merge conflicts.')
582 msg = _('No unresolved merge conflicts.')
583
583
584 return _commentlines(msg)
584 return _commentlines(msg)
585
585
586 def _helpmessage(continuecmd, abortcmd):
586 def _helpmessage(continuecmd, abortcmd):
587 msg = _('To continue: %s\n'
587 msg = _('To continue: %s\n'
588 'To abort: %s') % (continuecmd, abortcmd)
588 'To abort: %s') % (continuecmd, abortcmd)
589 return _commentlines(msg)
589 return _commentlines(msg)
590
590
591 def _rebasemsg():
591 def _rebasemsg():
592 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
592 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
593
593
594 def _histeditmsg():
594 def _histeditmsg():
595 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
595 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
596
596
597 def _unshelvemsg():
597 def _unshelvemsg():
598 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
598 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
599
599
600 def _updatecleanmsg(dest=None):
600 def _updatecleanmsg(dest=None):
601 warning = _('warning: this will discard uncommitted changes')
601 warning = _('warning: this will discard uncommitted changes')
602 return 'hg update --clean %s (%s)' % (dest or '.', warning)
602 return 'hg update --clean %s (%s)' % (dest or '.', warning)
603
603
604 def _graftmsg():
604 def _graftmsg():
605 # tweakdefaults requires `update` to have a rev hence the `.`
605 # tweakdefaults requires `update` to have a rev hence the `.`
606 return _helpmessage('hg graft --continue', _updatecleanmsg())
606 return _helpmessage('hg graft --continue', _updatecleanmsg())
607
607
608 def _mergemsg():
608 def _mergemsg():
609 # tweakdefaults requires `update` to have a rev hence the `.`
609 # tweakdefaults requires `update` to have a rev hence the `.`
610 return _helpmessage('hg commit', _updatecleanmsg())
610 return _helpmessage('hg commit', _updatecleanmsg())
611
611
612 def _bisectmsg():
612 def _bisectmsg():
613 msg = _('To mark the changeset good: hg bisect --good\n'
613 msg = _('To mark the changeset good: hg bisect --good\n'
614 'To mark the changeset bad: hg bisect --bad\n'
614 'To mark the changeset bad: hg bisect --bad\n'
615 'To abort: hg bisect --reset\n')
615 'To abort: hg bisect --reset\n')
616 return _commentlines(msg)
616 return _commentlines(msg)
617
617
618 def fileexistspredicate(filename):
618 def fileexistspredicate(filename):
619 return lambda repo: repo.vfs.exists(filename)
619 return lambda repo: repo.vfs.exists(filename)
620
620
621 def _mergepredicate(repo):
621 def _mergepredicate(repo):
622 return len(repo[None].parents()) > 1
622 return len(repo[None].parents()) > 1
623
623
624 STATES = (
624 STATES = (
625 # (state, predicate to detect states, helpful message function)
625 # (state, predicate to detect states, helpful message function)
626 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
626 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
627 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
627 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
628 ('graft', fileexistspredicate('graftstate'), _graftmsg),
628 ('graft', fileexistspredicate('graftstate'), _graftmsg),
629 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
629 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
630 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
630 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
631 # The merge state is part of a list that will be iterated over.
631 # The merge state is part of a list that will be iterated over.
632 # They need to be last because some of the other unfinished states may also
632 # They need to be last because some of the other unfinished states may also
633 # be in a merge or update state (eg. rebase, histedit, graft, etc).
633 # be in a merge or update state (eg. rebase, histedit, graft, etc).
634 # We want those to have priority.
634 # We want those to have priority.
635 ('merge', _mergepredicate, _mergemsg),
635 ('merge', _mergepredicate, _mergemsg),
636 )
636 )
637
637
638 def _getrepostate(repo):
638 def _getrepostate(repo):
639 # experimental config: commands.status.skipstates
639 # experimental config: commands.status.skipstates
640 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
640 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
641 for state, statedetectionpredicate, msgfn in STATES:
641 for state, statedetectionpredicate, msgfn in STATES:
642 if state in skip:
642 if state in skip:
643 continue
643 continue
644 if statedetectionpredicate(repo):
644 if statedetectionpredicate(repo):
645 return (state, statedetectionpredicate, msgfn)
645 return (state, statedetectionpredicate, msgfn)
646
646
647 def morestatus(repo, fm):
647 def morestatus(repo, fm):
648 statetuple = _getrepostate(repo)
648 statetuple = _getrepostate(repo)
649 label = 'status.morestatus'
649 label = 'status.morestatus'
650 if statetuple:
650 if statetuple:
651 fm.startitem()
651 fm.startitem()
652 state, statedetectionpredicate, helpfulmsg = statetuple
652 state, statedetectionpredicate, helpfulmsg = statetuple
653 statemsg = _('The repository is in an unfinished *%s* state.') % state
653 statemsg = _('The repository is in an unfinished *%s* state.') % state
654 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
654 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
655 conmsg = _conflictsmsg(repo)
655 conmsg = _conflictsmsg(repo)
656 if conmsg:
656 if conmsg:
657 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
657 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
658 if helpfulmsg:
658 if helpfulmsg:
659 helpmsg = helpfulmsg()
659 helpmsg = helpfulmsg()
660 fm.write('helpmsg', '%s\n', helpmsg, label=label)
660 fm.write('helpmsg', '%s\n', helpmsg, label=label)
661
661
662 def findpossible(cmd, table, strict=False):
662 def findpossible(cmd, table, strict=False):
663 """
663 """
664 Return cmd -> (aliases, command table entry)
664 Return cmd -> (aliases, command table entry)
665 for each matching command.
665 for each matching command.
666 Return debug commands (or their aliases) only if no normal command matches.
666 Return debug commands (or their aliases) only if no normal command matches.
667 """
667 """
668 choice = {}
668 choice = {}
669 debugchoice = {}
669 debugchoice = {}
670
670
671 if cmd in table:
671 if cmd in table:
672 # short-circuit exact matches, "log" alias beats "^log|history"
672 # short-circuit exact matches, "log" alias beats "^log|history"
673 keys = [cmd]
673 keys = [cmd]
674 else:
674 else:
675 keys = table.keys()
675 keys = table.keys()
676
676
677 allcmds = []
677 allcmds = []
678 for e in keys:
678 for e in keys:
679 aliases = parsealiases(e)
679 aliases = parsealiases(e)
680 allcmds.extend(aliases)
680 allcmds.extend(aliases)
681 found = None
681 found = None
682 if cmd in aliases:
682 if cmd in aliases:
683 found = cmd
683 found = cmd
684 elif not strict:
684 elif not strict:
685 for a in aliases:
685 for a in aliases:
686 if a.startswith(cmd):
686 if a.startswith(cmd):
687 found = a
687 found = a
688 break
688 break
689 if found is not None:
689 if found is not None:
690 if aliases[0].startswith("debug") or found.startswith("debug"):
690 if aliases[0].startswith("debug") or found.startswith("debug"):
691 debugchoice[found] = (aliases, table[e])
691 debugchoice[found] = (aliases, table[e])
692 else:
692 else:
693 choice[found] = (aliases, table[e])
693 choice[found] = (aliases, table[e])
694
694
695 if not choice and debugchoice:
695 if not choice and debugchoice:
696 choice = debugchoice
696 choice = debugchoice
697
697
698 return choice, allcmds
698 return choice, allcmds
699
699
700 def findcmd(cmd, table, strict=True):
700 def findcmd(cmd, table, strict=True):
701 """Return (aliases, command table entry) for command string."""
701 """Return (aliases, command table entry) for command string."""
702 choice, allcmds = findpossible(cmd, table, strict)
702 choice, allcmds = findpossible(cmd, table, strict)
703
703
704 if cmd in choice:
704 if cmd in choice:
705 return choice[cmd]
705 return choice[cmd]
706
706
707 if len(choice) > 1:
707 if len(choice) > 1:
708 clist = sorted(choice)
708 clist = sorted(choice)
709 raise error.AmbiguousCommand(cmd, clist)
709 raise error.AmbiguousCommand(cmd, clist)
710
710
711 if choice:
711 if choice:
712 return list(choice.values())[0]
712 return list(choice.values())[0]
713
713
714 raise error.UnknownCommand(cmd, allcmds)
714 raise error.UnknownCommand(cmd, allcmds)
715
715
716 def findrepo(p):
716 def findrepo(p):
717 while not os.path.isdir(os.path.join(p, ".hg")):
717 while not os.path.isdir(os.path.join(p, ".hg")):
718 oldp, p = p, os.path.dirname(p)
718 oldp, p = p, os.path.dirname(p)
719 if p == oldp:
719 if p == oldp:
720 return None
720 return None
721
721
722 return p
722 return p
723
723
724 def bailifchanged(repo, merge=True, hint=None):
724 def bailifchanged(repo, merge=True, hint=None):
725 """ enforce the precondition that working directory must be clean.
725 """ enforce the precondition that working directory must be clean.
726
726
727 'merge' can be set to false if a pending uncommitted merge should be
727 'merge' can be set to false if a pending uncommitted merge should be
728 ignored (such as when 'update --check' runs).
728 ignored (such as when 'update --check' runs).
729
729
730 'hint' is the usual hint given to Abort exception.
730 'hint' is the usual hint given to Abort exception.
731 """
731 """
732
732
733 if merge and repo.dirstate.p2() != nullid:
733 if merge and repo.dirstate.p2() != nullid:
734 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
734 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
735 modified, added, removed, deleted = repo.status()[:4]
735 modified, added, removed, deleted = repo.status()[:4]
736 if modified or added or removed or deleted:
736 if modified or added or removed or deleted:
737 raise error.Abort(_('uncommitted changes'), hint=hint)
737 raise error.Abort(_('uncommitted changes'), hint=hint)
738 ctx = repo[None]
738 ctx = repo[None]
739 for s in sorted(ctx.substate):
739 for s in sorted(ctx.substate):
740 ctx.sub(s).bailifchanged(hint=hint)
740 ctx.sub(s).bailifchanged(hint=hint)
741
741
742 def logmessage(ui, opts):
742 def logmessage(ui, opts):
743 """ get the log message according to -m and -l option """
743 """ get the log message according to -m and -l option """
744 message = opts.get('message')
744 message = opts.get('message')
745 logfile = opts.get('logfile')
745 logfile = opts.get('logfile')
746
746
747 if message and logfile:
747 if message and logfile:
748 raise error.Abort(_('options --message and --logfile are mutually '
748 raise error.Abort(_('options --message and --logfile are mutually '
749 'exclusive'))
749 'exclusive'))
750 if not message and logfile:
750 if not message and logfile:
751 try:
751 try:
752 if isstdiofilename(logfile):
752 if isstdiofilename(logfile):
753 message = ui.fin.read()
753 message = ui.fin.read()
754 else:
754 else:
755 message = '\n'.join(util.readfile(logfile).splitlines())
755 message = '\n'.join(util.readfile(logfile).splitlines())
756 except IOError as inst:
756 except IOError as inst:
757 raise error.Abort(_("can't read commit message '%s': %s") %
757 raise error.Abort(_("can't read commit message '%s': %s") %
758 (logfile, encoding.strtolocal(inst.strerror)))
758 (logfile, encoding.strtolocal(inst.strerror)))
759 return message
759 return message
760
760
761 def mergeeditform(ctxorbool, baseformname):
761 def mergeeditform(ctxorbool, baseformname):
762 """return appropriate editform name (referencing a committemplate)
762 """return appropriate editform name (referencing a committemplate)
763
763
764 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
764 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
765 merging is committed.
765 merging is committed.
766
766
767 This returns baseformname with '.merge' appended if it is a merge,
767 This returns baseformname with '.merge' appended if it is a merge,
768 otherwise '.normal' is appended.
768 otherwise '.normal' is appended.
769 """
769 """
770 if isinstance(ctxorbool, bool):
770 if isinstance(ctxorbool, bool):
771 if ctxorbool:
771 if ctxorbool:
772 return baseformname + ".merge"
772 return baseformname + ".merge"
773 elif 1 < len(ctxorbool.parents()):
773 elif 1 < len(ctxorbool.parents()):
774 return baseformname + ".merge"
774 return baseformname + ".merge"
775
775
776 return baseformname + ".normal"
776 return baseformname + ".normal"
777
777
778 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
778 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
779 editform='', **opts):
779 editform='', **opts):
780 """get appropriate commit message editor according to '--edit' option
780 """get appropriate commit message editor according to '--edit' option
781
781
782 'finishdesc' is a function to be called with edited commit message
782 'finishdesc' is a function to be called with edited commit message
783 (= 'description' of the new changeset) just after editing, but
783 (= 'description' of the new changeset) just after editing, but
784 before checking empty-ness. It should return actual text to be
784 before checking empty-ness. It should return actual text to be
785 stored into history. This allows to change description before
785 stored into history. This allows to change description before
786 storing.
786 storing.
787
787
788 'extramsg' is a extra message to be shown in the editor instead of
788 'extramsg' is a extra message to be shown in the editor instead of
789 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
789 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
790 is automatically added.
790 is automatically added.
791
791
792 'editform' is a dot-separated list of names, to distinguish
792 'editform' is a dot-separated list of names, to distinguish
793 the purpose of commit text editing.
793 the purpose of commit text editing.
794
794
795 'getcommiteditor' returns 'commitforceeditor' regardless of
795 'getcommiteditor' returns 'commitforceeditor' regardless of
796 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
796 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
797 they are specific for usage in MQ.
797 they are specific for usage in MQ.
798 """
798 """
799 if edit or finishdesc or extramsg:
799 if edit or finishdesc or extramsg:
800 return lambda r, c, s: commitforceeditor(r, c, s,
800 return lambda r, c, s: commitforceeditor(r, c, s,
801 finishdesc=finishdesc,
801 finishdesc=finishdesc,
802 extramsg=extramsg,
802 extramsg=extramsg,
803 editform=editform)
803 editform=editform)
804 elif editform:
804 elif editform:
805 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
805 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
806 else:
806 else:
807 return commiteditor
807 return commiteditor
808
808
809 def loglimit(opts):
809 def loglimit(opts):
810 """get the log limit according to option -l/--limit"""
810 """get the log limit according to option -l/--limit"""
811 limit = opts.get('limit')
811 limit = opts.get('limit')
812 if limit:
812 if limit:
813 try:
813 try:
814 limit = int(limit)
814 limit = int(limit)
815 except ValueError:
815 except ValueError:
816 raise error.Abort(_('limit must be a positive integer'))
816 raise error.Abort(_('limit must be a positive integer'))
817 if limit <= 0:
817 if limit <= 0:
818 raise error.Abort(_('limit must be positive'))
818 raise error.Abort(_('limit must be positive'))
819 else:
819 else:
820 limit = None
820 limit = None
821 return limit
821 return limit
822
822
823 def makefilename(repo, pat, node, desc=None,
823 def makefilename(repo, pat, node, desc=None,
824 total=None, seqno=None, revwidth=None, pathname=None):
824 total=None, seqno=None, revwidth=None, pathname=None):
825 node_expander = {
825 node_expander = {
826 'H': lambda: hex(node),
826 'H': lambda: hex(node),
827 'R': lambda: '%d' % repo.changelog.rev(node),
827 'R': lambda: '%d' % repo.changelog.rev(node),
828 'h': lambda: short(node),
828 'h': lambda: short(node),
829 'm': lambda: re.sub('[^\w]', '_', desc or '')
829 'm': lambda: re.sub('[^\w]', '_', desc or '')
830 }
830 }
831 expander = {
831 expander = {
832 '%': lambda: '%',
832 '%': lambda: '%',
833 'b': lambda: os.path.basename(repo.root),
833 'b': lambda: os.path.basename(repo.root),
834 }
834 }
835
835
836 try:
836 try:
837 if node:
837 if node:
838 expander.update(node_expander)
838 expander.update(node_expander)
839 if node:
839 if node:
840 expander['r'] = (lambda:
840 expander['r'] = (lambda:
841 ('%d' % repo.changelog.rev(node)).zfill(revwidth or 0))
841 ('%d' % repo.changelog.rev(node)).zfill(revwidth or 0))
842 if total is not None:
842 if total is not None:
843 expander['N'] = lambda: '%d' % total
843 expander['N'] = lambda: '%d' % total
844 if seqno is not None:
844 if seqno is not None:
845 expander['n'] = lambda: '%d' % seqno
845 expander['n'] = lambda: '%d' % seqno
846 if total is not None and seqno is not None:
846 if total is not None and seqno is not None:
847 expander['n'] = (lambda: ('%d' % seqno).zfill(len('%d' % total)))
847 expander['n'] = (lambda: ('%d' % seqno).zfill(len('%d' % total)))
848 if pathname is not None:
848 if pathname is not None:
849 expander['s'] = lambda: os.path.basename(pathname)
849 expander['s'] = lambda: os.path.basename(pathname)
850 expander['d'] = lambda: os.path.dirname(pathname) or '.'
850 expander['d'] = lambda: os.path.dirname(pathname) or '.'
851 expander['p'] = lambda: pathname
851 expander['p'] = lambda: pathname
852
852
853 newname = []
853 newname = []
854 patlen = len(pat)
854 patlen = len(pat)
855 i = 0
855 i = 0
856 while i < patlen:
856 while i < patlen:
857 c = pat[i:i + 1]
857 c = pat[i:i + 1]
858 if c == '%':
858 if c == '%':
859 i += 1
859 i += 1
860 c = pat[i:i + 1]
860 c = pat[i:i + 1]
861 c = expander[c]()
861 c = expander[c]()
862 newname.append(c)
862 newname.append(c)
863 i += 1
863 i += 1
864 return ''.join(newname)
864 return ''.join(newname)
865 except KeyError as inst:
865 except KeyError as inst:
866 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
866 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
867 inst.args[0])
867 inst.args[0])
868
868
869 def isstdiofilename(pat):
869 def isstdiofilename(pat):
870 """True if the given pat looks like a filename denoting stdin/stdout"""
870 """True if the given pat looks like a filename denoting stdin/stdout"""
871 return not pat or pat == '-'
871 return not pat or pat == '-'
872
872
873 class _unclosablefile(object):
873 class _unclosablefile(object):
874 def __init__(self, fp):
874 def __init__(self, fp):
875 self._fp = fp
875 self._fp = fp
876
876
877 def close(self):
877 def close(self):
878 pass
878 pass
879
879
880 def __iter__(self):
880 def __iter__(self):
881 return iter(self._fp)
881 return iter(self._fp)
882
882
883 def __getattr__(self, attr):
883 def __getattr__(self, attr):
884 return getattr(self._fp, attr)
884 return getattr(self._fp, attr)
885
885
886 def __enter__(self):
886 def __enter__(self):
887 return self
887 return self
888
888
889 def __exit__(self, exc_type, exc_value, exc_tb):
889 def __exit__(self, exc_type, exc_value, exc_tb):
890 pass
890 pass
891
891
892 def makefileobj(repo, pat, node=None, desc=None, total=None,
892 def makefileobj(repo, pat, node=None, desc=None, total=None,
893 seqno=None, revwidth=None, mode='wb', modemap=None,
893 seqno=None, revwidth=None, mode='wb', modemap=None,
894 pathname=None):
894 pathname=None):
895
895
896 writable = mode not in ('r', 'rb')
896 writable = mode not in ('r', 'rb')
897
897
898 if isstdiofilename(pat):
898 if isstdiofilename(pat):
899 if writable:
899 if writable:
900 fp = repo.ui.fout
900 fp = repo.ui.fout
901 else:
901 else:
902 fp = repo.ui.fin
902 fp = repo.ui.fin
903 return _unclosablefile(fp)
903 return _unclosablefile(fp)
904 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
904 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
905 if modemap is not None:
905 if modemap is not None:
906 mode = modemap.get(fn, mode)
906 mode = modemap.get(fn, mode)
907 if mode == 'wb':
907 if mode == 'wb':
908 modemap[fn] = 'ab'
908 modemap[fn] = 'ab'
909 return open(fn, mode)
909 return open(fn, mode)
910
910
911 def openrevlog(repo, cmd, file_, opts):
911 def openrevlog(repo, cmd, file_, opts):
912 """opens the changelog, manifest, a filelog or a given revlog"""
912 """opens the changelog, manifest, a filelog or a given revlog"""
913 cl = opts['changelog']
913 cl = opts['changelog']
914 mf = opts['manifest']
914 mf = opts['manifest']
915 dir = opts['dir']
915 dir = opts['dir']
916 msg = None
916 msg = None
917 if cl and mf:
917 if cl and mf:
918 msg = _('cannot specify --changelog and --manifest at the same time')
918 msg = _('cannot specify --changelog and --manifest at the same time')
919 elif cl and dir:
919 elif cl and dir:
920 msg = _('cannot specify --changelog and --dir at the same time')
920 msg = _('cannot specify --changelog and --dir at the same time')
921 elif cl or mf or dir:
921 elif cl or mf or dir:
922 if file_:
922 if file_:
923 msg = _('cannot specify filename with --changelog or --manifest')
923 msg = _('cannot specify filename with --changelog or --manifest')
924 elif not repo:
924 elif not repo:
925 msg = _('cannot specify --changelog or --manifest or --dir '
925 msg = _('cannot specify --changelog or --manifest or --dir '
926 'without a repository')
926 'without a repository')
927 if msg:
927 if msg:
928 raise error.Abort(msg)
928 raise error.Abort(msg)
929
929
930 r = None
930 r = None
931 if repo:
931 if repo:
932 if cl:
932 if cl:
933 r = repo.unfiltered().changelog
933 r = repo.unfiltered().changelog
934 elif dir:
934 elif dir:
935 if 'treemanifest' not in repo.requirements:
935 if 'treemanifest' not in repo.requirements:
936 raise error.Abort(_("--dir can only be used on repos with "
936 raise error.Abort(_("--dir can only be used on repos with "
937 "treemanifest enabled"))
937 "treemanifest enabled"))
938 dirlog = repo.manifestlog._revlog.dirlog(dir)
938 dirlog = repo.manifestlog._revlog.dirlog(dir)
939 if len(dirlog):
939 if len(dirlog):
940 r = dirlog
940 r = dirlog
941 elif mf:
941 elif mf:
942 r = repo.manifestlog._revlog
942 r = repo.manifestlog._revlog
943 elif file_:
943 elif file_:
944 filelog = repo.file(file_)
944 filelog = repo.file(file_)
945 if len(filelog):
945 if len(filelog):
946 r = filelog
946 r = filelog
947 if not r:
947 if not r:
948 if not file_:
948 if not file_:
949 raise error.CommandError(cmd, _('invalid arguments'))
949 raise error.CommandError(cmd, _('invalid arguments'))
950 if not os.path.isfile(file_):
950 if not os.path.isfile(file_):
951 raise error.Abort(_("revlog '%s' not found") % file_)
951 raise error.Abort(_("revlog '%s' not found") % file_)
952 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
952 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
953 file_[:-2] + ".i")
953 file_[:-2] + ".i")
954 return r
954 return r
955
955
956 def copy(ui, repo, pats, opts, rename=False):
956 def copy(ui, repo, pats, opts, rename=False):
957 # called with the repo lock held
957 # called with the repo lock held
958 #
958 #
959 # hgsep => pathname that uses "/" to separate directories
959 # hgsep => pathname that uses "/" to separate directories
960 # ossep => pathname that uses os.sep to separate directories
960 # ossep => pathname that uses os.sep to separate directories
961 cwd = repo.getcwd()
961 cwd = repo.getcwd()
962 targets = {}
962 targets = {}
963 after = opts.get("after")
963 after = opts.get("after")
964 dryrun = opts.get("dry_run")
964 dryrun = opts.get("dry_run")
965 wctx = repo[None]
965 wctx = repo[None]
966
966
967 def walkpat(pat):
967 def walkpat(pat):
968 srcs = []
968 srcs = []
969 if after:
969 if after:
970 badstates = '?'
970 badstates = '?'
971 else:
971 else:
972 badstates = '?r'
972 badstates = '?r'
973 m = scmutil.match(wctx, [pat], opts, globbed=True)
973 m = scmutil.match(wctx, [pat], opts, globbed=True)
974 for abs in wctx.walk(m):
974 for abs in wctx.walk(m):
975 state = repo.dirstate[abs]
975 state = repo.dirstate[abs]
976 rel = m.rel(abs)
976 rel = m.rel(abs)
977 exact = m.exact(abs)
977 exact = m.exact(abs)
978 if state in badstates:
978 if state in badstates:
979 if exact and state == '?':
979 if exact and state == '?':
980 ui.warn(_('%s: not copying - file is not managed\n') % rel)
980 ui.warn(_('%s: not copying - file is not managed\n') % rel)
981 if exact and state == 'r':
981 if exact and state == 'r':
982 ui.warn(_('%s: not copying - file has been marked for'
982 ui.warn(_('%s: not copying - file has been marked for'
983 ' remove\n') % rel)
983 ' remove\n') % rel)
984 continue
984 continue
985 # abs: hgsep
985 # abs: hgsep
986 # rel: ossep
986 # rel: ossep
987 srcs.append((abs, rel, exact))
987 srcs.append((abs, rel, exact))
988 return srcs
988 return srcs
989
989
990 # abssrc: hgsep
990 # abssrc: hgsep
991 # relsrc: ossep
991 # relsrc: ossep
992 # otarget: ossep
992 # otarget: ossep
993 def copyfile(abssrc, relsrc, otarget, exact):
993 def copyfile(abssrc, relsrc, otarget, exact):
994 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
994 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
995 if '/' in abstarget:
995 if '/' in abstarget:
996 # We cannot normalize abstarget itself, this would prevent
996 # We cannot normalize abstarget itself, this would prevent
997 # case only renames, like a => A.
997 # case only renames, like a => A.
998 abspath, absname = abstarget.rsplit('/', 1)
998 abspath, absname = abstarget.rsplit('/', 1)
999 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
999 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1000 reltarget = repo.pathto(abstarget, cwd)
1000 reltarget = repo.pathto(abstarget, cwd)
1001 target = repo.wjoin(abstarget)
1001 target = repo.wjoin(abstarget)
1002 src = repo.wjoin(abssrc)
1002 src = repo.wjoin(abssrc)
1003 state = repo.dirstate[abstarget]
1003 state = repo.dirstate[abstarget]
1004
1004
1005 scmutil.checkportable(ui, abstarget)
1005 scmutil.checkportable(ui, abstarget)
1006
1006
1007 # check for collisions
1007 # check for collisions
1008 prevsrc = targets.get(abstarget)
1008 prevsrc = targets.get(abstarget)
1009 if prevsrc is not None:
1009 if prevsrc is not None:
1010 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1010 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1011 (reltarget, repo.pathto(abssrc, cwd),
1011 (reltarget, repo.pathto(abssrc, cwd),
1012 repo.pathto(prevsrc, cwd)))
1012 repo.pathto(prevsrc, cwd)))
1013 return
1013 return
1014
1014
1015 # check for overwrites
1015 # check for overwrites
1016 exists = os.path.lexists(target)
1016 exists = os.path.lexists(target)
1017 samefile = False
1017 samefile = False
1018 if exists and abssrc != abstarget:
1018 if exists and abssrc != abstarget:
1019 if (repo.dirstate.normalize(abssrc) ==
1019 if (repo.dirstate.normalize(abssrc) ==
1020 repo.dirstate.normalize(abstarget)):
1020 repo.dirstate.normalize(abstarget)):
1021 if not rename:
1021 if not rename:
1022 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1022 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1023 return
1023 return
1024 exists = False
1024 exists = False
1025 samefile = True
1025 samefile = True
1026
1026
1027 if not after and exists or after and state in 'mn':
1027 if not after and exists or after and state in 'mn':
1028 if not opts['force']:
1028 if not opts['force']:
1029 if state in 'mn':
1029 if state in 'mn':
1030 msg = _('%s: not overwriting - file already committed\n')
1030 msg = _('%s: not overwriting - file already committed\n')
1031 if after:
1031 if after:
1032 flags = '--after --force'
1032 flags = '--after --force'
1033 else:
1033 else:
1034 flags = '--force'
1034 flags = '--force'
1035 if rename:
1035 if rename:
1036 hint = _('(hg rename %s to replace the file by '
1036 hint = _('(hg rename %s to replace the file by '
1037 'recording a rename)\n') % flags
1037 'recording a rename)\n') % flags
1038 else:
1038 else:
1039 hint = _('(hg copy %s to replace the file by '
1039 hint = _('(hg copy %s to replace the file by '
1040 'recording a copy)\n') % flags
1040 'recording a copy)\n') % flags
1041 else:
1041 else:
1042 msg = _('%s: not overwriting - file exists\n')
1042 msg = _('%s: not overwriting - file exists\n')
1043 if rename:
1043 if rename:
1044 hint = _('(hg rename --after to record the rename)\n')
1044 hint = _('(hg rename --after to record the rename)\n')
1045 else:
1045 else:
1046 hint = _('(hg copy --after to record the copy)\n')
1046 hint = _('(hg copy --after to record the copy)\n')
1047 ui.warn(msg % reltarget)
1047 ui.warn(msg % reltarget)
1048 ui.warn(hint)
1048 ui.warn(hint)
1049 return
1049 return
1050
1050
1051 if after:
1051 if after:
1052 if not exists:
1052 if not exists:
1053 if rename:
1053 if rename:
1054 ui.warn(_('%s: not recording move - %s does not exist\n') %
1054 ui.warn(_('%s: not recording move - %s does not exist\n') %
1055 (relsrc, reltarget))
1055 (relsrc, reltarget))
1056 else:
1056 else:
1057 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1057 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1058 (relsrc, reltarget))
1058 (relsrc, reltarget))
1059 return
1059 return
1060 elif not dryrun:
1060 elif not dryrun:
1061 try:
1061 try:
1062 if exists:
1062 if exists:
1063 os.unlink(target)
1063 os.unlink(target)
1064 targetdir = os.path.dirname(target) or '.'
1064 targetdir = os.path.dirname(target) or '.'
1065 if not os.path.isdir(targetdir):
1065 if not os.path.isdir(targetdir):
1066 os.makedirs(targetdir)
1066 os.makedirs(targetdir)
1067 if samefile:
1067 if samefile:
1068 tmp = target + "~hgrename"
1068 tmp = target + "~hgrename"
1069 os.rename(src, tmp)
1069 os.rename(src, tmp)
1070 os.rename(tmp, target)
1070 os.rename(tmp, target)
1071 else:
1071 else:
1072 util.copyfile(src, target)
1072 util.copyfile(src, target)
1073 srcexists = True
1073 srcexists = True
1074 except IOError as inst:
1074 except IOError as inst:
1075 if inst.errno == errno.ENOENT:
1075 if inst.errno == errno.ENOENT:
1076 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1076 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1077 srcexists = False
1077 srcexists = False
1078 else:
1078 else:
1079 ui.warn(_('%s: cannot copy - %s\n') %
1079 ui.warn(_('%s: cannot copy - %s\n') %
1080 (relsrc, encoding.strtolocal(inst.strerror)))
1080 (relsrc, encoding.strtolocal(inst.strerror)))
1081 return True # report a failure
1081 return True # report a failure
1082
1082
1083 if ui.verbose or not exact:
1083 if ui.verbose or not exact:
1084 if rename:
1084 if rename:
1085 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1085 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1086 else:
1086 else:
1087 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1087 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1088
1088
1089 targets[abstarget] = abssrc
1089 targets[abstarget] = abssrc
1090
1090
1091 # fix up dirstate
1091 # fix up dirstate
1092 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1092 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1093 dryrun=dryrun, cwd=cwd)
1093 dryrun=dryrun, cwd=cwd)
1094 if rename and not dryrun:
1094 if rename and not dryrun:
1095 if not after and srcexists and not samefile:
1095 if not after and srcexists and not samefile:
1096 repo.wvfs.unlinkpath(abssrc)
1096 repo.wvfs.unlinkpath(abssrc)
1097 wctx.forget([abssrc])
1097 wctx.forget([abssrc])
1098
1098
1099 # pat: ossep
1099 # pat: ossep
1100 # dest ossep
1100 # dest ossep
1101 # srcs: list of (hgsep, hgsep, ossep, bool)
1101 # srcs: list of (hgsep, hgsep, ossep, bool)
1102 # return: function that takes hgsep and returns ossep
1102 # return: function that takes hgsep and returns ossep
1103 def targetpathfn(pat, dest, srcs):
1103 def targetpathfn(pat, dest, srcs):
1104 if os.path.isdir(pat):
1104 if os.path.isdir(pat):
1105 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1105 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1106 abspfx = util.localpath(abspfx)
1106 abspfx = util.localpath(abspfx)
1107 if destdirexists:
1107 if destdirexists:
1108 striplen = len(os.path.split(abspfx)[0])
1108 striplen = len(os.path.split(abspfx)[0])
1109 else:
1109 else:
1110 striplen = len(abspfx)
1110 striplen = len(abspfx)
1111 if striplen:
1111 if striplen:
1112 striplen += len(pycompat.ossep)
1112 striplen += len(pycompat.ossep)
1113 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1113 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1114 elif destdirexists:
1114 elif destdirexists:
1115 res = lambda p: os.path.join(dest,
1115 res = lambda p: os.path.join(dest,
1116 os.path.basename(util.localpath(p)))
1116 os.path.basename(util.localpath(p)))
1117 else:
1117 else:
1118 res = lambda p: dest
1118 res = lambda p: dest
1119 return res
1119 return res
1120
1120
1121 # pat: ossep
1121 # pat: ossep
1122 # dest ossep
1122 # dest ossep
1123 # srcs: list of (hgsep, hgsep, ossep, bool)
1123 # srcs: list of (hgsep, hgsep, ossep, bool)
1124 # return: function that takes hgsep and returns ossep
1124 # return: function that takes hgsep and returns ossep
1125 def targetpathafterfn(pat, dest, srcs):
1125 def targetpathafterfn(pat, dest, srcs):
1126 if matchmod.patkind(pat):
1126 if matchmod.patkind(pat):
1127 # a mercurial pattern
1127 # a mercurial pattern
1128 res = lambda p: os.path.join(dest,
1128 res = lambda p: os.path.join(dest,
1129 os.path.basename(util.localpath(p)))
1129 os.path.basename(util.localpath(p)))
1130 else:
1130 else:
1131 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1131 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1132 if len(abspfx) < len(srcs[0][0]):
1132 if len(abspfx) < len(srcs[0][0]):
1133 # A directory. Either the target path contains the last
1133 # A directory. Either the target path contains the last
1134 # component of the source path or it does not.
1134 # component of the source path or it does not.
1135 def evalpath(striplen):
1135 def evalpath(striplen):
1136 score = 0
1136 score = 0
1137 for s in srcs:
1137 for s in srcs:
1138 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1138 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1139 if os.path.lexists(t):
1139 if os.path.lexists(t):
1140 score += 1
1140 score += 1
1141 return score
1141 return score
1142
1142
1143 abspfx = util.localpath(abspfx)
1143 abspfx = util.localpath(abspfx)
1144 striplen = len(abspfx)
1144 striplen = len(abspfx)
1145 if striplen:
1145 if striplen:
1146 striplen += len(pycompat.ossep)
1146 striplen += len(pycompat.ossep)
1147 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1147 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1148 score = evalpath(striplen)
1148 score = evalpath(striplen)
1149 striplen1 = len(os.path.split(abspfx)[0])
1149 striplen1 = len(os.path.split(abspfx)[0])
1150 if striplen1:
1150 if striplen1:
1151 striplen1 += len(pycompat.ossep)
1151 striplen1 += len(pycompat.ossep)
1152 if evalpath(striplen1) > score:
1152 if evalpath(striplen1) > score:
1153 striplen = striplen1
1153 striplen = striplen1
1154 res = lambda p: os.path.join(dest,
1154 res = lambda p: os.path.join(dest,
1155 util.localpath(p)[striplen:])
1155 util.localpath(p)[striplen:])
1156 else:
1156 else:
1157 # a file
1157 # a file
1158 if destdirexists:
1158 if destdirexists:
1159 res = lambda p: os.path.join(dest,
1159 res = lambda p: os.path.join(dest,
1160 os.path.basename(util.localpath(p)))
1160 os.path.basename(util.localpath(p)))
1161 else:
1161 else:
1162 res = lambda p: dest
1162 res = lambda p: dest
1163 return res
1163 return res
1164
1164
1165 pats = scmutil.expandpats(pats)
1165 pats = scmutil.expandpats(pats)
1166 if not pats:
1166 if not pats:
1167 raise error.Abort(_('no source or destination specified'))
1167 raise error.Abort(_('no source or destination specified'))
1168 if len(pats) == 1:
1168 if len(pats) == 1:
1169 raise error.Abort(_('no destination specified'))
1169 raise error.Abort(_('no destination specified'))
1170 dest = pats.pop()
1170 dest = pats.pop()
1171 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1171 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1172 if not destdirexists:
1172 if not destdirexists:
1173 if len(pats) > 1 or matchmod.patkind(pats[0]):
1173 if len(pats) > 1 or matchmod.patkind(pats[0]):
1174 raise error.Abort(_('with multiple sources, destination must be an '
1174 raise error.Abort(_('with multiple sources, destination must be an '
1175 'existing directory'))
1175 'existing directory'))
1176 if util.endswithsep(dest):
1176 if util.endswithsep(dest):
1177 raise error.Abort(_('destination %s is not a directory') % dest)
1177 raise error.Abort(_('destination %s is not a directory') % dest)
1178
1178
1179 tfn = targetpathfn
1179 tfn = targetpathfn
1180 if after:
1180 if after:
1181 tfn = targetpathafterfn
1181 tfn = targetpathafterfn
1182 copylist = []
1182 copylist = []
1183 for pat in pats:
1183 for pat in pats:
1184 srcs = walkpat(pat)
1184 srcs = walkpat(pat)
1185 if not srcs:
1185 if not srcs:
1186 continue
1186 continue
1187 copylist.append((tfn(pat, dest, srcs), srcs))
1187 copylist.append((tfn(pat, dest, srcs), srcs))
1188 if not copylist:
1188 if not copylist:
1189 raise error.Abort(_('no files to copy'))
1189 raise error.Abort(_('no files to copy'))
1190
1190
1191 errors = 0
1191 errors = 0
1192 for targetpath, srcs in copylist:
1192 for targetpath, srcs in copylist:
1193 for abssrc, relsrc, exact in srcs:
1193 for abssrc, relsrc, exact in srcs:
1194 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1194 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1195 errors += 1
1195 errors += 1
1196
1196
1197 if errors:
1197 if errors:
1198 ui.warn(_('(consider using --after)\n'))
1198 ui.warn(_('(consider using --after)\n'))
1199
1199
1200 return errors != 0
1200 return errors != 0
1201
1201
1202 ## facility to let extension process additional data into an import patch
1202 ## facility to let extension process additional data into an import patch
1203 # list of identifier to be executed in order
1203 # list of identifier to be executed in order
1204 extrapreimport = [] # run before commit
1204 extrapreimport = [] # run before commit
1205 extrapostimport = [] # run after commit
1205 extrapostimport = [] # run after commit
1206 # mapping from identifier to actual import function
1206 # mapping from identifier to actual import function
1207 #
1207 #
1208 # 'preimport' are run before the commit is made and are provided the following
1208 # 'preimport' are run before the commit is made and are provided the following
1209 # arguments:
1209 # arguments:
1210 # - repo: the localrepository instance,
1210 # - repo: the localrepository instance,
1211 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1211 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1212 # - extra: the future extra dictionary of the changeset, please mutate it,
1212 # - extra: the future extra dictionary of the changeset, please mutate it,
1213 # - opts: the import options.
1213 # - opts: the import options.
1214 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1214 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1215 # mutation of in memory commit and more. Feel free to rework the code to get
1215 # mutation of in memory commit and more. Feel free to rework the code to get
1216 # there.
1216 # there.
1217 extrapreimportmap = {}
1217 extrapreimportmap = {}
1218 # 'postimport' are run after the commit is made and are provided the following
1218 # 'postimport' are run after the commit is made and are provided the following
1219 # argument:
1219 # argument:
1220 # - ctx: the changectx created by import.
1220 # - ctx: the changectx created by import.
1221 extrapostimportmap = {}
1221 extrapostimportmap = {}
1222
1222
1223 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1223 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1224 """Utility function used by commands.import to import a single patch
1224 """Utility function used by commands.import to import a single patch
1225
1225
1226 This function is explicitly defined here to help the evolve extension to
1226 This function is explicitly defined here to help the evolve extension to
1227 wrap this part of the import logic.
1227 wrap this part of the import logic.
1228
1228
1229 The API is currently a bit ugly because it a simple code translation from
1229 The API is currently a bit ugly because it a simple code translation from
1230 the import command. Feel free to make it better.
1230 the import command. Feel free to make it better.
1231
1231
1232 :hunk: a patch (as a binary string)
1232 :hunk: a patch (as a binary string)
1233 :parents: nodes that will be parent of the created commit
1233 :parents: nodes that will be parent of the created commit
1234 :opts: the full dict of option passed to the import command
1234 :opts: the full dict of option passed to the import command
1235 :msgs: list to save commit message to.
1235 :msgs: list to save commit message to.
1236 (used in case we need to save it when failing)
1236 (used in case we need to save it when failing)
1237 :updatefunc: a function that update a repo to a given node
1237 :updatefunc: a function that update a repo to a given node
1238 updatefunc(<repo>, <node>)
1238 updatefunc(<repo>, <node>)
1239 """
1239 """
1240 # avoid cycle context -> subrepo -> cmdutil
1240 # avoid cycle context -> subrepo -> cmdutil
1241 from . import context
1241 from . import context
1242 extractdata = patch.extract(ui, hunk)
1242 extractdata = patch.extract(ui, hunk)
1243 tmpname = extractdata.get('filename')
1243 tmpname = extractdata.get('filename')
1244 message = extractdata.get('message')
1244 message = extractdata.get('message')
1245 user = opts.get('user') or extractdata.get('user')
1245 user = opts.get('user') or extractdata.get('user')
1246 date = opts.get('date') or extractdata.get('date')
1246 date = opts.get('date') or extractdata.get('date')
1247 branch = extractdata.get('branch')
1247 branch = extractdata.get('branch')
1248 nodeid = extractdata.get('nodeid')
1248 nodeid = extractdata.get('nodeid')
1249 p1 = extractdata.get('p1')
1249 p1 = extractdata.get('p1')
1250 p2 = extractdata.get('p2')
1250 p2 = extractdata.get('p2')
1251
1251
1252 nocommit = opts.get('no_commit')
1252 nocommit = opts.get('no_commit')
1253 importbranch = opts.get('import_branch')
1253 importbranch = opts.get('import_branch')
1254 update = not opts.get('bypass')
1254 update = not opts.get('bypass')
1255 strip = opts["strip"]
1255 strip = opts["strip"]
1256 prefix = opts["prefix"]
1256 prefix = opts["prefix"]
1257 sim = float(opts.get('similarity') or 0)
1257 sim = float(opts.get('similarity') or 0)
1258 if not tmpname:
1258 if not tmpname:
1259 return (None, None, False)
1259 return (None, None, False)
1260
1260
1261 rejects = False
1261 rejects = False
1262
1262
1263 try:
1263 try:
1264 cmdline_message = logmessage(ui, opts)
1264 cmdline_message = logmessage(ui, opts)
1265 if cmdline_message:
1265 if cmdline_message:
1266 # pickup the cmdline msg
1266 # pickup the cmdline msg
1267 message = cmdline_message
1267 message = cmdline_message
1268 elif message:
1268 elif message:
1269 # pickup the patch msg
1269 # pickup the patch msg
1270 message = message.strip()
1270 message = message.strip()
1271 else:
1271 else:
1272 # launch the editor
1272 # launch the editor
1273 message = None
1273 message = None
1274 ui.debug('message:\n%s\n' % message)
1274 ui.debug('message:\n%s\n' % message)
1275
1275
1276 if len(parents) == 1:
1276 if len(parents) == 1:
1277 parents.append(repo[nullid])
1277 parents.append(repo[nullid])
1278 if opts.get('exact'):
1278 if opts.get('exact'):
1279 if not nodeid or not p1:
1279 if not nodeid or not p1:
1280 raise error.Abort(_('not a Mercurial patch'))
1280 raise error.Abort(_('not a Mercurial patch'))
1281 p1 = repo[p1]
1281 p1 = repo[p1]
1282 p2 = repo[p2 or nullid]
1282 p2 = repo[p2 or nullid]
1283 elif p2:
1283 elif p2:
1284 try:
1284 try:
1285 p1 = repo[p1]
1285 p1 = repo[p1]
1286 p2 = repo[p2]
1286 p2 = repo[p2]
1287 # Without any options, consider p2 only if the
1287 # Without any options, consider p2 only if the
1288 # patch is being applied on top of the recorded
1288 # patch is being applied on top of the recorded
1289 # first parent.
1289 # first parent.
1290 if p1 != parents[0]:
1290 if p1 != parents[0]:
1291 p1 = parents[0]
1291 p1 = parents[0]
1292 p2 = repo[nullid]
1292 p2 = repo[nullid]
1293 except error.RepoError:
1293 except error.RepoError:
1294 p1, p2 = parents
1294 p1, p2 = parents
1295 if p2.node() == nullid:
1295 if p2.node() == nullid:
1296 ui.warn(_("warning: import the patch as a normal revision\n"
1296 ui.warn(_("warning: import the patch as a normal revision\n"
1297 "(use --exact to import the patch as a merge)\n"))
1297 "(use --exact to import the patch as a merge)\n"))
1298 else:
1298 else:
1299 p1, p2 = parents
1299 p1, p2 = parents
1300
1300
1301 n = None
1301 n = None
1302 if update:
1302 if update:
1303 if p1 != parents[0]:
1303 if p1 != parents[0]:
1304 updatefunc(repo, p1.node())
1304 updatefunc(repo, p1.node())
1305 if p2 != parents[1]:
1305 if p2 != parents[1]:
1306 repo.setparents(p1.node(), p2.node())
1306 repo.setparents(p1.node(), p2.node())
1307
1307
1308 if opts.get('exact') or importbranch:
1308 if opts.get('exact') or importbranch:
1309 repo.dirstate.setbranch(branch or 'default')
1309 repo.dirstate.setbranch(branch or 'default')
1310
1310
1311 partial = opts.get('partial', False)
1311 partial = opts.get('partial', False)
1312 files = set()
1312 files = set()
1313 try:
1313 try:
1314 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1314 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1315 files=files, eolmode=None, similarity=sim / 100.0)
1315 files=files, eolmode=None, similarity=sim / 100.0)
1316 except error.PatchError as e:
1316 except error.PatchError as e:
1317 if not partial:
1317 if not partial:
1318 raise error.Abort(str(e))
1318 raise error.Abort(str(e))
1319 if partial:
1319 if partial:
1320 rejects = True
1320 rejects = True
1321
1321
1322 files = list(files)
1322 files = list(files)
1323 if nocommit:
1323 if nocommit:
1324 if message:
1324 if message:
1325 msgs.append(message)
1325 msgs.append(message)
1326 else:
1326 else:
1327 if opts.get('exact') or p2:
1327 if opts.get('exact') or p2:
1328 # If you got here, you either use --force and know what
1328 # If you got here, you either use --force and know what
1329 # you are doing or used --exact or a merge patch while
1329 # you are doing or used --exact or a merge patch while
1330 # being updated to its first parent.
1330 # being updated to its first parent.
1331 m = None
1331 m = None
1332 else:
1332 else:
1333 m = scmutil.matchfiles(repo, files or [])
1333 m = scmutil.matchfiles(repo, files or [])
1334 editform = mergeeditform(repo[None], 'import.normal')
1334 editform = mergeeditform(repo[None], 'import.normal')
1335 if opts.get('exact'):
1335 if opts.get('exact'):
1336 editor = None
1336 editor = None
1337 else:
1337 else:
1338 editor = getcommiteditor(editform=editform,
1338 editor = getcommiteditor(editform=editform,
1339 **pycompat.strkwargs(opts))
1339 **pycompat.strkwargs(opts))
1340 extra = {}
1340 extra = {}
1341 for idfunc in extrapreimport:
1341 for idfunc in extrapreimport:
1342 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1342 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1343 overrides = {}
1343 overrides = {}
1344 if partial:
1344 if partial:
1345 overrides[('ui', 'allowemptycommit')] = True
1345 overrides[('ui', 'allowemptycommit')] = True
1346 with repo.ui.configoverride(overrides, 'import'):
1346 with repo.ui.configoverride(overrides, 'import'):
1347 n = repo.commit(message, user,
1347 n = repo.commit(message, user,
1348 date, match=m,
1348 date, match=m,
1349 editor=editor, extra=extra)
1349 editor=editor, extra=extra)
1350 for idfunc in extrapostimport:
1350 for idfunc in extrapostimport:
1351 extrapostimportmap[idfunc](repo[n])
1351 extrapostimportmap[idfunc](repo[n])
1352 else:
1352 else:
1353 if opts.get('exact') or importbranch:
1353 if opts.get('exact') or importbranch:
1354 branch = branch or 'default'
1354 branch = branch or 'default'
1355 else:
1355 else:
1356 branch = p1.branch()
1356 branch = p1.branch()
1357 store = patch.filestore()
1357 store = patch.filestore()
1358 try:
1358 try:
1359 files = set()
1359 files = set()
1360 try:
1360 try:
1361 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1361 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1362 files, eolmode=None)
1362 files, eolmode=None)
1363 except error.PatchError as e:
1363 except error.PatchError as e:
1364 raise error.Abort(str(e))
1364 raise error.Abort(str(e))
1365 if opts.get('exact'):
1365 if opts.get('exact'):
1366 editor = None
1366 editor = None
1367 else:
1367 else:
1368 editor = getcommiteditor(editform='import.bypass')
1368 editor = getcommiteditor(editform='import.bypass')
1369 memctx = context.memctx(repo, (p1.node(), p2.node()),
1369 memctx = context.memctx(repo, (p1.node(), p2.node()),
1370 message,
1370 message,
1371 files=files,
1371 files=files,
1372 filectxfn=store,
1372 filectxfn=store,
1373 user=user,
1373 user=user,
1374 date=date,
1374 date=date,
1375 branch=branch,
1375 branch=branch,
1376 editor=editor)
1376 editor=editor)
1377 n = memctx.commit()
1377 n = memctx.commit()
1378 finally:
1378 finally:
1379 store.close()
1379 store.close()
1380 if opts.get('exact') and nocommit:
1380 if opts.get('exact') and nocommit:
1381 # --exact with --no-commit is still useful in that it does merge
1381 # --exact with --no-commit is still useful in that it does merge
1382 # and branch bits
1382 # and branch bits
1383 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1383 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1384 elif opts.get('exact') and hex(n) != nodeid:
1384 elif opts.get('exact') and hex(n) != nodeid:
1385 raise error.Abort(_('patch is damaged or loses information'))
1385 raise error.Abort(_('patch is damaged or loses information'))
1386 msg = _('applied to working directory')
1386 msg = _('applied to working directory')
1387 if n:
1387 if n:
1388 # i18n: refers to a short changeset id
1388 # i18n: refers to a short changeset id
1389 msg = _('created %s') % short(n)
1389 msg = _('created %s') % short(n)
1390 return (msg, n, rejects)
1390 return (msg, n, rejects)
1391 finally:
1391 finally:
1392 os.unlink(tmpname)
1392 os.unlink(tmpname)
1393
1393
1394 # facility to let extensions include additional data in an exported patch
1394 # facility to let extensions include additional data in an exported patch
1395 # list of identifiers to be executed in order
1395 # list of identifiers to be executed in order
1396 extraexport = []
1396 extraexport = []
1397 # mapping from identifier to actual export function
1397 # mapping from identifier to actual export function
1398 # function as to return a string to be added to the header or None
1398 # function as to return a string to be added to the header or None
1399 # it is given two arguments (sequencenumber, changectx)
1399 # it is given two arguments (sequencenumber, changectx)
1400 extraexportmap = {}
1400 extraexportmap = {}
1401
1401
1402 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1402 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1403 node = scmutil.binnode(ctx)
1403 node = scmutil.binnode(ctx)
1404 parents = [p.node() for p in ctx.parents() if p]
1404 parents = [p.node() for p in ctx.parents() if p]
1405 branch = ctx.branch()
1405 branch = ctx.branch()
1406 if switch_parent:
1406 if switch_parent:
1407 parents.reverse()
1407 parents.reverse()
1408
1408
1409 if parents:
1409 if parents:
1410 prev = parents[0]
1410 prev = parents[0]
1411 else:
1411 else:
1412 prev = nullid
1412 prev = nullid
1413
1413
1414 write("# HG changeset patch\n")
1414 write("# HG changeset patch\n")
1415 write("# User %s\n" % ctx.user())
1415 write("# User %s\n" % ctx.user())
1416 write("# Date %d %d\n" % ctx.date())
1416 write("# Date %d %d\n" % ctx.date())
1417 write("# %s\n" % util.datestr(ctx.date()))
1417 write("# %s\n" % util.datestr(ctx.date()))
1418 if branch and branch != 'default':
1418 if branch and branch != 'default':
1419 write("# Branch %s\n" % branch)
1419 write("# Branch %s\n" % branch)
1420 write("# Node ID %s\n" % hex(node))
1420 write("# Node ID %s\n" % hex(node))
1421 write("# Parent %s\n" % hex(prev))
1421 write("# Parent %s\n" % hex(prev))
1422 if len(parents) > 1:
1422 if len(parents) > 1:
1423 write("# Parent %s\n" % hex(parents[1]))
1423 write("# Parent %s\n" % hex(parents[1]))
1424
1424
1425 for headerid in extraexport:
1425 for headerid in extraexport:
1426 header = extraexportmap[headerid](seqno, ctx)
1426 header = extraexportmap[headerid](seqno, ctx)
1427 if header is not None:
1427 if header is not None:
1428 write('# %s\n' % header)
1428 write('# %s\n' % header)
1429 write(ctx.description().rstrip())
1429 write(ctx.description().rstrip())
1430 write("\n\n")
1430 write("\n\n")
1431
1431
1432 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1432 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1433 write(chunk, label=label)
1433 write(chunk, label=label)
1434
1434
1435 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1435 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1436 opts=None, match=None):
1436 opts=None, match=None):
1437 '''export changesets as hg patches
1437 '''export changesets as hg patches
1438
1438
1439 Args:
1439 Args:
1440 repo: The repository from which we're exporting revisions.
1440 repo: The repository from which we're exporting revisions.
1441 revs: A list of revisions to export as revision numbers.
1441 revs: A list of revisions to export as revision numbers.
1442 fntemplate: An optional string to use for generating patch file names.
1442 fntemplate: An optional string to use for generating patch file names.
1443 fp: An optional file-like object to which patches should be written.
1443 fp: An optional file-like object to which patches should be written.
1444 switch_parent: If True, show diffs against second parent when not nullid.
1444 switch_parent: If True, show diffs against second parent when not nullid.
1445 Default is false, which always shows diff against p1.
1445 Default is false, which always shows diff against p1.
1446 opts: diff options to use for generating the patch.
1446 opts: diff options to use for generating the patch.
1447 match: If specified, only export changes to files matching this matcher.
1447 match: If specified, only export changes to files matching this matcher.
1448
1448
1449 Returns:
1449 Returns:
1450 Nothing.
1450 Nothing.
1451
1451
1452 Side Effect:
1452 Side Effect:
1453 "HG Changeset Patch" data is emitted to one of the following
1453 "HG Changeset Patch" data is emitted to one of the following
1454 destinations:
1454 destinations:
1455 fp is specified: All revs are written to the specified
1455 fp is specified: All revs are written to the specified
1456 file-like object.
1456 file-like object.
1457 fntemplate specified: Each rev is written to a unique file named using
1457 fntemplate specified: Each rev is written to a unique file named using
1458 the given template.
1458 the given template.
1459 Neither fp nor template specified: All revs written to repo.ui.write()
1459 Neither fp nor template specified: All revs written to repo.ui.write()
1460 '''
1460 '''
1461
1461
1462 total = len(revs)
1462 total = len(revs)
1463 revwidth = max(len(str(rev)) for rev in revs)
1463 revwidth = max(len(str(rev)) for rev in revs)
1464 filemode = {}
1464 filemode = {}
1465
1465
1466 write = None
1466 write = None
1467 dest = '<unnamed>'
1467 dest = '<unnamed>'
1468 if fp:
1468 if fp:
1469 dest = getattr(fp, 'name', dest)
1469 dest = getattr(fp, 'name', dest)
1470 def write(s, **kw):
1470 def write(s, **kw):
1471 fp.write(s)
1471 fp.write(s)
1472 elif not fntemplate:
1472 elif not fntemplate:
1473 write = repo.ui.write
1473 write = repo.ui.write
1474
1474
1475 for seqno, rev in enumerate(revs, 1):
1475 for seqno, rev in enumerate(revs, 1):
1476 ctx = repo[rev]
1476 ctx = repo[rev]
1477 fo = None
1477 fo = None
1478 if not fp and fntemplate:
1478 if not fp and fntemplate:
1479 desc_lines = ctx.description().rstrip().split('\n')
1479 desc_lines = ctx.description().rstrip().split('\n')
1480 desc = desc_lines[0] #Commit always has a first line.
1480 desc = desc_lines[0] #Commit always has a first line.
1481 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1481 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1482 total=total, seqno=seqno, revwidth=revwidth,
1482 total=total, seqno=seqno, revwidth=revwidth,
1483 mode='wb', modemap=filemode)
1483 mode='wb', modemap=filemode)
1484 dest = fo.name
1484 dest = fo.name
1485 def write(s, **kw):
1485 def write(s, **kw):
1486 fo.write(s)
1486 fo.write(s)
1487 if not dest.startswith('<'):
1487 if not dest.startswith('<'):
1488 repo.ui.note("%s\n" % dest)
1488 repo.ui.note("%s\n" % dest)
1489 _exportsingle(
1489 _exportsingle(
1490 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1490 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1491 if fo is not None:
1491 if fo is not None:
1492 fo.close()
1492 fo.close()
1493
1493
1494 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1494 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1495 changes=None, stat=False, fp=None, prefix='',
1495 changes=None, stat=False, fp=None, prefix='',
1496 root='', listsubrepos=False, hunksfilterfn=None):
1496 root='', listsubrepos=False, hunksfilterfn=None):
1497 '''show diff or diffstat.'''
1497 '''show diff or diffstat.'''
1498 if fp is None:
1498 if fp is None:
1499 write = ui.write
1499 write = ui.write
1500 else:
1500 else:
1501 def write(s, **kw):
1501 def write(s, **kw):
1502 fp.write(s)
1502 fp.write(s)
1503
1503
1504 if root:
1504 if root:
1505 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1505 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1506 else:
1506 else:
1507 relroot = ''
1507 relroot = ''
1508 if relroot != '':
1508 if relroot != '':
1509 # XXX relative roots currently don't work if the root is within a
1509 # XXX relative roots currently don't work if the root is within a
1510 # subrepo
1510 # subrepo
1511 uirelroot = match.uipath(relroot)
1511 uirelroot = match.uipath(relroot)
1512 relroot += '/'
1512 relroot += '/'
1513 for matchroot in match.files():
1513 for matchroot in match.files():
1514 if not matchroot.startswith(relroot):
1514 if not matchroot.startswith(relroot):
1515 ui.warn(_('warning: %s not inside relative root %s\n') % (
1515 ui.warn(_('warning: %s not inside relative root %s\n') % (
1516 match.uipath(matchroot), uirelroot))
1516 match.uipath(matchroot), uirelroot))
1517
1517
1518 if stat:
1518 if stat:
1519 diffopts = diffopts.copy(context=0, noprefix=False)
1519 diffopts = diffopts.copy(context=0, noprefix=False)
1520 width = 80
1520 width = 80
1521 if not ui.plain():
1521 if not ui.plain():
1522 width = ui.termwidth()
1522 width = ui.termwidth()
1523 chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts,
1523 chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts,
1524 prefix=prefix, relroot=relroot,
1524 prefix=prefix, relroot=relroot,
1525 hunksfilterfn=hunksfilterfn)
1525 hunksfilterfn=hunksfilterfn)
1526 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1526 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1527 width=width):
1527 width=width):
1528 write(chunk, label=label)
1528 write(chunk, label=label)
1529 else:
1529 else:
1530 for chunk, label in patch.diffui(repo, node1, node2, match,
1530 for chunk, label in patch.diffui(repo, node1, node2, match,
1531 changes, opts=diffopts, prefix=prefix,
1531 changes, opts=diffopts, prefix=prefix,
1532 relroot=relroot,
1532 relroot=relroot,
1533 hunksfilterfn=hunksfilterfn):
1533 hunksfilterfn=hunksfilterfn):
1534 write(chunk, label=label)
1534 write(chunk, label=label)
1535
1535
1536 if listsubrepos:
1536 if listsubrepos:
1537 ctx1 = repo[node1]
1537 ctx1 = repo[node1]
1538 ctx2 = repo[node2]
1538 ctx2 = repo[node2]
1539 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1539 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1540 tempnode2 = node2
1540 tempnode2 = node2
1541 try:
1541 try:
1542 if node2 is not None:
1542 if node2 is not None:
1543 tempnode2 = ctx2.substate[subpath][1]
1543 tempnode2 = ctx2.substate[subpath][1]
1544 except KeyError:
1544 except KeyError:
1545 # A subrepo that existed in node1 was deleted between node1 and
1545 # A subrepo that existed in node1 was deleted between node1 and
1546 # node2 (inclusive). Thus, ctx2's substate won't contain that
1546 # node2 (inclusive). Thus, ctx2's substate won't contain that
1547 # subpath. The best we can do is to ignore it.
1547 # subpath. The best we can do is to ignore it.
1548 tempnode2 = None
1548 tempnode2 = None
1549 submatch = matchmod.subdirmatcher(subpath, match)
1549 submatch = matchmod.subdirmatcher(subpath, match)
1550 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1550 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1551 stat=stat, fp=fp, prefix=prefix)
1551 stat=stat, fp=fp, prefix=prefix)
1552
1552
1553 def _changesetlabels(ctx):
1553 def _changesetlabels(ctx):
1554 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1554 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1555 if ctx.obsolete():
1555 if ctx.obsolete():
1556 labels.append('changeset.obsolete')
1556 labels.append('changeset.obsolete')
1557 if ctx.isunstable():
1557 if ctx.isunstable():
1558 labels.append('changeset.unstable')
1558 labels.append('changeset.unstable')
1559 for instability in ctx.instabilities():
1559 for instability in ctx.instabilities():
1560 labels.append('instability.%s' % instability)
1560 labels.append('instability.%s' % instability)
1561 return ' '.join(labels)
1561 return ' '.join(labels)
1562
1562
1563 class changeset_printer(object):
1563 class changeset_printer(object):
1564 '''show changeset information when templating not requested.'''
1564 '''show changeset information when templating not requested.'''
1565
1565
1566 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1566 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1567 self.ui = ui
1567 self.ui = ui
1568 self.repo = repo
1568 self.repo = repo
1569 self.buffered = buffered
1569 self.buffered = buffered
1570 self.matchfn = matchfn
1570 self.matchfn = matchfn
1571 self.diffopts = diffopts
1571 self.diffopts = diffopts
1572 self.header = {}
1572 self.header = {}
1573 self.hunk = {}
1573 self.hunk = {}
1574 self.lastheader = None
1574 self.lastheader = None
1575 self.footer = None
1575 self.footer = None
1576 self._columns = templatekw.getlogcolumns()
1576 self._columns = templatekw.getlogcolumns()
1577
1577
1578 def flush(self, ctx):
1578 def flush(self, ctx):
1579 rev = ctx.rev()
1579 rev = ctx.rev()
1580 if rev in self.header:
1580 if rev in self.header:
1581 h = self.header[rev]
1581 h = self.header[rev]
1582 if h != self.lastheader:
1582 if h != self.lastheader:
1583 self.lastheader = h
1583 self.lastheader = h
1584 self.ui.write(h)
1584 self.ui.write(h)
1585 del self.header[rev]
1585 del self.header[rev]
1586 if rev in self.hunk:
1586 if rev in self.hunk:
1587 self.ui.write(self.hunk[rev])
1587 self.ui.write(self.hunk[rev])
1588 del self.hunk[rev]
1588 del self.hunk[rev]
1589
1589
1590 def close(self):
1590 def close(self):
1591 if self.footer:
1591 if self.footer:
1592 self.ui.write(self.footer)
1592 self.ui.write(self.footer)
1593
1593
1594 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None,
1594 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None,
1595 **props):
1595 **props):
1596 props = pycompat.byteskwargs(props)
1596 props = pycompat.byteskwargs(props)
1597 if self.buffered:
1597 if self.buffered:
1598 self.ui.pushbuffer(labeled=True)
1598 self.ui.pushbuffer(labeled=True)
1599 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1599 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1600 self.hunk[ctx.rev()] = self.ui.popbuffer()
1600 self.hunk[ctx.rev()] = self.ui.popbuffer()
1601 else:
1601 else:
1602 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1602 self._show(ctx, copies, matchfn, hunksfilterfn, props)
1603
1603
1604 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1604 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1605 '''show a single changeset or file revision'''
1605 '''show a single changeset or file revision'''
1606 changenode = ctx.node()
1606 changenode = ctx.node()
1607 rev = ctx.rev()
1607 rev = ctx.rev()
1608
1608
1609 if self.ui.quiet:
1609 if self.ui.quiet:
1610 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
1610 self.ui.write("%s\n" % scmutil.formatchangeid(ctx),
1611 label='log.node')
1611 label='log.node')
1612 return
1612 return
1613
1613
1614 columns = self._columns
1614 columns = self._columns
1615 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
1615 self.ui.write(columns['changeset'] % scmutil.formatchangeid(ctx),
1616 label=_changesetlabels(ctx))
1616 label=_changesetlabels(ctx))
1617
1617
1618 # branches are shown first before any other names due to backwards
1618 # branches are shown first before any other names due to backwards
1619 # compatibility
1619 # compatibility
1620 branch = ctx.branch()
1620 branch = ctx.branch()
1621 # don't show the default branch name
1621 # don't show the default branch name
1622 if branch != 'default':
1622 if branch != 'default':
1623 self.ui.write(columns['branch'] % branch, label='log.branch')
1623 self.ui.write(columns['branch'] % branch, label='log.branch')
1624
1624
1625 for nsname, ns in self.repo.names.iteritems():
1625 for nsname, ns in self.repo.names.iteritems():
1626 # branches has special logic already handled above, so here we just
1626 # branches has special logic already handled above, so here we just
1627 # skip it
1627 # skip it
1628 if nsname == 'branches':
1628 if nsname == 'branches':
1629 continue
1629 continue
1630 # we will use the templatename as the color name since those two
1630 # we will use the templatename as the color name since those two
1631 # should be the same
1631 # should be the same
1632 for name in ns.names(self.repo, changenode):
1632 for name in ns.names(self.repo, changenode):
1633 self.ui.write(ns.logfmt % name,
1633 self.ui.write(ns.logfmt % name,
1634 label='log.%s' % ns.colorname)
1634 label='log.%s' % ns.colorname)
1635 if self.ui.debugflag:
1635 if self.ui.debugflag:
1636 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
1636 self.ui.write(columns['phase'] % ctx.phasestr(), label='log.phase')
1637 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1637 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1638 label = 'log.parent changeset.%s' % pctx.phasestr()
1638 label = 'log.parent changeset.%s' % pctx.phasestr()
1639 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
1639 self.ui.write(columns['parent'] % scmutil.formatchangeid(pctx),
1640 label=label)
1640 label=label)
1641
1641
1642 if self.ui.debugflag and rev is not None:
1642 if self.ui.debugflag and rev is not None:
1643 mnode = ctx.manifestnode()
1643 mnode = ctx.manifestnode()
1644 mrev = self.repo.manifestlog._revlog.rev(mnode)
1644 mrev = self.repo.manifestlog._revlog.rev(mnode)
1645 self.ui.write(columns['manifest']
1645 self.ui.write(columns['manifest']
1646 % scmutil.formatrevnode(self.ui, mrev, mnode),
1646 % scmutil.formatrevnode(self.ui, mrev, mnode),
1647 label='ui.debug log.manifest')
1647 label='ui.debug log.manifest')
1648 self.ui.write(columns['user'] % ctx.user(), label='log.user')
1648 self.ui.write(columns['user'] % ctx.user(), label='log.user')
1649 self.ui.write(columns['date'] % util.datestr(ctx.date()),
1649 self.ui.write(columns['date'] % util.datestr(ctx.date()),
1650 label='log.date')
1650 label='log.date')
1651
1651
1652 if ctx.isunstable():
1652 if ctx.isunstable():
1653 instabilities = ctx.instabilities()
1653 instabilities = ctx.instabilities()
1654 self.ui.write(columns['instability'] % ', '.join(instabilities),
1654 self.ui.write(columns['instability'] % ', '.join(instabilities),
1655 label='log.instability')
1655 label='log.instability')
1656
1656
1657 elif ctx.obsolete():
1657 elif ctx.obsolete():
1658 self._showobsfate(ctx)
1658 self._showobsfate(ctx)
1659
1659
1660 self._exthook(ctx)
1660 self._exthook(ctx)
1661
1661
1662 if self.ui.debugflag:
1662 if self.ui.debugflag:
1663 files = ctx.p1().status(ctx)[:3]
1663 files = ctx.p1().status(ctx)[:3]
1664 for key, value in zip(['files', 'files+', 'files-'], files):
1664 for key, value in zip(['files', 'files+', 'files-'], files):
1665 if value:
1665 if value:
1666 self.ui.write(columns[key] % " ".join(value),
1666 self.ui.write(columns[key] % " ".join(value),
1667 label='ui.debug log.files')
1667 label='ui.debug log.files')
1668 elif ctx.files() and self.ui.verbose:
1668 elif ctx.files() and self.ui.verbose:
1669 self.ui.write(columns['files'] % " ".join(ctx.files()),
1669 self.ui.write(columns['files'] % " ".join(ctx.files()),
1670 label='ui.note log.files')
1670 label='ui.note log.files')
1671 if copies and self.ui.verbose:
1671 if copies and self.ui.verbose:
1672 copies = ['%s (%s)' % c for c in copies]
1672 copies = ['%s (%s)' % c for c in copies]
1673 self.ui.write(columns['copies'] % ' '.join(copies),
1673 self.ui.write(columns['copies'] % ' '.join(copies),
1674 label='ui.note log.copies')
1674 label='ui.note log.copies')
1675
1675
1676 extra = ctx.extra()
1676 extra = ctx.extra()
1677 if extra and self.ui.debugflag:
1677 if extra and self.ui.debugflag:
1678 for key, value in sorted(extra.items()):
1678 for key, value in sorted(extra.items()):
1679 self.ui.write(columns['extra'] % (key, util.escapestr(value)),
1679 self.ui.write(columns['extra'] % (key, util.escapestr(value)),
1680 label='ui.debug log.extra')
1680 label='ui.debug log.extra')
1681
1681
1682 description = ctx.description().strip()
1682 description = ctx.description().strip()
1683 if description:
1683 if description:
1684 if self.ui.verbose:
1684 if self.ui.verbose:
1685 self.ui.write(_("description:\n"),
1685 self.ui.write(_("description:\n"),
1686 label='ui.note log.description')
1686 label='ui.note log.description')
1687 self.ui.write(description,
1687 self.ui.write(description,
1688 label='ui.note log.description')
1688 label='ui.note log.description')
1689 self.ui.write("\n\n")
1689 self.ui.write("\n\n")
1690 else:
1690 else:
1691 self.ui.write(columns['summary'] % description.splitlines()[0],
1691 self.ui.write(columns['summary'] % description.splitlines()[0],
1692 label='log.summary')
1692 label='log.summary')
1693 self.ui.write("\n")
1693 self.ui.write("\n")
1694
1694
1695 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1695 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1696
1696
1697 def _showobsfate(self, ctx):
1697 def _showobsfate(self, ctx):
1698 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
1698 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
1699
1699
1700 if obsfate:
1700 if obsfate:
1701 for obsfateline in obsfate:
1701 for obsfateline in obsfate:
1702 self.ui.write(self._columns['obsolete'] % obsfateline,
1702 self.ui.write(self._columns['obsolete'] % obsfateline,
1703 label='log.obsfate')
1703 label='log.obsfate')
1704
1704
1705 def _exthook(self, ctx):
1705 def _exthook(self, ctx):
1706 '''empty method used by extension as a hook point
1706 '''empty method used by extension as a hook point
1707 '''
1707 '''
1708
1708
1709 def showpatch(self, ctx, matchfn, hunksfilterfn=None):
1709 def showpatch(self, ctx, matchfn, hunksfilterfn=None):
1710 if not matchfn:
1710 if not matchfn:
1711 matchfn = self.matchfn
1711 matchfn = self.matchfn
1712 if matchfn:
1712 if matchfn:
1713 stat = self.diffopts.get('stat')
1713 stat = self.diffopts.get('stat')
1714 diff = self.diffopts.get('patch')
1714 diff = self.diffopts.get('patch')
1715 diffopts = patch.diffallopts(self.ui, self.diffopts)
1715 diffopts = patch.diffallopts(self.ui, self.diffopts)
1716 node = ctx.node()
1716 node = ctx.node()
1717 prev = ctx.p1().node()
1717 prev = ctx.p1().node()
1718 if stat:
1718 if stat:
1719 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1719 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1720 match=matchfn, stat=True,
1720 match=matchfn, stat=True,
1721 hunksfilterfn=hunksfilterfn)
1721 hunksfilterfn=hunksfilterfn)
1722 if diff:
1722 if diff:
1723 if stat:
1723 if stat:
1724 self.ui.write("\n")
1724 self.ui.write("\n")
1725 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1725 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1726 match=matchfn, stat=False,
1726 match=matchfn, stat=False,
1727 hunksfilterfn=hunksfilterfn)
1727 hunksfilterfn=hunksfilterfn)
1728 self.ui.write("\n")
1728 self.ui.write("\n")
1729
1729
1730 class jsonchangeset(changeset_printer):
1730 class jsonchangeset(changeset_printer):
1731 '''format changeset information.'''
1731 '''format changeset information.'''
1732
1732
1733 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1733 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1734 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1734 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1735 self.cache = {}
1735 self.cache = {}
1736 self._first = True
1736 self._first = True
1737
1737
1738 def close(self):
1738 def close(self):
1739 if not self._first:
1739 if not self._first:
1740 self.ui.write("\n]\n")
1740 self.ui.write("\n]\n")
1741 else:
1741 else:
1742 self.ui.write("[]\n")
1742 self.ui.write("[]\n")
1743
1743
1744 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1744 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1745 '''show a single changeset or file revision'''
1745 '''show a single changeset or file revision'''
1746 rev = ctx.rev()
1746 rev = ctx.rev()
1747 if rev is None:
1747 if rev is None:
1748 jrev = jnode = 'null'
1748 jrev = jnode = 'null'
1749 else:
1749 else:
1750 jrev = '%d' % rev
1750 jrev = '%d' % rev
1751 jnode = '"%s"' % hex(ctx.node())
1751 jnode = '"%s"' % hex(ctx.node())
1752 j = encoding.jsonescape
1752 j = encoding.jsonescape
1753
1753
1754 if self._first:
1754 if self._first:
1755 self.ui.write("[\n {")
1755 self.ui.write("[\n {")
1756 self._first = False
1756 self._first = False
1757 else:
1757 else:
1758 self.ui.write(",\n {")
1758 self.ui.write(",\n {")
1759
1759
1760 if self.ui.quiet:
1760 if self.ui.quiet:
1761 self.ui.write(('\n "rev": %s') % jrev)
1761 self.ui.write(('\n "rev": %s') % jrev)
1762 self.ui.write((',\n "node": %s') % jnode)
1762 self.ui.write((',\n "node": %s') % jnode)
1763 self.ui.write('\n }')
1763 self.ui.write('\n }')
1764 return
1764 return
1765
1765
1766 self.ui.write(('\n "rev": %s') % jrev)
1766 self.ui.write(('\n "rev": %s') % jrev)
1767 self.ui.write((',\n "node": %s') % jnode)
1767 self.ui.write((',\n "node": %s') % jnode)
1768 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1768 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1769 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1769 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1770 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1770 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1771 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1771 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1772 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1772 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1773
1773
1774 self.ui.write((',\n "bookmarks": [%s]') %
1774 self.ui.write((',\n "bookmarks": [%s]') %
1775 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1775 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1776 self.ui.write((',\n "tags": [%s]') %
1776 self.ui.write((',\n "tags": [%s]') %
1777 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1777 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1778 self.ui.write((',\n "parents": [%s]') %
1778 self.ui.write((',\n "parents": [%s]') %
1779 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1779 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1780
1780
1781 if self.ui.debugflag:
1781 if self.ui.debugflag:
1782 if rev is None:
1782 if rev is None:
1783 jmanifestnode = 'null'
1783 jmanifestnode = 'null'
1784 else:
1784 else:
1785 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1785 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1786 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1786 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1787
1787
1788 self.ui.write((',\n "extra": {%s}') %
1788 self.ui.write((',\n "extra": {%s}') %
1789 ", ".join('"%s": "%s"' % (j(k), j(v))
1789 ", ".join('"%s": "%s"' % (j(k), j(v))
1790 for k, v in ctx.extra().items()))
1790 for k, v in ctx.extra().items()))
1791
1791
1792 files = ctx.p1().status(ctx)
1792 files = ctx.p1().status(ctx)
1793 self.ui.write((',\n "modified": [%s]') %
1793 self.ui.write((',\n "modified": [%s]') %
1794 ", ".join('"%s"' % j(f) for f in files[0]))
1794 ", ".join('"%s"' % j(f) for f in files[0]))
1795 self.ui.write((',\n "added": [%s]') %
1795 self.ui.write((',\n "added": [%s]') %
1796 ", ".join('"%s"' % j(f) for f in files[1]))
1796 ", ".join('"%s"' % j(f) for f in files[1]))
1797 self.ui.write((',\n "removed": [%s]') %
1797 self.ui.write((',\n "removed": [%s]') %
1798 ", ".join('"%s"' % j(f) for f in files[2]))
1798 ", ".join('"%s"' % j(f) for f in files[2]))
1799
1799
1800 elif self.ui.verbose:
1800 elif self.ui.verbose:
1801 self.ui.write((',\n "files": [%s]') %
1801 self.ui.write((',\n "files": [%s]') %
1802 ", ".join('"%s"' % j(f) for f in ctx.files()))
1802 ", ".join('"%s"' % j(f) for f in ctx.files()))
1803
1803
1804 if copies:
1804 if copies:
1805 self.ui.write((',\n "copies": {%s}') %
1805 self.ui.write((',\n "copies": {%s}') %
1806 ", ".join('"%s": "%s"' % (j(k), j(v))
1806 ", ".join('"%s": "%s"' % (j(k), j(v))
1807 for k, v in copies))
1807 for k, v in copies))
1808
1808
1809 matchfn = self.matchfn
1809 matchfn = self.matchfn
1810 if matchfn:
1810 if matchfn:
1811 stat = self.diffopts.get('stat')
1811 stat = self.diffopts.get('stat')
1812 diff = self.diffopts.get('patch')
1812 diff = self.diffopts.get('patch')
1813 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1813 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1814 node, prev = ctx.node(), ctx.p1().node()
1814 node, prev = ctx.node(), ctx.p1().node()
1815 if stat:
1815 if stat:
1816 self.ui.pushbuffer()
1816 self.ui.pushbuffer()
1817 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1817 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1818 match=matchfn, stat=True)
1818 match=matchfn, stat=True)
1819 self.ui.write((',\n "diffstat": "%s"')
1819 self.ui.write((',\n "diffstat": "%s"')
1820 % j(self.ui.popbuffer()))
1820 % j(self.ui.popbuffer()))
1821 if diff:
1821 if diff:
1822 self.ui.pushbuffer()
1822 self.ui.pushbuffer()
1823 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1823 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1824 match=matchfn, stat=False)
1824 match=matchfn, stat=False)
1825 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1825 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1826
1826
1827 self.ui.write("\n }")
1827 self.ui.write("\n }")
1828
1828
1829 class changeset_templater(changeset_printer):
1829 class changeset_templater(changeset_printer):
1830 '''format changeset information.
1830 '''format changeset information.
1831
1831
1832 Note: there are a variety of convenience functions to build a
1832 Note: there are a variety of convenience functions to build a
1833 changeset_templater for common cases. See functions such as:
1833 changeset_templater for common cases. See functions such as:
1834 makelogtemplater, show_changeset, buildcommittemplate, or other
1834 makelogtemplater, show_changeset, buildcommittemplate, or other
1835 functions that use changesest_templater.
1835 functions that use changesest_templater.
1836 '''
1836 '''
1837
1837
1838 # Arguments before "buffered" used to be positional. Consider not
1838 # Arguments before "buffered" used to be positional. Consider not
1839 # adding/removing arguments before "buffered" to not break callers.
1839 # adding/removing arguments before "buffered" to not break callers.
1840 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1840 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1841 buffered=False):
1841 buffered=False):
1842 diffopts = diffopts or {}
1842 diffopts = diffopts or {}
1843
1843
1844 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1844 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1845 tres = formatter.templateresources(ui, repo)
1845 tres = formatter.templateresources(ui, repo)
1846 self.t = formatter.loadtemplater(ui, tmplspec,
1846 self.t = formatter.loadtemplater(ui, tmplspec,
1847 defaults=templatekw.keywords,
1847 defaults=templatekw.keywords,
1848 resources=tres,
1848 resources=tres,
1849 cache=templatekw.defaulttempl)
1849 cache=templatekw.defaulttempl)
1850 self._counter = itertools.count()
1850 self._counter = itertools.count()
1851 self.cache = tres['cache'] # shared with _graphnodeformatter()
1851 self.cache = tres['cache'] # shared with _graphnodeformatter()
1852
1852
1853 self._tref = tmplspec.ref
1853 self._tref = tmplspec.ref
1854 self._parts = {'header': '', 'footer': '',
1854 self._parts = {'header': '', 'footer': '',
1855 tmplspec.ref: tmplspec.ref,
1855 tmplspec.ref: tmplspec.ref,
1856 'docheader': '', 'docfooter': '',
1856 'docheader': '', 'docfooter': '',
1857 'separator': ''}
1857 'separator': ''}
1858 if tmplspec.mapfile:
1858 if tmplspec.mapfile:
1859 # find correct templates for current mode, for backward
1859 # find correct templates for current mode, for backward
1860 # compatibility with 'log -v/-q/--debug' using a mapfile
1860 # compatibility with 'log -v/-q/--debug' using a mapfile
1861 tmplmodes = [
1861 tmplmodes = [
1862 (True, ''),
1862 (True, ''),
1863 (self.ui.verbose, '_verbose'),
1863 (self.ui.verbose, '_verbose'),
1864 (self.ui.quiet, '_quiet'),
1864 (self.ui.quiet, '_quiet'),
1865 (self.ui.debugflag, '_debug'),
1865 (self.ui.debugflag, '_debug'),
1866 ]
1866 ]
1867 for mode, postfix in tmplmodes:
1867 for mode, postfix in tmplmodes:
1868 for t in self._parts:
1868 for t in self._parts:
1869 cur = t + postfix
1869 cur = t + postfix
1870 if mode and cur in self.t:
1870 if mode and cur in self.t:
1871 self._parts[t] = cur
1871 self._parts[t] = cur
1872 else:
1872 else:
1873 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1873 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1874 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1874 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1875 self._parts.update(m)
1875 self._parts.update(m)
1876
1876
1877 if self._parts['docheader']:
1877 if self._parts['docheader']:
1878 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1878 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1879
1879
1880 def close(self):
1880 def close(self):
1881 if self._parts['docfooter']:
1881 if self._parts['docfooter']:
1882 if not self.footer:
1882 if not self.footer:
1883 self.footer = ""
1883 self.footer = ""
1884 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1884 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1885 return super(changeset_templater, self).close()
1885 return super(changeset_templater, self).close()
1886
1886
1887 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1887 def _show(self, ctx, copies, matchfn, hunksfilterfn, props):
1888 '''show a single changeset or file revision'''
1888 '''show a single changeset or file revision'''
1889 props = props.copy()
1889 props = props.copy()
1890 props['ctx'] = ctx
1890 props['ctx'] = ctx
1891 props['index'] = index = next(self._counter)
1891 props['index'] = index = next(self._counter)
1892 props['revcache'] = {'copies': copies}
1892 props['revcache'] = {'copies': copies}
1893 props = pycompat.strkwargs(props)
1893 props = pycompat.strkwargs(props)
1894
1894
1895 # write separator, which wouldn't work well with the header part below
1895 # write separator, which wouldn't work well with the header part below
1896 # since there's inherently a conflict between header (across items) and
1896 # since there's inherently a conflict between header (across items) and
1897 # separator (per item)
1897 # separator (per item)
1898 if self._parts['separator'] and index > 0:
1898 if self._parts['separator'] and index > 0:
1899 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1899 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1900
1900
1901 # write header
1901 # write header
1902 if self._parts['header']:
1902 if self._parts['header']:
1903 h = templater.stringify(self.t(self._parts['header'], **props))
1903 h = templater.stringify(self.t(self._parts['header'], **props))
1904 if self.buffered:
1904 if self.buffered:
1905 self.header[ctx.rev()] = h
1905 self.header[ctx.rev()] = h
1906 else:
1906 else:
1907 if self.lastheader != h:
1907 if self.lastheader != h:
1908 self.lastheader = h
1908 self.lastheader = h
1909 self.ui.write(h)
1909 self.ui.write(h)
1910
1910
1911 # write changeset metadata, then patch if requested
1911 # write changeset metadata, then patch if requested
1912 key = self._parts[self._tref]
1912 key = self._parts[self._tref]
1913 self.ui.write(templater.stringify(self.t(key, **props)))
1913 self.ui.write(templater.stringify(self.t(key, **props)))
1914 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1914 self.showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn)
1915
1915
1916 if self._parts['footer']:
1916 if self._parts['footer']:
1917 if not self.footer:
1917 if not self.footer:
1918 self.footer = templater.stringify(
1918 self.footer = templater.stringify(
1919 self.t(self._parts['footer'], **props))
1919 self.t(self._parts['footer'], **props))
1920
1920
1921 def logtemplatespec(tmpl, mapfile):
1921 def logtemplatespec(tmpl, mapfile):
1922 if mapfile:
1922 if mapfile:
1923 return formatter.templatespec('changeset', tmpl, mapfile)
1923 return formatter.templatespec('changeset', tmpl, mapfile)
1924 else:
1924 else:
1925 return formatter.templatespec('', tmpl, None)
1925 return formatter.templatespec('', tmpl, None)
1926
1926
1927 def _lookuplogtemplate(ui, tmpl, style):
1927 def _lookuplogtemplate(ui, tmpl, style):
1928 """Find the template matching the given template spec or style
1928 """Find the template matching the given template spec or style
1929
1929
1930 See formatter.lookuptemplate() for details.
1930 See formatter.lookuptemplate() for details.
1931 """
1931 """
1932
1932
1933 # ui settings
1933 # ui settings
1934 if not tmpl and not style: # template are stronger than style
1934 if not tmpl and not style: # template are stronger than style
1935 tmpl = ui.config('ui', 'logtemplate')
1935 tmpl = ui.config('ui', 'logtemplate')
1936 if tmpl:
1936 if tmpl:
1937 return logtemplatespec(templater.unquotestring(tmpl), None)
1937 return logtemplatespec(templater.unquotestring(tmpl), None)
1938 else:
1938 else:
1939 style = util.expandpath(ui.config('ui', 'style'))
1939 style = util.expandpath(ui.config('ui', 'style'))
1940
1940
1941 if not tmpl and style:
1941 if not tmpl and style:
1942 mapfile = style
1942 mapfile = style
1943 if not os.path.split(mapfile)[0]:
1943 if not os.path.split(mapfile)[0]:
1944 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1944 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1945 or templater.templatepath(mapfile))
1945 or templater.templatepath(mapfile))
1946 if mapname:
1946 if mapname:
1947 mapfile = mapname
1947 mapfile = mapname
1948 return logtemplatespec(None, mapfile)
1948 return logtemplatespec(None, mapfile)
1949
1949
1950 if not tmpl:
1950 if not tmpl:
1951 return logtemplatespec(None, None)
1951 return logtemplatespec(None, None)
1952
1952
1953 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1953 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1954
1954
1955 def makelogtemplater(ui, repo, tmpl, buffered=False):
1955 def makelogtemplater(ui, repo, tmpl, buffered=False):
1956 """Create a changeset_templater from a literal template 'tmpl'
1956 """Create a changeset_templater from a literal template 'tmpl'
1957 byte-string."""
1957 byte-string."""
1958 spec = logtemplatespec(tmpl, None)
1958 spec = logtemplatespec(tmpl, None)
1959 return changeset_templater(ui, repo, spec, buffered=buffered)
1959 return changeset_templater(ui, repo, spec, buffered=buffered)
1960
1960
1961 def show_changeset(ui, repo, opts, buffered=False):
1961 def show_changeset(ui, repo, opts, buffered=False):
1962 """show one changeset using template or regular display.
1962 """show one changeset using template or regular display.
1963
1963
1964 Display format will be the first non-empty hit of:
1964 Display format will be the first non-empty hit of:
1965 1. option 'template'
1965 1. option 'template'
1966 2. option 'style'
1966 2. option 'style'
1967 3. [ui] setting 'logtemplate'
1967 3. [ui] setting 'logtemplate'
1968 4. [ui] setting 'style'
1968 4. [ui] setting 'style'
1969 If all of these values are either the unset or the empty string,
1969 If all of these values are either the unset or the empty string,
1970 regular display via changeset_printer() is done.
1970 regular display via changeset_printer() is done.
1971 """
1971 """
1972 # options
1972 # options
1973 match = None
1973 match = None
1974 if opts.get('patch') or opts.get('stat'):
1974 if opts.get('patch') or opts.get('stat'):
1975 match = scmutil.matchall(repo)
1975 match = scmutil.matchall(repo)
1976
1976
1977 if opts.get('template') == 'json':
1977 if opts.get('template') == 'json':
1978 return jsonchangeset(ui, repo, match, opts, buffered)
1978 return jsonchangeset(ui, repo, match, opts, buffered)
1979
1979
1980 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
1980 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
1981
1981
1982 if not spec.ref and not spec.tmpl and not spec.mapfile:
1982 if not spec.ref and not spec.tmpl and not spec.mapfile:
1983 return changeset_printer(ui, repo, match, opts, buffered)
1983 return changeset_printer(ui, repo, match, opts, buffered)
1984
1984
1985 return changeset_templater(ui, repo, spec, match, opts, buffered)
1985 return changeset_templater(ui, repo, spec, match, opts, buffered)
1986
1986
1987 def showmarker(fm, marker, index=None):
1987 def showmarker(fm, marker, index=None):
1988 """utility function to display obsolescence marker in a readable way
1988 """utility function to display obsolescence marker in a readable way
1989
1989
1990 To be used by debug function."""
1990 To be used by debug function."""
1991 if index is not None:
1991 if index is not None:
1992 fm.write('index', '%i ', index)
1992 fm.write('index', '%i ', index)
1993 fm.write('prednode', '%s ', hex(marker.prednode()))
1993 fm.write('prednode', '%s ', hex(marker.prednode()))
1994 succs = marker.succnodes()
1994 succs = marker.succnodes()
1995 fm.condwrite(succs, 'succnodes', '%s ',
1995 fm.condwrite(succs, 'succnodes', '%s ',
1996 fm.formatlist(map(hex, succs), name='node'))
1996 fm.formatlist(map(hex, succs), name='node'))
1997 fm.write('flag', '%X ', marker.flags())
1997 fm.write('flag', '%X ', marker.flags())
1998 parents = marker.parentnodes()
1998 parents = marker.parentnodes()
1999 if parents is not None:
1999 if parents is not None:
2000 fm.write('parentnodes', '{%s} ',
2000 fm.write('parentnodes', '{%s} ',
2001 fm.formatlist(map(hex, parents), name='node', sep=', '))
2001 fm.formatlist(map(hex, parents), name='node', sep=', '))
2002 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2002 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2003 meta = marker.metadata().copy()
2003 meta = marker.metadata().copy()
2004 meta.pop('date', None)
2004 meta.pop('date', None)
2005 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2005 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2006 fm.plain('\n')
2006 fm.plain('\n')
2007
2007
2008 def finddate(ui, repo, date):
2008 def finddate(ui, repo, date):
2009 """Find the tipmost changeset that matches the given date spec"""
2009 """Find the tipmost changeset that matches the given date spec"""
2010
2010
2011 df = util.matchdate(date)
2011 df = util.matchdate(date)
2012 m = scmutil.matchall(repo)
2012 m = scmutil.matchall(repo)
2013 results = {}
2013 results = {}
2014
2014
2015 def prep(ctx, fns):
2015 def prep(ctx, fns):
2016 d = ctx.date()
2016 d = ctx.date()
2017 if df(d[0]):
2017 if df(d[0]):
2018 results[ctx.rev()] = d
2018 results[ctx.rev()] = d
2019
2019
2020 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2020 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2021 rev = ctx.rev()
2021 rev = ctx.rev()
2022 if rev in results:
2022 if rev in results:
2023 ui.status(_("found revision %s from %s\n") %
2023 ui.status(_("found revision %s from %s\n") %
2024 (rev, util.datestr(results[rev])))
2024 (rev, util.datestr(results[rev])))
2025 return '%d' % rev
2025 return '%d' % rev
2026
2026
2027 raise error.Abort(_("revision matching date not found"))
2027 raise error.Abort(_("revision matching date not found"))
2028
2028
2029 def increasingwindows(windowsize=8, sizelimit=512):
2029 def increasingwindows(windowsize=8, sizelimit=512):
2030 while True:
2030 while True:
2031 yield windowsize
2031 yield windowsize
2032 if windowsize < sizelimit:
2032 if windowsize < sizelimit:
2033 windowsize *= 2
2033 windowsize *= 2
2034
2034
2035 def _walkrevs(repo, opts):
2035 def _walkrevs(repo, opts):
2036 # Default --rev value depends on --follow but --follow behavior
2036 # Default --rev value depends on --follow but --follow behavior
2037 # depends on revisions resolved from --rev...
2037 # depends on revisions resolved from --rev...
2038 follow = opts.get('follow') or opts.get('follow_first')
2038 follow = opts.get('follow') or opts.get('follow_first')
2039 if opts.get('rev'):
2039 if opts.get('rev'):
2040 revs = scmutil.revrange(repo, opts['rev'])
2040 revs = scmutil.revrange(repo, opts['rev'])
2041 elif follow and repo.dirstate.p1() == nullid:
2041 elif follow and repo.dirstate.p1() == nullid:
2042 revs = smartset.baseset()
2042 revs = smartset.baseset()
2043 elif follow:
2043 elif follow:
2044 revs = repo.revs('reverse(:.)')
2044 revs = repo.revs('reverse(:.)')
2045 else:
2045 else:
2046 revs = smartset.spanset(repo)
2046 revs = smartset.spanset(repo)
2047 revs.reverse()
2047 revs.reverse()
2048 return revs
2048 return revs
2049
2049
2050 class FileWalkError(Exception):
2050 class FileWalkError(Exception):
2051 pass
2051 pass
2052
2052
2053 def walkfilerevs(repo, match, follow, revs, fncache):
2053 def walkfilerevs(repo, match, follow, revs, fncache):
2054 '''Walks the file history for the matched files.
2054 '''Walks the file history for the matched files.
2055
2055
2056 Returns the changeset revs that are involved in the file history.
2056 Returns the changeset revs that are involved in the file history.
2057
2057
2058 Throws FileWalkError if the file history can't be walked using
2058 Throws FileWalkError if the file history can't be walked using
2059 filelogs alone.
2059 filelogs alone.
2060 '''
2060 '''
2061 wanted = set()
2061 wanted = set()
2062 copies = []
2062 copies = []
2063 minrev, maxrev = min(revs), max(revs)
2063 minrev, maxrev = min(revs), max(revs)
2064 def filerevgen(filelog, last):
2064 def filerevgen(filelog, last):
2065 """
2065 """
2066 Only files, no patterns. Check the history of each file.
2066 Only files, no patterns. Check the history of each file.
2067
2067
2068 Examines filelog entries within minrev, maxrev linkrev range
2068 Examines filelog entries within minrev, maxrev linkrev range
2069 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2069 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2070 tuples in backwards order
2070 tuples in backwards order
2071 """
2071 """
2072 cl_count = len(repo)
2072 cl_count = len(repo)
2073 revs = []
2073 revs = []
2074 for j in xrange(0, last + 1):
2074 for j in xrange(0, last + 1):
2075 linkrev = filelog.linkrev(j)
2075 linkrev = filelog.linkrev(j)
2076 if linkrev < minrev:
2076 if linkrev < minrev:
2077 continue
2077 continue
2078 # only yield rev for which we have the changelog, it can
2078 # only yield rev for which we have the changelog, it can
2079 # happen while doing "hg log" during a pull or commit
2079 # happen while doing "hg log" during a pull or commit
2080 if linkrev >= cl_count:
2080 if linkrev >= cl_count:
2081 break
2081 break
2082
2082
2083 parentlinkrevs = []
2083 parentlinkrevs = []
2084 for p in filelog.parentrevs(j):
2084 for p in filelog.parentrevs(j):
2085 if p != nullrev:
2085 if p != nullrev:
2086 parentlinkrevs.append(filelog.linkrev(p))
2086 parentlinkrevs.append(filelog.linkrev(p))
2087 n = filelog.node(j)
2087 n = filelog.node(j)
2088 revs.append((linkrev, parentlinkrevs,
2088 revs.append((linkrev, parentlinkrevs,
2089 follow and filelog.renamed(n)))
2089 follow and filelog.renamed(n)))
2090
2090
2091 return reversed(revs)
2091 return reversed(revs)
2092 def iterfiles():
2092 def iterfiles():
2093 pctx = repo['.']
2093 pctx = repo['.']
2094 for filename in match.files():
2094 for filename in match.files():
2095 if follow:
2095 if follow:
2096 if filename not in pctx:
2096 if filename not in pctx:
2097 raise error.Abort(_('cannot follow file not in parent '
2097 raise error.Abort(_('cannot follow file not in parent '
2098 'revision: "%s"') % filename)
2098 'revision: "%s"') % filename)
2099 yield filename, pctx[filename].filenode()
2099 yield filename, pctx[filename].filenode()
2100 else:
2100 else:
2101 yield filename, None
2101 yield filename, None
2102 for filename_node in copies:
2102 for filename_node in copies:
2103 yield filename_node
2103 yield filename_node
2104
2104
2105 for file_, node in iterfiles():
2105 for file_, node in iterfiles():
2106 filelog = repo.file(file_)
2106 filelog = repo.file(file_)
2107 if not len(filelog):
2107 if not len(filelog):
2108 if node is None:
2108 if node is None:
2109 # A zero count may be a directory or deleted file, so
2109 # A zero count may be a directory or deleted file, so
2110 # try to find matching entries on the slow path.
2110 # try to find matching entries on the slow path.
2111 if follow:
2111 if follow:
2112 raise error.Abort(
2112 raise error.Abort(
2113 _('cannot follow nonexistent file: "%s"') % file_)
2113 _('cannot follow nonexistent file: "%s"') % file_)
2114 raise FileWalkError("Cannot walk via filelog")
2114 raise FileWalkError("Cannot walk via filelog")
2115 else:
2115 else:
2116 continue
2116 continue
2117
2117
2118 if node is None:
2118 if node is None:
2119 last = len(filelog) - 1
2119 last = len(filelog) - 1
2120 else:
2120 else:
2121 last = filelog.rev(node)
2121 last = filelog.rev(node)
2122
2122
2123 # keep track of all ancestors of the file
2123 # keep track of all ancestors of the file
2124 ancestors = {filelog.linkrev(last)}
2124 ancestors = {filelog.linkrev(last)}
2125
2125
2126 # iterate from latest to oldest revision
2126 # iterate from latest to oldest revision
2127 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2127 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2128 if not follow:
2128 if not follow:
2129 if rev > maxrev:
2129 if rev > maxrev:
2130 continue
2130 continue
2131 else:
2131 else:
2132 # Note that last might not be the first interesting
2132 # Note that last might not be the first interesting
2133 # rev to us:
2133 # rev to us:
2134 # if the file has been changed after maxrev, we'll
2134 # if the file has been changed after maxrev, we'll
2135 # have linkrev(last) > maxrev, and we still need
2135 # have linkrev(last) > maxrev, and we still need
2136 # to explore the file graph
2136 # to explore the file graph
2137 if rev not in ancestors:
2137 if rev not in ancestors:
2138 continue
2138 continue
2139 # XXX insert 1327 fix here
2139 # XXX insert 1327 fix here
2140 if flparentlinkrevs:
2140 if flparentlinkrevs:
2141 ancestors.update(flparentlinkrevs)
2141 ancestors.update(flparentlinkrevs)
2142
2142
2143 fncache.setdefault(rev, []).append(file_)
2143 fncache.setdefault(rev, []).append(file_)
2144 wanted.add(rev)
2144 wanted.add(rev)
2145 if copied:
2145 if copied:
2146 copies.append(copied)
2146 copies.append(copied)
2147
2147
2148 return wanted
2148 return wanted
2149
2149
2150 class _followfilter(object):
2150 class _followfilter(object):
2151 def __init__(self, repo, onlyfirst=False):
2151 def __init__(self, repo, onlyfirst=False):
2152 self.repo = repo
2152 self.repo = repo
2153 self.startrev = nullrev
2153 self.startrev = nullrev
2154 self.roots = set()
2154 self.roots = set()
2155 self.onlyfirst = onlyfirst
2155 self.onlyfirst = onlyfirst
2156
2156
2157 def match(self, rev):
2157 def match(self, rev):
2158 def realparents(rev):
2158 def realparents(rev):
2159 if self.onlyfirst:
2159 if self.onlyfirst:
2160 return self.repo.changelog.parentrevs(rev)[0:1]
2160 return self.repo.changelog.parentrevs(rev)[0:1]
2161 else:
2161 else:
2162 return filter(lambda x: x != nullrev,
2162 return filter(lambda x: x != nullrev,
2163 self.repo.changelog.parentrevs(rev))
2163 self.repo.changelog.parentrevs(rev))
2164
2164
2165 if self.startrev == nullrev:
2165 if self.startrev == nullrev:
2166 self.startrev = rev
2166 self.startrev = rev
2167 return True
2167 return True
2168
2168
2169 if rev > self.startrev:
2169 if rev > self.startrev:
2170 # forward: all descendants
2170 # forward: all descendants
2171 if not self.roots:
2171 if not self.roots:
2172 self.roots.add(self.startrev)
2172 self.roots.add(self.startrev)
2173 for parent in realparents(rev):
2173 for parent in realparents(rev):
2174 if parent in self.roots:
2174 if parent in self.roots:
2175 self.roots.add(rev)
2175 self.roots.add(rev)
2176 return True
2176 return True
2177 else:
2177 else:
2178 # backwards: all parents
2178 # backwards: all parents
2179 if not self.roots:
2179 if not self.roots:
2180 self.roots.update(realparents(self.startrev))
2180 self.roots.update(realparents(self.startrev))
2181 if rev in self.roots:
2181 if rev in self.roots:
2182 self.roots.remove(rev)
2182 self.roots.remove(rev)
2183 self.roots.update(realparents(rev))
2183 self.roots.update(realparents(rev))
2184 return True
2184 return True
2185
2185
2186 return False
2186 return False
2187
2187
2188 def walkchangerevs(repo, match, opts, prepare):
2188 def walkchangerevs(repo, match, opts, prepare):
2189 '''Iterate over files and the revs in which they changed.
2189 '''Iterate over files and the revs in which they changed.
2190
2190
2191 Callers most commonly need to iterate backwards over the history
2191 Callers most commonly need to iterate backwards over the history
2192 in which they are interested. Doing so has awful (quadratic-looking)
2192 in which they are interested. Doing so has awful (quadratic-looking)
2193 performance, so we use iterators in a "windowed" way.
2193 performance, so we use iterators in a "windowed" way.
2194
2194
2195 We walk a window of revisions in the desired order. Within the
2195 We walk a window of revisions in the desired order. Within the
2196 window, we first walk forwards to gather data, then in the desired
2196 window, we first walk forwards to gather data, then in the desired
2197 order (usually backwards) to display it.
2197 order (usually backwards) to display it.
2198
2198
2199 This function returns an iterator yielding contexts. Before
2199 This function returns an iterator yielding contexts. Before
2200 yielding each context, the iterator will first call the prepare
2200 yielding each context, the iterator will first call the prepare
2201 function on each context in the window in forward order.'''
2201 function on each context in the window in forward order.'''
2202
2202
2203 follow = opts.get('follow') or opts.get('follow_first')
2203 follow = opts.get('follow') or opts.get('follow_first')
2204 revs = _walkrevs(repo, opts)
2204 revs = _walkrevs(repo, opts)
2205 if not revs:
2205 if not revs:
2206 return []
2206 return []
2207 wanted = set()
2207 wanted = set()
2208 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2208 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2209 fncache = {}
2209 fncache = {}
2210 change = repo.changectx
2210 change = repo.changectx
2211
2211
2212 # First step is to fill wanted, the set of revisions that we want to yield.
2212 # First step is to fill wanted, the set of revisions that we want to yield.
2213 # When it does not induce extra cost, we also fill fncache for revisions in
2213 # When it does not induce extra cost, we also fill fncache for revisions in
2214 # wanted: a cache of filenames that were changed (ctx.files()) and that
2214 # wanted: a cache of filenames that were changed (ctx.files()) and that
2215 # match the file filtering conditions.
2215 # match the file filtering conditions.
2216
2216
2217 if match.always():
2217 if match.always():
2218 # No files, no patterns. Display all revs.
2218 # No files, no patterns. Display all revs.
2219 wanted = revs
2219 wanted = revs
2220 elif not slowpath:
2220 elif not slowpath:
2221 # We only have to read through the filelog to find wanted revisions
2221 # We only have to read through the filelog to find wanted revisions
2222
2222
2223 try:
2223 try:
2224 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2224 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2225 except FileWalkError:
2225 except FileWalkError:
2226 slowpath = True
2226 slowpath = True
2227
2227
2228 # We decided to fall back to the slowpath because at least one
2228 # We decided to fall back to the slowpath because at least one
2229 # of the paths was not a file. Check to see if at least one of them
2229 # of the paths was not a file. Check to see if at least one of them
2230 # existed in history, otherwise simply return
2230 # existed in history, otherwise simply return
2231 for path in match.files():
2231 for path in match.files():
2232 if path == '.' or path in repo.store:
2232 if path == '.' or path in repo.store:
2233 break
2233 break
2234 else:
2234 else:
2235 return []
2235 return []
2236
2236
2237 if slowpath:
2237 if slowpath:
2238 # We have to read the changelog to match filenames against
2238 # We have to read the changelog to match filenames against
2239 # changed files
2239 # changed files
2240
2240
2241 if follow:
2241 if follow:
2242 raise error.Abort(_('can only follow copies/renames for explicit '
2242 raise error.Abort(_('can only follow copies/renames for explicit '
2243 'filenames'))
2243 'filenames'))
2244
2244
2245 # The slow path checks files modified in every changeset.
2245 # The slow path checks files modified in every changeset.
2246 # This is really slow on large repos, so compute the set lazily.
2246 # This is really slow on large repos, so compute the set lazily.
2247 class lazywantedset(object):
2247 class lazywantedset(object):
2248 def __init__(self):
2248 def __init__(self):
2249 self.set = set()
2249 self.set = set()
2250 self.revs = set(revs)
2250 self.revs = set(revs)
2251
2251
2252 # No need to worry about locality here because it will be accessed
2252 # No need to worry about locality here because it will be accessed
2253 # in the same order as the increasing window below.
2253 # in the same order as the increasing window below.
2254 def __contains__(self, value):
2254 def __contains__(self, value):
2255 if value in self.set:
2255 if value in self.set:
2256 return True
2256 return True
2257 elif not value in self.revs:
2257 elif not value in self.revs:
2258 return False
2258 return False
2259 else:
2259 else:
2260 self.revs.discard(value)
2260 self.revs.discard(value)
2261 ctx = change(value)
2261 ctx = change(value)
2262 matches = filter(match, ctx.files())
2262 matches = filter(match, ctx.files())
2263 if matches:
2263 if matches:
2264 fncache[value] = matches
2264 fncache[value] = matches
2265 self.set.add(value)
2265 self.set.add(value)
2266 return True
2266 return True
2267 return False
2267 return False
2268
2268
2269 def discard(self, value):
2269 def discard(self, value):
2270 self.revs.discard(value)
2270 self.revs.discard(value)
2271 self.set.discard(value)
2271 self.set.discard(value)
2272
2272
2273 wanted = lazywantedset()
2273 wanted = lazywantedset()
2274
2274
2275 # it might be worthwhile to do this in the iterator if the rev range
2275 # it might be worthwhile to do this in the iterator if the rev range
2276 # is descending and the prune args are all within that range
2276 # is descending and the prune args are all within that range
2277 for rev in opts.get('prune', ()):
2277 for rev in opts.get('prune', ()):
2278 rev = repo[rev].rev()
2278 rev = repo[rev].rev()
2279 ff = _followfilter(repo)
2279 ff = _followfilter(repo)
2280 stop = min(revs[0], revs[-1])
2280 stop = min(revs[0], revs[-1])
2281 for x in xrange(rev, stop - 1, -1):
2281 for x in xrange(rev, stop - 1, -1):
2282 if ff.match(x):
2282 if ff.match(x):
2283 wanted = wanted - [x]
2283 wanted = wanted - [x]
2284
2284
2285 # Now that wanted is correctly initialized, we can iterate over the
2285 # Now that wanted is correctly initialized, we can iterate over the
2286 # revision range, yielding only revisions in wanted.
2286 # revision range, yielding only revisions in wanted.
2287 def iterate():
2287 def iterate():
2288 if follow and match.always():
2288 if follow and match.always():
2289 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2289 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2290 def want(rev):
2290 def want(rev):
2291 return ff.match(rev) and rev in wanted
2291 return ff.match(rev) and rev in wanted
2292 else:
2292 else:
2293 def want(rev):
2293 def want(rev):
2294 return rev in wanted
2294 return rev in wanted
2295
2295
2296 it = iter(revs)
2296 it = iter(revs)
2297 stopiteration = False
2297 stopiteration = False
2298 for windowsize in increasingwindows():
2298 for windowsize in increasingwindows():
2299 nrevs = []
2299 nrevs = []
2300 for i in xrange(windowsize):
2300 for i in xrange(windowsize):
2301 rev = next(it, None)
2301 rev = next(it, None)
2302 if rev is None:
2302 if rev is None:
2303 stopiteration = True
2303 stopiteration = True
2304 break
2304 break
2305 elif want(rev):
2305 elif want(rev):
2306 nrevs.append(rev)
2306 nrevs.append(rev)
2307 for rev in sorted(nrevs):
2307 for rev in sorted(nrevs):
2308 fns = fncache.get(rev)
2308 fns = fncache.get(rev)
2309 ctx = change(rev)
2309 ctx = change(rev)
2310 if not fns:
2310 if not fns:
2311 def fns_generator():
2311 def fns_generator():
2312 for f in ctx.files():
2312 for f in ctx.files():
2313 if match(f):
2313 if match(f):
2314 yield f
2314 yield f
2315 fns = fns_generator()
2315 fns = fns_generator()
2316 prepare(ctx, fns)
2316 prepare(ctx, fns)
2317 for rev in nrevs:
2317 for rev in nrevs:
2318 yield change(rev)
2318 yield change(rev)
2319
2319
2320 if stopiteration:
2320 if stopiteration:
2321 break
2321 break
2322
2322
2323 return iterate()
2323 return iterate()
2324
2324
2325 def _makefollowlogfilematcher(repo, files, followfirst):
2325 def _makefollowlogfilematcher(repo, files, followfirst):
2326 # When displaying a revision with --patch --follow FILE, we have
2326 # When displaying a revision with --patch --follow FILE, we have
2327 # to know which file of the revision must be diffed. With
2327 # to know which file of the revision must be diffed. With
2328 # --follow, we want the names of the ancestors of FILE in the
2328 # --follow, we want the names of the ancestors of FILE in the
2329 # revision, stored in "fcache". "fcache" is populated by
2329 # revision, stored in "fcache". "fcache" is populated by
2330 # reproducing the graph traversal already done by --follow revset
2330 # reproducing the graph traversal already done by --follow revset
2331 # and relating revs to file names (which is not "correct" but
2331 # and relating revs to file names (which is not "correct" but
2332 # good enough).
2332 # good enough).
2333 fcache = {}
2333 fcache = {}
2334 fcacheready = [False]
2334 fcacheready = [False]
2335 pctx = repo['.']
2335 pctx = repo['.']
2336
2336
2337 def populate():
2337 def populate():
2338 for fn in files:
2338 for fn in files:
2339 fctx = pctx[fn]
2339 fctx = pctx[fn]
2340 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2340 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2341 for c in fctx.ancestors(followfirst=followfirst):
2341 for c in fctx.ancestors(followfirst=followfirst):
2342 fcache.setdefault(c.rev(), set()).add(c.path())
2342 fcache.setdefault(c.rev(), set()).add(c.path())
2343
2343
2344 def filematcher(rev):
2344 def filematcher(rev):
2345 if not fcacheready[0]:
2345 if not fcacheready[0]:
2346 # Lazy initialization
2346 # Lazy initialization
2347 fcacheready[0] = True
2347 fcacheready[0] = True
2348 populate()
2348 populate()
2349 return scmutil.matchfiles(repo, fcache.get(rev, []))
2349 return scmutil.matchfiles(repo, fcache.get(rev, []))
2350
2350
2351 return filematcher
2351 return filematcher
2352
2352
2353 def _makenofollowlogfilematcher(repo, pats, opts):
2353 def _makenofollowlogfilematcher(repo, pats, opts):
2354 '''hook for extensions to override the filematcher for non-follow cases'''
2354 '''hook for extensions to override the filematcher for non-follow cases'''
2355 return None
2355 return None
2356
2356
2357 _opt2logrevset = {
2357 _opt2logrevset = {
2358 'no_merges': ('not merge()', None),
2358 'no_merges': ('not merge()', None),
2359 'only_merges': ('merge()', None),
2359 'only_merges': ('merge()', None),
2360 '_ancestors': ('ancestors(%r)', None),
2360 '_ancestors': ('ancestors(%r)', None),
2361 '_fancestors': ('_firstancestors(%r)', None),
2361 '_fancestors': ('_firstancestors(%r)', None),
2362 '_matchfiles': (None, '_matchfiles(%ps)'),
2362 '_matchfiles': (None, '_matchfiles(%ps)'),
2363 'date': ('date(%s)', None),
2363 'date': ('date(%s)', None),
2364 'branch': ('branch(%s)', '%lr'),
2364 'branch': ('branch(%s)', '%lr'),
2365 '_patslog': ('filelog(%s)', '%lr'),
2365 '_patslog': ('filelog(%s)', '%lr'),
2366 '_patsfollow': ('follow(%s)', '%lr'),
2366 '_patsfollow': ('follow(%s)', '%lr'),
2367 '_patsfollowfirst': ('_followfirst(%s)', '%lr'),
2367 '_patsfollowfirst': ('_followfirst(%s)', '%lr'),
2368 'keyword': ('keyword(%s)', '%lr'),
2368 'keyword': ('keyword(%s)', '%lr'),
2369 'prune': ('ancestors(%s)', 'not %lr'),
2369 'prune': ('ancestors(%s)', 'not %lr'),
2370 'user': ('user(%s)', '%lr'),
2370 'user': ('user(%s)', '%lr'),
2371 }
2371 }
2372
2372
2373 def _makelogrevset(repo, pats, opts):
2373 def _makelogrevset(repo, pats, opts):
2374 """Return (expr, filematcher) where expr is a revset string built
2374 """Return (expr, filematcher) where expr is a revset string built
2375 from log options and file patterns or None. If --stat or --patch
2375 from log options and file patterns or None. If --stat or --patch
2376 are not passed filematcher is None. Otherwise it is a callable
2376 are not passed filematcher is None. Otherwise it is a callable
2377 taking a revision number and returning a match objects filtering
2377 taking a revision number and returning a match objects filtering
2378 the files to be detailed when displaying the revision.
2378 the files to be detailed when displaying the revision.
2379 """
2379 """
2380 opts = dict(opts)
2380 opts = dict(opts)
2381 # follow or not follow?
2381 # follow or not follow?
2382 follow = opts.get('follow') or opts.get('follow_first')
2382 follow = opts.get('follow') or opts.get('follow_first')
2383 if opts.get('follow_first'):
2383 if opts.get('follow_first'):
2384 followfirst = 1
2384 followfirst = 1
2385 else:
2385 else:
2386 followfirst = 0
2386 followfirst = 0
2387
2387
2388 # branch and only_branch are really aliases and must be handled at
2388 # branch and only_branch are really aliases and must be handled at
2389 # the same time
2389 # the same time
2390 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2390 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2391 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2391 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2392 # pats/include/exclude are passed to match.match() directly in
2392 # pats/include/exclude are passed to match.match() directly in
2393 # _matchfiles() revset but walkchangerevs() builds its matcher with
2393 # _matchfiles() revset but walkchangerevs() builds its matcher with
2394 # scmutil.match(). The difference is input pats are globbed on
2394 # scmutil.match(). The difference is input pats are globbed on
2395 # platforms without shell expansion (windows).
2395 # platforms without shell expansion (windows).
2396 wctx = repo[None]
2396 wctx = repo[None]
2397 match, pats = scmutil.matchandpats(wctx, pats, opts)
2397 match, pats = scmutil.matchandpats(wctx, pats, opts)
2398 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2398 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
2399 if not slowpath:
2399 if not slowpath:
2400 for f in match.files():
2400 for f in match.files():
2401 if follow and f not in wctx:
2401 if follow and f not in wctx:
2402 # If the file exists, it may be a directory, so let it
2402 # If the file exists, it may be a directory, so let it
2403 # take the slow path.
2403 # take the slow path.
2404 if os.path.exists(repo.wjoin(f)):
2404 if os.path.exists(repo.wjoin(f)):
2405 slowpath = True
2405 slowpath = True
2406 continue
2406 continue
2407 else:
2407 else:
2408 raise error.Abort(_('cannot follow file not in parent '
2408 raise error.Abort(_('cannot follow file not in parent '
2409 'revision: "%s"') % f)
2409 'revision: "%s"') % f)
2410 filelog = repo.file(f)
2410 filelog = repo.file(f)
2411 if not filelog:
2411 if not filelog:
2412 # A zero count may be a directory or deleted file, so
2412 # A zero count may be a directory or deleted file, so
2413 # try to find matching entries on the slow path.
2413 # try to find matching entries on the slow path.
2414 if follow:
2414 if follow:
2415 raise error.Abort(
2415 raise error.Abort(
2416 _('cannot follow nonexistent file: "%s"') % f)
2416 _('cannot follow nonexistent file: "%s"') % f)
2417 slowpath = True
2417 slowpath = True
2418
2418
2419 # We decided to fall back to the slowpath because at least one
2419 # We decided to fall back to the slowpath because at least one
2420 # of the paths was not a file. Check to see if at least one of them
2420 # of the paths was not a file. Check to see if at least one of them
2421 # existed in history - in that case, we'll continue down the
2421 # existed in history - in that case, we'll continue down the
2422 # slowpath; otherwise, we can turn off the slowpath
2422 # slowpath; otherwise, we can turn off the slowpath
2423 if slowpath:
2423 if slowpath:
2424 for path in match.files():
2424 for path in match.files():
2425 if path == '.' or path in repo.store:
2425 if path == '.' or path in repo.store:
2426 break
2426 break
2427 else:
2427 else:
2428 slowpath = False
2428 slowpath = False
2429
2429
2430 fpats = ('_patsfollow', '_patsfollowfirst')
2430 fpats = ('_patsfollow', '_patsfollowfirst')
2431 fnopats = ('_ancestors', '_fancestors')
2431 fnopats = ('_ancestors', '_fancestors')
2432
2432
2433 if slowpath:
2433 if slowpath:
2434 # See walkchangerevs() slow path.
2434 # See walkchangerevs() slow path.
2435 #
2435 #
2436 # pats/include/exclude cannot be represented as separate
2436 # pats/include/exclude cannot be represented as separate
2437 # revset expressions as their filtering logic applies at file
2437 # revset expressions as their filtering logic applies at file
2438 # level. For instance "-I a -X a" matches a revision touching
2438 # level. For instance "-I a -X a" matches a revision touching
2439 # "a" and "b" while "file(a) and not file(b)" does
2439 # "a" and "b" while "file(a) and not file(b)" does
2440 # not. Besides, filesets are evaluated against the working
2440 # not. Besides, filesets are evaluated against the working
2441 # directory.
2441 # directory.
2442 matchargs = ['r:', 'd:relpath']
2442 matchargs = ['r:', 'd:relpath']
2443 for p in pats:
2443 for p in pats:
2444 matchargs.append('p:' + p)
2444 matchargs.append('p:' + p)
2445 for p in opts.get('include', []):
2445 for p in opts.get('include', []):
2446 matchargs.append('i:' + p)
2446 matchargs.append('i:' + p)
2447 for p in opts.get('exclude', []):
2447 for p in opts.get('exclude', []):
2448 matchargs.append('x:' + p)
2448 matchargs.append('x:' + p)
2449 opts['_matchfiles'] = matchargs
2449 opts['_matchfiles'] = matchargs
2450 if follow:
2450 if follow:
2451 opts[fnopats[followfirst]] = '.'
2451 opts[fnopats[followfirst]] = '.'
2452 else:
2452 else:
2453 if follow:
2453 if follow:
2454 if pats:
2454 if pats:
2455 # follow() revset interprets its file argument as a
2455 # follow() revset interprets its file argument as a
2456 # manifest entry, so use match.files(), not pats.
2456 # manifest entry, so use match.files(), not pats.
2457 opts[fpats[followfirst]] = list(match.files())
2457 opts[fpats[followfirst]] = list(match.files())
2458 else:
2458 else:
2459 op = fnopats[followfirst]
2459 op = fnopats[followfirst]
2460 opts[op] = '.'
2460 opts[op] = '.'
2461 else:
2461 else:
2462 opts['_patslog'] = list(pats)
2462 opts['_patslog'] = list(pats)
2463
2463
2464 filematcher = None
2464 filematcher = None
2465 if opts.get('patch') or opts.get('stat'):
2465 if opts.get('patch') or opts.get('stat'):
2466 # When following files, track renames via a special matcher.
2466 # When following files, track renames via a special matcher.
2467 # If we're forced to take the slowpath it means we're following
2467 # If we're forced to take the slowpath it means we're following
2468 # at least one pattern/directory, so don't bother with rename tracking.
2468 # at least one pattern/directory, so don't bother with rename tracking.
2469 if follow and not match.always() and not slowpath:
2469 if follow and not match.always() and not slowpath:
2470 # _makefollowlogfilematcher expects its files argument to be
2470 # _makefollowlogfilematcher expects its files argument to be
2471 # relative to the repo root, so use match.files(), not pats.
2471 # relative to the repo root, so use match.files(), not pats.
2472 filematcher = _makefollowlogfilematcher(repo, match.files(),
2472 filematcher = _makefollowlogfilematcher(repo, match.files(),
2473 followfirst)
2473 followfirst)
2474 else:
2474 else:
2475 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2475 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2476 if filematcher is None:
2476 if filematcher is None:
2477 filematcher = lambda rev: match
2477 filematcher = lambda rev: match
2478
2478
2479 expr = []
2479 expr = []
2480 for op, val in sorted(opts.iteritems()):
2480 for op, val in sorted(opts.iteritems()):
2481 if not val:
2481 if not val:
2482 continue
2482 continue
2483 if op not in _opt2logrevset:
2483 if op not in _opt2logrevset:
2484 continue
2484 continue
2485 revop, listop = _opt2logrevset[op]
2485 revop, listop = _opt2logrevset[op]
2486 if revop and '%' not in revop:
2486 if revop and '%' not in revop:
2487 expr.append(revop)
2487 expr.append(revop)
2488 elif not listop:
2488 elif not listop:
2489 expr.append(revsetlang.formatspec(revop, val))
2489 expr.append(revsetlang.formatspec(revop, val))
2490 else:
2490 else:
2491 if revop:
2491 if revop:
2492 val = [revsetlang.formatspec(revop, v) for v in val]
2492 val = [revsetlang.formatspec(revop, v) for v in val]
2493 expr.append(revsetlang.formatspec(listop, val))
2493 expr.append(revsetlang.formatspec(listop, val))
2494
2494
2495 if expr:
2495 if expr:
2496 expr = '(' + ' and '.join(expr) + ')'
2496 expr = '(' + ' and '.join(expr) + ')'
2497 else:
2497 else:
2498 expr = None
2498 expr = None
2499 return expr, filematcher
2499 return expr, filematcher
2500
2500
2501 def _logrevs(repo, opts):
2501 def _logrevs(repo, opts):
2502 follow = opts.get('follow') or opts.get('follow_first')
2502 follow = opts.get('follow') or opts.get('follow_first')
2503 if opts.get('rev'):
2503 if opts.get('rev'):
2504 revs = scmutil.revrange(repo, opts['rev'])
2504 revs = scmutil.revrange(repo, opts['rev'])
2505 elif follow and repo.dirstate.p1() == nullid:
2505 elif follow and repo.dirstate.p1() == nullid:
2506 revs = smartset.baseset()
2506 revs = smartset.baseset()
2507 elif follow:
2507 elif follow:
2508 revs = repo.revs('reverse(:.)')
2508 revs = repo.revs('reverse(:.)')
2509 else:
2509 else:
2510 revs = smartset.spanset(repo)
2510 revs = smartset.spanset(repo)
2511 revs.reverse()
2511 revs.reverse()
2512 return revs
2512 return revs
2513
2513
2514 def getlogrevs(repo, pats, opts):
2514 def getlogrevs(repo, pats, opts):
2515 """Return (revs, filematcher) where revs is a smartset
2515 """Return (revs, filematcher) where revs is a smartset
2516
2516
2517 If --stat or --patch is not passed, filematcher is None. Otherwise it
2517 If --stat or --patch is not passed, filematcher is None. Otherwise it
2518 is a callable taking a revision number and returning a match objects
2518 is a callable taking a revision number and returning a match objects
2519 filtering the files to be detailed when displaying the revision.
2519 filtering the files to be detailed when displaying the revision.
2520 """
2520 """
2521 follow = opts.get('follow') or opts.get('follow_first')
2522 followfirst = opts.get('follow_first')
2523 if opts.get('rev'):
2524 # TODO: do not mutate opts here
2525 opts.pop('follow', None)
2526 opts.pop('follow_first', None)
2521 limit = loglimit(opts)
2527 limit = loglimit(opts)
2522 revs = _logrevs(repo, opts)
2528 revs = _logrevs(repo, opts)
2523 if not revs:
2529 if not revs:
2524 return smartset.baseset(), None
2530 return smartset.baseset(), None
2531 if opts.get('rev') and follow:
2532 revs = dagop.revancestors(repo, revs, followfirst=followfirst)
2533 revs.reverse()
2525 expr, filematcher = _makelogrevset(repo, pats, opts)
2534 expr, filematcher = _makelogrevset(repo, pats, opts)
2526 if opts.get('graph') and opts.get('rev'):
2535 if opts.get('graph') and opts.get('rev'):
2527 # User-specified revs might be unsorted, but don't sort before
2536 # User-specified revs might be unsorted, but don't sort before
2528 # _makelogrevset because it might depend on the order of revs
2537 # _makelogrevset because it might depend on the order of revs
2529 if not (revs.isdescending() or revs.istopo()):
2538 if not (revs.isdescending() or revs.istopo()):
2530 revs.sort(reverse=True)
2539 revs.sort(reverse=True)
2531 if expr:
2540 if expr:
2532 matcher = revset.match(None, expr)
2541 matcher = revset.match(None, expr)
2533 revs = matcher(repo, revs)
2542 revs = matcher(repo, revs)
2534 if limit is not None:
2543 if limit is not None:
2535 revs = revs.slice(0, limit)
2544 revs = revs.slice(0, limit)
2536 return revs, filematcher
2545 return revs, filematcher
2537
2546
2538 def _parselinerangelogopt(repo, opts):
2547 def _parselinerangelogopt(repo, opts):
2539 """Parse --line-range log option and return a list of tuples (filename,
2548 """Parse --line-range log option and return a list of tuples (filename,
2540 (fromline, toline)).
2549 (fromline, toline)).
2541 """
2550 """
2542 linerangebyfname = []
2551 linerangebyfname = []
2543 for pat in opts.get('line_range', []):
2552 for pat in opts.get('line_range', []):
2544 try:
2553 try:
2545 pat, linerange = pat.rsplit(',', 1)
2554 pat, linerange = pat.rsplit(',', 1)
2546 except ValueError:
2555 except ValueError:
2547 raise error.Abort(_('malformatted line-range pattern %s') % pat)
2556 raise error.Abort(_('malformatted line-range pattern %s') % pat)
2548 try:
2557 try:
2549 fromline, toline = map(int, linerange.split(':'))
2558 fromline, toline = map(int, linerange.split(':'))
2550 except ValueError:
2559 except ValueError:
2551 raise error.Abort(_("invalid line range for %s") % pat)
2560 raise error.Abort(_("invalid line range for %s") % pat)
2552 msg = _("line range pattern '%s' must match exactly one file") % pat
2561 msg = _("line range pattern '%s' must match exactly one file") % pat
2553 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
2562 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
2554 linerangebyfname.append(
2563 linerangebyfname.append(
2555 (fname, util.processlinerange(fromline, toline)))
2564 (fname, util.processlinerange(fromline, toline)))
2556 return linerangebyfname
2565 return linerangebyfname
2557
2566
2558 def getloglinerangerevs(repo, userrevs, opts):
2567 def getloglinerangerevs(repo, userrevs, opts):
2559 """Return (revs, filematcher, hunksfilter).
2568 """Return (revs, filematcher, hunksfilter).
2560
2569
2561 "revs" are revisions obtained by processing "line-range" log options and
2570 "revs" are revisions obtained by processing "line-range" log options and
2562 walking block ancestors of each specified file/line-range.
2571 walking block ancestors of each specified file/line-range.
2563
2572
2564 "filematcher(rev) -> match" is a factory function returning a match object
2573 "filematcher(rev) -> match" is a factory function returning a match object
2565 for a given revision for file patterns specified in --line-range option.
2574 for a given revision for file patterns specified in --line-range option.
2566 If neither --stat nor --patch options are passed, "filematcher" is None.
2575 If neither --stat nor --patch options are passed, "filematcher" is None.
2567
2576
2568 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function
2577 "hunksfilter(rev) -> filterfn(fctx, hunks)" is a factory function
2569 returning a hunks filtering function.
2578 returning a hunks filtering function.
2570 If neither --stat nor --patch options are passed, "filterhunks" is None.
2579 If neither --stat nor --patch options are passed, "filterhunks" is None.
2571 """
2580 """
2572 wctx = repo[None]
2581 wctx = repo[None]
2573
2582
2574 # Two-levels map of "rev -> file ctx -> [line range]".
2583 # Two-levels map of "rev -> file ctx -> [line range]".
2575 linerangesbyrev = {}
2584 linerangesbyrev = {}
2576 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
2585 for fname, (fromline, toline) in _parselinerangelogopt(repo, opts):
2577 if fname not in wctx:
2586 if fname not in wctx:
2578 raise error.Abort(_('cannot follow file not in parent '
2587 raise error.Abort(_('cannot follow file not in parent '
2579 'revision: "%s"') % fname)
2588 'revision: "%s"') % fname)
2580 fctx = wctx.filectx(fname)
2589 fctx = wctx.filectx(fname)
2581 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
2590 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
2582 rev = fctx.introrev()
2591 rev = fctx.introrev()
2583 if rev not in userrevs:
2592 if rev not in userrevs:
2584 continue
2593 continue
2585 linerangesbyrev.setdefault(
2594 linerangesbyrev.setdefault(
2586 rev, {}).setdefault(
2595 rev, {}).setdefault(
2587 fctx.path(), []).append(linerange)
2596 fctx.path(), []).append(linerange)
2588
2597
2589 filematcher = None
2598 filematcher = None
2590 hunksfilter = None
2599 hunksfilter = None
2591 if opts.get('patch') or opts.get('stat'):
2600 if opts.get('patch') or opts.get('stat'):
2592
2601
2593 def nofilterhunksfn(fctx, hunks):
2602 def nofilterhunksfn(fctx, hunks):
2594 return hunks
2603 return hunks
2595
2604
2596 def hunksfilter(rev):
2605 def hunksfilter(rev):
2597 fctxlineranges = linerangesbyrev.get(rev)
2606 fctxlineranges = linerangesbyrev.get(rev)
2598 if fctxlineranges is None:
2607 if fctxlineranges is None:
2599 return nofilterhunksfn
2608 return nofilterhunksfn
2600
2609
2601 def filterfn(fctx, hunks):
2610 def filterfn(fctx, hunks):
2602 lineranges = fctxlineranges.get(fctx.path())
2611 lineranges = fctxlineranges.get(fctx.path())
2603 if lineranges is not None:
2612 if lineranges is not None:
2604 for hr, lines in hunks:
2613 for hr, lines in hunks:
2605 if hr is None: # binary
2614 if hr is None: # binary
2606 yield hr, lines
2615 yield hr, lines
2607 continue
2616 continue
2608 if any(mdiff.hunkinrange(hr[2:], lr)
2617 if any(mdiff.hunkinrange(hr[2:], lr)
2609 for lr in lineranges):
2618 for lr in lineranges):
2610 yield hr, lines
2619 yield hr, lines
2611 else:
2620 else:
2612 for hunk in hunks:
2621 for hunk in hunks:
2613 yield hunk
2622 yield hunk
2614
2623
2615 return filterfn
2624 return filterfn
2616
2625
2617 def filematcher(rev):
2626 def filematcher(rev):
2618 files = list(linerangesbyrev.get(rev, []))
2627 files = list(linerangesbyrev.get(rev, []))
2619 return scmutil.matchfiles(repo, files)
2628 return scmutil.matchfiles(repo, files)
2620
2629
2621 revs = sorted(linerangesbyrev, reverse=True)
2630 revs = sorted(linerangesbyrev, reverse=True)
2622
2631
2623 return revs, filematcher, hunksfilter
2632 return revs, filematcher, hunksfilter
2624
2633
2625 def _graphnodeformatter(ui, displayer):
2634 def _graphnodeformatter(ui, displayer):
2626 spec = ui.config('ui', 'graphnodetemplate')
2635 spec = ui.config('ui', 'graphnodetemplate')
2627 if not spec:
2636 if not spec:
2628 return templatekw.showgraphnode # fast path for "{graphnode}"
2637 return templatekw.showgraphnode # fast path for "{graphnode}"
2629
2638
2630 spec = templater.unquotestring(spec)
2639 spec = templater.unquotestring(spec)
2631 tres = formatter.templateresources(ui)
2640 tres = formatter.templateresources(ui)
2632 if isinstance(displayer, changeset_templater):
2641 if isinstance(displayer, changeset_templater):
2633 tres['cache'] = displayer.cache # reuse cache of slow templates
2642 tres['cache'] = displayer.cache # reuse cache of slow templates
2634 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
2643 templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords,
2635 resources=tres)
2644 resources=tres)
2636 def formatnode(repo, ctx):
2645 def formatnode(repo, ctx):
2637 props = {'ctx': ctx, 'repo': repo, 'revcache': {}}
2646 props = {'ctx': ctx, 'repo': repo, 'revcache': {}}
2638 return templ.render(props)
2647 return templ.render(props)
2639 return formatnode
2648 return formatnode
2640
2649
2641 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2650 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2642 filematcher=None, props=None):
2651 filematcher=None, props=None):
2643 props = props or {}
2652 props = props or {}
2644 formatnode = _graphnodeformatter(ui, displayer)
2653 formatnode = _graphnodeformatter(ui, displayer)
2645 state = graphmod.asciistate()
2654 state = graphmod.asciistate()
2646 styles = state['styles']
2655 styles = state['styles']
2647
2656
2648 # only set graph styling if HGPLAIN is not set.
2657 # only set graph styling if HGPLAIN is not set.
2649 if ui.plain('graph'):
2658 if ui.plain('graph'):
2650 # set all edge styles to |, the default pre-3.8 behaviour
2659 # set all edge styles to |, the default pre-3.8 behaviour
2651 styles.update(dict.fromkeys(styles, '|'))
2660 styles.update(dict.fromkeys(styles, '|'))
2652 else:
2661 else:
2653 edgetypes = {
2662 edgetypes = {
2654 'parent': graphmod.PARENT,
2663 'parent': graphmod.PARENT,
2655 'grandparent': graphmod.GRANDPARENT,
2664 'grandparent': graphmod.GRANDPARENT,
2656 'missing': graphmod.MISSINGPARENT
2665 'missing': graphmod.MISSINGPARENT
2657 }
2666 }
2658 for name, key in edgetypes.items():
2667 for name, key in edgetypes.items():
2659 # experimental config: experimental.graphstyle.*
2668 # experimental config: experimental.graphstyle.*
2660 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2669 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2661 styles[key])
2670 styles[key])
2662 if not styles[key]:
2671 if not styles[key]:
2663 styles[key] = None
2672 styles[key] = None
2664
2673
2665 # experimental config: experimental.graphshorten
2674 # experimental config: experimental.graphshorten
2666 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2675 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2667
2676
2668 for rev, type, ctx, parents in dag:
2677 for rev, type, ctx, parents in dag:
2669 char = formatnode(repo, ctx)
2678 char = formatnode(repo, ctx)
2670 copies = None
2679 copies = None
2671 if getrenamed and ctx.rev():
2680 if getrenamed and ctx.rev():
2672 copies = []
2681 copies = []
2673 for fn in ctx.files():
2682 for fn in ctx.files():
2674 rename = getrenamed(fn, ctx.rev())
2683 rename = getrenamed(fn, ctx.rev())
2675 if rename:
2684 if rename:
2676 copies.append((fn, rename[0]))
2685 copies.append((fn, rename[0]))
2677 revmatchfn = None
2686 revmatchfn = None
2678 if filematcher is not None:
2687 if filematcher is not None:
2679 revmatchfn = filematcher(ctx.rev())
2688 revmatchfn = filematcher(ctx.rev())
2680 edges = edgefn(type, char, state, rev, parents)
2689 edges = edgefn(type, char, state, rev, parents)
2681 firstedge = next(edges)
2690 firstedge = next(edges)
2682 width = firstedge[2]
2691 width = firstedge[2]
2683 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2692 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
2684 _graphwidth=width, **pycompat.strkwargs(props))
2693 _graphwidth=width, **pycompat.strkwargs(props))
2685 lines = displayer.hunk.pop(rev).split('\n')
2694 lines = displayer.hunk.pop(rev).split('\n')
2686 if not lines[-1]:
2695 if not lines[-1]:
2687 del lines[-1]
2696 del lines[-1]
2688 displayer.flush(ctx)
2697 displayer.flush(ctx)
2689 for type, char, width, coldata in itertools.chain([firstedge], edges):
2698 for type, char, width, coldata in itertools.chain([firstedge], edges):
2690 graphmod.ascii(ui, state, type, char, lines, coldata)
2699 graphmod.ascii(ui, state, type, char, lines, coldata)
2691 lines = []
2700 lines = []
2692 displayer.close()
2701 displayer.close()
2693
2702
2694 def graphlog(ui, repo, revs, filematcher, opts):
2703 def graphlog(ui, repo, revs, filematcher, opts):
2695 # Parameters are identical to log command ones
2704 # Parameters are identical to log command ones
2696 revdag = graphmod.dagwalker(repo, revs)
2705 revdag = graphmod.dagwalker(repo, revs)
2697
2706
2698 getrenamed = None
2707 getrenamed = None
2699 if opts.get('copies'):
2708 if opts.get('copies'):
2700 endrev = None
2709 endrev = None
2701 if opts.get('rev'):
2710 if opts.get('rev'):
2702 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2711 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2703 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2712 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2704
2713
2705 ui.pager('log')
2714 ui.pager('log')
2706 displayer = show_changeset(ui, repo, opts, buffered=True)
2715 displayer = show_changeset(ui, repo, opts, buffered=True)
2707 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2716 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2708 filematcher)
2717 filematcher)
2709
2718
2710 def checkunsupportedgraphflags(pats, opts):
2719 def checkunsupportedgraphflags(pats, opts):
2711 for op in ["newest_first"]:
2720 for op in ["newest_first"]:
2712 if op in opts and opts[op]:
2721 if op in opts and opts[op]:
2713 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2722 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2714 % op.replace("_", "-"))
2723 % op.replace("_", "-"))
2715
2724
2716 def graphrevs(repo, nodes, opts):
2725 def graphrevs(repo, nodes, opts):
2717 limit = loglimit(opts)
2726 limit = loglimit(opts)
2718 nodes.reverse()
2727 nodes.reverse()
2719 if limit is not None:
2728 if limit is not None:
2720 nodes = nodes[:limit]
2729 nodes = nodes[:limit]
2721 return graphmod.nodes(repo, nodes)
2730 return graphmod.nodes(repo, nodes)
2722
2731
2723 def add(ui, repo, match, prefix, explicitonly, **opts):
2732 def add(ui, repo, match, prefix, explicitonly, **opts):
2724 join = lambda f: os.path.join(prefix, f)
2733 join = lambda f: os.path.join(prefix, f)
2725 bad = []
2734 bad = []
2726
2735
2727 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2736 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2728 names = []
2737 names = []
2729 wctx = repo[None]
2738 wctx = repo[None]
2730 cca = None
2739 cca = None
2731 abort, warn = scmutil.checkportabilityalert(ui)
2740 abort, warn = scmutil.checkportabilityalert(ui)
2732 if abort or warn:
2741 if abort or warn:
2733 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2742 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2734
2743
2735 badmatch = matchmod.badmatch(match, badfn)
2744 badmatch = matchmod.badmatch(match, badfn)
2736 dirstate = repo.dirstate
2745 dirstate = repo.dirstate
2737 # We don't want to just call wctx.walk here, since it would return a lot of
2746 # We don't want to just call wctx.walk here, since it would return a lot of
2738 # clean files, which we aren't interested in and takes time.
2747 # clean files, which we aren't interested in and takes time.
2739 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2748 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2740 unknown=True, ignored=False, full=False)):
2749 unknown=True, ignored=False, full=False)):
2741 exact = match.exact(f)
2750 exact = match.exact(f)
2742 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2751 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2743 if cca:
2752 if cca:
2744 cca(f)
2753 cca(f)
2745 names.append(f)
2754 names.append(f)
2746 if ui.verbose or not exact:
2755 if ui.verbose or not exact:
2747 ui.status(_('adding %s\n') % match.rel(f))
2756 ui.status(_('adding %s\n') % match.rel(f))
2748
2757
2749 for subpath in sorted(wctx.substate):
2758 for subpath in sorted(wctx.substate):
2750 sub = wctx.sub(subpath)
2759 sub = wctx.sub(subpath)
2751 try:
2760 try:
2752 submatch = matchmod.subdirmatcher(subpath, match)
2761 submatch = matchmod.subdirmatcher(subpath, match)
2753 if opts.get(r'subrepos'):
2762 if opts.get(r'subrepos'):
2754 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2763 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2755 else:
2764 else:
2756 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2765 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2757 except error.LookupError:
2766 except error.LookupError:
2758 ui.status(_("skipping missing subrepository: %s\n")
2767 ui.status(_("skipping missing subrepository: %s\n")
2759 % join(subpath))
2768 % join(subpath))
2760
2769
2761 if not opts.get(r'dry_run'):
2770 if not opts.get(r'dry_run'):
2762 rejected = wctx.add(names, prefix)
2771 rejected = wctx.add(names, prefix)
2763 bad.extend(f for f in rejected if f in match.files())
2772 bad.extend(f for f in rejected if f in match.files())
2764 return bad
2773 return bad
2765
2774
2766 def addwebdirpath(repo, serverpath, webconf):
2775 def addwebdirpath(repo, serverpath, webconf):
2767 webconf[serverpath] = repo.root
2776 webconf[serverpath] = repo.root
2768 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2777 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2769
2778
2770 for r in repo.revs('filelog("path:.hgsub")'):
2779 for r in repo.revs('filelog("path:.hgsub")'):
2771 ctx = repo[r]
2780 ctx = repo[r]
2772 for subpath in ctx.substate:
2781 for subpath in ctx.substate:
2773 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2782 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2774
2783
2775 def forget(ui, repo, match, prefix, explicitonly):
2784 def forget(ui, repo, match, prefix, explicitonly):
2776 join = lambda f: os.path.join(prefix, f)
2785 join = lambda f: os.path.join(prefix, f)
2777 bad = []
2786 bad = []
2778 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2787 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2779 wctx = repo[None]
2788 wctx = repo[None]
2780 forgot = []
2789 forgot = []
2781
2790
2782 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2791 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2783 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2792 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2784 if explicitonly:
2793 if explicitonly:
2785 forget = [f for f in forget if match.exact(f)]
2794 forget = [f for f in forget if match.exact(f)]
2786
2795
2787 for subpath in sorted(wctx.substate):
2796 for subpath in sorted(wctx.substate):
2788 sub = wctx.sub(subpath)
2797 sub = wctx.sub(subpath)
2789 try:
2798 try:
2790 submatch = matchmod.subdirmatcher(subpath, match)
2799 submatch = matchmod.subdirmatcher(subpath, match)
2791 subbad, subforgot = sub.forget(submatch, prefix)
2800 subbad, subforgot = sub.forget(submatch, prefix)
2792 bad.extend([subpath + '/' + f for f in subbad])
2801 bad.extend([subpath + '/' + f for f in subbad])
2793 forgot.extend([subpath + '/' + f for f in subforgot])
2802 forgot.extend([subpath + '/' + f for f in subforgot])
2794 except error.LookupError:
2803 except error.LookupError:
2795 ui.status(_("skipping missing subrepository: %s\n")
2804 ui.status(_("skipping missing subrepository: %s\n")
2796 % join(subpath))
2805 % join(subpath))
2797
2806
2798 if not explicitonly:
2807 if not explicitonly:
2799 for f in match.files():
2808 for f in match.files():
2800 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2809 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2801 if f not in forgot:
2810 if f not in forgot:
2802 if repo.wvfs.exists(f):
2811 if repo.wvfs.exists(f):
2803 # Don't complain if the exact case match wasn't given.
2812 # Don't complain if the exact case match wasn't given.
2804 # But don't do this until after checking 'forgot', so
2813 # But don't do this until after checking 'forgot', so
2805 # that subrepo files aren't normalized, and this op is
2814 # that subrepo files aren't normalized, and this op is
2806 # purely from data cached by the status walk above.
2815 # purely from data cached by the status walk above.
2807 if repo.dirstate.normalize(f) in repo.dirstate:
2816 if repo.dirstate.normalize(f) in repo.dirstate:
2808 continue
2817 continue
2809 ui.warn(_('not removing %s: '
2818 ui.warn(_('not removing %s: '
2810 'file is already untracked\n')
2819 'file is already untracked\n')
2811 % match.rel(f))
2820 % match.rel(f))
2812 bad.append(f)
2821 bad.append(f)
2813
2822
2814 for f in forget:
2823 for f in forget:
2815 if ui.verbose or not match.exact(f):
2824 if ui.verbose or not match.exact(f):
2816 ui.status(_('removing %s\n') % match.rel(f))
2825 ui.status(_('removing %s\n') % match.rel(f))
2817
2826
2818 rejected = wctx.forget(forget, prefix)
2827 rejected = wctx.forget(forget, prefix)
2819 bad.extend(f for f in rejected if f in match.files())
2828 bad.extend(f for f in rejected if f in match.files())
2820 forgot.extend(f for f in forget if f not in rejected)
2829 forgot.extend(f for f in forget if f not in rejected)
2821 return bad, forgot
2830 return bad, forgot
2822
2831
2823 def files(ui, ctx, m, fm, fmt, subrepos):
2832 def files(ui, ctx, m, fm, fmt, subrepos):
2824 rev = ctx.rev()
2833 rev = ctx.rev()
2825 ret = 1
2834 ret = 1
2826 ds = ctx.repo().dirstate
2835 ds = ctx.repo().dirstate
2827
2836
2828 for f in ctx.matches(m):
2837 for f in ctx.matches(m):
2829 if rev is None and ds[f] == 'r':
2838 if rev is None and ds[f] == 'r':
2830 continue
2839 continue
2831 fm.startitem()
2840 fm.startitem()
2832 if ui.verbose:
2841 if ui.verbose:
2833 fc = ctx[f]
2842 fc = ctx[f]
2834 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2843 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2835 fm.data(abspath=f)
2844 fm.data(abspath=f)
2836 fm.write('path', fmt, m.rel(f))
2845 fm.write('path', fmt, m.rel(f))
2837 ret = 0
2846 ret = 0
2838
2847
2839 for subpath in sorted(ctx.substate):
2848 for subpath in sorted(ctx.substate):
2840 submatch = matchmod.subdirmatcher(subpath, m)
2849 submatch = matchmod.subdirmatcher(subpath, m)
2841 if (subrepos or m.exact(subpath) or any(submatch.files())):
2850 if (subrepos or m.exact(subpath) or any(submatch.files())):
2842 sub = ctx.sub(subpath)
2851 sub = ctx.sub(subpath)
2843 try:
2852 try:
2844 recurse = m.exact(subpath) or subrepos
2853 recurse = m.exact(subpath) or subrepos
2845 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2854 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2846 ret = 0
2855 ret = 0
2847 except error.LookupError:
2856 except error.LookupError:
2848 ui.status(_("skipping missing subrepository: %s\n")
2857 ui.status(_("skipping missing subrepository: %s\n")
2849 % m.abs(subpath))
2858 % m.abs(subpath))
2850
2859
2851 return ret
2860 return ret
2852
2861
2853 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2862 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2854 join = lambda f: os.path.join(prefix, f)
2863 join = lambda f: os.path.join(prefix, f)
2855 ret = 0
2864 ret = 0
2856 s = repo.status(match=m, clean=True)
2865 s = repo.status(match=m, clean=True)
2857 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2866 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2858
2867
2859 wctx = repo[None]
2868 wctx = repo[None]
2860
2869
2861 if warnings is None:
2870 if warnings is None:
2862 warnings = []
2871 warnings = []
2863 warn = True
2872 warn = True
2864 else:
2873 else:
2865 warn = False
2874 warn = False
2866
2875
2867 subs = sorted(wctx.substate)
2876 subs = sorted(wctx.substate)
2868 total = len(subs)
2877 total = len(subs)
2869 count = 0
2878 count = 0
2870 for subpath in subs:
2879 for subpath in subs:
2871 count += 1
2880 count += 1
2872 submatch = matchmod.subdirmatcher(subpath, m)
2881 submatch = matchmod.subdirmatcher(subpath, m)
2873 if subrepos or m.exact(subpath) or any(submatch.files()):
2882 if subrepos or m.exact(subpath) or any(submatch.files()):
2874 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2883 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2875 sub = wctx.sub(subpath)
2884 sub = wctx.sub(subpath)
2876 try:
2885 try:
2877 if sub.removefiles(submatch, prefix, after, force, subrepos,
2886 if sub.removefiles(submatch, prefix, after, force, subrepos,
2878 warnings):
2887 warnings):
2879 ret = 1
2888 ret = 1
2880 except error.LookupError:
2889 except error.LookupError:
2881 warnings.append(_("skipping missing subrepository: %s\n")
2890 warnings.append(_("skipping missing subrepository: %s\n")
2882 % join(subpath))
2891 % join(subpath))
2883 ui.progress(_('searching'), None)
2892 ui.progress(_('searching'), None)
2884
2893
2885 # warn about failure to delete explicit files/dirs
2894 # warn about failure to delete explicit files/dirs
2886 deleteddirs = util.dirs(deleted)
2895 deleteddirs = util.dirs(deleted)
2887 files = m.files()
2896 files = m.files()
2888 total = len(files)
2897 total = len(files)
2889 count = 0
2898 count = 0
2890 for f in files:
2899 for f in files:
2891 def insubrepo():
2900 def insubrepo():
2892 for subpath in wctx.substate:
2901 for subpath in wctx.substate:
2893 if f.startswith(subpath + '/'):
2902 if f.startswith(subpath + '/'):
2894 return True
2903 return True
2895 return False
2904 return False
2896
2905
2897 count += 1
2906 count += 1
2898 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2907 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2899 isdir = f in deleteddirs or wctx.hasdir(f)
2908 isdir = f in deleteddirs or wctx.hasdir(f)
2900 if (f in repo.dirstate or isdir or f == '.'
2909 if (f in repo.dirstate or isdir or f == '.'
2901 or insubrepo() or f in subs):
2910 or insubrepo() or f in subs):
2902 continue
2911 continue
2903
2912
2904 if repo.wvfs.exists(f):
2913 if repo.wvfs.exists(f):
2905 if repo.wvfs.isdir(f):
2914 if repo.wvfs.isdir(f):
2906 warnings.append(_('not removing %s: no tracked files\n')
2915 warnings.append(_('not removing %s: no tracked files\n')
2907 % m.rel(f))
2916 % m.rel(f))
2908 else:
2917 else:
2909 warnings.append(_('not removing %s: file is untracked\n')
2918 warnings.append(_('not removing %s: file is untracked\n')
2910 % m.rel(f))
2919 % m.rel(f))
2911 # missing files will generate a warning elsewhere
2920 # missing files will generate a warning elsewhere
2912 ret = 1
2921 ret = 1
2913 ui.progress(_('deleting'), None)
2922 ui.progress(_('deleting'), None)
2914
2923
2915 if force:
2924 if force:
2916 list = modified + deleted + clean + added
2925 list = modified + deleted + clean + added
2917 elif after:
2926 elif after:
2918 list = deleted
2927 list = deleted
2919 remaining = modified + added + clean
2928 remaining = modified + added + clean
2920 total = len(remaining)
2929 total = len(remaining)
2921 count = 0
2930 count = 0
2922 for f in remaining:
2931 for f in remaining:
2923 count += 1
2932 count += 1
2924 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2933 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2925 if ui.verbose or (f in files):
2934 if ui.verbose or (f in files):
2926 warnings.append(_('not removing %s: file still exists\n')
2935 warnings.append(_('not removing %s: file still exists\n')
2927 % m.rel(f))
2936 % m.rel(f))
2928 ret = 1
2937 ret = 1
2929 ui.progress(_('skipping'), None)
2938 ui.progress(_('skipping'), None)
2930 else:
2939 else:
2931 list = deleted + clean
2940 list = deleted + clean
2932 total = len(modified) + len(added)
2941 total = len(modified) + len(added)
2933 count = 0
2942 count = 0
2934 for f in modified:
2943 for f in modified:
2935 count += 1
2944 count += 1
2936 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2945 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2937 warnings.append(_('not removing %s: file is modified (use -f'
2946 warnings.append(_('not removing %s: file is modified (use -f'
2938 ' to force removal)\n') % m.rel(f))
2947 ' to force removal)\n') % m.rel(f))
2939 ret = 1
2948 ret = 1
2940 for f in added:
2949 for f in added:
2941 count += 1
2950 count += 1
2942 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2951 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2943 warnings.append(_("not removing %s: file has been marked for add"
2952 warnings.append(_("not removing %s: file has been marked for add"
2944 " (use 'hg forget' to undo add)\n") % m.rel(f))
2953 " (use 'hg forget' to undo add)\n") % m.rel(f))
2945 ret = 1
2954 ret = 1
2946 ui.progress(_('skipping'), None)
2955 ui.progress(_('skipping'), None)
2947
2956
2948 list = sorted(list)
2957 list = sorted(list)
2949 total = len(list)
2958 total = len(list)
2950 count = 0
2959 count = 0
2951 for f in list:
2960 for f in list:
2952 count += 1
2961 count += 1
2953 if ui.verbose or not m.exact(f):
2962 if ui.verbose or not m.exact(f):
2954 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2963 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2955 ui.status(_('removing %s\n') % m.rel(f))
2964 ui.status(_('removing %s\n') % m.rel(f))
2956 ui.progress(_('deleting'), None)
2965 ui.progress(_('deleting'), None)
2957
2966
2958 with repo.wlock():
2967 with repo.wlock():
2959 if not after:
2968 if not after:
2960 for f in list:
2969 for f in list:
2961 if f in added:
2970 if f in added:
2962 continue # we never unlink added files on remove
2971 continue # we never unlink added files on remove
2963 repo.wvfs.unlinkpath(f, ignoremissing=True)
2972 repo.wvfs.unlinkpath(f, ignoremissing=True)
2964 repo[None].forget(list)
2973 repo[None].forget(list)
2965
2974
2966 if warn:
2975 if warn:
2967 for warning in warnings:
2976 for warning in warnings:
2968 ui.warn(warning)
2977 ui.warn(warning)
2969
2978
2970 return ret
2979 return ret
2971
2980
2972 def _updatecatformatter(fm, ctx, matcher, path, decode):
2981 def _updatecatformatter(fm, ctx, matcher, path, decode):
2973 """Hook for adding data to the formatter used by ``hg cat``.
2982 """Hook for adding data to the formatter used by ``hg cat``.
2974
2983
2975 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2984 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2976 this method first."""
2985 this method first."""
2977 data = ctx[path].data()
2986 data = ctx[path].data()
2978 if decode:
2987 if decode:
2979 data = ctx.repo().wwritedata(path, data)
2988 data = ctx.repo().wwritedata(path, data)
2980 fm.startitem()
2989 fm.startitem()
2981 fm.write('data', '%s', data)
2990 fm.write('data', '%s', data)
2982 fm.data(abspath=path, path=matcher.rel(path))
2991 fm.data(abspath=path, path=matcher.rel(path))
2983
2992
2984 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2993 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2985 err = 1
2994 err = 1
2986 opts = pycompat.byteskwargs(opts)
2995 opts = pycompat.byteskwargs(opts)
2987
2996
2988 def write(path):
2997 def write(path):
2989 filename = None
2998 filename = None
2990 if fntemplate:
2999 if fntemplate:
2991 filename = makefilename(repo, fntemplate, ctx.node(),
3000 filename = makefilename(repo, fntemplate, ctx.node(),
2992 pathname=os.path.join(prefix, path))
3001 pathname=os.path.join(prefix, path))
2993 # attempt to create the directory if it does not already exist
3002 # attempt to create the directory if it does not already exist
2994 try:
3003 try:
2995 os.makedirs(os.path.dirname(filename))
3004 os.makedirs(os.path.dirname(filename))
2996 except OSError:
3005 except OSError:
2997 pass
3006 pass
2998 with formatter.maybereopen(basefm, filename, opts) as fm:
3007 with formatter.maybereopen(basefm, filename, opts) as fm:
2999 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
3008 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
3000
3009
3001 # Automation often uses hg cat on single files, so special case it
3010 # Automation often uses hg cat on single files, so special case it
3002 # for performance to avoid the cost of parsing the manifest.
3011 # for performance to avoid the cost of parsing the manifest.
3003 if len(matcher.files()) == 1 and not matcher.anypats():
3012 if len(matcher.files()) == 1 and not matcher.anypats():
3004 file = matcher.files()[0]
3013 file = matcher.files()[0]
3005 mfl = repo.manifestlog
3014 mfl = repo.manifestlog
3006 mfnode = ctx.manifestnode()
3015 mfnode = ctx.manifestnode()
3007 try:
3016 try:
3008 if mfnode and mfl[mfnode].find(file)[0]:
3017 if mfnode and mfl[mfnode].find(file)[0]:
3009 write(file)
3018 write(file)
3010 return 0
3019 return 0
3011 except KeyError:
3020 except KeyError:
3012 pass
3021 pass
3013
3022
3014 for abs in ctx.walk(matcher):
3023 for abs in ctx.walk(matcher):
3015 write(abs)
3024 write(abs)
3016 err = 0
3025 err = 0
3017
3026
3018 for subpath in sorted(ctx.substate):
3027 for subpath in sorted(ctx.substate):
3019 sub = ctx.sub(subpath)
3028 sub = ctx.sub(subpath)
3020 try:
3029 try:
3021 submatch = matchmod.subdirmatcher(subpath, matcher)
3030 submatch = matchmod.subdirmatcher(subpath, matcher)
3022
3031
3023 if not sub.cat(submatch, basefm, fntemplate,
3032 if not sub.cat(submatch, basefm, fntemplate,
3024 os.path.join(prefix, sub._path),
3033 os.path.join(prefix, sub._path),
3025 **pycompat.strkwargs(opts)):
3034 **pycompat.strkwargs(opts)):
3026 err = 0
3035 err = 0
3027 except error.RepoLookupError:
3036 except error.RepoLookupError:
3028 ui.status(_("skipping missing subrepository: %s\n")
3037 ui.status(_("skipping missing subrepository: %s\n")
3029 % os.path.join(prefix, subpath))
3038 % os.path.join(prefix, subpath))
3030
3039
3031 return err
3040 return err
3032
3041
3033 def commit(ui, repo, commitfunc, pats, opts):
3042 def commit(ui, repo, commitfunc, pats, opts):
3034 '''commit the specified files or all outstanding changes'''
3043 '''commit the specified files or all outstanding changes'''
3035 date = opts.get('date')
3044 date = opts.get('date')
3036 if date:
3045 if date:
3037 opts['date'] = util.parsedate(date)
3046 opts['date'] = util.parsedate(date)
3038 message = logmessage(ui, opts)
3047 message = logmessage(ui, opts)
3039 matcher = scmutil.match(repo[None], pats, opts)
3048 matcher = scmutil.match(repo[None], pats, opts)
3040
3049
3041 dsguard = None
3050 dsguard = None
3042 # extract addremove carefully -- this function can be called from a command
3051 # extract addremove carefully -- this function can be called from a command
3043 # that doesn't support addremove
3052 # that doesn't support addremove
3044 if opts.get('addremove'):
3053 if opts.get('addremove'):
3045 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3054 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3046 with dsguard or util.nullcontextmanager():
3055 with dsguard or util.nullcontextmanager():
3047 if dsguard:
3056 if dsguard:
3048 if scmutil.addremove(repo, matcher, "", opts) != 0:
3057 if scmutil.addremove(repo, matcher, "", opts) != 0:
3049 raise error.Abort(
3058 raise error.Abort(
3050 _("failed to mark all new/missing files as added/removed"))
3059 _("failed to mark all new/missing files as added/removed"))
3051
3060
3052 return commitfunc(ui, repo, message, matcher, opts)
3061 return commitfunc(ui, repo, message, matcher, opts)
3053
3062
3054 def samefile(f, ctx1, ctx2):
3063 def samefile(f, ctx1, ctx2):
3055 if f in ctx1.manifest():
3064 if f in ctx1.manifest():
3056 a = ctx1.filectx(f)
3065 a = ctx1.filectx(f)
3057 if f in ctx2.manifest():
3066 if f in ctx2.manifest():
3058 b = ctx2.filectx(f)
3067 b = ctx2.filectx(f)
3059 return (not a.cmp(b)
3068 return (not a.cmp(b)
3060 and a.flags() == b.flags())
3069 and a.flags() == b.flags())
3061 else:
3070 else:
3062 return False
3071 return False
3063 else:
3072 else:
3064 return f not in ctx2.manifest()
3073 return f not in ctx2.manifest()
3065
3074
3066 def amend(ui, repo, old, extra, pats, opts):
3075 def amend(ui, repo, old, extra, pats, opts):
3067 # avoid cycle context -> subrepo -> cmdutil
3076 # avoid cycle context -> subrepo -> cmdutil
3068 from . import context
3077 from . import context
3069
3078
3070 # amend will reuse the existing user if not specified, but the obsolete
3079 # amend will reuse the existing user if not specified, but the obsolete
3071 # marker creation requires that the current user's name is specified.
3080 # marker creation requires that the current user's name is specified.
3072 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3081 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3073 ui.username() # raise exception if username not set
3082 ui.username() # raise exception if username not set
3074
3083
3075 ui.note(_('amending changeset %s\n') % old)
3084 ui.note(_('amending changeset %s\n') % old)
3076 base = old.p1()
3085 base = old.p1()
3077
3086
3078 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3087 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3079 # Participating changesets:
3088 # Participating changesets:
3080 #
3089 #
3081 # wctx o - workingctx that contains changes from working copy
3090 # wctx o - workingctx that contains changes from working copy
3082 # | to go into amending commit
3091 # | to go into amending commit
3083 # |
3092 # |
3084 # old o - changeset to amend
3093 # old o - changeset to amend
3085 # |
3094 # |
3086 # base o - first parent of the changeset to amend
3095 # base o - first parent of the changeset to amend
3087 wctx = repo[None]
3096 wctx = repo[None]
3088
3097
3089 # Copy to avoid mutating input
3098 # Copy to avoid mutating input
3090 extra = extra.copy()
3099 extra = extra.copy()
3091 # Update extra dict from amended commit (e.g. to preserve graft
3100 # Update extra dict from amended commit (e.g. to preserve graft
3092 # source)
3101 # source)
3093 extra.update(old.extra())
3102 extra.update(old.extra())
3094
3103
3095 # Also update it from the from the wctx
3104 # Also update it from the from the wctx
3096 extra.update(wctx.extra())
3105 extra.update(wctx.extra())
3097
3106
3098 user = opts.get('user') or old.user()
3107 user = opts.get('user') or old.user()
3099 date = opts.get('date') or old.date()
3108 date = opts.get('date') or old.date()
3100
3109
3101 # Parse the date to allow comparison between date and old.date()
3110 # Parse the date to allow comparison between date and old.date()
3102 date = util.parsedate(date)
3111 date = util.parsedate(date)
3103
3112
3104 if len(old.parents()) > 1:
3113 if len(old.parents()) > 1:
3105 # ctx.files() isn't reliable for merges, so fall back to the
3114 # ctx.files() isn't reliable for merges, so fall back to the
3106 # slower repo.status() method
3115 # slower repo.status() method
3107 files = set([fn for st in repo.status(base, old)[:3]
3116 files = set([fn for st in repo.status(base, old)[:3]
3108 for fn in st])
3117 for fn in st])
3109 else:
3118 else:
3110 files = set(old.files())
3119 files = set(old.files())
3111
3120
3112 # add/remove the files to the working copy if the "addremove" option
3121 # add/remove the files to the working copy if the "addremove" option
3113 # was specified.
3122 # was specified.
3114 matcher = scmutil.match(wctx, pats, opts)
3123 matcher = scmutil.match(wctx, pats, opts)
3115 if (opts.get('addremove')
3124 if (opts.get('addremove')
3116 and scmutil.addremove(repo, matcher, "", opts)):
3125 and scmutil.addremove(repo, matcher, "", opts)):
3117 raise error.Abort(
3126 raise error.Abort(
3118 _("failed to mark all new/missing files as added/removed"))
3127 _("failed to mark all new/missing files as added/removed"))
3119
3128
3120 # Check subrepos. This depends on in-place wctx._status update in
3129 # Check subrepos. This depends on in-place wctx._status update in
3121 # subrepo.precommit(). To minimize the risk of this hack, we do
3130 # subrepo.precommit(). To minimize the risk of this hack, we do
3122 # nothing if .hgsub does not exist.
3131 # nothing if .hgsub does not exist.
3123 if '.hgsub' in wctx or '.hgsub' in old:
3132 if '.hgsub' in wctx or '.hgsub' in old:
3124 from . import subrepo # avoid cycle: cmdutil -> subrepo -> cmdutil
3133 from . import subrepo # avoid cycle: cmdutil -> subrepo -> cmdutil
3125 subs, commitsubs, newsubstate = subrepo.precommit(
3134 subs, commitsubs, newsubstate = subrepo.precommit(
3126 ui, wctx, wctx._status, matcher)
3135 ui, wctx, wctx._status, matcher)
3127 # amend should abort if commitsubrepos is enabled
3136 # amend should abort if commitsubrepos is enabled
3128 assert not commitsubs
3137 assert not commitsubs
3129 if subs:
3138 if subs:
3130 subrepo.writestate(repo, newsubstate)
3139 subrepo.writestate(repo, newsubstate)
3131
3140
3132 filestoamend = set(f for f in wctx.files() if matcher(f))
3141 filestoamend = set(f for f in wctx.files() if matcher(f))
3133
3142
3134 changes = (len(filestoamend) > 0)
3143 changes = (len(filestoamend) > 0)
3135 if changes:
3144 if changes:
3136 # Recompute copies (avoid recording a -> b -> a)
3145 # Recompute copies (avoid recording a -> b -> a)
3137 copied = copies.pathcopies(base, wctx, matcher)
3146 copied = copies.pathcopies(base, wctx, matcher)
3138 if old.p2:
3147 if old.p2:
3139 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3148 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3140
3149
3141 # Prune files which were reverted by the updates: if old
3150 # Prune files which were reverted by the updates: if old
3142 # introduced file X and the file was renamed in the working
3151 # introduced file X and the file was renamed in the working
3143 # copy, then those two files are the same and
3152 # copy, then those two files are the same and
3144 # we can discard X from our list of files. Likewise if X
3153 # we can discard X from our list of files. Likewise if X
3145 # was removed, it's no longer relevant. If X is missing (aka
3154 # was removed, it's no longer relevant. If X is missing (aka
3146 # deleted), old X must be preserved.
3155 # deleted), old X must be preserved.
3147 files.update(filestoamend)
3156 files.update(filestoamend)
3148 files = [f for f in files if (not samefile(f, wctx, base)
3157 files = [f for f in files if (not samefile(f, wctx, base)
3149 or f in wctx.deleted())]
3158 or f in wctx.deleted())]
3150
3159
3151 def filectxfn(repo, ctx_, path):
3160 def filectxfn(repo, ctx_, path):
3152 try:
3161 try:
3153 # If the file being considered is not amongst the files
3162 # If the file being considered is not amongst the files
3154 # to be amended, we should return the file context from the
3163 # to be amended, we should return the file context from the
3155 # old changeset. This avoids issues when only some files in
3164 # old changeset. This avoids issues when only some files in
3156 # the working copy are being amended but there are also
3165 # the working copy are being amended but there are also
3157 # changes to other files from the old changeset.
3166 # changes to other files from the old changeset.
3158 if path not in filestoamend:
3167 if path not in filestoamend:
3159 return old.filectx(path)
3168 return old.filectx(path)
3160
3169
3161 # Return None for removed files.
3170 # Return None for removed files.
3162 if path in wctx.removed():
3171 if path in wctx.removed():
3163 return None
3172 return None
3164
3173
3165 fctx = wctx[path]
3174 fctx = wctx[path]
3166 flags = fctx.flags()
3175 flags = fctx.flags()
3167 mctx = context.memfilectx(repo, ctx_,
3176 mctx = context.memfilectx(repo, ctx_,
3168 fctx.path(), fctx.data(),
3177 fctx.path(), fctx.data(),
3169 islink='l' in flags,
3178 islink='l' in flags,
3170 isexec='x' in flags,
3179 isexec='x' in flags,
3171 copied=copied.get(path))
3180 copied=copied.get(path))
3172 return mctx
3181 return mctx
3173 except KeyError:
3182 except KeyError:
3174 return None
3183 return None
3175 else:
3184 else:
3176 ui.note(_('copying changeset %s to %s\n') % (old, base))
3185 ui.note(_('copying changeset %s to %s\n') % (old, base))
3177
3186
3178 # Use version of files as in the old cset
3187 # Use version of files as in the old cset
3179 def filectxfn(repo, ctx_, path):
3188 def filectxfn(repo, ctx_, path):
3180 try:
3189 try:
3181 return old.filectx(path)
3190 return old.filectx(path)
3182 except KeyError:
3191 except KeyError:
3183 return None
3192 return None
3184
3193
3185 # See if we got a message from -m or -l, if not, open the editor with
3194 # See if we got a message from -m or -l, if not, open the editor with
3186 # the message of the changeset to amend.
3195 # the message of the changeset to amend.
3187 message = logmessage(ui, opts)
3196 message = logmessage(ui, opts)
3188
3197
3189 editform = mergeeditform(old, 'commit.amend')
3198 editform = mergeeditform(old, 'commit.amend')
3190 editor = getcommiteditor(editform=editform,
3199 editor = getcommiteditor(editform=editform,
3191 **pycompat.strkwargs(opts))
3200 **pycompat.strkwargs(opts))
3192
3201
3193 if not message:
3202 if not message:
3194 editor = getcommiteditor(edit=True, editform=editform)
3203 editor = getcommiteditor(edit=True, editform=editform)
3195 message = old.description()
3204 message = old.description()
3196
3205
3197 pureextra = extra.copy()
3206 pureextra = extra.copy()
3198 extra['amend_source'] = old.hex()
3207 extra['amend_source'] = old.hex()
3199
3208
3200 new = context.memctx(repo,
3209 new = context.memctx(repo,
3201 parents=[base.node(), old.p2().node()],
3210 parents=[base.node(), old.p2().node()],
3202 text=message,
3211 text=message,
3203 files=files,
3212 files=files,
3204 filectxfn=filectxfn,
3213 filectxfn=filectxfn,
3205 user=user,
3214 user=user,
3206 date=date,
3215 date=date,
3207 extra=extra,
3216 extra=extra,
3208 editor=editor)
3217 editor=editor)
3209
3218
3210 newdesc = changelog.stripdesc(new.description())
3219 newdesc = changelog.stripdesc(new.description())
3211 if ((not changes)
3220 if ((not changes)
3212 and newdesc == old.description()
3221 and newdesc == old.description()
3213 and user == old.user()
3222 and user == old.user()
3214 and date == old.date()
3223 and date == old.date()
3215 and pureextra == old.extra()):
3224 and pureextra == old.extra()):
3216 # nothing changed. continuing here would create a new node
3225 # nothing changed. continuing here would create a new node
3217 # anyway because of the amend_source noise.
3226 # anyway because of the amend_source noise.
3218 #
3227 #
3219 # This not what we expect from amend.
3228 # This not what we expect from amend.
3220 return old.node()
3229 return old.node()
3221
3230
3222 if opts.get('secret'):
3231 if opts.get('secret'):
3223 commitphase = 'secret'
3232 commitphase = 'secret'
3224 else:
3233 else:
3225 commitphase = old.phase()
3234 commitphase = old.phase()
3226 overrides = {('phases', 'new-commit'): commitphase}
3235 overrides = {('phases', 'new-commit'): commitphase}
3227 with ui.configoverride(overrides, 'amend'):
3236 with ui.configoverride(overrides, 'amend'):
3228 newid = repo.commitctx(new)
3237 newid = repo.commitctx(new)
3229
3238
3230 # Reroute the working copy parent to the new changeset
3239 # Reroute the working copy parent to the new changeset
3231 repo.setparents(newid, nullid)
3240 repo.setparents(newid, nullid)
3232 mapping = {old.node(): (newid,)}
3241 mapping = {old.node(): (newid,)}
3233 obsmetadata = None
3242 obsmetadata = None
3234 if opts.get('note'):
3243 if opts.get('note'):
3235 obsmetadata = {'note': opts['note']}
3244 obsmetadata = {'note': opts['note']}
3236 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata)
3245 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata)
3237
3246
3238 # Fixing the dirstate because localrepo.commitctx does not update
3247 # Fixing the dirstate because localrepo.commitctx does not update
3239 # it. This is rather convenient because we did not need to update
3248 # it. This is rather convenient because we did not need to update
3240 # the dirstate for all the files in the new commit which commitctx
3249 # the dirstate for all the files in the new commit which commitctx
3241 # could have done if it updated the dirstate. Now, we can
3250 # could have done if it updated the dirstate. Now, we can
3242 # selectively update the dirstate only for the amended files.
3251 # selectively update the dirstate only for the amended files.
3243 dirstate = repo.dirstate
3252 dirstate = repo.dirstate
3244
3253
3245 # Update the state of the files which were added and
3254 # Update the state of the files which were added and
3246 # and modified in the amend to "normal" in the dirstate.
3255 # and modified in the amend to "normal" in the dirstate.
3247 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3256 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3248 for f in normalfiles:
3257 for f in normalfiles:
3249 dirstate.normal(f)
3258 dirstate.normal(f)
3250
3259
3251 # Update the state of files which were removed in the amend
3260 # Update the state of files which were removed in the amend
3252 # to "removed" in the dirstate.
3261 # to "removed" in the dirstate.
3253 removedfiles = set(wctx.removed()) & filestoamend
3262 removedfiles = set(wctx.removed()) & filestoamend
3254 for f in removedfiles:
3263 for f in removedfiles:
3255 dirstate.drop(f)
3264 dirstate.drop(f)
3256
3265
3257 return newid
3266 return newid
3258
3267
3259 def commiteditor(repo, ctx, subs, editform=''):
3268 def commiteditor(repo, ctx, subs, editform=''):
3260 if ctx.description():
3269 if ctx.description():
3261 return ctx.description()
3270 return ctx.description()
3262 return commitforceeditor(repo, ctx, subs, editform=editform,
3271 return commitforceeditor(repo, ctx, subs, editform=editform,
3263 unchangedmessagedetection=True)
3272 unchangedmessagedetection=True)
3264
3273
3265 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3274 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3266 editform='', unchangedmessagedetection=False):
3275 editform='', unchangedmessagedetection=False):
3267 if not extramsg:
3276 if not extramsg:
3268 extramsg = _("Leave message empty to abort commit.")
3277 extramsg = _("Leave message empty to abort commit.")
3269
3278
3270 forms = [e for e in editform.split('.') if e]
3279 forms = [e for e in editform.split('.') if e]
3271 forms.insert(0, 'changeset')
3280 forms.insert(0, 'changeset')
3272 templatetext = None
3281 templatetext = None
3273 while forms:
3282 while forms:
3274 ref = '.'.join(forms)
3283 ref = '.'.join(forms)
3275 if repo.ui.config('committemplate', ref):
3284 if repo.ui.config('committemplate', ref):
3276 templatetext = committext = buildcommittemplate(
3285 templatetext = committext = buildcommittemplate(
3277 repo, ctx, subs, extramsg, ref)
3286 repo, ctx, subs, extramsg, ref)
3278 break
3287 break
3279 forms.pop()
3288 forms.pop()
3280 else:
3289 else:
3281 committext = buildcommittext(repo, ctx, subs, extramsg)
3290 committext = buildcommittext(repo, ctx, subs, extramsg)
3282
3291
3283 # run editor in the repository root
3292 # run editor in the repository root
3284 olddir = pycompat.getcwd()
3293 olddir = pycompat.getcwd()
3285 os.chdir(repo.root)
3294 os.chdir(repo.root)
3286
3295
3287 # make in-memory changes visible to external process
3296 # make in-memory changes visible to external process
3288 tr = repo.currenttransaction()
3297 tr = repo.currenttransaction()
3289 repo.dirstate.write(tr)
3298 repo.dirstate.write(tr)
3290 pending = tr and tr.writepending() and repo.root
3299 pending = tr and tr.writepending() and repo.root
3291
3300
3292 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3301 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3293 editform=editform, pending=pending,
3302 editform=editform, pending=pending,
3294 repopath=repo.path, action='commit')
3303 repopath=repo.path, action='commit')
3295 text = editortext
3304 text = editortext
3296
3305
3297 # strip away anything below this special string (used for editors that want
3306 # strip away anything below this special string (used for editors that want
3298 # to display the diff)
3307 # to display the diff)
3299 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3308 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3300 if stripbelow:
3309 if stripbelow:
3301 text = text[:stripbelow.start()]
3310 text = text[:stripbelow.start()]
3302
3311
3303 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3312 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3304 os.chdir(olddir)
3313 os.chdir(olddir)
3305
3314
3306 if finishdesc:
3315 if finishdesc:
3307 text = finishdesc(text)
3316 text = finishdesc(text)
3308 if not text.strip():
3317 if not text.strip():
3309 raise error.Abort(_("empty commit message"))
3318 raise error.Abort(_("empty commit message"))
3310 if unchangedmessagedetection and editortext == templatetext:
3319 if unchangedmessagedetection and editortext == templatetext:
3311 raise error.Abort(_("commit message unchanged"))
3320 raise error.Abort(_("commit message unchanged"))
3312
3321
3313 return text
3322 return text
3314
3323
3315 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3324 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3316 ui = repo.ui
3325 ui = repo.ui
3317 spec = formatter.templatespec(ref, None, None)
3326 spec = formatter.templatespec(ref, None, None)
3318 t = changeset_templater(ui, repo, spec, None, {}, False)
3327 t = changeset_templater(ui, repo, spec, None, {}, False)
3319 t.t.cache.update((k, templater.unquotestring(v))
3328 t.t.cache.update((k, templater.unquotestring(v))
3320 for k, v in repo.ui.configitems('committemplate'))
3329 for k, v in repo.ui.configitems('committemplate'))
3321
3330
3322 if not extramsg:
3331 if not extramsg:
3323 extramsg = '' # ensure that extramsg is string
3332 extramsg = '' # ensure that extramsg is string
3324
3333
3325 ui.pushbuffer()
3334 ui.pushbuffer()
3326 t.show(ctx, extramsg=extramsg)
3335 t.show(ctx, extramsg=extramsg)
3327 return ui.popbuffer()
3336 return ui.popbuffer()
3328
3337
3329 def hgprefix(msg):
3338 def hgprefix(msg):
3330 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3339 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3331
3340
3332 def buildcommittext(repo, ctx, subs, extramsg):
3341 def buildcommittext(repo, ctx, subs, extramsg):
3333 edittext = []
3342 edittext = []
3334 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3343 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3335 if ctx.description():
3344 if ctx.description():
3336 edittext.append(ctx.description())
3345 edittext.append(ctx.description())
3337 edittext.append("")
3346 edittext.append("")
3338 edittext.append("") # Empty line between message and comments.
3347 edittext.append("") # Empty line between message and comments.
3339 edittext.append(hgprefix(_("Enter commit message."
3348 edittext.append(hgprefix(_("Enter commit message."
3340 " Lines beginning with 'HG:' are removed.")))
3349 " Lines beginning with 'HG:' are removed.")))
3341 edittext.append(hgprefix(extramsg))
3350 edittext.append(hgprefix(extramsg))
3342 edittext.append("HG: --")
3351 edittext.append("HG: --")
3343 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3352 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3344 if ctx.p2():
3353 if ctx.p2():
3345 edittext.append(hgprefix(_("branch merge")))
3354 edittext.append(hgprefix(_("branch merge")))
3346 if ctx.branch():
3355 if ctx.branch():
3347 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3356 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3348 if bookmarks.isactivewdirparent(repo):
3357 if bookmarks.isactivewdirparent(repo):
3349 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3358 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3350 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3359 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3351 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3360 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3352 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3361 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3353 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3362 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3354 if not added and not modified and not removed:
3363 if not added and not modified and not removed:
3355 edittext.append(hgprefix(_("no files changed")))
3364 edittext.append(hgprefix(_("no files changed")))
3356 edittext.append("")
3365 edittext.append("")
3357
3366
3358 return "\n".join(edittext)
3367 return "\n".join(edittext)
3359
3368
3360 def commitstatus(repo, node, branch, bheads=None, opts=None):
3369 def commitstatus(repo, node, branch, bheads=None, opts=None):
3361 if opts is None:
3370 if opts is None:
3362 opts = {}
3371 opts = {}
3363 ctx = repo[node]
3372 ctx = repo[node]
3364 parents = ctx.parents()
3373 parents = ctx.parents()
3365
3374
3366 if (not opts.get('amend') and bheads and node not in bheads and not
3375 if (not opts.get('amend') and bheads and node not in bheads and not
3367 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3376 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3368 repo.ui.status(_('created new head\n'))
3377 repo.ui.status(_('created new head\n'))
3369 # The message is not printed for initial roots. For the other
3378 # The message is not printed for initial roots. For the other
3370 # changesets, it is printed in the following situations:
3379 # changesets, it is printed in the following situations:
3371 #
3380 #
3372 # Par column: for the 2 parents with ...
3381 # Par column: for the 2 parents with ...
3373 # N: null or no parent
3382 # N: null or no parent
3374 # B: parent is on another named branch
3383 # B: parent is on another named branch
3375 # C: parent is a regular non head changeset
3384 # C: parent is a regular non head changeset
3376 # H: parent was a branch head of the current branch
3385 # H: parent was a branch head of the current branch
3377 # Msg column: whether we print "created new head" message
3386 # Msg column: whether we print "created new head" message
3378 # In the following, it is assumed that there already exists some
3387 # In the following, it is assumed that there already exists some
3379 # initial branch heads of the current branch, otherwise nothing is
3388 # initial branch heads of the current branch, otherwise nothing is
3380 # printed anyway.
3389 # printed anyway.
3381 #
3390 #
3382 # Par Msg Comment
3391 # Par Msg Comment
3383 # N N y additional topo root
3392 # N N y additional topo root
3384 #
3393 #
3385 # B N y additional branch root
3394 # B N y additional branch root
3386 # C N y additional topo head
3395 # C N y additional topo head
3387 # H N n usual case
3396 # H N n usual case
3388 #
3397 #
3389 # B B y weird additional branch root
3398 # B B y weird additional branch root
3390 # C B y branch merge
3399 # C B y branch merge
3391 # H B n merge with named branch
3400 # H B n merge with named branch
3392 #
3401 #
3393 # C C y additional head from merge
3402 # C C y additional head from merge
3394 # C H n merge with a head
3403 # C H n merge with a head
3395 #
3404 #
3396 # H H n head merge: head count decreases
3405 # H H n head merge: head count decreases
3397
3406
3398 if not opts.get('close_branch'):
3407 if not opts.get('close_branch'):
3399 for r in parents:
3408 for r in parents:
3400 if r.closesbranch() and r.branch() == branch:
3409 if r.closesbranch() and r.branch() == branch:
3401 repo.ui.status(_('reopening closed branch head %d\n') % r)
3410 repo.ui.status(_('reopening closed branch head %d\n') % r)
3402
3411
3403 if repo.ui.debugflag:
3412 if repo.ui.debugflag:
3404 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3413 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3405 elif repo.ui.verbose:
3414 elif repo.ui.verbose:
3406 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3415 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3407
3416
3408 def postcommitstatus(repo, pats, opts):
3417 def postcommitstatus(repo, pats, opts):
3409 return repo.status(match=scmutil.match(repo[None], pats, opts))
3418 return repo.status(match=scmutil.match(repo[None], pats, opts))
3410
3419
3411 def revert(ui, repo, ctx, parents, *pats, **opts):
3420 def revert(ui, repo, ctx, parents, *pats, **opts):
3412 opts = pycompat.byteskwargs(opts)
3421 opts = pycompat.byteskwargs(opts)
3413 parent, p2 = parents
3422 parent, p2 = parents
3414 node = ctx.node()
3423 node = ctx.node()
3415
3424
3416 mf = ctx.manifest()
3425 mf = ctx.manifest()
3417 if node == p2:
3426 if node == p2:
3418 parent = p2
3427 parent = p2
3419
3428
3420 # need all matching names in dirstate and manifest of target rev,
3429 # need all matching names in dirstate and manifest of target rev,
3421 # so have to walk both. do not print errors if files exist in one
3430 # so have to walk both. do not print errors if files exist in one
3422 # but not other. in both cases, filesets should be evaluated against
3431 # but not other. in both cases, filesets should be evaluated against
3423 # workingctx to get consistent result (issue4497). this means 'set:**'
3432 # workingctx to get consistent result (issue4497). this means 'set:**'
3424 # cannot be used to select missing files from target rev.
3433 # cannot be used to select missing files from target rev.
3425
3434
3426 # `names` is a mapping for all elements in working copy and target revision
3435 # `names` is a mapping for all elements in working copy and target revision
3427 # The mapping is in the form:
3436 # The mapping is in the form:
3428 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3437 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3429 names = {}
3438 names = {}
3430
3439
3431 with repo.wlock():
3440 with repo.wlock():
3432 ## filling of the `names` mapping
3441 ## filling of the `names` mapping
3433 # walk dirstate to fill `names`
3442 # walk dirstate to fill `names`
3434
3443
3435 interactive = opts.get('interactive', False)
3444 interactive = opts.get('interactive', False)
3436 wctx = repo[None]
3445 wctx = repo[None]
3437 m = scmutil.match(wctx, pats, opts)
3446 m = scmutil.match(wctx, pats, opts)
3438
3447
3439 # we'll need this later
3448 # we'll need this later
3440 targetsubs = sorted(s for s in wctx.substate if m(s))
3449 targetsubs = sorted(s for s in wctx.substate if m(s))
3441
3450
3442 if not m.always():
3451 if not m.always():
3443 matcher = matchmod.badmatch(m, lambda x, y: False)
3452 matcher = matchmod.badmatch(m, lambda x, y: False)
3444 for abs in wctx.walk(matcher):
3453 for abs in wctx.walk(matcher):
3445 names[abs] = m.rel(abs), m.exact(abs)
3454 names[abs] = m.rel(abs), m.exact(abs)
3446
3455
3447 # walk target manifest to fill `names`
3456 # walk target manifest to fill `names`
3448
3457
3449 def badfn(path, msg):
3458 def badfn(path, msg):
3450 if path in names:
3459 if path in names:
3451 return
3460 return
3452 if path in ctx.substate:
3461 if path in ctx.substate:
3453 return
3462 return
3454 path_ = path + '/'
3463 path_ = path + '/'
3455 for f in names:
3464 for f in names:
3456 if f.startswith(path_):
3465 if f.startswith(path_):
3457 return
3466 return
3458 ui.warn("%s: %s\n" % (m.rel(path), msg))
3467 ui.warn("%s: %s\n" % (m.rel(path), msg))
3459
3468
3460 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3469 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3461 if abs not in names:
3470 if abs not in names:
3462 names[abs] = m.rel(abs), m.exact(abs)
3471 names[abs] = m.rel(abs), m.exact(abs)
3463
3472
3464 # Find status of all file in `names`.
3473 # Find status of all file in `names`.
3465 m = scmutil.matchfiles(repo, names)
3474 m = scmutil.matchfiles(repo, names)
3466
3475
3467 changes = repo.status(node1=node, match=m,
3476 changes = repo.status(node1=node, match=m,
3468 unknown=True, ignored=True, clean=True)
3477 unknown=True, ignored=True, clean=True)
3469 else:
3478 else:
3470 changes = repo.status(node1=node, match=m)
3479 changes = repo.status(node1=node, match=m)
3471 for kind in changes:
3480 for kind in changes:
3472 for abs in kind:
3481 for abs in kind:
3473 names[abs] = m.rel(abs), m.exact(abs)
3482 names[abs] = m.rel(abs), m.exact(abs)
3474
3483
3475 m = scmutil.matchfiles(repo, names)
3484 m = scmutil.matchfiles(repo, names)
3476
3485
3477 modified = set(changes.modified)
3486 modified = set(changes.modified)
3478 added = set(changes.added)
3487 added = set(changes.added)
3479 removed = set(changes.removed)
3488 removed = set(changes.removed)
3480 _deleted = set(changes.deleted)
3489 _deleted = set(changes.deleted)
3481 unknown = set(changes.unknown)
3490 unknown = set(changes.unknown)
3482 unknown.update(changes.ignored)
3491 unknown.update(changes.ignored)
3483 clean = set(changes.clean)
3492 clean = set(changes.clean)
3484 modadded = set()
3493 modadded = set()
3485
3494
3486 # We need to account for the state of the file in the dirstate,
3495 # We need to account for the state of the file in the dirstate,
3487 # even when we revert against something else than parent. This will
3496 # even when we revert against something else than parent. This will
3488 # slightly alter the behavior of revert (doing back up or not, delete
3497 # slightly alter the behavior of revert (doing back up or not, delete
3489 # or just forget etc).
3498 # or just forget etc).
3490 if parent == node:
3499 if parent == node:
3491 dsmodified = modified
3500 dsmodified = modified
3492 dsadded = added
3501 dsadded = added
3493 dsremoved = removed
3502 dsremoved = removed
3494 # store all local modifications, useful later for rename detection
3503 # store all local modifications, useful later for rename detection
3495 localchanges = dsmodified | dsadded
3504 localchanges = dsmodified | dsadded
3496 modified, added, removed = set(), set(), set()
3505 modified, added, removed = set(), set(), set()
3497 else:
3506 else:
3498 changes = repo.status(node1=parent, match=m)
3507 changes = repo.status(node1=parent, match=m)
3499 dsmodified = set(changes.modified)
3508 dsmodified = set(changes.modified)
3500 dsadded = set(changes.added)
3509 dsadded = set(changes.added)
3501 dsremoved = set(changes.removed)
3510 dsremoved = set(changes.removed)
3502 # store all local modifications, useful later for rename detection
3511 # store all local modifications, useful later for rename detection
3503 localchanges = dsmodified | dsadded
3512 localchanges = dsmodified | dsadded
3504
3513
3505 # only take into account for removes between wc and target
3514 # only take into account for removes between wc and target
3506 clean |= dsremoved - removed
3515 clean |= dsremoved - removed
3507 dsremoved &= removed
3516 dsremoved &= removed
3508 # distinct between dirstate remove and other
3517 # distinct between dirstate remove and other
3509 removed -= dsremoved
3518 removed -= dsremoved
3510
3519
3511 modadded = added & dsmodified
3520 modadded = added & dsmodified
3512 added -= modadded
3521 added -= modadded
3513
3522
3514 # tell newly modified apart.
3523 # tell newly modified apart.
3515 dsmodified &= modified
3524 dsmodified &= modified
3516 dsmodified |= modified & dsadded # dirstate added may need backup
3525 dsmodified |= modified & dsadded # dirstate added may need backup
3517 modified -= dsmodified
3526 modified -= dsmodified
3518
3527
3519 # We need to wait for some post-processing to update this set
3528 # We need to wait for some post-processing to update this set
3520 # before making the distinction. The dirstate will be used for
3529 # before making the distinction. The dirstate will be used for
3521 # that purpose.
3530 # that purpose.
3522 dsadded = added
3531 dsadded = added
3523
3532
3524 # in case of merge, files that are actually added can be reported as
3533 # in case of merge, files that are actually added can be reported as
3525 # modified, we need to post process the result
3534 # modified, we need to post process the result
3526 if p2 != nullid:
3535 if p2 != nullid:
3527 mergeadd = set(dsmodified)
3536 mergeadd = set(dsmodified)
3528 for path in dsmodified:
3537 for path in dsmodified:
3529 if path in mf:
3538 if path in mf:
3530 mergeadd.remove(path)
3539 mergeadd.remove(path)
3531 dsadded |= mergeadd
3540 dsadded |= mergeadd
3532 dsmodified -= mergeadd
3541 dsmodified -= mergeadd
3533
3542
3534 # if f is a rename, update `names` to also revert the source
3543 # if f is a rename, update `names` to also revert the source
3535 cwd = repo.getcwd()
3544 cwd = repo.getcwd()
3536 for f in localchanges:
3545 for f in localchanges:
3537 src = repo.dirstate.copied(f)
3546 src = repo.dirstate.copied(f)
3538 # XXX should we check for rename down to target node?
3547 # XXX should we check for rename down to target node?
3539 if src and src not in names and repo.dirstate[src] == 'r':
3548 if src and src not in names and repo.dirstate[src] == 'r':
3540 dsremoved.add(src)
3549 dsremoved.add(src)
3541 names[src] = (repo.pathto(src, cwd), True)
3550 names[src] = (repo.pathto(src, cwd), True)
3542
3551
3543 # determine the exact nature of the deleted changesets
3552 # determine the exact nature of the deleted changesets
3544 deladded = set(_deleted)
3553 deladded = set(_deleted)
3545 for path in _deleted:
3554 for path in _deleted:
3546 if path in mf:
3555 if path in mf:
3547 deladded.remove(path)
3556 deladded.remove(path)
3548 deleted = _deleted - deladded
3557 deleted = _deleted - deladded
3549
3558
3550 # distinguish between file to forget and the other
3559 # distinguish between file to forget and the other
3551 added = set()
3560 added = set()
3552 for abs in dsadded:
3561 for abs in dsadded:
3553 if repo.dirstate[abs] != 'a':
3562 if repo.dirstate[abs] != 'a':
3554 added.add(abs)
3563 added.add(abs)
3555 dsadded -= added
3564 dsadded -= added
3556
3565
3557 for abs in deladded:
3566 for abs in deladded:
3558 if repo.dirstate[abs] == 'a':
3567 if repo.dirstate[abs] == 'a':
3559 dsadded.add(abs)
3568 dsadded.add(abs)
3560 deladded -= dsadded
3569 deladded -= dsadded
3561
3570
3562 # For files marked as removed, we check if an unknown file is present at
3571 # For files marked as removed, we check if an unknown file is present at
3563 # the same path. If a such file exists it may need to be backed up.
3572 # the same path. If a such file exists it may need to be backed up.
3564 # Making the distinction at this stage helps have simpler backup
3573 # Making the distinction at this stage helps have simpler backup
3565 # logic.
3574 # logic.
3566 removunk = set()
3575 removunk = set()
3567 for abs in removed:
3576 for abs in removed:
3568 target = repo.wjoin(abs)
3577 target = repo.wjoin(abs)
3569 if os.path.lexists(target):
3578 if os.path.lexists(target):
3570 removunk.add(abs)
3579 removunk.add(abs)
3571 removed -= removunk
3580 removed -= removunk
3572
3581
3573 dsremovunk = set()
3582 dsremovunk = set()
3574 for abs in dsremoved:
3583 for abs in dsremoved:
3575 target = repo.wjoin(abs)
3584 target = repo.wjoin(abs)
3576 if os.path.lexists(target):
3585 if os.path.lexists(target):
3577 dsremovunk.add(abs)
3586 dsremovunk.add(abs)
3578 dsremoved -= dsremovunk
3587 dsremoved -= dsremovunk
3579
3588
3580 # action to be actually performed by revert
3589 # action to be actually performed by revert
3581 # (<list of file>, message>) tuple
3590 # (<list of file>, message>) tuple
3582 actions = {'revert': ([], _('reverting %s\n')),
3591 actions = {'revert': ([], _('reverting %s\n')),
3583 'add': ([], _('adding %s\n')),
3592 'add': ([], _('adding %s\n')),
3584 'remove': ([], _('removing %s\n')),
3593 'remove': ([], _('removing %s\n')),
3585 'drop': ([], _('removing %s\n')),
3594 'drop': ([], _('removing %s\n')),
3586 'forget': ([], _('forgetting %s\n')),
3595 'forget': ([], _('forgetting %s\n')),
3587 'undelete': ([], _('undeleting %s\n')),
3596 'undelete': ([], _('undeleting %s\n')),
3588 'noop': (None, _('no changes needed to %s\n')),
3597 'noop': (None, _('no changes needed to %s\n')),
3589 'unknown': (None, _('file not managed: %s\n')),
3598 'unknown': (None, _('file not managed: %s\n')),
3590 }
3599 }
3591
3600
3592 # "constant" that convey the backup strategy.
3601 # "constant" that convey the backup strategy.
3593 # All set to `discard` if `no-backup` is set do avoid checking
3602 # All set to `discard` if `no-backup` is set do avoid checking
3594 # no_backup lower in the code.
3603 # no_backup lower in the code.
3595 # These values are ordered for comparison purposes
3604 # These values are ordered for comparison purposes
3596 backupinteractive = 3 # do backup if interactively modified
3605 backupinteractive = 3 # do backup if interactively modified
3597 backup = 2 # unconditionally do backup
3606 backup = 2 # unconditionally do backup
3598 check = 1 # check if the existing file differs from target
3607 check = 1 # check if the existing file differs from target
3599 discard = 0 # never do backup
3608 discard = 0 # never do backup
3600 if opts.get('no_backup'):
3609 if opts.get('no_backup'):
3601 backupinteractive = backup = check = discard
3610 backupinteractive = backup = check = discard
3602 if interactive:
3611 if interactive:
3603 dsmodifiedbackup = backupinteractive
3612 dsmodifiedbackup = backupinteractive
3604 else:
3613 else:
3605 dsmodifiedbackup = backup
3614 dsmodifiedbackup = backup
3606 tobackup = set()
3615 tobackup = set()
3607
3616
3608 backupanddel = actions['remove']
3617 backupanddel = actions['remove']
3609 if not opts.get('no_backup'):
3618 if not opts.get('no_backup'):
3610 backupanddel = actions['drop']
3619 backupanddel = actions['drop']
3611
3620
3612 disptable = (
3621 disptable = (
3613 # dispatch table:
3622 # dispatch table:
3614 # file state
3623 # file state
3615 # action
3624 # action
3616 # make backup
3625 # make backup
3617
3626
3618 ## Sets that results that will change file on disk
3627 ## Sets that results that will change file on disk
3619 # Modified compared to target, no local change
3628 # Modified compared to target, no local change
3620 (modified, actions['revert'], discard),
3629 (modified, actions['revert'], discard),
3621 # Modified compared to target, but local file is deleted
3630 # Modified compared to target, but local file is deleted
3622 (deleted, actions['revert'], discard),
3631 (deleted, actions['revert'], discard),
3623 # Modified compared to target, local change
3632 # Modified compared to target, local change
3624 (dsmodified, actions['revert'], dsmodifiedbackup),
3633 (dsmodified, actions['revert'], dsmodifiedbackup),
3625 # Added since target
3634 # Added since target
3626 (added, actions['remove'], discard),
3635 (added, actions['remove'], discard),
3627 # Added in working directory
3636 # Added in working directory
3628 (dsadded, actions['forget'], discard),
3637 (dsadded, actions['forget'], discard),
3629 # Added since target, have local modification
3638 # Added since target, have local modification
3630 (modadded, backupanddel, backup),
3639 (modadded, backupanddel, backup),
3631 # Added since target but file is missing in working directory
3640 # Added since target but file is missing in working directory
3632 (deladded, actions['drop'], discard),
3641 (deladded, actions['drop'], discard),
3633 # Removed since target, before working copy parent
3642 # Removed since target, before working copy parent
3634 (removed, actions['add'], discard),
3643 (removed, actions['add'], discard),
3635 # Same as `removed` but an unknown file exists at the same path
3644 # Same as `removed` but an unknown file exists at the same path
3636 (removunk, actions['add'], check),
3645 (removunk, actions['add'], check),
3637 # Removed since targe, marked as such in working copy parent
3646 # Removed since targe, marked as such in working copy parent
3638 (dsremoved, actions['undelete'], discard),
3647 (dsremoved, actions['undelete'], discard),
3639 # Same as `dsremoved` but an unknown file exists at the same path
3648 # Same as `dsremoved` but an unknown file exists at the same path
3640 (dsremovunk, actions['undelete'], check),
3649 (dsremovunk, actions['undelete'], check),
3641 ## the following sets does not result in any file changes
3650 ## the following sets does not result in any file changes
3642 # File with no modification
3651 # File with no modification
3643 (clean, actions['noop'], discard),
3652 (clean, actions['noop'], discard),
3644 # Existing file, not tracked anywhere
3653 # Existing file, not tracked anywhere
3645 (unknown, actions['unknown'], discard),
3654 (unknown, actions['unknown'], discard),
3646 )
3655 )
3647
3656
3648 for abs, (rel, exact) in sorted(names.items()):
3657 for abs, (rel, exact) in sorted(names.items()):
3649 # target file to be touch on disk (relative to cwd)
3658 # target file to be touch on disk (relative to cwd)
3650 target = repo.wjoin(abs)
3659 target = repo.wjoin(abs)
3651 # search the entry in the dispatch table.
3660 # search the entry in the dispatch table.
3652 # if the file is in any of these sets, it was touched in the working
3661 # if the file is in any of these sets, it was touched in the working
3653 # directory parent and we are sure it needs to be reverted.
3662 # directory parent and we are sure it needs to be reverted.
3654 for table, (xlist, msg), dobackup in disptable:
3663 for table, (xlist, msg), dobackup in disptable:
3655 if abs not in table:
3664 if abs not in table:
3656 continue
3665 continue
3657 if xlist is not None:
3666 if xlist is not None:
3658 xlist.append(abs)
3667 xlist.append(abs)
3659 if dobackup:
3668 if dobackup:
3660 # If in interactive mode, don't automatically create
3669 # If in interactive mode, don't automatically create
3661 # .orig files (issue4793)
3670 # .orig files (issue4793)
3662 if dobackup == backupinteractive:
3671 if dobackup == backupinteractive:
3663 tobackup.add(abs)
3672 tobackup.add(abs)
3664 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3673 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3665 bakname = scmutil.origpath(ui, repo, rel)
3674 bakname = scmutil.origpath(ui, repo, rel)
3666 ui.note(_('saving current version of %s as %s\n') %
3675 ui.note(_('saving current version of %s as %s\n') %
3667 (rel, bakname))
3676 (rel, bakname))
3668 if not opts.get('dry_run'):
3677 if not opts.get('dry_run'):
3669 if interactive:
3678 if interactive:
3670 util.copyfile(target, bakname)
3679 util.copyfile(target, bakname)
3671 else:
3680 else:
3672 util.rename(target, bakname)
3681 util.rename(target, bakname)
3673 if ui.verbose or not exact:
3682 if ui.verbose or not exact:
3674 if not isinstance(msg, bytes):
3683 if not isinstance(msg, bytes):
3675 msg = msg(abs)
3684 msg = msg(abs)
3676 ui.status(msg % rel)
3685 ui.status(msg % rel)
3677 elif exact:
3686 elif exact:
3678 ui.warn(msg % rel)
3687 ui.warn(msg % rel)
3679 break
3688 break
3680
3689
3681 if not opts.get('dry_run'):
3690 if not opts.get('dry_run'):
3682 needdata = ('revert', 'add', 'undelete')
3691 needdata = ('revert', 'add', 'undelete')
3683 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3692 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3684 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3693 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3685
3694
3686 if targetsubs:
3695 if targetsubs:
3687 # Revert the subrepos on the revert list
3696 # Revert the subrepos on the revert list
3688 for sub in targetsubs:
3697 for sub in targetsubs:
3689 try:
3698 try:
3690 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3699 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3691 **pycompat.strkwargs(opts))
3700 **pycompat.strkwargs(opts))
3692 except KeyError:
3701 except KeyError:
3693 raise error.Abort("subrepository '%s' does not exist in %s!"
3702 raise error.Abort("subrepository '%s' does not exist in %s!"
3694 % (sub, short(ctx.node())))
3703 % (sub, short(ctx.node())))
3695
3704
3696 def _revertprefetch(repo, ctx, *files):
3705 def _revertprefetch(repo, ctx, *files):
3697 """Let extension changing the storage layer prefetch content"""
3706 """Let extension changing the storage layer prefetch content"""
3698
3707
3699 def _performrevert(repo, parents, ctx, actions, interactive=False,
3708 def _performrevert(repo, parents, ctx, actions, interactive=False,
3700 tobackup=None):
3709 tobackup=None):
3701 """function that actually perform all the actions computed for revert
3710 """function that actually perform all the actions computed for revert
3702
3711
3703 This is an independent function to let extension to plug in and react to
3712 This is an independent function to let extension to plug in and react to
3704 the imminent revert.
3713 the imminent revert.
3705
3714
3706 Make sure you have the working directory locked when calling this function.
3715 Make sure you have the working directory locked when calling this function.
3707 """
3716 """
3708 parent, p2 = parents
3717 parent, p2 = parents
3709 node = ctx.node()
3718 node = ctx.node()
3710 excluded_files = []
3719 excluded_files = []
3711 matcher_opts = {"exclude": excluded_files}
3720 matcher_opts = {"exclude": excluded_files}
3712
3721
3713 def checkout(f):
3722 def checkout(f):
3714 fc = ctx[f]
3723 fc = ctx[f]
3715 repo.wwrite(f, fc.data(), fc.flags())
3724 repo.wwrite(f, fc.data(), fc.flags())
3716
3725
3717 def doremove(f):
3726 def doremove(f):
3718 try:
3727 try:
3719 repo.wvfs.unlinkpath(f)
3728 repo.wvfs.unlinkpath(f)
3720 except OSError:
3729 except OSError:
3721 pass
3730 pass
3722 repo.dirstate.remove(f)
3731 repo.dirstate.remove(f)
3723
3732
3724 audit_path = pathutil.pathauditor(repo.root, cached=True)
3733 audit_path = pathutil.pathauditor(repo.root, cached=True)
3725 for f in actions['forget'][0]:
3734 for f in actions['forget'][0]:
3726 if interactive:
3735 if interactive:
3727 choice = repo.ui.promptchoice(
3736 choice = repo.ui.promptchoice(
3728 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3737 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3729 if choice == 0:
3738 if choice == 0:
3730 repo.dirstate.drop(f)
3739 repo.dirstate.drop(f)
3731 else:
3740 else:
3732 excluded_files.append(repo.wjoin(f))
3741 excluded_files.append(repo.wjoin(f))
3733 else:
3742 else:
3734 repo.dirstate.drop(f)
3743 repo.dirstate.drop(f)
3735 for f in actions['remove'][0]:
3744 for f in actions['remove'][0]:
3736 audit_path(f)
3745 audit_path(f)
3737 if interactive:
3746 if interactive:
3738 choice = repo.ui.promptchoice(
3747 choice = repo.ui.promptchoice(
3739 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3748 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3740 if choice == 0:
3749 if choice == 0:
3741 doremove(f)
3750 doremove(f)
3742 else:
3751 else:
3743 excluded_files.append(repo.wjoin(f))
3752 excluded_files.append(repo.wjoin(f))
3744 else:
3753 else:
3745 doremove(f)
3754 doremove(f)
3746 for f in actions['drop'][0]:
3755 for f in actions['drop'][0]:
3747 audit_path(f)
3756 audit_path(f)
3748 repo.dirstate.remove(f)
3757 repo.dirstate.remove(f)
3749
3758
3750 normal = None
3759 normal = None
3751 if node == parent:
3760 if node == parent:
3752 # We're reverting to our parent. If possible, we'd like status
3761 # We're reverting to our parent. If possible, we'd like status
3753 # to report the file as clean. We have to use normallookup for
3762 # to report the file as clean. We have to use normallookup for
3754 # merges to avoid losing information about merged/dirty files.
3763 # merges to avoid losing information about merged/dirty files.
3755 if p2 != nullid:
3764 if p2 != nullid:
3756 normal = repo.dirstate.normallookup
3765 normal = repo.dirstate.normallookup
3757 else:
3766 else:
3758 normal = repo.dirstate.normal
3767 normal = repo.dirstate.normal
3759
3768
3760 newlyaddedandmodifiedfiles = set()
3769 newlyaddedandmodifiedfiles = set()
3761 if interactive:
3770 if interactive:
3762 # Prompt the user for changes to revert
3771 # Prompt the user for changes to revert
3763 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3772 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3764 m = scmutil.match(ctx, torevert, matcher_opts)
3773 m = scmutil.match(ctx, torevert, matcher_opts)
3765 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3774 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3766 diffopts.nodates = True
3775 diffopts.nodates = True
3767 diffopts.git = True
3776 diffopts.git = True
3768 operation = 'discard'
3777 operation = 'discard'
3769 reversehunks = True
3778 reversehunks = True
3770 if node != parent:
3779 if node != parent:
3771 operation = 'apply'
3780 operation = 'apply'
3772 reversehunks = False
3781 reversehunks = False
3773 if reversehunks:
3782 if reversehunks:
3774 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3783 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3775 else:
3784 else:
3776 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3785 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3777 originalchunks = patch.parsepatch(diff)
3786 originalchunks = patch.parsepatch(diff)
3778
3787
3779 try:
3788 try:
3780
3789
3781 chunks, opts = recordfilter(repo.ui, originalchunks,
3790 chunks, opts = recordfilter(repo.ui, originalchunks,
3782 operation=operation)
3791 operation=operation)
3783 if reversehunks:
3792 if reversehunks:
3784 chunks = patch.reversehunks(chunks)
3793 chunks = patch.reversehunks(chunks)
3785
3794
3786 except error.PatchError as err:
3795 except error.PatchError as err:
3787 raise error.Abort(_('error parsing patch: %s') % err)
3796 raise error.Abort(_('error parsing patch: %s') % err)
3788
3797
3789 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3798 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3790 if tobackup is None:
3799 if tobackup is None:
3791 tobackup = set()
3800 tobackup = set()
3792 # Apply changes
3801 # Apply changes
3793 fp = stringio()
3802 fp = stringio()
3794 for c in chunks:
3803 for c in chunks:
3795 # Create a backup file only if this hunk should be backed up
3804 # Create a backup file only if this hunk should be backed up
3796 if ishunk(c) and c.header.filename() in tobackup:
3805 if ishunk(c) and c.header.filename() in tobackup:
3797 abs = c.header.filename()
3806 abs = c.header.filename()
3798 target = repo.wjoin(abs)
3807 target = repo.wjoin(abs)
3799 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3808 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3800 util.copyfile(target, bakname)
3809 util.copyfile(target, bakname)
3801 tobackup.remove(abs)
3810 tobackup.remove(abs)
3802 c.write(fp)
3811 c.write(fp)
3803 dopatch = fp.tell()
3812 dopatch = fp.tell()
3804 fp.seek(0)
3813 fp.seek(0)
3805 if dopatch:
3814 if dopatch:
3806 try:
3815 try:
3807 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3816 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3808 except error.PatchError as err:
3817 except error.PatchError as err:
3809 raise error.Abort(str(err))
3818 raise error.Abort(str(err))
3810 del fp
3819 del fp
3811 else:
3820 else:
3812 for f in actions['revert'][0]:
3821 for f in actions['revert'][0]:
3813 checkout(f)
3822 checkout(f)
3814 if normal:
3823 if normal:
3815 normal(f)
3824 normal(f)
3816
3825
3817 for f in actions['add'][0]:
3826 for f in actions['add'][0]:
3818 # Don't checkout modified files, they are already created by the diff
3827 # Don't checkout modified files, they are already created by the diff
3819 if f not in newlyaddedandmodifiedfiles:
3828 if f not in newlyaddedandmodifiedfiles:
3820 checkout(f)
3829 checkout(f)
3821 repo.dirstate.add(f)
3830 repo.dirstate.add(f)
3822
3831
3823 normal = repo.dirstate.normallookup
3832 normal = repo.dirstate.normallookup
3824 if node == parent and p2 == nullid:
3833 if node == parent and p2 == nullid:
3825 normal = repo.dirstate.normal
3834 normal = repo.dirstate.normal
3826 for f in actions['undelete'][0]:
3835 for f in actions['undelete'][0]:
3827 checkout(f)
3836 checkout(f)
3828 normal(f)
3837 normal(f)
3829
3838
3830 copied = copies.pathcopies(repo[parent], ctx)
3839 copied = copies.pathcopies(repo[parent], ctx)
3831
3840
3832 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3841 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3833 if f in copied:
3842 if f in copied:
3834 repo.dirstate.copy(copied[f], f)
3843 repo.dirstate.copy(copied[f], f)
3835
3844
3836 class command(registrar.command):
3845 class command(registrar.command):
3837 """deprecated: used registrar.command instead"""
3846 """deprecated: used registrar.command instead"""
3838 def _doregister(self, func, name, *args, **kwargs):
3847 def _doregister(self, func, name, *args, **kwargs):
3839 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3848 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3840 return super(command, self)._doregister(func, name, *args, **kwargs)
3849 return super(command, self)._doregister(func, name, *args, **kwargs)
3841
3850
3842 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3851 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3843 # commands.outgoing. "missing" is "missing" of the result of
3852 # commands.outgoing. "missing" is "missing" of the result of
3844 # "findcommonoutgoing()"
3853 # "findcommonoutgoing()"
3845 outgoinghooks = util.hooks()
3854 outgoinghooks = util.hooks()
3846
3855
3847 # a list of (ui, repo) functions called by commands.summary
3856 # a list of (ui, repo) functions called by commands.summary
3848 summaryhooks = util.hooks()
3857 summaryhooks = util.hooks()
3849
3858
3850 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3859 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3851 #
3860 #
3852 # functions should return tuple of booleans below, if 'changes' is None:
3861 # functions should return tuple of booleans below, if 'changes' is None:
3853 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3862 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3854 #
3863 #
3855 # otherwise, 'changes' is a tuple of tuples below:
3864 # otherwise, 'changes' is a tuple of tuples below:
3856 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3865 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3857 # - (desturl, destbranch, destpeer, outgoing)
3866 # - (desturl, destbranch, destpeer, outgoing)
3858 summaryremotehooks = util.hooks()
3867 summaryremotehooks = util.hooks()
3859
3868
3860 # A list of state files kept by multistep operations like graft.
3869 # A list of state files kept by multistep operations like graft.
3861 # Since graft cannot be aborted, it is considered 'clearable' by update.
3870 # Since graft cannot be aborted, it is considered 'clearable' by update.
3862 # note: bisect is intentionally excluded
3871 # note: bisect is intentionally excluded
3863 # (state file, clearable, allowcommit, error, hint)
3872 # (state file, clearable, allowcommit, error, hint)
3864 unfinishedstates = [
3873 unfinishedstates = [
3865 ('graftstate', True, False, _('graft in progress'),
3874 ('graftstate', True, False, _('graft in progress'),
3866 _("use 'hg graft --continue' or 'hg update' to abort")),
3875 _("use 'hg graft --continue' or 'hg update' to abort")),
3867 ('updatestate', True, False, _('last update was interrupted'),
3876 ('updatestate', True, False, _('last update was interrupted'),
3868 _("use 'hg update' to get a consistent checkout"))
3877 _("use 'hg update' to get a consistent checkout"))
3869 ]
3878 ]
3870
3879
3871 def checkunfinished(repo, commit=False):
3880 def checkunfinished(repo, commit=False):
3872 '''Look for an unfinished multistep operation, like graft, and abort
3881 '''Look for an unfinished multistep operation, like graft, and abort
3873 if found. It's probably good to check this right before
3882 if found. It's probably good to check this right before
3874 bailifchanged().
3883 bailifchanged().
3875 '''
3884 '''
3876 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3885 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3877 if commit and allowcommit:
3886 if commit and allowcommit:
3878 continue
3887 continue
3879 if repo.vfs.exists(f):
3888 if repo.vfs.exists(f):
3880 raise error.Abort(msg, hint=hint)
3889 raise error.Abort(msg, hint=hint)
3881
3890
3882 def clearunfinished(repo):
3891 def clearunfinished(repo):
3883 '''Check for unfinished operations (as above), and clear the ones
3892 '''Check for unfinished operations (as above), and clear the ones
3884 that are clearable.
3893 that are clearable.
3885 '''
3894 '''
3886 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3895 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3887 if not clearable and repo.vfs.exists(f):
3896 if not clearable and repo.vfs.exists(f):
3888 raise error.Abort(msg, hint=hint)
3897 raise error.Abort(msg, hint=hint)
3889 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3898 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3890 if clearable and repo.vfs.exists(f):
3899 if clearable and repo.vfs.exists(f):
3891 util.unlink(repo.vfs.join(f))
3900 util.unlink(repo.vfs.join(f))
3892
3901
3893 afterresolvedstates = [
3902 afterresolvedstates = [
3894 ('graftstate',
3903 ('graftstate',
3895 _('hg graft --continue')),
3904 _('hg graft --continue')),
3896 ]
3905 ]
3897
3906
3898 def howtocontinue(repo):
3907 def howtocontinue(repo):
3899 '''Check for an unfinished operation and return the command to finish
3908 '''Check for an unfinished operation and return the command to finish
3900 it.
3909 it.
3901
3910
3902 afterresolvedstates tuples define a .hg/{file} and the corresponding
3911 afterresolvedstates tuples define a .hg/{file} and the corresponding
3903 command needed to finish it.
3912 command needed to finish it.
3904
3913
3905 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3914 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3906 a boolean.
3915 a boolean.
3907 '''
3916 '''
3908 contmsg = _("continue: %s")
3917 contmsg = _("continue: %s")
3909 for f, msg in afterresolvedstates:
3918 for f, msg in afterresolvedstates:
3910 if repo.vfs.exists(f):
3919 if repo.vfs.exists(f):
3911 return contmsg % msg, True
3920 return contmsg % msg, True
3912 if repo[None].dirty(missing=True, merge=False, branch=False):
3921 if repo[None].dirty(missing=True, merge=False, branch=False):
3913 return contmsg % _("hg commit"), False
3922 return contmsg % _("hg commit"), False
3914 return None, None
3923 return None, None
3915
3924
3916 def checkafterresolved(repo):
3925 def checkafterresolved(repo):
3917 '''Inform the user about the next action after completing hg resolve
3926 '''Inform the user about the next action after completing hg resolve
3918
3927
3919 If there's a matching afterresolvedstates, howtocontinue will yield
3928 If there's a matching afterresolvedstates, howtocontinue will yield
3920 repo.ui.warn as the reporter.
3929 repo.ui.warn as the reporter.
3921
3930
3922 Otherwise, it will yield repo.ui.note.
3931 Otherwise, it will yield repo.ui.note.
3923 '''
3932 '''
3924 msg, warning = howtocontinue(repo)
3933 msg, warning = howtocontinue(repo)
3925 if msg is not None:
3934 if msg is not None:
3926 if warning:
3935 if warning:
3927 repo.ui.warn("%s\n" % msg)
3936 repo.ui.warn("%s\n" % msg)
3928 else:
3937 else:
3929 repo.ui.note("%s\n" % msg)
3938 repo.ui.note("%s\n" % msg)
3930
3939
3931 def wrongtooltocontinue(repo, task):
3940 def wrongtooltocontinue(repo, task):
3932 '''Raise an abort suggesting how to properly continue if there is an
3941 '''Raise an abort suggesting how to properly continue if there is an
3933 active task.
3942 active task.
3934
3943
3935 Uses howtocontinue() to find the active task.
3944 Uses howtocontinue() to find the active task.
3936
3945
3937 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3946 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3938 a hint.
3947 a hint.
3939 '''
3948 '''
3940 after = howtocontinue(repo)
3949 after = howtocontinue(repo)
3941 hint = None
3950 hint = None
3942 if after[1]:
3951 if after[1]:
3943 hint = after[0]
3952 hint = after[0]
3944 raise error.Abort(_('no %s in progress') % task, hint=hint)
3953 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,5617 +1,5609 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for 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 difflib
10 import difflib
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
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23 from . import (
23 from . import (
24 archival,
24 archival,
25 bookmarks,
25 bookmarks,
26 bundle2,
26 bundle2,
27 changegroup,
27 changegroup,
28 cmdutil,
28 cmdutil,
29 copies,
29 copies,
30 debugcommands as debugcommandsmod,
30 debugcommands as debugcommandsmod,
31 destutil,
31 destutil,
32 dirstateguard,
32 dirstateguard,
33 discovery,
33 discovery,
34 encoding,
34 encoding,
35 error,
35 error,
36 exchange,
36 exchange,
37 extensions,
37 extensions,
38 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 hbisect,
40 hbisect,
41 help,
41 help,
42 hg,
42 hg,
43 lock as lockmod,
43 lock as lockmod,
44 merge as mergemod,
44 merge as mergemod,
45 obsolete,
45 obsolete,
46 patch,
46 patch,
47 phases,
47 phases,
48 pycompat,
48 pycompat,
49 rcutil,
49 rcutil,
50 registrar,
50 registrar,
51 revsetlang,
51 revsetlang,
52 rewriteutil,
52 rewriteutil,
53 scmutil,
53 scmutil,
54 server,
54 server,
55 sshserver,
55 sshserver,
56 streamclone,
56 streamclone,
57 tags as tagsmod,
57 tags as tagsmod,
58 templatekw,
58 templatekw,
59 ui as uimod,
59 ui as uimod,
60 util,
60 util,
61 )
61 )
62
62
63 release = lockmod.release
63 release = lockmod.release
64
64
65 table = {}
65 table = {}
66 table.update(debugcommandsmod.command._table)
66 table.update(debugcommandsmod.command._table)
67
67
68 command = registrar.command(table)
68 command = registrar.command(table)
69 readonly = registrar.command.readonly
69 readonly = registrar.command.readonly
70
70
71 # common command options
71 # common command options
72
72
73 globalopts = [
73 globalopts = [
74 ('R', 'repository', '',
74 ('R', 'repository', '',
75 _('repository root directory or name of overlay bundle file'),
75 _('repository root directory or name of overlay bundle file'),
76 _('REPO')),
76 _('REPO')),
77 ('', 'cwd', '',
77 ('', 'cwd', '',
78 _('change working directory'), _('DIR')),
78 _('change working directory'), _('DIR')),
79 ('y', 'noninteractive', None,
79 ('y', 'noninteractive', None,
80 _('do not prompt, automatically pick the first choice for all prompts')),
80 _('do not prompt, automatically pick the first choice for all prompts')),
81 ('q', 'quiet', None, _('suppress output')),
81 ('q', 'quiet', None, _('suppress output')),
82 ('v', 'verbose', None, _('enable additional output')),
82 ('v', 'verbose', None, _('enable additional output')),
83 ('', 'color', '',
83 ('', 'color', '',
84 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
84 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
85 # and should not be translated
85 # and should not be translated
86 _("when to colorize (boolean, always, auto, never, or debug)"),
86 _("when to colorize (boolean, always, auto, never, or debug)"),
87 _('TYPE')),
87 _('TYPE')),
88 ('', 'config', [],
88 ('', 'config', [],
89 _('set/override config option (use \'section.name=value\')'),
89 _('set/override config option (use \'section.name=value\')'),
90 _('CONFIG')),
90 _('CONFIG')),
91 ('', 'debug', None, _('enable debugging output')),
91 ('', 'debug', None, _('enable debugging output')),
92 ('', 'debugger', None, _('start debugger')),
92 ('', 'debugger', None, _('start debugger')),
93 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
93 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
94 _('ENCODE')),
94 _('ENCODE')),
95 ('', 'encodingmode', encoding.encodingmode,
95 ('', 'encodingmode', encoding.encodingmode,
96 _('set the charset encoding mode'), _('MODE')),
96 _('set the charset encoding mode'), _('MODE')),
97 ('', 'traceback', None, _('always print a traceback on exception')),
97 ('', 'traceback', None, _('always print a traceback on exception')),
98 ('', 'time', None, _('time how long the command takes')),
98 ('', 'time', None, _('time how long the command takes')),
99 ('', 'profile', None, _('print command execution profile')),
99 ('', 'profile', None, _('print command execution profile')),
100 ('', 'version', None, _('output version information and exit')),
100 ('', 'version', None, _('output version information and exit')),
101 ('h', 'help', None, _('display help and exit')),
101 ('h', 'help', None, _('display help and exit')),
102 ('', 'hidden', False, _('consider hidden changesets')),
102 ('', 'hidden', False, _('consider hidden changesets')),
103 ('', 'pager', 'auto',
103 ('', 'pager', 'auto',
104 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
104 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
105 ]
105 ]
106
106
107 dryrunopts = cmdutil.dryrunopts
107 dryrunopts = cmdutil.dryrunopts
108 remoteopts = cmdutil.remoteopts
108 remoteopts = cmdutil.remoteopts
109 walkopts = cmdutil.walkopts
109 walkopts = cmdutil.walkopts
110 commitopts = cmdutil.commitopts
110 commitopts = cmdutil.commitopts
111 commitopts2 = cmdutil.commitopts2
111 commitopts2 = cmdutil.commitopts2
112 formatteropts = cmdutil.formatteropts
112 formatteropts = cmdutil.formatteropts
113 templateopts = cmdutil.templateopts
113 templateopts = cmdutil.templateopts
114 logopts = cmdutil.logopts
114 logopts = cmdutil.logopts
115 diffopts = cmdutil.diffopts
115 diffopts = cmdutil.diffopts
116 diffwsopts = cmdutil.diffwsopts
116 diffwsopts = cmdutil.diffwsopts
117 diffopts2 = cmdutil.diffopts2
117 diffopts2 = cmdutil.diffopts2
118 mergetoolopts = cmdutil.mergetoolopts
118 mergetoolopts = cmdutil.mergetoolopts
119 similarityopts = cmdutil.similarityopts
119 similarityopts = cmdutil.similarityopts
120 subrepoopts = cmdutil.subrepoopts
120 subrepoopts = cmdutil.subrepoopts
121 debugrevlogopts = cmdutil.debugrevlogopts
121 debugrevlogopts = cmdutil.debugrevlogopts
122
122
123 # Commands start here, listed alphabetically
123 # Commands start here, listed alphabetically
124
124
125 @command('^add',
125 @command('^add',
126 walkopts + subrepoopts + dryrunopts,
126 walkopts + subrepoopts + dryrunopts,
127 _('[OPTION]... [FILE]...'),
127 _('[OPTION]... [FILE]...'),
128 inferrepo=True)
128 inferrepo=True)
129 def add(ui, repo, *pats, **opts):
129 def add(ui, repo, *pats, **opts):
130 """add the specified files on the next commit
130 """add the specified files on the next commit
131
131
132 Schedule files to be version controlled and added to the
132 Schedule files to be version controlled and added to the
133 repository.
133 repository.
134
134
135 The files will be added to the repository at the next commit. To
135 The files will be added to the repository at the next commit. To
136 undo an add before that, see :hg:`forget`.
136 undo an add before that, see :hg:`forget`.
137
137
138 If no names are given, add all files to the repository (except
138 If no names are given, add all files to the repository (except
139 files matching ``.hgignore``).
139 files matching ``.hgignore``).
140
140
141 .. container:: verbose
141 .. container:: verbose
142
142
143 Examples:
143 Examples:
144
144
145 - New (unknown) files are added
145 - New (unknown) files are added
146 automatically by :hg:`add`::
146 automatically by :hg:`add`::
147
147
148 $ ls
148 $ ls
149 foo.c
149 foo.c
150 $ hg status
150 $ hg status
151 ? foo.c
151 ? foo.c
152 $ hg add
152 $ hg add
153 adding foo.c
153 adding foo.c
154 $ hg status
154 $ hg status
155 A foo.c
155 A foo.c
156
156
157 - Specific files to be added can be specified::
157 - Specific files to be added can be specified::
158
158
159 $ ls
159 $ ls
160 bar.c foo.c
160 bar.c foo.c
161 $ hg status
161 $ hg status
162 ? bar.c
162 ? bar.c
163 ? foo.c
163 ? foo.c
164 $ hg add bar.c
164 $ hg add bar.c
165 $ hg status
165 $ hg status
166 A bar.c
166 A bar.c
167 ? foo.c
167 ? foo.c
168
168
169 Returns 0 if all files are successfully added.
169 Returns 0 if all files are successfully added.
170 """
170 """
171
171
172 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
172 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
173 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
173 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
174 return rejected and 1 or 0
174 return rejected and 1 or 0
175
175
176 @command('addremove',
176 @command('addremove',
177 similarityopts + subrepoopts + walkopts + dryrunopts,
177 similarityopts + subrepoopts + walkopts + dryrunopts,
178 _('[OPTION]... [FILE]...'),
178 _('[OPTION]... [FILE]...'),
179 inferrepo=True)
179 inferrepo=True)
180 def addremove(ui, repo, *pats, **opts):
180 def addremove(ui, repo, *pats, **opts):
181 """add all new files, delete all missing files
181 """add all new files, delete all missing files
182
182
183 Add all new files and remove all missing files from the
183 Add all new files and remove all missing files from the
184 repository.
184 repository.
185
185
186 Unless names are given, new files are ignored if they match any of
186 Unless names are given, new files are ignored if they match any of
187 the patterns in ``.hgignore``. As with add, these changes take
187 the patterns in ``.hgignore``. As with add, these changes take
188 effect at the next commit.
188 effect at the next commit.
189
189
190 Use the -s/--similarity option to detect renamed files. This
190 Use the -s/--similarity option to detect renamed files. This
191 option takes a percentage between 0 (disabled) and 100 (files must
191 option takes a percentage between 0 (disabled) and 100 (files must
192 be identical) as its parameter. With a parameter greater than 0,
192 be identical) as its parameter. With a parameter greater than 0,
193 this compares every removed file with every added file and records
193 this compares every removed file with every added file and records
194 those similar enough as renames. Detecting renamed files this way
194 those similar enough as renames. Detecting renamed files this way
195 can be expensive. After using this option, :hg:`status -C` can be
195 can be expensive. After using this option, :hg:`status -C` can be
196 used to check which files were identified as moved or renamed. If
196 used to check which files were identified as moved or renamed. If
197 not specified, -s/--similarity defaults to 100 and only renames of
197 not specified, -s/--similarity defaults to 100 and only renames of
198 identical files are detected.
198 identical files are detected.
199
199
200 .. container:: verbose
200 .. container:: verbose
201
201
202 Examples:
202 Examples:
203
203
204 - A number of files (bar.c and foo.c) are new,
204 - A number of files (bar.c and foo.c) are new,
205 while foobar.c has been removed (without using :hg:`remove`)
205 while foobar.c has been removed (without using :hg:`remove`)
206 from the repository::
206 from the repository::
207
207
208 $ ls
208 $ ls
209 bar.c foo.c
209 bar.c foo.c
210 $ hg status
210 $ hg status
211 ! foobar.c
211 ! foobar.c
212 ? bar.c
212 ? bar.c
213 ? foo.c
213 ? foo.c
214 $ hg addremove
214 $ hg addremove
215 adding bar.c
215 adding bar.c
216 adding foo.c
216 adding foo.c
217 removing foobar.c
217 removing foobar.c
218 $ hg status
218 $ hg status
219 A bar.c
219 A bar.c
220 A foo.c
220 A foo.c
221 R foobar.c
221 R foobar.c
222
222
223 - A file foobar.c was moved to foo.c without using :hg:`rename`.
223 - A file foobar.c was moved to foo.c without using :hg:`rename`.
224 Afterwards, it was edited slightly::
224 Afterwards, it was edited slightly::
225
225
226 $ ls
226 $ ls
227 foo.c
227 foo.c
228 $ hg status
228 $ hg status
229 ! foobar.c
229 ! foobar.c
230 ? foo.c
230 ? foo.c
231 $ hg addremove --similarity 90
231 $ hg addremove --similarity 90
232 removing foobar.c
232 removing foobar.c
233 adding foo.c
233 adding foo.c
234 recording removal of foobar.c as rename to foo.c (94% similar)
234 recording removal of foobar.c as rename to foo.c (94% similar)
235 $ hg status -C
235 $ hg status -C
236 A foo.c
236 A foo.c
237 foobar.c
237 foobar.c
238 R foobar.c
238 R foobar.c
239
239
240 Returns 0 if all files are successfully added.
240 Returns 0 if all files are successfully added.
241 """
241 """
242 opts = pycompat.byteskwargs(opts)
242 opts = pycompat.byteskwargs(opts)
243 try:
243 try:
244 sim = float(opts.get('similarity') or 100)
244 sim = float(opts.get('similarity') or 100)
245 except ValueError:
245 except ValueError:
246 raise error.Abort(_('similarity must be a number'))
246 raise error.Abort(_('similarity must be a number'))
247 if sim < 0 or sim > 100:
247 if sim < 0 or sim > 100:
248 raise error.Abort(_('similarity must be between 0 and 100'))
248 raise error.Abort(_('similarity must be between 0 and 100'))
249 matcher = scmutil.match(repo[None], pats, opts)
249 matcher = scmutil.match(repo[None], pats, opts)
250 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
250 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
251
251
252 @command('^annotate|blame',
252 @command('^annotate|blame',
253 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
253 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
254 ('', 'follow', None,
254 ('', 'follow', None,
255 _('follow copies/renames and list the filename (DEPRECATED)')),
255 _('follow copies/renames and list the filename (DEPRECATED)')),
256 ('', 'no-follow', None, _("don't follow copies and renames")),
256 ('', 'no-follow', None, _("don't follow copies and renames")),
257 ('a', 'text', None, _('treat all files as text')),
257 ('a', 'text', None, _('treat all files as text')),
258 ('u', 'user', None, _('list the author (long with -v)')),
258 ('u', 'user', None, _('list the author (long with -v)')),
259 ('f', 'file', None, _('list the filename')),
259 ('f', 'file', None, _('list the filename')),
260 ('d', 'date', None, _('list the date (short with -q)')),
260 ('d', 'date', None, _('list the date (short with -q)')),
261 ('n', 'number', None, _('list the revision number (default)')),
261 ('n', 'number', None, _('list the revision number (default)')),
262 ('c', 'changeset', None, _('list the changeset')),
262 ('c', 'changeset', None, _('list the changeset')),
263 ('l', 'line-number', None, _('show line number at the first appearance')),
263 ('l', 'line-number', None, _('show line number at the first appearance')),
264 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
264 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
265 ] + diffwsopts + walkopts + formatteropts,
265 ] + diffwsopts + walkopts + formatteropts,
266 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
266 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
267 inferrepo=True)
267 inferrepo=True)
268 def annotate(ui, repo, *pats, **opts):
268 def annotate(ui, repo, *pats, **opts):
269 """show changeset information by line for each file
269 """show changeset information by line for each file
270
270
271 List changes in files, showing the revision id responsible for
271 List changes in files, showing the revision id responsible for
272 each line.
272 each line.
273
273
274 This command is useful for discovering when a change was made and
274 This command is useful for discovering when a change was made and
275 by whom.
275 by whom.
276
276
277 If you include --file, --user, or --date, the revision number is
277 If you include --file, --user, or --date, the revision number is
278 suppressed unless you also include --number.
278 suppressed unless you also include --number.
279
279
280 Without the -a/--text option, annotate will avoid processing files
280 Without the -a/--text option, annotate will avoid processing files
281 it detects as binary. With -a, annotate will annotate the file
281 it detects as binary. With -a, annotate will annotate the file
282 anyway, although the results will probably be neither useful
282 anyway, although the results will probably be neither useful
283 nor desirable.
283 nor desirable.
284
284
285 Returns 0 on success.
285 Returns 0 on success.
286 """
286 """
287 opts = pycompat.byteskwargs(opts)
287 opts = pycompat.byteskwargs(opts)
288 if not pats:
288 if not pats:
289 raise error.Abort(_('at least one filename or pattern is required'))
289 raise error.Abort(_('at least one filename or pattern is required'))
290
290
291 if opts.get('follow'):
291 if opts.get('follow'):
292 # --follow is deprecated and now just an alias for -f/--file
292 # --follow is deprecated and now just an alias for -f/--file
293 # to mimic the behavior of Mercurial before version 1.5
293 # to mimic the behavior of Mercurial before version 1.5
294 opts['file'] = True
294 opts['file'] = True
295
295
296 rev = opts.get('rev')
296 rev = opts.get('rev')
297 if rev:
297 if rev:
298 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
298 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
299 ctx = scmutil.revsingle(repo, rev)
299 ctx = scmutil.revsingle(repo, rev)
300
300
301 rootfm = ui.formatter('annotate', opts)
301 rootfm = ui.formatter('annotate', opts)
302 if ui.quiet:
302 if ui.quiet:
303 datefunc = util.shortdate
303 datefunc = util.shortdate
304 else:
304 else:
305 datefunc = util.datestr
305 datefunc = util.datestr
306 if ctx.rev() is None:
306 if ctx.rev() is None:
307 def hexfn(node):
307 def hexfn(node):
308 if node is None:
308 if node is None:
309 return None
309 return None
310 else:
310 else:
311 return rootfm.hexfunc(node)
311 return rootfm.hexfunc(node)
312 if opts.get('changeset'):
312 if opts.get('changeset'):
313 # omit "+" suffix which is appended to node hex
313 # omit "+" suffix which is appended to node hex
314 def formatrev(rev):
314 def formatrev(rev):
315 if rev is None:
315 if rev is None:
316 return '%d' % ctx.p1().rev()
316 return '%d' % ctx.p1().rev()
317 else:
317 else:
318 return '%d' % rev
318 return '%d' % rev
319 else:
319 else:
320 def formatrev(rev):
320 def formatrev(rev):
321 if rev is None:
321 if rev is None:
322 return '%d+' % ctx.p1().rev()
322 return '%d+' % ctx.p1().rev()
323 else:
323 else:
324 return '%d ' % rev
324 return '%d ' % rev
325 def formathex(hex):
325 def formathex(hex):
326 if hex is None:
326 if hex is None:
327 return '%s+' % rootfm.hexfunc(ctx.p1().node())
327 return '%s+' % rootfm.hexfunc(ctx.p1().node())
328 else:
328 else:
329 return '%s ' % hex
329 return '%s ' % hex
330 else:
330 else:
331 hexfn = rootfm.hexfunc
331 hexfn = rootfm.hexfunc
332 formatrev = formathex = pycompat.bytestr
332 formatrev = formathex = pycompat.bytestr
333
333
334 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
334 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
335 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
335 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
336 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
336 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
337 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
337 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
338 ('file', ' ', lambda x: x.fctx.path(), str),
338 ('file', ' ', lambda x: x.fctx.path(), str),
339 ('line_number', ':', lambda x: x.lineno, str),
339 ('line_number', ':', lambda x: x.lineno, str),
340 ]
340 ]
341 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
341 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
342
342
343 if (not opts.get('user') and not opts.get('changeset')
343 if (not opts.get('user') and not opts.get('changeset')
344 and not opts.get('date') and not opts.get('file')):
344 and not opts.get('date') and not opts.get('file')):
345 opts['number'] = True
345 opts['number'] = True
346
346
347 linenumber = opts.get('line_number') is not None
347 linenumber = opts.get('line_number') is not None
348 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
348 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
349 raise error.Abort(_('at least one of -n/-c is required for -l'))
349 raise error.Abort(_('at least one of -n/-c is required for -l'))
350
350
351 ui.pager('annotate')
351 ui.pager('annotate')
352
352
353 if rootfm.isplain():
353 if rootfm.isplain():
354 def makefunc(get, fmt):
354 def makefunc(get, fmt):
355 return lambda x: fmt(get(x))
355 return lambda x: fmt(get(x))
356 else:
356 else:
357 def makefunc(get, fmt):
357 def makefunc(get, fmt):
358 return get
358 return get
359 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
359 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
360 if opts.get(op)]
360 if opts.get(op)]
361 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
361 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
362 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
362 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
363 if opts.get(op))
363 if opts.get(op))
364
364
365 def bad(x, y):
365 def bad(x, y):
366 raise error.Abort("%s: %s" % (x, y))
366 raise error.Abort("%s: %s" % (x, y))
367
367
368 m = scmutil.match(ctx, pats, opts, badfn=bad)
368 m = scmutil.match(ctx, pats, opts, badfn=bad)
369
369
370 follow = not opts.get('no_follow')
370 follow = not opts.get('no_follow')
371 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
371 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
372 whitespace=True)
372 whitespace=True)
373 skiprevs = opts.get('skip')
373 skiprevs = opts.get('skip')
374 if skiprevs:
374 if skiprevs:
375 skiprevs = scmutil.revrange(repo, skiprevs)
375 skiprevs = scmutil.revrange(repo, skiprevs)
376
376
377 for abs in ctx.walk(m):
377 for abs in ctx.walk(m):
378 fctx = ctx[abs]
378 fctx = ctx[abs]
379 rootfm.startitem()
379 rootfm.startitem()
380 rootfm.data(abspath=abs, path=m.rel(abs))
380 rootfm.data(abspath=abs, path=m.rel(abs))
381 if not opts.get('text') and fctx.isbinary():
381 if not opts.get('text') and fctx.isbinary():
382 rootfm.plain(_("%s: binary file\n")
382 rootfm.plain(_("%s: binary file\n")
383 % ((pats and m.rel(abs)) or abs))
383 % ((pats and m.rel(abs)) or abs))
384 continue
384 continue
385
385
386 fm = rootfm.nested('lines')
386 fm = rootfm.nested('lines')
387 lines = fctx.annotate(follow=follow, linenumber=linenumber,
387 lines = fctx.annotate(follow=follow, linenumber=linenumber,
388 skiprevs=skiprevs, diffopts=diffopts)
388 skiprevs=skiprevs, diffopts=diffopts)
389 if not lines:
389 if not lines:
390 fm.end()
390 fm.end()
391 continue
391 continue
392 formats = []
392 formats = []
393 pieces = []
393 pieces = []
394
394
395 for f, sep in funcmap:
395 for f, sep in funcmap:
396 l = [f(n) for n, dummy in lines]
396 l = [f(n) for n, dummy in lines]
397 if fm.isplain():
397 if fm.isplain():
398 sizes = [encoding.colwidth(x) for x in l]
398 sizes = [encoding.colwidth(x) for x in l]
399 ml = max(sizes)
399 ml = max(sizes)
400 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
400 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
401 else:
401 else:
402 formats.append(['%s' for x in l])
402 formats.append(['%s' for x in l])
403 pieces.append(l)
403 pieces.append(l)
404
404
405 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
405 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
406 fm.startitem()
406 fm.startitem()
407 fm.write(fields, "".join(f), *p)
407 fm.write(fields, "".join(f), *p)
408 if l[0].skip:
408 if l[0].skip:
409 fmt = "* %s"
409 fmt = "* %s"
410 else:
410 else:
411 fmt = ": %s"
411 fmt = ": %s"
412 fm.write('line', fmt, l[1])
412 fm.write('line', fmt, l[1])
413
413
414 if not lines[-1][1].endswith('\n'):
414 if not lines[-1][1].endswith('\n'):
415 fm.plain('\n')
415 fm.plain('\n')
416 fm.end()
416 fm.end()
417
417
418 rootfm.end()
418 rootfm.end()
419
419
420 @command('archive',
420 @command('archive',
421 [('', 'no-decode', None, _('do not pass files through decoders')),
421 [('', 'no-decode', None, _('do not pass files through decoders')),
422 ('p', 'prefix', '', _('directory prefix for files in archive'),
422 ('p', 'prefix', '', _('directory prefix for files in archive'),
423 _('PREFIX')),
423 _('PREFIX')),
424 ('r', 'rev', '', _('revision to distribute'), _('REV')),
424 ('r', 'rev', '', _('revision to distribute'), _('REV')),
425 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
425 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
426 ] + subrepoopts + walkopts,
426 ] + subrepoopts + walkopts,
427 _('[OPTION]... DEST'))
427 _('[OPTION]... DEST'))
428 def archive(ui, repo, dest, **opts):
428 def archive(ui, repo, dest, **opts):
429 '''create an unversioned archive of a repository revision
429 '''create an unversioned archive of a repository revision
430
430
431 By default, the revision used is the parent of the working
431 By default, the revision used is the parent of the working
432 directory; use -r/--rev to specify a different revision.
432 directory; use -r/--rev to specify a different revision.
433
433
434 The archive type is automatically detected based on file
434 The archive type is automatically detected based on file
435 extension (to override, use -t/--type).
435 extension (to override, use -t/--type).
436
436
437 .. container:: verbose
437 .. container:: verbose
438
438
439 Examples:
439 Examples:
440
440
441 - create a zip file containing the 1.0 release::
441 - create a zip file containing the 1.0 release::
442
442
443 hg archive -r 1.0 project-1.0.zip
443 hg archive -r 1.0 project-1.0.zip
444
444
445 - create a tarball excluding .hg files::
445 - create a tarball excluding .hg files::
446
446
447 hg archive project.tar.gz -X ".hg*"
447 hg archive project.tar.gz -X ".hg*"
448
448
449 Valid types are:
449 Valid types are:
450
450
451 :``files``: a directory full of files (default)
451 :``files``: a directory full of files (default)
452 :``tar``: tar archive, uncompressed
452 :``tar``: tar archive, uncompressed
453 :``tbz2``: tar archive, compressed using bzip2
453 :``tbz2``: tar archive, compressed using bzip2
454 :``tgz``: tar archive, compressed using gzip
454 :``tgz``: tar archive, compressed using gzip
455 :``uzip``: zip archive, uncompressed
455 :``uzip``: zip archive, uncompressed
456 :``zip``: zip archive, compressed using deflate
456 :``zip``: zip archive, compressed using deflate
457
457
458 The exact name of the destination archive or directory is given
458 The exact name of the destination archive or directory is given
459 using a format string; see :hg:`help export` for details.
459 using a format string; see :hg:`help export` for details.
460
460
461 Each member added to an archive file has a directory prefix
461 Each member added to an archive file has a directory prefix
462 prepended. Use -p/--prefix to specify a format string for the
462 prepended. Use -p/--prefix to specify a format string for the
463 prefix. The default is the basename of the archive, with suffixes
463 prefix. The default is the basename of the archive, with suffixes
464 removed.
464 removed.
465
465
466 Returns 0 on success.
466 Returns 0 on success.
467 '''
467 '''
468
468
469 opts = pycompat.byteskwargs(opts)
469 opts = pycompat.byteskwargs(opts)
470 rev = opts.get('rev')
470 rev = opts.get('rev')
471 if rev:
471 if rev:
472 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
472 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
473 ctx = scmutil.revsingle(repo, rev)
473 ctx = scmutil.revsingle(repo, rev)
474 if not ctx:
474 if not ctx:
475 raise error.Abort(_('no working directory: please specify a revision'))
475 raise error.Abort(_('no working directory: please specify a revision'))
476 node = ctx.node()
476 node = ctx.node()
477 dest = cmdutil.makefilename(repo, dest, node)
477 dest = cmdutil.makefilename(repo, dest, node)
478 if os.path.realpath(dest) == repo.root:
478 if os.path.realpath(dest) == repo.root:
479 raise error.Abort(_('repository root cannot be destination'))
479 raise error.Abort(_('repository root cannot be destination'))
480
480
481 kind = opts.get('type') or archival.guesskind(dest) or 'files'
481 kind = opts.get('type') or archival.guesskind(dest) or 'files'
482 prefix = opts.get('prefix')
482 prefix = opts.get('prefix')
483
483
484 if dest == '-':
484 if dest == '-':
485 if kind == 'files':
485 if kind == 'files':
486 raise error.Abort(_('cannot archive plain files to stdout'))
486 raise error.Abort(_('cannot archive plain files to stdout'))
487 dest = cmdutil.makefileobj(repo, dest)
487 dest = cmdutil.makefileobj(repo, dest)
488 if not prefix:
488 if not prefix:
489 prefix = os.path.basename(repo.root) + '-%h'
489 prefix = os.path.basename(repo.root) + '-%h'
490
490
491 prefix = cmdutil.makefilename(repo, prefix, node)
491 prefix = cmdutil.makefilename(repo, prefix, node)
492 match = scmutil.match(ctx, [], opts)
492 match = scmutil.match(ctx, [], opts)
493 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
493 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
494 match, prefix, subrepos=opts.get('subrepos'))
494 match, prefix, subrepos=opts.get('subrepos'))
495
495
496 @command('backout',
496 @command('backout',
497 [('', 'merge', None, _('merge with old dirstate parent after backout')),
497 [('', 'merge', None, _('merge with old dirstate parent after backout')),
498 ('', 'commit', None,
498 ('', 'commit', None,
499 _('commit if no conflicts were encountered (DEPRECATED)')),
499 _('commit if no conflicts were encountered (DEPRECATED)')),
500 ('', 'no-commit', None, _('do not commit')),
500 ('', 'no-commit', None, _('do not commit')),
501 ('', 'parent', '',
501 ('', 'parent', '',
502 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
502 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
503 ('r', 'rev', '', _('revision to backout'), _('REV')),
503 ('r', 'rev', '', _('revision to backout'), _('REV')),
504 ('e', 'edit', False, _('invoke editor on commit messages')),
504 ('e', 'edit', False, _('invoke editor on commit messages')),
505 ] + mergetoolopts + walkopts + commitopts + commitopts2,
505 ] + mergetoolopts + walkopts + commitopts + commitopts2,
506 _('[OPTION]... [-r] REV'))
506 _('[OPTION]... [-r] REV'))
507 def backout(ui, repo, node=None, rev=None, **opts):
507 def backout(ui, repo, node=None, rev=None, **opts):
508 '''reverse effect of earlier changeset
508 '''reverse effect of earlier changeset
509
509
510 Prepare a new changeset with the effect of REV undone in the
510 Prepare a new changeset with the effect of REV undone in the
511 current working directory. If no conflicts were encountered,
511 current working directory. If no conflicts were encountered,
512 it will be committed immediately.
512 it will be committed immediately.
513
513
514 If REV is the parent of the working directory, then this new changeset
514 If REV is the parent of the working directory, then this new changeset
515 is committed automatically (unless --no-commit is specified).
515 is committed automatically (unless --no-commit is specified).
516
516
517 .. note::
517 .. note::
518
518
519 :hg:`backout` cannot be used to fix either an unwanted or
519 :hg:`backout` cannot be used to fix either an unwanted or
520 incorrect merge.
520 incorrect merge.
521
521
522 .. container:: verbose
522 .. container:: verbose
523
523
524 Examples:
524 Examples:
525
525
526 - Reverse the effect of the parent of the working directory.
526 - Reverse the effect of the parent of the working directory.
527 This backout will be committed immediately::
527 This backout will be committed immediately::
528
528
529 hg backout -r .
529 hg backout -r .
530
530
531 - Reverse the effect of previous bad revision 23::
531 - Reverse the effect of previous bad revision 23::
532
532
533 hg backout -r 23
533 hg backout -r 23
534
534
535 - Reverse the effect of previous bad revision 23 and
535 - Reverse the effect of previous bad revision 23 and
536 leave changes uncommitted::
536 leave changes uncommitted::
537
537
538 hg backout -r 23 --no-commit
538 hg backout -r 23 --no-commit
539 hg commit -m "Backout revision 23"
539 hg commit -m "Backout revision 23"
540
540
541 By default, the pending changeset will have one parent,
541 By default, the pending changeset will have one parent,
542 maintaining a linear history. With --merge, the pending
542 maintaining a linear history. With --merge, the pending
543 changeset will instead have two parents: the old parent of the
543 changeset will instead have two parents: the old parent of the
544 working directory and a new child of REV that simply undoes REV.
544 working directory and a new child of REV that simply undoes REV.
545
545
546 Before version 1.7, the behavior without --merge was equivalent
546 Before version 1.7, the behavior without --merge was equivalent
547 to specifying --merge followed by :hg:`update --clean .` to
547 to specifying --merge followed by :hg:`update --clean .` to
548 cancel the merge and leave the child of REV as a head to be
548 cancel the merge and leave the child of REV as a head to be
549 merged separately.
549 merged separately.
550
550
551 See :hg:`help dates` for a list of formats valid for -d/--date.
551 See :hg:`help dates` for a list of formats valid for -d/--date.
552
552
553 See :hg:`help revert` for a way to restore files to the state
553 See :hg:`help revert` for a way to restore files to the state
554 of another revision.
554 of another revision.
555
555
556 Returns 0 on success, 1 if nothing to backout or there are unresolved
556 Returns 0 on success, 1 if nothing to backout or there are unresolved
557 files.
557 files.
558 '''
558 '''
559 wlock = lock = None
559 wlock = lock = None
560 try:
560 try:
561 wlock = repo.wlock()
561 wlock = repo.wlock()
562 lock = repo.lock()
562 lock = repo.lock()
563 return _dobackout(ui, repo, node, rev, **opts)
563 return _dobackout(ui, repo, node, rev, **opts)
564 finally:
564 finally:
565 release(lock, wlock)
565 release(lock, wlock)
566
566
567 def _dobackout(ui, repo, node=None, rev=None, **opts):
567 def _dobackout(ui, repo, node=None, rev=None, **opts):
568 opts = pycompat.byteskwargs(opts)
568 opts = pycompat.byteskwargs(opts)
569 if opts.get('commit') and opts.get('no_commit'):
569 if opts.get('commit') and opts.get('no_commit'):
570 raise error.Abort(_("cannot use --commit with --no-commit"))
570 raise error.Abort(_("cannot use --commit with --no-commit"))
571 if opts.get('merge') and opts.get('no_commit'):
571 if opts.get('merge') and opts.get('no_commit'):
572 raise error.Abort(_("cannot use --merge with --no-commit"))
572 raise error.Abort(_("cannot use --merge with --no-commit"))
573
573
574 if rev and node:
574 if rev and node:
575 raise error.Abort(_("please specify just one revision"))
575 raise error.Abort(_("please specify just one revision"))
576
576
577 if not rev:
577 if not rev:
578 rev = node
578 rev = node
579
579
580 if not rev:
580 if not rev:
581 raise error.Abort(_("please specify a revision to backout"))
581 raise error.Abort(_("please specify a revision to backout"))
582
582
583 date = opts.get('date')
583 date = opts.get('date')
584 if date:
584 if date:
585 opts['date'] = util.parsedate(date)
585 opts['date'] = util.parsedate(date)
586
586
587 cmdutil.checkunfinished(repo)
587 cmdutil.checkunfinished(repo)
588 cmdutil.bailifchanged(repo)
588 cmdutil.bailifchanged(repo)
589 node = scmutil.revsingle(repo, rev).node()
589 node = scmutil.revsingle(repo, rev).node()
590
590
591 op1, op2 = repo.dirstate.parents()
591 op1, op2 = repo.dirstate.parents()
592 if not repo.changelog.isancestor(node, op1):
592 if not repo.changelog.isancestor(node, op1):
593 raise error.Abort(_('cannot backout change that is not an ancestor'))
593 raise error.Abort(_('cannot backout change that is not an ancestor'))
594
594
595 p1, p2 = repo.changelog.parents(node)
595 p1, p2 = repo.changelog.parents(node)
596 if p1 == nullid:
596 if p1 == nullid:
597 raise error.Abort(_('cannot backout a change with no parents'))
597 raise error.Abort(_('cannot backout a change with no parents'))
598 if p2 != nullid:
598 if p2 != nullid:
599 if not opts.get('parent'):
599 if not opts.get('parent'):
600 raise error.Abort(_('cannot backout a merge changeset'))
600 raise error.Abort(_('cannot backout a merge changeset'))
601 p = repo.lookup(opts['parent'])
601 p = repo.lookup(opts['parent'])
602 if p not in (p1, p2):
602 if p not in (p1, p2):
603 raise error.Abort(_('%s is not a parent of %s') %
603 raise error.Abort(_('%s is not a parent of %s') %
604 (short(p), short(node)))
604 (short(p), short(node)))
605 parent = p
605 parent = p
606 else:
606 else:
607 if opts.get('parent'):
607 if opts.get('parent'):
608 raise error.Abort(_('cannot use --parent on non-merge changeset'))
608 raise error.Abort(_('cannot use --parent on non-merge changeset'))
609 parent = p1
609 parent = p1
610
610
611 # the backout should appear on the same branch
611 # the backout should appear on the same branch
612 branch = repo.dirstate.branch()
612 branch = repo.dirstate.branch()
613 bheads = repo.branchheads(branch)
613 bheads = repo.branchheads(branch)
614 rctx = scmutil.revsingle(repo, hex(parent))
614 rctx = scmutil.revsingle(repo, hex(parent))
615 if not opts.get('merge') and op1 != node:
615 if not opts.get('merge') and op1 != node:
616 dsguard = dirstateguard.dirstateguard(repo, 'backout')
616 dsguard = dirstateguard.dirstateguard(repo, 'backout')
617 try:
617 try:
618 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
618 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
619 'backout')
619 'backout')
620 stats = mergemod.update(repo, parent, True, True, node, False)
620 stats = mergemod.update(repo, parent, True, True, node, False)
621 repo.setparents(op1, op2)
621 repo.setparents(op1, op2)
622 dsguard.close()
622 dsguard.close()
623 hg._showstats(repo, stats)
623 hg._showstats(repo, stats)
624 if stats[3]:
624 if stats[3]:
625 repo.ui.status(_("use 'hg resolve' to retry unresolved "
625 repo.ui.status(_("use 'hg resolve' to retry unresolved "
626 "file merges\n"))
626 "file merges\n"))
627 return 1
627 return 1
628 finally:
628 finally:
629 ui.setconfig('ui', 'forcemerge', '', '')
629 ui.setconfig('ui', 'forcemerge', '', '')
630 lockmod.release(dsguard)
630 lockmod.release(dsguard)
631 else:
631 else:
632 hg.clean(repo, node, show_stats=False)
632 hg.clean(repo, node, show_stats=False)
633 repo.dirstate.setbranch(branch)
633 repo.dirstate.setbranch(branch)
634 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
634 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
635
635
636 if opts.get('no_commit'):
636 if opts.get('no_commit'):
637 msg = _("changeset %s backed out, "
637 msg = _("changeset %s backed out, "
638 "don't forget to commit.\n")
638 "don't forget to commit.\n")
639 ui.status(msg % short(node))
639 ui.status(msg % short(node))
640 return 0
640 return 0
641
641
642 def commitfunc(ui, repo, message, match, opts):
642 def commitfunc(ui, repo, message, match, opts):
643 editform = 'backout'
643 editform = 'backout'
644 e = cmdutil.getcommiteditor(editform=editform,
644 e = cmdutil.getcommiteditor(editform=editform,
645 **pycompat.strkwargs(opts))
645 **pycompat.strkwargs(opts))
646 if not message:
646 if not message:
647 # we don't translate commit messages
647 # we don't translate commit messages
648 message = "Backed out changeset %s" % short(node)
648 message = "Backed out changeset %s" % short(node)
649 e = cmdutil.getcommiteditor(edit=True, editform=editform)
649 e = cmdutil.getcommiteditor(edit=True, editform=editform)
650 return repo.commit(message, opts.get('user'), opts.get('date'),
650 return repo.commit(message, opts.get('user'), opts.get('date'),
651 match, editor=e)
651 match, editor=e)
652 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
652 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
653 if not newnode:
653 if not newnode:
654 ui.status(_("nothing changed\n"))
654 ui.status(_("nothing changed\n"))
655 return 1
655 return 1
656 cmdutil.commitstatus(repo, newnode, branch, bheads)
656 cmdutil.commitstatus(repo, newnode, branch, bheads)
657
657
658 def nice(node):
658 def nice(node):
659 return '%d:%s' % (repo.changelog.rev(node), short(node))
659 return '%d:%s' % (repo.changelog.rev(node), short(node))
660 ui.status(_('changeset %s backs out changeset %s\n') %
660 ui.status(_('changeset %s backs out changeset %s\n') %
661 (nice(repo.changelog.tip()), nice(node)))
661 (nice(repo.changelog.tip()), nice(node)))
662 if opts.get('merge') and op1 != node:
662 if opts.get('merge') and op1 != node:
663 hg.clean(repo, op1, show_stats=False)
663 hg.clean(repo, op1, show_stats=False)
664 ui.status(_('merging with changeset %s\n')
664 ui.status(_('merging with changeset %s\n')
665 % nice(repo.changelog.tip()))
665 % nice(repo.changelog.tip()))
666 try:
666 try:
667 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
667 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
668 'backout')
668 'backout')
669 return hg.merge(repo, hex(repo.changelog.tip()))
669 return hg.merge(repo, hex(repo.changelog.tip()))
670 finally:
670 finally:
671 ui.setconfig('ui', 'forcemerge', '', '')
671 ui.setconfig('ui', 'forcemerge', '', '')
672 return 0
672 return 0
673
673
674 @command('bisect',
674 @command('bisect',
675 [('r', 'reset', False, _('reset bisect state')),
675 [('r', 'reset', False, _('reset bisect state')),
676 ('g', 'good', False, _('mark changeset good')),
676 ('g', 'good', False, _('mark changeset good')),
677 ('b', 'bad', False, _('mark changeset bad')),
677 ('b', 'bad', False, _('mark changeset bad')),
678 ('s', 'skip', False, _('skip testing changeset')),
678 ('s', 'skip', False, _('skip testing changeset')),
679 ('e', 'extend', False, _('extend the bisect range')),
679 ('e', 'extend', False, _('extend the bisect range')),
680 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
680 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
681 ('U', 'noupdate', False, _('do not update to target'))],
681 ('U', 'noupdate', False, _('do not update to target'))],
682 _("[-gbsr] [-U] [-c CMD] [REV]"))
682 _("[-gbsr] [-U] [-c CMD] [REV]"))
683 def bisect(ui, repo, rev=None, extra=None, command=None,
683 def bisect(ui, repo, rev=None, extra=None, command=None,
684 reset=None, good=None, bad=None, skip=None, extend=None,
684 reset=None, good=None, bad=None, skip=None, extend=None,
685 noupdate=None):
685 noupdate=None):
686 """subdivision search of changesets
686 """subdivision search of changesets
687
687
688 This command helps to find changesets which introduce problems. To
688 This command helps to find changesets which introduce problems. To
689 use, mark the earliest changeset you know exhibits the problem as
689 use, mark the earliest changeset you know exhibits the problem as
690 bad, then mark the latest changeset which is free from the problem
690 bad, then mark the latest changeset which is free from the problem
691 as good. Bisect will update your working directory to a revision
691 as good. Bisect will update your working directory to a revision
692 for testing (unless the -U/--noupdate option is specified). Once
692 for testing (unless the -U/--noupdate option is specified). Once
693 you have performed tests, mark the working directory as good or
693 you have performed tests, mark the working directory as good or
694 bad, and bisect will either update to another candidate changeset
694 bad, and bisect will either update to another candidate changeset
695 or announce that it has found the bad revision.
695 or announce that it has found the bad revision.
696
696
697 As a shortcut, you can also use the revision argument to mark a
697 As a shortcut, you can also use the revision argument to mark a
698 revision as good or bad without checking it out first.
698 revision as good or bad without checking it out first.
699
699
700 If you supply a command, it will be used for automatic bisection.
700 If you supply a command, it will be used for automatic bisection.
701 The environment variable HG_NODE will contain the ID of the
701 The environment variable HG_NODE will contain the ID of the
702 changeset being tested. The exit status of the command will be
702 changeset being tested. The exit status of the command will be
703 used to mark revisions as good or bad: status 0 means good, 125
703 used to mark revisions as good or bad: status 0 means good, 125
704 means to skip the revision, 127 (command not found) will abort the
704 means to skip the revision, 127 (command not found) will abort the
705 bisection, and any other non-zero exit status means the revision
705 bisection, and any other non-zero exit status means the revision
706 is bad.
706 is bad.
707
707
708 .. container:: verbose
708 .. container:: verbose
709
709
710 Some examples:
710 Some examples:
711
711
712 - start a bisection with known bad revision 34, and good revision 12::
712 - start a bisection with known bad revision 34, and good revision 12::
713
713
714 hg bisect --bad 34
714 hg bisect --bad 34
715 hg bisect --good 12
715 hg bisect --good 12
716
716
717 - advance the current bisection by marking current revision as good or
717 - advance the current bisection by marking current revision as good or
718 bad::
718 bad::
719
719
720 hg bisect --good
720 hg bisect --good
721 hg bisect --bad
721 hg bisect --bad
722
722
723 - mark the current revision, or a known revision, to be skipped (e.g. if
723 - mark the current revision, or a known revision, to be skipped (e.g. if
724 that revision is not usable because of another issue)::
724 that revision is not usable because of another issue)::
725
725
726 hg bisect --skip
726 hg bisect --skip
727 hg bisect --skip 23
727 hg bisect --skip 23
728
728
729 - skip all revisions that do not touch directories ``foo`` or ``bar``::
729 - skip all revisions that do not touch directories ``foo`` or ``bar``::
730
730
731 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
731 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
732
732
733 - forget the current bisection::
733 - forget the current bisection::
734
734
735 hg bisect --reset
735 hg bisect --reset
736
736
737 - use 'make && make tests' to automatically find the first broken
737 - use 'make && make tests' to automatically find the first broken
738 revision::
738 revision::
739
739
740 hg bisect --reset
740 hg bisect --reset
741 hg bisect --bad 34
741 hg bisect --bad 34
742 hg bisect --good 12
742 hg bisect --good 12
743 hg bisect --command "make && make tests"
743 hg bisect --command "make && make tests"
744
744
745 - see all changesets whose states are already known in the current
745 - see all changesets whose states are already known in the current
746 bisection::
746 bisection::
747
747
748 hg log -r "bisect(pruned)"
748 hg log -r "bisect(pruned)"
749
749
750 - see the changeset currently being bisected (especially useful
750 - see the changeset currently being bisected (especially useful
751 if running with -U/--noupdate)::
751 if running with -U/--noupdate)::
752
752
753 hg log -r "bisect(current)"
753 hg log -r "bisect(current)"
754
754
755 - see all changesets that took part in the current bisection::
755 - see all changesets that took part in the current bisection::
756
756
757 hg log -r "bisect(range)"
757 hg log -r "bisect(range)"
758
758
759 - you can even get a nice graph::
759 - you can even get a nice graph::
760
760
761 hg log --graph -r "bisect(range)"
761 hg log --graph -r "bisect(range)"
762
762
763 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
763 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
764
764
765 Returns 0 on success.
765 Returns 0 on success.
766 """
766 """
767 # backward compatibility
767 # backward compatibility
768 if rev in "good bad reset init".split():
768 if rev in "good bad reset init".split():
769 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
769 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
770 cmd, rev, extra = rev, extra, None
770 cmd, rev, extra = rev, extra, None
771 if cmd == "good":
771 if cmd == "good":
772 good = True
772 good = True
773 elif cmd == "bad":
773 elif cmd == "bad":
774 bad = True
774 bad = True
775 else:
775 else:
776 reset = True
776 reset = True
777 elif extra:
777 elif extra:
778 raise error.Abort(_('incompatible arguments'))
778 raise error.Abort(_('incompatible arguments'))
779
779
780 incompatibles = {
780 incompatibles = {
781 '--bad': bad,
781 '--bad': bad,
782 '--command': bool(command),
782 '--command': bool(command),
783 '--extend': extend,
783 '--extend': extend,
784 '--good': good,
784 '--good': good,
785 '--reset': reset,
785 '--reset': reset,
786 '--skip': skip,
786 '--skip': skip,
787 }
787 }
788
788
789 enabled = [x for x in incompatibles if incompatibles[x]]
789 enabled = [x for x in incompatibles if incompatibles[x]]
790
790
791 if len(enabled) > 1:
791 if len(enabled) > 1:
792 raise error.Abort(_('%s and %s are incompatible') %
792 raise error.Abort(_('%s and %s are incompatible') %
793 tuple(sorted(enabled)[0:2]))
793 tuple(sorted(enabled)[0:2]))
794
794
795 if reset:
795 if reset:
796 hbisect.resetstate(repo)
796 hbisect.resetstate(repo)
797 return
797 return
798
798
799 state = hbisect.load_state(repo)
799 state = hbisect.load_state(repo)
800
800
801 # update state
801 # update state
802 if good or bad or skip:
802 if good or bad or skip:
803 if rev:
803 if rev:
804 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
804 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
805 else:
805 else:
806 nodes = [repo.lookup('.')]
806 nodes = [repo.lookup('.')]
807 if good:
807 if good:
808 state['good'] += nodes
808 state['good'] += nodes
809 elif bad:
809 elif bad:
810 state['bad'] += nodes
810 state['bad'] += nodes
811 elif skip:
811 elif skip:
812 state['skip'] += nodes
812 state['skip'] += nodes
813 hbisect.save_state(repo, state)
813 hbisect.save_state(repo, state)
814 if not (state['good'] and state['bad']):
814 if not (state['good'] and state['bad']):
815 return
815 return
816
816
817 def mayupdate(repo, node, show_stats=True):
817 def mayupdate(repo, node, show_stats=True):
818 """common used update sequence"""
818 """common used update sequence"""
819 if noupdate:
819 if noupdate:
820 return
820 return
821 cmdutil.checkunfinished(repo)
821 cmdutil.checkunfinished(repo)
822 cmdutil.bailifchanged(repo)
822 cmdutil.bailifchanged(repo)
823 return hg.clean(repo, node, show_stats=show_stats)
823 return hg.clean(repo, node, show_stats=show_stats)
824
824
825 displayer = cmdutil.show_changeset(ui, repo, {})
825 displayer = cmdutil.show_changeset(ui, repo, {})
826
826
827 if command:
827 if command:
828 changesets = 1
828 changesets = 1
829 if noupdate:
829 if noupdate:
830 try:
830 try:
831 node = state['current'][0]
831 node = state['current'][0]
832 except LookupError:
832 except LookupError:
833 raise error.Abort(_('current bisect revision is unknown - '
833 raise error.Abort(_('current bisect revision is unknown - '
834 'start a new bisect to fix'))
834 'start a new bisect to fix'))
835 else:
835 else:
836 node, p2 = repo.dirstate.parents()
836 node, p2 = repo.dirstate.parents()
837 if p2 != nullid:
837 if p2 != nullid:
838 raise error.Abort(_('current bisect revision is a merge'))
838 raise error.Abort(_('current bisect revision is a merge'))
839 if rev:
839 if rev:
840 node = repo[scmutil.revsingle(repo, rev, node)].node()
840 node = repo[scmutil.revsingle(repo, rev, node)].node()
841 try:
841 try:
842 while changesets:
842 while changesets:
843 # update state
843 # update state
844 state['current'] = [node]
844 state['current'] = [node]
845 hbisect.save_state(repo, state)
845 hbisect.save_state(repo, state)
846 status = ui.system(command, environ={'HG_NODE': hex(node)},
846 status = ui.system(command, environ={'HG_NODE': hex(node)},
847 blockedtag='bisect_check')
847 blockedtag='bisect_check')
848 if status == 125:
848 if status == 125:
849 transition = "skip"
849 transition = "skip"
850 elif status == 0:
850 elif status == 0:
851 transition = "good"
851 transition = "good"
852 # status < 0 means process was killed
852 # status < 0 means process was killed
853 elif status == 127:
853 elif status == 127:
854 raise error.Abort(_("failed to execute %s") % command)
854 raise error.Abort(_("failed to execute %s") % command)
855 elif status < 0:
855 elif status < 0:
856 raise error.Abort(_("%s killed") % command)
856 raise error.Abort(_("%s killed") % command)
857 else:
857 else:
858 transition = "bad"
858 transition = "bad"
859 state[transition].append(node)
859 state[transition].append(node)
860 ctx = repo[node]
860 ctx = repo[node]
861 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
861 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
862 hbisect.checkstate(state)
862 hbisect.checkstate(state)
863 # bisect
863 # bisect
864 nodes, changesets, bgood = hbisect.bisect(repo, state)
864 nodes, changesets, bgood = hbisect.bisect(repo, state)
865 # update to next check
865 # update to next check
866 node = nodes[0]
866 node = nodes[0]
867 mayupdate(repo, node, show_stats=False)
867 mayupdate(repo, node, show_stats=False)
868 finally:
868 finally:
869 state['current'] = [node]
869 state['current'] = [node]
870 hbisect.save_state(repo, state)
870 hbisect.save_state(repo, state)
871 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
871 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
872 return
872 return
873
873
874 hbisect.checkstate(state)
874 hbisect.checkstate(state)
875
875
876 # actually bisect
876 # actually bisect
877 nodes, changesets, good = hbisect.bisect(repo, state)
877 nodes, changesets, good = hbisect.bisect(repo, state)
878 if extend:
878 if extend:
879 if not changesets:
879 if not changesets:
880 extendnode = hbisect.extendrange(repo, state, nodes, good)
880 extendnode = hbisect.extendrange(repo, state, nodes, good)
881 if extendnode is not None:
881 if extendnode is not None:
882 ui.write(_("Extending search to changeset %d:%s\n")
882 ui.write(_("Extending search to changeset %d:%s\n")
883 % (extendnode.rev(), extendnode))
883 % (extendnode.rev(), extendnode))
884 state['current'] = [extendnode.node()]
884 state['current'] = [extendnode.node()]
885 hbisect.save_state(repo, state)
885 hbisect.save_state(repo, state)
886 return mayupdate(repo, extendnode.node())
886 return mayupdate(repo, extendnode.node())
887 raise error.Abort(_("nothing to extend"))
887 raise error.Abort(_("nothing to extend"))
888
888
889 if changesets == 0:
889 if changesets == 0:
890 hbisect.printresult(ui, repo, state, displayer, nodes, good)
890 hbisect.printresult(ui, repo, state, displayer, nodes, good)
891 else:
891 else:
892 assert len(nodes) == 1 # only a single node can be tested next
892 assert len(nodes) == 1 # only a single node can be tested next
893 node = nodes[0]
893 node = nodes[0]
894 # compute the approximate number of remaining tests
894 # compute the approximate number of remaining tests
895 tests, size = 0, 2
895 tests, size = 0, 2
896 while size <= changesets:
896 while size <= changesets:
897 tests, size = tests + 1, size * 2
897 tests, size = tests + 1, size * 2
898 rev = repo.changelog.rev(node)
898 rev = repo.changelog.rev(node)
899 ui.write(_("Testing changeset %d:%s "
899 ui.write(_("Testing changeset %d:%s "
900 "(%d changesets remaining, ~%d tests)\n")
900 "(%d changesets remaining, ~%d tests)\n")
901 % (rev, short(node), changesets, tests))
901 % (rev, short(node), changesets, tests))
902 state['current'] = [node]
902 state['current'] = [node]
903 hbisect.save_state(repo, state)
903 hbisect.save_state(repo, state)
904 return mayupdate(repo, node)
904 return mayupdate(repo, node)
905
905
906 @command('bookmarks|bookmark',
906 @command('bookmarks|bookmark',
907 [('f', 'force', False, _('force')),
907 [('f', 'force', False, _('force')),
908 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
908 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
909 ('d', 'delete', False, _('delete a given bookmark')),
909 ('d', 'delete', False, _('delete a given bookmark')),
910 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
910 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
911 ('i', 'inactive', False, _('mark a bookmark inactive')),
911 ('i', 'inactive', False, _('mark a bookmark inactive')),
912 ] + formatteropts,
912 ] + formatteropts,
913 _('hg bookmarks [OPTIONS]... [NAME]...'))
913 _('hg bookmarks [OPTIONS]... [NAME]...'))
914 def bookmark(ui, repo, *names, **opts):
914 def bookmark(ui, repo, *names, **opts):
915 '''create a new bookmark or list existing bookmarks
915 '''create a new bookmark or list existing bookmarks
916
916
917 Bookmarks are labels on changesets to help track lines of development.
917 Bookmarks are labels on changesets to help track lines of development.
918 Bookmarks are unversioned and can be moved, renamed and deleted.
918 Bookmarks are unversioned and can be moved, renamed and deleted.
919 Deleting or moving a bookmark has no effect on the associated changesets.
919 Deleting or moving a bookmark has no effect on the associated changesets.
920
920
921 Creating or updating to a bookmark causes it to be marked as 'active'.
921 Creating or updating to a bookmark causes it to be marked as 'active'.
922 The active bookmark is indicated with a '*'.
922 The active bookmark is indicated with a '*'.
923 When a commit is made, the active bookmark will advance to the new commit.
923 When a commit is made, the active bookmark will advance to the new commit.
924 A plain :hg:`update` will also advance an active bookmark, if possible.
924 A plain :hg:`update` will also advance an active bookmark, if possible.
925 Updating away from a bookmark will cause it to be deactivated.
925 Updating away from a bookmark will cause it to be deactivated.
926
926
927 Bookmarks can be pushed and pulled between repositories (see
927 Bookmarks can be pushed and pulled between repositories (see
928 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
928 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
929 diverged, a new 'divergent bookmark' of the form 'name@path' will
929 diverged, a new 'divergent bookmark' of the form 'name@path' will
930 be created. Using :hg:`merge` will resolve the divergence.
930 be created. Using :hg:`merge` will resolve the divergence.
931
931
932 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
932 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
933 the active bookmark's name.
933 the active bookmark's name.
934
934
935 A bookmark named '@' has the special property that :hg:`clone` will
935 A bookmark named '@' has the special property that :hg:`clone` will
936 check it out by default if it exists.
936 check it out by default if it exists.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Examples:
940 Examples:
941
941
942 - create an active bookmark for a new line of development::
942 - create an active bookmark for a new line of development::
943
943
944 hg book new-feature
944 hg book new-feature
945
945
946 - create an inactive bookmark as a place marker::
946 - create an inactive bookmark as a place marker::
947
947
948 hg book -i reviewed
948 hg book -i reviewed
949
949
950 - create an inactive bookmark on another changeset::
950 - create an inactive bookmark on another changeset::
951
951
952 hg book -r .^ tested
952 hg book -r .^ tested
953
953
954 - rename bookmark turkey to dinner::
954 - rename bookmark turkey to dinner::
955
955
956 hg book -m turkey dinner
956 hg book -m turkey dinner
957
957
958 - move the '@' bookmark from another branch::
958 - move the '@' bookmark from another branch::
959
959
960 hg book -f @
960 hg book -f @
961 '''
961 '''
962 force = opts.get(r'force')
962 force = opts.get(r'force')
963 rev = opts.get(r'rev')
963 rev = opts.get(r'rev')
964 delete = opts.get(r'delete')
964 delete = opts.get(r'delete')
965 rename = opts.get(r'rename')
965 rename = opts.get(r'rename')
966 inactive = opts.get(r'inactive')
966 inactive = opts.get(r'inactive')
967
967
968 if delete and rename:
968 if delete and rename:
969 raise error.Abort(_("--delete and --rename are incompatible"))
969 raise error.Abort(_("--delete and --rename are incompatible"))
970 if delete and rev:
970 if delete and rev:
971 raise error.Abort(_("--rev is incompatible with --delete"))
971 raise error.Abort(_("--rev is incompatible with --delete"))
972 if rename and rev:
972 if rename and rev:
973 raise error.Abort(_("--rev is incompatible with --rename"))
973 raise error.Abort(_("--rev is incompatible with --rename"))
974 if not names and (delete or rev):
974 if not names and (delete or rev):
975 raise error.Abort(_("bookmark name required"))
975 raise error.Abort(_("bookmark name required"))
976
976
977 if delete or rename or names or inactive:
977 if delete or rename or names or inactive:
978 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
978 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
979 if delete:
979 if delete:
980 names = pycompat.maplist(repo._bookmarks.expandname, names)
980 names = pycompat.maplist(repo._bookmarks.expandname, names)
981 bookmarks.delete(repo, tr, names)
981 bookmarks.delete(repo, tr, names)
982 elif rename:
982 elif rename:
983 if not names:
983 if not names:
984 raise error.Abort(_("new bookmark name required"))
984 raise error.Abort(_("new bookmark name required"))
985 elif len(names) > 1:
985 elif len(names) > 1:
986 raise error.Abort(_("only one new bookmark name allowed"))
986 raise error.Abort(_("only one new bookmark name allowed"))
987 rename = repo._bookmarks.expandname(rename)
987 rename = repo._bookmarks.expandname(rename)
988 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
988 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
989 elif names:
989 elif names:
990 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
990 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
991 elif inactive:
991 elif inactive:
992 if len(repo._bookmarks) == 0:
992 if len(repo._bookmarks) == 0:
993 ui.status(_("no bookmarks set\n"))
993 ui.status(_("no bookmarks set\n"))
994 elif not repo._activebookmark:
994 elif not repo._activebookmark:
995 ui.status(_("no active bookmark\n"))
995 ui.status(_("no active bookmark\n"))
996 else:
996 else:
997 bookmarks.deactivate(repo)
997 bookmarks.deactivate(repo)
998 else: # show bookmarks
998 else: # show bookmarks
999 bookmarks.printbookmarks(ui, repo, **opts)
999 bookmarks.printbookmarks(ui, repo, **opts)
1000
1000
1001 @command('branch',
1001 @command('branch',
1002 [('f', 'force', None,
1002 [('f', 'force', None,
1003 _('set branch name even if it shadows an existing branch')),
1003 _('set branch name even if it shadows an existing branch')),
1004 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1004 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1005 _('[-fC] [NAME]'))
1005 _('[-fC] [NAME]'))
1006 def branch(ui, repo, label=None, **opts):
1006 def branch(ui, repo, label=None, **opts):
1007 """set or show the current branch name
1007 """set or show the current branch name
1008
1008
1009 .. note::
1009 .. note::
1010
1010
1011 Branch names are permanent and global. Use :hg:`bookmark` to create a
1011 Branch names are permanent and global. Use :hg:`bookmark` to create a
1012 light-weight bookmark instead. See :hg:`help glossary` for more
1012 light-weight bookmark instead. See :hg:`help glossary` for more
1013 information about named branches and bookmarks.
1013 information about named branches and bookmarks.
1014
1014
1015 With no argument, show the current branch name. With one argument,
1015 With no argument, show the current branch name. With one argument,
1016 set the working directory branch name (the branch will not exist
1016 set the working directory branch name (the branch will not exist
1017 in the repository until the next commit). Standard practice
1017 in the repository until the next commit). Standard practice
1018 recommends that primary development take place on the 'default'
1018 recommends that primary development take place on the 'default'
1019 branch.
1019 branch.
1020
1020
1021 Unless -f/--force is specified, branch will not let you set a
1021 Unless -f/--force is specified, branch will not let you set a
1022 branch name that already exists.
1022 branch name that already exists.
1023
1023
1024 Use -C/--clean to reset the working directory branch to that of
1024 Use -C/--clean to reset the working directory branch to that of
1025 the parent of the working directory, negating a previous branch
1025 the parent of the working directory, negating a previous branch
1026 change.
1026 change.
1027
1027
1028 Use the command :hg:`update` to switch to an existing branch. Use
1028 Use the command :hg:`update` to switch to an existing branch. Use
1029 :hg:`commit --close-branch` to mark this branch head as closed.
1029 :hg:`commit --close-branch` to mark this branch head as closed.
1030 When all heads of a branch are closed, the branch will be
1030 When all heads of a branch are closed, the branch will be
1031 considered closed.
1031 considered closed.
1032
1032
1033 Returns 0 on success.
1033 Returns 0 on success.
1034 """
1034 """
1035 opts = pycompat.byteskwargs(opts)
1035 opts = pycompat.byteskwargs(opts)
1036 if label:
1036 if label:
1037 label = label.strip()
1037 label = label.strip()
1038
1038
1039 if not opts.get('clean') and not label:
1039 if not opts.get('clean') and not label:
1040 ui.write("%s\n" % repo.dirstate.branch())
1040 ui.write("%s\n" % repo.dirstate.branch())
1041 return
1041 return
1042
1042
1043 with repo.wlock():
1043 with repo.wlock():
1044 if opts.get('clean'):
1044 if opts.get('clean'):
1045 label = repo[None].p1().branch()
1045 label = repo[None].p1().branch()
1046 repo.dirstate.setbranch(label)
1046 repo.dirstate.setbranch(label)
1047 ui.status(_('reset working directory to branch %s\n') % label)
1047 ui.status(_('reset working directory to branch %s\n') % label)
1048 elif label:
1048 elif label:
1049 if not opts.get('force') and label in repo.branchmap():
1049 if not opts.get('force') and label in repo.branchmap():
1050 if label not in [p.branch() for p in repo[None].parents()]:
1050 if label not in [p.branch() for p in repo[None].parents()]:
1051 raise error.Abort(_('a branch of the same name already'
1051 raise error.Abort(_('a branch of the same name already'
1052 ' exists'),
1052 ' exists'),
1053 # i18n: "it" refers to an existing branch
1053 # i18n: "it" refers to an existing branch
1054 hint=_("use 'hg update' to switch to it"))
1054 hint=_("use 'hg update' to switch to it"))
1055 scmutil.checknewlabel(repo, label, 'branch')
1055 scmutil.checknewlabel(repo, label, 'branch')
1056 repo.dirstate.setbranch(label)
1056 repo.dirstate.setbranch(label)
1057 ui.status(_('marked working directory as branch %s\n') % label)
1057 ui.status(_('marked working directory as branch %s\n') % label)
1058
1058
1059 # find any open named branches aside from default
1059 # find any open named branches aside from default
1060 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1060 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1061 if n != "default" and not c]
1061 if n != "default" and not c]
1062 if not others:
1062 if not others:
1063 ui.status(_('(branches are permanent and global, '
1063 ui.status(_('(branches are permanent and global, '
1064 'did you want a bookmark?)\n'))
1064 'did you want a bookmark?)\n'))
1065
1065
1066 @command('branches',
1066 @command('branches',
1067 [('a', 'active', False,
1067 [('a', 'active', False,
1068 _('show only branches that have unmerged heads (DEPRECATED)')),
1068 _('show only branches that have unmerged heads (DEPRECATED)')),
1069 ('c', 'closed', False, _('show normal and closed branches')),
1069 ('c', 'closed', False, _('show normal and closed branches')),
1070 ] + formatteropts,
1070 ] + formatteropts,
1071 _('[-c]'), cmdtype=readonly)
1071 _('[-c]'), cmdtype=readonly)
1072 def branches(ui, repo, active=False, closed=False, **opts):
1072 def branches(ui, repo, active=False, closed=False, **opts):
1073 """list repository named branches
1073 """list repository named branches
1074
1074
1075 List the repository's named branches, indicating which ones are
1075 List the repository's named branches, indicating which ones are
1076 inactive. If -c/--closed is specified, also list branches which have
1076 inactive. If -c/--closed is specified, also list branches which have
1077 been marked closed (see :hg:`commit --close-branch`).
1077 been marked closed (see :hg:`commit --close-branch`).
1078
1078
1079 Use the command :hg:`update` to switch to an existing branch.
1079 Use the command :hg:`update` to switch to an existing branch.
1080
1080
1081 Returns 0.
1081 Returns 0.
1082 """
1082 """
1083
1083
1084 opts = pycompat.byteskwargs(opts)
1084 opts = pycompat.byteskwargs(opts)
1085 ui.pager('branches')
1085 ui.pager('branches')
1086 fm = ui.formatter('branches', opts)
1086 fm = ui.formatter('branches', opts)
1087 hexfunc = fm.hexfunc
1087 hexfunc = fm.hexfunc
1088
1088
1089 allheads = set(repo.heads())
1089 allheads = set(repo.heads())
1090 branches = []
1090 branches = []
1091 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1091 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1092 isactive = False
1092 isactive = False
1093 if not isclosed:
1093 if not isclosed:
1094 openheads = set(repo.branchmap().iteropen(heads))
1094 openheads = set(repo.branchmap().iteropen(heads))
1095 isactive = bool(openheads & allheads)
1095 isactive = bool(openheads & allheads)
1096 branches.append((tag, repo[tip], isactive, not isclosed))
1096 branches.append((tag, repo[tip], isactive, not isclosed))
1097 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1097 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1098 reverse=True)
1098 reverse=True)
1099
1099
1100 for tag, ctx, isactive, isopen in branches:
1100 for tag, ctx, isactive, isopen in branches:
1101 if active and not isactive:
1101 if active and not isactive:
1102 continue
1102 continue
1103 if isactive:
1103 if isactive:
1104 label = 'branches.active'
1104 label = 'branches.active'
1105 notice = ''
1105 notice = ''
1106 elif not isopen:
1106 elif not isopen:
1107 if not closed:
1107 if not closed:
1108 continue
1108 continue
1109 label = 'branches.closed'
1109 label = 'branches.closed'
1110 notice = _(' (closed)')
1110 notice = _(' (closed)')
1111 else:
1111 else:
1112 label = 'branches.inactive'
1112 label = 'branches.inactive'
1113 notice = _(' (inactive)')
1113 notice = _(' (inactive)')
1114 current = (tag == repo.dirstate.branch())
1114 current = (tag == repo.dirstate.branch())
1115 if current:
1115 if current:
1116 label = 'branches.current'
1116 label = 'branches.current'
1117
1117
1118 fm.startitem()
1118 fm.startitem()
1119 fm.write('branch', '%s', tag, label=label)
1119 fm.write('branch', '%s', tag, label=label)
1120 rev = ctx.rev()
1120 rev = ctx.rev()
1121 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1121 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1122 fmt = ' ' * padsize + ' %d:%s'
1122 fmt = ' ' * padsize + ' %d:%s'
1123 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1123 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1124 label='log.changeset changeset.%s' % ctx.phasestr())
1124 label='log.changeset changeset.%s' % ctx.phasestr())
1125 fm.context(ctx=ctx)
1125 fm.context(ctx=ctx)
1126 fm.data(active=isactive, closed=not isopen, current=current)
1126 fm.data(active=isactive, closed=not isopen, current=current)
1127 if not ui.quiet:
1127 if not ui.quiet:
1128 fm.plain(notice)
1128 fm.plain(notice)
1129 fm.plain('\n')
1129 fm.plain('\n')
1130 fm.end()
1130 fm.end()
1131
1131
1132 @command('bundle',
1132 @command('bundle',
1133 [('f', 'force', None, _('run even when the destination is unrelated')),
1133 [('f', 'force', None, _('run even when the destination is unrelated')),
1134 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1134 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1135 _('REV')),
1135 _('REV')),
1136 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1136 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1137 _('BRANCH')),
1137 _('BRANCH')),
1138 ('', 'base', [],
1138 ('', 'base', [],
1139 _('a base changeset assumed to be available at the destination'),
1139 _('a base changeset assumed to be available at the destination'),
1140 _('REV')),
1140 _('REV')),
1141 ('a', 'all', None, _('bundle all changesets in the repository')),
1141 ('a', 'all', None, _('bundle all changesets in the repository')),
1142 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1142 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1143 ] + remoteopts,
1143 ] + remoteopts,
1144 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1144 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1145 def bundle(ui, repo, fname, dest=None, **opts):
1145 def bundle(ui, repo, fname, dest=None, **opts):
1146 """create a bundle file
1146 """create a bundle file
1147
1147
1148 Generate a bundle file containing data to be added to a repository.
1148 Generate a bundle file containing data to be added to a repository.
1149
1149
1150 To create a bundle containing all changesets, use -a/--all
1150 To create a bundle containing all changesets, use -a/--all
1151 (or --base null). Otherwise, hg assumes the destination will have
1151 (or --base null). Otherwise, hg assumes the destination will have
1152 all the nodes you specify with --base parameters. Otherwise, hg
1152 all the nodes you specify with --base parameters. Otherwise, hg
1153 will assume the repository has all the nodes in destination, or
1153 will assume the repository has all the nodes in destination, or
1154 default-push/default if no destination is specified.
1154 default-push/default if no destination is specified.
1155
1155
1156 You can change bundle format with the -t/--type option. See
1156 You can change bundle format with the -t/--type option. See
1157 :hg:`help bundlespec` for documentation on this format. By default,
1157 :hg:`help bundlespec` for documentation on this format. By default,
1158 the most appropriate format is used and compression defaults to
1158 the most appropriate format is used and compression defaults to
1159 bzip2.
1159 bzip2.
1160
1160
1161 The bundle file can then be transferred using conventional means
1161 The bundle file can then be transferred using conventional means
1162 and applied to another repository with the unbundle or pull
1162 and applied to another repository with the unbundle or pull
1163 command. This is useful when direct push and pull are not
1163 command. This is useful when direct push and pull are not
1164 available or when exporting an entire repository is undesirable.
1164 available or when exporting an entire repository is undesirable.
1165
1165
1166 Applying bundles preserves all changeset contents including
1166 Applying bundles preserves all changeset contents including
1167 permissions, copy/rename information, and revision history.
1167 permissions, copy/rename information, and revision history.
1168
1168
1169 Returns 0 on success, 1 if no changes found.
1169 Returns 0 on success, 1 if no changes found.
1170 """
1170 """
1171 opts = pycompat.byteskwargs(opts)
1171 opts = pycompat.byteskwargs(opts)
1172 revs = None
1172 revs = None
1173 if 'rev' in opts:
1173 if 'rev' in opts:
1174 revstrings = opts['rev']
1174 revstrings = opts['rev']
1175 revs = scmutil.revrange(repo, revstrings)
1175 revs = scmutil.revrange(repo, revstrings)
1176 if revstrings and not revs:
1176 if revstrings and not revs:
1177 raise error.Abort(_('no commits to bundle'))
1177 raise error.Abort(_('no commits to bundle'))
1178
1178
1179 bundletype = opts.get('type', 'bzip2').lower()
1179 bundletype = opts.get('type', 'bzip2').lower()
1180 try:
1180 try:
1181 bcompression, cgversion, params = exchange.parsebundlespec(
1181 bcompression, cgversion, params = exchange.parsebundlespec(
1182 repo, bundletype, strict=False)
1182 repo, bundletype, strict=False)
1183 except error.UnsupportedBundleSpecification as e:
1183 except error.UnsupportedBundleSpecification as e:
1184 raise error.Abort(str(e),
1184 raise error.Abort(str(e),
1185 hint=_("see 'hg help bundlespec' for supported "
1185 hint=_("see 'hg help bundlespec' for supported "
1186 "values for --type"))
1186 "values for --type"))
1187
1187
1188 # Packed bundles are a pseudo bundle format for now.
1188 # Packed bundles are a pseudo bundle format for now.
1189 if cgversion == 's1':
1189 if cgversion == 's1':
1190 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1190 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1191 hint=_("use 'hg debugcreatestreamclonebundle'"))
1191 hint=_("use 'hg debugcreatestreamclonebundle'"))
1192
1192
1193 if opts.get('all'):
1193 if opts.get('all'):
1194 if dest:
1194 if dest:
1195 raise error.Abort(_("--all is incompatible with specifying "
1195 raise error.Abort(_("--all is incompatible with specifying "
1196 "a destination"))
1196 "a destination"))
1197 if opts.get('base'):
1197 if opts.get('base'):
1198 ui.warn(_("ignoring --base because --all was specified\n"))
1198 ui.warn(_("ignoring --base because --all was specified\n"))
1199 base = ['null']
1199 base = ['null']
1200 else:
1200 else:
1201 base = scmutil.revrange(repo, opts.get('base'))
1201 base = scmutil.revrange(repo, opts.get('base'))
1202 if cgversion not in changegroup.supportedoutgoingversions(repo):
1202 if cgversion not in changegroup.supportedoutgoingversions(repo):
1203 raise error.Abort(_("repository does not support bundle version %s") %
1203 raise error.Abort(_("repository does not support bundle version %s") %
1204 cgversion)
1204 cgversion)
1205
1205
1206 if base:
1206 if base:
1207 if dest:
1207 if dest:
1208 raise error.Abort(_("--base is incompatible with specifying "
1208 raise error.Abort(_("--base is incompatible with specifying "
1209 "a destination"))
1209 "a destination"))
1210 common = [repo.lookup(rev) for rev in base]
1210 common = [repo.lookup(rev) for rev in base]
1211 heads = revs and map(repo.lookup, revs) or None
1211 heads = revs and map(repo.lookup, revs) or None
1212 outgoing = discovery.outgoing(repo, common, heads)
1212 outgoing = discovery.outgoing(repo, common, heads)
1213 else:
1213 else:
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1214 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1215 dest, branches = hg.parseurl(dest, opts.get('branch'))
1216 other = hg.peer(repo, opts, dest)
1216 other = hg.peer(repo, opts, dest)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1217 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1218 heads = revs and map(repo.lookup, revs) or revs
1218 heads = revs and map(repo.lookup, revs) or revs
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1219 outgoing = discovery.findcommonoutgoing(repo, other,
1220 onlyheads=heads,
1220 onlyheads=heads,
1221 force=opts.get('force'),
1221 force=opts.get('force'),
1222 portable=True)
1222 portable=True)
1223
1223
1224 if not outgoing.missing:
1224 if not outgoing.missing:
1225 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1225 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1226 return 1
1226 return 1
1227
1227
1228 if cgversion == '01': #bundle1
1228 if cgversion == '01': #bundle1
1229 if bcompression is None:
1229 if bcompression is None:
1230 bcompression = 'UN'
1230 bcompression = 'UN'
1231 bversion = 'HG10' + bcompression
1231 bversion = 'HG10' + bcompression
1232 bcompression = None
1232 bcompression = None
1233 elif cgversion in ('02', '03'):
1233 elif cgversion in ('02', '03'):
1234 bversion = 'HG20'
1234 bversion = 'HG20'
1235 else:
1235 else:
1236 raise error.ProgrammingError(
1236 raise error.ProgrammingError(
1237 'bundle: unexpected changegroup version %s' % cgversion)
1237 'bundle: unexpected changegroup version %s' % cgversion)
1238
1238
1239 # TODO compression options should be derived from bundlespec parsing.
1239 # TODO compression options should be derived from bundlespec parsing.
1240 # This is a temporary hack to allow adjusting bundle compression
1240 # This is a temporary hack to allow adjusting bundle compression
1241 # level without a) formalizing the bundlespec changes to declare it
1241 # level without a) formalizing the bundlespec changes to declare it
1242 # b) introducing a command flag.
1242 # b) introducing a command flag.
1243 compopts = {}
1243 compopts = {}
1244 complevel = ui.configint('experimental', 'bundlecomplevel')
1244 complevel = ui.configint('experimental', 'bundlecomplevel')
1245 if complevel is not None:
1245 if complevel is not None:
1246 compopts['level'] = complevel
1246 compopts['level'] = complevel
1247
1247
1248
1248
1249 contentopts = {'cg.version': cgversion}
1249 contentopts = {'cg.version': cgversion}
1250 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1250 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1251 contentopts['obsolescence'] = True
1251 contentopts['obsolescence'] = True
1252 if repo.ui.configbool('experimental', 'bundle-phases'):
1252 if repo.ui.configbool('experimental', 'bundle-phases'):
1253 contentopts['phases'] = True
1253 contentopts['phases'] = True
1254 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1254 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1255 contentopts, compression=bcompression,
1255 contentopts, compression=bcompression,
1256 compopts=compopts)
1256 compopts=compopts)
1257
1257
1258 @command('cat',
1258 @command('cat',
1259 [('o', 'output', '',
1259 [('o', 'output', '',
1260 _('print output to file with formatted name'), _('FORMAT')),
1260 _('print output to file with formatted name'), _('FORMAT')),
1261 ('r', 'rev', '', _('print the given revision'), _('REV')),
1261 ('r', 'rev', '', _('print the given revision'), _('REV')),
1262 ('', 'decode', None, _('apply any matching decode filter')),
1262 ('', 'decode', None, _('apply any matching decode filter')),
1263 ] + walkopts + formatteropts,
1263 ] + walkopts + formatteropts,
1264 _('[OPTION]... FILE...'),
1264 _('[OPTION]... FILE...'),
1265 inferrepo=True, cmdtype=readonly)
1265 inferrepo=True, cmdtype=readonly)
1266 def cat(ui, repo, file1, *pats, **opts):
1266 def cat(ui, repo, file1, *pats, **opts):
1267 """output the current or given revision of files
1267 """output the current or given revision of files
1268
1268
1269 Print the specified files as they were at the given revision. If
1269 Print the specified files as they were at the given revision. If
1270 no revision is given, the parent of the working directory is used.
1270 no revision is given, the parent of the working directory is used.
1271
1271
1272 Output may be to a file, in which case the name of the file is
1272 Output may be to a file, in which case the name of the file is
1273 given using a format string. The formatting rules as follows:
1273 given using a format string. The formatting rules as follows:
1274
1274
1275 :``%%``: literal "%" character
1275 :``%%``: literal "%" character
1276 :``%s``: basename of file being printed
1276 :``%s``: basename of file being printed
1277 :``%d``: dirname of file being printed, or '.' if in repository root
1277 :``%d``: dirname of file being printed, or '.' if in repository root
1278 :``%p``: root-relative path name of file being printed
1278 :``%p``: root-relative path name of file being printed
1279 :``%H``: changeset hash (40 hexadecimal digits)
1279 :``%H``: changeset hash (40 hexadecimal digits)
1280 :``%R``: changeset revision number
1280 :``%R``: changeset revision number
1281 :``%h``: short-form changeset hash (12 hexadecimal digits)
1281 :``%h``: short-form changeset hash (12 hexadecimal digits)
1282 :``%r``: zero-padded changeset revision number
1282 :``%r``: zero-padded changeset revision number
1283 :``%b``: basename of the exporting repository
1283 :``%b``: basename of the exporting repository
1284
1284
1285 Returns 0 on success.
1285 Returns 0 on success.
1286 """
1286 """
1287 opts = pycompat.byteskwargs(opts)
1287 opts = pycompat.byteskwargs(opts)
1288 rev = opts.get('rev')
1288 rev = opts.get('rev')
1289 if rev:
1289 if rev:
1290 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1290 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1291 ctx = scmutil.revsingle(repo, rev)
1291 ctx = scmutil.revsingle(repo, rev)
1292 m = scmutil.match(ctx, (file1,) + pats, opts)
1292 m = scmutil.match(ctx, (file1,) + pats, opts)
1293 fntemplate = opts.pop('output', '')
1293 fntemplate = opts.pop('output', '')
1294 if cmdutil.isstdiofilename(fntemplate):
1294 if cmdutil.isstdiofilename(fntemplate):
1295 fntemplate = ''
1295 fntemplate = ''
1296
1296
1297 if fntemplate:
1297 if fntemplate:
1298 fm = formatter.nullformatter(ui, 'cat')
1298 fm = formatter.nullformatter(ui, 'cat')
1299 else:
1299 else:
1300 ui.pager('cat')
1300 ui.pager('cat')
1301 fm = ui.formatter('cat', opts)
1301 fm = ui.formatter('cat', opts)
1302 with fm:
1302 with fm:
1303 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1303 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1304 **pycompat.strkwargs(opts))
1304 **pycompat.strkwargs(opts))
1305
1305
1306 @command('^clone',
1306 @command('^clone',
1307 [('U', 'noupdate', None, _('the clone will include an empty working '
1307 [('U', 'noupdate', None, _('the clone will include an empty working '
1308 'directory (only a repository)')),
1308 'directory (only a repository)')),
1309 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1309 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1310 _('REV')),
1310 _('REV')),
1311 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1311 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1312 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1312 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1313 ('', 'pull', None, _('use pull protocol to copy metadata')),
1313 ('', 'pull', None, _('use pull protocol to copy metadata')),
1314 ('', 'uncompressed', None,
1314 ('', 'uncompressed', None,
1315 _('an alias to --stream (DEPRECATED)')),
1315 _('an alias to --stream (DEPRECATED)')),
1316 ('', 'stream', None,
1316 ('', 'stream', None,
1317 _('clone with minimal data processing')),
1317 _('clone with minimal data processing')),
1318 ] + remoteopts,
1318 ] + remoteopts,
1319 _('[OPTION]... SOURCE [DEST]'),
1319 _('[OPTION]... SOURCE [DEST]'),
1320 norepo=True)
1320 norepo=True)
1321 def clone(ui, source, dest=None, **opts):
1321 def clone(ui, source, dest=None, **opts):
1322 """make a copy of an existing repository
1322 """make a copy of an existing repository
1323
1323
1324 Create a copy of an existing repository in a new directory.
1324 Create a copy of an existing repository in a new directory.
1325
1325
1326 If no destination directory name is specified, it defaults to the
1326 If no destination directory name is specified, it defaults to the
1327 basename of the source.
1327 basename of the source.
1328
1328
1329 The location of the source is added to the new repository's
1329 The location of the source is added to the new repository's
1330 ``.hg/hgrc`` file, as the default to be used for future pulls.
1330 ``.hg/hgrc`` file, as the default to be used for future pulls.
1331
1331
1332 Only local paths and ``ssh://`` URLs are supported as
1332 Only local paths and ``ssh://`` URLs are supported as
1333 destinations. For ``ssh://`` destinations, no working directory or
1333 destinations. For ``ssh://`` destinations, no working directory or
1334 ``.hg/hgrc`` will be created on the remote side.
1334 ``.hg/hgrc`` will be created on the remote side.
1335
1335
1336 If the source repository has a bookmark called '@' set, that
1336 If the source repository has a bookmark called '@' set, that
1337 revision will be checked out in the new repository by default.
1337 revision will be checked out in the new repository by default.
1338
1338
1339 To check out a particular version, use -u/--update, or
1339 To check out a particular version, use -u/--update, or
1340 -U/--noupdate to create a clone with no working directory.
1340 -U/--noupdate to create a clone with no working directory.
1341
1341
1342 To pull only a subset of changesets, specify one or more revisions
1342 To pull only a subset of changesets, specify one or more revisions
1343 identifiers with -r/--rev or branches with -b/--branch. The
1343 identifiers with -r/--rev or branches with -b/--branch. The
1344 resulting clone will contain only the specified changesets and
1344 resulting clone will contain only the specified changesets and
1345 their ancestors. These options (or 'clone src#rev dest') imply
1345 their ancestors. These options (or 'clone src#rev dest') imply
1346 --pull, even for local source repositories.
1346 --pull, even for local source repositories.
1347
1347
1348 In normal clone mode, the remote normalizes repository data into a common
1348 In normal clone mode, the remote normalizes repository data into a common
1349 exchange format and the receiving end translates this data into its local
1349 exchange format and the receiving end translates this data into its local
1350 storage format. --stream activates a different clone mode that essentially
1350 storage format. --stream activates a different clone mode that essentially
1351 copies repository files from the remote with minimal data processing. This
1351 copies repository files from the remote with minimal data processing. This
1352 significantly reduces the CPU cost of a clone both remotely and locally.
1352 significantly reduces the CPU cost of a clone both remotely and locally.
1353 However, it often increases the transferred data size by 30-40%. This can
1353 However, it often increases the transferred data size by 30-40%. This can
1354 result in substantially faster clones where I/O throughput is plentiful,
1354 result in substantially faster clones where I/O throughput is plentiful,
1355 especially for larger repositories. A side-effect of --stream clones is
1355 especially for larger repositories. A side-effect of --stream clones is
1356 that storage settings and requirements on the remote are applied locally:
1356 that storage settings and requirements on the remote are applied locally:
1357 a modern client may inherit legacy or inefficient storage used by the
1357 a modern client may inherit legacy or inefficient storage used by the
1358 remote or a legacy Mercurial client may not be able to clone from a
1358 remote or a legacy Mercurial client may not be able to clone from a
1359 modern Mercurial remote.
1359 modern Mercurial remote.
1360
1360
1361 .. note::
1361 .. note::
1362
1362
1363 Specifying a tag will include the tagged changeset but not the
1363 Specifying a tag will include the tagged changeset but not the
1364 changeset containing the tag.
1364 changeset containing the tag.
1365
1365
1366 .. container:: verbose
1366 .. container:: verbose
1367
1367
1368 For efficiency, hardlinks are used for cloning whenever the
1368 For efficiency, hardlinks are used for cloning whenever the
1369 source and destination are on the same filesystem (note this
1369 source and destination are on the same filesystem (note this
1370 applies only to the repository data, not to the working
1370 applies only to the repository data, not to the working
1371 directory). Some filesystems, such as AFS, implement hardlinking
1371 directory). Some filesystems, such as AFS, implement hardlinking
1372 incorrectly, but do not report errors. In these cases, use the
1372 incorrectly, but do not report errors. In these cases, use the
1373 --pull option to avoid hardlinking.
1373 --pull option to avoid hardlinking.
1374
1374
1375 Mercurial will update the working directory to the first applicable
1375 Mercurial will update the working directory to the first applicable
1376 revision from this list:
1376 revision from this list:
1377
1377
1378 a) null if -U or the source repository has no changesets
1378 a) null if -U or the source repository has no changesets
1379 b) if -u . and the source repository is local, the first parent of
1379 b) if -u . and the source repository is local, the first parent of
1380 the source repository's working directory
1380 the source repository's working directory
1381 c) the changeset specified with -u (if a branch name, this means the
1381 c) the changeset specified with -u (if a branch name, this means the
1382 latest head of that branch)
1382 latest head of that branch)
1383 d) the changeset specified with -r
1383 d) the changeset specified with -r
1384 e) the tipmost head specified with -b
1384 e) the tipmost head specified with -b
1385 f) the tipmost head specified with the url#branch source syntax
1385 f) the tipmost head specified with the url#branch source syntax
1386 g) the revision marked with the '@' bookmark, if present
1386 g) the revision marked with the '@' bookmark, if present
1387 h) the tipmost head of the default branch
1387 h) the tipmost head of the default branch
1388 i) tip
1388 i) tip
1389
1389
1390 When cloning from servers that support it, Mercurial may fetch
1390 When cloning from servers that support it, Mercurial may fetch
1391 pre-generated data from a server-advertised URL. When this is done,
1391 pre-generated data from a server-advertised URL. When this is done,
1392 hooks operating on incoming changesets and changegroups may fire twice,
1392 hooks operating on incoming changesets and changegroups may fire twice,
1393 once for the bundle fetched from the URL and another for any additional
1393 once for the bundle fetched from the URL and another for any additional
1394 data not fetched from this URL. In addition, if an error occurs, the
1394 data not fetched from this URL. In addition, if an error occurs, the
1395 repository may be rolled back to a partial clone. This behavior may
1395 repository may be rolled back to a partial clone. This behavior may
1396 change in future releases. See :hg:`help -e clonebundles` for more.
1396 change in future releases. See :hg:`help -e clonebundles` for more.
1397
1397
1398 Examples:
1398 Examples:
1399
1399
1400 - clone a remote repository to a new directory named hg/::
1400 - clone a remote repository to a new directory named hg/::
1401
1401
1402 hg clone https://www.mercurial-scm.org/repo/hg/
1402 hg clone https://www.mercurial-scm.org/repo/hg/
1403
1403
1404 - create a lightweight local clone::
1404 - create a lightweight local clone::
1405
1405
1406 hg clone project/ project-feature/
1406 hg clone project/ project-feature/
1407
1407
1408 - clone from an absolute path on an ssh server (note double-slash)::
1408 - clone from an absolute path on an ssh server (note double-slash)::
1409
1409
1410 hg clone ssh://user@server//home/projects/alpha/
1410 hg clone ssh://user@server//home/projects/alpha/
1411
1411
1412 - do a streaming clone while checking out a specified version::
1412 - do a streaming clone while checking out a specified version::
1413
1413
1414 hg clone --stream http://server/repo -u 1.5
1414 hg clone --stream http://server/repo -u 1.5
1415
1415
1416 - create a repository without changesets after a particular revision::
1416 - create a repository without changesets after a particular revision::
1417
1417
1418 hg clone -r 04e544 experimental/ good/
1418 hg clone -r 04e544 experimental/ good/
1419
1419
1420 - clone (and track) a particular named branch::
1420 - clone (and track) a particular named branch::
1421
1421
1422 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1422 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1423
1423
1424 See :hg:`help urls` for details on specifying URLs.
1424 See :hg:`help urls` for details on specifying URLs.
1425
1425
1426 Returns 0 on success.
1426 Returns 0 on success.
1427 """
1427 """
1428 opts = pycompat.byteskwargs(opts)
1428 opts = pycompat.byteskwargs(opts)
1429 if opts.get('noupdate') and opts.get('updaterev'):
1429 if opts.get('noupdate') and opts.get('updaterev'):
1430 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1430 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1431
1431
1432 r = hg.clone(ui, opts, source, dest,
1432 r = hg.clone(ui, opts, source, dest,
1433 pull=opts.get('pull'),
1433 pull=opts.get('pull'),
1434 stream=opts.get('stream') or opts.get('uncompressed'),
1434 stream=opts.get('stream') or opts.get('uncompressed'),
1435 rev=opts.get('rev'),
1435 rev=opts.get('rev'),
1436 update=opts.get('updaterev') or not opts.get('noupdate'),
1436 update=opts.get('updaterev') or not opts.get('noupdate'),
1437 branch=opts.get('branch'),
1437 branch=opts.get('branch'),
1438 shareopts=opts.get('shareopts'))
1438 shareopts=opts.get('shareopts'))
1439
1439
1440 return r is None
1440 return r is None
1441
1441
1442 @command('^commit|ci',
1442 @command('^commit|ci',
1443 [('A', 'addremove', None,
1443 [('A', 'addremove', None,
1444 _('mark new/missing files as added/removed before committing')),
1444 _('mark new/missing files as added/removed before committing')),
1445 ('', 'close-branch', None,
1445 ('', 'close-branch', None,
1446 _('mark a branch head as closed')),
1446 _('mark a branch head as closed')),
1447 ('', 'amend', None, _('amend the parent of the working directory')),
1447 ('', 'amend', None, _('amend the parent of the working directory')),
1448 ('s', 'secret', None, _('use the secret phase for committing')),
1448 ('s', 'secret', None, _('use the secret phase for committing')),
1449 ('e', 'edit', None, _('invoke editor on commit messages')),
1449 ('e', 'edit', None, _('invoke editor on commit messages')),
1450 ('i', 'interactive', None, _('use interactive mode')),
1450 ('i', 'interactive', None, _('use interactive mode')),
1451 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1451 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1452 _('[OPTION]... [FILE]...'),
1452 _('[OPTION]... [FILE]...'),
1453 inferrepo=True)
1453 inferrepo=True)
1454 def commit(ui, repo, *pats, **opts):
1454 def commit(ui, repo, *pats, **opts):
1455 """commit the specified files or all outstanding changes
1455 """commit the specified files or all outstanding changes
1456
1456
1457 Commit changes to the given files into the repository. Unlike a
1457 Commit changes to the given files into the repository. Unlike a
1458 centralized SCM, this operation is a local operation. See
1458 centralized SCM, this operation is a local operation. See
1459 :hg:`push` for a way to actively distribute your changes.
1459 :hg:`push` for a way to actively distribute your changes.
1460
1460
1461 If a list of files is omitted, all changes reported by :hg:`status`
1461 If a list of files is omitted, all changes reported by :hg:`status`
1462 will be committed.
1462 will be committed.
1463
1463
1464 If you are committing the result of a merge, do not provide any
1464 If you are committing the result of a merge, do not provide any
1465 filenames or -I/-X filters.
1465 filenames or -I/-X filters.
1466
1466
1467 If no commit message is specified, Mercurial starts your
1467 If no commit message is specified, Mercurial starts your
1468 configured editor where you can enter a message. In case your
1468 configured editor where you can enter a message. In case your
1469 commit fails, you will find a backup of your message in
1469 commit fails, you will find a backup of your message in
1470 ``.hg/last-message.txt``.
1470 ``.hg/last-message.txt``.
1471
1471
1472 The --close-branch flag can be used to mark the current branch
1472 The --close-branch flag can be used to mark the current branch
1473 head closed. When all heads of a branch are closed, the branch
1473 head closed. When all heads of a branch are closed, the branch
1474 will be considered closed and no longer listed.
1474 will be considered closed and no longer listed.
1475
1475
1476 The --amend flag can be used to amend the parent of the
1476 The --amend flag can be used to amend the parent of the
1477 working directory with a new commit that contains the changes
1477 working directory with a new commit that contains the changes
1478 in the parent in addition to those currently reported by :hg:`status`,
1478 in the parent in addition to those currently reported by :hg:`status`,
1479 if there are any. The old commit is stored in a backup bundle in
1479 if there are any. The old commit is stored in a backup bundle in
1480 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1480 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1481 on how to restore it).
1481 on how to restore it).
1482
1482
1483 Message, user and date are taken from the amended commit unless
1483 Message, user and date are taken from the amended commit unless
1484 specified. When a message isn't specified on the command line,
1484 specified. When a message isn't specified on the command line,
1485 the editor will open with the message of the amended commit.
1485 the editor will open with the message of the amended commit.
1486
1486
1487 It is not possible to amend public changesets (see :hg:`help phases`)
1487 It is not possible to amend public changesets (see :hg:`help phases`)
1488 or changesets that have children.
1488 or changesets that have children.
1489
1489
1490 See :hg:`help dates` for a list of formats valid for -d/--date.
1490 See :hg:`help dates` for a list of formats valid for -d/--date.
1491
1491
1492 Returns 0 on success, 1 if nothing changed.
1492 Returns 0 on success, 1 if nothing changed.
1493
1493
1494 .. container:: verbose
1494 .. container:: verbose
1495
1495
1496 Examples:
1496 Examples:
1497
1497
1498 - commit all files ending in .py::
1498 - commit all files ending in .py::
1499
1499
1500 hg commit --include "set:**.py"
1500 hg commit --include "set:**.py"
1501
1501
1502 - commit all non-binary files::
1502 - commit all non-binary files::
1503
1503
1504 hg commit --exclude "set:binary()"
1504 hg commit --exclude "set:binary()"
1505
1505
1506 - amend the current commit and set the date to now::
1506 - amend the current commit and set the date to now::
1507
1507
1508 hg commit --amend --date now
1508 hg commit --amend --date now
1509 """
1509 """
1510 wlock = lock = None
1510 wlock = lock = None
1511 try:
1511 try:
1512 wlock = repo.wlock()
1512 wlock = repo.wlock()
1513 lock = repo.lock()
1513 lock = repo.lock()
1514 return _docommit(ui, repo, *pats, **opts)
1514 return _docommit(ui, repo, *pats, **opts)
1515 finally:
1515 finally:
1516 release(lock, wlock)
1516 release(lock, wlock)
1517
1517
1518 def _docommit(ui, repo, *pats, **opts):
1518 def _docommit(ui, repo, *pats, **opts):
1519 if opts.get(r'interactive'):
1519 if opts.get(r'interactive'):
1520 opts.pop(r'interactive')
1520 opts.pop(r'interactive')
1521 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1521 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1522 cmdutil.recordfilter, *pats,
1522 cmdutil.recordfilter, *pats,
1523 **opts)
1523 **opts)
1524 # ret can be 0 (no changes to record) or the value returned by
1524 # ret can be 0 (no changes to record) or the value returned by
1525 # commit(), 1 if nothing changed or None on success.
1525 # commit(), 1 if nothing changed or None on success.
1526 return 1 if ret == 0 else ret
1526 return 1 if ret == 0 else ret
1527
1527
1528 opts = pycompat.byteskwargs(opts)
1528 opts = pycompat.byteskwargs(opts)
1529 if opts.get('subrepos'):
1529 if opts.get('subrepos'):
1530 if opts.get('amend'):
1530 if opts.get('amend'):
1531 raise error.Abort(_('cannot amend with --subrepos'))
1531 raise error.Abort(_('cannot amend with --subrepos'))
1532 # Let --subrepos on the command line override config setting.
1532 # Let --subrepos on the command line override config setting.
1533 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1533 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1534
1534
1535 cmdutil.checkunfinished(repo, commit=True)
1535 cmdutil.checkunfinished(repo, commit=True)
1536
1536
1537 branch = repo[None].branch()
1537 branch = repo[None].branch()
1538 bheads = repo.branchheads(branch)
1538 bheads = repo.branchheads(branch)
1539
1539
1540 extra = {}
1540 extra = {}
1541 if opts.get('close_branch'):
1541 if opts.get('close_branch'):
1542 extra['close'] = 1
1542 extra['close'] = 1
1543
1543
1544 if not bheads:
1544 if not bheads:
1545 raise error.Abort(_('can only close branch heads'))
1545 raise error.Abort(_('can only close branch heads'))
1546 elif opts.get('amend'):
1546 elif opts.get('amend'):
1547 if repo[None].parents()[0].p1().branch() != branch and \
1547 if repo[None].parents()[0].p1().branch() != branch and \
1548 repo[None].parents()[0].p2().branch() != branch:
1548 repo[None].parents()[0].p2().branch() != branch:
1549 raise error.Abort(_('can only close branch heads'))
1549 raise error.Abort(_('can only close branch heads'))
1550
1550
1551 if opts.get('amend'):
1551 if opts.get('amend'):
1552 if ui.configbool('ui', 'commitsubrepos'):
1552 if ui.configbool('ui', 'commitsubrepos'):
1553 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1553 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1554
1554
1555 old = repo['.']
1555 old = repo['.']
1556 rewriteutil.precheck(repo, [old.rev()], 'amend')
1556 rewriteutil.precheck(repo, [old.rev()], 'amend')
1557
1557
1558 # Currently histedit gets confused if an amend happens while histedit
1558 # Currently histedit gets confused if an amend happens while histedit
1559 # is in progress. Since we have a checkunfinished command, we are
1559 # is in progress. Since we have a checkunfinished command, we are
1560 # temporarily honoring it.
1560 # temporarily honoring it.
1561 #
1561 #
1562 # Note: eventually this guard will be removed. Please do not expect
1562 # Note: eventually this guard will be removed. Please do not expect
1563 # this behavior to remain.
1563 # this behavior to remain.
1564 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1564 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1565 cmdutil.checkunfinished(repo)
1565 cmdutil.checkunfinished(repo)
1566
1566
1567 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1567 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1568 if node == old.node():
1568 if node == old.node():
1569 ui.status(_("nothing changed\n"))
1569 ui.status(_("nothing changed\n"))
1570 return 1
1570 return 1
1571 else:
1571 else:
1572 def commitfunc(ui, repo, message, match, opts):
1572 def commitfunc(ui, repo, message, match, opts):
1573 overrides = {}
1573 overrides = {}
1574 if opts.get('secret'):
1574 if opts.get('secret'):
1575 overrides[('phases', 'new-commit')] = 'secret'
1575 overrides[('phases', 'new-commit')] = 'secret'
1576
1576
1577 baseui = repo.baseui
1577 baseui = repo.baseui
1578 with baseui.configoverride(overrides, 'commit'):
1578 with baseui.configoverride(overrides, 'commit'):
1579 with ui.configoverride(overrides, 'commit'):
1579 with ui.configoverride(overrides, 'commit'):
1580 editform = cmdutil.mergeeditform(repo[None],
1580 editform = cmdutil.mergeeditform(repo[None],
1581 'commit.normal')
1581 'commit.normal')
1582 editor = cmdutil.getcommiteditor(
1582 editor = cmdutil.getcommiteditor(
1583 editform=editform, **pycompat.strkwargs(opts))
1583 editform=editform, **pycompat.strkwargs(opts))
1584 return repo.commit(message,
1584 return repo.commit(message,
1585 opts.get('user'),
1585 opts.get('user'),
1586 opts.get('date'),
1586 opts.get('date'),
1587 match,
1587 match,
1588 editor=editor,
1588 editor=editor,
1589 extra=extra)
1589 extra=extra)
1590
1590
1591 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1591 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1592
1592
1593 if not node:
1593 if not node:
1594 stat = cmdutil.postcommitstatus(repo, pats, opts)
1594 stat = cmdutil.postcommitstatus(repo, pats, opts)
1595 if stat[3]:
1595 if stat[3]:
1596 ui.status(_("nothing changed (%d missing files, see "
1596 ui.status(_("nothing changed (%d missing files, see "
1597 "'hg status')\n") % len(stat[3]))
1597 "'hg status')\n") % len(stat[3]))
1598 else:
1598 else:
1599 ui.status(_("nothing changed\n"))
1599 ui.status(_("nothing changed\n"))
1600 return 1
1600 return 1
1601
1601
1602 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1602 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1603
1603
1604 @command('config|showconfig|debugconfig',
1604 @command('config|showconfig|debugconfig',
1605 [('u', 'untrusted', None, _('show untrusted configuration options')),
1605 [('u', 'untrusted', None, _('show untrusted configuration options')),
1606 ('e', 'edit', None, _('edit user config')),
1606 ('e', 'edit', None, _('edit user config')),
1607 ('l', 'local', None, _('edit repository config')),
1607 ('l', 'local', None, _('edit repository config')),
1608 ('g', 'global', None, _('edit global config'))] + formatteropts,
1608 ('g', 'global', None, _('edit global config'))] + formatteropts,
1609 _('[-u] [NAME]...'),
1609 _('[-u] [NAME]...'),
1610 optionalrepo=True, cmdtype=readonly)
1610 optionalrepo=True, cmdtype=readonly)
1611 def config(ui, repo, *values, **opts):
1611 def config(ui, repo, *values, **opts):
1612 """show combined config settings from all hgrc files
1612 """show combined config settings from all hgrc files
1613
1613
1614 With no arguments, print names and values of all config items.
1614 With no arguments, print names and values of all config items.
1615
1615
1616 With one argument of the form section.name, print just the value
1616 With one argument of the form section.name, print just the value
1617 of that config item.
1617 of that config item.
1618
1618
1619 With multiple arguments, print names and values of all config
1619 With multiple arguments, print names and values of all config
1620 items with matching section names.
1620 items with matching section names.
1621
1621
1622 With --edit, start an editor on the user-level config file. With
1622 With --edit, start an editor on the user-level config file. With
1623 --global, edit the system-wide config file. With --local, edit the
1623 --global, edit the system-wide config file. With --local, edit the
1624 repository-level config file.
1624 repository-level config file.
1625
1625
1626 With --debug, the source (filename and line number) is printed
1626 With --debug, the source (filename and line number) is printed
1627 for each config item.
1627 for each config item.
1628
1628
1629 See :hg:`help config` for more information about config files.
1629 See :hg:`help config` for more information about config files.
1630
1630
1631 Returns 0 on success, 1 if NAME does not exist.
1631 Returns 0 on success, 1 if NAME does not exist.
1632
1632
1633 """
1633 """
1634
1634
1635 opts = pycompat.byteskwargs(opts)
1635 opts = pycompat.byteskwargs(opts)
1636 if opts.get('edit') or opts.get('local') or opts.get('global'):
1636 if opts.get('edit') or opts.get('local') or opts.get('global'):
1637 if opts.get('local') and opts.get('global'):
1637 if opts.get('local') and opts.get('global'):
1638 raise error.Abort(_("can't use --local and --global together"))
1638 raise error.Abort(_("can't use --local and --global together"))
1639
1639
1640 if opts.get('local'):
1640 if opts.get('local'):
1641 if not repo:
1641 if not repo:
1642 raise error.Abort(_("can't use --local outside a repository"))
1642 raise error.Abort(_("can't use --local outside a repository"))
1643 paths = [repo.vfs.join('hgrc')]
1643 paths = [repo.vfs.join('hgrc')]
1644 elif opts.get('global'):
1644 elif opts.get('global'):
1645 paths = rcutil.systemrcpath()
1645 paths = rcutil.systemrcpath()
1646 else:
1646 else:
1647 paths = rcutil.userrcpath()
1647 paths = rcutil.userrcpath()
1648
1648
1649 for f in paths:
1649 for f in paths:
1650 if os.path.exists(f):
1650 if os.path.exists(f):
1651 break
1651 break
1652 else:
1652 else:
1653 if opts.get('global'):
1653 if opts.get('global'):
1654 samplehgrc = uimod.samplehgrcs['global']
1654 samplehgrc = uimod.samplehgrcs['global']
1655 elif opts.get('local'):
1655 elif opts.get('local'):
1656 samplehgrc = uimod.samplehgrcs['local']
1656 samplehgrc = uimod.samplehgrcs['local']
1657 else:
1657 else:
1658 samplehgrc = uimod.samplehgrcs['user']
1658 samplehgrc = uimod.samplehgrcs['user']
1659
1659
1660 f = paths[0]
1660 f = paths[0]
1661 fp = open(f, "wb")
1661 fp = open(f, "wb")
1662 fp.write(util.tonativeeol(samplehgrc))
1662 fp.write(util.tonativeeol(samplehgrc))
1663 fp.close()
1663 fp.close()
1664
1664
1665 editor = ui.geteditor()
1665 editor = ui.geteditor()
1666 ui.system("%s \"%s\"" % (editor, f),
1666 ui.system("%s \"%s\"" % (editor, f),
1667 onerr=error.Abort, errprefix=_("edit failed"),
1667 onerr=error.Abort, errprefix=_("edit failed"),
1668 blockedtag='config_edit')
1668 blockedtag='config_edit')
1669 return
1669 return
1670 ui.pager('config')
1670 ui.pager('config')
1671 fm = ui.formatter('config', opts)
1671 fm = ui.formatter('config', opts)
1672 for t, f in rcutil.rccomponents():
1672 for t, f in rcutil.rccomponents():
1673 if t == 'path':
1673 if t == 'path':
1674 ui.debug('read config from: %s\n' % f)
1674 ui.debug('read config from: %s\n' % f)
1675 elif t == 'items':
1675 elif t == 'items':
1676 for section, name, value, source in f:
1676 for section, name, value, source in f:
1677 ui.debug('set config by: %s\n' % source)
1677 ui.debug('set config by: %s\n' % source)
1678 else:
1678 else:
1679 raise error.ProgrammingError('unknown rctype: %s' % t)
1679 raise error.ProgrammingError('unknown rctype: %s' % t)
1680 untrusted = bool(opts.get('untrusted'))
1680 untrusted = bool(opts.get('untrusted'))
1681 if values:
1681 if values:
1682 sections = [v for v in values if '.' not in v]
1682 sections = [v for v in values if '.' not in v]
1683 items = [v for v in values if '.' in v]
1683 items = [v for v in values if '.' in v]
1684 if len(items) > 1 or items and sections:
1684 if len(items) > 1 or items and sections:
1685 raise error.Abort(_('only one config item permitted'))
1685 raise error.Abort(_('only one config item permitted'))
1686 matched = False
1686 matched = False
1687 for section, name, value in ui.walkconfig(untrusted=untrusted):
1687 for section, name, value in ui.walkconfig(untrusted=untrusted):
1688 source = ui.configsource(section, name, untrusted)
1688 source = ui.configsource(section, name, untrusted)
1689 value = pycompat.bytestr(value)
1689 value = pycompat.bytestr(value)
1690 if fm.isplain():
1690 if fm.isplain():
1691 source = source or 'none'
1691 source = source or 'none'
1692 value = value.replace('\n', '\\n')
1692 value = value.replace('\n', '\\n')
1693 entryname = section + '.' + name
1693 entryname = section + '.' + name
1694 if values:
1694 if values:
1695 for v in values:
1695 for v in values:
1696 if v == section:
1696 if v == section:
1697 fm.startitem()
1697 fm.startitem()
1698 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1698 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1699 fm.write('name value', '%s=%s\n', entryname, value)
1699 fm.write('name value', '%s=%s\n', entryname, value)
1700 matched = True
1700 matched = True
1701 elif v == entryname:
1701 elif v == entryname:
1702 fm.startitem()
1702 fm.startitem()
1703 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1703 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1704 fm.write('value', '%s\n', value)
1704 fm.write('value', '%s\n', value)
1705 fm.data(name=entryname)
1705 fm.data(name=entryname)
1706 matched = True
1706 matched = True
1707 else:
1707 else:
1708 fm.startitem()
1708 fm.startitem()
1709 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1709 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1710 fm.write('name value', '%s=%s\n', entryname, value)
1710 fm.write('name value', '%s=%s\n', entryname, value)
1711 matched = True
1711 matched = True
1712 fm.end()
1712 fm.end()
1713 if matched:
1713 if matched:
1714 return 0
1714 return 0
1715 return 1
1715 return 1
1716
1716
1717 @command('copy|cp',
1717 @command('copy|cp',
1718 [('A', 'after', None, _('record a copy that has already occurred')),
1718 [('A', 'after', None, _('record a copy that has already occurred')),
1719 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1719 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1720 ] + walkopts + dryrunopts,
1720 ] + walkopts + dryrunopts,
1721 _('[OPTION]... [SOURCE]... DEST'))
1721 _('[OPTION]... [SOURCE]... DEST'))
1722 def copy(ui, repo, *pats, **opts):
1722 def copy(ui, repo, *pats, **opts):
1723 """mark files as copied for the next commit
1723 """mark files as copied for the next commit
1724
1724
1725 Mark dest as having copies of source files. If dest is a
1725 Mark dest as having copies of source files. If dest is a
1726 directory, copies are put in that directory. If dest is a file,
1726 directory, copies are put in that directory. If dest is a file,
1727 the source must be a single file.
1727 the source must be a single file.
1728
1728
1729 By default, this command copies the contents of files as they
1729 By default, this command copies the contents of files as they
1730 exist in the working directory. If invoked with -A/--after, the
1730 exist in the working directory. If invoked with -A/--after, the
1731 operation is recorded, but no copying is performed.
1731 operation is recorded, but no copying is performed.
1732
1732
1733 This command takes effect with the next commit. To undo a copy
1733 This command takes effect with the next commit. To undo a copy
1734 before that, see :hg:`revert`.
1734 before that, see :hg:`revert`.
1735
1735
1736 Returns 0 on success, 1 if errors are encountered.
1736 Returns 0 on success, 1 if errors are encountered.
1737 """
1737 """
1738 opts = pycompat.byteskwargs(opts)
1738 opts = pycompat.byteskwargs(opts)
1739 with repo.wlock(False):
1739 with repo.wlock(False):
1740 return cmdutil.copy(ui, repo, pats, opts)
1740 return cmdutil.copy(ui, repo, pats, opts)
1741
1741
1742 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1742 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1743 def debugcommands(ui, cmd='', *args):
1743 def debugcommands(ui, cmd='', *args):
1744 """list all available commands and options"""
1744 """list all available commands and options"""
1745 for cmd, vals in sorted(table.iteritems()):
1745 for cmd, vals in sorted(table.iteritems()):
1746 cmd = cmd.split('|')[0].strip('^')
1746 cmd = cmd.split('|')[0].strip('^')
1747 opts = ', '.join([i[1] for i in vals[1]])
1747 opts = ', '.join([i[1] for i in vals[1]])
1748 ui.write('%s: %s\n' % (cmd, opts))
1748 ui.write('%s: %s\n' % (cmd, opts))
1749
1749
1750 @command('debugcomplete',
1750 @command('debugcomplete',
1751 [('o', 'options', None, _('show the command options'))],
1751 [('o', 'options', None, _('show the command options'))],
1752 _('[-o] CMD'),
1752 _('[-o] CMD'),
1753 norepo=True)
1753 norepo=True)
1754 def debugcomplete(ui, cmd='', **opts):
1754 def debugcomplete(ui, cmd='', **opts):
1755 """returns the completion list associated with the given command"""
1755 """returns the completion list associated with the given command"""
1756
1756
1757 if opts.get(r'options'):
1757 if opts.get(r'options'):
1758 options = []
1758 options = []
1759 otables = [globalopts]
1759 otables = [globalopts]
1760 if cmd:
1760 if cmd:
1761 aliases, entry = cmdutil.findcmd(cmd, table, False)
1761 aliases, entry = cmdutil.findcmd(cmd, table, False)
1762 otables.append(entry[1])
1762 otables.append(entry[1])
1763 for t in otables:
1763 for t in otables:
1764 for o in t:
1764 for o in t:
1765 if "(DEPRECATED)" in o[3]:
1765 if "(DEPRECATED)" in o[3]:
1766 continue
1766 continue
1767 if o[0]:
1767 if o[0]:
1768 options.append('-%s' % o[0])
1768 options.append('-%s' % o[0])
1769 options.append('--%s' % o[1])
1769 options.append('--%s' % o[1])
1770 ui.write("%s\n" % "\n".join(options))
1770 ui.write("%s\n" % "\n".join(options))
1771 return
1771 return
1772
1772
1773 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1773 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1774 if ui.verbose:
1774 if ui.verbose:
1775 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1775 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1776 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1776 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1777
1777
1778 @command('^diff',
1778 @command('^diff',
1779 [('r', 'rev', [], _('revision'), _('REV')),
1779 [('r', 'rev', [], _('revision'), _('REV')),
1780 ('c', 'change', '', _('change made by revision'), _('REV'))
1780 ('c', 'change', '', _('change made by revision'), _('REV'))
1781 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1781 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1782 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1782 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1783 inferrepo=True, cmdtype=readonly)
1783 inferrepo=True, cmdtype=readonly)
1784 def diff(ui, repo, *pats, **opts):
1784 def diff(ui, repo, *pats, **opts):
1785 """diff repository (or selected files)
1785 """diff repository (or selected files)
1786
1786
1787 Show differences between revisions for the specified files.
1787 Show differences between revisions for the specified files.
1788
1788
1789 Differences between files are shown using the unified diff format.
1789 Differences between files are shown using the unified diff format.
1790
1790
1791 .. note::
1791 .. note::
1792
1792
1793 :hg:`diff` may generate unexpected results for merges, as it will
1793 :hg:`diff` may generate unexpected results for merges, as it will
1794 default to comparing against the working directory's first
1794 default to comparing against the working directory's first
1795 parent changeset if no revisions are specified.
1795 parent changeset if no revisions are specified.
1796
1796
1797 When two revision arguments are given, then changes are shown
1797 When two revision arguments are given, then changes are shown
1798 between those revisions. If only one revision is specified then
1798 between those revisions. If only one revision is specified then
1799 that revision is compared to the working directory, and, when no
1799 that revision is compared to the working directory, and, when no
1800 revisions are specified, the working directory files are compared
1800 revisions are specified, the working directory files are compared
1801 to its first parent.
1801 to its first parent.
1802
1802
1803 Alternatively you can specify -c/--change with a revision to see
1803 Alternatively you can specify -c/--change with a revision to see
1804 the changes in that changeset relative to its first parent.
1804 the changes in that changeset relative to its first parent.
1805
1805
1806 Without the -a/--text option, diff will avoid generating diffs of
1806 Without the -a/--text option, diff will avoid generating diffs of
1807 files it detects as binary. With -a, diff will generate a diff
1807 files it detects as binary. With -a, diff will generate a diff
1808 anyway, probably with undesirable results.
1808 anyway, probably with undesirable results.
1809
1809
1810 Use the -g/--git option to generate diffs in the git extended diff
1810 Use the -g/--git option to generate diffs in the git extended diff
1811 format. For more information, read :hg:`help diffs`.
1811 format. For more information, read :hg:`help diffs`.
1812
1812
1813 .. container:: verbose
1813 .. container:: verbose
1814
1814
1815 Examples:
1815 Examples:
1816
1816
1817 - compare a file in the current working directory to its parent::
1817 - compare a file in the current working directory to its parent::
1818
1818
1819 hg diff foo.c
1819 hg diff foo.c
1820
1820
1821 - compare two historical versions of a directory, with rename info::
1821 - compare two historical versions of a directory, with rename info::
1822
1822
1823 hg diff --git -r 1.0:1.2 lib/
1823 hg diff --git -r 1.0:1.2 lib/
1824
1824
1825 - get change stats relative to the last change on some date::
1825 - get change stats relative to the last change on some date::
1826
1826
1827 hg diff --stat -r "date('may 2')"
1827 hg diff --stat -r "date('may 2')"
1828
1828
1829 - diff all newly-added files that contain a keyword::
1829 - diff all newly-added files that contain a keyword::
1830
1830
1831 hg diff "set:added() and grep(GNU)"
1831 hg diff "set:added() and grep(GNU)"
1832
1832
1833 - compare a revision and its parents::
1833 - compare a revision and its parents::
1834
1834
1835 hg diff -c 9353 # compare against first parent
1835 hg diff -c 9353 # compare against first parent
1836 hg diff -r 9353^:9353 # same using revset syntax
1836 hg diff -r 9353^:9353 # same using revset syntax
1837 hg diff -r 9353^2:9353 # compare against the second parent
1837 hg diff -r 9353^2:9353 # compare against the second parent
1838
1838
1839 Returns 0 on success.
1839 Returns 0 on success.
1840 """
1840 """
1841
1841
1842 opts = pycompat.byteskwargs(opts)
1842 opts = pycompat.byteskwargs(opts)
1843 revs = opts.get('rev')
1843 revs = opts.get('rev')
1844 change = opts.get('change')
1844 change = opts.get('change')
1845 stat = opts.get('stat')
1845 stat = opts.get('stat')
1846 reverse = opts.get('reverse')
1846 reverse = opts.get('reverse')
1847
1847
1848 if revs and change:
1848 if revs and change:
1849 msg = _('cannot specify --rev and --change at the same time')
1849 msg = _('cannot specify --rev and --change at the same time')
1850 raise error.Abort(msg)
1850 raise error.Abort(msg)
1851 elif change:
1851 elif change:
1852 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1852 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1853 node2 = scmutil.revsingle(repo, change, None).node()
1853 node2 = scmutil.revsingle(repo, change, None).node()
1854 node1 = repo[node2].p1().node()
1854 node1 = repo[node2].p1().node()
1855 else:
1855 else:
1856 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1856 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1857 node1, node2 = scmutil.revpair(repo, revs)
1857 node1, node2 = scmutil.revpair(repo, revs)
1858
1858
1859 if reverse:
1859 if reverse:
1860 node1, node2 = node2, node1
1860 node1, node2 = node2, node1
1861
1861
1862 diffopts = patch.diffallopts(ui, opts)
1862 diffopts = patch.diffallopts(ui, opts)
1863 m = scmutil.match(repo[node2], pats, opts)
1863 m = scmutil.match(repo[node2], pats, opts)
1864 ui.pager('diff')
1864 ui.pager('diff')
1865 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1865 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1866 listsubrepos=opts.get('subrepos'),
1866 listsubrepos=opts.get('subrepos'),
1867 root=opts.get('root'))
1867 root=opts.get('root'))
1868
1868
1869 @command('^export',
1869 @command('^export',
1870 [('o', 'output', '',
1870 [('o', 'output', '',
1871 _('print output to file with formatted name'), _('FORMAT')),
1871 _('print output to file with formatted name'), _('FORMAT')),
1872 ('', 'switch-parent', None, _('diff against the second parent')),
1872 ('', 'switch-parent', None, _('diff against the second parent')),
1873 ('r', 'rev', [], _('revisions to export'), _('REV')),
1873 ('r', 'rev', [], _('revisions to export'), _('REV')),
1874 ] + diffopts,
1874 ] + diffopts,
1875 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1875 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1876 def export(ui, repo, *changesets, **opts):
1876 def export(ui, repo, *changesets, **opts):
1877 """dump the header and diffs for one or more changesets
1877 """dump the header and diffs for one or more changesets
1878
1878
1879 Print the changeset header and diffs for one or more revisions.
1879 Print the changeset header and diffs for one or more revisions.
1880 If no revision is given, the parent of the working directory is used.
1880 If no revision is given, the parent of the working directory is used.
1881
1881
1882 The information shown in the changeset header is: author, date,
1882 The information shown in the changeset header is: author, date,
1883 branch name (if non-default), changeset hash, parent(s) and commit
1883 branch name (if non-default), changeset hash, parent(s) and commit
1884 comment.
1884 comment.
1885
1885
1886 .. note::
1886 .. note::
1887
1887
1888 :hg:`export` may generate unexpected diff output for merge
1888 :hg:`export` may generate unexpected diff output for merge
1889 changesets, as it will compare the merge changeset against its
1889 changesets, as it will compare the merge changeset against its
1890 first parent only.
1890 first parent only.
1891
1891
1892 Output may be to a file, in which case the name of the file is
1892 Output may be to a file, in which case the name of the file is
1893 given using a format string. The formatting rules are as follows:
1893 given using a format string. The formatting rules are as follows:
1894
1894
1895 :``%%``: literal "%" character
1895 :``%%``: literal "%" character
1896 :``%H``: changeset hash (40 hexadecimal digits)
1896 :``%H``: changeset hash (40 hexadecimal digits)
1897 :``%N``: number of patches being generated
1897 :``%N``: number of patches being generated
1898 :``%R``: changeset revision number
1898 :``%R``: changeset revision number
1899 :``%b``: basename of the exporting repository
1899 :``%b``: basename of the exporting repository
1900 :``%h``: short-form changeset hash (12 hexadecimal digits)
1900 :``%h``: short-form changeset hash (12 hexadecimal digits)
1901 :``%m``: first line of the commit message (only alphanumeric characters)
1901 :``%m``: first line of the commit message (only alphanumeric characters)
1902 :``%n``: zero-padded sequence number, starting at 1
1902 :``%n``: zero-padded sequence number, starting at 1
1903 :``%r``: zero-padded changeset revision number
1903 :``%r``: zero-padded changeset revision number
1904
1904
1905 Without the -a/--text option, export will avoid generating diffs
1905 Without the -a/--text option, export will avoid generating diffs
1906 of files it detects as binary. With -a, export will generate a
1906 of files it detects as binary. With -a, export will generate a
1907 diff anyway, probably with undesirable results.
1907 diff anyway, probably with undesirable results.
1908
1908
1909 Use the -g/--git option to generate diffs in the git extended diff
1909 Use the -g/--git option to generate diffs in the git extended diff
1910 format. See :hg:`help diffs` for more information.
1910 format. See :hg:`help diffs` for more information.
1911
1911
1912 With the --switch-parent option, the diff will be against the
1912 With the --switch-parent option, the diff will be against the
1913 second parent. It can be useful to review a merge.
1913 second parent. It can be useful to review a merge.
1914
1914
1915 .. container:: verbose
1915 .. container:: verbose
1916
1916
1917 Examples:
1917 Examples:
1918
1918
1919 - use export and import to transplant a bugfix to the current
1919 - use export and import to transplant a bugfix to the current
1920 branch::
1920 branch::
1921
1921
1922 hg export -r 9353 | hg import -
1922 hg export -r 9353 | hg import -
1923
1923
1924 - export all the changesets between two revisions to a file with
1924 - export all the changesets between two revisions to a file with
1925 rename information::
1925 rename information::
1926
1926
1927 hg export --git -r 123:150 > changes.txt
1927 hg export --git -r 123:150 > changes.txt
1928
1928
1929 - split outgoing changes into a series of patches with
1929 - split outgoing changes into a series of patches with
1930 descriptive names::
1930 descriptive names::
1931
1931
1932 hg export -r "outgoing()" -o "%n-%m.patch"
1932 hg export -r "outgoing()" -o "%n-%m.patch"
1933
1933
1934 Returns 0 on success.
1934 Returns 0 on success.
1935 """
1935 """
1936 opts = pycompat.byteskwargs(opts)
1936 opts = pycompat.byteskwargs(opts)
1937 changesets += tuple(opts.get('rev', []))
1937 changesets += tuple(opts.get('rev', []))
1938 if not changesets:
1938 if not changesets:
1939 changesets = ['.']
1939 changesets = ['.']
1940 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1940 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1941 revs = scmutil.revrange(repo, changesets)
1941 revs = scmutil.revrange(repo, changesets)
1942 if not revs:
1942 if not revs:
1943 raise error.Abort(_("export requires at least one changeset"))
1943 raise error.Abort(_("export requires at least one changeset"))
1944 if len(revs) > 1:
1944 if len(revs) > 1:
1945 ui.note(_('exporting patches:\n'))
1945 ui.note(_('exporting patches:\n'))
1946 else:
1946 else:
1947 ui.note(_('exporting patch:\n'))
1947 ui.note(_('exporting patch:\n'))
1948 ui.pager('export')
1948 ui.pager('export')
1949 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1949 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1950 switch_parent=opts.get('switch_parent'),
1950 switch_parent=opts.get('switch_parent'),
1951 opts=patch.diffallopts(ui, opts))
1951 opts=patch.diffallopts(ui, opts))
1952
1952
1953 @command('files',
1953 @command('files',
1954 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1954 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1955 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1955 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1956 ] + walkopts + formatteropts + subrepoopts,
1956 ] + walkopts + formatteropts + subrepoopts,
1957 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1957 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1958 def files(ui, repo, *pats, **opts):
1958 def files(ui, repo, *pats, **opts):
1959 """list tracked files
1959 """list tracked files
1960
1960
1961 Print files under Mercurial control in the working directory or
1961 Print files under Mercurial control in the working directory or
1962 specified revision for given files (excluding removed files).
1962 specified revision for given files (excluding removed files).
1963 Files can be specified as filenames or filesets.
1963 Files can be specified as filenames or filesets.
1964
1964
1965 If no files are given to match, this command prints the names
1965 If no files are given to match, this command prints the names
1966 of all files under Mercurial control.
1966 of all files under Mercurial control.
1967
1967
1968 .. container:: verbose
1968 .. container:: verbose
1969
1969
1970 Examples:
1970 Examples:
1971
1971
1972 - list all files under the current directory::
1972 - list all files under the current directory::
1973
1973
1974 hg files .
1974 hg files .
1975
1975
1976 - shows sizes and flags for current revision::
1976 - shows sizes and flags for current revision::
1977
1977
1978 hg files -vr .
1978 hg files -vr .
1979
1979
1980 - list all files named README::
1980 - list all files named README::
1981
1981
1982 hg files -I "**/README"
1982 hg files -I "**/README"
1983
1983
1984 - list all binary files::
1984 - list all binary files::
1985
1985
1986 hg files "set:binary()"
1986 hg files "set:binary()"
1987
1987
1988 - find files containing a regular expression::
1988 - find files containing a regular expression::
1989
1989
1990 hg files "set:grep('bob')"
1990 hg files "set:grep('bob')"
1991
1991
1992 - search tracked file contents with xargs and grep::
1992 - search tracked file contents with xargs and grep::
1993
1993
1994 hg files -0 | xargs -0 grep foo
1994 hg files -0 | xargs -0 grep foo
1995
1995
1996 See :hg:`help patterns` and :hg:`help filesets` for more information
1996 See :hg:`help patterns` and :hg:`help filesets` for more information
1997 on specifying file patterns.
1997 on specifying file patterns.
1998
1998
1999 Returns 0 if a match is found, 1 otherwise.
1999 Returns 0 if a match is found, 1 otherwise.
2000
2000
2001 """
2001 """
2002
2002
2003 opts = pycompat.byteskwargs(opts)
2003 opts = pycompat.byteskwargs(opts)
2004 rev = opts.get('rev')
2004 rev = opts.get('rev')
2005 if rev:
2005 if rev:
2006 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2006 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2007 ctx = scmutil.revsingle(repo, rev, None)
2007 ctx = scmutil.revsingle(repo, rev, None)
2008
2008
2009 end = '\n'
2009 end = '\n'
2010 if opts.get('print0'):
2010 if opts.get('print0'):
2011 end = '\0'
2011 end = '\0'
2012 fmt = '%s' + end
2012 fmt = '%s' + end
2013
2013
2014 m = scmutil.match(ctx, pats, opts)
2014 m = scmutil.match(ctx, pats, opts)
2015 ui.pager('files')
2015 ui.pager('files')
2016 with ui.formatter('files', opts) as fm:
2016 with ui.formatter('files', opts) as fm:
2017 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2017 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2018
2018
2019 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2019 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2020 def forget(ui, repo, *pats, **opts):
2020 def forget(ui, repo, *pats, **opts):
2021 """forget the specified files on the next commit
2021 """forget the specified files on the next commit
2022
2022
2023 Mark the specified files so they will no longer be tracked
2023 Mark the specified files so they will no longer be tracked
2024 after the next commit.
2024 after the next commit.
2025
2025
2026 This only removes files from the current branch, not from the
2026 This only removes files from the current branch, not from the
2027 entire project history, and it does not delete them from the
2027 entire project history, and it does not delete them from the
2028 working directory.
2028 working directory.
2029
2029
2030 To delete the file from the working directory, see :hg:`remove`.
2030 To delete the file from the working directory, see :hg:`remove`.
2031
2031
2032 To undo a forget before the next commit, see :hg:`add`.
2032 To undo a forget before the next commit, see :hg:`add`.
2033
2033
2034 .. container:: verbose
2034 .. container:: verbose
2035
2035
2036 Examples:
2036 Examples:
2037
2037
2038 - forget newly-added binary files::
2038 - forget newly-added binary files::
2039
2039
2040 hg forget "set:added() and binary()"
2040 hg forget "set:added() and binary()"
2041
2041
2042 - forget files that would be excluded by .hgignore::
2042 - forget files that would be excluded by .hgignore::
2043
2043
2044 hg forget "set:hgignore()"
2044 hg forget "set:hgignore()"
2045
2045
2046 Returns 0 on success.
2046 Returns 0 on success.
2047 """
2047 """
2048
2048
2049 opts = pycompat.byteskwargs(opts)
2049 opts = pycompat.byteskwargs(opts)
2050 if not pats:
2050 if not pats:
2051 raise error.Abort(_('no files specified'))
2051 raise error.Abort(_('no files specified'))
2052
2052
2053 m = scmutil.match(repo[None], pats, opts)
2053 m = scmutil.match(repo[None], pats, opts)
2054 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2054 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2055 return rejected and 1 or 0
2055 return rejected and 1 or 0
2056
2056
2057 @command(
2057 @command(
2058 'graft',
2058 'graft',
2059 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2059 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2060 ('c', 'continue', False, _('resume interrupted graft')),
2060 ('c', 'continue', False, _('resume interrupted graft')),
2061 ('e', 'edit', False, _('invoke editor on commit messages')),
2061 ('e', 'edit', False, _('invoke editor on commit messages')),
2062 ('', 'log', None, _('append graft info to log message')),
2062 ('', 'log', None, _('append graft info to log message')),
2063 ('f', 'force', False, _('force graft')),
2063 ('f', 'force', False, _('force graft')),
2064 ('D', 'currentdate', False,
2064 ('D', 'currentdate', False,
2065 _('record the current date as commit date')),
2065 _('record the current date as commit date')),
2066 ('U', 'currentuser', False,
2066 ('U', 'currentuser', False,
2067 _('record the current user as committer'), _('DATE'))]
2067 _('record the current user as committer'), _('DATE'))]
2068 + commitopts2 + mergetoolopts + dryrunopts,
2068 + commitopts2 + mergetoolopts + dryrunopts,
2069 _('[OPTION]... [-r REV]... REV...'))
2069 _('[OPTION]... [-r REV]... REV...'))
2070 def graft(ui, repo, *revs, **opts):
2070 def graft(ui, repo, *revs, **opts):
2071 '''copy changes from other branches onto the current branch
2071 '''copy changes from other branches onto the current branch
2072
2072
2073 This command uses Mercurial's merge logic to copy individual
2073 This command uses Mercurial's merge logic to copy individual
2074 changes from other branches without merging branches in the
2074 changes from other branches without merging branches in the
2075 history graph. This is sometimes known as 'backporting' or
2075 history graph. This is sometimes known as 'backporting' or
2076 'cherry-picking'. By default, graft will copy user, date, and
2076 'cherry-picking'. By default, graft will copy user, date, and
2077 description from the source changesets.
2077 description from the source changesets.
2078
2078
2079 Changesets that are ancestors of the current revision, that have
2079 Changesets that are ancestors of the current revision, that have
2080 already been grafted, or that are merges will be skipped.
2080 already been grafted, or that are merges will be skipped.
2081
2081
2082 If --log is specified, log messages will have a comment appended
2082 If --log is specified, log messages will have a comment appended
2083 of the form::
2083 of the form::
2084
2084
2085 (grafted from CHANGESETHASH)
2085 (grafted from CHANGESETHASH)
2086
2086
2087 If --force is specified, revisions will be grafted even if they
2087 If --force is specified, revisions will be grafted even if they
2088 are already ancestors of, or have been grafted to, the destination.
2088 are already ancestors of, or have been grafted to, the destination.
2089 This is useful when the revisions have since been backed out.
2089 This is useful when the revisions have since been backed out.
2090
2090
2091 If a graft merge results in conflicts, the graft process is
2091 If a graft merge results in conflicts, the graft process is
2092 interrupted so that the current merge can be manually resolved.
2092 interrupted so that the current merge can be manually resolved.
2093 Once all conflicts are addressed, the graft process can be
2093 Once all conflicts are addressed, the graft process can be
2094 continued with the -c/--continue option.
2094 continued with the -c/--continue option.
2095
2095
2096 .. note::
2096 .. note::
2097
2097
2098 The -c/--continue option does not reapply earlier options, except
2098 The -c/--continue option does not reapply earlier options, except
2099 for --force.
2099 for --force.
2100
2100
2101 .. container:: verbose
2101 .. container:: verbose
2102
2102
2103 Examples:
2103 Examples:
2104
2104
2105 - copy a single change to the stable branch and edit its description::
2105 - copy a single change to the stable branch and edit its description::
2106
2106
2107 hg update stable
2107 hg update stable
2108 hg graft --edit 9393
2108 hg graft --edit 9393
2109
2109
2110 - graft a range of changesets with one exception, updating dates::
2110 - graft a range of changesets with one exception, updating dates::
2111
2111
2112 hg graft -D "2085::2093 and not 2091"
2112 hg graft -D "2085::2093 and not 2091"
2113
2113
2114 - continue a graft after resolving conflicts::
2114 - continue a graft after resolving conflicts::
2115
2115
2116 hg graft -c
2116 hg graft -c
2117
2117
2118 - show the source of a grafted changeset::
2118 - show the source of a grafted changeset::
2119
2119
2120 hg log --debug -r .
2120 hg log --debug -r .
2121
2121
2122 - show revisions sorted by date::
2122 - show revisions sorted by date::
2123
2123
2124 hg log -r "sort(all(), date)"
2124 hg log -r "sort(all(), date)"
2125
2125
2126 See :hg:`help revisions` for more about specifying revisions.
2126 See :hg:`help revisions` for more about specifying revisions.
2127
2127
2128 Returns 0 on successful completion.
2128 Returns 0 on successful completion.
2129 '''
2129 '''
2130 with repo.wlock():
2130 with repo.wlock():
2131 return _dograft(ui, repo, *revs, **opts)
2131 return _dograft(ui, repo, *revs, **opts)
2132
2132
2133 def _dograft(ui, repo, *revs, **opts):
2133 def _dograft(ui, repo, *revs, **opts):
2134 opts = pycompat.byteskwargs(opts)
2134 opts = pycompat.byteskwargs(opts)
2135 if revs and opts.get('rev'):
2135 if revs and opts.get('rev'):
2136 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2136 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2137 'revision ordering!\n'))
2137 'revision ordering!\n'))
2138
2138
2139 revs = list(revs)
2139 revs = list(revs)
2140 revs.extend(opts.get('rev'))
2140 revs.extend(opts.get('rev'))
2141
2141
2142 if not opts.get('user') and opts.get('currentuser'):
2142 if not opts.get('user') and opts.get('currentuser'):
2143 opts['user'] = ui.username()
2143 opts['user'] = ui.username()
2144 if not opts.get('date') and opts.get('currentdate'):
2144 if not opts.get('date') and opts.get('currentdate'):
2145 opts['date'] = "%d %d" % util.makedate()
2145 opts['date'] = "%d %d" % util.makedate()
2146
2146
2147 editor = cmdutil.getcommiteditor(editform='graft',
2147 editor = cmdutil.getcommiteditor(editform='graft',
2148 **pycompat.strkwargs(opts))
2148 **pycompat.strkwargs(opts))
2149
2149
2150 cont = False
2150 cont = False
2151 if opts.get('continue'):
2151 if opts.get('continue'):
2152 cont = True
2152 cont = True
2153 if revs:
2153 if revs:
2154 raise error.Abort(_("can't specify --continue and revisions"))
2154 raise error.Abort(_("can't specify --continue and revisions"))
2155 # read in unfinished revisions
2155 # read in unfinished revisions
2156 try:
2156 try:
2157 nodes = repo.vfs.read('graftstate').splitlines()
2157 nodes = repo.vfs.read('graftstate').splitlines()
2158 revs = [repo[node].rev() for node in nodes]
2158 revs = [repo[node].rev() for node in nodes]
2159 except IOError as inst:
2159 except IOError as inst:
2160 if inst.errno != errno.ENOENT:
2160 if inst.errno != errno.ENOENT:
2161 raise
2161 raise
2162 cmdutil.wrongtooltocontinue(repo, _('graft'))
2162 cmdutil.wrongtooltocontinue(repo, _('graft'))
2163 else:
2163 else:
2164 cmdutil.checkunfinished(repo)
2164 cmdutil.checkunfinished(repo)
2165 cmdutil.bailifchanged(repo)
2165 cmdutil.bailifchanged(repo)
2166 if not revs:
2166 if not revs:
2167 raise error.Abort(_('no revisions specified'))
2167 raise error.Abort(_('no revisions specified'))
2168 revs = scmutil.revrange(repo, revs)
2168 revs = scmutil.revrange(repo, revs)
2169
2169
2170 skipped = set()
2170 skipped = set()
2171 # check for merges
2171 # check for merges
2172 for rev in repo.revs('%ld and merge()', revs):
2172 for rev in repo.revs('%ld and merge()', revs):
2173 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2173 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2174 skipped.add(rev)
2174 skipped.add(rev)
2175 revs = [r for r in revs if r not in skipped]
2175 revs = [r for r in revs if r not in skipped]
2176 if not revs:
2176 if not revs:
2177 return -1
2177 return -1
2178
2178
2179 # Don't check in the --continue case, in effect retaining --force across
2179 # Don't check in the --continue case, in effect retaining --force across
2180 # --continues. That's because without --force, any revisions we decided to
2180 # --continues. That's because without --force, any revisions we decided to
2181 # skip would have been filtered out here, so they wouldn't have made their
2181 # skip would have been filtered out here, so they wouldn't have made their
2182 # way to the graftstate. With --force, any revisions we would have otherwise
2182 # way to the graftstate. With --force, any revisions we would have otherwise
2183 # skipped would not have been filtered out, and if they hadn't been applied
2183 # skipped would not have been filtered out, and if they hadn't been applied
2184 # already, they'd have been in the graftstate.
2184 # already, they'd have been in the graftstate.
2185 if not (cont or opts.get('force')):
2185 if not (cont or opts.get('force')):
2186 # check for ancestors of dest branch
2186 # check for ancestors of dest branch
2187 crev = repo['.'].rev()
2187 crev = repo['.'].rev()
2188 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2188 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2189 # XXX make this lazy in the future
2189 # XXX make this lazy in the future
2190 # don't mutate while iterating, create a copy
2190 # don't mutate while iterating, create a copy
2191 for rev in list(revs):
2191 for rev in list(revs):
2192 if rev in ancestors:
2192 if rev in ancestors:
2193 ui.warn(_('skipping ancestor revision %d:%s\n') %
2193 ui.warn(_('skipping ancestor revision %d:%s\n') %
2194 (rev, repo[rev]))
2194 (rev, repo[rev]))
2195 # XXX remove on list is slow
2195 # XXX remove on list is slow
2196 revs.remove(rev)
2196 revs.remove(rev)
2197 if not revs:
2197 if not revs:
2198 return -1
2198 return -1
2199
2199
2200 # analyze revs for earlier grafts
2200 # analyze revs for earlier grafts
2201 ids = {}
2201 ids = {}
2202 for ctx in repo.set("%ld", revs):
2202 for ctx in repo.set("%ld", revs):
2203 ids[ctx.hex()] = ctx.rev()
2203 ids[ctx.hex()] = ctx.rev()
2204 n = ctx.extra().get('source')
2204 n = ctx.extra().get('source')
2205 if n:
2205 if n:
2206 ids[n] = ctx.rev()
2206 ids[n] = ctx.rev()
2207
2207
2208 # check ancestors for earlier grafts
2208 # check ancestors for earlier grafts
2209 ui.debug('scanning for duplicate grafts\n')
2209 ui.debug('scanning for duplicate grafts\n')
2210
2210
2211 # The only changesets we can be sure doesn't contain grafts of any
2211 # The only changesets we can be sure doesn't contain grafts of any
2212 # revs, are the ones that are common ancestors of *all* revs:
2212 # revs, are the ones that are common ancestors of *all* revs:
2213 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2213 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2214 ctx = repo[rev]
2214 ctx = repo[rev]
2215 n = ctx.extra().get('source')
2215 n = ctx.extra().get('source')
2216 if n in ids:
2216 if n in ids:
2217 try:
2217 try:
2218 r = repo[n].rev()
2218 r = repo[n].rev()
2219 except error.RepoLookupError:
2219 except error.RepoLookupError:
2220 r = None
2220 r = None
2221 if r in revs:
2221 if r in revs:
2222 ui.warn(_('skipping revision %d:%s '
2222 ui.warn(_('skipping revision %d:%s '
2223 '(already grafted to %d:%s)\n')
2223 '(already grafted to %d:%s)\n')
2224 % (r, repo[r], rev, ctx))
2224 % (r, repo[r], rev, ctx))
2225 revs.remove(r)
2225 revs.remove(r)
2226 elif ids[n] in revs:
2226 elif ids[n] in revs:
2227 if r is None:
2227 if r is None:
2228 ui.warn(_('skipping already grafted revision %d:%s '
2228 ui.warn(_('skipping already grafted revision %d:%s '
2229 '(%d:%s also has unknown origin %s)\n')
2229 '(%d:%s also has unknown origin %s)\n')
2230 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2230 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2231 else:
2231 else:
2232 ui.warn(_('skipping already grafted revision %d:%s '
2232 ui.warn(_('skipping already grafted revision %d:%s '
2233 '(%d:%s also has origin %d:%s)\n')
2233 '(%d:%s also has origin %d:%s)\n')
2234 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2234 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2235 revs.remove(ids[n])
2235 revs.remove(ids[n])
2236 elif ctx.hex() in ids:
2236 elif ctx.hex() in ids:
2237 r = ids[ctx.hex()]
2237 r = ids[ctx.hex()]
2238 ui.warn(_('skipping already grafted revision %d:%s '
2238 ui.warn(_('skipping already grafted revision %d:%s '
2239 '(was grafted from %d:%s)\n') %
2239 '(was grafted from %d:%s)\n') %
2240 (r, repo[r], rev, ctx))
2240 (r, repo[r], rev, ctx))
2241 revs.remove(r)
2241 revs.remove(r)
2242 if not revs:
2242 if not revs:
2243 return -1
2243 return -1
2244
2244
2245 for pos, ctx in enumerate(repo.set("%ld", revs)):
2245 for pos, ctx in enumerate(repo.set("%ld", revs)):
2246 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2246 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2247 ctx.description().split('\n', 1)[0])
2247 ctx.description().split('\n', 1)[0])
2248 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2248 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2249 if names:
2249 if names:
2250 desc += ' (%s)' % ' '.join(names)
2250 desc += ' (%s)' % ' '.join(names)
2251 ui.status(_('grafting %s\n') % desc)
2251 ui.status(_('grafting %s\n') % desc)
2252 if opts.get('dry_run'):
2252 if opts.get('dry_run'):
2253 continue
2253 continue
2254
2254
2255 source = ctx.extra().get('source')
2255 source = ctx.extra().get('source')
2256 extra = {}
2256 extra = {}
2257 if source:
2257 if source:
2258 extra['source'] = source
2258 extra['source'] = source
2259 extra['intermediate-source'] = ctx.hex()
2259 extra['intermediate-source'] = ctx.hex()
2260 else:
2260 else:
2261 extra['source'] = ctx.hex()
2261 extra['source'] = ctx.hex()
2262 user = ctx.user()
2262 user = ctx.user()
2263 if opts.get('user'):
2263 if opts.get('user'):
2264 user = opts['user']
2264 user = opts['user']
2265 date = ctx.date()
2265 date = ctx.date()
2266 if opts.get('date'):
2266 if opts.get('date'):
2267 date = opts['date']
2267 date = opts['date']
2268 message = ctx.description()
2268 message = ctx.description()
2269 if opts.get('log'):
2269 if opts.get('log'):
2270 message += '\n(grafted from %s)' % ctx.hex()
2270 message += '\n(grafted from %s)' % ctx.hex()
2271
2271
2272 # we don't merge the first commit when continuing
2272 # we don't merge the first commit when continuing
2273 if not cont:
2273 if not cont:
2274 # perform the graft merge with p1(rev) as 'ancestor'
2274 # perform the graft merge with p1(rev) as 'ancestor'
2275 try:
2275 try:
2276 # ui.forcemerge is an internal variable, do not document
2276 # ui.forcemerge is an internal variable, do not document
2277 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2277 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2278 'graft')
2278 'graft')
2279 stats = mergemod.graft(repo, ctx, ctx.p1(),
2279 stats = mergemod.graft(repo, ctx, ctx.p1(),
2280 ['local', 'graft'])
2280 ['local', 'graft'])
2281 finally:
2281 finally:
2282 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2282 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2283 # report any conflicts
2283 # report any conflicts
2284 if stats and stats[3] > 0:
2284 if stats and stats[3] > 0:
2285 # write out state for --continue
2285 # write out state for --continue
2286 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2286 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2287 repo.vfs.write('graftstate', ''.join(nodelines))
2287 repo.vfs.write('graftstate', ''.join(nodelines))
2288 extra = ''
2288 extra = ''
2289 if opts.get('user'):
2289 if opts.get('user'):
2290 extra += ' --user %s' % util.shellquote(opts['user'])
2290 extra += ' --user %s' % util.shellquote(opts['user'])
2291 if opts.get('date'):
2291 if opts.get('date'):
2292 extra += ' --date %s' % util.shellquote(opts['date'])
2292 extra += ' --date %s' % util.shellquote(opts['date'])
2293 if opts.get('log'):
2293 if opts.get('log'):
2294 extra += ' --log'
2294 extra += ' --log'
2295 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2295 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2296 raise error.Abort(
2296 raise error.Abort(
2297 _("unresolved conflicts, can't continue"),
2297 _("unresolved conflicts, can't continue"),
2298 hint=hint)
2298 hint=hint)
2299 else:
2299 else:
2300 cont = False
2300 cont = False
2301
2301
2302 # commit
2302 # commit
2303 node = repo.commit(text=message, user=user,
2303 node = repo.commit(text=message, user=user,
2304 date=date, extra=extra, editor=editor)
2304 date=date, extra=extra, editor=editor)
2305 if node is None:
2305 if node is None:
2306 ui.warn(
2306 ui.warn(
2307 _('note: graft of %d:%s created no changes to commit\n') %
2307 _('note: graft of %d:%s created no changes to commit\n') %
2308 (ctx.rev(), ctx))
2308 (ctx.rev(), ctx))
2309
2309
2310 # remove state when we complete successfully
2310 # remove state when we complete successfully
2311 if not opts.get('dry_run'):
2311 if not opts.get('dry_run'):
2312 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2312 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2313
2313
2314 return 0
2314 return 0
2315
2315
2316 @command('grep',
2316 @command('grep',
2317 [('0', 'print0', None, _('end fields with NUL')),
2317 [('0', 'print0', None, _('end fields with NUL')),
2318 ('', 'all', None, _('print all revisions that match')),
2318 ('', 'all', None, _('print all revisions that match')),
2319 ('a', 'text', None, _('treat all files as text')),
2319 ('a', 'text', None, _('treat all files as text')),
2320 ('f', 'follow', None,
2320 ('f', 'follow', None,
2321 _('follow changeset history,'
2321 _('follow changeset history,'
2322 ' or file history across copies and renames')),
2322 ' or file history across copies and renames')),
2323 ('i', 'ignore-case', None, _('ignore case when matching')),
2323 ('i', 'ignore-case', None, _('ignore case when matching')),
2324 ('l', 'files-with-matches', None,
2324 ('l', 'files-with-matches', None,
2325 _('print only filenames and revisions that match')),
2325 _('print only filenames and revisions that match')),
2326 ('n', 'line-number', None, _('print matching line numbers')),
2326 ('n', 'line-number', None, _('print matching line numbers')),
2327 ('r', 'rev', [],
2327 ('r', 'rev', [],
2328 _('only search files changed within revision range'), _('REV')),
2328 _('only search files changed within revision range'), _('REV')),
2329 ('u', 'user', None, _('list the author (long with -v)')),
2329 ('u', 'user', None, _('list the author (long with -v)')),
2330 ('d', 'date', None, _('list the date (short with -q)')),
2330 ('d', 'date', None, _('list the date (short with -q)')),
2331 ] + formatteropts + walkopts,
2331 ] + formatteropts + walkopts,
2332 _('[OPTION]... PATTERN [FILE]...'),
2332 _('[OPTION]... PATTERN [FILE]...'),
2333 inferrepo=True, cmdtype=readonly)
2333 inferrepo=True, cmdtype=readonly)
2334 def grep(ui, repo, pattern, *pats, **opts):
2334 def grep(ui, repo, pattern, *pats, **opts):
2335 """search revision history for a pattern in specified files
2335 """search revision history for a pattern in specified files
2336
2336
2337 Search revision history for a regular expression in the specified
2337 Search revision history for a regular expression in the specified
2338 files or the entire project.
2338 files or the entire project.
2339
2339
2340 By default, grep prints the most recent revision number for each
2340 By default, grep prints the most recent revision number for each
2341 file in which it finds a match. To get it to print every revision
2341 file in which it finds a match. To get it to print every revision
2342 that contains a change in match status ("-" for a match that becomes
2342 that contains a change in match status ("-" for a match that becomes
2343 a non-match, or "+" for a non-match that becomes a match), use the
2343 a non-match, or "+" for a non-match that becomes a match), use the
2344 --all flag.
2344 --all flag.
2345
2345
2346 PATTERN can be any Python (roughly Perl-compatible) regular
2346 PATTERN can be any Python (roughly Perl-compatible) regular
2347 expression.
2347 expression.
2348
2348
2349 If no FILEs are specified (and -f/--follow isn't set), all files in
2349 If no FILEs are specified (and -f/--follow isn't set), all files in
2350 the repository are searched, including those that don't exist in the
2350 the repository are searched, including those that don't exist in the
2351 current branch or have been deleted in a prior changeset.
2351 current branch or have been deleted in a prior changeset.
2352
2352
2353 Returns 0 if a match is found, 1 otherwise.
2353 Returns 0 if a match is found, 1 otherwise.
2354 """
2354 """
2355 opts = pycompat.byteskwargs(opts)
2355 opts = pycompat.byteskwargs(opts)
2356 reflags = re.M
2356 reflags = re.M
2357 if opts.get('ignore_case'):
2357 if opts.get('ignore_case'):
2358 reflags |= re.I
2358 reflags |= re.I
2359 try:
2359 try:
2360 regexp = util.re.compile(pattern, reflags)
2360 regexp = util.re.compile(pattern, reflags)
2361 except re.error as inst:
2361 except re.error as inst:
2362 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2362 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2363 return 1
2363 return 1
2364 sep, eol = ':', '\n'
2364 sep, eol = ':', '\n'
2365 if opts.get('print0'):
2365 if opts.get('print0'):
2366 sep = eol = '\0'
2366 sep = eol = '\0'
2367
2367
2368 getfile = util.lrucachefunc(repo.file)
2368 getfile = util.lrucachefunc(repo.file)
2369
2369
2370 def matchlines(body):
2370 def matchlines(body):
2371 begin = 0
2371 begin = 0
2372 linenum = 0
2372 linenum = 0
2373 while begin < len(body):
2373 while begin < len(body):
2374 match = regexp.search(body, begin)
2374 match = regexp.search(body, begin)
2375 if not match:
2375 if not match:
2376 break
2376 break
2377 mstart, mend = match.span()
2377 mstart, mend = match.span()
2378 linenum += body.count('\n', begin, mstart) + 1
2378 linenum += body.count('\n', begin, mstart) + 1
2379 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2379 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2380 begin = body.find('\n', mend) + 1 or len(body) + 1
2380 begin = body.find('\n', mend) + 1 or len(body) + 1
2381 lend = begin - 1
2381 lend = begin - 1
2382 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2382 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2383
2383
2384 class linestate(object):
2384 class linestate(object):
2385 def __init__(self, line, linenum, colstart, colend):
2385 def __init__(self, line, linenum, colstart, colend):
2386 self.line = line
2386 self.line = line
2387 self.linenum = linenum
2387 self.linenum = linenum
2388 self.colstart = colstart
2388 self.colstart = colstart
2389 self.colend = colend
2389 self.colend = colend
2390
2390
2391 def __hash__(self):
2391 def __hash__(self):
2392 return hash((self.linenum, self.line))
2392 return hash((self.linenum, self.line))
2393
2393
2394 def __eq__(self, other):
2394 def __eq__(self, other):
2395 return self.line == other.line
2395 return self.line == other.line
2396
2396
2397 def findpos(self):
2397 def findpos(self):
2398 """Iterate all (start, end) indices of matches"""
2398 """Iterate all (start, end) indices of matches"""
2399 yield self.colstart, self.colend
2399 yield self.colstart, self.colend
2400 p = self.colend
2400 p = self.colend
2401 while p < len(self.line):
2401 while p < len(self.line):
2402 m = regexp.search(self.line, p)
2402 m = regexp.search(self.line, p)
2403 if not m:
2403 if not m:
2404 break
2404 break
2405 yield m.span()
2405 yield m.span()
2406 p = m.end()
2406 p = m.end()
2407
2407
2408 matches = {}
2408 matches = {}
2409 copies = {}
2409 copies = {}
2410 def grepbody(fn, rev, body):
2410 def grepbody(fn, rev, body):
2411 matches[rev].setdefault(fn, [])
2411 matches[rev].setdefault(fn, [])
2412 m = matches[rev][fn]
2412 m = matches[rev][fn]
2413 for lnum, cstart, cend, line in matchlines(body):
2413 for lnum, cstart, cend, line in matchlines(body):
2414 s = linestate(line, lnum, cstart, cend)
2414 s = linestate(line, lnum, cstart, cend)
2415 m.append(s)
2415 m.append(s)
2416
2416
2417 def difflinestates(a, b):
2417 def difflinestates(a, b):
2418 sm = difflib.SequenceMatcher(None, a, b)
2418 sm = difflib.SequenceMatcher(None, a, b)
2419 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2419 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2420 if tag == 'insert':
2420 if tag == 'insert':
2421 for i in xrange(blo, bhi):
2421 for i in xrange(blo, bhi):
2422 yield ('+', b[i])
2422 yield ('+', b[i])
2423 elif tag == 'delete':
2423 elif tag == 'delete':
2424 for i in xrange(alo, ahi):
2424 for i in xrange(alo, ahi):
2425 yield ('-', a[i])
2425 yield ('-', a[i])
2426 elif tag == 'replace':
2426 elif tag == 'replace':
2427 for i in xrange(alo, ahi):
2427 for i in xrange(alo, ahi):
2428 yield ('-', a[i])
2428 yield ('-', a[i])
2429 for i in xrange(blo, bhi):
2429 for i in xrange(blo, bhi):
2430 yield ('+', b[i])
2430 yield ('+', b[i])
2431
2431
2432 def display(fm, fn, ctx, pstates, states):
2432 def display(fm, fn, ctx, pstates, states):
2433 rev = ctx.rev()
2433 rev = ctx.rev()
2434 if fm.isplain():
2434 if fm.isplain():
2435 formatuser = ui.shortuser
2435 formatuser = ui.shortuser
2436 else:
2436 else:
2437 formatuser = str
2437 formatuser = str
2438 if ui.quiet:
2438 if ui.quiet:
2439 datefmt = '%Y-%m-%d'
2439 datefmt = '%Y-%m-%d'
2440 else:
2440 else:
2441 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2441 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2442 found = False
2442 found = False
2443 @util.cachefunc
2443 @util.cachefunc
2444 def binary():
2444 def binary():
2445 flog = getfile(fn)
2445 flog = getfile(fn)
2446 return util.binary(flog.read(ctx.filenode(fn)))
2446 return util.binary(flog.read(ctx.filenode(fn)))
2447
2447
2448 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2448 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2449 if opts.get('all'):
2449 if opts.get('all'):
2450 iter = difflinestates(pstates, states)
2450 iter = difflinestates(pstates, states)
2451 else:
2451 else:
2452 iter = [('', l) for l in states]
2452 iter = [('', l) for l in states]
2453 for change, l in iter:
2453 for change, l in iter:
2454 fm.startitem()
2454 fm.startitem()
2455 fm.data(node=fm.hexfunc(ctx.node()))
2455 fm.data(node=fm.hexfunc(ctx.node()))
2456 cols = [
2456 cols = [
2457 ('filename', fn, True),
2457 ('filename', fn, True),
2458 ('rev', rev, True),
2458 ('rev', rev, True),
2459 ('linenumber', l.linenum, opts.get('line_number')),
2459 ('linenumber', l.linenum, opts.get('line_number')),
2460 ]
2460 ]
2461 if opts.get('all'):
2461 if opts.get('all'):
2462 cols.append(('change', change, True))
2462 cols.append(('change', change, True))
2463 cols.extend([
2463 cols.extend([
2464 ('user', formatuser(ctx.user()), opts.get('user')),
2464 ('user', formatuser(ctx.user()), opts.get('user')),
2465 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2465 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2466 ])
2466 ])
2467 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2467 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2468 for name, data, cond in cols:
2468 for name, data, cond in cols:
2469 field = fieldnamemap.get(name, name)
2469 field = fieldnamemap.get(name, name)
2470 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2470 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2471 if cond and name != lastcol:
2471 if cond and name != lastcol:
2472 fm.plain(sep, label='grep.sep')
2472 fm.plain(sep, label='grep.sep')
2473 if not opts.get('files_with_matches'):
2473 if not opts.get('files_with_matches'):
2474 fm.plain(sep, label='grep.sep')
2474 fm.plain(sep, label='grep.sep')
2475 if not opts.get('text') and binary():
2475 if not opts.get('text') and binary():
2476 fm.plain(_(" Binary file matches"))
2476 fm.plain(_(" Binary file matches"))
2477 else:
2477 else:
2478 displaymatches(fm.nested('texts'), l)
2478 displaymatches(fm.nested('texts'), l)
2479 fm.plain(eol)
2479 fm.plain(eol)
2480 found = True
2480 found = True
2481 if opts.get('files_with_matches'):
2481 if opts.get('files_with_matches'):
2482 break
2482 break
2483 return found
2483 return found
2484
2484
2485 def displaymatches(fm, l):
2485 def displaymatches(fm, l):
2486 p = 0
2486 p = 0
2487 for s, e in l.findpos():
2487 for s, e in l.findpos():
2488 if p < s:
2488 if p < s:
2489 fm.startitem()
2489 fm.startitem()
2490 fm.write('text', '%s', l.line[p:s])
2490 fm.write('text', '%s', l.line[p:s])
2491 fm.data(matched=False)
2491 fm.data(matched=False)
2492 fm.startitem()
2492 fm.startitem()
2493 fm.write('text', '%s', l.line[s:e], label='grep.match')
2493 fm.write('text', '%s', l.line[s:e], label='grep.match')
2494 fm.data(matched=True)
2494 fm.data(matched=True)
2495 p = e
2495 p = e
2496 if p < len(l.line):
2496 if p < len(l.line):
2497 fm.startitem()
2497 fm.startitem()
2498 fm.write('text', '%s', l.line[p:])
2498 fm.write('text', '%s', l.line[p:])
2499 fm.data(matched=False)
2499 fm.data(matched=False)
2500 fm.end()
2500 fm.end()
2501
2501
2502 skip = {}
2502 skip = {}
2503 revfiles = {}
2503 revfiles = {}
2504 match = scmutil.match(repo[None], pats, opts)
2504 match = scmutil.match(repo[None], pats, opts)
2505 found = False
2505 found = False
2506 follow = opts.get('follow')
2506 follow = opts.get('follow')
2507
2507
2508 def prep(ctx, fns):
2508 def prep(ctx, fns):
2509 rev = ctx.rev()
2509 rev = ctx.rev()
2510 pctx = ctx.p1()
2510 pctx = ctx.p1()
2511 parent = pctx.rev()
2511 parent = pctx.rev()
2512 matches.setdefault(rev, {})
2512 matches.setdefault(rev, {})
2513 matches.setdefault(parent, {})
2513 matches.setdefault(parent, {})
2514 files = revfiles.setdefault(rev, [])
2514 files = revfiles.setdefault(rev, [])
2515 for fn in fns:
2515 for fn in fns:
2516 flog = getfile(fn)
2516 flog = getfile(fn)
2517 try:
2517 try:
2518 fnode = ctx.filenode(fn)
2518 fnode = ctx.filenode(fn)
2519 except error.LookupError:
2519 except error.LookupError:
2520 continue
2520 continue
2521
2521
2522 copied = flog.renamed(fnode)
2522 copied = flog.renamed(fnode)
2523 copy = follow and copied and copied[0]
2523 copy = follow and copied and copied[0]
2524 if copy:
2524 if copy:
2525 copies.setdefault(rev, {})[fn] = copy
2525 copies.setdefault(rev, {})[fn] = copy
2526 if fn in skip:
2526 if fn in skip:
2527 if copy:
2527 if copy:
2528 skip[copy] = True
2528 skip[copy] = True
2529 continue
2529 continue
2530 files.append(fn)
2530 files.append(fn)
2531
2531
2532 if fn not in matches[rev]:
2532 if fn not in matches[rev]:
2533 grepbody(fn, rev, flog.read(fnode))
2533 grepbody(fn, rev, flog.read(fnode))
2534
2534
2535 pfn = copy or fn
2535 pfn = copy or fn
2536 if pfn not in matches[parent]:
2536 if pfn not in matches[parent]:
2537 try:
2537 try:
2538 fnode = pctx.filenode(pfn)
2538 fnode = pctx.filenode(pfn)
2539 grepbody(pfn, parent, flog.read(fnode))
2539 grepbody(pfn, parent, flog.read(fnode))
2540 except error.LookupError:
2540 except error.LookupError:
2541 pass
2541 pass
2542
2542
2543 ui.pager('grep')
2543 ui.pager('grep')
2544 fm = ui.formatter('grep', opts)
2544 fm = ui.formatter('grep', opts)
2545 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2545 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2546 rev = ctx.rev()
2546 rev = ctx.rev()
2547 parent = ctx.p1().rev()
2547 parent = ctx.p1().rev()
2548 for fn in sorted(revfiles.get(rev, [])):
2548 for fn in sorted(revfiles.get(rev, [])):
2549 states = matches[rev][fn]
2549 states = matches[rev][fn]
2550 copy = copies.get(rev, {}).get(fn)
2550 copy = copies.get(rev, {}).get(fn)
2551 if fn in skip:
2551 if fn in skip:
2552 if copy:
2552 if copy:
2553 skip[copy] = True
2553 skip[copy] = True
2554 continue
2554 continue
2555 pstates = matches.get(parent, {}).get(copy or fn, [])
2555 pstates = matches.get(parent, {}).get(copy or fn, [])
2556 if pstates or states:
2556 if pstates or states:
2557 r = display(fm, fn, ctx, pstates, states)
2557 r = display(fm, fn, ctx, pstates, states)
2558 found = found or r
2558 found = found or r
2559 if r and not opts.get('all'):
2559 if r and not opts.get('all'):
2560 skip[fn] = True
2560 skip[fn] = True
2561 if copy:
2561 if copy:
2562 skip[copy] = True
2562 skip[copy] = True
2563 del matches[rev]
2563 del matches[rev]
2564 del revfiles[rev]
2564 del revfiles[rev]
2565 fm.end()
2565 fm.end()
2566
2566
2567 return not found
2567 return not found
2568
2568
2569 @command('heads',
2569 @command('heads',
2570 [('r', 'rev', '',
2570 [('r', 'rev', '',
2571 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2571 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2572 ('t', 'topo', False, _('show topological heads only')),
2572 ('t', 'topo', False, _('show topological heads only')),
2573 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2573 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2574 ('c', 'closed', False, _('show normal and closed branch heads')),
2574 ('c', 'closed', False, _('show normal and closed branch heads')),
2575 ] + templateopts,
2575 ] + templateopts,
2576 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2576 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2577 def heads(ui, repo, *branchrevs, **opts):
2577 def heads(ui, repo, *branchrevs, **opts):
2578 """show branch heads
2578 """show branch heads
2579
2579
2580 With no arguments, show all open branch heads in the repository.
2580 With no arguments, show all open branch heads in the repository.
2581 Branch heads are changesets that have no descendants on the
2581 Branch heads are changesets that have no descendants on the
2582 same branch. They are where development generally takes place and
2582 same branch. They are where development generally takes place and
2583 are the usual targets for update and merge operations.
2583 are the usual targets for update and merge operations.
2584
2584
2585 If one or more REVs are given, only open branch heads on the
2585 If one or more REVs are given, only open branch heads on the
2586 branches associated with the specified changesets are shown. This
2586 branches associated with the specified changesets are shown. This
2587 means that you can use :hg:`heads .` to see the heads on the
2587 means that you can use :hg:`heads .` to see the heads on the
2588 currently checked-out branch.
2588 currently checked-out branch.
2589
2589
2590 If -c/--closed is specified, also show branch heads marked closed
2590 If -c/--closed is specified, also show branch heads marked closed
2591 (see :hg:`commit --close-branch`).
2591 (see :hg:`commit --close-branch`).
2592
2592
2593 If STARTREV is specified, only those heads that are descendants of
2593 If STARTREV is specified, only those heads that are descendants of
2594 STARTREV will be displayed.
2594 STARTREV will be displayed.
2595
2595
2596 If -t/--topo is specified, named branch mechanics will be ignored and only
2596 If -t/--topo is specified, named branch mechanics will be ignored and only
2597 topological heads (changesets with no children) will be shown.
2597 topological heads (changesets with no children) will be shown.
2598
2598
2599 Returns 0 if matching heads are found, 1 if not.
2599 Returns 0 if matching heads are found, 1 if not.
2600 """
2600 """
2601
2601
2602 opts = pycompat.byteskwargs(opts)
2602 opts = pycompat.byteskwargs(opts)
2603 start = None
2603 start = None
2604 rev = opts.get('rev')
2604 rev = opts.get('rev')
2605 if rev:
2605 if rev:
2606 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2606 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2607 start = scmutil.revsingle(repo, rev, None).node()
2607 start = scmutil.revsingle(repo, rev, None).node()
2608
2608
2609 if opts.get('topo'):
2609 if opts.get('topo'):
2610 heads = [repo[h] for h in repo.heads(start)]
2610 heads = [repo[h] for h in repo.heads(start)]
2611 else:
2611 else:
2612 heads = []
2612 heads = []
2613 for branch in repo.branchmap():
2613 for branch in repo.branchmap():
2614 heads += repo.branchheads(branch, start, opts.get('closed'))
2614 heads += repo.branchheads(branch, start, opts.get('closed'))
2615 heads = [repo[h] for h in heads]
2615 heads = [repo[h] for h in heads]
2616
2616
2617 if branchrevs:
2617 if branchrevs:
2618 branches = set(repo[br].branch() for br in branchrevs)
2618 branches = set(repo[br].branch() for br in branchrevs)
2619 heads = [h for h in heads if h.branch() in branches]
2619 heads = [h for h in heads if h.branch() in branches]
2620
2620
2621 if opts.get('active') and branchrevs:
2621 if opts.get('active') and branchrevs:
2622 dagheads = repo.heads(start)
2622 dagheads = repo.heads(start)
2623 heads = [h for h in heads if h.node() in dagheads]
2623 heads = [h for h in heads if h.node() in dagheads]
2624
2624
2625 if branchrevs:
2625 if branchrevs:
2626 haveheads = set(h.branch() for h in heads)
2626 haveheads = set(h.branch() for h in heads)
2627 if branches - haveheads:
2627 if branches - haveheads:
2628 headless = ', '.join(b for b in branches - haveheads)
2628 headless = ', '.join(b for b in branches - haveheads)
2629 msg = _('no open branch heads found on branches %s')
2629 msg = _('no open branch heads found on branches %s')
2630 if opts.get('rev'):
2630 if opts.get('rev'):
2631 msg += _(' (started at %s)') % opts['rev']
2631 msg += _(' (started at %s)') % opts['rev']
2632 ui.warn((msg + '\n') % headless)
2632 ui.warn((msg + '\n') % headless)
2633
2633
2634 if not heads:
2634 if not heads:
2635 return 1
2635 return 1
2636
2636
2637 ui.pager('heads')
2637 ui.pager('heads')
2638 heads = sorted(heads, key=lambda x: -x.rev())
2638 heads = sorted(heads, key=lambda x: -x.rev())
2639 displayer = cmdutil.show_changeset(ui, repo, opts)
2639 displayer = cmdutil.show_changeset(ui, repo, opts)
2640 for ctx in heads:
2640 for ctx in heads:
2641 displayer.show(ctx)
2641 displayer.show(ctx)
2642 displayer.close()
2642 displayer.close()
2643
2643
2644 @command('help',
2644 @command('help',
2645 [('e', 'extension', None, _('show only help for extensions')),
2645 [('e', 'extension', None, _('show only help for extensions')),
2646 ('c', 'command', None, _('show only help for commands')),
2646 ('c', 'command', None, _('show only help for commands')),
2647 ('k', 'keyword', None, _('show topics matching keyword')),
2647 ('k', 'keyword', None, _('show topics matching keyword')),
2648 ('s', 'system', [], _('show help for specific platform(s)')),
2648 ('s', 'system', [], _('show help for specific platform(s)')),
2649 ],
2649 ],
2650 _('[-ecks] [TOPIC]'),
2650 _('[-ecks] [TOPIC]'),
2651 norepo=True, cmdtype=readonly)
2651 norepo=True, cmdtype=readonly)
2652 def help_(ui, name=None, **opts):
2652 def help_(ui, name=None, **opts):
2653 """show help for a given topic or a help overview
2653 """show help for a given topic or a help overview
2654
2654
2655 With no arguments, print a list of commands with short help messages.
2655 With no arguments, print a list of commands with short help messages.
2656
2656
2657 Given a topic, extension, or command name, print help for that
2657 Given a topic, extension, or command name, print help for that
2658 topic.
2658 topic.
2659
2659
2660 Returns 0 if successful.
2660 Returns 0 if successful.
2661 """
2661 """
2662
2662
2663 keep = opts.get(r'system') or []
2663 keep = opts.get(r'system') or []
2664 if len(keep) == 0:
2664 if len(keep) == 0:
2665 if pycompat.sysplatform.startswith('win'):
2665 if pycompat.sysplatform.startswith('win'):
2666 keep.append('windows')
2666 keep.append('windows')
2667 elif pycompat.sysplatform == 'OpenVMS':
2667 elif pycompat.sysplatform == 'OpenVMS':
2668 keep.append('vms')
2668 keep.append('vms')
2669 elif pycompat.sysplatform == 'plan9':
2669 elif pycompat.sysplatform == 'plan9':
2670 keep.append('plan9')
2670 keep.append('plan9')
2671 else:
2671 else:
2672 keep.append('unix')
2672 keep.append('unix')
2673 keep.append(pycompat.sysplatform.lower())
2673 keep.append(pycompat.sysplatform.lower())
2674 if ui.verbose:
2674 if ui.verbose:
2675 keep.append('verbose')
2675 keep.append('verbose')
2676
2676
2677 commands = sys.modules[__name__]
2677 commands = sys.modules[__name__]
2678 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2678 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2679 ui.pager('help')
2679 ui.pager('help')
2680 ui.write(formatted)
2680 ui.write(formatted)
2681
2681
2682
2682
2683 @command('identify|id',
2683 @command('identify|id',
2684 [('r', 'rev', '',
2684 [('r', 'rev', '',
2685 _('identify the specified revision'), _('REV')),
2685 _('identify the specified revision'), _('REV')),
2686 ('n', 'num', None, _('show local revision number')),
2686 ('n', 'num', None, _('show local revision number')),
2687 ('i', 'id', None, _('show global revision id')),
2687 ('i', 'id', None, _('show global revision id')),
2688 ('b', 'branch', None, _('show branch')),
2688 ('b', 'branch', None, _('show branch')),
2689 ('t', 'tags', None, _('show tags')),
2689 ('t', 'tags', None, _('show tags')),
2690 ('B', 'bookmarks', None, _('show bookmarks')),
2690 ('B', 'bookmarks', None, _('show bookmarks')),
2691 ] + remoteopts + formatteropts,
2691 ] + remoteopts + formatteropts,
2692 _('[-nibtB] [-r REV] [SOURCE]'),
2692 _('[-nibtB] [-r REV] [SOURCE]'),
2693 optionalrepo=True, cmdtype=readonly)
2693 optionalrepo=True, cmdtype=readonly)
2694 def identify(ui, repo, source=None, rev=None,
2694 def identify(ui, repo, source=None, rev=None,
2695 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2695 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2696 """identify the working directory or specified revision
2696 """identify the working directory or specified revision
2697
2697
2698 Print a summary identifying the repository state at REV using one or
2698 Print a summary identifying the repository state at REV using one or
2699 two parent hash identifiers, followed by a "+" if the working
2699 two parent hash identifiers, followed by a "+" if the working
2700 directory has uncommitted changes, the branch name (if not default),
2700 directory has uncommitted changes, the branch name (if not default),
2701 a list of tags, and a list of bookmarks.
2701 a list of tags, and a list of bookmarks.
2702
2702
2703 When REV is not given, print a summary of the current state of the
2703 When REV is not given, print a summary of the current state of the
2704 repository.
2704 repository.
2705
2705
2706 Specifying a path to a repository root or Mercurial bundle will
2706 Specifying a path to a repository root or Mercurial bundle will
2707 cause lookup to operate on that repository/bundle.
2707 cause lookup to operate on that repository/bundle.
2708
2708
2709 .. container:: verbose
2709 .. container:: verbose
2710
2710
2711 Examples:
2711 Examples:
2712
2712
2713 - generate a build identifier for the working directory::
2713 - generate a build identifier for the working directory::
2714
2714
2715 hg id --id > build-id.dat
2715 hg id --id > build-id.dat
2716
2716
2717 - find the revision corresponding to a tag::
2717 - find the revision corresponding to a tag::
2718
2718
2719 hg id -n -r 1.3
2719 hg id -n -r 1.3
2720
2720
2721 - check the most recent revision of a remote repository::
2721 - check the most recent revision of a remote repository::
2722
2722
2723 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2723 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2724
2724
2725 See :hg:`log` for generating more information about specific revisions,
2725 See :hg:`log` for generating more information about specific revisions,
2726 including full hash identifiers.
2726 including full hash identifiers.
2727
2727
2728 Returns 0 if successful.
2728 Returns 0 if successful.
2729 """
2729 """
2730
2730
2731 opts = pycompat.byteskwargs(opts)
2731 opts = pycompat.byteskwargs(opts)
2732 if not repo and not source:
2732 if not repo and not source:
2733 raise error.Abort(_("there is no Mercurial repository here "
2733 raise error.Abort(_("there is no Mercurial repository here "
2734 "(.hg not found)"))
2734 "(.hg not found)"))
2735
2735
2736 if ui.debugflag:
2736 if ui.debugflag:
2737 hexfunc = hex
2737 hexfunc = hex
2738 else:
2738 else:
2739 hexfunc = short
2739 hexfunc = short
2740 default = not (num or id or branch or tags or bookmarks)
2740 default = not (num or id or branch or tags or bookmarks)
2741 output = []
2741 output = []
2742 revs = []
2742 revs = []
2743
2743
2744 if source:
2744 if source:
2745 source, branches = hg.parseurl(ui.expandpath(source))
2745 source, branches = hg.parseurl(ui.expandpath(source))
2746 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2746 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2747 repo = peer.local()
2747 repo = peer.local()
2748 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2748 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2749
2749
2750 fm = ui.formatter('identify', opts)
2750 fm = ui.formatter('identify', opts)
2751 fm.startitem()
2751 fm.startitem()
2752
2752
2753 if not repo:
2753 if not repo:
2754 if num or branch or tags:
2754 if num or branch or tags:
2755 raise error.Abort(
2755 raise error.Abort(
2756 _("can't query remote revision number, branch, or tags"))
2756 _("can't query remote revision number, branch, or tags"))
2757 if not rev and revs:
2757 if not rev and revs:
2758 rev = revs[0]
2758 rev = revs[0]
2759 if not rev:
2759 if not rev:
2760 rev = "tip"
2760 rev = "tip"
2761
2761
2762 remoterev = peer.lookup(rev)
2762 remoterev = peer.lookup(rev)
2763 hexrev = hexfunc(remoterev)
2763 hexrev = hexfunc(remoterev)
2764 if default or id:
2764 if default or id:
2765 output = [hexrev]
2765 output = [hexrev]
2766 fm.data(id=hexrev)
2766 fm.data(id=hexrev)
2767
2767
2768 def getbms():
2768 def getbms():
2769 bms = []
2769 bms = []
2770
2770
2771 if 'bookmarks' in peer.listkeys('namespaces'):
2771 if 'bookmarks' in peer.listkeys('namespaces'):
2772 hexremoterev = hex(remoterev)
2772 hexremoterev = hex(remoterev)
2773 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2773 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2774 if bmr == hexremoterev]
2774 if bmr == hexremoterev]
2775
2775
2776 return sorted(bms)
2776 return sorted(bms)
2777
2777
2778 bms = getbms()
2778 bms = getbms()
2779 if bookmarks:
2779 if bookmarks:
2780 output.extend(bms)
2780 output.extend(bms)
2781 elif default and not ui.quiet:
2781 elif default and not ui.quiet:
2782 # multiple bookmarks for a single parent separated by '/'
2782 # multiple bookmarks for a single parent separated by '/'
2783 bm = '/'.join(bms)
2783 bm = '/'.join(bms)
2784 if bm:
2784 if bm:
2785 output.append(bm)
2785 output.append(bm)
2786
2786
2787 fm.data(node=hex(remoterev))
2787 fm.data(node=hex(remoterev))
2788 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2788 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2789 else:
2789 else:
2790 if rev:
2790 if rev:
2791 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2791 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2792 ctx = scmutil.revsingle(repo, rev, None)
2792 ctx = scmutil.revsingle(repo, rev, None)
2793
2793
2794 if ctx.rev() is None:
2794 if ctx.rev() is None:
2795 ctx = repo[None]
2795 ctx = repo[None]
2796 parents = ctx.parents()
2796 parents = ctx.parents()
2797 taglist = []
2797 taglist = []
2798 for p in parents:
2798 for p in parents:
2799 taglist.extend(p.tags())
2799 taglist.extend(p.tags())
2800
2800
2801 dirty = ""
2801 dirty = ""
2802 if ctx.dirty(missing=True, merge=False, branch=False):
2802 if ctx.dirty(missing=True, merge=False, branch=False):
2803 dirty = '+'
2803 dirty = '+'
2804 fm.data(dirty=dirty)
2804 fm.data(dirty=dirty)
2805
2805
2806 hexoutput = [hexfunc(p.node()) for p in parents]
2806 hexoutput = [hexfunc(p.node()) for p in parents]
2807 if default or id:
2807 if default or id:
2808 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2808 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2809 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2809 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2810
2810
2811 if num:
2811 if num:
2812 numoutput = ["%d" % p.rev() for p in parents]
2812 numoutput = ["%d" % p.rev() for p in parents]
2813 output.append("%s%s" % ('+'.join(numoutput), dirty))
2813 output.append("%s%s" % ('+'.join(numoutput), dirty))
2814
2814
2815 fn = fm.nested('parents')
2815 fn = fm.nested('parents')
2816 for p in parents:
2816 for p in parents:
2817 fn.startitem()
2817 fn.startitem()
2818 fn.data(rev=p.rev())
2818 fn.data(rev=p.rev())
2819 fn.data(node=p.hex())
2819 fn.data(node=p.hex())
2820 fn.context(ctx=p)
2820 fn.context(ctx=p)
2821 fn.end()
2821 fn.end()
2822 else:
2822 else:
2823 hexoutput = hexfunc(ctx.node())
2823 hexoutput = hexfunc(ctx.node())
2824 if default or id:
2824 if default or id:
2825 output = [hexoutput]
2825 output = [hexoutput]
2826 fm.data(id=hexoutput)
2826 fm.data(id=hexoutput)
2827
2827
2828 if num:
2828 if num:
2829 output.append(pycompat.bytestr(ctx.rev()))
2829 output.append(pycompat.bytestr(ctx.rev()))
2830 taglist = ctx.tags()
2830 taglist = ctx.tags()
2831
2831
2832 if default and not ui.quiet:
2832 if default and not ui.quiet:
2833 b = ctx.branch()
2833 b = ctx.branch()
2834 if b != 'default':
2834 if b != 'default':
2835 output.append("(%s)" % b)
2835 output.append("(%s)" % b)
2836
2836
2837 # multiple tags for a single parent separated by '/'
2837 # multiple tags for a single parent separated by '/'
2838 t = '/'.join(taglist)
2838 t = '/'.join(taglist)
2839 if t:
2839 if t:
2840 output.append(t)
2840 output.append(t)
2841
2841
2842 # multiple bookmarks for a single parent separated by '/'
2842 # multiple bookmarks for a single parent separated by '/'
2843 bm = '/'.join(ctx.bookmarks())
2843 bm = '/'.join(ctx.bookmarks())
2844 if bm:
2844 if bm:
2845 output.append(bm)
2845 output.append(bm)
2846 else:
2846 else:
2847 if branch:
2847 if branch:
2848 output.append(ctx.branch())
2848 output.append(ctx.branch())
2849
2849
2850 if tags:
2850 if tags:
2851 output.extend(taglist)
2851 output.extend(taglist)
2852
2852
2853 if bookmarks:
2853 if bookmarks:
2854 output.extend(ctx.bookmarks())
2854 output.extend(ctx.bookmarks())
2855
2855
2856 fm.data(node=ctx.hex())
2856 fm.data(node=ctx.hex())
2857 fm.data(branch=ctx.branch())
2857 fm.data(branch=ctx.branch())
2858 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2858 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2859 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2859 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2860 fm.context(ctx=ctx)
2860 fm.context(ctx=ctx)
2861
2861
2862 fm.plain("%s\n" % ' '.join(output))
2862 fm.plain("%s\n" % ' '.join(output))
2863 fm.end()
2863 fm.end()
2864
2864
2865 @command('import|patch',
2865 @command('import|patch',
2866 [('p', 'strip', 1,
2866 [('p', 'strip', 1,
2867 _('directory strip option for patch. This has the same '
2867 _('directory strip option for patch. This has the same '
2868 'meaning as the corresponding patch option'), _('NUM')),
2868 'meaning as the corresponding patch option'), _('NUM')),
2869 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2869 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2870 ('e', 'edit', False, _('invoke editor on commit messages')),
2870 ('e', 'edit', False, _('invoke editor on commit messages')),
2871 ('f', 'force', None,
2871 ('f', 'force', None,
2872 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2872 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2873 ('', 'no-commit', None,
2873 ('', 'no-commit', None,
2874 _("don't commit, just update the working directory")),
2874 _("don't commit, just update the working directory")),
2875 ('', 'bypass', None,
2875 ('', 'bypass', None,
2876 _("apply patch without touching the working directory")),
2876 _("apply patch without touching the working directory")),
2877 ('', 'partial', None,
2877 ('', 'partial', None,
2878 _('commit even if some hunks fail')),
2878 _('commit even if some hunks fail')),
2879 ('', 'exact', None,
2879 ('', 'exact', None,
2880 _('abort if patch would apply lossily')),
2880 _('abort if patch would apply lossily')),
2881 ('', 'prefix', '',
2881 ('', 'prefix', '',
2882 _('apply patch to subdirectory'), _('DIR')),
2882 _('apply patch to subdirectory'), _('DIR')),
2883 ('', 'import-branch', None,
2883 ('', 'import-branch', None,
2884 _('use any branch information in patch (implied by --exact)'))] +
2884 _('use any branch information in patch (implied by --exact)'))] +
2885 commitopts + commitopts2 + similarityopts,
2885 commitopts + commitopts2 + similarityopts,
2886 _('[OPTION]... PATCH...'))
2886 _('[OPTION]... PATCH...'))
2887 def import_(ui, repo, patch1=None, *patches, **opts):
2887 def import_(ui, repo, patch1=None, *patches, **opts):
2888 """import an ordered set of patches
2888 """import an ordered set of patches
2889
2889
2890 Import a list of patches and commit them individually (unless
2890 Import a list of patches and commit them individually (unless
2891 --no-commit is specified).
2891 --no-commit is specified).
2892
2892
2893 To read a patch from standard input (stdin), use "-" as the patch
2893 To read a patch from standard input (stdin), use "-" as the patch
2894 name. If a URL is specified, the patch will be downloaded from
2894 name. If a URL is specified, the patch will be downloaded from
2895 there.
2895 there.
2896
2896
2897 Import first applies changes to the working directory (unless
2897 Import first applies changes to the working directory (unless
2898 --bypass is specified), import will abort if there are outstanding
2898 --bypass is specified), import will abort if there are outstanding
2899 changes.
2899 changes.
2900
2900
2901 Use --bypass to apply and commit patches directly to the
2901 Use --bypass to apply and commit patches directly to the
2902 repository, without affecting the working directory. Without
2902 repository, without affecting the working directory. Without
2903 --exact, patches will be applied on top of the working directory
2903 --exact, patches will be applied on top of the working directory
2904 parent revision.
2904 parent revision.
2905
2905
2906 You can import a patch straight from a mail message. Even patches
2906 You can import a patch straight from a mail message. Even patches
2907 as attachments work (to use the body part, it must have type
2907 as attachments work (to use the body part, it must have type
2908 text/plain or text/x-patch). From and Subject headers of email
2908 text/plain or text/x-patch). From and Subject headers of email
2909 message are used as default committer and commit message. All
2909 message are used as default committer and commit message. All
2910 text/plain body parts before first diff are added to the commit
2910 text/plain body parts before first diff are added to the commit
2911 message.
2911 message.
2912
2912
2913 If the imported patch was generated by :hg:`export`, user and
2913 If the imported patch was generated by :hg:`export`, user and
2914 description from patch override values from message headers and
2914 description from patch override values from message headers and
2915 body. Values given on command line with -m/--message and -u/--user
2915 body. Values given on command line with -m/--message and -u/--user
2916 override these.
2916 override these.
2917
2917
2918 If --exact is specified, import will set the working directory to
2918 If --exact is specified, import will set the working directory to
2919 the parent of each patch before applying it, and will abort if the
2919 the parent of each patch before applying it, and will abort if the
2920 resulting changeset has a different ID than the one recorded in
2920 resulting changeset has a different ID than the one recorded in
2921 the patch. This will guard against various ways that portable
2921 the patch. This will guard against various ways that portable
2922 patch formats and mail systems might fail to transfer Mercurial
2922 patch formats and mail systems might fail to transfer Mercurial
2923 data or metadata. See :hg:`bundle` for lossless transmission.
2923 data or metadata. See :hg:`bundle` for lossless transmission.
2924
2924
2925 Use --partial to ensure a changeset will be created from the patch
2925 Use --partial to ensure a changeset will be created from the patch
2926 even if some hunks fail to apply. Hunks that fail to apply will be
2926 even if some hunks fail to apply. Hunks that fail to apply will be
2927 written to a <target-file>.rej file. Conflicts can then be resolved
2927 written to a <target-file>.rej file. Conflicts can then be resolved
2928 by hand before :hg:`commit --amend` is run to update the created
2928 by hand before :hg:`commit --amend` is run to update the created
2929 changeset. This flag exists to let people import patches that
2929 changeset. This flag exists to let people import patches that
2930 partially apply without losing the associated metadata (author,
2930 partially apply without losing the associated metadata (author,
2931 date, description, ...).
2931 date, description, ...).
2932
2932
2933 .. note::
2933 .. note::
2934
2934
2935 When no hunks apply cleanly, :hg:`import --partial` will create
2935 When no hunks apply cleanly, :hg:`import --partial` will create
2936 an empty changeset, importing only the patch metadata.
2936 an empty changeset, importing only the patch metadata.
2937
2937
2938 With -s/--similarity, hg will attempt to discover renames and
2938 With -s/--similarity, hg will attempt to discover renames and
2939 copies in the patch in the same way as :hg:`addremove`.
2939 copies in the patch in the same way as :hg:`addremove`.
2940
2940
2941 It is possible to use external patch programs to perform the patch
2941 It is possible to use external patch programs to perform the patch
2942 by setting the ``ui.patch`` configuration option. For the default
2942 by setting the ``ui.patch`` configuration option. For the default
2943 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2943 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2944 See :hg:`help config` for more information about configuration
2944 See :hg:`help config` for more information about configuration
2945 files and how to use these options.
2945 files and how to use these options.
2946
2946
2947 See :hg:`help dates` for a list of formats valid for -d/--date.
2947 See :hg:`help dates` for a list of formats valid for -d/--date.
2948
2948
2949 .. container:: verbose
2949 .. container:: verbose
2950
2950
2951 Examples:
2951 Examples:
2952
2952
2953 - import a traditional patch from a website and detect renames::
2953 - import a traditional patch from a website and detect renames::
2954
2954
2955 hg import -s 80 http://example.com/bugfix.patch
2955 hg import -s 80 http://example.com/bugfix.patch
2956
2956
2957 - import a changeset from an hgweb server::
2957 - import a changeset from an hgweb server::
2958
2958
2959 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2959 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2960
2960
2961 - import all the patches in an Unix-style mbox::
2961 - import all the patches in an Unix-style mbox::
2962
2962
2963 hg import incoming-patches.mbox
2963 hg import incoming-patches.mbox
2964
2964
2965 - import patches from stdin::
2965 - import patches from stdin::
2966
2966
2967 hg import -
2967 hg import -
2968
2968
2969 - attempt to exactly restore an exported changeset (not always
2969 - attempt to exactly restore an exported changeset (not always
2970 possible)::
2970 possible)::
2971
2971
2972 hg import --exact proposed-fix.patch
2972 hg import --exact proposed-fix.patch
2973
2973
2974 - use an external tool to apply a patch which is too fuzzy for
2974 - use an external tool to apply a patch which is too fuzzy for
2975 the default internal tool.
2975 the default internal tool.
2976
2976
2977 hg import --config ui.patch="patch --merge" fuzzy.patch
2977 hg import --config ui.patch="patch --merge" fuzzy.patch
2978
2978
2979 - change the default fuzzing from 2 to a less strict 7
2979 - change the default fuzzing from 2 to a less strict 7
2980
2980
2981 hg import --config ui.fuzz=7 fuzz.patch
2981 hg import --config ui.fuzz=7 fuzz.patch
2982
2982
2983 Returns 0 on success, 1 on partial success (see --partial).
2983 Returns 0 on success, 1 on partial success (see --partial).
2984 """
2984 """
2985
2985
2986 opts = pycompat.byteskwargs(opts)
2986 opts = pycompat.byteskwargs(opts)
2987 if not patch1:
2987 if not patch1:
2988 raise error.Abort(_('need at least one patch to import'))
2988 raise error.Abort(_('need at least one patch to import'))
2989
2989
2990 patches = (patch1,) + patches
2990 patches = (patch1,) + patches
2991
2991
2992 date = opts.get('date')
2992 date = opts.get('date')
2993 if date:
2993 if date:
2994 opts['date'] = util.parsedate(date)
2994 opts['date'] = util.parsedate(date)
2995
2995
2996 exact = opts.get('exact')
2996 exact = opts.get('exact')
2997 update = not opts.get('bypass')
2997 update = not opts.get('bypass')
2998 if not update and opts.get('no_commit'):
2998 if not update and opts.get('no_commit'):
2999 raise error.Abort(_('cannot use --no-commit with --bypass'))
2999 raise error.Abort(_('cannot use --no-commit with --bypass'))
3000 try:
3000 try:
3001 sim = float(opts.get('similarity') or 0)
3001 sim = float(opts.get('similarity') or 0)
3002 except ValueError:
3002 except ValueError:
3003 raise error.Abort(_('similarity must be a number'))
3003 raise error.Abort(_('similarity must be a number'))
3004 if sim < 0 or sim > 100:
3004 if sim < 0 or sim > 100:
3005 raise error.Abort(_('similarity must be between 0 and 100'))
3005 raise error.Abort(_('similarity must be between 0 and 100'))
3006 if sim and not update:
3006 if sim and not update:
3007 raise error.Abort(_('cannot use --similarity with --bypass'))
3007 raise error.Abort(_('cannot use --similarity with --bypass'))
3008 if exact:
3008 if exact:
3009 if opts.get('edit'):
3009 if opts.get('edit'):
3010 raise error.Abort(_('cannot use --exact with --edit'))
3010 raise error.Abort(_('cannot use --exact with --edit'))
3011 if opts.get('prefix'):
3011 if opts.get('prefix'):
3012 raise error.Abort(_('cannot use --exact with --prefix'))
3012 raise error.Abort(_('cannot use --exact with --prefix'))
3013
3013
3014 base = opts["base"]
3014 base = opts["base"]
3015 wlock = dsguard = lock = tr = None
3015 wlock = dsguard = lock = tr = None
3016 msgs = []
3016 msgs = []
3017 ret = 0
3017 ret = 0
3018
3018
3019
3019
3020 try:
3020 try:
3021 wlock = repo.wlock()
3021 wlock = repo.wlock()
3022
3022
3023 if update:
3023 if update:
3024 cmdutil.checkunfinished(repo)
3024 cmdutil.checkunfinished(repo)
3025 if (exact or not opts.get('force')):
3025 if (exact or not opts.get('force')):
3026 cmdutil.bailifchanged(repo)
3026 cmdutil.bailifchanged(repo)
3027
3027
3028 if not opts.get('no_commit'):
3028 if not opts.get('no_commit'):
3029 lock = repo.lock()
3029 lock = repo.lock()
3030 tr = repo.transaction('import')
3030 tr = repo.transaction('import')
3031 else:
3031 else:
3032 dsguard = dirstateguard.dirstateguard(repo, 'import')
3032 dsguard = dirstateguard.dirstateguard(repo, 'import')
3033 parents = repo[None].parents()
3033 parents = repo[None].parents()
3034 for patchurl in patches:
3034 for patchurl in patches:
3035 if patchurl == '-':
3035 if patchurl == '-':
3036 ui.status(_('applying patch from stdin\n'))
3036 ui.status(_('applying patch from stdin\n'))
3037 patchfile = ui.fin
3037 patchfile = ui.fin
3038 patchurl = 'stdin' # for error message
3038 patchurl = 'stdin' # for error message
3039 else:
3039 else:
3040 patchurl = os.path.join(base, patchurl)
3040 patchurl = os.path.join(base, patchurl)
3041 ui.status(_('applying %s\n') % patchurl)
3041 ui.status(_('applying %s\n') % patchurl)
3042 patchfile = hg.openpath(ui, patchurl)
3042 patchfile = hg.openpath(ui, patchurl)
3043
3043
3044 haspatch = False
3044 haspatch = False
3045 for hunk in patch.split(patchfile):
3045 for hunk in patch.split(patchfile):
3046 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3046 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3047 parents, opts,
3047 parents, opts,
3048 msgs, hg.clean)
3048 msgs, hg.clean)
3049 if msg:
3049 if msg:
3050 haspatch = True
3050 haspatch = True
3051 ui.note(msg + '\n')
3051 ui.note(msg + '\n')
3052 if update or exact:
3052 if update or exact:
3053 parents = repo[None].parents()
3053 parents = repo[None].parents()
3054 else:
3054 else:
3055 parents = [repo[node]]
3055 parents = [repo[node]]
3056 if rej:
3056 if rej:
3057 ui.write_err(_("patch applied partially\n"))
3057 ui.write_err(_("patch applied partially\n"))
3058 ui.write_err(_("(fix the .rej files and run "
3058 ui.write_err(_("(fix the .rej files and run "
3059 "`hg commit --amend`)\n"))
3059 "`hg commit --amend`)\n"))
3060 ret = 1
3060 ret = 1
3061 break
3061 break
3062
3062
3063 if not haspatch:
3063 if not haspatch:
3064 raise error.Abort(_('%s: no diffs found') % patchurl)
3064 raise error.Abort(_('%s: no diffs found') % patchurl)
3065
3065
3066 if tr:
3066 if tr:
3067 tr.close()
3067 tr.close()
3068 if msgs:
3068 if msgs:
3069 repo.savecommitmessage('\n* * *\n'.join(msgs))
3069 repo.savecommitmessage('\n* * *\n'.join(msgs))
3070 if dsguard:
3070 if dsguard:
3071 dsguard.close()
3071 dsguard.close()
3072 return ret
3072 return ret
3073 finally:
3073 finally:
3074 if tr:
3074 if tr:
3075 tr.release()
3075 tr.release()
3076 release(lock, dsguard, wlock)
3076 release(lock, dsguard, wlock)
3077
3077
3078 @command('incoming|in',
3078 @command('incoming|in',
3079 [('f', 'force', None,
3079 [('f', 'force', None,
3080 _('run even if remote repository is unrelated')),
3080 _('run even if remote repository is unrelated')),
3081 ('n', 'newest-first', None, _('show newest record first')),
3081 ('n', 'newest-first', None, _('show newest record first')),
3082 ('', 'bundle', '',
3082 ('', 'bundle', '',
3083 _('file to store the bundles into'), _('FILE')),
3083 _('file to store the bundles into'), _('FILE')),
3084 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3084 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3085 ('B', 'bookmarks', False, _("compare bookmarks")),
3085 ('B', 'bookmarks', False, _("compare bookmarks")),
3086 ('b', 'branch', [],
3086 ('b', 'branch', [],
3087 _('a specific branch you would like to pull'), _('BRANCH')),
3087 _('a specific branch you would like to pull'), _('BRANCH')),
3088 ] + logopts + remoteopts + subrepoopts,
3088 ] + logopts + remoteopts + subrepoopts,
3089 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3089 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3090 def incoming(ui, repo, source="default", **opts):
3090 def incoming(ui, repo, source="default", **opts):
3091 """show new changesets found in source
3091 """show new changesets found in source
3092
3092
3093 Show new changesets found in the specified path/URL or the default
3093 Show new changesets found in the specified path/URL or the default
3094 pull location. These are the changesets that would have been pulled
3094 pull location. These are the changesets that would have been pulled
3095 by :hg:`pull` at the time you issued this command.
3095 by :hg:`pull` at the time you issued this command.
3096
3096
3097 See pull for valid source format details.
3097 See pull for valid source format details.
3098
3098
3099 .. container:: verbose
3099 .. container:: verbose
3100
3100
3101 With -B/--bookmarks, the result of bookmark comparison between
3101 With -B/--bookmarks, the result of bookmark comparison between
3102 local and remote repositories is displayed. With -v/--verbose,
3102 local and remote repositories is displayed. With -v/--verbose,
3103 status is also displayed for each bookmark like below::
3103 status is also displayed for each bookmark like below::
3104
3104
3105 BM1 01234567890a added
3105 BM1 01234567890a added
3106 BM2 1234567890ab advanced
3106 BM2 1234567890ab advanced
3107 BM3 234567890abc diverged
3107 BM3 234567890abc diverged
3108 BM4 34567890abcd changed
3108 BM4 34567890abcd changed
3109
3109
3110 The action taken locally when pulling depends on the
3110 The action taken locally when pulling depends on the
3111 status of each bookmark:
3111 status of each bookmark:
3112
3112
3113 :``added``: pull will create it
3113 :``added``: pull will create it
3114 :``advanced``: pull will update it
3114 :``advanced``: pull will update it
3115 :``diverged``: pull will create a divergent bookmark
3115 :``diverged``: pull will create a divergent bookmark
3116 :``changed``: result depends on remote changesets
3116 :``changed``: result depends on remote changesets
3117
3117
3118 From the point of view of pulling behavior, bookmark
3118 From the point of view of pulling behavior, bookmark
3119 existing only in the remote repository are treated as ``added``,
3119 existing only in the remote repository are treated as ``added``,
3120 even if it is in fact locally deleted.
3120 even if it is in fact locally deleted.
3121
3121
3122 .. container:: verbose
3122 .. container:: verbose
3123
3123
3124 For remote repository, using --bundle avoids downloading the
3124 For remote repository, using --bundle avoids downloading the
3125 changesets twice if the incoming is followed by a pull.
3125 changesets twice if the incoming is followed by a pull.
3126
3126
3127 Examples:
3127 Examples:
3128
3128
3129 - show incoming changes with patches and full description::
3129 - show incoming changes with patches and full description::
3130
3130
3131 hg incoming -vp
3131 hg incoming -vp
3132
3132
3133 - show incoming changes excluding merges, store a bundle::
3133 - show incoming changes excluding merges, store a bundle::
3134
3134
3135 hg in -vpM --bundle incoming.hg
3135 hg in -vpM --bundle incoming.hg
3136 hg pull incoming.hg
3136 hg pull incoming.hg
3137
3137
3138 - briefly list changes inside a bundle::
3138 - briefly list changes inside a bundle::
3139
3139
3140 hg in changes.hg -T "{desc|firstline}\\n"
3140 hg in changes.hg -T "{desc|firstline}\\n"
3141
3141
3142 Returns 0 if there are incoming changes, 1 otherwise.
3142 Returns 0 if there are incoming changes, 1 otherwise.
3143 """
3143 """
3144 opts = pycompat.byteskwargs(opts)
3144 opts = pycompat.byteskwargs(opts)
3145 if opts.get('graph'):
3145 if opts.get('graph'):
3146 cmdutil.checkunsupportedgraphflags([], opts)
3146 cmdutil.checkunsupportedgraphflags([], opts)
3147 def display(other, chlist, displayer):
3147 def display(other, chlist, displayer):
3148 revdag = cmdutil.graphrevs(other, chlist, opts)
3148 revdag = cmdutil.graphrevs(other, chlist, opts)
3149 cmdutil.displaygraph(ui, repo, revdag, displayer,
3149 cmdutil.displaygraph(ui, repo, revdag, displayer,
3150 graphmod.asciiedges)
3150 graphmod.asciiedges)
3151
3151
3152 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3152 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3153 return 0
3153 return 0
3154
3154
3155 if opts.get('bundle') and opts.get('subrepos'):
3155 if opts.get('bundle') and opts.get('subrepos'):
3156 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3156 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3157
3157
3158 if opts.get('bookmarks'):
3158 if opts.get('bookmarks'):
3159 source, branches = hg.parseurl(ui.expandpath(source),
3159 source, branches = hg.parseurl(ui.expandpath(source),
3160 opts.get('branch'))
3160 opts.get('branch'))
3161 other = hg.peer(repo, opts, source)
3161 other = hg.peer(repo, opts, source)
3162 if 'bookmarks' not in other.listkeys('namespaces'):
3162 if 'bookmarks' not in other.listkeys('namespaces'):
3163 ui.warn(_("remote doesn't support bookmarks\n"))
3163 ui.warn(_("remote doesn't support bookmarks\n"))
3164 return 0
3164 return 0
3165 ui.pager('incoming')
3165 ui.pager('incoming')
3166 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3166 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3167 return bookmarks.incoming(ui, repo, other)
3167 return bookmarks.incoming(ui, repo, other)
3168
3168
3169 repo._subtoppath = ui.expandpath(source)
3169 repo._subtoppath = ui.expandpath(source)
3170 try:
3170 try:
3171 return hg.incoming(ui, repo, source, opts)
3171 return hg.incoming(ui, repo, source, opts)
3172 finally:
3172 finally:
3173 del repo._subtoppath
3173 del repo._subtoppath
3174
3174
3175
3175
3176 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3176 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3177 norepo=True)
3177 norepo=True)
3178 def init(ui, dest=".", **opts):
3178 def init(ui, dest=".", **opts):
3179 """create a new repository in the given directory
3179 """create a new repository in the given directory
3180
3180
3181 Initialize a new repository in the given directory. If the given
3181 Initialize a new repository in the given directory. If the given
3182 directory does not exist, it will be created.
3182 directory does not exist, it will be created.
3183
3183
3184 If no directory is given, the current directory is used.
3184 If no directory is given, the current directory is used.
3185
3185
3186 It is possible to specify an ``ssh://`` URL as the destination.
3186 It is possible to specify an ``ssh://`` URL as the destination.
3187 See :hg:`help urls` for more information.
3187 See :hg:`help urls` for more information.
3188
3188
3189 Returns 0 on success.
3189 Returns 0 on success.
3190 """
3190 """
3191 opts = pycompat.byteskwargs(opts)
3191 opts = pycompat.byteskwargs(opts)
3192 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3192 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3193
3193
3194 @command('locate',
3194 @command('locate',
3195 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3195 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3196 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3196 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3197 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3197 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3198 ] + walkopts,
3198 ] + walkopts,
3199 _('[OPTION]... [PATTERN]...'))
3199 _('[OPTION]... [PATTERN]...'))
3200 def locate(ui, repo, *pats, **opts):
3200 def locate(ui, repo, *pats, **opts):
3201 """locate files matching specific patterns (DEPRECATED)
3201 """locate files matching specific patterns (DEPRECATED)
3202
3202
3203 Print files under Mercurial control in the working directory whose
3203 Print files under Mercurial control in the working directory whose
3204 names match the given patterns.
3204 names match the given patterns.
3205
3205
3206 By default, this command searches all directories in the working
3206 By default, this command searches all directories in the working
3207 directory. To search just the current directory and its
3207 directory. To search just the current directory and its
3208 subdirectories, use "--include .".
3208 subdirectories, use "--include .".
3209
3209
3210 If no patterns are given to match, this command prints the names
3210 If no patterns are given to match, this command prints the names
3211 of all files under Mercurial control in the working directory.
3211 of all files under Mercurial control in the working directory.
3212
3212
3213 If you want to feed the output of this command into the "xargs"
3213 If you want to feed the output of this command into the "xargs"
3214 command, use the -0 option to both this command and "xargs". This
3214 command, use the -0 option to both this command and "xargs". This
3215 will avoid the problem of "xargs" treating single filenames that
3215 will avoid the problem of "xargs" treating single filenames that
3216 contain whitespace as multiple filenames.
3216 contain whitespace as multiple filenames.
3217
3217
3218 See :hg:`help files` for a more versatile command.
3218 See :hg:`help files` for a more versatile command.
3219
3219
3220 Returns 0 if a match is found, 1 otherwise.
3220 Returns 0 if a match is found, 1 otherwise.
3221 """
3221 """
3222 opts = pycompat.byteskwargs(opts)
3222 opts = pycompat.byteskwargs(opts)
3223 if opts.get('print0'):
3223 if opts.get('print0'):
3224 end = '\0'
3224 end = '\0'
3225 else:
3225 else:
3226 end = '\n'
3226 end = '\n'
3227 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3227 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3228
3228
3229 ret = 1
3229 ret = 1
3230 ctx = repo[rev]
3230 ctx = repo[rev]
3231 m = scmutil.match(ctx, pats, opts, default='relglob',
3231 m = scmutil.match(ctx, pats, opts, default='relglob',
3232 badfn=lambda x, y: False)
3232 badfn=lambda x, y: False)
3233
3233
3234 ui.pager('locate')
3234 ui.pager('locate')
3235 for abs in ctx.matches(m):
3235 for abs in ctx.matches(m):
3236 if opts.get('fullpath'):
3236 if opts.get('fullpath'):
3237 ui.write(repo.wjoin(abs), end)
3237 ui.write(repo.wjoin(abs), end)
3238 else:
3238 else:
3239 ui.write(((pats and m.rel(abs)) or abs), end)
3239 ui.write(((pats and m.rel(abs)) or abs), end)
3240 ret = 0
3240 ret = 0
3241
3241
3242 return ret
3242 return ret
3243
3243
3244 @command('^log|history',
3244 @command('^log|history',
3245 [('f', 'follow', None,
3245 [('f', 'follow', None,
3246 _('follow changeset history, or file history across copies and renames')),
3246 _('follow changeset history, or file history across copies and renames')),
3247 ('', 'follow-first', None,
3247 ('', 'follow-first', None,
3248 _('only follow the first parent of merge changesets (DEPRECATED)')),
3248 _('only follow the first parent of merge changesets (DEPRECATED)')),
3249 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3249 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3250 ('C', 'copies', None, _('show copied files')),
3250 ('C', 'copies', None, _('show copied files')),
3251 ('k', 'keyword', [],
3251 ('k', 'keyword', [],
3252 _('do case-insensitive search for a given text'), _('TEXT')),
3252 _('do case-insensitive search for a given text'), _('TEXT')),
3253 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3253 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3254 ('L', 'line-range', [],
3254 ('L', 'line-range', [],
3255 _('follow line range of specified file (EXPERIMENTAL)'),
3255 _('follow line range of specified file (EXPERIMENTAL)'),
3256 _('FILE,RANGE')),
3256 _('FILE,RANGE')),
3257 ('', 'removed', None, _('include revisions where files were removed')),
3257 ('', 'removed', None, _('include revisions where files were removed')),
3258 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3258 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3259 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3259 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3260 ('', 'only-branch', [],
3260 ('', 'only-branch', [],
3261 _('show only changesets within the given named branch (DEPRECATED)'),
3261 _('show only changesets within the given named branch (DEPRECATED)'),
3262 _('BRANCH')),
3262 _('BRANCH')),
3263 ('b', 'branch', [],
3263 ('b', 'branch', [],
3264 _('show changesets within the given named branch'), _('BRANCH')),
3264 _('show changesets within the given named branch'), _('BRANCH')),
3265 ('P', 'prune', [],
3265 ('P', 'prune', [],
3266 _('do not display revision or any of its ancestors'), _('REV')),
3266 _('do not display revision or any of its ancestors'), _('REV')),
3267 ] + logopts + walkopts,
3267 ] + logopts + walkopts,
3268 _('[OPTION]... [FILE]'),
3268 _('[OPTION]... [FILE]'),
3269 inferrepo=True, cmdtype=readonly)
3269 inferrepo=True, cmdtype=readonly)
3270 def log(ui, repo, *pats, **opts):
3270 def log(ui, repo, *pats, **opts):
3271 """show revision history of entire repository or files
3271 """show revision history of entire repository or files
3272
3272
3273 Print the revision history of the specified files or the entire
3273 Print the revision history of the specified files or the entire
3274 project.
3274 project.
3275
3275
3276 If no revision range is specified, the default is ``tip:0`` unless
3276 If no revision range is specified, the default is ``tip:0`` unless
3277 --follow is set, in which case the working directory parent is
3277 --follow is set, in which case the working directory parent is
3278 used as the starting revision.
3278 used as the starting revision.
3279
3279
3280 File history is shown without following rename or copy history of
3280 File history is shown without following rename or copy history of
3281 files. Use -f/--follow with a filename to follow history across
3281 files. Use -f/--follow with a filename to follow history across
3282 renames and copies. --follow without a filename will only show
3282 renames and copies. --follow without a filename will only show
3283 ancestors of the starting revision.
3283 ancestors of the starting revision.
3284
3284
3285 By default this command prints revision number and changeset id,
3285 By default this command prints revision number and changeset id,
3286 tags, non-trivial parents, user, date and time, and a summary for
3286 tags, non-trivial parents, user, date and time, and a summary for
3287 each commit. When the -v/--verbose switch is used, the list of
3287 each commit. When the -v/--verbose switch is used, the list of
3288 changed files and full commit message are shown.
3288 changed files and full commit message are shown.
3289
3289
3290 With --graph the revisions are shown as an ASCII art DAG with the most
3290 With --graph the revisions are shown as an ASCII art DAG with the most
3291 recent changeset at the top.
3291 recent changeset at the top.
3292 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3292 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3293 and '+' represents a fork where the changeset from the lines below is a
3293 and '+' represents a fork where the changeset from the lines below is a
3294 parent of the 'o' merge on the same line.
3294 parent of the 'o' merge on the same line.
3295 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3295 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3296 of a '|' indicates one or more revisions in a path are omitted.
3296 of a '|' indicates one or more revisions in a path are omitted.
3297
3297
3298 .. container:: verbose
3298 .. container:: verbose
3299
3299
3300 Use -L/--line-range FILE,M:N options to follow the history of lines
3300 Use -L/--line-range FILE,M:N options to follow the history of lines
3301 from M to N in FILE. With -p/--patch only diff hunks affecting
3301 from M to N in FILE. With -p/--patch only diff hunks affecting
3302 specified line range will be shown. This option requires --follow;
3302 specified line range will be shown. This option requires --follow;
3303 it can be specified multiple times. Currently, this option is not
3303 it can be specified multiple times. Currently, this option is not
3304 compatible with --graph. This option is experimental.
3304 compatible with --graph. This option is experimental.
3305
3305
3306 .. note::
3306 .. note::
3307
3307
3308 :hg:`log --patch` may generate unexpected diff output for merge
3308 :hg:`log --patch` may generate unexpected diff output for merge
3309 changesets, as it will only compare the merge changeset against
3309 changesets, as it will only compare the merge changeset against
3310 its first parent. Also, only files different from BOTH parents
3310 its first parent. Also, only files different from BOTH parents
3311 will appear in files:.
3311 will appear in files:.
3312
3312
3313 .. note::
3313 .. note::
3314
3314
3315 For performance reasons, :hg:`log FILE` may omit duplicate changes
3315 For performance reasons, :hg:`log FILE` may omit duplicate changes
3316 made on branches and will not show removals or mode changes. To
3316 made on branches and will not show removals or mode changes. To
3317 see all such changes, use the --removed switch.
3317 see all such changes, use the --removed switch.
3318
3318
3319 .. container:: verbose
3319 .. container:: verbose
3320
3320
3321 .. note::
3321 .. note::
3322
3322
3323 The history resulting from -L/--line-range options depends on diff
3323 The history resulting from -L/--line-range options depends on diff
3324 options; for instance if white-spaces are ignored, respective changes
3324 options; for instance if white-spaces are ignored, respective changes
3325 with only white-spaces in specified line range will not be listed.
3325 with only white-spaces in specified line range will not be listed.
3326
3326
3327 .. container:: verbose
3327 .. container:: verbose
3328
3328
3329 Some examples:
3329 Some examples:
3330
3330
3331 - changesets with full descriptions and file lists::
3331 - changesets with full descriptions and file lists::
3332
3332
3333 hg log -v
3333 hg log -v
3334
3334
3335 - changesets ancestral to the working directory::
3335 - changesets ancestral to the working directory::
3336
3336
3337 hg log -f
3337 hg log -f
3338
3338
3339 - last 10 commits on the current branch::
3339 - last 10 commits on the current branch::
3340
3340
3341 hg log -l 10 -b .
3341 hg log -l 10 -b .
3342
3342
3343 - changesets showing all modifications of a file, including removals::
3343 - changesets showing all modifications of a file, including removals::
3344
3344
3345 hg log --removed file.c
3345 hg log --removed file.c
3346
3346
3347 - all changesets that touch a directory, with diffs, excluding merges::
3347 - all changesets that touch a directory, with diffs, excluding merges::
3348
3348
3349 hg log -Mp lib/
3349 hg log -Mp lib/
3350
3350
3351 - all revision numbers that match a keyword::
3351 - all revision numbers that match a keyword::
3352
3352
3353 hg log -k bug --template "{rev}\\n"
3353 hg log -k bug --template "{rev}\\n"
3354
3354
3355 - the full hash identifier of the working directory parent::
3355 - the full hash identifier of the working directory parent::
3356
3356
3357 hg log -r . --template "{node}\\n"
3357 hg log -r . --template "{node}\\n"
3358
3358
3359 - list available log templates::
3359 - list available log templates::
3360
3360
3361 hg log -T list
3361 hg log -T list
3362
3362
3363 - check if a given changeset is included in a tagged release::
3363 - check if a given changeset is included in a tagged release::
3364
3364
3365 hg log -r "a21ccf and ancestor(1.9)"
3365 hg log -r "a21ccf and ancestor(1.9)"
3366
3366
3367 - find all changesets by some user in a date range::
3367 - find all changesets by some user in a date range::
3368
3368
3369 hg log -k alice -d "may 2008 to jul 2008"
3369 hg log -k alice -d "may 2008 to jul 2008"
3370
3370
3371 - summary of all changesets after the last tag::
3371 - summary of all changesets after the last tag::
3372
3372
3373 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3373 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3374
3374
3375 - changesets touching lines 13 to 23 for file.c::
3375 - changesets touching lines 13 to 23 for file.c::
3376
3376
3377 hg log -L file.c,13:23
3377 hg log -L file.c,13:23
3378
3378
3379 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3379 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3380 main.c with patch::
3380 main.c with patch::
3381
3381
3382 hg log -L file.c,13:23 -L main.c,2:6 -p
3382 hg log -L file.c,13:23 -L main.c,2:6 -p
3383
3383
3384 See :hg:`help dates` for a list of formats valid for -d/--date.
3384 See :hg:`help dates` for a list of formats valid for -d/--date.
3385
3385
3386 See :hg:`help revisions` for more about specifying and ordering
3386 See :hg:`help revisions` for more about specifying and ordering
3387 revisions.
3387 revisions.
3388
3388
3389 See :hg:`help templates` for more about pre-packaged styles and
3389 See :hg:`help templates` for more about pre-packaged styles and
3390 specifying custom templates. The default template used by the log
3390 specifying custom templates. The default template used by the log
3391 command can be customized via the ``ui.logtemplate`` configuration
3391 command can be customized via the ``ui.logtemplate`` configuration
3392 setting.
3392 setting.
3393
3393
3394 Returns 0 on success.
3394 Returns 0 on success.
3395
3395
3396 """
3396 """
3397 opts = pycompat.byteskwargs(opts)
3397 opts = pycompat.byteskwargs(opts)
3398 linerange = opts.get('line_range')
3398 linerange = opts.get('line_range')
3399
3399
3400 if linerange and not opts.get('follow'):
3400 if linerange and not opts.get('follow'):
3401 raise error.Abort(_('--line-range requires --follow'))
3401 raise error.Abort(_('--line-range requires --follow'))
3402
3402
3403 if linerange and pats:
3403 if linerange and pats:
3404 raise error.Abort(
3404 raise error.Abort(
3405 _('FILE arguments are not compatible with --line-range option')
3405 _('FILE arguments are not compatible with --line-range option')
3406 )
3406 )
3407
3407
3408 if opts.get('follow_first') and opts.get('rev'):
3409 opts['rev'] = [revsetlang.formatspec('reverse(_firstancestors(%lr))',
3410 opts.get('rev'))]
3411 del opts['follow_first']
3412 elif opts.get('follow') and opts.get('rev'):
3413 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3414 del opts['follow']
3415
3416 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3408 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3417 revs, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3409 revs, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3418 hunksfilter = None
3410 hunksfilter = None
3419
3411
3420 if opts.get('graph'):
3412 if opts.get('graph'):
3421 if linerange:
3413 if linerange:
3422 raise error.Abort(_('graph not supported with line range patterns'))
3414 raise error.Abort(_('graph not supported with line range patterns'))
3423 return cmdutil.graphlog(ui, repo, revs, filematcher, opts)
3415 return cmdutil.graphlog(ui, repo, revs, filematcher, opts)
3424
3416
3425 if linerange:
3417 if linerange:
3426 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3418 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3427 repo, revs, opts)
3419 repo, revs, opts)
3428
3420
3429 if filematcher is not None and lrfilematcher is not None:
3421 if filematcher is not None and lrfilematcher is not None:
3430 basefilematcher = filematcher
3422 basefilematcher = filematcher
3431
3423
3432 def filematcher(rev):
3424 def filematcher(rev):
3433 files = (basefilematcher(rev).files()
3425 files = (basefilematcher(rev).files()
3434 + lrfilematcher(rev).files())
3426 + lrfilematcher(rev).files())
3435 return scmutil.matchfiles(repo, files)
3427 return scmutil.matchfiles(repo, files)
3436
3428
3437 elif filematcher is None:
3429 elif filematcher is None:
3438 filematcher = lrfilematcher
3430 filematcher = lrfilematcher
3439
3431
3440 getrenamed = None
3432 getrenamed = None
3441 if opts.get('copies'):
3433 if opts.get('copies'):
3442 endrev = None
3434 endrev = None
3443 if opts.get('rev'):
3435 if opts.get('rev'):
3444 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3436 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3445 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3437 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3446
3438
3447 ui.pager('log')
3439 ui.pager('log')
3448 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3440 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3449 for rev in revs:
3441 for rev in revs:
3450 ctx = repo[rev]
3442 ctx = repo[rev]
3451 copies = None
3443 copies = None
3452 if getrenamed is not None and rev:
3444 if getrenamed is not None and rev:
3453 copies = []
3445 copies = []
3454 for fn in ctx.files():
3446 for fn in ctx.files():
3455 rename = getrenamed(fn, rev)
3447 rename = getrenamed(fn, rev)
3456 if rename:
3448 if rename:
3457 copies.append((fn, rename[0]))
3449 copies.append((fn, rename[0]))
3458 if filematcher:
3450 if filematcher:
3459 revmatchfn = filematcher(ctx.rev())
3451 revmatchfn = filematcher(ctx.rev())
3460 else:
3452 else:
3461 revmatchfn = None
3453 revmatchfn = None
3462 if hunksfilter:
3454 if hunksfilter:
3463 revhunksfilter = hunksfilter(rev)
3455 revhunksfilter = hunksfilter(rev)
3464 else:
3456 else:
3465 revhunksfilter = None
3457 revhunksfilter = None
3466 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3458 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3467 hunksfilterfn=revhunksfilter)
3459 hunksfilterfn=revhunksfilter)
3468 displayer.flush(ctx)
3460 displayer.flush(ctx)
3469
3461
3470 displayer.close()
3462 displayer.close()
3471
3463
3472 @command('manifest',
3464 @command('manifest',
3473 [('r', 'rev', '', _('revision to display'), _('REV')),
3465 [('r', 'rev', '', _('revision to display'), _('REV')),
3474 ('', 'all', False, _("list files from all revisions"))]
3466 ('', 'all', False, _("list files from all revisions"))]
3475 + formatteropts,
3467 + formatteropts,
3476 _('[-r REV]'), cmdtype=readonly)
3468 _('[-r REV]'), cmdtype=readonly)
3477 def manifest(ui, repo, node=None, rev=None, **opts):
3469 def manifest(ui, repo, node=None, rev=None, **opts):
3478 """output the current or given revision of the project manifest
3470 """output the current or given revision of the project manifest
3479
3471
3480 Print a list of version controlled files for the given revision.
3472 Print a list of version controlled files for the given revision.
3481 If no revision is given, the first parent of the working directory
3473 If no revision is given, the first parent of the working directory
3482 is used, or the null revision if no revision is checked out.
3474 is used, or the null revision if no revision is checked out.
3483
3475
3484 With -v, print file permissions, symlink and executable bits.
3476 With -v, print file permissions, symlink and executable bits.
3485 With --debug, print file revision hashes.
3477 With --debug, print file revision hashes.
3486
3478
3487 If option --all is specified, the list of all files from all revisions
3479 If option --all is specified, the list of all files from all revisions
3488 is printed. This includes deleted and renamed files.
3480 is printed. This includes deleted and renamed files.
3489
3481
3490 Returns 0 on success.
3482 Returns 0 on success.
3491 """
3483 """
3492 opts = pycompat.byteskwargs(opts)
3484 opts = pycompat.byteskwargs(opts)
3493 fm = ui.formatter('manifest', opts)
3485 fm = ui.formatter('manifest', opts)
3494
3486
3495 if opts.get('all'):
3487 if opts.get('all'):
3496 if rev or node:
3488 if rev or node:
3497 raise error.Abort(_("can't specify a revision with --all"))
3489 raise error.Abort(_("can't specify a revision with --all"))
3498
3490
3499 res = []
3491 res = []
3500 prefix = "data/"
3492 prefix = "data/"
3501 suffix = ".i"
3493 suffix = ".i"
3502 plen = len(prefix)
3494 plen = len(prefix)
3503 slen = len(suffix)
3495 slen = len(suffix)
3504 with repo.lock():
3496 with repo.lock():
3505 for fn, b, size in repo.store.datafiles():
3497 for fn, b, size in repo.store.datafiles():
3506 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3498 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3507 res.append(fn[plen:-slen])
3499 res.append(fn[plen:-slen])
3508 ui.pager('manifest')
3500 ui.pager('manifest')
3509 for f in res:
3501 for f in res:
3510 fm.startitem()
3502 fm.startitem()
3511 fm.write("path", '%s\n', f)
3503 fm.write("path", '%s\n', f)
3512 fm.end()
3504 fm.end()
3513 return
3505 return
3514
3506
3515 if rev and node:
3507 if rev and node:
3516 raise error.Abort(_("please specify just one revision"))
3508 raise error.Abort(_("please specify just one revision"))
3517
3509
3518 if not node:
3510 if not node:
3519 node = rev
3511 node = rev
3520
3512
3521 char = {'l': '@', 'x': '*', '': ''}
3513 char = {'l': '@', 'x': '*', '': ''}
3522 mode = {'l': '644', 'x': '755', '': '644'}
3514 mode = {'l': '644', 'x': '755', '': '644'}
3523 if node:
3515 if node:
3524 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3516 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3525 ctx = scmutil.revsingle(repo, node)
3517 ctx = scmutil.revsingle(repo, node)
3526 mf = ctx.manifest()
3518 mf = ctx.manifest()
3527 ui.pager('manifest')
3519 ui.pager('manifest')
3528 for f in ctx:
3520 for f in ctx:
3529 fm.startitem()
3521 fm.startitem()
3530 fl = ctx[f].flags()
3522 fl = ctx[f].flags()
3531 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3523 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3532 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3524 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3533 fm.write('path', '%s\n', f)
3525 fm.write('path', '%s\n', f)
3534 fm.end()
3526 fm.end()
3535
3527
3536 @command('^merge',
3528 @command('^merge',
3537 [('f', 'force', None,
3529 [('f', 'force', None,
3538 _('force a merge including outstanding changes (DEPRECATED)')),
3530 _('force a merge including outstanding changes (DEPRECATED)')),
3539 ('r', 'rev', '', _('revision to merge'), _('REV')),
3531 ('r', 'rev', '', _('revision to merge'), _('REV')),
3540 ('P', 'preview', None,
3532 ('P', 'preview', None,
3541 _('review revisions to merge (no merge is performed)'))
3533 _('review revisions to merge (no merge is performed)'))
3542 ] + mergetoolopts,
3534 ] + mergetoolopts,
3543 _('[-P] [[-r] REV]'))
3535 _('[-P] [[-r] REV]'))
3544 def merge(ui, repo, node=None, **opts):
3536 def merge(ui, repo, node=None, **opts):
3545 """merge another revision into working directory
3537 """merge another revision into working directory
3546
3538
3547 The current working directory is updated with all changes made in
3539 The current working directory is updated with all changes made in
3548 the requested revision since the last common predecessor revision.
3540 the requested revision since the last common predecessor revision.
3549
3541
3550 Files that changed between either parent are marked as changed for
3542 Files that changed between either parent are marked as changed for
3551 the next commit and a commit must be performed before any further
3543 the next commit and a commit must be performed before any further
3552 updates to the repository are allowed. The next commit will have
3544 updates to the repository are allowed. The next commit will have
3553 two parents.
3545 two parents.
3554
3546
3555 ``--tool`` can be used to specify the merge tool used for file
3547 ``--tool`` can be used to specify the merge tool used for file
3556 merges. It overrides the HGMERGE environment variable and your
3548 merges. It overrides the HGMERGE environment variable and your
3557 configuration files. See :hg:`help merge-tools` for options.
3549 configuration files. See :hg:`help merge-tools` for options.
3558
3550
3559 If no revision is specified, the working directory's parent is a
3551 If no revision is specified, the working directory's parent is a
3560 head revision, and the current branch contains exactly one other
3552 head revision, and the current branch contains exactly one other
3561 head, the other head is merged with by default. Otherwise, an
3553 head, the other head is merged with by default. Otherwise, an
3562 explicit revision with which to merge with must be provided.
3554 explicit revision with which to merge with must be provided.
3563
3555
3564 See :hg:`help resolve` for information on handling file conflicts.
3556 See :hg:`help resolve` for information on handling file conflicts.
3565
3557
3566 To undo an uncommitted merge, use :hg:`update --clean .` which
3558 To undo an uncommitted merge, use :hg:`update --clean .` which
3567 will check out a clean copy of the original merge parent, losing
3559 will check out a clean copy of the original merge parent, losing
3568 all changes.
3560 all changes.
3569
3561
3570 Returns 0 on success, 1 if there are unresolved files.
3562 Returns 0 on success, 1 if there are unresolved files.
3571 """
3563 """
3572
3564
3573 opts = pycompat.byteskwargs(opts)
3565 opts = pycompat.byteskwargs(opts)
3574 if opts.get('rev') and node:
3566 if opts.get('rev') and node:
3575 raise error.Abort(_("please specify just one revision"))
3567 raise error.Abort(_("please specify just one revision"))
3576 if not node:
3568 if not node:
3577 node = opts.get('rev')
3569 node = opts.get('rev')
3578
3570
3579 if node:
3571 if node:
3580 node = scmutil.revsingle(repo, node).node()
3572 node = scmutil.revsingle(repo, node).node()
3581
3573
3582 if not node:
3574 if not node:
3583 node = repo[destutil.destmerge(repo)].node()
3575 node = repo[destutil.destmerge(repo)].node()
3584
3576
3585 if opts.get('preview'):
3577 if opts.get('preview'):
3586 # find nodes that are ancestors of p2 but not of p1
3578 # find nodes that are ancestors of p2 but not of p1
3587 p1 = repo.lookup('.')
3579 p1 = repo.lookup('.')
3588 p2 = repo.lookup(node)
3580 p2 = repo.lookup(node)
3589 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3581 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3590
3582
3591 displayer = cmdutil.show_changeset(ui, repo, opts)
3583 displayer = cmdutil.show_changeset(ui, repo, opts)
3592 for node in nodes:
3584 for node in nodes:
3593 displayer.show(repo[node])
3585 displayer.show(repo[node])
3594 displayer.close()
3586 displayer.close()
3595 return 0
3587 return 0
3596
3588
3597 try:
3589 try:
3598 # ui.forcemerge is an internal variable, do not document
3590 # ui.forcemerge is an internal variable, do not document
3599 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3591 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3600 force = opts.get('force')
3592 force = opts.get('force')
3601 labels = ['working copy', 'merge rev']
3593 labels = ['working copy', 'merge rev']
3602 return hg.merge(repo, node, force=force, mergeforce=force,
3594 return hg.merge(repo, node, force=force, mergeforce=force,
3603 labels=labels)
3595 labels=labels)
3604 finally:
3596 finally:
3605 ui.setconfig('ui', 'forcemerge', '', 'merge')
3597 ui.setconfig('ui', 'forcemerge', '', 'merge')
3606
3598
3607 @command('outgoing|out',
3599 @command('outgoing|out',
3608 [('f', 'force', None, _('run even when the destination is unrelated')),
3600 [('f', 'force', None, _('run even when the destination is unrelated')),
3609 ('r', 'rev', [],
3601 ('r', 'rev', [],
3610 _('a changeset intended to be included in the destination'), _('REV')),
3602 _('a changeset intended to be included in the destination'), _('REV')),
3611 ('n', 'newest-first', None, _('show newest record first')),
3603 ('n', 'newest-first', None, _('show newest record first')),
3612 ('B', 'bookmarks', False, _('compare bookmarks')),
3604 ('B', 'bookmarks', False, _('compare bookmarks')),
3613 ('b', 'branch', [], _('a specific branch you would like to push'),
3605 ('b', 'branch', [], _('a specific branch you would like to push'),
3614 _('BRANCH')),
3606 _('BRANCH')),
3615 ] + logopts + remoteopts + subrepoopts,
3607 ] + logopts + remoteopts + subrepoopts,
3616 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3608 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3617 def outgoing(ui, repo, dest=None, **opts):
3609 def outgoing(ui, repo, dest=None, **opts):
3618 """show changesets not found in the destination
3610 """show changesets not found in the destination
3619
3611
3620 Show changesets not found in the specified destination repository
3612 Show changesets not found in the specified destination repository
3621 or the default push location. These are the changesets that would
3613 or the default push location. These are the changesets that would
3622 be pushed if a push was requested.
3614 be pushed if a push was requested.
3623
3615
3624 See pull for details of valid destination formats.
3616 See pull for details of valid destination formats.
3625
3617
3626 .. container:: verbose
3618 .. container:: verbose
3627
3619
3628 With -B/--bookmarks, the result of bookmark comparison between
3620 With -B/--bookmarks, the result of bookmark comparison between
3629 local and remote repositories is displayed. With -v/--verbose,
3621 local and remote repositories is displayed. With -v/--verbose,
3630 status is also displayed for each bookmark like below::
3622 status is also displayed for each bookmark like below::
3631
3623
3632 BM1 01234567890a added
3624 BM1 01234567890a added
3633 BM2 deleted
3625 BM2 deleted
3634 BM3 234567890abc advanced
3626 BM3 234567890abc advanced
3635 BM4 34567890abcd diverged
3627 BM4 34567890abcd diverged
3636 BM5 4567890abcde changed
3628 BM5 4567890abcde changed
3637
3629
3638 The action taken when pushing depends on the
3630 The action taken when pushing depends on the
3639 status of each bookmark:
3631 status of each bookmark:
3640
3632
3641 :``added``: push with ``-B`` will create it
3633 :``added``: push with ``-B`` will create it
3642 :``deleted``: push with ``-B`` will delete it
3634 :``deleted``: push with ``-B`` will delete it
3643 :``advanced``: push will update it
3635 :``advanced``: push will update it
3644 :``diverged``: push with ``-B`` will update it
3636 :``diverged``: push with ``-B`` will update it
3645 :``changed``: push with ``-B`` will update it
3637 :``changed``: push with ``-B`` will update it
3646
3638
3647 From the point of view of pushing behavior, bookmarks
3639 From the point of view of pushing behavior, bookmarks
3648 existing only in the remote repository are treated as
3640 existing only in the remote repository are treated as
3649 ``deleted``, even if it is in fact added remotely.
3641 ``deleted``, even if it is in fact added remotely.
3650
3642
3651 Returns 0 if there are outgoing changes, 1 otherwise.
3643 Returns 0 if there are outgoing changes, 1 otherwise.
3652 """
3644 """
3653 opts = pycompat.byteskwargs(opts)
3645 opts = pycompat.byteskwargs(opts)
3654 if opts.get('graph'):
3646 if opts.get('graph'):
3655 cmdutil.checkunsupportedgraphflags([], opts)
3647 cmdutil.checkunsupportedgraphflags([], opts)
3656 o, other = hg._outgoing(ui, repo, dest, opts)
3648 o, other = hg._outgoing(ui, repo, dest, opts)
3657 if not o:
3649 if not o:
3658 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3650 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3659 return
3651 return
3660
3652
3661 revdag = cmdutil.graphrevs(repo, o, opts)
3653 revdag = cmdutil.graphrevs(repo, o, opts)
3662 ui.pager('outgoing')
3654 ui.pager('outgoing')
3663 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3655 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3664 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3656 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3665 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3657 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3666 return 0
3658 return 0
3667
3659
3668 if opts.get('bookmarks'):
3660 if opts.get('bookmarks'):
3669 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3661 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3670 dest, branches = hg.parseurl(dest, opts.get('branch'))
3662 dest, branches = hg.parseurl(dest, opts.get('branch'))
3671 other = hg.peer(repo, opts, dest)
3663 other = hg.peer(repo, opts, dest)
3672 if 'bookmarks' not in other.listkeys('namespaces'):
3664 if 'bookmarks' not in other.listkeys('namespaces'):
3673 ui.warn(_("remote doesn't support bookmarks\n"))
3665 ui.warn(_("remote doesn't support bookmarks\n"))
3674 return 0
3666 return 0
3675 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3667 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3676 ui.pager('outgoing')
3668 ui.pager('outgoing')
3677 return bookmarks.outgoing(ui, repo, other)
3669 return bookmarks.outgoing(ui, repo, other)
3678
3670
3679 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3671 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3680 try:
3672 try:
3681 return hg.outgoing(ui, repo, dest, opts)
3673 return hg.outgoing(ui, repo, dest, opts)
3682 finally:
3674 finally:
3683 del repo._subtoppath
3675 del repo._subtoppath
3684
3676
3685 @command('parents',
3677 @command('parents',
3686 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3678 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3687 ] + templateopts,
3679 ] + templateopts,
3688 _('[-r REV] [FILE]'),
3680 _('[-r REV] [FILE]'),
3689 inferrepo=True)
3681 inferrepo=True)
3690 def parents(ui, repo, file_=None, **opts):
3682 def parents(ui, repo, file_=None, **opts):
3691 """show the parents of the working directory or revision (DEPRECATED)
3683 """show the parents of the working directory or revision (DEPRECATED)
3692
3684
3693 Print the working directory's parent revisions. If a revision is
3685 Print the working directory's parent revisions. If a revision is
3694 given via -r/--rev, the parent of that revision will be printed.
3686 given via -r/--rev, the parent of that revision will be printed.
3695 If a file argument is given, the revision in which the file was
3687 If a file argument is given, the revision in which the file was
3696 last changed (before the working directory revision or the
3688 last changed (before the working directory revision or the
3697 argument to --rev if given) is printed.
3689 argument to --rev if given) is printed.
3698
3690
3699 This command is equivalent to::
3691 This command is equivalent to::
3700
3692
3701 hg log -r "p1()+p2()" or
3693 hg log -r "p1()+p2()" or
3702 hg log -r "p1(REV)+p2(REV)" or
3694 hg log -r "p1(REV)+p2(REV)" or
3703 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3695 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3704 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3696 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3705
3697
3706 See :hg:`summary` and :hg:`help revsets` for related information.
3698 See :hg:`summary` and :hg:`help revsets` for related information.
3707
3699
3708 Returns 0 on success.
3700 Returns 0 on success.
3709 """
3701 """
3710
3702
3711 opts = pycompat.byteskwargs(opts)
3703 opts = pycompat.byteskwargs(opts)
3712 rev = opts.get('rev')
3704 rev = opts.get('rev')
3713 if rev:
3705 if rev:
3714 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3706 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3715 ctx = scmutil.revsingle(repo, rev, None)
3707 ctx = scmutil.revsingle(repo, rev, None)
3716
3708
3717 if file_:
3709 if file_:
3718 m = scmutil.match(ctx, (file_,), opts)
3710 m = scmutil.match(ctx, (file_,), opts)
3719 if m.anypats() or len(m.files()) != 1:
3711 if m.anypats() or len(m.files()) != 1:
3720 raise error.Abort(_('can only specify an explicit filename'))
3712 raise error.Abort(_('can only specify an explicit filename'))
3721 file_ = m.files()[0]
3713 file_ = m.files()[0]
3722 filenodes = []
3714 filenodes = []
3723 for cp in ctx.parents():
3715 for cp in ctx.parents():
3724 if not cp:
3716 if not cp:
3725 continue
3717 continue
3726 try:
3718 try:
3727 filenodes.append(cp.filenode(file_))
3719 filenodes.append(cp.filenode(file_))
3728 except error.LookupError:
3720 except error.LookupError:
3729 pass
3721 pass
3730 if not filenodes:
3722 if not filenodes:
3731 raise error.Abort(_("'%s' not found in manifest!") % file_)
3723 raise error.Abort(_("'%s' not found in manifest!") % file_)
3732 p = []
3724 p = []
3733 for fn in filenodes:
3725 for fn in filenodes:
3734 fctx = repo.filectx(file_, fileid=fn)
3726 fctx = repo.filectx(file_, fileid=fn)
3735 p.append(fctx.node())
3727 p.append(fctx.node())
3736 else:
3728 else:
3737 p = [cp.node() for cp in ctx.parents()]
3729 p = [cp.node() for cp in ctx.parents()]
3738
3730
3739 displayer = cmdutil.show_changeset(ui, repo, opts)
3731 displayer = cmdutil.show_changeset(ui, repo, opts)
3740 for n in p:
3732 for n in p:
3741 if n != nullid:
3733 if n != nullid:
3742 displayer.show(repo[n])
3734 displayer.show(repo[n])
3743 displayer.close()
3735 displayer.close()
3744
3736
3745 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3737 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3746 cmdtype=readonly)
3738 cmdtype=readonly)
3747 def paths(ui, repo, search=None, **opts):
3739 def paths(ui, repo, search=None, **opts):
3748 """show aliases for remote repositories
3740 """show aliases for remote repositories
3749
3741
3750 Show definition of symbolic path name NAME. If no name is given,
3742 Show definition of symbolic path name NAME. If no name is given,
3751 show definition of all available names.
3743 show definition of all available names.
3752
3744
3753 Option -q/--quiet suppresses all output when searching for NAME
3745 Option -q/--quiet suppresses all output when searching for NAME
3754 and shows only the path names when listing all definitions.
3746 and shows only the path names when listing all definitions.
3755
3747
3756 Path names are defined in the [paths] section of your
3748 Path names are defined in the [paths] section of your
3757 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3749 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3758 repository, ``.hg/hgrc`` is used, too.
3750 repository, ``.hg/hgrc`` is used, too.
3759
3751
3760 The path names ``default`` and ``default-push`` have a special
3752 The path names ``default`` and ``default-push`` have a special
3761 meaning. When performing a push or pull operation, they are used
3753 meaning. When performing a push or pull operation, they are used
3762 as fallbacks if no location is specified on the command-line.
3754 as fallbacks if no location is specified on the command-line.
3763 When ``default-push`` is set, it will be used for push and
3755 When ``default-push`` is set, it will be used for push and
3764 ``default`` will be used for pull; otherwise ``default`` is used
3756 ``default`` will be used for pull; otherwise ``default`` is used
3765 as the fallback for both. When cloning a repository, the clone
3757 as the fallback for both. When cloning a repository, the clone
3766 source is written as ``default`` in ``.hg/hgrc``.
3758 source is written as ``default`` in ``.hg/hgrc``.
3767
3759
3768 .. note::
3760 .. note::
3769
3761
3770 ``default`` and ``default-push`` apply to all inbound (e.g.
3762 ``default`` and ``default-push`` apply to all inbound (e.g.
3771 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3763 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3772 and :hg:`bundle`) operations.
3764 and :hg:`bundle`) operations.
3773
3765
3774 See :hg:`help urls` for more information.
3766 See :hg:`help urls` for more information.
3775
3767
3776 Returns 0 on success.
3768 Returns 0 on success.
3777 """
3769 """
3778
3770
3779 opts = pycompat.byteskwargs(opts)
3771 opts = pycompat.byteskwargs(opts)
3780 ui.pager('paths')
3772 ui.pager('paths')
3781 if search:
3773 if search:
3782 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3774 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3783 if name == search]
3775 if name == search]
3784 else:
3776 else:
3785 pathitems = sorted(ui.paths.iteritems())
3777 pathitems = sorted(ui.paths.iteritems())
3786
3778
3787 fm = ui.formatter('paths', opts)
3779 fm = ui.formatter('paths', opts)
3788 if fm.isplain():
3780 if fm.isplain():
3789 hidepassword = util.hidepassword
3781 hidepassword = util.hidepassword
3790 else:
3782 else:
3791 hidepassword = str
3783 hidepassword = str
3792 if ui.quiet:
3784 if ui.quiet:
3793 namefmt = '%s\n'
3785 namefmt = '%s\n'
3794 else:
3786 else:
3795 namefmt = '%s = '
3787 namefmt = '%s = '
3796 showsubopts = not search and not ui.quiet
3788 showsubopts = not search and not ui.quiet
3797
3789
3798 for name, path in pathitems:
3790 for name, path in pathitems:
3799 fm.startitem()
3791 fm.startitem()
3800 fm.condwrite(not search, 'name', namefmt, name)
3792 fm.condwrite(not search, 'name', namefmt, name)
3801 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3793 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3802 for subopt, value in sorted(path.suboptions.items()):
3794 for subopt, value in sorted(path.suboptions.items()):
3803 assert subopt not in ('name', 'url')
3795 assert subopt not in ('name', 'url')
3804 if showsubopts:
3796 if showsubopts:
3805 fm.plain('%s:%s = ' % (name, subopt))
3797 fm.plain('%s:%s = ' % (name, subopt))
3806 fm.condwrite(showsubopts, subopt, '%s\n', value)
3798 fm.condwrite(showsubopts, subopt, '%s\n', value)
3807
3799
3808 fm.end()
3800 fm.end()
3809
3801
3810 if search and not pathitems:
3802 if search and not pathitems:
3811 if not ui.quiet:
3803 if not ui.quiet:
3812 ui.warn(_("not found!\n"))
3804 ui.warn(_("not found!\n"))
3813 return 1
3805 return 1
3814 else:
3806 else:
3815 return 0
3807 return 0
3816
3808
3817 @command('phase',
3809 @command('phase',
3818 [('p', 'public', False, _('set changeset phase to public')),
3810 [('p', 'public', False, _('set changeset phase to public')),
3819 ('d', 'draft', False, _('set changeset phase to draft')),
3811 ('d', 'draft', False, _('set changeset phase to draft')),
3820 ('s', 'secret', False, _('set changeset phase to secret')),
3812 ('s', 'secret', False, _('set changeset phase to secret')),
3821 ('f', 'force', False, _('allow to move boundary backward')),
3813 ('f', 'force', False, _('allow to move boundary backward')),
3822 ('r', 'rev', [], _('target revision'), _('REV')),
3814 ('r', 'rev', [], _('target revision'), _('REV')),
3823 ],
3815 ],
3824 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3816 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3825 def phase(ui, repo, *revs, **opts):
3817 def phase(ui, repo, *revs, **opts):
3826 """set or show the current phase name
3818 """set or show the current phase name
3827
3819
3828 With no argument, show the phase name of the current revision(s).
3820 With no argument, show the phase name of the current revision(s).
3829
3821
3830 With one of -p/--public, -d/--draft or -s/--secret, change the
3822 With one of -p/--public, -d/--draft or -s/--secret, change the
3831 phase value of the specified revisions.
3823 phase value of the specified revisions.
3832
3824
3833 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3825 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3834 lower phase to a higher phase. Phases are ordered as follows::
3826 lower phase to a higher phase. Phases are ordered as follows::
3835
3827
3836 public < draft < secret
3828 public < draft < secret
3837
3829
3838 Returns 0 on success, 1 if some phases could not be changed.
3830 Returns 0 on success, 1 if some phases could not be changed.
3839
3831
3840 (For more information about the phases concept, see :hg:`help phases`.)
3832 (For more information about the phases concept, see :hg:`help phases`.)
3841 """
3833 """
3842 opts = pycompat.byteskwargs(opts)
3834 opts = pycompat.byteskwargs(opts)
3843 # search for a unique phase argument
3835 # search for a unique phase argument
3844 targetphase = None
3836 targetphase = None
3845 for idx, name in enumerate(phases.phasenames):
3837 for idx, name in enumerate(phases.phasenames):
3846 if opts[name]:
3838 if opts[name]:
3847 if targetphase is not None:
3839 if targetphase is not None:
3848 raise error.Abort(_('only one phase can be specified'))
3840 raise error.Abort(_('only one phase can be specified'))
3849 targetphase = idx
3841 targetphase = idx
3850
3842
3851 # look for specified revision
3843 # look for specified revision
3852 revs = list(revs)
3844 revs = list(revs)
3853 revs.extend(opts['rev'])
3845 revs.extend(opts['rev'])
3854 if not revs:
3846 if not revs:
3855 # display both parents as the second parent phase can influence
3847 # display both parents as the second parent phase can influence
3856 # the phase of a merge commit
3848 # the phase of a merge commit
3857 revs = [c.rev() for c in repo[None].parents()]
3849 revs = [c.rev() for c in repo[None].parents()]
3858
3850
3859 revs = scmutil.revrange(repo, revs)
3851 revs = scmutil.revrange(repo, revs)
3860
3852
3861 ret = 0
3853 ret = 0
3862 if targetphase is None:
3854 if targetphase is None:
3863 # display
3855 # display
3864 for r in revs:
3856 for r in revs:
3865 ctx = repo[r]
3857 ctx = repo[r]
3866 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3858 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3867 else:
3859 else:
3868 with repo.lock(), repo.transaction("phase") as tr:
3860 with repo.lock(), repo.transaction("phase") as tr:
3869 # set phase
3861 # set phase
3870 if not revs:
3862 if not revs:
3871 raise error.Abort(_('empty revision set'))
3863 raise error.Abort(_('empty revision set'))
3872 nodes = [repo[r].node() for r in revs]
3864 nodes = [repo[r].node() for r in revs]
3873 # moving revision from public to draft may hide them
3865 # moving revision from public to draft may hide them
3874 # We have to check result on an unfiltered repository
3866 # We have to check result on an unfiltered repository
3875 unfi = repo.unfiltered()
3867 unfi = repo.unfiltered()
3876 getphase = unfi._phasecache.phase
3868 getphase = unfi._phasecache.phase
3877 olddata = [getphase(unfi, r) for r in unfi]
3869 olddata = [getphase(unfi, r) for r in unfi]
3878 phases.advanceboundary(repo, tr, targetphase, nodes)
3870 phases.advanceboundary(repo, tr, targetphase, nodes)
3879 if opts['force']:
3871 if opts['force']:
3880 phases.retractboundary(repo, tr, targetphase, nodes)
3872 phases.retractboundary(repo, tr, targetphase, nodes)
3881 getphase = unfi._phasecache.phase
3873 getphase = unfi._phasecache.phase
3882 newdata = [getphase(unfi, r) for r in unfi]
3874 newdata = [getphase(unfi, r) for r in unfi]
3883 changes = sum(newdata[r] != olddata[r] for r in unfi)
3875 changes = sum(newdata[r] != olddata[r] for r in unfi)
3884 cl = unfi.changelog
3876 cl = unfi.changelog
3885 rejected = [n for n in nodes
3877 rejected = [n for n in nodes
3886 if newdata[cl.rev(n)] < targetphase]
3878 if newdata[cl.rev(n)] < targetphase]
3887 if rejected:
3879 if rejected:
3888 ui.warn(_('cannot move %i changesets to a higher '
3880 ui.warn(_('cannot move %i changesets to a higher '
3889 'phase, use --force\n') % len(rejected))
3881 'phase, use --force\n') % len(rejected))
3890 ret = 1
3882 ret = 1
3891 if changes:
3883 if changes:
3892 msg = _('phase changed for %i changesets\n') % changes
3884 msg = _('phase changed for %i changesets\n') % changes
3893 if ret:
3885 if ret:
3894 ui.status(msg)
3886 ui.status(msg)
3895 else:
3887 else:
3896 ui.note(msg)
3888 ui.note(msg)
3897 else:
3889 else:
3898 ui.warn(_('no phases changed\n'))
3890 ui.warn(_('no phases changed\n'))
3899 return ret
3891 return ret
3900
3892
3901 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3893 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3902 """Run after a changegroup has been added via pull/unbundle
3894 """Run after a changegroup has been added via pull/unbundle
3903
3895
3904 This takes arguments below:
3896 This takes arguments below:
3905
3897
3906 :modheads: change of heads by pull/unbundle
3898 :modheads: change of heads by pull/unbundle
3907 :optupdate: updating working directory is needed or not
3899 :optupdate: updating working directory is needed or not
3908 :checkout: update destination revision (or None to default destination)
3900 :checkout: update destination revision (or None to default destination)
3909 :brev: a name, which might be a bookmark to be activated after updating
3901 :brev: a name, which might be a bookmark to be activated after updating
3910 """
3902 """
3911 if modheads == 0:
3903 if modheads == 0:
3912 return
3904 return
3913 if optupdate:
3905 if optupdate:
3914 try:
3906 try:
3915 return hg.updatetotally(ui, repo, checkout, brev)
3907 return hg.updatetotally(ui, repo, checkout, brev)
3916 except error.UpdateAbort as inst:
3908 except error.UpdateAbort as inst:
3917 msg = _("not updating: %s") % str(inst)
3909 msg = _("not updating: %s") % str(inst)
3918 hint = inst.hint
3910 hint = inst.hint
3919 raise error.UpdateAbort(msg, hint=hint)
3911 raise error.UpdateAbort(msg, hint=hint)
3920 if modheads > 1:
3912 if modheads > 1:
3921 currentbranchheads = len(repo.branchheads())
3913 currentbranchheads = len(repo.branchheads())
3922 if currentbranchheads == modheads:
3914 if currentbranchheads == modheads:
3923 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3915 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3924 elif currentbranchheads > 1:
3916 elif currentbranchheads > 1:
3925 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3917 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3926 "merge)\n"))
3918 "merge)\n"))
3927 else:
3919 else:
3928 ui.status(_("(run 'hg heads' to see heads)\n"))
3920 ui.status(_("(run 'hg heads' to see heads)\n"))
3929 elif not ui.configbool('commands', 'update.requiredest'):
3921 elif not ui.configbool('commands', 'update.requiredest'):
3930 ui.status(_("(run 'hg update' to get a working copy)\n"))
3922 ui.status(_("(run 'hg update' to get a working copy)\n"))
3931
3923
3932 @command('^pull',
3924 @command('^pull',
3933 [('u', 'update', None,
3925 [('u', 'update', None,
3934 _('update to new branch head if new descendants were pulled')),
3926 _('update to new branch head if new descendants were pulled')),
3935 ('f', 'force', None, _('run even when remote repository is unrelated')),
3927 ('f', 'force', None, _('run even when remote repository is unrelated')),
3936 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3928 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3937 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3929 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3938 ('b', 'branch', [], _('a specific branch you would like to pull'),
3930 ('b', 'branch', [], _('a specific branch you would like to pull'),
3939 _('BRANCH')),
3931 _('BRANCH')),
3940 ] + remoteopts,
3932 ] + remoteopts,
3941 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3933 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3942 def pull(ui, repo, source="default", **opts):
3934 def pull(ui, repo, source="default", **opts):
3943 """pull changes from the specified source
3935 """pull changes from the specified source
3944
3936
3945 Pull changes from a remote repository to a local one.
3937 Pull changes from a remote repository to a local one.
3946
3938
3947 This finds all changes from the repository at the specified path
3939 This finds all changes from the repository at the specified path
3948 or URL and adds them to a local repository (the current one unless
3940 or URL and adds them to a local repository (the current one unless
3949 -R is specified). By default, this does not update the copy of the
3941 -R is specified). By default, this does not update the copy of the
3950 project in the working directory.
3942 project in the working directory.
3951
3943
3952 Use :hg:`incoming` if you want to see what would have been added
3944 Use :hg:`incoming` if you want to see what would have been added
3953 by a pull at the time you issued this command. If you then decide
3945 by a pull at the time you issued this command. If you then decide
3954 to add those changes to the repository, you should use :hg:`pull
3946 to add those changes to the repository, you should use :hg:`pull
3955 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3947 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3956
3948
3957 If SOURCE is omitted, the 'default' path will be used.
3949 If SOURCE is omitted, the 'default' path will be used.
3958 See :hg:`help urls` for more information.
3950 See :hg:`help urls` for more information.
3959
3951
3960 Specifying bookmark as ``.`` is equivalent to specifying the active
3952 Specifying bookmark as ``.`` is equivalent to specifying the active
3961 bookmark's name.
3953 bookmark's name.
3962
3954
3963 Returns 0 on success, 1 if an update had unresolved files.
3955 Returns 0 on success, 1 if an update had unresolved files.
3964 """
3956 """
3965
3957
3966 opts = pycompat.byteskwargs(opts)
3958 opts = pycompat.byteskwargs(opts)
3967 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3959 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3968 msg = _('update destination required by configuration')
3960 msg = _('update destination required by configuration')
3969 hint = _('use hg pull followed by hg update DEST')
3961 hint = _('use hg pull followed by hg update DEST')
3970 raise error.Abort(msg, hint=hint)
3962 raise error.Abort(msg, hint=hint)
3971
3963
3972 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3964 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3973 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3965 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3974 other = hg.peer(repo, opts, source)
3966 other = hg.peer(repo, opts, source)
3975 try:
3967 try:
3976 revs, checkout = hg.addbranchrevs(repo, other, branches,
3968 revs, checkout = hg.addbranchrevs(repo, other, branches,
3977 opts.get('rev'))
3969 opts.get('rev'))
3978
3970
3979
3971
3980 pullopargs = {}
3972 pullopargs = {}
3981 if opts.get('bookmark'):
3973 if opts.get('bookmark'):
3982 if not revs:
3974 if not revs:
3983 revs = []
3975 revs = []
3984 # The list of bookmark used here is not the one used to actually
3976 # The list of bookmark used here is not the one used to actually
3985 # update the bookmark name. This can result in the revision pulled
3977 # update the bookmark name. This can result in the revision pulled
3986 # not ending up with the name of the bookmark because of a race
3978 # not ending up with the name of the bookmark because of a race
3987 # condition on the server. (See issue 4689 for details)
3979 # condition on the server. (See issue 4689 for details)
3988 remotebookmarks = other.listkeys('bookmarks')
3980 remotebookmarks = other.listkeys('bookmarks')
3989 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
3981 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
3990 pullopargs['remotebookmarks'] = remotebookmarks
3982 pullopargs['remotebookmarks'] = remotebookmarks
3991 for b in opts['bookmark']:
3983 for b in opts['bookmark']:
3992 b = repo._bookmarks.expandname(b)
3984 b = repo._bookmarks.expandname(b)
3993 if b not in remotebookmarks:
3985 if b not in remotebookmarks:
3994 raise error.Abort(_('remote bookmark %s not found!') % b)
3986 raise error.Abort(_('remote bookmark %s not found!') % b)
3995 revs.append(hex(remotebookmarks[b]))
3987 revs.append(hex(remotebookmarks[b]))
3996
3988
3997 if revs:
3989 if revs:
3998 try:
3990 try:
3999 # When 'rev' is a bookmark name, we cannot guarantee that it
3991 # When 'rev' is a bookmark name, we cannot guarantee that it
4000 # will be updated with that name because of a race condition
3992 # will be updated with that name because of a race condition
4001 # server side. (See issue 4689 for details)
3993 # server side. (See issue 4689 for details)
4002 oldrevs = revs
3994 oldrevs = revs
4003 revs = [] # actually, nodes
3995 revs = [] # actually, nodes
4004 for r in oldrevs:
3996 for r in oldrevs:
4005 node = other.lookup(r)
3997 node = other.lookup(r)
4006 revs.append(node)
3998 revs.append(node)
4007 if r == checkout:
3999 if r == checkout:
4008 checkout = node
4000 checkout = node
4009 except error.CapabilityError:
4001 except error.CapabilityError:
4010 err = _("other repository doesn't support revision lookup, "
4002 err = _("other repository doesn't support revision lookup, "
4011 "so a rev cannot be specified.")
4003 "so a rev cannot be specified.")
4012 raise error.Abort(err)
4004 raise error.Abort(err)
4013
4005
4014 wlock = util.nullcontextmanager()
4006 wlock = util.nullcontextmanager()
4015 if opts.get('update'):
4007 if opts.get('update'):
4016 wlock = repo.wlock()
4008 wlock = repo.wlock()
4017 with wlock:
4009 with wlock:
4018 pullopargs.update(opts.get('opargs', {}))
4010 pullopargs.update(opts.get('opargs', {}))
4019 modheads = exchange.pull(repo, other, heads=revs,
4011 modheads = exchange.pull(repo, other, heads=revs,
4020 force=opts.get('force'),
4012 force=opts.get('force'),
4021 bookmarks=opts.get('bookmark', ()),
4013 bookmarks=opts.get('bookmark', ()),
4022 opargs=pullopargs).cgresult
4014 opargs=pullopargs).cgresult
4023
4015
4024 # brev is a name, which might be a bookmark to be activated at
4016 # brev is a name, which might be a bookmark to be activated at
4025 # the end of the update. In other words, it is an explicit
4017 # the end of the update. In other words, it is an explicit
4026 # destination of the update
4018 # destination of the update
4027 brev = None
4019 brev = None
4028
4020
4029 if checkout:
4021 if checkout:
4030 checkout = str(repo.changelog.rev(checkout))
4022 checkout = str(repo.changelog.rev(checkout))
4031
4023
4032 # order below depends on implementation of
4024 # order below depends on implementation of
4033 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4025 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4034 # because 'checkout' is determined without it.
4026 # because 'checkout' is determined without it.
4035 if opts.get('rev'):
4027 if opts.get('rev'):
4036 brev = opts['rev'][0]
4028 brev = opts['rev'][0]
4037 elif opts.get('branch'):
4029 elif opts.get('branch'):
4038 brev = opts['branch'][0]
4030 brev = opts['branch'][0]
4039 else:
4031 else:
4040 brev = branches[0]
4032 brev = branches[0]
4041 repo._subtoppath = source
4033 repo._subtoppath = source
4042 try:
4034 try:
4043 ret = postincoming(ui, repo, modheads, opts.get('update'),
4035 ret = postincoming(ui, repo, modheads, opts.get('update'),
4044 checkout, brev)
4036 checkout, brev)
4045
4037
4046 finally:
4038 finally:
4047 del repo._subtoppath
4039 del repo._subtoppath
4048
4040
4049 finally:
4041 finally:
4050 other.close()
4042 other.close()
4051 return ret
4043 return ret
4052
4044
4053 @command('^push',
4045 @command('^push',
4054 [('f', 'force', None, _('force push')),
4046 [('f', 'force', None, _('force push')),
4055 ('r', 'rev', [],
4047 ('r', 'rev', [],
4056 _('a changeset intended to be included in the destination'),
4048 _('a changeset intended to be included in the destination'),
4057 _('REV')),
4049 _('REV')),
4058 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4050 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4059 ('b', 'branch', [],
4051 ('b', 'branch', [],
4060 _('a specific branch you would like to push'), _('BRANCH')),
4052 _('a specific branch you would like to push'), _('BRANCH')),
4061 ('', 'new-branch', False, _('allow pushing a new branch')),
4053 ('', 'new-branch', False, _('allow pushing a new branch')),
4062 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4054 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4063 ] + remoteopts,
4055 ] + remoteopts,
4064 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4056 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4065 def push(ui, repo, dest=None, **opts):
4057 def push(ui, repo, dest=None, **opts):
4066 """push changes to the specified destination
4058 """push changes to the specified destination
4067
4059
4068 Push changesets from the local repository to the specified
4060 Push changesets from the local repository to the specified
4069 destination.
4061 destination.
4070
4062
4071 This operation is symmetrical to pull: it is identical to a pull
4063 This operation is symmetrical to pull: it is identical to a pull
4072 in the destination repository from the current one.
4064 in the destination repository from the current one.
4073
4065
4074 By default, push will not allow creation of new heads at the
4066 By default, push will not allow creation of new heads at the
4075 destination, since multiple heads would make it unclear which head
4067 destination, since multiple heads would make it unclear which head
4076 to use. In this situation, it is recommended to pull and merge
4068 to use. In this situation, it is recommended to pull and merge
4077 before pushing.
4069 before pushing.
4078
4070
4079 Use --new-branch if you want to allow push to create a new named
4071 Use --new-branch if you want to allow push to create a new named
4080 branch that is not present at the destination. This allows you to
4072 branch that is not present at the destination. This allows you to
4081 only create a new branch without forcing other changes.
4073 only create a new branch without forcing other changes.
4082
4074
4083 .. note::
4075 .. note::
4084
4076
4085 Extra care should be taken with the -f/--force option,
4077 Extra care should be taken with the -f/--force option,
4086 which will push all new heads on all branches, an action which will
4078 which will push all new heads on all branches, an action which will
4087 almost always cause confusion for collaborators.
4079 almost always cause confusion for collaborators.
4088
4080
4089 If -r/--rev is used, the specified revision and all its ancestors
4081 If -r/--rev is used, the specified revision and all its ancestors
4090 will be pushed to the remote repository.
4082 will be pushed to the remote repository.
4091
4083
4092 If -B/--bookmark is used, the specified bookmarked revision, its
4084 If -B/--bookmark is used, the specified bookmarked revision, its
4093 ancestors, and the bookmark will be pushed to the remote
4085 ancestors, and the bookmark will be pushed to the remote
4094 repository. Specifying ``.`` is equivalent to specifying the active
4086 repository. Specifying ``.`` is equivalent to specifying the active
4095 bookmark's name.
4087 bookmark's name.
4096
4088
4097 Please see :hg:`help urls` for important details about ``ssh://``
4089 Please see :hg:`help urls` for important details about ``ssh://``
4098 URLs. If DESTINATION is omitted, a default path will be used.
4090 URLs. If DESTINATION is omitted, a default path will be used.
4099
4091
4100 .. container:: verbose
4092 .. container:: verbose
4101
4093
4102 The --pushvars option sends strings to the server that become
4094 The --pushvars option sends strings to the server that become
4103 environment variables prepended with ``HG_USERVAR_``. For example,
4095 environment variables prepended with ``HG_USERVAR_``. For example,
4104 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4096 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4105 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4097 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4106
4098
4107 pushvars can provide for user-overridable hooks as well as set debug
4099 pushvars can provide for user-overridable hooks as well as set debug
4108 levels. One example is having a hook that blocks commits containing
4100 levels. One example is having a hook that blocks commits containing
4109 conflict markers, but enables the user to override the hook if the file
4101 conflict markers, but enables the user to override the hook if the file
4110 is using conflict markers for testing purposes or the file format has
4102 is using conflict markers for testing purposes or the file format has
4111 strings that look like conflict markers.
4103 strings that look like conflict markers.
4112
4104
4113 By default, servers will ignore `--pushvars`. To enable it add the
4105 By default, servers will ignore `--pushvars`. To enable it add the
4114 following to your configuration file::
4106 following to your configuration file::
4115
4107
4116 [push]
4108 [push]
4117 pushvars.server = true
4109 pushvars.server = true
4118
4110
4119 Returns 0 if push was successful, 1 if nothing to push.
4111 Returns 0 if push was successful, 1 if nothing to push.
4120 """
4112 """
4121
4113
4122 opts = pycompat.byteskwargs(opts)
4114 opts = pycompat.byteskwargs(opts)
4123 if opts.get('bookmark'):
4115 if opts.get('bookmark'):
4124 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4116 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4125 for b in opts['bookmark']:
4117 for b in opts['bookmark']:
4126 # translate -B options to -r so changesets get pushed
4118 # translate -B options to -r so changesets get pushed
4127 b = repo._bookmarks.expandname(b)
4119 b = repo._bookmarks.expandname(b)
4128 if b in repo._bookmarks:
4120 if b in repo._bookmarks:
4129 opts.setdefault('rev', []).append(b)
4121 opts.setdefault('rev', []).append(b)
4130 else:
4122 else:
4131 # if we try to push a deleted bookmark, translate it to null
4123 # if we try to push a deleted bookmark, translate it to null
4132 # this lets simultaneous -r, -b options continue working
4124 # this lets simultaneous -r, -b options continue working
4133 opts.setdefault('rev', []).append("null")
4125 opts.setdefault('rev', []).append("null")
4134
4126
4135 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4127 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4136 if not path:
4128 if not path:
4137 raise error.Abort(_('default repository not configured!'),
4129 raise error.Abort(_('default repository not configured!'),
4138 hint=_("see 'hg help config.paths'"))
4130 hint=_("see 'hg help config.paths'"))
4139 dest = path.pushloc or path.loc
4131 dest = path.pushloc or path.loc
4140 branches = (path.branch, opts.get('branch') or [])
4132 branches = (path.branch, opts.get('branch') or [])
4141 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4133 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4142 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4134 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4143 other = hg.peer(repo, opts, dest)
4135 other = hg.peer(repo, opts, dest)
4144
4136
4145 if revs:
4137 if revs:
4146 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4138 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4147 if not revs:
4139 if not revs:
4148 raise error.Abort(_("specified revisions evaluate to an empty set"),
4140 raise error.Abort(_("specified revisions evaluate to an empty set"),
4149 hint=_("use different revision arguments"))
4141 hint=_("use different revision arguments"))
4150 elif path.pushrev:
4142 elif path.pushrev:
4151 # It doesn't make any sense to specify ancestor revisions. So limit
4143 # It doesn't make any sense to specify ancestor revisions. So limit
4152 # to DAG heads to make discovery simpler.
4144 # to DAG heads to make discovery simpler.
4153 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4145 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4154 revs = scmutil.revrange(repo, [expr])
4146 revs = scmutil.revrange(repo, [expr])
4155 revs = [repo[rev].node() for rev in revs]
4147 revs = [repo[rev].node() for rev in revs]
4156 if not revs:
4148 if not revs:
4157 raise error.Abort(_('default push revset for path evaluates to an '
4149 raise error.Abort(_('default push revset for path evaluates to an '
4158 'empty set'))
4150 'empty set'))
4159
4151
4160 repo._subtoppath = dest
4152 repo._subtoppath = dest
4161 try:
4153 try:
4162 # push subrepos depth-first for coherent ordering
4154 # push subrepos depth-first for coherent ordering
4163 c = repo['']
4155 c = repo['']
4164 subs = c.substate # only repos that are committed
4156 subs = c.substate # only repos that are committed
4165 for s in sorted(subs):
4157 for s in sorted(subs):
4166 result = c.sub(s).push(opts)
4158 result = c.sub(s).push(opts)
4167 if result == 0:
4159 if result == 0:
4168 return not result
4160 return not result
4169 finally:
4161 finally:
4170 del repo._subtoppath
4162 del repo._subtoppath
4171
4163
4172 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4164 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4173 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4165 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4174
4166
4175 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4167 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4176 newbranch=opts.get('new_branch'),
4168 newbranch=opts.get('new_branch'),
4177 bookmarks=opts.get('bookmark', ()),
4169 bookmarks=opts.get('bookmark', ()),
4178 opargs=opargs)
4170 opargs=opargs)
4179
4171
4180 result = not pushop.cgresult
4172 result = not pushop.cgresult
4181
4173
4182 if pushop.bkresult is not None:
4174 if pushop.bkresult is not None:
4183 if pushop.bkresult == 2:
4175 if pushop.bkresult == 2:
4184 result = 2
4176 result = 2
4185 elif not result and pushop.bkresult:
4177 elif not result and pushop.bkresult:
4186 result = 2
4178 result = 2
4187
4179
4188 return result
4180 return result
4189
4181
4190 @command('recover', [])
4182 @command('recover', [])
4191 def recover(ui, repo):
4183 def recover(ui, repo):
4192 """roll back an interrupted transaction
4184 """roll back an interrupted transaction
4193
4185
4194 Recover from an interrupted commit or pull.
4186 Recover from an interrupted commit or pull.
4195
4187
4196 This command tries to fix the repository status after an
4188 This command tries to fix the repository status after an
4197 interrupted operation. It should only be necessary when Mercurial
4189 interrupted operation. It should only be necessary when Mercurial
4198 suggests it.
4190 suggests it.
4199
4191
4200 Returns 0 if successful, 1 if nothing to recover or verify fails.
4192 Returns 0 if successful, 1 if nothing to recover or verify fails.
4201 """
4193 """
4202 if repo.recover():
4194 if repo.recover():
4203 return hg.verify(repo)
4195 return hg.verify(repo)
4204 return 1
4196 return 1
4205
4197
4206 @command('^remove|rm',
4198 @command('^remove|rm',
4207 [('A', 'after', None, _('record delete for missing files')),
4199 [('A', 'after', None, _('record delete for missing files')),
4208 ('f', 'force', None,
4200 ('f', 'force', None,
4209 _('forget added files, delete modified files')),
4201 _('forget added files, delete modified files')),
4210 ] + subrepoopts + walkopts,
4202 ] + subrepoopts + walkopts,
4211 _('[OPTION]... FILE...'),
4203 _('[OPTION]... FILE...'),
4212 inferrepo=True)
4204 inferrepo=True)
4213 def remove(ui, repo, *pats, **opts):
4205 def remove(ui, repo, *pats, **opts):
4214 """remove the specified files on the next commit
4206 """remove the specified files on the next commit
4215
4207
4216 Schedule the indicated files for removal from the current branch.
4208 Schedule the indicated files for removal from the current branch.
4217
4209
4218 This command schedules the files to be removed at the next commit.
4210 This command schedules the files to be removed at the next commit.
4219 To undo a remove before that, see :hg:`revert`. To undo added
4211 To undo a remove before that, see :hg:`revert`. To undo added
4220 files, see :hg:`forget`.
4212 files, see :hg:`forget`.
4221
4213
4222 .. container:: verbose
4214 .. container:: verbose
4223
4215
4224 -A/--after can be used to remove only files that have already
4216 -A/--after can be used to remove only files that have already
4225 been deleted, -f/--force can be used to force deletion, and -Af
4217 been deleted, -f/--force can be used to force deletion, and -Af
4226 can be used to remove files from the next revision without
4218 can be used to remove files from the next revision without
4227 deleting them from the working directory.
4219 deleting them from the working directory.
4228
4220
4229 The following table details the behavior of remove for different
4221 The following table details the behavior of remove for different
4230 file states (columns) and option combinations (rows). The file
4222 file states (columns) and option combinations (rows). The file
4231 states are Added [A], Clean [C], Modified [M] and Missing [!]
4223 states are Added [A], Clean [C], Modified [M] and Missing [!]
4232 (as reported by :hg:`status`). The actions are Warn, Remove
4224 (as reported by :hg:`status`). The actions are Warn, Remove
4233 (from branch) and Delete (from disk):
4225 (from branch) and Delete (from disk):
4234
4226
4235 ========= == == == ==
4227 ========= == == == ==
4236 opt/state A C M !
4228 opt/state A C M !
4237 ========= == == == ==
4229 ========= == == == ==
4238 none W RD W R
4230 none W RD W R
4239 -f R RD RD R
4231 -f R RD RD R
4240 -A W W W R
4232 -A W W W R
4241 -Af R R R R
4233 -Af R R R R
4242 ========= == == == ==
4234 ========= == == == ==
4243
4235
4244 .. note::
4236 .. note::
4245
4237
4246 :hg:`remove` never deletes files in Added [A] state from the
4238 :hg:`remove` never deletes files in Added [A] state from the
4247 working directory, not even if ``--force`` is specified.
4239 working directory, not even if ``--force`` is specified.
4248
4240
4249 Returns 0 on success, 1 if any warnings encountered.
4241 Returns 0 on success, 1 if any warnings encountered.
4250 """
4242 """
4251
4243
4252 opts = pycompat.byteskwargs(opts)
4244 opts = pycompat.byteskwargs(opts)
4253 after, force = opts.get('after'), opts.get('force')
4245 after, force = opts.get('after'), opts.get('force')
4254 if not pats and not after:
4246 if not pats and not after:
4255 raise error.Abort(_('no files specified'))
4247 raise error.Abort(_('no files specified'))
4256
4248
4257 m = scmutil.match(repo[None], pats, opts)
4249 m = scmutil.match(repo[None], pats, opts)
4258 subrepos = opts.get('subrepos')
4250 subrepos = opts.get('subrepos')
4259 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4251 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4260
4252
4261 @command('rename|move|mv',
4253 @command('rename|move|mv',
4262 [('A', 'after', None, _('record a rename that has already occurred')),
4254 [('A', 'after', None, _('record a rename that has already occurred')),
4263 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4255 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4264 ] + walkopts + dryrunopts,
4256 ] + walkopts + dryrunopts,
4265 _('[OPTION]... SOURCE... DEST'))
4257 _('[OPTION]... SOURCE... DEST'))
4266 def rename(ui, repo, *pats, **opts):
4258 def rename(ui, repo, *pats, **opts):
4267 """rename files; equivalent of copy + remove
4259 """rename files; equivalent of copy + remove
4268
4260
4269 Mark dest as copies of sources; mark sources for deletion. If dest
4261 Mark dest as copies of sources; mark sources for deletion. If dest
4270 is a directory, copies are put in that directory. If dest is a
4262 is a directory, copies are put in that directory. If dest is a
4271 file, there can only be one source.
4263 file, there can only be one source.
4272
4264
4273 By default, this command copies the contents of files as they
4265 By default, this command copies the contents of files as they
4274 exist in the working directory. If invoked with -A/--after, the
4266 exist in the working directory. If invoked with -A/--after, the
4275 operation is recorded, but no copying is performed.
4267 operation is recorded, but no copying is performed.
4276
4268
4277 This command takes effect at the next commit. To undo a rename
4269 This command takes effect at the next commit. To undo a rename
4278 before that, see :hg:`revert`.
4270 before that, see :hg:`revert`.
4279
4271
4280 Returns 0 on success, 1 if errors are encountered.
4272 Returns 0 on success, 1 if errors are encountered.
4281 """
4273 """
4282 opts = pycompat.byteskwargs(opts)
4274 opts = pycompat.byteskwargs(opts)
4283 with repo.wlock(False):
4275 with repo.wlock(False):
4284 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4276 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4285
4277
4286 @command('resolve',
4278 @command('resolve',
4287 [('a', 'all', None, _('select all unresolved files')),
4279 [('a', 'all', None, _('select all unresolved files')),
4288 ('l', 'list', None, _('list state of files needing merge')),
4280 ('l', 'list', None, _('list state of files needing merge')),
4289 ('m', 'mark', None, _('mark files as resolved')),
4281 ('m', 'mark', None, _('mark files as resolved')),
4290 ('u', 'unmark', None, _('mark files as unresolved')),
4282 ('u', 'unmark', None, _('mark files as unresolved')),
4291 ('n', 'no-status', None, _('hide status prefix'))]
4283 ('n', 'no-status', None, _('hide status prefix'))]
4292 + mergetoolopts + walkopts + formatteropts,
4284 + mergetoolopts + walkopts + formatteropts,
4293 _('[OPTION]... [FILE]...'),
4285 _('[OPTION]... [FILE]...'),
4294 inferrepo=True)
4286 inferrepo=True)
4295 def resolve(ui, repo, *pats, **opts):
4287 def resolve(ui, repo, *pats, **opts):
4296 """redo merges or set/view the merge status of files
4288 """redo merges or set/view the merge status of files
4297
4289
4298 Merges with unresolved conflicts are often the result of
4290 Merges with unresolved conflicts are often the result of
4299 non-interactive merging using the ``internal:merge`` configuration
4291 non-interactive merging using the ``internal:merge`` configuration
4300 setting, or a command-line merge tool like ``diff3``. The resolve
4292 setting, or a command-line merge tool like ``diff3``. The resolve
4301 command is used to manage the files involved in a merge, after
4293 command is used to manage the files involved in a merge, after
4302 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4294 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4303 working directory must have two parents). See :hg:`help
4295 working directory must have two parents). See :hg:`help
4304 merge-tools` for information on configuring merge tools.
4296 merge-tools` for information on configuring merge tools.
4305
4297
4306 The resolve command can be used in the following ways:
4298 The resolve command can be used in the following ways:
4307
4299
4308 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4300 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4309 files, discarding any previous merge attempts. Re-merging is not
4301 files, discarding any previous merge attempts. Re-merging is not
4310 performed for files already marked as resolved. Use ``--all/-a``
4302 performed for files already marked as resolved. Use ``--all/-a``
4311 to select all unresolved files. ``--tool`` can be used to specify
4303 to select all unresolved files. ``--tool`` can be used to specify
4312 the merge tool used for the given files. It overrides the HGMERGE
4304 the merge tool used for the given files. It overrides the HGMERGE
4313 environment variable and your configuration files. Previous file
4305 environment variable and your configuration files. Previous file
4314 contents are saved with a ``.orig`` suffix.
4306 contents are saved with a ``.orig`` suffix.
4315
4307
4316 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4308 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4317 (e.g. after having manually fixed-up the files). The default is
4309 (e.g. after having manually fixed-up the files). The default is
4318 to mark all unresolved files.
4310 to mark all unresolved files.
4319
4311
4320 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4312 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4321 default is to mark all resolved files.
4313 default is to mark all resolved files.
4322
4314
4323 - :hg:`resolve -l`: list files which had or still have conflicts.
4315 - :hg:`resolve -l`: list files which had or still have conflicts.
4324 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4316 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4325 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4317 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4326 the list. See :hg:`help filesets` for details.
4318 the list. See :hg:`help filesets` for details.
4327
4319
4328 .. note::
4320 .. note::
4329
4321
4330 Mercurial will not let you commit files with unresolved merge
4322 Mercurial will not let you commit files with unresolved merge
4331 conflicts. You must use :hg:`resolve -m ...` before you can
4323 conflicts. You must use :hg:`resolve -m ...` before you can
4332 commit after a conflicting merge.
4324 commit after a conflicting merge.
4333
4325
4334 Returns 0 on success, 1 if any files fail a resolve attempt.
4326 Returns 0 on success, 1 if any files fail a resolve attempt.
4335 """
4327 """
4336
4328
4337 opts = pycompat.byteskwargs(opts)
4329 opts = pycompat.byteskwargs(opts)
4338 flaglist = 'all mark unmark list no_status'.split()
4330 flaglist = 'all mark unmark list no_status'.split()
4339 all, mark, unmark, show, nostatus = \
4331 all, mark, unmark, show, nostatus = \
4340 [opts.get(o) for o in flaglist]
4332 [opts.get(o) for o in flaglist]
4341
4333
4342 if (show and (mark or unmark)) or (mark and unmark):
4334 if (show and (mark or unmark)) or (mark and unmark):
4343 raise error.Abort(_("too many options specified"))
4335 raise error.Abort(_("too many options specified"))
4344 if pats and all:
4336 if pats and all:
4345 raise error.Abort(_("can't specify --all and patterns"))
4337 raise error.Abort(_("can't specify --all and patterns"))
4346 if not (all or pats or show or mark or unmark):
4338 if not (all or pats or show or mark or unmark):
4347 raise error.Abort(_('no files or directories specified'),
4339 raise error.Abort(_('no files or directories specified'),
4348 hint=('use --all to re-merge all unresolved files'))
4340 hint=('use --all to re-merge all unresolved files'))
4349
4341
4350 if show:
4342 if show:
4351 ui.pager('resolve')
4343 ui.pager('resolve')
4352 fm = ui.formatter('resolve', opts)
4344 fm = ui.formatter('resolve', opts)
4353 ms = mergemod.mergestate.read(repo)
4345 ms = mergemod.mergestate.read(repo)
4354 m = scmutil.match(repo[None], pats, opts)
4346 m = scmutil.match(repo[None], pats, opts)
4355
4347
4356 # Labels and keys based on merge state. Unresolved path conflicts show
4348 # Labels and keys based on merge state. Unresolved path conflicts show
4357 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4349 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4358 # resolved conflicts.
4350 # resolved conflicts.
4359 mergestateinfo = {
4351 mergestateinfo = {
4360 'u': ('resolve.unresolved', 'U'),
4352 'u': ('resolve.unresolved', 'U'),
4361 'r': ('resolve.resolved', 'R'),
4353 'r': ('resolve.resolved', 'R'),
4362 'pu': ('resolve.unresolved', 'P'),
4354 'pu': ('resolve.unresolved', 'P'),
4363 'pr': ('resolve.resolved', 'R'),
4355 'pr': ('resolve.resolved', 'R'),
4364 'd': ('resolve.driverresolved', 'D'),
4356 'd': ('resolve.driverresolved', 'D'),
4365 }
4357 }
4366
4358
4367 for f in ms:
4359 for f in ms:
4368 if not m(f):
4360 if not m(f):
4369 continue
4361 continue
4370
4362
4371 label, key = mergestateinfo[ms[f]]
4363 label, key = mergestateinfo[ms[f]]
4372 fm.startitem()
4364 fm.startitem()
4373 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4365 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4374 fm.write('path', '%s\n', f, label=label)
4366 fm.write('path', '%s\n', f, label=label)
4375 fm.end()
4367 fm.end()
4376 return 0
4368 return 0
4377
4369
4378 with repo.wlock():
4370 with repo.wlock():
4379 ms = mergemod.mergestate.read(repo)
4371 ms = mergemod.mergestate.read(repo)
4380
4372
4381 if not (ms.active() or repo.dirstate.p2() != nullid):
4373 if not (ms.active() or repo.dirstate.p2() != nullid):
4382 raise error.Abort(
4374 raise error.Abort(
4383 _('resolve command not applicable when not merging'))
4375 _('resolve command not applicable when not merging'))
4384
4376
4385 wctx = repo[None]
4377 wctx = repo[None]
4386
4378
4387 if ms.mergedriver and ms.mdstate() == 'u':
4379 if ms.mergedriver and ms.mdstate() == 'u':
4388 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4380 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4389 ms.commit()
4381 ms.commit()
4390 # allow mark and unmark to go through
4382 # allow mark and unmark to go through
4391 if not mark and not unmark and not proceed:
4383 if not mark and not unmark and not proceed:
4392 return 1
4384 return 1
4393
4385
4394 m = scmutil.match(wctx, pats, opts)
4386 m = scmutil.match(wctx, pats, opts)
4395 ret = 0
4387 ret = 0
4396 didwork = False
4388 didwork = False
4397 runconclude = False
4389 runconclude = False
4398
4390
4399 tocomplete = []
4391 tocomplete = []
4400 for f in ms:
4392 for f in ms:
4401 if not m(f):
4393 if not m(f):
4402 continue
4394 continue
4403
4395
4404 didwork = True
4396 didwork = True
4405
4397
4406 # don't let driver-resolved files be marked, and run the conclude
4398 # don't let driver-resolved files be marked, and run the conclude
4407 # step if asked to resolve
4399 # step if asked to resolve
4408 if ms[f] == "d":
4400 if ms[f] == "d":
4409 exact = m.exact(f)
4401 exact = m.exact(f)
4410 if mark:
4402 if mark:
4411 if exact:
4403 if exact:
4412 ui.warn(_('not marking %s as it is driver-resolved\n')
4404 ui.warn(_('not marking %s as it is driver-resolved\n')
4413 % f)
4405 % f)
4414 elif unmark:
4406 elif unmark:
4415 if exact:
4407 if exact:
4416 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4408 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4417 % f)
4409 % f)
4418 else:
4410 else:
4419 runconclude = True
4411 runconclude = True
4420 continue
4412 continue
4421
4413
4422 # path conflicts must be resolved manually
4414 # path conflicts must be resolved manually
4423 if ms[f] in ("pu", "pr"):
4415 if ms[f] in ("pu", "pr"):
4424 if mark:
4416 if mark:
4425 ms.mark(f, "pr")
4417 ms.mark(f, "pr")
4426 elif unmark:
4418 elif unmark:
4427 ms.mark(f, "pu")
4419 ms.mark(f, "pu")
4428 elif ms[f] == "pu":
4420 elif ms[f] == "pu":
4429 ui.warn(_('%s: path conflict must be resolved manually\n')
4421 ui.warn(_('%s: path conflict must be resolved manually\n')
4430 % f)
4422 % f)
4431 continue
4423 continue
4432
4424
4433 if mark:
4425 if mark:
4434 ms.mark(f, "r")
4426 ms.mark(f, "r")
4435 elif unmark:
4427 elif unmark:
4436 ms.mark(f, "u")
4428 ms.mark(f, "u")
4437 else:
4429 else:
4438 # backup pre-resolve (merge uses .orig for its own purposes)
4430 # backup pre-resolve (merge uses .orig for its own purposes)
4439 a = repo.wjoin(f)
4431 a = repo.wjoin(f)
4440 try:
4432 try:
4441 util.copyfile(a, a + ".resolve")
4433 util.copyfile(a, a + ".resolve")
4442 except (IOError, OSError) as inst:
4434 except (IOError, OSError) as inst:
4443 if inst.errno != errno.ENOENT:
4435 if inst.errno != errno.ENOENT:
4444 raise
4436 raise
4445
4437
4446 try:
4438 try:
4447 # preresolve file
4439 # preresolve file
4448 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4440 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4449 'resolve')
4441 'resolve')
4450 complete, r = ms.preresolve(f, wctx)
4442 complete, r = ms.preresolve(f, wctx)
4451 if not complete:
4443 if not complete:
4452 tocomplete.append(f)
4444 tocomplete.append(f)
4453 elif r:
4445 elif r:
4454 ret = 1
4446 ret = 1
4455 finally:
4447 finally:
4456 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4448 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4457 ms.commit()
4449 ms.commit()
4458
4450
4459 # replace filemerge's .orig file with our resolve file, but only
4451 # replace filemerge's .orig file with our resolve file, but only
4460 # for merges that are complete
4452 # for merges that are complete
4461 if complete:
4453 if complete:
4462 try:
4454 try:
4463 util.rename(a + ".resolve",
4455 util.rename(a + ".resolve",
4464 scmutil.origpath(ui, repo, a))
4456 scmutil.origpath(ui, repo, a))
4465 except OSError as inst:
4457 except OSError as inst:
4466 if inst.errno != errno.ENOENT:
4458 if inst.errno != errno.ENOENT:
4467 raise
4459 raise
4468
4460
4469 for f in tocomplete:
4461 for f in tocomplete:
4470 try:
4462 try:
4471 # resolve file
4463 # resolve file
4472 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4464 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4473 'resolve')
4465 'resolve')
4474 r = ms.resolve(f, wctx)
4466 r = ms.resolve(f, wctx)
4475 if r:
4467 if r:
4476 ret = 1
4468 ret = 1
4477 finally:
4469 finally:
4478 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4470 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4479 ms.commit()
4471 ms.commit()
4480
4472
4481 # replace filemerge's .orig file with our resolve file
4473 # replace filemerge's .orig file with our resolve file
4482 a = repo.wjoin(f)
4474 a = repo.wjoin(f)
4483 try:
4475 try:
4484 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4476 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4485 except OSError as inst:
4477 except OSError as inst:
4486 if inst.errno != errno.ENOENT:
4478 if inst.errno != errno.ENOENT:
4487 raise
4479 raise
4488
4480
4489 ms.commit()
4481 ms.commit()
4490 ms.recordactions()
4482 ms.recordactions()
4491
4483
4492 if not didwork and pats:
4484 if not didwork and pats:
4493 hint = None
4485 hint = None
4494 if not any([p for p in pats if p.find(':') >= 0]):
4486 if not any([p for p in pats if p.find(':') >= 0]):
4495 pats = ['path:%s' % p for p in pats]
4487 pats = ['path:%s' % p for p in pats]
4496 m = scmutil.match(wctx, pats, opts)
4488 m = scmutil.match(wctx, pats, opts)
4497 for f in ms:
4489 for f in ms:
4498 if not m(f):
4490 if not m(f):
4499 continue
4491 continue
4500 flags = ''.join(['-%s ' % o[0] for o in flaglist
4492 flags = ''.join(['-%s ' % o[0] for o in flaglist
4501 if opts.get(o)])
4493 if opts.get(o)])
4502 hint = _("(try: hg resolve %s%s)\n") % (
4494 hint = _("(try: hg resolve %s%s)\n") % (
4503 flags,
4495 flags,
4504 ' '.join(pats))
4496 ' '.join(pats))
4505 break
4497 break
4506 ui.warn(_("arguments do not match paths that need resolving\n"))
4498 ui.warn(_("arguments do not match paths that need resolving\n"))
4507 if hint:
4499 if hint:
4508 ui.warn(hint)
4500 ui.warn(hint)
4509 elif ms.mergedriver and ms.mdstate() != 's':
4501 elif ms.mergedriver and ms.mdstate() != 's':
4510 # run conclude step when either a driver-resolved file is requested
4502 # run conclude step when either a driver-resolved file is requested
4511 # or there are no driver-resolved files
4503 # or there are no driver-resolved files
4512 # we can't use 'ret' to determine whether any files are unresolved
4504 # we can't use 'ret' to determine whether any files are unresolved
4513 # because we might not have tried to resolve some
4505 # because we might not have tried to resolve some
4514 if ((runconclude or not list(ms.driverresolved()))
4506 if ((runconclude or not list(ms.driverresolved()))
4515 and not list(ms.unresolved())):
4507 and not list(ms.unresolved())):
4516 proceed = mergemod.driverconclude(repo, ms, wctx)
4508 proceed = mergemod.driverconclude(repo, ms, wctx)
4517 ms.commit()
4509 ms.commit()
4518 if not proceed:
4510 if not proceed:
4519 return 1
4511 return 1
4520
4512
4521 # Nudge users into finishing an unfinished operation
4513 # Nudge users into finishing an unfinished operation
4522 unresolvedf = list(ms.unresolved())
4514 unresolvedf = list(ms.unresolved())
4523 driverresolvedf = list(ms.driverresolved())
4515 driverresolvedf = list(ms.driverresolved())
4524 if not unresolvedf and not driverresolvedf:
4516 if not unresolvedf and not driverresolvedf:
4525 ui.status(_('(no more unresolved files)\n'))
4517 ui.status(_('(no more unresolved files)\n'))
4526 cmdutil.checkafterresolved(repo)
4518 cmdutil.checkafterresolved(repo)
4527 elif not unresolvedf:
4519 elif not unresolvedf:
4528 ui.status(_('(no more unresolved files -- '
4520 ui.status(_('(no more unresolved files -- '
4529 'run "hg resolve --all" to conclude)\n'))
4521 'run "hg resolve --all" to conclude)\n'))
4530
4522
4531 return ret
4523 return ret
4532
4524
4533 @command('revert',
4525 @command('revert',
4534 [('a', 'all', None, _('revert all changes when no arguments given')),
4526 [('a', 'all', None, _('revert all changes when no arguments given')),
4535 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4527 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4536 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4528 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4537 ('C', 'no-backup', None, _('do not save backup copies of files')),
4529 ('C', 'no-backup', None, _('do not save backup copies of files')),
4538 ('i', 'interactive', None, _('interactively select the changes')),
4530 ('i', 'interactive', None, _('interactively select the changes')),
4539 ] + walkopts + dryrunopts,
4531 ] + walkopts + dryrunopts,
4540 _('[OPTION]... [-r REV] [NAME]...'))
4532 _('[OPTION]... [-r REV] [NAME]...'))
4541 def revert(ui, repo, *pats, **opts):
4533 def revert(ui, repo, *pats, **opts):
4542 """restore files to their checkout state
4534 """restore files to their checkout state
4543
4535
4544 .. note::
4536 .. note::
4545
4537
4546 To check out earlier revisions, you should use :hg:`update REV`.
4538 To check out earlier revisions, you should use :hg:`update REV`.
4547 To cancel an uncommitted merge (and lose your changes),
4539 To cancel an uncommitted merge (and lose your changes),
4548 use :hg:`update --clean .`.
4540 use :hg:`update --clean .`.
4549
4541
4550 With no revision specified, revert the specified files or directories
4542 With no revision specified, revert the specified files or directories
4551 to the contents they had in the parent of the working directory.
4543 to the contents they had in the parent of the working directory.
4552 This restores the contents of files to an unmodified
4544 This restores the contents of files to an unmodified
4553 state and unschedules adds, removes, copies, and renames. If the
4545 state and unschedules adds, removes, copies, and renames. If the
4554 working directory has two parents, you must explicitly specify a
4546 working directory has two parents, you must explicitly specify a
4555 revision.
4547 revision.
4556
4548
4557 Using the -r/--rev or -d/--date options, revert the given files or
4549 Using the -r/--rev or -d/--date options, revert the given files or
4558 directories to their states as of a specific revision. Because
4550 directories to their states as of a specific revision. Because
4559 revert does not change the working directory parents, this will
4551 revert does not change the working directory parents, this will
4560 cause these files to appear modified. This can be helpful to "back
4552 cause these files to appear modified. This can be helpful to "back
4561 out" some or all of an earlier change. See :hg:`backout` for a
4553 out" some or all of an earlier change. See :hg:`backout` for a
4562 related method.
4554 related method.
4563
4555
4564 Modified files are saved with a .orig suffix before reverting.
4556 Modified files are saved with a .orig suffix before reverting.
4565 To disable these backups, use --no-backup. It is possible to store
4557 To disable these backups, use --no-backup. It is possible to store
4566 the backup files in a custom directory relative to the root of the
4558 the backup files in a custom directory relative to the root of the
4567 repository by setting the ``ui.origbackuppath`` configuration
4559 repository by setting the ``ui.origbackuppath`` configuration
4568 option.
4560 option.
4569
4561
4570 See :hg:`help dates` for a list of formats valid for -d/--date.
4562 See :hg:`help dates` for a list of formats valid for -d/--date.
4571
4563
4572 See :hg:`help backout` for a way to reverse the effect of an
4564 See :hg:`help backout` for a way to reverse the effect of an
4573 earlier changeset.
4565 earlier changeset.
4574
4566
4575 Returns 0 on success.
4567 Returns 0 on success.
4576 """
4568 """
4577
4569
4578 opts = pycompat.byteskwargs(opts)
4570 opts = pycompat.byteskwargs(opts)
4579 if opts.get("date"):
4571 if opts.get("date"):
4580 if opts.get("rev"):
4572 if opts.get("rev"):
4581 raise error.Abort(_("you can't specify a revision and a date"))
4573 raise error.Abort(_("you can't specify a revision and a date"))
4582 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4574 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4583
4575
4584 parent, p2 = repo.dirstate.parents()
4576 parent, p2 = repo.dirstate.parents()
4585 if not opts.get('rev') and p2 != nullid:
4577 if not opts.get('rev') and p2 != nullid:
4586 # revert after merge is a trap for new users (issue2915)
4578 # revert after merge is a trap for new users (issue2915)
4587 raise error.Abort(_('uncommitted merge with no revision specified'),
4579 raise error.Abort(_('uncommitted merge with no revision specified'),
4588 hint=_("use 'hg update' or see 'hg help revert'"))
4580 hint=_("use 'hg update' or see 'hg help revert'"))
4589
4581
4590 rev = opts.get('rev')
4582 rev = opts.get('rev')
4591 if rev:
4583 if rev:
4592 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4584 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4593 ctx = scmutil.revsingle(repo, rev)
4585 ctx = scmutil.revsingle(repo, rev)
4594
4586
4595 if (not (pats or opts.get('include') or opts.get('exclude') or
4587 if (not (pats or opts.get('include') or opts.get('exclude') or
4596 opts.get('all') or opts.get('interactive'))):
4588 opts.get('all') or opts.get('interactive'))):
4597 msg = _("no files or directories specified")
4589 msg = _("no files or directories specified")
4598 if p2 != nullid:
4590 if p2 != nullid:
4599 hint = _("uncommitted merge, use --all to discard all changes,"
4591 hint = _("uncommitted merge, use --all to discard all changes,"
4600 " or 'hg update -C .' to abort the merge")
4592 " or 'hg update -C .' to abort the merge")
4601 raise error.Abort(msg, hint=hint)
4593 raise error.Abort(msg, hint=hint)
4602 dirty = any(repo.status())
4594 dirty = any(repo.status())
4603 node = ctx.node()
4595 node = ctx.node()
4604 if node != parent:
4596 if node != parent:
4605 if dirty:
4597 if dirty:
4606 hint = _("uncommitted changes, use --all to discard all"
4598 hint = _("uncommitted changes, use --all to discard all"
4607 " changes, or 'hg update %s' to update") % ctx.rev()
4599 " changes, or 'hg update %s' to update") % ctx.rev()
4608 else:
4600 else:
4609 hint = _("use --all to revert all files,"
4601 hint = _("use --all to revert all files,"
4610 " or 'hg update %s' to update") % ctx.rev()
4602 " or 'hg update %s' to update") % ctx.rev()
4611 elif dirty:
4603 elif dirty:
4612 hint = _("uncommitted changes, use --all to discard all changes")
4604 hint = _("uncommitted changes, use --all to discard all changes")
4613 else:
4605 else:
4614 hint = _("use --all to revert all files")
4606 hint = _("use --all to revert all files")
4615 raise error.Abort(msg, hint=hint)
4607 raise error.Abort(msg, hint=hint)
4616
4608
4617 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4609 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4618 **pycompat.strkwargs(opts))
4610 **pycompat.strkwargs(opts))
4619
4611
4620 @command('rollback', dryrunopts +
4612 @command('rollback', dryrunopts +
4621 [('f', 'force', False, _('ignore safety measures'))])
4613 [('f', 'force', False, _('ignore safety measures'))])
4622 def rollback(ui, repo, **opts):
4614 def rollback(ui, repo, **opts):
4623 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4615 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4624
4616
4625 Please use :hg:`commit --amend` instead of rollback to correct
4617 Please use :hg:`commit --amend` instead of rollback to correct
4626 mistakes in the last commit.
4618 mistakes in the last commit.
4627
4619
4628 This command should be used with care. There is only one level of
4620 This command should be used with care. There is only one level of
4629 rollback, and there is no way to undo a rollback. It will also
4621 rollback, and there is no way to undo a rollback. It will also
4630 restore the dirstate at the time of the last transaction, losing
4622 restore the dirstate at the time of the last transaction, losing
4631 any dirstate changes since that time. This command does not alter
4623 any dirstate changes since that time. This command does not alter
4632 the working directory.
4624 the working directory.
4633
4625
4634 Transactions are used to encapsulate the effects of all commands
4626 Transactions are used to encapsulate the effects of all commands
4635 that create new changesets or propagate existing changesets into a
4627 that create new changesets or propagate existing changesets into a
4636 repository.
4628 repository.
4637
4629
4638 .. container:: verbose
4630 .. container:: verbose
4639
4631
4640 For example, the following commands are transactional, and their
4632 For example, the following commands are transactional, and their
4641 effects can be rolled back:
4633 effects can be rolled back:
4642
4634
4643 - commit
4635 - commit
4644 - import
4636 - import
4645 - pull
4637 - pull
4646 - push (with this repository as the destination)
4638 - push (with this repository as the destination)
4647 - unbundle
4639 - unbundle
4648
4640
4649 To avoid permanent data loss, rollback will refuse to rollback a
4641 To avoid permanent data loss, rollback will refuse to rollback a
4650 commit transaction if it isn't checked out. Use --force to
4642 commit transaction if it isn't checked out. Use --force to
4651 override this protection.
4643 override this protection.
4652
4644
4653 The rollback command can be entirely disabled by setting the
4645 The rollback command can be entirely disabled by setting the
4654 ``ui.rollback`` configuration setting to false. If you're here
4646 ``ui.rollback`` configuration setting to false. If you're here
4655 because you want to use rollback and it's disabled, you can
4647 because you want to use rollback and it's disabled, you can
4656 re-enable the command by setting ``ui.rollback`` to true.
4648 re-enable the command by setting ``ui.rollback`` to true.
4657
4649
4658 This command is not intended for use on public repositories. Once
4650 This command is not intended for use on public repositories. Once
4659 changes are visible for pull by other users, rolling a transaction
4651 changes are visible for pull by other users, rolling a transaction
4660 back locally is ineffective (someone else may already have pulled
4652 back locally is ineffective (someone else may already have pulled
4661 the changes). Furthermore, a race is possible with readers of the
4653 the changes). Furthermore, a race is possible with readers of the
4662 repository; for example an in-progress pull from the repository
4654 repository; for example an in-progress pull from the repository
4663 may fail if a rollback is performed.
4655 may fail if a rollback is performed.
4664
4656
4665 Returns 0 on success, 1 if no rollback data is available.
4657 Returns 0 on success, 1 if no rollback data is available.
4666 """
4658 """
4667 if not ui.configbool('ui', 'rollback'):
4659 if not ui.configbool('ui', 'rollback'):
4668 raise error.Abort(_('rollback is disabled because it is unsafe'),
4660 raise error.Abort(_('rollback is disabled because it is unsafe'),
4669 hint=('see `hg help -v rollback` for information'))
4661 hint=('see `hg help -v rollback` for information'))
4670 return repo.rollback(dryrun=opts.get(r'dry_run'),
4662 return repo.rollback(dryrun=opts.get(r'dry_run'),
4671 force=opts.get(r'force'))
4663 force=opts.get(r'force'))
4672
4664
4673 @command('root', [], cmdtype=readonly)
4665 @command('root', [], cmdtype=readonly)
4674 def root(ui, repo):
4666 def root(ui, repo):
4675 """print the root (top) of the current working directory
4667 """print the root (top) of the current working directory
4676
4668
4677 Print the root directory of the current repository.
4669 Print the root directory of the current repository.
4678
4670
4679 Returns 0 on success.
4671 Returns 0 on success.
4680 """
4672 """
4681 ui.write(repo.root + "\n")
4673 ui.write(repo.root + "\n")
4682
4674
4683 @command('^serve',
4675 @command('^serve',
4684 [('A', 'accesslog', '', _('name of access log file to write to'),
4676 [('A', 'accesslog', '', _('name of access log file to write to'),
4685 _('FILE')),
4677 _('FILE')),
4686 ('d', 'daemon', None, _('run server in background')),
4678 ('d', 'daemon', None, _('run server in background')),
4687 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4679 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4688 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4680 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4689 # use string type, then we can check if something was passed
4681 # use string type, then we can check if something was passed
4690 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4682 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4691 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4683 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4692 _('ADDR')),
4684 _('ADDR')),
4693 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4685 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4694 _('PREFIX')),
4686 _('PREFIX')),
4695 ('n', 'name', '',
4687 ('n', 'name', '',
4696 _('name to show in web pages (default: working directory)'), _('NAME')),
4688 _('name to show in web pages (default: working directory)'), _('NAME')),
4697 ('', 'web-conf', '',
4689 ('', 'web-conf', '',
4698 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4690 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4699 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4691 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4700 _('FILE')),
4692 _('FILE')),
4701 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4693 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4702 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4694 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4703 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4695 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4704 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4696 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4705 ('', 'style', '', _('template style to use'), _('STYLE')),
4697 ('', 'style', '', _('template style to use'), _('STYLE')),
4706 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4698 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4707 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4699 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4708 + subrepoopts,
4700 + subrepoopts,
4709 _('[OPTION]...'),
4701 _('[OPTION]...'),
4710 optionalrepo=True)
4702 optionalrepo=True)
4711 def serve(ui, repo, **opts):
4703 def serve(ui, repo, **opts):
4712 """start stand-alone webserver
4704 """start stand-alone webserver
4713
4705
4714 Start a local HTTP repository browser and pull server. You can use
4706 Start a local HTTP repository browser and pull server. You can use
4715 this for ad-hoc sharing and browsing of repositories. It is
4707 this for ad-hoc sharing and browsing of repositories. It is
4716 recommended to use a real web server to serve a repository for
4708 recommended to use a real web server to serve a repository for
4717 longer periods of time.
4709 longer periods of time.
4718
4710
4719 Please note that the server does not implement access control.
4711 Please note that the server does not implement access control.
4720 This means that, by default, anybody can read from the server and
4712 This means that, by default, anybody can read from the server and
4721 nobody can write to it by default. Set the ``web.allow-push``
4713 nobody can write to it by default. Set the ``web.allow-push``
4722 option to ``*`` to allow everybody to push to the server. You
4714 option to ``*`` to allow everybody to push to the server. You
4723 should use a real web server if you need to authenticate users.
4715 should use a real web server if you need to authenticate users.
4724
4716
4725 By default, the server logs accesses to stdout and errors to
4717 By default, the server logs accesses to stdout and errors to
4726 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4718 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4727 files.
4719 files.
4728
4720
4729 To have the server choose a free port number to listen on, specify
4721 To have the server choose a free port number to listen on, specify
4730 a port number of 0; in this case, the server will print the port
4722 a port number of 0; in this case, the server will print the port
4731 number it uses.
4723 number it uses.
4732
4724
4733 Returns 0 on success.
4725 Returns 0 on success.
4734 """
4726 """
4735
4727
4736 opts = pycompat.byteskwargs(opts)
4728 opts = pycompat.byteskwargs(opts)
4737 if opts["stdio"] and opts["cmdserver"]:
4729 if opts["stdio"] and opts["cmdserver"]:
4738 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4730 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4739
4731
4740 if opts["stdio"]:
4732 if opts["stdio"]:
4741 if repo is None:
4733 if repo is None:
4742 raise error.RepoError(_("there is no Mercurial repository here"
4734 raise error.RepoError(_("there is no Mercurial repository here"
4743 " (.hg not found)"))
4735 " (.hg not found)"))
4744 s = sshserver.sshserver(ui, repo)
4736 s = sshserver.sshserver(ui, repo)
4745 s.serve_forever()
4737 s.serve_forever()
4746
4738
4747 service = server.createservice(ui, repo, opts)
4739 service = server.createservice(ui, repo, opts)
4748 return server.runservice(opts, initfn=service.init, runfn=service.run)
4740 return server.runservice(opts, initfn=service.init, runfn=service.run)
4749
4741
4750 @command('^status|st',
4742 @command('^status|st',
4751 [('A', 'all', None, _('show status of all files')),
4743 [('A', 'all', None, _('show status of all files')),
4752 ('m', 'modified', None, _('show only modified files')),
4744 ('m', 'modified', None, _('show only modified files')),
4753 ('a', 'added', None, _('show only added files')),
4745 ('a', 'added', None, _('show only added files')),
4754 ('r', 'removed', None, _('show only removed files')),
4746 ('r', 'removed', None, _('show only removed files')),
4755 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4747 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4756 ('c', 'clean', None, _('show only files without changes')),
4748 ('c', 'clean', None, _('show only files without changes')),
4757 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4749 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4758 ('i', 'ignored', None, _('show only ignored files')),
4750 ('i', 'ignored', None, _('show only ignored files')),
4759 ('n', 'no-status', None, _('hide status prefix')),
4751 ('n', 'no-status', None, _('hide status prefix')),
4760 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4752 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4761 ('C', 'copies', None, _('show source of copied files')),
4753 ('C', 'copies', None, _('show source of copied files')),
4762 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4754 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4763 ('', 'rev', [], _('show difference from revision'), _('REV')),
4755 ('', 'rev', [], _('show difference from revision'), _('REV')),
4764 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4756 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4765 ] + walkopts + subrepoopts + formatteropts,
4757 ] + walkopts + subrepoopts + formatteropts,
4766 _('[OPTION]... [FILE]...'),
4758 _('[OPTION]... [FILE]...'),
4767 inferrepo=True, cmdtype=readonly)
4759 inferrepo=True, cmdtype=readonly)
4768 def status(ui, repo, *pats, **opts):
4760 def status(ui, repo, *pats, **opts):
4769 """show changed files in the working directory
4761 """show changed files in the working directory
4770
4762
4771 Show status of files in the repository. If names are given, only
4763 Show status of files in the repository. If names are given, only
4772 files that match are shown. Files that are clean or ignored or
4764 files that match are shown. Files that are clean or ignored or
4773 the source of a copy/move operation, are not listed unless
4765 the source of a copy/move operation, are not listed unless
4774 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4766 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4775 Unless options described with "show only ..." are given, the
4767 Unless options described with "show only ..." are given, the
4776 options -mardu are used.
4768 options -mardu are used.
4777
4769
4778 Option -q/--quiet hides untracked (unknown and ignored) files
4770 Option -q/--quiet hides untracked (unknown and ignored) files
4779 unless explicitly requested with -u/--unknown or -i/--ignored.
4771 unless explicitly requested with -u/--unknown or -i/--ignored.
4780
4772
4781 .. note::
4773 .. note::
4782
4774
4783 :hg:`status` may appear to disagree with diff if permissions have
4775 :hg:`status` may appear to disagree with diff if permissions have
4784 changed or a merge has occurred. The standard diff format does
4776 changed or a merge has occurred. The standard diff format does
4785 not report permission changes and diff only reports changes
4777 not report permission changes and diff only reports changes
4786 relative to one merge parent.
4778 relative to one merge parent.
4787
4779
4788 If one revision is given, it is used as the base revision.
4780 If one revision is given, it is used as the base revision.
4789 If two revisions are given, the differences between them are
4781 If two revisions are given, the differences between them are
4790 shown. The --change option can also be used as a shortcut to list
4782 shown. The --change option can also be used as a shortcut to list
4791 the changed files of a revision from its first parent.
4783 the changed files of a revision from its first parent.
4792
4784
4793 The codes used to show the status of files are::
4785 The codes used to show the status of files are::
4794
4786
4795 M = modified
4787 M = modified
4796 A = added
4788 A = added
4797 R = removed
4789 R = removed
4798 C = clean
4790 C = clean
4799 ! = missing (deleted by non-hg command, but still tracked)
4791 ! = missing (deleted by non-hg command, but still tracked)
4800 ? = not tracked
4792 ? = not tracked
4801 I = ignored
4793 I = ignored
4802 = origin of the previous file (with --copies)
4794 = origin of the previous file (with --copies)
4803
4795
4804 .. container:: verbose
4796 .. container:: verbose
4805
4797
4806 The -t/--terse option abbreviates the output by showing only the directory
4798 The -t/--terse option abbreviates the output by showing only the directory
4807 name if all the files in it share the same status. The option takes an
4799 name if all the files in it share the same status. The option takes an
4808 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4800 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4809 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4801 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4810 for 'ignored' and 'c' for clean.
4802 for 'ignored' and 'c' for clean.
4811
4803
4812 It abbreviates only those statuses which are passed. Note that clean and
4804 It abbreviates only those statuses which are passed. Note that clean and
4813 ignored files are not displayed with '--terse ic' unless the -c/--clean
4805 ignored files are not displayed with '--terse ic' unless the -c/--clean
4814 and -i/--ignored options are also used.
4806 and -i/--ignored options are also used.
4815
4807
4816 The -v/--verbose option shows information when the repository is in an
4808 The -v/--verbose option shows information when the repository is in an
4817 unfinished merge, shelve, rebase state etc. You can have this behavior
4809 unfinished merge, shelve, rebase state etc. You can have this behavior
4818 turned on by default by enabling the ``commands.status.verbose`` option.
4810 turned on by default by enabling the ``commands.status.verbose`` option.
4819
4811
4820 You can skip displaying some of these states by setting
4812 You can skip displaying some of these states by setting
4821 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4813 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4822 'histedit', 'merge', 'rebase', or 'unshelve'.
4814 'histedit', 'merge', 'rebase', or 'unshelve'.
4823
4815
4824 Examples:
4816 Examples:
4825
4817
4826 - show changes in the working directory relative to a
4818 - show changes in the working directory relative to a
4827 changeset::
4819 changeset::
4828
4820
4829 hg status --rev 9353
4821 hg status --rev 9353
4830
4822
4831 - show changes in the working directory relative to the
4823 - show changes in the working directory relative to the
4832 current directory (see :hg:`help patterns` for more information)::
4824 current directory (see :hg:`help patterns` for more information)::
4833
4825
4834 hg status re:
4826 hg status re:
4835
4827
4836 - show all changes including copies in an existing changeset::
4828 - show all changes including copies in an existing changeset::
4837
4829
4838 hg status --copies --change 9353
4830 hg status --copies --change 9353
4839
4831
4840 - get a NUL separated list of added files, suitable for xargs::
4832 - get a NUL separated list of added files, suitable for xargs::
4841
4833
4842 hg status -an0
4834 hg status -an0
4843
4835
4844 - show more information about the repository status, abbreviating
4836 - show more information about the repository status, abbreviating
4845 added, removed, modified, deleted, and untracked paths::
4837 added, removed, modified, deleted, and untracked paths::
4846
4838
4847 hg status -v -t mardu
4839 hg status -v -t mardu
4848
4840
4849 Returns 0 on success.
4841 Returns 0 on success.
4850
4842
4851 """
4843 """
4852
4844
4853 opts = pycompat.byteskwargs(opts)
4845 opts = pycompat.byteskwargs(opts)
4854 revs = opts.get('rev')
4846 revs = opts.get('rev')
4855 change = opts.get('change')
4847 change = opts.get('change')
4856 terse = opts.get('terse')
4848 terse = opts.get('terse')
4857
4849
4858 if revs and change:
4850 if revs and change:
4859 msg = _('cannot specify --rev and --change at the same time')
4851 msg = _('cannot specify --rev and --change at the same time')
4860 raise error.Abort(msg)
4852 raise error.Abort(msg)
4861 elif revs and terse:
4853 elif revs and terse:
4862 msg = _('cannot use --terse with --rev')
4854 msg = _('cannot use --terse with --rev')
4863 raise error.Abort(msg)
4855 raise error.Abort(msg)
4864 elif change:
4856 elif change:
4865 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4857 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4866 node2 = scmutil.revsingle(repo, change, None).node()
4858 node2 = scmutil.revsingle(repo, change, None).node()
4867 node1 = repo[node2].p1().node()
4859 node1 = repo[node2].p1().node()
4868 else:
4860 else:
4869 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4861 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4870 node1, node2 = scmutil.revpair(repo, revs)
4862 node1, node2 = scmutil.revpair(repo, revs)
4871
4863
4872 if pats or ui.configbool('commands', 'status.relative'):
4864 if pats or ui.configbool('commands', 'status.relative'):
4873 cwd = repo.getcwd()
4865 cwd = repo.getcwd()
4874 else:
4866 else:
4875 cwd = ''
4867 cwd = ''
4876
4868
4877 if opts.get('print0'):
4869 if opts.get('print0'):
4878 end = '\0'
4870 end = '\0'
4879 else:
4871 else:
4880 end = '\n'
4872 end = '\n'
4881 copy = {}
4873 copy = {}
4882 states = 'modified added removed deleted unknown ignored clean'.split()
4874 states = 'modified added removed deleted unknown ignored clean'.split()
4883 show = [k for k in states if opts.get(k)]
4875 show = [k for k in states if opts.get(k)]
4884 if opts.get('all'):
4876 if opts.get('all'):
4885 show += ui.quiet and (states[:4] + ['clean']) or states
4877 show += ui.quiet and (states[:4] + ['clean']) or states
4886
4878
4887 if not show:
4879 if not show:
4888 if ui.quiet:
4880 if ui.quiet:
4889 show = states[:4]
4881 show = states[:4]
4890 else:
4882 else:
4891 show = states[:5]
4883 show = states[:5]
4892
4884
4893 m = scmutil.match(repo[node2], pats, opts)
4885 m = scmutil.match(repo[node2], pats, opts)
4894 if terse:
4886 if terse:
4895 # we need to compute clean and unknown to terse
4887 # we need to compute clean and unknown to terse
4896 stat = repo.status(node1, node2, m,
4888 stat = repo.status(node1, node2, m,
4897 'ignored' in show or 'i' in terse,
4889 'ignored' in show or 'i' in terse,
4898 True, True, opts.get('subrepos'))
4890 True, True, opts.get('subrepos'))
4899
4891
4900 stat = cmdutil.tersedir(stat, terse)
4892 stat = cmdutil.tersedir(stat, terse)
4901 else:
4893 else:
4902 stat = repo.status(node1, node2, m,
4894 stat = repo.status(node1, node2, m,
4903 'ignored' in show, 'clean' in show,
4895 'ignored' in show, 'clean' in show,
4904 'unknown' in show, opts.get('subrepos'))
4896 'unknown' in show, opts.get('subrepos'))
4905
4897
4906 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4898 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4907
4899
4908 if (opts.get('all') or opts.get('copies')
4900 if (opts.get('all') or opts.get('copies')
4909 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4901 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4910 copy = copies.pathcopies(repo[node1], repo[node2], m)
4902 copy = copies.pathcopies(repo[node1], repo[node2], m)
4911
4903
4912 ui.pager('status')
4904 ui.pager('status')
4913 fm = ui.formatter('status', opts)
4905 fm = ui.formatter('status', opts)
4914 fmt = '%s' + end
4906 fmt = '%s' + end
4915 showchar = not opts.get('no_status')
4907 showchar = not opts.get('no_status')
4916
4908
4917 for state, char, files in changestates:
4909 for state, char, files in changestates:
4918 if state in show:
4910 if state in show:
4919 label = 'status.' + state
4911 label = 'status.' + state
4920 for f in files:
4912 for f in files:
4921 fm.startitem()
4913 fm.startitem()
4922 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4914 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4923 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4915 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4924 if f in copy:
4916 if f in copy:
4925 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4917 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4926 label='status.copied')
4918 label='status.copied')
4927
4919
4928 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4920 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4929 and not ui.plain()):
4921 and not ui.plain()):
4930 cmdutil.morestatus(repo, fm)
4922 cmdutil.morestatus(repo, fm)
4931 fm.end()
4923 fm.end()
4932
4924
4933 @command('^summary|sum',
4925 @command('^summary|sum',
4934 [('', 'remote', None, _('check for push and pull'))],
4926 [('', 'remote', None, _('check for push and pull'))],
4935 '[--remote]', cmdtype=readonly)
4927 '[--remote]', cmdtype=readonly)
4936 def summary(ui, repo, **opts):
4928 def summary(ui, repo, **opts):
4937 """summarize working directory state
4929 """summarize working directory state
4938
4930
4939 This generates a brief summary of the working directory state,
4931 This generates a brief summary of the working directory state,
4940 including parents, branch, commit status, phase and available updates.
4932 including parents, branch, commit status, phase and available updates.
4941
4933
4942 With the --remote option, this will check the default paths for
4934 With the --remote option, this will check the default paths for
4943 incoming and outgoing changes. This can be time-consuming.
4935 incoming and outgoing changes. This can be time-consuming.
4944
4936
4945 Returns 0 on success.
4937 Returns 0 on success.
4946 """
4938 """
4947
4939
4948 opts = pycompat.byteskwargs(opts)
4940 opts = pycompat.byteskwargs(opts)
4949 ui.pager('summary')
4941 ui.pager('summary')
4950 ctx = repo[None]
4942 ctx = repo[None]
4951 parents = ctx.parents()
4943 parents = ctx.parents()
4952 pnode = parents[0].node()
4944 pnode = parents[0].node()
4953 marks = []
4945 marks = []
4954
4946
4955 ms = None
4947 ms = None
4956 try:
4948 try:
4957 ms = mergemod.mergestate.read(repo)
4949 ms = mergemod.mergestate.read(repo)
4958 except error.UnsupportedMergeRecords as e:
4950 except error.UnsupportedMergeRecords as e:
4959 s = ' '.join(e.recordtypes)
4951 s = ' '.join(e.recordtypes)
4960 ui.warn(
4952 ui.warn(
4961 _('warning: merge state has unsupported record types: %s\n') % s)
4953 _('warning: merge state has unsupported record types: %s\n') % s)
4962 unresolved = []
4954 unresolved = []
4963 else:
4955 else:
4964 unresolved = list(ms.unresolved())
4956 unresolved = list(ms.unresolved())
4965
4957
4966 for p in parents:
4958 for p in parents:
4967 # label with log.changeset (instead of log.parent) since this
4959 # label with log.changeset (instead of log.parent) since this
4968 # shows a working directory parent *changeset*:
4960 # shows a working directory parent *changeset*:
4969 # i18n: column positioning for "hg summary"
4961 # i18n: column positioning for "hg summary"
4970 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4962 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4971 label=cmdutil._changesetlabels(p))
4963 label=cmdutil._changesetlabels(p))
4972 ui.write(' '.join(p.tags()), label='log.tag')
4964 ui.write(' '.join(p.tags()), label='log.tag')
4973 if p.bookmarks():
4965 if p.bookmarks():
4974 marks.extend(p.bookmarks())
4966 marks.extend(p.bookmarks())
4975 if p.rev() == -1:
4967 if p.rev() == -1:
4976 if not len(repo):
4968 if not len(repo):
4977 ui.write(_(' (empty repository)'))
4969 ui.write(_(' (empty repository)'))
4978 else:
4970 else:
4979 ui.write(_(' (no revision checked out)'))
4971 ui.write(_(' (no revision checked out)'))
4980 if p.obsolete():
4972 if p.obsolete():
4981 ui.write(_(' (obsolete)'))
4973 ui.write(_(' (obsolete)'))
4982 if p.isunstable():
4974 if p.isunstable():
4983 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4975 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4984 for instability in p.instabilities())
4976 for instability in p.instabilities())
4985 ui.write(' ('
4977 ui.write(' ('
4986 + ', '.join(instabilities)
4978 + ', '.join(instabilities)
4987 + ')')
4979 + ')')
4988 ui.write('\n')
4980 ui.write('\n')
4989 if p.description():
4981 if p.description():
4990 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4982 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4991 label='log.summary')
4983 label='log.summary')
4992
4984
4993 branch = ctx.branch()
4985 branch = ctx.branch()
4994 bheads = repo.branchheads(branch)
4986 bheads = repo.branchheads(branch)
4995 # i18n: column positioning for "hg summary"
4987 # i18n: column positioning for "hg summary"
4996 m = _('branch: %s\n') % branch
4988 m = _('branch: %s\n') % branch
4997 if branch != 'default':
4989 if branch != 'default':
4998 ui.write(m, label='log.branch')
4990 ui.write(m, label='log.branch')
4999 else:
4991 else:
5000 ui.status(m, label='log.branch')
4992 ui.status(m, label='log.branch')
5001
4993
5002 if marks:
4994 if marks:
5003 active = repo._activebookmark
4995 active = repo._activebookmark
5004 # i18n: column positioning for "hg summary"
4996 # i18n: column positioning for "hg summary"
5005 ui.write(_('bookmarks:'), label='log.bookmark')
4997 ui.write(_('bookmarks:'), label='log.bookmark')
5006 if active is not None:
4998 if active is not None:
5007 if active in marks:
4999 if active in marks:
5008 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5000 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5009 marks.remove(active)
5001 marks.remove(active)
5010 else:
5002 else:
5011 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5003 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5012 for m in marks:
5004 for m in marks:
5013 ui.write(' ' + m, label='log.bookmark')
5005 ui.write(' ' + m, label='log.bookmark')
5014 ui.write('\n', label='log.bookmark')
5006 ui.write('\n', label='log.bookmark')
5015
5007
5016 status = repo.status(unknown=True)
5008 status = repo.status(unknown=True)
5017
5009
5018 c = repo.dirstate.copies()
5010 c = repo.dirstate.copies()
5019 copied, renamed = [], []
5011 copied, renamed = [], []
5020 for d, s in c.iteritems():
5012 for d, s in c.iteritems():
5021 if s in status.removed:
5013 if s in status.removed:
5022 status.removed.remove(s)
5014 status.removed.remove(s)
5023 renamed.append(d)
5015 renamed.append(d)
5024 else:
5016 else:
5025 copied.append(d)
5017 copied.append(d)
5026 if d in status.added:
5018 if d in status.added:
5027 status.added.remove(d)
5019 status.added.remove(d)
5028
5020
5029 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5021 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5030
5022
5031 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5023 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5032 (ui.label(_('%d added'), 'status.added'), status.added),
5024 (ui.label(_('%d added'), 'status.added'), status.added),
5033 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5025 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5034 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5026 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5035 (ui.label(_('%d copied'), 'status.copied'), copied),
5027 (ui.label(_('%d copied'), 'status.copied'), copied),
5036 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5028 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5037 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5029 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5038 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5030 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5039 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5031 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5040 t = []
5032 t = []
5041 for l, s in labels:
5033 for l, s in labels:
5042 if s:
5034 if s:
5043 t.append(l % len(s))
5035 t.append(l % len(s))
5044
5036
5045 t = ', '.join(t)
5037 t = ', '.join(t)
5046 cleanworkdir = False
5038 cleanworkdir = False
5047
5039
5048 if repo.vfs.exists('graftstate'):
5040 if repo.vfs.exists('graftstate'):
5049 t += _(' (graft in progress)')
5041 t += _(' (graft in progress)')
5050 if repo.vfs.exists('updatestate'):
5042 if repo.vfs.exists('updatestate'):
5051 t += _(' (interrupted update)')
5043 t += _(' (interrupted update)')
5052 elif len(parents) > 1:
5044 elif len(parents) > 1:
5053 t += _(' (merge)')
5045 t += _(' (merge)')
5054 elif branch != parents[0].branch():
5046 elif branch != parents[0].branch():
5055 t += _(' (new branch)')
5047 t += _(' (new branch)')
5056 elif (parents[0].closesbranch() and
5048 elif (parents[0].closesbranch() and
5057 pnode in repo.branchheads(branch, closed=True)):
5049 pnode in repo.branchheads(branch, closed=True)):
5058 t += _(' (head closed)')
5050 t += _(' (head closed)')
5059 elif not (status.modified or status.added or status.removed or renamed or
5051 elif not (status.modified or status.added or status.removed or renamed or
5060 copied or subs):
5052 copied or subs):
5061 t += _(' (clean)')
5053 t += _(' (clean)')
5062 cleanworkdir = True
5054 cleanworkdir = True
5063 elif pnode not in bheads:
5055 elif pnode not in bheads:
5064 t += _(' (new branch head)')
5056 t += _(' (new branch head)')
5065
5057
5066 if parents:
5058 if parents:
5067 pendingphase = max(p.phase() for p in parents)
5059 pendingphase = max(p.phase() for p in parents)
5068 else:
5060 else:
5069 pendingphase = phases.public
5061 pendingphase = phases.public
5070
5062
5071 if pendingphase > phases.newcommitphase(ui):
5063 if pendingphase > phases.newcommitphase(ui):
5072 t += ' (%s)' % phases.phasenames[pendingphase]
5064 t += ' (%s)' % phases.phasenames[pendingphase]
5073
5065
5074 if cleanworkdir:
5066 if cleanworkdir:
5075 # i18n: column positioning for "hg summary"
5067 # i18n: column positioning for "hg summary"
5076 ui.status(_('commit: %s\n') % t.strip())
5068 ui.status(_('commit: %s\n') % t.strip())
5077 else:
5069 else:
5078 # i18n: column positioning for "hg summary"
5070 # i18n: column positioning for "hg summary"
5079 ui.write(_('commit: %s\n') % t.strip())
5071 ui.write(_('commit: %s\n') % t.strip())
5080
5072
5081 # all ancestors of branch heads - all ancestors of parent = new csets
5073 # all ancestors of branch heads - all ancestors of parent = new csets
5082 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5074 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5083 bheads))
5075 bheads))
5084
5076
5085 if new == 0:
5077 if new == 0:
5086 # i18n: column positioning for "hg summary"
5078 # i18n: column positioning for "hg summary"
5087 ui.status(_('update: (current)\n'))
5079 ui.status(_('update: (current)\n'))
5088 elif pnode not in bheads:
5080 elif pnode not in bheads:
5089 # i18n: column positioning for "hg summary"
5081 # i18n: column positioning for "hg summary"
5090 ui.write(_('update: %d new changesets (update)\n') % new)
5082 ui.write(_('update: %d new changesets (update)\n') % new)
5091 else:
5083 else:
5092 # i18n: column positioning for "hg summary"
5084 # i18n: column positioning for "hg summary"
5093 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5085 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5094 (new, len(bheads)))
5086 (new, len(bheads)))
5095
5087
5096 t = []
5088 t = []
5097 draft = len(repo.revs('draft()'))
5089 draft = len(repo.revs('draft()'))
5098 if draft:
5090 if draft:
5099 t.append(_('%d draft') % draft)
5091 t.append(_('%d draft') % draft)
5100 secret = len(repo.revs('secret()'))
5092 secret = len(repo.revs('secret()'))
5101 if secret:
5093 if secret:
5102 t.append(_('%d secret') % secret)
5094 t.append(_('%d secret') % secret)
5103
5095
5104 if draft or secret:
5096 if draft or secret:
5105 ui.status(_('phases: %s\n') % ', '.join(t))
5097 ui.status(_('phases: %s\n') % ', '.join(t))
5106
5098
5107 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5099 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5108 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5100 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5109 numtrouble = len(repo.revs(trouble + "()"))
5101 numtrouble = len(repo.revs(trouble + "()"))
5110 # We write all the possibilities to ease translation
5102 # We write all the possibilities to ease translation
5111 troublemsg = {
5103 troublemsg = {
5112 "orphan": _("orphan: %d changesets"),
5104 "orphan": _("orphan: %d changesets"),
5113 "contentdivergent": _("content-divergent: %d changesets"),
5105 "contentdivergent": _("content-divergent: %d changesets"),
5114 "phasedivergent": _("phase-divergent: %d changesets"),
5106 "phasedivergent": _("phase-divergent: %d changesets"),
5115 }
5107 }
5116 if numtrouble > 0:
5108 if numtrouble > 0:
5117 ui.status(troublemsg[trouble] % numtrouble + "\n")
5109 ui.status(troublemsg[trouble] % numtrouble + "\n")
5118
5110
5119 cmdutil.summaryhooks(ui, repo)
5111 cmdutil.summaryhooks(ui, repo)
5120
5112
5121 if opts.get('remote'):
5113 if opts.get('remote'):
5122 needsincoming, needsoutgoing = True, True
5114 needsincoming, needsoutgoing = True, True
5123 else:
5115 else:
5124 needsincoming, needsoutgoing = False, False
5116 needsincoming, needsoutgoing = False, False
5125 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5117 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5126 if i:
5118 if i:
5127 needsincoming = True
5119 needsincoming = True
5128 if o:
5120 if o:
5129 needsoutgoing = True
5121 needsoutgoing = True
5130 if not needsincoming and not needsoutgoing:
5122 if not needsincoming and not needsoutgoing:
5131 return
5123 return
5132
5124
5133 def getincoming():
5125 def getincoming():
5134 source, branches = hg.parseurl(ui.expandpath('default'))
5126 source, branches = hg.parseurl(ui.expandpath('default'))
5135 sbranch = branches[0]
5127 sbranch = branches[0]
5136 try:
5128 try:
5137 other = hg.peer(repo, {}, source)
5129 other = hg.peer(repo, {}, source)
5138 except error.RepoError:
5130 except error.RepoError:
5139 if opts.get('remote'):
5131 if opts.get('remote'):
5140 raise
5132 raise
5141 return source, sbranch, None, None, None
5133 return source, sbranch, None, None, None
5142 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5134 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5143 if revs:
5135 if revs:
5144 revs = [other.lookup(rev) for rev in revs]
5136 revs = [other.lookup(rev) for rev in revs]
5145 ui.debug('comparing with %s\n' % util.hidepassword(source))
5137 ui.debug('comparing with %s\n' % util.hidepassword(source))
5146 repo.ui.pushbuffer()
5138 repo.ui.pushbuffer()
5147 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5139 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5148 repo.ui.popbuffer()
5140 repo.ui.popbuffer()
5149 return source, sbranch, other, commoninc, commoninc[1]
5141 return source, sbranch, other, commoninc, commoninc[1]
5150
5142
5151 if needsincoming:
5143 if needsincoming:
5152 source, sbranch, sother, commoninc, incoming = getincoming()
5144 source, sbranch, sother, commoninc, incoming = getincoming()
5153 else:
5145 else:
5154 source = sbranch = sother = commoninc = incoming = None
5146 source = sbranch = sother = commoninc = incoming = None
5155
5147
5156 def getoutgoing():
5148 def getoutgoing():
5157 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5149 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5158 dbranch = branches[0]
5150 dbranch = branches[0]
5159 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5151 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5160 if source != dest:
5152 if source != dest:
5161 try:
5153 try:
5162 dother = hg.peer(repo, {}, dest)
5154 dother = hg.peer(repo, {}, dest)
5163 except error.RepoError:
5155 except error.RepoError:
5164 if opts.get('remote'):
5156 if opts.get('remote'):
5165 raise
5157 raise
5166 return dest, dbranch, None, None
5158 return dest, dbranch, None, None
5167 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5159 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5168 elif sother is None:
5160 elif sother is None:
5169 # there is no explicit destination peer, but source one is invalid
5161 # there is no explicit destination peer, but source one is invalid
5170 return dest, dbranch, None, None
5162 return dest, dbranch, None, None
5171 else:
5163 else:
5172 dother = sother
5164 dother = sother
5173 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5165 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5174 common = None
5166 common = None
5175 else:
5167 else:
5176 common = commoninc
5168 common = commoninc
5177 if revs:
5169 if revs:
5178 revs = [repo.lookup(rev) for rev in revs]
5170 revs = [repo.lookup(rev) for rev in revs]
5179 repo.ui.pushbuffer()
5171 repo.ui.pushbuffer()
5180 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5172 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5181 commoninc=common)
5173 commoninc=common)
5182 repo.ui.popbuffer()
5174 repo.ui.popbuffer()
5183 return dest, dbranch, dother, outgoing
5175 return dest, dbranch, dother, outgoing
5184
5176
5185 if needsoutgoing:
5177 if needsoutgoing:
5186 dest, dbranch, dother, outgoing = getoutgoing()
5178 dest, dbranch, dother, outgoing = getoutgoing()
5187 else:
5179 else:
5188 dest = dbranch = dother = outgoing = None
5180 dest = dbranch = dother = outgoing = None
5189
5181
5190 if opts.get('remote'):
5182 if opts.get('remote'):
5191 t = []
5183 t = []
5192 if incoming:
5184 if incoming:
5193 t.append(_('1 or more incoming'))
5185 t.append(_('1 or more incoming'))
5194 o = outgoing.missing
5186 o = outgoing.missing
5195 if o:
5187 if o:
5196 t.append(_('%d outgoing') % len(o))
5188 t.append(_('%d outgoing') % len(o))
5197 other = dother or sother
5189 other = dother or sother
5198 if 'bookmarks' in other.listkeys('namespaces'):
5190 if 'bookmarks' in other.listkeys('namespaces'):
5199 counts = bookmarks.summary(repo, other)
5191 counts = bookmarks.summary(repo, other)
5200 if counts[0] > 0:
5192 if counts[0] > 0:
5201 t.append(_('%d incoming bookmarks') % counts[0])
5193 t.append(_('%d incoming bookmarks') % counts[0])
5202 if counts[1] > 0:
5194 if counts[1] > 0:
5203 t.append(_('%d outgoing bookmarks') % counts[1])
5195 t.append(_('%d outgoing bookmarks') % counts[1])
5204
5196
5205 if t:
5197 if t:
5206 # i18n: column positioning for "hg summary"
5198 # i18n: column positioning for "hg summary"
5207 ui.write(_('remote: %s\n') % (', '.join(t)))
5199 ui.write(_('remote: %s\n') % (', '.join(t)))
5208 else:
5200 else:
5209 # i18n: column positioning for "hg summary"
5201 # i18n: column positioning for "hg summary"
5210 ui.status(_('remote: (synced)\n'))
5202 ui.status(_('remote: (synced)\n'))
5211
5203
5212 cmdutil.summaryremotehooks(ui, repo, opts,
5204 cmdutil.summaryremotehooks(ui, repo, opts,
5213 ((source, sbranch, sother, commoninc),
5205 ((source, sbranch, sother, commoninc),
5214 (dest, dbranch, dother, outgoing)))
5206 (dest, dbranch, dother, outgoing)))
5215
5207
5216 @command('tag',
5208 @command('tag',
5217 [('f', 'force', None, _('force tag')),
5209 [('f', 'force', None, _('force tag')),
5218 ('l', 'local', None, _('make the tag local')),
5210 ('l', 'local', None, _('make the tag local')),
5219 ('r', 'rev', '', _('revision to tag'), _('REV')),
5211 ('r', 'rev', '', _('revision to tag'), _('REV')),
5220 ('', 'remove', None, _('remove a tag')),
5212 ('', 'remove', None, _('remove a tag')),
5221 # -l/--local is already there, commitopts cannot be used
5213 # -l/--local is already there, commitopts cannot be used
5222 ('e', 'edit', None, _('invoke editor on commit messages')),
5214 ('e', 'edit', None, _('invoke editor on commit messages')),
5223 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5215 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5224 ] + commitopts2,
5216 ] + commitopts2,
5225 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5217 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5226 def tag(ui, repo, name1, *names, **opts):
5218 def tag(ui, repo, name1, *names, **opts):
5227 """add one or more tags for the current or given revision
5219 """add one or more tags for the current or given revision
5228
5220
5229 Name a particular revision using <name>.
5221 Name a particular revision using <name>.
5230
5222
5231 Tags are used to name particular revisions of the repository and are
5223 Tags are used to name particular revisions of the repository and are
5232 very useful to compare different revisions, to go back to significant
5224 very useful to compare different revisions, to go back to significant
5233 earlier versions or to mark branch points as releases, etc. Changing
5225 earlier versions or to mark branch points as releases, etc. Changing
5234 an existing tag is normally disallowed; use -f/--force to override.
5226 an existing tag is normally disallowed; use -f/--force to override.
5235
5227
5236 If no revision is given, the parent of the working directory is
5228 If no revision is given, the parent of the working directory is
5237 used.
5229 used.
5238
5230
5239 To facilitate version control, distribution, and merging of tags,
5231 To facilitate version control, distribution, and merging of tags,
5240 they are stored as a file named ".hgtags" which is managed similarly
5232 they are stored as a file named ".hgtags" which is managed similarly
5241 to other project files and can be hand-edited if necessary. This
5233 to other project files and can be hand-edited if necessary. This
5242 also means that tagging creates a new commit. The file
5234 also means that tagging creates a new commit. The file
5243 ".hg/localtags" is used for local tags (not shared among
5235 ".hg/localtags" is used for local tags (not shared among
5244 repositories).
5236 repositories).
5245
5237
5246 Tag commits are usually made at the head of a branch. If the parent
5238 Tag commits are usually made at the head of a branch. If the parent
5247 of the working directory is not a branch head, :hg:`tag` aborts; use
5239 of the working directory is not a branch head, :hg:`tag` aborts; use
5248 -f/--force to force the tag commit to be based on a non-head
5240 -f/--force to force the tag commit to be based on a non-head
5249 changeset.
5241 changeset.
5250
5242
5251 See :hg:`help dates` for a list of formats valid for -d/--date.
5243 See :hg:`help dates` for a list of formats valid for -d/--date.
5252
5244
5253 Since tag names have priority over branch names during revision
5245 Since tag names have priority over branch names during revision
5254 lookup, using an existing branch name as a tag name is discouraged.
5246 lookup, using an existing branch name as a tag name is discouraged.
5255
5247
5256 Returns 0 on success.
5248 Returns 0 on success.
5257 """
5249 """
5258 opts = pycompat.byteskwargs(opts)
5250 opts = pycompat.byteskwargs(opts)
5259 wlock = lock = None
5251 wlock = lock = None
5260 try:
5252 try:
5261 wlock = repo.wlock()
5253 wlock = repo.wlock()
5262 lock = repo.lock()
5254 lock = repo.lock()
5263 rev_ = "."
5255 rev_ = "."
5264 names = [t.strip() for t in (name1,) + names]
5256 names = [t.strip() for t in (name1,) + names]
5265 if len(names) != len(set(names)):
5257 if len(names) != len(set(names)):
5266 raise error.Abort(_('tag names must be unique'))
5258 raise error.Abort(_('tag names must be unique'))
5267 for n in names:
5259 for n in names:
5268 scmutil.checknewlabel(repo, n, 'tag')
5260 scmutil.checknewlabel(repo, n, 'tag')
5269 if not n:
5261 if not n:
5270 raise error.Abort(_('tag names cannot consist entirely of '
5262 raise error.Abort(_('tag names cannot consist entirely of '
5271 'whitespace'))
5263 'whitespace'))
5272 if opts.get('rev') and opts.get('remove'):
5264 if opts.get('rev') and opts.get('remove'):
5273 raise error.Abort(_("--rev and --remove are incompatible"))
5265 raise error.Abort(_("--rev and --remove are incompatible"))
5274 if opts.get('rev'):
5266 if opts.get('rev'):
5275 rev_ = opts['rev']
5267 rev_ = opts['rev']
5276 message = opts.get('message')
5268 message = opts.get('message')
5277 if opts.get('remove'):
5269 if opts.get('remove'):
5278 if opts.get('local'):
5270 if opts.get('local'):
5279 expectedtype = 'local'
5271 expectedtype = 'local'
5280 else:
5272 else:
5281 expectedtype = 'global'
5273 expectedtype = 'global'
5282
5274
5283 for n in names:
5275 for n in names:
5284 if not repo.tagtype(n):
5276 if not repo.tagtype(n):
5285 raise error.Abort(_("tag '%s' does not exist") % n)
5277 raise error.Abort(_("tag '%s' does not exist") % n)
5286 if repo.tagtype(n) != expectedtype:
5278 if repo.tagtype(n) != expectedtype:
5287 if expectedtype == 'global':
5279 if expectedtype == 'global':
5288 raise error.Abort(_("tag '%s' is not a global tag") % n)
5280 raise error.Abort(_("tag '%s' is not a global tag") % n)
5289 else:
5281 else:
5290 raise error.Abort(_("tag '%s' is not a local tag") % n)
5282 raise error.Abort(_("tag '%s' is not a local tag") % n)
5291 rev_ = 'null'
5283 rev_ = 'null'
5292 if not message:
5284 if not message:
5293 # we don't translate commit messages
5285 # we don't translate commit messages
5294 message = 'Removed tag %s' % ', '.join(names)
5286 message = 'Removed tag %s' % ', '.join(names)
5295 elif not opts.get('force'):
5287 elif not opts.get('force'):
5296 for n in names:
5288 for n in names:
5297 if n in repo.tags():
5289 if n in repo.tags():
5298 raise error.Abort(_("tag '%s' already exists "
5290 raise error.Abort(_("tag '%s' already exists "
5299 "(use -f to force)") % n)
5291 "(use -f to force)") % n)
5300 if not opts.get('local'):
5292 if not opts.get('local'):
5301 p1, p2 = repo.dirstate.parents()
5293 p1, p2 = repo.dirstate.parents()
5302 if p2 != nullid:
5294 if p2 != nullid:
5303 raise error.Abort(_('uncommitted merge'))
5295 raise error.Abort(_('uncommitted merge'))
5304 bheads = repo.branchheads()
5296 bheads = repo.branchheads()
5305 if not opts.get('force') and bheads and p1 not in bheads:
5297 if not opts.get('force') and bheads and p1 not in bheads:
5306 raise error.Abort(_('working directory is not at a branch head '
5298 raise error.Abort(_('working directory is not at a branch head '
5307 '(use -f to force)'))
5299 '(use -f to force)'))
5308 r = scmutil.revsingle(repo, rev_).node()
5300 r = scmutil.revsingle(repo, rev_).node()
5309
5301
5310 if not message:
5302 if not message:
5311 # we don't translate commit messages
5303 # we don't translate commit messages
5312 message = ('Added tag %s for changeset %s' %
5304 message = ('Added tag %s for changeset %s' %
5313 (', '.join(names), short(r)))
5305 (', '.join(names), short(r)))
5314
5306
5315 date = opts.get('date')
5307 date = opts.get('date')
5316 if date:
5308 if date:
5317 date = util.parsedate(date)
5309 date = util.parsedate(date)
5318
5310
5319 if opts.get('remove'):
5311 if opts.get('remove'):
5320 editform = 'tag.remove'
5312 editform = 'tag.remove'
5321 else:
5313 else:
5322 editform = 'tag.add'
5314 editform = 'tag.add'
5323 editor = cmdutil.getcommiteditor(editform=editform,
5315 editor = cmdutil.getcommiteditor(editform=editform,
5324 **pycompat.strkwargs(opts))
5316 **pycompat.strkwargs(opts))
5325
5317
5326 # don't allow tagging the null rev
5318 # don't allow tagging the null rev
5327 if (not opts.get('remove') and
5319 if (not opts.get('remove') and
5328 scmutil.revsingle(repo, rev_).rev() == nullrev):
5320 scmutil.revsingle(repo, rev_).rev() == nullrev):
5329 raise error.Abort(_("cannot tag null revision"))
5321 raise error.Abort(_("cannot tag null revision"))
5330
5322
5331 tagsmod.tag(repo, names, r, message, opts.get('local'),
5323 tagsmod.tag(repo, names, r, message, opts.get('local'),
5332 opts.get('user'), date, editor=editor)
5324 opts.get('user'), date, editor=editor)
5333 finally:
5325 finally:
5334 release(lock, wlock)
5326 release(lock, wlock)
5335
5327
5336 @command('tags', formatteropts, '', cmdtype=readonly)
5328 @command('tags', formatteropts, '', cmdtype=readonly)
5337 def tags(ui, repo, **opts):
5329 def tags(ui, repo, **opts):
5338 """list repository tags
5330 """list repository tags
5339
5331
5340 This lists both regular and local tags. When the -v/--verbose
5332 This lists both regular and local tags. When the -v/--verbose
5341 switch is used, a third column "local" is printed for local tags.
5333 switch is used, a third column "local" is printed for local tags.
5342 When the -q/--quiet switch is used, only the tag name is printed.
5334 When the -q/--quiet switch is used, only the tag name is printed.
5343
5335
5344 Returns 0 on success.
5336 Returns 0 on success.
5345 """
5337 """
5346
5338
5347 opts = pycompat.byteskwargs(opts)
5339 opts = pycompat.byteskwargs(opts)
5348 ui.pager('tags')
5340 ui.pager('tags')
5349 fm = ui.formatter('tags', opts)
5341 fm = ui.formatter('tags', opts)
5350 hexfunc = fm.hexfunc
5342 hexfunc = fm.hexfunc
5351 tagtype = ""
5343 tagtype = ""
5352
5344
5353 for t, n in reversed(repo.tagslist()):
5345 for t, n in reversed(repo.tagslist()):
5354 hn = hexfunc(n)
5346 hn = hexfunc(n)
5355 label = 'tags.normal'
5347 label = 'tags.normal'
5356 tagtype = ''
5348 tagtype = ''
5357 if repo.tagtype(t) == 'local':
5349 if repo.tagtype(t) == 'local':
5358 label = 'tags.local'
5350 label = 'tags.local'
5359 tagtype = 'local'
5351 tagtype = 'local'
5360
5352
5361 fm.startitem()
5353 fm.startitem()
5362 fm.write('tag', '%s', t, label=label)
5354 fm.write('tag', '%s', t, label=label)
5363 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5355 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5364 fm.condwrite(not ui.quiet, 'rev node', fmt,
5356 fm.condwrite(not ui.quiet, 'rev node', fmt,
5365 repo.changelog.rev(n), hn, label=label)
5357 repo.changelog.rev(n), hn, label=label)
5366 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5358 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5367 tagtype, label=label)
5359 tagtype, label=label)
5368 fm.plain('\n')
5360 fm.plain('\n')
5369 fm.end()
5361 fm.end()
5370
5362
5371 @command('tip',
5363 @command('tip',
5372 [('p', 'patch', None, _('show patch')),
5364 [('p', 'patch', None, _('show patch')),
5373 ('g', 'git', None, _('use git extended diff format')),
5365 ('g', 'git', None, _('use git extended diff format')),
5374 ] + templateopts,
5366 ] + templateopts,
5375 _('[-p] [-g]'))
5367 _('[-p] [-g]'))
5376 def tip(ui, repo, **opts):
5368 def tip(ui, repo, **opts):
5377 """show the tip revision (DEPRECATED)
5369 """show the tip revision (DEPRECATED)
5378
5370
5379 The tip revision (usually just called the tip) is the changeset
5371 The tip revision (usually just called the tip) is the changeset
5380 most recently added to the repository (and therefore the most
5372 most recently added to the repository (and therefore the most
5381 recently changed head).
5373 recently changed head).
5382
5374
5383 If you have just made a commit, that commit will be the tip. If
5375 If you have just made a commit, that commit will be the tip. If
5384 you have just pulled changes from another repository, the tip of
5376 you have just pulled changes from another repository, the tip of
5385 that repository becomes the current tip. The "tip" tag is special
5377 that repository becomes the current tip. The "tip" tag is special
5386 and cannot be renamed or assigned to a different changeset.
5378 and cannot be renamed or assigned to a different changeset.
5387
5379
5388 This command is deprecated, please use :hg:`heads` instead.
5380 This command is deprecated, please use :hg:`heads` instead.
5389
5381
5390 Returns 0 on success.
5382 Returns 0 on success.
5391 """
5383 """
5392 opts = pycompat.byteskwargs(opts)
5384 opts = pycompat.byteskwargs(opts)
5393 displayer = cmdutil.show_changeset(ui, repo, opts)
5385 displayer = cmdutil.show_changeset(ui, repo, opts)
5394 displayer.show(repo['tip'])
5386 displayer.show(repo['tip'])
5395 displayer.close()
5387 displayer.close()
5396
5388
5397 @command('unbundle',
5389 @command('unbundle',
5398 [('u', 'update', None,
5390 [('u', 'update', None,
5399 _('update to new branch head if changesets were unbundled'))],
5391 _('update to new branch head if changesets were unbundled'))],
5400 _('[-u] FILE...'))
5392 _('[-u] FILE...'))
5401 def unbundle(ui, repo, fname1, *fnames, **opts):
5393 def unbundle(ui, repo, fname1, *fnames, **opts):
5402 """apply one or more bundle files
5394 """apply one or more bundle files
5403
5395
5404 Apply one or more bundle files generated by :hg:`bundle`.
5396 Apply one or more bundle files generated by :hg:`bundle`.
5405
5397
5406 Returns 0 on success, 1 if an update has unresolved files.
5398 Returns 0 on success, 1 if an update has unresolved files.
5407 """
5399 """
5408 fnames = (fname1,) + fnames
5400 fnames = (fname1,) + fnames
5409
5401
5410 with repo.lock():
5402 with repo.lock():
5411 for fname in fnames:
5403 for fname in fnames:
5412 f = hg.openpath(ui, fname)
5404 f = hg.openpath(ui, fname)
5413 gen = exchange.readbundle(ui, f, fname)
5405 gen = exchange.readbundle(ui, f, fname)
5414 if isinstance(gen, streamclone.streamcloneapplier):
5406 if isinstance(gen, streamclone.streamcloneapplier):
5415 raise error.Abort(
5407 raise error.Abort(
5416 _('packed bundles cannot be applied with '
5408 _('packed bundles cannot be applied with '
5417 '"hg unbundle"'),
5409 '"hg unbundle"'),
5418 hint=_('use "hg debugapplystreamclonebundle"'))
5410 hint=_('use "hg debugapplystreamclonebundle"'))
5419 url = 'bundle:' + fname
5411 url = 'bundle:' + fname
5420 try:
5412 try:
5421 txnname = 'unbundle'
5413 txnname = 'unbundle'
5422 if not isinstance(gen, bundle2.unbundle20):
5414 if not isinstance(gen, bundle2.unbundle20):
5423 txnname = 'unbundle\n%s' % util.hidepassword(url)
5415 txnname = 'unbundle\n%s' % util.hidepassword(url)
5424 with repo.transaction(txnname) as tr:
5416 with repo.transaction(txnname) as tr:
5425 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5417 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5426 url=url)
5418 url=url)
5427 except error.BundleUnknownFeatureError as exc:
5419 except error.BundleUnknownFeatureError as exc:
5428 raise error.Abort(
5420 raise error.Abort(
5429 _('%s: unknown bundle feature, %s') % (fname, exc),
5421 _('%s: unknown bundle feature, %s') % (fname, exc),
5430 hint=_("see https://mercurial-scm.org/"
5422 hint=_("see https://mercurial-scm.org/"
5431 "wiki/BundleFeature for more "
5423 "wiki/BundleFeature for more "
5432 "information"))
5424 "information"))
5433 modheads = bundle2.combinechangegroupresults(op)
5425 modheads = bundle2.combinechangegroupresults(op)
5434
5426
5435 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5427 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5436
5428
5437 @command('^update|up|checkout|co',
5429 @command('^update|up|checkout|co',
5438 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5430 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5439 ('c', 'check', None, _('require clean working directory')),
5431 ('c', 'check', None, _('require clean working directory')),
5440 ('m', 'merge', None, _('merge uncommitted changes')),
5432 ('m', 'merge', None, _('merge uncommitted changes')),
5441 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5433 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5442 ('r', 'rev', '', _('revision'), _('REV'))
5434 ('r', 'rev', '', _('revision'), _('REV'))
5443 ] + mergetoolopts,
5435 ] + mergetoolopts,
5444 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5436 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5445 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5437 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5446 merge=None, tool=None):
5438 merge=None, tool=None):
5447 """update working directory (or switch revisions)
5439 """update working directory (or switch revisions)
5448
5440
5449 Update the repository's working directory to the specified
5441 Update the repository's working directory to the specified
5450 changeset. If no changeset is specified, update to the tip of the
5442 changeset. If no changeset is specified, update to the tip of the
5451 current named branch and move the active bookmark (see :hg:`help
5443 current named branch and move the active bookmark (see :hg:`help
5452 bookmarks`).
5444 bookmarks`).
5453
5445
5454 Update sets the working directory's parent revision to the specified
5446 Update sets the working directory's parent revision to the specified
5455 changeset (see :hg:`help parents`).
5447 changeset (see :hg:`help parents`).
5456
5448
5457 If the changeset is not a descendant or ancestor of the working
5449 If the changeset is not a descendant or ancestor of the working
5458 directory's parent and there are uncommitted changes, the update is
5450 directory's parent and there are uncommitted changes, the update is
5459 aborted. With the -c/--check option, the working directory is checked
5451 aborted. With the -c/--check option, the working directory is checked
5460 for uncommitted changes; if none are found, the working directory is
5452 for uncommitted changes; if none are found, the working directory is
5461 updated to the specified changeset.
5453 updated to the specified changeset.
5462
5454
5463 .. container:: verbose
5455 .. container:: verbose
5464
5456
5465 The -C/--clean, -c/--check, and -m/--merge options control what
5457 The -C/--clean, -c/--check, and -m/--merge options control what
5466 happens if the working directory contains uncommitted changes.
5458 happens if the working directory contains uncommitted changes.
5467 At most of one of them can be specified.
5459 At most of one of them can be specified.
5468
5460
5469 1. If no option is specified, and if
5461 1. If no option is specified, and if
5470 the requested changeset is an ancestor or descendant of
5462 the requested changeset is an ancestor or descendant of
5471 the working directory's parent, the uncommitted changes
5463 the working directory's parent, the uncommitted changes
5472 are merged into the requested changeset and the merged
5464 are merged into the requested changeset and the merged
5473 result is left uncommitted. If the requested changeset is
5465 result is left uncommitted. If the requested changeset is
5474 not an ancestor or descendant (that is, it is on another
5466 not an ancestor or descendant (that is, it is on another
5475 branch), the update is aborted and the uncommitted changes
5467 branch), the update is aborted and the uncommitted changes
5476 are preserved.
5468 are preserved.
5477
5469
5478 2. With the -m/--merge option, the update is allowed even if the
5470 2. With the -m/--merge option, the update is allowed even if the
5479 requested changeset is not an ancestor or descendant of
5471 requested changeset is not an ancestor or descendant of
5480 the working directory's parent.
5472 the working directory's parent.
5481
5473
5482 3. With the -c/--check option, the update is aborted and the
5474 3. With the -c/--check option, the update is aborted and the
5483 uncommitted changes are preserved.
5475 uncommitted changes are preserved.
5484
5476
5485 4. With the -C/--clean option, uncommitted changes are discarded and
5477 4. With the -C/--clean option, uncommitted changes are discarded and
5486 the working directory is updated to the requested changeset.
5478 the working directory is updated to the requested changeset.
5487
5479
5488 To cancel an uncommitted merge (and lose your changes), use
5480 To cancel an uncommitted merge (and lose your changes), use
5489 :hg:`update --clean .`.
5481 :hg:`update --clean .`.
5490
5482
5491 Use null as the changeset to remove the working directory (like
5483 Use null as the changeset to remove the working directory (like
5492 :hg:`clone -U`).
5484 :hg:`clone -U`).
5493
5485
5494 If you want to revert just one file to an older revision, use
5486 If you want to revert just one file to an older revision, use
5495 :hg:`revert [-r REV] NAME`.
5487 :hg:`revert [-r REV] NAME`.
5496
5488
5497 See :hg:`help dates` for a list of formats valid for -d/--date.
5489 See :hg:`help dates` for a list of formats valid for -d/--date.
5498
5490
5499 Returns 0 on success, 1 if there are unresolved files.
5491 Returns 0 on success, 1 if there are unresolved files.
5500 """
5492 """
5501 if rev and node:
5493 if rev and node:
5502 raise error.Abort(_("please specify just one revision"))
5494 raise error.Abort(_("please specify just one revision"))
5503
5495
5504 if ui.configbool('commands', 'update.requiredest'):
5496 if ui.configbool('commands', 'update.requiredest'):
5505 if not node and not rev and not date:
5497 if not node and not rev and not date:
5506 raise error.Abort(_('you must specify a destination'),
5498 raise error.Abort(_('you must specify a destination'),
5507 hint=_('for example: hg update ".::"'))
5499 hint=_('for example: hg update ".::"'))
5508
5500
5509 if rev is None or rev == '':
5501 if rev is None or rev == '':
5510 rev = node
5502 rev = node
5511
5503
5512 if date and rev is not None:
5504 if date and rev is not None:
5513 raise error.Abort(_("you can't specify a revision and a date"))
5505 raise error.Abort(_("you can't specify a revision and a date"))
5514
5506
5515 if len([x for x in (clean, check, merge) if x]) > 1:
5507 if len([x for x in (clean, check, merge) if x]) > 1:
5516 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5508 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5517 "or -m/--merge"))
5509 "or -m/--merge"))
5518
5510
5519 updatecheck = None
5511 updatecheck = None
5520 if check:
5512 if check:
5521 updatecheck = 'abort'
5513 updatecheck = 'abort'
5522 elif merge:
5514 elif merge:
5523 updatecheck = 'none'
5515 updatecheck = 'none'
5524
5516
5525 with repo.wlock():
5517 with repo.wlock():
5526 cmdutil.clearunfinished(repo)
5518 cmdutil.clearunfinished(repo)
5527
5519
5528 if date:
5520 if date:
5529 rev = cmdutil.finddate(ui, repo, date)
5521 rev = cmdutil.finddate(ui, repo, date)
5530
5522
5531 # if we defined a bookmark, we have to remember the original name
5523 # if we defined a bookmark, we have to remember the original name
5532 brev = rev
5524 brev = rev
5533 if rev:
5525 if rev:
5534 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5526 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5535 ctx = scmutil.revsingle(repo, rev, rev)
5527 ctx = scmutil.revsingle(repo, rev, rev)
5536 rev = ctx.rev()
5528 rev = ctx.rev()
5537 if ctx.hidden():
5529 if ctx.hidden():
5538 ui.warn(_("updating to a hidden changeset %s\n") % ctx.hex()[:12])
5530 ui.warn(_("updating to a hidden changeset %s\n") % ctx.hex()[:12])
5539
5531
5540 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5532 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5541
5533
5542 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5534 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5543 updatecheck=updatecheck)
5535 updatecheck=updatecheck)
5544
5536
5545 @command('verify', [])
5537 @command('verify', [])
5546 def verify(ui, repo):
5538 def verify(ui, repo):
5547 """verify the integrity of the repository
5539 """verify the integrity of the repository
5548
5540
5549 Verify the integrity of the current repository.
5541 Verify the integrity of the current repository.
5550
5542
5551 This will perform an extensive check of the repository's
5543 This will perform an extensive check of the repository's
5552 integrity, validating the hashes and checksums of each entry in
5544 integrity, validating the hashes and checksums of each entry in
5553 the changelog, manifest, and tracked files, as well as the
5545 the changelog, manifest, and tracked files, as well as the
5554 integrity of their crosslinks and indices.
5546 integrity of their crosslinks and indices.
5555
5547
5556 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5548 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5557 for more information about recovery from corruption of the
5549 for more information about recovery from corruption of the
5558 repository.
5550 repository.
5559
5551
5560 Returns 0 on success, 1 if errors are encountered.
5552 Returns 0 on success, 1 if errors are encountered.
5561 """
5553 """
5562 return hg.verify(repo)
5554 return hg.verify(repo)
5563
5555
5564 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5556 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5565 def version_(ui, **opts):
5557 def version_(ui, **opts):
5566 """output version and copyright information"""
5558 """output version and copyright information"""
5567 opts = pycompat.byteskwargs(opts)
5559 opts = pycompat.byteskwargs(opts)
5568 if ui.verbose:
5560 if ui.verbose:
5569 ui.pager('version')
5561 ui.pager('version')
5570 fm = ui.formatter("version", opts)
5562 fm = ui.formatter("version", opts)
5571 fm.startitem()
5563 fm.startitem()
5572 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5564 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5573 util.version())
5565 util.version())
5574 license = _(
5566 license = _(
5575 "(see https://mercurial-scm.org for more information)\n"
5567 "(see https://mercurial-scm.org for more information)\n"
5576 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5568 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5577 "This is free software; see the source for copying conditions. "
5569 "This is free software; see the source for copying conditions. "
5578 "There is NO\nwarranty; "
5570 "There is NO\nwarranty; "
5579 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5571 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5580 )
5572 )
5581 if not ui.quiet:
5573 if not ui.quiet:
5582 fm.plain(license)
5574 fm.plain(license)
5583
5575
5584 if ui.verbose:
5576 if ui.verbose:
5585 fm.plain(_("\nEnabled extensions:\n\n"))
5577 fm.plain(_("\nEnabled extensions:\n\n"))
5586 # format names and versions into columns
5578 # format names and versions into columns
5587 names = []
5579 names = []
5588 vers = []
5580 vers = []
5589 isinternals = []
5581 isinternals = []
5590 for name, module in extensions.extensions():
5582 for name, module in extensions.extensions():
5591 names.append(name)
5583 names.append(name)
5592 vers.append(extensions.moduleversion(module) or None)
5584 vers.append(extensions.moduleversion(module) or None)
5593 isinternals.append(extensions.ismoduleinternal(module))
5585 isinternals.append(extensions.ismoduleinternal(module))
5594 fn = fm.nested("extensions")
5586 fn = fm.nested("extensions")
5595 if names:
5587 if names:
5596 namefmt = " %%-%ds " % max(len(n) for n in names)
5588 namefmt = " %%-%ds " % max(len(n) for n in names)
5597 places = [_("external"), _("internal")]
5589 places = [_("external"), _("internal")]
5598 for n, v, p in zip(names, vers, isinternals):
5590 for n, v, p in zip(names, vers, isinternals):
5599 fn.startitem()
5591 fn.startitem()
5600 fn.condwrite(ui.verbose, "name", namefmt, n)
5592 fn.condwrite(ui.verbose, "name", namefmt, n)
5601 if ui.verbose:
5593 if ui.verbose:
5602 fn.plain("%s " % places[p])
5594 fn.plain("%s " % places[p])
5603 fn.data(bundled=p)
5595 fn.data(bundled=p)
5604 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5596 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5605 if ui.verbose:
5597 if ui.verbose:
5606 fn.plain("\n")
5598 fn.plain("\n")
5607 fn.end()
5599 fn.end()
5608 fm.end()
5600 fm.end()
5609
5601
5610 def loadcmdtable(ui, name, cmdtable):
5602 def loadcmdtable(ui, name, cmdtable):
5611 """Load command functions from specified cmdtable
5603 """Load command functions from specified cmdtable
5612 """
5604 """
5613 overrides = [cmd for cmd in cmdtable if cmd in table]
5605 overrides = [cmd for cmd in cmdtable if cmd in table]
5614 if overrides:
5606 if overrides:
5615 ui.warn(_("extension '%s' overrides commands: %s\n")
5607 ui.warn(_("extension '%s' overrides commands: %s\n")
5616 % (name, " ".join(overrides)))
5608 % (name, " ".join(overrides)))
5617 table.update(cmdtable)
5609 table.update(cmdtable)
@@ -1,3540 +1,3540 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 __future__ import absolute_import
85 > from __future__ import absolute_import
86 > from mercurial import (
86 > from mercurial import (
87 > cmdutil,
87 > cmdutil,
88 > commands,
88 > commands,
89 > extensions,
89 > extensions,
90 > revsetlang,
90 > revsetlang,
91 > smartset,
91 > smartset,
92 > )
92 > )
93 >
93 >
94 > def logrevset(repo, pats, opts):
94 > def logrevset(repo, pats, opts):
95 > revs = cmdutil._logrevs(repo, opts)
95 > revs = cmdutil._logrevs(repo, opts)
96 > if not revs:
96 > if not revs:
97 > return None
97 > return None
98 > return cmdutil._makelogrevset(repo, pats, opts)[0]
98 > return cmdutil._makelogrevset(repo, pats, opts)[0]
99 >
99 >
100 > def uisetup(ui):
100 > def uisetup(ui):
101 > def printrevset(orig, repo, pats, opts):
101 > def printrevset(orig, repo, pats, opts):
102 > revs, filematcher = orig(repo, pats, opts)
102 > revs, filematcher = orig(repo, pats, opts)
103 > if opts.get('print_revset'):
103 > if opts.get('print_revset'):
104 > expr = logrevset(repo, pats, opts)
104 > expr = logrevset(repo, pats, opts)
105 > if expr:
105 > if expr:
106 > tree = revsetlang.parse(expr)
106 > tree = revsetlang.parse(expr)
107 > tree = revsetlang.analyze(tree)
107 > tree = revsetlang.analyze(tree)
108 > else:
108 > else:
109 > tree = []
109 > tree = []
110 > ui = repo.ui
110 > ui = repo.ui
111 > ui.write('%r\n' % (opts.get('rev', []),))
111 > ui.write('%r\n' % (opts.get('rev', []),))
112 > ui.write(revsetlang.prettyformat(tree) + '\n')
112 > ui.write(revsetlang.prettyformat(tree) + '\n')
113 > ui.write(smartset.prettyformat(revs) + '\n')
113 > ui.write(smartset.prettyformat(revs) + '\n')
114 > revs = smartset.baseset() # display no revisions
114 > revs = smartset.baseset() # display no revisions
115 > return revs, filematcher
115 > return revs, filematcher
116 > extensions.wrapfunction(cmdutil, 'getlogrevs', printrevset)
116 > extensions.wrapfunction(cmdutil, 'getlogrevs', printrevset)
117 > aliases, entry = cmdutil.findcmd('log', commands.table)
117 > aliases, entry = cmdutil.findcmd('log', commands.table)
118 > entry[1].append(('', 'print-revset', False,
118 > entry[1].append(('', 'print-revset', False,
119 > 'print generated revset and exit (DEPRECATED)'))
119 > 'print generated revset and exit (DEPRECATED)'))
120 > EOF
120 > EOF
121
121
122 $ echo "[extensions]" >> $HGRCPATH
122 $ echo "[extensions]" >> $HGRCPATH
123 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
123 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
124
124
125 $ hg init repo
125 $ hg init repo
126 $ cd repo
126 $ cd repo
127
127
128 Empty repo:
128 Empty repo:
129
129
130 $ hg log -G
130 $ hg log -G
131
131
132
132
133 Building DAG:
133 Building DAG:
134
134
135 $ commit 0 "root"
135 $ commit 0 "root"
136 $ commit 1 "collapse" 0
136 $ commit 1 "collapse" 0
137 $ commit 2 "collapse" 1
137 $ commit 2 "collapse" 1
138 $ commit 3 "collapse" 2
138 $ commit 3 "collapse" 2
139 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
139 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
140 $ commit 5 "expand" 3 4
140 $ commit 5 "expand" 3 4
141 $ commit 6 "merge two known; one immediate left, one far left" 2 5
141 $ commit 6 "merge two known; one immediate left, one far left" 2 5
142 $ commit 7 "expand" 2 5
142 $ commit 7 "expand" 2 5
143 $ commit 8 "merge two known; one immediate left, one far right" 0 7
143 $ commit 8 "merge two known; one immediate left, one far right" 0 7
144 $ commit 9 "expand" 7 8
144 $ commit 9 "expand" 7 8
145 $ commit 10 "merge two known; one immediate left, one near right" 0 6
145 $ commit 10 "merge two known; one immediate left, one near right" 0 6
146 $ commit 11 "expand" 6 10
146 $ commit 11 "expand" 6 10
147 $ commit 12 "merge two known; one immediate right, one far left" 1 9
147 $ commit 12 "merge two known; one immediate right, one far left" 1 9
148 $ commit 13 "expand" 9 11
148 $ commit 13 "expand" 9 11
149 $ commit 14 "merge two known; one immediate right, one far right" 0 12
149 $ commit 14 "merge two known; one immediate right, one far right" 0 12
150 $ commit 15 "expand" 13 14
150 $ commit 15 "expand" 13 14
151 $ commit 16 "merge two known; one immediate right, one near right" 0 1
151 $ commit 16 "merge two known; one immediate right, one near right" 0 1
152 $ commit 17 "expand" 12 16
152 $ commit 17 "expand" 12 16
153 $ commit 18 "merge two known; two far left" 1 15
153 $ commit 18 "merge two known; two far left" 1 15
154 $ commit 19 "expand" 15 17
154 $ commit 19 "expand" 15 17
155 $ commit 20 "merge two known; two far right" 0 18
155 $ commit 20 "merge two known; two far right" 0 18
156 $ commit 21 "expand" 19 20
156 $ commit 21 "expand" 19 20
157 $ commit 22 "merge two known; one far left, one far right" 18 21
157 $ commit 22 "merge two known; one far left, one far right" 18 21
158 $ commit 23 "merge one known; immediate left" 1 22
158 $ commit 23 "merge one known; immediate left" 1 22
159 $ commit 24 "merge one known; immediate right" 0 23
159 $ commit 24 "merge one known; immediate right" 0 23
160 $ commit 25 "merge one known; far left" 21 24
160 $ commit 25 "merge one known; far left" 21 24
161 $ commit 26 "merge one known; far right" 18 25
161 $ commit 26 "merge one known; far right" 18 25
162 $ commit 27 "collapse" 21
162 $ commit 27 "collapse" 21
163 $ commit 28 "merge zero known" 1 26
163 $ commit 28 "merge zero known" 1 26
164 $ commit 29 "regular commit" 0
164 $ commit 29 "regular commit" 0
165 $ commit 30 "expand" 28 29
165 $ commit 30 "expand" 28 29
166 $ commit 31 "expand" 21 30
166 $ commit 31 "expand" 21 30
167 $ commit 32 "expand" 27 31
167 $ commit 32 "expand" 27 31
168 $ commit 33 "head" 18
168 $ commit 33 "head" 18
169 $ commit 34 "head" 32
169 $ commit 34 "head" 32
170
170
171
171
172 $ hg log -G -q
172 $ hg log -G -q
173 @ 34:fea3ac5810e0
173 @ 34:fea3ac5810e0
174 |
174 |
175 | o 33:68608f5145f9
175 | o 33:68608f5145f9
176 | |
176 | |
177 o | 32:d06dffa21a31
177 o | 32:d06dffa21a31
178 |\ \
178 |\ \
179 | o \ 31:621d83e11f67
179 | o \ 31:621d83e11f67
180 | |\ \
180 | |\ \
181 | | o \ 30:6e11cd4b648f
181 | | o \ 30:6e11cd4b648f
182 | | |\ \
182 | | |\ \
183 | | | o | 29:cd9bb2be7593
183 | | | o | 29:cd9bb2be7593
184 | | | | |
184 | | | | |
185 | | o | | 28:44ecd0b9ae99
185 | | o | | 28:44ecd0b9ae99
186 | | |\ \ \
186 | | |\ \ \
187 o | | | | | 27:886ed638191b
187 o | | | | | 27:886ed638191b
188 |/ / / / /
188 |/ / / / /
189 | | o---+ 26:7f25b6c2f0b9
189 | | o---+ 26:7f25b6c2f0b9
190 | | | | |
190 | | | | |
191 +---o | | 25:91da8ed57247
191 +---o | | 25:91da8ed57247
192 | | | | |
192 | | | | |
193 | | o | | 24:a9c19a3d96b7
193 | | o | | 24:a9c19a3d96b7
194 | | |\| |
194 | | |\| |
195 | | o | | 23:a01cddf0766d
195 | | o | | 23:a01cddf0766d
196 | |/| | |
196 | |/| | |
197 +---o---+ 22:e0d9cccacb5d
197 +---o---+ 22:e0d9cccacb5d
198 | | / /
198 | | / /
199 o | | | 21:d42a756af44d
199 o | | | 21:d42a756af44d
200 |\ \ \ \
200 |\ \ \ \
201 | o---+-+ 20:d30ed6450e32
201 | o---+-+ 20:d30ed6450e32
202 | / / /
202 | / / /
203 o | | | 19:31ddc2c1573b
203 o | | | 19:31ddc2c1573b
204 |\ \ \ \
204 |\ \ \ \
205 +---+---o 18:1aa84d96232a
205 +---+---o 18:1aa84d96232a
206 | | | |
206 | | | |
207 | o | | 17:44765d7c06e0
207 | o | | 17:44765d7c06e0
208 | |\ \ \
208 | |\ \ \
209 | | o---+ 16:3677d192927d
209 | | o---+ 16:3677d192927d
210 | | |/ /
210 | | |/ /
211 o | | | 15:1dda3f72782d
211 o | | | 15:1dda3f72782d
212 |\ \ \ \
212 |\ \ \ \
213 | o-----+ 14:8eac370358ef
213 | o-----+ 14:8eac370358ef
214 | |/ / /
214 | |/ / /
215 o | | | 13:22d8966a97e3
215 o | | | 13:22d8966a97e3
216 |\ \ \ \
216 |\ \ \ \
217 +---o | | 12:86b91144a6e9
217 +---o | | 12:86b91144a6e9
218 | | |/ /
218 | | |/ /
219 | o | | 11:832d76e6bdf2
219 | o | | 11:832d76e6bdf2
220 | |\ \ \
220 | |\ \ \
221 | | o---+ 10:74c64d036d72
221 | | o---+ 10:74c64d036d72
222 | |/ / /
222 | |/ / /
223 o | | | 9:7010c0af0a35
223 o | | | 9:7010c0af0a35
224 |\ \ \ \
224 |\ \ \ \
225 | o-----+ 8:7a0b11f71937
225 | o-----+ 8:7a0b11f71937
226 |/ / / /
226 |/ / / /
227 o | | | 7:b632bb1b1224
227 o | | | 7:b632bb1b1224
228 |\ \ \ \
228 |\ \ \ \
229 +---o | | 6:b105a072e251
229 +---o | | 6:b105a072e251
230 | |/ / /
230 | |/ / /
231 | o | | 5:4409d547b708
231 | o | | 5:4409d547b708
232 | |\ \ \
232 | |\ \ \
233 | | o | | 4:26a8bac39d9f
233 | | o | | 4:26a8bac39d9f
234 | |/|/ /
234 | |/|/ /
235 | o / / 3:27eef8ed80b4
235 | o / / 3:27eef8ed80b4
236 |/ / /
236 |/ / /
237 o / / 2:3d9a33b8d1e1
237 o / / 2:3d9a33b8d1e1
238 |/ /
238 |/ /
239 o / 1:6db2ef61d156
239 o / 1:6db2ef61d156
240 |/
240 |/
241 o 0:e6eb3150255d
241 o 0:e6eb3150255d
242
242
243
243
244 $ hg log -G
244 $ hg log -G
245 @ changeset: 34:fea3ac5810e0
245 @ changeset: 34:fea3ac5810e0
246 | tag: tip
246 | tag: tip
247 | parent: 32:d06dffa21a31
247 | parent: 32:d06dffa21a31
248 | user: test
248 | user: test
249 | date: Thu Jan 01 00:00:34 1970 +0000
249 | date: Thu Jan 01 00:00:34 1970 +0000
250 | summary: (34) head
250 | summary: (34) head
251 |
251 |
252 | o changeset: 33:68608f5145f9
252 | o changeset: 33:68608f5145f9
253 | | parent: 18:1aa84d96232a
253 | | parent: 18:1aa84d96232a
254 | | user: test
254 | | user: test
255 | | date: Thu Jan 01 00:00:33 1970 +0000
255 | | date: Thu Jan 01 00:00:33 1970 +0000
256 | | summary: (33) head
256 | | summary: (33) head
257 | |
257 | |
258 o | changeset: 32:d06dffa21a31
258 o | changeset: 32:d06dffa21a31
259 |\ \ parent: 27:886ed638191b
259 |\ \ parent: 27:886ed638191b
260 | | | parent: 31:621d83e11f67
260 | | | parent: 31:621d83e11f67
261 | | | user: test
261 | | | user: test
262 | | | date: Thu Jan 01 00:00:32 1970 +0000
262 | | | date: Thu Jan 01 00:00:32 1970 +0000
263 | | | summary: (32) expand
263 | | | summary: (32) expand
264 | | |
264 | | |
265 | o | changeset: 31:621d83e11f67
265 | o | changeset: 31:621d83e11f67
266 | |\ \ parent: 21:d42a756af44d
266 | |\ \ parent: 21:d42a756af44d
267 | | | | parent: 30:6e11cd4b648f
267 | | | | parent: 30:6e11cd4b648f
268 | | | | user: test
268 | | | | user: test
269 | | | | date: Thu Jan 01 00:00:31 1970 +0000
269 | | | | date: Thu Jan 01 00:00:31 1970 +0000
270 | | | | summary: (31) expand
270 | | | | summary: (31) expand
271 | | | |
271 | | | |
272 | | o | changeset: 30:6e11cd4b648f
272 | | o | changeset: 30:6e11cd4b648f
273 | | |\ \ parent: 28:44ecd0b9ae99
273 | | |\ \ parent: 28:44ecd0b9ae99
274 | | | | | parent: 29:cd9bb2be7593
274 | | | | | parent: 29:cd9bb2be7593
275 | | | | | user: test
275 | | | | | user: test
276 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
276 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
277 | | | | | summary: (30) expand
277 | | | | | summary: (30) expand
278 | | | | |
278 | | | | |
279 | | | o | changeset: 29:cd9bb2be7593
279 | | | o | changeset: 29:cd9bb2be7593
280 | | | | | parent: 0:e6eb3150255d
280 | | | | | parent: 0:e6eb3150255d
281 | | | | | user: test
281 | | | | | user: test
282 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
282 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
283 | | | | | summary: (29) regular commit
283 | | | | | summary: (29) regular commit
284 | | | | |
284 | | | | |
285 | | o | | changeset: 28:44ecd0b9ae99
285 | | o | | changeset: 28:44ecd0b9ae99
286 | | |\ \ \ parent: 1:6db2ef61d156
286 | | |\ \ \ parent: 1:6db2ef61d156
287 | | | | | | parent: 26:7f25b6c2f0b9
287 | | | | | | parent: 26:7f25b6c2f0b9
288 | | | | | | user: test
288 | | | | | | user: test
289 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
289 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
290 | | | | | | summary: (28) merge zero known
290 | | | | | | summary: (28) merge zero known
291 | | | | | |
291 | | | | | |
292 o | | | | | changeset: 27:886ed638191b
292 o | | | | | changeset: 27:886ed638191b
293 |/ / / / / parent: 21:d42a756af44d
293 |/ / / / / parent: 21:d42a756af44d
294 | | | | | user: test
294 | | | | | user: test
295 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
295 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
296 | | | | | summary: (27) collapse
296 | | | | | summary: (27) collapse
297 | | | | |
297 | | | | |
298 | | o---+ changeset: 26:7f25b6c2f0b9
298 | | o---+ changeset: 26:7f25b6c2f0b9
299 | | | | | parent: 18:1aa84d96232a
299 | | | | | parent: 18:1aa84d96232a
300 | | | | | parent: 25:91da8ed57247
300 | | | | | parent: 25:91da8ed57247
301 | | | | | user: test
301 | | | | | user: test
302 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
302 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
303 | | | | | summary: (26) merge one known; far right
303 | | | | | summary: (26) merge one known; far right
304 | | | | |
304 | | | | |
305 +---o | | changeset: 25:91da8ed57247
305 +---o | | changeset: 25:91da8ed57247
306 | | | | | parent: 21:d42a756af44d
306 | | | | | parent: 21:d42a756af44d
307 | | | | | parent: 24:a9c19a3d96b7
307 | | | | | parent: 24:a9c19a3d96b7
308 | | | | | user: test
308 | | | | | user: test
309 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
309 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
310 | | | | | summary: (25) merge one known; far left
310 | | | | | summary: (25) merge one known; far left
311 | | | | |
311 | | | | |
312 | | o | | changeset: 24:a9c19a3d96b7
312 | | o | | changeset: 24:a9c19a3d96b7
313 | | |\| | parent: 0:e6eb3150255d
313 | | |\| | parent: 0:e6eb3150255d
314 | | | | | parent: 23:a01cddf0766d
314 | | | | | parent: 23:a01cddf0766d
315 | | | | | user: test
315 | | | | | user: test
316 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
316 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
317 | | | | | summary: (24) merge one known; immediate right
317 | | | | | summary: (24) merge one known; immediate right
318 | | | | |
318 | | | | |
319 | | o | | changeset: 23:a01cddf0766d
319 | | o | | changeset: 23:a01cddf0766d
320 | |/| | | parent: 1:6db2ef61d156
320 | |/| | | parent: 1:6db2ef61d156
321 | | | | | parent: 22:e0d9cccacb5d
321 | | | | | parent: 22:e0d9cccacb5d
322 | | | | | user: test
322 | | | | | user: test
323 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
323 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
324 | | | | | summary: (23) merge one known; immediate left
324 | | | | | summary: (23) merge one known; immediate left
325 | | | | |
325 | | | | |
326 +---o---+ changeset: 22:e0d9cccacb5d
326 +---o---+ changeset: 22:e0d9cccacb5d
327 | | | | parent: 18:1aa84d96232a
327 | | | | parent: 18:1aa84d96232a
328 | | / / parent: 21:d42a756af44d
328 | | / / parent: 21:d42a756af44d
329 | | | | user: test
329 | | | | user: test
330 | | | | date: Thu Jan 01 00:00:22 1970 +0000
330 | | | | date: Thu Jan 01 00:00:22 1970 +0000
331 | | | | summary: (22) merge two known; one far left, one far right
331 | | | | summary: (22) merge two known; one far left, one far right
332 | | | |
332 | | | |
333 o | | | changeset: 21:d42a756af44d
333 o | | | changeset: 21:d42a756af44d
334 |\ \ \ \ parent: 19:31ddc2c1573b
334 |\ \ \ \ parent: 19:31ddc2c1573b
335 | | | | | parent: 20:d30ed6450e32
335 | | | | | parent: 20:d30ed6450e32
336 | | | | | user: test
336 | | | | | user: test
337 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
337 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
338 | | | | | summary: (21) expand
338 | | | | | summary: (21) expand
339 | | | | |
339 | | | | |
340 | o---+-+ changeset: 20:d30ed6450e32
340 | o---+-+ changeset: 20:d30ed6450e32
341 | | | | parent: 0:e6eb3150255d
341 | | | | parent: 0:e6eb3150255d
342 | / / / parent: 18:1aa84d96232a
342 | / / / parent: 18:1aa84d96232a
343 | | | | user: test
343 | | | | user: test
344 | | | | date: Thu Jan 01 00:00:20 1970 +0000
344 | | | | date: Thu Jan 01 00:00:20 1970 +0000
345 | | | | summary: (20) merge two known; two far right
345 | | | | summary: (20) merge two known; two far right
346 | | | |
346 | | | |
347 o | | | changeset: 19:31ddc2c1573b
347 o | | | changeset: 19:31ddc2c1573b
348 |\ \ \ \ parent: 15:1dda3f72782d
348 |\ \ \ \ parent: 15:1dda3f72782d
349 | | | | | parent: 17:44765d7c06e0
349 | | | | | parent: 17:44765d7c06e0
350 | | | | | user: test
350 | | | | | user: test
351 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
351 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
352 | | | | | summary: (19) expand
352 | | | | | summary: (19) expand
353 | | | | |
353 | | | | |
354 +---+---o changeset: 18:1aa84d96232a
354 +---+---o changeset: 18:1aa84d96232a
355 | | | | parent: 1:6db2ef61d156
355 | | | | parent: 1:6db2ef61d156
356 | | | | parent: 15:1dda3f72782d
356 | | | | parent: 15:1dda3f72782d
357 | | | | user: test
357 | | | | user: test
358 | | | | date: Thu Jan 01 00:00:18 1970 +0000
358 | | | | date: Thu Jan 01 00:00:18 1970 +0000
359 | | | | summary: (18) merge two known; two far left
359 | | | | summary: (18) merge two known; two far left
360 | | | |
360 | | | |
361 | o | | changeset: 17:44765d7c06e0
361 | o | | changeset: 17:44765d7c06e0
362 | |\ \ \ parent: 12:86b91144a6e9
362 | |\ \ \ parent: 12:86b91144a6e9
363 | | | | | parent: 16:3677d192927d
363 | | | | | parent: 16:3677d192927d
364 | | | | | user: test
364 | | | | | user: test
365 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
365 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
366 | | | | | summary: (17) expand
366 | | | | | summary: (17) expand
367 | | | | |
367 | | | | |
368 | | o---+ changeset: 16:3677d192927d
368 | | o---+ changeset: 16:3677d192927d
369 | | | | | parent: 0:e6eb3150255d
369 | | | | | parent: 0:e6eb3150255d
370 | | |/ / parent: 1:6db2ef61d156
370 | | |/ / parent: 1:6db2ef61d156
371 | | | | user: test
371 | | | | user: test
372 | | | | date: Thu Jan 01 00:00:16 1970 +0000
372 | | | | date: Thu Jan 01 00:00:16 1970 +0000
373 | | | | summary: (16) merge two known; one immediate right, one near right
373 | | | | summary: (16) merge two known; one immediate right, one near right
374 | | | |
374 | | | |
375 o | | | changeset: 15:1dda3f72782d
375 o | | | changeset: 15:1dda3f72782d
376 |\ \ \ \ parent: 13:22d8966a97e3
376 |\ \ \ \ parent: 13:22d8966a97e3
377 | | | | | parent: 14:8eac370358ef
377 | | | | | parent: 14:8eac370358ef
378 | | | | | user: test
378 | | | | | user: test
379 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
379 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
380 | | | | | summary: (15) expand
380 | | | | | summary: (15) expand
381 | | | | |
381 | | | | |
382 | o-----+ changeset: 14:8eac370358ef
382 | o-----+ changeset: 14:8eac370358ef
383 | | | | | parent: 0:e6eb3150255d
383 | | | | | parent: 0:e6eb3150255d
384 | |/ / / parent: 12:86b91144a6e9
384 | |/ / / parent: 12:86b91144a6e9
385 | | | | user: test
385 | | | | user: test
386 | | | | date: Thu Jan 01 00:00:14 1970 +0000
386 | | | | date: Thu Jan 01 00:00:14 1970 +0000
387 | | | | summary: (14) merge two known; one immediate right, one far right
387 | | | | summary: (14) merge two known; one immediate right, one far right
388 | | | |
388 | | | |
389 o | | | changeset: 13:22d8966a97e3
389 o | | | changeset: 13:22d8966a97e3
390 |\ \ \ \ parent: 9:7010c0af0a35
390 |\ \ \ \ parent: 9:7010c0af0a35
391 | | | | | parent: 11:832d76e6bdf2
391 | | | | | parent: 11:832d76e6bdf2
392 | | | | | user: test
392 | | | | | user: test
393 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
393 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
394 | | | | | summary: (13) expand
394 | | | | | summary: (13) expand
395 | | | | |
395 | | | | |
396 +---o | | changeset: 12:86b91144a6e9
396 +---o | | changeset: 12:86b91144a6e9
397 | | |/ / parent: 1:6db2ef61d156
397 | | |/ / parent: 1:6db2ef61d156
398 | | | | parent: 9:7010c0af0a35
398 | | | | parent: 9:7010c0af0a35
399 | | | | user: test
399 | | | | user: test
400 | | | | date: Thu Jan 01 00:00:12 1970 +0000
400 | | | | date: Thu Jan 01 00:00:12 1970 +0000
401 | | | | summary: (12) merge two known; one immediate right, one far left
401 | | | | summary: (12) merge two known; one immediate right, one far left
402 | | | |
402 | | | |
403 | o | | changeset: 11:832d76e6bdf2
403 | o | | changeset: 11:832d76e6bdf2
404 | |\ \ \ parent: 6:b105a072e251
404 | |\ \ \ parent: 6:b105a072e251
405 | | | | | parent: 10:74c64d036d72
405 | | | | | parent: 10:74c64d036d72
406 | | | | | user: test
406 | | | | | user: test
407 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
407 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
408 | | | | | summary: (11) expand
408 | | | | | summary: (11) expand
409 | | | | |
409 | | | | |
410 | | o---+ changeset: 10:74c64d036d72
410 | | o---+ changeset: 10:74c64d036d72
411 | | | | | parent: 0:e6eb3150255d
411 | | | | | parent: 0:e6eb3150255d
412 | |/ / / parent: 6:b105a072e251
412 | |/ / / parent: 6:b105a072e251
413 | | | | user: test
413 | | | | user: test
414 | | | | date: Thu Jan 01 00:00:10 1970 +0000
414 | | | | date: Thu Jan 01 00:00:10 1970 +0000
415 | | | | summary: (10) merge two known; one immediate left, one near right
415 | | | | summary: (10) merge two known; one immediate left, one near right
416 | | | |
416 | | | |
417 o | | | changeset: 9:7010c0af0a35
417 o | | | changeset: 9:7010c0af0a35
418 |\ \ \ \ parent: 7:b632bb1b1224
418 |\ \ \ \ parent: 7:b632bb1b1224
419 | | | | | parent: 8:7a0b11f71937
419 | | | | | parent: 8:7a0b11f71937
420 | | | | | user: test
420 | | | | | user: test
421 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
421 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
422 | | | | | summary: (9) expand
422 | | | | | summary: (9) expand
423 | | | | |
423 | | | | |
424 | o-----+ changeset: 8:7a0b11f71937
424 | o-----+ changeset: 8:7a0b11f71937
425 | | | | | parent: 0:e6eb3150255d
425 | | | | | parent: 0:e6eb3150255d
426 |/ / / / parent: 7:b632bb1b1224
426 |/ / / / parent: 7:b632bb1b1224
427 | | | | user: test
427 | | | | user: test
428 | | | | date: Thu Jan 01 00:00:08 1970 +0000
428 | | | | date: Thu Jan 01 00:00:08 1970 +0000
429 | | | | summary: (8) merge two known; one immediate left, one far right
429 | | | | summary: (8) merge two known; one immediate left, one far right
430 | | | |
430 | | | |
431 o | | | changeset: 7:b632bb1b1224
431 o | | | changeset: 7:b632bb1b1224
432 |\ \ \ \ parent: 2:3d9a33b8d1e1
432 |\ \ \ \ parent: 2:3d9a33b8d1e1
433 | | | | | parent: 5:4409d547b708
433 | | | | | parent: 5:4409d547b708
434 | | | | | user: test
434 | | | | | user: test
435 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
435 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
436 | | | | | summary: (7) expand
436 | | | | | summary: (7) expand
437 | | | | |
437 | | | | |
438 +---o | | changeset: 6:b105a072e251
438 +---o | | changeset: 6:b105a072e251
439 | |/ / / parent: 2:3d9a33b8d1e1
439 | |/ / / parent: 2:3d9a33b8d1e1
440 | | | | parent: 5:4409d547b708
440 | | | | parent: 5:4409d547b708
441 | | | | user: test
441 | | | | user: test
442 | | | | date: Thu Jan 01 00:00:06 1970 +0000
442 | | | | date: Thu Jan 01 00:00:06 1970 +0000
443 | | | | summary: (6) merge two known; one immediate left, one far left
443 | | | | summary: (6) merge two known; one immediate left, one far left
444 | | | |
444 | | | |
445 | o | | changeset: 5:4409d547b708
445 | o | | changeset: 5:4409d547b708
446 | |\ \ \ parent: 3:27eef8ed80b4
446 | |\ \ \ parent: 3:27eef8ed80b4
447 | | | | | parent: 4:26a8bac39d9f
447 | | | | | parent: 4:26a8bac39d9f
448 | | | | | user: test
448 | | | | | user: test
449 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
449 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
450 | | | | | summary: (5) expand
450 | | | | | summary: (5) expand
451 | | | | |
451 | | | | |
452 | | o | | changeset: 4:26a8bac39d9f
452 | | o | | changeset: 4:26a8bac39d9f
453 | |/|/ / parent: 1:6db2ef61d156
453 | |/|/ / parent: 1:6db2ef61d156
454 | | | | parent: 3:27eef8ed80b4
454 | | | | parent: 3:27eef8ed80b4
455 | | | | user: test
455 | | | | user: test
456 | | | | date: Thu Jan 01 00:00:04 1970 +0000
456 | | | | date: Thu Jan 01 00:00:04 1970 +0000
457 | | | | summary: (4) merge two known; one immediate left, one immediate right
457 | | | | summary: (4) merge two known; one immediate left, one immediate right
458 | | | |
458 | | | |
459 | o | | changeset: 3:27eef8ed80b4
459 | o | | changeset: 3:27eef8ed80b4
460 |/ / / user: test
460 |/ / / user: test
461 | | | date: Thu Jan 01 00:00:03 1970 +0000
461 | | | date: Thu Jan 01 00:00:03 1970 +0000
462 | | | summary: (3) collapse
462 | | | summary: (3) collapse
463 | | |
463 | | |
464 o | | changeset: 2:3d9a33b8d1e1
464 o | | changeset: 2:3d9a33b8d1e1
465 |/ / user: test
465 |/ / user: test
466 | | date: Thu Jan 01 00:00:02 1970 +0000
466 | | date: Thu Jan 01 00:00:02 1970 +0000
467 | | summary: (2) collapse
467 | | summary: (2) collapse
468 | |
468 | |
469 o | changeset: 1:6db2ef61d156
469 o | changeset: 1:6db2ef61d156
470 |/ user: test
470 |/ user: test
471 | date: Thu Jan 01 00:00:01 1970 +0000
471 | date: Thu Jan 01 00:00:01 1970 +0000
472 | summary: (1) collapse
472 | summary: (1) collapse
473 |
473 |
474 o changeset: 0:e6eb3150255d
474 o changeset: 0:e6eb3150255d
475 user: test
475 user: test
476 date: Thu Jan 01 00:00:00 1970 +0000
476 date: Thu Jan 01 00:00:00 1970 +0000
477 summary: (0) root
477 summary: (0) root
478
478
479
479
480 File glog:
480 File glog:
481 $ hg log -G a
481 $ hg log -G a
482 @ changeset: 34:fea3ac5810e0
482 @ changeset: 34:fea3ac5810e0
483 | tag: tip
483 | tag: tip
484 | parent: 32:d06dffa21a31
484 | parent: 32:d06dffa21a31
485 | user: test
485 | user: test
486 | date: Thu Jan 01 00:00:34 1970 +0000
486 | date: Thu Jan 01 00:00:34 1970 +0000
487 | summary: (34) head
487 | summary: (34) head
488 |
488 |
489 | o changeset: 33:68608f5145f9
489 | o changeset: 33:68608f5145f9
490 | | parent: 18:1aa84d96232a
490 | | parent: 18:1aa84d96232a
491 | | user: test
491 | | user: test
492 | | date: Thu Jan 01 00:00:33 1970 +0000
492 | | date: Thu Jan 01 00:00:33 1970 +0000
493 | | summary: (33) head
493 | | summary: (33) head
494 | |
494 | |
495 o | changeset: 32:d06dffa21a31
495 o | changeset: 32:d06dffa21a31
496 |\ \ parent: 27:886ed638191b
496 |\ \ parent: 27:886ed638191b
497 | | | parent: 31:621d83e11f67
497 | | | parent: 31:621d83e11f67
498 | | | user: test
498 | | | user: test
499 | | | date: Thu Jan 01 00:00:32 1970 +0000
499 | | | date: Thu Jan 01 00:00:32 1970 +0000
500 | | | summary: (32) expand
500 | | | summary: (32) expand
501 | | |
501 | | |
502 | o | changeset: 31:621d83e11f67
502 | o | changeset: 31:621d83e11f67
503 | |\ \ parent: 21:d42a756af44d
503 | |\ \ parent: 21:d42a756af44d
504 | | | | parent: 30:6e11cd4b648f
504 | | | | parent: 30:6e11cd4b648f
505 | | | | user: test
505 | | | | user: test
506 | | | | date: Thu Jan 01 00:00:31 1970 +0000
506 | | | | date: Thu Jan 01 00:00:31 1970 +0000
507 | | | | summary: (31) expand
507 | | | | summary: (31) expand
508 | | | |
508 | | | |
509 | | o | changeset: 30:6e11cd4b648f
509 | | o | changeset: 30:6e11cd4b648f
510 | | |\ \ parent: 28:44ecd0b9ae99
510 | | |\ \ parent: 28:44ecd0b9ae99
511 | | | | | parent: 29:cd9bb2be7593
511 | | | | | parent: 29:cd9bb2be7593
512 | | | | | user: test
512 | | | | | user: test
513 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
513 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
514 | | | | | summary: (30) expand
514 | | | | | summary: (30) expand
515 | | | | |
515 | | | | |
516 | | | o | changeset: 29:cd9bb2be7593
516 | | | o | changeset: 29:cd9bb2be7593
517 | | | | | parent: 0:e6eb3150255d
517 | | | | | parent: 0:e6eb3150255d
518 | | | | | user: test
518 | | | | | user: test
519 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
519 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
520 | | | | | summary: (29) regular commit
520 | | | | | summary: (29) regular commit
521 | | | | |
521 | | | | |
522 | | o | | changeset: 28:44ecd0b9ae99
522 | | o | | changeset: 28:44ecd0b9ae99
523 | | |\ \ \ parent: 1:6db2ef61d156
523 | | |\ \ \ parent: 1:6db2ef61d156
524 | | | | | | parent: 26:7f25b6c2f0b9
524 | | | | | | parent: 26:7f25b6c2f0b9
525 | | | | | | user: test
525 | | | | | | user: test
526 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
526 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
527 | | | | | | summary: (28) merge zero known
527 | | | | | | summary: (28) merge zero known
528 | | | | | |
528 | | | | | |
529 o | | | | | changeset: 27:886ed638191b
529 o | | | | | changeset: 27:886ed638191b
530 |/ / / / / parent: 21:d42a756af44d
530 |/ / / / / parent: 21:d42a756af44d
531 | | | | | user: test
531 | | | | | user: test
532 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
532 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
533 | | | | | summary: (27) collapse
533 | | | | | summary: (27) collapse
534 | | | | |
534 | | | | |
535 | | o---+ changeset: 26:7f25b6c2f0b9
535 | | o---+ changeset: 26:7f25b6c2f0b9
536 | | | | | parent: 18:1aa84d96232a
536 | | | | | parent: 18:1aa84d96232a
537 | | | | | parent: 25:91da8ed57247
537 | | | | | parent: 25:91da8ed57247
538 | | | | | user: test
538 | | | | | user: test
539 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
539 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
540 | | | | | summary: (26) merge one known; far right
540 | | | | | summary: (26) merge one known; far right
541 | | | | |
541 | | | | |
542 +---o | | changeset: 25:91da8ed57247
542 +---o | | changeset: 25:91da8ed57247
543 | | | | | parent: 21:d42a756af44d
543 | | | | | parent: 21:d42a756af44d
544 | | | | | parent: 24:a9c19a3d96b7
544 | | | | | parent: 24:a9c19a3d96b7
545 | | | | | user: test
545 | | | | | user: test
546 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
546 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
547 | | | | | summary: (25) merge one known; far left
547 | | | | | summary: (25) merge one known; far left
548 | | | | |
548 | | | | |
549 | | o | | changeset: 24:a9c19a3d96b7
549 | | o | | changeset: 24:a9c19a3d96b7
550 | | |\| | parent: 0:e6eb3150255d
550 | | |\| | parent: 0:e6eb3150255d
551 | | | | | parent: 23:a01cddf0766d
551 | | | | | parent: 23:a01cddf0766d
552 | | | | | user: test
552 | | | | | user: test
553 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
553 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
554 | | | | | summary: (24) merge one known; immediate right
554 | | | | | summary: (24) merge one known; immediate right
555 | | | | |
555 | | | | |
556 | | o | | changeset: 23:a01cddf0766d
556 | | o | | changeset: 23:a01cddf0766d
557 | |/| | | parent: 1:6db2ef61d156
557 | |/| | | parent: 1:6db2ef61d156
558 | | | | | parent: 22:e0d9cccacb5d
558 | | | | | parent: 22:e0d9cccacb5d
559 | | | | | user: test
559 | | | | | user: test
560 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
560 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
561 | | | | | summary: (23) merge one known; immediate left
561 | | | | | summary: (23) merge one known; immediate left
562 | | | | |
562 | | | | |
563 +---o---+ changeset: 22:e0d9cccacb5d
563 +---o---+ changeset: 22:e0d9cccacb5d
564 | | | | parent: 18:1aa84d96232a
564 | | | | parent: 18:1aa84d96232a
565 | | / / parent: 21:d42a756af44d
565 | | / / parent: 21:d42a756af44d
566 | | | | user: test
566 | | | | user: test
567 | | | | date: Thu Jan 01 00:00:22 1970 +0000
567 | | | | date: Thu Jan 01 00:00:22 1970 +0000
568 | | | | summary: (22) merge two known; one far left, one far right
568 | | | | summary: (22) merge two known; one far left, one far right
569 | | | |
569 | | | |
570 o | | | changeset: 21:d42a756af44d
570 o | | | changeset: 21:d42a756af44d
571 |\ \ \ \ parent: 19:31ddc2c1573b
571 |\ \ \ \ parent: 19:31ddc2c1573b
572 | | | | | parent: 20:d30ed6450e32
572 | | | | | parent: 20:d30ed6450e32
573 | | | | | user: test
573 | | | | | user: test
574 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
574 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
575 | | | | | summary: (21) expand
575 | | | | | summary: (21) expand
576 | | | | |
576 | | | | |
577 | o---+-+ changeset: 20:d30ed6450e32
577 | o---+-+ changeset: 20:d30ed6450e32
578 | | | | parent: 0:e6eb3150255d
578 | | | | parent: 0:e6eb3150255d
579 | / / / parent: 18:1aa84d96232a
579 | / / / parent: 18:1aa84d96232a
580 | | | | user: test
580 | | | | user: test
581 | | | | date: Thu Jan 01 00:00:20 1970 +0000
581 | | | | date: Thu Jan 01 00:00:20 1970 +0000
582 | | | | summary: (20) merge two known; two far right
582 | | | | summary: (20) merge two known; two far right
583 | | | |
583 | | | |
584 o | | | changeset: 19:31ddc2c1573b
584 o | | | changeset: 19:31ddc2c1573b
585 |\ \ \ \ parent: 15:1dda3f72782d
585 |\ \ \ \ parent: 15:1dda3f72782d
586 | | | | | parent: 17:44765d7c06e0
586 | | | | | parent: 17:44765d7c06e0
587 | | | | | user: test
587 | | | | | user: test
588 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
588 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
589 | | | | | summary: (19) expand
589 | | | | | summary: (19) expand
590 | | | | |
590 | | | | |
591 +---+---o changeset: 18:1aa84d96232a
591 +---+---o changeset: 18:1aa84d96232a
592 | | | | parent: 1:6db2ef61d156
592 | | | | parent: 1:6db2ef61d156
593 | | | | parent: 15:1dda3f72782d
593 | | | | parent: 15:1dda3f72782d
594 | | | | user: test
594 | | | | user: test
595 | | | | date: Thu Jan 01 00:00:18 1970 +0000
595 | | | | date: Thu Jan 01 00:00:18 1970 +0000
596 | | | | summary: (18) merge two known; two far left
596 | | | | summary: (18) merge two known; two far left
597 | | | |
597 | | | |
598 | o | | changeset: 17:44765d7c06e0
598 | o | | changeset: 17:44765d7c06e0
599 | |\ \ \ parent: 12:86b91144a6e9
599 | |\ \ \ parent: 12:86b91144a6e9
600 | | | | | parent: 16:3677d192927d
600 | | | | | parent: 16:3677d192927d
601 | | | | | user: test
601 | | | | | user: test
602 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
602 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
603 | | | | | summary: (17) expand
603 | | | | | summary: (17) expand
604 | | | | |
604 | | | | |
605 | | o---+ changeset: 16:3677d192927d
605 | | o---+ changeset: 16:3677d192927d
606 | | | | | parent: 0:e6eb3150255d
606 | | | | | parent: 0:e6eb3150255d
607 | | |/ / parent: 1:6db2ef61d156
607 | | |/ / parent: 1:6db2ef61d156
608 | | | | user: test
608 | | | | user: test
609 | | | | date: Thu Jan 01 00:00:16 1970 +0000
609 | | | | date: Thu Jan 01 00:00:16 1970 +0000
610 | | | | summary: (16) merge two known; one immediate right, one near right
610 | | | | summary: (16) merge two known; one immediate right, one near right
611 | | | |
611 | | | |
612 o | | | changeset: 15:1dda3f72782d
612 o | | | changeset: 15:1dda3f72782d
613 |\ \ \ \ parent: 13:22d8966a97e3
613 |\ \ \ \ parent: 13:22d8966a97e3
614 | | | | | parent: 14:8eac370358ef
614 | | | | | parent: 14:8eac370358ef
615 | | | | | user: test
615 | | | | | user: test
616 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
616 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
617 | | | | | summary: (15) expand
617 | | | | | summary: (15) expand
618 | | | | |
618 | | | | |
619 | o-----+ changeset: 14:8eac370358ef
619 | o-----+ changeset: 14:8eac370358ef
620 | | | | | parent: 0:e6eb3150255d
620 | | | | | parent: 0:e6eb3150255d
621 | |/ / / parent: 12:86b91144a6e9
621 | |/ / / parent: 12:86b91144a6e9
622 | | | | user: test
622 | | | | user: test
623 | | | | date: Thu Jan 01 00:00:14 1970 +0000
623 | | | | date: Thu Jan 01 00:00:14 1970 +0000
624 | | | | summary: (14) merge two known; one immediate right, one far right
624 | | | | summary: (14) merge two known; one immediate right, one far right
625 | | | |
625 | | | |
626 o | | | changeset: 13:22d8966a97e3
626 o | | | changeset: 13:22d8966a97e3
627 |\ \ \ \ parent: 9:7010c0af0a35
627 |\ \ \ \ parent: 9:7010c0af0a35
628 | | | | | parent: 11:832d76e6bdf2
628 | | | | | parent: 11:832d76e6bdf2
629 | | | | | user: test
629 | | | | | user: test
630 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
630 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
631 | | | | | summary: (13) expand
631 | | | | | summary: (13) expand
632 | | | | |
632 | | | | |
633 +---o | | changeset: 12:86b91144a6e9
633 +---o | | changeset: 12:86b91144a6e9
634 | | |/ / parent: 1:6db2ef61d156
634 | | |/ / parent: 1:6db2ef61d156
635 | | | | parent: 9:7010c0af0a35
635 | | | | parent: 9:7010c0af0a35
636 | | | | user: test
636 | | | | user: test
637 | | | | date: Thu Jan 01 00:00:12 1970 +0000
637 | | | | date: Thu Jan 01 00:00:12 1970 +0000
638 | | | | summary: (12) merge two known; one immediate right, one far left
638 | | | | summary: (12) merge two known; one immediate right, one far left
639 | | | |
639 | | | |
640 | o | | changeset: 11:832d76e6bdf2
640 | o | | changeset: 11:832d76e6bdf2
641 | |\ \ \ parent: 6:b105a072e251
641 | |\ \ \ parent: 6:b105a072e251
642 | | | | | parent: 10:74c64d036d72
642 | | | | | parent: 10:74c64d036d72
643 | | | | | user: test
643 | | | | | user: test
644 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
644 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
645 | | | | | summary: (11) expand
645 | | | | | summary: (11) expand
646 | | | | |
646 | | | | |
647 | | o---+ changeset: 10:74c64d036d72
647 | | o---+ changeset: 10:74c64d036d72
648 | | | | | parent: 0:e6eb3150255d
648 | | | | | parent: 0:e6eb3150255d
649 | |/ / / parent: 6:b105a072e251
649 | |/ / / parent: 6:b105a072e251
650 | | | | user: test
650 | | | | user: test
651 | | | | date: Thu Jan 01 00:00:10 1970 +0000
651 | | | | date: Thu Jan 01 00:00:10 1970 +0000
652 | | | | summary: (10) merge two known; one immediate left, one near right
652 | | | | summary: (10) merge two known; one immediate left, one near right
653 | | | |
653 | | | |
654 o | | | changeset: 9:7010c0af0a35
654 o | | | changeset: 9:7010c0af0a35
655 |\ \ \ \ parent: 7:b632bb1b1224
655 |\ \ \ \ parent: 7:b632bb1b1224
656 | | | | | parent: 8:7a0b11f71937
656 | | | | | parent: 8:7a0b11f71937
657 | | | | | user: test
657 | | | | | user: test
658 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
658 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
659 | | | | | summary: (9) expand
659 | | | | | summary: (9) expand
660 | | | | |
660 | | | | |
661 | o-----+ changeset: 8:7a0b11f71937
661 | o-----+ changeset: 8:7a0b11f71937
662 | | | | | parent: 0:e6eb3150255d
662 | | | | | parent: 0:e6eb3150255d
663 |/ / / / parent: 7:b632bb1b1224
663 |/ / / / parent: 7:b632bb1b1224
664 | | | | user: test
664 | | | | user: test
665 | | | | date: Thu Jan 01 00:00:08 1970 +0000
665 | | | | date: Thu Jan 01 00:00:08 1970 +0000
666 | | | | summary: (8) merge two known; one immediate left, one far right
666 | | | | summary: (8) merge two known; one immediate left, one far right
667 | | | |
667 | | | |
668 o | | | changeset: 7:b632bb1b1224
668 o | | | changeset: 7:b632bb1b1224
669 |\ \ \ \ parent: 2:3d9a33b8d1e1
669 |\ \ \ \ parent: 2:3d9a33b8d1e1
670 | | | | | parent: 5:4409d547b708
670 | | | | | parent: 5:4409d547b708
671 | | | | | user: test
671 | | | | | user: test
672 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
672 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
673 | | | | | summary: (7) expand
673 | | | | | summary: (7) expand
674 | | | | |
674 | | | | |
675 +---o | | changeset: 6:b105a072e251
675 +---o | | changeset: 6:b105a072e251
676 | |/ / / parent: 2:3d9a33b8d1e1
676 | |/ / / parent: 2:3d9a33b8d1e1
677 | | | | parent: 5:4409d547b708
677 | | | | parent: 5:4409d547b708
678 | | | | user: test
678 | | | | user: test
679 | | | | date: Thu Jan 01 00:00:06 1970 +0000
679 | | | | date: Thu Jan 01 00:00:06 1970 +0000
680 | | | | summary: (6) merge two known; one immediate left, one far left
680 | | | | summary: (6) merge two known; one immediate left, one far left
681 | | | |
681 | | | |
682 | o | | changeset: 5:4409d547b708
682 | o | | changeset: 5:4409d547b708
683 | |\ \ \ parent: 3:27eef8ed80b4
683 | |\ \ \ parent: 3:27eef8ed80b4
684 | | | | | parent: 4:26a8bac39d9f
684 | | | | | parent: 4:26a8bac39d9f
685 | | | | | user: test
685 | | | | | user: test
686 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
686 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
687 | | | | | summary: (5) expand
687 | | | | | summary: (5) expand
688 | | | | |
688 | | | | |
689 | | o | | changeset: 4:26a8bac39d9f
689 | | o | | changeset: 4:26a8bac39d9f
690 | |/|/ / parent: 1:6db2ef61d156
690 | |/|/ / parent: 1:6db2ef61d156
691 | | | | parent: 3:27eef8ed80b4
691 | | | | parent: 3:27eef8ed80b4
692 | | | | user: test
692 | | | | user: test
693 | | | | date: Thu Jan 01 00:00:04 1970 +0000
693 | | | | date: Thu Jan 01 00:00:04 1970 +0000
694 | | | | summary: (4) merge two known; one immediate left, one immediate right
694 | | | | summary: (4) merge two known; one immediate left, one immediate right
695 | | | |
695 | | | |
696 | o | | changeset: 3:27eef8ed80b4
696 | o | | changeset: 3:27eef8ed80b4
697 |/ / / user: test
697 |/ / / user: test
698 | | | date: Thu Jan 01 00:00:03 1970 +0000
698 | | | date: Thu Jan 01 00:00:03 1970 +0000
699 | | | summary: (3) collapse
699 | | | summary: (3) collapse
700 | | |
700 | | |
701 o | | changeset: 2:3d9a33b8d1e1
701 o | | changeset: 2:3d9a33b8d1e1
702 |/ / user: test
702 |/ / user: test
703 | | date: Thu Jan 01 00:00:02 1970 +0000
703 | | date: Thu Jan 01 00:00:02 1970 +0000
704 | | summary: (2) collapse
704 | | summary: (2) collapse
705 | |
705 | |
706 o | changeset: 1:6db2ef61d156
706 o | changeset: 1:6db2ef61d156
707 |/ user: test
707 |/ user: test
708 | date: Thu Jan 01 00:00:01 1970 +0000
708 | date: Thu Jan 01 00:00:01 1970 +0000
709 | summary: (1) collapse
709 | summary: (1) collapse
710 |
710 |
711 o changeset: 0:e6eb3150255d
711 o changeset: 0:e6eb3150255d
712 user: test
712 user: test
713 date: Thu Jan 01 00:00:00 1970 +0000
713 date: Thu Jan 01 00:00:00 1970 +0000
714 summary: (0) root
714 summary: (0) root
715
715
716
716
717 File glog per revset:
717 File glog per revset:
718
718
719 $ hg log -G -r 'file("a")'
719 $ hg log -G -r 'file("a")'
720 @ changeset: 34:fea3ac5810e0
720 @ changeset: 34:fea3ac5810e0
721 | tag: tip
721 | tag: tip
722 | parent: 32:d06dffa21a31
722 | parent: 32:d06dffa21a31
723 | user: test
723 | user: test
724 | date: Thu Jan 01 00:00:34 1970 +0000
724 | date: Thu Jan 01 00:00:34 1970 +0000
725 | summary: (34) head
725 | summary: (34) head
726 |
726 |
727 | o changeset: 33:68608f5145f9
727 | o changeset: 33:68608f5145f9
728 | | parent: 18:1aa84d96232a
728 | | parent: 18:1aa84d96232a
729 | | user: test
729 | | user: test
730 | | date: Thu Jan 01 00:00:33 1970 +0000
730 | | date: Thu Jan 01 00:00:33 1970 +0000
731 | | summary: (33) head
731 | | summary: (33) head
732 | |
732 | |
733 o | changeset: 32:d06dffa21a31
733 o | changeset: 32:d06dffa21a31
734 |\ \ parent: 27:886ed638191b
734 |\ \ parent: 27:886ed638191b
735 | | | parent: 31:621d83e11f67
735 | | | parent: 31:621d83e11f67
736 | | | user: test
736 | | | user: test
737 | | | date: Thu Jan 01 00:00:32 1970 +0000
737 | | | date: Thu Jan 01 00:00:32 1970 +0000
738 | | | summary: (32) expand
738 | | | summary: (32) expand
739 | | |
739 | | |
740 | o | changeset: 31:621d83e11f67
740 | o | changeset: 31:621d83e11f67
741 | |\ \ parent: 21:d42a756af44d
741 | |\ \ parent: 21:d42a756af44d
742 | | | | parent: 30:6e11cd4b648f
742 | | | | parent: 30:6e11cd4b648f
743 | | | | user: test
743 | | | | user: test
744 | | | | date: Thu Jan 01 00:00:31 1970 +0000
744 | | | | date: Thu Jan 01 00:00:31 1970 +0000
745 | | | | summary: (31) expand
745 | | | | summary: (31) expand
746 | | | |
746 | | | |
747 | | o | changeset: 30:6e11cd4b648f
747 | | o | changeset: 30:6e11cd4b648f
748 | | |\ \ parent: 28:44ecd0b9ae99
748 | | |\ \ parent: 28:44ecd0b9ae99
749 | | | | | parent: 29:cd9bb2be7593
749 | | | | | parent: 29:cd9bb2be7593
750 | | | | | user: test
750 | | | | | user: test
751 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
751 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
752 | | | | | summary: (30) expand
752 | | | | | summary: (30) expand
753 | | | | |
753 | | | | |
754 | | | o | changeset: 29:cd9bb2be7593
754 | | | o | changeset: 29:cd9bb2be7593
755 | | | | | parent: 0:e6eb3150255d
755 | | | | | parent: 0:e6eb3150255d
756 | | | | | user: test
756 | | | | | user: test
757 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
757 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
758 | | | | | summary: (29) regular commit
758 | | | | | summary: (29) regular commit
759 | | | | |
759 | | | | |
760 | | o | | changeset: 28:44ecd0b9ae99
760 | | o | | changeset: 28:44ecd0b9ae99
761 | | |\ \ \ parent: 1:6db2ef61d156
761 | | |\ \ \ parent: 1:6db2ef61d156
762 | | | | | | parent: 26:7f25b6c2f0b9
762 | | | | | | parent: 26:7f25b6c2f0b9
763 | | | | | | user: test
763 | | | | | | user: test
764 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
764 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
765 | | | | | | summary: (28) merge zero known
765 | | | | | | summary: (28) merge zero known
766 | | | | | |
766 | | | | | |
767 o | | | | | changeset: 27:886ed638191b
767 o | | | | | changeset: 27:886ed638191b
768 |/ / / / / parent: 21:d42a756af44d
768 |/ / / / / parent: 21:d42a756af44d
769 | | | | | user: test
769 | | | | | user: test
770 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
770 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
771 | | | | | summary: (27) collapse
771 | | | | | summary: (27) collapse
772 | | | | |
772 | | | | |
773 | | o---+ changeset: 26:7f25b6c2f0b9
773 | | o---+ changeset: 26:7f25b6c2f0b9
774 | | | | | parent: 18:1aa84d96232a
774 | | | | | parent: 18:1aa84d96232a
775 | | | | | parent: 25:91da8ed57247
775 | | | | | parent: 25:91da8ed57247
776 | | | | | user: test
776 | | | | | user: test
777 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
777 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
778 | | | | | summary: (26) merge one known; far right
778 | | | | | summary: (26) merge one known; far right
779 | | | | |
779 | | | | |
780 +---o | | changeset: 25:91da8ed57247
780 +---o | | changeset: 25:91da8ed57247
781 | | | | | parent: 21:d42a756af44d
781 | | | | | parent: 21:d42a756af44d
782 | | | | | parent: 24:a9c19a3d96b7
782 | | | | | parent: 24:a9c19a3d96b7
783 | | | | | user: test
783 | | | | | user: test
784 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
784 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
785 | | | | | summary: (25) merge one known; far left
785 | | | | | summary: (25) merge one known; far left
786 | | | | |
786 | | | | |
787 | | o | | changeset: 24:a9c19a3d96b7
787 | | o | | changeset: 24:a9c19a3d96b7
788 | | |\| | parent: 0:e6eb3150255d
788 | | |\| | parent: 0:e6eb3150255d
789 | | | | | parent: 23:a01cddf0766d
789 | | | | | parent: 23:a01cddf0766d
790 | | | | | user: test
790 | | | | | user: test
791 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
791 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
792 | | | | | summary: (24) merge one known; immediate right
792 | | | | | summary: (24) merge one known; immediate right
793 | | | | |
793 | | | | |
794 | | o | | changeset: 23:a01cddf0766d
794 | | o | | changeset: 23:a01cddf0766d
795 | |/| | | parent: 1:6db2ef61d156
795 | |/| | | parent: 1:6db2ef61d156
796 | | | | | parent: 22:e0d9cccacb5d
796 | | | | | parent: 22:e0d9cccacb5d
797 | | | | | user: test
797 | | | | | user: test
798 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
798 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
799 | | | | | summary: (23) merge one known; immediate left
799 | | | | | summary: (23) merge one known; immediate left
800 | | | | |
800 | | | | |
801 +---o---+ changeset: 22:e0d9cccacb5d
801 +---o---+ changeset: 22:e0d9cccacb5d
802 | | | | parent: 18:1aa84d96232a
802 | | | | parent: 18:1aa84d96232a
803 | | / / parent: 21:d42a756af44d
803 | | / / parent: 21:d42a756af44d
804 | | | | user: test
804 | | | | user: test
805 | | | | date: Thu Jan 01 00:00:22 1970 +0000
805 | | | | date: Thu Jan 01 00:00:22 1970 +0000
806 | | | | summary: (22) merge two known; one far left, one far right
806 | | | | summary: (22) merge two known; one far left, one far right
807 | | | |
807 | | | |
808 o | | | changeset: 21:d42a756af44d
808 o | | | changeset: 21:d42a756af44d
809 |\ \ \ \ parent: 19:31ddc2c1573b
809 |\ \ \ \ parent: 19:31ddc2c1573b
810 | | | | | parent: 20:d30ed6450e32
810 | | | | | parent: 20:d30ed6450e32
811 | | | | | user: test
811 | | | | | user: test
812 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
812 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
813 | | | | | summary: (21) expand
813 | | | | | summary: (21) expand
814 | | | | |
814 | | | | |
815 | o---+-+ changeset: 20:d30ed6450e32
815 | o---+-+ changeset: 20:d30ed6450e32
816 | | | | parent: 0:e6eb3150255d
816 | | | | parent: 0:e6eb3150255d
817 | / / / parent: 18:1aa84d96232a
817 | / / / parent: 18:1aa84d96232a
818 | | | | user: test
818 | | | | user: test
819 | | | | date: Thu Jan 01 00:00:20 1970 +0000
819 | | | | date: Thu Jan 01 00:00:20 1970 +0000
820 | | | | summary: (20) merge two known; two far right
820 | | | | summary: (20) merge two known; two far right
821 | | | |
821 | | | |
822 o | | | changeset: 19:31ddc2c1573b
822 o | | | changeset: 19:31ddc2c1573b
823 |\ \ \ \ parent: 15:1dda3f72782d
823 |\ \ \ \ parent: 15:1dda3f72782d
824 | | | | | parent: 17:44765d7c06e0
824 | | | | | parent: 17:44765d7c06e0
825 | | | | | user: test
825 | | | | | user: test
826 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
826 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
827 | | | | | summary: (19) expand
827 | | | | | summary: (19) expand
828 | | | | |
828 | | | | |
829 +---+---o changeset: 18:1aa84d96232a
829 +---+---o changeset: 18:1aa84d96232a
830 | | | | parent: 1:6db2ef61d156
830 | | | | parent: 1:6db2ef61d156
831 | | | | parent: 15:1dda3f72782d
831 | | | | parent: 15:1dda3f72782d
832 | | | | user: test
832 | | | | user: test
833 | | | | date: Thu Jan 01 00:00:18 1970 +0000
833 | | | | date: Thu Jan 01 00:00:18 1970 +0000
834 | | | | summary: (18) merge two known; two far left
834 | | | | summary: (18) merge two known; two far left
835 | | | |
835 | | | |
836 | o | | changeset: 17:44765d7c06e0
836 | o | | changeset: 17:44765d7c06e0
837 | |\ \ \ parent: 12:86b91144a6e9
837 | |\ \ \ parent: 12:86b91144a6e9
838 | | | | | parent: 16:3677d192927d
838 | | | | | parent: 16:3677d192927d
839 | | | | | user: test
839 | | | | | user: test
840 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
840 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
841 | | | | | summary: (17) expand
841 | | | | | summary: (17) expand
842 | | | | |
842 | | | | |
843 | | o---+ changeset: 16:3677d192927d
843 | | o---+ changeset: 16:3677d192927d
844 | | | | | parent: 0:e6eb3150255d
844 | | | | | parent: 0:e6eb3150255d
845 | | |/ / parent: 1:6db2ef61d156
845 | | |/ / parent: 1:6db2ef61d156
846 | | | | user: test
846 | | | | user: test
847 | | | | date: Thu Jan 01 00:00:16 1970 +0000
847 | | | | date: Thu Jan 01 00:00:16 1970 +0000
848 | | | | summary: (16) merge two known; one immediate right, one near right
848 | | | | summary: (16) merge two known; one immediate right, one near right
849 | | | |
849 | | | |
850 o | | | changeset: 15:1dda3f72782d
850 o | | | changeset: 15:1dda3f72782d
851 |\ \ \ \ parent: 13:22d8966a97e3
851 |\ \ \ \ parent: 13:22d8966a97e3
852 | | | | | parent: 14:8eac370358ef
852 | | | | | parent: 14:8eac370358ef
853 | | | | | user: test
853 | | | | | user: test
854 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
854 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
855 | | | | | summary: (15) expand
855 | | | | | summary: (15) expand
856 | | | | |
856 | | | | |
857 | o-----+ changeset: 14:8eac370358ef
857 | o-----+ changeset: 14:8eac370358ef
858 | | | | | parent: 0:e6eb3150255d
858 | | | | | parent: 0:e6eb3150255d
859 | |/ / / parent: 12:86b91144a6e9
859 | |/ / / parent: 12:86b91144a6e9
860 | | | | user: test
860 | | | | user: test
861 | | | | date: Thu Jan 01 00:00:14 1970 +0000
861 | | | | date: Thu Jan 01 00:00:14 1970 +0000
862 | | | | summary: (14) merge two known; one immediate right, one far right
862 | | | | summary: (14) merge two known; one immediate right, one far right
863 | | | |
863 | | | |
864 o | | | changeset: 13:22d8966a97e3
864 o | | | changeset: 13:22d8966a97e3
865 |\ \ \ \ parent: 9:7010c0af0a35
865 |\ \ \ \ parent: 9:7010c0af0a35
866 | | | | | parent: 11:832d76e6bdf2
866 | | | | | parent: 11:832d76e6bdf2
867 | | | | | user: test
867 | | | | | user: test
868 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
868 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
869 | | | | | summary: (13) expand
869 | | | | | summary: (13) expand
870 | | | | |
870 | | | | |
871 +---o | | changeset: 12:86b91144a6e9
871 +---o | | changeset: 12:86b91144a6e9
872 | | |/ / parent: 1:6db2ef61d156
872 | | |/ / parent: 1:6db2ef61d156
873 | | | | parent: 9:7010c0af0a35
873 | | | | parent: 9:7010c0af0a35
874 | | | | user: test
874 | | | | user: test
875 | | | | date: Thu Jan 01 00:00:12 1970 +0000
875 | | | | date: Thu Jan 01 00:00:12 1970 +0000
876 | | | | summary: (12) merge two known; one immediate right, one far left
876 | | | | summary: (12) merge two known; one immediate right, one far left
877 | | | |
877 | | | |
878 | o | | changeset: 11:832d76e6bdf2
878 | o | | changeset: 11:832d76e6bdf2
879 | |\ \ \ parent: 6:b105a072e251
879 | |\ \ \ parent: 6:b105a072e251
880 | | | | | parent: 10:74c64d036d72
880 | | | | | parent: 10:74c64d036d72
881 | | | | | user: test
881 | | | | | user: test
882 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
882 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
883 | | | | | summary: (11) expand
883 | | | | | summary: (11) expand
884 | | | | |
884 | | | | |
885 | | o---+ changeset: 10:74c64d036d72
885 | | o---+ changeset: 10:74c64d036d72
886 | | | | | parent: 0:e6eb3150255d
886 | | | | | parent: 0:e6eb3150255d
887 | |/ / / parent: 6:b105a072e251
887 | |/ / / parent: 6:b105a072e251
888 | | | | user: test
888 | | | | user: test
889 | | | | date: Thu Jan 01 00:00:10 1970 +0000
889 | | | | date: Thu Jan 01 00:00:10 1970 +0000
890 | | | | summary: (10) merge two known; one immediate left, one near right
890 | | | | summary: (10) merge two known; one immediate left, one near right
891 | | | |
891 | | | |
892 o | | | changeset: 9:7010c0af0a35
892 o | | | changeset: 9:7010c0af0a35
893 |\ \ \ \ parent: 7:b632bb1b1224
893 |\ \ \ \ parent: 7:b632bb1b1224
894 | | | | | parent: 8:7a0b11f71937
894 | | | | | parent: 8:7a0b11f71937
895 | | | | | user: test
895 | | | | | user: test
896 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
896 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
897 | | | | | summary: (9) expand
897 | | | | | summary: (9) expand
898 | | | | |
898 | | | | |
899 | o-----+ changeset: 8:7a0b11f71937
899 | o-----+ changeset: 8:7a0b11f71937
900 | | | | | parent: 0:e6eb3150255d
900 | | | | | parent: 0:e6eb3150255d
901 |/ / / / parent: 7:b632bb1b1224
901 |/ / / / parent: 7:b632bb1b1224
902 | | | | user: test
902 | | | | user: test
903 | | | | date: Thu Jan 01 00:00:08 1970 +0000
903 | | | | date: Thu Jan 01 00:00:08 1970 +0000
904 | | | | summary: (8) merge two known; one immediate left, one far right
904 | | | | summary: (8) merge two known; one immediate left, one far right
905 | | | |
905 | | | |
906 o | | | changeset: 7:b632bb1b1224
906 o | | | changeset: 7:b632bb1b1224
907 |\ \ \ \ parent: 2:3d9a33b8d1e1
907 |\ \ \ \ parent: 2:3d9a33b8d1e1
908 | | | | | parent: 5:4409d547b708
908 | | | | | parent: 5:4409d547b708
909 | | | | | user: test
909 | | | | | user: test
910 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
910 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
911 | | | | | summary: (7) expand
911 | | | | | summary: (7) expand
912 | | | | |
912 | | | | |
913 +---o | | changeset: 6:b105a072e251
913 +---o | | changeset: 6:b105a072e251
914 | |/ / / parent: 2:3d9a33b8d1e1
914 | |/ / / parent: 2:3d9a33b8d1e1
915 | | | | parent: 5:4409d547b708
915 | | | | parent: 5:4409d547b708
916 | | | | user: test
916 | | | | user: test
917 | | | | date: Thu Jan 01 00:00:06 1970 +0000
917 | | | | date: Thu Jan 01 00:00:06 1970 +0000
918 | | | | summary: (6) merge two known; one immediate left, one far left
918 | | | | summary: (6) merge two known; one immediate left, one far left
919 | | | |
919 | | | |
920 | o | | changeset: 5:4409d547b708
920 | o | | changeset: 5:4409d547b708
921 | |\ \ \ parent: 3:27eef8ed80b4
921 | |\ \ \ parent: 3:27eef8ed80b4
922 | | | | | parent: 4:26a8bac39d9f
922 | | | | | parent: 4:26a8bac39d9f
923 | | | | | user: test
923 | | | | | user: test
924 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
924 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
925 | | | | | summary: (5) expand
925 | | | | | summary: (5) expand
926 | | | | |
926 | | | | |
927 | | o | | changeset: 4:26a8bac39d9f
927 | | o | | changeset: 4:26a8bac39d9f
928 | |/|/ / parent: 1:6db2ef61d156
928 | |/|/ / parent: 1:6db2ef61d156
929 | | | | parent: 3:27eef8ed80b4
929 | | | | parent: 3:27eef8ed80b4
930 | | | | user: test
930 | | | | user: test
931 | | | | date: Thu Jan 01 00:00:04 1970 +0000
931 | | | | date: Thu Jan 01 00:00:04 1970 +0000
932 | | | | summary: (4) merge two known; one immediate left, one immediate right
932 | | | | summary: (4) merge two known; one immediate left, one immediate right
933 | | | |
933 | | | |
934 | o | | changeset: 3:27eef8ed80b4
934 | o | | changeset: 3:27eef8ed80b4
935 |/ / / user: test
935 |/ / / user: test
936 | | | date: Thu Jan 01 00:00:03 1970 +0000
936 | | | date: Thu Jan 01 00:00:03 1970 +0000
937 | | | summary: (3) collapse
937 | | | summary: (3) collapse
938 | | |
938 | | |
939 o | | changeset: 2:3d9a33b8d1e1
939 o | | changeset: 2:3d9a33b8d1e1
940 |/ / user: test
940 |/ / user: test
941 | | date: Thu Jan 01 00:00:02 1970 +0000
941 | | date: Thu Jan 01 00:00:02 1970 +0000
942 | | summary: (2) collapse
942 | | summary: (2) collapse
943 | |
943 | |
944 o | changeset: 1:6db2ef61d156
944 o | changeset: 1:6db2ef61d156
945 |/ user: test
945 |/ user: test
946 | date: Thu Jan 01 00:00:01 1970 +0000
946 | date: Thu Jan 01 00:00:01 1970 +0000
947 | summary: (1) collapse
947 | summary: (1) collapse
948 |
948 |
949 o changeset: 0:e6eb3150255d
949 o changeset: 0:e6eb3150255d
950 user: test
950 user: test
951 date: Thu Jan 01 00:00:00 1970 +0000
951 date: Thu Jan 01 00:00:00 1970 +0000
952 summary: (0) root
952 summary: (0) root
953
953
954
954
955
955
956 File glog per revset (only merges):
956 File glog per revset (only merges):
957
957
958 $ hg log -G -r 'file("a")' -m
958 $ hg log -G -r 'file("a")' -m
959 o changeset: 32:d06dffa21a31
959 o changeset: 32:d06dffa21a31
960 |\ parent: 27:886ed638191b
960 |\ parent: 27:886ed638191b
961 | : parent: 31:621d83e11f67
961 | : parent: 31:621d83e11f67
962 | : user: test
962 | : user: test
963 | : date: Thu Jan 01 00:00:32 1970 +0000
963 | : date: Thu Jan 01 00:00:32 1970 +0000
964 | : summary: (32) expand
964 | : summary: (32) expand
965 | :
965 | :
966 o : changeset: 31:621d83e11f67
966 o : changeset: 31:621d83e11f67
967 |\: parent: 21:d42a756af44d
967 |\: parent: 21:d42a756af44d
968 | : parent: 30:6e11cd4b648f
968 | : parent: 30:6e11cd4b648f
969 | : user: test
969 | : user: test
970 | : date: Thu Jan 01 00:00:31 1970 +0000
970 | : date: Thu Jan 01 00:00:31 1970 +0000
971 | : summary: (31) expand
971 | : summary: (31) expand
972 | :
972 | :
973 o : changeset: 30:6e11cd4b648f
973 o : changeset: 30:6e11cd4b648f
974 |\ \ parent: 28:44ecd0b9ae99
974 |\ \ parent: 28:44ecd0b9ae99
975 | ~ : parent: 29:cd9bb2be7593
975 | ~ : parent: 29:cd9bb2be7593
976 | : user: test
976 | : user: test
977 | : date: Thu Jan 01 00:00:30 1970 +0000
977 | : date: Thu Jan 01 00:00:30 1970 +0000
978 | : summary: (30) expand
978 | : summary: (30) expand
979 | /
979 | /
980 o : changeset: 28:44ecd0b9ae99
980 o : changeset: 28:44ecd0b9ae99
981 |\ \ parent: 1:6db2ef61d156
981 |\ \ parent: 1:6db2ef61d156
982 | ~ : parent: 26:7f25b6c2f0b9
982 | ~ : parent: 26:7f25b6c2f0b9
983 | : user: test
983 | : user: test
984 | : date: Thu Jan 01 00:00:28 1970 +0000
984 | : date: Thu Jan 01 00:00:28 1970 +0000
985 | : summary: (28) merge zero known
985 | : summary: (28) merge zero known
986 | /
986 | /
987 o : changeset: 26:7f25b6c2f0b9
987 o : changeset: 26:7f25b6c2f0b9
988 |\ \ parent: 18:1aa84d96232a
988 |\ \ parent: 18:1aa84d96232a
989 | | : parent: 25:91da8ed57247
989 | | : parent: 25:91da8ed57247
990 | | : user: test
990 | | : user: test
991 | | : date: Thu Jan 01 00:00:26 1970 +0000
991 | | : date: Thu Jan 01 00:00:26 1970 +0000
992 | | : summary: (26) merge one known; far right
992 | | : summary: (26) merge one known; far right
993 | | :
993 | | :
994 | o : changeset: 25:91da8ed57247
994 | o : changeset: 25:91da8ed57247
995 | |\: parent: 21:d42a756af44d
995 | |\: parent: 21:d42a756af44d
996 | | : parent: 24:a9c19a3d96b7
996 | | : parent: 24:a9c19a3d96b7
997 | | : user: test
997 | | : user: test
998 | | : date: Thu Jan 01 00:00:25 1970 +0000
998 | | : date: Thu Jan 01 00:00:25 1970 +0000
999 | | : summary: (25) merge one known; far left
999 | | : summary: (25) merge one known; far left
1000 | | :
1000 | | :
1001 | o : changeset: 24:a9c19a3d96b7
1001 | o : changeset: 24:a9c19a3d96b7
1002 | |\ \ parent: 0:e6eb3150255d
1002 | |\ \ parent: 0:e6eb3150255d
1003 | | ~ : parent: 23:a01cddf0766d
1003 | | ~ : parent: 23:a01cddf0766d
1004 | | : user: test
1004 | | : user: test
1005 | | : date: Thu Jan 01 00:00:24 1970 +0000
1005 | | : date: Thu Jan 01 00:00:24 1970 +0000
1006 | | : summary: (24) merge one known; immediate right
1006 | | : summary: (24) merge one known; immediate right
1007 | | /
1007 | | /
1008 | o : changeset: 23:a01cddf0766d
1008 | o : changeset: 23:a01cddf0766d
1009 | |\ \ parent: 1:6db2ef61d156
1009 | |\ \ parent: 1:6db2ef61d156
1010 | | ~ : parent: 22:e0d9cccacb5d
1010 | | ~ : parent: 22:e0d9cccacb5d
1011 | | : user: test
1011 | | : user: test
1012 | | : date: Thu Jan 01 00:00:23 1970 +0000
1012 | | : date: Thu Jan 01 00:00:23 1970 +0000
1013 | | : summary: (23) merge one known; immediate left
1013 | | : summary: (23) merge one known; immediate left
1014 | | /
1014 | | /
1015 | o : changeset: 22:e0d9cccacb5d
1015 | o : changeset: 22:e0d9cccacb5d
1016 |/:/ parent: 18:1aa84d96232a
1016 |/:/ parent: 18:1aa84d96232a
1017 | : parent: 21:d42a756af44d
1017 | : parent: 21:d42a756af44d
1018 | : user: test
1018 | : user: test
1019 | : date: Thu Jan 01 00:00:22 1970 +0000
1019 | : date: Thu Jan 01 00:00:22 1970 +0000
1020 | : summary: (22) merge two known; one far left, one far right
1020 | : summary: (22) merge two known; one far left, one far right
1021 | :
1021 | :
1022 | o changeset: 21:d42a756af44d
1022 | o changeset: 21:d42a756af44d
1023 | |\ parent: 19:31ddc2c1573b
1023 | |\ parent: 19:31ddc2c1573b
1024 | | | parent: 20:d30ed6450e32
1024 | | | parent: 20:d30ed6450e32
1025 | | | user: test
1025 | | | user: test
1026 | | | date: Thu Jan 01 00:00:21 1970 +0000
1026 | | | date: Thu Jan 01 00:00:21 1970 +0000
1027 | | | summary: (21) expand
1027 | | | summary: (21) expand
1028 | | |
1028 | | |
1029 +---o changeset: 20:d30ed6450e32
1029 +---o changeset: 20:d30ed6450e32
1030 | | | parent: 0:e6eb3150255d
1030 | | | parent: 0:e6eb3150255d
1031 | | ~ parent: 18:1aa84d96232a
1031 | | ~ parent: 18:1aa84d96232a
1032 | | user: test
1032 | | user: test
1033 | | date: Thu Jan 01 00:00:20 1970 +0000
1033 | | date: Thu Jan 01 00:00:20 1970 +0000
1034 | | summary: (20) merge two known; two far right
1034 | | summary: (20) merge two known; two far right
1035 | |
1035 | |
1036 | o changeset: 19:31ddc2c1573b
1036 | o changeset: 19:31ddc2c1573b
1037 | |\ parent: 15:1dda3f72782d
1037 | |\ parent: 15:1dda3f72782d
1038 | | | parent: 17:44765d7c06e0
1038 | | | parent: 17:44765d7c06e0
1039 | | | user: test
1039 | | | user: test
1040 | | | date: Thu Jan 01 00:00:19 1970 +0000
1040 | | | date: Thu Jan 01 00:00:19 1970 +0000
1041 | | | summary: (19) expand
1041 | | | summary: (19) expand
1042 | | |
1042 | | |
1043 o | | changeset: 18:1aa84d96232a
1043 o | | changeset: 18:1aa84d96232a
1044 |\| | parent: 1:6db2ef61d156
1044 |\| | parent: 1:6db2ef61d156
1045 ~ | | parent: 15:1dda3f72782d
1045 ~ | | parent: 15:1dda3f72782d
1046 | | user: test
1046 | | user: test
1047 | | date: Thu Jan 01 00:00:18 1970 +0000
1047 | | date: Thu Jan 01 00:00:18 1970 +0000
1048 | | summary: (18) merge two known; two far left
1048 | | summary: (18) merge two known; two far left
1049 / /
1049 / /
1050 | o changeset: 17:44765d7c06e0
1050 | o changeset: 17:44765d7c06e0
1051 | |\ parent: 12:86b91144a6e9
1051 | |\ parent: 12:86b91144a6e9
1052 | | | parent: 16:3677d192927d
1052 | | | parent: 16:3677d192927d
1053 | | | user: test
1053 | | | user: test
1054 | | | date: Thu Jan 01 00:00:17 1970 +0000
1054 | | | date: Thu Jan 01 00:00:17 1970 +0000
1055 | | | summary: (17) expand
1055 | | | summary: (17) expand
1056 | | |
1056 | | |
1057 | | o changeset: 16:3677d192927d
1057 | | o changeset: 16:3677d192927d
1058 | | |\ parent: 0:e6eb3150255d
1058 | | |\ parent: 0:e6eb3150255d
1059 | | ~ ~ parent: 1:6db2ef61d156
1059 | | ~ ~ parent: 1:6db2ef61d156
1060 | | user: test
1060 | | user: test
1061 | | date: Thu Jan 01 00:00:16 1970 +0000
1061 | | date: Thu Jan 01 00:00:16 1970 +0000
1062 | | summary: (16) merge two known; one immediate right, one near right
1062 | | summary: (16) merge two known; one immediate right, one near right
1063 | |
1063 | |
1064 o | changeset: 15:1dda3f72782d
1064 o | changeset: 15:1dda3f72782d
1065 |\ \ parent: 13:22d8966a97e3
1065 |\ \ parent: 13:22d8966a97e3
1066 | | | parent: 14:8eac370358ef
1066 | | | parent: 14:8eac370358ef
1067 | | | user: test
1067 | | | user: test
1068 | | | date: Thu Jan 01 00:00:15 1970 +0000
1068 | | | date: Thu Jan 01 00:00:15 1970 +0000
1069 | | | summary: (15) expand
1069 | | | summary: (15) expand
1070 | | |
1070 | | |
1071 | o | changeset: 14:8eac370358ef
1071 | o | changeset: 14:8eac370358ef
1072 | |\| parent: 0:e6eb3150255d
1072 | |\| parent: 0:e6eb3150255d
1073 | ~ | parent: 12:86b91144a6e9
1073 | ~ | parent: 12:86b91144a6e9
1074 | | user: test
1074 | | user: test
1075 | | date: Thu Jan 01 00:00:14 1970 +0000
1075 | | date: Thu Jan 01 00:00:14 1970 +0000
1076 | | summary: (14) merge two known; one immediate right, one far right
1076 | | summary: (14) merge two known; one immediate right, one far right
1077 | /
1077 | /
1078 o | changeset: 13:22d8966a97e3
1078 o | changeset: 13:22d8966a97e3
1079 |\ \ parent: 9:7010c0af0a35
1079 |\ \ parent: 9:7010c0af0a35
1080 | | | parent: 11:832d76e6bdf2
1080 | | | parent: 11:832d76e6bdf2
1081 | | | user: test
1081 | | | user: test
1082 | | | date: Thu Jan 01 00:00:13 1970 +0000
1082 | | | date: Thu Jan 01 00:00:13 1970 +0000
1083 | | | summary: (13) expand
1083 | | | summary: (13) expand
1084 | | |
1084 | | |
1085 +---o changeset: 12:86b91144a6e9
1085 +---o changeset: 12:86b91144a6e9
1086 | | | parent: 1:6db2ef61d156
1086 | | | parent: 1:6db2ef61d156
1087 | | ~ parent: 9:7010c0af0a35
1087 | | ~ parent: 9:7010c0af0a35
1088 | | user: test
1088 | | user: test
1089 | | date: Thu Jan 01 00:00:12 1970 +0000
1089 | | date: Thu Jan 01 00:00:12 1970 +0000
1090 | | summary: (12) merge two known; one immediate right, one far left
1090 | | summary: (12) merge two known; one immediate right, one far left
1091 | |
1091 | |
1092 | o changeset: 11:832d76e6bdf2
1092 | o changeset: 11:832d76e6bdf2
1093 | |\ parent: 6:b105a072e251
1093 | |\ parent: 6:b105a072e251
1094 | | | parent: 10:74c64d036d72
1094 | | | parent: 10:74c64d036d72
1095 | | | user: test
1095 | | | user: test
1096 | | | date: Thu Jan 01 00:00:11 1970 +0000
1096 | | | date: Thu Jan 01 00:00:11 1970 +0000
1097 | | | summary: (11) expand
1097 | | | summary: (11) expand
1098 | | |
1098 | | |
1099 | | o changeset: 10:74c64d036d72
1099 | | o changeset: 10:74c64d036d72
1100 | |/| parent: 0:e6eb3150255d
1100 | |/| parent: 0:e6eb3150255d
1101 | | ~ parent: 6:b105a072e251
1101 | | ~ parent: 6:b105a072e251
1102 | | user: test
1102 | | user: test
1103 | | date: Thu Jan 01 00:00:10 1970 +0000
1103 | | date: Thu Jan 01 00:00:10 1970 +0000
1104 | | summary: (10) merge two known; one immediate left, one near right
1104 | | summary: (10) merge two known; one immediate left, one near right
1105 | |
1105 | |
1106 o | changeset: 9:7010c0af0a35
1106 o | changeset: 9:7010c0af0a35
1107 |\ \ parent: 7:b632bb1b1224
1107 |\ \ parent: 7:b632bb1b1224
1108 | | | parent: 8:7a0b11f71937
1108 | | | parent: 8:7a0b11f71937
1109 | | | user: test
1109 | | | user: test
1110 | | | date: Thu Jan 01 00:00:09 1970 +0000
1110 | | | date: Thu Jan 01 00:00:09 1970 +0000
1111 | | | summary: (9) expand
1111 | | | summary: (9) expand
1112 | | |
1112 | | |
1113 | o | changeset: 8:7a0b11f71937
1113 | o | changeset: 8:7a0b11f71937
1114 |/| | parent: 0:e6eb3150255d
1114 |/| | parent: 0:e6eb3150255d
1115 | ~ | parent: 7:b632bb1b1224
1115 | ~ | parent: 7:b632bb1b1224
1116 | | user: test
1116 | | user: test
1117 | | date: Thu Jan 01 00:00:08 1970 +0000
1117 | | date: Thu Jan 01 00:00:08 1970 +0000
1118 | | summary: (8) merge two known; one immediate left, one far right
1118 | | summary: (8) merge two known; one immediate left, one far right
1119 | /
1119 | /
1120 o | changeset: 7:b632bb1b1224
1120 o | changeset: 7:b632bb1b1224
1121 |\ \ parent: 2:3d9a33b8d1e1
1121 |\ \ parent: 2:3d9a33b8d1e1
1122 | ~ | parent: 5:4409d547b708
1122 | ~ | parent: 5:4409d547b708
1123 | | user: test
1123 | | user: test
1124 | | date: Thu Jan 01 00:00:07 1970 +0000
1124 | | date: Thu Jan 01 00:00:07 1970 +0000
1125 | | summary: (7) expand
1125 | | summary: (7) expand
1126 | /
1126 | /
1127 | o changeset: 6:b105a072e251
1127 | o changeset: 6:b105a072e251
1128 |/| parent: 2:3d9a33b8d1e1
1128 |/| parent: 2:3d9a33b8d1e1
1129 | ~ parent: 5:4409d547b708
1129 | ~ parent: 5:4409d547b708
1130 | user: test
1130 | user: test
1131 | date: Thu Jan 01 00:00:06 1970 +0000
1131 | date: Thu Jan 01 00:00:06 1970 +0000
1132 | summary: (6) merge two known; one immediate left, one far left
1132 | summary: (6) merge two known; one immediate left, one far left
1133 |
1133 |
1134 o changeset: 5:4409d547b708
1134 o changeset: 5:4409d547b708
1135 |\ parent: 3:27eef8ed80b4
1135 |\ parent: 3:27eef8ed80b4
1136 | ~ parent: 4:26a8bac39d9f
1136 | ~ parent: 4:26a8bac39d9f
1137 | user: test
1137 | user: test
1138 | date: Thu Jan 01 00:00:05 1970 +0000
1138 | date: Thu Jan 01 00:00:05 1970 +0000
1139 | summary: (5) expand
1139 | summary: (5) expand
1140 |
1140 |
1141 o changeset: 4:26a8bac39d9f
1141 o changeset: 4:26a8bac39d9f
1142 |\ parent: 1:6db2ef61d156
1142 |\ parent: 1:6db2ef61d156
1143 ~ ~ parent: 3:27eef8ed80b4
1143 ~ ~ parent: 3:27eef8ed80b4
1144 user: test
1144 user: test
1145 date: Thu Jan 01 00:00:04 1970 +0000
1145 date: Thu Jan 01 00:00:04 1970 +0000
1146 summary: (4) merge two known; one immediate left, one immediate right
1146 summary: (4) merge two known; one immediate left, one immediate right
1147
1147
1148
1148
1149
1149
1150 Empty revision range - display nothing:
1150 Empty revision range - display nothing:
1151 $ hg log -G -r 1..0
1151 $ hg log -G -r 1..0
1152
1152
1153 $ cd ..
1153 $ cd ..
1154
1154
1155 #if no-outer-repo
1155 #if no-outer-repo
1156
1156
1157 From outer space:
1157 From outer space:
1158 $ hg log -G -l1 repo
1158 $ hg log -G -l1 repo
1159 @ changeset: 34:fea3ac5810e0
1159 @ changeset: 34:fea3ac5810e0
1160 | tag: tip
1160 | tag: tip
1161 ~ parent: 32:d06dffa21a31
1161 ~ parent: 32:d06dffa21a31
1162 user: test
1162 user: test
1163 date: Thu Jan 01 00:00:34 1970 +0000
1163 date: Thu Jan 01 00:00:34 1970 +0000
1164 summary: (34) head
1164 summary: (34) head
1165
1165
1166 $ hg log -G -l1 repo/a
1166 $ hg log -G -l1 repo/a
1167 @ changeset: 34:fea3ac5810e0
1167 @ changeset: 34:fea3ac5810e0
1168 | tag: tip
1168 | tag: tip
1169 ~ parent: 32:d06dffa21a31
1169 ~ parent: 32:d06dffa21a31
1170 user: test
1170 user: test
1171 date: Thu Jan 01 00:00:34 1970 +0000
1171 date: Thu Jan 01 00:00:34 1970 +0000
1172 summary: (34) head
1172 summary: (34) head
1173
1173
1174 $ hg log -G -l1 repo/missing
1174 $ hg log -G -l1 repo/missing
1175
1175
1176 #endif
1176 #endif
1177
1177
1178 File log with revs != cset revs:
1178 File log with revs != cset revs:
1179 $ hg init flog
1179 $ hg init flog
1180 $ cd flog
1180 $ cd flog
1181 $ echo one >one
1181 $ echo one >one
1182 $ hg add one
1182 $ hg add one
1183 $ hg commit -mone
1183 $ hg commit -mone
1184 $ echo two >two
1184 $ echo two >two
1185 $ hg add two
1185 $ hg add two
1186 $ hg commit -mtwo
1186 $ hg commit -mtwo
1187 $ echo more >two
1187 $ echo more >two
1188 $ hg commit -mmore
1188 $ hg commit -mmore
1189 $ hg log -G two
1189 $ hg log -G two
1190 @ changeset: 2:12c28321755b
1190 @ changeset: 2:12c28321755b
1191 | tag: tip
1191 | tag: tip
1192 | user: test
1192 | user: test
1193 | date: Thu Jan 01 00:00:00 1970 +0000
1193 | date: Thu Jan 01 00:00:00 1970 +0000
1194 | summary: more
1194 | summary: more
1195 |
1195 |
1196 o changeset: 1:5ac72c0599bf
1196 o changeset: 1:5ac72c0599bf
1197 | user: test
1197 | user: test
1198 ~ date: Thu Jan 01 00:00:00 1970 +0000
1198 ~ date: Thu Jan 01 00:00:00 1970 +0000
1199 summary: two
1199 summary: two
1200
1200
1201
1201
1202 Issue1896: File log with explicit style
1202 Issue1896: File log with explicit style
1203 $ hg log -G --style=default one
1203 $ hg log -G --style=default one
1204 o changeset: 0:3d578b4a1f53
1204 o changeset: 0:3d578b4a1f53
1205 user: test
1205 user: test
1206 date: Thu Jan 01 00:00:00 1970 +0000
1206 date: Thu Jan 01 00:00:00 1970 +0000
1207 summary: one
1207 summary: one
1208
1208
1209 Issue2395: glog --style header and footer
1209 Issue2395: glog --style header and footer
1210 $ hg log -G --style=xml one
1210 $ hg log -G --style=xml one
1211 <?xml version="1.0"?>
1211 <?xml version="1.0"?>
1212 <log>
1212 <log>
1213 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1213 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1214 <author email="test">test</author>
1214 <author email="test">test</author>
1215 <date>1970-01-01T00:00:00+00:00</date>
1215 <date>1970-01-01T00:00:00+00:00</date>
1216 <msg xml:space="preserve">one</msg>
1216 <msg xml:space="preserve">one</msg>
1217 </logentry>
1217 </logentry>
1218 </log>
1218 </log>
1219
1219
1220 $ cd ..
1220 $ cd ..
1221
1221
1222 Incoming and outgoing:
1222 Incoming and outgoing:
1223
1223
1224 $ hg clone -U -r31 repo repo2
1224 $ hg clone -U -r31 repo repo2
1225 adding changesets
1225 adding changesets
1226 adding manifests
1226 adding manifests
1227 adding file changes
1227 adding file changes
1228 added 31 changesets with 31 changes to 1 files
1228 added 31 changesets with 31 changes to 1 files
1229 new changesets e6eb3150255d:621d83e11f67
1229 new changesets e6eb3150255d:621d83e11f67
1230 $ cd repo2
1230 $ cd repo2
1231
1231
1232 $ hg incoming --graph ../repo
1232 $ hg incoming --graph ../repo
1233 comparing with ../repo
1233 comparing with ../repo
1234 searching for changes
1234 searching for changes
1235 o changeset: 34:fea3ac5810e0
1235 o changeset: 34:fea3ac5810e0
1236 | tag: tip
1236 | tag: tip
1237 | parent: 32:d06dffa21a31
1237 | parent: 32:d06dffa21a31
1238 | user: test
1238 | user: test
1239 | date: Thu Jan 01 00:00:34 1970 +0000
1239 | date: Thu Jan 01 00:00:34 1970 +0000
1240 | summary: (34) head
1240 | summary: (34) head
1241 |
1241 |
1242 | o changeset: 33:68608f5145f9
1242 | o changeset: 33:68608f5145f9
1243 | parent: 18:1aa84d96232a
1243 | parent: 18:1aa84d96232a
1244 | user: test
1244 | user: test
1245 | date: Thu Jan 01 00:00:33 1970 +0000
1245 | date: Thu Jan 01 00:00:33 1970 +0000
1246 | summary: (33) head
1246 | summary: (33) head
1247 |
1247 |
1248 o changeset: 32:d06dffa21a31
1248 o changeset: 32:d06dffa21a31
1249 | parent: 27:886ed638191b
1249 | parent: 27:886ed638191b
1250 | parent: 31:621d83e11f67
1250 | parent: 31:621d83e11f67
1251 | user: test
1251 | user: test
1252 | date: Thu Jan 01 00:00:32 1970 +0000
1252 | date: Thu Jan 01 00:00:32 1970 +0000
1253 | summary: (32) expand
1253 | summary: (32) expand
1254 |
1254 |
1255 o changeset: 27:886ed638191b
1255 o changeset: 27:886ed638191b
1256 parent: 21:d42a756af44d
1256 parent: 21:d42a756af44d
1257 user: test
1257 user: test
1258 date: Thu Jan 01 00:00:27 1970 +0000
1258 date: Thu Jan 01 00:00:27 1970 +0000
1259 summary: (27) collapse
1259 summary: (27) collapse
1260
1260
1261 $ cd ..
1261 $ cd ..
1262
1262
1263 $ hg -R repo outgoing --graph repo2
1263 $ hg -R repo outgoing --graph repo2
1264 comparing with repo2
1264 comparing with repo2
1265 searching for changes
1265 searching for changes
1266 @ changeset: 34:fea3ac5810e0
1266 @ changeset: 34:fea3ac5810e0
1267 | tag: tip
1267 | tag: tip
1268 | parent: 32:d06dffa21a31
1268 | parent: 32:d06dffa21a31
1269 | user: test
1269 | user: test
1270 | date: Thu Jan 01 00:00:34 1970 +0000
1270 | date: Thu Jan 01 00:00:34 1970 +0000
1271 | summary: (34) head
1271 | summary: (34) head
1272 |
1272 |
1273 | o changeset: 33:68608f5145f9
1273 | o changeset: 33:68608f5145f9
1274 | parent: 18:1aa84d96232a
1274 | parent: 18:1aa84d96232a
1275 | user: test
1275 | user: test
1276 | date: Thu Jan 01 00:00:33 1970 +0000
1276 | date: Thu Jan 01 00:00:33 1970 +0000
1277 | summary: (33) head
1277 | summary: (33) head
1278 |
1278 |
1279 o changeset: 32:d06dffa21a31
1279 o changeset: 32:d06dffa21a31
1280 | parent: 27:886ed638191b
1280 | parent: 27:886ed638191b
1281 | parent: 31:621d83e11f67
1281 | parent: 31:621d83e11f67
1282 | user: test
1282 | user: test
1283 | date: Thu Jan 01 00:00:32 1970 +0000
1283 | date: Thu Jan 01 00:00:32 1970 +0000
1284 | summary: (32) expand
1284 | summary: (32) expand
1285 |
1285 |
1286 o changeset: 27:886ed638191b
1286 o changeset: 27:886ed638191b
1287 parent: 21:d42a756af44d
1287 parent: 21:d42a756af44d
1288 user: test
1288 user: test
1289 date: Thu Jan 01 00:00:27 1970 +0000
1289 date: Thu Jan 01 00:00:27 1970 +0000
1290 summary: (27) collapse
1290 summary: (27) collapse
1291
1291
1292
1292
1293 File + limit with revs != cset revs:
1293 File + limit with revs != cset revs:
1294 $ cd repo
1294 $ cd repo
1295 $ touch b
1295 $ touch b
1296 $ hg ci -Aqm0
1296 $ hg ci -Aqm0
1297 $ hg log -G -l2 a
1297 $ hg log -G -l2 a
1298 o changeset: 34:fea3ac5810e0
1298 o changeset: 34:fea3ac5810e0
1299 | parent: 32:d06dffa21a31
1299 | parent: 32:d06dffa21a31
1300 ~ user: test
1300 ~ user: test
1301 date: Thu Jan 01 00:00:34 1970 +0000
1301 date: Thu Jan 01 00:00:34 1970 +0000
1302 summary: (34) head
1302 summary: (34) head
1303
1303
1304 o changeset: 33:68608f5145f9
1304 o changeset: 33:68608f5145f9
1305 | parent: 18:1aa84d96232a
1305 | parent: 18:1aa84d96232a
1306 ~ user: test
1306 ~ user: test
1307 date: Thu Jan 01 00:00:33 1970 +0000
1307 date: Thu Jan 01 00:00:33 1970 +0000
1308 summary: (33) head
1308 summary: (33) head
1309
1309
1310
1310
1311 File + limit + -ra:b, (b - a) < limit:
1311 File + limit + -ra:b, (b - a) < limit:
1312 $ hg log -G -l3000 -r32:tip a
1312 $ hg log -G -l3000 -r32:tip a
1313 o changeset: 34:fea3ac5810e0
1313 o changeset: 34:fea3ac5810e0
1314 | parent: 32:d06dffa21a31
1314 | parent: 32:d06dffa21a31
1315 | user: test
1315 | user: test
1316 | date: Thu Jan 01 00:00:34 1970 +0000
1316 | date: Thu Jan 01 00:00:34 1970 +0000
1317 | summary: (34) head
1317 | summary: (34) head
1318 |
1318 |
1319 | o changeset: 33:68608f5145f9
1319 | o changeset: 33:68608f5145f9
1320 | | parent: 18:1aa84d96232a
1320 | | parent: 18:1aa84d96232a
1321 | ~ user: test
1321 | ~ user: test
1322 | date: Thu Jan 01 00:00:33 1970 +0000
1322 | date: Thu Jan 01 00:00:33 1970 +0000
1323 | summary: (33) head
1323 | summary: (33) head
1324 |
1324 |
1325 o changeset: 32:d06dffa21a31
1325 o changeset: 32:d06dffa21a31
1326 |\ parent: 27:886ed638191b
1326 |\ parent: 27:886ed638191b
1327 ~ ~ parent: 31:621d83e11f67
1327 ~ ~ parent: 31:621d83e11f67
1328 user: test
1328 user: test
1329 date: Thu Jan 01 00:00:32 1970 +0000
1329 date: Thu Jan 01 00:00:32 1970 +0000
1330 summary: (32) expand
1330 summary: (32) expand
1331
1331
1332
1332
1333 Point out a common and an uncommon unshown parent
1333 Point out a common and an uncommon unshown parent
1334
1334
1335 $ hg log -G -r 'rev(8) or rev(9)'
1335 $ hg log -G -r 'rev(8) or rev(9)'
1336 o changeset: 9:7010c0af0a35
1336 o changeset: 9:7010c0af0a35
1337 |\ parent: 7:b632bb1b1224
1337 |\ parent: 7:b632bb1b1224
1338 | ~ parent: 8:7a0b11f71937
1338 | ~ parent: 8:7a0b11f71937
1339 | user: test
1339 | user: test
1340 | date: Thu Jan 01 00:00:09 1970 +0000
1340 | date: Thu Jan 01 00:00:09 1970 +0000
1341 | summary: (9) expand
1341 | summary: (9) expand
1342 |
1342 |
1343 o changeset: 8:7a0b11f71937
1343 o changeset: 8:7a0b11f71937
1344 |\ parent: 0:e6eb3150255d
1344 |\ parent: 0:e6eb3150255d
1345 ~ ~ parent: 7:b632bb1b1224
1345 ~ ~ parent: 7:b632bb1b1224
1346 user: test
1346 user: test
1347 date: Thu Jan 01 00:00:08 1970 +0000
1347 date: Thu Jan 01 00:00:08 1970 +0000
1348 summary: (8) merge two known; one immediate left, one far right
1348 summary: (8) merge two known; one immediate left, one far right
1349
1349
1350
1350
1351 File + limit + -ra:b, b < tip:
1351 File + limit + -ra:b, b < tip:
1352
1352
1353 $ hg log -G -l1 -r32:34 a
1353 $ hg log -G -l1 -r32:34 a
1354 o changeset: 34:fea3ac5810e0
1354 o changeset: 34:fea3ac5810e0
1355 | parent: 32:d06dffa21a31
1355 | parent: 32:d06dffa21a31
1356 ~ user: test
1356 ~ user: test
1357 date: Thu Jan 01 00:00:34 1970 +0000
1357 date: Thu Jan 01 00:00:34 1970 +0000
1358 summary: (34) head
1358 summary: (34) head
1359
1359
1360
1360
1361 file(File) + limit + -ra:b, b < tip:
1361 file(File) + limit + -ra:b, b < tip:
1362
1362
1363 $ hg log -G -l1 -r32:34 -r 'file("a")'
1363 $ hg log -G -l1 -r32:34 -r 'file("a")'
1364 o changeset: 34:fea3ac5810e0
1364 o changeset: 34:fea3ac5810e0
1365 | parent: 32:d06dffa21a31
1365 | parent: 32:d06dffa21a31
1366 ~ user: test
1366 ~ user: test
1367 date: Thu Jan 01 00:00:34 1970 +0000
1367 date: Thu Jan 01 00:00:34 1970 +0000
1368 summary: (34) head
1368 summary: (34) head
1369
1369
1370
1370
1371 limit(file(File) and a::b), b < tip:
1371 limit(file(File) and a::b), b < tip:
1372
1372
1373 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1373 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1374 o changeset: 32:d06dffa21a31
1374 o changeset: 32:d06dffa21a31
1375 |\ parent: 27:886ed638191b
1375 |\ parent: 27:886ed638191b
1376 ~ ~ parent: 31:621d83e11f67
1376 ~ ~ parent: 31:621d83e11f67
1377 user: test
1377 user: test
1378 date: Thu Jan 01 00:00:32 1970 +0000
1378 date: Thu Jan 01 00:00:32 1970 +0000
1379 summary: (32) expand
1379 summary: (32) expand
1380
1380
1381
1381
1382 File + limit + -ra:b, b < tip:
1382 File + limit + -ra:b, b < tip:
1383
1383
1384 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1384 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1385
1385
1386 File + limit + -ra:b, b < tip, (b - a) < limit:
1386 File + limit + -ra:b, b < tip, (b - a) < limit:
1387
1387
1388 $ hg log -G -l10 -r33:34 a
1388 $ hg log -G -l10 -r33:34 a
1389 o changeset: 34:fea3ac5810e0
1389 o changeset: 34:fea3ac5810e0
1390 | parent: 32:d06dffa21a31
1390 | parent: 32:d06dffa21a31
1391 ~ user: test
1391 ~ user: test
1392 date: Thu Jan 01 00:00:34 1970 +0000
1392 date: Thu Jan 01 00:00:34 1970 +0000
1393 summary: (34) head
1393 summary: (34) head
1394
1394
1395 o changeset: 33:68608f5145f9
1395 o changeset: 33:68608f5145f9
1396 | parent: 18:1aa84d96232a
1396 | parent: 18:1aa84d96232a
1397 ~ user: test
1397 ~ user: test
1398 date: Thu Jan 01 00:00:33 1970 +0000
1398 date: Thu Jan 01 00:00:33 1970 +0000
1399 summary: (33) head
1399 summary: (33) head
1400
1400
1401
1401
1402 Do not crash or produce strange graphs if history is buggy
1402 Do not crash or produce strange graphs if history is buggy
1403
1403
1404 $ hg branch branch
1404 $ hg branch branch
1405 marked working directory as branch branch
1405 marked working directory as branch branch
1406 (branches are permanent and global, did you want a bookmark?)
1406 (branches are permanent and global, did you want a bookmark?)
1407 $ commit 36 "buggy merge: identical parents" 35 35
1407 $ commit 36 "buggy merge: identical parents" 35 35
1408 $ hg log -G -l5
1408 $ hg log -G -l5
1409 @ changeset: 36:08a19a744424
1409 @ changeset: 36:08a19a744424
1410 | branch: branch
1410 | branch: branch
1411 | tag: tip
1411 | tag: tip
1412 | parent: 35:9159c3644c5e
1412 | parent: 35:9159c3644c5e
1413 | parent: 35:9159c3644c5e
1413 | parent: 35:9159c3644c5e
1414 | user: test
1414 | user: test
1415 | date: Thu Jan 01 00:00:36 1970 +0000
1415 | date: Thu Jan 01 00:00:36 1970 +0000
1416 | summary: (36) buggy merge: identical parents
1416 | summary: (36) buggy merge: identical parents
1417 |
1417 |
1418 o changeset: 35:9159c3644c5e
1418 o changeset: 35:9159c3644c5e
1419 | user: test
1419 | user: test
1420 | date: Thu Jan 01 00:00:00 1970 +0000
1420 | date: Thu Jan 01 00:00:00 1970 +0000
1421 | summary: 0
1421 | summary: 0
1422 |
1422 |
1423 o changeset: 34:fea3ac5810e0
1423 o changeset: 34:fea3ac5810e0
1424 | parent: 32:d06dffa21a31
1424 | parent: 32:d06dffa21a31
1425 | user: test
1425 | user: test
1426 | date: Thu Jan 01 00:00:34 1970 +0000
1426 | date: Thu Jan 01 00:00:34 1970 +0000
1427 | summary: (34) head
1427 | summary: (34) head
1428 |
1428 |
1429 | o changeset: 33:68608f5145f9
1429 | o changeset: 33:68608f5145f9
1430 | | parent: 18:1aa84d96232a
1430 | | parent: 18:1aa84d96232a
1431 | ~ user: test
1431 | ~ user: test
1432 | date: Thu Jan 01 00:00:33 1970 +0000
1432 | date: Thu Jan 01 00:00:33 1970 +0000
1433 | summary: (33) head
1433 | summary: (33) head
1434 |
1434 |
1435 o changeset: 32:d06dffa21a31
1435 o changeset: 32:d06dffa21a31
1436 |\ parent: 27:886ed638191b
1436 |\ parent: 27:886ed638191b
1437 ~ ~ parent: 31:621d83e11f67
1437 ~ ~ parent: 31:621d83e11f67
1438 user: test
1438 user: test
1439 date: Thu Jan 01 00:00:32 1970 +0000
1439 date: Thu Jan 01 00:00:32 1970 +0000
1440 summary: (32) expand
1440 summary: (32) expand
1441
1441
1442
1442
1443 Test log -G options
1443 Test log -G options
1444
1444
1445 $ testlog() {
1445 $ testlog() {
1446 > hg log -G --print-revset "$@"
1446 > hg log -G --print-revset "$@"
1447 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1447 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1448 > | sed 's/.*nodetag/nodetag/' > log.nodes
1448 > | sed 's/.*nodetag/nodetag/' > log.nodes
1449 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1449 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1450 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1450 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1451 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1451 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1452 > | grep '^[-+@ ]' || :
1452 > | grep '^[-+@ ]' || :
1453 > }
1453 > }
1454
1454
1455 glog always reorders nodes which explains the difference with log
1455 glog always reorders nodes which explains the difference with log
1456
1456
1457 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1457 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1458 ['27', '25', '21', '34', '32', '31']
1458 ['27', '25', '21', '34', '32', '31']
1459 []
1459 []
1460 <baseset- [21, 25, 27, 31, 32, 34]>
1460 <baseset- [21, 25, 27, 31, 32, 34]>
1461 --- log.nodes * (glob)
1461 --- log.nodes * (glob)
1462 +++ glog.nodes * (glob)
1462 +++ glog.nodes * (glob)
1463 @@ -1,6 +1,6 @@
1463 @@ -1,6 +1,6 @@
1464 -nodetag 27
1464 -nodetag 27
1465 -nodetag 25
1465 -nodetag 25
1466 -nodetag 21
1466 -nodetag 21
1467 nodetag 34
1467 nodetag 34
1468 nodetag 32
1468 nodetag 32
1469 nodetag 31
1469 nodetag 31
1470 +nodetag 27
1470 +nodetag 27
1471 +nodetag 25
1471 +nodetag 25
1472 +nodetag 21
1472 +nodetag 21
1473 $ testlog -u test -u not-a-user
1473 $ testlog -u test -u not-a-user
1474 []
1474 []
1475 (or
1475 (or
1476 (list
1476 (list
1477 (func
1477 (func
1478 (symbol 'user')
1478 (symbol 'user')
1479 (string 'test'))
1479 (string 'test'))
1480 (func
1480 (func
1481 (symbol 'user')
1481 (symbol 'user')
1482 (string 'not-a-user'))))
1482 (string 'not-a-user'))))
1483 <filteredset
1483 <filteredset
1484 <spanset- 0:37>,
1484 <spanset- 0:37>,
1485 <addset
1485 <addset
1486 <filteredset
1486 <filteredset
1487 <fullreposet+ 0:37>,
1487 <fullreposet+ 0:37>,
1488 <user 'test'>>,
1488 <user 'test'>>,
1489 <filteredset
1489 <filteredset
1490 <fullreposet+ 0:37>,
1490 <fullreposet+ 0:37>,
1491 <user 'not-a-user'>>>>
1491 <user 'not-a-user'>>>>
1492 $ testlog -b not-a-branch
1492 $ testlog -b not-a-branch
1493 abort: unknown revision 'not-a-branch'!
1493 abort: unknown revision 'not-a-branch'!
1494 abort: unknown revision 'not-a-branch'!
1494 abort: unknown revision 'not-a-branch'!
1495 abort: unknown revision 'not-a-branch'!
1495 abort: unknown revision 'not-a-branch'!
1496 $ testlog -b 35 -b 36 --only-branch branch
1496 $ testlog -b 35 -b 36 --only-branch branch
1497 []
1497 []
1498 (or
1498 (or
1499 (list
1499 (list
1500 (func
1500 (func
1501 (symbol 'branch')
1501 (symbol 'branch')
1502 (string 'default'))
1502 (string 'default'))
1503 (or
1503 (or
1504 (list
1504 (list
1505 (func
1505 (func
1506 (symbol 'branch')
1506 (symbol 'branch')
1507 (string 'branch'))
1507 (string 'branch'))
1508 (func
1508 (func
1509 (symbol 'branch')
1509 (symbol 'branch')
1510 (string 'branch'))))))
1510 (string 'branch'))))))
1511 <filteredset
1511 <filteredset
1512 <spanset- 0:37>,
1512 <spanset- 0:37>,
1513 <addset
1513 <addset
1514 <filteredset
1514 <filteredset
1515 <fullreposet+ 0:37>,
1515 <fullreposet+ 0:37>,
1516 <branch 'default'>>,
1516 <branch 'default'>>,
1517 <addset
1517 <addset
1518 <filteredset
1518 <filteredset
1519 <fullreposet+ 0:37>,
1519 <fullreposet+ 0:37>,
1520 <branch 'branch'>>,
1520 <branch 'branch'>>,
1521 <filteredset
1521 <filteredset
1522 <fullreposet+ 0:37>,
1522 <fullreposet+ 0:37>,
1523 <branch 'branch'>>>>>
1523 <branch 'branch'>>>>>
1524 $ testlog -k expand -k merge
1524 $ testlog -k expand -k merge
1525 []
1525 []
1526 (or
1526 (or
1527 (list
1527 (list
1528 (func
1528 (func
1529 (symbol 'keyword')
1529 (symbol 'keyword')
1530 (string 'expand'))
1530 (string 'expand'))
1531 (func
1531 (func
1532 (symbol 'keyword')
1532 (symbol 'keyword')
1533 (string 'merge'))))
1533 (string 'merge'))))
1534 <filteredset
1534 <filteredset
1535 <spanset- 0:37>,
1535 <spanset- 0:37>,
1536 <addset
1536 <addset
1537 <filteredset
1537 <filteredset
1538 <fullreposet+ 0:37>,
1538 <fullreposet+ 0:37>,
1539 <keyword 'expand'>>,
1539 <keyword 'expand'>>,
1540 <filteredset
1540 <filteredset
1541 <fullreposet+ 0:37>,
1541 <fullreposet+ 0:37>,
1542 <keyword 'merge'>>>>
1542 <keyword 'merge'>>>>
1543 $ testlog --only-merges
1543 $ testlog --only-merges
1544 []
1544 []
1545 (func
1545 (func
1546 (symbol 'merge')
1546 (symbol 'merge')
1547 None)
1547 None)
1548 <filteredset
1548 <filteredset
1549 <spanset- 0:37>,
1549 <spanset- 0:37>,
1550 <merge>>
1550 <merge>>
1551 $ testlog --no-merges
1551 $ testlog --no-merges
1552 []
1552 []
1553 (not
1553 (not
1554 (func
1554 (func
1555 (symbol 'merge')
1555 (symbol 'merge')
1556 None))
1556 None))
1557 <filteredset
1557 <filteredset
1558 <spanset- 0:37>,
1558 <spanset- 0:37>,
1559 <not
1559 <not
1560 <filteredset
1560 <filteredset
1561 <spanset- 0:37>,
1561 <spanset- 0:37>,
1562 <merge>>>>
1562 <merge>>>>
1563 $ testlog --date '2 0 to 4 0'
1563 $ testlog --date '2 0 to 4 0'
1564 []
1564 []
1565 (func
1565 (func
1566 (symbol 'date')
1566 (symbol 'date')
1567 (string '2 0 to 4 0'))
1567 (string '2 0 to 4 0'))
1568 <filteredset
1568 <filteredset
1569 <spanset- 0:37>,
1569 <spanset- 0:37>,
1570 <date '2 0 to 4 0'>>
1570 <date '2 0 to 4 0'>>
1571 $ hg log -G -d 'brace ) in a date'
1571 $ hg log -G -d 'brace ) in a date'
1572 hg: parse error: invalid date: 'brace ) in a date'
1572 hg: parse error: invalid date: 'brace ) in a date'
1573 [255]
1573 [255]
1574 $ testlog --prune 31 --prune 32
1574 $ testlog --prune 31 --prune 32
1575 []
1575 []
1576 (not
1576 (not
1577 (or
1577 (or
1578 (list
1578 (list
1579 (func
1579 (func
1580 (symbol 'ancestors')
1580 (symbol 'ancestors')
1581 (string '31'))
1581 (string '31'))
1582 (func
1582 (func
1583 (symbol 'ancestors')
1583 (symbol 'ancestors')
1584 (string '32')))))
1584 (string '32')))))
1585 <filteredset
1585 <filteredset
1586 <spanset- 0:37>,
1586 <spanset- 0:37>,
1587 <not
1587 <not
1588 <addset
1588 <addset
1589 <filteredset
1589 <filteredset
1590 <spanset- 0:37>,
1590 <spanset- 0:37>,
1591 <generatorsetdesc+>>,
1591 <generatorsetdesc+>>,
1592 <filteredset
1592 <filteredset
1593 <spanset- 0:37>,
1593 <spanset- 0:37>,
1594 <generatorsetdesc+>>>>>
1594 <generatorsetdesc+>>>>>
1595
1595
1596 Dedicated repo for --follow and paths filtering. The g is crafted to
1596 Dedicated repo for --follow and paths filtering. The g is crafted to
1597 have 2 filelog topological heads in a linear changeset graph.
1597 have 2 filelog topological heads in a linear changeset graph.
1598
1598
1599 $ cd ..
1599 $ cd ..
1600 $ hg init follow
1600 $ hg init follow
1601 $ cd follow
1601 $ cd follow
1602 $ testlog --follow
1602 $ testlog --follow
1603 []
1603 []
1604 []
1604 []
1605 <baseset []>
1605 <baseset []>
1606 $ testlog -rnull
1606 $ testlog -rnull
1607 ['null']
1607 ['null']
1608 []
1608 []
1609 <baseset [-1]>
1609 <baseset [-1]>
1610 $ echo a > a
1610 $ echo a > a
1611 $ echo aa > aa
1611 $ echo aa > aa
1612 $ echo f > f
1612 $ echo f > f
1613 $ hg ci -Am "add a" a aa f
1613 $ hg ci -Am "add a" a aa f
1614 $ hg cp a b
1614 $ hg cp a b
1615 $ hg cp f g
1615 $ hg cp f g
1616 $ hg ci -m "copy a b"
1616 $ hg ci -m "copy a b"
1617 $ mkdir dir
1617 $ mkdir dir
1618 $ hg mv b dir
1618 $ hg mv b dir
1619 $ echo g >> g
1619 $ echo g >> g
1620 $ echo f >> f
1620 $ echo f >> f
1621 $ hg ci -m "mv b dir/b"
1621 $ hg ci -m "mv b dir/b"
1622 $ hg mv a b
1622 $ hg mv a b
1623 $ hg cp -f f g
1623 $ hg cp -f f g
1624 $ echo a > d
1624 $ echo a > d
1625 $ hg add d
1625 $ hg add d
1626 $ hg ci -m "mv a b; add d"
1626 $ hg ci -m "mv a b; add d"
1627 $ hg mv dir/b e
1627 $ hg mv dir/b e
1628 $ hg ci -m "mv dir/b e"
1628 $ hg ci -m "mv dir/b e"
1629 $ hg log -G --template '({rev}) {desc|firstline}\n'
1629 $ hg log -G --template '({rev}) {desc|firstline}\n'
1630 @ (4) mv dir/b e
1630 @ (4) mv dir/b e
1631 |
1631 |
1632 o (3) mv a b; add d
1632 o (3) mv a b; add d
1633 |
1633 |
1634 o (2) mv b dir/b
1634 o (2) mv b dir/b
1635 |
1635 |
1636 o (1) copy a b
1636 o (1) copy a b
1637 |
1637 |
1638 o (0) add a
1638 o (0) add a
1639
1639
1640
1640
1641 $ testlog a
1641 $ testlog a
1642 []
1642 []
1643 (func
1643 (func
1644 (symbol 'filelog')
1644 (symbol 'filelog')
1645 (string 'a'))
1645 (string 'a'))
1646 <filteredset
1646 <filteredset
1647 <spanset- 0:5>, set([0])>
1647 <spanset- 0:5>, set([0])>
1648 $ testlog a b
1648 $ testlog a b
1649 []
1649 []
1650 (or
1650 (or
1651 (list
1651 (list
1652 (func
1652 (func
1653 (symbol 'filelog')
1653 (symbol 'filelog')
1654 (string 'a'))
1654 (string 'a'))
1655 (func
1655 (func
1656 (symbol 'filelog')
1656 (symbol 'filelog')
1657 (string 'b'))))
1657 (string 'b'))))
1658 <filteredset
1658 <filteredset
1659 <spanset- 0:5>,
1659 <spanset- 0:5>,
1660 <addset
1660 <addset
1661 <baseset+ [0]>,
1661 <baseset+ [0]>,
1662 <baseset+ [1]>>>
1662 <baseset+ [1]>>>
1663
1663
1664 Test falling back to slow path for non-existing files
1664 Test falling back to slow path for non-existing files
1665
1665
1666 $ testlog a c
1666 $ testlog a c
1667 []
1667 []
1668 (func
1668 (func
1669 (symbol '_matchfiles')
1669 (symbol '_matchfiles')
1670 (list
1670 (list
1671 (string 'r:')
1671 (string 'r:')
1672 (string 'd:relpath')
1672 (string 'd:relpath')
1673 (string 'p:a')
1673 (string 'p:a')
1674 (string 'p:c')))
1674 (string 'p:c')))
1675 <filteredset
1675 <filteredset
1676 <spanset- 0:5>,
1676 <spanset- 0:5>,
1677 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=None>>
1677 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=None>>
1678
1678
1679 Test multiple --include/--exclude/paths
1679 Test multiple --include/--exclude/paths
1680
1680
1681 $ testlog --include a --include e --exclude b --exclude e a e
1681 $ testlog --include a --include e --exclude b --exclude e a e
1682 []
1682 []
1683 (func
1683 (func
1684 (symbol '_matchfiles')
1684 (symbol '_matchfiles')
1685 (list
1685 (list
1686 (string 'r:')
1686 (string 'r:')
1687 (string 'd:relpath')
1687 (string 'd:relpath')
1688 (string 'p:a')
1688 (string 'p:a')
1689 (string 'p:e')
1689 (string 'p:e')
1690 (string 'i:a')
1690 (string 'i:a')
1691 (string 'i:e')
1691 (string 'i:e')
1692 (string 'x:b')
1692 (string 'x:b')
1693 (string 'x:e')))
1693 (string 'x:e')))
1694 <filteredset
1694 <filteredset
1695 <spanset- 0:5>,
1695 <spanset- 0:5>,
1696 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=None>>
1696 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=None>>
1697
1697
1698 Test glob expansion of pats
1698 Test glob expansion of pats
1699
1699
1700 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1700 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1701 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1701 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1702 $ if [ $expandglobs = "true" ]; then
1702 $ if [ $expandglobs = "true" ]; then
1703 > testlog 'a*';
1703 > testlog 'a*';
1704 > else
1704 > else
1705 > testlog a*;
1705 > testlog a*;
1706 > fi;
1706 > fi;
1707 []
1707 []
1708 (func
1708 (func
1709 (symbol 'filelog')
1709 (symbol 'filelog')
1710 (string 'aa'))
1710 (string 'aa'))
1711 <filteredset
1711 <filteredset
1712 <spanset- 0:5>, set([0])>
1712 <spanset- 0:5>, set([0])>
1713
1713
1714 Test --follow on a non-existent directory
1714 Test --follow on a non-existent directory
1715
1715
1716 $ testlog -f dir
1716 $ testlog -f dir
1717 abort: cannot follow file not in parent revision: "dir"
1717 abort: cannot follow file not in parent revision: "dir"
1718 abort: cannot follow file not in parent revision: "dir"
1718 abort: cannot follow file not in parent revision: "dir"
1719 abort: cannot follow file not in parent revision: "dir"
1719 abort: cannot follow file not in parent revision: "dir"
1720
1720
1721 Test --follow on a directory
1721 Test --follow on a directory
1722
1722
1723 $ hg up -q '.^'
1723 $ hg up -q '.^'
1724 $ testlog -f dir
1724 $ testlog -f dir
1725 []
1725 []
1726 (and
1726 (and
1727 (func
1727 (func
1728 (symbol 'ancestors')
1728 (symbol 'ancestors')
1729 (symbol '.'))
1729 (symbol '.'))
1730 (func
1730 (func
1731 (symbol '_matchfiles')
1731 (symbol '_matchfiles')
1732 (list
1732 (list
1733 (string 'r:')
1733 (string 'r:')
1734 (string 'd:relpath')
1734 (string 'd:relpath')
1735 (string 'p:dir'))))
1735 (string 'p:dir'))))
1736 <filteredset
1736 <filteredset
1737 <filteredset
1737 <filteredset
1738 <spanset- 0:4>,
1738 <spanset- 0:4>,
1739 <generatorsetdesc+>>,
1739 <generatorsetdesc+>>,
1740 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=None>>
1740 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=None>>
1741 $ hg up -q tip
1741 $ hg up -q tip
1742
1742
1743 Test --follow on file not in parent revision
1743 Test --follow on file not in parent revision
1744
1744
1745 $ testlog -f a
1745 $ testlog -f a
1746 abort: cannot follow file not in parent revision: "a"
1746 abort: cannot follow file not in parent revision: "a"
1747 abort: cannot follow file not in parent revision: "a"
1747 abort: cannot follow file not in parent revision: "a"
1748 abort: cannot follow file not in parent revision: "a"
1748 abort: cannot follow file not in parent revision: "a"
1749
1749
1750 Test --follow and patterns
1750 Test --follow and patterns
1751
1751
1752 $ testlog -f 'glob:*'
1752 $ testlog -f 'glob:*'
1753 []
1753 []
1754 (and
1754 (and
1755 (func
1755 (func
1756 (symbol 'ancestors')
1756 (symbol 'ancestors')
1757 (symbol '.'))
1757 (symbol '.'))
1758 (func
1758 (func
1759 (symbol '_matchfiles')
1759 (symbol '_matchfiles')
1760 (list
1760 (list
1761 (string 'r:')
1761 (string 'r:')
1762 (string 'd:relpath')
1762 (string 'd:relpath')
1763 (string 'p:glob:*'))))
1763 (string 'p:glob:*'))))
1764 <filteredset
1764 <filteredset
1765 <filteredset
1765 <filteredset
1766 <spanset- 0:5>,
1766 <spanset- 0:5>,
1767 <generatorsetdesc+>>,
1767 <generatorsetdesc+>>,
1768 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=None>>
1768 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=None>>
1769
1769
1770 Test --follow on a single rename
1770 Test --follow on a single rename
1771
1771
1772 $ hg up -q 2
1772 $ hg up -q 2
1773 $ testlog -f a
1773 $ testlog -f a
1774 []
1774 []
1775 (func
1775 (func
1776 (symbol 'follow')
1776 (symbol 'follow')
1777 (string 'a'))
1777 (string 'a'))
1778 <filteredset
1778 <filteredset
1779 <spanset- 0:3>,
1779 <spanset- 0:3>,
1780 <generatorsetdesc+>>
1780 <generatorsetdesc+>>
1781
1781
1782 Test --follow and multiple renames
1782 Test --follow and multiple renames
1783
1783
1784 $ hg up -q tip
1784 $ hg up -q tip
1785 $ testlog -f e
1785 $ testlog -f e
1786 []
1786 []
1787 (func
1787 (func
1788 (symbol 'follow')
1788 (symbol 'follow')
1789 (string 'e'))
1789 (string 'e'))
1790 <filteredset
1790 <filteredset
1791 <spanset- 0:5>,
1791 <spanset- 0:5>,
1792 <generatorsetdesc+>>
1792 <generatorsetdesc+>>
1793
1793
1794 Test --follow and multiple filelog heads
1794 Test --follow and multiple filelog heads
1795
1795
1796 $ hg up -q 2
1796 $ hg up -q 2
1797 $ testlog -f g
1797 $ testlog -f g
1798 []
1798 []
1799 (func
1799 (func
1800 (symbol 'follow')
1800 (symbol 'follow')
1801 (string 'g'))
1801 (string 'g'))
1802 <filteredset
1802 <filteredset
1803 <spanset- 0:3>,
1803 <spanset- 0:3>,
1804 <generatorsetdesc+>>
1804 <generatorsetdesc+>>
1805 $ cat log.nodes
1805 $ cat log.nodes
1806 nodetag 2
1806 nodetag 2
1807 nodetag 1
1807 nodetag 1
1808 nodetag 0
1808 nodetag 0
1809 $ hg up -q tip
1809 $ hg up -q tip
1810 $ testlog -f g
1810 $ testlog -f g
1811 []
1811 []
1812 (func
1812 (func
1813 (symbol 'follow')
1813 (symbol 'follow')
1814 (string 'g'))
1814 (string 'g'))
1815 <filteredset
1815 <filteredset
1816 <spanset- 0:5>,
1816 <spanset- 0:5>,
1817 <generatorsetdesc+>>
1817 <generatorsetdesc+>>
1818 $ cat log.nodes
1818 $ cat log.nodes
1819 nodetag 3
1819 nodetag 3
1820 nodetag 2
1820 nodetag 2
1821 nodetag 0
1821 nodetag 0
1822
1822
1823 Test --follow and multiple files
1823 Test --follow and multiple files
1824
1824
1825 $ testlog -f g e
1825 $ testlog -f g e
1826 []
1826 []
1827 (or
1827 (or
1828 (list
1828 (list
1829 (func
1829 (func
1830 (symbol 'follow')
1830 (symbol 'follow')
1831 (string 'g'))
1831 (string 'g'))
1832 (func
1832 (func
1833 (symbol 'follow')
1833 (symbol 'follow')
1834 (string 'e'))))
1834 (string 'e'))))
1835 <filteredset
1835 <filteredset
1836 <spanset- 0:5>,
1836 <spanset- 0:5>,
1837 <addset
1837 <addset
1838 <generatorsetdesc+>,
1838 <generatorsetdesc+>,
1839 <generatorsetdesc+>>>
1839 <generatorsetdesc+>>>
1840 $ cat log.nodes
1840 $ cat log.nodes
1841 nodetag 4
1841 nodetag 4
1842 nodetag 3
1842 nodetag 3
1843 nodetag 2
1843 nodetag 2
1844 nodetag 1
1844 nodetag 1
1845 nodetag 0
1845 nodetag 0
1846
1846
1847 Test --follow null parent
1847 Test --follow null parent
1848
1848
1849 $ hg up -q null
1849 $ hg up -q null
1850 $ testlog -f
1850 $ testlog -f
1851 []
1851 []
1852 []
1852 []
1853 <baseset []>
1853 <baseset []>
1854
1854
1855 Test --follow-first
1855 Test --follow-first
1856
1856
1857 $ hg up -q 3
1857 $ hg up -q 3
1858 $ echo ee > e
1858 $ echo ee > e
1859 $ hg ci -Am "add another e" e
1859 $ hg ci -Am "add another e" e
1860 created new head
1860 created new head
1861 $ hg merge --tool internal:other 4
1861 $ hg merge --tool internal:other 4
1862 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1862 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1863 (branch merge, don't forget to commit)
1863 (branch merge, don't forget to commit)
1864 $ echo merge > e
1864 $ echo merge > e
1865 $ hg ci -m "merge 5 and 4"
1865 $ hg ci -m "merge 5 and 4"
1866 $ testlog --follow-first
1866 $ testlog --follow-first
1867 []
1867 []
1868 (func
1868 (func
1869 (symbol '_firstancestors')
1869 (symbol '_firstancestors')
1870 (symbol '.'))
1870 (symbol '.'))
1871 <filteredset
1871 <filteredset
1872 <spanset- 0:7>,
1872 <spanset- 0:7>,
1873 <generatorsetdesc+>>
1873 <generatorsetdesc+>>
1874
1874
1875 Cannot compare with log --follow-first FILE as it never worked
1875 Cannot compare with log --follow-first FILE as it never worked
1876
1876
1877 $ hg log -G --print-revset --follow-first e
1877 $ hg log -G --print-revset --follow-first e
1878 []
1878 []
1879 (func
1879 (func
1880 (symbol '_followfirst')
1880 (symbol '_followfirst')
1881 (string 'e'))
1881 (string 'e'))
1882 <filteredset
1882 <filteredset
1883 <spanset- 0:7>,
1883 <spanset- 0:7>,
1884 <generatorsetdesc+>>
1884 <generatorsetdesc+>>
1885 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1885 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1886 @ 6 merge 5 and 4
1886 @ 6 merge 5 and 4
1887 |\
1887 |\
1888 | ~
1888 | ~
1889 o 5 add another e
1889 o 5 add another e
1890 |
1890 |
1891 ~
1891 ~
1892
1892
1893 Test --copies
1893 Test --copies
1894
1894
1895 $ hg log -G --copies --template "{rev} {desc|firstline} \
1895 $ hg log -G --copies --template "{rev} {desc|firstline} \
1896 > copies: {file_copies_switch}\n"
1896 > copies: {file_copies_switch}\n"
1897 @ 6 merge 5 and 4 copies:
1897 @ 6 merge 5 and 4 copies:
1898 |\
1898 |\
1899 | o 5 add another e copies:
1899 | o 5 add another e copies:
1900 | |
1900 | |
1901 o | 4 mv dir/b e copies: e (dir/b)
1901 o | 4 mv dir/b e copies: e (dir/b)
1902 |/
1902 |/
1903 o 3 mv a b; add d copies: b (a)g (f)
1903 o 3 mv a b; add d copies: b (a)g (f)
1904 |
1904 |
1905 o 2 mv b dir/b copies: dir/b (b)
1905 o 2 mv b dir/b copies: dir/b (b)
1906 |
1906 |
1907 o 1 copy a b copies: b (a)g (f)
1907 o 1 copy a b copies: b (a)g (f)
1908 |
1908 |
1909 o 0 add a copies:
1909 o 0 add a copies:
1910
1910
1911 Test "set:..." and parent revision
1911 Test "set:..." and parent revision
1912
1912
1913 $ hg up -q 4
1913 $ hg up -q 4
1914 $ testlog "set:copied()"
1914 $ testlog "set:copied()"
1915 []
1915 []
1916 (func
1916 (func
1917 (symbol '_matchfiles')
1917 (symbol '_matchfiles')
1918 (list
1918 (list
1919 (string 'r:')
1919 (string 'r:')
1920 (string 'd:relpath')
1920 (string 'd:relpath')
1921 (string 'p:set:copied()')))
1921 (string 'p:set:copied()')))
1922 <filteredset
1922 <filteredset
1923 <spanset- 0:7>,
1923 <spanset- 0:7>,
1924 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=None>>
1924 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=None>>
1925 $ testlog --include "set:copied()"
1925 $ testlog --include "set:copied()"
1926 []
1926 []
1927 (func
1927 (func
1928 (symbol '_matchfiles')
1928 (symbol '_matchfiles')
1929 (list
1929 (list
1930 (string 'r:')
1930 (string 'r:')
1931 (string 'd:relpath')
1931 (string 'd:relpath')
1932 (string 'i:set:copied()')))
1932 (string 'i:set:copied()')))
1933 <filteredset
1933 <filteredset
1934 <spanset- 0:7>,
1934 <spanset- 0:7>,
1935 <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=None>>
1935 <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=None>>
1936 $ testlog -r "sort(file('set:copied()'), -rev)"
1936 $ testlog -r "sort(file('set:copied()'), -rev)"
1937 ["sort(file('set:copied()'), -rev)"]
1937 ["sort(file('set:copied()'), -rev)"]
1938 []
1938 []
1939 <baseset []>
1939 <baseset []>
1940
1940
1941 Test --removed
1941 Test --removed
1942
1942
1943 $ testlog --removed
1943 $ testlog --removed
1944 []
1944 []
1945 []
1945 []
1946 <spanset- 0:7>
1946 <spanset- 0:7>
1947 $ testlog --removed a
1947 $ testlog --removed a
1948 []
1948 []
1949 (func
1949 (func
1950 (symbol '_matchfiles')
1950 (symbol '_matchfiles')
1951 (list
1951 (list
1952 (string 'r:')
1952 (string 'r:')
1953 (string 'd:relpath')
1953 (string 'd:relpath')
1954 (string 'p:a')))
1954 (string 'p:a')))
1955 <filteredset
1955 <filteredset
1956 <spanset- 0:7>,
1956 <spanset- 0:7>,
1957 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
1957 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
1958 $ testlog --removed --follow a
1958 $ testlog --removed --follow a
1959 []
1959 []
1960 (and
1960 (and
1961 (func
1961 (func
1962 (symbol 'ancestors')
1962 (symbol 'ancestors')
1963 (symbol '.'))
1963 (symbol '.'))
1964 (func
1964 (func
1965 (symbol '_matchfiles')
1965 (symbol '_matchfiles')
1966 (list
1966 (list
1967 (string 'r:')
1967 (string 'r:')
1968 (string 'd:relpath')
1968 (string 'd:relpath')
1969 (string 'p:a'))))
1969 (string 'p:a'))))
1970 <filteredset
1970 <filteredset
1971 <filteredset
1971 <filteredset
1972 <spanset- 0:5>,
1972 <spanset- 0:5>,
1973 <generatorsetdesc+>>,
1973 <generatorsetdesc+>>,
1974 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
1974 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=None>>
1975
1975
1976 Test --patch and --stat with --follow and --follow-first
1976 Test --patch and --stat with --follow and --follow-first
1977
1977
1978 $ hg up -q 3
1978 $ hg up -q 3
1979 $ hg log -G --git --patch b
1979 $ hg log -G --git --patch b
1980 o changeset: 1:216d4c92cf98
1980 o changeset: 1:216d4c92cf98
1981 | user: test
1981 | user: test
1982 ~ date: Thu Jan 01 00:00:00 1970 +0000
1982 ~ date: Thu Jan 01 00:00:00 1970 +0000
1983 summary: copy a b
1983 summary: copy a b
1984
1984
1985 diff --git a/a b/b
1985 diff --git a/a b/b
1986 copy from a
1986 copy from a
1987 copy to b
1987 copy to b
1988
1988
1989
1989
1990 $ hg log -G --git --stat b
1990 $ hg log -G --git --stat b
1991 o changeset: 1:216d4c92cf98
1991 o changeset: 1:216d4c92cf98
1992 | user: test
1992 | user: test
1993 ~ date: Thu Jan 01 00:00:00 1970 +0000
1993 ~ date: Thu Jan 01 00:00:00 1970 +0000
1994 summary: copy a b
1994 summary: copy a b
1995
1995
1996 b | 0
1996 b | 0
1997 1 files changed, 0 insertions(+), 0 deletions(-)
1997 1 files changed, 0 insertions(+), 0 deletions(-)
1998
1998
1999
1999
2000 $ hg log -G --git --patch --follow b
2000 $ hg log -G --git --patch --follow b
2001 o changeset: 1:216d4c92cf98
2001 o changeset: 1:216d4c92cf98
2002 | user: test
2002 | user: test
2003 | date: Thu Jan 01 00:00:00 1970 +0000
2003 | date: Thu Jan 01 00:00:00 1970 +0000
2004 | summary: copy a b
2004 | summary: copy a b
2005 |
2005 |
2006 | diff --git a/a b/b
2006 | diff --git a/a b/b
2007 | copy from a
2007 | copy from a
2008 | copy to b
2008 | copy to b
2009 |
2009 |
2010 o changeset: 0:f8035bb17114
2010 o changeset: 0:f8035bb17114
2011 user: test
2011 user: test
2012 date: Thu Jan 01 00:00:00 1970 +0000
2012 date: Thu Jan 01 00:00:00 1970 +0000
2013 summary: add a
2013 summary: add a
2014
2014
2015 diff --git a/a b/a
2015 diff --git a/a b/a
2016 new file mode 100644
2016 new file mode 100644
2017 --- /dev/null
2017 --- /dev/null
2018 +++ b/a
2018 +++ b/a
2019 @@ -0,0 +1,1 @@
2019 @@ -0,0 +1,1 @@
2020 +a
2020 +a
2021
2021
2022
2022
2023 $ hg log -G --git --stat --follow b
2023 $ hg log -G --git --stat --follow b
2024 o changeset: 1:216d4c92cf98
2024 o changeset: 1:216d4c92cf98
2025 | user: test
2025 | user: test
2026 | date: Thu Jan 01 00:00:00 1970 +0000
2026 | date: Thu Jan 01 00:00:00 1970 +0000
2027 | summary: copy a b
2027 | summary: copy a b
2028 |
2028 |
2029 | b | 0
2029 | b | 0
2030 | 1 files changed, 0 insertions(+), 0 deletions(-)
2030 | 1 files changed, 0 insertions(+), 0 deletions(-)
2031 |
2031 |
2032 o changeset: 0:f8035bb17114
2032 o changeset: 0:f8035bb17114
2033 user: test
2033 user: test
2034 date: Thu Jan 01 00:00:00 1970 +0000
2034 date: Thu Jan 01 00:00:00 1970 +0000
2035 summary: add a
2035 summary: add a
2036
2036
2037 a | 1 +
2037 a | 1 +
2038 1 files changed, 1 insertions(+), 0 deletions(-)
2038 1 files changed, 1 insertions(+), 0 deletions(-)
2039
2039
2040
2040
2041 $ hg up -q 6
2041 $ hg up -q 6
2042 $ hg log -G --git --patch --follow-first e
2042 $ hg log -G --git --patch --follow-first e
2043 @ changeset: 6:fc281d8ff18d
2043 @ changeset: 6:fc281d8ff18d
2044 |\ tag: tip
2044 |\ tag: tip
2045 | ~ parent: 5:99b31f1c2782
2045 | ~ parent: 5:99b31f1c2782
2046 | parent: 4:17d952250a9d
2046 | parent: 4:17d952250a9d
2047 | user: test
2047 | user: test
2048 | date: Thu Jan 01 00:00:00 1970 +0000
2048 | date: Thu Jan 01 00:00:00 1970 +0000
2049 | summary: merge 5 and 4
2049 | summary: merge 5 and 4
2050 |
2050 |
2051 | diff --git a/e b/e
2051 | diff --git a/e b/e
2052 | --- a/e
2052 | --- a/e
2053 | +++ b/e
2053 | +++ b/e
2054 | @@ -1,1 +1,1 @@
2054 | @@ -1,1 +1,1 @@
2055 | -ee
2055 | -ee
2056 | +merge
2056 | +merge
2057 |
2057 |
2058 o changeset: 5:99b31f1c2782
2058 o changeset: 5:99b31f1c2782
2059 | parent: 3:5918b8d165d1
2059 | parent: 3:5918b8d165d1
2060 ~ user: test
2060 ~ user: test
2061 date: Thu Jan 01 00:00:00 1970 +0000
2061 date: Thu Jan 01 00:00:00 1970 +0000
2062 summary: add another e
2062 summary: add another e
2063
2063
2064 diff --git a/e b/e
2064 diff --git a/e b/e
2065 new file mode 100644
2065 new file mode 100644
2066 --- /dev/null
2066 --- /dev/null
2067 +++ b/e
2067 +++ b/e
2068 @@ -0,0 +1,1 @@
2068 @@ -0,0 +1,1 @@
2069 +ee
2069 +ee
2070
2070
2071
2071
2072 Test old-style --rev
2072 Test old-style --rev
2073
2073
2074 $ hg tag 'foo-bar'
2074 $ hg tag 'foo-bar'
2075 $ testlog -r 'foo-bar'
2075 $ testlog -r 'foo-bar'
2076 ['foo-bar']
2076 ['foo-bar']
2077 []
2077 []
2078 <baseset [6]>
2078 <baseset [6]>
2079
2079
2080 Test --follow and forward --rev
2080 Test --follow and forward --rev
2081
2081
2082 $ hg up -q 6
2082 $ hg up -q 6
2083 $ echo g > g
2083 $ echo g > g
2084 $ hg ci -Am 'add g' g
2084 $ hg ci -Am 'add g' g
2085 created new head
2085 created new head
2086 $ hg up -q 2
2086 $ hg up -q 2
2087 $ hg log -G --template "{rev} {desc|firstline}\n"
2087 $ hg log -G --template "{rev} {desc|firstline}\n"
2088 o 8 add g
2088 o 8 add g
2089 |
2089 |
2090 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2090 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2091 |/
2091 |/
2092 o 6 merge 5 and 4
2092 o 6 merge 5 and 4
2093 |\
2093 |\
2094 | o 5 add another e
2094 | o 5 add another e
2095 | |
2095 | |
2096 o | 4 mv dir/b e
2096 o | 4 mv dir/b e
2097 |/
2097 |/
2098 o 3 mv a b; add d
2098 o 3 mv a b; add d
2099 |
2099 |
2100 @ 2 mv b dir/b
2100 @ 2 mv b dir/b
2101 |
2101 |
2102 o 1 copy a b
2102 o 1 copy a b
2103 |
2103 |
2104 o 0 add a
2104 o 0 add a
2105
2105
2106 $ hg archive -r 7 archive
2106 $ hg archive -r 7 archive
2107 $ grep changessincelatesttag archive/.hg_archival.txt
2107 $ grep changessincelatesttag archive/.hg_archival.txt
2108 changessincelatesttag: 1
2108 changessincelatesttag: 1
2109 $ rm -r archive
2109 $ rm -r archive
2110
2110
2111 changessincelatesttag with no prior tag
2111 changessincelatesttag with no prior tag
2112 $ hg archive -r 4 archive
2112 $ hg archive -r 4 archive
2113 $ grep changessincelatesttag archive/.hg_archival.txt
2113 $ grep changessincelatesttag archive/.hg_archival.txt
2114 changessincelatesttag: 5
2114 changessincelatesttag: 5
2115
2115
2116 $ hg export 'all()'
2116 $ hg export 'all()'
2117 # HG changeset patch
2117 # HG changeset patch
2118 # User test
2118 # User test
2119 # Date 0 0
2119 # Date 0 0
2120 # Thu Jan 01 00:00:00 1970 +0000
2120 # Thu Jan 01 00:00:00 1970 +0000
2121 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2121 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2122 # Parent 0000000000000000000000000000000000000000
2122 # Parent 0000000000000000000000000000000000000000
2123 add a
2123 add a
2124
2124
2125 diff -r 000000000000 -r f8035bb17114 a
2125 diff -r 000000000000 -r f8035bb17114 a
2126 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2126 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2127 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2127 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2128 @@ -0,0 +1,1 @@
2128 @@ -0,0 +1,1 @@
2129 +a
2129 +a
2130 diff -r 000000000000 -r f8035bb17114 aa
2130 diff -r 000000000000 -r f8035bb17114 aa
2131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2132 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2132 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2133 @@ -0,0 +1,1 @@
2133 @@ -0,0 +1,1 @@
2134 +aa
2134 +aa
2135 diff -r 000000000000 -r f8035bb17114 f
2135 diff -r 000000000000 -r f8035bb17114 f
2136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2137 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2137 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2138 @@ -0,0 +1,1 @@
2138 @@ -0,0 +1,1 @@
2139 +f
2139 +f
2140 # HG changeset patch
2140 # HG changeset patch
2141 # User test
2141 # User test
2142 # Date 0 0
2142 # Date 0 0
2143 # Thu Jan 01 00:00:00 1970 +0000
2143 # Thu Jan 01 00:00:00 1970 +0000
2144 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2144 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2145 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2145 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2146 copy a b
2146 copy a b
2147
2147
2148 diff -r f8035bb17114 -r 216d4c92cf98 b
2148 diff -r f8035bb17114 -r 216d4c92cf98 b
2149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2149 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2150 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2150 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2151 @@ -0,0 +1,1 @@
2151 @@ -0,0 +1,1 @@
2152 +a
2152 +a
2153 diff -r f8035bb17114 -r 216d4c92cf98 g
2153 diff -r f8035bb17114 -r 216d4c92cf98 g
2154 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2154 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2155 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2155 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2156 @@ -0,0 +1,1 @@
2156 @@ -0,0 +1,1 @@
2157 +f
2157 +f
2158 # HG changeset patch
2158 # HG changeset patch
2159 # User test
2159 # User test
2160 # Date 0 0
2160 # Date 0 0
2161 # Thu Jan 01 00:00:00 1970 +0000
2161 # Thu Jan 01 00:00:00 1970 +0000
2162 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2162 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2163 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2163 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2164 mv b dir/b
2164 mv b dir/b
2165
2165
2166 diff -r 216d4c92cf98 -r bb573313a9e8 b
2166 diff -r 216d4c92cf98 -r bb573313a9e8 b
2167 --- a/b Thu Jan 01 00:00:00 1970 +0000
2167 --- a/b Thu Jan 01 00:00:00 1970 +0000
2168 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2168 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2169 @@ -1,1 +0,0 @@
2169 @@ -1,1 +0,0 @@
2170 -a
2170 -a
2171 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2171 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2172 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2172 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2173 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2173 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2174 @@ -0,0 +1,1 @@
2174 @@ -0,0 +1,1 @@
2175 +a
2175 +a
2176 diff -r 216d4c92cf98 -r bb573313a9e8 f
2176 diff -r 216d4c92cf98 -r bb573313a9e8 f
2177 --- a/f Thu Jan 01 00:00:00 1970 +0000
2177 --- a/f Thu Jan 01 00:00:00 1970 +0000
2178 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2178 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2179 @@ -1,1 +1,2 @@
2179 @@ -1,1 +1,2 @@
2180 f
2180 f
2181 +f
2181 +f
2182 diff -r 216d4c92cf98 -r bb573313a9e8 g
2182 diff -r 216d4c92cf98 -r bb573313a9e8 g
2183 --- a/g Thu Jan 01 00:00:00 1970 +0000
2183 --- a/g Thu Jan 01 00:00:00 1970 +0000
2184 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2184 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2185 @@ -1,1 +1,2 @@
2185 @@ -1,1 +1,2 @@
2186 f
2186 f
2187 +g
2187 +g
2188 # HG changeset patch
2188 # HG changeset patch
2189 # User test
2189 # User test
2190 # Date 0 0
2190 # Date 0 0
2191 # Thu Jan 01 00:00:00 1970 +0000
2191 # Thu Jan 01 00:00:00 1970 +0000
2192 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2192 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2193 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2193 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2194 mv a b; add d
2194 mv a b; add d
2195
2195
2196 diff -r bb573313a9e8 -r 5918b8d165d1 a
2196 diff -r bb573313a9e8 -r 5918b8d165d1 a
2197 --- a/a Thu Jan 01 00:00:00 1970 +0000
2197 --- a/a Thu Jan 01 00:00:00 1970 +0000
2198 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2198 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2199 @@ -1,1 +0,0 @@
2199 @@ -1,1 +0,0 @@
2200 -a
2200 -a
2201 diff -r bb573313a9e8 -r 5918b8d165d1 b
2201 diff -r bb573313a9e8 -r 5918b8d165d1 b
2202 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2202 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2203 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2203 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2204 @@ -0,0 +1,1 @@
2204 @@ -0,0 +1,1 @@
2205 +a
2205 +a
2206 diff -r bb573313a9e8 -r 5918b8d165d1 d
2206 diff -r bb573313a9e8 -r 5918b8d165d1 d
2207 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2207 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2208 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2208 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2209 @@ -0,0 +1,1 @@
2209 @@ -0,0 +1,1 @@
2210 +a
2210 +a
2211 diff -r bb573313a9e8 -r 5918b8d165d1 g
2211 diff -r bb573313a9e8 -r 5918b8d165d1 g
2212 --- a/g Thu Jan 01 00:00:00 1970 +0000
2212 --- a/g Thu Jan 01 00:00:00 1970 +0000
2213 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2213 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2214 @@ -1,2 +1,2 @@
2214 @@ -1,2 +1,2 @@
2215 f
2215 f
2216 -g
2216 -g
2217 +f
2217 +f
2218 # HG changeset patch
2218 # HG changeset patch
2219 # User test
2219 # User test
2220 # Date 0 0
2220 # Date 0 0
2221 # Thu Jan 01 00:00:00 1970 +0000
2221 # Thu Jan 01 00:00:00 1970 +0000
2222 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2222 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2223 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2223 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2224 mv dir/b e
2224 mv dir/b e
2225
2225
2226 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2226 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2227 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2227 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2228 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2228 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2229 @@ -1,1 +0,0 @@
2229 @@ -1,1 +0,0 @@
2230 -a
2230 -a
2231 diff -r 5918b8d165d1 -r 17d952250a9d e
2231 diff -r 5918b8d165d1 -r 17d952250a9d e
2232 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2232 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2233 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2233 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2234 @@ -0,0 +1,1 @@
2234 @@ -0,0 +1,1 @@
2235 +a
2235 +a
2236 # HG changeset patch
2236 # HG changeset patch
2237 # User test
2237 # User test
2238 # Date 0 0
2238 # Date 0 0
2239 # Thu Jan 01 00:00:00 1970 +0000
2239 # Thu Jan 01 00:00:00 1970 +0000
2240 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2240 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2241 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2241 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2242 add another e
2242 add another e
2243
2243
2244 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2244 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2245 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2245 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2246 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2246 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2247 @@ -0,0 +1,1 @@
2247 @@ -0,0 +1,1 @@
2248 +ee
2248 +ee
2249 # HG changeset patch
2249 # HG changeset patch
2250 # User test
2250 # User test
2251 # Date 0 0
2251 # Date 0 0
2252 # Thu Jan 01 00:00:00 1970 +0000
2252 # Thu Jan 01 00:00:00 1970 +0000
2253 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2253 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2254 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2254 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2255 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2255 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2256 merge 5 and 4
2256 merge 5 and 4
2257
2257
2258 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2258 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2259 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2259 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2260 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2260 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2261 @@ -1,1 +0,0 @@
2261 @@ -1,1 +0,0 @@
2262 -a
2262 -a
2263 diff -r 99b31f1c2782 -r fc281d8ff18d e
2263 diff -r 99b31f1c2782 -r fc281d8ff18d e
2264 --- a/e Thu Jan 01 00:00:00 1970 +0000
2264 --- a/e Thu Jan 01 00:00:00 1970 +0000
2265 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2265 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2266 @@ -1,1 +1,1 @@
2266 @@ -1,1 +1,1 @@
2267 -ee
2267 -ee
2268 +merge
2268 +merge
2269 # HG changeset patch
2269 # HG changeset patch
2270 # User test
2270 # User test
2271 # Date 0 0
2271 # Date 0 0
2272 # Thu Jan 01 00:00:00 1970 +0000
2272 # Thu Jan 01 00:00:00 1970 +0000
2273 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2273 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2274 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2274 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2275 Added tag foo-bar for changeset fc281d8ff18d
2275 Added tag foo-bar for changeset fc281d8ff18d
2276
2276
2277 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2277 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2278 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2278 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2279 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2279 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2280 @@ -0,0 +1,1 @@
2280 @@ -0,0 +1,1 @@
2281 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2281 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2282 # HG changeset patch
2282 # HG changeset patch
2283 # User test
2283 # User test
2284 # Date 0 0
2284 # Date 0 0
2285 # Thu Jan 01 00:00:00 1970 +0000
2285 # Thu Jan 01 00:00:00 1970 +0000
2286 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2286 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2287 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2287 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2288 add g
2288 add g
2289
2289
2290 diff -r fc281d8ff18d -r 24c2e826ddeb g
2290 diff -r fc281d8ff18d -r 24c2e826ddeb g
2291 --- a/g Thu Jan 01 00:00:00 1970 +0000
2291 --- a/g Thu Jan 01 00:00:00 1970 +0000
2292 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2292 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2293 @@ -1,2 +1,1 @@
2293 @@ -1,2 +1,1 @@
2294 -f
2294 -f
2295 -f
2295 -f
2296 +g
2296 +g
2297 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2297 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2298 ['reverse(::(((6) or (8)) or ((5) or ((7) or (4)))))']
2298 ['6', '8', '5', '7', '4']
2299 []
2299 []
2300 <generatorsetdesc->
2300 <generatorsetdesc->
2301
2301
2302 Test --follow-first and forward --rev
2302 Test --follow-first and forward --rev
2303
2303
2304 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2304 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2305 ['reverse(_firstancestors((((6) or (8)) or ((5) or ((7) or (4))))))']
2305 ['6', '8', '5', '7', '4']
2306 []
2306 []
2307 <generatorsetdesc->
2307 <generatorsetdesc->
2308
2308
2309 Test --follow and backward --rev
2309 Test --follow and backward --rev
2310
2310
2311 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2311 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2312 ['reverse(::(((6) or (5)) or ((7) or ((8) or (4)))))']
2312 ['6', '5', '7', '8', '4']
2313 []
2313 []
2314 <generatorsetdesc->
2314 <generatorsetdesc->
2315
2315
2316 Test --follow-first and backward --rev
2316 Test --follow-first and backward --rev
2317
2317
2318 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2318 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2319 ['reverse(_firstancestors((((6) or (5)) or ((7) or ((8) or (4))))))']
2319 ['6', '5', '7', '8', '4']
2320 []
2320 []
2321 <generatorsetdesc->
2321 <generatorsetdesc->
2322
2322
2323 Test --follow with --rev of graphlog extension
2323 Test --follow with --rev of graphlog extension
2324
2324
2325 $ hg --config extensions.graphlog= glog -qfr1
2325 $ hg --config extensions.graphlog= glog -qfr1
2326 o 1:216d4c92cf98
2326 o 1:216d4c92cf98
2327 |
2327 |
2328 o 0:f8035bb17114
2328 o 0:f8035bb17114
2329
2329
2330
2330
2331 Test subdir
2331 Test subdir
2332
2332
2333 $ hg up -q 3
2333 $ hg up -q 3
2334 $ cd dir
2334 $ cd dir
2335 $ testlog .
2335 $ testlog .
2336 []
2336 []
2337 (func
2337 (func
2338 (symbol '_matchfiles')
2338 (symbol '_matchfiles')
2339 (list
2339 (list
2340 (string 'r:')
2340 (string 'r:')
2341 (string 'd:relpath')
2341 (string 'd:relpath')
2342 (string 'p:.')))
2342 (string 'p:.')))
2343 <filteredset
2343 <filteredset
2344 <spanset- 0:9>,
2344 <spanset- 0:9>,
2345 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=None>>
2345 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=None>>
2346 $ testlog ../b
2346 $ testlog ../b
2347 []
2347 []
2348 (func
2348 (func
2349 (symbol 'filelog')
2349 (symbol 'filelog')
2350 (string '../b'))
2350 (string '../b'))
2351 <filteredset
2351 <filteredset
2352 <spanset- 0:9>, set([1])>
2352 <spanset- 0:9>, set([1])>
2353 $ testlog -f ../b
2353 $ testlog -f ../b
2354 []
2354 []
2355 (func
2355 (func
2356 (symbol 'follow')
2356 (symbol 'follow')
2357 (string 'b'))
2357 (string 'b'))
2358 <filteredset
2358 <filteredset
2359 <spanset- 0:4>,
2359 <spanset- 0:4>,
2360 <generatorsetdesc+>>
2360 <generatorsetdesc+>>
2361 $ cd ..
2361 $ cd ..
2362
2362
2363 Test --hidden
2363 Test --hidden
2364 (enable obsolete)
2364 (enable obsolete)
2365
2365
2366 $ cat >> $HGRCPATH << EOF
2366 $ cat >> $HGRCPATH << EOF
2367 > [experimental]
2367 > [experimental]
2368 > evolution.createmarkers=True
2368 > evolution.createmarkers=True
2369 > EOF
2369 > EOF
2370
2370
2371 $ hg debugobsolete `hg id --debug -i -r 8`
2371 $ hg debugobsolete `hg id --debug -i -r 8`
2372 obsoleted 1 changesets
2372 obsoleted 1 changesets
2373 $ testlog
2373 $ testlog
2374 []
2374 []
2375 []
2375 []
2376 <spanset- 0:9>
2376 <spanset- 0:9>
2377 $ testlog --hidden
2377 $ testlog --hidden
2378 []
2378 []
2379 []
2379 []
2380 <spanset- 0:9>
2380 <spanset- 0:9>
2381 $ hg log -G --template '{rev} {desc}\n'
2381 $ hg log -G --template '{rev} {desc}\n'
2382 o 7 Added tag foo-bar for changeset fc281d8ff18d
2382 o 7 Added tag foo-bar for changeset fc281d8ff18d
2383 |
2383 |
2384 o 6 merge 5 and 4
2384 o 6 merge 5 and 4
2385 |\
2385 |\
2386 | o 5 add another e
2386 | o 5 add another e
2387 | |
2387 | |
2388 o | 4 mv dir/b e
2388 o | 4 mv dir/b e
2389 |/
2389 |/
2390 @ 3 mv a b; add d
2390 @ 3 mv a b; add d
2391 |
2391 |
2392 o 2 mv b dir/b
2392 o 2 mv b dir/b
2393 |
2393 |
2394 o 1 copy a b
2394 o 1 copy a b
2395 |
2395 |
2396 o 0 add a
2396 o 0 add a
2397
2397
2398
2398
2399 A template without trailing newline should do something sane
2399 A template without trailing newline should do something sane
2400
2400
2401 $ hg log -G -r ::2 --template '{rev} {desc}'
2401 $ hg log -G -r ::2 --template '{rev} {desc}'
2402 o 2 mv b dir/b
2402 o 2 mv b dir/b
2403 |
2403 |
2404 o 1 copy a b
2404 o 1 copy a b
2405 |
2405 |
2406 o 0 add a
2406 o 0 add a
2407
2407
2408
2408
2409 Extra newlines must be preserved
2409 Extra newlines must be preserved
2410
2410
2411 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2411 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2412 o
2412 o
2413 | 2 mv b dir/b
2413 | 2 mv b dir/b
2414 |
2414 |
2415 o
2415 o
2416 | 1 copy a b
2416 | 1 copy a b
2417 |
2417 |
2418 o
2418 o
2419 0 add a
2419 0 add a
2420
2420
2421
2421
2422 The almost-empty template should do something sane too ...
2422 The almost-empty template should do something sane too ...
2423
2423
2424 $ hg log -G -r ::2 --template '\n'
2424 $ hg log -G -r ::2 --template '\n'
2425 o
2425 o
2426 |
2426 |
2427 o
2427 o
2428 |
2428 |
2429 o
2429 o
2430
2430
2431
2431
2432 issue3772
2432 issue3772
2433
2433
2434 $ hg log -G -r :null
2434 $ hg log -G -r :null
2435 o changeset: 0:f8035bb17114
2435 o changeset: 0:f8035bb17114
2436 | user: test
2436 | user: test
2437 | date: Thu Jan 01 00:00:00 1970 +0000
2437 | date: Thu Jan 01 00:00:00 1970 +0000
2438 | summary: add a
2438 | summary: add a
2439 |
2439 |
2440 o changeset: -1:000000000000
2440 o changeset: -1:000000000000
2441 user:
2441 user:
2442 date: Thu Jan 01 00:00:00 1970 +0000
2442 date: Thu Jan 01 00:00:00 1970 +0000
2443
2443
2444 $ hg log -G -r null:null
2444 $ hg log -G -r null:null
2445 o changeset: -1:000000000000
2445 o changeset: -1:000000000000
2446 user:
2446 user:
2447 date: Thu Jan 01 00:00:00 1970 +0000
2447 date: Thu Jan 01 00:00:00 1970 +0000
2448
2448
2449
2449
2450 should not draw line down to null due to the magic of fullreposet
2450 should not draw line down to null due to the magic of fullreposet
2451
2451
2452 $ hg log -G -r 'all()' | tail -6
2452 $ hg log -G -r 'all()' | tail -6
2453 |
2453 |
2454 o changeset: 0:f8035bb17114
2454 o changeset: 0:f8035bb17114
2455 user: test
2455 user: test
2456 date: Thu Jan 01 00:00:00 1970 +0000
2456 date: Thu Jan 01 00:00:00 1970 +0000
2457 summary: add a
2457 summary: add a
2458
2458
2459
2459
2460 $ hg log -G -r 'branch(default)' | tail -6
2460 $ hg log -G -r 'branch(default)' | tail -6
2461 |
2461 |
2462 o changeset: 0:f8035bb17114
2462 o changeset: 0:f8035bb17114
2463 user: test
2463 user: test
2464 date: Thu Jan 01 00:00:00 1970 +0000
2464 date: Thu Jan 01 00:00:00 1970 +0000
2465 summary: add a
2465 summary: add a
2466
2466
2467
2467
2468 working-directory revision
2468 working-directory revision
2469
2469
2470 $ hg log -G -qr '. + wdir()'
2470 $ hg log -G -qr '. + wdir()'
2471 o 2147483647:ffffffffffff
2471 o 2147483647:ffffffffffff
2472 |
2472 |
2473 @ 3:5918b8d165d1
2473 @ 3:5918b8d165d1
2474 |
2474 |
2475 ~
2475 ~
2476
2476
2477 node template with changeset_printer:
2477 node template with changeset_printer:
2478
2478
2479 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2479 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2480 7 7:02dbb8e276b8
2480 7 7:02dbb8e276b8
2481 |
2481 |
2482 6 6:fc281d8ff18d
2482 6 6:fc281d8ff18d
2483 |\
2483 |\
2484 | ~
2484 | ~
2485 5 5:99b31f1c2782
2485 5 5:99b31f1c2782
2486 |
2486 |
2487 ~
2487 ~
2488
2488
2489 node template with changeset_templater (shared cache variable):
2489 node template with changeset_templater (shared cache variable):
2490
2490
2491 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2491 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2492 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2492 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2493 o 7 foo-bar+1
2493 o 7 foo-bar+1
2494 |
2494 |
2495 # 6 foo-bar+0
2495 # 6 foo-bar+0
2496 |\
2496 |\
2497 | ~
2497 | ~
2498 o 5 null+5
2498 o 5 null+5
2499 |
2499 |
2500 ~
2500 ~
2501
2501
2502 label() should just work in node template:
2502 label() should just work in node template:
2503
2503
2504 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2504 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2505 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2505 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2506 [branch.default|7] [log.node|7:02dbb8e276b8]
2506 [branch.default|7] [log.node|7:02dbb8e276b8]
2507 |
2507 |
2508 ~
2508 ~
2509
2509
2510 $ cd ..
2510 $ cd ..
2511
2511
2512 change graph edge styling
2512 change graph edge styling
2513
2513
2514 $ cd repo
2514 $ cd repo
2515 $ cat << EOF >> $HGRCPATH
2515 $ cat << EOF >> $HGRCPATH
2516 > [experimental]
2516 > [experimental]
2517 > graphstyle.parent = |
2517 > graphstyle.parent = |
2518 > graphstyle.grandparent = :
2518 > graphstyle.grandparent = :
2519 > graphstyle.missing =
2519 > graphstyle.missing =
2520 > EOF
2520 > EOF
2521 $ hg log -G -r 'file("a")' -m
2521 $ hg log -G -r 'file("a")' -m
2522 @ changeset: 36:08a19a744424
2522 @ changeset: 36:08a19a744424
2523 : branch: branch
2523 : branch: branch
2524 : tag: tip
2524 : tag: tip
2525 : parent: 35:9159c3644c5e
2525 : parent: 35:9159c3644c5e
2526 : parent: 35:9159c3644c5e
2526 : parent: 35:9159c3644c5e
2527 : user: test
2527 : user: test
2528 : date: Thu Jan 01 00:00:36 1970 +0000
2528 : date: Thu Jan 01 00:00:36 1970 +0000
2529 : summary: (36) buggy merge: identical parents
2529 : summary: (36) buggy merge: identical parents
2530 :
2530 :
2531 o changeset: 32:d06dffa21a31
2531 o changeset: 32:d06dffa21a31
2532 |\ parent: 27:886ed638191b
2532 |\ parent: 27:886ed638191b
2533 | : parent: 31:621d83e11f67
2533 | : parent: 31:621d83e11f67
2534 | : user: test
2534 | : user: test
2535 | : date: Thu Jan 01 00:00:32 1970 +0000
2535 | : date: Thu Jan 01 00:00:32 1970 +0000
2536 | : summary: (32) expand
2536 | : summary: (32) expand
2537 | :
2537 | :
2538 o : changeset: 31:621d83e11f67
2538 o : changeset: 31:621d83e11f67
2539 |\: parent: 21:d42a756af44d
2539 |\: parent: 21:d42a756af44d
2540 | : parent: 30:6e11cd4b648f
2540 | : parent: 30:6e11cd4b648f
2541 | : user: test
2541 | : user: test
2542 | : date: Thu Jan 01 00:00:31 1970 +0000
2542 | : date: Thu Jan 01 00:00:31 1970 +0000
2543 | : summary: (31) expand
2543 | : summary: (31) expand
2544 | :
2544 | :
2545 o : changeset: 30:6e11cd4b648f
2545 o : changeset: 30:6e11cd4b648f
2546 |\ \ parent: 28:44ecd0b9ae99
2546 |\ \ parent: 28:44ecd0b9ae99
2547 | ~ : parent: 29:cd9bb2be7593
2547 | ~ : parent: 29:cd9bb2be7593
2548 | : user: test
2548 | : user: test
2549 | : date: Thu Jan 01 00:00:30 1970 +0000
2549 | : date: Thu Jan 01 00:00:30 1970 +0000
2550 | : summary: (30) expand
2550 | : summary: (30) expand
2551 | /
2551 | /
2552 o : changeset: 28:44ecd0b9ae99
2552 o : changeset: 28:44ecd0b9ae99
2553 |\ \ parent: 1:6db2ef61d156
2553 |\ \ parent: 1:6db2ef61d156
2554 | ~ : parent: 26:7f25b6c2f0b9
2554 | ~ : parent: 26:7f25b6c2f0b9
2555 | : user: test
2555 | : user: test
2556 | : date: Thu Jan 01 00:00:28 1970 +0000
2556 | : date: Thu Jan 01 00:00:28 1970 +0000
2557 | : summary: (28) merge zero known
2557 | : summary: (28) merge zero known
2558 | /
2558 | /
2559 o : changeset: 26:7f25b6c2f0b9
2559 o : changeset: 26:7f25b6c2f0b9
2560 |\ \ parent: 18:1aa84d96232a
2560 |\ \ parent: 18:1aa84d96232a
2561 | | : parent: 25:91da8ed57247
2561 | | : parent: 25:91da8ed57247
2562 | | : user: test
2562 | | : user: test
2563 | | : date: Thu Jan 01 00:00:26 1970 +0000
2563 | | : date: Thu Jan 01 00:00:26 1970 +0000
2564 | | : summary: (26) merge one known; far right
2564 | | : summary: (26) merge one known; far right
2565 | | :
2565 | | :
2566 | o : changeset: 25:91da8ed57247
2566 | o : changeset: 25:91da8ed57247
2567 | |\: parent: 21:d42a756af44d
2567 | |\: parent: 21:d42a756af44d
2568 | | : parent: 24:a9c19a3d96b7
2568 | | : parent: 24:a9c19a3d96b7
2569 | | : user: test
2569 | | : user: test
2570 | | : date: Thu Jan 01 00:00:25 1970 +0000
2570 | | : date: Thu Jan 01 00:00:25 1970 +0000
2571 | | : summary: (25) merge one known; far left
2571 | | : summary: (25) merge one known; far left
2572 | | :
2572 | | :
2573 | o : changeset: 24:a9c19a3d96b7
2573 | o : changeset: 24:a9c19a3d96b7
2574 | |\ \ parent: 0:e6eb3150255d
2574 | |\ \ parent: 0:e6eb3150255d
2575 | | ~ : parent: 23:a01cddf0766d
2575 | | ~ : parent: 23:a01cddf0766d
2576 | | : user: test
2576 | | : user: test
2577 | | : date: Thu Jan 01 00:00:24 1970 +0000
2577 | | : date: Thu Jan 01 00:00:24 1970 +0000
2578 | | : summary: (24) merge one known; immediate right
2578 | | : summary: (24) merge one known; immediate right
2579 | | /
2579 | | /
2580 | o : changeset: 23:a01cddf0766d
2580 | o : changeset: 23:a01cddf0766d
2581 | |\ \ parent: 1:6db2ef61d156
2581 | |\ \ parent: 1:6db2ef61d156
2582 | | ~ : parent: 22:e0d9cccacb5d
2582 | | ~ : parent: 22:e0d9cccacb5d
2583 | | : user: test
2583 | | : user: test
2584 | | : date: Thu Jan 01 00:00:23 1970 +0000
2584 | | : date: Thu Jan 01 00:00:23 1970 +0000
2585 | | : summary: (23) merge one known; immediate left
2585 | | : summary: (23) merge one known; immediate left
2586 | | /
2586 | | /
2587 | o : changeset: 22:e0d9cccacb5d
2587 | o : changeset: 22:e0d9cccacb5d
2588 |/:/ parent: 18:1aa84d96232a
2588 |/:/ parent: 18:1aa84d96232a
2589 | : parent: 21:d42a756af44d
2589 | : parent: 21:d42a756af44d
2590 | : user: test
2590 | : user: test
2591 | : date: Thu Jan 01 00:00:22 1970 +0000
2591 | : date: Thu Jan 01 00:00:22 1970 +0000
2592 | : summary: (22) merge two known; one far left, one far right
2592 | : summary: (22) merge two known; one far left, one far right
2593 | :
2593 | :
2594 | o changeset: 21:d42a756af44d
2594 | o changeset: 21:d42a756af44d
2595 | |\ parent: 19:31ddc2c1573b
2595 | |\ parent: 19:31ddc2c1573b
2596 | | | parent: 20:d30ed6450e32
2596 | | | parent: 20:d30ed6450e32
2597 | | | user: test
2597 | | | user: test
2598 | | | date: Thu Jan 01 00:00:21 1970 +0000
2598 | | | date: Thu Jan 01 00:00:21 1970 +0000
2599 | | | summary: (21) expand
2599 | | | summary: (21) expand
2600 | | |
2600 | | |
2601 +---o changeset: 20:d30ed6450e32
2601 +---o changeset: 20:d30ed6450e32
2602 | | | parent: 0:e6eb3150255d
2602 | | | parent: 0:e6eb3150255d
2603 | | ~ parent: 18:1aa84d96232a
2603 | | ~ parent: 18:1aa84d96232a
2604 | | user: test
2604 | | user: test
2605 | | date: Thu Jan 01 00:00:20 1970 +0000
2605 | | date: Thu Jan 01 00:00:20 1970 +0000
2606 | | summary: (20) merge two known; two far right
2606 | | summary: (20) merge two known; two far right
2607 | |
2607 | |
2608 | o changeset: 19:31ddc2c1573b
2608 | o changeset: 19:31ddc2c1573b
2609 | |\ parent: 15:1dda3f72782d
2609 | |\ parent: 15:1dda3f72782d
2610 | | | parent: 17:44765d7c06e0
2610 | | | parent: 17:44765d7c06e0
2611 | | | user: test
2611 | | | user: test
2612 | | | date: Thu Jan 01 00:00:19 1970 +0000
2612 | | | date: Thu Jan 01 00:00:19 1970 +0000
2613 | | | summary: (19) expand
2613 | | | summary: (19) expand
2614 | | |
2614 | | |
2615 o | | changeset: 18:1aa84d96232a
2615 o | | changeset: 18:1aa84d96232a
2616 |\| | parent: 1:6db2ef61d156
2616 |\| | parent: 1:6db2ef61d156
2617 ~ | | parent: 15:1dda3f72782d
2617 ~ | | parent: 15:1dda3f72782d
2618 | | user: test
2618 | | user: test
2619 | | date: Thu Jan 01 00:00:18 1970 +0000
2619 | | date: Thu Jan 01 00:00:18 1970 +0000
2620 | | summary: (18) merge two known; two far left
2620 | | summary: (18) merge two known; two far left
2621 / /
2621 / /
2622 | o changeset: 17:44765d7c06e0
2622 | o changeset: 17:44765d7c06e0
2623 | |\ parent: 12:86b91144a6e9
2623 | |\ parent: 12:86b91144a6e9
2624 | | | parent: 16:3677d192927d
2624 | | | parent: 16:3677d192927d
2625 | | | user: test
2625 | | | user: test
2626 | | | date: Thu Jan 01 00:00:17 1970 +0000
2626 | | | date: Thu Jan 01 00:00:17 1970 +0000
2627 | | | summary: (17) expand
2627 | | | summary: (17) expand
2628 | | |
2628 | | |
2629 | | o changeset: 16:3677d192927d
2629 | | o changeset: 16:3677d192927d
2630 | | |\ parent: 0:e6eb3150255d
2630 | | |\ parent: 0:e6eb3150255d
2631 | | ~ ~ parent: 1:6db2ef61d156
2631 | | ~ ~ parent: 1:6db2ef61d156
2632 | | user: test
2632 | | user: test
2633 | | date: Thu Jan 01 00:00:16 1970 +0000
2633 | | date: Thu Jan 01 00:00:16 1970 +0000
2634 | | summary: (16) merge two known; one immediate right, one near right
2634 | | summary: (16) merge two known; one immediate right, one near right
2635 | |
2635 | |
2636 o | changeset: 15:1dda3f72782d
2636 o | changeset: 15:1dda3f72782d
2637 |\ \ parent: 13:22d8966a97e3
2637 |\ \ parent: 13:22d8966a97e3
2638 | | | parent: 14:8eac370358ef
2638 | | | parent: 14:8eac370358ef
2639 | | | user: test
2639 | | | user: test
2640 | | | date: Thu Jan 01 00:00:15 1970 +0000
2640 | | | date: Thu Jan 01 00:00:15 1970 +0000
2641 | | | summary: (15) expand
2641 | | | summary: (15) expand
2642 | | |
2642 | | |
2643 | o | changeset: 14:8eac370358ef
2643 | o | changeset: 14:8eac370358ef
2644 | |\| parent: 0:e6eb3150255d
2644 | |\| parent: 0:e6eb3150255d
2645 | ~ | parent: 12:86b91144a6e9
2645 | ~ | parent: 12:86b91144a6e9
2646 | | user: test
2646 | | user: test
2647 | | date: Thu Jan 01 00:00:14 1970 +0000
2647 | | date: Thu Jan 01 00:00:14 1970 +0000
2648 | | summary: (14) merge two known; one immediate right, one far right
2648 | | summary: (14) merge two known; one immediate right, one far right
2649 | /
2649 | /
2650 o | changeset: 13:22d8966a97e3
2650 o | changeset: 13:22d8966a97e3
2651 |\ \ parent: 9:7010c0af0a35
2651 |\ \ parent: 9:7010c0af0a35
2652 | | | parent: 11:832d76e6bdf2
2652 | | | parent: 11:832d76e6bdf2
2653 | | | user: test
2653 | | | user: test
2654 | | | date: Thu Jan 01 00:00:13 1970 +0000
2654 | | | date: Thu Jan 01 00:00:13 1970 +0000
2655 | | | summary: (13) expand
2655 | | | summary: (13) expand
2656 | | |
2656 | | |
2657 +---o changeset: 12:86b91144a6e9
2657 +---o changeset: 12:86b91144a6e9
2658 | | | parent: 1:6db2ef61d156
2658 | | | parent: 1:6db2ef61d156
2659 | | ~ parent: 9:7010c0af0a35
2659 | | ~ parent: 9:7010c0af0a35
2660 | | user: test
2660 | | user: test
2661 | | date: Thu Jan 01 00:00:12 1970 +0000
2661 | | date: Thu Jan 01 00:00:12 1970 +0000
2662 | | summary: (12) merge two known; one immediate right, one far left
2662 | | summary: (12) merge two known; one immediate right, one far left
2663 | |
2663 | |
2664 | o changeset: 11:832d76e6bdf2
2664 | o changeset: 11:832d76e6bdf2
2665 | |\ parent: 6:b105a072e251
2665 | |\ parent: 6:b105a072e251
2666 | | | parent: 10:74c64d036d72
2666 | | | parent: 10:74c64d036d72
2667 | | | user: test
2667 | | | user: test
2668 | | | date: Thu Jan 01 00:00:11 1970 +0000
2668 | | | date: Thu Jan 01 00:00:11 1970 +0000
2669 | | | summary: (11) expand
2669 | | | summary: (11) expand
2670 | | |
2670 | | |
2671 | | o changeset: 10:74c64d036d72
2671 | | o changeset: 10:74c64d036d72
2672 | |/| parent: 0:e6eb3150255d
2672 | |/| parent: 0:e6eb3150255d
2673 | | ~ parent: 6:b105a072e251
2673 | | ~ parent: 6:b105a072e251
2674 | | user: test
2674 | | user: test
2675 | | date: Thu Jan 01 00:00:10 1970 +0000
2675 | | date: Thu Jan 01 00:00:10 1970 +0000
2676 | | summary: (10) merge two known; one immediate left, one near right
2676 | | summary: (10) merge two known; one immediate left, one near right
2677 | |
2677 | |
2678 o | changeset: 9:7010c0af0a35
2678 o | changeset: 9:7010c0af0a35
2679 |\ \ parent: 7:b632bb1b1224
2679 |\ \ parent: 7:b632bb1b1224
2680 | | | parent: 8:7a0b11f71937
2680 | | | parent: 8:7a0b11f71937
2681 | | | user: test
2681 | | | user: test
2682 | | | date: Thu Jan 01 00:00:09 1970 +0000
2682 | | | date: Thu Jan 01 00:00:09 1970 +0000
2683 | | | summary: (9) expand
2683 | | | summary: (9) expand
2684 | | |
2684 | | |
2685 | o | changeset: 8:7a0b11f71937
2685 | o | changeset: 8:7a0b11f71937
2686 |/| | parent: 0:e6eb3150255d
2686 |/| | parent: 0:e6eb3150255d
2687 | ~ | parent: 7:b632bb1b1224
2687 | ~ | parent: 7:b632bb1b1224
2688 | | user: test
2688 | | user: test
2689 | | date: Thu Jan 01 00:00:08 1970 +0000
2689 | | date: Thu Jan 01 00:00:08 1970 +0000
2690 | | summary: (8) merge two known; one immediate left, one far right
2690 | | summary: (8) merge two known; one immediate left, one far right
2691 | /
2691 | /
2692 o | changeset: 7:b632bb1b1224
2692 o | changeset: 7:b632bb1b1224
2693 |\ \ parent: 2:3d9a33b8d1e1
2693 |\ \ parent: 2:3d9a33b8d1e1
2694 | ~ | parent: 5:4409d547b708
2694 | ~ | parent: 5:4409d547b708
2695 | | user: test
2695 | | user: test
2696 | | date: Thu Jan 01 00:00:07 1970 +0000
2696 | | date: Thu Jan 01 00:00:07 1970 +0000
2697 | | summary: (7) expand
2697 | | summary: (7) expand
2698 | /
2698 | /
2699 | o changeset: 6:b105a072e251
2699 | o changeset: 6:b105a072e251
2700 |/| parent: 2:3d9a33b8d1e1
2700 |/| parent: 2:3d9a33b8d1e1
2701 | ~ parent: 5:4409d547b708
2701 | ~ parent: 5:4409d547b708
2702 | user: test
2702 | user: test
2703 | date: Thu Jan 01 00:00:06 1970 +0000
2703 | date: Thu Jan 01 00:00:06 1970 +0000
2704 | summary: (6) merge two known; one immediate left, one far left
2704 | summary: (6) merge two known; one immediate left, one far left
2705 |
2705 |
2706 o changeset: 5:4409d547b708
2706 o changeset: 5:4409d547b708
2707 |\ parent: 3:27eef8ed80b4
2707 |\ parent: 3:27eef8ed80b4
2708 | ~ parent: 4:26a8bac39d9f
2708 | ~ parent: 4:26a8bac39d9f
2709 | user: test
2709 | user: test
2710 | date: Thu Jan 01 00:00:05 1970 +0000
2710 | date: Thu Jan 01 00:00:05 1970 +0000
2711 | summary: (5) expand
2711 | summary: (5) expand
2712 |
2712 |
2713 o changeset: 4:26a8bac39d9f
2713 o changeset: 4:26a8bac39d9f
2714 |\ parent: 1:6db2ef61d156
2714 |\ parent: 1:6db2ef61d156
2715 ~ ~ parent: 3:27eef8ed80b4
2715 ~ ~ parent: 3:27eef8ed80b4
2716 user: test
2716 user: test
2717 date: Thu Jan 01 00:00:04 1970 +0000
2717 date: Thu Jan 01 00:00:04 1970 +0000
2718 summary: (4) merge two known; one immediate left, one immediate right
2718 summary: (4) merge two known; one immediate left, one immediate right
2719
2719
2720
2720
2721 Setting HGPLAIN ignores graphmod styling:
2721 Setting HGPLAIN ignores graphmod styling:
2722
2722
2723 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2723 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2724 @ changeset: 36:08a19a744424
2724 @ changeset: 36:08a19a744424
2725 | branch: branch
2725 | branch: branch
2726 | tag: tip
2726 | tag: tip
2727 | parent: 35:9159c3644c5e
2727 | parent: 35:9159c3644c5e
2728 | parent: 35:9159c3644c5e
2728 | parent: 35:9159c3644c5e
2729 | user: test
2729 | user: test
2730 | date: Thu Jan 01 00:00:36 1970 +0000
2730 | date: Thu Jan 01 00:00:36 1970 +0000
2731 | summary: (36) buggy merge: identical parents
2731 | summary: (36) buggy merge: identical parents
2732 |
2732 |
2733 o changeset: 32:d06dffa21a31
2733 o changeset: 32:d06dffa21a31
2734 |\ parent: 27:886ed638191b
2734 |\ parent: 27:886ed638191b
2735 | | parent: 31:621d83e11f67
2735 | | parent: 31:621d83e11f67
2736 | | user: test
2736 | | user: test
2737 | | date: Thu Jan 01 00:00:32 1970 +0000
2737 | | date: Thu Jan 01 00:00:32 1970 +0000
2738 | | summary: (32) expand
2738 | | summary: (32) expand
2739 | |
2739 | |
2740 o | changeset: 31:621d83e11f67
2740 o | changeset: 31:621d83e11f67
2741 |\| parent: 21:d42a756af44d
2741 |\| parent: 21:d42a756af44d
2742 | | parent: 30:6e11cd4b648f
2742 | | parent: 30:6e11cd4b648f
2743 | | user: test
2743 | | user: test
2744 | | date: Thu Jan 01 00:00:31 1970 +0000
2744 | | date: Thu Jan 01 00:00:31 1970 +0000
2745 | | summary: (31) expand
2745 | | summary: (31) expand
2746 | |
2746 | |
2747 o | changeset: 30:6e11cd4b648f
2747 o | changeset: 30:6e11cd4b648f
2748 |\ \ parent: 28:44ecd0b9ae99
2748 |\ \ parent: 28:44ecd0b9ae99
2749 | | | parent: 29:cd9bb2be7593
2749 | | | parent: 29:cd9bb2be7593
2750 | | | user: test
2750 | | | user: test
2751 | | | date: Thu Jan 01 00:00:30 1970 +0000
2751 | | | date: Thu Jan 01 00:00:30 1970 +0000
2752 | | | summary: (30) expand
2752 | | | summary: (30) expand
2753 | | |
2753 | | |
2754 o | | changeset: 28:44ecd0b9ae99
2754 o | | changeset: 28:44ecd0b9ae99
2755 |\ \ \ parent: 1:6db2ef61d156
2755 |\ \ \ parent: 1:6db2ef61d156
2756 | | | | parent: 26:7f25b6c2f0b9
2756 | | | | parent: 26:7f25b6c2f0b9
2757 | | | | user: test
2757 | | | | user: test
2758 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2758 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2759 | | | | summary: (28) merge zero known
2759 | | | | summary: (28) merge zero known
2760 | | | |
2760 | | | |
2761 o | | | changeset: 26:7f25b6c2f0b9
2761 o | | | changeset: 26:7f25b6c2f0b9
2762 |\ \ \ \ parent: 18:1aa84d96232a
2762 |\ \ \ \ parent: 18:1aa84d96232a
2763 | | | | | parent: 25:91da8ed57247
2763 | | | | | parent: 25:91da8ed57247
2764 | | | | | user: test
2764 | | | | | user: test
2765 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2765 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2766 | | | | | summary: (26) merge one known; far right
2766 | | | | | summary: (26) merge one known; far right
2767 | | | | |
2767 | | | | |
2768 | o-----+ changeset: 25:91da8ed57247
2768 | o-----+ changeset: 25:91da8ed57247
2769 | | | | | parent: 21:d42a756af44d
2769 | | | | | parent: 21:d42a756af44d
2770 | | | | | parent: 24:a9c19a3d96b7
2770 | | | | | parent: 24:a9c19a3d96b7
2771 | | | | | user: test
2771 | | | | | user: test
2772 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2772 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2773 | | | | | summary: (25) merge one known; far left
2773 | | | | | summary: (25) merge one known; far left
2774 | | | | |
2774 | | | | |
2775 | o | | | changeset: 24:a9c19a3d96b7
2775 | o | | | changeset: 24:a9c19a3d96b7
2776 | |\ \ \ \ parent: 0:e6eb3150255d
2776 | |\ \ \ \ parent: 0:e6eb3150255d
2777 | | | | | | parent: 23:a01cddf0766d
2777 | | | | | | parent: 23:a01cddf0766d
2778 | | | | | | user: test
2778 | | | | | | user: test
2779 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2779 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2780 | | | | | | summary: (24) merge one known; immediate right
2780 | | | | | | summary: (24) merge one known; immediate right
2781 | | | | | |
2781 | | | | | |
2782 | o---+ | | changeset: 23:a01cddf0766d
2782 | o---+ | | changeset: 23:a01cddf0766d
2783 | | | | | | parent: 1:6db2ef61d156
2783 | | | | | | parent: 1:6db2ef61d156
2784 | | | | | | parent: 22:e0d9cccacb5d
2784 | | | | | | parent: 22:e0d9cccacb5d
2785 | | | | | | user: test
2785 | | | | | | user: test
2786 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2786 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2787 | | | | | | summary: (23) merge one known; immediate left
2787 | | | | | | summary: (23) merge one known; immediate left
2788 | | | | | |
2788 | | | | | |
2789 | o-------+ changeset: 22:e0d9cccacb5d
2789 | o-------+ changeset: 22:e0d9cccacb5d
2790 | | | | | | parent: 18:1aa84d96232a
2790 | | | | | | parent: 18:1aa84d96232a
2791 |/ / / / / parent: 21:d42a756af44d
2791 |/ / / / / parent: 21:d42a756af44d
2792 | | | | | user: test
2792 | | | | | user: test
2793 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2793 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2794 | | | | | summary: (22) merge two known; one far left, one far right
2794 | | | | | summary: (22) merge two known; one far left, one far right
2795 | | | | |
2795 | | | | |
2796 | | | | o changeset: 21:d42a756af44d
2796 | | | | o changeset: 21:d42a756af44d
2797 | | | | |\ parent: 19:31ddc2c1573b
2797 | | | | |\ parent: 19:31ddc2c1573b
2798 | | | | | | parent: 20:d30ed6450e32
2798 | | | | | | parent: 20:d30ed6450e32
2799 | | | | | | user: test
2799 | | | | | | user: test
2800 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2800 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2801 | | | | | | summary: (21) expand
2801 | | | | | | summary: (21) expand
2802 | | | | | |
2802 | | | | | |
2803 +-+-------o changeset: 20:d30ed6450e32
2803 +-+-------o changeset: 20:d30ed6450e32
2804 | | | | | parent: 0:e6eb3150255d
2804 | | | | | parent: 0:e6eb3150255d
2805 | | | | | parent: 18:1aa84d96232a
2805 | | | | | parent: 18:1aa84d96232a
2806 | | | | | user: test
2806 | | | | | user: test
2807 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2807 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2808 | | | | | summary: (20) merge two known; two far right
2808 | | | | | summary: (20) merge two known; two far right
2809 | | | | |
2809 | | | | |
2810 | | | | o changeset: 19:31ddc2c1573b
2810 | | | | o changeset: 19:31ddc2c1573b
2811 | | | | |\ parent: 15:1dda3f72782d
2811 | | | | |\ parent: 15:1dda3f72782d
2812 | | | | | | parent: 17:44765d7c06e0
2812 | | | | | | parent: 17:44765d7c06e0
2813 | | | | | | user: test
2813 | | | | | | user: test
2814 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2814 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2815 | | | | | | summary: (19) expand
2815 | | | | | | summary: (19) expand
2816 | | | | | |
2816 | | | | | |
2817 o---+---+ | changeset: 18:1aa84d96232a
2817 o---+---+ | changeset: 18:1aa84d96232a
2818 | | | | | parent: 1:6db2ef61d156
2818 | | | | | parent: 1:6db2ef61d156
2819 / / / / / parent: 15:1dda3f72782d
2819 / / / / / parent: 15:1dda3f72782d
2820 | | | | | user: test
2820 | | | | | user: test
2821 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2821 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2822 | | | | | summary: (18) merge two known; two far left
2822 | | | | | summary: (18) merge two known; two far left
2823 | | | | |
2823 | | | | |
2824 | | | | o changeset: 17:44765d7c06e0
2824 | | | | o changeset: 17:44765d7c06e0
2825 | | | | |\ parent: 12:86b91144a6e9
2825 | | | | |\ parent: 12:86b91144a6e9
2826 | | | | | | parent: 16:3677d192927d
2826 | | | | | | parent: 16:3677d192927d
2827 | | | | | | user: test
2827 | | | | | | user: test
2828 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2828 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2829 | | | | | | summary: (17) expand
2829 | | | | | | summary: (17) expand
2830 | | | | | |
2830 | | | | | |
2831 +-+-------o changeset: 16:3677d192927d
2831 +-+-------o changeset: 16:3677d192927d
2832 | | | | | parent: 0:e6eb3150255d
2832 | | | | | parent: 0:e6eb3150255d
2833 | | | | | parent: 1:6db2ef61d156
2833 | | | | | parent: 1:6db2ef61d156
2834 | | | | | user: test
2834 | | | | | user: test
2835 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2835 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2836 | | | | | summary: (16) merge two known; one immediate right, one near right
2836 | | | | | summary: (16) merge two known; one immediate right, one near right
2837 | | | | |
2837 | | | | |
2838 | | | o | changeset: 15:1dda3f72782d
2838 | | | o | changeset: 15:1dda3f72782d
2839 | | | |\ \ parent: 13:22d8966a97e3
2839 | | | |\ \ parent: 13:22d8966a97e3
2840 | | | | | | parent: 14:8eac370358ef
2840 | | | | | | parent: 14:8eac370358ef
2841 | | | | | | user: test
2841 | | | | | | user: test
2842 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2842 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2843 | | | | | | summary: (15) expand
2843 | | | | | | summary: (15) expand
2844 | | | | | |
2844 | | | | | |
2845 +-------o | changeset: 14:8eac370358ef
2845 +-------o | changeset: 14:8eac370358ef
2846 | | | | |/ parent: 0:e6eb3150255d
2846 | | | | |/ parent: 0:e6eb3150255d
2847 | | | | | parent: 12:86b91144a6e9
2847 | | | | | parent: 12:86b91144a6e9
2848 | | | | | user: test
2848 | | | | | user: test
2849 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2849 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2850 | | | | | summary: (14) merge two known; one immediate right, one far right
2850 | | | | | summary: (14) merge two known; one immediate right, one far right
2851 | | | | |
2851 | | | | |
2852 | | | o | changeset: 13:22d8966a97e3
2852 | | | o | changeset: 13:22d8966a97e3
2853 | | | |\ \ parent: 9:7010c0af0a35
2853 | | | |\ \ parent: 9:7010c0af0a35
2854 | | | | | | parent: 11:832d76e6bdf2
2854 | | | | | | parent: 11:832d76e6bdf2
2855 | | | | | | user: test
2855 | | | | | | user: test
2856 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2856 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2857 | | | | | | summary: (13) expand
2857 | | | | | | summary: (13) expand
2858 | | | | | |
2858 | | | | | |
2859 | +---+---o changeset: 12:86b91144a6e9
2859 | +---+---o changeset: 12:86b91144a6e9
2860 | | | | | parent: 1:6db2ef61d156
2860 | | | | | parent: 1:6db2ef61d156
2861 | | | | | parent: 9:7010c0af0a35
2861 | | | | | parent: 9:7010c0af0a35
2862 | | | | | user: test
2862 | | | | | user: test
2863 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2863 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2864 | | | | | summary: (12) merge two known; one immediate right, one far left
2864 | | | | | summary: (12) merge two known; one immediate right, one far left
2865 | | | | |
2865 | | | | |
2866 | | | | o changeset: 11:832d76e6bdf2
2866 | | | | o changeset: 11:832d76e6bdf2
2867 | | | | |\ parent: 6:b105a072e251
2867 | | | | |\ parent: 6:b105a072e251
2868 | | | | | | parent: 10:74c64d036d72
2868 | | | | | | parent: 10:74c64d036d72
2869 | | | | | | user: test
2869 | | | | | | user: test
2870 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2870 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2871 | | | | | | summary: (11) expand
2871 | | | | | | summary: (11) expand
2872 | | | | | |
2872 | | | | | |
2873 +---------o changeset: 10:74c64d036d72
2873 +---------o changeset: 10:74c64d036d72
2874 | | | | |/ parent: 0:e6eb3150255d
2874 | | | | |/ parent: 0:e6eb3150255d
2875 | | | | | parent: 6:b105a072e251
2875 | | | | | parent: 6:b105a072e251
2876 | | | | | user: test
2876 | | | | | user: test
2877 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2877 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2878 | | | | | summary: (10) merge two known; one immediate left, one near right
2878 | | | | | summary: (10) merge two known; one immediate left, one near right
2879 | | | | |
2879 | | | | |
2880 | | | o | changeset: 9:7010c0af0a35
2880 | | | o | changeset: 9:7010c0af0a35
2881 | | | |\ \ parent: 7:b632bb1b1224
2881 | | | |\ \ parent: 7:b632bb1b1224
2882 | | | | | | parent: 8:7a0b11f71937
2882 | | | | | | parent: 8:7a0b11f71937
2883 | | | | | | user: test
2883 | | | | | | user: test
2884 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2884 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2885 | | | | | | summary: (9) expand
2885 | | | | | | summary: (9) expand
2886 | | | | | |
2886 | | | | | |
2887 +-------o | changeset: 8:7a0b11f71937
2887 +-------o | changeset: 8:7a0b11f71937
2888 | | | |/ / parent: 0:e6eb3150255d
2888 | | | |/ / parent: 0:e6eb3150255d
2889 | | | | | parent: 7:b632bb1b1224
2889 | | | | | parent: 7:b632bb1b1224
2890 | | | | | user: test
2890 | | | | | user: test
2891 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2891 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2892 | | | | | summary: (8) merge two known; one immediate left, one far right
2892 | | | | | summary: (8) merge two known; one immediate left, one far right
2893 | | | | |
2893 | | | | |
2894 | | | o | changeset: 7:b632bb1b1224
2894 | | | o | changeset: 7:b632bb1b1224
2895 | | | |\ \ parent: 2:3d9a33b8d1e1
2895 | | | |\ \ parent: 2:3d9a33b8d1e1
2896 | | | | | | parent: 5:4409d547b708
2896 | | | | | | parent: 5:4409d547b708
2897 | | | | | | user: test
2897 | | | | | | user: test
2898 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2898 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2899 | | | | | | summary: (7) expand
2899 | | | | | | summary: (7) expand
2900 | | | | | |
2900 | | | | | |
2901 | | | +---o changeset: 6:b105a072e251
2901 | | | +---o changeset: 6:b105a072e251
2902 | | | | |/ parent: 2:3d9a33b8d1e1
2902 | | | | |/ parent: 2:3d9a33b8d1e1
2903 | | | | | parent: 5:4409d547b708
2903 | | | | | parent: 5:4409d547b708
2904 | | | | | user: test
2904 | | | | | user: test
2905 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2905 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2906 | | | | | summary: (6) merge two known; one immediate left, one far left
2906 | | | | | summary: (6) merge two known; one immediate left, one far left
2907 | | | | |
2907 | | | | |
2908 | | | o | changeset: 5:4409d547b708
2908 | | | o | changeset: 5:4409d547b708
2909 | | | |\ \ parent: 3:27eef8ed80b4
2909 | | | |\ \ parent: 3:27eef8ed80b4
2910 | | | | | | parent: 4:26a8bac39d9f
2910 | | | | | | parent: 4:26a8bac39d9f
2911 | | | | | | user: test
2911 | | | | | | user: test
2912 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2912 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2913 | | | | | | summary: (5) expand
2913 | | | | | | summary: (5) expand
2914 | | | | | |
2914 | | | | | |
2915 | +---o | | changeset: 4:26a8bac39d9f
2915 | +---o | | changeset: 4:26a8bac39d9f
2916 | | | |/ / parent: 1:6db2ef61d156
2916 | | | |/ / parent: 1:6db2ef61d156
2917 | | | | | parent: 3:27eef8ed80b4
2917 | | | | | parent: 3:27eef8ed80b4
2918 | | | | | user: test
2918 | | | | | user: test
2919 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2919 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2920 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2920 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2921 | | | | |
2921 | | | | |
2922
2922
2923 .. unless HGPLAINEXCEPT=graph is set:
2923 .. unless HGPLAINEXCEPT=graph is set:
2924
2924
2925 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2925 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2926 @ changeset: 36:08a19a744424
2926 @ changeset: 36:08a19a744424
2927 : branch: branch
2927 : branch: branch
2928 : tag: tip
2928 : tag: tip
2929 : parent: 35:9159c3644c5e
2929 : parent: 35:9159c3644c5e
2930 : parent: 35:9159c3644c5e
2930 : parent: 35:9159c3644c5e
2931 : user: test
2931 : user: test
2932 : date: Thu Jan 01 00:00:36 1970 +0000
2932 : date: Thu Jan 01 00:00:36 1970 +0000
2933 : summary: (36) buggy merge: identical parents
2933 : summary: (36) buggy merge: identical parents
2934 :
2934 :
2935 o changeset: 32:d06dffa21a31
2935 o changeset: 32:d06dffa21a31
2936 |\ parent: 27:886ed638191b
2936 |\ parent: 27:886ed638191b
2937 | : parent: 31:621d83e11f67
2937 | : parent: 31:621d83e11f67
2938 | : user: test
2938 | : user: test
2939 | : date: Thu Jan 01 00:00:32 1970 +0000
2939 | : date: Thu Jan 01 00:00:32 1970 +0000
2940 | : summary: (32) expand
2940 | : summary: (32) expand
2941 | :
2941 | :
2942 o : changeset: 31:621d83e11f67
2942 o : changeset: 31:621d83e11f67
2943 |\: parent: 21:d42a756af44d
2943 |\: parent: 21:d42a756af44d
2944 | : parent: 30:6e11cd4b648f
2944 | : parent: 30:6e11cd4b648f
2945 | : user: test
2945 | : user: test
2946 | : date: Thu Jan 01 00:00:31 1970 +0000
2946 | : date: Thu Jan 01 00:00:31 1970 +0000
2947 | : summary: (31) expand
2947 | : summary: (31) expand
2948 | :
2948 | :
2949 o : changeset: 30:6e11cd4b648f
2949 o : changeset: 30:6e11cd4b648f
2950 |\ \ parent: 28:44ecd0b9ae99
2950 |\ \ parent: 28:44ecd0b9ae99
2951 | ~ : parent: 29:cd9bb2be7593
2951 | ~ : parent: 29:cd9bb2be7593
2952 | : user: test
2952 | : user: test
2953 | : date: Thu Jan 01 00:00:30 1970 +0000
2953 | : date: Thu Jan 01 00:00:30 1970 +0000
2954 | : summary: (30) expand
2954 | : summary: (30) expand
2955 | /
2955 | /
2956 o : changeset: 28:44ecd0b9ae99
2956 o : changeset: 28:44ecd0b9ae99
2957 |\ \ parent: 1:6db2ef61d156
2957 |\ \ parent: 1:6db2ef61d156
2958 | ~ : parent: 26:7f25b6c2f0b9
2958 | ~ : parent: 26:7f25b6c2f0b9
2959 | : user: test
2959 | : user: test
2960 | : date: Thu Jan 01 00:00:28 1970 +0000
2960 | : date: Thu Jan 01 00:00:28 1970 +0000
2961 | : summary: (28) merge zero known
2961 | : summary: (28) merge zero known
2962 | /
2962 | /
2963 o : changeset: 26:7f25b6c2f0b9
2963 o : changeset: 26:7f25b6c2f0b9
2964 |\ \ parent: 18:1aa84d96232a
2964 |\ \ parent: 18:1aa84d96232a
2965 | | : parent: 25:91da8ed57247
2965 | | : parent: 25:91da8ed57247
2966 | | : user: test
2966 | | : user: test
2967 | | : date: Thu Jan 01 00:00:26 1970 +0000
2967 | | : date: Thu Jan 01 00:00:26 1970 +0000
2968 | | : summary: (26) merge one known; far right
2968 | | : summary: (26) merge one known; far right
2969 | | :
2969 | | :
2970 | o : changeset: 25:91da8ed57247
2970 | o : changeset: 25:91da8ed57247
2971 | |\: parent: 21:d42a756af44d
2971 | |\: parent: 21:d42a756af44d
2972 | | : parent: 24:a9c19a3d96b7
2972 | | : parent: 24:a9c19a3d96b7
2973 | | : user: test
2973 | | : user: test
2974 | | : date: Thu Jan 01 00:00:25 1970 +0000
2974 | | : date: Thu Jan 01 00:00:25 1970 +0000
2975 | | : summary: (25) merge one known; far left
2975 | | : summary: (25) merge one known; far left
2976 | | :
2976 | | :
2977 | o : changeset: 24:a9c19a3d96b7
2977 | o : changeset: 24:a9c19a3d96b7
2978 | |\ \ parent: 0:e6eb3150255d
2978 | |\ \ parent: 0:e6eb3150255d
2979 | | ~ : parent: 23:a01cddf0766d
2979 | | ~ : parent: 23:a01cddf0766d
2980 | | : user: test
2980 | | : user: test
2981 | | : date: Thu Jan 01 00:00:24 1970 +0000
2981 | | : date: Thu Jan 01 00:00:24 1970 +0000
2982 | | : summary: (24) merge one known; immediate right
2982 | | : summary: (24) merge one known; immediate right
2983 | | /
2983 | | /
2984 | o : changeset: 23:a01cddf0766d
2984 | o : changeset: 23:a01cddf0766d
2985 | |\ \ parent: 1:6db2ef61d156
2985 | |\ \ parent: 1:6db2ef61d156
2986 | | ~ : parent: 22:e0d9cccacb5d
2986 | | ~ : parent: 22:e0d9cccacb5d
2987 | | : user: test
2987 | | : user: test
2988 | | : date: Thu Jan 01 00:00:23 1970 +0000
2988 | | : date: Thu Jan 01 00:00:23 1970 +0000
2989 | | : summary: (23) merge one known; immediate left
2989 | | : summary: (23) merge one known; immediate left
2990 | | /
2990 | | /
2991 | o : changeset: 22:e0d9cccacb5d
2991 | o : changeset: 22:e0d9cccacb5d
2992 |/:/ parent: 18:1aa84d96232a
2992 |/:/ parent: 18:1aa84d96232a
2993 | : parent: 21:d42a756af44d
2993 | : parent: 21:d42a756af44d
2994 | : user: test
2994 | : user: test
2995 | : date: Thu Jan 01 00:00:22 1970 +0000
2995 | : date: Thu Jan 01 00:00:22 1970 +0000
2996 | : summary: (22) merge two known; one far left, one far right
2996 | : summary: (22) merge two known; one far left, one far right
2997 | :
2997 | :
2998 | o changeset: 21:d42a756af44d
2998 | o changeset: 21:d42a756af44d
2999 | |\ parent: 19:31ddc2c1573b
2999 | |\ parent: 19:31ddc2c1573b
3000 | | | parent: 20:d30ed6450e32
3000 | | | parent: 20:d30ed6450e32
3001 | | | user: test
3001 | | | user: test
3002 | | | date: Thu Jan 01 00:00:21 1970 +0000
3002 | | | date: Thu Jan 01 00:00:21 1970 +0000
3003 | | | summary: (21) expand
3003 | | | summary: (21) expand
3004 | | |
3004 | | |
3005 +---o changeset: 20:d30ed6450e32
3005 +---o changeset: 20:d30ed6450e32
3006 | | | parent: 0:e6eb3150255d
3006 | | | parent: 0:e6eb3150255d
3007 | | ~ parent: 18:1aa84d96232a
3007 | | ~ parent: 18:1aa84d96232a
3008 | | user: test
3008 | | user: test
3009 | | date: Thu Jan 01 00:00:20 1970 +0000
3009 | | date: Thu Jan 01 00:00:20 1970 +0000
3010 | | summary: (20) merge two known; two far right
3010 | | summary: (20) merge two known; two far right
3011 | |
3011 | |
3012 | o changeset: 19:31ddc2c1573b
3012 | o changeset: 19:31ddc2c1573b
3013 | |\ parent: 15:1dda3f72782d
3013 | |\ parent: 15:1dda3f72782d
3014 | | | parent: 17:44765d7c06e0
3014 | | | parent: 17:44765d7c06e0
3015 | | | user: test
3015 | | | user: test
3016 | | | date: Thu Jan 01 00:00:19 1970 +0000
3016 | | | date: Thu Jan 01 00:00:19 1970 +0000
3017 | | | summary: (19) expand
3017 | | | summary: (19) expand
3018 | | |
3018 | | |
3019 o | | changeset: 18:1aa84d96232a
3019 o | | changeset: 18:1aa84d96232a
3020 |\| | parent: 1:6db2ef61d156
3020 |\| | parent: 1:6db2ef61d156
3021 ~ | | parent: 15:1dda3f72782d
3021 ~ | | parent: 15:1dda3f72782d
3022 | | user: test
3022 | | user: test
3023 | | date: Thu Jan 01 00:00:18 1970 +0000
3023 | | date: Thu Jan 01 00:00:18 1970 +0000
3024 | | summary: (18) merge two known; two far left
3024 | | summary: (18) merge two known; two far left
3025 / /
3025 / /
3026 | o changeset: 17:44765d7c06e0
3026 | o changeset: 17:44765d7c06e0
3027 | |\ parent: 12:86b91144a6e9
3027 | |\ parent: 12:86b91144a6e9
3028 | | | parent: 16:3677d192927d
3028 | | | parent: 16:3677d192927d
3029 | | | user: test
3029 | | | user: test
3030 | | | date: Thu Jan 01 00:00:17 1970 +0000
3030 | | | date: Thu Jan 01 00:00:17 1970 +0000
3031 | | | summary: (17) expand
3031 | | | summary: (17) expand
3032 | | |
3032 | | |
3033 | | o changeset: 16:3677d192927d
3033 | | o changeset: 16:3677d192927d
3034 | | |\ parent: 0:e6eb3150255d
3034 | | |\ parent: 0:e6eb3150255d
3035 | | ~ ~ parent: 1:6db2ef61d156
3035 | | ~ ~ parent: 1:6db2ef61d156
3036 | | user: test
3036 | | user: test
3037 | | date: Thu Jan 01 00:00:16 1970 +0000
3037 | | date: Thu Jan 01 00:00:16 1970 +0000
3038 | | summary: (16) merge two known; one immediate right, one near right
3038 | | summary: (16) merge two known; one immediate right, one near right
3039 | |
3039 | |
3040 o | changeset: 15:1dda3f72782d
3040 o | changeset: 15:1dda3f72782d
3041 |\ \ parent: 13:22d8966a97e3
3041 |\ \ parent: 13:22d8966a97e3
3042 | | | parent: 14:8eac370358ef
3042 | | | parent: 14:8eac370358ef
3043 | | | user: test
3043 | | | user: test
3044 | | | date: Thu Jan 01 00:00:15 1970 +0000
3044 | | | date: Thu Jan 01 00:00:15 1970 +0000
3045 | | | summary: (15) expand
3045 | | | summary: (15) expand
3046 | | |
3046 | | |
3047 | o | changeset: 14:8eac370358ef
3047 | o | changeset: 14:8eac370358ef
3048 | |\| parent: 0:e6eb3150255d
3048 | |\| parent: 0:e6eb3150255d
3049 | ~ | parent: 12:86b91144a6e9
3049 | ~ | parent: 12:86b91144a6e9
3050 | | user: test
3050 | | user: test
3051 | | date: Thu Jan 01 00:00:14 1970 +0000
3051 | | date: Thu Jan 01 00:00:14 1970 +0000
3052 | | summary: (14) merge two known; one immediate right, one far right
3052 | | summary: (14) merge two known; one immediate right, one far right
3053 | /
3053 | /
3054 o | changeset: 13:22d8966a97e3
3054 o | changeset: 13:22d8966a97e3
3055 |\ \ parent: 9:7010c0af0a35
3055 |\ \ parent: 9:7010c0af0a35
3056 | | | parent: 11:832d76e6bdf2
3056 | | | parent: 11:832d76e6bdf2
3057 | | | user: test
3057 | | | user: test
3058 | | | date: Thu Jan 01 00:00:13 1970 +0000
3058 | | | date: Thu Jan 01 00:00:13 1970 +0000
3059 | | | summary: (13) expand
3059 | | | summary: (13) expand
3060 | | |
3060 | | |
3061 +---o changeset: 12:86b91144a6e9
3061 +---o changeset: 12:86b91144a6e9
3062 | | | parent: 1:6db2ef61d156
3062 | | | parent: 1:6db2ef61d156
3063 | | ~ parent: 9:7010c0af0a35
3063 | | ~ parent: 9:7010c0af0a35
3064 | | user: test
3064 | | user: test
3065 | | date: Thu Jan 01 00:00:12 1970 +0000
3065 | | date: Thu Jan 01 00:00:12 1970 +0000
3066 | | summary: (12) merge two known; one immediate right, one far left
3066 | | summary: (12) merge two known; one immediate right, one far left
3067 | |
3067 | |
3068 | o changeset: 11:832d76e6bdf2
3068 | o changeset: 11:832d76e6bdf2
3069 | |\ parent: 6:b105a072e251
3069 | |\ parent: 6:b105a072e251
3070 | | | parent: 10:74c64d036d72
3070 | | | parent: 10:74c64d036d72
3071 | | | user: test
3071 | | | user: test
3072 | | | date: Thu Jan 01 00:00:11 1970 +0000
3072 | | | date: Thu Jan 01 00:00:11 1970 +0000
3073 | | | summary: (11) expand
3073 | | | summary: (11) expand
3074 | | |
3074 | | |
3075 | | o changeset: 10:74c64d036d72
3075 | | o changeset: 10:74c64d036d72
3076 | |/| parent: 0:e6eb3150255d
3076 | |/| parent: 0:e6eb3150255d
3077 | | ~ parent: 6:b105a072e251
3077 | | ~ parent: 6:b105a072e251
3078 | | user: test
3078 | | user: test
3079 | | date: Thu Jan 01 00:00:10 1970 +0000
3079 | | date: Thu Jan 01 00:00:10 1970 +0000
3080 | | summary: (10) merge two known; one immediate left, one near right
3080 | | summary: (10) merge two known; one immediate left, one near right
3081 | |
3081 | |
3082 o | changeset: 9:7010c0af0a35
3082 o | changeset: 9:7010c0af0a35
3083 |\ \ parent: 7:b632bb1b1224
3083 |\ \ parent: 7:b632bb1b1224
3084 | | | parent: 8:7a0b11f71937
3084 | | | parent: 8:7a0b11f71937
3085 | | | user: test
3085 | | | user: test
3086 | | | date: Thu Jan 01 00:00:09 1970 +0000
3086 | | | date: Thu Jan 01 00:00:09 1970 +0000
3087 | | | summary: (9) expand
3087 | | | summary: (9) expand
3088 | | |
3088 | | |
3089 | o | changeset: 8:7a0b11f71937
3089 | o | changeset: 8:7a0b11f71937
3090 |/| | parent: 0:e6eb3150255d
3090 |/| | parent: 0:e6eb3150255d
3091 | ~ | parent: 7:b632bb1b1224
3091 | ~ | parent: 7:b632bb1b1224
3092 | | user: test
3092 | | user: test
3093 | | date: Thu Jan 01 00:00:08 1970 +0000
3093 | | date: Thu Jan 01 00:00:08 1970 +0000
3094 | | summary: (8) merge two known; one immediate left, one far right
3094 | | summary: (8) merge two known; one immediate left, one far right
3095 | /
3095 | /
3096 o | changeset: 7:b632bb1b1224
3096 o | changeset: 7:b632bb1b1224
3097 |\ \ parent: 2:3d9a33b8d1e1
3097 |\ \ parent: 2:3d9a33b8d1e1
3098 | ~ | parent: 5:4409d547b708
3098 | ~ | parent: 5:4409d547b708
3099 | | user: test
3099 | | user: test
3100 | | date: Thu Jan 01 00:00:07 1970 +0000
3100 | | date: Thu Jan 01 00:00:07 1970 +0000
3101 | | summary: (7) expand
3101 | | summary: (7) expand
3102 | /
3102 | /
3103 | o changeset: 6:b105a072e251
3103 | o changeset: 6:b105a072e251
3104 |/| parent: 2:3d9a33b8d1e1
3104 |/| parent: 2:3d9a33b8d1e1
3105 | ~ parent: 5:4409d547b708
3105 | ~ parent: 5:4409d547b708
3106 | user: test
3106 | user: test
3107 | date: Thu Jan 01 00:00:06 1970 +0000
3107 | date: Thu Jan 01 00:00:06 1970 +0000
3108 | summary: (6) merge two known; one immediate left, one far left
3108 | summary: (6) merge two known; one immediate left, one far left
3109 |
3109 |
3110 o changeset: 5:4409d547b708
3110 o changeset: 5:4409d547b708
3111 |\ parent: 3:27eef8ed80b4
3111 |\ parent: 3:27eef8ed80b4
3112 | ~ parent: 4:26a8bac39d9f
3112 | ~ parent: 4:26a8bac39d9f
3113 | user: test
3113 | user: test
3114 | date: Thu Jan 01 00:00:05 1970 +0000
3114 | date: Thu Jan 01 00:00:05 1970 +0000
3115 | summary: (5) expand
3115 | summary: (5) expand
3116 |
3116 |
3117 o changeset: 4:26a8bac39d9f
3117 o changeset: 4:26a8bac39d9f
3118 |\ parent: 1:6db2ef61d156
3118 |\ parent: 1:6db2ef61d156
3119 ~ ~ parent: 3:27eef8ed80b4
3119 ~ ~ parent: 3:27eef8ed80b4
3120 user: test
3120 user: test
3121 date: Thu Jan 01 00:00:04 1970 +0000
3121 date: Thu Jan 01 00:00:04 1970 +0000
3122 summary: (4) merge two known; one immediate left, one immediate right
3122 summary: (4) merge two known; one immediate left, one immediate right
3123
3123
3124 Draw only part of a grandparent line differently with "<N><char>"; only the
3124 Draw only part of a grandparent line differently with "<N><char>"; only the
3125 last N lines (for positive N) or everything but the first N lines (for
3125 last N lines (for positive N) or everything but the first N lines (for
3126 negative N) along the current node use the style, the rest of the edge uses
3126 negative N) along the current node use the style, the rest of the edge uses
3127 the parent edge styling.
3127 the parent edge styling.
3128
3128
3129 Last 3 lines:
3129 Last 3 lines:
3130
3130
3131 $ cat << EOF >> $HGRCPATH
3131 $ cat << EOF >> $HGRCPATH
3132 > [experimental]
3132 > [experimental]
3133 > graphstyle.parent = !
3133 > graphstyle.parent = !
3134 > graphstyle.grandparent = 3.
3134 > graphstyle.grandparent = 3.
3135 > graphstyle.missing =
3135 > graphstyle.missing =
3136 > EOF
3136 > EOF
3137 $ hg log -G -r '36:18 & file("a")' -m
3137 $ hg log -G -r '36:18 & file("a")' -m
3138 @ changeset: 36:08a19a744424
3138 @ changeset: 36:08a19a744424
3139 ! branch: branch
3139 ! branch: branch
3140 ! tag: tip
3140 ! tag: tip
3141 ! parent: 35:9159c3644c5e
3141 ! parent: 35:9159c3644c5e
3142 ! parent: 35:9159c3644c5e
3142 ! parent: 35:9159c3644c5e
3143 ! user: test
3143 ! user: test
3144 . date: Thu Jan 01 00:00:36 1970 +0000
3144 . date: Thu Jan 01 00:00:36 1970 +0000
3145 . summary: (36) buggy merge: identical parents
3145 . summary: (36) buggy merge: identical parents
3146 .
3146 .
3147 o changeset: 32:d06dffa21a31
3147 o changeset: 32:d06dffa21a31
3148 !\ parent: 27:886ed638191b
3148 !\ parent: 27:886ed638191b
3149 ! ! parent: 31:621d83e11f67
3149 ! ! parent: 31:621d83e11f67
3150 ! ! user: test
3150 ! ! user: test
3151 ! . date: Thu Jan 01 00:00:32 1970 +0000
3151 ! . date: Thu Jan 01 00:00:32 1970 +0000
3152 ! . summary: (32) expand
3152 ! . summary: (32) expand
3153 ! .
3153 ! .
3154 o ! changeset: 31:621d83e11f67
3154 o ! changeset: 31:621d83e11f67
3155 !\! parent: 21:d42a756af44d
3155 !\! parent: 21:d42a756af44d
3156 ! ! parent: 30:6e11cd4b648f
3156 ! ! parent: 30:6e11cd4b648f
3157 ! ! user: test
3157 ! ! user: test
3158 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3158 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3159 ! ! summary: (31) expand
3159 ! ! summary: (31) expand
3160 ! !
3160 ! !
3161 o ! changeset: 30:6e11cd4b648f
3161 o ! changeset: 30:6e11cd4b648f
3162 !\ \ parent: 28:44ecd0b9ae99
3162 !\ \ parent: 28:44ecd0b9ae99
3163 ! ~ ! parent: 29:cd9bb2be7593
3163 ! ~ ! parent: 29:cd9bb2be7593
3164 ! ! user: test
3164 ! ! user: test
3165 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3165 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3166 ! ! summary: (30) expand
3166 ! ! summary: (30) expand
3167 ! /
3167 ! /
3168 o ! changeset: 28:44ecd0b9ae99
3168 o ! changeset: 28:44ecd0b9ae99
3169 !\ \ parent: 1:6db2ef61d156
3169 !\ \ parent: 1:6db2ef61d156
3170 ! ~ ! parent: 26:7f25b6c2f0b9
3170 ! ~ ! parent: 26:7f25b6c2f0b9
3171 ! ! user: test
3171 ! ! user: test
3172 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3172 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3173 ! ! summary: (28) merge zero known
3173 ! ! summary: (28) merge zero known
3174 ! /
3174 ! /
3175 o ! changeset: 26:7f25b6c2f0b9
3175 o ! changeset: 26:7f25b6c2f0b9
3176 !\ \ parent: 18:1aa84d96232a
3176 !\ \ parent: 18:1aa84d96232a
3177 ! ! ! parent: 25:91da8ed57247
3177 ! ! ! parent: 25:91da8ed57247
3178 ! ! ! user: test
3178 ! ! ! user: test
3179 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3179 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3180 ! ! ! summary: (26) merge one known; far right
3180 ! ! ! summary: (26) merge one known; far right
3181 ! ! !
3181 ! ! !
3182 ! o ! changeset: 25:91da8ed57247
3182 ! o ! changeset: 25:91da8ed57247
3183 ! !\! parent: 21:d42a756af44d
3183 ! !\! parent: 21:d42a756af44d
3184 ! ! ! parent: 24:a9c19a3d96b7
3184 ! ! ! parent: 24:a9c19a3d96b7
3185 ! ! ! user: test
3185 ! ! ! user: test
3186 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3186 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3187 ! ! ! summary: (25) merge one known; far left
3187 ! ! ! summary: (25) merge one known; far left
3188 ! ! !
3188 ! ! !
3189 ! o ! changeset: 24:a9c19a3d96b7
3189 ! o ! changeset: 24:a9c19a3d96b7
3190 ! !\ \ parent: 0:e6eb3150255d
3190 ! !\ \ parent: 0:e6eb3150255d
3191 ! ! ~ ! parent: 23:a01cddf0766d
3191 ! ! ~ ! parent: 23:a01cddf0766d
3192 ! ! ! user: test
3192 ! ! ! user: test
3193 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3193 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3194 ! ! ! summary: (24) merge one known; immediate right
3194 ! ! ! summary: (24) merge one known; immediate right
3195 ! ! /
3195 ! ! /
3196 ! o ! changeset: 23:a01cddf0766d
3196 ! o ! changeset: 23:a01cddf0766d
3197 ! !\ \ parent: 1:6db2ef61d156
3197 ! !\ \ parent: 1:6db2ef61d156
3198 ! ! ~ ! parent: 22:e0d9cccacb5d
3198 ! ! ~ ! parent: 22:e0d9cccacb5d
3199 ! ! ! user: test
3199 ! ! ! user: test
3200 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3200 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3201 ! ! ! summary: (23) merge one known; immediate left
3201 ! ! ! summary: (23) merge one known; immediate left
3202 ! ! /
3202 ! ! /
3203 ! o ! changeset: 22:e0d9cccacb5d
3203 ! o ! changeset: 22:e0d9cccacb5d
3204 !/!/ parent: 18:1aa84d96232a
3204 !/!/ parent: 18:1aa84d96232a
3205 ! ! parent: 21:d42a756af44d
3205 ! ! parent: 21:d42a756af44d
3206 ! ! user: test
3206 ! ! user: test
3207 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3207 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3208 ! ! summary: (22) merge two known; one far left, one far right
3208 ! ! summary: (22) merge two known; one far left, one far right
3209 ! !
3209 ! !
3210 ! o changeset: 21:d42a756af44d
3210 ! o changeset: 21:d42a756af44d
3211 ! !\ parent: 19:31ddc2c1573b
3211 ! !\ parent: 19:31ddc2c1573b
3212 ! ! ! parent: 20:d30ed6450e32
3212 ! ! ! parent: 20:d30ed6450e32
3213 ! ! ! user: test
3213 ! ! ! user: test
3214 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3214 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3215 ! ! ! summary: (21) expand
3215 ! ! ! summary: (21) expand
3216 ! ! !
3216 ! ! !
3217 +---o changeset: 20:d30ed6450e32
3217 +---o changeset: 20:d30ed6450e32
3218 ! ! | parent: 0:e6eb3150255d
3218 ! ! | parent: 0:e6eb3150255d
3219 ! ! ~ parent: 18:1aa84d96232a
3219 ! ! ~ parent: 18:1aa84d96232a
3220 ! ! user: test
3220 ! ! user: test
3221 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3221 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3222 ! ! summary: (20) merge two known; two far right
3222 ! ! summary: (20) merge two known; two far right
3223 ! !
3223 ! !
3224 ! o changeset: 19:31ddc2c1573b
3224 ! o changeset: 19:31ddc2c1573b
3225 ! |\ parent: 15:1dda3f72782d
3225 ! |\ parent: 15:1dda3f72782d
3226 ! ~ ~ parent: 17:44765d7c06e0
3226 ! ~ ~ parent: 17:44765d7c06e0
3227 ! user: test
3227 ! user: test
3228 ! date: Thu Jan 01 00:00:19 1970 +0000
3228 ! date: Thu Jan 01 00:00:19 1970 +0000
3229 ! summary: (19) expand
3229 ! summary: (19) expand
3230 !
3230 !
3231 o changeset: 18:1aa84d96232a
3231 o changeset: 18:1aa84d96232a
3232 |\ parent: 1:6db2ef61d156
3232 |\ parent: 1:6db2ef61d156
3233 ~ ~ parent: 15:1dda3f72782d
3233 ~ ~ parent: 15:1dda3f72782d
3234 user: test
3234 user: test
3235 date: Thu Jan 01 00:00:18 1970 +0000
3235 date: Thu Jan 01 00:00:18 1970 +0000
3236 summary: (18) merge two known; two far left
3236 summary: (18) merge two known; two far left
3237
3237
3238 All but the first 3 lines:
3238 All but the first 3 lines:
3239
3239
3240 $ cat << EOF >> $HGRCPATH
3240 $ cat << EOF >> $HGRCPATH
3241 > [experimental]
3241 > [experimental]
3242 > graphstyle.parent = !
3242 > graphstyle.parent = !
3243 > graphstyle.grandparent = -3.
3243 > graphstyle.grandparent = -3.
3244 > graphstyle.missing =
3244 > graphstyle.missing =
3245 > EOF
3245 > EOF
3246 $ hg log -G -r '36:18 & file("a")' -m
3246 $ hg log -G -r '36:18 & file("a")' -m
3247 @ changeset: 36:08a19a744424
3247 @ changeset: 36:08a19a744424
3248 ! branch: branch
3248 ! branch: branch
3249 ! tag: tip
3249 ! tag: tip
3250 . parent: 35:9159c3644c5e
3250 . parent: 35:9159c3644c5e
3251 . parent: 35:9159c3644c5e
3251 . parent: 35:9159c3644c5e
3252 . user: test
3252 . user: test
3253 . date: Thu Jan 01 00:00:36 1970 +0000
3253 . date: Thu Jan 01 00:00:36 1970 +0000
3254 . summary: (36) buggy merge: identical parents
3254 . summary: (36) buggy merge: identical parents
3255 .
3255 .
3256 o changeset: 32:d06dffa21a31
3256 o changeset: 32:d06dffa21a31
3257 !\ parent: 27:886ed638191b
3257 !\ parent: 27:886ed638191b
3258 ! ! parent: 31:621d83e11f67
3258 ! ! parent: 31:621d83e11f67
3259 ! . user: test
3259 ! . user: test
3260 ! . date: Thu Jan 01 00:00:32 1970 +0000
3260 ! . date: Thu Jan 01 00:00:32 1970 +0000
3261 ! . summary: (32) expand
3261 ! . summary: (32) expand
3262 ! .
3262 ! .
3263 o ! changeset: 31:621d83e11f67
3263 o ! changeset: 31:621d83e11f67
3264 !\! parent: 21:d42a756af44d
3264 !\! parent: 21:d42a756af44d
3265 ! ! parent: 30:6e11cd4b648f
3265 ! ! parent: 30:6e11cd4b648f
3266 ! ! user: test
3266 ! ! user: test
3267 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3267 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3268 ! ! summary: (31) expand
3268 ! ! summary: (31) expand
3269 ! !
3269 ! !
3270 o ! changeset: 30:6e11cd4b648f
3270 o ! changeset: 30:6e11cd4b648f
3271 !\ \ parent: 28:44ecd0b9ae99
3271 !\ \ parent: 28:44ecd0b9ae99
3272 ! ~ ! parent: 29:cd9bb2be7593
3272 ! ~ ! parent: 29:cd9bb2be7593
3273 ! ! user: test
3273 ! ! user: test
3274 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3274 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3275 ! ! summary: (30) expand
3275 ! ! summary: (30) expand
3276 ! /
3276 ! /
3277 o ! changeset: 28:44ecd0b9ae99
3277 o ! changeset: 28:44ecd0b9ae99
3278 !\ \ parent: 1:6db2ef61d156
3278 !\ \ parent: 1:6db2ef61d156
3279 ! ~ ! parent: 26:7f25b6c2f0b9
3279 ! ~ ! parent: 26:7f25b6c2f0b9
3280 ! ! user: test
3280 ! ! user: test
3281 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3281 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3282 ! ! summary: (28) merge zero known
3282 ! ! summary: (28) merge zero known
3283 ! /
3283 ! /
3284 o ! changeset: 26:7f25b6c2f0b9
3284 o ! changeset: 26:7f25b6c2f0b9
3285 !\ \ parent: 18:1aa84d96232a
3285 !\ \ parent: 18:1aa84d96232a
3286 ! ! ! parent: 25:91da8ed57247
3286 ! ! ! parent: 25:91da8ed57247
3287 ! ! ! user: test
3287 ! ! ! user: test
3288 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3288 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3289 ! ! ! summary: (26) merge one known; far right
3289 ! ! ! summary: (26) merge one known; far right
3290 ! ! !
3290 ! ! !
3291 ! o ! changeset: 25:91da8ed57247
3291 ! o ! changeset: 25:91da8ed57247
3292 ! !\! parent: 21:d42a756af44d
3292 ! !\! parent: 21:d42a756af44d
3293 ! ! ! parent: 24:a9c19a3d96b7
3293 ! ! ! parent: 24:a9c19a3d96b7
3294 ! ! ! user: test
3294 ! ! ! user: test
3295 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3295 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3296 ! ! ! summary: (25) merge one known; far left
3296 ! ! ! summary: (25) merge one known; far left
3297 ! ! !
3297 ! ! !
3298 ! o ! changeset: 24:a9c19a3d96b7
3298 ! o ! changeset: 24:a9c19a3d96b7
3299 ! !\ \ parent: 0:e6eb3150255d
3299 ! !\ \ parent: 0:e6eb3150255d
3300 ! ! ~ ! parent: 23:a01cddf0766d
3300 ! ! ~ ! parent: 23:a01cddf0766d
3301 ! ! ! user: test
3301 ! ! ! user: test
3302 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3302 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3303 ! ! ! summary: (24) merge one known; immediate right
3303 ! ! ! summary: (24) merge one known; immediate right
3304 ! ! /
3304 ! ! /
3305 ! o ! changeset: 23:a01cddf0766d
3305 ! o ! changeset: 23:a01cddf0766d
3306 ! !\ \ parent: 1:6db2ef61d156
3306 ! !\ \ parent: 1:6db2ef61d156
3307 ! ! ~ ! parent: 22:e0d9cccacb5d
3307 ! ! ~ ! parent: 22:e0d9cccacb5d
3308 ! ! ! user: test
3308 ! ! ! user: test
3309 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3309 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3310 ! ! ! summary: (23) merge one known; immediate left
3310 ! ! ! summary: (23) merge one known; immediate left
3311 ! ! /
3311 ! ! /
3312 ! o ! changeset: 22:e0d9cccacb5d
3312 ! o ! changeset: 22:e0d9cccacb5d
3313 !/!/ parent: 18:1aa84d96232a
3313 !/!/ parent: 18:1aa84d96232a
3314 ! ! parent: 21:d42a756af44d
3314 ! ! parent: 21:d42a756af44d
3315 ! ! user: test
3315 ! ! user: test
3316 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3316 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3317 ! ! summary: (22) merge two known; one far left, one far right
3317 ! ! summary: (22) merge two known; one far left, one far right
3318 ! !
3318 ! !
3319 ! o changeset: 21:d42a756af44d
3319 ! o changeset: 21:d42a756af44d
3320 ! !\ parent: 19:31ddc2c1573b
3320 ! !\ parent: 19:31ddc2c1573b
3321 ! ! ! parent: 20:d30ed6450e32
3321 ! ! ! parent: 20:d30ed6450e32
3322 ! ! ! user: test
3322 ! ! ! user: test
3323 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3323 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3324 ! ! ! summary: (21) expand
3324 ! ! ! summary: (21) expand
3325 ! ! !
3325 ! ! !
3326 +---o changeset: 20:d30ed6450e32
3326 +---o changeset: 20:d30ed6450e32
3327 ! ! | parent: 0:e6eb3150255d
3327 ! ! | parent: 0:e6eb3150255d
3328 ! ! ~ parent: 18:1aa84d96232a
3328 ! ! ~ parent: 18:1aa84d96232a
3329 ! ! user: test
3329 ! ! user: test
3330 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3330 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3331 ! ! summary: (20) merge two known; two far right
3331 ! ! summary: (20) merge two known; two far right
3332 ! !
3332 ! !
3333 ! o changeset: 19:31ddc2c1573b
3333 ! o changeset: 19:31ddc2c1573b
3334 ! |\ parent: 15:1dda3f72782d
3334 ! |\ parent: 15:1dda3f72782d
3335 ! ~ ~ parent: 17:44765d7c06e0
3335 ! ~ ~ parent: 17:44765d7c06e0
3336 ! user: test
3336 ! user: test
3337 ! date: Thu Jan 01 00:00:19 1970 +0000
3337 ! date: Thu Jan 01 00:00:19 1970 +0000
3338 ! summary: (19) expand
3338 ! summary: (19) expand
3339 !
3339 !
3340 o changeset: 18:1aa84d96232a
3340 o changeset: 18:1aa84d96232a
3341 |\ parent: 1:6db2ef61d156
3341 |\ parent: 1:6db2ef61d156
3342 ~ ~ parent: 15:1dda3f72782d
3342 ~ ~ parent: 15:1dda3f72782d
3343 user: test
3343 user: test
3344 date: Thu Jan 01 00:00:18 1970 +0000
3344 date: Thu Jan 01 00:00:18 1970 +0000
3345 summary: (18) merge two known; two far left
3345 summary: (18) merge two known; two far left
3346
3346
3347 $ cd ..
3347 $ cd ..
3348
3348
3349 Change graph shorten, test better with graphstyle.missing not none
3349 Change graph shorten, test better with graphstyle.missing not none
3350
3350
3351 $ cd repo
3351 $ cd repo
3352 $ cat << EOF >> $HGRCPATH
3352 $ cat << EOF >> $HGRCPATH
3353 > [experimental]
3353 > [experimental]
3354 > graphstyle.parent = |
3354 > graphstyle.parent = |
3355 > graphstyle.grandparent = :
3355 > graphstyle.grandparent = :
3356 > graphstyle.missing = '
3356 > graphstyle.missing = '
3357 > graphshorten = true
3357 > graphshorten = true
3358 > EOF
3358 > EOF
3359 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3359 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3360 @ 36 (36) buggy merge: identical parents
3360 @ 36 (36) buggy merge: identical parents
3361 o 32 (32) expand
3361 o 32 (32) expand
3362 |\
3362 |\
3363 o : 31 (31) expand
3363 o : 31 (31) expand
3364 |\:
3364 |\:
3365 o : 30 (30) expand
3365 o : 30 (30) expand
3366 |\ \
3366 |\ \
3367 o \ \ 28 (28) merge zero known
3367 o \ \ 28 (28) merge zero known
3368 |\ \ \
3368 |\ \ \
3369 o \ \ \ 26 (26) merge one known; far right
3369 o \ \ \ 26 (26) merge one known; far right
3370 |\ \ \ \
3370 |\ \ \ \
3371 | o-----+ 25 (25) merge one known; far left
3371 | o-----+ 25 (25) merge one known; far left
3372 | o ' ' : 24 (24) merge one known; immediate right
3372 | o ' ' : 24 (24) merge one known; immediate right
3373 | |\ \ \ \
3373 | |\ \ \ \
3374 | o---+ ' : 23 (23) merge one known; immediate left
3374 | o---+ ' : 23 (23) merge one known; immediate left
3375 | o-------+ 22 (22) merge two known; one far left, one far right
3375 | o-------+ 22 (22) merge two known; one far left, one far right
3376 |/ / / / /
3376 |/ / / / /
3377 | ' ' ' o 21 (21) expand
3377 | ' ' ' o 21 (21) expand
3378 | ' ' ' |\
3378 | ' ' ' |\
3379 +-+-------o 20 (20) merge two known; two far right
3379 +-+-------o 20 (20) merge two known; two far right
3380 | ' ' ' o 19 (19) expand
3380 | ' ' ' o 19 (19) expand
3381 | ' ' ' |\
3381 | ' ' ' |\
3382 o---+---+ | 18 (18) merge two known; two far left
3382 o---+---+ | 18 (18) merge two known; two far left
3383 / / / / /
3383 / / / / /
3384 ' ' ' | o 17 (17) expand
3384 ' ' ' | o 17 (17) expand
3385 ' ' ' | |\
3385 ' ' ' | |\
3386 +-+-------o 16 (16) merge two known; one immediate right, one near right
3386 +-+-------o 16 (16) merge two known; one immediate right, one near right
3387 ' ' ' o | 15 (15) expand
3387 ' ' ' o | 15 (15) expand
3388 ' ' ' |\ \
3388 ' ' ' |\ \
3389 +-------o | 14 (14) merge two known; one immediate right, one far right
3389 +-------o | 14 (14) merge two known; one immediate right, one far right
3390 ' ' ' | |/
3390 ' ' ' | |/
3391 ' ' ' o | 13 (13) expand
3391 ' ' ' o | 13 (13) expand
3392 ' ' ' |\ \
3392 ' ' ' |\ \
3393 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3393 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3394 ' ' ' | o 11 (11) expand
3394 ' ' ' | o 11 (11) expand
3395 ' ' ' | |\
3395 ' ' ' | |\
3396 +---------o 10 (10) merge two known; one immediate left, one near right
3396 +---------o 10 (10) merge two known; one immediate left, one near right
3397 ' ' ' | |/
3397 ' ' ' | |/
3398 ' ' ' o | 9 (9) expand
3398 ' ' ' o | 9 (9) expand
3399 ' ' ' |\ \
3399 ' ' ' |\ \
3400 +-------o | 8 (8) merge two known; one immediate left, one far right
3400 +-------o | 8 (8) merge two known; one immediate left, one far right
3401 ' ' ' |/ /
3401 ' ' ' |/ /
3402 ' ' ' o | 7 (7) expand
3402 ' ' ' o | 7 (7) expand
3403 ' ' ' |\ \
3403 ' ' ' |\ \
3404 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3404 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3405 ' ' ' | '/
3405 ' ' ' | '/
3406 ' ' ' o ' 5 (5) expand
3406 ' ' ' o ' 5 (5) expand
3407 ' ' ' |\ \
3407 ' ' ' |\ \
3408 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3408 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3409 ' ' ' '/ /
3409 ' ' ' '/ /
3410
3410
3411 behavior with newlines
3411 behavior with newlines
3412
3412
3413 $ hg log -G -r ::2 -T '{rev} {desc}'
3413 $ hg log -G -r ::2 -T '{rev} {desc}'
3414 o 2 (2) collapse
3414 o 2 (2) collapse
3415 o 1 (1) collapse
3415 o 1 (1) collapse
3416 o 0 (0) root
3416 o 0 (0) root
3417
3417
3418 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3418 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3419 o 2 (2) collapse
3419 o 2 (2) collapse
3420 o 1 (1) collapse
3420 o 1 (1) collapse
3421 o 0 (0) root
3421 o 0 (0) root
3422
3422
3423 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3423 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3424 o 2 (2) collapse
3424 o 2 (2) collapse
3425 |
3425 |
3426 o 1 (1) collapse
3426 o 1 (1) collapse
3427 |
3427 |
3428 o 0 (0) root
3428 o 0 (0) root
3429
3429
3430
3430
3431 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3431 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3432 o
3432 o
3433 | 2 (2) collapse
3433 | 2 (2) collapse
3434 o
3434 o
3435 | 1 (1) collapse
3435 | 1 (1) collapse
3436 o
3436 o
3437 0 (0) root
3437 0 (0) root
3438
3438
3439 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3439 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3440 o 2 (2) collapse
3440 o 2 (2) collapse
3441 |
3441 |
3442 |
3442 |
3443 o 1 (1) collapse
3443 o 1 (1) collapse
3444 |
3444 |
3445 |
3445 |
3446 o 0 (0) root
3446 o 0 (0) root
3447
3447
3448
3448
3449 $ cd ..
3449 $ cd ..
3450
3450
3451 When inserting extra line nodes to handle more than 2 parents, ensure that
3451 When inserting extra line nodes to handle more than 2 parents, ensure that
3452 the right node styles are used (issue5174):
3452 the right node styles are used (issue5174):
3453
3453
3454 $ hg init repo-issue5174
3454 $ hg init repo-issue5174
3455 $ cd repo-issue5174
3455 $ cd repo-issue5174
3456 $ echo a > f0
3456 $ echo a > f0
3457 $ hg ci -Aqm 0
3457 $ hg ci -Aqm 0
3458 $ echo a > f1
3458 $ echo a > f1
3459 $ hg ci -Aqm 1
3459 $ hg ci -Aqm 1
3460 $ echo a > f2
3460 $ echo a > f2
3461 $ hg ci -Aqm 2
3461 $ hg ci -Aqm 2
3462 $ hg co ".^"
3462 $ hg co ".^"
3463 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3463 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3464 $ echo a > f3
3464 $ echo a > f3
3465 $ hg ci -Aqm 3
3465 $ hg ci -Aqm 3
3466 $ hg co ".^^"
3466 $ hg co ".^^"
3467 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3467 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3468 $ echo a > f4
3468 $ echo a > f4
3469 $ hg ci -Aqm 4
3469 $ hg ci -Aqm 4
3470 $ hg merge -r 2
3470 $ hg merge -r 2
3471 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3471 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3472 (branch merge, don't forget to commit)
3472 (branch merge, don't forget to commit)
3473 $ hg ci -qm 5
3473 $ hg ci -qm 5
3474 $ hg merge -r 3
3474 $ hg merge -r 3
3475 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3475 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3476 (branch merge, don't forget to commit)
3476 (branch merge, don't forget to commit)
3477 $ hg ci -qm 6
3477 $ hg ci -qm 6
3478 $ hg log -G -r '0 | 1 | 2 | 6'
3478 $ hg log -G -r '0 | 1 | 2 | 6'
3479 @ changeset: 6:851fe89689ad
3479 @ changeset: 6:851fe89689ad
3480 :\ tag: tip
3480 :\ tag: tip
3481 : : parent: 5:4f1e3cf15f5d
3481 : : parent: 5:4f1e3cf15f5d
3482 : : parent: 3:b74ba7084d2d
3482 : : parent: 3:b74ba7084d2d
3483 : : user: test
3483 : : user: test
3484 : : date: Thu Jan 01 00:00:00 1970 +0000
3484 : : date: Thu Jan 01 00:00:00 1970 +0000
3485 : : summary: 6
3485 : : summary: 6
3486 : :
3486 : :
3487 : \
3487 : \
3488 : :\
3488 : :\
3489 : o : changeset: 2:3e6599df4cce
3489 : o : changeset: 2:3e6599df4cce
3490 : :/ user: test
3490 : :/ user: test
3491 : : date: Thu Jan 01 00:00:00 1970 +0000
3491 : : date: Thu Jan 01 00:00:00 1970 +0000
3492 : : summary: 2
3492 : : summary: 2
3493 : :
3493 : :
3494 : o changeset: 1:bd9a55143933
3494 : o changeset: 1:bd9a55143933
3495 :/ user: test
3495 :/ user: test
3496 : date: Thu Jan 01 00:00:00 1970 +0000
3496 : date: Thu Jan 01 00:00:00 1970 +0000
3497 : summary: 1
3497 : summary: 1
3498 :
3498 :
3499 o changeset: 0:870a5edc339c
3499 o changeset: 0:870a5edc339c
3500 user: test
3500 user: test
3501 date: Thu Jan 01 00:00:00 1970 +0000
3501 date: Thu Jan 01 00:00:00 1970 +0000
3502 summary: 0
3502 summary: 0
3503
3503
3504
3504
3505 $ cd ..
3505 $ cd ..
3506
3506
3507 Multiple roots (issue5440):
3507 Multiple roots (issue5440):
3508
3508
3509 $ hg init multiroots
3509 $ hg init multiroots
3510 $ cd multiroots
3510 $ cd multiroots
3511 $ cat <<EOF > .hg/hgrc
3511 $ cat <<EOF > .hg/hgrc
3512 > [ui]
3512 > [ui]
3513 > logtemplate = '{rev} {desc}\n\n'
3513 > logtemplate = '{rev} {desc}\n\n'
3514 > EOF
3514 > EOF
3515
3515
3516 $ touch foo
3516 $ touch foo
3517 $ hg ci -Aqm foo
3517 $ hg ci -Aqm foo
3518 $ hg co -q null
3518 $ hg co -q null
3519 $ touch bar
3519 $ touch bar
3520 $ hg ci -Aqm bar
3520 $ hg ci -Aqm bar
3521
3521
3522 $ hg log -Gr null:
3522 $ hg log -Gr null:
3523 @ 1 bar
3523 @ 1 bar
3524 |
3524 |
3525 | o 0 foo
3525 | o 0 foo
3526 |/
3526 |/
3527 o -1
3527 o -1
3528
3528
3529 $ hg log -Gr null+0
3529 $ hg log -Gr null+0
3530 o 0 foo
3530 o 0 foo
3531 |
3531 |
3532 o -1
3532 o -1
3533
3533
3534 $ hg log -Gr null+1
3534 $ hg log -Gr null+1
3535 @ 1 bar
3535 @ 1 bar
3536 |
3536 |
3537 o -1
3537 o -1
3538
3538
3539
3539
3540 $ cd ..
3540 $ cd ..
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now