##// END OF EJS Templates
grep: add MULTIREV support to --allfiles flag...
Sangeet Kumar Mishra -
r38981:f3f10997 default
parent child Browse files
Show More
@@ -1,3274 +1,3271 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 hex,
16 hex,
17 nullid,
17 nullid,
18 nullrev,
18 nullrev,
19 short,
19 short,
20 )
20 )
21
21
22 from . import (
22 from . import (
23 bookmarks,
23 bookmarks,
24 changelog,
24 changelog,
25 copies,
25 copies,
26 crecord as crecordmod,
26 crecord as crecordmod,
27 dirstateguard,
27 dirstateguard,
28 encoding,
28 encoding,
29 error,
29 error,
30 formatter,
30 formatter,
31 logcmdutil,
31 logcmdutil,
32 match as matchmod,
32 match as matchmod,
33 merge as mergemod,
33 merge as mergemod,
34 mergeutil,
34 mergeutil,
35 obsolete,
35 obsolete,
36 patch,
36 patch,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 pycompat,
39 pycompat,
40 revlog,
40 revlog,
41 rewriteutil,
41 rewriteutil,
42 scmutil,
42 scmutil,
43 smartset,
43 smartset,
44 subrepoutil,
44 subrepoutil,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 vfs as vfsmod,
48 vfs as vfsmod,
49 )
49 )
50
50
51 from .utils import (
51 from .utils import (
52 dateutil,
52 dateutil,
53 stringutil,
53 stringutil,
54 )
54 )
55
55
56 stringio = util.stringio
56 stringio = util.stringio
57
57
58 # templates of common command options
58 # templates of common command options
59
59
60 dryrunopts = [
60 dryrunopts = [
61 ('n', 'dry-run', None,
61 ('n', 'dry-run', None,
62 _('do not perform actions, just print output')),
62 _('do not perform actions, just print output')),
63 ]
63 ]
64
64
65 confirmopts = [
65 confirmopts = [
66 ('', 'confirm', None,
66 ('', 'confirm', None,
67 _('ask before applying actions')),
67 _('ask before applying actions')),
68 ]
68 ]
69
69
70 remoteopts = [
70 remoteopts = [
71 ('e', 'ssh', '',
71 ('e', 'ssh', '',
72 _('specify ssh command to use'), _('CMD')),
72 _('specify ssh command to use'), _('CMD')),
73 ('', 'remotecmd', '',
73 ('', 'remotecmd', '',
74 _('specify hg command to run on the remote side'), _('CMD')),
74 _('specify hg command to run on the remote side'), _('CMD')),
75 ('', 'insecure', None,
75 ('', 'insecure', None,
76 _('do not verify server certificate (ignoring web.cacerts config)')),
76 _('do not verify server certificate (ignoring web.cacerts config)')),
77 ]
77 ]
78
78
79 walkopts = [
79 walkopts = [
80 ('I', 'include', [],
80 ('I', 'include', [],
81 _('include names matching the given patterns'), _('PATTERN')),
81 _('include names matching the given patterns'), _('PATTERN')),
82 ('X', 'exclude', [],
82 ('X', 'exclude', [],
83 _('exclude names matching the given patterns'), _('PATTERN')),
83 _('exclude names matching the given patterns'), _('PATTERN')),
84 ]
84 ]
85
85
86 commitopts = [
86 commitopts = [
87 ('m', 'message', '',
87 ('m', 'message', '',
88 _('use text as commit message'), _('TEXT')),
88 _('use text as commit message'), _('TEXT')),
89 ('l', 'logfile', '',
89 ('l', 'logfile', '',
90 _('read commit message from file'), _('FILE')),
90 _('read commit message from file'), _('FILE')),
91 ]
91 ]
92
92
93 commitopts2 = [
93 commitopts2 = [
94 ('d', 'date', '',
94 ('d', 'date', '',
95 _('record the specified date as commit date'), _('DATE')),
95 _('record the specified date as commit date'), _('DATE')),
96 ('u', 'user', '',
96 ('u', 'user', '',
97 _('record the specified user as committer'), _('USER')),
97 _('record the specified user as committer'), _('USER')),
98 ]
98 ]
99
99
100 # hidden for now
100 # hidden for now
101 formatteropts = [
101 formatteropts = [
102 ('T', 'template', '',
102 ('T', 'template', '',
103 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
103 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
104 ]
104 ]
105
105
106 templateopts = [
106 templateopts = [
107 ('', 'style', '',
107 ('', 'style', '',
108 _('display using template map file (DEPRECATED)'), _('STYLE')),
108 _('display using template map file (DEPRECATED)'), _('STYLE')),
109 ('T', 'template', '',
109 ('T', 'template', '',
110 _('display with template'), _('TEMPLATE')),
110 _('display with template'), _('TEMPLATE')),
111 ]
111 ]
112
112
113 logopts = [
113 logopts = [
114 ('p', 'patch', None, _('show patch')),
114 ('p', 'patch', None, _('show patch')),
115 ('g', 'git', None, _('use git extended diff format')),
115 ('g', 'git', None, _('use git extended diff format')),
116 ('l', 'limit', '',
116 ('l', 'limit', '',
117 _('limit number of changes displayed'), _('NUM')),
117 _('limit number of changes displayed'), _('NUM')),
118 ('M', 'no-merges', None, _('do not show merges')),
118 ('M', 'no-merges', None, _('do not show merges')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
120 ('G', 'graph', None, _("show the revision DAG")),
120 ('G', 'graph', None, _("show the revision DAG")),
121 ] + templateopts
121 ] + templateopts
122
122
123 diffopts = [
123 diffopts = [
124 ('a', 'text', None, _('treat all files as text')),
124 ('a', 'text', None, _('treat all files as text')),
125 ('g', 'git', None, _('use git extended diff format')),
125 ('g', 'git', None, _('use git extended diff format')),
126 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
126 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
127 ('', 'nodates', None, _('omit dates from diff headers'))
127 ('', 'nodates', None, _('omit dates from diff headers'))
128 ]
128 ]
129
129
130 diffwsopts = [
130 diffwsopts = [
131 ('w', 'ignore-all-space', None,
131 ('w', 'ignore-all-space', None,
132 _('ignore white space when comparing lines')),
132 _('ignore white space when comparing lines')),
133 ('b', 'ignore-space-change', None,
133 ('b', 'ignore-space-change', None,
134 _('ignore changes in the amount of white space')),
134 _('ignore changes in the amount of white space')),
135 ('B', 'ignore-blank-lines', None,
135 ('B', 'ignore-blank-lines', None,
136 _('ignore changes whose lines are all blank')),
136 _('ignore changes whose lines are all blank')),
137 ('Z', 'ignore-space-at-eol', None,
137 ('Z', 'ignore-space-at-eol', None,
138 _('ignore changes in whitespace at EOL')),
138 _('ignore changes in whitespace at EOL')),
139 ]
139 ]
140
140
141 diffopts2 = [
141 diffopts2 = [
142 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
142 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
143 ('p', 'show-function', None, _('show which function each change is in')),
143 ('p', 'show-function', None, _('show which function each change is in')),
144 ('', 'reverse', None, _('produce a diff that undoes the changes')),
144 ('', 'reverse', None, _('produce a diff that undoes the changes')),
145 ] + diffwsopts + [
145 ] + diffwsopts + [
146 ('U', 'unified', '',
146 ('U', 'unified', '',
147 _('number of lines of context to show'), _('NUM')),
147 _('number of lines of context to show'), _('NUM')),
148 ('', 'stat', None, _('output diffstat-style summary of changes')),
148 ('', 'stat', None, _('output diffstat-style summary of changes')),
149 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
149 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
150 ]
150 ]
151
151
152 mergetoolopts = [
152 mergetoolopts = [
153 ('t', 'tool', '', _('specify merge tool')),
153 ('t', 'tool', '', _('specify merge tool')),
154 ]
154 ]
155
155
156 similarityopts = [
156 similarityopts = [
157 ('s', 'similarity', '',
157 ('s', 'similarity', '',
158 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
158 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
159 ]
159 ]
160
160
161 subrepoopts = [
161 subrepoopts = [
162 ('S', 'subrepos', None,
162 ('S', 'subrepos', None,
163 _('recurse into subrepositories'))
163 _('recurse into subrepositories'))
164 ]
164 ]
165
165
166 debugrevlogopts = [
166 debugrevlogopts = [
167 ('c', 'changelog', False, _('open changelog')),
167 ('c', 'changelog', False, _('open changelog')),
168 ('m', 'manifest', False, _('open manifest')),
168 ('m', 'manifest', False, _('open manifest')),
169 ('', 'dir', '', _('open directory manifest')),
169 ('', 'dir', '', _('open directory manifest')),
170 ]
170 ]
171
171
172 # special string such that everything below this line will be ingored in the
172 # special string such that everything below this line will be ingored in the
173 # editor text
173 # editor text
174 _linebelow = "^HG: ------------------------ >8 ------------------------$"
174 _linebelow = "^HG: ------------------------ >8 ------------------------$"
175
175
176 def ishunk(x):
176 def ishunk(x):
177 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
177 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
178 return isinstance(x, hunkclasses)
178 return isinstance(x, hunkclasses)
179
179
180 def newandmodified(chunks, originalchunks):
180 def newandmodified(chunks, originalchunks):
181 newlyaddedandmodifiedfiles = set()
181 newlyaddedandmodifiedfiles = set()
182 for chunk in chunks:
182 for chunk in chunks:
183 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
183 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
184 originalchunks:
184 originalchunks:
185 newlyaddedandmodifiedfiles.add(chunk.header.filename())
185 newlyaddedandmodifiedfiles.add(chunk.header.filename())
186 return newlyaddedandmodifiedfiles
186 return newlyaddedandmodifiedfiles
187
187
188 def parsealiases(cmd):
188 def parsealiases(cmd):
189 return cmd.lstrip("^").split("|")
189 return cmd.lstrip("^").split("|")
190
190
191 def setupwrapcolorwrite(ui):
191 def setupwrapcolorwrite(ui):
192 # wrap ui.write so diff output can be labeled/colorized
192 # wrap ui.write so diff output can be labeled/colorized
193 def wrapwrite(orig, *args, **kw):
193 def wrapwrite(orig, *args, **kw):
194 label = kw.pop(r'label', '')
194 label = kw.pop(r'label', '')
195 for chunk, l in patch.difflabel(lambda: args):
195 for chunk, l in patch.difflabel(lambda: args):
196 orig(chunk, label=label + l)
196 orig(chunk, label=label + l)
197
197
198 oldwrite = ui.write
198 oldwrite = ui.write
199 def wrap(*args, **kwargs):
199 def wrap(*args, **kwargs):
200 return wrapwrite(oldwrite, *args, **kwargs)
200 return wrapwrite(oldwrite, *args, **kwargs)
201 setattr(ui, 'write', wrap)
201 setattr(ui, 'write', wrap)
202 return oldwrite
202 return oldwrite
203
203
204 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
204 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
205 try:
205 try:
206 if usecurses:
206 if usecurses:
207 if testfile:
207 if testfile:
208 recordfn = crecordmod.testdecorator(
208 recordfn = crecordmod.testdecorator(
209 testfile, crecordmod.testchunkselector)
209 testfile, crecordmod.testchunkselector)
210 else:
210 else:
211 recordfn = crecordmod.chunkselector
211 recordfn = crecordmod.chunkselector
212
212
213 return crecordmod.filterpatch(ui, originalhunks, recordfn,
213 return crecordmod.filterpatch(ui, originalhunks, recordfn,
214 operation)
214 operation)
215 except crecordmod.fallbackerror as e:
215 except crecordmod.fallbackerror as e:
216 ui.warn('%s\n' % e.message)
216 ui.warn('%s\n' % e.message)
217 ui.warn(_('falling back to text mode\n'))
217 ui.warn(_('falling back to text mode\n'))
218
218
219 return patch.filterpatch(ui, originalhunks, operation)
219 return patch.filterpatch(ui, originalhunks, operation)
220
220
221 def recordfilter(ui, originalhunks, operation=None):
221 def recordfilter(ui, originalhunks, operation=None):
222 """ Prompts the user to filter the originalhunks and return a list of
222 """ Prompts the user to filter the originalhunks and return a list of
223 selected hunks.
223 selected hunks.
224 *operation* is used for to build ui messages to indicate the user what
224 *operation* is used for to build ui messages to indicate the user what
225 kind of filtering they are doing: reverting, committing, shelving, etc.
225 kind of filtering they are doing: reverting, committing, shelving, etc.
226 (see patch.filterpatch).
226 (see patch.filterpatch).
227 """
227 """
228 usecurses = crecordmod.checkcurses(ui)
228 usecurses = crecordmod.checkcurses(ui)
229 testfile = ui.config('experimental', 'crecordtest')
229 testfile = ui.config('experimental', 'crecordtest')
230 oldwrite = setupwrapcolorwrite(ui)
230 oldwrite = setupwrapcolorwrite(ui)
231 try:
231 try:
232 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
232 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
233 testfile, operation)
233 testfile, operation)
234 finally:
234 finally:
235 ui.write = oldwrite
235 ui.write = oldwrite
236 return newchunks, newopts
236 return newchunks, newopts
237
237
238 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
238 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
239 filterfn, *pats, **opts):
239 filterfn, *pats, **opts):
240 opts = pycompat.byteskwargs(opts)
240 opts = pycompat.byteskwargs(opts)
241 if not ui.interactive():
241 if not ui.interactive():
242 if cmdsuggest:
242 if cmdsuggest:
243 msg = _('running non-interactively, use %s instead') % cmdsuggest
243 msg = _('running non-interactively, use %s instead') % cmdsuggest
244 else:
244 else:
245 msg = _('running non-interactively')
245 msg = _('running non-interactively')
246 raise error.Abort(msg)
246 raise error.Abort(msg)
247
247
248 # make sure username is set before going interactive
248 # make sure username is set before going interactive
249 if not opts.get('user'):
249 if not opts.get('user'):
250 ui.username() # raise exception, username not provided
250 ui.username() # raise exception, username not provided
251
251
252 def recordfunc(ui, repo, message, match, opts):
252 def recordfunc(ui, repo, message, match, opts):
253 """This is generic record driver.
253 """This is generic record driver.
254
254
255 Its job is to interactively filter local changes, and
255 Its job is to interactively filter local changes, and
256 accordingly prepare working directory into a state in which the
256 accordingly prepare working directory into a state in which the
257 job can be delegated to a non-interactive commit command such as
257 job can be delegated to a non-interactive commit command such as
258 'commit' or 'qrefresh'.
258 'commit' or 'qrefresh'.
259
259
260 After the actual job is done by non-interactive command, the
260 After the actual job is done by non-interactive command, the
261 working directory is restored to its original state.
261 working directory is restored to its original state.
262
262
263 In the end we'll record interesting changes, and everything else
263 In the end we'll record interesting changes, and everything else
264 will be left in place, so the user can continue working.
264 will be left in place, so the user can continue working.
265 """
265 """
266
266
267 checkunfinished(repo, commit=True)
267 checkunfinished(repo, commit=True)
268 wctx = repo[None]
268 wctx = repo[None]
269 merge = len(wctx.parents()) > 1
269 merge = len(wctx.parents()) > 1
270 if merge:
270 if merge:
271 raise error.Abort(_('cannot partially commit a merge '
271 raise error.Abort(_('cannot partially commit a merge '
272 '(use "hg commit" instead)'))
272 '(use "hg commit" instead)'))
273
273
274 def fail(f, msg):
274 def fail(f, msg):
275 raise error.Abort('%s: %s' % (f, msg))
275 raise error.Abort('%s: %s' % (f, msg))
276
276
277 force = opts.get('force')
277 force = opts.get('force')
278 if not force:
278 if not force:
279 vdirs = []
279 vdirs = []
280 match.explicitdir = vdirs.append
280 match.explicitdir = vdirs.append
281 match.bad = fail
281 match.bad = fail
282
282
283 status = repo.status(match=match)
283 status = repo.status(match=match)
284 if not force:
284 if not force:
285 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
285 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
286 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
286 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
287 diffopts.nodates = True
287 diffopts.nodates = True
288 diffopts.git = True
288 diffopts.git = True
289 diffopts.showfunc = True
289 diffopts.showfunc = True
290 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
290 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
291 originalchunks = patch.parsepatch(originaldiff)
291 originalchunks = patch.parsepatch(originaldiff)
292
292
293 # 1. filter patch, since we are intending to apply subset of it
293 # 1. filter patch, since we are intending to apply subset of it
294 try:
294 try:
295 chunks, newopts = filterfn(ui, originalchunks)
295 chunks, newopts = filterfn(ui, originalchunks)
296 except error.PatchError as err:
296 except error.PatchError as err:
297 raise error.Abort(_('error parsing patch: %s') % err)
297 raise error.Abort(_('error parsing patch: %s') % err)
298 opts.update(newopts)
298 opts.update(newopts)
299
299
300 # We need to keep a backup of files that have been newly added and
300 # We need to keep a backup of files that have been newly added and
301 # modified during the recording process because there is a previous
301 # modified during the recording process because there is a previous
302 # version without the edit in the workdir
302 # version without the edit in the workdir
303 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
303 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
304 contenders = set()
304 contenders = set()
305 for h in chunks:
305 for h in chunks:
306 try:
306 try:
307 contenders.update(set(h.files()))
307 contenders.update(set(h.files()))
308 except AttributeError:
308 except AttributeError:
309 pass
309 pass
310
310
311 changed = status.modified + status.added + status.removed
311 changed = status.modified + status.added + status.removed
312 newfiles = [f for f in changed if f in contenders]
312 newfiles = [f for f in changed if f in contenders]
313 if not newfiles:
313 if not newfiles:
314 ui.status(_('no changes to record\n'))
314 ui.status(_('no changes to record\n'))
315 return 0
315 return 0
316
316
317 modified = set(status.modified)
317 modified = set(status.modified)
318
318
319 # 2. backup changed files, so we can restore them in the end
319 # 2. backup changed files, so we can restore them in the end
320
320
321 if backupall:
321 if backupall:
322 tobackup = changed
322 tobackup = changed
323 else:
323 else:
324 tobackup = [f for f in newfiles if f in modified or f in \
324 tobackup = [f for f in newfiles if f in modified or f in \
325 newlyaddedandmodifiedfiles]
325 newlyaddedandmodifiedfiles]
326 backups = {}
326 backups = {}
327 if tobackup:
327 if tobackup:
328 backupdir = repo.vfs.join('record-backups')
328 backupdir = repo.vfs.join('record-backups')
329 try:
329 try:
330 os.mkdir(backupdir)
330 os.mkdir(backupdir)
331 except OSError as err:
331 except OSError as err:
332 if err.errno != errno.EEXIST:
332 if err.errno != errno.EEXIST:
333 raise
333 raise
334 try:
334 try:
335 # backup continues
335 # backup continues
336 for f in tobackup:
336 for f in tobackup:
337 fd, tmpname = pycompat.mkstemp(prefix=f.replace('/', '_') + '.',
337 fd, tmpname = pycompat.mkstemp(prefix=f.replace('/', '_') + '.',
338 dir=backupdir)
338 dir=backupdir)
339 os.close(fd)
339 os.close(fd)
340 ui.debug('backup %r as %r\n' % (f, tmpname))
340 ui.debug('backup %r as %r\n' % (f, tmpname))
341 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
341 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
342 backups[f] = tmpname
342 backups[f] = tmpname
343
343
344 fp = stringio()
344 fp = stringio()
345 for c in chunks:
345 for c in chunks:
346 fname = c.filename()
346 fname = c.filename()
347 if fname in backups:
347 if fname in backups:
348 c.write(fp)
348 c.write(fp)
349 dopatch = fp.tell()
349 dopatch = fp.tell()
350 fp.seek(0)
350 fp.seek(0)
351
351
352 # 2.5 optionally review / modify patch in text editor
352 # 2.5 optionally review / modify patch in text editor
353 if opts.get('review', False):
353 if opts.get('review', False):
354 patchtext = (crecordmod.diffhelptext
354 patchtext = (crecordmod.diffhelptext
355 + crecordmod.patchhelptext
355 + crecordmod.patchhelptext
356 + fp.read())
356 + fp.read())
357 reviewedpatch = ui.edit(patchtext, "",
357 reviewedpatch = ui.edit(patchtext, "",
358 action="diff",
358 action="diff",
359 repopath=repo.path)
359 repopath=repo.path)
360 fp.truncate(0)
360 fp.truncate(0)
361 fp.write(reviewedpatch)
361 fp.write(reviewedpatch)
362 fp.seek(0)
362 fp.seek(0)
363
363
364 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
364 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
365 # 3a. apply filtered patch to clean repo (clean)
365 # 3a. apply filtered patch to clean repo (clean)
366 if backups:
366 if backups:
367 # Equivalent to hg.revert
367 # Equivalent to hg.revert
368 m = scmutil.matchfiles(repo, backups.keys())
368 m = scmutil.matchfiles(repo, backups.keys())
369 mergemod.update(repo, repo.dirstate.p1(),
369 mergemod.update(repo, repo.dirstate.p1(),
370 False, True, matcher=m)
370 False, True, matcher=m)
371
371
372 # 3b. (apply)
372 # 3b. (apply)
373 if dopatch:
373 if dopatch:
374 try:
374 try:
375 ui.debug('applying patch\n')
375 ui.debug('applying patch\n')
376 ui.debug(fp.getvalue())
376 ui.debug(fp.getvalue())
377 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
377 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
378 except error.PatchError as err:
378 except error.PatchError as err:
379 raise error.Abort(pycompat.bytestr(err))
379 raise error.Abort(pycompat.bytestr(err))
380 del fp
380 del fp
381
381
382 # 4. We prepared working directory according to filtered
382 # 4. We prepared working directory according to filtered
383 # patch. Now is the time to delegate the job to
383 # patch. Now is the time to delegate the job to
384 # commit/qrefresh or the like!
384 # commit/qrefresh or the like!
385
385
386 # Make all of the pathnames absolute.
386 # Make all of the pathnames absolute.
387 newfiles = [repo.wjoin(nf) for nf in newfiles]
387 newfiles = [repo.wjoin(nf) for nf in newfiles]
388 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
388 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
389 finally:
389 finally:
390 # 5. finally restore backed-up files
390 # 5. finally restore backed-up files
391 try:
391 try:
392 dirstate = repo.dirstate
392 dirstate = repo.dirstate
393 for realname, tmpname in backups.iteritems():
393 for realname, tmpname in backups.iteritems():
394 ui.debug('restoring %r to %r\n' % (tmpname, realname))
394 ui.debug('restoring %r to %r\n' % (tmpname, realname))
395
395
396 if dirstate[realname] == 'n':
396 if dirstate[realname] == 'n':
397 # without normallookup, restoring timestamp
397 # without normallookup, restoring timestamp
398 # may cause partially committed files
398 # may cause partially committed files
399 # to be treated as unmodified
399 # to be treated as unmodified
400 dirstate.normallookup(realname)
400 dirstate.normallookup(realname)
401
401
402 # copystat=True here and above are a hack to trick any
402 # copystat=True here and above are a hack to trick any
403 # editors that have f open that we haven't modified them.
403 # editors that have f open that we haven't modified them.
404 #
404 #
405 # Also note that this racy as an editor could notice the
405 # Also note that this racy as an editor could notice the
406 # file's mtime before we've finished writing it.
406 # file's mtime before we've finished writing it.
407 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
407 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
408 os.unlink(tmpname)
408 os.unlink(tmpname)
409 if tobackup:
409 if tobackup:
410 os.rmdir(backupdir)
410 os.rmdir(backupdir)
411 except OSError:
411 except OSError:
412 pass
412 pass
413
413
414 def recordinwlock(ui, repo, message, match, opts):
414 def recordinwlock(ui, repo, message, match, opts):
415 with repo.wlock():
415 with repo.wlock():
416 return recordfunc(ui, repo, message, match, opts)
416 return recordfunc(ui, repo, message, match, opts)
417
417
418 return commit(ui, repo, recordinwlock, pats, opts)
418 return commit(ui, repo, recordinwlock, pats, opts)
419
419
420 class dirnode(object):
420 class dirnode(object):
421 """
421 """
422 Represent a directory in user working copy with information required for
422 Represent a directory in user working copy with information required for
423 the purpose of tersing its status.
423 the purpose of tersing its status.
424
424
425 path is the path to the directory, without a trailing '/'
425 path is the path to the directory, without a trailing '/'
426
426
427 statuses is a set of statuses of all files in this directory (this includes
427 statuses is a set of statuses of all files in this directory (this includes
428 all the files in all the subdirectories too)
428 all the files in all the subdirectories too)
429
429
430 files is a list of files which are direct child of this directory
430 files is a list of files which are direct child of this directory
431
431
432 subdirs is a dictionary of sub-directory name as the key and it's own
432 subdirs is a dictionary of sub-directory name as the key and it's own
433 dirnode object as the value
433 dirnode object as the value
434 """
434 """
435
435
436 def __init__(self, dirpath):
436 def __init__(self, dirpath):
437 self.path = dirpath
437 self.path = dirpath
438 self.statuses = set([])
438 self.statuses = set([])
439 self.files = []
439 self.files = []
440 self.subdirs = {}
440 self.subdirs = {}
441
441
442 def _addfileindir(self, filename, status):
442 def _addfileindir(self, filename, status):
443 """Add a file in this directory as a direct child."""
443 """Add a file in this directory as a direct child."""
444 self.files.append((filename, status))
444 self.files.append((filename, status))
445
445
446 def addfile(self, filename, status):
446 def addfile(self, filename, status):
447 """
447 """
448 Add a file to this directory or to its direct parent directory.
448 Add a file to this directory or to its direct parent directory.
449
449
450 If the file is not direct child of this directory, we traverse to the
450 If the file is not direct child of this directory, we traverse to the
451 directory of which this file is a direct child of and add the file
451 directory of which this file is a direct child of and add the file
452 there.
452 there.
453 """
453 """
454
454
455 # the filename contains a path separator, it means it's not the direct
455 # the filename contains a path separator, it means it's not the direct
456 # child of this directory
456 # child of this directory
457 if '/' in filename:
457 if '/' in filename:
458 subdir, filep = filename.split('/', 1)
458 subdir, filep = filename.split('/', 1)
459
459
460 # does the dirnode object for subdir exists
460 # does the dirnode object for subdir exists
461 if subdir not in self.subdirs:
461 if subdir not in self.subdirs:
462 subdirpath = pathutil.join(self.path, subdir)
462 subdirpath = pathutil.join(self.path, subdir)
463 self.subdirs[subdir] = dirnode(subdirpath)
463 self.subdirs[subdir] = dirnode(subdirpath)
464
464
465 # try adding the file in subdir
465 # try adding the file in subdir
466 self.subdirs[subdir].addfile(filep, status)
466 self.subdirs[subdir].addfile(filep, status)
467
467
468 else:
468 else:
469 self._addfileindir(filename, status)
469 self._addfileindir(filename, status)
470
470
471 if status not in self.statuses:
471 if status not in self.statuses:
472 self.statuses.add(status)
472 self.statuses.add(status)
473
473
474 def iterfilepaths(self):
474 def iterfilepaths(self):
475 """Yield (status, path) for files directly under this directory."""
475 """Yield (status, path) for files directly under this directory."""
476 for f, st in self.files:
476 for f, st in self.files:
477 yield st, pathutil.join(self.path, f)
477 yield st, pathutil.join(self.path, f)
478
478
479 def tersewalk(self, terseargs):
479 def tersewalk(self, terseargs):
480 """
480 """
481 Yield (status, path) obtained by processing the status of this
481 Yield (status, path) obtained by processing the status of this
482 dirnode.
482 dirnode.
483
483
484 terseargs is the string of arguments passed by the user with `--terse`
484 terseargs is the string of arguments passed by the user with `--terse`
485 flag.
485 flag.
486
486
487 Following are the cases which can happen:
487 Following are the cases which can happen:
488
488
489 1) All the files in the directory (including all the files in its
489 1) All the files in the directory (including all the files in its
490 subdirectories) share the same status and the user has asked us to terse
490 subdirectories) share the same status and the user has asked us to terse
491 that status. -> yield (status, dirpath). dirpath will end in '/'.
491 that status. -> yield (status, dirpath). dirpath will end in '/'.
492
492
493 2) Otherwise, we do following:
493 2) Otherwise, we do following:
494
494
495 a) Yield (status, filepath) for all the files which are in this
495 a) Yield (status, filepath) for all the files which are in this
496 directory (only the ones in this directory, not the subdirs)
496 directory (only the ones in this directory, not the subdirs)
497
497
498 b) Recurse the function on all the subdirectories of this
498 b) Recurse the function on all the subdirectories of this
499 directory
499 directory
500 """
500 """
501
501
502 if len(self.statuses) == 1:
502 if len(self.statuses) == 1:
503 onlyst = self.statuses.pop()
503 onlyst = self.statuses.pop()
504
504
505 # Making sure we terse only when the status abbreviation is
505 # Making sure we terse only when the status abbreviation is
506 # passed as terse argument
506 # passed as terse argument
507 if onlyst in terseargs:
507 if onlyst in terseargs:
508 yield onlyst, self.path + '/'
508 yield onlyst, self.path + '/'
509 return
509 return
510
510
511 # add the files to status list
511 # add the files to status list
512 for st, fpath in self.iterfilepaths():
512 for st, fpath in self.iterfilepaths():
513 yield st, fpath
513 yield st, fpath
514
514
515 #recurse on the subdirs
515 #recurse on the subdirs
516 for dirobj in self.subdirs.values():
516 for dirobj in self.subdirs.values():
517 for st, fpath in dirobj.tersewalk(terseargs):
517 for st, fpath in dirobj.tersewalk(terseargs):
518 yield st, fpath
518 yield st, fpath
519
519
520 def tersedir(statuslist, terseargs):
520 def tersedir(statuslist, terseargs):
521 """
521 """
522 Terse the status if all the files in a directory shares the same status.
522 Terse the status if all the files in a directory shares the same status.
523
523
524 statuslist is scmutil.status() object which contains a list of files for
524 statuslist is scmutil.status() object which contains a list of files for
525 each status.
525 each status.
526 terseargs is string which is passed by the user as the argument to `--terse`
526 terseargs is string which is passed by the user as the argument to `--terse`
527 flag.
527 flag.
528
528
529 The function makes a tree of objects of dirnode class, and at each node it
529 The function makes a tree of objects of dirnode class, and at each node it
530 stores the information required to know whether we can terse a certain
530 stores the information required to know whether we can terse a certain
531 directory or not.
531 directory or not.
532 """
532 """
533 # the order matters here as that is used to produce final list
533 # the order matters here as that is used to produce final list
534 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
534 allst = ('m', 'a', 'r', 'd', 'u', 'i', 'c')
535
535
536 # checking the argument validity
536 # checking the argument validity
537 for s in pycompat.bytestr(terseargs):
537 for s in pycompat.bytestr(terseargs):
538 if s not in allst:
538 if s not in allst:
539 raise error.Abort(_("'%s' not recognized") % s)
539 raise error.Abort(_("'%s' not recognized") % s)
540
540
541 # creating a dirnode object for the root of the repo
541 # creating a dirnode object for the root of the repo
542 rootobj = dirnode('')
542 rootobj = dirnode('')
543 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
543 pstatus = ('modified', 'added', 'deleted', 'clean', 'unknown',
544 'ignored', 'removed')
544 'ignored', 'removed')
545
545
546 tersedict = {}
546 tersedict = {}
547 for attrname in pstatus:
547 for attrname in pstatus:
548 statuschar = attrname[0:1]
548 statuschar = attrname[0:1]
549 for f in getattr(statuslist, attrname):
549 for f in getattr(statuslist, attrname):
550 rootobj.addfile(f, statuschar)
550 rootobj.addfile(f, statuschar)
551 tersedict[statuschar] = []
551 tersedict[statuschar] = []
552
552
553 # we won't be tersing the root dir, so add files in it
553 # we won't be tersing the root dir, so add files in it
554 for st, fpath in rootobj.iterfilepaths():
554 for st, fpath in rootobj.iterfilepaths():
555 tersedict[st].append(fpath)
555 tersedict[st].append(fpath)
556
556
557 # process each sub-directory and build tersedict
557 # process each sub-directory and build tersedict
558 for subdir in rootobj.subdirs.values():
558 for subdir in rootobj.subdirs.values():
559 for st, f in subdir.tersewalk(terseargs):
559 for st, f in subdir.tersewalk(terseargs):
560 tersedict[st].append(f)
560 tersedict[st].append(f)
561
561
562 tersedlist = []
562 tersedlist = []
563 for st in allst:
563 for st in allst:
564 tersedict[st].sort()
564 tersedict[st].sort()
565 tersedlist.append(tersedict[st])
565 tersedlist.append(tersedict[st])
566
566
567 return tersedlist
567 return tersedlist
568
568
569 def _commentlines(raw):
569 def _commentlines(raw):
570 '''Surround lineswith a comment char and a new line'''
570 '''Surround lineswith a comment char and a new line'''
571 lines = raw.splitlines()
571 lines = raw.splitlines()
572 commentedlines = ['# %s' % line for line in lines]
572 commentedlines = ['# %s' % line for line in lines]
573 return '\n'.join(commentedlines) + '\n'
573 return '\n'.join(commentedlines) + '\n'
574
574
575 def _conflictsmsg(repo):
575 def _conflictsmsg(repo):
576 mergestate = mergemod.mergestate.read(repo)
576 mergestate = mergemod.mergestate.read(repo)
577 if not mergestate.active():
577 if not mergestate.active():
578 return
578 return
579
579
580 m = scmutil.match(repo[None])
580 m = scmutil.match(repo[None])
581 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
581 unresolvedlist = [f for f in mergestate.unresolved() if m(f)]
582 if unresolvedlist:
582 if unresolvedlist:
583 mergeliststr = '\n'.join(
583 mergeliststr = '\n'.join(
584 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
584 [' %s' % util.pathto(repo.root, pycompat.getcwd(), path)
585 for path in unresolvedlist])
585 for path in unresolvedlist])
586 msg = _('''Unresolved merge conflicts:
586 msg = _('''Unresolved merge conflicts:
587
587
588 %s
588 %s
589
589
590 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
590 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
591 else:
591 else:
592 msg = _('No unresolved merge conflicts.')
592 msg = _('No unresolved merge conflicts.')
593
593
594 return _commentlines(msg)
594 return _commentlines(msg)
595
595
596 def _helpmessage(continuecmd, abortcmd):
596 def _helpmessage(continuecmd, abortcmd):
597 msg = _('To continue: %s\n'
597 msg = _('To continue: %s\n'
598 'To abort: %s') % (continuecmd, abortcmd)
598 'To abort: %s') % (continuecmd, abortcmd)
599 return _commentlines(msg)
599 return _commentlines(msg)
600
600
601 def _rebasemsg():
601 def _rebasemsg():
602 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
602 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
603
603
604 def _histeditmsg():
604 def _histeditmsg():
605 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
605 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
606
606
607 def _unshelvemsg():
607 def _unshelvemsg():
608 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
608 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
609
609
610 def _updatecleanmsg(dest=None):
610 def _updatecleanmsg(dest=None):
611 warning = _('warning: this will discard uncommitted changes')
611 warning = _('warning: this will discard uncommitted changes')
612 return 'hg update --clean %s (%s)' % (dest or '.', warning)
612 return 'hg update --clean %s (%s)' % (dest or '.', warning)
613
613
614 def _graftmsg():
614 def _graftmsg():
615 # tweakdefaults requires `update` to have a rev hence the `.`
615 # tweakdefaults requires `update` to have a rev hence the `.`
616 return _helpmessage('hg graft --continue', _updatecleanmsg())
616 return _helpmessage('hg graft --continue', _updatecleanmsg())
617
617
618 def _mergemsg():
618 def _mergemsg():
619 # tweakdefaults requires `update` to have a rev hence the `.`
619 # tweakdefaults requires `update` to have a rev hence the `.`
620 return _helpmessage('hg commit', _updatecleanmsg())
620 return _helpmessage('hg commit', _updatecleanmsg())
621
621
622 def _bisectmsg():
622 def _bisectmsg():
623 msg = _('To mark the changeset good: hg bisect --good\n'
623 msg = _('To mark the changeset good: hg bisect --good\n'
624 'To mark the changeset bad: hg bisect --bad\n'
624 'To mark the changeset bad: hg bisect --bad\n'
625 'To abort: hg bisect --reset\n')
625 'To abort: hg bisect --reset\n')
626 return _commentlines(msg)
626 return _commentlines(msg)
627
627
628 def fileexistspredicate(filename):
628 def fileexistspredicate(filename):
629 return lambda repo: repo.vfs.exists(filename)
629 return lambda repo: repo.vfs.exists(filename)
630
630
631 def _mergepredicate(repo):
631 def _mergepredicate(repo):
632 return len(repo[None].parents()) > 1
632 return len(repo[None].parents()) > 1
633
633
634 STATES = (
634 STATES = (
635 # (state, predicate to detect states, helpful message function)
635 # (state, predicate to detect states, helpful message function)
636 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
636 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
637 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
637 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
638 ('graft', fileexistspredicate('graftstate'), _graftmsg),
638 ('graft', fileexistspredicate('graftstate'), _graftmsg),
639 ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg),
639 ('unshelve', fileexistspredicate('shelvedstate'), _unshelvemsg),
640 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
640 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
641 # The merge state is part of a list that will be iterated over.
641 # The merge state is part of a list that will be iterated over.
642 # They need to be last because some of the other unfinished states may also
642 # They need to be last because some of the other unfinished states may also
643 # be in a merge or update state (eg. rebase, histedit, graft, etc).
643 # be in a merge or update state (eg. rebase, histedit, graft, etc).
644 # We want those to have priority.
644 # We want those to have priority.
645 ('merge', _mergepredicate, _mergemsg),
645 ('merge', _mergepredicate, _mergemsg),
646 )
646 )
647
647
648 def _getrepostate(repo):
648 def _getrepostate(repo):
649 # experimental config: commands.status.skipstates
649 # experimental config: commands.status.skipstates
650 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
650 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
651 for state, statedetectionpredicate, msgfn in STATES:
651 for state, statedetectionpredicate, msgfn in STATES:
652 if state in skip:
652 if state in skip:
653 continue
653 continue
654 if statedetectionpredicate(repo):
654 if statedetectionpredicate(repo):
655 return (state, statedetectionpredicate, msgfn)
655 return (state, statedetectionpredicate, msgfn)
656
656
657 def morestatus(repo, fm):
657 def morestatus(repo, fm):
658 statetuple = _getrepostate(repo)
658 statetuple = _getrepostate(repo)
659 label = 'status.morestatus'
659 label = 'status.morestatus'
660 if statetuple:
660 if statetuple:
661 fm.startitem()
661 fm.startitem()
662 state, statedetectionpredicate, helpfulmsg = statetuple
662 state, statedetectionpredicate, helpfulmsg = statetuple
663 statemsg = _('The repository is in an unfinished *%s* state.') % state
663 statemsg = _('The repository is in an unfinished *%s* state.') % state
664 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
664 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
665 conmsg = _conflictsmsg(repo)
665 conmsg = _conflictsmsg(repo)
666 if conmsg:
666 if conmsg:
667 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
667 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
668 if helpfulmsg:
668 if helpfulmsg:
669 helpmsg = helpfulmsg()
669 helpmsg = helpfulmsg()
670 fm.write('helpmsg', '%s\n', helpmsg, label=label)
670 fm.write('helpmsg', '%s\n', helpmsg, label=label)
671
671
672 def findpossible(cmd, table, strict=False):
672 def findpossible(cmd, table, strict=False):
673 """
673 """
674 Return cmd -> (aliases, command table entry)
674 Return cmd -> (aliases, command table entry)
675 for each matching command.
675 for each matching command.
676 Return debug commands (or their aliases) only if no normal command matches.
676 Return debug commands (or their aliases) only if no normal command matches.
677 """
677 """
678 choice = {}
678 choice = {}
679 debugchoice = {}
679 debugchoice = {}
680
680
681 if cmd in table:
681 if cmd in table:
682 # short-circuit exact matches, "log" alias beats "^log|history"
682 # short-circuit exact matches, "log" alias beats "^log|history"
683 keys = [cmd]
683 keys = [cmd]
684 else:
684 else:
685 keys = table.keys()
685 keys = table.keys()
686
686
687 allcmds = []
687 allcmds = []
688 for e in keys:
688 for e in keys:
689 aliases = parsealiases(e)
689 aliases = parsealiases(e)
690 allcmds.extend(aliases)
690 allcmds.extend(aliases)
691 found = None
691 found = None
692 if cmd in aliases:
692 if cmd in aliases:
693 found = cmd
693 found = cmd
694 elif not strict:
694 elif not strict:
695 for a in aliases:
695 for a in aliases:
696 if a.startswith(cmd):
696 if a.startswith(cmd):
697 found = a
697 found = a
698 break
698 break
699 if found is not None:
699 if found is not None:
700 if aliases[0].startswith("debug") or found.startswith("debug"):
700 if aliases[0].startswith("debug") or found.startswith("debug"):
701 debugchoice[found] = (aliases, table[e])
701 debugchoice[found] = (aliases, table[e])
702 else:
702 else:
703 choice[found] = (aliases, table[e])
703 choice[found] = (aliases, table[e])
704
704
705 if not choice and debugchoice:
705 if not choice and debugchoice:
706 choice = debugchoice
706 choice = debugchoice
707
707
708 return choice, allcmds
708 return choice, allcmds
709
709
710 def findcmd(cmd, table, strict=True):
710 def findcmd(cmd, table, strict=True):
711 """Return (aliases, command table entry) for command string."""
711 """Return (aliases, command table entry) for command string."""
712 choice, allcmds = findpossible(cmd, table, strict)
712 choice, allcmds = findpossible(cmd, table, strict)
713
713
714 if cmd in choice:
714 if cmd in choice:
715 return choice[cmd]
715 return choice[cmd]
716
716
717 if len(choice) > 1:
717 if len(choice) > 1:
718 clist = sorted(choice)
718 clist = sorted(choice)
719 raise error.AmbiguousCommand(cmd, clist)
719 raise error.AmbiguousCommand(cmd, clist)
720
720
721 if choice:
721 if choice:
722 return list(choice.values())[0]
722 return list(choice.values())[0]
723
723
724 raise error.UnknownCommand(cmd, allcmds)
724 raise error.UnknownCommand(cmd, allcmds)
725
725
726 def changebranch(ui, repo, revs, label):
726 def changebranch(ui, repo, revs, label):
727 """ Change the branch name of given revs to label """
727 """ Change the branch name of given revs to label """
728
728
729 with repo.wlock(), repo.lock(), repo.transaction('branches'):
729 with repo.wlock(), repo.lock(), repo.transaction('branches'):
730 # abort in case of uncommitted merge or dirty wdir
730 # abort in case of uncommitted merge or dirty wdir
731 bailifchanged(repo)
731 bailifchanged(repo)
732 revs = scmutil.revrange(repo, revs)
732 revs = scmutil.revrange(repo, revs)
733 if not revs:
733 if not revs:
734 raise error.Abort("empty revision set")
734 raise error.Abort("empty revision set")
735 roots = repo.revs('roots(%ld)', revs)
735 roots = repo.revs('roots(%ld)', revs)
736 if len(roots) > 1:
736 if len(roots) > 1:
737 raise error.Abort(_("cannot change branch of non-linear revisions"))
737 raise error.Abort(_("cannot change branch of non-linear revisions"))
738 rewriteutil.precheck(repo, revs, 'change branch of')
738 rewriteutil.precheck(repo, revs, 'change branch of')
739
739
740 root = repo[roots.first()]
740 root = repo[roots.first()]
741 if not root.p1().branch() == label and label in repo.branchmap():
741 if not root.p1().branch() == label and label in repo.branchmap():
742 raise error.Abort(_("a branch of the same name already exists"))
742 raise error.Abort(_("a branch of the same name already exists"))
743
743
744 if repo.revs('merge() and %ld', revs):
744 if repo.revs('merge() and %ld', revs):
745 raise error.Abort(_("cannot change branch of a merge commit"))
745 raise error.Abort(_("cannot change branch of a merge commit"))
746 if repo.revs('obsolete() and %ld', revs):
746 if repo.revs('obsolete() and %ld', revs):
747 raise error.Abort(_("cannot change branch of a obsolete changeset"))
747 raise error.Abort(_("cannot change branch of a obsolete changeset"))
748
748
749 # make sure only topological heads
749 # make sure only topological heads
750 if repo.revs('heads(%ld) - head()', revs):
750 if repo.revs('heads(%ld) - head()', revs):
751 raise error.Abort(_("cannot change branch in middle of a stack"))
751 raise error.Abort(_("cannot change branch in middle of a stack"))
752
752
753 replacements = {}
753 replacements = {}
754 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
754 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
755 # mercurial.subrepo -> mercurial.cmdutil
755 # mercurial.subrepo -> mercurial.cmdutil
756 from . import context
756 from . import context
757 for rev in revs:
757 for rev in revs:
758 ctx = repo[rev]
758 ctx = repo[rev]
759 oldbranch = ctx.branch()
759 oldbranch = ctx.branch()
760 # check if ctx has same branch
760 # check if ctx has same branch
761 if oldbranch == label:
761 if oldbranch == label:
762 continue
762 continue
763
763
764 def filectxfn(repo, newctx, path):
764 def filectxfn(repo, newctx, path):
765 try:
765 try:
766 return ctx[path]
766 return ctx[path]
767 except error.ManifestLookupError:
767 except error.ManifestLookupError:
768 return None
768 return None
769
769
770 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
770 ui.debug("changing branch of '%s' from '%s' to '%s'\n"
771 % (hex(ctx.node()), oldbranch, label))
771 % (hex(ctx.node()), oldbranch, label))
772 extra = ctx.extra()
772 extra = ctx.extra()
773 extra['branch_change'] = hex(ctx.node())
773 extra['branch_change'] = hex(ctx.node())
774 # While changing branch of set of linear commits, make sure that
774 # While changing branch of set of linear commits, make sure that
775 # we base our commits on new parent rather than old parent which
775 # we base our commits on new parent rather than old parent which
776 # was obsoleted while changing the branch
776 # was obsoleted while changing the branch
777 p1 = ctx.p1().node()
777 p1 = ctx.p1().node()
778 p2 = ctx.p2().node()
778 p2 = ctx.p2().node()
779 if p1 in replacements:
779 if p1 in replacements:
780 p1 = replacements[p1][0]
780 p1 = replacements[p1][0]
781 if p2 in replacements:
781 if p2 in replacements:
782 p2 = replacements[p2][0]
782 p2 = replacements[p2][0]
783
783
784 mc = context.memctx(repo, (p1, p2),
784 mc = context.memctx(repo, (p1, p2),
785 ctx.description(),
785 ctx.description(),
786 ctx.files(),
786 ctx.files(),
787 filectxfn,
787 filectxfn,
788 user=ctx.user(),
788 user=ctx.user(),
789 date=ctx.date(),
789 date=ctx.date(),
790 extra=extra,
790 extra=extra,
791 branch=label)
791 branch=label)
792
792
793 newnode = repo.commitctx(mc)
793 newnode = repo.commitctx(mc)
794 replacements[ctx.node()] = (newnode,)
794 replacements[ctx.node()] = (newnode,)
795 ui.debug('new node id is %s\n' % hex(newnode))
795 ui.debug('new node id is %s\n' % hex(newnode))
796
796
797 # create obsmarkers and move bookmarks
797 # create obsmarkers and move bookmarks
798 scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True)
798 scmutil.cleanupnodes(repo, replacements, 'branch-change', fixphase=True)
799
799
800 # move the working copy too
800 # move the working copy too
801 wctx = repo[None]
801 wctx = repo[None]
802 # in-progress merge is a bit too complex for now.
802 # in-progress merge is a bit too complex for now.
803 if len(wctx.parents()) == 1:
803 if len(wctx.parents()) == 1:
804 newid = replacements.get(wctx.p1().node())
804 newid = replacements.get(wctx.p1().node())
805 if newid is not None:
805 if newid is not None:
806 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
806 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
807 # mercurial.cmdutil
807 # mercurial.cmdutil
808 from . import hg
808 from . import hg
809 hg.update(repo, newid[0], quietempty=True)
809 hg.update(repo, newid[0], quietempty=True)
810
810
811 ui.status(_("changed branch on %d changesets\n") % len(replacements))
811 ui.status(_("changed branch on %d changesets\n") % len(replacements))
812
812
813 def findrepo(p):
813 def findrepo(p):
814 while not os.path.isdir(os.path.join(p, ".hg")):
814 while not os.path.isdir(os.path.join(p, ".hg")):
815 oldp, p = p, os.path.dirname(p)
815 oldp, p = p, os.path.dirname(p)
816 if p == oldp:
816 if p == oldp:
817 return None
817 return None
818
818
819 return p
819 return p
820
820
821 def bailifchanged(repo, merge=True, hint=None):
821 def bailifchanged(repo, merge=True, hint=None):
822 """ enforce the precondition that working directory must be clean.
822 """ enforce the precondition that working directory must be clean.
823
823
824 'merge' can be set to false if a pending uncommitted merge should be
824 'merge' can be set to false if a pending uncommitted merge should be
825 ignored (such as when 'update --check' runs).
825 ignored (such as when 'update --check' runs).
826
826
827 'hint' is the usual hint given to Abort exception.
827 'hint' is the usual hint given to Abort exception.
828 """
828 """
829
829
830 if merge and repo.dirstate.p2() != nullid:
830 if merge and repo.dirstate.p2() != nullid:
831 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
831 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
832 modified, added, removed, deleted = repo.status()[:4]
832 modified, added, removed, deleted = repo.status()[:4]
833 if modified or added or removed or deleted:
833 if modified or added or removed or deleted:
834 raise error.Abort(_('uncommitted changes'), hint=hint)
834 raise error.Abort(_('uncommitted changes'), hint=hint)
835 ctx = repo[None]
835 ctx = repo[None]
836 for s in sorted(ctx.substate):
836 for s in sorted(ctx.substate):
837 ctx.sub(s).bailifchanged(hint=hint)
837 ctx.sub(s).bailifchanged(hint=hint)
838
838
839 def logmessage(ui, opts):
839 def logmessage(ui, opts):
840 """ get the log message according to -m and -l option """
840 """ get the log message according to -m and -l option """
841 message = opts.get('message')
841 message = opts.get('message')
842 logfile = opts.get('logfile')
842 logfile = opts.get('logfile')
843
843
844 if message and logfile:
844 if message and logfile:
845 raise error.Abort(_('options --message and --logfile are mutually '
845 raise error.Abort(_('options --message and --logfile are mutually '
846 'exclusive'))
846 'exclusive'))
847 if not message and logfile:
847 if not message and logfile:
848 try:
848 try:
849 if isstdiofilename(logfile):
849 if isstdiofilename(logfile):
850 message = ui.fin.read()
850 message = ui.fin.read()
851 else:
851 else:
852 message = '\n'.join(util.readfile(logfile).splitlines())
852 message = '\n'.join(util.readfile(logfile).splitlines())
853 except IOError as inst:
853 except IOError as inst:
854 raise error.Abort(_("can't read commit message '%s': %s") %
854 raise error.Abort(_("can't read commit message '%s': %s") %
855 (logfile, encoding.strtolocal(inst.strerror)))
855 (logfile, encoding.strtolocal(inst.strerror)))
856 return message
856 return message
857
857
858 def mergeeditform(ctxorbool, baseformname):
858 def mergeeditform(ctxorbool, baseformname):
859 """return appropriate editform name (referencing a committemplate)
859 """return appropriate editform name (referencing a committemplate)
860
860
861 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
861 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
862 merging is committed.
862 merging is committed.
863
863
864 This returns baseformname with '.merge' appended if it is a merge,
864 This returns baseformname with '.merge' appended if it is a merge,
865 otherwise '.normal' is appended.
865 otherwise '.normal' is appended.
866 """
866 """
867 if isinstance(ctxorbool, bool):
867 if isinstance(ctxorbool, bool):
868 if ctxorbool:
868 if ctxorbool:
869 return baseformname + ".merge"
869 return baseformname + ".merge"
870 elif 1 < len(ctxorbool.parents()):
870 elif 1 < len(ctxorbool.parents()):
871 return baseformname + ".merge"
871 return baseformname + ".merge"
872
872
873 return baseformname + ".normal"
873 return baseformname + ".normal"
874
874
875 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
875 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
876 editform='', **opts):
876 editform='', **opts):
877 """get appropriate commit message editor according to '--edit' option
877 """get appropriate commit message editor according to '--edit' option
878
878
879 'finishdesc' is a function to be called with edited commit message
879 'finishdesc' is a function to be called with edited commit message
880 (= 'description' of the new changeset) just after editing, but
880 (= 'description' of the new changeset) just after editing, but
881 before checking empty-ness. It should return actual text to be
881 before checking empty-ness. It should return actual text to be
882 stored into history. This allows to change description before
882 stored into history. This allows to change description before
883 storing.
883 storing.
884
884
885 'extramsg' is a extra message to be shown in the editor instead of
885 'extramsg' is a extra message to be shown in the editor instead of
886 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
886 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
887 is automatically added.
887 is automatically added.
888
888
889 'editform' is a dot-separated list of names, to distinguish
889 'editform' is a dot-separated list of names, to distinguish
890 the purpose of commit text editing.
890 the purpose of commit text editing.
891
891
892 'getcommiteditor' returns 'commitforceeditor' regardless of
892 'getcommiteditor' returns 'commitforceeditor' regardless of
893 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
893 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
894 they are specific for usage in MQ.
894 they are specific for usage in MQ.
895 """
895 """
896 if edit or finishdesc or extramsg:
896 if edit or finishdesc or extramsg:
897 return lambda r, c, s: commitforceeditor(r, c, s,
897 return lambda r, c, s: commitforceeditor(r, c, s,
898 finishdesc=finishdesc,
898 finishdesc=finishdesc,
899 extramsg=extramsg,
899 extramsg=extramsg,
900 editform=editform)
900 editform=editform)
901 elif editform:
901 elif editform:
902 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
902 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
903 else:
903 else:
904 return commiteditor
904 return commiteditor
905
905
906 def _escapecommandtemplate(tmpl):
906 def _escapecommandtemplate(tmpl):
907 parts = []
907 parts = []
908 for typ, start, end in templater.scantemplate(tmpl, raw=True):
908 for typ, start, end in templater.scantemplate(tmpl, raw=True):
909 if typ == b'string':
909 if typ == b'string':
910 parts.append(stringutil.escapestr(tmpl[start:end]))
910 parts.append(stringutil.escapestr(tmpl[start:end]))
911 else:
911 else:
912 parts.append(tmpl[start:end])
912 parts.append(tmpl[start:end])
913 return b''.join(parts)
913 return b''.join(parts)
914
914
915 def rendercommandtemplate(ui, tmpl, props):
915 def rendercommandtemplate(ui, tmpl, props):
916 r"""Expand a literal template 'tmpl' in a way suitable for command line
916 r"""Expand a literal template 'tmpl' in a way suitable for command line
917
917
918 '\' in outermost string is not taken as an escape character because it
918 '\' in outermost string is not taken as an escape character because it
919 is a directory separator on Windows.
919 is a directory separator on Windows.
920
920
921 >>> from . import ui as uimod
921 >>> from . import ui as uimod
922 >>> ui = uimod.ui()
922 >>> ui = uimod.ui()
923 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
923 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
924 'c:\\foo'
924 'c:\\foo'
925 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
925 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
926 'c:{path}'
926 'c:{path}'
927 """
927 """
928 if not tmpl:
928 if not tmpl:
929 return tmpl
929 return tmpl
930 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
930 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
931 return t.renderdefault(props)
931 return t.renderdefault(props)
932
932
933 def rendertemplate(ctx, tmpl, props=None):
933 def rendertemplate(ctx, tmpl, props=None):
934 """Expand a literal template 'tmpl' byte-string against one changeset
934 """Expand a literal template 'tmpl' byte-string against one changeset
935
935
936 Each props item must be a stringify-able value or a callable returning
936 Each props item must be a stringify-able value or a callable returning
937 such value, i.e. no bare list nor dict should be passed.
937 such value, i.e. no bare list nor dict should be passed.
938 """
938 """
939 repo = ctx.repo()
939 repo = ctx.repo()
940 tres = formatter.templateresources(repo.ui, repo)
940 tres = formatter.templateresources(repo.ui, repo)
941 t = formatter.maketemplater(repo.ui, tmpl, defaults=templatekw.keywords,
941 t = formatter.maketemplater(repo.ui, tmpl, defaults=templatekw.keywords,
942 resources=tres)
942 resources=tres)
943 mapping = {'ctx': ctx}
943 mapping = {'ctx': ctx}
944 if props:
944 if props:
945 mapping.update(props)
945 mapping.update(props)
946 return t.renderdefault(mapping)
946 return t.renderdefault(mapping)
947
947
948 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
948 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
949 r"""Convert old-style filename format string to template string
949 r"""Convert old-style filename format string to template string
950
950
951 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
951 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
952 'foo-{reporoot|basename}-{seqno}.patch'
952 'foo-{reporoot|basename}-{seqno}.patch'
953 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
953 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
954 '{rev}{tags % "{tag}"}{node}'
954 '{rev}{tags % "{tag}"}{node}'
955
955
956 '\' in outermost strings has to be escaped because it is a directory
956 '\' in outermost strings has to be escaped because it is a directory
957 separator on Windows:
957 separator on Windows:
958
958
959 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
959 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
960 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
960 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
961 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
961 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
962 '\\\\\\\\foo\\\\bar.patch'
962 '\\\\\\\\foo\\\\bar.patch'
963 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
963 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
964 '\\\\{tags % "{tag}"}'
964 '\\\\{tags % "{tag}"}'
965
965
966 but inner strings follow the template rules (i.e. '\' is taken as an
966 but inner strings follow the template rules (i.e. '\' is taken as an
967 escape character):
967 escape character):
968
968
969 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
969 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
970 '{"c:\\tmp"}'
970 '{"c:\\tmp"}'
971 """
971 """
972 expander = {
972 expander = {
973 b'H': b'{node}',
973 b'H': b'{node}',
974 b'R': b'{rev}',
974 b'R': b'{rev}',
975 b'h': b'{node|short}',
975 b'h': b'{node|short}',
976 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
976 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
977 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
977 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
978 b'%': b'%',
978 b'%': b'%',
979 b'b': b'{reporoot|basename}',
979 b'b': b'{reporoot|basename}',
980 }
980 }
981 if total is not None:
981 if total is not None:
982 expander[b'N'] = b'{total}'
982 expander[b'N'] = b'{total}'
983 if seqno is not None:
983 if seqno is not None:
984 expander[b'n'] = b'{seqno}'
984 expander[b'n'] = b'{seqno}'
985 if total is not None and seqno is not None:
985 if total is not None and seqno is not None:
986 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
986 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
987 if pathname is not None:
987 if pathname is not None:
988 expander[b's'] = b'{pathname|basename}'
988 expander[b's'] = b'{pathname|basename}'
989 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
989 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
990 expander[b'p'] = b'{pathname}'
990 expander[b'p'] = b'{pathname}'
991
991
992 newname = []
992 newname = []
993 for typ, start, end in templater.scantemplate(pat, raw=True):
993 for typ, start, end in templater.scantemplate(pat, raw=True):
994 if typ != b'string':
994 if typ != b'string':
995 newname.append(pat[start:end])
995 newname.append(pat[start:end])
996 continue
996 continue
997 i = start
997 i = start
998 while i < end:
998 while i < end:
999 n = pat.find(b'%', i, end)
999 n = pat.find(b'%', i, end)
1000 if n < 0:
1000 if n < 0:
1001 newname.append(stringutil.escapestr(pat[i:end]))
1001 newname.append(stringutil.escapestr(pat[i:end]))
1002 break
1002 break
1003 newname.append(stringutil.escapestr(pat[i:n]))
1003 newname.append(stringutil.escapestr(pat[i:n]))
1004 if n + 2 > end:
1004 if n + 2 > end:
1005 raise error.Abort(_("incomplete format spec in output "
1005 raise error.Abort(_("incomplete format spec in output "
1006 "filename"))
1006 "filename"))
1007 c = pat[n + 1:n + 2]
1007 c = pat[n + 1:n + 2]
1008 i = n + 2
1008 i = n + 2
1009 try:
1009 try:
1010 newname.append(expander[c])
1010 newname.append(expander[c])
1011 except KeyError:
1011 except KeyError:
1012 raise error.Abort(_("invalid format spec '%%%s' in output "
1012 raise error.Abort(_("invalid format spec '%%%s' in output "
1013 "filename") % c)
1013 "filename") % c)
1014 return ''.join(newname)
1014 return ''.join(newname)
1015
1015
1016 def makefilename(ctx, pat, **props):
1016 def makefilename(ctx, pat, **props):
1017 if not pat:
1017 if not pat:
1018 return pat
1018 return pat
1019 tmpl = _buildfntemplate(pat, **props)
1019 tmpl = _buildfntemplate(pat, **props)
1020 # BUG: alias expansion shouldn't be made against template fragments
1020 # BUG: alias expansion shouldn't be made against template fragments
1021 # rewritten from %-format strings, but we have no easy way to partially
1021 # rewritten from %-format strings, but we have no easy way to partially
1022 # disable the expansion.
1022 # disable the expansion.
1023 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1023 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1024
1024
1025 def isstdiofilename(pat):
1025 def isstdiofilename(pat):
1026 """True if the given pat looks like a filename denoting stdin/stdout"""
1026 """True if the given pat looks like a filename denoting stdin/stdout"""
1027 return not pat or pat == '-'
1027 return not pat or pat == '-'
1028
1028
1029 class _unclosablefile(object):
1029 class _unclosablefile(object):
1030 def __init__(self, fp):
1030 def __init__(self, fp):
1031 self._fp = fp
1031 self._fp = fp
1032
1032
1033 def close(self):
1033 def close(self):
1034 pass
1034 pass
1035
1035
1036 def __iter__(self):
1036 def __iter__(self):
1037 return iter(self._fp)
1037 return iter(self._fp)
1038
1038
1039 def __getattr__(self, attr):
1039 def __getattr__(self, attr):
1040 return getattr(self._fp, attr)
1040 return getattr(self._fp, attr)
1041
1041
1042 def __enter__(self):
1042 def __enter__(self):
1043 return self
1043 return self
1044
1044
1045 def __exit__(self, exc_type, exc_value, exc_tb):
1045 def __exit__(self, exc_type, exc_value, exc_tb):
1046 pass
1046 pass
1047
1047
1048 def makefileobj(ctx, pat, mode='wb', **props):
1048 def makefileobj(ctx, pat, mode='wb', **props):
1049 writable = mode not in ('r', 'rb')
1049 writable = mode not in ('r', 'rb')
1050
1050
1051 if isstdiofilename(pat):
1051 if isstdiofilename(pat):
1052 repo = ctx.repo()
1052 repo = ctx.repo()
1053 if writable:
1053 if writable:
1054 fp = repo.ui.fout
1054 fp = repo.ui.fout
1055 else:
1055 else:
1056 fp = repo.ui.fin
1056 fp = repo.ui.fin
1057 return _unclosablefile(fp)
1057 return _unclosablefile(fp)
1058 fn = makefilename(ctx, pat, **props)
1058 fn = makefilename(ctx, pat, **props)
1059 return open(fn, mode)
1059 return open(fn, mode)
1060
1060
1061 def openrevlog(repo, cmd, file_, opts):
1061 def openrevlog(repo, cmd, file_, opts):
1062 """opens the changelog, manifest, a filelog or a given revlog"""
1062 """opens the changelog, manifest, a filelog or a given revlog"""
1063 cl = opts['changelog']
1063 cl = opts['changelog']
1064 mf = opts['manifest']
1064 mf = opts['manifest']
1065 dir = opts['dir']
1065 dir = opts['dir']
1066 msg = None
1066 msg = None
1067 if cl and mf:
1067 if cl and mf:
1068 msg = _('cannot specify --changelog and --manifest at the same time')
1068 msg = _('cannot specify --changelog and --manifest at the same time')
1069 elif cl and dir:
1069 elif cl and dir:
1070 msg = _('cannot specify --changelog and --dir at the same time')
1070 msg = _('cannot specify --changelog and --dir at the same time')
1071 elif cl or mf or dir:
1071 elif cl or mf or dir:
1072 if file_:
1072 if file_:
1073 msg = _('cannot specify filename with --changelog or --manifest')
1073 msg = _('cannot specify filename with --changelog or --manifest')
1074 elif not repo:
1074 elif not repo:
1075 msg = _('cannot specify --changelog or --manifest or --dir '
1075 msg = _('cannot specify --changelog or --manifest or --dir '
1076 'without a repository')
1076 'without a repository')
1077 if msg:
1077 if msg:
1078 raise error.Abort(msg)
1078 raise error.Abort(msg)
1079
1079
1080 r = None
1080 r = None
1081 if repo:
1081 if repo:
1082 if cl:
1082 if cl:
1083 r = repo.unfiltered().changelog
1083 r = repo.unfiltered().changelog
1084 elif dir:
1084 elif dir:
1085 if 'treemanifest' not in repo.requirements:
1085 if 'treemanifest' not in repo.requirements:
1086 raise error.Abort(_("--dir can only be used on repos with "
1086 raise error.Abort(_("--dir can only be used on repos with "
1087 "treemanifest enabled"))
1087 "treemanifest enabled"))
1088 if not dir.endswith('/'):
1088 if not dir.endswith('/'):
1089 dir = dir + '/'
1089 dir = dir + '/'
1090 dirlog = repo.manifestlog._revlog.dirlog(dir)
1090 dirlog = repo.manifestlog._revlog.dirlog(dir)
1091 if len(dirlog):
1091 if len(dirlog):
1092 r = dirlog
1092 r = dirlog
1093 elif mf:
1093 elif mf:
1094 r = repo.manifestlog._revlog
1094 r = repo.manifestlog._revlog
1095 elif file_:
1095 elif file_:
1096 filelog = repo.file(file_)
1096 filelog = repo.file(file_)
1097 if len(filelog):
1097 if len(filelog):
1098 r = filelog
1098 r = filelog
1099 if not r:
1099 if not r:
1100 if not file_:
1100 if not file_:
1101 raise error.CommandError(cmd, _('invalid arguments'))
1101 raise error.CommandError(cmd, _('invalid arguments'))
1102 if not os.path.isfile(file_):
1102 if not os.path.isfile(file_):
1103 raise error.Abort(_("revlog '%s' not found") % file_)
1103 raise error.Abort(_("revlog '%s' not found") % file_)
1104 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
1104 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
1105 file_[:-2] + ".i")
1105 file_[:-2] + ".i")
1106 return r
1106 return r
1107
1107
1108 def copy(ui, repo, pats, opts, rename=False):
1108 def copy(ui, repo, pats, opts, rename=False):
1109 # called with the repo lock held
1109 # called with the repo lock held
1110 #
1110 #
1111 # hgsep => pathname that uses "/" to separate directories
1111 # hgsep => pathname that uses "/" to separate directories
1112 # ossep => pathname that uses os.sep to separate directories
1112 # ossep => pathname that uses os.sep to separate directories
1113 cwd = repo.getcwd()
1113 cwd = repo.getcwd()
1114 targets = {}
1114 targets = {}
1115 after = opts.get("after")
1115 after = opts.get("after")
1116 dryrun = opts.get("dry_run")
1116 dryrun = opts.get("dry_run")
1117 wctx = repo[None]
1117 wctx = repo[None]
1118
1118
1119 def walkpat(pat):
1119 def walkpat(pat):
1120 srcs = []
1120 srcs = []
1121 if after:
1121 if after:
1122 badstates = '?'
1122 badstates = '?'
1123 else:
1123 else:
1124 badstates = '?r'
1124 badstates = '?r'
1125 m = scmutil.match(wctx, [pat], opts, globbed=True)
1125 m = scmutil.match(wctx, [pat], opts, globbed=True)
1126 for abs in wctx.walk(m):
1126 for abs in wctx.walk(m):
1127 state = repo.dirstate[abs]
1127 state = repo.dirstate[abs]
1128 rel = m.rel(abs)
1128 rel = m.rel(abs)
1129 exact = m.exact(abs)
1129 exact = m.exact(abs)
1130 if state in badstates:
1130 if state in badstates:
1131 if exact and state == '?':
1131 if exact and state == '?':
1132 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1132 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1133 if exact and state == 'r':
1133 if exact and state == 'r':
1134 ui.warn(_('%s: not copying - file has been marked for'
1134 ui.warn(_('%s: not copying - file has been marked for'
1135 ' remove\n') % rel)
1135 ' remove\n') % rel)
1136 continue
1136 continue
1137 # abs: hgsep
1137 # abs: hgsep
1138 # rel: ossep
1138 # rel: ossep
1139 srcs.append((abs, rel, exact))
1139 srcs.append((abs, rel, exact))
1140 return srcs
1140 return srcs
1141
1141
1142 # abssrc: hgsep
1142 # abssrc: hgsep
1143 # relsrc: ossep
1143 # relsrc: ossep
1144 # otarget: ossep
1144 # otarget: ossep
1145 def copyfile(abssrc, relsrc, otarget, exact):
1145 def copyfile(abssrc, relsrc, otarget, exact):
1146 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1146 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1147 if '/' in abstarget:
1147 if '/' in abstarget:
1148 # We cannot normalize abstarget itself, this would prevent
1148 # We cannot normalize abstarget itself, this would prevent
1149 # case only renames, like a => A.
1149 # case only renames, like a => A.
1150 abspath, absname = abstarget.rsplit('/', 1)
1150 abspath, absname = abstarget.rsplit('/', 1)
1151 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1151 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1152 reltarget = repo.pathto(abstarget, cwd)
1152 reltarget = repo.pathto(abstarget, cwd)
1153 target = repo.wjoin(abstarget)
1153 target = repo.wjoin(abstarget)
1154 src = repo.wjoin(abssrc)
1154 src = repo.wjoin(abssrc)
1155 state = repo.dirstate[abstarget]
1155 state = repo.dirstate[abstarget]
1156
1156
1157 scmutil.checkportable(ui, abstarget)
1157 scmutil.checkportable(ui, abstarget)
1158
1158
1159 # check for collisions
1159 # check for collisions
1160 prevsrc = targets.get(abstarget)
1160 prevsrc = targets.get(abstarget)
1161 if prevsrc is not None:
1161 if prevsrc is not None:
1162 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1162 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1163 (reltarget, repo.pathto(abssrc, cwd),
1163 (reltarget, repo.pathto(abssrc, cwd),
1164 repo.pathto(prevsrc, cwd)))
1164 repo.pathto(prevsrc, cwd)))
1165 return
1165 return
1166
1166
1167 # check for overwrites
1167 # check for overwrites
1168 exists = os.path.lexists(target)
1168 exists = os.path.lexists(target)
1169 samefile = False
1169 samefile = False
1170 if exists and abssrc != abstarget:
1170 if exists and abssrc != abstarget:
1171 if (repo.dirstate.normalize(abssrc) ==
1171 if (repo.dirstate.normalize(abssrc) ==
1172 repo.dirstate.normalize(abstarget)):
1172 repo.dirstate.normalize(abstarget)):
1173 if not rename:
1173 if not rename:
1174 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1174 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1175 return
1175 return
1176 exists = False
1176 exists = False
1177 samefile = True
1177 samefile = True
1178
1178
1179 if not after and exists or after and state in 'mn':
1179 if not after and exists or after and state in 'mn':
1180 if not opts['force']:
1180 if not opts['force']:
1181 if state in 'mn':
1181 if state in 'mn':
1182 msg = _('%s: not overwriting - file already committed\n')
1182 msg = _('%s: not overwriting - file already committed\n')
1183 if after:
1183 if after:
1184 flags = '--after --force'
1184 flags = '--after --force'
1185 else:
1185 else:
1186 flags = '--force'
1186 flags = '--force'
1187 if rename:
1187 if rename:
1188 hint = _('(hg rename %s to replace the file by '
1188 hint = _('(hg rename %s to replace the file by '
1189 'recording a rename)\n') % flags
1189 'recording a rename)\n') % flags
1190 else:
1190 else:
1191 hint = _('(hg copy %s to replace the file by '
1191 hint = _('(hg copy %s to replace the file by '
1192 'recording a copy)\n') % flags
1192 'recording a copy)\n') % flags
1193 else:
1193 else:
1194 msg = _('%s: not overwriting - file exists\n')
1194 msg = _('%s: not overwriting - file exists\n')
1195 if rename:
1195 if rename:
1196 hint = _('(hg rename --after to record the rename)\n')
1196 hint = _('(hg rename --after to record the rename)\n')
1197 else:
1197 else:
1198 hint = _('(hg copy --after to record the copy)\n')
1198 hint = _('(hg copy --after to record the copy)\n')
1199 ui.warn(msg % reltarget)
1199 ui.warn(msg % reltarget)
1200 ui.warn(hint)
1200 ui.warn(hint)
1201 return
1201 return
1202
1202
1203 if after:
1203 if after:
1204 if not exists:
1204 if not exists:
1205 if rename:
1205 if rename:
1206 ui.warn(_('%s: not recording move - %s does not exist\n') %
1206 ui.warn(_('%s: not recording move - %s does not exist\n') %
1207 (relsrc, reltarget))
1207 (relsrc, reltarget))
1208 else:
1208 else:
1209 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1209 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1210 (relsrc, reltarget))
1210 (relsrc, reltarget))
1211 return
1211 return
1212 elif not dryrun:
1212 elif not dryrun:
1213 try:
1213 try:
1214 if exists:
1214 if exists:
1215 os.unlink(target)
1215 os.unlink(target)
1216 targetdir = os.path.dirname(target) or '.'
1216 targetdir = os.path.dirname(target) or '.'
1217 if not os.path.isdir(targetdir):
1217 if not os.path.isdir(targetdir):
1218 os.makedirs(targetdir)
1218 os.makedirs(targetdir)
1219 if samefile:
1219 if samefile:
1220 tmp = target + "~hgrename"
1220 tmp = target + "~hgrename"
1221 os.rename(src, tmp)
1221 os.rename(src, tmp)
1222 os.rename(tmp, target)
1222 os.rename(tmp, target)
1223 else:
1223 else:
1224 # Preserve stat info on renames, not on copies; this matches
1224 # Preserve stat info on renames, not on copies; this matches
1225 # Linux CLI behavior.
1225 # Linux CLI behavior.
1226 util.copyfile(src, target, copystat=rename)
1226 util.copyfile(src, target, copystat=rename)
1227 srcexists = True
1227 srcexists = True
1228 except IOError as inst:
1228 except IOError as inst:
1229 if inst.errno == errno.ENOENT:
1229 if inst.errno == errno.ENOENT:
1230 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1230 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1231 srcexists = False
1231 srcexists = False
1232 else:
1232 else:
1233 ui.warn(_('%s: cannot copy - %s\n') %
1233 ui.warn(_('%s: cannot copy - %s\n') %
1234 (relsrc, encoding.strtolocal(inst.strerror)))
1234 (relsrc, encoding.strtolocal(inst.strerror)))
1235 return True # report a failure
1235 return True # report a failure
1236
1236
1237 if ui.verbose or not exact:
1237 if ui.verbose or not exact:
1238 if rename:
1238 if rename:
1239 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1239 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1240 else:
1240 else:
1241 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1241 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1242
1242
1243 targets[abstarget] = abssrc
1243 targets[abstarget] = abssrc
1244
1244
1245 # fix up dirstate
1245 # fix up dirstate
1246 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1246 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1247 dryrun=dryrun, cwd=cwd)
1247 dryrun=dryrun, cwd=cwd)
1248 if rename and not dryrun:
1248 if rename and not dryrun:
1249 if not after and srcexists and not samefile:
1249 if not after and srcexists and not samefile:
1250 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
1250 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
1251 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1251 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1252 wctx.forget([abssrc])
1252 wctx.forget([abssrc])
1253
1253
1254 # pat: ossep
1254 # pat: ossep
1255 # dest ossep
1255 # dest ossep
1256 # srcs: list of (hgsep, hgsep, ossep, bool)
1256 # srcs: list of (hgsep, hgsep, ossep, bool)
1257 # return: function that takes hgsep and returns ossep
1257 # return: function that takes hgsep and returns ossep
1258 def targetpathfn(pat, dest, srcs):
1258 def targetpathfn(pat, dest, srcs):
1259 if os.path.isdir(pat):
1259 if os.path.isdir(pat):
1260 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1260 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1261 abspfx = util.localpath(abspfx)
1261 abspfx = util.localpath(abspfx)
1262 if destdirexists:
1262 if destdirexists:
1263 striplen = len(os.path.split(abspfx)[0])
1263 striplen = len(os.path.split(abspfx)[0])
1264 else:
1264 else:
1265 striplen = len(abspfx)
1265 striplen = len(abspfx)
1266 if striplen:
1266 if striplen:
1267 striplen += len(pycompat.ossep)
1267 striplen += len(pycompat.ossep)
1268 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1268 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1269 elif destdirexists:
1269 elif destdirexists:
1270 res = lambda p: os.path.join(dest,
1270 res = lambda p: os.path.join(dest,
1271 os.path.basename(util.localpath(p)))
1271 os.path.basename(util.localpath(p)))
1272 else:
1272 else:
1273 res = lambda p: dest
1273 res = lambda p: dest
1274 return res
1274 return res
1275
1275
1276 # pat: ossep
1276 # pat: ossep
1277 # dest ossep
1277 # dest ossep
1278 # srcs: list of (hgsep, hgsep, ossep, bool)
1278 # srcs: list of (hgsep, hgsep, ossep, bool)
1279 # return: function that takes hgsep and returns ossep
1279 # return: function that takes hgsep and returns ossep
1280 def targetpathafterfn(pat, dest, srcs):
1280 def targetpathafterfn(pat, dest, srcs):
1281 if matchmod.patkind(pat):
1281 if matchmod.patkind(pat):
1282 # a mercurial pattern
1282 # a mercurial pattern
1283 res = lambda p: os.path.join(dest,
1283 res = lambda p: os.path.join(dest,
1284 os.path.basename(util.localpath(p)))
1284 os.path.basename(util.localpath(p)))
1285 else:
1285 else:
1286 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1286 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1287 if len(abspfx) < len(srcs[0][0]):
1287 if len(abspfx) < len(srcs[0][0]):
1288 # A directory. Either the target path contains the last
1288 # A directory. Either the target path contains the last
1289 # component of the source path or it does not.
1289 # component of the source path or it does not.
1290 def evalpath(striplen):
1290 def evalpath(striplen):
1291 score = 0
1291 score = 0
1292 for s in srcs:
1292 for s in srcs:
1293 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1293 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1294 if os.path.lexists(t):
1294 if os.path.lexists(t):
1295 score += 1
1295 score += 1
1296 return score
1296 return score
1297
1297
1298 abspfx = util.localpath(abspfx)
1298 abspfx = util.localpath(abspfx)
1299 striplen = len(abspfx)
1299 striplen = len(abspfx)
1300 if striplen:
1300 if striplen:
1301 striplen += len(pycompat.ossep)
1301 striplen += len(pycompat.ossep)
1302 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1302 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1303 score = evalpath(striplen)
1303 score = evalpath(striplen)
1304 striplen1 = len(os.path.split(abspfx)[0])
1304 striplen1 = len(os.path.split(abspfx)[0])
1305 if striplen1:
1305 if striplen1:
1306 striplen1 += len(pycompat.ossep)
1306 striplen1 += len(pycompat.ossep)
1307 if evalpath(striplen1) > score:
1307 if evalpath(striplen1) > score:
1308 striplen = striplen1
1308 striplen = striplen1
1309 res = lambda p: os.path.join(dest,
1309 res = lambda p: os.path.join(dest,
1310 util.localpath(p)[striplen:])
1310 util.localpath(p)[striplen:])
1311 else:
1311 else:
1312 # a file
1312 # a file
1313 if destdirexists:
1313 if destdirexists:
1314 res = lambda p: os.path.join(dest,
1314 res = lambda p: os.path.join(dest,
1315 os.path.basename(util.localpath(p)))
1315 os.path.basename(util.localpath(p)))
1316 else:
1316 else:
1317 res = lambda p: dest
1317 res = lambda p: dest
1318 return res
1318 return res
1319
1319
1320 pats = scmutil.expandpats(pats)
1320 pats = scmutil.expandpats(pats)
1321 if not pats:
1321 if not pats:
1322 raise error.Abort(_('no source or destination specified'))
1322 raise error.Abort(_('no source or destination specified'))
1323 if len(pats) == 1:
1323 if len(pats) == 1:
1324 raise error.Abort(_('no destination specified'))
1324 raise error.Abort(_('no destination specified'))
1325 dest = pats.pop()
1325 dest = pats.pop()
1326 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1326 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1327 if not destdirexists:
1327 if not destdirexists:
1328 if len(pats) > 1 or matchmod.patkind(pats[0]):
1328 if len(pats) > 1 or matchmod.patkind(pats[0]):
1329 raise error.Abort(_('with multiple sources, destination must be an '
1329 raise error.Abort(_('with multiple sources, destination must be an '
1330 'existing directory'))
1330 'existing directory'))
1331 if util.endswithsep(dest):
1331 if util.endswithsep(dest):
1332 raise error.Abort(_('destination %s is not a directory') % dest)
1332 raise error.Abort(_('destination %s is not a directory') % dest)
1333
1333
1334 tfn = targetpathfn
1334 tfn = targetpathfn
1335 if after:
1335 if after:
1336 tfn = targetpathafterfn
1336 tfn = targetpathafterfn
1337 copylist = []
1337 copylist = []
1338 for pat in pats:
1338 for pat in pats:
1339 srcs = walkpat(pat)
1339 srcs = walkpat(pat)
1340 if not srcs:
1340 if not srcs:
1341 continue
1341 continue
1342 copylist.append((tfn(pat, dest, srcs), srcs))
1342 copylist.append((tfn(pat, dest, srcs), srcs))
1343 if not copylist:
1343 if not copylist:
1344 raise error.Abort(_('no files to copy'))
1344 raise error.Abort(_('no files to copy'))
1345
1345
1346 errors = 0
1346 errors = 0
1347 for targetpath, srcs in copylist:
1347 for targetpath, srcs in copylist:
1348 for abssrc, relsrc, exact in srcs:
1348 for abssrc, relsrc, exact in srcs:
1349 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1349 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1350 errors += 1
1350 errors += 1
1351
1351
1352 if errors:
1352 if errors:
1353 ui.warn(_('(consider using --after)\n'))
1353 ui.warn(_('(consider using --after)\n'))
1354
1354
1355 return errors != 0
1355 return errors != 0
1356
1356
1357 ## facility to let extension process additional data into an import patch
1357 ## facility to let extension process additional data into an import patch
1358 # list of identifier to be executed in order
1358 # list of identifier to be executed in order
1359 extrapreimport = [] # run before commit
1359 extrapreimport = [] # run before commit
1360 extrapostimport = [] # run after commit
1360 extrapostimport = [] # run after commit
1361 # mapping from identifier to actual import function
1361 # mapping from identifier to actual import function
1362 #
1362 #
1363 # 'preimport' are run before the commit is made and are provided the following
1363 # 'preimport' are run before the commit is made and are provided the following
1364 # arguments:
1364 # arguments:
1365 # - repo: the localrepository instance,
1365 # - repo: the localrepository instance,
1366 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1366 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1367 # - extra: the future extra dictionary of the changeset, please mutate it,
1367 # - extra: the future extra dictionary of the changeset, please mutate it,
1368 # - opts: the import options.
1368 # - opts: the import options.
1369 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1369 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1370 # mutation of in memory commit and more. Feel free to rework the code to get
1370 # mutation of in memory commit and more. Feel free to rework the code to get
1371 # there.
1371 # there.
1372 extrapreimportmap = {}
1372 extrapreimportmap = {}
1373 # 'postimport' are run after the commit is made and are provided the following
1373 # 'postimport' are run after the commit is made and are provided the following
1374 # argument:
1374 # argument:
1375 # - ctx: the changectx created by import.
1375 # - ctx: the changectx created by import.
1376 extrapostimportmap = {}
1376 extrapostimportmap = {}
1377
1377
1378 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1378 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1379 """Utility function used by commands.import to import a single patch
1379 """Utility function used by commands.import to import a single patch
1380
1380
1381 This function is explicitly defined here to help the evolve extension to
1381 This function is explicitly defined here to help the evolve extension to
1382 wrap this part of the import logic.
1382 wrap this part of the import logic.
1383
1383
1384 The API is currently a bit ugly because it a simple code translation from
1384 The API is currently a bit ugly because it a simple code translation from
1385 the import command. Feel free to make it better.
1385 the import command. Feel free to make it better.
1386
1386
1387 :patchdata: a dictionary containing parsed patch data (such as from
1387 :patchdata: a dictionary containing parsed patch data (such as from
1388 ``patch.extract()``)
1388 ``patch.extract()``)
1389 :parents: nodes that will be parent of the created commit
1389 :parents: nodes that will be parent of the created commit
1390 :opts: the full dict of option passed to the import command
1390 :opts: the full dict of option passed to the import command
1391 :msgs: list to save commit message to.
1391 :msgs: list to save commit message to.
1392 (used in case we need to save it when failing)
1392 (used in case we need to save it when failing)
1393 :updatefunc: a function that update a repo to a given node
1393 :updatefunc: a function that update a repo to a given node
1394 updatefunc(<repo>, <node>)
1394 updatefunc(<repo>, <node>)
1395 """
1395 """
1396 # avoid cycle context -> subrepo -> cmdutil
1396 # avoid cycle context -> subrepo -> cmdutil
1397 from . import context
1397 from . import context
1398
1398
1399 tmpname = patchdata.get('filename')
1399 tmpname = patchdata.get('filename')
1400 message = patchdata.get('message')
1400 message = patchdata.get('message')
1401 user = opts.get('user') or patchdata.get('user')
1401 user = opts.get('user') or patchdata.get('user')
1402 date = opts.get('date') or patchdata.get('date')
1402 date = opts.get('date') or patchdata.get('date')
1403 branch = patchdata.get('branch')
1403 branch = patchdata.get('branch')
1404 nodeid = patchdata.get('nodeid')
1404 nodeid = patchdata.get('nodeid')
1405 p1 = patchdata.get('p1')
1405 p1 = patchdata.get('p1')
1406 p2 = patchdata.get('p2')
1406 p2 = patchdata.get('p2')
1407
1407
1408 nocommit = opts.get('no_commit')
1408 nocommit = opts.get('no_commit')
1409 importbranch = opts.get('import_branch')
1409 importbranch = opts.get('import_branch')
1410 update = not opts.get('bypass')
1410 update = not opts.get('bypass')
1411 strip = opts["strip"]
1411 strip = opts["strip"]
1412 prefix = opts["prefix"]
1412 prefix = opts["prefix"]
1413 sim = float(opts.get('similarity') or 0)
1413 sim = float(opts.get('similarity') or 0)
1414
1414
1415 if not tmpname:
1415 if not tmpname:
1416 return None, None, False
1416 return None, None, False
1417
1417
1418 rejects = False
1418 rejects = False
1419
1419
1420 cmdline_message = logmessage(ui, opts)
1420 cmdline_message = logmessage(ui, opts)
1421 if cmdline_message:
1421 if cmdline_message:
1422 # pickup the cmdline msg
1422 # pickup the cmdline msg
1423 message = cmdline_message
1423 message = cmdline_message
1424 elif message:
1424 elif message:
1425 # pickup the patch msg
1425 # pickup the patch msg
1426 message = message.strip()
1426 message = message.strip()
1427 else:
1427 else:
1428 # launch the editor
1428 # launch the editor
1429 message = None
1429 message = None
1430 ui.debug('message:\n%s\n' % (message or ''))
1430 ui.debug('message:\n%s\n' % (message or ''))
1431
1431
1432 if len(parents) == 1:
1432 if len(parents) == 1:
1433 parents.append(repo[nullid])
1433 parents.append(repo[nullid])
1434 if opts.get('exact'):
1434 if opts.get('exact'):
1435 if not nodeid or not p1:
1435 if not nodeid or not p1:
1436 raise error.Abort(_('not a Mercurial patch'))
1436 raise error.Abort(_('not a Mercurial patch'))
1437 p1 = repo[p1]
1437 p1 = repo[p1]
1438 p2 = repo[p2 or nullid]
1438 p2 = repo[p2 or nullid]
1439 elif p2:
1439 elif p2:
1440 try:
1440 try:
1441 p1 = repo[p1]
1441 p1 = repo[p1]
1442 p2 = repo[p2]
1442 p2 = repo[p2]
1443 # Without any options, consider p2 only if the
1443 # Without any options, consider p2 only if the
1444 # patch is being applied on top of the recorded
1444 # patch is being applied on top of the recorded
1445 # first parent.
1445 # first parent.
1446 if p1 != parents[0]:
1446 if p1 != parents[0]:
1447 p1 = parents[0]
1447 p1 = parents[0]
1448 p2 = repo[nullid]
1448 p2 = repo[nullid]
1449 except error.RepoError:
1449 except error.RepoError:
1450 p1, p2 = parents
1450 p1, p2 = parents
1451 if p2.node() == nullid:
1451 if p2.node() == nullid:
1452 ui.warn(_("warning: import the patch as a normal revision\n"
1452 ui.warn(_("warning: import the patch as a normal revision\n"
1453 "(use --exact to import the patch as a merge)\n"))
1453 "(use --exact to import the patch as a merge)\n"))
1454 else:
1454 else:
1455 p1, p2 = parents
1455 p1, p2 = parents
1456
1456
1457 n = None
1457 n = None
1458 if update:
1458 if update:
1459 if p1 != parents[0]:
1459 if p1 != parents[0]:
1460 updatefunc(repo, p1.node())
1460 updatefunc(repo, p1.node())
1461 if p2 != parents[1]:
1461 if p2 != parents[1]:
1462 repo.setparents(p1.node(), p2.node())
1462 repo.setparents(p1.node(), p2.node())
1463
1463
1464 if opts.get('exact') or importbranch:
1464 if opts.get('exact') or importbranch:
1465 repo.dirstate.setbranch(branch or 'default')
1465 repo.dirstate.setbranch(branch or 'default')
1466
1466
1467 partial = opts.get('partial', False)
1467 partial = opts.get('partial', False)
1468 files = set()
1468 files = set()
1469 try:
1469 try:
1470 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1470 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1471 files=files, eolmode=None, similarity=sim / 100.0)
1471 files=files, eolmode=None, similarity=sim / 100.0)
1472 except error.PatchError as e:
1472 except error.PatchError as e:
1473 if not partial:
1473 if not partial:
1474 raise error.Abort(pycompat.bytestr(e))
1474 raise error.Abort(pycompat.bytestr(e))
1475 if partial:
1475 if partial:
1476 rejects = True
1476 rejects = True
1477
1477
1478 files = list(files)
1478 files = list(files)
1479 if nocommit:
1479 if nocommit:
1480 if message:
1480 if message:
1481 msgs.append(message)
1481 msgs.append(message)
1482 else:
1482 else:
1483 if opts.get('exact') or p2:
1483 if opts.get('exact') or p2:
1484 # If you got here, you either use --force and know what
1484 # If you got here, you either use --force and know what
1485 # you are doing or used --exact or a merge patch while
1485 # you are doing or used --exact or a merge patch while
1486 # being updated to its first parent.
1486 # being updated to its first parent.
1487 m = None
1487 m = None
1488 else:
1488 else:
1489 m = scmutil.matchfiles(repo, files or [])
1489 m = scmutil.matchfiles(repo, files or [])
1490 editform = mergeeditform(repo[None], 'import.normal')
1490 editform = mergeeditform(repo[None], 'import.normal')
1491 if opts.get('exact'):
1491 if opts.get('exact'):
1492 editor = None
1492 editor = None
1493 else:
1493 else:
1494 editor = getcommiteditor(editform=editform,
1494 editor = getcommiteditor(editform=editform,
1495 **pycompat.strkwargs(opts))
1495 **pycompat.strkwargs(opts))
1496 extra = {}
1496 extra = {}
1497 for idfunc in extrapreimport:
1497 for idfunc in extrapreimport:
1498 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1498 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1499 overrides = {}
1499 overrides = {}
1500 if partial:
1500 if partial:
1501 overrides[('ui', 'allowemptycommit')] = True
1501 overrides[('ui', 'allowemptycommit')] = True
1502 with repo.ui.configoverride(overrides, 'import'):
1502 with repo.ui.configoverride(overrides, 'import'):
1503 n = repo.commit(message, user,
1503 n = repo.commit(message, user,
1504 date, match=m,
1504 date, match=m,
1505 editor=editor, extra=extra)
1505 editor=editor, extra=extra)
1506 for idfunc in extrapostimport:
1506 for idfunc in extrapostimport:
1507 extrapostimportmap[idfunc](repo[n])
1507 extrapostimportmap[idfunc](repo[n])
1508 else:
1508 else:
1509 if opts.get('exact') or importbranch:
1509 if opts.get('exact') or importbranch:
1510 branch = branch or 'default'
1510 branch = branch or 'default'
1511 else:
1511 else:
1512 branch = p1.branch()
1512 branch = p1.branch()
1513 store = patch.filestore()
1513 store = patch.filestore()
1514 try:
1514 try:
1515 files = set()
1515 files = set()
1516 try:
1516 try:
1517 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1517 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1518 files, eolmode=None)
1518 files, eolmode=None)
1519 except error.PatchError as e:
1519 except error.PatchError as e:
1520 raise error.Abort(stringutil.forcebytestr(e))
1520 raise error.Abort(stringutil.forcebytestr(e))
1521 if opts.get('exact'):
1521 if opts.get('exact'):
1522 editor = None
1522 editor = None
1523 else:
1523 else:
1524 editor = getcommiteditor(editform='import.bypass')
1524 editor = getcommiteditor(editform='import.bypass')
1525 memctx = context.memctx(repo, (p1.node(), p2.node()),
1525 memctx = context.memctx(repo, (p1.node(), p2.node()),
1526 message,
1526 message,
1527 files=files,
1527 files=files,
1528 filectxfn=store,
1528 filectxfn=store,
1529 user=user,
1529 user=user,
1530 date=date,
1530 date=date,
1531 branch=branch,
1531 branch=branch,
1532 editor=editor)
1532 editor=editor)
1533 n = memctx.commit()
1533 n = memctx.commit()
1534 finally:
1534 finally:
1535 store.close()
1535 store.close()
1536 if opts.get('exact') and nocommit:
1536 if opts.get('exact') and nocommit:
1537 # --exact with --no-commit is still useful in that it does merge
1537 # --exact with --no-commit is still useful in that it does merge
1538 # and branch bits
1538 # and branch bits
1539 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1539 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1540 elif opts.get('exact') and (not n or hex(n) != nodeid):
1540 elif opts.get('exact') and (not n or hex(n) != nodeid):
1541 raise error.Abort(_('patch is damaged or loses information'))
1541 raise error.Abort(_('patch is damaged or loses information'))
1542 msg = _('applied to working directory')
1542 msg = _('applied to working directory')
1543 if n:
1543 if n:
1544 # i18n: refers to a short changeset id
1544 # i18n: refers to a short changeset id
1545 msg = _('created %s') % short(n)
1545 msg = _('created %s') % short(n)
1546 return msg, n, rejects
1546 return msg, n, rejects
1547
1547
1548 # facility to let extensions include additional data in an exported patch
1548 # facility to let extensions include additional data in an exported patch
1549 # list of identifiers to be executed in order
1549 # list of identifiers to be executed in order
1550 extraexport = []
1550 extraexport = []
1551 # mapping from identifier to actual export function
1551 # mapping from identifier to actual export function
1552 # function as to return a string to be added to the header or None
1552 # function as to return a string to be added to the header or None
1553 # it is given two arguments (sequencenumber, changectx)
1553 # it is given two arguments (sequencenumber, changectx)
1554 extraexportmap = {}
1554 extraexportmap = {}
1555
1555
1556 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1556 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1557 node = scmutil.binnode(ctx)
1557 node = scmutil.binnode(ctx)
1558 parents = [p.node() for p in ctx.parents() if p]
1558 parents = [p.node() for p in ctx.parents() if p]
1559 branch = ctx.branch()
1559 branch = ctx.branch()
1560 if switch_parent:
1560 if switch_parent:
1561 parents.reverse()
1561 parents.reverse()
1562
1562
1563 if parents:
1563 if parents:
1564 prev = parents[0]
1564 prev = parents[0]
1565 else:
1565 else:
1566 prev = nullid
1566 prev = nullid
1567
1567
1568 fm.context(ctx=ctx)
1568 fm.context(ctx=ctx)
1569 fm.plain('# HG changeset patch\n')
1569 fm.plain('# HG changeset patch\n')
1570 fm.write('user', '# User %s\n', ctx.user())
1570 fm.write('user', '# User %s\n', ctx.user())
1571 fm.plain('# Date %d %d\n' % ctx.date())
1571 fm.plain('# Date %d %d\n' % ctx.date())
1572 fm.write('date', '# %s\n', fm.formatdate(ctx.date()))
1572 fm.write('date', '# %s\n', fm.formatdate(ctx.date()))
1573 fm.condwrite(branch and branch != 'default',
1573 fm.condwrite(branch and branch != 'default',
1574 'branch', '# Branch %s\n', branch)
1574 'branch', '# Branch %s\n', branch)
1575 fm.write('node', '# Node ID %s\n', hex(node))
1575 fm.write('node', '# Node ID %s\n', hex(node))
1576 fm.plain('# Parent %s\n' % hex(prev))
1576 fm.plain('# Parent %s\n' % hex(prev))
1577 if len(parents) > 1:
1577 if len(parents) > 1:
1578 fm.plain('# Parent %s\n' % hex(parents[1]))
1578 fm.plain('# Parent %s\n' % hex(parents[1]))
1579 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name='node'))
1579 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name='node'))
1580
1580
1581 # TODO: redesign extraexportmap function to support formatter
1581 # TODO: redesign extraexportmap function to support formatter
1582 for headerid in extraexport:
1582 for headerid in extraexport:
1583 header = extraexportmap[headerid](seqno, ctx)
1583 header = extraexportmap[headerid](seqno, ctx)
1584 if header is not None:
1584 if header is not None:
1585 fm.plain('# %s\n' % header)
1585 fm.plain('# %s\n' % header)
1586
1586
1587 fm.write('desc', '%s\n', ctx.description().rstrip())
1587 fm.write('desc', '%s\n', ctx.description().rstrip())
1588 fm.plain('\n')
1588 fm.plain('\n')
1589
1589
1590 if fm.isplain():
1590 if fm.isplain():
1591 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1591 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1592 for chunk, label in chunkiter:
1592 for chunk, label in chunkiter:
1593 fm.plain(chunk, label=label)
1593 fm.plain(chunk, label=label)
1594 else:
1594 else:
1595 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1595 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1596 # TODO: make it structured?
1596 # TODO: make it structured?
1597 fm.data(diff=b''.join(chunkiter))
1597 fm.data(diff=b''.join(chunkiter))
1598
1598
1599 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1599 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1600 """Export changesets to stdout or a single file"""
1600 """Export changesets to stdout or a single file"""
1601 for seqno, rev in enumerate(revs, 1):
1601 for seqno, rev in enumerate(revs, 1):
1602 ctx = repo[rev]
1602 ctx = repo[rev]
1603 if not dest.startswith('<'):
1603 if not dest.startswith('<'):
1604 repo.ui.note("%s\n" % dest)
1604 repo.ui.note("%s\n" % dest)
1605 fm.startitem()
1605 fm.startitem()
1606 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1606 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1607
1607
1608 def _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, diffopts,
1608 def _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, diffopts,
1609 match):
1609 match):
1610 """Export changesets to possibly multiple files"""
1610 """Export changesets to possibly multiple files"""
1611 total = len(revs)
1611 total = len(revs)
1612 revwidth = max(len(str(rev)) for rev in revs)
1612 revwidth = max(len(str(rev)) for rev in revs)
1613 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1613 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1614
1614
1615 for seqno, rev in enumerate(revs, 1):
1615 for seqno, rev in enumerate(revs, 1):
1616 ctx = repo[rev]
1616 ctx = repo[rev]
1617 dest = makefilename(ctx, fntemplate,
1617 dest = makefilename(ctx, fntemplate,
1618 total=total, seqno=seqno, revwidth=revwidth)
1618 total=total, seqno=seqno, revwidth=revwidth)
1619 filemap.setdefault(dest, []).append((seqno, rev))
1619 filemap.setdefault(dest, []).append((seqno, rev))
1620
1620
1621 for dest in filemap:
1621 for dest in filemap:
1622 with formatter.maybereopen(basefm, dest) as fm:
1622 with formatter.maybereopen(basefm, dest) as fm:
1623 repo.ui.note("%s\n" % dest)
1623 repo.ui.note("%s\n" % dest)
1624 for seqno, rev in filemap[dest]:
1624 for seqno, rev in filemap[dest]:
1625 fm.startitem()
1625 fm.startitem()
1626 ctx = repo[rev]
1626 ctx = repo[rev]
1627 _exportsingle(repo, ctx, fm, match, switch_parent, seqno,
1627 _exportsingle(repo, ctx, fm, match, switch_parent, seqno,
1628 diffopts)
1628 diffopts)
1629
1629
1630 def export(repo, revs, basefm, fntemplate='hg-%h.patch', switch_parent=False,
1630 def export(repo, revs, basefm, fntemplate='hg-%h.patch', switch_parent=False,
1631 opts=None, match=None):
1631 opts=None, match=None):
1632 '''export changesets as hg patches
1632 '''export changesets as hg patches
1633
1633
1634 Args:
1634 Args:
1635 repo: The repository from which we're exporting revisions.
1635 repo: The repository from which we're exporting revisions.
1636 revs: A list of revisions to export as revision numbers.
1636 revs: A list of revisions to export as revision numbers.
1637 basefm: A formatter to which patches should be written.
1637 basefm: A formatter to which patches should be written.
1638 fntemplate: An optional string to use for generating patch file names.
1638 fntemplate: An optional string to use for generating patch file names.
1639 switch_parent: If True, show diffs against second parent when not nullid.
1639 switch_parent: If True, show diffs against second parent when not nullid.
1640 Default is false, which always shows diff against p1.
1640 Default is false, which always shows diff against p1.
1641 opts: diff options to use for generating the patch.
1641 opts: diff options to use for generating the patch.
1642 match: If specified, only export changes to files matching this matcher.
1642 match: If specified, only export changes to files matching this matcher.
1643
1643
1644 Returns:
1644 Returns:
1645 Nothing.
1645 Nothing.
1646
1646
1647 Side Effect:
1647 Side Effect:
1648 "HG Changeset Patch" data is emitted to one of the following
1648 "HG Changeset Patch" data is emitted to one of the following
1649 destinations:
1649 destinations:
1650 fntemplate specified: Each rev is written to a unique file named using
1650 fntemplate specified: Each rev is written to a unique file named using
1651 the given template.
1651 the given template.
1652 Otherwise: All revs will be written to basefm.
1652 Otherwise: All revs will be written to basefm.
1653 '''
1653 '''
1654 scmutil.prefetchfiles(repo, revs, match)
1654 scmutil.prefetchfiles(repo, revs, match)
1655
1655
1656 if not fntemplate:
1656 if not fntemplate:
1657 _exportfile(repo, revs, basefm, '<unnamed>', switch_parent, opts, match)
1657 _exportfile(repo, revs, basefm, '<unnamed>', switch_parent, opts, match)
1658 else:
1658 else:
1659 _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, opts,
1659 _exportfntemplate(repo, revs, basefm, fntemplate, switch_parent, opts,
1660 match)
1660 match)
1661
1661
1662 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1662 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
1663 """Export changesets to the given file stream"""
1663 """Export changesets to the given file stream"""
1664 scmutil.prefetchfiles(repo, revs, match)
1664 scmutil.prefetchfiles(repo, revs, match)
1665
1665
1666 dest = getattr(fp, 'name', '<unnamed>')
1666 dest = getattr(fp, 'name', '<unnamed>')
1667 with formatter.formatter(repo.ui, fp, 'export', {}) as fm:
1667 with formatter.formatter(repo.ui, fp, 'export', {}) as fm:
1668 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1668 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
1669
1669
1670 def showmarker(fm, marker, index=None):
1670 def showmarker(fm, marker, index=None):
1671 """utility function to display obsolescence marker in a readable way
1671 """utility function to display obsolescence marker in a readable way
1672
1672
1673 To be used by debug function."""
1673 To be used by debug function."""
1674 if index is not None:
1674 if index is not None:
1675 fm.write('index', '%i ', index)
1675 fm.write('index', '%i ', index)
1676 fm.write('prednode', '%s ', hex(marker.prednode()))
1676 fm.write('prednode', '%s ', hex(marker.prednode()))
1677 succs = marker.succnodes()
1677 succs = marker.succnodes()
1678 fm.condwrite(succs, 'succnodes', '%s ',
1678 fm.condwrite(succs, 'succnodes', '%s ',
1679 fm.formatlist(map(hex, succs), name='node'))
1679 fm.formatlist(map(hex, succs), name='node'))
1680 fm.write('flag', '%X ', marker.flags())
1680 fm.write('flag', '%X ', marker.flags())
1681 parents = marker.parentnodes()
1681 parents = marker.parentnodes()
1682 if parents is not None:
1682 if parents is not None:
1683 fm.write('parentnodes', '{%s} ',
1683 fm.write('parentnodes', '{%s} ',
1684 fm.formatlist(map(hex, parents), name='node', sep=', '))
1684 fm.formatlist(map(hex, parents), name='node', sep=', '))
1685 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1685 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
1686 meta = marker.metadata().copy()
1686 meta = marker.metadata().copy()
1687 meta.pop('date', None)
1687 meta.pop('date', None)
1688 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
1688 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
1689 fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', '))
1689 fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', '))
1690 fm.plain('\n')
1690 fm.plain('\n')
1691
1691
1692 def finddate(ui, repo, date):
1692 def finddate(ui, repo, date):
1693 """Find the tipmost changeset that matches the given date spec"""
1693 """Find the tipmost changeset that matches the given date spec"""
1694
1694
1695 df = dateutil.matchdate(date)
1695 df = dateutil.matchdate(date)
1696 m = scmutil.matchall(repo)
1696 m = scmutil.matchall(repo)
1697 results = {}
1697 results = {}
1698
1698
1699 def prep(ctx, fns):
1699 def prep(ctx, fns):
1700 d = ctx.date()
1700 d = ctx.date()
1701 if df(d[0]):
1701 if df(d[0]):
1702 results[ctx.rev()] = d
1702 results[ctx.rev()] = d
1703
1703
1704 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1704 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1705 rev = ctx.rev()
1705 rev = ctx.rev()
1706 if rev in results:
1706 if rev in results:
1707 ui.status(_("found revision %s from %s\n") %
1707 ui.status(_("found revision %s from %s\n") %
1708 (rev, dateutil.datestr(results[rev])))
1708 (rev, dateutil.datestr(results[rev])))
1709 return '%d' % rev
1709 return '%d' % rev
1710
1710
1711 raise error.Abort(_("revision matching date not found"))
1711 raise error.Abort(_("revision matching date not found"))
1712
1712
1713 def increasingwindows(windowsize=8, sizelimit=512):
1713 def increasingwindows(windowsize=8, sizelimit=512):
1714 while True:
1714 while True:
1715 yield windowsize
1715 yield windowsize
1716 if windowsize < sizelimit:
1716 if windowsize < sizelimit:
1717 windowsize *= 2
1717 windowsize *= 2
1718
1718
1719 def _walkrevs(repo, opts):
1719 def _walkrevs(repo, opts):
1720 # Default --rev value depends on --follow but --follow behavior
1720 # Default --rev value depends on --follow but --follow behavior
1721 # depends on revisions resolved from --rev...
1721 # depends on revisions resolved from --rev...
1722 follow = opts.get('follow') or opts.get('follow_first')
1722 follow = opts.get('follow') or opts.get('follow_first')
1723 if opts.get('rev'):
1723 if opts.get('rev'):
1724 revs = scmutil.revrange(repo, opts['rev'])
1724 revs = scmutil.revrange(repo, opts['rev'])
1725 elif follow and repo.dirstate.p1() == nullid:
1725 elif follow and repo.dirstate.p1() == nullid:
1726 revs = smartset.baseset()
1726 revs = smartset.baseset()
1727 elif follow:
1727 elif follow:
1728 revs = repo.revs('reverse(:.)')
1728 revs = repo.revs('reverse(:.)')
1729 else:
1729 else:
1730 revs = smartset.spanset(repo)
1730 revs = smartset.spanset(repo)
1731 revs.reverse()
1731 revs.reverse()
1732 return revs
1732 return revs
1733
1733
1734 class FileWalkError(Exception):
1734 class FileWalkError(Exception):
1735 pass
1735 pass
1736
1736
1737 def walkfilerevs(repo, match, follow, revs, fncache):
1737 def walkfilerevs(repo, match, follow, revs, fncache):
1738 '''Walks the file history for the matched files.
1738 '''Walks the file history for the matched files.
1739
1739
1740 Returns the changeset revs that are involved in the file history.
1740 Returns the changeset revs that are involved in the file history.
1741
1741
1742 Throws FileWalkError if the file history can't be walked using
1742 Throws FileWalkError if the file history can't be walked using
1743 filelogs alone.
1743 filelogs alone.
1744 '''
1744 '''
1745 wanted = set()
1745 wanted = set()
1746 copies = []
1746 copies = []
1747 minrev, maxrev = min(revs), max(revs)
1747 minrev, maxrev = min(revs), max(revs)
1748 def filerevgen(filelog, last):
1748 def filerevgen(filelog, last):
1749 """
1749 """
1750 Only files, no patterns. Check the history of each file.
1750 Only files, no patterns. Check the history of each file.
1751
1751
1752 Examines filelog entries within minrev, maxrev linkrev range
1752 Examines filelog entries within minrev, maxrev linkrev range
1753 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1753 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1754 tuples in backwards order
1754 tuples in backwards order
1755 """
1755 """
1756 cl_count = len(repo)
1756 cl_count = len(repo)
1757 revs = []
1757 revs = []
1758 for j in pycompat.xrange(0, last + 1):
1758 for j in pycompat.xrange(0, last + 1):
1759 linkrev = filelog.linkrev(j)
1759 linkrev = filelog.linkrev(j)
1760 if linkrev < minrev:
1760 if linkrev < minrev:
1761 continue
1761 continue
1762 # only yield rev for which we have the changelog, it can
1762 # only yield rev for which we have the changelog, it can
1763 # happen while doing "hg log" during a pull or commit
1763 # happen while doing "hg log" during a pull or commit
1764 if linkrev >= cl_count:
1764 if linkrev >= cl_count:
1765 break
1765 break
1766
1766
1767 parentlinkrevs = []
1767 parentlinkrevs = []
1768 for p in filelog.parentrevs(j):
1768 for p in filelog.parentrevs(j):
1769 if p != nullrev:
1769 if p != nullrev:
1770 parentlinkrevs.append(filelog.linkrev(p))
1770 parentlinkrevs.append(filelog.linkrev(p))
1771 n = filelog.node(j)
1771 n = filelog.node(j)
1772 revs.append((linkrev, parentlinkrevs,
1772 revs.append((linkrev, parentlinkrevs,
1773 follow and filelog.renamed(n)))
1773 follow and filelog.renamed(n)))
1774
1774
1775 return reversed(revs)
1775 return reversed(revs)
1776 def iterfiles():
1776 def iterfiles():
1777 pctx = repo['.']
1777 pctx = repo['.']
1778 for filename in match.files():
1778 for filename in match.files():
1779 if follow:
1779 if follow:
1780 if filename not in pctx:
1780 if filename not in pctx:
1781 raise error.Abort(_('cannot follow file not in parent '
1781 raise error.Abort(_('cannot follow file not in parent '
1782 'revision: "%s"') % filename)
1782 'revision: "%s"') % filename)
1783 yield filename, pctx[filename].filenode()
1783 yield filename, pctx[filename].filenode()
1784 else:
1784 else:
1785 yield filename, None
1785 yield filename, None
1786 for filename_node in copies:
1786 for filename_node in copies:
1787 yield filename_node
1787 yield filename_node
1788
1788
1789 for file_, node in iterfiles():
1789 for file_, node in iterfiles():
1790 filelog = repo.file(file_)
1790 filelog = repo.file(file_)
1791 if not len(filelog):
1791 if not len(filelog):
1792 if node is None:
1792 if node is None:
1793 # A zero count may be a directory or deleted file, so
1793 # A zero count may be a directory or deleted file, so
1794 # try to find matching entries on the slow path.
1794 # try to find matching entries on the slow path.
1795 if follow:
1795 if follow:
1796 raise error.Abort(
1796 raise error.Abort(
1797 _('cannot follow nonexistent file: "%s"') % file_)
1797 _('cannot follow nonexistent file: "%s"') % file_)
1798 raise FileWalkError("Cannot walk via filelog")
1798 raise FileWalkError("Cannot walk via filelog")
1799 else:
1799 else:
1800 continue
1800 continue
1801
1801
1802 if node is None:
1802 if node is None:
1803 last = len(filelog) - 1
1803 last = len(filelog) - 1
1804 else:
1804 else:
1805 last = filelog.rev(node)
1805 last = filelog.rev(node)
1806
1806
1807 # keep track of all ancestors of the file
1807 # keep track of all ancestors of the file
1808 ancestors = {filelog.linkrev(last)}
1808 ancestors = {filelog.linkrev(last)}
1809
1809
1810 # iterate from latest to oldest revision
1810 # iterate from latest to oldest revision
1811 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1811 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1812 if not follow:
1812 if not follow:
1813 if rev > maxrev:
1813 if rev > maxrev:
1814 continue
1814 continue
1815 else:
1815 else:
1816 # Note that last might not be the first interesting
1816 # Note that last might not be the first interesting
1817 # rev to us:
1817 # rev to us:
1818 # if the file has been changed after maxrev, we'll
1818 # if the file has been changed after maxrev, we'll
1819 # have linkrev(last) > maxrev, and we still need
1819 # have linkrev(last) > maxrev, and we still need
1820 # to explore the file graph
1820 # to explore the file graph
1821 if rev not in ancestors:
1821 if rev not in ancestors:
1822 continue
1822 continue
1823 # XXX insert 1327 fix here
1823 # XXX insert 1327 fix here
1824 if flparentlinkrevs:
1824 if flparentlinkrevs:
1825 ancestors.update(flparentlinkrevs)
1825 ancestors.update(flparentlinkrevs)
1826
1826
1827 fncache.setdefault(rev, []).append(file_)
1827 fncache.setdefault(rev, []).append(file_)
1828 wanted.add(rev)
1828 wanted.add(rev)
1829 if copied:
1829 if copied:
1830 copies.append(copied)
1830 copies.append(copied)
1831
1831
1832 return wanted
1832 return wanted
1833
1833
1834 class _followfilter(object):
1834 class _followfilter(object):
1835 def __init__(self, repo, onlyfirst=False):
1835 def __init__(self, repo, onlyfirst=False):
1836 self.repo = repo
1836 self.repo = repo
1837 self.startrev = nullrev
1837 self.startrev = nullrev
1838 self.roots = set()
1838 self.roots = set()
1839 self.onlyfirst = onlyfirst
1839 self.onlyfirst = onlyfirst
1840
1840
1841 def match(self, rev):
1841 def match(self, rev):
1842 def realparents(rev):
1842 def realparents(rev):
1843 if self.onlyfirst:
1843 if self.onlyfirst:
1844 return self.repo.changelog.parentrevs(rev)[0:1]
1844 return self.repo.changelog.parentrevs(rev)[0:1]
1845 else:
1845 else:
1846 return filter(lambda x: x != nullrev,
1846 return filter(lambda x: x != nullrev,
1847 self.repo.changelog.parentrevs(rev))
1847 self.repo.changelog.parentrevs(rev))
1848
1848
1849 if self.startrev == nullrev:
1849 if self.startrev == nullrev:
1850 self.startrev = rev
1850 self.startrev = rev
1851 return True
1851 return True
1852
1852
1853 if rev > self.startrev:
1853 if rev > self.startrev:
1854 # forward: all descendants
1854 # forward: all descendants
1855 if not self.roots:
1855 if not self.roots:
1856 self.roots.add(self.startrev)
1856 self.roots.add(self.startrev)
1857 for parent in realparents(rev):
1857 for parent in realparents(rev):
1858 if parent in self.roots:
1858 if parent in self.roots:
1859 self.roots.add(rev)
1859 self.roots.add(rev)
1860 return True
1860 return True
1861 else:
1861 else:
1862 # backwards: all parents
1862 # backwards: all parents
1863 if not self.roots:
1863 if not self.roots:
1864 self.roots.update(realparents(self.startrev))
1864 self.roots.update(realparents(self.startrev))
1865 if rev in self.roots:
1865 if rev in self.roots:
1866 self.roots.remove(rev)
1866 self.roots.remove(rev)
1867 self.roots.update(realparents(rev))
1867 self.roots.update(realparents(rev))
1868 return True
1868 return True
1869
1869
1870 return False
1870 return False
1871
1871
1872 def walkchangerevs(repo, match, opts, prepare):
1872 def walkchangerevs(repo, match, opts, prepare):
1873 '''Iterate over files and the revs in which they changed.
1873 '''Iterate over files and the revs in which they changed.
1874
1874
1875 Callers most commonly need to iterate backwards over the history
1875 Callers most commonly need to iterate backwards over the history
1876 in which they are interested. Doing so has awful (quadratic-looking)
1876 in which they are interested. Doing so has awful (quadratic-looking)
1877 performance, so we use iterators in a "windowed" way.
1877 performance, so we use iterators in a "windowed" way.
1878
1878
1879 We walk a window of revisions in the desired order. Within the
1879 We walk a window of revisions in the desired order. Within the
1880 window, we first walk forwards to gather data, then in the desired
1880 window, we first walk forwards to gather data, then in the desired
1881 order (usually backwards) to display it.
1881 order (usually backwards) to display it.
1882
1882
1883 This function returns an iterator yielding contexts. Before
1883 This function returns an iterator yielding contexts. Before
1884 yielding each context, the iterator will first call the prepare
1884 yielding each context, the iterator will first call the prepare
1885 function on each context in the window in forward order.'''
1885 function on each context in the window in forward order.'''
1886
1886
1887 allfiles = opts.get('all_files')
1887 allfiles = opts.get('all_files')
1888 follow = opts.get('follow') or opts.get('follow_first')
1888 follow = opts.get('follow') or opts.get('follow_first')
1889 revs = _walkrevs(repo, opts)
1889 revs = _walkrevs(repo, opts)
1890 if not revs:
1890 if not revs:
1891 return []
1891 return []
1892 if allfiles and len(revs) > 1:
1893 raise error.Abort(_("multiple revisions not supported with "
1894 "--all-files"))
1895 wanted = set()
1892 wanted = set()
1896 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
1893 slowpath = match.anypats() or (not match.always() and opts.get('removed'))
1897 fncache = {}
1894 fncache = {}
1898 change = repo.__getitem__
1895 change = repo.__getitem__
1899
1896
1900 # First step is to fill wanted, the set of revisions that we want to yield.
1897 # First step is to fill wanted, the set of revisions that we want to yield.
1901 # When it does not induce extra cost, we also fill fncache for revisions in
1898 # When it does not induce extra cost, we also fill fncache for revisions in
1902 # wanted: a cache of filenames that were changed (ctx.files()) and that
1899 # wanted: a cache of filenames that were changed (ctx.files()) and that
1903 # match the file filtering conditions.
1900 # match the file filtering conditions.
1904
1901
1905 if match.always():
1902 if match.always():
1906 # No files, no patterns. Display all revs.
1903 # No files, no patterns. Display all revs.
1907 wanted = revs
1904 wanted = revs
1908 elif not slowpath:
1905 elif not slowpath:
1909 # We only have to read through the filelog to find wanted revisions
1906 # We only have to read through the filelog to find wanted revisions
1910
1907
1911 try:
1908 try:
1912 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1909 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1913 except FileWalkError:
1910 except FileWalkError:
1914 slowpath = True
1911 slowpath = True
1915
1912
1916 # We decided to fall back to the slowpath because at least one
1913 # We decided to fall back to the slowpath because at least one
1917 # of the paths was not a file. Check to see if at least one of them
1914 # of the paths was not a file. Check to see if at least one of them
1918 # existed in history, otherwise simply return
1915 # existed in history, otherwise simply return
1919 for path in match.files():
1916 for path in match.files():
1920 if path == '.' or path in repo.store:
1917 if path == '.' or path in repo.store:
1921 break
1918 break
1922 else:
1919 else:
1923 return []
1920 return []
1924
1921
1925 if slowpath:
1922 if slowpath:
1926 # We have to read the changelog to match filenames against
1923 # We have to read the changelog to match filenames against
1927 # changed files
1924 # changed files
1928
1925
1929 if follow:
1926 if follow:
1930 raise error.Abort(_('can only follow copies/renames for explicit '
1927 raise error.Abort(_('can only follow copies/renames for explicit '
1931 'filenames'))
1928 'filenames'))
1932
1929
1933 # The slow path checks files modified in every changeset.
1930 # The slow path checks files modified in every changeset.
1934 # This is really slow on large repos, so compute the set lazily.
1931 # This is really slow on large repos, so compute the set lazily.
1935 class lazywantedset(object):
1932 class lazywantedset(object):
1936 def __init__(self):
1933 def __init__(self):
1937 self.set = set()
1934 self.set = set()
1938 self.revs = set(revs)
1935 self.revs = set(revs)
1939
1936
1940 # No need to worry about locality here because it will be accessed
1937 # No need to worry about locality here because it will be accessed
1941 # in the same order as the increasing window below.
1938 # in the same order as the increasing window below.
1942 def __contains__(self, value):
1939 def __contains__(self, value):
1943 if value in self.set:
1940 if value in self.set:
1944 return True
1941 return True
1945 elif not value in self.revs:
1942 elif not value in self.revs:
1946 return False
1943 return False
1947 else:
1944 else:
1948 self.revs.discard(value)
1945 self.revs.discard(value)
1949 ctx = change(value)
1946 ctx = change(value)
1950 matches = [f for f in ctx.files() if match(f)]
1947 matches = [f for f in ctx.files() if match(f)]
1951 if matches:
1948 if matches:
1952 fncache[value] = matches
1949 fncache[value] = matches
1953 self.set.add(value)
1950 self.set.add(value)
1954 return True
1951 return True
1955 return False
1952 return False
1956
1953
1957 def discard(self, value):
1954 def discard(self, value):
1958 self.revs.discard(value)
1955 self.revs.discard(value)
1959 self.set.discard(value)
1956 self.set.discard(value)
1960
1957
1961 wanted = lazywantedset()
1958 wanted = lazywantedset()
1962
1959
1963 # it might be worthwhile to do this in the iterator if the rev range
1960 # it might be worthwhile to do this in the iterator if the rev range
1964 # is descending and the prune args are all within that range
1961 # is descending and the prune args are all within that range
1965 for rev in opts.get('prune', ()):
1962 for rev in opts.get('prune', ()):
1966 rev = repo[rev].rev()
1963 rev = repo[rev].rev()
1967 ff = _followfilter(repo)
1964 ff = _followfilter(repo)
1968 stop = min(revs[0], revs[-1])
1965 stop = min(revs[0], revs[-1])
1969 for x in pycompat.xrange(rev, stop - 1, -1):
1966 for x in pycompat.xrange(rev, stop - 1, -1):
1970 if ff.match(x):
1967 if ff.match(x):
1971 wanted = wanted - [x]
1968 wanted = wanted - [x]
1972
1969
1973 # Now that wanted is correctly initialized, we can iterate over the
1970 # Now that wanted is correctly initialized, we can iterate over the
1974 # revision range, yielding only revisions in wanted.
1971 # revision range, yielding only revisions in wanted.
1975 def iterate():
1972 def iterate():
1976 if follow and match.always():
1973 if follow and match.always():
1977 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1974 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1978 def want(rev):
1975 def want(rev):
1979 return ff.match(rev) and rev in wanted
1976 return ff.match(rev) and rev in wanted
1980 else:
1977 else:
1981 def want(rev):
1978 def want(rev):
1982 return rev in wanted
1979 return rev in wanted
1983
1980
1984 it = iter(revs)
1981 it = iter(revs)
1985 stopiteration = False
1982 stopiteration = False
1986 for windowsize in increasingwindows():
1983 for windowsize in increasingwindows():
1987 nrevs = []
1984 nrevs = []
1988 for i in pycompat.xrange(windowsize):
1985 for i in pycompat.xrange(windowsize):
1989 rev = next(it, None)
1986 rev = next(it, None)
1990 if rev is None:
1987 if rev is None:
1991 stopiteration = True
1988 stopiteration = True
1992 break
1989 break
1993 elif want(rev):
1990 elif want(rev):
1994 nrevs.append(rev)
1991 nrevs.append(rev)
1995 for rev in sorted(nrevs):
1992 for rev in sorted(nrevs):
1996 fns = fncache.get(rev)
1993 fns = fncache.get(rev)
1997 ctx = change(rev)
1994 ctx = change(rev)
1998 if not fns:
1995 if not fns:
1999 def fns_generator():
1996 def fns_generator():
2000 if allfiles:
1997 if allfiles:
2001 fiter = iter(ctx)
1998 fiter = iter(ctx)
2002 else:
1999 else:
2003 fiter = ctx.files()
2000 fiter = ctx.files()
2004 for f in fiter:
2001 for f in fiter:
2005 if match(f):
2002 if match(f):
2006 yield f
2003 yield f
2007 fns = fns_generator()
2004 fns = fns_generator()
2008 prepare(ctx, fns)
2005 prepare(ctx, fns)
2009 for rev in nrevs:
2006 for rev in nrevs:
2010 yield change(rev)
2007 yield change(rev)
2011
2008
2012 if stopiteration:
2009 if stopiteration:
2013 break
2010 break
2014
2011
2015 return iterate()
2012 return iterate()
2016
2013
2017 def add(ui, repo, match, prefix, explicitonly, **opts):
2014 def add(ui, repo, match, prefix, explicitonly, **opts):
2018 join = lambda f: os.path.join(prefix, f)
2015 join = lambda f: os.path.join(prefix, f)
2019 bad = []
2016 bad = []
2020
2017
2021 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2018 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2022 names = []
2019 names = []
2023 wctx = repo[None]
2020 wctx = repo[None]
2024 cca = None
2021 cca = None
2025 abort, warn = scmutil.checkportabilityalert(ui)
2022 abort, warn = scmutil.checkportabilityalert(ui)
2026 if abort or warn:
2023 if abort or warn:
2027 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2024 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2028
2025
2029 badmatch = matchmod.badmatch(match, badfn)
2026 badmatch = matchmod.badmatch(match, badfn)
2030 dirstate = repo.dirstate
2027 dirstate = repo.dirstate
2031 # We don't want to just call wctx.walk here, since it would return a lot of
2028 # We don't want to just call wctx.walk here, since it would return a lot of
2032 # clean files, which we aren't interested in and takes time.
2029 # clean files, which we aren't interested in and takes time.
2033 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2030 for f in sorted(dirstate.walk(badmatch, subrepos=sorted(wctx.substate),
2034 unknown=True, ignored=False, full=False)):
2031 unknown=True, ignored=False, full=False)):
2035 exact = match.exact(f)
2032 exact = match.exact(f)
2036 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2033 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2037 if cca:
2034 if cca:
2038 cca(f)
2035 cca(f)
2039 names.append(f)
2036 names.append(f)
2040 if ui.verbose or not exact:
2037 if ui.verbose or not exact:
2041 ui.status(_('adding %s\n') % match.rel(f))
2038 ui.status(_('adding %s\n') % match.rel(f))
2042
2039
2043 for subpath in sorted(wctx.substate):
2040 for subpath in sorted(wctx.substate):
2044 sub = wctx.sub(subpath)
2041 sub = wctx.sub(subpath)
2045 try:
2042 try:
2046 submatch = matchmod.subdirmatcher(subpath, match)
2043 submatch = matchmod.subdirmatcher(subpath, match)
2047 if opts.get(r'subrepos'):
2044 if opts.get(r'subrepos'):
2048 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2045 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2049 else:
2046 else:
2050 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2047 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2051 except error.LookupError:
2048 except error.LookupError:
2052 ui.status(_("skipping missing subrepository: %s\n")
2049 ui.status(_("skipping missing subrepository: %s\n")
2053 % join(subpath))
2050 % join(subpath))
2054
2051
2055 if not opts.get(r'dry_run'):
2052 if not opts.get(r'dry_run'):
2056 rejected = wctx.add(names, prefix)
2053 rejected = wctx.add(names, prefix)
2057 bad.extend(f for f in rejected if f in match.files())
2054 bad.extend(f for f in rejected if f in match.files())
2058 return bad
2055 return bad
2059
2056
2060 def addwebdirpath(repo, serverpath, webconf):
2057 def addwebdirpath(repo, serverpath, webconf):
2061 webconf[serverpath] = repo.root
2058 webconf[serverpath] = repo.root
2062 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2059 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2063
2060
2064 for r in repo.revs('filelog("path:.hgsub")'):
2061 for r in repo.revs('filelog("path:.hgsub")'):
2065 ctx = repo[r]
2062 ctx = repo[r]
2066 for subpath in ctx.substate:
2063 for subpath in ctx.substate:
2067 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2064 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2068
2065
2069 def forget(ui, repo, match, prefix, explicitonly, dryrun, interactive):
2066 def forget(ui, repo, match, prefix, explicitonly, dryrun, interactive):
2070 if dryrun and interactive:
2067 if dryrun and interactive:
2071 raise error.Abort(_("cannot specify both --dry-run and --interactive"))
2068 raise error.Abort(_("cannot specify both --dry-run and --interactive"))
2072 join = lambda f: os.path.join(prefix, f)
2069 join = lambda f: os.path.join(prefix, f)
2073 bad = []
2070 bad = []
2074 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2071 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2075 wctx = repo[None]
2072 wctx = repo[None]
2076 forgot = []
2073 forgot = []
2077
2074
2078 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2075 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2079 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2076 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2080 if explicitonly:
2077 if explicitonly:
2081 forget = [f for f in forget if match.exact(f)]
2078 forget = [f for f in forget if match.exact(f)]
2082
2079
2083 for subpath in sorted(wctx.substate):
2080 for subpath in sorted(wctx.substate):
2084 sub = wctx.sub(subpath)
2081 sub = wctx.sub(subpath)
2085 try:
2082 try:
2086 submatch = matchmod.subdirmatcher(subpath, match)
2083 submatch = matchmod.subdirmatcher(subpath, match)
2087 subbad, subforgot = sub.forget(submatch, prefix, dryrun=dryrun,
2084 subbad, subforgot = sub.forget(submatch, prefix, dryrun=dryrun,
2088 interactive=interactive)
2085 interactive=interactive)
2089 bad.extend([subpath + '/' + f for f in subbad])
2086 bad.extend([subpath + '/' + f for f in subbad])
2090 forgot.extend([subpath + '/' + f for f in subforgot])
2087 forgot.extend([subpath + '/' + f for f in subforgot])
2091 except error.LookupError:
2088 except error.LookupError:
2092 ui.status(_("skipping missing subrepository: %s\n")
2089 ui.status(_("skipping missing subrepository: %s\n")
2093 % join(subpath))
2090 % join(subpath))
2094
2091
2095 if not explicitonly:
2092 if not explicitonly:
2096 for f in match.files():
2093 for f in match.files():
2097 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2094 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2098 if f not in forgot:
2095 if f not in forgot:
2099 if repo.wvfs.exists(f):
2096 if repo.wvfs.exists(f):
2100 # Don't complain if the exact case match wasn't given.
2097 # Don't complain if the exact case match wasn't given.
2101 # But don't do this until after checking 'forgot', so
2098 # But don't do this until after checking 'forgot', so
2102 # that subrepo files aren't normalized, and this op is
2099 # that subrepo files aren't normalized, and this op is
2103 # purely from data cached by the status walk above.
2100 # purely from data cached by the status walk above.
2104 if repo.dirstate.normalize(f) in repo.dirstate:
2101 if repo.dirstate.normalize(f) in repo.dirstate:
2105 continue
2102 continue
2106 ui.warn(_('not removing %s: '
2103 ui.warn(_('not removing %s: '
2107 'file is already untracked\n')
2104 'file is already untracked\n')
2108 % match.rel(f))
2105 % match.rel(f))
2109 bad.append(f)
2106 bad.append(f)
2110
2107
2111 if interactive:
2108 if interactive:
2112 responses = _('[Ynsa?]'
2109 responses = _('[Ynsa?]'
2113 '$$ &Yes, forget this file'
2110 '$$ &Yes, forget this file'
2114 '$$ &No, skip this file'
2111 '$$ &No, skip this file'
2115 '$$ &Skip remaining files'
2112 '$$ &Skip remaining files'
2116 '$$ Include &all remaining files'
2113 '$$ Include &all remaining files'
2117 '$$ &? (display help)')
2114 '$$ &? (display help)')
2118 for filename in forget[:]:
2115 for filename in forget[:]:
2119 r = ui.promptchoice(_('forget %s %s') % (filename, responses))
2116 r = ui.promptchoice(_('forget %s %s') % (filename, responses))
2120 if r == 4: # ?
2117 if r == 4: # ?
2121 while r == 4:
2118 while r == 4:
2122 for c, t in ui.extractchoices(responses)[1]:
2119 for c, t in ui.extractchoices(responses)[1]:
2123 ui.write('%s - %s\n' % (c, encoding.lower(t)))
2120 ui.write('%s - %s\n' % (c, encoding.lower(t)))
2124 r = ui.promptchoice(_('forget %s %s') % (filename,
2121 r = ui.promptchoice(_('forget %s %s') % (filename,
2125 responses))
2122 responses))
2126 if r == 0: # yes
2123 if r == 0: # yes
2127 continue
2124 continue
2128 elif r == 1: # no
2125 elif r == 1: # no
2129 forget.remove(filename)
2126 forget.remove(filename)
2130 elif r == 2: # Skip
2127 elif r == 2: # Skip
2131 fnindex = forget.index(filename)
2128 fnindex = forget.index(filename)
2132 del forget[fnindex:]
2129 del forget[fnindex:]
2133 break
2130 break
2134 elif r == 3: # All
2131 elif r == 3: # All
2135 break
2132 break
2136
2133
2137 for f in forget:
2134 for f in forget:
2138 if ui.verbose or not match.exact(f) or interactive:
2135 if ui.verbose or not match.exact(f) or interactive:
2139 ui.status(_('removing %s\n') % match.rel(f))
2136 ui.status(_('removing %s\n') % match.rel(f))
2140
2137
2141 if not dryrun:
2138 if not dryrun:
2142 rejected = wctx.forget(forget, prefix)
2139 rejected = wctx.forget(forget, prefix)
2143 bad.extend(f for f in rejected if f in match.files())
2140 bad.extend(f for f in rejected if f in match.files())
2144 forgot.extend(f for f in forget if f not in rejected)
2141 forgot.extend(f for f in forget if f not in rejected)
2145 return bad, forgot
2142 return bad, forgot
2146
2143
2147 def files(ui, ctx, m, fm, fmt, subrepos):
2144 def files(ui, ctx, m, fm, fmt, subrepos):
2148 ret = 1
2145 ret = 1
2149
2146
2150 needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint()
2147 needsfctx = ui.verbose or {'size', 'flags'} & fm.datahint()
2151 for f in ctx.matches(m):
2148 for f in ctx.matches(m):
2152 fm.startitem()
2149 fm.startitem()
2153 fm.context(ctx=ctx)
2150 fm.context(ctx=ctx)
2154 if needsfctx:
2151 if needsfctx:
2155 fc = ctx[f]
2152 fc = ctx[f]
2156 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2153 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2157 fm.data(abspath=f)
2154 fm.data(abspath=f)
2158 fm.write('path', fmt, m.rel(f))
2155 fm.write('path', fmt, m.rel(f))
2159 ret = 0
2156 ret = 0
2160
2157
2161 for subpath in sorted(ctx.substate):
2158 for subpath in sorted(ctx.substate):
2162 submatch = matchmod.subdirmatcher(subpath, m)
2159 submatch = matchmod.subdirmatcher(subpath, m)
2163 if (subrepos or m.exact(subpath) or any(submatch.files())):
2160 if (subrepos or m.exact(subpath) or any(submatch.files())):
2164 sub = ctx.sub(subpath)
2161 sub = ctx.sub(subpath)
2165 try:
2162 try:
2166 recurse = m.exact(subpath) or subrepos
2163 recurse = m.exact(subpath) or subrepos
2167 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2164 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2168 ret = 0
2165 ret = 0
2169 except error.LookupError:
2166 except error.LookupError:
2170 ui.status(_("skipping missing subrepository: %s\n")
2167 ui.status(_("skipping missing subrepository: %s\n")
2171 % m.abs(subpath))
2168 % m.abs(subpath))
2172
2169
2173 return ret
2170 return ret
2174
2171
2175 def remove(ui, repo, m, prefix, after, force, subrepos, dryrun, warnings=None):
2172 def remove(ui, repo, m, prefix, after, force, subrepos, dryrun, warnings=None):
2176 join = lambda f: os.path.join(prefix, f)
2173 join = lambda f: os.path.join(prefix, f)
2177 ret = 0
2174 ret = 0
2178 s = repo.status(match=m, clean=True)
2175 s = repo.status(match=m, clean=True)
2179 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2176 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2180
2177
2181 wctx = repo[None]
2178 wctx = repo[None]
2182
2179
2183 if warnings is None:
2180 if warnings is None:
2184 warnings = []
2181 warnings = []
2185 warn = True
2182 warn = True
2186 else:
2183 else:
2187 warn = False
2184 warn = False
2188
2185
2189 subs = sorted(wctx.substate)
2186 subs = sorted(wctx.substate)
2190 progress = ui.makeprogress(_('searching'), total=len(subs),
2187 progress = ui.makeprogress(_('searching'), total=len(subs),
2191 unit=_('subrepos'))
2188 unit=_('subrepos'))
2192 for subpath in subs:
2189 for subpath in subs:
2193 submatch = matchmod.subdirmatcher(subpath, m)
2190 submatch = matchmod.subdirmatcher(subpath, m)
2194 if subrepos or m.exact(subpath) or any(submatch.files()):
2191 if subrepos or m.exact(subpath) or any(submatch.files()):
2195 progress.increment()
2192 progress.increment()
2196 sub = wctx.sub(subpath)
2193 sub = wctx.sub(subpath)
2197 try:
2194 try:
2198 if sub.removefiles(submatch, prefix, after, force, subrepos,
2195 if sub.removefiles(submatch, prefix, after, force, subrepos,
2199 dryrun, warnings):
2196 dryrun, warnings):
2200 ret = 1
2197 ret = 1
2201 except error.LookupError:
2198 except error.LookupError:
2202 warnings.append(_("skipping missing subrepository: %s\n")
2199 warnings.append(_("skipping missing subrepository: %s\n")
2203 % join(subpath))
2200 % join(subpath))
2204 progress.complete()
2201 progress.complete()
2205
2202
2206 # warn about failure to delete explicit files/dirs
2203 # warn about failure to delete explicit files/dirs
2207 deleteddirs = util.dirs(deleted)
2204 deleteddirs = util.dirs(deleted)
2208 files = m.files()
2205 files = m.files()
2209 progress = ui.makeprogress(_('deleting'), total=len(files),
2206 progress = ui.makeprogress(_('deleting'), total=len(files),
2210 unit=_('files'))
2207 unit=_('files'))
2211 for f in files:
2208 for f in files:
2212 def insubrepo():
2209 def insubrepo():
2213 for subpath in wctx.substate:
2210 for subpath in wctx.substate:
2214 if f.startswith(subpath + '/'):
2211 if f.startswith(subpath + '/'):
2215 return True
2212 return True
2216 return False
2213 return False
2217
2214
2218 progress.increment()
2215 progress.increment()
2219 isdir = f in deleteddirs or wctx.hasdir(f)
2216 isdir = f in deleteddirs or wctx.hasdir(f)
2220 if (f in repo.dirstate or isdir or f == '.'
2217 if (f in repo.dirstate or isdir or f == '.'
2221 or insubrepo() or f in subs):
2218 or insubrepo() or f in subs):
2222 continue
2219 continue
2223
2220
2224 if repo.wvfs.exists(f):
2221 if repo.wvfs.exists(f):
2225 if repo.wvfs.isdir(f):
2222 if repo.wvfs.isdir(f):
2226 warnings.append(_('not removing %s: no tracked files\n')
2223 warnings.append(_('not removing %s: no tracked files\n')
2227 % m.rel(f))
2224 % m.rel(f))
2228 else:
2225 else:
2229 warnings.append(_('not removing %s: file is untracked\n')
2226 warnings.append(_('not removing %s: file is untracked\n')
2230 % m.rel(f))
2227 % m.rel(f))
2231 # missing files will generate a warning elsewhere
2228 # missing files will generate a warning elsewhere
2232 ret = 1
2229 ret = 1
2233 progress.complete()
2230 progress.complete()
2234
2231
2235 if force:
2232 if force:
2236 list = modified + deleted + clean + added
2233 list = modified + deleted + clean + added
2237 elif after:
2234 elif after:
2238 list = deleted
2235 list = deleted
2239 remaining = modified + added + clean
2236 remaining = modified + added + clean
2240 progress = ui.makeprogress(_('skipping'), total=len(remaining),
2237 progress = ui.makeprogress(_('skipping'), total=len(remaining),
2241 unit=_('files'))
2238 unit=_('files'))
2242 for f in remaining:
2239 for f in remaining:
2243 progress.increment()
2240 progress.increment()
2244 if ui.verbose or (f in files):
2241 if ui.verbose or (f in files):
2245 warnings.append(_('not removing %s: file still exists\n')
2242 warnings.append(_('not removing %s: file still exists\n')
2246 % m.rel(f))
2243 % m.rel(f))
2247 ret = 1
2244 ret = 1
2248 progress.complete()
2245 progress.complete()
2249 else:
2246 else:
2250 list = deleted + clean
2247 list = deleted + clean
2251 progress = ui.makeprogress(_('skipping'),
2248 progress = ui.makeprogress(_('skipping'),
2252 total=(len(modified) + len(added)),
2249 total=(len(modified) + len(added)),
2253 unit=_('files'))
2250 unit=_('files'))
2254 for f in modified:
2251 for f in modified:
2255 progress.increment()
2252 progress.increment()
2256 warnings.append(_('not removing %s: file is modified (use -f'
2253 warnings.append(_('not removing %s: file is modified (use -f'
2257 ' to force removal)\n') % m.rel(f))
2254 ' to force removal)\n') % m.rel(f))
2258 ret = 1
2255 ret = 1
2259 for f in added:
2256 for f in added:
2260 progress.increment()
2257 progress.increment()
2261 warnings.append(_("not removing %s: file has been marked for add"
2258 warnings.append(_("not removing %s: file has been marked for add"
2262 " (use 'hg forget' to undo add)\n") % m.rel(f))
2259 " (use 'hg forget' to undo add)\n") % m.rel(f))
2263 ret = 1
2260 ret = 1
2264 progress.complete()
2261 progress.complete()
2265
2262
2266 list = sorted(list)
2263 list = sorted(list)
2267 progress = ui.makeprogress(_('deleting'), total=len(list),
2264 progress = ui.makeprogress(_('deleting'), total=len(list),
2268 unit=_('files'))
2265 unit=_('files'))
2269 for f in list:
2266 for f in list:
2270 if ui.verbose or not m.exact(f):
2267 if ui.verbose or not m.exact(f):
2271 progress.increment()
2268 progress.increment()
2272 ui.status(_('removing %s\n') % m.rel(f))
2269 ui.status(_('removing %s\n') % m.rel(f))
2273 progress.complete()
2270 progress.complete()
2274
2271
2275 if not dryrun:
2272 if not dryrun:
2276 with repo.wlock():
2273 with repo.wlock():
2277 if not after:
2274 if not after:
2278 for f in list:
2275 for f in list:
2279 if f in added:
2276 if f in added:
2280 continue # we never unlink added files on remove
2277 continue # we never unlink added files on remove
2281 rmdir = repo.ui.configbool('experimental',
2278 rmdir = repo.ui.configbool('experimental',
2282 'removeemptydirs')
2279 'removeemptydirs')
2283 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2280 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2284 repo[None].forget(list)
2281 repo[None].forget(list)
2285
2282
2286 if warn:
2283 if warn:
2287 for warning in warnings:
2284 for warning in warnings:
2288 ui.warn(warning)
2285 ui.warn(warning)
2289
2286
2290 return ret
2287 return ret
2291
2288
2292 def _updatecatformatter(fm, ctx, matcher, path, decode):
2289 def _updatecatformatter(fm, ctx, matcher, path, decode):
2293 """Hook for adding data to the formatter used by ``hg cat``.
2290 """Hook for adding data to the formatter used by ``hg cat``.
2294
2291
2295 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2292 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2296 this method first."""
2293 this method first."""
2297 data = ctx[path].data()
2294 data = ctx[path].data()
2298 if decode:
2295 if decode:
2299 data = ctx.repo().wwritedata(path, data)
2296 data = ctx.repo().wwritedata(path, data)
2300 fm.startitem()
2297 fm.startitem()
2301 fm.context(ctx=ctx)
2298 fm.context(ctx=ctx)
2302 fm.write('data', '%s', data)
2299 fm.write('data', '%s', data)
2303 fm.data(abspath=path, path=matcher.rel(path))
2300 fm.data(abspath=path, path=matcher.rel(path))
2304
2301
2305 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2302 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2306 err = 1
2303 err = 1
2307 opts = pycompat.byteskwargs(opts)
2304 opts = pycompat.byteskwargs(opts)
2308
2305
2309 def write(path):
2306 def write(path):
2310 filename = None
2307 filename = None
2311 if fntemplate:
2308 if fntemplate:
2312 filename = makefilename(ctx, fntemplate,
2309 filename = makefilename(ctx, fntemplate,
2313 pathname=os.path.join(prefix, path))
2310 pathname=os.path.join(prefix, path))
2314 # attempt to create the directory if it does not already exist
2311 # attempt to create the directory if it does not already exist
2315 try:
2312 try:
2316 os.makedirs(os.path.dirname(filename))
2313 os.makedirs(os.path.dirname(filename))
2317 except OSError:
2314 except OSError:
2318 pass
2315 pass
2319 with formatter.maybereopen(basefm, filename) as fm:
2316 with formatter.maybereopen(basefm, filename) as fm:
2320 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
2317 _updatecatformatter(fm, ctx, matcher, path, opts.get('decode'))
2321
2318
2322 # Automation often uses hg cat on single files, so special case it
2319 # Automation often uses hg cat on single files, so special case it
2323 # for performance to avoid the cost of parsing the manifest.
2320 # for performance to avoid the cost of parsing the manifest.
2324 if len(matcher.files()) == 1 and not matcher.anypats():
2321 if len(matcher.files()) == 1 and not matcher.anypats():
2325 file = matcher.files()[0]
2322 file = matcher.files()[0]
2326 mfl = repo.manifestlog
2323 mfl = repo.manifestlog
2327 mfnode = ctx.manifestnode()
2324 mfnode = ctx.manifestnode()
2328 try:
2325 try:
2329 if mfnode and mfl[mfnode].find(file)[0]:
2326 if mfnode and mfl[mfnode].find(file)[0]:
2330 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2327 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2331 write(file)
2328 write(file)
2332 return 0
2329 return 0
2333 except KeyError:
2330 except KeyError:
2334 pass
2331 pass
2335
2332
2336 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2333 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2337
2334
2338 for abs in ctx.walk(matcher):
2335 for abs in ctx.walk(matcher):
2339 write(abs)
2336 write(abs)
2340 err = 0
2337 err = 0
2341
2338
2342 for subpath in sorted(ctx.substate):
2339 for subpath in sorted(ctx.substate):
2343 sub = ctx.sub(subpath)
2340 sub = ctx.sub(subpath)
2344 try:
2341 try:
2345 submatch = matchmod.subdirmatcher(subpath, matcher)
2342 submatch = matchmod.subdirmatcher(subpath, matcher)
2346
2343
2347 if not sub.cat(submatch, basefm, fntemplate,
2344 if not sub.cat(submatch, basefm, fntemplate,
2348 os.path.join(prefix, sub._path),
2345 os.path.join(prefix, sub._path),
2349 **pycompat.strkwargs(opts)):
2346 **pycompat.strkwargs(opts)):
2350 err = 0
2347 err = 0
2351 except error.RepoLookupError:
2348 except error.RepoLookupError:
2352 ui.status(_("skipping missing subrepository: %s\n")
2349 ui.status(_("skipping missing subrepository: %s\n")
2353 % os.path.join(prefix, subpath))
2350 % os.path.join(prefix, subpath))
2354
2351
2355 return err
2352 return err
2356
2353
2357 def commit(ui, repo, commitfunc, pats, opts):
2354 def commit(ui, repo, commitfunc, pats, opts):
2358 '''commit the specified files or all outstanding changes'''
2355 '''commit the specified files or all outstanding changes'''
2359 date = opts.get('date')
2356 date = opts.get('date')
2360 if date:
2357 if date:
2361 opts['date'] = dateutil.parsedate(date)
2358 opts['date'] = dateutil.parsedate(date)
2362 message = logmessage(ui, opts)
2359 message = logmessage(ui, opts)
2363 matcher = scmutil.match(repo[None], pats, opts)
2360 matcher = scmutil.match(repo[None], pats, opts)
2364
2361
2365 dsguard = None
2362 dsguard = None
2366 # extract addremove carefully -- this function can be called from a command
2363 # extract addremove carefully -- this function can be called from a command
2367 # that doesn't support addremove
2364 # that doesn't support addremove
2368 if opts.get('addremove'):
2365 if opts.get('addremove'):
2369 dsguard = dirstateguard.dirstateguard(repo, 'commit')
2366 dsguard = dirstateguard.dirstateguard(repo, 'commit')
2370 with dsguard or util.nullcontextmanager():
2367 with dsguard or util.nullcontextmanager():
2371 if dsguard:
2368 if dsguard:
2372 if scmutil.addremove(repo, matcher, "", opts) != 0:
2369 if scmutil.addremove(repo, matcher, "", opts) != 0:
2373 raise error.Abort(
2370 raise error.Abort(
2374 _("failed to mark all new/missing files as added/removed"))
2371 _("failed to mark all new/missing files as added/removed"))
2375
2372
2376 return commitfunc(ui, repo, message, matcher, opts)
2373 return commitfunc(ui, repo, message, matcher, opts)
2377
2374
2378 def samefile(f, ctx1, ctx2):
2375 def samefile(f, ctx1, ctx2):
2379 if f in ctx1.manifest():
2376 if f in ctx1.manifest():
2380 a = ctx1.filectx(f)
2377 a = ctx1.filectx(f)
2381 if f in ctx2.manifest():
2378 if f in ctx2.manifest():
2382 b = ctx2.filectx(f)
2379 b = ctx2.filectx(f)
2383 return (not a.cmp(b)
2380 return (not a.cmp(b)
2384 and a.flags() == b.flags())
2381 and a.flags() == b.flags())
2385 else:
2382 else:
2386 return False
2383 return False
2387 else:
2384 else:
2388 return f not in ctx2.manifest()
2385 return f not in ctx2.manifest()
2389
2386
2390 def amend(ui, repo, old, extra, pats, opts):
2387 def amend(ui, repo, old, extra, pats, opts):
2391 # avoid cycle context -> subrepo -> cmdutil
2388 # avoid cycle context -> subrepo -> cmdutil
2392 from . import context
2389 from . import context
2393
2390
2394 # amend will reuse the existing user if not specified, but the obsolete
2391 # amend will reuse the existing user if not specified, but the obsolete
2395 # marker creation requires that the current user's name is specified.
2392 # marker creation requires that the current user's name is specified.
2396 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2393 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2397 ui.username() # raise exception if username not set
2394 ui.username() # raise exception if username not set
2398
2395
2399 ui.note(_('amending changeset %s\n') % old)
2396 ui.note(_('amending changeset %s\n') % old)
2400 base = old.p1()
2397 base = old.p1()
2401
2398
2402 with repo.wlock(), repo.lock(), repo.transaction('amend'):
2399 with repo.wlock(), repo.lock(), repo.transaction('amend'):
2403 # Participating changesets:
2400 # Participating changesets:
2404 #
2401 #
2405 # wctx o - workingctx that contains changes from working copy
2402 # wctx o - workingctx that contains changes from working copy
2406 # | to go into amending commit
2403 # | to go into amending commit
2407 # |
2404 # |
2408 # old o - changeset to amend
2405 # old o - changeset to amend
2409 # |
2406 # |
2410 # base o - first parent of the changeset to amend
2407 # base o - first parent of the changeset to amend
2411 wctx = repo[None]
2408 wctx = repo[None]
2412
2409
2413 # Copy to avoid mutating input
2410 # Copy to avoid mutating input
2414 extra = extra.copy()
2411 extra = extra.copy()
2415 # Update extra dict from amended commit (e.g. to preserve graft
2412 # Update extra dict from amended commit (e.g. to preserve graft
2416 # source)
2413 # source)
2417 extra.update(old.extra())
2414 extra.update(old.extra())
2418
2415
2419 # Also update it from the from the wctx
2416 # Also update it from the from the wctx
2420 extra.update(wctx.extra())
2417 extra.update(wctx.extra())
2421
2418
2422 user = opts.get('user') or old.user()
2419 user = opts.get('user') or old.user()
2423 date = opts.get('date') or old.date()
2420 date = opts.get('date') or old.date()
2424
2421
2425 # Parse the date to allow comparison between date and old.date()
2422 # Parse the date to allow comparison between date and old.date()
2426 date = dateutil.parsedate(date)
2423 date = dateutil.parsedate(date)
2427
2424
2428 if len(old.parents()) > 1:
2425 if len(old.parents()) > 1:
2429 # ctx.files() isn't reliable for merges, so fall back to the
2426 # ctx.files() isn't reliable for merges, so fall back to the
2430 # slower repo.status() method
2427 # slower repo.status() method
2431 files = set([fn for st in base.status(old)[:3]
2428 files = set([fn for st in base.status(old)[:3]
2432 for fn in st])
2429 for fn in st])
2433 else:
2430 else:
2434 files = set(old.files())
2431 files = set(old.files())
2435
2432
2436 # add/remove the files to the working copy if the "addremove" option
2433 # add/remove the files to the working copy if the "addremove" option
2437 # was specified.
2434 # was specified.
2438 matcher = scmutil.match(wctx, pats, opts)
2435 matcher = scmutil.match(wctx, pats, opts)
2439 if (opts.get('addremove')
2436 if (opts.get('addremove')
2440 and scmutil.addremove(repo, matcher, "", opts)):
2437 and scmutil.addremove(repo, matcher, "", opts)):
2441 raise error.Abort(
2438 raise error.Abort(
2442 _("failed to mark all new/missing files as added/removed"))
2439 _("failed to mark all new/missing files as added/removed"))
2443
2440
2444 # Check subrepos. This depends on in-place wctx._status update in
2441 # Check subrepos. This depends on in-place wctx._status update in
2445 # subrepo.precommit(). To minimize the risk of this hack, we do
2442 # subrepo.precommit(). To minimize the risk of this hack, we do
2446 # nothing if .hgsub does not exist.
2443 # nothing if .hgsub does not exist.
2447 if '.hgsub' in wctx or '.hgsub' in old:
2444 if '.hgsub' in wctx or '.hgsub' in old:
2448 subs, commitsubs, newsubstate = subrepoutil.precommit(
2445 subs, commitsubs, newsubstate = subrepoutil.precommit(
2449 ui, wctx, wctx._status, matcher)
2446 ui, wctx, wctx._status, matcher)
2450 # amend should abort if commitsubrepos is enabled
2447 # amend should abort if commitsubrepos is enabled
2451 assert not commitsubs
2448 assert not commitsubs
2452 if subs:
2449 if subs:
2453 subrepoutil.writestate(repo, newsubstate)
2450 subrepoutil.writestate(repo, newsubstate)
2454
2451
2455 ms = mergemod.mergestate.read(repo)
2452 ms = mergemod.mergestate.read(repo)
2456 mergeutil.checkunresolved(ms)
2453 mergeutil.checkunresolved(ms)
2457
2454
2458 filestoamend = set(f for f in wctx.files() if matcher(f))
2455 filestoamend = set(f for f in wctx.files() if matcher(f))
2459
2456
2460 changes = (len(filestoamend) > 0)
2457 changes = (len(filestoamend) > 0)
2461 if changes:
2458 if changes:
2462 # Recompute copies (avoid recording a -> b -> a)
2459 # Recompute copies (avoid recording a -> b -> a)
2463 copied = copies.pathcopies(base, wctx, matcher)
2460 copied = copies.pathcopies(base, wctx, matcher)
2464 if old.p2:
2461 if old.p2:
2465 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2462 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2466
2463
2467 # Prune files which were reverted by the updates: if old
2464 # Prune files which were reverted by the updates: if old
2468 # introduced file X and the file was renamed in the working
2465 # introduced file X and the file was renamed in the working
2469 # copy, then those two files are the same and
2466 # copy, then those two files are the same and
2470 # we can discard X from our list of files. Likewise if X
2467 # we can discard X from our list of files. Likewise if X
2471 # was removed, it's no longer relevant. If X is missing (aka
2468 # was removed, it's no longer relevant. If X is missing (aka
2472 # deleted), old X must be preserved.
2469 # deleted), old X must be preserved.
2473 files.update(filestoamend)
2470 files.update(filestoamend)
2474 files = [f for f in files if (not samefile(f, wctx, base)
2471 files = [f for f in files if (not samefile(f, wctx, base)
2475 or f in wctx.deleted())]
2472 or f in wctx.deleted())]
2476
2473
2477 def filectxfn(repo, ctx_, path):
2474 def filectxfn(repo, ctx_, path):
2478 try:
2475 try:
2479 # If the file being considered is not amongst the files
2476 # If the file being considered is not amongst the files
2480 # to be amended, we should return the file context from the
2477 # to be amended, we should return the file context from the
2481 # old changeset. This avoids issues when only some files in
2478 # old changeset. This avoids issues when only some files in
2482 # the working copy are being amended but there are also
2479 # the working copy are being amended but there are also
2483 # changes to other files from the old changeset.
2480 # changes to other files from the old changeset.
2484 if path not in filestoamend:
2481 if path not in filestoamend:
2485 return old.filectx(path)
2482 return old.filectx(path)
2486
2483
2487 # Return None for removed files.
2484 # Return None for removed files.
2488 if path in wctx.removed():
2485 if path in wctx.removed():
2489 return None
2486 return None
2490
2487
2491 fctx = wctx[path]
2488 fctx = wctx[path]
2492 flags = fctx.flags()
2489 flags = fctx.flags()
2493 mctx = context.memfilectx(repo, ctx_,
2490 mctx = context.memfilectx(repo, ctx_,
2494 fctx.path(), fctx.data(),
2491 fctx.path(), fctx.data(),
2495 islink='l' in flags,
2492 islink='l' in flags,
2496 isexec='x' in flags,
2493 isexec='x' in flags,
2497 copied=copied.get(path))
2494 copied=copied.get(path))
2498 return mctx
2495 return mctx
2499 except KeyError:
2496 except KeyError:
2500 return None
2497 return None
2501 else:
2498 else:
2502 ui.note(_('copying changeset %s to %s\n') % (old, base))
2499 ui.note(_('copying changeset %s to %s\n') % (old, base))
2503
2500
2504 # Use version of files as in the old cset
2501 # Use version of files as in the old cset
2505 def filectxfn(repo, ctx_, path):
2502 def filectxfn(repo, ctx_, path):
2506 try:
2503 try:
2507 return old.filectx(path)
2504 return old.filectx(path)
2508 except KeyError:
2505 except KeyError:
2509 return None
2506 return None
2510
2507
2511 # See if we got a message from -m or -l, if not, open the editor with
2508 # See if we got a message from -m or -l, if not, open the editor with
2512 # the message of the changeset to amend.
2509 # the message of the changeset to amend.
2513 message = logmessage(ui, opts)
2510 message = logmessage(ui, opts)
2514
2511
2515 editform = mergeeditform(old, 'commit.amend')
2512 editform = mergeeditform(old, 'commit.amend')
2516 editor = getcommiteditor(editform=editform,
2513 editor = getcommiteditor(editform=editform,
2517 **pycompat.strkwargs(opts))
2514 **pycompat.strkwargs(opts))
2518
2515
2519 if not message:
2516 if not message:
2520 editor = getcommiteditor(edit=True, editform=editform)
2517 editor = getcommiteditor(edit=True, editform=editform)
2521 message = old.description()
2518 message = old.description()
2522
2519
2523 pureextra = extra.copy()
2520 pureextra = extra.copy()
2524 extra['amend_source'] = old.hex()
2521 extra['amend_source'] = old.hex()
2525
2522
2526 new = context.memctx(repo,
2523 new = context.memctx(repo,
2527 parents=[base.node(), old.p2().node()],
2524 parents=[base.node(), old.p2().node()],
2528 text=message,
2525 text=message,
2529 files=files,
2526 files=files,
2530 filectxfn=filectxfn,
2527 filectxfn=filectxfn,
2531 user=user,
2528 user=user,
2532 date=date,
2529 date=date,
2533 extra=extra,
2530 extra=extra,
2534 editor=editor)
2531 editor=editor)
2535
2532
2536 newdesc = changelog.stripdesc(new.description())
2533 newdesc = changelog.stripdesc(new.description())
2537 if ((not changes)
2534 if ((not changes)
2538 and newdesc == old.description()
2535 and newdesc == old.description()
2539 and user == old.user()
2536 and user == old.user()
2540 and date == old.date()
2537 and date == old.date()
2541 and pureextra == old.extra()):
2538 and pureextra == old.extra()):
2542 # nothing changed. continuing here would create a new node
2539 # nothing changed. continuing here would create a new node
2543 # anyway because of the amend_source noise.
2540 # anyway because of the amend_source noise.
2544 #
2541 #
2545 # This not what we expect from amend.
2542 # This not what we expect from amend.
2546 return old.node()
2543 return old.node()
2547
2544
2548 commitphase = None
2545 commitphase = None
2549 if opts.get('secret'):
2546 if opts.get('secret'):
2550 commitphase = phases.secret
2547 commitphase = phases.secret
2551 newid = repo.commitctx(new)
2548 newid = repo.commitctx(new)
2552
2549
2553 # Reroute the working copy parent to the new changeset
2550 # Reroute the working copy parent to the new changeset
2554 repo.setparents(newid, nullid)
2551 repo.setparents(newid, nullid)
2555 mapping = {old.node(): (newid,)}
2552 mapping = {old.node(): (newid,)}
2556 obsmetadata = None
2553 obsmetadata = None
2557 if opts.get('note'):
2554 if opts.get('note'):
2558 obsmetadata = {'note': encoding.fromlocal(opts['note'])}
2555 obsmetadata = {'note': encoding.fromlocal(opts['note'])}
2559 backup = ui.configbool('ui', 'history-editing-backup')
2556 backup = ui.configbool('ui', 'history-editing-backup')
2560 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata,
2557 scmutil.cleanupnodes(repo, mapping, 'amend', metadata=obsmetadata,
2561 fixphase=True, targetphase=commitphase,
2558 fixphase=True, targetphase=commitphase,
2562 backup=backup)
2559 backup=backup)
2563
2560
2564 # Fixing the dirstate because localrepo.commitctx does not update
2561 # Fixing the dirstate because localrepo.commitctx does not update
2565 # it. This is rather convenient because we did not need to update
2562 # it. This is rather convenient because we did not need to update
2566 # the dirstate for all the files in the new commit which commitctx
2563 # the dirstate for all the files in the new commit which commitctx
2567 # could have done if it updated the dirstate. Now, we can
2564 # could have done if it updated the dirstate. Now, we can
2568 # selectively update the dirstate only for the amended files.
2565 # selectively update the dirstate only for the amended files.
2569 dirstate = repo.dirstate
2566 dirstate = repo.dirstate
2570
2567
2571 # Update the state of the files which were added and
2568 # Update the state of the files which were added and
2572 # and modified in the amend to "normal" in the dirstate.
2569 # and modified in the amend to "normal" in the dirstate.
2573 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2570 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2574 for f in normalfiles:
2571 for f in normalfiles:
2575 dirstate.normal(f)
2572 dirstate.normal(f)
2576
2573
2577 # Update the state of files which were removed in the amend
2574 # Update the state of files which were removed in the amend
2578 # to "removed" in the dirstate.
2575 # to "removed" in the dirstate.
2579 removedfiles = set(wctx.removed()) & filestoamend
2576 removedfiles = set(wctx.removed()) & filestoamend
2580 for f in removedfiles:
2577 for f in removedfiles:
2581 dirstate.drop(f)
2578 dirstate.drop(f)
2582
2579
2583 return newid
2580 return newid
2584
2581
2585 def commiteditor(repo, ctx, subs, editform=''):
2582 def commiteditor(repo, ctx, subs, editform=''):
2586 if ctx.description():
2583 if ctx.description():
2587 return ctx.description()
2584 return ctx.description()
2588 return commitforceeditor(repo, ctx, subs, editform=editform,
2585 return commitforceeditor(repo, ctx, subs, editform=editform,
2589 unchangedmessagedetection=True)
2586 unchangedmessagedetection=True)
2590
2587
2591 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2588 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2592 editform='', unchangedmessagedetection=False):
2589 editform='', unchangedmessagedetection=False):
2593 if not extramsg:
2590 if not extramsg:
2594 extramsg = _("Leave message empty to abort commit.")
2591 extramsg = _("Leave message empty to abort commit.")
2595
2592
2596 forms = [e for e in editform.split('.') if e]
2593 forms = [e for e in editform.split('.') if e]
2597 forms.insert(0, 'changeset')
2594 forms.insert(0, 'changeset')
2598 templatetext = None
2595 templatetext = None
2599 while forms:
2596 while forms:
2600 ref = '.'.join(forms)
2597 ref = '.'.join(forms)
2601 if repo.ui.config('committemplate', ref):
2598 if repo.ui.config('committemplate', ref):
2602 templatetext = committext = buildcommittemplate(
2599 templatetext = committext = buildcommittemplate(
2603 repo, ctx, subs, extramsg, ref)
2600 repo, ctx, subs, extramsg, ref)
2604 break
2601 break
2605 forms.pop()
2602 forms.pop()
2606 else:
2603 else:
2607 committext = buildcommittext(repo, ctx, subs, extramsg)
2604 committext = buildcommittext(repo, ctx, subs, extramsg)
2608
2605
2609 # run editor in the repository root
2606 # run editor in the repository root
2610 olddir = pycompat.getcwd()
2607 olddir = pycompat.getcwd()
2611 os.chdir(repo.root)
2608 os.chdir(repo.root)
2612
2609
2613 # make in-memory changes visible to external process
2610 # make in-memory changes visible to external process
2614 tr = repo.currenttransaction()
2611 tr = repo.currenttransaction()
2615 repo.dirstate.write(tr)
2612 repo.dirstate.write(tr)
2616 pending = tr and tr.writepending() and repo.root
2613 pending = tr and tr.writepending() and repo.root
2617
2614
2618 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2615 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
2619 editform=editform, pending=pending,
2616 editform=editform, pending=pending,
2620 repopath=repo.path, action='commit')
2617 repopath=repo.path, action='commit')
2621 text = editortext
2618 text = editortext
2622
2619
2623 # strip away anything below this special string (used for editors that want
2620 # strip away anything below this special string (used for editors that want
2624 # to display the diff)
2621 # to display the diff)
2625 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2622 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
2626 if stripbelow:
2623 if stripbelow:
2627 text = text[:stripbelow.start()]
2624 text = text[:stripbelow.start()]
2628
2625
2629 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2626 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2630 os.chdir(olddir)
2627 os.chdir(olddir)
2631
2628
2632 if finishdesc:
2629 if finishdesc:
2633 text = finishdesc(text)
2630 text = finishdesc(text)
2634 if not text.strip():
2631 if not text.strip():
2635 raise error.Abort(_("empty commit message"))
2632 raise error.Abort(_("empty commit message"))
2636 if unchangedmessagedetection and editortext == templatetext:
2633 if unchangedmessagedetection and editortext == templatetext:
2637 raise error.Abort(_("commit message unchanged"))
2634 raise error.Abort(_("commit message unchanged"))
2638
2635
2639 return text
2636 return text
2640
2637
2641 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
2638 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
2642 ui = repo.ui
2639 ui = repo.ui
2643 spec = formatter.templatespec(ref, None, None)
2640 spec = formatter.templatespec(ref, None, None)
2644 t = logcmdutil.changesettemplater(ui, repo, spec)
2641 t = logcmdutil.changesettemplater(ui, repo, spec)
2645 t.t.cache.update((k, templater.unquotestring(v))
2642 t.t.cache.update((k, templater.unquotestring(v))
2646 for k, v in repo.ui.configitems('committemplate'))
2643 for k, v in repo.ui.configitems('committemplate'))
2647
2644
2648 if not extramsg:
2645 if not extramsg:
2649 extramsg = '' # ensure that extramsg is string
2646 extramsg = '' # ensure that extramsg is string
2650
2647
2651 ui.pushbuffer()
2648 ui.pushbuffer()
2652 t.show(ctx, extramsg=extramsg)
2649 t.show(ctx, extramsg=extramsg)
2653 return ui.popbuffer()
2650 return ui.popbuffer()
2654
2651
2655 def hgprefix(msg):
2652 def hgprefix(msg):
2656 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2653 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2657
2654
2658 def buildcommittext(repo, ctx, subs, extramsg):
2655 def buildcommittext(repo, ctx, subs, extramsg):
2659 edittext = []
2656 edittext = []
2660 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2657 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2661 if ctx.description():
2658 if ctx.description():
2662 edittext.append(ctx.description())
2659 edittext.append(ctx.description())
2663 edittext.append("")
2660 edittext.append("")
2664 edittext.append("") # Empty line between message and comments.
2661 edittext.append("") # Empty line between message and comments.
2665 edittext.append(hgprefix(_("Enter commit message."
2662 edittext.append(hgprefix(_("Enter commit message."
2666 " Lines beginning with 'HG:' are removed.")))
2663 " Lines beginning with 'HG:' are removed.")))
2667 edittext.append(hgprefix(extramsg))
2664 edittext.append(hgprefix(extramsg))
2668 edittext.append("HG: --")
2665 edittext.append("HG: --")
2669 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2666 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2670 if ctx.p2():
2667 if ctx.p2():
2671 edittext.append(hgprefix(_("branch merge")))
2668 edittext.append(hgprefix(_("branch merge")))
2672 if ctx.branch():
2669 if ctx.branch():
2673 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2670 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2674 if bookmarks.isactivewdirparent(repo):
2671 if bookmarks.isactivewdirparent(repo):
2675 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2672 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2676 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2673 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2677 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2674 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2678 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2675 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2679 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2676 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2680 if not added and not modified and not removed:
2677 if not added and not modified and not removed:
2681 edittext.append(hgprefix(_("no files changed")))
2678 edittext.append(hgprefix(_("no files changed")))
2682 edittext.append("")
2679 edittext.append("")
2683
2680
2684 return "\n".join(edittext)
2681 return "\n".join(edittext)
2685
2682
2686 def commitstatus(repo, node, branch, bheads=None, opts=None):
2683 def commitstatus(repo, node, branch, bheads=None, opts=None):
2687 if opts is None:
2684 if opts is None:
2688 opts = {}
2685 opts = {}
2689 ctx = repo[node]
2686 ctx = repo[node]
2690 parents = ctx.parents()
2687 parents = ctx.parents()
2691
2688
2692 if (not opts.get('amend') and bheads and node not in bheads and not
2689 if (not opts.get('amend') and bheads and node not in bheads and not
2693 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2690 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2694 repo.ui.status(_('created new head\n'))
2691 repo.ui.status(_('created new head\n'))
2695 # The message is not printed for initial roots. For the other
2692 # The message is not printed for initial roots. For the other
2696 # changesets, it is printed in the following situations:
2693 # changesets, it is printed in the following situations:
2697 #
2694 #
2698 # Par column: for the 2 parents with ...
2695 # Par column: for the 2 parents with ...
2699 # N: null or no parent
2696 # N: null or no parent
2700 # B: parent is on another named branch
2697 # B: parent is on another named branch
2701 # C: parent is a regular non head changeset
2698 # C: parent is a regular non head changeset
2702 # H: parent was a branch head of the current branch
2699 # H: parent was a branch head of the current branch
2703 # Msg column: whether we print "created new head" message
2700 # Msg column: whether we print "created new head" message
2704 # In the following, it is assumed that there already exists some
2701 # In the following, it is assumed that there already exists some
2705 # initial branch heads of the current branch, otherwise nothing is
2702 # initial branch heads of the current branch, otherwise nothing is
2706 # printed anyway.
2703 # printed anyway.
2707 #
2704 #
2708 # Par Msg Comment
2705 # Par Msg Comment
2709 # N N y additional topo root
2706 # N N y additional topo root
2710 #
2707 #
2711 # B N y additional branch root
2708 # B N y additional branch root
2712 # C N y additional topo head
2709 # C N y additional topo head
2713 # H N n usual case
2710 # H N n usual case
2714 #
2711 #
2715 # B B y weird additional branch root
2712 # B B y weird additional branch root
2716 # C B y branch merge
2713 # C B y branch merge
2717 # H B n merge with named branch
2714 # H B n merge with named branch
2718 #
2715 #
2719 # C C y additional head from merge
2716 # C C y additional head from merge
2720 # C H n merge with a head
2717 # C H n merge with a head
2721 #
2718 #
2722 # H H n head merge: head count decreases
2719 # H H n head merge: head count decreases
2723
2720
2724 if not opts.get('close_branch'):
2721 if not opts.get('close_branch'):
2725 for r in parents:
2722 for r in parents:
2726 if r.closesbranch() and r.branch() == branch:
2723 if r.closesbranch() and r.branch() == branch:
2727 repo.ui.status(_('reopening closed branch head %d\n') % r.rev())
2724 repo.ui.status(_('reopening closed branch head %d\n') % r.rev())
2728
2725
2729 if repo.ui.debugflag:
2726 if repo.ui.debugflag:
2730 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx.hex()))
2727 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx.hex()))
2731 elif repo.ui.verbose:
2728 elif repo.ui.verbose:
2732 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx))
2729 repo.ui.write(_('committed changeset %d:%s\n') % (ctx.rev(), ctx))
2733
2730
2734 def postcommitstatus(repo, pats, opts):
2731 def postcommitstatus(repo, pats, opts):
2735 return repo.status(match=scmutil.match(repo[None], pats, opts))
2732 return repo.status(match=scmutil.match(repo[None], pats, opts))
2736
2733
2737 def revert(ui, repo, ctx, parents, *pats, **opts):
2734 def revert(ui, repo, ctx, parents, *pats, **opts):
2738 opts = pycompat.byteskwargs(opts)
2735 opts = pycompat.byteskwargs(opts)
2739 parent, p2 = parents
2736 parent, p2 = parents
2740 node = ctx.node()
2737 node = ctx.node()
2741
2738
2742 mf = ctx.manifest()
2739 mf = ctx.manifest()
2743 if node == p2:
2740 if node == p2:
2744 parent = p2
2741 parent = p2
2745
2742
2746 # need all matching names in dirstate and manifest of target rev,
2743 # need all matching names in dirstate and manifest of target rev,
2747 # so have to walk both. do not print errors if files exist in one
2744 # so have to walk both. do not print errors if files exist in one
2748 # but not other. in both cases, filesets should be evaluated against
2745 # but not other. in both cases, filesets should be evaluated against
2749 # workingctx to get consistent result (issue4497). this means 'set:**'
2746 # workingctx to get consistent result (issue4497). this means 'set:**'
2750 # cannot be used to select missing files from target rev.
2747 # cannot be used to select missing files from target rev.
2751
2748
2752 # `names` is a mapping for all elements in working copy and target revision
2749 # `names` is a mapping for all elements in working copy and target revision
2753 # The mapping is in the form:
2750 # The mapping is in the form:
2754 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2751 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2755 names = {}
2752 names = {}
2756
2753
2757 with repo.wlock():
2754 with repo.wlock():
2758 ## filling of the `names` mapping
2755 ## filling of the `names` mapping
2759 # walk dirstate to fill `names`
2756 # walk dirstate to fill `names`
2760
2757
2761 interactive = opts.get('interactive', False)
2758 interactive = opts.get('interactive', False)
2762 wctx = repo[None]
2759 wctx = repo[None]
2763 m = scmutil.match(wctx, pats, opts)
2760 m = scmutil.match(wctx, pats, opts)
2764
2761
2765 # we'll need this later
2762 # we'll need this later
2766 targetsubs = sorted(s for s in wctx.substate if m(s))
2763 targetsubs = sorted(s for s in wctx.substate if m(s))
2767
2764
2768 if not m.always():
2765 if not m.always():
2769 matcher = matchmod.badmatch(m, lambda x, y: False)
2766 matcher = matchmod.badmatch(m, lambda x, y: False)
2770 for abs in wctx.walk(matcher):
2767 for abs in wctx.walk(matcher):
2771 names[abs] = m.rel(abs), m.exact(abs)
2768 names[abs] = m.rel(abs), m.exact(abs)
2772
2769
2773 # walk target manifest to fill `names`
2770 # walk target manifest to fill `names`
2774
2771
2775 def badfn(path, msg):
2772 def badfn(path, msg):
2776 if path in names:
2773 if path in names:
2777 return
2774 return
2778 if path in ctx.substate:
2775 if path in ctx.substate:
2779 return
2776 return
2780 path_ = path + '/'
2777 path_ = path + '/'
2781 for f in names:
2778 for f in names:
2782 if f.startswith(path_):
2779 if f.startswith(path_):
2783 return
2780 return
2784 ui.warn("%s: %s\n" % (m.rel(path), msg))
2781 ui.warn("%s: %s\n" % (m.rel(path), msg))
2785
2782
2786 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2783 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2787 if abs not in names:
2784 if abs not in names:
2788 names[abs] = m.rel(abs), m.exact(abs)
2785 names[abs] = m.rel(abs), m.exact(abs)
2789
2786
2790 # Find status of all file in `names`.
2787 # Find status of all file in `names`.
2791 m = scmutil.matchfiles(repo, names)
2788 m = scmutil.matchfiles(repo, names)
2792
2789
2793 changes = repo.status(node1=node, match=m,
2790 changes = repo.status(node1=node, match=m,
2794 unknown=True, ignored=True, clean=True)
2791 unknown=True, ignored=True, clean=True)
2795 else:
2792 else:
2796 changes = repo.status(node1=node, match=m)
2793 changes = repo.status(node1=node, match=m)
2797 for kind in changes:
2794 for kind in changes:
2798 for abs in kind:
2795 for abs in kind:
2799 names[abs] = m.rel(abs), m.exact(abs)
2796 names[abs] = m.rel(abs), m.exact(abs)
2800
2797
2801 m = scmutil.matchfiles(repo, names)
2798 m = scmutil.matchfiles(repo, names)
2802
2799
2803 modified = set(changes.modified)
2800 modified = set(changes.modified)
2804 added = set(changes.added)
2801 added = set(changes.added)
2805 removed = set(changes.removed)
2802 removed = set(changes.removed)
2806 _deleted = set(changes.deleted)
2803 _deleted = set(changes.deleted)
2807 unknown = set(changes.unknown)
2804 unknown = set(changes.unknown)
2808 unknown.update(changes.ignored)
2805 unknown.update(changes.ignored)
2809 clean = set(changes.clean)
2806 clean = set(changes.clean)
2810 modadded = set()
2807 modadded = set()
2811
2808
2812 # We need to account for the state of the file in the dirstate,
2809 # We need to account for the state of the file in the dirstate,
2813 # even when we revert against something else than parent. This will
2810 # even when we revert against something else than parent. This will
2814 # slightly alter the behavior of revert (doing back up or not, delete
2811 # slightly alter the behavior of revert (doing back up or not, delete
2815 # or just forget etc).
2812 # or just forget etc).
2816 if parent == node:
2813 if parent == node:
2817 dsmodified = modified
2814 dsmodified = modified
2818 dsadded = added
2815 dsadded = added
2819 dsremoved = removed
2816 dsremoved = removed
2820 # store all local modifications, useful later for rename detection
2817 # store all local modifications, useful later for rename detection
2821 localchanges = dsmodified | dsadded
2818 localchanges = dsmodified | dsadded
2822 modified, added, removed = set(), set(), set()
2819 modified, added, removed = set(), set(), set()
2823 else:
2820 else:
2824 changes = repo.status(node1=parent, match=m)
2821 changes = repo.status(node1=parent, match=m)
2825 dsmodified = set(changes.modified)
2822 dsmodified = set(changes.modified)
2826 dsadded = set(changes.added)
2823 dsadded = set(changes.added)
2827 dsremoved = set(changes.removed)
2824 dsremoved = set(changes.removed)
2828 # store all local modifications, useful later for rename detection
2825 # store all local modifications, useful later for rename detection
2829 localchanges = dsmodified | dsadded
2826 localchanges = dsmodified | dsadded
2830
2827
2831 # only take into account for removes between wc and target
2828 # only take into account for removes between wc and target
2832 clean |= dsremoved - removed
2829 clean |= dsremoved - removed
2833 dsremoved &= removed
2830 dsremoved &= removed
2834 # distinct between dirstate remove and other
2831 # distinct between dirstate remove and other
2835 removed -= dsremoved
2832 removed -= dsremoved
2836
2833
2837 modadded = added & dsmodified
2834 modadded = added & dsmodified
2838 added -= modadded
2835 added -= modadded
2839
2836
2840 # tell newly modified apart.
2837 # tell newly modified apart.
2841 dsmodified &= modified
2838 dsmodified &= modified
2842 dsmodified |= modified & dsadded # dirstate added may need backup
2839 dsmodified |= modified & dsadded # dirstate added may need backup
2843 modified -= dsmodified
2840 modified -= dsmodified
2844
2841
2845 # We need to wait for some post-processing to update this set
2842 # We need to wait for some post-processing to update this set
2846 # before making the distinction. The dirstate will be used for
2843 # before making the distinction. The dirstate will be used for
2847 # that purpose.
2844 # that purpose.
2848 dsadded = added
2845 dsadded = added
2849
2846
2850 # in case of merge, files that are actually added can be reported as
2847 # in case of merge, files that are actually added can be reported as
2851 # modified, we need to post process the result
2848 # modified, we need to post process the result
2852 if p2 != nullid:
2849 if p2 != nullid:
2853 mergeadd = set(dsmodified)
2850 mergeadd = set(dsmodified)
2854 for path in dsmodified:
2851 for path in dsmodified:
2855 if path in mf:
2852 if path in mf:
2856 mergeadd.remove(path)
2853 mergeadd.remove(path)
2857 dsadded |= mergeadd
2854 dsadded |= mergeadd
2858 dsmodified -= mergeadd
2855 dsmodified -= mergeadd
2859
2856
2860 # if f is a rename, update `names` to also revert the source
2857 # if f is a rename, update `names` to also revert the source
2861 cwd = repo.getcwd()
2858 cwd = repo.getcwd()
2862 for f in localchanges:
2859 for f in localchanges:
2863 src = repo.dirstate.copied(f)
2860 src = repo.dirstate.copied(f)
2864 # XXX should we check for rename down to target node?
2861 # XXX should we check for rename down to target node?
2865 if src and src not in names and repo.dirstate[src] == 'r':
2862 if src and src not in names and repo.dirstate[src] == 'r':
2866 dsremoved.add(src)
2863 dsremoved.add(src)
2867 names[src] = (repo.pathto(src, cwd), True)
2864 names[src] = (repo.pathto(src, cwd), True)
2868
2865
2869 # determine the exact nature of the deleted changesets
2866 # determine the exact nature of the deleted changesets
2870 deladded = set(_deleted)
2867 deladded = set(_deleted)
2871 for path in _deleted:
2868 for path in _deleted:
2872 if path in mf:
2869 if path in mf:
2873 deladded.remove(path)
2870 deladded.remove(path)
2874 deleted = _deleted - deladded
2871 deleted = _deleted - deladded
2875
2872
2876 # distinguish between file to forget and the other
2873 # distinguish between file to forget and the other
2877 added = set()
2874 added = set()
2878 for abs in dsadded:
2875 for abs in dsadded:
2879 if repo.dirstate[abs] != 'a':
2876 if repo.dirstate[abs] != 'a':
2880 added.add(abs)
2877 added.add(abs)
2881 dsadded -= added
2878 dsadded -= added
2882
2879
2883 for abs in deladded:
2880 for abs in deladded:
2884 if repo.dirstate[abs] == 'a':
2881 if repo.dirstate[abs] == 'a':
2885 dsadded.add(abs)
2882 dsadded.add(abs)
2886 deladded -= dsadded
2883 deladded -= dsadded
2887
2884
2888 # For files marked as removed, we check if an unknown file is present at
2885 # For files marked as removed, we check if an unknown file is present at
2889 # the same path. If a such file exists it may need to be backed up.
2886 # the same path. If a such file exists it may need to be backed up.
2890 # Making the distinction at this stage helps have simpler backup
2887 # Making the distinction at this stage helps have simpler backup
2891 # logic.
2888 # logic.
2892 removunk = set()
2889 removunk = set()
2893 for abs in removed:
2890 for abs in removed:
2894 target = repo.wjoin(abs)
2891 target = repo.wjoin(abs)
2895 if os.path.lexists(target):
2892 if os.path.lexists(target):
2896 removunk.add(abs)
2893 removunk.add(abs)
2897 removed -= removunk
2894 removed -= removunk
2898
2895
2899 dsremovunk = set()
2896 dsremovunk = set()
2900 for abs in dsremoved:
2897 for abs in dsremoved:
2901 target = repo.wjoin(abs)
2898 target = repo.wjoin(abs)
2902 if os.path.lexists(target):
2899 if os.path.lexists(target):
2903 dsremovunk.add(abs)
2900 dsremovunk.add(abs)
2904 dsremoved -= dsremovunk
2901 dsremoved -= dsremovunk
2905
2902
2906 # action to be actually performed by revert
2903 # action to be actually performed by revert
2907 # (<list of file>, message>) tuple
2904 # (<list of file>, message>) tuple
2908 actions = {'revert': ([], _('reverting %s\n')),
2905 actions = {'revert': ([], _('reverting %s\n')),
2909 'add': ([], _('adding %s\n')),
2906 'add': ([], _('adding %s\n')),
2910 'remove': ([], _('removing %s\n')),
2907 'remove': ([], _('removing %s\n')),
2911 'drop': ([], _('removing %s\n')),
2908 'drop': ([], _('removing %s\n')),
2912 'forget': ([], _('forgetting %s\n')),
2909 'forget': ([], _('forgetting %s\n')),
2913 'undelete': ([], _('undeleting %s\n')),
2910 'undelete': ([], _('undeleting %s\n')),
2914 'noop': (None, _('no changes needed to %s\n')),
2911 'noop': (None, _('no changes needed to %s\n')),
2915 'unknown': (None, _('file not managed: %s\n')),
2912 'unknown': (None, _('file not managed: %s\n')),
2916 }
2913 }
2917
2914
2918 # "constant" that convey the backup strategy.
2915 # "constant" that convey the backup strategy.
2919 # All set to `discard` if `no-backup` is set do avoid checking
2916 # All set to `discard` if `no-backup` is set do avoid checking
2920 # no_backup lower in the code.
2917 # no_backup lower in the code.
2921 # These values are ordered for comparison purposes
2918 # These values are ordered for comparison purposes
2922 backupinteractive = 3 # do backup if interactively modified
2919 backupinteractive = 3 # do backup if interactively modified
2923 backup = 2 # unconditionally do backup
2920 backup = 2 # unconditionally do backup
2924 check = 1 # check if the existing file differs from target
2921 check = 1 # check if the existing file differs from target
2925 discard = 0 # never do backup
2922 discard = 0 # never do backup
2926 if opts.get('no_backup'):
2923 if opts.get('no_backup'):
2927 backupinteractive = backup = check = discard
2924 backupinteractive = backup = check = discard
2928 if interactive:
2925 if interactive:
2929 dsmodifiedbackup = backupinteractive
2926 dsmodifiedbackup = backupinteractive
2930 else:
2927 else:
2931 dsmodifiedbackup = backup
2928 dsmodifiedbackup = backup
2932 tobackup = set()
2929 tobackup = set()
2933
2930
2934 backupanddel = actions['remove']
2931 backupanddel = actions['remove']
2935 if not opts.get('no_backup'):
2932 if not opts.get('no_backup'):
2936 backupanddel = actions['drop']
2933 backupanddel = actions['drop']
2937
2934
2938 disptable = (
2935 disptable = (
2939 # dispatch table:
2936 # dispatch table:
2940 # file state
2937 # file state
2941 # action
2938 # action
2942 # make backup
2939 # make backup
2943
2940
2944 ## Sets that results that will change file on disk
2941 ## Sets that results that will change file on disk
2945 # Modified compared to target, no local change
2942 # Modified compared to target, no local change
2946 (modified, actions['revert'], discard),
2943 (modified, actions['revert'], discard),
2947 # Modified compared to target, but local file is deleted
2944 # Modified compared to target, but local file is deleted
2948 (deleted, actions['revert'], discard),
2945 (deleted, actions['revert'], discard),
2949 # Modified compared to target, local change
2946 # Modified compared to target, local change
2950 (dsmodified, actions['revert'], dsmodifiedbackup),
2947 (dsmodified, actions['revert'], dsmodifiedbackup),
2951 # Added since target
2948 # Added since target
2952 (added, actions['remove'], discard),
2949 (added, actions['remove'], discard),
2953 # Added in working directory
2950 # Added in working directory
2954 (dsadded, actions['forget'], discard),
2951 (dsadded, actions['forget'], discard),
2955 # Added since target, have local modification
2952 # Added since target, have local modification
2956 (modadded, backupanddel, backup),
2953 (modadded, backupanddel, backup),
2957 # Added since target but file is missing in working directory
2954 # Added since target but file is missing in working directory
2958 (deladded, actions['drop'], discard),
2955 (deladded, actions['drop'], discard),
2959 # Removed since target, before working copy parent
2956 # Removed since target, before working copy parent
2960 (removed, actions['add'], discard),
2957 (removed, actions['add'], discard),
2961 # Same as `removed` but an unknown file exists at the same path
2958 # Same as `removed` but an unknown file exists at the same path
2962 (removunk, actions['add'], check),
2959 (removunk, actions['add'], check),
2963 # Removed since targe, marked as such in working copy parent
2960 # Removed since targe, marked as such in working copy parent
2964 (dsremoved, actions['undelete'], discard),
2961 (dsremoved, actions['undelete'], discard),
2965 # Same as `dsremoved` but an unknown file exists at the same path
2962 # Same as `dsremoved` but an unknown file exists at the same path
2966 (dsremovunk, actions['undelete'], check),
2963 (dsremovunk, actions['undelete'], check),
2967 ## the following sets does not result in any file changes
2964 ## the following sets does not result in any file changes
2968 # File with no modification
2965 # File with no modification
2969 (clean, actions['noop'], discard),
2966 (clean, actions['noop'], discard),
2970 # Existing file, not tracked anywhere
2967 # Existing file, not tracked anywhere
2971 (unknown, actions['unknown'], discard),
2968 (unknown, actions['unknown'], discard),
2972 )
2969 )
2973
2970
2974 for abs, (rel, exact) in sorted(names.items()):
2971 for abs, (rel, exact) in sorted(names.items()):
2975 # target file to be touch on disk (relative to cwd)
2972 # target file to be touch on disk (relative to cwd)
2976 target = repo.wjoin(abs)
2973 target = repo.wjoin(abs)
2977 # search the entry in the dispatch table.
2974 # search the entry in the dispatch table.
2978 # if the file is in any of these sets, it was touched in the working
2975 # if the file is in any of these sets, it was touched in the working
2979 # directory parent and we are sure it needs to be reverted.
2976 # directory parent and we are sure it needs to be reverted.
2980 for table, (xlist, msg), dobackup in disptable:
2977 for table, (xlist, msg), dobackup in disptable:
2981 if abs not in table:
2978 if abs not in table:
2982 continue
2979 continue
2983 if xlist is not None:
2980 if xlist is not None:
2984 xlist.append(abs)
2981 xlist.append(abs)
2985 if dobackup:
2982 if dobackup:
2986 # If in interactive mode, don't automatically create
2983 # If in interactive mode, don't automatically create
2987 # .orig files (issue4793)
2984 # .orig files (issue4793)
2988 if dobackup == backupinteractive:
2985 if dobackup == backupinteractive:
2989 tobackup.add(abs)
2986 tobackup.add(abs)
2990 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
2987 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
2991 bakname = scmutil.origpath(ui, repo, rel)
2988 bakname = scmutil.origpath(ui, repo, rel)
2992 ui.note(_('saving current version of %s as %s\n') %
2989 ui.note(_('saving current version of %s as %s\n') %
2993 (rel, bakname))
2990 (rel, bakname))
2994 if not opts.get('dry_run'):
2991 if not opts.get('dry_run'):
2995 if interactive:
2992 if interactive:
2996 util.copyfile(target, bakname)
2993 util.copyfile(target, bakname)
2997 else:
2994 else:
2998 util.rename(target, bakname)
2995 util.rename(target, bakname)
2999 if ui.verbose or not exact:
2996 if ui.verbose or not exact:
3000 if not isinstance(msg, bytes):
2997 if not isinstance(msg, bytes):
3001 msg = msg(abs)
2998 msg = msg(abs)
3002 ui.status(msg % rel)
2999 ui.status(msg % rel)
3003 elif exact:
3000 elif exact:
3004 ui.warn(msg % rel)
3001 ui.warn(msg % rel)
3005 break
3002 break
3006
3003
3007 if not opts.get('dry_run'):
3004 if not opts.get('dry_run'):
3008 needdata = ('revert', 'add', 'undelete')
3005 needdata = ('revert', 'add', 'undelete')
3009 oplist = [actions[name][0] for name in needdata]
3006 oplist = [actions[name][0] for name in needdata]
3010 prefetch = scmutil.prefetchfiles
3007 prefetch = scmutil.prefetchfiles
3011 matchfiles = scmutil.matchfiles
3008 matchfiles = scmutil.matchfiles
3012 prefetch(repo, [ctx.rev()],
3009 prefetch(repo, [ctx.rev()],
3013 matchfiles(repo,
3010 matchfiles(repo,
3014 [f for sublist in oplist for f in sublist]))
3011 [f for sublist in oplist for f in sublist]))
3015 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3012 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3016
3013
3017 if targetsubs:
3014 if targetsubs:
3018 # Revert the subrepos on the revert list
3015 # Revert the subrepos on the revert list
3019 for sub in targetsubs:
3016 for sub in targetsubs:
3020 try:
3017 try:
3021 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3018 wctx.sub(sub).revert(ctx.substate[sub], *pats,
3022 **pycompat.strkwargs(opts))
3019 **pycompat.strkwargs(opts))
3023 except KeyError:
3020 except KeyError:
3024 raise error.Abort("subrepository '%s' does not exist in %s!"
3021 raise error.Abort("subrepository '%s' does not exist in %s!"
3025 % (sub, short(ctx.node())))
3022 % (sub, short(ctx.node())))
3026
3023
3027 def _performrevert(repo, parents, ctx, actions, interactive=False,
3024 def _performrevert(repo, parents, ctx, actions, interactive=False,
3028 tobackup=None):
3025 tobackup=None):
3029 """function that actually perform all the actions computed for revert
3026 """function that actually perform all the actions computed for revert
3030
3027
3031 This is an independent function to let extension to plug in and react to
3028 This is an independent function to let extension to plug in and react to
3032 the imminent revert.
3029 the imminent revert.
3033
3030
3034 Make sure you have the working directory locked when calling this function.
3031 Make sure you have the working directory locked when calling this function.
3035 """
3032 """
3036 parent, p2 = parents
3033 parent, p2 = parents
3037 node = ctx.node()
3034 node = ctx.node()
3038 excluded_files = []
3035 excluded_files = []
3039
3036
3040 def checkout(f):
3037 def checkout(f):
3041 fc = ctx[f]
3038 fc = ctx[f]
3042 repo.wwrite(f, fc.data(), fc.flags())
3039 repo.wwrite(f, fc.data(), fc.flags())
3043
3040
3044 def doremove(f):
3041 def doremove(f):
3045 try:
3042 try:
3046 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
3043 rmdir = repo.ui.configbool('experimental', 'removeemptydirs')
3047 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3044 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3048 except OSError:
3045 except OSError:
3049 pass
3046 pass
3050 repo.dirstate.remove(f)
3047 repo.dirstate.remove(f)
3051
3048
3052 audit_path = pathutil.pathauditor(repo.root, cached=True)
3049 audit_path = pathutil.pathauditor(repo.root, cached=True)
3053 for f in actions['forget'][0]:
3050 for f in actions['forget'][0]:
3054 if interactive:
3051 if interactive:
3055 choice = repo.ui.promptchoice(
3052 choice = repo.ui.promptchoice(
3056 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3053 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3057 if choice == 0:
3054 if choice == 0:
3058 repo.dirstate.drop(f)
3055 repo.dirstate.drop(f)
3059 else:
3056 else:
3060 excluded_files.append(f)
3057 excluded_files.append(f)
3061 else:
3058 else:
3062 repo.dirstate.drop(f)
3059 repo.dirstate.drop(f)
3063 for f in actions['remove'][0]:
3060 for f in actions['remove'][0]:
3064 audit_path(f)
3061 audit_path(f)
3065 if interactive:
3062 if interactive:
3066 choice = repo.ui.promptchoice(
3063 choice = repo.ui.promptchoice(
3067 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3064 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3068 if choice == 0:
3065 if choice == 0:
3069 doremove(f)
3066 doremove(f)
3070 else:
3067 else:
3071 excluded_files.append(f)
3068 excluded_files.append(f)
3072 else:
3069 else:
3073 doremove(f)
3070 doremove(f)
3074 for f in actions['drop'][0]:
3071 for f in actions['drop'][0]:
3075 audit_path(f)
3072 audit_path(f)
3076 repo.dirstate.remove(f)
3073 repo.dirstate.remove(f)
3077
3074
3078 normal = None
3075 normal = None
3079 if node == parent:
3076 if node == parent:
3080 # We're reverting to our parent. If possible, we'd like status
3077 # We're reverting to our parent. If possible, we'd like status
3081 # to report the file as clean. We have to use normallookup for
3078 # to report the file as clean. We have to use normallookup for
3082 # merges to avoid losing information about merged/dirty files.
3079 # merges to avoid losing information about merged/dirty files.
3083 if p2 != nullid:
3080 if p2 != nullid:
3084 normal = repo.dirstate.normallookup
3081 normal = repo.dirstate.normallookup
3085 else:
3082 else:
3086 normal = repo.dirstate.normal
3083 normal = repo.dirstate.normal
3087
3084
3088 newlyaddedandmodifiedfiles = set()
3085 newlyaddedandmodifiedfiles = set()
3089 if interactive:
3086 if interactive:
3090 # Prompt the user for changes to revert
3087 # Prompt the user for changes to revert
3091 torevert = [f for f in actions['revert'][0] if f not in excluded_files]
3088 torevert = [f for f in actions['revert'][0] if f not in excluded_files]
3092 m = scmutil.matchfiles(repo, torevert)
3089 m = scmutil.matchfiles(repo, torevert)
3093 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3090 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3094 diffopts.nodates = True
3091 diffopts.nodates = True
3095 diffopts.git = True
3092 diffopts.git = True
3096 operation = 'discard'
3093 operation = 'discard'
3097 reversehunks = True
3094 reversehunks = True
3098 if node != parent:
3095 if node != parent:
3099 operation = 'apply'
3096 operation = 'apply'
3100 reversehunks = False
3097 reversehunks = False
3101 if reversehunks:
3098 if reversehunks:
3102 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3099 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3103 else:
3100 else:
3104 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3101 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3105 originalchunks = patch.parsepatch(diff)
3102 originalchunks = patch.parsepatch(diff)
3106
3103
3107 try:
3104 try:
3108
3105
3109 chunks, opts = recordfilter(repo.ui, originalchunks,
3106 chunks, opts = recordfilter(repo.ui, originalchunks,
3110 operation=operation)
3107 operation=operation)
3111 if reversehunks:
3108 if reversehunks:
3112 chunks = patch.reversehunks(chunks)
3109 chunks = patch.reversehunks(chunks)
3113
3110
3114 except error.PatchError as err:
3111 except error.PatchError as err:
3115 raise error.Abort(_('error parsing patch: %s') % err)
3112 raise error.Abort(_('error parsing patch: %s') % err)
3116
3113
3117 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3114 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3118 if tobackup is None:
3115 if tobackup is None:
3119 tobackup = set()
3116 tobackup = set()
3120 # Apply changes
3117 # Apply changes
3121 fp = stringio()
3118 fp = stringio()
3122 for c in chunks:
3119 for c in chunks:
3123 # Create a backup file only if this hunk should be backed up
3120 # Create a backup file only if this hunk should be backed up
3124 if ishunk(c) and c.header.filename() in tobackup:
3121 if ishunk(c) and c.header.filename() in tobackup:
3125 abs = c.header.filename()
3122 abs = c.header.filename()
3126 target = repo.wjoin(abs)
3123 target = repo.wjoin(abs)
3127 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3124 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3128 util.copyfile(target, bakname)
3125 util.copyfile(target, bakname)
3129 tobackup.remove(abs)
3126 tobackup.remove(abs)
3130 c.write(fp)
3127 c.write(fp)
3131 dopatch = fp.tell()
3128 dopatch = fp.tell()
3132 fp.seek(0)
3129 fp.seek(0)
3133 if dopatch:
3130 if dopatch:
3134 try:
3131 try:
3135 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3132 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3136 except error.PatchError as err:
3133 except error.PatchError as err:
3137 raise error.Abort(pycompat.bytestr(err))
3134 raise error.Abort(pycompat.bytestr(err))
3138 del fp
3135 del fp
3139 else:
3136 else:
3140 for f in actions['revert'][0]:
3137 for f in actions['revert'][0]:
3141 checkout(f)
3138 checkout(f)
3142 if normal:
3139 if normal:
3143 normal(f)
3140 normal(f)
3144
3141
3145 for f in actions['add'][0]:
3142 for f in actions['add'][0]:
3146 # Don't checkout modified files, they are already created by the diff
3143 # Don't checkout modified files, they are already created by the diff
3147 if f not in newlyaddedandmodifiedfiles:
3144 if f not in newlyaddedandmodifiedfiles:
3148 checkout(f)
3145 checkout(f)
3149 repo.dirstate.add(f)
3146 repo.dirstate.add(f)
3150
3147
3151 normal = repo.dirstate.normallookup
3148 normal = repo.dirstate.normallookup
3152 if node == parent and p2 == nullid:
3149 if node == parent and p2 == nullid:
3153 normal = repo.dirstate.normal
3150 normal = repo.dirstate.normal
3154 for f in actions['undelete'][0]:
3151 for f in actions['undelete'][0]:
3155 checkout(f)
3152 checkout(f)
3156 normal(f)
3153 normal(f)
3157
3154
3158 copied = copies.pathcopies(repo[parent], ctx)
3155 copied = copies.pathcopies(repo[parent], ctx)
3159
3156
3160 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3157 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3161 if f in copied:
3158 if f in copied:
3162 repo.dirstate.copy(copied[f], f)
3159 repo.dirstate.copy(copied[f], f)
3163
3160
3164 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3161 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3165 # commands.outgoing. "missing" is "missing" of the result of
3162 # commands.outgoing. "missing" is "missing" of the result of
3166 # "findcommonoutgoing()"
3163 # "findcommonoutgoing()"
3167 outgoinghooks = util.hooks()
3164 outgoinghooks = util.hooks()
3168
3165
3169 # a list of (ui, repo) functions called by commands.summary
3166 # a list of (ui, repo) functions called by commands.summary
3170 summaryhooks = util.hooks()
3167 summaryhooks = util.hooks()
3171
3168
3172 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3169 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3173 #
3170 #
3174 # functions should return tuple of booleans below, if 'changes' is None:
3171 # functions should return tuple of booleans below, if 'changes' is None:
3175 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3172 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3176 #
3173 #
3177 # otherwise, 'changes' is a tuple of tuples below:
3174 # otherwise, 'changes' is a tuple of tuples below:
3178 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3175 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3179 # - (desturl, destbranch, destpeer, outgoing)
3176 # - (desturl, destbranch, destpeer, outgoing)
3180 summaryremotehooks = util.hooks()
3177 summaryremotehooks = util.hooks()
3181
3178
3182 # A list of state files kept by multistep operations like graft.
3179 # A list of state files kept by multistep operations like graft.
3183 # Since graft cannot be aborted, it is considered 'clearable' by update.
3180 # Since graft cannot be aborted, it is considered 'clearable' by update.
3184 # note: bisect is intentionally excluded
3181 # note: bisect is intentionally excluded
3185 # (state file, clearable, allowcommit, error, hint)
3182 # (state file, clearable, allowcommit, error, hint)
3186 unfinishedstates = [
3183 unfinishedstates = [
3187 ('graftstate', True, False, _('graft in progress'),
3184 ('graftstate', True, False, _('graft in progress'),
3188 _("use 'hg graft --continue' or 'hg graft --stop' to stop")),
3185 _("use 'hg graft --continue' or 'hg graft --stop' to stop")),
3189 ('updatestate', True, False, _('last update was interrupted'),
3186 ('updatestate', True, False, _('last update was interrupted'),
3190 _("use 'hg update' to get a consistent checkout"))
3187 _("use 'hg update' to get a consistent checkout"))
3191 ]
3188 ]
3192
3189
3193 def checkunfinished(repo, commit=False):
3190 def checkunfinished(repo, commit=False):
3194 '''Look for an unfinished multistep operation, like graft, and abort
3191 '''Look for an unfinished multistep operation, like graft, and abort
3195 if found. It's probably good to check this right before
3192 if found. It's probably good to check this right before
3196 bailifchanged().
3193 bailifchanged().
3197 '''
3194 '''
3198 # Check for non-clearable states first, so things like rebase will take
3195 # Check for non-clearable states first, so things like rebase will take
3199 # precedence over update.
3196 # precedence over update.
3200 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3197 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3201 if clearable or (commit and allowcommit):
3198 if clearable or (commit and allowcommit):
3202 continue
3199 continue
3203 if repo.vfs.exists(f):
3200 if repo.vfs.exists(f):
3204 raise error.Abort(msg, hint=hint)
3201 raise error.Abort(msg, hint=hint)
3205
3202
3206 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3203 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3207 if not clearable or (commit and allowcommit):
3204 if not clearable or (commit and allowcommit):
3208 continue
3205 continue
3209 if repo.vfs.exists(f):
3206 if repo.vfs.exists(f):
3210 raise error.Abort(msg, hint=hint)
3207 raise error.Abort(msg, hint=hint)
3211
3208
3212 def clearunfinished(repo):
3209 def clearunfinished(repo):
3213 '''Check for unfinished operations (as above), and clear the ones
3210 '''Check for unfinished operations (as above), and clear the ones
3214 that are clearable.
3211 that are clearable.
3215 '''
3212 '''
3216 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3213 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3217 if not clearable and repo.vfs.exists(f):
3214 if not clearable and repo.vfs.exists(f):
3218 raise error.Abort(msg, hint=hint)
3215 raise error.Abort(msg, hint=hint)
3219 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3216 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3220 if clearable and repo.vfs.exists(f):
3217 if clearable and repo.vfs.exists(f):
3221 util.unlink(repo.vfs.join(f))
3218 util.unlink(repo.vfs.join(f))
3222
3219
3223 afterresolvedstates = [
3220 afterresolvedstates = [
3224 ('graftstate',
3221 ('graftstate',
3225 _('hg graft --continue')),
3222 _('hg graft --continue')),
3226 ]
3223 ]
3227
3224
3228 def howtocontinue(repo):
3225 def howtocontinue(repo):
3229 '''Check for an unfinished operation and return the command to finish
3226 '''Check for an unfinished operation and return the command to finish
3230 it.
3227 it.
3231
3228
3232 afterresolvedstates tuples define a .hg/{file} and the corresponding
3229 afterresolvedstates tuples define a .hg/{file} and the corresponding
3233 command needed to finish it.
3230 command needed to finish it.
3234
3231
3235 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3232 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3236 a boolean.
3233 a boolean.
3237 '''
3234 '''
3238 contmsg = _("continue: %s")
3235 contmsg = _("continue: %s")
3239 for f, msg in afterresolvedstates:
3236 for f, msg in afterresolvedstates:
3240 if repo.vfs.exists(f):
3237 if repo.vfs.exists(f):
3241 return contmsg % msg, True
3238 return contmsg % msg, True
3242 if repo[None].dirty(missing=True, merge=False, branch=False):
3239 if repo[None].dirty(missing=True, merge=False, branch=False):
3243 return contmsg % _("hg commit"), False
3240 return contmsg % _("hg commit"), False
3244 return None, None
3241 return None, None
3245
3242
3246 def checkafterresolved(repo):
3243 def checkafterresolved(repo):
3247 '''Inform the user about the next action after completing hg resolve
3244 '''Inform the user about the next action after completing hg resolve
3248
3245
3249 If there's a matching afterresolvedstates, howtocontinue will yield
3246 If there's a matching afterresolvedstates, howtocontinue will yield
3250 repo.ui.warn as the reporter.
3247 repo.ui.warn as the reporter.
3251
3248
3252 Otherwise, it will yield repo.ui.note.
3249 Otherwise, it will yield repo.ui.note.
3253 '''
3250 '''
3254 msg, warning = howtocontinue(repo)
3251 msg, warning = howtocontinue(repo)
3255 if msg is not None:
3252 if msg is not None:
3256 if warning:
3253 if warning:
3257 repo.ui.warn("%s\n" % msg)
3254 repo.ui.warn("%s\n" % msg)
3258 else:
3255 else:
3259 repo.ui.note("%s\n" % msg)
3256 repo.ui.note("%s\n" % msg)
3260
3257
3261 def wrongtooltocontinue(repo, task):
3258 def wrongtooltocontinue(repo, task):
3262 '''Raise an abort suggesting how to properly continue if there is an
3259 '''Raise an abort suggesting how to properly continue if there is an
3263 active task.
3260 active task.
3264
3261
3265 Uses howtocontinue() to find the active task.
3262 Uses howtocontinue() to find the active task.
3266
3263
3267 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3264 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3268 a hint.
3265 a hint.
3269 '''
3266 '''
3270 after = howtocontinue(repo)
3267 after = howtocontinue(repo)
3271 hint = None
3268 hint = None
3272 if after[1]:
3269 if after[1]:
3273 hint = after[0]
3270 hint = after[0]
3274 raise error.Abort(_('no %s in progress') % task, hint=hint)
3271 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,5871 +1,5872 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 filemerge,
38 filemerge,
39 formatter,
39 formatter,
40 graphmod,
40 graphmod,
41 hbisect,
41 hbisect,
42 help,
42 help,
43 hg,
43 hg,
44 logcmdutil,
44 logcmdutil,
45 merge as mergemod,
45 merge as mergemod,
46 obsolete,
46 obsolete,
47 obsutil,
47 obsutil,
48 patch,
48 patch,
49 phases,
49 phases,
50 pycompat,
50 pycompat,
51 rcutil,
51 rcutil,
52 registrar,
52 registrar,
53 repair,
53 repair,
54 revsetlang,
54 revsetlang,
55 rewriteutil,
55 rewriteutil,
56 scmutil,
56 scmutil,
57 server,
57 server,
58 state as statemod,
58 state as statemod,
59 streamclone,
59 streamclone,
60 tags as tagsmod,
60 tags as tagsmod,
61 templatekw,
61 templatekw,
62 ui as uimod,
62 ui as uimod,
63 util,
63 util,
64 wireprotoserver,
64 wireprotoserver,
65 )
65 )
66 from .utils import (
66 from .utils import (
67 dateutil,
67 dateutil,
68 stringutil,
68 stringutil,
69 )
69 )
70
70
71 table = {}
71 table = {}
72 table.update(debugcommandsmod.command._table)
72 table.update(debugcommandsmod.command._table)
73
73
74 command = registrar.command(table)
74 command = registrar.command(table)
75 INTENT_READONLY = registrar.INTENT_READONLY
75 INTENT_READONLY = registrar.INTENT_READONLY
76
76
77 # common command options
77 # common command options
78
78
79 globalopts = [
79 globalopts = [
80 ('R', 'repository', '',
80 ('R', 'repository', '',
81 _('repository root directory or name of overlay bundle file'),
81 _('repository root directory or name of overlay bundle file'),
82 _('REPO')),
82 _('REPO')),
83 ('', 'cwd', '',
83 ('', 'cwd', '',
84 _('change working directory'), _('DIR')),
84 _('change working directory'), _('DIR')),
85 ('y', 'noninteractive', None,
85 ('y', 'noninteractive', None,
86 _('do not prompt, automatically pick the first choice for all prompts')),
86 _('do not prompt, automatically pick the first choice for all prompts')),
87 ('q', 'quiet', None, _('suppress output')),
87 ('q', 'quiet', None, _('suppress output')),
88 ('v', 'verbose', None, _('enable additional output')),
88 ('v', 'verbose', None, _('enable additional output')),
89 ('', 'color', '',
89 ('', 'color', '',
90 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
90 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
91 # and should not be translated
91 # and should not be translated
92 _("when to colorize (boolean, always, auto, never, or debug)"),
92 _("when to colorize (boolean, always, auto, never, or debug)"),
93 _('TYPE')),
93 _('TYPE')),
94 ('', 'config', [],
94 ('', 'config', [],
95 _('set/override config option (use \'section.name=value\')'),
95 _('set/override config option (use \'section.name=value\')'),
96 _('CONFIG')),
96 _('CONFIG')),
97 ('', 'debug', None, _('enable debugging output')),
97 ('', 'debug', None, _('enable debugging output')),
98 ('', 'debugger', None, _('start debugger')),
98 ('', 'debugger', None, _('start debugger')),
99 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
99 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
100 _('ENCODE')),
100 _('ENCODE')),
101 ('', 'encodingmode', encoding.encodingmode,
101 ('', 'encodingmode', encoding.encodingmode,
102 _('set the charset encoding mode'), _('MODE')),
102 _('set the charset encoding mode'), _('MODE')),
103 ('', 'traceback', None, _('always print a traceback on exception')),
103 ('', 'traceback', None, _('always print a traceback on exception')),
104 ('', 'time', None, _('time how long the command takes')),
104 ('', 'time', None, _('time how long the command takes')),
105 ('', 'profile', None, _('print command execution profile')),
105 ('', 'profile', None, _('print command execution profile')),
106 ('', 'version', None, _('output version information and exit')),
106 ('', 'version', None, _('output version information and exit')),
107 ('h', 'help', None, _('display help and exit')),
107 ('h', 'help', None, _('display help and exit')),
108 ('', 'hidden', False, _('consider hidden changesets')),
108 ('', 'hidden', False, _('consider hidden changesets')),
109 ('', 'pager', 'auto',
109 ('', 'pager', 'auto',
110 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
110 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
111 ]
111 ]
112
112
113 dryrunopts = cmdutil.dryrunopts
113 dryrunopts = cmdutil.dryrunopts
114 remoteopts = cmdutil.remoteopts
114 remoteopts = cmdutil.remoteopts
115 walkopts = cmdutil.walkopts
115 walkopts = cmdutil.walkopts
116 commitopts = cmdutil.commitopts
116 commitopts = cmdutil.commitopts
117 commitopts2 = cmdutil.commitopts2
117 commitopts2 = cmdutil.commitopts2
118 formatteropts = cmdutil.formatteropts
118 formatteropts = cmdutil.formatteropts
119 templateopts = cmdutil.templateopts
119 templateopts = cmdutil.templateopts
120 logopts = cmdutil.logopts
120 logopts = cmdutil.logopts
121 diffopts = cmdutil.diffopts
121 diffopts = cmdutil.diffopts
122 diffwsopts = cmdutil.diffwsopts
122 diffwsopts = cmdutil.diffwsopts
123 diffopts2 = cmdutil.diffopts2
123 diffopts2 = cmdutil.diffopts2
124 mergetoolopts = cmdutil.mergetoolopts
124 mergetoolopts = cmdutil.mergetoolopts
125 similarityopts = cmdutil.similarityopts
125 similarityopts = cmdutil.similarityopts
126 subrepoopts = cmdutil.subrepoopts
126 subrepoopts = cmdutil.subrepoopts
127 debugrevlogopts = cmdutil.debugrevlogopts
127 debugrevlogopts = cmdutil.debugrevlogopts
128
128
129 # Commands start here, listed alphabetically
129 # Commands start here, listed alphabetically
130
130
131 @command('^add',
131 @command('^add',
132 walkopts + subrepoopts + dryrunopts,
132 walkopts + subrepoopts + dryrunopts,
133 _('[OPTION]... [FILE]...'),
133 _('[OPTION]... [FILE]...'),
134 inferrepo=True)
134 inferrepo=True)
135 def add(ui, repo, *pats, **opts):
135 def add(ui, repo, *pats, **opts):
136 """add the specified files on the next commit
136 """add the specified files on the next commit
137
137
138 Schedule files to be version controlled and added to the
138 Schedule files to be version controlled and added to the
139 repository.
139 repository.
140
140
141 The files will be added to the repository at the next commit. To
141 The files will be added to the repository at the next commit. To
142 undo an add before that, see :hg:`forget`.
142 undo an add before that, see :hg:`forget`.
143
143
144 If no names are given, add all files to the repository (except
144 If no names are given, add all files to the repository (except
145 files matching ``.hgignore``).
145 files matching ``.hgignore``).
146
146
147 .. container:: verbose
147 .. container:: verbose
148
148
149 Examples:
149 Examples:
150
150
151 - New (unknown) files are added
151 - New (unknown) files are added
152 automatically by :hg:`add`::
152 automatically by :hg:`add`::
153
153
154 $ ls
154 $ ls
155 foo.c
155 foo.c
156 $ hg status
156 $ hg status
157 ? foo.c
157 ? foo.c
158 $ hg add
158 $ hg add
159 adding foo.c
159 adding foo.c
160 $ hg status
160 $ hg status
161 A foo.c
161 A foo.c
162
162
163 - Specific files to be added can be specified::
163 - Specific files to be added can be specified::
164
164
165 $ ls
165 $ ls
166 bar.c foo.c
166 bar.c foo.c
167 $ hg status
167 $ hg status
168 ? bar.c
168 ? bar.c
169 ? foo.c
169 ? foo.c
170 $ hg add bar.c
170 $ hg add bar.c
171 $ hg status
171 $ hg status
172 A bar.c
172 A bar.c
173 ? foo.c
173 ? foo.c
174
174
175 Returns 0 if all files are successfully added.
175 Returns 0 if all files are successfully added.
176 """
176 """
177
177
178 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
178 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
179 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
179 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
180 return rejected and 1 or 0
180 return rejected and 1 or 0
181
181
182 @command('addremove',
182 @command('addremove',
183 similarityopts + subrepoopts + walkopts + dryrunopts,
183 similarityopts + subrepoopts + walkopts + dryrunopts,
184 _('[OPTION]... [FILE]...'),
184 _('[OPTION]... [FILE]...'),
185 inferrepo=True)
185 inferrepo=True)
186 def addremove(ui, repo, *pats, **opts):
186 def addremove(ui, repo, *pats, **opts):
187 """add all new files, delete all missing files
187 """add all new files, delete all missing files
188
188
189 Add all new files and remove all missing files from the
189 Add all new files and remove all missing files from the
190 repository.
190 repository.
191
191
192 Unless names are given, new files are ignored if they match any of
192 Unless names are given, new files are ignored if they match any of
193 the patterns in ``.hgignore``. As with add, these changes take
193 the patterns in ``.hgignore``. As with add, these changes take
194 effect at the next commit.
194 effect at the next commit.
195
195
196 Use the -s/--similarity option to detect renamed files. This
196 Use the -s/--similarity option to detect renamed files. This
197 option takes a percentage between 0 (disabled) and 100 (files must
197 option takes a percentage between 0 (disabled) and 100 (files must
198 be identical) as its parameter. With a parameter greater than 0,
198 be identical) as its parameter. With a parameter greater than 0,
199 this compares every removed file with every added file and records
199 this compares every removed file with every added file and records
200 those similar enough as renames. Detecting renamed files this way
200 those similar enough as renames. Detecting renamed files this way
201 can be expensive. After using this option, :hg:`status -C` can be
201 can be expensive. After using this option, :hg:`status -C` can be
202 used to check which files were identified as moved or renamed. If
202 used to check which files were identified as moved or renamed. If
203 not specified, -s/--similarity defaults to 100 and only renames of
203 not specified, -s/--similarity defaults to 100 and only renames of
204 identical files are detected.
204 identical files are detected.
205
205
206 .. container:: verbose
206 .. container:: verbose
207
207
208 Examples:
208 Examples:
209
209
210 - A number of files (bar.c and foo.c) are new,
210 - A number of files (bar.c and foo.c) are new,
211 while foobar.c has been removed (without using :hg:`remove`)
211 while foobar.c has been removed (without using :hg:`remove`)
212 from the repository::
212 from the repository::
213
213
214 $ ls
214 $ ls
215 bar.c foo.c
215 bar.c foo.c
216 $ hg status
216 $ hg status
217 ! foobar.c
217 ! foobar.c
218 ? bar.c
218 ? bar.c
219 ? foo.c
219 ? foo.c
220 $ hg addremove
220 $ hg addremove
221 adding bar.c
221 adding bar.c
222 adding foo.c
222 adding foo.c
223 removing foobar.c
223 removing foobar.c
224 $ hg status
224 $ hg status
225 A bar.c
225 A bar.c
226 A foo.c
226 A foo.c
227 R foobar.c
227 R foobar.c
228
228
229 - A file foobar.c was moved to foo.c without using :hg:`rename`.
229 - A file foobar.c was moved to foo.c without using :hg:`rename`.
230 Afterwards, it was edited slightly::
230 Afterwards, it was edited slightly::
231
231
232 $ ls
232 $ ls
233 foo.c
233 foo.c
234 $ hg status
234 $ hg status
235 ! foobar.c
235 ! foobar.c
236 ? foo.c
236 ? foo.c
237 $ hg addremove --similarity 90
237 $ hg addremove --similarity 90
238 removing foobar.c
238 removing foobar.c
239 adding foo.c
239 adding foo.c
240 recording removal of foobar.c as rename to foo.c (94% similar)
240 recording removal of foobar.c as rename to foo.c (94% similar)
241 $ hg status -C
241 $ hg status -C
242 A foo.c
242 A foo.c
243 foobar.c
243 foobar.c
244 R foobar.c
244 R foobar.c
245
245
246 Returns 0 if all files are successfully added.
246 Returns 0 if all files are successfully added.
247 """
247 """
248 opts = pycompat.byteskwargs(opts)
248 opts = pycompat.byteskwargs(opts)
249 if not opts.get('similarity'):
249 if not opts.get('similarity'):
250 opts['similarity'] = '100'
250 opts['similarity'] = '100'
251 matcher = scmutil.match(repo[None], pats, opts)
251 matcher = scmutil.match(repo[None], pats, opts)
252 return scmutil.addremove(repo, matcher, "", opts)
252 return scmutil.addremove(repo, matcher, "", opts)
253
253
254 @command('^annotate|blame',
254 @command('^annotate|blame',
255 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
255 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
256 ('', 'follow', None,
256 ('', 'follow', None,
257 _('follow copies/renames and list the filename (DEPRECATED)')),
257 _('follow copies/renames and list the filename (DEPRECATED)')),
258 ('', 'no-follow', None, _("don't follow copies and renames")),
258 ('', 'no-follow', None, _("don't follow copies and renames")),
259 ('a', 'text', None, _('treat all files as text')),
259 ('a', 'text', None, _('treat all files as text')),
260 ('u', 'user', None, _('list the author (long with -v)')),
260 ('u', 'user', None, _('list the author (long with -v)')),
261 ('f', 'file', None, _('list the filename')),
261 ('f', 'file', None, _('list the filename')),
262 ('d', 'date', None, _('list the date (short with -q)')),
262 ('d', 'date', None, _('list the date (short with -q)')),
263 ('n', 'number', None, _('list the revision number (default)')),
263 ('n', 'number', None, _('list the revision number (default)')),
264 ('c', 'changeset', None, _('list the changeset')),
264 ('c', 'changeset', None, _('list the changeset')),
265 ('l', 'line-number', None, _('show line number at the first appearance')),
265 ('l', 'line-number', None, _('show line number at the first appearance')),
266 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
266 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
267 ] + diffwsopts + walkopts + formatteropts,
267 ] + diffwsopts + walkopts + formatteropts,
268 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
268 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
269 inferrepo=True)
269 inferrepo=True)
270 def annotate(ui, repo, *pats, **opts):
270 def annotate(ui, repo, *pats, **opts):
271 """show changeset information by line for each file
271 """show changeset information by line for each file
272
272
273 List changes in files, showing the revision id responsible for
273 List changes in files, showing the revision id responsible for
274 each line.
274 each line.
275
275
276 This command is useful for discovering when a change was made and
276 This command is useful for discovering when a change was made and
277 by whom.
277 by whom.
278
278
279 If you include --file, --user, or --date, the revision number is
279 If you include --file, --user, or --date, the revision number is
280 suppressed unless you also include --number.
280 suppressed unless you also include --number.
281
281
282 Without the -a/--text option, annotate will avoid processing files
282 Without the -a/--text option, annotate will avoid processing files
283 it detects as binary. With -a, annotate will annotate the file
283 it detects as binary. With -a, annotate will annotate the file
284 anyway, although the results will probably be neither useful
284 anyway, although the results will probably be neither useful
285 nor desirable.
285 nor desirable.
286
286
287 Returns 0 on success.
287 Returns 0 on success.
288 """
288 """
289 opts = pycompat.byteskwargs(opts)
289 opts = pycompat.byteskwargs(opts)
290 if not pats:
290 if not pats:
291 raise error.Abort(_('at least one filename or pattern is required'))
291 raise error.Abort(_('at least one filename or pattern is required'))
292
292
293 if opts.get('follow'):
293 if opts.get('follow'):
294 # --follow is deprecated and now just an alias for -f/--file
294 # --follow is deprecated and now just an alias for -f/--file
295 # to mimic the behavior of Mercurial before version 1.5
295 # to mimic the behavior of Mercurial before version 1.5
296 opts['file'] = True
296 opts['file'] = True
297
297
298 rev = opts.get('rev')
298 rev = opts.get('rev')
299 if rev:
299 if rev:
300 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
300 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
301 ctx = scmutil.revsingle(repo, rev)
301 ctx = scmutil.revsingle(repo, rev)
302
302
303 rootfm = ui.formatter('annotate', opts)
303 rootfm = ui.formatter('annotate', opts)
304 if ui.quiet:
304 if ui.quiet:
305 datefunc = dateutil.shortdate
305 datefunc = dateutil.shortdate
306 else:
306 else:
307 datefunc = dateutil.datestr
307 datefunc = dateutil.datestr
308 if ctx.rev() is None:
308 if ctx.rev() is None:
309 def hexfn(node):
309 def hexfn(node):
310 if node is None:
310 if node is None:
311 return None
311 return None
312 else:
312 else:
313 return rootfm.hexfunc(node)
313 return rootfm.hexfunc(node)
314 if opts.get('changeset'):
314 if opts.get('changeset'):
315 # omit "+" suffix which is appended to node hex
315 # omit "+" suffix which is appended to node hex
316 def formatrev(rev):
316 def formatrev(rev):
317 if rev is None:
317 if rev is None:
318 return '%d' % ctx.p1().rev()
318 return '%d' % ctx.p1().rev()
319 else:
319 else:
320 return '%d' % rev
320 return '%d' % rev
321 else:
321 else:
322 def formatrev(rev):
322 def formatrev(rev):
323 if rev is None:
323 if rev is None:
324 return '%d+' % ctx.p1().rev()
324 return '%d+' % ctx.p1().rev()
325 else:
325 else:
326 return '%d ' % rev
326 return '%d ' % rev
327 def formathex(hex):
327 def formathex(hex):
328 if hex is None:
328 if hex is None:
329 return '%s+' % rootfm.hexfunc(ctx.p1().node())
329 return '%s+' % rootfm.hexfunc(ctx.p1().node())
330 else:
330 else:
331 return '%s ' % hex
331 return '%s ' % hex
332 else:
332 else:
333 hexfn = rootfm.hexfunc
333 hexfn = rootfm.hexfunc
334 formatrev = formathex = pycompat.bytestr
334 formatrev = formathex = pycompat.bytestr
335
335
336 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
336 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
337 ('rev', ' ', lambda x: x.fctx.rev(), formatrev),
337 ('rev', ' ', lambda x: x.fctx.rev(), formatrev),
338 ('node', ' ', lambda x: hexfn(x.fctx.node()), formathex),
338 ('node', ' ', lambda x: hexfn(x.fctx.node()), formathex),
339 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
339 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
340 ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
340 ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
341 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
341 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
342 ]
342 ]
343 opnamemap = {'rev': 'number', 'node': 'changeset'}
343 opnamemap = {'rev': 'number', 'node': 'changeset'}
344
344
345 if (not opts.get('user') and not opts.get('changeset')
345 if (not opts.get('user') and not opts.get('changeset')
346 and not opts.get('date') and not opts.get('file')):
346 and not opts.get('date') and not opts.get('file')):
347 opts['number'] = True
347 opts['number'] = True
348
348
349 linenumber = opts.get('line_number') is not None
349 linenumber = opts.get('line_number') is not None
350 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
350 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
351 raise error.Abort(_('at least one of -n/-c is required for -l'))
351 raise error.Abort(_('at least one of -n/-c is required for -l'))
352
352
353 ui.pager('annotate')
353 ui.pager('annotate')
354
354
355 if rootfm.isplain():
355 if rootfm.isplain():
356 def makefunc(get, fmt):
356 def makefunc(get, fmt):
357 return lambda x: fmt(get(x))
357 return lambda x: fmt(get(x))
358 else:
358 else:
359 def makefunc(get, fmt):
359 def makefunc(get, fmt):
360 return get
360 return get
361 datahint = rootfm.datahint()
361 datahint = rootfm.datahint()
362 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
362 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
363 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
363 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
364 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
364 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
365 fields = ' '.join(fn for fn, sep, get, fmt in opmap
365 fields = ' '.join(fn for fn, sep, get, fmt in opmap
366 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
366 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
367
367
368 def bad(x, y):
368 def bad(x, y):
369 raise error.Abort("%s: %s" % (x, y))
369 raise error.Abort("%s: %s" % (x, y))
370
370
371 m = scmutil.match(ctx, pats, opts, badfn=bad)
371 m = scmutil.match(ctx, pats, opts, badfn=bad)
372
372
373 follow = not opts.get('no_follow')
373 follow = not opts.get('no_follow')
374 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
374 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
375 whitespace=True)
375 whitespace=True)
376 skiprevs = opts.get('skip')
376 skiprevs = opts.get('skip')
377 if skiprevs:
377 if skiprevs:
378 skiprevs = scmutil.revrange(repo, skiprevs)
378 skiprevs = scmutil.revrange(repo, skiprevs)
379
379
380 for abs in ctx.walk(m):
380 for abs in ctx.walk(m):
381 fctx = ctx[abs]
381 fctx = ctx[abs]
382 rootfm.startitem()
382 rootfm.startitem()
383 rootfm.data(abspath=abs, path=m.rel(abs))
383 rootfm.data(abspath=abs, path=m.rel(abs))
384 if not opts.get('text') and fctx.isbinary():
384 if not opts.get('text') and fctx.isbinary():
385 rootfm.plain(_("%s: binary file\n")
385 rootfm.plain(_("%s: binary file\n")
386 % ((pats and m.rel(abs)) or abs))
386 % ((pats and m.rel(abs)) or abs))
387 continue
387 continue
388
388
389 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
389 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
390 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
390 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
391 diffopts=diffopts)
391 diffopts=diffopts)
392 if not lines:
392 if not lines:
393 fm.end()
393 fm.end()
394 continue
394 continue
395 formats = []
395 formats = []
396 pieces = []
396 pieces = []
397
397
398 for f, sep in funcmap:
398 for f, sep in funcmap:
399 l = [f(n) for n in lines]
399 l = [f(n) for n in lines]
400 if fm.isplain():
400 if fm.isplain():
401 sizes = [encoding.colwidth(x) for x in l]
401 sizes = [encoding.colwidth(x) for x in l]
402 ml = max(sizes)
402 ml = max(sizes)
403 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
403 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
404 else:
404 else:
405 formats.append(['%s' for x in l])
405 formats.append(['%s' for x in l])
406 pieces.append(l)
406 pieces.append(l)
407
407
408 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
408 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
409 fm.startitem()
409 fm.startitem()
410 fm.context(fctx=n.fctx)
410 fm.context(fctx=n.fctx)
411 fm.write(fields, "".join(f), *p)
411 fm.write(fields, "".join(f), *p)
412 if n.skip:
412 if n.skip:
413 fmt = "* %s"
413 fmt = "* %s"
414 else:
414 else:
415 fmt = ": %s"
415 fmt = ": %s"
416 fm.write('line', fmt, n.text)
416 fm.write('line', fmt, n.text)
417
417
418 if not lines[-1].text.endswith('\n'):
418 if not lines[-1].text.endswith('\n'):
419 fm.plain('\n')
419 fm.plain('\n')
420 fm.end()
420 fm.end()
421
421
422 rootfm.end()
422 rootfm.end()
423
423
424 @command('archive',
424 @command('archive',
425 [('', 'no-decode', None, _('do not pass files through decoders')),
425 [('', 'no-decode', None, _('do not pass files through decoders')),
426 ('p', 'prefix', '', _('directory prefix for files in archive'),
426 ('p', 'prefix', '', _('directory prefix for files in archive'),
427 _('PREFIX')),
427 _('PREFIX')),
428 ('r', 'rev', '', _('revision to distribute'), _('REV')),
428 ('r', 'rev', '', _('revision to distribute'), _('REV')),
429 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
429 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
430 ] + subrepoopts + walkopts,
430 ] + subrepoopts + walkopts,
431 _('[OPTION]... DEST'))
431 _('[OPTION]... DEST'))
432 def archive(ui, repo, dest, **opts):
432 def archive(ui, repo, dest, **opts):
433 '''create an unversioned archive of a repository revision
433 '''create an unversioned archive of a repository revision
434
434
435 By default, the revision used is the parent of the working
435 By default, the revision used is the parent of the working
436 directory; use -r/--rev to specify a different revision.
436 directory; use -r/--rev to specify a different revision.
437
437
438 The archive type is automatically detected based on file
438 The archive type is automatically detected based on file
439 extension (to override, use -t/--type).
439 extension (to override, use -t/--type).
440
440
441 .. container:: verbose
441 .. container:: verbose
442
442
443 Examples:
443 Examples:
444
444
445 - create a zip file containing the 1.0 release::
445 - create a zip file containing the 1.0 release::
446
446
447 hg archive -r 1.0 project-1.0.zip
447 hg archive -r 1.0 project-1.0.zip
448
448
449 - create a tarball excluding .hg files::
449 - create a tarball excluding .hg files::
450
450
451 hg archive project.tar.gz -X ".hg*"
451 hg archive project.tar.gz -X ".hg*"
452
452
453 Valid types are:
453 Valid types are:
454
454
455 :``files``: a directory full of files (default)
455 :``files``: a directory full of files (default)
456 :``tar``: tar archive, uncompressed
456 :``tar``: tar archive, uncompressed
457 :``tbz2``: tar archive, compressed using bzip2
457 :``tbz2``: tar archive, compressed using bzip2
458 :``tgz``: tar archive, compressed using gzip
458 :``tgz``: tar archive, compressed using gzip
459 :``uzip``: zip archive, uncompressed
459 :``uzip``: zip archive, uncompressed
460 :``zip``: zip archive, compressed using deflate
460 :``zip``: zip archive, compressed using deflate
461
461
462 The exact name of the destination archive or directory is given
462 The exact name of the destination archive or directory is given
463 using a format string; see :hg:`help export` for details.
463 using a format string; see :hg:`help export` for details.
464
464
465 Each member added to an archive file has a directory prefix
465 Each member added to an archive file has a directory prefix
466 prepended. Use -p/--prefix to specify a format string for the
466 prepended. Use -p/--prefix to specify a format string for the
467 prefix. The default is the basename of the archive, with suffixes
467 prefix. The default is the basename of the archive, with suffixes
468 removed.
468 removed.
469
469
470 Returns 0 on success.
470 Returns 0 on success.
471 '''
471 '''
472
472
473 opts = pycompat.byteskwargs(opts)
473 opts = pycompat.byteskwargs(opts)
474 rev = opts.get('rev')
474 rev = opts.get('rev')
475 if rev:
475 if rev:
476 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
476 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
477 ctx = scmutil.revsingle(repo, rev)
477 ctx = scmutil.revsingle(repo, rev)
478 if not ctx:
478 if not ctx:
479 raise error.Abort(_('no working directory: please specify a revision'))
479 raise error.Abort(_('no working directory: please specify a revision'))
480 node = ctx.node()
480 node = ctx.node()
481 dest = cmdutil.makefilename(ctx, dest)
481 dest = cmdutil.makefilename(ctx, dest)
482 if os.path.realpath(dest) == repo.root:
482 if os.path.realpath(dest) == repo.root:
483 raise error.Abort(_('repository root cannot be destination'))
483 raise error.Abort(_('repository root cannot be destination'))
484
484
485 kind = opts.get('type') or archival.guesskind(dest) or 'files'
485 kind = opts.get('type') or archival.guesskind(dest) or 'files'
486 prefix = opts.get('prefix')
486 prefix = opts.get('prefix')
487
487
488 if dest == '-':
488 if dest == '-':
489 if kind == 'files':
489 if kind == 'files':
490 raise error.Abort(_('cannot archive plain files to stdout'))
490 raise error.Abort(_('cannot archive plain files to stdout'))
491 dest = cmdutil.makefileobj(ctx, dest)
491 dest = cmdutil.makefileobj(ctx, dest)
492 if not prefix:
492 if not prefix:
493 prefix = os.path.basename(repo.root) + '-%h'
493 prefix = os.path.basename(repo.root) + '-%h'
494
494
495 prefix = cmdutil.makefilename(ctx, prefix)
495 prefix = cmdutil.makefilename(ctx, prefix)
496 match = scmutil.match(ctx, [], opts)
496 match = scmutil.match(ctx, [], opts)
497 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
497 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
498 match, prefix, subrepos=opts.get('subrepos'))
498 match, prefix, subrepos=opts.get('subrepos'))
499
499
500 @command('backout',
500 @command('backout',
501 [('', 'merge', None, _('merge with old dirstate parent after backout')),
501 [('', 'merge', None, _('merge with old dirstate parent after backout')),
502 ('', 'commit', None,
502 ('', 'commit', None,
503 _('commit if no conflicts were encountered (DEPRECATED)')),
503 _('commit if no conflicts were encountered (DEPRECATED)')),
504 ('', 'no-commit', None, _('do not commit')),
504 ('', 'no-commit', None, _('do not commit')),
505 ('', 'parent', '',
505 ('', 'parent', '',
506 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
506 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
507 ('r', 'rev', '', _('revision to backout'), _('REV')),
507 ('r', 'rev', '', _('revision to backout'), _('REV')),
508 ('e', 'edit', False, _('invoke editor on commit messages')),
508 ('e', 'edit', False, _('invoke editor on commit messages')),
509 ] + mergetoolopts + walkopts + commitopts + commitopts2,
509 ] + mergetoolopts + walkopts + commitopts + commitopts2,
510 _('[OPTION]... [-r] REV'))
510 _('[OPTION]... [-r] REV'))
511 def backout(ui, repo, node=None, rev=None, **opts):
511 def backout(ui, repo, node=None, rev=None, **opts):
512 '''reverse effect of earlier changeset
512 '''reverse effect of earlier changeset
513
513
514 Prepare a new changeset with the effect of REV undone in the
514 Prepare a new changeset with the effect of REV undone in the
515 current working directory. If no conflicts were encountered,
515 current working directory. If no conflicts were encountered,
516 it will be committed immediately.
516 it will be committed immediately.
517
517
518 If REV is the parent of the working directory, then this new changeset
518 If REV is the parent of the working directory, then this new changeset
519 is committed automatically (unless --no-commit is specified).
519 is committed automatically (unless --no-commit is specified).
520
520
521 .. note::
521 .. note::
522
522
523 :hg:`backout` cannot be used to fix either an unwanted or
523 :hg:`backout` cannot be used to fix either an unwanted or
524 incorrect merge.
524 incorrect merge.
525
525
526 .. container:: verbose
526 .. container:: verbose
527
527
528 Examples:
528 Examples:
529
529
530 - Reverse the effect of the parent of the working directory.
530 - Reverse the effect of the parent of the working directory.
531 This backout will be committed immediately::
531 This backout will be committed immediately::
532
532
533 hg backout -r .
533 hg backout -r .
534
534
535 - Reverse the effect of previous bad revision 23::
535 - Reverse the effect of previous bad revision 23::
536
536
537 hg backout -r 23
537 hg backout -r 23
538
538
539 - Reverse the effect of previous bad revision 23 and
539 - Reverse the effect of previous bad revision 23 and
540 leave changes uncommitted::
540 leave changes uncommitted::
541
541
542 hg backout -r 23 --no-commit
542 hg backout -r 23 --no-commit
543 hg commit -m "Backout revision 23"
543 hg commit -m "Backout revision 23"
544
544
545 By default, the pending changeset will have one parent,
545 By default, the pending changeset will have one parent,
546 maintaining a linear history. With --merge, the pending
546 maintaining a linear history. With --merge, the pending
547 changeset will instead have two parents: the old parent of the
547 changeset will instead have two parents: the old parent of the
548 working directory and a new child of REV that simply undoes REV.
548 working directory and a new child of REV that simply undoes REV.
549
549
550 Before version 1.7, the behavior without --merge was equivalent
550 Before version 1.7, the behavior without --merge was equivalent
551 to specifying --merge followed by :hg:`update --clean .` to
551 to specifying --merge followed by :hg:`update --clean .` to
552 cancel the merge and leave the child of REV as a head to be
552 cancel the merge and leave the child of REV as a head to be
553 merged separately.
553 merged separately.
554
554
555 See :hg:`help dates` for a list of formats valid for -d/--date.
555 See :hg:`help dates` for a list of formats valid for -d/--date.
556
556
557 See :hg:`help revert` for a way to restore files to the state
557 See :hg:`help revert` for a way to restore files to the state
558 of another revision.
558 of another revision.
559
559
560 Returns 0 on success, 1 if nothing to backout or there are unresolved
560 Returns 0 on success, 1 if nothing to backout or there are unresolved
561 files.
561 files.
562 '''
562 '''
563 with repo.wlock(), repo.lock():
563 with repo.wlock(), repo.lock():
564 return _dobackout(ui, repo, node, rev, **opts)
564 return _dobackout(ui, repo, node, rev, **opts)
565
565
566 def _dobackout(ui, repo, node=None, rev=None, **opts):
566 def _dobackout(ui, repo, node=None, rev=None, **opts):
567 opts = pycompat.byteskwargs(opts)
567 opts = pycompat.byteskwargs(opts)
568 if opts.get('commit') and opts.get('no_commit'):
568 if opts.get('commit') and opts.get('no_commit'):
569 raise error.Abort(_("cannot use --commit with --no-commit"))
569 raise error.Abort(_("cannot use --commit with --no-commit"))
570 if opts.get('merge') and opts.get('no_commit'):
570 if opts.get('merge') and opts.get('no_commit'):
571 raise error.Abort(_("cannot use --merge with --no-commit"))
571 raise error.Abort(_("cannot use --merge with --no-commit"))
572
572
573 if rev and node:
573 if rev and node:
574 raise error.Abort(_("please specify just one revision"))
574 raise error.Abort(_("please specify just one revision"))
575
575
576 if not rev:
576 if not rev:
577 rev = node
577 rev = node
578
578
579 if not rev:
579 if not rev:
580 raise error.Abort(_("please specify a revision to backout"))
580 raise error.Abort(_("please specify a revision to backout"))
581
581
582 date = opts.get('date')
582 date = opts.get('date')
583 if date:
583 if date:
584 opts['date'] = dateutil.parsedate(date)
584 opts['date'] = dateutil.parsedate(date)
585
585
586 cmdutil.checkunfinished(repo)
586 cmdutil.checkunfinished(repo)
587 cmdutil.bailifchanged(repo)
587 cmdutil.bailifchanged(repo)
588 node = scmutil.revsingle(repo, rev).node()
588 node = scmutil.revsingle(repo, rev).node()
589
589
590 op1, op2 = repo.dirstate.parents()
590 op1, op2 = repo.dirstate.parents()
591 if not repo.changelog.isancestor(node, op1):
591 if not repo.changelog.isancestor(node, op1):
592 raise error.Abort(_('cannot backout change that is not an ancestor'))
592 raise error.Abort(_('cannot backout change that is not an ancestor'))
593
593
594 p1, p2 = repo.changelog.parents(node)
594 p1, p2 = repo.changelog.parents(node)
595 if p1 == nullid:
595 if p1 == nullid:
596 raise error.Abort(_('cannot backout a change with no parents'))
596 raise error.Abort(_('cannot backout a change with no parents'))
597 if p2 != nullid:
597 if p2 != nullid:
598 if not opts.get('parent'):
598 if not opts.get('parent'):
599 raise error.Abort(_('cannot backout a merge changeset'))
599 raise error.Abort(_('cannot backout a merge changeset'))
600 p = repo.lookup(opts['parent'])
600 p = repo.lookup(opts['parent'])
601 if p not in (p1, p2):
601 if p not in (p1, p2):
602 raise error.Abort(_('%s is not a parent of %s') %
602 raise error.Abort(_('%s is not a parent of %s') %
603 (short(p), short(node)))
603 (short(p), short(node)))
604 parent = p
604 parent = p
605 else:
605 else:
606 if opts.get('parent'):
606 if opts.get('parent'):
607 raise error.Abort(_('cannot use --parent on non-merge changeset'))
607 raise error.Abort(_('cannot use --parent on non-merge changeset'))
608 parent = p1
608 parent = p1
609
609
610 # the backout should appear on the same branch
610 # the backout should appear on the same branch
611 branch = repo.dirstate.branch()
611 branch = repo.dirstate.branch()
612 bheads = repo.branchheads(branch)
612 bheads = repo.branchheads(branch)
613 rctx = scmutil.revsingle(repo, hex(parent))
613 rctx = scmutil.revsingle(repo, hex(parent))
614 if not opts.get('merge') and op1 != node:
614 if not opts.get('merge') and op1 != node:
615 with dirstateguard.dirstateguard(repo, 'backout'):
615 with dirstateguard.dirstateguard(repo, 'backout'):
616 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
616 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
617 with ui.configoverride(overrides, 'backout'):
617 with ui.configoverride(overrides, 'backout'):
618 stats = mergemod.update(repo, parent, True, True, node, False)
618 stats = mergemod.update(repo, parent, True, True, node, False)
619 repo.setparents(op1, op2)
619 repo.setparents(op1, op2)
620 hg._showstats(repo, stats)
620 hg._showstats(repo, stats)
621 if stats.unresolvedcount:
621 if stats.unresolvedcount:
622 repo.ui.status(_("use 'hg resolve' to retry unresolved "
622 repo.ui.status(_("use 'hg resolve' to retry unresolved "
623 "file merges\n"))
623 "file merges\n"))
624 return 1
624 return 1
625 else:
625 else:
626 hg.clean(repo, node, show_stats=False)
626 hg.clean(repo, node, show_stats=False)
627 repo.dirstate.setbranch(branch)
627 repo.dirstate.setbranch(branch)
628 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
628 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
629
629
630 if opts.get('no_commit'):
630 if opts.get('no_commit'):
631 msg = _("changeset %s backed out, "
631 msg = _("changeset %s backed out, "
632 "don't forget to commit.\n")
632 "don't forget to commit.\n")
633 ui.status(msg % short(node))
633 ui.status(msg % short(node))
634 return 0
634 return 0
635
635
636 def commitfunc(ui, repo, message, match, opts):
636 def commitfunc(ui, repo, message, match, opts):
637 editform = 'backout'
637 editform = 'backout'
638 e = cmdutil.getcommiteditor(editform=editform,
638 e = cmdutil.getcommiteditor(editform=editform,
639 **pycompat.strkwargs(opts))
639 **pycompat.strkwargs(opts))
640 if not message:
640 if not message:
641 # we don't translate commit messages
641 # we don't translate commit messages
642 message = "Backed out changeset %s" % short(node)
642 message = "Backed out changeset %s" % short(node)
643 e = cmdutil.getcommiteditor(edit=True, editform=editform)
643 e = cmdutil.getcommiteditor(edit=True, editform=editform)
644 return repo.commit(message, opts.get('user'), opts.get('date'),
644 return repo.commit(message, opts.get('user'), opts.get('date'),
645 match, editor=e)
645 match, editor=e)
646 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
646 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
647 if not newnode:
647 if not newnode:
648 ui.status(_("nothing changed\n"))
648 ui.status(_("nothing changed\n"))
649 return 1
649 return 1
650 cmdutil.commitstatus(repo, newnode, branch, bheads)
650 cmdutil.commitstatus(repo, newnode, branch, bheads)
651
651
652 def nice(node):
652 def nice(node):
653 return '%d:%s' % (repo.changelog.rev(node), short(node))
653 return '%d:%s' % (repo.changelog.rev(node), short(node))
654 ui.status(_('changeset %s backs out changeset %s\n') %
654 ui.status(_('changeset %s backs out changeset %s\n') %
655 (nice(repo.changelog.tip()), nice(node)))
655 (nice(repo.changelog.tip()), nice(node)))
656 if opts.get('merge') and op1 != node:
656 if opts.get('merge') and op1 != node:
657 hg.clean(repo, op1, show_stats=False)
657 hg.clean(repo, op1, show_stats=False)
658 ui.status(_('merging with changeset %s\n')
658 ui.status(_('merging with changeset %s\n')
659 % nice(repo.changelog.tip()))
659 % nice(repo.changelog.tip()))
660 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
660 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
661 with ui.configoverride(overrides, 'backout'):
661 with ui.configoverride(overrides, 'backout'):
662 return hg.merge(repo, hex(repo.changelog.tip()))
662 return hg.merge(repo, hex(repo.changelog.tip()))
663 return 0
663 return 0
664
664
665 @command('bisect',
665 @command('bisect',
666 [('r', 'reset', False, _('reset bisect state')),
666 [('r', 'reset', False, _('reset bisect state')),
667 ('g', 'good', False, _('mark changeset good')),
667 ('g', 'good', False, _('mark changeset good')),
668 ('b', 'bad', False, _('mark changeset bad')),
668 ('b', 'bad', False, _('mark changeset bad')),
669 ('s', 'skip', False, _('skip testing changeset')),
669 ('s', 'skip', False, _('skip testing changeset')),
670 ('e', 'extend', False, _('extend the bisect range')),
670 ('e', 'extend', False, _('extend the bisect range')),
671 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
671 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
672 ('U', 'noupdate', False, _('do not update to target'))],
672 ('U', 'noupdate', False, _('do not update to target'))],
673 _("[-gbsr] [-U] [-c CMD] [REV]"))
673 _("[-gbsr] [-U] [-c CMD] [REV]"))
674 def bisect(ui, repo, rev=None, extra=None, command=None,
674 def bisect(ui, repo, rev=None, extra=None, command=None,
675 reset=None, good=None, bad=None, skip=None, extend=None,
675 reset=None, good=None, bad=None, skip=None, extend=None,
676 noupdate=None):
676 noupdate=None):
677 """subdivision search of changesets
677 """subdivision search of changesets
678
678
679 This command helps to find changesets which introduce problems. To
679 This command helps to find changesets which introduce problems. To
680 use, mark the earliest changeset you know exhibits the problem as
680 use, mark the earliest changeset you know exhibits the problem as
681 bad, then mark the latest changeset which is free from the problem
681 bad, then mark the latest changeset which is free from the problem
682 as good. Bisect will update your working directory to a revision
682 as good. Bisect will update your working directory to a revision
683 for testing (unless the -U/--noupdate option is specified). Once
683 for testing (unless the -U/--noupdate option is specified). Once
684 you have performed tests, mark the working directory as good or
684 you have performed tests, mark the working directory as good or
685 bad, and bisect will either update to another candidate changeset
685 bad, and bisect will either update to another candidate changeset
686 or announce that it has found the bad revision.
686 or announce that it has found the bad revision.
687
687
688 As a shortcut, you can also use the revision argument to mark a
688 As a shortcut, you can also use the revision argument to mark a
689 revision as good or bad without checking it out first.
689 revision as good or bad without checking it out first.
690
690
691 If you supply a command, it will be used for automatic bisection.
691 If you supply a command, it will be used for automatic bisection.
692 The environment variable HG_NODE will contain the ID of the
692 The environment variable HG_NODE will contain the ID of the
693 changeset being tested. The exit status of the command will be
693 changeset being tested. The exit status of the command will be
694 used to mark revisions as good or bad: status 0 means good, 125
694 used to mark revisions as good or bad: status 0 means good, 125
695 means to skip the revision, 127 (command not found) will abort the
695 means to skip the revision, 127 (command not found) will abort the
696 bisection, and any other non-zero exit status means the revision
696 bisection, and any other non-zero exit status means the revision
697 is bad.
697 is bad.
698
698
699 .. container:: verbose
699 .. container:: verbose
700
700
701 Some examples:
701 Some examples:
702
702
703 - start a bisection with known bad revision 34, and good revision 12::
703 - start a bisection with known bad revision 34, and good revision 12::
704
704
705 hg bisect --bad 34
705 hg bisect --bad 34
706 hg bisect --good 12
706 hg bisect --good 12
707
707
708 - advance the current bisection by marking current revision as good or
708 - advance the current bisection by marking current revision as good or
709 bad::
709 bad::
710
710
711 hg bisect --good
711 hg bisect --good
712 hg bisect --bad
712 hg bisect --bad
713
713
714 - mark the current revision, or a known revision, to be skipped (e.g. if
714 - mark the current revision, or a known revision, to be skipped (e.g. if
715 that revision is not usable because of another issue)::
715 that revision is not usable because of another issue)::
716
716
717 hg bisect --skip
717 hg bisect --skip
718 hg bisect --skip 23
718 hg bisect --skip 23
719
719
720 - skip all revisions that do not touch directories ``foo`` or ``bar``::
720 - skip all revisions that do not touch directories ``foo`` or ``bar``::
721
721
722 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
722 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
723
723
724 - forget the current bisection::
724 - forget the current bisection::
725
725
726 hg bisect --reset
726 hg bisect --reset
727
727
728 - use 'make && make tests' to automatically find the first broken
728 - use 'make && make tests' to automatically find the first broken
729 revision::
729 revision::
730
730
731 hg bisect --reset
731 hg bisect --reset
732 hg bisect --bad 34
732 hg bisect --bad 34
733 hg bisect --good 12
733 hg bisect --good 12
734 hg bisect --command "make && make tests"
734 hg bisect --command "make && make tests"
735
735
736 - see all changesets whose states are already known in the current
736 - see all changesets whose states are already known in the current
737 bisection::
737 bisection::
738
738
739 hg log -r "bisect(pruned)"
739 hg log -r "bisect(pruned)"
740
740
741 - see the changeset currently being bisected (especially useful
741 - see the changeset currently being bisected (especially useful
742 if running with -U/--noupdate)::
742 if running with -U/--noupdate)::
743
743
744 hg log -r "bisect(current)"
744 hg log -r "bisect(current)"
745
745
746 - see all changesets that took part in the current bisection::
746 - see all changesets that took part in the current bisection::
747
747
748 hg log -r "bisect(range)"
748 hg log -r "bisect(range)"
749
749
750 - you can even get a nice graph::
750 - you can even get a nice graph::
751
751
752 hg log --graph -r "bisect(range)"
752 hg log --graph -r "bisect(range)"
753
753
754 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
754 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
755
755
756 Returns 0 on success.
756 Returns 0 on success.
757 """
757 """
758 # backward compatibility
758 # backward compatibility
759 if rev in "good bad reset init".split():
759 if rev in "good bad reset init".split():
760 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
760 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
761 cmd, rev, extra = rev, extra, None
761 cmd, rev, extra = rev, extra, None
762 if cmd == "good":
762 if cmd == "good":
763 good = True
763 good = True
764 elif cmd == "bad":
764 elif cmd == "bad":
765 bad = True
765 bad = True
766 else:
766 else:
767 reset = True
767 reset = True
768 elif extra:
768 elif extra:
769 raise error.Abort(_('incompatible arguments'))
769 raise error.Abort(_('incompatible arguments'))
770
770
771 incompatibles = {
771 incompatibles = {
772 '--bad': bad,
772 '--bad': bad,
773 '--command': bool(command),
773 '--command': bool(command),
774 '--extend': extend,
774 '--extend': extend,
775 '--good': good,
775 '--good': good,
776 '--reset': reset,
776 '--reset': reset,
777 '--skip': skip,
777 '--skip': skip,
778 }
778 }
779
779
780 enabled = [x for x in incompatibles if incompatibles[x]]
780 enabled = [x for x in incompatibles if incompatibles[x]]
781
781
782 if len(enabled) > 1:
782 if len(enabled) > 1:
783 raise error.Abort(_('%s and %s are incompatible') %
783 raise error.Abort(_('%s and %s are incompatible') %
784 tuple(sorted(enabled)[0:2]))
784 tuple(sorted(enabled)[0:2]))
785
785
786 if reset:
786 if reset:
787 hbisect.resetstate(repo)
787 hbisect.resetstate(repo)
788 return
788 return
789
789
790 state = hbisect.load_state(repo)
790 state = hbisect.load_state(repo)
791
791
792 # update state
792 # update state
793 if good or bad or skip:
793 if good or bad or skip:
794 if rev:
794 if rev:
795 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
795 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
796 else:
796 else:
797 nodes = [repo.lookup('.')]
797 nodes = [repo.lookup('.')]
798 if good:
798 if good:
799 state['good'] += nodes
799 state['good'] += nodes
800 elif bad:
800 elif bad:
801 state['bad'] += nodes
801 state['bad'] += nodes
802 elif skip:
802 elif skip:
803 state['skip'] += nodes
803 state['skip'] += nodes
804 hbisect.save_state(repo, state)
804 hbisect.save_state(repo, state)
805 if not (state['good'] and state['bad']):
805 if not (state['good'] and state['bad']):
806 return
806 return
807
807
808 def mayupdate(repo, node, show_stats=True):
808 def mayupdate(repo, node, show_stats=True):
809 """common used update sequence"""
809 """common used update sequence"""
810 if noupdate:
810 if noupdate:
811 return
811 return
812 cmdutil.checkunfinished(repo)
812 cmdutil.checkunfinished(repo)
813 cmdutil.bailifchanged(repo)
813 cmdutil.bailifchanged(repo)
814 return hg.clean(repo, node, show_stats=show_stats)
814 return hg.clean(repo, node, show_stats=show_stats)
815
815
816 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
816 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
817
817
818 if command:
818 if command:
819 changesets = 1
819 changesets = 1
820 if noupdate:
820 if noupdate:
821 try:
821 try:
822 node = state['current'][0]
822 node = state['current'][0]
823 except LookupError:
823 except LookupError:
824 raise error.Abort(_('current bisect revision is unknown - '
824 raise error.Abort(_('current bisect revision is unknown - '
825 'start a new bisect to fix'))
825 'start a new bisect to fix'))
826 else:
826 else:
827 node, p2 = repo.dirstate.parents()
827 node, p2 = repo.dirstate.parents()
828 if p2 != nullid:
828 if p2 != nullid:
829 raise error.Abort(_('current bisect revision is a merge'))
829 raise error.Abort(_('current bisect revision is a merge'))
830 if rev:
830 if rev:
831 node = repo[scmutil.revsingle(repo, rev, node)].node()
831 node = repo[scmutil.revsingle(repo, rev, node)].node()
832 try:
832 try:
833 while changesets:
833 while changesets:
834 # update state
834 # update state
835 state['current'] = [node]
835 state['current'] = [node]
836 hbisect.save_state(repo, state)
836 hbisect.save_state(repo, state)
837 status = ui.system(command, environ={'HG_NODE': hex(node)},
837 status = ui.system(command, environ={'HG_NODE': hex(node)},
838 blockedtag='bisect_check')
838 blockedtag='bisect_check')
839 if status == 125:
839 if status == 125:
840 transition = "skip"
840 transition = "skip"
841 elif status == 0:
841 elif status == 0:
842 transition = "good"
842 transition = "good"
843 # status < 0 means process was killed
843 # status < 0 means process was killed
844 elif status == 127:
844 elif status == 127:
845 raise error.Abort(_("failed to execute %s") % command)
845 raise error.Abort(_("failed to execute %s") % command)
846 elif status < 0:
846 elif status < 0:
847 raise error.Abort(_("%s killed") % command)
847 raise error.Abort(_("%s killed") % command)
848 else:
848 else:
849 transition = "bad"
849 transition = "bad"
850 state[transition].append(node)
850 state[transition].append(node)
851 ctx = repo[node]
851 ctx = repo[node]
852 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
852 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
853 transition))
853 transition))
854 hbisect.checkstate(state)
854 hbisect.checkstate(state)
855 # bisect
855 # bisect
856 nodes, changesets, bgood = hbisect.bisect(repo, state)
856 nodes, changesets, bgood = hbisect.bisect(repo, state)
857 # update to next check
857 # update to next check
858 node = nodes[0]
858 node = nodes[0]
859 mayupdate(repo, node, show_stats=False)
859 mayupdate(repo, node, show_stats=False)
860 finally:
860 finally:
861 state['current'] = [node]
861 state['current'] = [node]
862 hbisect.save_state(repo, state)
862 hbisect.save_state(repo, state)
863 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
863 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
864 return
864 return
865
865
866 hbisect.checkstate(state)
866 hbisect.checkstate(state)
867
867
868 # actually bisect
868 # actually bisect
869 nodes, changesets, good = hbisect.bisect(repo, state)
869 nodes, changesets, good = hbisect.bisect(repo, state)
870 if extend:
870 if extend:
871 if not changesets:
871 if not changesets:
872 extendnode = hbisect.extendrange(repo, state, nodes, good)
872 extendnode = hbisect.extendrange(repo, state, nodes, good)
873 if extendnode is not None:
873 if extendnode is not None:
874 ui.write(_("Extending search to changeset %d:%s\n")
874 ui.write(_("Extending search to changeset %d:%s\n")
875 % (extendnode.rev(), extendnode))
875 % (extendnode.rev(), extendnode))
876 state['current'] = [extendnode.node()]
876 state['current'] = [extendnode.node()]
877 hbisect.save_state(repo, state)
877 hbisect.save_state(repo, state)
878 return mayupdate(repo, extendnode.node())
878 return mayupdate(repo, extendnode.node())
879 raise error.Abort(_("nothing to extend"))
879 raise error.Abort(_("nothing to extend"))
880
880
881 if changesets == 0:
881 if changesets == 0:
882 hbisect.printresult(ui, repo, state, displayer, nodes, good)
882 hbisect.printresult(ui, repo, state, displayer, nodes, good)
883 else:
883 else:
884 assert len(nodes) == 1 # only a single node can be tested next
884 assert len(nodes) == 1 # only a single node can be tested next
885 node = nodes[0]
885 node = nodes[0]
886 # compute the approximate number of remaining tests
886 # compute the approximate number of remaining tests
887 tests, size = 0, 2
887 tests, size = 0, 2
888 while size <= changesets:
888 while size <= changesets:
889 tests, size = tests + 1, size * 2
889 tests, size = tests + 1, size * 2
890 rev = repo.changelog.rev(node)
890 rev = repo.changelog.rev(node)
891 ui.write(_("Testing changeset %d:%s "
891 ui.write(_("Testing changeset %d:%s "
892 "(%d changesets remaining, ~%d tests)\n")
892 "(%d changesets remaining, ~%d tests)\n")
893 % (rev, short(node), changesets, tests))
893 % (rev, short(node), changesets, tests))
894 state['current'] = [node]
894 state['current'] = [node]
895 hbisect.save_state(repo, state)
895 hbisect.save_state(repo, state)
896 return mayupdate(repo, node)
896 return mayupdate(repo, node)
897
897
898 @command('bookmarks|bookmark',
898 @command('bookmarks|bookmark',
899 [('f', 'force', False, _('force')),
899 [('f', 'force', False, _('force')),
900 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
900 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
901 ('d', 'delete', False, _('delete a given bookmark')),
901 ('d', 'delete', False, _('delete a given bookmark')),
902 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
902 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
903 ('i', 'inactive', False, _('mark a bookmark inactive')),
903 ('i', 'inactive', False, _('mark a bookmark inactive')),
904 ] + formatteropts,
904 ] + formatteropts,
905 _('hg bookmarks [OPTIONS]... [NAME]...'))
905 _('hg bookmarks [OPTIONS]... [NAME]...'))
906 def bookmark(ui, repo, *names, **opts):
906 def bookmark(ui, repo, *names, **opts):
907 '''create a new bookmark or list existing bookmarks
907 '''create a new bookmark or list existing bookmarks
908
908
909 Bookmarks are labels on changesets to help track lines of development.
909 Bookmarks are labels on changesets to help track lines of development.
910 Bookmarks are unversioned and can be moved, renamed and deleted.
910 Bookmarks are unversioned and can be moved, renamed and deleted.
911 Deleting or moving a bookmark has no effect on the associated changesets.
911 Deleting or moving a bookmark has no effect on the associated changesets.
912
912
913 Creating or updating to a bookmark causes it to be marked as 'active'.
913 Creating or updating to a bookmark causes it to be marked as 'active'.
914 The active bookmark is indicated with a '*'.
914 The active bookmark is indicated with a '*'.
915 When a commit is made, the active bookmark will advance to the new commit.
915 When a commit is made, the active bookmark will advance to the new commit.
916 A plain :hg:`update` will also advance an active bookmark, if possible.
916 A plain :hg:`update` will also advance an active bookmark, if possible.
917 Updating away from a bookmark will cause it to be deactivated.
917 Updating away from a bookmark will cause it to be deactivated.
918
918
919 Bookmarks can be pushed and pulled between repositories (see
919 Bookmarks can be pushed and pulled between repositories (see
920 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
920 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
921 diverged, a new 'divergent bookmark' of the form 'name@path' will
921 diverged, a new 'divergent bookmark' of the form 'name@path' will
922 be created. Using :hg:`merge` will resolve the divergence.
922 be created. Using :hg:`merge` will resolve the divergence.
923
923
924 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
924 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
925 the active bookmark's name.
925 the active bookmark's name.
926
926
927 A bookmark named '@' has the special property that :hg:`clone` will
927 A bookmark named '@' has the special property that :hg:`clone` will
928 check it out by default if it exists.
928 check it out by default if it exists.
929
929
930 .. container:: verbose
930 .. container:: verbose
931
931
932 Examples:
932 Examples:
933
933
934 - create an active bookmark for a new line of development::
934 - create an active bookmark for a new line of development::
935
935
936 hg book new-feature
936 hg book new-feature
937
937
938 - create an inactive bookmark as a place marker::
938 - create an inactive bookmark as a place marker::
939
939
940 hg book -i reviewed
940 hg book -i reviewed
941
941
942 - create an inactive bookmark on another changeset::
942 - create an inactive bookmark on another changeset::
943
943
944 hg book -r .^ tested
944 hg book -r .^ tested
945
945
946 - rename bookmark turkey to dinner::
946 - rename bookmark turkey to dinner::
947
947
948 hg book -m turkey dinner
948 hg book -m turkey dinner
949
949
950 - move the '@' bookmark from another branch::
950 - move the '@' bookmark from another branch::
951
951
952 hg book -f @
952 hg book -f @
953 '''
953 '''
954 force = opts.get(r'force')
954 force = opts.get(r'force')
955 rev = opts.get(r'rev')
955 rev = opts.get(r'rev')
956 delete = opts.get(r'delete')
956 delete = opts.get(r'delete')
957 rename = opts.get(r'rename')
957 rename = opts.get(r'rename')
958 inactive = opts.get(r'inactive')
958 inactive = opts.get(r'inactive')
959
959
960 if delete and rename:
960 if delete and rename:
961 raise error.Abort(_("--delete and --rename are incompatible"))
961 raise error.Abort(_("--delete and --rename are incompatible"))
962 if delete and rev:
962 if delete and rev:
963 raise error.Abort(_("--rev is incompatible with --delete"))
963 raise error.Abort(_("--rev is incompatible with --delete"))
964 if rename and rev:
964 if rename and rev:
965 raise error.Abort(_("--rev is incompatible with --rename"))
965 raise error.Abort(_("--rev is incompatible with --rename"))
966 if not names and (delete or rev):
966 if not names and (delete or rev):
967 raise error.Abort(_("bookmark name required"))
967 raise error.Abort(_("bookmark name required"))
968
968
969 if delete or rename or names or inactive:
969 if delete or rename or names or inactive:
970 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
970 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
971 if delete:
971 if delete:
972 names = pycompat.maplist(repo._bookmarks.expandname, names)
972 names = pycompat.maplist(repo._bookmarks.expandname, names)
973 bookmarks.delete(repo, tr, names)
973 bookmarks.delete(repo, tr, names)
974 elif rename:
974 elif rename:
975 if not names:
975 if not names:
976 raise error.Abort(_("new bookmark name required"))
976 raise error.Abort(_("new bookmark name required"))
977 elif len(names) > 1:
977 elif len(names) > 1:
978 raise error.Abort(_("only one new bookmark name allowed"))
978 raise error.Abort(_("only one new bookmark name allowed"))
979 rename = repo._bookmarks.expandname(rename)
979 rename = repo._bookmarks.expandname(rename)
980 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
980 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
981 elif names:
981 elif names:
982 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
982 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
983 elif inactive:
983 elif inactive:
984 if len(repo._bookmarks) == 0:
984 if len(repo._bookmarks) == 0:
985 ui.status(_("no bookmarks set\n"))
985 ui.status(_("no bookmarks set\n"))
986 elif not repo._activebookmark:
986 elif not repo._activebookmark:
987 ui.status(_("no active bookmark\n"))
987 ui.status(_("no active bookmark\n"))
988 else:
988 else:
989 bookmarks.deactivate(repo)
989 bookmarks.deactivate(repo)
990 else: # show bookmarks
990 else: # show bookmarks
991 bookmarks.printbookmarks(ui, repo, **opts)
991 bookmarks.printbookmarks(ui, repo, **opts)
992
992
993 @command('branch',
993 @command('branch',
994 [('f', 'force', None,
994 [('f', 'force', None,
995 _('set branch name even if it shadows an existing branch')),
995 _('set branch name even if it shadows an existing branch')),
996 ('C', 'clean', None, _('reset branch name to parent branch name')),
996 ('C', 'clean', None, _('reset branch name to parent branch name')),
997 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
997 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
998 ],
998 ],
999 _('[-fC] [NAME]'))
999 _('[-fC] [NAME]'))
1000 def branch(ui, repo, label=None, **opts):
1000 def branch(ui, repo, label=None, **opts):
1001 """set or show the current branch name
1001 """set or show the current branch name
1002
1002
1003 .. note::
1003 .. note::
1004
1004
1005 Branch names are permanent and global. Use :hg:`bookmark` to create a
1005 Branch names are permanent and global. Use :hg:`bookmark` to create a
1006 light-weight bookmark instead. See :hg:`help glossary` for more
1006 light-weight bookmark instead. See :hg:`help glossary` for more
1007 information about named branches and bookmarks.
1007 information about named branches and bookmarks.
1008
1008
1009 With no argument, show the current branch name. With one argument,
1009 With no argument, show the current branch name. With one argument,
1010 set the working directory branch name (the branch will not exist
1010 set the working directory branch name (the branch will not exist
1011 in the repository until the next commit). Standard practice
1011 in the repository until the next commit). Standard practice
1012 recommends that primary development take place on the 'default'
1012 recommends that primary development take place on the 'default'
1013 branch.
1013 branch.
1014
1014
1015 Unless -f/--force is specified, branch will not let you set a
1015 Unless -f/--force is specified, branch will not let you set a
1016 branch name that already exists.
1016 branch name that already exists.
1017
1017
1018 Use -C/--clean to reset the working directory branch to that of
1018 Use -C/--clean to reset the working directory branch to that of
1019 the parent of the working directory, negating a previous branch
1019 the parent of the working directory, negating a previous branch
1020 change.
1020 change.
1021
1021
1022 Use the command :hg:`update` to switch to an existing branch. Use
1022 Use the command :hg:`update` to switch to an existing branch. Use
1023 :hg:`commit --close-branch` to mark this branch head as closed.
1023 :hg:`commit --close-branch` to mark this branch head as closed.
1024 When all heads of a branch are closed, the branch will be
1024 When all heads of a branch are closed, the branch will be
1025 considered closed.
1025 considered closed.
1026
1026
1027 Returns 0 on success.
1027 Returns 0 on success.
1028 """
1028 """
1029 opts = pycompat.byteskwargs(opts)
1029 opts = pycompat.byteskwargs(opts)
1030 revs = opts.get('rev')
1030 revs = opts.get('rev')
1031 if label:
1031 if label:
1032 label = label.strip()
1032 label = label.strip()
1033
1033
1034 if not opts.get('clean') and not label:
1034 if not opts.get('clean') and not label:
1035 if revs:
1035 if revs:
1036 raise error.Abort(_("no branch name specified for the revisions"))
1036 raise error.Abort(_("no branch name specified for the revisions"))
1037 ui.write("%s\n" % repo.dirstate.branch())
1037 ui.write("%s\n" % repo.dirstate.branch())
1038 return
1038 return
1039
1039
1040 with repo.wlock():
1040 with repo.wlock():
1041 if opts.get('clean'):
1041 if opts.get('clean'):
1042 label = repo[None].p1().branch()
1042 label = repo[None].p1().branch()
1043 repo.dirstate.setbranch(label)
1043 repo.dirstate.setbranch(label)
1044 ui.status(_('reset working directory to branch %s\n') % label)
1044 ui.status(_('reset working directory to branch %s\n') % label)
1045 elif label:
1045 elif label:
1046
1046
1047 scmutil.checknewlabel(repo, label, 'branch')
1047 scmutil.checknewlabel(repo, label, 'branch')
1048 if revs:
1048 if revs:
1049 return cmdutil.changebranch(ui, repo, revs, label)
1049 return cmdutil.changebranch(ui, repo, revs, label)
1050
1050
1051 if not opts.get('force') and label in repo.branchmap():
1051 if not opts.get('force') and label in repo.branchmap():
1052 if label not in [p.branch() for p in repo[None].parents()]:
1052 if label not in [p.branch() for p in repo[None].parents()]:
1053 raise error.Abort(_('a branch of the same name already'
1053 raise error.Abort(_('a branch of the same name already'
1054 ' exists'),
1054 ' exists'),
1055 # i18n: "it" refers to an existing branch
1055 # i18n: "it" refers to an existing branch
1056 hint=_("use 'hg update' to switch to it"))
1056 hint=_("use 'hg update' to switch to it"))
1057
1057
1058 repo.dirstate.setbranch(label)
1058 repo.dirstate.setbranch(label)
1059 ui.status(_('marked working directory as branch %s\n') % label)
1059 ui.status(_('marked working directory as branch %s\n') % label)
1060
1060
1061 # find any open named branches aside from default
1061 # find any open named branches aside from default
1062 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1062 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1063 if n != "default" and not c]
1063 if n != "default" and not c]
1064 if not others:
1064 if not others:
1065 ui.status(_('(branches are permanent and global, '
1065 ui.status(_('(branches are permanent and global, '
1066 'did you want a bookmark?)\n'))
1066 'did you want a bookmark?)\n'))
1067
1067
1068 @command('branches',
1068 @command('branches',
1069 [('a', 'active', False,
1069 [('a', 'active', False,
1070 _('show only branches that have unmerged heads (DEPRECATED)')),
1070 _('show only branches that have unmerged heads (DEPRECATED)')),
1071 ('c', 'closed', False, _('show normal and closed branches')),
1071 ('c', 'closed', False, _('show normal and closed branches')),
1072 ] + formatteropts,
1072 ] + formatteropts,
1073 _('[-c]'),
1073 _('[-c]'),
1074 intents={INTENT_READONLY})
1074 intents={INTENT_READONLY})
1075 def branches(ui, repo, active=False, closed=False, **opts):
1075 def branches(ui, repo, active=False, closed=False, **opts):
1076 """list repository named branches
1076 """list repository named branches
1077
1077
1078 List the repository's named branches, indicating which ones are
1078 List the repository's named branches, indicating which ones are
1079 inactive. If -c/--closed is specified, also list branches which have
1079 inactive. If -c/--closed is specified, also list branches which have
1080 been marked closed (see :hg:`commit --close-branch`).
1080 been marked closed (see :hg:`commit --close-branch`).
1081
1081
1082 Use the command :hg:`update` to switch to an existing branch.
1082 Use the command :hg:`update` to switch to an existing branch.
1083
1083
1084 Returns 0.
1084 Returns 0.
1085 """
1085 """
1086
1086
1087 opts = pycompat.byteskwargs(opts)
1087 opts = pycompat.byteskwargs(opts)
1088 ui.pager('branches')
1088 ui.pager('branches')
1089 fm = ui.formatter('branches', opts)
1089 fm = ui.formatter('branches', opts)
1090 hexfunc = fm.hexfunc
1090 hexfunc = fm.hexfunc
1091
1091
1092 allheads = set(repo.heads())
1092 allheads = set(repo.heads())
1093 branches = []
1093 branches = []
1094 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1094 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1095 isactive = False
1095 isactive = False
1096 if not isclosed:
1096 if not isclosed:
1097 openheads = set(repo.branchmap().iteropen(heads))
1097 openheads = set(repo.branchmap().iteropen(heads))
1098 isactive = bool(openheads & allheads)
1098 isactive = bool(openheads & allheads)
1099 branches.append((tag, repo[tip], isactive, not isclosed))
1099 branches.append((tag, repo[tip], isactive, not isclosed))
1100 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1100 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1101 reverse=True)
1101 reverse=True)
1102
1102
1103 for tag, ctx, isactive, isopen in branches:
1103 for tag, ctx, isactive, isopen in branches:
1104 if active and not isactive:
1104 if active and not isactive:
1105 continue
1105 continue
1106 if isactive:
1106 if isactive:
1107 label = 'branches.active'
1107 label = 'branches.active'
1108 notice = ''
1108 notice = ''
1109 elif not isopen:
1109 elif not isopen:
1110 if not closed:
1110 if not closed:
1111 continue
1111 continue
1112 label = 'branches.closed'
1112 label = 'branches.closed'
1113 notice = _(' (closed)')
1113 notice = _(' (closed)')
1114 else:
1114 else:
1115 label = 'branches.inactive'
1115 label = 'branches.inactive'
1116 notice = _(' (inactive)')
1116 notice = _(' (inactive)')
1117 current = (tag == repo.dirstate.branch())
1117 current = (tag == repo.dirstate.branch())
1118 if current:
1118 if current:
1119 label = 'branches.current'
1119 label = 'branches.current'
1120
1120
1121 fm.startitem()
1121 fm.startitem()
1122 fm.write('branch', '%s', tag, label=label)
1122 fm.write('branch', '%s', tag, label=label)
1123 rev = ctx.rev()
1123 rev = ctx.rev()
1124 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1124 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1125 fmt = ' ' * padsize + ' %d:%s'
1125 fmt = ' ' * padsize + ' %d:%s'
1126 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1126 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1127 label='log.changeset changeset.%s' % ctx.phasestr())
1127 label='log.changeset changeset.%s' % ctx.phasestr())
1128 fm.context(ctx=ctx)
1128 fm.context(ctx=ctx)
1129 fm.data(active=isactive, closed=not isopen, current=current)
1129 fm.data(active=isactive, closed=not isopen, current=current)
1130 if not ui.quiet:
1130 if not ui.quiet:
1131 fm.plain(notice)
1131 fm.plain(notice)
1132 fm.plain('\n')
1132 fm.plain('\n')
1133 fm.end()
1133 fm.end()
1134
1134
1135 @command('bundle',
1135 @command('bundle',
1136 [('f', 'force', None, _('run even when the destination is unrelated')),
1136 [('f', 'force', None, _('run even when the destination is unrelated')),
1137 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1137 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1138 _('REV')),
1138 _('REV')),
1139 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1139 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1140 _('BRANCH')),
1140 _('BRANCH')),
1141 ('', 'base', [],
1141 ('', 'base', [],
1142 _('a base changeset assumed to be available at the destination'),
1142 _('a base changeset assumed to be available at the destination'),
1143 _('REV')),
1143 _('REV')),
1144 ('a', 'all', None, _('bundle all changesets in the repository')),
1144 ('a', 'all', None, _('bundle all changesets in the repository')),
1145 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1145 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1146 ] + remoteopts,
1146 ] + remoteopts,
1147 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1147 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1148 def bundle(ui, repo, fname, dest=None, **opts):
1148 def bundle(ui, repo, fname, dest=None, **opts):
1149 """create a bundle file
1149 """create a bundle file
1150
1150
1151 Generate a bundle file containing data to be transferred to another
1151 Generate a bundle file containing data to be transferred to another
1152 repository.
1152 repository.
1153
1153
1154 To create a bundle containing all changesets, use -a/--all
1154 To create a bundle containing all changesets, use -a/--all
1155 (or --base null). Otherwise, hg assumes the destination will have
1155 (or --base null). Otherwise, hg assumes the destination will have
1156 all the nodes you specify with --base parameters. Otherwise, hg
1156 all the nodes you specify with --base parameters. Otherwise, hg
1157 will assume the repository has all the nodes in destination, or
1157 will assume the repository has all the nodes in destination, or
1158 default-push/default if no destination is specified, where destination
1158 default-push/default if no destination is specified, where destination
1159 is the repository you provide through DEST option.
1159 is the repository you provide through DEST option.
1160
1160
1161 You can change bundle format with the -t/--type option. See
1161 You can change bundle format with the -t/--type option. See
1162 :hg:`help bundlespec` for documentation on this format. By default,
1162 :hg:`help bundlespec` for documentation on this format. By default,
1163 the most appropriate format is used and compression defaults to
1163 the most appropriate format is used and compression defaults to
1164 bzip2.
1164 bzip2.
1165
1165
1166 The bundle file can then be transferred using conventional means
1166 The bundle file can then be transferred using conventional means
1167 and applied to another repository with the unbundle or pull
1167 and applied to another repository with the unbundle or pull
1168 command. This is useful when direct push and pull are not
1168 command. This is useful when direct push and pull are not
1169 available or when exporting an entire repository is undesirable.
1169 available or when exporting an entire repository is undesirable.
1170
1170
1171 Applying bundles preserves all changeset contents including
1171 Applying bundles preserves all changeset contents including
1172 permissions, copy/rename information, and revision history.
1172 permissions, copy/rename information, and revision history.
1173
1173
1174 Returns 0 on success, 1 if no changes found.
1174 Returns 0 on success, 1 if no changes found.
1175 """
1175 """
1176 opts = pycompat.byteskwargs(opts)
1176 opts = pycompat.byteskwargs(opts)
1177 revs = None
1177 revs = None
1178 if 'rev' in opts:
1178 if 'rev' in opts:
1179 revstrings = opts['rev']
1179 revstrings = opts['rev']
1180 revs = scmutil.revrange(repo, revstrings)
1180 revs = scmutil.revrange(repo, revstrings)
1181 if revstrings and not revs:
1181 if revstrings and not revs:
1182 raise error.Abort(_('no commits to bundle'))
1182 raise error.Abort(_('no commits to bundle'))
1183
1183
1184 bundletype = opts.get('type', 'bzip2').lower()
1184 bundletype = opts.get('type', 'bzip2').lower()
1185 try:
1185 try:
1186 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1186 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1187 except error.UnsupportedBundleSpecification as e:
1187 except error.UnsupportedBundleSpecification as e:
1188 raise error.Abort(pycompat.bytestr(e),
1188 raise error.Abort(pycompat.bytestr(e),
1189 hint=_("see 'hg help bundlespec' for supported "
1189 hint=_("see 'hg help bundlespec' for supported "
1190 "values for --type"))
1190 "values for --type"))
1191 cgversion = bundlespec.contentopts["cg.version"]
1191 cgversion = bundlespec.contentopts["cg.version"]
1192
1192
1193 # Packed bundles are a pseudo bundle format for now.
1193 # Packed bundles are a pseudo bundle format for now.
1194 if cgversion == 's1':
1194 if cgversion == 's1':
1195 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1195 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1196 hint=_("use 'hg debugcreatestreamclonebundle'"))
1196 hint=_("use 'hg debugcreatestreamclonebundle'"))
1197
1197
1198 if opts.get('all'):
1198 if opts.get('all'):
1199 if dest:
1199 if dest:
1200 raise error.Abort(_("--all is incompatible with specifying "
1200 raise error.Abort(_("--all is incompatible with specifying "
1201 "a destination"))
1201 "a destination"))
1202 if opts.get('base'):
1202 if opts.get('base'):
1203 ui.warn(_("ignoring --base because --all was specified\n"))
1203 ui.warn(_("ignoring --base because --all was specified\n"))
1204 base = ['null']
1204 base = ['null']
1205 else:
1205 else:
1206 base = scmutil.revrange(repo, opts.get('base'))
1206 base = scmutil.revrange(repo, opts.get('base'))
1207 if cgversion not in changegroup.supportedoutgoingversions(repo):
1207 if cgversion not in changegroup.supportedoutgoingversions(repo):
1208 raise error.Abort(_("repository does not support bundle version %s") %
1208 raise error.Abort(_("repository does not support bundle version %s") %
1209 cgversion)
1209 cgversion)
1210
1210
1211 if base:
1211 if base:
1212 if dest:
1212 if dest:
1213 raise error.Abort(_("--base is incompatible with specifying "
1213 raise error.Abort(_("--base is incompatible with specifying "
1214 "a destination"))
1214 "a destination"))
1215 common = [repo[rev].node() for rev in base]
1215 common = [repo[rev].node() for rev in base]
1216 heads = [repo[r].node() for r in revs] if revs else None
1216 heads = [repo[r].node() for r in revs] if revs else None
1217 outgoing = discovery.outgoing(repo, common, heads)
1217 outgoing = discovery.outgoing(repo, common, heads)
1218 else:
1218 else:
1219 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1219 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1220 dest, branches = hg.parseurl(dest, opts.get('branch'))
1220 dest, branches = hg.parseurl(dest, opts.get('branch'))
1221 other = hg.peer(repo, opts, dest)
1221 other = hg.peer(repo, opts, dest)
1222 revs = [repo[r].hex() for r in revs]
1222 revs = [repo[r].hex() for r in revs]
1223 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1223 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1224 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1224 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1225 outgoing = discovery.findcommonoutgoing(repo, other,
1225 outgoing = discovery.findcommonoutgoing(repo, other,
1226 onlyheads=heads,
1226 onlyheads=heads,
1227 force=opts.get('force'),
1227 force=opts.get('force'),
1228 portable=True)
1228 portable=True)
1229
1229
1230 if not outgoing.missing:
1230 if not outgoing.missing:
1231 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1231 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1232 return 1
1232 return 1
1233
1233
1234 if cgversion == '01': #bundle1
1234 if cgversion == '01': #bundle1
1235 bversion = 'HG10' + bundlespec.wirecompression
1235 bversion = 'HG10' + bundlespec.wirecompression
1236 bcompression = None
1236 bcompression = None
1237 elif cgversion in ('02', '03'):
1237 elif cgversion in ('02', '03'):
1238 bversion = 'HG20'
1238 bversion = 'HG20'
1239 bcompression = bundlespec.wirecompression
1239 bcompression = bundlespec.wirecompression
1240 else:
1240 else:
1241 raise error.ProgrammingError(
1241 raise error.ProgrammingError(
1242 'bundle: unexpected changegroup version %s' % cgversion)
1242 'bundle: unexpected changegroup version %s' % cgversion)
1243
1243
1244 # TODO compression options should be derived from bundlespec parsing.
1244 # TODO compression options should be derived from bundlespec parsing.
1245 # This is a temporary hack to allow adjusting bundle compression
1245 # This is a temporary hack to allow adjusting bundle compression
1246 # level without a) formalizing the bundlespec changes to declare it
1246 # level without a) formalizing the bundlespec changes to declare it
1247 # b) introducing a command flag.
1247 # b) introducing a command flag.
1248 compopts = {}
1248 compopts = {}
1249 complevel = ui.configint('experimental',
1249 complevel = ui.configint('experimental',
1250 'bundlecomplevel.' + bundlespec.compression)
1250 'bundlecomplevel.' + bundlespec.compression)
1251 if complevel is None:
1251 if complevel is None:
1252 complevel = ui.configint('experimental', 'bundlecomplevel')
1252 complevel = ui.configint('experimental', 'bundlecomplevel')
1253 if complevel is not None:
1253 if complevel is not None:
1254 compopts['level'] = complevel
1254 compopts['level'] = complevel
1255
1255
1256 # Allow overriding the bundling of obsmarker in phases through
1256 # Allow overriding the bundling of obsmarker in phases through
1257 # configuration while we don't have a bundle version that include them
1257 # configuration while we don't have a bundle version that include them
1258 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1258 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1259 bundlespec.contentopts['obsolescence'] = True
1259 bundlespec.contentopts['obsolescence'] = True
1260 if repo.ui.configbool('experimental', 'bundle-phases'):
1260 if repo.ui.configbool('experimental', 'bundle-phases'):
1261 bundlespec.contentopts['phases'] = True
1261 bundlespec.contentopts['phases'] = True
1262
1262
1263 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1263 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1264 bundlespec.contentopts, compression=bcompression,
1264 bundlespec.contentopts, compression=bcompression,
1265 compopts=compopts)
1265 compopts=compopts)
1266
1266
1267 @command('cat',
1267 @command('cat',
1268 [('o', 'output', '',
1268 [('o', 'output', '',
1269 _('print output to file with formatted name'), _('FORMAT')),
1269 _('print output to file with formatted name'), _('FORMAT')),
1270 ('r', 'rev', '', _('print the given revision'), _('REV')),
1270 ('r', 'rev', '', _('print the given revision'), _('REV')),
1271 ('', 'decode', None, _('apply any matching decode filter')),
1271 ('', 'decode', None, _('apply any matching decode filter')),
1272 ] + walkopts + formatteropts,
1272 ] + walkopts + formatteropts,
1273 _('[OPTION]... FILE...'),
1273 _('[OPTION]... FILE...'),
1274 inferrepo=True,
1274 inferrepo=True,
1275 intents={INTENT_READONLY})
1275 intents={INTENT_READONLY})
1276 def cat(ui, repo, file1, *pats, **opts):
1276 def cat(ui, repo, file1, *pats, **opts):
1277 """output the current or given revision of files
1277 """output the current or given revision of files
1278
1278
1279 Print the specified files as they were at the given revision. If
1279 Print the specified files as they were at the given revision. If
1280 no revision is given, the parent of the working directory is used.
1280 no revision is given, the parent of the working directory is used.
1281
1281
1282 Output may be to a file, in which case the name of the file is
1282 Output may be to a file, in which case the name of the file is
1283 given using a template string. See :hg:`help templates`. In addition
1283 given using a template string. See :hg:`help templates`. In addition
1284 to the common template keywords, the following formatting rules are
1284 to the common template keywords, the following formatting rules are
1285 supported:
1285 supported:
1286
1286
1287 :``%%``: literal "%" character
1287 :``%%``: literal "%" character
1288 :``%s``: basename of file being printed
1288 :``%s``: basename of file being printed
1289 :``%d``: dirname of file being printed, or '.' if in repository root
1289 :``%d``: dirname of file being printed, or '.' if in repository root
1290 :``%p``: root-relative path name of file being printed
1290 :``%p``: root-relative path name of file being printed
1291 :``%H``: changeset hash (40 hexadecimal digits)
1291 :``%H``: changeset hash (40 hexadecimal digits)
1292 :``%R``: changeset revision number
1292 :``%R``: changeset revision number
1293 :``%h``: short-form changeset hash (12 hexadecimal digits)
1293 :``%h``: short-form changeset hash (12 hexadecimal digits)
1294 :``%r``: zero-padded changeset revision number
1294 :``%r``: zero-padded changeset revision number
1295 :``%b``: basename of the exporting repository
1295 :``%b``: basename of the exporting repository
1296 :``\\``: literal "\\" character
1296 :``\\``: literal "\\" character
1297
1297
1298 Returns 0 on success.
1298 Returns 0 on success.
1299 """
1299 """
1300 opts = pycompat.byteskwargs(opts)
1300 opts = pycompat.byteskwargs(opts)
1301 rev = opts.get('rev')
1301 rev = opts.get('rev')
1302 if rev:
1302 if rev:
1303 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1303 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1304 ctx = scmutil.revsingle(repo, rev)
1304 ctx = scmutil.revsingle(repo, rev)
1305 m = scmutil.match(ctx, (file1,) + pats, opts)
1305 m = scmutil.match(ctx, (file1,) + pats, opts)
1306 fntemplate = opts.pop('output', '')
1306 fntemplate = opts.pop('output', '')
1307 if cmdutil.isstdiofilename(fntemplate):
1307 if cmdutil.isstdiofilename(fntemplate):
1308 fntemplate = ''
1308 fntemplate = ''
1309
1309
1310 if fntemplate:
1310 if fntemplate:
1311 fm = formatter.nullformatter(ui, 'cat', opts)
1311 fm = formatter.nullformatter(ui, 'cat', opts)
1312 else:
1312 else:
1313 ui.pager('cat')
1313 ui.pager('cat')
1314 fm = ui.formatter('cat', opts)
1314 fm = ui.formatter('cat', opts)
1315 with fm:
1315 with fm:
1316 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1316 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1317 **pycompat.strkwargs(opts))
1317 **pycompat.strkwargs(opts))
1318
1318
1319 @command('^clone',
1319 @command('^clone',
1320 [('U', 'noupdate', None, _('the clone will include an empty working '
1320 [('U', 'noupdate', None, _('the clone will include an empty working '
1321 'directory (only a repository)')),
1321 'directory (only a repository)')),
1322 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1322 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1323 _('REV')),
1323 _('REV')),
1324 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1324 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1325 ' and its ancestors'), _('REV')),
1325 ' and its ancestors'), _('REV')),
1326 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1326 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1327 ' changesets and their ancestors'), _('BRANCH')),
1327 ' changesets and their ancestors'), _('BRANCH')),
1328 ('', 'pull', None, _('use pull protocol to copy metadata')),
1328 ('', 'pull', None, _('use pull protocol to copy metadata')),
1329 ('', 'uncompressed', None,
1329 ('', 'uncompressed', None,
1330 _('an alias to --stream (DEPRECATED)')),
1330 _('an alias to --stream (DEPRECATED)')),
1331 ('', 'stream', None,
1331 ('', 'stream', None,
1332 _('clone with minimal data processing')),
1332 _('clone with minimal data processing')),
1333 ] + remoteopts,
1333 ] + remoteopts,
1334 _('[OPTION]... SOURCE [DEST]'),
1334 _('[OPTION]... SOURCE [DEST]'),
1335 norepo=True)
1335 norepo=True)
1336 def clone(ui, source, dest=None, **opts):
1336 def clone(ui, source, dest=None, **opts):
1337 """make a copy of an existing repository
1337 """make a copy of an existing repository
1338
1338
1339 Create a copy of an existing repository in a new directory.
1339 Create a copy of an existing repository in a new directory.
1340
1340
1341 If no destination directory name is specified, it defaults to the
1341 If no destination directory name is specified, it defaults to the
1342 basename of the source.
1342 basename of the source.
1343
1343
1344 The location of the source is added to the new repository's
1344 The location of the source is added to the new repository's
1345 ``.hg/hgrc`` file, as the default to be used for future pulls.
1345 ``.hg/hgrc`` file, as the default to be used for future pulls.
1346
1346
1347 Only local paths and ``ssh://`` URLs are supported as
1347 Only local paths and ``ssh://`` URLs are supported as
1348 destinations. For ``ssh://`` destinations, no working directory or
1348 destinations. For ``ssh://`` destinations, no working directory or
1349 ``.hg/hgrc`` will be created on the remote side.
1349 ``.hg/hgrc`` will be created on the remote side.
1350
1350
1351 If the source repository has a bookmark called '@' set, that
1351 If the source repository has a bookmark called '@' set, that
1352 revision will be checked out in the new repository by default.
1352 revision will be checked out in the new repository by default.
1353
1353
1354 To check out a particular version, use -u/--update, or
1354 To check out a particular version, use -u/--update, or
1355 -U/--noupdate to create a clone with no working directory.
1355 -U/--noupdate to create a clone with no working directory.
1356
1356
1357 To pull only a subset of changesets, specify one or more revisions
1357 To pull only a subset of changesets, specify one or more revisions
1358 identifiers with -r/--rev or branches with -b/--branch. The
1358 identifiers with -r/--rev or branches with -b/--branch. The
1359 resulting clone will contain only the specified changesets and
1359 resulting clone will contain only the specified changesets and
1360 their ancestors. These options (or 'clone src#rev dest') imply
1360 their ancestors. These options (or 'clone src#rev dest') imply
1361 --pull, even for local source repositories.
1361 --pull, even for local source repositories.
1362
1362
1363 In normal clone mode, the remote normalizes repository data into a common
1363 In normal clone mode, the remote normalizes repository data into a common
1364 exchange format and the receiving end translates this data into its local
1364 exchange format and the receiving end translates this data into its local
1365 storage format. --stream activates a different clone mode that essentially
1365 storage format. --stream activates a different clone mode that essentially
1366 copies repository files from the remote with minimal data processing. This
1366 copies repository files from the remote with minimal data processing. This
1367 significantly reduces the CPU cost of a clone both remotely and locally.
1367 significantly reduces the CPU cost of a clone both remotely and locally.
1368 However, it often increases the transferred data size by 30-40%. This can
1368 However, it often increases the transferred data size by 30-40%. This can
1369 result in substantially faster clones where I/O throughput is plentiful,
1369 result in substantially faster clones where I/O throughput is plentiful,
1370 especially for larger repositories. A side-effect of --stream clones is
1370 especially for larger repositories. A side-effect of --stream clones is
1371 that storage settings and requirements on the remote are applied locally:
1371 that storage settings and requirements on the remote are applied locally:
1372 a modern client may inherit legacy or inefficient storage used by the
1372 a modern client may inherit legacy or inefficient storage used by the
1373 remote or a legacy Mercurial client may not be able to clone from a
1373 remote or a legacy Mercurial client may not be able to clone from a
1374 modern Mercurial remote.
1374 modern Mercurial remote.
1375
1375
1376 .. note::
1376 .. note::
1377
1377
1378 Specifying a tag will include the tagged changeset but not the
1378 Specifying a tag will include the tagged changeset but not the
1379 changeset containing the tag.
1379 changeset containing the tag.
1380
1380
1381 .. container:: verbose
1381 .. container:: verbose
1382
1382
1383 For efficiency, hardlinks are used for cloning whenever the
1383 For efficiency, hardlinks are used for cloning whenever the
1384 source and destination are on the same filesystem (note this
1384 source and destination are on the same filesystem (note this
1385 applies only to the repository data, not to the working
1385 applies only to the repository data, not to the working
1386 directory). Some filesystems, such as AFS, implement hardlinking
1386 directory). Some filesystems, such as AFS, implement hardlinking
1387 incorrectly, but do not report errors. In these cases, use the
1387 incorrectly, but do not report errors. In these cases, use the
1388 --pull option to avoid hardlinking.
1388 --pull option to avoid hardlinking.
1389
1389
1390 Mercurial will update the working directory to the first applicable
1390 Mercurial will update the working directory to the first applicable
1391 revision from this list:
1391 revision from this list:
1392
1392
1393 a) null if -U or the source repository has no changesets
1393 a) null if -U or the source repository has no changesets
1394 b) if -u . and the source repository is local, the first parent of
1394 b) if -u . and the source repository is local, the first parent of
1395 the source repository's working directory
1395 the source repository's working directory
1396 c) the changeset specified with -u (if a branch name, this means the
1396 c) the changeset specified with -u (if a branch name, this means the
1397 latest head of that branch)
1397 latest head of that branch)
1398 d) the changeset specified with -r
1398 d) the changeset specified with -r
1399 e) the tipmost head specified with -b
1399 e) the tipmost head specified with -b
1400 f) the tipmost head specified with the url#branch source syntax
1400 f) the tipmost head specified with the url#branch source syntax
1401 g) the revision marked with the '@' bookmark, if present
1401 g) the revision marked with the '@' bookmark, if present
1402 h) the tipmost head of the default branch
1402 h) the tipmost head of the default branch
1403 i) tip
1403 i) tip
1404
1404
1405 When cloning from servers that support it, Mercurial may fetch
1405 When cloning from servers that support it, Mercurial may fetch
1406 pre-generated data from a server-advertised URL or inline from the
1406 pre-generated data from a server-advertised URL or inline from the
1407 same stream. When this is done, hooks operating on incoming changesets
1407 same stream. When this is done, hooks operating on incoming changesets
1408 and changegroups may fire more than once, once for each pre-generated
1408 and changegroups may fire more than once, once for each pre-generated
1409 bundle and as well as for any additional remaining data. In addition,
1409 bundle and as well as for any additional remaining data. In addition,
1410 if an error occurs, the repository may be rolled back to a partial
1410 if an error occurs, the repository may be rolled back to a partial
1411 clone. This behavior may change in future releases.
1411 clone. This behavior may change in future releases.
1412 See :hg:`help -e clonebundles` for more.
1412 See :hg:`help -e clonebundles` for more.
1413
1413
1414 Examples:
1414 Examples:
1415
1415
1416 - clone a remote repository to a new directory named hg/::
1416 - clone a remote repository to a new directory named hg/::
1417
1417
1418 hg clone https://www.mercurial-scm.org/repo/hg/
1418 hg clone https://www.mercurial-scm.org/repo/hg/
1419
1419
1420 - create a lightweight local clone::
1420 - create a lightweight local clone::
1421
1421
1422 hg clone project/ project-feature/
1422 hg clone project/ project-feature/
1423
1423
1424 - clone from an absolute path on an ssh server (note double-slash)::
1424 - clone from an absolute path on an ssh server (note double-slash)::
1425
1425
1426 hg clone ssh://user@server//home/projects/alpha/
1426 hg clone ssh://user@server//home/projects/alpha/
1427
1427
1428 - do a streaming clone while checking out a specified version::
1428 - do a streaming clone while checking out a specified version::
1429
1429
1430 hg clone --stream http://server/repo -u 1.5
1430 hg clone --stream http://server/repo -u 1.5
1431
1431
1432 - create a repository without changesets after a particular revision::
1432 - create a repository without changesets after a particular revision::
1433
1433
1434 hg clone -r 04e544 experimental/ good/
1434 hg clone -r 04e544 experimental/ good/
1435
1435
1436 - clone (and track) a particular named branch::
1436 - clone (and track) a particular named branch::
1437
1437
1438 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1438 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1439
1439
1440 See :hg:`help urls` for details on specifying URLs.
1440 See :hg:`help urls` for details on specifying URLs.
1441
1441
1442 Returns 0 on success.
1442 Returns 0 on success.
1443 """
1443 """
1444 opts = pycompat.byteskwargs(opts)
1444 opts = pycompat.byteskwargs(opts)
1445 if opts.get('noupdate') and opts.get('updaterev'):
1445 if opts.get('noupdate') and opts.get('updaterev'):
1446 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1446 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1447
1447
1448 r = hg.clone(ui, opts, source, dest,
1448 r = hg.clone(ui, opts, source, dest,
1449 pull=opts.get('pull'),
1449 pull=opts.get('pull'),
1450 stream=opts.get('stream') or opts.get('uncompressed'),
1450 stream=opts.get('stream') or opts.get('uncompressed'),
1451 revs=opts.get('rev'),
1451 revs=opts.get('rev'),
1452 update=opts.get('updaterev') or not opts.get('noupdate'),
1452 update=opts.get('updaterev') or not opts.get('noupdate'),
1453 branch=opts.get('branch'),
1453 branch=opts.get('branch'),
1454 shareopts=opts.get('shareopts'))
1454 shareopts=opts.get('shareopts'))
1455
1455
1456 return r is None
1456 return r is None
1457
1457
1458 @command('^commit|ci',
1458 @command('^commit|ci',
1459 [('A', 'addremove', None,
1459 [('A', 'addremove', None,
1460 _('mark new/missing files as added/removed before committing')),
1460 _('mark new/missing files as added/removed before committing')),
1461 ('', 'close-branch', None,
1461 ('', 'close-branch', None,
1462 _('mark a branch head as closed')),
1462 _('mark a branch head as closed')),
1463 ('', 'amend', None, _('amend the parent of the working directory')),
1463 ('', 'amend', None, _('amend the parent of the working directory')),
1464 ('s', 'secret', None, _('use the secret phase for committing')),
1464 ('s', 'secret', None, _('use the secret phase for committing')),
1465 ('e', 'edit', None, _('invoke editor on commit messages')),
1465 ('e', 'edit', None, _('invoke editor on commit messages')),
1466 ('i', 'interactive', None, _('use interactive mode')),
1466 ('i', 'interactive', None, _('use interactive mode')),
1467 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1467 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1468 _('[OPTION]... [FILE]...'),
1468 _('[OPTION]... [FILE]...'),
1469 inferrepo=True)
1469 inferrepo=True)
1470 def commit(ui, repo, *pats, **opts):
1470 def commit(ui, repo, *pats, **opts):
1471 """commit the specified files or all outstanding changes
1471 """commit the specified files or all outstanding changes
1472
1472
1473 Commit changes to the given files into the repository. Unlike a
1473 Commit changes to the given files into the repository. Unlike a
1474 centralized SCM, this operation is a local operation. See
1474 centralized SCM, this operation is a local operation. See
1475 :hg:`push` for a way to actively distribute your changes.
1475 :hg:`push` for a way to actively distribute your changes.
1476
1476
1477 If a list of files is omitted, all changes reported by :hg:`status`
1477 If a list of files is omitted, all changes reported by :hg:`status`
1478 will be committed.
1478 will be committed.
1479
1479
1480 If you are committing the result of a merge, do not provide any
1480 If you are committing the result of a merge, do not provide any
1481 filenames or -I/-X filters.
1481 filenames or -I/-X filters.
1482
1482
1483 If no commit message is specified, Mercurial starts your
1483 If no commit message is specified, Mercurial starts your
1484 configured editor where you can enter a message. In case your
1484 configured editor where you can enter a message. In case your
1485 commit fails, you will find a backup of your message in
1485 commit fails, you will find a backup of your message in
1486 ``.hg/last-message.txt``.
1486 ``.hg/last-message.txt``.
1487
1487
1488 The --close-branch flag can be used to mark the current branch
1488 The --close-branch flag can be used to mark the current branch
1489 head closed. When all heads of a branch are closed, the branch
1489 head closed. When all heads of a branch are closed, the branch
1490 will be considered closed and no longer listed.
1490 will be considered closed and no longer listed.
1491
1491
1492 The --amend flag can be used to amend the parent of the
1492 The --amend flag can be used to amend the parent of the
1493 working directory with a new commit that contains the changes
1493 working directory with a new commit that contains the changes
1494 in the parent in addition to those currently reported by :hg:`status`,
1494 in the parent in addition to those currently reported by :hg:`status`,
1495 if there are any. The old commit is stored in a backup bundle in
1495 if there are any. The old commit is stored in a backup bundle in
1496 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1496 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1497 on how to restore it).
1497 on how to restore it).
1498
1498
1499 Message, user and date are taken from the amended commit unless
1499 Message, user and date are taken from the amended commit unless
1500 specified. When a message isn't specified on the command line,
1500 specified. When a message isn't specified on the command line,
1501 the editor will open with the message of the amended commit.
1501 the editor will open with the message of the amended commit.
1502
1502
1503 It is not possible to amend public changesets (see :hg:`help phases`)
1503 It is not possible to amend public changesets (see :hg:`help phases`)
1504 or changesets that have children.
1504 or changesets that have children.
1505
1505
1506 See :hg:`help dates` for a list of formats valid for -d/--date.
1506 See :hg:`help dates` for a list of formats valid for -d/--date.
1507
1507
1508 Returns 0 on success, 1 if nothing changed.
1508 Returns 0 on success, 1 if nothing changed.
1509
1509
1510 .. container:: verbose
1510 .. container:: verbose
1511
1511
1512 Examples:
1512 Examples:
1513
1513
1514 - commit all files ending in .py::
1514 - commit all files ending in .py::
1515
1515
1516 hg commit --include "set:**.py"
1516 hg commit --include "set:**.py"
1517
1517
1518 - commit all non-binary files::
1518 - commit all non-binary files::
1519
1519
1520 hg commit --exclude "set:binary()"
1520 hg commit --exclude "set:binary()"
1521
1521
1522 - amend the current commit and set the date to now::
1522 - amend the current commit and set the date to now::
1523
1523
1524 hg commit --amend --date now
1524 hg commit --amend --date now
1525 """
1525 """
1526 with repo.wlock(), repo.lock():
1526 with repo.wlock(), repo.lock():
1527 return _docommit(ui, repo, *pats, **opts)
1527 return _docommit(ui, repo, *pats, **opts)
1528
1528
1529 def _docommit(ui, repo, *pats, **opts):
1529 def _docommit(ui, repo, *pats, **opts):
1530 if opts.get(r'interactive'):
1530 if opts.get(r'interactive'):
1531 opts.pop(r'interactive')
1531 opts.pop(r'interactive')
1532 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1532 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1533 cmdutil.recordfilter, *pats,
1533 cmdutil.recordfilter, *pats,
1534 **opts)
1534 **opts)
1535 # ret can be 0 (no changes to record) or the value returned by
1535 # ret can be 0 (no changes to record) or the value returned by
1536 # commit(), 1 if nothing changed or None on success.
1536 # commit(), 1 if nothing changed or None on success.
1537 return 1 if ret == 0 else ret
1537 return 1 if ret == 0 else ret
1538
1538
1539 opts = pycompat.byteskwargs(opts)
1539 opts = pycompat.byteskwargs(opts)
1540 if opts.get('subrepos'):
1540 if opts.get('subrepos'):
1541 if opts.get('amend'):
1541 if opts.get('amend'):
1542 raise error.Abort(_('cannot amend with --subrepos'))
1542 raise error.Abort(_('cannot amend with --subrepos'))
1543 # Let --subrepos on the command line override config setting.
1543 # Let --subrepos on the command line override config setting.
1544 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1544 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1545
1545
1546 cmdutil.checkunfinished(repo, commit=True)
1546 cmdutil.checkunfinished(repo, commit=True)
1547
1547
1548 branch = repo[None].branch()
1548 branch = repo[None].branch()
1549 bheads = repo.branchheads(branch)
1549 bheads = repo.branchheads(branch)
1550
1550
1551 extra = {}
1551 extra = {}
1552 if opts.get('close_branch'):
1552 if opts.get('close_branch'):
1553 extra['close'] = '1'
1553 extra['close'] = '1'
1554
1554
1555 if not bheads:
1555 if not bheads:
1556 raise error.Abort(_('can only close branch heads'))
1556 raise error.Abort(_('can only close branch heads'))
1557 elif opts.get('amend'):
1557 elif opts.get('amend'):
1558 if repo[None].parents()[0].p1().branch() != branch and \
1558 if repo[None].parents()[0].p1().branch() != branch and \
1559 repo[None].parents()[0].p2().branch() != branch:
1559 repo[None].parents()[0].p2().branch() != branch:
1560 raise error.Abort(_('can only close branch heads'))
1560 raise error.Abort(_('can only close branch heads'))
1561
1561
1562 if opts.get('amend'):
1562 if opts.get('amend'):
1563 if ui.configbool('ui', 'commitsubrepos'):
1563 if ui.configbool('ui', 'commitsubrepos'):
1564 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1564 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1565
1565
1566 old = repo['.']
1566 old = repo['.']
1567 rewriteutil.precheck(repo, [old.rev()], 'amend')
1567 rewriteutil.precheck(repo, [old.rev()], 'amend')
1568
1568
1569 # Currently histedit gets confused if an amend happens while histedit
1569 # Currently histedit gets confused if an amend happens while histedit
1570 # is in progress. Since we have a checkunfinished command, we are
1570 # is in progress. Since we have a checkunfinished command, we are
1571 # temporarily honoring it.
1571 # temporarily honoring it.
1572 #
1572 #
1573 # Note: eventually this guard will be removed. Please do not expect
1573 # Note: eventually this guard will be removed. Please do not expect
1574 # this behavior to remain.
1574 # this behavior to remain.
1575 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1575 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1576 cmdutil.checkunfinished(repo)
1576 cmdutil.checkunfinished(repo)
1577
1577
1578 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1578 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1579 if node == old.node():
1579 if node == old.node():
1580 ui.status(_("nothing changed\n"))
1580 ui.status(_("nothing changed\n"))
1581 return 1
1581 return 1
1582 else:
1582 else:
1583 def commitfunc(ui, repo, message, match, opts):
1583 def commitfunc(ui, repo, message, match, opts):
1584 overrides = {}
1584 overrides = {}
1585 if opts.get('secret'):
1585 if opts.get('secret'):
1586 overrides[('phases', 'new-commit')] = 'secret'
1586 overrides[('phases', 'new-commit')] = 'secret'
1587
1587
1588 baseui = repo.baseui
1588 baseui = repo.baseui
1589 with baseui.configoverride(overrides, 'commit'):
1589 with baseui.configoverride(overrides, 'commit'):
1590 with ui.configoverride(overrides, 'commit'):
1590 with ui.configoverride(overrides, 'commit'):
1591 editform = cmdutil.mergeeditform(repo[None],
1591 editform = cmdutil.mergeeditform(repo[None],
1592 'commit.normal')
1592 'commit.normal')
1593 editor = cmdutil.getcommiteditor(
1593 editor = cmdutil.getcommiteditor(
1594 editform=editform, **pycompat.strkwargs(opts))
1594 editform=editform, **pycompat.strkwargs(opts))
1595 return repo.commit(message,
1595 return repo.commit(message,
1596 opts.get('user'),
1596 opts.get('user'),
1597 opts.get('date'),
1597 opts.get('date'),
1598 match,
1598 match,
1599 editor=editor,
1599 editor=editor,
1600 extra=extra)
1600 extra=extra)
1601
1601
1602 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1602 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1603
1603
1604 if not node:
1604 if not node:
1605 stat = cmdutil.postcommitstatus(repo, pats, opts)
1605 stat = cmdutil.postcommitstatus(repo, pats, opts)
1606 if stat[3]:
1606 if stat[3]:
1607 ui.status(_("nothing changed (%d missing files, see "
1607 ui.status(_("nothing changed (%d missing files, see "
1608 "'hg status')\n") % len(stat[3]))
1608 "'hg status')\n") % len(stat[3]))
1609 else:
1609 else:
1610 ui.status(_("nothing changed\n"))
1610 ui.status(_("nothing changed\n"))
1611 return 1
1611 return 1
1612
1612
1613 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1613 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1614
1614
1615 @command('config|showconfig|debugconfig',
1615 @command('config|showconfig|debugconfig',
1616 [('u', 'untrusted', None, _('show untrusted configuration options')),
1616 [('u', 'untrusted', None, _('show untrusted configuration options')),
1617 ('e', 'edit', None, _('edit user config')),
1617 ('e', 'edit', None, _('edit user config')),
1618 ('l', 'local', None, _('edit repository config')),
1618 ('l', 'local', None, _('edit repository config')),
1619 ('g', 'global', None, _('edit global config'))] + formatteropts,
1619 ('g', 'global', None, _('edit global config'))] + formatteropts,
1620 _('[-u] [NAME]...'),
1620 _('[-u] [NAME]...'),
1621 optionalrepo=True,
1621 optionalrepo=True,
1622 intents={INTENT_READONLY})
1622 intents={INTENT_READONLY})
1623 def config(ui, repo, *values, **opts):
1623 def config(ui, repo, *values, **opts):
1624 """show combined config settings from all hgrc files
1624 """show combined config settings from all hgrc files
1625
1625
1626 With no arguments, print names and values of all config items.
1626 With no arguments, print names and values of all config items.
1627
1627
1628 With one argument of the form section.name, print just the value
1628 With one argument of the form section.name, print just the value
1629 of that config item.
1629 of that config item.
1630
1630
1631 With multiple arguments, print names and values of all config
1631 With multiple arguments, print names and values of all config
1632 items with matching section names or section.names.
1632 items with matching section names or section.names.
1633
1633
1634 With --edit, start an editor on the user-level config file. With
1634 With --edit, start an editor on the user-level config file. With
1635 --global, edit the system-wide config file. With --local, edit the
1635 --global, edit the system-wide config file. With --local, edit the
1636 repository-level config file.
1636 repository-level config file.
1637
1637
1638 With --debug, the source (filename and line number) is printed
1638 With --debug, the source (filename and line number) is printed
1639 for each config item.
1639 for each config item.
1640
1640
1641 See :hg:`help config` for more information about config files.
1641 See :hg:`help config` for more information about config files.
1642
1642
1643 Returns 0 on success, 1 if NAME does not exist.
1643 Returns 0 on success, 1 if NAME does not exist.
1644
1644
1645 """
1645 """
1646
1646
1647 opts = pycompat.byteskwargs(opts)
1647 opts = pycompat.byteskwargs(opts)
1648 if opts.get('edit') or opts.get('local') or opts.get('global'):
1648 if opts.get('edit') or opts.get('local') or opts.get('global'):
1649 if opts.get('local') and opts.get('global'):
1649 if opts.get('local') and opts.get('global'):
1650 raise error.Abort(_("can't use --local and --global together"))
1650 raise error.Abort(_("can't use --local and --global together"))
1651
1651
1652 if opts.get('local'):
1652 if opts.get('local'):
1653 if not repo:
1653 if not repo:
1654 raise error.Abort(_("can't use --local outside a repository"))
1654 raise error.Abort(_("can't use --local outside a repository"))
1655 paths = [repo.vfs.join('hgrc')]
1655 paths = [repo.vfs.join('hgrc')]
1656 elif opts.get('global'):
1656 elif opts.get('global'):
1657 paths = rcutil.systemrcpath()
1657 paths = rcutil.systemrcpath()
1658 else:
1658 else:
1659 paths = rcutil.userrcpath()
1659 paths = rcutil.userrcpath()
1660
1660
1661 for f in paths:
1661 for f in paths:
1662 if os.path.exists(f):
1662 if os.path.exists(f):
1663 break
1663 break
1664 else:
1664 else:
1665 if opts.get('global'):
1665 if opts.get('global'):
1666 samplehgrc = uimod.samplehgrcs['global']
1666 samplehgrc = uimod.samplehgrcs['global']
1667 elif opts.get('local'):
1667 elif opts.get('local'):
1668 samplehgrc = uimod.samplehgrcs['local']
1668 samplehgrc = uimod.samplehgrcs['local']
1669 else:
1669 else:
1670 samplehgrc = uimod.samplehgrcs['user']
1670 samplehgrc = uimod.samplehgrcs['user']
1671
1671
1672 f = paths[0]
1672 f = paths[0]
1673 fp = open(f, "wb")
1673 fp = open(f, "wb")
1674 fp.write(util.tonativeeol(samplehgrc))
1674 fp.write(util.tonativeeol(samplehgrc))
1675 fp.close()
1675 fp.close()
1676
1676
1677 editor = ui.geteditor()
1677 editor = ui.geteditor()
1678 ui.system("%s \"%s\"" % (editor, f),
1678 ui.system("%s \"%s\"" % (editor, f),
1679 onerr=error.Abort, errprefix=_("edit failed"),
1679 onerr=error.Abort, errprefix=_("edit failed"),
1680 blockedtag='config_edit')
1680 blockedtag='config_edit')
1681 return
1681 return
1682 ui.pager('config')
1682 ui.pager('config')
1683 fm = ui.formatter('config', opts)
1683 fm = ui.formatter('config', opts)
1684 for t, f in rcutil.rccomponents():
1684 for t, f in rcutil.rccomponents():
1685 if t == 'path':
1685 if t == 'path':
1686 ui.debug('read config from: %s\n' % f)
1686 ui.debug('read config from: %s\n' % f)
1687 elif t == 'items':
1687 elif t == 'items':
1688 for section, name, value, source in f:
1688 for section, name, value, source in f:
1689 ui.debug('set config by: %s\n' % source)
1689 ui.debug('set config by: %s\n' % source)
1690 else:
1690 else:
1691 raise error.ProgrammingError('unknown rctype: %s' % t)
1691 raise error.ProgrammingError('unknown rctype: %s' % t)
1692 untrusted = bool(opts.get('untrusted'))
1692 untrusted = bool(opts.get('untrusted'))
1693
1693
1694 selsections = selentries = []
1694 selsections = selentries = []
1695 if values:
1695 if values:
1696 selsections = [v for v in values if '.' not in v]
1696 selsections = [v for v in values if '.' not in v]
1697 selentries = [v for v in values if '.' in v]
1697 selentries = [v for v in values if '.' in v]
1698 uniquesel = (len(selentries) == 1 and not selsections)
1698 uniquesel = (len(selentries) == 1 and not selsections)
1699 selsections = set(selsections)
1699 selsections = set(selsections)
1700 selentries = set(selentries)
1700 selentries = set(selentries)
1701
1701
1702 matched = False
1702 matched = False
1703 for section, name, value in ui.walkconfig(untrusted=untrusted):
1703 for section, name, value in ui.walkconfig(untrusted=untrusted):
1704 source = ui.configsource(section, name, untrusted)
1704 source = ui.configsource(section, name, untrusted)
1705 value = pycompat.bytestr(value)
1705 value = pycompat.bytestr(value)
1706 if fm.isplain():
1706 if fm.isplain():
1707 source = source or 'none'
1707 source = source or 'none'
1708 value = value.replace('\n', '\\n')
1708 value = value.replace('\n', '\\n')
1709 entryname = section + '.' + name
1709 entryname = section + '.' + name
1710 if values and not (section in selsections or entryname in selentries):
1710 if values and not (section in selsections or entryname in selentries):
1711 continue
1711 continue
1712 fm.startitem()
1712 fm.startitem()
1713 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1713 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1714 if uniquesel:
1714 if uniquesel:
1715 fm.data(name=entryname)
1715 fm.data(name=entryname)
1716 fm.write('value', '%s\n', value)
1716 fm.write('value', '%s\n', value)
1717 else:
1717 else:
1718 fm.write('name value', '%s=%s\n', entryname, value)
1718 fm.write('name value', '%s=%s\n', entryname, value)
1719 matched = True
1719 matched = True
1720 fm.end()
1720 fm.end()
1721 if matched:
1721 if matched:
1722 return 0
1722 return 0
1723 return 1
1723 return 1
1724
1724
1725 @command('copy|cp',
1725 @command('copy|cp',
1726 [('A', 'after', None, _('record a copy that has already occurred')),
1726 [('A', 'after', None, _('record a copy that has already occurred')),
1727 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1727 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1728 ] + walkopts + dryrunopts,
1728 ] + walkopts + dryrunopts,
1729 _('[OPTION]... [SOURCE]... DEST'))
1729 _('[OPTION]... [SOURCE]... DEST'))
1730 def copy(ui, repo, *pats, **opts):
1730 def copy(ui, repo, *pats, **opts):
1731 """mark files as copied for the next commit
1731 """mark files as copied for the next commit
1732
1732
1733 Mark dest as having copies of source files. If dest is a
1733 Mark dest as having copies of source files. If dest is a
1734 directory, copies are put in that directory. If dest is a file,
1734 directory, copies are put in that directory. If dest is a file,
1735 the source must be a single file.
1735 the source must be a single file.
1736
1736
1737 By default, this command copies the contents of files as they
1737 By default, this command copies the contents of files as they
1738 exist in the working directory. If invoked with -A/--after, the
1738 exist in the working directory. If invoked with -A/--after, the
1739 operation is recorded, but no copying is performed.
1739 operation is recorded, but no copying is performed.
1740
1740
1741 This command takes effect with the next commit. To undo a copy
1741 This command takes effect with the next commit. To undo a copy
1742 before that, see :hg:`revert`.
1742 before that, see :hg:`revert`.
1743
1743
1744 Returns 0 on success, 1 if errors are encountered.
1744 Returns 0 on success, 1 if errors are encountered.
1745 """
1745 """
1746 opts = pycompat.byteskwargs(opts)
1746 opts = pycompat.byteskwargs(opts)
1747 with repo.wlock(False):
1747 with repo.wlock(False):
1748 return cmdutil.copy(ui, repo, pats, opts)
1748 return cmdutil.copy(ui, repo, pats, opts)
1749
1749
1750 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1750 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1751 def debugcommands(ui, cmd='', *args):
1751 def debugcommands(ui, cmd='', *args):
1752 """list all available commands and options"""
1752 """list all available commands and options"""
1753 for cmd, vals in sorted(table.iteritems()):
1753 for cmd, vals in sorted(table.iteritems()):
1754 cmd = cmd.split('|')[0].strip('^')
1754 cmd = cmd.split('|')[0].strip('^')
1755 opts = ', '.join([i[1] for i in vals[1]])
1755 opts = ', '.join([i[1] for i in vals[1]])
1756 ui.write('%s: %s\n' % (cmd, opts))
1756 ui.write('%s: %s\n' % (cmd, opts))
1757
1757
1758 @command('debugcomplete',
1758 @command('debugcomplete',
1759 [('o', 'options', None, _('show the command options'))],
1759 [('o', 'options', None, _('show the command options'))],
1760 _('[-o] CMD'),
1760 _('[-o] CMD'),
1761 norepo=True)
1761 norepo=True)
1762 def debugcomplete(ui, cmd='', **opts):
1762 def debugcomplete(ui, cmd='', **opts):
1763 """returns the completion list associated with the given command"""
1763 """returns the completion list associated with the given command"""
1764
1764
1765 if opts.get(r'options'):
1765 if opts.get(r'options'):
1766 options = []
1766 options = []
1767 otables = [globalopts]
1767 otables = [globalopts]
1768 if cmd:
1768 if cmd:
1769 aliases, entry = cmdutil.findcmd(cmd, table, False)
1769 aliases, entry = cmdutil.findcmd(cmd, table, False)
1770 otables.append(entry[1])
1770 otables.append(entry[1])
1771 for t in otables:
1771 for t in otables:
1772 for o in t:
1772 for o in t:
1773 if "(DEPRECATED)" in o[3]:
1773 if "(DEPRECATED)" in o[3]:
1774 continue
1774 continue
1775 if o[0]:
1775 if o[0]:
1776 options.append('-%s' % o[0])
1776 options.append('-%s' % o[0])
1777 options.append('--%s' % o[1])
1777 options.append('--%s' % o[1])
1778 ui.write("%s\n" % "\n".join(options))
1778 ui.write("%s\n" % "\n".join(options))
1779 return
1779 return
1780
1780
1781 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1781 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1782 if ui.verbose:
1782 if ui.verbose:
1783 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1783 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1784 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1784 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1785
1785
1786 @command('^diff',
1786 @command('^diff',
1787 [('r', 'rev', [], _('revision'), _('REV')),
1787 [('r', 'rev', [], _('revision'), _('REV')),
1788 ('c', 'change', '', _('change made by revision'), _('REV'))
1788 ('c', 'change', '', _('change made by revision'), _('REV'))
1789 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1789 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1790 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1790 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1791 inferrepo=True,
1791 inferrepo=True,
1792 intents={INTENT_READONLY})
1792 intents={INTENT_READONLY})
1793 def diff(ui, repo, *pats, **opts):
1793 def diff(ui, repo, *pats, **opts):
1794 """diff repository (or selected files)
1794 """diff repository (or selected files)
1795
1795
1796 Show differences between revisions for the specified files.
1796 Show differences between revisions for the specified files.
1797
1797
1798 Differences between files are shown using the unified diff format.
1798 Differences between files are shown using the unified diff format.
1799
1799
1800 .. note::
1800 .. note::
1801
1801
1802 :hg:`diff` may generate unexpected results for merges, as it will
1802 :hg:`diff` may generate unexpected results for merges, as it will
1803 default to comparing against the working directory's first
1803 default to comparing against the working directory's first
1804 parent changeset if no revisions are specified.
1804 parent changeset if no revisions are specified.
1805
1805
1806 When two revision arguments are given, then changes are shown
1806 When two revision arguments are given, then changes are shown
1807 between those revisions. If only one revision is specified then
1807 between those revisions. If only one revision is specified then
1808 that revision is compared to the working directory, and, when no
1808 that revision is compared to the working directory, and, when no
1809 revisions are specified, the working directory files are compared
1809 revisions are specified, the working directory files are compared
1810 to its first parent.
1810 to its first parent.
1811
1811
1812 Alternatively you can specify -c/--change with a revision to see
1812 Alternatively you can specify -c/--change with a revision to see
1813 the changes in that changeset relative to its first parent.
1813 the changes in that changeset relative to its first parent.
1814
1814
1815 Without the -a/--text option, diff will avoid generating diffs of
1815 Without the -a/--text option, diff will avoid generating diffs of
1816 files it detects as binary. With -a, diff will generate a diff
1816 files it detects as binary. With -a, diff will generate a diff
1817 anyway, probably with undesirable results.
1817 anyway, probably with undesirable results.
1818
1818
1819 Use the -g/--git option to generate diffs in the git extended diff
1819 Use the -g/--git option to generate diffs in the git extended diff
1820 format. For more information, read :hg:`help diffs`.
1820 format. For more information, read :hg:`help diffs`.
1821
1821
1822 .. container:: verbose
1822 .. container:: verbose
1823
1823
1824 Examples:
1824 Examples:
1825
1825
1826 - compare a file in the current working directory to its parent::
1826 - compare a file in the current working directory to its parent::
1827
1827
1828 hg diff foo.c
1828 hg diff foo.c
1829
1829
1830 - compare two historical versions of a directory, with rename info::
1830 - compare two historical versions of a directory, with rename info::
1831
1831
1832 hg diff --git -r 1.0:1.2 lib/
1832 hg diff --git -r 1.0:1.2 lib/
1833
1833
1834 - get change stats relative to the last change on some date::
1834 - get change stats relative to the last change on some date::
1835
1835
1836 hg diff --stat -r "date('may 2')"
1836 hg diff --stat -r "date('may 2')"
1837
1837
1838 - diff all newly-added files that contain a keyword::
1838 - diff all newly-added files that contain a keyword::
1839
1839
1840 hg diff "set:added() and grep(GNU)"
1840 hg diff "set:added() and grep(GNU)"
1841
1841
1842 - compare a revision and its parents::
1842 - compare a revision and its parents::
1843
1843
1844 hg diff -c 9353 # compare against first parent
1844 hg diff -c 9353 # compare against first parent
1845 hg diff -r 9353^:9353 # same using revset syntax
1845 hg diff -r 9353^:9353 # same using revset syntax
1846 hg diff -r 9353^2:9353 # compare against the second parent
1846 hg diff -r 9353^2:9353 # compare against the second parent
1847
1847
1848 Returns 0 on success.
1848 Returns 0 on success.
1849 """
1849 """
1850
1850
1851 opts = pycompat.byteskwargs(opts)
1851 opts = pycompat.byteskwargs(opts)
1852 revs = opts.get('rev')
1852 revs = opts.get('rev')
1853 change = opts.get('change')
1853 change = opts.get('change')
1854 stat = opts.get('stat')
1854 stat = opts.get('stat')
1855 reverse = opts.get('reverse')
1855 reverse = opts.get('reverse')
1856
1856
1857 if revs and change:
1857 if revs and change:
1858 msg = _('cannot specify --rev and --change at the same time')
1858 msg = _('cannot specify --rev and --change at the same time')
1859 raise error.Abort(msg)
1859 raise error.Abort(msg)
1860 elif change:
1860 elif change:
1861 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1861 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1862 ctx2 = scmutil.revsingle(repo, change, None)
1862 ctx2 = scmutil.revsingle(repo, change, None)
1863 ctx1 = ctx2.p1()
1863 ctx1 = ctx2.p1()
1864 else:
1864 else:
1865 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1865 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1866 ctx1, ctx2 = scmutil.revpair(repo, revs)
1866 ctx1, ctx2 = scmutil.revpair(repo, revs)
1867 node1, node2 = ctx1.node(), ctx2.node()
1867 node1, node2 = ctx1.node(), ctx2.node()
1868
1868
1869 if reverse:
1869 if reverse:
1870 node1, node2 = node2, node1
1870 node1, node2 = node2, node1
1871
1871
1872 diffopts = patch.diffallopts(ui, opts)
1872 diffopts = patch.diffallopts(ui, opts)
1873 m = scmutil.match(ctx2, pats, opts)
1873 m = scmutil.match(ctx2, pats, opts)
1874 ui.pager('diff')
1874 ui.pager('diff')
1875 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1875 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1876 listsubrepos=opts.get('subrepos'),
1876 listsubrepos=opts.get('subrepos'),
1877 root=opts.get('root'))
1877 root=opts.get('root'))
1878
1878
1879 @command('^export',
1879 @command('^export',
1880 [('B', 'bookmark', '',
1880 [('B', 'bookmark', '',
1881 _('export changes only reachable by given bookmark')),
1881 _('export changes only reachable by given bookmark')),
1882 ('o', 'output', '',
1882 ('o', 'output', '',
1883 _('print output to file with formatted name'), _('FORMAT')),
1883 _('print output to file with formatted name'), _('FORMAT')),
1884 ('', 'switch-parent', None, _('diff against the second parent')),
1884 ('', 'switch-parent', None, _('diff against the second parent')),
1885 ('r', 'rev', [], _('revisions to export'), _('REV')),
1885 ('r', 'rev', [], _('revisions to export'), _('REV')),
1886 ] + diffopts + formatteropts,
1886 ] + diffopts + formatteropts,
1887 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
1887 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
1888 intents={INTENT_READONLY})
1888 intents={INTENT_READONLY})
1889 def export(ui, repo, *changesets, **opts):
1889 def export(ui, repo, *changesets, **opts):
1890 """dump the header and diffs for one or more changesets
1890 """dump the header and diffs for one or more changesets
1891
1891
1892 Print the changeset header and diffs for one or more revisions.
1892 Print the changeset header and diffs for one or more revisions.
1893 If no revision is given, the parent of the working directory is used.
1893 If no revision is given, the parent of the working directory is used.
1894
1894
1895 The information shown in the changeset header is: author, date,
1895 The information shown in the changeset header is: author, date,
1896 branch name (if non-default), changeset hash, parent(s) and commit
1896 branch name (if non-default), changeset hash, parent(s) and commit
1897 comment.
1897 comment.
1898
1898
1899 .. note::
1899 .. note::
1900
1900
1901 :hg:`export` may generate unexpected diff output for merge
1901 :hg:`export` may generate unexpected diff output for merge
1902 changesets, as it will compare the merge changeset against its
1902 changesets, as it will compare the merge changeset against its
1903 first parent only.
1903 first parent only.
1904
1904
1905 Output may be to a file, in which case the name of the file is
1905 Output may be to a file, in which case the name of the file is
1906 given using a template string. See :hg:`help templates`. In addition
1906 given using a template string. See :hg:`help templates`. In addition
1907 to the common template keywords, the following formatting rules are
1907 to the common template keywords, the following formatting rules are
1908 supported:
1908 supported:
1909
1909
1910 :``%%``: literal "%" character
1910 :``%%``: literal "%" character
1911 :``%H``: changeset hash (40 hexadecimal digits)
1911 :``%H``: changeset hash (40 hexadecimal digits)
1912 :``%N``: number of patches being generated
1912 :``%N``: number of patches being generated
1913 :``%R``: changeset revision number
1913 :``%R``: changeset revision number
1914 :``%b``: basename of the exporting repository
1914 :``%b``: basename of the exporting repository
1915 :``%h``: short-form changeset hash (12 hexadecimal digits)
1915 :``%h``: short-form changeset hash (12 hexadecimal digits)
1916 :``%m``: first line of the commit message (only alphanumeric characters)
1916 :``%m``: first line of the commit message (only alphanumeric characters)
1917 :``%n``: zero-padded sequence number, starting at 1
1917 :``%n``: zero-padded sequence number, starting at 1
1918 :``%r``: zero-padded changeset revision number
1918 :``%r``: zero-padded changeset revision number
1919 :``\\``: literal "\\" character
1919 :``\\``: literal "\\" character
1920
1920
1921 Without the -a/--text option, export will avoid generating diffs
1921 Without the -a/--text option, export will avoid generating diffs
1922 of files it detects as binary. With -a, export will generate a
1922 of files it detects as binary. With -a, export will generate a
1923 diff anyway, probably with undesirable results.
1923 diff anyway, probably with undesirable results.
1924
1924
1925 With -B/--bookmark changesets reachable by the given bookmark are
1925 With -B/--bookmark changesets reachable by the given bookmark are
1926 selected.
1926 selected.
1927
1927
1928 Use the -g/--git option to generate diffs in the git extended diff
1928 Use the -g/--git option to generate diffs in the git extended diff
1929 format. See :hg:`help diffs` for more information.
1929 format. See :hg:`help diffs` for more information.
1930
1930
1931 With the --switch-parent option, the diff will be against the
1931 With the --switch-parent option, the diff will be against the
1932 second parent. It can be useful to review a merge.
1932 second parent. It can be useful to review a merge.
1933
1933
1934 .. container:: verbose
1934 .. container:: verbose
1935
1935
1936 Examples:
1936 Examples:
1937
1937
1938 - use export and import to transplant a bugfix to the current
1938 - use export and import to transplant a bugfix to the current
1939 branch::
1939 branch::
1940
1940
1941 hg export -r 9353 | hg import -
1941 hg export -r 9353 | hg import -
1942
1942
1943 - export all the changesets between two revisions to a file with
1943 - export all the changesets between two revisions to a file with
1944 rename information::
1944 rename information::
1945
1945
1946 hg export --git -r 123:150 > changes.txt
1946 hg export --git -r 123:150 > changes.txt
1947
1947
1948 - split outgoing changes into a series of patches with
1948 - split outgoing changes into a series of patches with
1949 descriptive names::
1949 descriptive names::
1950
1950
1951 hg export -r "outgoing()" -o "%n-%m.patch"
1951 hg export -r "outgoing()" -o "%n-%m.patch"
1952
1952
1953 Returns 0 on success.
1953 Returns 0 on success.
1954 """
1954 """
1955 opts = pycompat.byteskwargs(opts)
1955 opts = pycompat.byteskwargs(opts)
1956 bookmark = opts.get('bookmark')
1956 bookmark = opts.get('bookmark')
1957 changesets += tuple(opts.get('rev', []))
1957 changesets += tuple(opts.get('rev', []))
1958
1958
1959 if bookmark and changesets:
1959 if bookmark and changesets:
1960 raise error.Abort(_("-r and -B are mutually exclusive"))
1960 raise error.Abort(_("-r and -B are mutually exclusive"))
1961
1961
1962 if bookmark:
1962 if bookmark:
1963 if bookmark not in repo._bookmarks:
1963 if bookmark not in repo._bookmarks:
1964 raise error.Abort(_("bookmark '%s' not found") % bookmark)
1964 raise error.Abort(_("bookmark '%s' not found") % bookmark)
1965
1965
1966 revs = scmutil.bookmarkrevs(repo, bookmark)
1966 revs = scmutil.bookmarkrevs(repo, bookmark)
1967 else:
1967 else:
1968 if not changesets:
1968 if not changesets:
1969 changesets = ['.']
1969 changesets = ['.']
1970
1970
1971 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1971 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1972 revs = scmutil.revrange(repo, changesets)
1972 revs = scmutil.revrange(repo, changesets)
1973
1973
1974 if not revs:
1974 if not revs:
1975 raise error.Abort(_("export requires at least one changeset"))
1975 raise error.Abort(_("export requires at least one changeset"))
1976 if len(revs) > 1:
1976 if len(revs) > 1:
1977 ui.note(_('exporting patches:\n'))
1977 ui.note(_('exporting patches:\n'))
1978 else:
1978 else:
1979 ui.note(_('exporting patch:\n'))
1979 ui.note(_('exporting patch:\n'))
1980
1980
1981 fntemplate = opts.get('output')
1981 fntemplate = opts.get('output')
1982 if cmdutil.isstdiofilename(fntemplate):
1982 if cmdutil.isstdiofilename(fntemplate):
1983 fntemplate = ''
1983 fntemplate = ''
1984
1984
1985 if fntemplate:
1985 if fntemplate:
1986 fm = formatter.nullformatter(ui, 'export', opts)
1986 fm = formatter.nullformatter(ui, 'export', opts)
1987 else:
1987 else:
1988 ui.pager('export')
1988 ui.pager('export')
1989 fm = ui.formatter('export', opts)
1989 fm = ui.formatter('export', opts)
1990 with fm:
1990 with fm:
1991 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
1991 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
1992 switch_parent=opts.get('switch_parent'),
1992 switch_parent=opts.get('switch_parent'),
1993 opts=patch.diffallopts(ui, opts))
1993 opts=patch.diffallopts(ui, opts))
1994
1994
1995 @command('files',
1995 @command('files',
1996 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1996 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1997 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1997 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1998 ] + walkopts + formatteropts + subrepoopts,
1998 ] + walkopts + formatteropts + subrepoopts,
1999 _('[OPTION]... [FILE]...'),
1999 _('[OPTION]... [FILE]...'),
2000 intents={INTENT_READONLY})
2000 intents={INTENT_READONLY})
2001 def files(ui, repo, *pats, **opts):
2001 def files(ui, repo, *pats, **opts):
2002 """list tracked files
2002 """list tracked files
2003
2003
2004 Print files under Mercurial control in the working directory or
2004 Print files under Mercurial control in the working directory or
2005 specified revision for given files (excluding removed files).
2005 specified revision for given files (excluding removed files).
2006 Files can be specified as filenames or filesets.
2006 Files can be specified as filenames or filesets.
2007
2007
2008 If no files are given to match, this command prints the names
2008 If no files are given to match, this command prints the names
2009 of all files under Mercurial control.
2009 of all files under Mercurial control.
2010
2010
2011 .. container:: verbose
2011 .. container:: verbose
2012
2012
2013 Examples:
2013 Examples:
2014
2014
2015 - list all files under the current directory::
2015 - list all files under the current directory::
2016
2016
2017 hg files .
2017 hg files .
2018
2018
2019 - shows sizes and flags for current revision::
2019 - shows sizes and flags for current revision::
2020
2020
2021 hg files -vr .
2021 hg files -vr .
2022
2022
2023 - list all files named README::
2023 - list all files named README::
2024
2024
2025 hg files -I "**/README"
2025 hg files -I "**/README"
2026
2026
2027 - list all binary files::
2027 - list all binary files::
2028
2028
2029 hg files "set:binary()"
2029 hg files "set:binary()"
2030
2030
2031 - find files containing a regular expression::
2031 - find files containing a regular expression::
2032
2032
2033 hg files "set:grep('bob')"
2033 hg files "set:grep('bob')"
2034
2034
2035 - search tracked file contents with xargs and grep::
2035 - search tracked file contents with xargs and grep::
2036
2036
2037 hg files -0 | xargs -0 grep foo
2037 hg files -0 | xargs -0 grep foo
2038
2038
2039 See :hg:`help patterns` and :hg:`help filesets` for more information
2039 See :hg:`help patterns` and :hg:`help filesets` for more information
2040 on specifying file patterns.
2040 on specifying file patterns.
2041
2041
2042 Returns 0 if a match is found, 1 otherwise.
2042 Returns 0 if a match is found, 1 otherwise.
2043
2043
2044 """
2044 """
2045
2045
2046 opts = pycompat.byteskwargs(opts)
2046 opts = pycompat.byteskwargs(opts)
2047 rev = opts.get('rev')
2047 rev = opts.get('rev')
2048 if rev:
2048 if rev:
2049 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2049 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2050 ctx = scmutil.revsingle(repo, rev, None)
2050 ctx = scmutil.revsingle(repo, rev, None)
2051
2051
2052 end = '\n'
2052 end = '\n'
2053 if opts.get('print0'):
2053 if opts.get('print0'):
2054 end = '\0'
2054 end = '\0'
2055 fmt = '%s' + end
2055 fmt = '%s' + end
2056
2056
2057 m = scmutil.match(ctx, pats, opts)
2057 m = scmutil.match(ctx, pats, opts)
2058 ui.pager('files')
2058 ui.pager('files')
2059 with ui.formatter('files', opts) as fm:
2059 with ui.formatter('files', opts) as fm:
2060 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2060 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2061
2061
2062 @command(
2062 @command(
2063 '^forget',
2063 '^forget',
2064 [('i', 'interactive', None, _('use interactive mode')),
2064 [('i', 'interactive', None, _('use interactive mode')),
2065 ] + walkopts + dryrunopts,
2065 ] + walkopts + dryrunopts,
2066 _('[OPTION]... FILE...'), inferrepo=True)
2066 _('[OPTION]... FILE...'), inferrepo=True)
2067 def forget(ui, repo, *pats, **opts):
2067 def forget(ui, repo, *pats, **opts):
2068 """forget the specified files on the next commit
2068 """forget the specified files on the next commit
2069
2069
2070 Mark the specified files so they will no longer be tracked
2070 Mark the specified files so they will no longer be tracked
2071 after the next commit.
2071 after the next commit.
2072
2072
2073 This only removes files from the current branch, not from the
2073 This only removes files from the current branch, not from the
2074 entire project history, and it does not delete them from the
2074 entire project history, and it does not delete them from the
2075 working directory.
2075 working directory.
2076
2076
2077 To delete the file from the working directory, see :hg:`remove`.
2077 To delete the file from the working directory, see :hg:`remove`.
2078
2078
2079 To undo a forget before the next commit, see :hg:`add`.
2079 To undo a forget before the next commit, see :hg:`add`.
2080
2080
2081 .. container:: verbose
2081 .. container:: verbose
2082
2082
2083 Examples:
2083 Examples:
2084
2084
2085 - forget newly-added binary files::
2085 - forget newly-added binary files::
2086
2086
2087 hg forget "set:added() and binary()"
2087 hg forget "set:added() and binary()"
2088
2088
2089 - forget files that would be excluded by .hgignore::
2089 - forget files that would be excluded by .hgignore::
2090
2090
2091 hg forget "set:hgignore()"
2091 hg forget "set:hgignore()"
2092
2092
2093 Returns 0 on success.
2093 Returns 0 on success.
2094 """
2094 """
2095
2095
2096 opts = pycompat.byteskwargs(opts)
2096 opts = pycompat.byteskwargs(opts)
2097 if not pats:
2097 if not pats:
2098 raise error.Abort(_('no files specified'))
2098 raise error.Abort(_('no files specified'))
2099
2099
2100 m = scmutil.match(repo[None], pats, opts)
2100 m = scmutil.match(repo[None], pats, opts)
2101 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2101 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2102 rejected = cmdutil.forget(ui, repo, m, prefix="",
2102 rejected = cmdutil.forget(ui, repo, m, prefix="",
2103 explicitonly=False, dryrun=dryrun,
2103 explicitonly=False, dryrun=dryrun,
2104 interactive=interactive)[0]
2104 interactive=interactive)[0]
2105 return rejected and 1 or 0
2105 return rejected and 1 or 0
2106
2106
2107 @command(
2107 @command(
2108 'graft',
2108 'graft',
2109 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2109 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2110 ('c', 'continue', False, _('resume interrupted graft')),
2110 ('c', 'continue', False, _('resume interrupted graft')),
2111 ('', 'stop', False, _('stop interrupted graft')),
2111 ('', 'stop', False, _('stop interrupted graft')),
2112 ('', 'abort', False, _('abort interrupted graft')),
2112 ('', 'abort', False, _('abort interrupted graft')),
2113 ('e', 'edit', False, _('invoke editor on commit messages')),
2113 ('e', 'edit', False, _('invoke editor on commit messages')),
2114 ('', 'log', None, _('append graft info to log message')),
2114 ('', 'log', None, _('append graft info to log message')),
2115 ('', 'no-commit', None,
2115 ('', 'no-commit', None,
2116 _("don't commit, just apply the changes in working directory")),
2116 _("don't commit, just apply the changes in working directory")),
2117 ('f', 'force', False, _('force graft')),
2117 ('f', 'force', False, _('force graft')),
2118 ('D', 'currentdate', False,
2118 ('D', 'currentdate', False,
2119 _('record the current date as commit date')),
2119 _('record the current date as commit date')),
2120 ('U', 'currentuser', False,
2120 ('U', 'currentuser', False,
2121 _('record the current user as committer'), _('DATE'))]
2121 _('record the current user as committer'), _('DATE'))]
2122 + commitopts2 + mergetoolopts + dryrunopts,
2122 + commitopts2 + mergetoolopts + dryrunopts,
2123 _('[OPTION]... [-r REV]... REV...'))
2123 _('[OPTION]... [-r REV]... REV...'))
2124 def graft(ui, repo, *revs, **opts):
2124 def graft(ui, repo, *revs, **opts):
2125 '''copy changes from other branches onto the current branch
2125 '''copy changes from other branches onto the current branch
2126
2126
2127 This command uses Mercurial's merge logic to copy individual
2127 This command uses Mercurial's merge logic to copy individual
2128 changes from other branches without merging branches in the
2128 changes from other branches without merging branches in the
2129 history graph. This is sometimes known as 'backporting' or
2129 history graph. This is sometimes known as 'backporting' or
2130 'cherry-picking'. By default, graft will copy user, date, and
2130 'cherry-picking'. By default, graft will copy user, date, and
2131 description from the source changesets.
2131 description from the source changesets.
2132
2132
2133 Changesets that are ancestors of the current revision, that have
2133 Changesets that are ancestors of the current revision, that have
2134 already been grafted, or that are merges will be skipped.
2134 already been grafted, or that are merges will be skipped.
2135
2135
2136 If --log is specified, log messages will have a comment appended
2136 If --log is specified, log messages will have a comment appended
2137 of the form::
2137 of the form::
2138
2138
2139 (grafted from CHANGESETHASH)
2139 (grafted from CHANGESETHASH)
2140
2140
2141 If --force is specified, revisions will be grafted even if they
2141 If --force is specified, revisions will be grafted even if they
2142 are already ancestors of, or have been grafted to, the destination.
2142 are already ancestors of, or have been grafted to, the destination.
2143 This is useful when the revisions have since been backed out.
2143 This is useful when the revisions have since been backed out.
2144
2144
2145 If a graft merge results in conflicts, the graft process is
2145 If a graft merge results in conflicts, the graft process is
2146 interrupted so that the current merge can be manually resolved.
2146 interrupted so that the current merge can be manually resolved.
2147 Once all conflicts are addressed, the graft process can be
2147 Once all conflicts are addressed, the graft process can be
2148 continued with the -c/--continue option.
2148 continued with the -c/--continue option.
2149
2149
2150 The -c/--continue option reapplies all the earlier options.
2150 The -c/--continue option reapplies all the earlier options.
2151
2151
2152 .. container:: verbose
2152 .. container:: verbose
2153
2153
2154 Examples:
2154 Examples:
2155
2155
2156 - copy a single change to the stable branch and edit its description::
2156 - copy a single change to the stable branch and edit its description::
2157
2157
2158 hg update stable
2158 hg update stable
2159 hg graft --edit 9393
2159 hg graft --edit 9393
2160
2160
2161 - graft a range of changesets with one exception, updating dates::
2161 - graft a range of changesets with one exception, updating dates::
2162
2162
2163 hg graft -D "2085::2093 and not 2091"
2163 hg graft -D "2085::2093 and not 2091"
2164
2164
2165 - continue a graft after resolving conflicts::
2165 - continue a graft after resolving conflicts::
2166
2166
2167 hg graft -c
2167 hg graft -c
2168
2168
2169 - show the source of a grafted changeset::
2169 - show the source of a grafted changeset::
2170
2170
2171 hg log --debug -r .
2171 hg log --debug -r .
2172
2172
2173 - show revisions sorted by date::
2173 - show revisions sorted by date::
2174
2174
2175 hg log -r "sort(all(), date)"
2175 hg log -r "sort(all(), date)"
2176
2176
2177 See :hg:`help revisions` for more about specifying revisions.
2177 See :hg:`help revisions` for more about specifying revisions.
2178
2178
2179 Returns 0 on successful completion.
2179 Returns 0 on successful completion.
2180 '''
2180 '''
2181 with repo.wlock():
2181 with repo.wlock():
2182 return _dograft(ui, repo, *revs, **opts)
2182 return _dograft(ui, repo, *revs, **opts)
2183
2183
2184 def _dograft(ui, repo, *revs, **opts):
2184 def _dograft(ui, repo, *revs, **opts):
2185 opts = pycompat.byteskwargs(opts)
2185 opts = pycompat.byteskwargs(opts)
2186 if revs and opts.get('rev'):
2186 if revs and opts.get('rev'):
2187 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2187 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2188 'revision ordering!\n'))
2188 'revision ordering!\n'))
2189
2189
2190 revs = list(revs)
2190 revs = list(revs)
2191 revs.extend(opts.get('rev'))
2191 revs.extend(opts.get('rev'))
2192 # a dict of data to be stored in state file
2192 # a dict of data to be stored in state file
2193 statedata = {}
2193 statedata = {}
2194 # list of new nodes created by ongoing graft
2194 # list of new nodes created by ongoing graft
2195 statedata['newnodes'] = []
2195 statedata['newnodes'] = []
2196
2196
2197 if not opts.get('user') and opts.get('currentuser'):
2197 if not opts.get('user') and opts.get('currentuser'):
2198 opts['user'] = ui.username()
2198 opts['user'] = ui.username()
2199 if not opts.get('date') and opts.get('currentdate'):
2199 if not opts.get('date') and opts.get('currentdate'):
2200 opts['date'] = "%d %d" % dateutil.makedate()
2200 opts['date'] = "%d %d" % dateutil.makedate()
2201
2201
2202 editor = cmdutil.getcommiteditor(editform='graft',
2202 editor = cmdutil.getcommiteditor(editform='graft',
2203 **pycompat.strkwargs(opts))
2203 **pycompat.strkwargs(opts))
2204
2204
2205 cont = False
2205 cont = False
2206 if opts.get('no_commit'):
2206 if opts.get('no_commit'):
2207 if opts.get('edit'):
2207 if opts.get('edit'):
2208 raise error.Abort(_("cannot specify --no-commit and "
2208 raise error.Abort(_("cannot specify --no-commit and "
2209 "--edit together"))
2209 "--edit together"))
2210 if opts.get('currentuser'):
2210 if opts.get('currentuser'):
2211 raise error.Abort(_("cannot specify --no-commit and "
2211 raise error.Abort(_("cannot specify --no-commit and "
2212 "--currentuser together"))
2212 "--currentuser together"))
2213 if opts.get('currentdate'):
2213 if opts.get('currentdate'):
2214 raise error.Abort(_("cannot specify --no-commit and "
2214 raise error.Abort(_("cannot specify --no-commit and "
2215 "--currentdate together"))
2215 "--currentdate together"))
2216 if opts.get('log'):
2216 if opts.get('log'):
2217 raise error.Abort(_("cannot specify --no-commit and "
2217 raise error.Abort(_("cannot specify --no-commit and "
2218 "--log together"))
2218 "--log together"))
2219
2219
2220 graftstate = statemod.cmdstate(repo, 'graftstate')
2220 graftstate = statemod.cmdstate(repo, 'graftstate')
2221
2221
2222 if opts.get('stop'):
2222 if opts.get('stop'):
2223 if opts.get('continue'):
2223 if opts.get('continue'):
2224 raise error.Abort(_("cannot use '--continue' and "
2224 raise error.Abort(_("cannot use '--continue' and "
2225 "'--stop' together"))
2225 "'--stop' together"))
2226 if opts.get('abort'):
2226 if opts.get('abort'):
2227 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2227 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2228
2228
2229 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2229 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2230 opts.get('date'), opts.get('currentdate'),
2230 opts.get('date'), opts.get('currentdate'),
2231 opts.get('currentuser'), opts.get('rev'))):
2231 opts.get('currentuser'), opts.get('rev'))):
2232 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2232 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2233 return _stopgraft(ui, repo, graftstate)
2233 return _stopgraft(ui, repo, graftstate)
2234 elif opts.get('abort'):
2234 elif opts.get('abort'):
2235 if opts.get('continue'):
2235 if opts.get('continue'):
2236 raise error.Abort(_("cannot use '--continue' and "
2236 raise error.Abort(_("cannot use '--continue' and "
2237 "'--abort' together"))
2237 "'--abort' together"))
2238 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2238 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2239 opts.get('date'), opts.get('currentdate'),
2239 opts.get('date'), opts.get('currentdate'),
2240 opts.get('currentuser'), opts.get('rev'))):
2240 opts.get('currentuser'), opts.get('rev'))):
2241 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2241 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2242
2242
2243 return _abortgraft(ui, repo, graftstate)
2243 return _abortgraft(ui, repo, graftstate)
2244 elif opts.get('continue'):
2244 elif opts.get('continue'):
2245 cont = True
2245 cont = True
2246 if revs:
2246 if revs:
2247 raise error.Abort(_("can't specify --continue and revisions"))
2247 raise error.Abort(_("can't specify --continue and revisions"))
2248 # read in unfinished revisions
2248 # read in unfinished revisions
2249 if graftstate.exists():
2249 if graftstate.exists():
2250 statedata = _readgraftstate(repo, graftstate)
2250 statedata = _readgraftstate(repo, graftstate)
2251 if statedata.get('date'):
2251 if statedata.get('date'):
2252 opts['date'] = statedata['date']
2252 opts['date'] = statedata['date']
2253 if statedata.get('user'):
2253 if statedata.get('user'):
2254 opts['user'] = statedata['user']
2254 opts['user'] = statedata['user']
2255 if statedata.get('log'):
2255 if statedata.get('log'):
2256 opts['log'] = True
2256 opts['log'] = True
2257 if statedata.get('no_commit'):
2257 if statedata.get('no_commit'):
2258 opts['no_commit'] = statedata.get('no_commit')
2258 opts['no_commit'] = statedata.get('no_commit')
2259 nodes = statedata['nodes']
2259 nodes = statedata['nodes']
2260 revs = [repo[node].rev() for node in nodes]
2260 revs = [repo[node].rev() for node in nodes]
2261 else:
2261 else:
2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2262 cmdutil.wrongtooltocontinue(repo, _('graft'))
2263 else:
2263 else:
2264 if not revs:
2264 if not revs:
2265 raise error.Abort(_('no revisions specified'))
2265 raise error.Abort(_('no revisions specified'))
2266 cmdutil.checkunfinished(repo)
2266 cmdutil.checkunfinished(repo)
2267 cmdutil.bailifchanged(repo)
2267 cmdutil.bailifchanged(repo)
2268 revs = scmutil.revrange(repo, revs)
2268 revs = scmutil.revrange(repo, revs)
2269
2269
2270 skipped = set()
2270 skipped = set()
2271 # check for merges
2271 # check for merges
2272 for rev in repo.revs('%ld and merge()', revs):
2272 for rev in repo.revs('%ld and merge()', revs):
2273 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2273 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2274 skipped.add(rev)
2274 skipped.add(rev)
2275 revs = [r for r in revs if r not in skipped]
2275 revs = [r for r in revs if r not in skipped]
2276 if not revs:
2276 if not revs:
2277 return -1
2277 return -1
2278
2278
2279 # Don't check in the --continue case, in effect retaining --force across
2279 # Don't check in the --continue case, in effect retaining --force across
2280 # --continues. That's because without --force, any revisions we decided to
2280 # --continues. That's because without --force, any revisions we decided to
2281 # skip would have been filtered out here, so they wouldn't have made their
2281 # skip would have been filtered out here, so they wouldn't have made their
2282 # way to the graftstate. With --force, any revisions we would have otherwise
2282 # way to the graftstate. With --force, any revisions we would have otherwise
2283 # skipped would not have been filtered out, and if they hadn't been applied
2283 # skipped would not have been filtered out, and if they hadn't been applied
2284 # already, they'd have been in the graftstate.
2284 # already, they'd have been in the graftstate.
2285 if not (cont or opts.get('force')):
2285 if not (cont or opts.get('force')):
2286 # check for ancestors of dest branch
2286 # check for ancestors of dest branch
2287 crev = repo['.'].rev()
2287 crev = repo['.'].rev()
2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2288 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2289 # XXX make this lazy in the future
2289 # XXX make this lazy in the future
2290 # don't mutate while iterating, create a copy
2290 # don't mutate while iterating, create a copy
2291 for rev in list(revs):
2291 for rev in list(revs):
2292 if rev in ancestors:
2292 if rev in ancestors:
2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2293 ui.warn(_('skipping ancestor revision %d:%s\n') %
2294 (rev, repo[rev]))
2294 (rev, repo[rev]))
2295 # XXX remove on list is slow
2295 # XXX remove on list is slow
2296 revs.remove(rev)
2296 revs.remove(rev)
2297 if not revs:
2297 if not revs:
2298 return -1
2298 return -1
2299
2299
2300 # analyze revs for earlier grafts
2300 # analyze revs for earlier grafts
2301 ids = {}
2301 ids = {}
2302 for ctx in repo.set("%ld", revs):
2302 for ctx in repo.set("%ld", revs):
2303 ids[ctx.hex()] = ctx.rev()
2303 ids[ctx.hex()] = ctx.rev()
2304 n = ctx.extra().get('source')
2304 n = ctx.extra().get('source')
2305 if n:
2305 if n:
2306 ids[n] = ctx.rev()
2306 ids[n] = ctx.rev()
2307
2307
2308 # check ancestors for earlier grafts
2308 # check ancestors for earlier grafts
2309 ui.debug('scanning for duplicate grafts\n')
2309 ui.debug('scanning for duplicate grafts\n')
2310
2310
2311 # The only changesets we can be sure doesn't contain grafts of any
2311 # The only changesets we can be sure doesn't contain grafts of any
2312 # revs, are the ones that are common ancestors of *all* revs:
2312 # revs, are the ones that are common ancestors of *all* revs:
2313 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2313 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2314 ctx = repo[rev]
2314 ctx = repo[rev]
2315 n = ctx.extra().get('source')
2315 n = ctx.extra().get('source')
2316 if n in ids:
2316 if n in ids:
2317 try:
2317 try:
2318 r = repo[n].rev()
2318 r = repo[n].rev()
2319 except error.RepoLookupError:
2319 except error.RepoLookupError:
2320 r = None
2320 r = None
2321 if r in revs:
2321 if r in revs:
2322 ui.warn(_('skipping revision %d:%s '
2322 ui.warn(_('skipping revision %d:%s '
2323 '(already grafted to %d:%s)\n')
2323 '(already grafted to %d:%s)\n')
2324 % (r, repo[r], rev, ctx))
2324 % (r, repo[r], rev, ctx))
2325 revs.remove(r)
2325 revs.remove(r)
2326 elif ids[n] in revs:
2326 elif ids[n] in revs:
2327 if r is None:
2327 if r is None:
2328 ui.warn(_('skipping already grafted revision %d:%s '
2328 ui.warn(_('skipping already grafted revision %d:%s '
2329 '(%d:%s also has unknown origin %s)\n')
2329 '(%d:%s also has unknown origin %s)\n')
2330 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2330 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2331 else:
2331 else:
2332 ui.warn(_('skipping already grafted revision %d:%s '
2332 ui.warn(_('skipping already grafted revision %d:%s '
2333 '(%d:%s also has origin %d:%s)\n')
2333 '(%d:%s also has origin %d:%s)\n')
2334 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2334 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2335 revs.remove(ids[n])
2335 revs.remove(ids[n])
2336 elif ctx.hex() in ids:
2336 elif ctx.hex() in ids:
2337 r = ids[ctx.hex()]
2337 r = ids[ctx.hex()]
2338 ui.warn(_('skipping already grafted revision %d:%s '
2338 ui.warn(_('skipping already grafted revision %d:%s '
2339 '(was grafted from %d:%s)\n') %
2339 '(was grafted from %d:%s)\n') %
2340 (r, repo[r], rev, ctx))
2340 (r, repo[r], rev, ctx))
2341 revs.remove(r)
2341 revs.remove(r)
2342 if not revs:
2342 if not revs:
2343 return -1
2343 return -1
2344
2344
2345 if opts.get('no_commit'):
2345 if opts.get('no_commit'):
2346 statedata['no_commit'] = True
2346 statedata['no_commit'] = True
2347 for pos, ctx in enumerate(repo.set("%ld", revs)):
2347 for pos, ctx in enumerate(repo.set("%ld", revs)):
2348 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2348 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2349 ctx.description().split('\n', 1)[0])
2349 ctx.description().split('\n', 1)[0])
2350 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2350 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2351 if names:
2351 if names:
2352 desc += ' (%s)' % ' '.join(names)
2352 desc += ' (%s)' % ' '.join(names)
2353 ui.status(_('grafting %s\n') % desc)
2353 ui.status(_('grafting %s\n') % desc)
2354 if opts.get('dry_run'):
2354 if opts.get('dry_run'):
2355 continue
2355 continue
2356
2356
2357 source = ctx.extra().get('source')
2357 source = ctx.extra().get('source')
2358 extra = {}
2358 extra = {}
2359 if source:
2359 if source:
2360 extra['source'] = source
2360 extra['source'] = source
2361 extra['intermediate-source'] = ctx.hex()
2361 extra['intermediate-source'] = ctx.hex()
2362 else:
2362 else:
2363 extra['source'] = ctx.hex()
2363 extra['source'] = ctx.hex()
2364 user = ctx.user()
2364 user = ctx.user()
2365 if opts.get('user'):
2365 if opts.get('user'):
2366 user = opts['user']
2366 user = opts['user']
2367 statedata['user'] = user
2367 statedata['user'] = user
2368 date = ctx.date()
2368 date = ctx.date()
2369 if opts.get('date'):
2369 if opts.get('date'):
2370 date = opts['date']
2370 date = opts['date']
2371 statedata['date'] = date
2371 statedata['date'] = date
2372 message = ctx.description()
2372 message = ctx.description()
2373 if opts.get('log'):
2373 if opts.get('log'):
2374 message += '\n(grafted from %s)' % ctx.hex()
2374 message += '\n(grafted from %s)' % ctx.hex()
2375 statedata['log'] = True
2375 statedata['log'] = True
2376
2376
2377 # we don't merge the first commit when continuing
2377 # we don't merge the first commit when continuing
2378 if not cont:
2378 if not cont:
2379 # perform the graft merge with p1(rev) as 'ancestor'
2379 # perform the graft merge with p1(rev) as 'ancestor'
2380 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2380 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2381 with ui.configoverride(overrides, 'graft'):
2381 with ui.configoverride(overrides, 'graft'):
2382 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft'])
2382 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft'])
2383 # report any conflicts
2383 # report any conflicts
2384 if stats.unresolvedcount > 0:
2384 if stats.unresolvedcount > 0:
2385 # write out state for --continue
2385 # write out state for --continue
2386 nodes = [repo[rev].hex() for rev in revs[pos:]]
2386 nodes = [repo[rev].hex() for rev in revs[pos:]]
2387 statedata['nodes'] = nodes
2387 statedata['nodes'] = nodes
2388 stateversion = 1
2388 stateversion = 1
2389 graftstate.save(stateversion, statedata)
2389 graftstate.save(stateversion, statedata)
2390 hint = _("use 'hg resolve' and 'hg graft --continue'")
2390 hint = _("use 'hg resolve' and 'hg graft --continue'")
2391 raise error.Abort(
2391 raise error.Abort(
2392 _("unresolved conflicts, can't continue"),
2392 _("unresolved conflicts, can't continue"),
2393 hint=hint)
2393 hint=hint)
2394 else:
2394 else:
2395 cont = False
2395 cont = False
2396
2396
2397 # commit if --no-commit is false
2397 # commit if --no-commit is false
2398 if not opts.get('no_commit'):
2398 if not opts.get('no_commit'):
2399 node = repo.commit(text=message, user=user, date=date, extra=extra,
2399 node = repo.commit(text=message, user=user, date=date, extra=extra,
2400 editor=editor)
2400 editor=editor)
2401 if node is None:
2401 if node is None:
2402 ui.warn(
2402 ui.warn(
2403 _('note: graft of %d:%s created no changes to commit\n') %
2403 _('note: graft of %d:%s created no changes to commit\n') %
2404 (ctx.rev(), ctx))
2404 (ctx.rev(), ctx))
2405 # checking that newnodes exist because old state files won't have it
2405 # checking that newnodes exist because old state files won't have it
2406 elif statedata.get('newnodes') is not None:
2406 elif statedata.get('newnodes') is not None:
2407 statedata['newnodes'].append(node)
2407 statedata['newnodes'].append(node)
2408
2408
2409 # remove state when we complete successfully
2409 # remove state when we complete successfully
2410 if not opts.get('dry_run'):
2410 if not opts.get('dry_run'):
2411 graftstate.delete()
2411 graftstate.delete()
2412
2412
2413 return 0
2413 return 0
2414
2414
2415 def _abortgraft(ui, repo, graftstate):
2415 def _abortgraft(ui, repo, graftstate):
2416 """abort the interrupted graft and rollbacks to the state before interrupted
2416 """abort the interrupted graft and rollbacks to the state before interrupted
2417 graft"""
2417 graft"""
2418 if not graftstate.exists():
2418 if not graftstate.exists():
2419 raise error.Abort(_("no interrupted graft to abort"))
2419 raise error.Abort(_("no interrupted graft to abort"))
2420 statedata = _readgraftstate(repo, graftstate)
2420 statedata = _readgraftstate(repo, graftstate)
2421 newnodes = statedata.get('newnodes')
2421 newnodes = statedata.get('newnodes')
2422 if newnodes is None:
2422 if newnodes is None:
2423 # and old graft state which does not have all the data required to abort
2423 # and old graft state which does not have all the data required to abort
2424 # the graft
2424 # the graft
2425 raise error.Abort(_("cannot abort using an old graftstate"))
2425 raise error.Abort(_("cannot abort using an old graftstate"))
2426
2426
2427 # changeset from which graft operation was started
2427 # changeset from which graft operation was started
2428 startctx = None
2428 startctx = None
2429 if len(newnodes) > 0:
2429 if len(newnodes) > 0:
2430 startctx = repo[newnodes[0]].p1()
2430 startctx = repo[newnodes[0]].p1()
2431 else:
2431 else:
2432 startctx = repo['.']
2432 startctx = repo['.']
2433 # whether to strip or not
2433 # whether to strip or not
2434 cleanup = False
2434 cleanup = False
2435 if newnodes:
2435 if newnodes:
2436 newnodes = [repo[r].rev() for r in newnodes]
2436 newnodes = [repo[r].rev() for r in newnodes]
2437 cleanup = True
2437 cleanup = True
2438 # checking that none of the newnodes turned public or is public
2438 # checking that none of the newnodes turned public or is public
2439 immutable = [c for c in newnodes if not repo[c].mutable()]
2439 immutable = [c for c in newnodes if not repo[c].mutable()]
2440 if immutable:
2440 if immutable:
2441 repo.ui.warn(_("cannot clean up public changesets %s\n")
2441 repo.ui.warn(_("cannot clean up public changesets %s\n")
2442 % ', '.join(bytes(repo[r]) for r in immutable),
2442 % ', '.join(bytes(repo[r]) for r in immutable),
2443 hint=_("see 'hg help phases' for details"))
2443 hint=_("see 'hg help phases' for details"))
2444 cleanup = False
2444 cleanup = False
2445
2445
2446 # checking that no new nodes are created on top of grafted revs
2446 # checking that no new nodes are created on top of grafted revs
2447 desc = set(repo.changelog.descendants(newnodes))
2447 desc = set(repo.changelog.descendants(newnodes))
2448 if desc - set(newnodes):
2448 if desc - set(newnodes):
2449 repo.ui.warn(_("new changesets detected on destination "
2449 repo.ui.warn(_("new changesets detected on destination "
2450 "branch, can't strip\n"))
2450 "branch, can't strip\n"))
2451 cleanup = False
2451 cleanup = False
2452
2452
2453 if cleanup:
2453 if cleanup:
2454 with repo.wlock(), repo.lock():
2454 with repo.wlock(), repo.lock():
2455 hg.updaterepo(repo, startctx.node(), overwrite=True)
2455 hg.updaterepo(repo, startctx.node(), overwrite=True)
2456 # stripping the new nodes created
2456 # stripping the new nodes created
2457 strippoints = [c.node() for c in repo.set("roots(%ld)",
2457 strippoints = [c.node() for c in repo.set("roots(%ld)",
2458 newnodes)]
2458 newnodes)]
2459 repair.strip(repo.ui, repo, strippoints, backup=False)
2459 repair.strip(repo.ui, repo, strippoints, backup=False)
2460
2460
2461 if not cleanup:
2461 if not cleanup:
2462 # we don't update to the startnode if we can't strip
2462 # we don't update to the startnode if we can't strip
2463 startctx = repo['.']
2463 startctx = repo['.']
2464 hg.updaterepo(repo, startctx.node(), overwrite=True)
2464 hg.updaterepo(repo, startctx.node(), overwrite=True)
2465
2465
2466 ui.status(_("graft aborted\n"))
2466 ui.status(_("graft aborted\n"))
2467 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2467 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2468 graftstate.delete()
2468 graftstate.delete()
2469 return 0
2469 return 0
2470
2470
2471 def _readgraftstate(repo, graftstate):
2471 def _readgraftstate(repo, graftstate):
2472 """read the graft state file and return a dict of the data stored in it"""
2472 """read the graft state file and return a dict of the data stored in it"""
2473 try:
2473 try:
2474 return graftstate.read()
2474 return graftstate.read()
2475 except error.CorruptedState:
2475 except error.CorruptedState:
2476 nodes = repo.vfs.read('graftstate').splitlines()
2476 nodes = repo.vfs.read('graftstate').splitlines()
2477 return {'nodes': nodes}
2477 return {'nodes': nodes}
2478
2478
2479 def _stopgraft(ui, repo, graftstate):
2479 def _stopgraft(ui, repo, graftstate):
2480 """stop the interrupted graft"""
2480 """stop the interrupted graft"""
2481 if not graftstate.exists():
2481 if not graftstate.exists():
2482 raise error.Abort(_("no interrupted graft found"))
2482 raise error.Abort(_("no interrupted graft found"))
2483 pctx = repo['.']
2483 pctx = repo['.']
2484 hg.updaterepo(repo, pctx.node(), overwrite=True)
2484 hg.updaterepo(repo, pctx.node(), overwrite=True)
2485 graftstate.delete()
2485 graftstate.delete()
2486 ui.status(_("stopped the interrupted graft\n"))
2486 ui.status(_("stopped the interrupted graft\n"))
2487 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2487 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2488 return 0
2488 return 0
2489
2489
2490 @command('grep',
2490 @command('grep',
2491 [('0', 'print0', None, _('end fields with NUL')),
2491 [('0', 'print0', None, _('end fields with NUL')),
2492 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2492 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2493 ('', 'diff', None, _('print all revisions when the term was introduced '
2493 ('', 'diff', None, _('print all revisions when the term was introduced '
2494 'or removed')),
2494 'or removed')),
2495 ('a', 'text', None, _('treat all files as text')),
2495 ('a', 'text', None, _('treat all files as text')),
2496 ('f', 'follow', None,
2496 ('f', 'follow', None,
2497 _('follow changeset history,'
2497 _('follow changeset history,'
2498 ' or file history across copies and renames')),
2498 ' or file history across copies and renames')),
2499 ('i', 'ignore-case', None, _('ignore case when matching')),
2499 ('i', 'ignore-case', None, _('ignore case when matching')),
2500 ('l', 'files-with-matches', None,
2500 ('l', 'files-with-matches', None,
2501 _('print only filenames and revisions that match')),
2501 _('print only filenames and revisions that match')),
2502 ('n', 'line-number', None, _('print matching line numbers')),
2502 ('n', 'line-number', None, _('print matching line numbers')),
2503 ('r', 'rev', [],
2503 ('r', 'rev', [],
2504 _('only search files changed within revision range'), _('REV')),
2504 _('only search files changed within revision range'), _('REV')),
2505 ('', 'all-files', None,
2505 ('', 'all-files', None,
2506 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2506 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2507 ('u', 'user', None, _('list the author (long with -v)')),
2507 ('u', 'user', None, _('list the author (long with -v)')),
2508 ('d', 'date', None, _('list the date (short with -q)')),
2508 ('d', 'date', None, _('list the date (short with -q)')),
2509 ] + formatteropts + walkopts,
2509 ] + formatteropts + walkopts,
2510 _('[OPTION]... PATTERN [FILE]...'),
2510 _('[OPTION]... PATTERN [FILE]...'),
2511 inferrepo=True,
2511 inferrepo=True,
2512 intents={INTENT_READONLY})
2512 intents={INTENT_READONLY})
2513 def grep(ui, repo, pattern, *pats, **opts):
2513 def grep(ui, repo, pattern, *pats, **opts):
2514 """search revision history for a pattern in specified files
2514 """search revision history for a pattern in specified files
2515
2515
2516 Search revision history for a regular expression in the specified
2516 Search revision history for a regular expression in the specified
2517 files or the entire project.
2517 files or the entire project.
2518
2518
2519 By default, grep prints the most recent revision number for each
2519 By default, grep prints the most recent revision number for each
2520 file in which it finds a match. To get it to print every revision
2520 file in which it finds a match. To get it to print every revision
2521 that contains a change in match status ("-" for a match that becomes
2521 that contains a change in match status ("-" for a match that becomes
2522 a non-match, or "+" for a non-match that becomes a match), use the
2522 a non-match, or "+" for a non-match that becomes a match), use the
2523 --diff flag.
2523 --diff flag.
2524
2524
2525 PATTERN can be any Python (roughly Perl-compatible) regular
2525 PATTERN can be any Python (roughly Perl-compatible) regular
2526 expression.
2526 expression.
2527
2527
2528 If no FILEs are specified (and -f/--follow isn't set), all files in
2528 If no FILEs are specified (and -f/--follow isn't set), all files in
2529 the repository are searched, including those that don't exist in the
2529 the repository are searched, including those that don't exist in the
2530 current branch or have been deleted in a prior changeset.
2530 current branch or have been deleted in a prior changeset.
2531
2531
2532 Returns 0 if a match is found, 1 otherwise.
2532 Returns 0 if a match is found, 1 otherwise.
2533 """
2533 """
2534 opts = pycompat.byteskwargs(opts)
2534 opts = pycompat.byteskwargs(opts)
2535 diff = opts.get('all') or opts.get('diff')
2535 diff = opts.get('all') or opts.get('diff')
2536 all_files = opts.get('all_files')
2536 if diff and opts.get('all_files'):
2537 if diff and opts.get('all_files'):
2537 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2538 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2538 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2539 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2539 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2540 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2540 # experimental config: commands.grep.all-files
2541 # experimental config: commands.grep.all-files
2541 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2542 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2542 plaingrep = opts.get('all_files') and not opts.get('rev')
2543 plaingrep = opts.get('all_files') and not opts.get('rev')
2543 if plaingrep:
2544 if plaingrep:
2544 opts['rev'] = ['wdir()']
2545 opts['rev'] = ['wdir()']
2545
2546
2546 reflags = re.M
2547 reflags = re.M
2547 if opts.get('ignore_case'):
2548 if opts.get('ignore_case'):
2548 reflags |= re.I
2549 reflags |= re.I
2549 try:
2550 try:
2550 regexp = util.re.compile(pattern, reflags)
2551 regexp = util.re.compile(pattern, reflags)
2551 except re.error as inst:
2552 except re.error as inst:
2552 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2553 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2553 return 1
2554 return 1
2554 sep, eol = ':', '\n'
2555 sep, eol = ':', '\n'
2555 if opts.get('print0'):
2556 if opts.get('print0'):
2556 sep = eol = '\0'
2557 sep = eol = '\0'
2557
2558
2558 getfile = util.lrucachefunc(repo.file)
2559 getfile = util.lrucachefunc(repo.file)
2559
2560
2560 def matchlines(body):
2561 def matchlines(body):
2561 begin = 0
2562 begin = 0
2562 linenum = 0
2563 linenum = 0
2563 while begin < len(body):
2564 while begin < len(body):
2564 match = regexp.search(body, begin)
2565 match = regexp.search(body, begin)
2565 if not match:
2566 if not match:
2566 break
2567 break
2567 mstart, mend = match.span()
2568 mstart, mend = match.span()
2568 linenum += body.count('\n', begin, mstart) + 1
2569 linenum += body.count('\n', begin, mstart) + 1
2569 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2570 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2570 begin = body.find('\n', mend) + 1 or len(body) + 1
2571 begin = body.find('\n', mend) + 1 or len(body) + 1
2571 lend = begin - 1
2572 lend = begin - 1
2572 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2573 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2573
2574
2574 class linestate(object):
2575 class linestate(object):
2575 def __init__(self, line, linenum, colstart, colend):
2576 def __init__(self, line, linenum, colstart, colend):
2576 self.line = line
2577 self.line = line
2577 self.linenum = linenum
2578 self.linenum = linenum
2578 self.colstart = colstart
2579 self.colstart = colstart
2579 self.colend = colend
2580 self.colend = colend
2580
2581
2581 def __hash__(self):
2582 def __hash__(self):
2582 return hash((self.linenum, self.line))
2583 return hash((self.linenum, self.line))
2583
2584
2584 def __eq__(self, other):
2585 def __eq__(self, other):
2585 return self.line == other.line
2586 return self.line == other.line
2586
2587
2587 def findpos(self):
2588 def findpos(self):
2588 """Iterate all (start, end) indices of matches"""
2589 """Iterate all (start, end) indices of matches"""
2589 yield self.colstart, self.colend
2590 yield self.colstart, self.colend
2590 p = self.colend
2591 p = self.colend
2591 while p < len(self.line):
2592 while p < len(self.line):
2592 m = regexp.search(self.line, p)
2593 m = regexp.search(self.line, p)
2593 if not m:
2594 if not m:
2594 break
2595 break
2595 yield m.span()
2596 yield m.span()
2596 p = m.end()
2597 p = m.end()
2597
2598
2598 matches = {}
2599 matches = {}
2599 copies = {}
2600 copies = {}
2600 def grepbody(fn, rev, body):
2601 def grepbody(fn, rev, body):
2601 matches[rev].setdefault(fn, [])
2602 matches[rev].setdefault(fn, [])
2602 m = matches[rev][fn]
2603 m = matches[rev][fn]
2603 for lnum, cstart, cend, line in matchlines(body):
2604 for lnum, cstart, cend, line in matchlines(body):
2604 s = linestate(line, lnum, cstart, cend)
2605 s = linestate(line, lnum, cstart, cend)
2605 m.append(s)
2606 m.append(s)
2606
2607
2607 def difflinestates(a, b):
2608 def difflinestates(a, b):
2608 sm = difflib.SequenceMatcher(None, a, b)
2609 sm = difflib.SequenceMatcher(None, a, b)
2609 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2610 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2610 if tag == 'insert':
2611 if tag == 'insert':
2611 for i in pycompat.xrange(blo, bhi):
2612 for i in pycompat.xrange(blo, bhi):
2612 yield ('+', b[i])
2613 yield ('+', b[i])
2613 elif tag == 'delete':
2614 elif tag == 'delete':
2614 for i in pycompat.xrange(alo, ahi):
2615 for i in pycompat.xrange(alo, ahi):
2615 yield ('-', a[i])
2616 yield ('-', a[i])
2616 elif tag == 'replace':
2617 elif tag == 'replace':
2617 for i in pycompat.xrange(alo, ahi):
2618 for i in pycompat.xrange(alo, ahi):
2618 yield ('-', a[i])
2619 yield ('-', a[i])
2619 for i in pycompat.xrange(blo, bhi):
2620 for i in pycompat.xrange(blo, bhi):
2620 yield ('+', b[i])
2621 yield ('+', b[i])
2621
2622
2622 def display(fm, fn, ctx, pstates, states):
2623 def display(fm, fn, ctx, pstates, states):
2623 rev = scmutil.intrev(ctx)
2624 rev = scmutil.intrev(ctx)
2624 if fm.isplain():
2625 if fm.isplain():
2625 formatuser = ui.shortuser
2626 formatuser = ui.shortuser
2626 else:
2627 else:
2627 formatuser = str
2628 formatuser = str
2628 if ui.quiet:
2629 if ui.quiet:
2629 datefmt = '%Y-%m-%d'
2630 datefmt = '%Y-%m-%d'
2630 else:
2631 else:
2631 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2632 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2632 found = False
2633 found = False
2633 @util.cachefunc
2634 @util.cachefunc
2634 def binary():
2635 def binary():
2635 flog = getfile(fn)
2636 flog = getfile(fn)
2636 try:
2637 try:
2637 return stringutil.binary(flog.read(ctx.filenode(fn)))
2638 return stringutil.binary(flog.read(ctx.filenode(fn)))
2638 except error.WdirUnsupported:
2639 except error.WdirUnsupported:
2639 return ctx[fn].isbinary()
2640 return ctx[fn].isbinary()
2640
2641
2641 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2642 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2642 if diff:
2643 if diff:
2643 iter = difflinestates(pstates, states)
2644 iter = difflinestates(pstates, states)
2644 else:
2645 else:
2645 iter = [('', l) for l in states]
2646 iter = [('', l) for l in states]
2646 for change, l in iter:
2647 for change, l in iter:
2647 fm.startitem()
2648 fm.startitem()
2648 fm.context(ctx=ctx)
2649 fm.context(ctx=ctx)
2649 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2650 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2650
2651
2651 cols = [
2652 cols = [
2652 ('filename', fn, True),
2653 ('filename', fn, True),
2653 ('rev', rev, not plaingrep),
2654 ('rev', rev, not plaingrep),
2654 ('linenumber', l.linenum, opts.get('line_number')),
2655 ('linenumber', l.linenum, opts.get('line_number')),
2655 ]
2656 ]
2656 if diff:
2657 if diff:
2657 cols.append(('change', change, True))
2658 cols.append(('change', change, True))
2658 cols.extend([
2659 cols.extend([
2659 ('user', formatuser(ctx.user()), opts.get('user')),
2660 ('user', formatuser(ctx.user()), opts.get('user')),
2660 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2661 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2661 ])
2662 ])
2662 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2663 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2663 for name, data, cond in cols:
2664 for name, data, cond in cols:
2664 field = fieldnamemap.get(name, name)
2665 field = fieldnamemap.get(name, name)
2665 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2666 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2666 if cond and name != lastcol:
2667 if cond and name != lastcol:
2667 fm.plain(sep, label='grep.sep')
2668 fm.plain(sep, label='grep.sep')
2668 if not opts.get('files_with_matches'):
2669 if not opts.get('files_with_matches'):
2669 fm.plain(sep, label='grep.sep')
2670 fm.plain(sep, label='grep.sep')
2670 if not opts.get('text') and binary():
2671 if not opts.get('text') and binary():
2671 fm.plain(_(" Binary file matches"))
2672 fm.plain(_(" Binary file matches"))
2672 else:
2673 else:
2673 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2674 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2674 fm.plain(eol)
2675 fm.plain(eol)
2675 found = True
2676 found = True
2676 if opts.get('files_with_matches'):
2677 if opts.get('files_with_matches'):
2677 break
2678 break
2678 return found
2679 return found
2679
2680
2680 def displaymatches(fm, l):
2681 def displaymatches(fm, l):
2681 p = 0
2682 p = 0
2682 for s, e in l.findpos():
2683 for s, e in l.findpos():
2683 if p < s:
2684 if p < s:
2684 fm.startitem()
2685 fm.startitem()
2685 fm.write('text', '%s', l.line[p:s])
2686 fm.write('text', '%s', l.line[p:s])
2686 fm.data(matched=False)
2687 fm.data(matched=False)
2687 fm.startitem()
2688 fm.startitem()
2688 fm.write('text', '%s', l.line[s:e], label='grep.match')
2689 fm.write('text', '%s', l.line[s:e], label='grep.match')
2689 fm.data(matched=True)
2690 fm.data(matched=True)
2690 p = e
2691 p = e
2691 if p < len(l.line):
2692 if p < len(l.line):
2692 fm.startitem()
2693 fm.startitem()
2693 fm.write('text', '%s', l.line[p:])
2694 fm.write('text', '%s', l.line[p:])
2694 fm.data(matched=False)
2695 fm.data(matched=False)
2695 fm.end()
2696 fm.end()
2696
2697
2697 skip = {}
2698 skip = {}
2698 revfiles = {}
2699 revfiles = {}
2699 match = scmutil.match(repo[None], pats, opts)
2700 match = scmutil.match(repo[None], pats, opts)
2700 found = False
2701 found = False
2701 follow = opts.get('follow')
2702 follow = opts.get('follow')
2702
2703
2703 def prep(ctx, fns):
2704 def prep(ctx, fns):
2704 rev = ctx.rev()
2705 rev = ctx.rev()
2705 pctx = ctx.p1()
2706 pctx = ctx.p1()
2706 parent = pctx.rev()
2707 parent = pctx.rev()
2707 matches.setdefault(rev, {})
2708 matches.setdefault(rev, {})
2708 matches.setdefault(parent, {})
2709 matches.setdefault(parent, {})
2709 files = revfiles.setdefault(rev, [])
2710 files = revfiles.setdefault(rev, [])
2710 for fn in fns:
2711 for fn in fns:
2711 flog = getfile(fn)
2712 flog = getfile(fn)
2712 try:
2713 try:
2713 fnode = ctx.filenode(fn)
2714 fnode = ctx.filenode(fn)
2714 except error.LookupError:
2715 except error.LookupError:
2715 continue
2716 continue
2716 try:
2717 try:
2717 copied = flog.renamed(fnode)
2718 copied = flog.renamed(fnode)
2718 except error.WdirUnsupported:
2719 except error.WdirUnsupported:
2719 copied = ctx[fn].renamed()
2720 copied = ctx[fn].renamed()
2720 copy = follow and copied and copied[0]
2721 copy = follow and copied and copied[0]
2721 if copy:
2722 if copy:
2722 copies.setdefault(rev, {})[fn] = copy
2723 copies.setdefault(rev, {})[fn] = copy
2723 if fn in skip:
2724 if fn in skip:
2724 if copy:
2725 if copy:
2725 skip[copy] = True
2726 skip[copy] = True
2726 continue
2727 continue
2727 files.append(fn)
2728 files.append(fn)
2728
2729
2729 if fn not in matches[rev]:
2730 if fn not in matches[rev]:
2730 try:
2731 try:
2731 content = flog.read(fnode)
2732 content = flog.read(fnode)
2732 except error.WdirUnsupported:
2733 except error.WdirUnsupported:
2733 content = ctx[fn].data()
2734 content = ctx[fn].data()
2734 grepbody(fn, rev, content)
2735 grepbody(fn, rev, content)
2735
2736
2736 pfn = copy or fn
2737 pfn = copy or fn
2737 if pfn not in matches[parent]:
2738 if pfn not in matches[parent]:
2738 try:
2739 try:
2739 fnode = pctx.filenode(pfn)
2740 fnode = pctx.filenode(pfn)
2740 grepbody(pfn, parent, flog.read(fnode))
2741 grepbody(pfn, parent, flog.read(fnode))
2741 except error.LookupError:
2742 except error.LookupError:
2742 pass
2743 pass
2743
2744
2744 ui.pager('grep')
2745 ui.pager('grep')
2745 fm = ui.formatter('grep', opts)
2746 fm = ui.formatter('grep', opts)
2746 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2747 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2747 rev = ctx.rev()
2748 rev = ctx.rev()
2748 parent = ctx.p1().rev()
2749 parent = ctx.p1().rev()
2749 for fn in sorted(revfiles.get(rev, [])):
2750 for fn in sorted(revfiles.get(rev, [])):
2750 states = matches[rev][fn]
2751 states = matches[rev][fn]
2751 copy = copies.get(rev, {}).get(fn)
2752 copy = copies.get(rev, {}).get(fn)
2752 if fn in skip:
2753 if fn in skip:
2753 if copy:
2754 if copy:
2754 skip[copy] = True
2755 skip[copy] = True
2755 continue
2756 continue
2756 pstates = matches.get(parent, {}).get(copy or fn, [])
2757 pstates = matches.get(parent, {}).get(copy or fn, [])
2757 if pstates or states:
2758 if pstates or states:
2758 r = display(fm, fn, ctx, pstates, states)
2759 r = display(fm, fn, ctx, pstates, states)
2759 found = found or r
2760 found = found or r
2760 if r and not diff:
2761 if r and not diff and not all_files:
2761 skip[fn] = True
2762 skip[fn] = True
2762 if copy:
2763 if copy:
2763 skip[copy] = True
2764 skip[copy] = True
2764 del revfiles[rev]
2765 del revfiles[rev]
2765 # We will keep the matches dict for the duration of the window
2766 # We will keep the matches dict for the duration of the window
2766 # clear the matches dict once the window is over
2767 # clear the matches dict once the window is over
2767 if not revfiles:
2768 if not revfiles:
2768 matches.clear()
2769 matches.clear()
2769 fm.end()
2770 fm.end()
2770
2771
2771 return not found
2772 return not found
2772
2773
2773 @command('heads',
2774 @command('heads',
2774 [('r', 'rev', '',
2775 [('r', 'rev', '',
2775 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2776 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2776 ('t', 'topo', False, _('show topological heads only')),
2777 ('t', 'topo', False, _('show topological heads only')),
2777 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2778 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2778 ('c', 'closed', False, _('show normal and closed branch heads')),
2779 ('c', 'closed', False, _('show normal and closed branch heads')),
2779 ] + templateopts,
2780 ] + templateopts,
2780 _('[-ct] [-r STARTREV] [REV]...'),
2781 _('[-ct] [-r STARTREV] [REV]...'),
2781 intents={INTENT_READONLY})
2782 intents={INTENT_READONLY})
2782 def heads(ui, repo, *branchrevs, **opts):
2783 def heads(ui, repo, *branchrevs, **opts):
2783 """show branch heads
2784 """show branch heads
2784
2785
2785 With no arguments, show all open branch heads in the repository.
2786 With no arguments, show all open branch heads in the repository.
2786 Branch heads are changesets that have no descendants on the
2787 Branch heads are changesets that have no descendants on the
2787 same branch. They are where development generally takes place and
2788 same branch. They are where development generally takes place and
2788 are the usual targets for update and merge operations.
2789 are the usual targets for update and merge operations.
2789
2790
2790 If one or more REVs are given, only open branch heads on the
2791 If one or more REVs are given, only open branch heads on the
2791 branches associated with the specified changesets are shown. This
2792 branches associated with the specified changesets are shown. This
2792 means that you can use :hg:`heads .` to see the heads on the
2793 means that you can use :hg:`heads .` to see the heads on the
2793 currently checked-out branch.
2794 currently checked-out branch.
2794
2795
2795 If -c/--closed is specified, also show branch heads marked closed
2796 If -c/--closed is specified, also show branch heads marked closed
2796 (see :hg:`commit --close-branch`).
2797 (see :hg:`commit --close-branch`).
2797
2798
2798 If STARTREV is specified, only those heads that are descendants of
2799 If STARTREV is specified, only those heads that are descendants of
2799 STARTREV will be displayed.
2800 STARTREV will be displayed.
2800
2801
2801 If -t/--topo is specified, named branch mechanics will be ignored and only
2802 If -t/--topo is specified, named branch mechanics will be ignored and only
2802 topological heads (changesets with no children) will be shown.
2803 topological heads (changesets with no children) will be shown.
2803
2804
2804 Returns 0 if matching heads are found, 1 if not.
2805 Returns 0 if matching heads are found, 1 if not.
2805 """
2806 """
2806
2807
2807 opts = pycompat.byteskwargs(opts)
2808 opts = pycompat.byteskwargs(opts)
2808 start = None
2809 start = None
2809 rev = opts.get('rev')
2810 rev = opts.get('rev')
2810 if rev:
2811 if rev:
2811 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2812 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2812 start = scmutil.revsingle(repo, rev, None).node()
2813 start = scmutil.revsingle(repo, rev, None).node()
2813
2814
2814 if opts.get('topo'):
2815 if opts.get('topo'):
2815 heads = [repo[h] for h in repo.heads(start)]
2816 heads = [repo[h] for h in repo.heads(start)]
2816 else:
2817 else:
2817 heads = []
2818 heads = []
2818 for branch in repo.branchmap():
2819 for branch in repo.branchmap():
2819 heads += repo.branchheads(branch, start, opts.get('closed'))
2820 heads += repo.branchheads(branch, start, opts.get('closed'))
2820 heads = [repo[h] for h in heads]
2821 heads = [repo[h] for h in heads]
2821
2822
2822 if branchrevs:
2823 if branchrevs:
2823 branches = set(repo[r].branch()
2824 branches = set(repo[r].branch()
2824 for r in scmutil.revrange(repo, branchrevs))
2825 for r in scmutil.revrange(repo, branchrevs))
2825 heads = [h for h in heads if h.branch() in branches]
2826 heads = [h for h in heads if h.branch() in branches]
2826
2827
2827 if opts.get('active') and branchrevs:
2828 if opts.get('active') and branchrevs:
2828 dagheads = repo.heads(start)
2829 dagheads = repo.heads(start)
2829 heads = [h for h in heads if h.node() in dagheads]
2830 heads = [h for h in heads if h.node() in dagheads]
2830
2831
2831 if branchrevs:
2832 if branchrevs:
2832 haveheads = set(h.branch() for h in heads)
2833 haveheads = set(h.branch() for h in heads)
2833 if branches - haveheads:
2834 if branches - haveheads:
2834 headless = ', '.join(b for b in branches - haveheads)
2835 headless = ', '.join(b for b in branches - haveheads)
2835 msg = _('no open branch heads found on branches %s')
2836 msg = _('no open branch heads found on branches %s')
2836 if opts.get('rev'):
2837 if opts.get('rev'):
2837 msg += _(' (started at %s)') % opts['rev']
2838 msg += _(' (started at %s)') % opts['rev']
2838 ui.warn((msg + '\n') % headless)
2839 ui.warn((msg + '\n') % headless)
2839
2840
2840 if not heads:
2841 if not heads:
2841 return 1
2842 return 1
2842
2843
2843 ui.pager('heads')
2844 ui.pager('heads')
2844 heads = sorted(heads, key=lambda x: -x.rev())
2845 heads = sorted(heads, key=lambda x: -x.rev())
2845 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2846 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2846 for ctx in heads:
2847 for ctx in heads:
2847 displayer.show(ctx)
2848 displayer.show(ctx)
2848 displayer.close()
2849 displayer.close()
2849
2850
2850 @command('help',
2851 @command('help',
2851 [('e', 'extension', None, _('show only help for extensions')),
2852 [('e', 'extension', None, _('show only help for extensions')),
2852 ('c', 'command', None, _('show only help for commands')),
2853 ('c', 'command', None, _('show only help for commands')),
2853 ('k', 'keyword', None, _('show topics matching keyword')),
2854 ('k', 'keyword', None, _('show topics matching keyword')),
2854 ('s', 'system', [], _('show help for specific platform(s)')),
2855 ('s', 'system', [], _('show help for specific platform(s)')),
2855 ],
2856 ],
2856 _('[-ecks] [TOPIC]'),
2857 _('[-ecks] [TOPIC]'),
2857 norepo=True,
2858 norepo=True,
2858 intents={INTENT_READONLY})
2859 intents={INTENT_READONLY})
2859 def help_(ui, name=None, **opts):
2860 def help_(ui, name=None, **opts):
2860 """show help for a given topic or a help overview
2861 """show help for a given topic or a help overview
2861
2862
2862 With no arguments, print a list of commands with short help messages.
2863 With no arguments, print a list of commands with short help messages.
2863
2864
2864 Given a topic, extension, or command name, print help for that
2865 Given a topic, extension, or command name, print help for that
2865 topic.
2866 topic.
2866
2867
2867 Returns 0 if successful.
2868 Returns 0 if successful.
2868 """
2869 """
2869
2870
2870 keep = opts.get(r'system') or []
2871 keep = opts.get(r'system') or []
2871 if len(keep) == 0:
2872 if len(keep) == 0:
2872 if pycompat.sysplatform.startswith('win'):
2873 if pycompat.sysplatform.startswith('win'):
2873 keep.append('windows')
2874 keep.append('windows')
2874 elif pycompat.sysplatform == 'OpenVMS':
2875 elif pycompat.sysplatform == 'OpenVMS':
2875 keep.append('vms')
2876 keep.append('vms')
2876 elif pycompat.sysplatform == 'plan9':
2877 elif pycompat.sysplatform == 'plan9':
2877 keep.append('plan9')
2878 keep.append('plan9')
2878 else:
2879 else:
2879 keep.append('unix')
2880 keep.append('unix')
2880 keep.append(pycompat.sysplatform.lower())
2881 keep.append(pycompat.sysplatform.lower())
2881 if ui.verbose:
2882 if ui.verbose:
2882 keep.append('verbose')
2883 keep.append('verbose')
2883
2884
2884 commands = sys.modules[__name__]
2885 commands = sys.modules[__name__]
2885 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2886 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2886 ui.pager('help')
2887 ui.pager('help')
2887 ui.write(formatted)
2888 ui.write(formatted)
2888
2889
2889
2890
2890 @command('identify|id',
2891 @command('identify|id',
2891 [('r', 'rev', '',
2892 [('r', 'rev', '',
2892 _('identify the specified revision'), _('REV')),
2893 _('identify the specified revision'), _('REV')),
2893 ('n', 'num', None, _('show local revision number')),
2894 ('n', 'num', None, _('show local revision number')),
2894 ('i', 'id', None, _('show global revision id')),
2895 ('i', 'id', None, _('show global revision id')),
2895 ('b', 'branch', None, _('show branch')),
2896 ('b', 'branch', None, _('show branch')),
2896 ('t', 'tags', None, _('show tags')),
2897 ('t', 'tags', None, _('show tags')),
2897 ('B', 'bookmarks', None, _('show bookmarks')),
2898 ('B', 'bookmarks', None, _('show bookmarks')),
2898 ] + remoteopts + formatteropts,
2899 ] + remoteopts + formatteropts,
2899 _('[-nibtB] [-r REV] [SOURCE]'),
2900 _('[-nibtB] [-r REV] [SOURCE]'),
2900 optionalrepo=True,
2901 optionalrepo=True,
2901 intents={INTENT_READONLY})
2902 intents={INTENT_READONLY})
2902 def identify(ui, repo, source=None, rev=None,
2903 def identify(ui, repo, source=None, rev=None,
2903 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2904 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2904 """identify the working directory or specified revision
2905 """identify the working directory or specified revision
2905
2906
2906 Print a summary identifying the repository state at REV using one or
2907 Print a summary identifying the repository state at REV using one or
2907 two parent hash identifiers, followed by a "+" if the working
2908 two parent hash identifiers, followed by a "+" if the working
2908 directory has uncommitted changes, the branch name (if not default),
2909 directory has uncommitted changes, the branch name (if not default),
2909 a list of tags, and a list of bookmarks.
2910 a list of tags, and a list of bookmarks.
2910
2911
2911 When REV is not given, print a summary of the current state of the
2912 When REV is not given, print a summary of the current state of the
2912 repository including the working directory. Specify -r. to get information
2913 repository including the working directory. Specify -r. to get information
2913 of the working directory parent without scanning uncommitted changes.
2914 of the working directory parent without scanning uncommitted changes.
2914
2915
2915 Specifying a path to a repository root or Mercurial bundle will
2916 Specifying a path to a repository root or Mercurial bundle will
2916 cause lookup to operate on that repository/bundle.
2917 cause lookup to operate on that repository/bundle.
2917
2918
2918 .. container:: verbose
2919 .. container:: verbose
2919
2920
2920 Examples:
2921 Examples:
2921
2922
2922 - generate a build identifier for the working directory::
2923 - generate a build identifier for the working directory::
2923
2924
2924 hg id --id > build-id.dat
2925 hg id --id > build-id.dat
2925
2926
2926 - find the revision corresponding to a tag::
2927 - find the revision corresponding to a tag::
2927
2928
2928 hg id -n -r 1.3
2929 hg id -n -r 1.3
2929
2930
2930 - check the most recent revision of a remote repository::
2931 - check the most recent revision of a remote repository::
2931
2932
2932 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2933 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2933
2934
2934 See :hg:`log` for generating more information about specific revisions,
2935 See :hg:`log` for generating more information about specific revisions,
2935 including full hash identifiers.
2936 including full hash identifiers.
2936
2937
2937 Returns 0 if successful.
2938 Returns 0 if successful.
2938 """
2939 """
2939
2940
2940 opts = pycompat.byteskwargs(opts)
2941 opts = pycompat.byteskwargs(opts)
2941 if not repo and not source:
2942 if not repo and not source:
2942 raise error.Abort(_("there is no Mercurial repository here "
2943 raise error.Abort(_("there is no Mercurial repository here "
2943 "(.hg not found)"))
2944 "(.hg not found)"))
2944
2945
2945 if ui.debugflag:
2946 if ui.debugflag:
2946 hexfunc = hex
2947 hexfunc = hex
2947 else:
2948 else:
2948 hexfunc = short
2949 hexfunc = short
2949 default = not (num or id or branch or tags or bookmarks)
2950 default = not (num or id or branch or tags or bookmarks)
2950 output = []
2951 output = []
2951 revs = []
2952 revs = []
2952
2953
2953 if source:
2954 if source:
2954 source, branches = hg.parseurl(ui.expandpath(source))
2955 source, branches = hg.parseurl(ui.expandpath(source))
2955 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2956 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2956 repo = peer.local()
2957 repo = peer.local()
2957 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2958 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2958
2959
2959 fm = ui.formatter('identify', opts)
2960 fm = ui.formatter('identify', opts)
2960 fm.startitem()
2961 fm.startitem()
2961
2962
2962 if not repo:
2963 if not repo:
2963 if num or branch or tags:
2964 if num or branch or tags:
2964 raise error.Abort(
2965 raise error.Abort(
2965 _("can't query remote revision number, branch, or tags"))
2966 _("can't query remote revision number, branch, or tags"))
2966 if not rev and revs:
2967 if not rev and revs:
2967 rev = revs[0]
2968 rev = revs[0]
2968 if not rev:
2969 if not rev:
2969 rev = "tip"
2970 rev = "tip"
2970
2971
2971 remoterev = peer.lookup(rev)
2972 remoterev = peer.lookup(rev)
2972 hexrev = hexfunc(remoterev)
2973 hexrev = hexfunc(remoterev)
2973 if default or id:
2974 if default or id:
2974 output = [hexrev]
2975 output = [hexrev]
2975 fm.data(id=hexrev)
2976 fm.data(id=hexrev)
2976
2977
2977 def getbms():
2978 def getbms():
2978 bms = []
2979 bms = []
2979
2980
2980 if 'bookmarks' in peer.listkeys('namespaces'):
2981 if 'bookmarks' in peer.listkeys('namespaces'):
2981 hexremoterev = hex(remoterev)
2982 hexremoterev = hex(remoterev)
2982 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2983 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2983 if bmr == hexremoterev]
2984 if bmr == hexremoterev]
2984
2985
2985 return sorted(bms)
2986 return sorted(bms)
2986
2987
2987 bms = getbms()
2988 bms = getbms()
2988 if bookmarks:
2989 if bookmarks:
2989 output.extend(bms)
2990 output.extend(bms)
2990 elif default and not ui.quiet:
2991 elif default and not ui.quiet:
2991 # multiple bookmarks for a single parent separated by '/'
2992 # multiple bookmarks for a single parent separated by '/'
2992 bm = '/'.join(bms)
2993 bm = '/'.join(bms)
2993 if bm:
2994 if bm:
2994 output.append(bm)
2995 output.append(bm)
2995
2996
2996 fm.data(node=hex(remoterev))
2997 fm.data(node=hex(remoterev))
2997 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2998 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2998 else:
2999 else:
2999 if rev:
3000 if rev:
3000 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3001 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3001 ctx = scmutil.revsingle(repo, rev, None)
3002 ctx = scmutil.revsingle(repo, rev, None)
3002
3003
3003 if ctx.rev() is None:
3004 if ctx.rev() is None:
3004 ctx = repo[None]
3005 ctx = repo[None]
3005 parents = ctx.parents()
3006 parents = ctx.parents()
3006 taglist = []
3007 taglist = []
3007 for p in parents:
3008 for p in parents:
3008 taglist.extend(p.tags())
3009 taglist.extend(p.tags())
3009
3010
3010 dirty = ""
3011 dirty = ""
3011 if ctx.dirty(missing=True, merge=False, branch=False):
3012 if ctx.dirty(missing=True, merge=False, branch=False):
3012 dirty = '+'
3013 dirty = '+'
3013 fm.data(dirty=dirty)
3014 fm.data(dirty=dirty)
3014
3015
3015 hexoutput = [hexfunc(p.node()) for p in parents]
3016 hexoutput = [hexfunc(p.node()) for p in parents]
3016 if default or id:
3017 if default or id:
3017 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3018 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3018 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3019 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3019
3020
3020 if num:
3021 if num:
3021 numoutput = ["%d" % p.rev() for p in parents]
3022 numoutput = ["%d" % p.rev() for p in parents]
3022 output.append("%s%s" % ('+'.join(numoutput), dirty))
3023 output.append("%s%s" % ('+'.join(numoutput), dirty))
3023
3024
3024 fn = fm.nested('parents', tmpl='{rev}:{node|formatnode}', sep=' ')
3025 fn = fm.nested('parents', tmpl='{rev}:{node|formatnode}', sep=' ')
3025 for p in parents:
3026 for p in parents:
3026 fn.startitem()
3027 fn.startitem()
3027 fn.data(rev=p.rev())
3028 fn.data(rev=p.rev())
3028 fn.data(node=p.hex())
3029 fn.data(node=p.hex())
3029 fn.context(ctx=p)
3030 fn.context(ctx=p)
3030 fn.end()
3031 fn.end()
3031 else:
3032 else:
3032 hexoutput = hexfunc(ctx.node())
3033 hexoutput = hexfunc(ctx.node())
3033 if default or id:
3034 if default or id:
3034 output = [hexoutput]
3035 output = [hexoutput]
3035 fm.data(id=hexoutput)
3036 fm.data(id=hexoutput)
3036
3037
3037 if num:
3038 if num:
3038 output.append(pycompat.bytestr(ctx.rev()))
3039 output.append(pycompat.bytestr(ctx.rev()))
3039 taglist = ctx.tags()
3040 taglist = ctx.tags()
3040
3041
3041 if default and not ui.quiet:
3042 if default and not ui.quiet:
3042 b = ctx.branch()
3043 b = ctx.branch()
3043 if b != 'default':
3044 if b != 'default':
3044 output.append("(%s)" % b)
3045 output.append("(%s)" % b)
3045
3046
3046 # multiple tags for a single parent separated by '/'
3047 # multiple tags for a single parent separated by '/'
3047 t = '/'.join(taglist)
3048 t = '/'.join(taglist)
3048 if t:
3049 if t:
3049 output.append(t)
3050 output.append(t)
3050
3051
3051 # multiple bookmarks for a single parent separated by '/'
3052 # multiple bookmarks for a single parent separated by '/'
3052 bm = '/'.join(ctx.bookmarks())
3053 bm = '/'.join(ctx.bookmarks())
3053 if bm:
3054 if bm:
3054 output.append(bm)
3055 output.append(bm)
3055 else:
3056 else:
3056 if branch:
3057 if branch:
3057 output.append(ctx.branch())
3058 output.append(ctx.branch())
3058
3059
3059 if tags:
3060 if tags:
3060 output.extend(taglist)
3061 output.extend(taglist)
3061
3062
3062 if bookmarks:
3063 if bookmarks:
3063 output.extend(ctx.bookmarks())
3064 output.extend(ctx.bookmarks())
3064
3065
3065 fm.data(node=ctx.hex())
3066 fm.data(node=ctx.hex())
3066 fm.data(branch=ctx.branch())
3067 fm.data(branch=ctx.branch())
3067 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3068 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3068 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3069 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3069 fm.context(ctx=ctx)
3070 fm.context(ctx=ctx)
3070
3071
3071 fm.plain("%s\n" % ' '.join(output))
3072 fm.plain("%s\n" % ' '.join(output))
3072 fm.end()
3073 fm.end()
3073
3074
3074 @command('import|patch',
3075 @command('import|patch',
3075 [('p', 'strip', 1,
3076 [('p', 'strip', 1,
3076 _('directory strip option for patch. This has the same '
3077 _('directory strip option for patch. This has the same '
3077 'meaning as the corresponding patch option'), _('NUM')),
3078 'meaning as the corresponding patch option'), _('NUM')),
3078 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3079 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3079 ('e', 'edit', False, _('invoke editor on commit messages')),
3080 ('e', 'edit', False, _('invoke editor on commit messages')),
3080 ('f', 'force', None,
3081 ('f', 'force', None,
3081 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3082 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3082 ('', 'no-commit', None,
3083 ('', 'no-commit', None,
3083 _("don't commit, just update the working directory")),
3084 _("don't commit, just update the working directory")),
3084 ('', 'bypass', None,
3085 ('', 'bypass', None,
3085 _("apply patch without touching the working directory")),
3086 _("apply patch without touching the working directory")),
3086 ('', 'partial', None,
3087 ('', 'partial', None,
3087 _('commit even if some hunks fail')),
3088 _('commit even if some hunks fail')),
3088 ('', 'exact', None,
3089 ('', 'exact', None,
3089 _('abort if patch would apply lossily')),
3090 _('abort if patch would apply lossily')),
3090 ('', 'prefix', '',
3091 ('', 'prefix', '',
3091 _('apply patch to subdirectory'), _('DIR')),
3092 _('apply patch to subdirectory'), _('DIR')),
3092 ('', 'import-branch', None,
3093 ('', 'import-branch', None,
3093 _('use any branch information in patch (implied by --exact)'))] +
3094 _('use any branch information in patch (implied by --exact)'))] +
3094 commitopts + commitopts2 + similarityopts,
3095 commitopts + commitopts2 + similarityopts,
3095 _('[OPTION]... PATCH...'))
3096 _('[OPTION]... PATCH...'))
3096 def import_(ui, repo, patch1=None, *patches, **opts):
3097 def import_(ui, repo, patch1=None, *patches, **opts):
3097 """import an ordered set of patches
3098 """import an ordered set of patches
3098
3099
3099 Import a list of patches and commit them individually (unless
3100 Import a list of patches and commit them individually (unless
3100 --no-commit is specified).
3101 --no-commit is specified).
3101
3102
3102 To read a patch from standard input (stdin), use "-" as the patch
3103 To read a patch from standard input (stdin), use "-" as the patch
3103 name. If a URL is specified, the patch will be downloaded from
3104 name. If a URL is specified, the patch will be downloaded from
3104 there.
3105 there.
3105
3106
3106 Import first applies changes to the working directory (unless
3107 Import first applies changes to the working directory (unless
3107 --bypass is specified), import will abort if there are outstanding
3108 --bypass is specified), import will abort if there are outstanding
3108 changes.
3109 changes.
3109
3110
3110 Use --bypass to apply and commit patches directly to the
3111 Use --bypass to apply and commit patches directly to the
3111 repository, without affecting the working directory. Without
3112 repository, without affecting the working directory. Without
3112 --exact, patches will be applied on top of the working directory
3113 --exact, patches will be applied on top of the working directory
3113 parent revision.
3114 parent revision.
3114
3115
3115 You can import a patch straight from a mail message. Even patches
3116 You can import a patch straight from a mail message. Even patches
3116 as attachments work (to use the body part, it must have type
3117 as attachments work (to use the body part, it must have type
3117 text/plain or text/x-patch). From and Subject headers of email
3118 text/plain or text/x-patch). From and Subject headers of email
3118 message are used as default committer and commit message. All
3119 message are used as default committer and commit message. All
3119 text/plain body parts before first diff are added to the commit
3120 text/plain body parts before first diff are added to the commit
3120 message.
3121 message.
3121
3122
3122 If the imported patch was generated by :hg:`export`, user and
3123 If the imported patch was generated by :hg:`export`, user and
3123 description from patch override values from message headers and
3124 description from patch override values from message headers and
3124 body. Values given on command line with -m/--message and -u/--user
3125 body. Values given on command line with -m/--message and -u/--user
3125 override these.
3126 override these.
3126
3127
3127 If --exact is specified, import will set the working directory to
3128 If --exact is specified, import will set the working directory to
3128 the parent of each patch before applying it, and will abort if the
3129 the parent of each patch before applying it, and will abort if the
3129 resulting changeset has a different ID than the one recorded in
3130 resulting changeset has a different ID than the one recorded in
3130 the patch. This will guard against various ways that portable
3131 the patch. This will guard against various ways that portable
3131 patch formats and mail systems might fail to transfer Mercurial
3132 patch formats and mail systems might fail to transfer Mercurial
3132 data or metadata. See :hg:`bundle` for lossless transmission.
3133 data or metadata. See :hg:`bundle` for lossless transmission.
3133
3134
3134 Use --partial to ensure a changeset will be created from the patch
3135 Use --partial to ensure a changeset will be created from the patch
3135 even if some hunks fail to apply. Hunks that fail to apply will be
3136 even if some hunks fail to apply. Hunks that fail to apply will be
3136 written to a <target-file>.rej file. Conflicts can then be resolved
3137 written to a <target-file>.rej file. Conflicts can then be resolved
3137 by hand before :hg:`commit --amend` is run to update the created
3138 by hand before :hg:`commit --amend` is run to update the created
3138 changeset. This flag exists to let people import patches that
3139 changeset. This flag exists to let people import patches that
3139 partially apply without losing the associated metadata (author,
3140 partially apply without losing the associated metadata (author,
3140 date, description, ...).
3141 date, description, ...).
3141
3142
3142 .. note::
3143 .. note::
3143
3144
3144 When no hunks apply cleanly, :hg:`import --partial` will create
3145 When no hunks apply cleanly, :hg:`import --partial` will create
3145 an empty changeset, importing only the patch metadata.
3146 an empty changeset, importing only the patch metadata.
3146
3147
3147 With -s/--similarity, hg will attempt to discover renames and
3148 With -s/--similarity, hg will attempt to discover renames and
3148 copies in the patch in the same way as :hg:`addremove`.
3149 copies in the patch in the same way as :hg:`addremove`.
3149
3150
3150 It is possible to use external patch programs to perform the patch
3151 It is possible to use external patch programs to perform the patch
3151 by setting the ``ui.patch`` configuration option. For the default
3152 by setting the ``ui.patch`` configuration option. For the default
3152 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3153 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3153 See :hg:`help config` for more information about configuration
3154 See :hg:`help config` for more information about configuration
3154 files and how to use these options.
3155 files and how to use these options.
3155
3156
3156 See :hg:`help dates` for a list of formats valid for -d/--date.
3157 See :hg:`help dates` for a list of formats valid for -d/--date.
3157
3158
3158 .. container:: verbose
3159 .. container:: verbose
3159
3160
3160 Examples:
3161 Examples:
3161
3162
3162 - import a traditional patch from a website and detect renames::
3163 - import a traditional patch from a website and detect renames::
3163
3164
3164 hg import -s 80 http://example.com/bugfix.patch
3165 hg import -s 80 http://example.com/bugfix.patch
3165
3166
3166 - import a changeset from an hgweb server::
3167 - import a changeset from an hgweb server::
3167
3168
3168 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3169 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3169
3170
3170 - import all the patches in an Unix-style mbox::
3171 - import all the patches in an Unix-style mbox::
3171
3172
3172 hg import incoming-patches.mbox
3173 hg import incoming-patches.mbox
3173
3174
3174 - import patches from stdin::
3175 - import patches from stdin::
3175
3176
3176 hg import -
3177 hg import -
3177
3178
3178 - attempt to exactly restore an exported changeset (not always
3179 - attempt to exactly restore an exported changeset (not always
3179 possible)::
3180 possible)::
3180
3181
3181 hg import --exact proposed-fix.patch
3182 hg import --exact proposed-fix.patch
3182
3183
3183 - use an external tool to apply a patch which is too fuzzy for
3184 - use an external tool to apply a patch which is too fuzzy for
3184 the default internal tool.
3185 the default internal tool.
3185
3186
3186 hg import --config ui.patch="patch --merge" fuzzy.patch
3187 hg import --config ui.patch="patch --merge" fuzzy.patch
3187
3188
3188 - change the default fuzzing from 2 to a less strict 7
3189 - change the default fuzzing from 2 to a less strict 7
3189
3190
3190 hg import --config ui.fuzz=7 fuzz.patch
3191 hg import --config ui.fuzz=7 fuzz.patch
3191
3192
3192 Returns 0 on success, 1 on partial success (see --partial).
3193 Returns 0 on success, 1 on partial success (see --partial).
3193 """
3194 """
3194
3195
3195 opts = pycompat.byteskwargs(opts)
3196 opts = pycompat.byteskwargs(opts)
3196 if not patch1:
3197 if not patch1:
3197 raise error.Abort(_('need at least one patch to import'))
3198 raise error.Abort(_('need at least one patch to import'))
3198
3199
3199 patches = (patch1,) + patches
3200 patches = (patch1,) + patches
3200
3201
3201 date = opts.get('date')
3202 date = opts.get('date')
3202 if date:
3203 if date:
3203 opts['date'] = dateutil.parsedate(date)
3204 opts['date'] = dateutil.parsedate(date)
3204
3205
3205 exact = opts.get('exact')
3206 exact = opts.get('exact')
3206 update = not opts.get('bypass')
3207 update = not opts.get('bypass')
3207 if not update and opts.get('no_commit'):
3208 if not update and opts.get('no_commit'):
3208 raise error.Abort(_('cannot use --no-commit with --bypass'))
3209 raise error.Abort(_('cannot use --no-commit with --bypass'))
3209 try:
3210 try:
3210 sim = float(opts.get('similarity') or 0)
3211 sim = float(opts.get('similarity') or 0)
3211 except ValueError:
3212 except ValueError:
3212 raise error.Abort(_('similarity must be a number'))
3213 raise error.Abort(_('similarity must be a number'))
3213 if sim < 0 or sim > 100:
3214 if sim < 0 or sim > 100:
3214 raise error.Abort(_('similarity must be between 0 and 100'))
3215 raise error.Abort(_('similarity must be between 0 and 100'))
3215 if sim and not update:
3216 if sim and not update:
3216 raise error.Abort(_('cannot use --similarity with --bypass'))
3217 raise error.Abort(_('cannot use --similarity with --bypass'))
3217 if exact:
3218 if exact:
3218 if opts.get('edit'):
3219 if opts.get('edit'):
3219 raise error.Abort(_('cannot use --exact with --edit'))
3220 raise error.Abort(_('cannot use --exact with --edit'))
3220 if opts.get('prefix'):
3221 if opts.get('prefix'):
3221 raise error.Abort(_('cannot use --exact with --prefix'))
3222 raise error.Abort(_('cannot use --exact with --prefix'))
3222
3223
3223 base = opts["base"]
3224 base = opts["base"]
3224 msgs = []
3225 msgs = []
3225 ret = 0
3226 ret = 0
3226
3227
3227 with repo.wlock():
3228 with repo.wlock():
3228 if update:
3229 if update:
3229 cmdutil.checkunfinished(repo)
3230 cmdutil.checkunfinished(repo)
3230 if (exact or not opts.get('force')):
3231 if (exact or not opts.get('force')):
3231 cmdutil.bailifchanged(repo)
3232 cmdutil.bailifchanged(repo)
3232
3233
3233 if not opts.get('no_commit'):
3234 if not opts.get('no_commit'):
3234 lock = repo.lock
3235 lock = repo.lock
3235 tr = lambda: repo.transaction('import')
3236 tr = lambda: repo.transaction('import')
3236 dsguard = util.nullcontextmanager
3237 dsguard = util.nullcontextmanager
3237 else:
3238 else:
3238 lock = util.nullcontextmanager
3239 lock = util.nullcontextmanager
3239 tr = util.nullcontextmanager
3240 tr = util.nullcontextmanager
3240 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3241 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3241 with lock(), tr(), dsguard():
3242 with lock(), tr(), dsguard():
3242 parents = repo[None].parents()
3243 parents = repo[None].parents()
3243 for patchurl in patches:
3244 for patchurl in patches:
3244 if patchurl == '-':
3245 if patchurl == '-':
3245 ui.status(_('applying patch from stdin\n'))
3246 ui.status(_('applying patch from stdin\n'))
3246 patchfile = ui.fin
3247 patchfile = ui.fin
3247 patchurl = 'stdin' # for error message
3248 patchurl = 'stdin' # for error message
3248 else:
3249 else:
3249 patchurl = os.path.join(base, patchurl)
3250 patchurl = os.path.join(base, patchurl)
3250 ui.status(_('applying %s\n') % patchurl)
3251 ui.status(_('applying %s\n') % patchurl)
3251 patchfile = hg.openpath(ui, patchurl)
3252 patchfile = hg.openpath(ui, patchurl)
3252
3253
3253 haspatch = False
3254 haspatch = False
3254 for hunk in patch.split(patchfile):
3255 for hunk in patch.split(patchfile):
3255 with patch.extract(ui, hunk) as patchdata:
3256 with patch.extract(ui, hunk) as patchdata:
3256 msg, node, rej = cmdutil.tryimportone(ui, repo,
3257 msg, node, rej = cmdutil.tryimportone(ui, repo,
3257 patchdata,
3258 patchdata,
3258 parents, opts,
3259 parents, opts,
3259 msgs, hg.clean)
3260 msgs, hg.clean)
3260 if msg:
3261 if msg:
3261 haspatch = True
3262 haspatch = True
3262 ui.note(msg + '\n')
3263 ui.note(msg + '\n')
3263 if update or exact:
3264 if update or exact:
3264 parents = repo[None].parents()
3265 parents = repo[None].parents()
3265 else:
3266 else:
3266 parents = [repo[node]]
3267 parents = [repo[node]]
3267 if rej:
3268 if rej:
3268 ui.write_err(_("patch applied partially\n"))
3269 ui.write_err(_("patch applied partially\n"))
3269 ui.write_err(_("(fix the .rej files and run "
3270 ui.write_err(_("(fix the .rej files and run "
3270 "`hg commit --amend`)\n"))
3271 "`hg commit --amend`)\n"))
3271 ret = 1
3272 ret = 1
3272 break
3273 break
3273
3274
3274 if not haspatch:
3275 if not haspatch:
3275 raise error.Abort(_('%s: no diffs found') % patchurl)
3276 raise error.Abort(_('%s: no diffs found') % patchurl)
3276
3277
3277 if msgs:
3278 if msgs:
3278 repo.savecommitmessage('\n* * *\n'.join(msgs))
3279 repo.savecommitmessage('\n* * *\n'.join(msgs))
3279 return ret
3280 return ret
3280
3281
3281 @command('incoming|in',
3282 @command('incoming|in',
3282 [('f', 'force', None,
3283 [('f', 'force', None,
3283 _('run even if remote repository is unrelated')),
3284 _('run even if remote repository is unrelated')),
3284 ('n', 'newest-first', None, _('show newest record first')),
3285 ('n', 'newest-first', None, _('show newest record first')),
3285 ('', 'bundle', '',
3286 ('', 'bundle', '',
3286 _('file to store the bundles into'), _('FILE')),
3287 _('file to store the bundles into'), _('FILE')),
3287 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3288 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3288 ('B', 'bookmarks', False, _("compare bookmarks")),
3289 ('B', 'bookmarks', False, _("compare bookmarks")),
3289 ('b', 'branch', [],
3290 ('b', 'branch', [],
3290 _('a specific branch you would like to pull'), _('BRANCH')),
3291 _('a specific branch you would like to pull'), _('BRANCH')),
3291 ] + logopts + remoteopts + subrepoopts,
3292 ] + logopts + remoteopts + subrepoopts,
3292 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3293 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3293 def incoming(ui, repo, source="default", **opts):
3294 def incoming(ui, repo, source="default", **opts):
3294 """show new changesets found in source
3295 """show new changesets found in source
3295
3296
3296 Show new changesets found in the specified path/URL or the default
3297 Show new changesets found in the specified path/URL or the default
3297 pull location. These are the changesets that would have been pulled
3298 pull location. These are the changesets that would have been pulled
3298 by :hg:`pull` at the time you issued this command.
3299 by :hg:`pull` at the time you issued this command.
3299
3300
3300 See pull for valid source format details.
3301 See pull for valid source format details.
3301
3302
3302 .. container:: verbose
3303 .. container:: verbose
3303
3304
3304 With -B/--bookmarks, the result of bookmark comparison between
3305 With -B/--bookmarks, the result of bookmark comparison between
3305 local and remote repositories is displayed. With -v/--verbose,
3306 local and remote repositories is displayed. With -v/--verbose,
3306 status is also displayed for each bookmark like below::
3307 status is also displayed for each bookmark like below::
3307
3308
3308 BM1 01234567890a added
3309 BM1 01234567890a added
3309 BM2 1234567890ab advanced
3310 BM2 1234567890ab advanced
3310 BM3 234567890abc diverged
3311 BM3 234567890abc diverged
3311 BM4 34567890abcd changed
3312 BM4 34567890abcd changed
3312
3313
3313 The action taken locally when pulling depends on the
3314 The action taken locally when pulling depends on the
3314 status of each bookmark:
3315 status of each bookmark:
3315
3316
3316 :``added``: pull will create it
3317 :``added``: pull will create it
3317 :``advanced``: pull will update it
3318 :``advanced``: pull will update it
3318 :``diverged``: pull will create a divergent bookmark
3319 :``diverged``: pull will create a divergent bookmark
3319 :``changed``: result depends on remote changesets
3320 :``changed``: result depends on remote changesets
3320
3321
3321 From the point of view of pulling behavior, bookmark
3322 From the point of view of pulling behavior, bookmark
3322 existing only in the remote repository are treated as ``added``,
3323 existing only in the remote repository are treated as ``added``,
3323 even if it is in fact locally deleted.
3324 even if it is in fact locally deleted.
3324
3325
3325 .. container:: verbose
3326 .. container:: verbose
3326
3327
3327 For remote repository, using --bundle avoids downloading the
3328 For remote repository, using --bundle avoids downloading the
3328 changesets twice if the incoming is followed by a pull.
3329 changesets twice if the incoming is followed by a pull.
3329
3330
3330 Examples:
3331 Examples:
3331
3332
3332 - show incoming changes with patches and full description::
3333 - show incoming changes with patches and full description::
3333
3334
3334 hg incoming -vp
3335 hg incoming -vp
3335
3336
3336 - show incoming changes excluding merges, store a bundle::
3337 - show incoming changes excluding merges, store a bundle::
3337
3338
3338 hg in -vpM --bundle incoming.hg
3339 hg in -vpM --bundle incoming.hg
3339 hg pull incoming.hg
3340 hg pull incoming.hg
3340
3341
3341 - briefly list changes inside a bundle::
3342 - briefly list changes inside a bundle::
3342
3343
3343 hg in changes.hg -T "{desc|firstline}\\n"
3344 hg in changes.hg -T "{desc|firstline}\\n"
3344
3345
3345 Returns 0 if there are incoming changes, 1 otherwise.
3346 Returns 0 if there are incoming changes, 1 otherwise.
3346 """
3347 """
3347 opts = pycompat.byteskwargs(opts)
3348 opts = pycompat.byteskwargs(opts)
3348 if opts.get('graph'):
3349 if opts.get('graph'):
3349 logcmdutil.checkunsupportedgraphflags([], opts)
3350 logcmdutil.checkunsupportedgraphflags([], opts)
3350 def display(other, chlist, displayer):
3351 def display(other, chlist, displayer):
3351 revdag = logcmdutil.graphrevs(other, chlist, opts)
3352 revdag = logcmdutil.graphrevs(other, chlist, opts)
3352 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3353 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3353 graphmod.asciiedges)
3354 graphmod.asciiedges)
3354
3355
3355 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3356 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3356 return 0
3357 return 0
3357
3358
3358 if opts.get('bundle') and opts.get('subrepos'):
3359 if opts.get('bundle') and opts.get('subrepos'):
3359 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3360 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3360
3361
3361 if opts.get('bookmarks'):
3362 if opts.get('bookmarks'):
3362 source, branches = hg.parseurl(ui.expandpath(source),
3363 source, branches = hg.parseurl(ui.expandpath(source),
3363 opts.get('branch'))
3364 opts.get('branch'))
3364 other = hg.peer(repo, opts, source)
3365 other = hg.peer(repo, opts, source)
3365 if 'bookmarks' not in other.listkeys('namespaces'):
3366 if 'bookmarks' not in other.listkeys('namespaces'):
3366 ui.warn(_("remote doesn't support bookmarks\n"))
3367 ui.warn(_("remote doesn't support bookmarks\n"))
3367 return 0
3368 return 0
3368 ui.pager('incoming')
3369 ui.pager('incoming')
3369 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3370 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3370 return bookmarks.incoming(ui, repo, other)
3371 return bookmarks.incoming(ui, repo, other)
3371
3372
3372 repo._subtoppath = ui.expandpath(source)
3373 repo._subtoppath = ui.expandpath(source)
3373 try:
3374 try:
3374 return hg.incoming(ui, repo, source, opts)
3375 return hg.incoming(ui, repo, source, opts)
3375 finally:
3376 finally:
3376 del repo._subtoppath
3377 del repo._subtoppath
3377
3378
3378
3379
3379 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3380 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3380 norepo=True)
3381 norepo=True)
3381 def init(ui, dest=".", **opts):
3382 def init(ui, dest=".", **opts):
3382 """create a new repository in the given directory
3383 """create a new repository in the given directory
3383
3384
3384 Initialize a new repository in the given directory. If the given
3385 Initialize a new repository in the given directory. If the given
3385 directory does not exist, it will be created.
3386 directory does not exist, it will be created.
3386
3387
3387 If no directory is given, the current directory is used.
3388 If no directory is given, the current directory is used.
3388
3389
3389 It is possible to specify an ``ssh://`` URL as the destination.
3390 It is possible to specify an ``ssh://`` URL as the destination.
3390 See :hg:`help urls` for more information.
3391 See :hg:`help urls` for more information.
3391
3392
3392 Returns 0 on success.
3393 Returns 0 on success.
3393 """
3394 """
3394 opts = pycompat.byteskwargs(opts)
3395 opts = pycompat.byteskwargs(opts)
3395 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3396 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3396
3397
3397 @command('locate',
3398 @command('locate',
3398 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3399 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3399 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3400 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3400 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3401 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3401 ] + walkopts,
3402 ] + walkopts,
3402 _('[OPTION]... [PATTERN]...'))
3403 _('[OPTION]... [PATTERN]...'))
3403 def locate(ui, repo, *pats, **opts):
3404 def locate(ui, repo, *pats, **opts):
3404 """locate files matching specific patterns (DEPRECATED)
3405 """locate files matching specific patterns (DEPRECATED)
3405
3406
3406 Print files under Mercurial control in the working directory whose
3407 Print files under Mercurial control in the working directory whose
3407 names match the given patterns.
3408 names match the given patterns.
3408
3409
3409 By default, this command searches all directories in the working
3410 By default, this command searches all directories in the working
3410 directory. To search just the current directory and its
3411 directory. To search just the current directory and its
3411 subdirectories, use "--include .".
3412 subdirectories, use "--include .".
3412
3413
3413 If no patterns are given to match, this command prints the names
3414 If no patterns are given to match, this command prints the names
3414 of all files under Mercurial control in the working directory.
3415 of all files under Mercurial control in the working directory.
3415
3416
3416 If you want to feed the output of this command into the "xargs"
3417 If you want to feed the output of this command into the "xargs"
3417 command, use the -0 option to both this command and "xargs". This
3418 command, use the -0 option to both this command and "xargs". This
3418 will avoid the problem of "xargs" treating single filenames that
3419 will avoid the problem of "xargs" treating single filenames that
3419 contain whitespace as multiple filenames.
3420 contain whitespace as multiple filenames.
3420
3421
3421 See :hg:`help files` for a more versatile command.
3422 See :hg:`help files` for a more versatile command.
3422
3423
3423 Returns 0 if a match is found, 1 otherwise.
3424 Returns 0 if a match is found, 1 otherwise.
3424 """
3425 """
3425 opts = pycompat.byteskwargs(opts)
3426 opts = pycompat.byteskwargs(opts)
3426 if opts.get('print0'):
3427 if opts.get('print0'):
3427 end = '\0'
3428 end = '\0'
3428 else:
3429 else:
3429 end = '\n'
3430 end = '\n'
3430 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3431 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3431
3432
3432 ret = 1
3433 ret = 1
3433 m = scmutil.match(ctx, pats, opts, default='relglob',
3434 m = scmutil.match(ctx, pats, opts, default='relglob',
3434 badfn=lambda x, y: False)
3435 badfn=lambda x, y: False)
3435
3436
3436 ui.pager('locate')
3437 ui.pager('locate')
3437 if ctx.rev() is None:
3438 if ctx.rev() is None:
3438 # When run on the working copy, "locate" includes removed files, so
3439 # When run on the working copy, "locate" includes removed files, so
3439 # we get the list of files from the dirstate.
3440 # we get the list of files from the dirstate.
3440 filesgen = sorted(repo.dirstate.matches(m))
3441 filesgen = sorted(repo.dirstate.matches(m))
3441 else:
3442 else:
3442 filesgen = ctx.matches(m)
3443 filesgen = ctx.matches(m)
3443 for abs in filesgen:
3444 for abs in filesgen:
3444 if opts.get('fullpath'):
3445 if opts.get('fullpath'):
3445 ui.write(repo.wjoin(abs), end)
3446 ui.write(repo.wjoin(abs), end)
3446 else:
3447 else:
3447 ui.write(((pats and m.rel(abs)) or abs), end)
3448 ui.write(((pats and m.rel(abs)) or abs), end)
3448 ret = 0
3449 ret = 0
3449
3450
3450 return ret
3451 return ret
3451
3452
3452 @command('^log|history',
3453 @command('^log|history',
3453 [('f', 'follow', None,
3454 [('f', 'follow', None,
3454 _('follow changeset history, or file history across copies and renames')),
3455 _('follow changeset history, or file history across copies and renames')),
3455 ('', 'follow-first', None,
3456 ('', 'follow-first', None,
3456 _('only follow the first parent of merge changesets (DEPRECATED)')),
3457 _('only follow the first parent of merge changesets (DEPRECATED)')),
3457 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3458 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3458 ('C', 'copies', None, _('show copied files')),
3459 ('C', 'copies', None, _('show copied files')),
3459 ('k', 'keyword', [],
3460 ('k', 'keyword', [],
3460 _('do case-insensitive search for a given text'), _('TEXT')),
3461 _('do case-insensitive search for a given text'), _('TEXT')),
3461 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3462 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3462 ('L', 'line-range', [],
3463 ('L', 'line-range', [],
3463 _('follow line range of specified file (EXPERIMENTAL)'),
3464 _('follow line range of specified file (EXPERIMENTAL)'),
3464 _('FILE,RANGE')),
3465 _('FILE,RANGE')),
3465 ('', 'removed', None, _('include revisions where files were removed')),
3466 ('', 'removed', None, _('include revisions where files were removed')),
3466 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3467 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3467 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3468 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3468 ('', 'only-branch', [],
3469 ('', 'only-branch', [],
3469 _('show only changesets within the given named branch (DEPRECATED)'),
3470 _('show only changesets within the given named branch (DEPRECATED)'),
3470 _('BRANCH')),
3471 _('BRANCH')),
3471 ('b', 'branch', [],
3472 ('b', 'branch', [],
3472 _('show changesets within the given named branch'), _('BRANCH')),
3473 _('show changesets within the given named branch'), _('BRANCH')),
3473 ('P', 'prune', [],
3474 ('P', 'prune', [],
3474 _('do not display revision or any of its ancestors'), _('REV')),
3475 _('do not display revision or any of its ancestors'), _('REV')),
3475 ] + logopts + walkopts,
3476 ] + logopts + walkopts,
3476 _('[OPTION]... [FILE]'),
3477 _('[OPTION]... [FILE]'),
3477 inferrepo=True,
3478 inferrepo=True,
3478 intents={INTENT_READONLY})
3479 intents={INTENT_READONLY})
3479 def log(ui, repo, *pats, **opts):
3480 def log(ui, repo, *pats, **opts):
3480 """show revision history of entire repository or files
3481 """show revision history of entire repository or files
3481
3482
3482 Print the revision history of the specified files or the entire
3483 Print the revision history of the specified files or the entire
3483 project.
3484 project.
3484
3485
3485 If no revision range is specified, the default is ``tip:0`` unless
3486 If no revision range is specified, the default is ``tip:0`` unless
3486 --follow is set, in which case the working directory parent is
3487 --follow is set, in which case the working directory parent is
3487 used as the starting revision.
3488 used as the starting revision.
3488
3489
3489 File history is shown without following rename or copy history of
3490 File history is shown without following rename or copy history of
3490 files. Use -f/--follow with a filename to follow history across
3491 files. Use -f/--follow with a filename to follow history across
3491 renames and copies. --follow without a filename will only show
3492 renames and copies. --follow without a filename will only show
3492 ancestors of the starting revision.
3493 ancestors of the starting revision.
3493
3494
3494 By default this command prints revision number and changeset id,
3495 By default this command prints revision number and changeset id,
3495 tags, non-trivial parents, user, date and time, and a summary for
3496 tags, non-trivial parents, user, date and time, and a summary for
3496 each commit. When the -v/--verbose switch is used, the list of
3497 each commit. When the -v/--verbose switch is used, the list of
3497 changed files and full commit message are shown.
3498 changed files and full commit message are shown.
3498
3499
3499 With --graph the revisions are shown as an ASCII art DAG with the most
3500 With --graph the revisions are shown as an ASCII art DAG with the most
3500 recent changeset at the top.
3501 recent changeset at the top.
3501 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3502 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3502 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3503 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3503 changeset from the lines below is a parent of the 'o' merge on the same
3504 changeset from the lines below is a parent of the 'o' merge on the same
3504 line.
3505 line.
3505 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3506 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3506 of a '|' indicates one or more revisions in a path are omitted.
3507 of a '|' indicates one or more revisions in a path are omitted.
3507
3508
3508 .. container:: verbose
3509 .. container:: verbose
3509
3510
3510 Use -L/--line-range FILE,M:N options to follow the history of lines
3511 Use -L/--line-range FILE,M:N options to follow the history of lines
3511 from M to N in FILE. With -p/--patch only diff hunks affecting
3512 from M to N in FILE. With -p/--patch only diff hunks affecting
3512 specified line range will be shown. This option requires --follow;
3513 specified line range will be shown. This option requires --follow;
3513 it can be specified multiple times. Currently, this option is not
3514 it can be specified multiple times. Currently, this option is not
3514 compatible with --graph. This option is experimental.
3515 compatible with --graph. This option is experimental.
3515
3516
3516 .. note::
3517 .. note::
3517
3518
3518 :hg:`log --patch` may generate unexpected diff output for merge
3519 :hg:`log --patch` may generate unexpected diff output for merge
3519 changesets, as it will only compare the merge changeset against
3520 changesets, as it will only compare the merge changeset against
3520 its first parent. Also, only files different from BOTH parents
3521 its first parent. Also, only files different from BOTH parents
3521 will appear in files:.
3522 will appear in files:.
3522
3523
3523 .. note::
3524 .. note::
3524
3525
3525 For performance reasons, :hg:`log FILE` may omit duplicate changes
3526 For performance reasons, :hg:`log FILE` may omit duplicate changes
3526 made on branches and will not show removals or mode changes. To
3527 made on branches and will not show removals or mode changes. To
3527 see all such changes, use the --removed switch.
3528 see all such changes, use the --removed switch.
3528
3529
3529 .. container:: verbose
3530 .. container:: verbose
3530
3531
3531 .. note::
3532 .. note::
3532
3533
3533 The history resulting from -L/--line-range options depends on diff
3534 The history resulting from -L/--line-range options depends on diff
3534 options; for instance if white-spaces are ignored, respective changes
3535 options; for instance if white-spaces are ignored, respective changes
3535 with only white-spaces in specified line range will not be listed.
3536 with only white-spaces in specified line range will not be listed.
3536
3537
3537 .. container:: verbose
3538 .. container:: verbose
3538
3539
3539 Some examples:
3540 Some examples:
3540
3541
3541 - changesets with full descriptions and file lists::
3542 - changesets with full descriptions and file lists::
3542
3543
3543 hg log -v
3544 hg log -v
3544
3545
3545 - changesets ancestral to the working directory::
3546 - changesets ancestral to the working directory::
3546
3547
3547 hg log -f
3548 hg log -f
3548
3549
3549 - last 10 commits on the current branch::
3550 - last 10 commits on the current branch::
3550
3551
3551 hg log -l 10 -b .
3552 hg log -l 10 -b .
3552
3553
3553 - changesets showing all modifications of a file, including removals::
3554 - changesets showing all modifications of a file, including removals::
3554
3555
3555 hg log --removed file.c
3556 hg log --removed file.c
3556
3557
3557 - all changesets that touch a directory, with diffs, excluding merges::
3558 - all changesets that touch a directory, with diffs, excluding merges::
3558
3559
3559 hg log -Mp lib/
3560 hg log -Mp lib/
3560
3561
3561 - all revision numbers that match a keyword::
3562 - all revision numbers that match a keyword::
3562
3563
3563 hg log -k bug --template "{rev}\\n"
3564 hg log -k bug --template "{rev}\\n"
3564
3565
3565 - the full hash identifier of the working directory parent::
3566 - the full hash identifier of the working directory parent::
3566
3567
3567 hg log -r . --template "{node}\\n"
3568 hg log -r . --template "{node}\\n"
3568
3569
3569 - list available log templates::
3570 - list available log templates::
3570
3571
3571 hg log -T list
3572 hg log -T list
3572
3573
3573 - check if a given changeset is included in a tagged release::
3574 - check if a given changeset is included in a tagged release::
3574
3575
3575 hg log -r "a21ccf and ancestor(1.9)"
3576 hg log -r "a21ccf and ancestor(1.9)"
3576
3577
3577 - find all changesets by some user in a date range::
3578 - find all changesets by some user in a date range::
3578
3579
3579 hg log -k alice -d "may 2008 to jul 2008"
3580 hg log -k alice -d "may 2008 to jul 2008"
3580
3581
3581 - summary of all changesets after the last tag::
3582 - summary of all changesets after the last tag::
3582
3583
3583 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3584 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3584
3585
3585 - changesets touching lines 13 to 23 for file.c::
3586 - changesets touching lines 13 to 23 for file.c::
3586
3587
3587 hg log -L file.c,13:23
3588 hg log -L file.c,13:23
3588
3589
3589 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3590 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3590 main.c with patch::
3591 main.c with patch::
3591
3592
3592 hg log -L file.c,13:23 -L main.c,2:6 -p
3593 hg log -L file.c,13:23 -L main.c,2:6 -p
3593
3594
3594 See :hg:`help dates` for a list of formats valid for -d/--date.
3595 See :hg:`help dates` for a list of formats valid for -d/--date.
3595
3596
3596 See :hg:`help revisions` for more about specifying and ordering
3597 See :hg:`help revisions` for more about specifying and ordering
3597 revisions.
3598 revisions.
3598
3599
3599 See :hg:`help templates` for more about pre-packaged styles and
3600 See :hg:`help templates` for more about pre-packaged styles and
3600 specifying custom templates. The default template used by the log
3601 specifying custom templates. The default template used by the log
3601 command can be customized via the ``ui.logtemplate`` configuration
3602 command can be customized via the ``ui.logtemplate`` configuration
3602 setting.
3603 setting.
3603
3604
3604 Returns 0 on success.
3605 Returns 0 on success.
3605
3606
3606 """
3607 """
3607 opts = pycompat.byteskwargs(opts)
3608 opts = pycompat.byteskwargs(opts)
3608 linerange = opts.get('line_range')
3609 linerange = opts.get('line_range')
3609
3610
3610 if linerange and not opts.get('follow'):
3611 if linerange and not opts.get('follow'):
3611 raise error.Abort(_('--line-range requires --follow'))
3612 raise error.Abort(_('--line-range requires --follow'))
3612
3613
3613 if linerange and pats:
3614 if linerange and pats:
3614 # TODO: take pats as patterns with no line-range filter
3615 # TODO: take pats as patterns with no line-range filter
3615 raise error.Abort(
3616 raise error.Abort(
3616 _('FILE arguments are not compatible with --line-range option')
3617 _('FILE arguments are not compatible with --line-range option')
3617 )
3618 )
3618
3619
3619 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3620 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3620 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3621 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3621 if linerange:
3622 if linerange:
3622 # TODO: should follow file history from logcmdutil._initialrevs(),
3623 # TODO: should follow file history from logcmdutil._initialrevs(),
3623 # then filter the result by logcmdutil._makerevset() and --limit
3624 # then filter the result by logcmdutil._makerevset() and --limit
3624 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3625 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3625
3626
3626 getrenamed = None
3627 getrenamed = None
3627 if opts.get('copies'):
3628 if opts.get('copies'):
3628 endrev = None
3629 endrev = None
3629 if revs:
3630 if revs:
3630 endrev = revs.max() + 1
3631 endrev = revs.max() + 1
3631 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3632 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3632
3633
3633 ui.pager('log')
3634 ui.pager('log')
3634 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3635 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3635 buffered=True)
3636 buffered=True)
3636 if opts.get('graph'):
3637 if opts.get('graph'):
3637 displayfn = logcmdutil.displaygraphrevs
3638 displayfn = logcmdutil.displaygraphrevs
3638 else:
3639 else:
3639 displayfn = logcmdutil.displayrevs
3640 displayfn = logcmdutil.displayrevs
3640 displayfn(ui, repo, revs, displayer, getrenamed)
3641 displayfn(ui, repo, revs, displayer, getrenamed)
3641
3642
3642 @command('manifest',
3643 @command('manifest',
3643 [('r', 'rev', '', _('revision to display'), _('REV')),
3644 [('r', 'rev', '', _('revision to display'), _('REV')),
3644 ('', 'all', False, _("list files from all revisions"))]
3645 ('', 'all', False, _("list files from all revisions"))]
3645 + formatteropts,
3646 + formatteropts,
3646 _('[-r REV]'),
3647 _('[-r REV]'),
3647 intents={INTENT_READONLY})
3648 intents={INTENT_READONLY})
3648 def manifest(ui, repo, node=None, rev=None, **opts):
3649 def manifest(ui, repo, node=None, rev=None, **opts):
3649 """output the current or given revision of the project manifest
3650 """output the current or given revision of the project manifest
3650
3651
3651 Print a list of version controlled files for the given revision.
3652 Print a list of version controlled files for the given revision.
3652 If no revision is given, the first parent of the working directory
3653 If no revision is given, the first parent of the working directory
3653 is used, or the null revision if no revision is checked out.
3654 is used, or the null revision if no revision is checked out.
3654
3655
3655 With -v, print file permissions, symlink and executable bits.
3656 With -v, print file permissions, symlink and executable bits.
3656 With --debug, print file revision hashes.
3657 With --debug, print file revision hashes.
3657
3658
3658 If option --all is specified, the list of all files from all revisions
3659 If option --all is specified, the list of all files from all revisions
3659 is printed. This includes deleted and renamed files.
3660 is printed. This includes deleted and renamed files.
3660
3661
3661 Returns 0 on success.
3662 Returns 0 on success.
3662 """
3663 """
3663 opts = pycompat.byteskwargs(opts)
3664 opts = pycompat.byteskwargs(opts)
3664 fm = ui.formatter('manifest', opts)
3665 fm = ui.formatter('manifest', opts)
3665
3666
3666 if opts.get('all'):
3667 if opts.get('all'):
3667 if rev or node:
3668 if rev or node:
3668 raise error.Abort(_("can't specify a revision with --all"))
3669 raise error.Abort(_("can't specify a revision with --all"))
3669
3670
3670 res = set()
3671 res = set()
3671 for rev in repo:
3672 for rev in repo:
3672 ctx = repo[rev]
3673 ctx = repo[rev]
3673 res |= set(ctx.files())
3674 res |= set(ctx.files())
3674
3675
3675 ui.pager('manifest')
3676 ui.pager('manifest')
3676 for f in sorted(res):
3677 for f in sorted(res):
3677 fm.startitem()
3678 fm.startitem()
3678 fm.write("path", '%s\n', f)
3679 fm.write("path", '%s\n', f)
3679 fm.end()
3680 fm.end()
3680 return
3681 return
3681
3682
3682 if rev and node:
3683 if rev and node:
3683 raise error.Abort(_("please specify just one revision"))
3684 raise error.Abort(_("please specify just one revision"))
3684
3685
3685 if not node:
3686 if not node:
3686 node = rev
3687 node = rev
3687
3688
3688 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3689 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3689 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3690 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3690 if node:
3691 if node:
3691 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3692 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3692 ctx = scmutil.revsingle(repo, node)
3693 ctx = scmutil.revsingle(repo, node)
3693 mf = ctx.manifest()
3694 mf = ctx.manifest()
3694 ui.pager('manifest')
3695 ui.pager('manifest')
3695 for f in ctx:
3696 for f in ctx:
3696 fm.startitem()
3697 fm.startitem()
3697 fm.context(ctx=ctx)
3698 fm.context(ctx=ctx)
3698 fl = ctx[f].flags()
3699 fl = ctx[f].flags()
3699 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3700 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3700 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3701 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3701 fm.write('path', '%s\n', f)
3702 fm.write('path', '%s\n', f)
3702 fm.end()
3703 fm.end()
3703
3704
3704 @command('^merge',
3705 @command('^merge',
3705 [('f', 'force', None,
3706 [('f', 'force', None,
3706 _('force a merge including outstanding changes (DEPRECATED)')),
3707 _('force a merge including outstanding changes (DEPRECATED)')),
3707 ('r', 'rev', '', _('revision to merge'), _('REV')),
3708 ('r', 'rev', '', _('revision to merge'), _('REV')),
3708 ('P', 'preview', None,
3709 ('P', 'preview', None,
3709 _('review revisions to merge (no merge is performed)')),
3710 _('review revisions to merge (no merge is performed)')),
3710 ('', 'abort', None, _('abort the ongoing merge')),
3711 ('', 'abort', None, _('abort the ongoing merge')),
3711 ] + mergetoolopts,
3712 ] + mergetoolopts,
3712 _('[-P] [[-r] REV]'))
3713 _('[-P] [[-r] REV]'))
3713 def merge(ui, repo, node=None, **opts):
3714 def merge(ui, repo, node=None, **opts):
3714 """merge another revision into working directory
3715 """merge another revision into working directory
3715
3716
3716 The current working directory is updated with all changes made in
3717 The current working directory is updated with all changes made in
3717 the requested revision since the last common predecessor revision.
3718 the requested revision since the last common predecessor revision.
3718
3719
3719 Files that changed between either parent are marked as changed for
3720 Files that changed between either parent are marked as changed for
3720 the next commit and a commit must be performed before any further
3721 the next commit and a commit must be performed before any further
3721 updates to the repository are allowed. The next commit will have
3722 updates to the repository are allowed. The next commit will have
3722 two parents.
3723 two parents.
3723
3724
3724 ``--tool`` can be used to specify the merge tool used for file
3725 ``--tool`` can be used to specify the merge tool used for file
3725 merges. It overrides the HGMERGE environment variable and your
3726 merges. It overrides the HGMERGE environment variable and your
3726 configuration files. See :hg:`help merge-tools` for options.
3727 configuration files. See :hg:`help merge-tools` for options.
3727
3728
3728 If no revision is specified, the working directory's parent is a
3729 If no revision is specified, the working directory's parent is a
3729 head revision, and the current branch contains exactly one other
3730 head revision, and the current branch contains exactly one other
3730 head, the other head is merged with by default. Otherwise, an
3731 head, the other head is merged with by default. Otherwise, an
3731 explicit revision with which to merge with must be provided.
3732 explicit revision with which to merge with must be provided.
3732
3733
3733 See :hg:`help resolve` for information on handling file conflicts.
3734 See :hg:`help resolve` for information on handling file conflicts.
3734
3735
3735 To undo an uncommitted merge, use :hg:`merge --abort` which
3736 To undo an uncommitted merge, use :hg:`merge --abort` which
3736 will check out a clean copy of the original merge parent, losing
3737 will check out a clean copy of the original merge parent, losing
3737 all changes.
3738 all changes.
3738
3739
3739 Returns 0 on success, 1 if there are unresolved files.
3740 Returns 0 on success, 1 if there are unresolved files.
3740 """
3741 """
3741
3742
3742 opts = pycompat.byteskwargs(opts)
3743 opts = pycompat.byteskwargs(opts)
3743 abort = opts.get('abort')
3744 abort = opts.get('abort')
3744 if abort and repo.dirstate.p2() == nullid:
3745 if abort and repo.dirstate.p2() == nullid:
3745 cmdutil.wrongtooltocontinue(repo, _('merge'))
3746 cmdutil.wrongtooltocontinue(repo, _('merge'))
3746 if abort:
3747 if abort:
3747 if node:
3748 if node:
3748 raise error.Abort(_("cannot specify a node with --abort"))
3749 raise error.Abort(_("cannot specify a node with --abort"))
3749 if opts.get('rev'):
3750 if opts.get('rev'):
3750 raise error.Abort(_("cannot specify both --rev and --abort"))
3751 raise error.Abort(_("cannot specify both --rev and --abort"))
3751 if opts.get('preview'):
3752 if opts.get('preview'):
3752 raise error.Abort(_("cannot specify --preview with --abort"))
3753 raise error.Abort(_("cannot specify --preview with --abort"))
3753 if opts.get('rev') and node:
3754 if opts.get('rev') and node:
3754 raise error.Abort(_("please specify just one revision"))
3755 raise error.Abort(_("please specify just one revision"))
3755 if not node:
3756 if not node:
3756 node = opts.get('rev')
3757 node = opts.get('rev')
3757
3758
3758 if node:
3759 if node:
3759 node = scmutil.revsingle(repo, node).node()
3760 node = scmutil.revsingle(repo, node).node()
3760
3761
3761 if not node and not abort:
3762 if not node and not abort:
3762 node = repo[destutil.destmerge(repo)].node()
3763 node = repo[destutil.destmerge(repo)].node()
3763
3764
3764 if opts.get('preview'):
3765 if opts.get('preview'):
3765 # find nodes that are ancestors of p2 but not of p1
3766 # find nodes that are ancestors of p2 but not of p1
3766 p1 = repo.lookup('.')
3767 p1 = repo.lookup('.')
3767 p2 = node
3768 p2 = node
3768 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3769 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3769
3770
3770 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3771 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3771 for node in nodes:
3772 for node in nodes:
3772 displayer.show(repo[node])
3773 displayer.show(repo[node])
3773 displayer.close()
3774 displayer.close()
3774 return 0
3775 return 0
3775
3776
3776 # ui.forcemerge is an internal variable, do not document
3777 # ui.forcemerge is an internal variable, do not document
3777 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
3778 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
3778 with ui.configoverride(overrides, 'merge'):
3779 with ui.configoverride(overrides, 'merge'):
3779 force = opts.get('force')
3780 force = opts.get('force')
3780 labels = ['working copy', 'merge rev']
3781 labels = ['working copy', 'merge rev']
3781 return hg.merge(repo, node, force=force, mergeforce=force,
3782 return hg.merge(repo, node, force=force, mergeforce=force,
3782 labels=labels, abort=abort)
3783 labels=labels, abort=abort)
3783
3784
3784 @command('outgoing|out',
3785 @command('outgoing|out',
3785 [('f', 'force', None, _('run even when the destination is unrelated')),
3786 [('f', 'force', None, _('run even when the destination is unrelated')),
3786 ('r', 'rev', [],
3787 ('r', 'rev', [],
3787 _('a changeset intended to be included in the destination'), _('REV')),
3788 _('a changeset intended to be included in the destination'), _('REV')),
3788 ('n', 'newest-first', None, _('show newest record first')),
3789 ('n', 'newest-first', None, _('show newest record first')),
3789 ('B', 'bookmarks', False, _('compare bookmarks')),
3790 ('B', 'bookmarks', False, _('compare bookmarks')),
3790 ('b', 'branch', [], _('a specific branch you would like to push'),
3791 ('b', 'branch', [], _('a specific branch you would like to push'),
3791 _('BRANCH')),
3792 _('BRANCH')),
3792 ] + logopts + remoteopts + subrepoopts,
3793 ] + logopts + remoteopts + subrepoopts,
3793 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3794 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3794 def outgoing(ui, repo, dest=None, **opts):
3795 def outgoing(ui, repo, dest=None, **opts):
3795 """show changesets not found in the destination
3796 """show changesets not found in the destination
3796
3797
3797 Show changesets not found in the specified destination repository
3798 Show changesets not found in the specified destination repository
3798 or the default push location. These are the changesets that would
3799 or the default push location. These are the changesets that would
3799 be pushed if a push was requested.
3800 be pushed if a push was requested.
3800
3801
3801 See pull for details of valid destination formats.
3802 See pull for details of valid destination formats.
3802
3803
3803 .. container:: verbose
3804 .. container:: verbose
3804
3805
3805 With -B/--bookmarks, the result of bookmark comparison between
3806 With -B/--bookmarks, the result of bookmark comparison between
3806 local and remote repositories is displayed. With -v/--verbose,
3807 local and remote repositories is displayed. With -v/--verbose,
3807 status is also displayed for each bookmark like below::
3808 status is also displayed for each bookmark like below::
3808
3809
3809 BM1 01234567890a added
3810 BM1 01234567890a added
3810 BM2 deleted
3811 BM2 deleted
3811 BM3 234567890abc advanced
3812 BM3 234567890abc advanced
3812 BM4 34567890abcd diverged
3813 BM4 34567890abcd diverged
3813 BM5 4567890abcde changed
3814 BM5 4567890abcde changed
3814
3815
3815 The action taken when pushing depends on the
3816 The action taken when pushing depends on the
3816 status of each bookmark:
3817 status of each bookmark:
3817
3818
3818 :``added``: push with ``-B`` will create it
3819 :``added``: push with ``-B`` will create it
3819 :``deleted``: push with ``-B`` will delete it
3820 :``deleted``: push with ``-B`` will delete it
3820 :``advanced``: push will update it
3821 :``advanced``: push will update it
3821 :``diverged``: push with ``-B`` will update it
3822 :``diverged``: push with ``-B`` will update it
3822 :``changed``: push with ``-B`` will update it
3823 :``changed``: push with ``-B`` will update it
3823
3824
3824 From the point of view of pushing behavior, bookmarks
3825 From the point of view of pushing behavior, bookmarks
3825 existing only in the remote repository are treated as
3826 existing only in the remote repository are treated as
3826 ``deleted``, even if it is in fact added remotely.
3827 ``deleted``, even if it is in fact added remotely.
3827
3828
3828 Returns 0 if there are outgoing changes, 1 otherwise.
3829 Returns 0 if there are outgoing changes, 1 otherwise.
3829 """
3830 """
3830 # hg._outgoing() needs to re-resolve the path in order to handle #branch
3831 # hg._outgoing() needs to re-resolve the path in order to handle #branch
3831 # style URLs, so don't overwrite dest.
3832 # style URLs, so don't overwrite dest.
3832 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3833 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3833 if not path:
3834 if not path:
3834 raise error.Abort(_('default repository not configured!'),
3835 raise error.Abort(_('default repository not configured!'),
3835 hint=_("see 'hg help config.paths'"))
3836 hint=_("see 'hg help config.paths'"))
3836
3837
3837 opts = pycompat.byteskwargs(opts)
3838 opts = pycompat.byteskwargs(opts)
3838 if opts.get('graph'):
3839 if opts.get('graph'):
3839 logcmdutil.checkunsupportedgraphflags([], opts)
3840 logcmdutil.checkunsupportedgraphflags([], opts)
3840 o, other = hg._outgoing(ui, repo, dest, opts)
3841 o, other = hg._outgoing(ui, repo, dest, opts)
3841 if not o:
3842 if not o:
3842 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3843 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3843 return
3844 return
3844
3845
3845 revdag = logcmdutil.graphrevs(repo, o, opts)
3846 revdag = logcmdutil.graphrevs(repo, o, opts)
3846 ui.pager('outgoing')
3847 ui.pager('outgoing')
3847 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3848 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3848 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3849 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3849 graphmod.asciiedges)
3850 graphmod.asciiedges)
3850 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3851 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3851 return 0
3852 return 0
3852
3853
3853 if opts.get('bookmarks'):
3854 if opts.get('bookmarks'):
3854 dest = path.pushloc or path.loc
3855 dest = path.pushloc or path.loc
3855 other = hg.peer(repo, opts, dest)
3856 other = hg.peer(repo, opts, dest)
3856 if 'bookmarks' not in other.listkeys('namespaces'):
3857 if 'bookmarks' not in other.listkeys('namespaces'):
3857 ui.warn(_("remote doesn't support bookmarks\n"))
3858 ui.warn(_("remote doesn't support bookmarks\n"))
3858 return 0
3859 return 0
3859 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3860 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3860 ui.pager('outgoing')
3861 ui.pager('outgoing')
3861 return bookmarks.outgoing(ui, repo, other)
3862 return bookmarks.outgoing(ui, repo, other)
3862
3863
3863 repo._subtoppath = path.pushloc or path.loc
3864 repo._subtoppath = path.pushloc or path.loc
3864 try:
3865 try:
3865 return hg.outgoing(ui, repo, dest, opts)
3866 return hg.outgoing(ui, repo, dest, opts)
3866 finally:
3867 finally:
3867 del repo._subtoppath
3868 del repo._subtoppath
3868
3869
3869 @command('parents',
3870 @command('parents',
3870 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3871 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3871 ] + templateopts,
3872 ] + templateopts,
3872 _('[-r REV] [FILE]'),
3873 _('[-r REV] [FILE]'),
3873 inferrepo=True)
3874 inferrepo=True)
3874 def parents(ui, repo, file_=None, **opts):
3875 def parents(ui, repo, file_=None, **opts):
3875 """show the parents of the working directory or revision (DEPRECATED)
3876 """show the parents of the working directory or revision (DEPRECATED)
3876
3877
3877 Print the working directory's parent revisions. If a revision is
3878 Print the working directory's parent revisions. If a revision is
3878 given via -r/--rev, the parent of that revision will be printed.
3879 given via -r/--rev, the parent of that revision will be printed.
3879 If a file argument is given, the revision in which the file was
3880 If a file argument is given, the revision in which the file was
3880 last changed (before the working directory revision or the
3881 last changed (before the working directory revision or the
3881 argument to --rev if given) is printed.
3882 argument to --rev if given) is printed.
3882
3883
3883 This command is equivalent to::
3884 This command is equivalent to::
3884
3885
3885 hg log -r "p1()+p2()" or
3886 hg log -r "p1()+p2()" or
3886 hg log -r "p1(REV)+p2(REV)" or
3887 hg log -r "p1(REV)+p2(REV)" or
3887 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3888 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3888 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3889 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3889
3890
3890 See :hg:`summary` and :hg:`help revsets` for related information.
3891 See :hg:`summary` and :hg:`help revsets` for related information.
3891
3892
3892 Returns 0 on success.
3893 Returns 0 on success.
3893 """
3894 """
3894
3895
3895 opts = pycompat.byteskwargs(opts)
3896 opts = pycompat.byteskwargs(opts)
3896 rev = opts.get('rev')
3897 rev = opts.get('rev')
3897 if rev:
3898 if rev:
3898 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3899 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3899 ctx = scmutil.revsingle(repo, rev, None)
3900 ctx = scmutil.revsingle(repo, rev, None)
3900
3901
3901 if file_:
3902 if file_:
3902 m = scmutil.match(ctx, (file_,), opts)
3903 m = scmutil.match(ctx, (file_,), opts)
3903 if m.anypats() or len(m.files()) != 1:
3904 if m.anypats() or len(m.files()) != 1:
3904 raise error.Abort(_('can only specify an explicit filename'))
3905 raise error.Abort(_('can only specify an explicit filename'))
3905 file_ = m.files()[0]
3906 file_ = m.files()[0]
3906 filenodes = []
3907 filenodes = []
3907 for cp in ctx.parents():
3908 for cp in ctx.parents():
3908 if not cp:
3909 if not cp:
3909 continue
3910 continue
3910 try:
3911 try:
3911 filenodes.append(cp.filenode(file_))
3912 filenodes.append(cp.filenode(file_))
3912 except error.LookupError:
3913 except error.LookupError:
3913 pass
3914 pass
3914 if not filenodes:
3915 if not filenodes:
3915 raise error.Abort(_("'%s' not found in manifest!") % file_)
3916 raise error.Abort(_("'%s' not found in manifest!") % file_)
3916 p = []
3917 p = []
3917 for fn in filenodes:
3918 for fn in filenodes:
3918 fctx = repo.filectx(file_, fileid=fn)
3919 fctx = repo.filectx(file_, fileid=fn)
3919 p.append(fctx.node())
3920 p.append(fctx.node())
3920 else:
3921 else:
3921 p = [cp.node() for cp in ctx.parents()]
3922 p = [cp.node() for cp in ctx.parents()]
3922
3923
3923 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3924 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3924 for n in p:
3925 for n in p:
3925 if n != nullid:
3926 if n != nullid:
3926 displayer.show(repo[n])
3927 displayer.show(repo[n])
3927 displayer.close()
3928 displayer.close()
3928
3929
3929 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3930 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3930 intents={INTENT_READONLY})
3931 intents={INTENT_READONLY})
3931 def paths(ui, repo, search=None, **opts):
3932 def paths(ui, repo, search=None, **opts):
3932 """show aliases for remote repositories
3933 """show aliases for remote repositories
3933
3934
3934 Show definition of symbolic path name NAME. If no name is given,
3935 Show definition of symbolic path name NAME. If no name is given,
3935 show definition of all available names.
3936 show definition of all available names.
3936
3937
3937 Option -q/--quiet suppresses all output when searching for NAME
3938 Option -q/--quiet suppresses all output when searching for NAME
3938 and shows only the path names when listing all definitions.
3939 and shows only the path names when listing all definitions.
3939
3940
3940 Path names are defined in the [paths] section of your
3941 Path names are defined in the [paths] section of your
3941 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3942 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3942 repository, ``.hg/hgrc`` is used, too.
3943 repository, ``.hg/hgrc`` is used, too.
3943
3944
3944 The path names ``default`` and ``default-push`` have a special
3945 The path names ``default`` and ``default-push`` have a special
3945 meaning. When performing a push or pull operation, they are used
3946 meaning. When performing a push or pull operation, they are used
3946 as fallbacks if no location is specified on the command-line.
3947 as fallbacks if no location is specified on the command-line.
3947 When ``default-push`` is set, it will be used for push and
3948 When ``default-push`` is set, it will be used for push and
3948 ``default`` will be used for pull; otherwise ``default`` is used
3949 ``default`` will be used for pull; otherwise ``default`` is used
3949 as the fallback for both. When cloning a repository, the clone
3950 as the fallback for both. When cloning a repository, the clone
3950 source is written as ``default`` in ``.hg/hgrc``.
3951 source is written as ``default`` in ``.hg/hgrc``.
3951
3952
3952 .. note::
3953 .. note::
3953
3954
3954 ``default`` and ``default-push`` apply to all inbound (e.g.
3955 ``default`` and ``default-push`` apply to all inbound (e.g.
3955 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3956 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3956 and :hg:`bundle`) operations.
3957 and :hg:`bundle`) operations.
3957
3958
3958 See :hg:`help urls` for more information.
3959 See :hg:`help urls` for more information.
3959
3960
3960 Returns 0 on success.
3961 Returns 0 on success.
3961 """
3962 """
3962
3963
3963 opts = pycompat.byteskwargs(opts)
3964 opts = pycompat.byteskwargs(opts)
3964 ui.pager('paths')
3965 ui.pager('paths')
3965 if search:
3966 if search:
3966 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3967 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3967 if name == search]
3968 if name == search]
3968 else:
3969 else:
3969 pathitems = sorted(ui.paths.iteritems())
3970 pathitems = sorted(ui.paths.iteritems())
3970
3971
3971 fm = ui.formatter('paths', opts)
3972 fm = ui.formatter('paths', opts)
3972 if fm.isplain():
3973 if fm.isplain():
3973 hidepassword = util.hidepassword
3974 hidepassword = util.hidepassword
3974 else:
3975 else:
3975 hidepassword = bytes
3976 hidepassword = bytes
3976 if ui.quiet:
3977 if ui.quiet:
3977 namefmt = '%s\n'
3978 namefmt = '%s\n'
3978 else:
3979 else:
3979 namefmt = '%s = '
3980 namefmt = '%s = '
3980 showsubopts = not search and not ui.quiet
3981 showsubopts = not search and not ui.quiet
3981
3982
3982 for name, path in pathitems:
3983 for name, path in pathitems:
3983 fm.startitem()
3984 fm.startitem()
3984 fm.condwrite(not search, 'name', namefmt, name)
3985 fm.condwrite(not search, 'name', namefmt, name)
3985 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3986 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3986 for subopt, value in sorted(path.suboptions.items()):
3987 for subopt, value in sorted(path.suboptions.items()):
3987 assert subopt not in ('name', 'url')
3988 assert subopt not in ('name', 'url')
3988 if showsubopts:
3989 if showsubopts:
3989 fm.plain('%s:%s = ' % (name, subopt))
3990 fm.plain('%s:%s = ' % (name, subopt))
3990 fm.condwrite(showsubopts, subopt, '%s\n', value)
3991 fm.condwrite(showsubopts, subopt, '%s\n', value)
3991
3992
3992 fm.end()
3993 fm.end()
3993
3994
3994 if search and not pathitems:
3995 if search and not pathitems:
3995 if not ui.quiet:
3996 if not ui.quiet:
3996 ui.warn(_("not found!\n"))
3997 ui.warn(_("not found!\n"))
3997 return 1
3998 return 1
3998 else:
3999 else:
3999 return 0
4000 return 0
4000
4001
4001 @command('phase',
4002 @command('phase',
4002 [('p', 'public', False, _('set changeset phase to public')),
4003 [('p', 'public', False, _('set changeset phase to public')),
4003 ('d', 'draft', False, _('set changeset phase to draft')),
4004 ('d', 'draft', False, _('set changeset phase to draft')),
4004 ('s', 'secret', False, _('set changeset phase to secret')),
4005 ('s', 'secret', False, _('set changeset phase to secret')),
4005 ('f', 'force', False, _('allow to move boundary backward')),
4006 ('f', 'force', False, _('allow to move boundary backward')),
4006 ('r', 'rev', [], _('target revision'), _('REV')),
4007 ('r', 'rev', [], _('target revision'), _('REV')),
4007 ],
4008 ],
4008 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4009 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4009 def phase(ui, repo, *revs, **opts):
4010 def phase(ui, repo, *revs, **opts):
4010 """set or show the current phase name
4011 """set or show the current phase name
4011
4012
4012 With no argument, show the phase name of the current revision(s).
4013 With no argument, show the phase name of the current revision(s).
4013
4014
4014 With one of -p/--public, -d/--draft or -s/--secret, change the
4015 With one of -p/--public, -d/--draft or -s/--secret, change the
4015 phase value of the specified revisions.
4016 phase value of the specified revisions.
4016
4017
4017 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4018 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4018 lower phase to a higher phase. Phases are ordered as follows::
4019 lower phase to a higher phase. Phases are ordered as follows::
4019
4020
4020 public < draft < secret
4021 public < draft < secret
4021
4022
4022 Returns 0 on success, 1 if some phases could not be changed.
4023 Returns 0 on success, 1 if some phases could not be changed.
4023
4024
4024 (For more information about the phases concept, see :hg:`help phases`.)
4025 (For more information about the phases concept, see :hg:`help phases`.)
4025 """
4026 """
4026 opts = pycompat.byteskwargs(opts)
4027 opts = pycompat.byteskwargs(opts)
4027 # search for a unique phase argument
4028 # search for a unique phase argument
4028 targetphase = None
4029 targetphase = None
4029 for idx, name in enumerate(phases.phasenames):
4030 for idx, name in enumerate(phases.phasenames):
4030 if opts[name]:
4031 if opts[name]:
4031 if targetphase is not None:
4032 if targetphase is not None:
4032 raise error.Abort(_('only one phase can be specified'))
4033 raise error.Abort(_('only one phase can be specified'))
4033 targetphase = idx
4034 targetphase = idx
4034
4035
4035 # look for specified revision
4036 # look for specified revision
4036 revs = list(revs)
4037 revs = list(revs)
4037 revs.extend(opts['rev'])
4038 revs.extend(opts['rev'])
4038 if not revs:
4039 if not revs:
4039 # display both parents as the second parent phase can influence
4040 # display both parents as the second parent phase can influence
4040 # the phase of a merge commit
4041 # the phase of a merge commit
4041 revs = [c.rev() for c in repo[None].parents()]
4042 revs = [c.rev() for c in repo[None].parents()]
4042
4043
4043 revs = scmutil.revrange(repo, revs)
4044 revs = scmutil.revrange(repo, revs)
4044
4045
4045 ret = 0
4046 ret = 0
4046 if targetphase is None:
4047 if targetphase is None:
4047 # display
4048 # display
4048 for r in revs:
4049 for r in revs:
4049 ctx = repo[r]
4050 ctx = repo[r]
4050 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4051 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4051 else:
4052 else:
4052 with repo.lock(), repo.transaction("phase") as tr:
4053 with repo.lock(), repo.transaction("phase") as tr:
4053 # set phase
4054 # set phase
4054 if not revs:
4055 if not revs:
4055 raise error.Abort(_('empty revision set'))
4056 raise error.Abort(_('empty revision set'))
4056 nodes = [repo[r].node() for r in revs]
4057 nodes = [repo[r].node() for r in revs]
4057 # moving revision from public to draft may hide them
4058 # moving revision from public to draft may hide them
4058 # We have to check result on an unfiltered repository
4059 # We have to check result on an unfiltered repository
4059 unfi = repo.unfiltered()
4060 unfi = repo.unfiltered()
4060 getphase = unfi._phasecache.phase
4061 getphase = unfi._phasecache.phase
4061 olddata = [getphase(unfi, r) for r in unfi]
4062 olddata = [getphase(unfi, r) for r in unfi]
4062 phases.advanceboundary(repo, tr, targetphase, nodes)
4063 phases.advanceboundary(repo, tr, targetphase, nodes)
4063 if opts['force']:
4064 if opts['force']:
4064 phases.retractboundary(repo, tr, targetphase, nodes)
4065 phases.retractboundary(repo, tr, targetphase, nodes)
4065 getphase = unfi._phasecache.phase
4066 getphase = unfi._phasecache.phase
4066 newdata = [getphase(unfi, r) for r in unfi]
4067 newdata = [getphase(unfi, r) for r in unfi]
4067 changes = sum(newdata[r] != olddata[r] for r in unfi)
4068 changes = sum(newdata[r] != olddata[r] for r in unfi)
4068 cl = unfi.changelog
4069 cl = unfi.changelog
4069 rejected = [n for n in nodes
4070 rejected = [n for n in nodes
4070 if newdata[cl.rev(n)] < targetphase]
4071 if newdata[cl.rev(n)] < targetphase]
4071 if rejected:
4072 if rejected:
4072 ui.warn(_('cannot move %i changesets to a higher '
4073 ui.warn(_('cannot move %i changesets to a higher '
4073 'phase, use --force\n') % len(rejected))
4074 'phase, use --force\n') % len(rejected))
4074 ret = 1
4075 ret = 1
4075 if changes:
4076 if changes:
4076 msg = _('phase changed for %i changesets\n') % changes
4077 msg = _('phase changed for %i changesets\n') % changes
4077 if ret:
4078 if ret:
4078 ui.status(msg)
4079 ui.status(msg)
4079 else:
4080 else:
4080 ui.note(msg)
4081 ui.note(msg)
4081 else:
4082 else:
4082 ui.warn(_('no phases changed\n'))
4083 ui.warn(_('no phases changed\n'))
4083 return ret
4084 return ret
4084
4085
4085 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4086 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4086 """Run after a changegroup has been added via pull/unbundle
4087 """Run after a changegroup has been added via pull/unbundle
4087
4088
4088 This takes arguments below:
4089 This takes arguments below:
4089
4090
4090 :modheads: change of heads by pull/unbundle
4091 :modheads: change of heads by pull/unbundle
4091 :optupdate: updating working directory is needed or not
4092 :optupdate: updating working directory is needed or not
4092 :checkout: update destination revision (or None to default destination)
4093 :checkout: update destination revision (or None to default destination)
4093 :brev: a name, which might be a bookmark to be activated after updating
4094 :brev: a name, which might be a bookmark to be activated after updating
4094 """
4095 """
4095 if modheads == 0:
4096 if modheads == 0:
4096 return
4097 return
4097 if optupdate:
4098 if optupdate:
4098 try:
4099 try:
4099 return hg.updatetotally(ui, repo, checkout, brev)
4100 return hg.updatetotally(ui, repo, checkout, brev)
4100 except error.UpdateAbort as inst:
4101 except error.UpdateAbort as inst:
4101 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4102 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4102 hint = inst.hint
4103 hint = inst.hint
4103 raise error.UpdateAbort(msg, hint=hint)
4104 raise error.UpdateAbort(msg, hint=hint)
4104 if modheads > 1:
4105 if modheads > 1:
4105 currentbranchheads = len(repo.branchheads())
4106 currentbranchheads = len(repo.branchheads())
4106 if currentbranchheads == modheads:
4107 if currentbranchheads == modheads:
4107 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4108 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4108 elif currentbranchheads > 1:
4109 elif currentbranchheads > 1:
4109 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4110 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4110 "merge)\n"))
4111 "merge)\n"))
4111 else:
4112 else:
4112 ui.status(_("(run 'hg heads' to see heads)\n"))
4113 ui.status(_("(run 'hg heads' to see heads)\n"))
4113 elif not ui.configbool('commands', 'update.requiredest'):
4114 elif not ui.configbool('commands', 'update.requiredest'):
4114 ui.status(_("(run 'hg update' to get a working copy)\n"))
4115 ui.status(_("(run 'hg update' to get a working copy)\n"))
4115
4116
4116 @command('^pull',
4117 @command('^pull',
4117 [('u', 'update', None,
4118 [('u', 'update', None,
4118 _('update to new branch head if new descendants were pulled')),
4119 _('update to new branch head if new descendants were pulled')),
4119 ('f', 'force', None, _('run even when remote repository is unrelated')),
4120 ('f', 'force', None, _('run even when remote repository is unrelated')),
4120 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4121 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4121 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4122 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4122 ('b', 'branch', [], _('a specific branch you would like to pull'),
4123 ('b', 'branch', [], _('a specific branch you would like to pull'),
4123 _('BRANCH')),
4124 _('BRANCH')),
4124 ] + remoteopts,
4125 ] + remoteopts,
4125 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4126 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4126 def pull(ui, repo, source="default", **opts):
4127 def pull(ui, repo, source="default", **opts):
4127 """pull changes from the specified source
4128 """pull changes from the specified source
4128
4129
4129 Pull changes from a remote repository to a local one.
4130 Pull changes from a remote repository to a local one.
4130
4131
4131 This finds all changes from the repository at the specified path
4132 This finds all changes from the repository at the specified path
4132 or URL and adds them to a local repository (the current one unless
4133 or URL and adds them to a local repository (the current one unless
4133 -R is specified). By default, this does not update the copy of the
4134 -R is specified). By default, this does not update the copy of the
4134 project in the working directory.
4135 project in the working directory.
4135
4136
4136 When cloning from servers that support it, Mercurial may fetch
4137 When cloning from servers that support it, Mercurial may fetch
4137 pre-generated data. When this is done, hooks operating on incoming
4138 pre-generated data. When this is done, hooks operating on incoming
4138 changesets and changegroups may fire more than once, once for each
4139 changesets and changegroups may fire more than once, once for each
4139 pre-generated bundle and as well as for any additional remaining
4140 pre-generated bundle and as well as for any additional remaining
4140 data. See :hg:`help -e clonebundles` for more.
4141 data. See :hg:`help -e clonebundles` for more.
4141
4142
4142 Use :hg:`incoming` if you want to see what would have been added
4143 Use :hg:`incoming` if you want to see what would have been added
4143 by a pull at the time you issued this command. If you then decide
4144 by a pull at the time you issued this command. If you then decide
4144 to add those changes to the repository, you should use :hg:`pull
4145 to add those changes to the repository, you should use :hg:`pull
4145 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4146 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4146
4147
4147 If SOURCE is omitted, the 'default' path will be used.
4148 If SOURCE is omitted, the 'default' path will be used.
4148 See :hg:`help urls` for more information.
4149 See :hg:`help urls` for more information.
4149
4150
4150 Specifying bookmark as ``.`` is equivalent to specifying the active
4151 Specifying bookmark as ``.`` is equivalent to specifying the active
4151 bookmark's name.
4152 bookmark's name.
4152
4153
4153 Returns 0 on success, 1 if an update had unresolved files.
4154 Returns 0 on success, 1 if an update had unresolved files.
4154 """
4155 """
4155
4156
4156 opts = pycompat.byteskwargs(opts)
4157 opts = pycompat.byteskwargs(opts)
4157 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4158 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4158 msg = _('update destination required by configuration')
4159 msg = _('update destination required by configuration')
4159 hint = _('use hg pull followed by hg update DEST')
4160 hint = _('use hg pull followed by hg update DEST')
4160 raise error.Abort(msg, hint=hint)
4161 raise error.Abort(msg, hint=hint)
4161
4162
4162 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4163 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4163 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4164 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4164 other = hg.peer(repo, opts, source)
4165 other = hg.peer(repo, opts, source)
4165 try:
4166 try:
4166 revs, checkout = hg.addbranchrevs(repo, other, branches,
4167 revs, checkout = hg.addbranchrevs(repo, other, branches,
4167 opts.get('rev'))
4168 opts.get('rev'))
4168
4169
4169
4170
4170 pullopargs = {}
4171 pullopargs = {}
4171 if opts.get('bookmark'):
4172 if opts.get('bookmark'):
4172 if not revs:
4173 if not revs:
4173 revs = []
4174 revs = []
4174 # The list of bookmark used here is not the one used to actually
4175 # The list of bookmark used here is not the one used to actually
4175 # update the bookmark name. This can result in the revision pulled
4176 # update the bookmark name. This can result in the revision pulled
4176 # not ending up with the name of the bookmark because of a race
4177 # not ending up with the name of the bookmark because of a race
4177 # condition on the server. (See issue 4689 for details)
4178 # condition on the server. (See issue 4689 for details)
4178 remotebookmarks = other.listkeys('bookmarks')
4179 remotebookmarks = other.listkeys('bookmarks')
4179 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4180 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4180 pullopargs['remotebookmarks'] = remotebookmarks
4181 pullopargs['remotebookmarks'] = remotebookmarks
4181 for b in opts['bookmark']:
4182 for b in opts['bookmark']:
4182 b = repo._bookmarks.expandname(b)
4183 b = repo._bookmarks.expandname(b)
4183 if b not in remotebookmarks:
4184 if b not in remotebookmarks:
4184 raise error.Abort(_('remote bookmark %s not found!') % b)
4185 raise error.Abort(_('remote bookmark %s not found!') % b)
4185 revs.append(hex(remotebookmarks[b]))
4186 revs.append(hex(remotebookmarks[b]))
4186
4187
4187 if revs:
4188 if revs:
4188 try:
4189 try:
4189 # When 'rev' is a bookmark name, we cannot guarantee that it
4190 # When 'rev' is a bookmark name, we cannot guarantee that it
4190 # will be updated with that name because of a race condition
4191 # will be updated with that name because of a race condition
4191 # server side. (See issue 4689 for details)
4192 # server side. (See issue 4689 for details)
4192 oldrevs = revs
4193 oldrevs = revs
4193 revs = [] # actually, nodes
4194 revs = [] # actually, nodes
4194 for r in oldrevs:
4195 for r in oldrevs:
4195 with other.commandexecutor() as e:
4196 with other.commandexecutor() as e:
4196 node = e.callcommand('lookup', {'key': r}).result()
4197 node = e.callcommand('lookup', {'key': r}).result()
4197
4198
4198 revs.append(node)
4199 revs.append(node)
4199 if r == checkout:
4200 if r == checkout:
4200 checkout = node
4201 checkout = node
4201 except error.CapabilityError:
4202 except error.CapabilityError:
4202 err = _("other repository doesn't support revision lookup, "
4203 err = _("other repository doesn't support revision lookup, "
4203 "so a rev cannot be specified.")
4204 "so a rev cannot be specified.")
4204 raise error.Abort(err)
4205 raise error.Abort(err)
4205
4206
4206 wlock = util.nullcontextmanager()
4207 wlock = util.nullcontextmanager()
4207 if opts.get('update'):
4208 if opts.get('update'):
4208 wlock = repo.wlock()
4209 wlock = repo.wlock()
4209 with wlock:
4210 with wlock:
4210 pullopargs.update(opts.get('opargs', {}))
4211 pullopargs.update(opts.get('opargs', {}))
4211 modheads = exchange.pull(repo, other, heads=revs,
4212 modheads = exchange.pull(repo, other, heads=revs,
4212 force=opts.get('force'),
4213 force=opts.get('force'),
4213 bookmarks=opts.get('bookmark', ()),
4214 bookmarks=opts.get('bookmark', ()),
4214 opargs=pullopargs).cgresult
4215 opargs=pullopargs).cgresult
4215
4216
4216 # brev is a name, which might be a bookmark to be activated at
4217 # brev is a name, which might be a bookmark to be activated at
4217 # the end of the update. In other words, it is an explicit
4218 # the end of the update. In other words, it is an explicit
4218 # destination of the update
4219 # destination of the update
4219 brev = None
4220 brev = None
4220
4221
4221 if checkout:
4222 if checkout:
4222 checkout = repo.changelog.rev(checkout)
4223 checkout = repo.changelog.rev(checkout)
4223
4224
4224 # order below depends on implementation of
4225 # order below depends on implementation of
4225 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4226 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4226 # because 'checkout' is determined without it.
4227 # because 'checkout' is determined without it.
4227 if opts.get('rev'):
4228 if opts.get('rev'):
4228 brev = opts['rev'][0]
4229 brev = opts['rev'][0]
4229 elif opts.get('branch'):
4230 elif opts.get('branch'):
4230 brev = opts['branch'][0]
4231 brev = opts['branch'][0]
4231 else:
4232 else:
4232 brev = branches[0]
4233 brev = branches[0]
4233 repo._subtoppath = source
4234 repo._subtoppath = source
4234 try:
4235 try:
4235 ret = postincoming(ui, repo, modheads, opts.get('update'),
4236 ret = postincoming(ui, repo, modheads, opts.get('update'),
4236 checkout, brev)
4237 checkout, brev)
4237
4238
4238 finally:
4239 finally:
4239 del repo._subtoppath
4240 del repo._subtoppath
4240
4241
4241 finally:
4242 finally:
4242 other.close()
4243 other.close()
4243 return ret
4244 return ret
4244
4245
4245 @command('^push',
4246 @command('^push',
4246 [('f', 'force', None, _('force push')),
4247 [('f', 'force', None, _('force push')),
4247 ('r', 'rev', [],
4248 ('r', 'rev', [],
4248 _('a changeset intended to be included in the destination'),
4249 _('a changeset intended to be included in the destination'),
4249 _('REV')),
4250 _('REV')),
4250 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4251 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4251 ('b', 'branch', [],
4252 ('b', 'branch', [],
4252 _('a specific branch you would like to push'), _('BRANCH')),
4253 _('a specific branch you would like to push'), _('BRANCH')),
4253 ('', 'new-branch', False, _('allow pushing a new branch')),
4254 ('', 'new-branch', False, _('allow pushing a new branch')),
4254 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4255 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4255 ] + remoteopts,
4256 ] + remoteopts,
4256 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4257 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4257 def push(ui, repo, dest=None, **opts):
4258 def push(ui, repo, dest=None, **opts):
4258 """push changes to the specified destination
4259 """push changes to the specified destination
4259
4260
4260 Push changesets from the local repository to the specified
4261 Push changesets from the local repository to the specified
4261 destination.
4262 destination.
4262
4263
4263 This operation is symmetrical to pull: it is identical to a pull
4264 This operation is symmetrical to pull: it is identical to a pull
4264 in the destination repository from the current one.
4265 in the destination repository from the current one.
4265
4266
4266 By default, push will not allow creation of new heads at the
4267 By default, push will not allow creation of new heads at the
4267 destination, since multiple heads would make it unclear which head
4268 destination, since multiple heads would make it unclear which head
4268 to use. In this situation, it is recommended to pull and merge
4269 to use. In this situation, it is recommended to pull and merge
4269 before pushing.
4270 before pushing.
4270
4271
4271 Use --new-branch if you want to allow push to create a new named
4272 Use --new-branch if you want to allow push to create a new named
4272 branch that is not present at the destination. This allows you to
4273 branch that is not present at the destination. This allows you to
4273 only create a new branch without forcing other changes.
4274 only create a new branch without forcing other changes.
4274
4275
4275 .. note::
4276 .. note::
4276
4277
4277 Extra care should be taken with the -f/--force option,
4278 Extra care should be taken with the -f/--force option,
4278 which will push all new heads on all branches, an action which will
4279 which will push all new heads on all branches, an action which will
4279 almost always cause confusion for collaborators.
4280 almost always cause confusion for collaborators.
4280
4281
4281 If -r/--rev is used, the specified revision and all its ancestors
4282 If -r/--rev is used, the specified revision and all its ancestors
4282 will be pushed to the remote repository.
4283 will be pushed to the remote repository.
4283
4284
4284 If -B/--bookmark is used, the specified bookmarked revision, its
4285 If -B/--bookmark is used, the specified bookmarked revision, its
4285 ancestors, and the bookmark will be pushed to the remote
4286 ancestors, and the bookmark will be pushed to the remote
4286 repository. Specifying ``.`` is equivalent to specifying the active
4287 repository. Specifying ``.`` is equivalent to specifying the active
4287 bookmark's name.
4288 bookmark's name.
4288
4289
4289 Please see :hg:`help urls` for important details about ``ssh://``
4290 Please see :hg:`help urls` for important details about ``ssh://``
4290 URLs. If DESTINATION is omitted, a default path will be used.
4291 URLs. If DESTINATION is omitted, a default path will be used.
4291
4292
4292 .. container:: verbose
4293 .. container:: verbose
4293
4294
4294 The --pushvars option sends strings to the server that become
4295 The --pushvars option sends strings to the server that become
4295 environment variables prepended with ``HG_USERVAR_``. For example,
4296 environment variables prepended with ``HG_USERVAR_``. For example,
4296 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4297 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4297 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4298 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4298
4299
4299 pushvars can provide for user-overridable hooks as well as set debug
4300 pushvars can provide for user-overridable hooks as well as set debug
4300 levels. One example is having a hook that blocks commits containing
4301 levels. One example is having a hook that blocks commits containing
4301 conflict markers, but enables the user to override the hook if the file
4302 conflict markers, but enables the user to override the hook if the file
4302 is using conflict markers for testing purposes or the file format has
4303 is using conflict markers for testing purposes or the file format has
4303 strings that look like conflict markers.
4304 strings that look like conflict markers.
4304
4305
4305 By default, servers will ignore `--pushvars`. To enable it add the
4306 By default, servers will ignore `--pushvars`. To enable it add the
4306 following to your configuration file::
4307 following to your configuration file::
4307
4308
4308 [push]
4309 [push]
4309 pushvars.server = true
4310 pushvars.server = true
4310
4311
4311 Returns 0 if push was successful, 1 if nothing to push.
4312 Returns 0 if push was successful, 1 if nothing to push.
4312 """
4313 """
4313
4314
4314 opts = pycompat.byteskwargs(opts)
4315 opts = pycompat.byteskwargs(opts)
4315 if opts.get('bookmark'):
4316 if opts.get('bookmark'):
4316 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4317 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4317 for b in opts['bookmark']:
4318 for b in opts['bookmark']:
4318 # translate -B options to -r so changesets get pushed
4319 # translate -B options to -r so changesets get pushed
4319 b = repo._bookmarks.expandname(b)
4320 b = repo._bookmarks.expandname(b)
4320 if b in repo._bookmarks:
4321 if b in repo._bookmarks:
4321 opts.setdefault('rev', []).append(b)
4322 opts.setdefault('rev', []).append(b)
4322 else:
4323 else:
4323 # if we try to push a deleted bookmark, translate it to null
4324 # if we try to push a deleted bookmark, translate it to null
4324 # this lets simultaneous -r, -b options continue working
4325 # this lets simultaneous -r, -b options continue working
4325 opts.setdefault('rev', []).append("null")
4326 opts.setdefault('rev', []).append("null")
4326
4327
4327 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4328 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4328 if not path:
4329 if not path:
4329 raise error.Abort(_('default repository not configured!'),
4330 raise error.Abort(_('default repository not configured!'),
4330 hint=_("see 'hg help config.paths'"))
4331 hint=_("see 'hg help config.paths'"))
4331 dest = path.pushloc or path.loc
4332 dest = path.pushloc or path.loc
4332 branches = (path.branch, opts.get('branch') or [])
4333 branches = (path.branch, opts.get('branch') or [])
4333 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4334 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4334 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4335 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4335 other = hg.peer(repo, opts, dest)
4336 other = hg.peer(repo, opts, dest)
4336
4337
4337 if revs:
4338 if revs:
4338 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4339 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4339 if not revs:
4340 if not revs:
4340 raise error.Abort(_("specified revisions evaluate to an empty set"),
4341 raise error.Abort(_("specified revisions evaluate to an empty set"),
4341 hint=_("use different revision arguments"))
4342 hint=_("use different revision arguments"))
4342 elif path.pushrev:
4343 elif path.pushrev:
4343 # It doesn't make any sense to specify ancestor revisions. So limit
4344 # It doesn't make any sense to specify ancestor revisions. So limit
4344 # to DAG heads to make discovery simpler.
4345 # to DAG heads to make discovery simpler.
4345 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4346 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4346 revs = scmutil.revrange(repo, [expr])
4347 revs = scmutil.revrange(repo, [expr])
4347 revs = [repo[rev].node() for rev in revs]
4348 revs = [repo[rev].node() for rev in revs]
4348 if not revs:
4349 if not revs:
4349 raise error.Abort(_('default push revset for path evaluates to an '
4350 raise error.Abort(_('default push revset for path evaluates to an '
4350 'empty set'))
4351 'empty set'))
4351
4352
4352 repo._subtoppath = dest
4353 repo._subtoppath = dest
4353 try:
4354 try:
4354 # push subrepos depth-first for coherent ordering
4355 # push subrepos depth-first for coherent ordering
4355 c = repo['.']
4356 c = repo['.']
4356 subs = c.substate # only repos that are committed
4357 subs = c.substate # only repos that are committed
4357 for s in sorted(subs):
4358 for s in sorted(subs):
4358 result = c.sub(s).push(opts)
4359 result = c.sub(s).push(opts)
4359 if result == 0:
4360 if result == 0:
4360 return not result
4361 return not result
4361 finally:
4362 finally:
4362 del repo._subtoppath
4363 del repo._subtoppath
4363
4364
4364 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4365 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4365 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4366 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4366
4367
4367 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4368 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4368 newbranch=opts.get('new_branch'),
4369 newbranch=opts.get('new_branch'),
4369 bookmarks=opts.get('bookmark', ()),
4370 bookmarks=opts.get('bookmark', ()),
4370 opargs=opargs)
4371 opargs=opargs)
4371
4372
4372 result = not pushop.cgresult
4373 result = not pushop.cgresult
4373
4374
4374 if pushop.bkresult is not None:
4375 if pushop.bkresult is not None:
4375 if pushop.bkresult == 2:
4376 if pushop.bkresult == 2:
4376 result = 2
4377 result = 2
4377 elif not result and pushop.bkresult:
4378 elif not result and pushop.bkresult:
4378 result = 2
4379 result = 2
4379
4380
4380 return result
4381 return result
4381
4382
4382 @command('recover', [])
4383 @command('recover', [])
4383 def recover(ui, repo):
4384 def recover(ui, repo):
4384 """roll back an interrupted transaction
4385 """roll back an interrupted transaction
4385
4386
4386 Recover from an interrupted commit or pull.
4387 Recover from an interrupted commit or pull.
4387
4388
4388 This command tries to fix the repository status after an
4389 This command tries to fix the repository status after an
4389 interrupted operation. It should only be necessary when Mercurial
4390 interrupted operation. It should only be necessary when Mercurial
4390 suggests it.
4391 suggests it.
4391
4392
4392 Returns 0 if successful, 1 if nothing to recover or verify fails.
4393 Returns 0 if successful, 1 if nothing to recover or verify fails.
4393 """
4394 """
4394 if repo.recover():
4395 if repo.recover():
4395 return hg.verify(repo)
4396 return hg.verify(repo)
4396 return 1
4397 return 1
4397
4398
4398 @command('^remove|rm',
4399 @command('^remove|rm',
4399 [('A', 'after', None, _('record delete for missing files')),
4400 [('A', 'after', None, _('record delete for missing files')),
4400 ('f', 'force', None,
4401 ('f', 'force', None,
4401 _('forget added files, delete modified files')),
4402 _('forget added files, delete modified files')),
4402 ] + subrepoopts + walkopts + dryrunopts,
4403 ] + subrepoopts + walkopts + dryrunopts,
4403 _('[OPTION]... FILE...'),
4404 _('[OPTION]... FILE...'),
4404 inferrepo=True)
4405 inferrepo=True)
4405 def remove(ui, repo, *pats, **opts):
4406 def remove(ui, repo, *pats, **opts):
4406 """remove the specified files on the next commit
4407 """remove the specified files on the next commit
4407
4408
4408 Schedule the indicated files for removal from the current branch.
4409 Schedule the indicated files for removal from the current branch.
4409
4410
4410 This command schedules the files to be removed at the next commit.
4411 This command schedules the files to be removed at the next commit.
4411 To undo a remove before that, see :hg:`revert`. To undo added
4412 To undo a remove before that, see :hg:`revert`. To undo added
4412 files, see :hg:`forget`.
4413 files, see :hg:`forget`.
4413
4414
4414 .. container:: verbose
4415 .. container:: verbose
4415
4416
4416 -A/--after can be used to remove only files that have already
4417 -A/--after can be used to remove only files that have already
4417 been deleted, -f/--force can be used to force deletion, and -Af
4418 been deleted, -f/--force can be used to force deletion, and -Af
4418 can be used to remove files from the next revision without
4419 can be used to remove files from the next revision without
4419 deleting them from the working directory.
4420 deleting them from the working directory.
4420
4421
4421 The following table details the behavior of remove for different
4422 The following table details the behavior of remove for different
4422 file states (columns) and option combinations (rows). The file
4423 file states (columns) and option combinations (rows). The file
4423 states are Added [A], Clean [C], Modified [M] and Missing [!]
4424 states are Added [A], Clean [C], Modified [M] and Missing [!]
4424 (as reported by :hg:`status`). The actions are Warn, Remove
4425 (as reported by :hg:`status`). The actions are Warn, Remove
4425 (from branch) and Delete (from disk):
4426 (from branch) and Delete (from disk):
4426
4427
4427 ========= == == == ==
4428 ========= == == == ==
4428 opt/state A C M !
4429 opt/state A C M !
4429 ========= == == == ==
4430 ========= == == == ==
4430 none W RD W R
4431 none W RD W R
4431 -f R RD RD R
4432 -f R RD RD R
4432 -A W W W R
4433 -A W W W R
4433 -Af R R R R
4434 -Af R R R R
4434 ========= == == == ==
4435 ========= == == == ==
4435
4436
4436 .. note::
4437 .. note::
4437
4438
4438 :hg:`remove` never deletes files in Added [A] state from the
4439 :hg:`remove` never deletes files in Added [A] state from the
4439 working directory, not even if ``--force`` is specified.
4440 working directory, not even if ``--force`` is specified.
4440
4441
4441 Returns 0 on success, 1 if any warnings encountered.
4442 Returns 0 on success, 1 if any warnings encountered.
4442 """
4443 """
4443
4444
4444 opts = pycompat.byteskwargs(opts)
4445 opts = pycompat.byteskwargs(opts)
4445 after, force = opts.get('after'), opts.get('force')
4446 after, force = opts.get('after'), opts.get('force')
4446 dryrun = opts.get('dry_run')
4447 dryrun = opts.get('dry_run')
4447 if not pats and not after:
4448 if not pats and not after:
4448 raise error.Abort(_('no files specified'))
4449 raise error.Abort(_('no files specified'))
4449
4450
4450 m = scmutil.match(repo[None], pats, opts)
4451 m = scmutil.match(repo[None], pats, opts)
4451 subrepos = opts.get('subrepos')
4452 subrepos = opts.get('subrepos')
4452 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4453 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4453 dryrun=dryrun)
4454 dryrun=dryrun)
4454
4455
4455 @command('rename|move|mv',
4456 @command('rename|move|mv',
4456 [('A', 'after', None, _('record a rename that has already occurred')),
4457 [('A', 'after', None, _('record a rename that has already occurred')),
4457 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4458 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4458 ] + walkopts + dryrunopts,
4459 ] + walkopts + dryrunopts,
4459 _('[OPTION]... SOURCE... DEST'))
4460 _('[OPTION]... SOURCE... DEST'))
4460 def rename(ui, repo, *pats, **opts):
4461 def rename(ui, repo, *pats, **opts):
4461 """rename files; equivalent of copy + remove
4462 """rename files; equivalent of copy + remove
4462
4463
4463 Mark dest as copies of sources; mark sources for deletion. If dest
4464 Mark dest as copies of sources; mark sources for deletion. If dest
4464 is a directory, copies are put in that directory. If dest is a
4465 is a directory, copies are put in that directory. If dest is a
4465 file, there can only be one source.
4466 file, there can only be one source.
4466
4467
4467 By default, this command copies the contents of files as they
4468 By default, this command copies the contents of files as they
4468 exist in the working directory. If invoked with -A/--after, the
4469 exist in the working directory. If invoked with -A/--after, the
4469 operation is recorded, but no copying is performed.
4470 operation is recorded, but no copying is performed.
4470
4471
4471 This command takes effect at the next commit. To undo a rename
4472 This command takes effect at the next commit. To undo a rename
4472 before that, see :hg:`revert`.
4473 before that, see :hg:`revert`.
4473
4474
4474 Returns 0 on success, 1 if errors are encountered.
4475 Returns 0 on success, 1 if errors are encountered.
4475 """
4476 """
4476 opts = pycompat.byteskwargs(opts)
4477 opts = pycompat.byteskwargs(opts)
4477 with repo.wlock(False):
4478 with repo.wlock(False):
4478 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4479 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4479
4480
4480 @command('resolve',
4481 @command('resolve',
4481 [('a', 'all', None, _('select all unresolved files')),
4482 [('a', 'all', None, _('select all unresolved files')),
4482 ('l', 'list', None, _('list state of files needing merge')),
4483 ('l', 'list', None, _('list state of files needing merge')),
4483 ('m', 'mark', None, _('mark files as resolved')),
4484 ('m', 'mark', None, _('mark files as resolved')),
4484 ('u', 'unmark', None, _('mark files as unresolved')),
4485 ('u', 'unmark', None, _('mark files as unresolved')),
4485 ('n', 'no-status', None, _('hide status prefix'))]
4486 ('n', 'no-status', None, _('hide status prefix'))]
4486 + mergetoolopts + walkopts + formatteropts,
4487 + mergetoolopts + walkopts + formatteropts,
4487 _('[OPTION]... [FILE]...'),
4488 _('[OPTION]... [FILE]...'),
4488 inferrepo=True)
4489 inferrepo=True)
4489 def resolve(ui, repo, *pats, **opts):
4490 def resolve(ui, repo, *pats, **opts):
4490 """redo merges or set/view the merge status of files
4491 """redo merges or set/view the merge status of files
4491
4492
4492 Merges with unresolved conflicts are often the result of
4493 Merges with unresolved conflicts are often the result of
4493 non-interactive merging using the ``internal:merge`` configuration
4494 non-interactive merging using the ``internal:merge`` configuration
4494 setting, or a command-line merge tool like ``diff3``. The resolve
4495 setting, or a command-line merge tool like ``diff3``. The resolve
4495 command is used to manage the files involved in a merge, after
4496 command is used to manage the files involved in a merge, after
4496 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4497 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4497 working directory must have two parents). See :hg:`help
4498 working directory must have two parents). See :hg:`help
4498 merge-tools` for information on configuring merge tools.
4499 merge-tools` for information on configuring merge tools.
4499
4500
4500 The resolve command can be used in the following ways:
4501 The resolve command can be used in the following ways:
4501
4502
4502 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4503 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4503 files, discarding any previous merge attempts. Re-merging is not
4504 files, discarding any previous merge attempts. Re-merging is not
4504 performed for files already marked as resolved. Use ``--all/-a``
4505 performed for files already marked as resolved. Use ``--all/-a``
4505 to select all unresolved files. ``--tool`` can be used to specify
4506 to select all unresolved files. ``--tool`` can be used to specify
4506 the merge tool used for the given files. It overrides the HGMERGE
4507 the merge tool used for the given files. It overrides the HGMERGE
4507 environment variable and your configuration files. Previous file
4508 environment variable and your configuration files. Previous file
4508 contents are saved with a ``.orig`` suffix.
4509 contents are saved with a ``.orig`` suffix.
4509
4510
4510 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4511 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4511 (e.g. after having manually fixed-up the files). The default is
4512 (e.g. after having manually fixed-up the files). The default is
4512 to mark all unresolved files.
4513 to mark all unresolved files.
4513
4514
4514 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4515 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4515 default is to mark all resolved files.
4516 default is to mark all resolved files.
4516
4517
4517 - :hg:`resolve -l`: list files which had or still have conflicts.
4518 - :hg:`resolve -l`: list files which had or still have conflicts.
4518 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4519 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4519 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4520 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4520 the list. See :hg:`help filesets` for details.
4521 the list. See :hg:`help filesets` for details.
4521
4522
4522 .. note::
4523 .. note::
4523
4524
4524 Mercurial will not let you commit files with unresolved merge
4525 Mercurial will not let you commit files with unresolved merge
4525 conflicts. You must use :hg:`resolve -m ...` before you can
4526 conflicts. You must use :hg:`resolve -m ...` before you can
4526 commit after a conflicting merge.
4527 commit after a conflicting merge.
4527
4528
4528 Returns 0 on success, 1 if any files fail a resolve attempt.
4529 Returns 0 on success, 1 if any files fail a resolve attempt.
4529 """
4530 """
4530
4531
4531 opts = pycompat.byteskwargs(opts)
4532 opts = pycompat.byteskwargs(opts)
4532 confirm = ui.configbool('commands', 'resolve.confirm')
4533 confirm = ui.configbool('commands', 'resolve.confirm')
4533 flaglist = 'all mark unmark list no_status'.split()
4534 flaglist = 'all mark unmark list no_status'.split()
4534 all, mark, unmark, show, nostatus = \
4535 all, mark, unmark, show, nostatus = \
4535 [opts.get(o) for o in flaglist]
4536 [opts.get(o) for o in flaglist]
4536
4537
4537 if (show and (mark or unmark)) or (mark and unmark):
4538 if (show and (mark or unmark)) or (mark and unmark):
4538 raise error.Abort(_("too many options specified"))
4539 raise error.Abort(_("too many options specified"))
4539 if pats and all:
4540 if pats and all:
4540 raise error.Abort(_("can't specify --all and patterns"))
4541 raise error.Abort(_("can't specify --all and patterns"))
4541 if not (all or pats or show or mark or unmark):
4542 if not (all or pats or show or mark or unmark):
4542 raise error.Abort(_('no files or directories specified'),
4543 raise error.Abort(_('no files or directories specified'),
4543 hint=('use --all to re-merge all unresolved files'))
4544 hint=('use --all to re-merge all unresolved files'))
4544
4545
4545 if confirm:
4546 if confirm:
4546 if all:
4547 if all:
4547 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4548 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4548 b'$$ &Yes $$ &No')):
4549 b'$$ &Yes $$ &No')):
4549 raise error.Abort(_('user quit'))
4550 raise error.Abort(_('user quit'))
4550 if mark and not pats:
4551 if mark and not pats:
4551 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4552 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4552 b'$$ &Yes $$ &No')):
4553 b'$$ &Yes $$ &No')):
4553 raise error.Abort(_('user quit'))
4554 raise error.Abort(_('user quit'))
4554 if unmark and not pats:
4555 if unmark and not pats:
4555 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4556 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4556 b'$$ &Yes $$ &No')):
4557 b'$$ &Yes $$ &No')):
4557 raise error.Abort(_('user quit'))
4558 raise error.Abort(_('user quit'))
4558
4559
4559 if show:
4560 if show:
4560 ui.pager('resolve')
4561 ui.pager('resolve')
4561 fm = ui.formatter('resolve', opts)
4562 fm = ui.formatter('resolve', opts)
4562 ms = mergemod.mergestate.read(repo)
4563 ms = mergemod.mergestate.read(repo)
4563 wctx = repo[None]
4564 wctx = repo[None]
4564 m = scmutil.match(wctx, pats, opts)
4565 m = scmutil.match(wctx, pats, opts)
4565
4566
4566 # Labels and keys based on merge state. Unresolved path conflicts show
4567 # Labels and keys based on merge state. Unresolved path conflicts show
4567 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4568 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4568 # resolved conflicts.
4569 # resolved conflicts.
4569 mergestateinfo = {
4570 mergestateinfo = {
4570 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4571 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4571 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4572 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4572 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4573 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4573 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4574 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4574 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4575 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4575 'D'),
4576 'D'),
4576 }
4577 }
4577
4578
4578 for f in ms:
4579 for f in ms:
4579 if not m(f):
4580 if not m(f):
4580 continue
4581 continue
4581
4582
4582 label, key = mergestateinfo[ms[f]]
4583 label, key = mergestateinfo[ms[f]]
4583 fm.startitem()
4584 fm.startitem()
4584 fm.context(ctx=wctx)
4585 fm.context(ctx=wctx)
4585 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4586 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4586 fm.write('path', '%s\n', f, label=label)
4587 fm.write('path', '%s\n', f, label=label)
4587 fm.end()
4588 fm.end()
4588 return 0
4589 return 0
4589
4590
4590 with repo.wlock():
4591 with repo.wlock():
4591 ms = mergemod.mergestate.read(repo)
4592 ms = mergemod.mergestate.read(repo)
4592
4593
4593 if not (ms.active() or repo.dirstate.p2() != nullid):
4594 if not (ms.active() or repo.dirstate.p2() != nullid):
4594 raise error.Abort(
4595 raise error.Abort(
4595 _('resolve command not applicable when not merging'))
4596 _('resolve command not applicable when not merging'))
4596
4597
4597 wctx = repo[None]
4598 wctx = repo[None]
4598
4599
4599 if (ms.mergedriver
4600 if (ms.mergedriver
4600 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4601 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4601 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4602 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4602 ms.commit()
4603 ms.commit()
4603 # allow mark and unmark to go through
4604 # allow mark and unmark to go through
4604 if not mark and not unmark and not proceed:
4605 if not mark and not unmark and not proceed:
4605 return 1
4606 return 1
4606
4607
4607 m = scmutil.match(wctx, pats, opts)
4608 m = scmutil.match(wctx, pats, opts)
4608 ret = 0
4609 ret = 0
4609 didwork = False
4610 didwork = False
4610 runconclude = False
4611 runconclude = False
4611
4612
4612 tocomplete = []
4613 tocomplete = []
4613 hasconflictmarkers = []
4614 hasconflictmarkers = []
4614 if mark:
4615 if mark:
4615 markcheck = ui.config('commands', 'resolve.mark-check')
4616 markcheck = ui.config('commands', 'resolve.mark-check')
4616 if markcheck not in ['warn', 'abort']:
4617 if markcheck not in ['warn', 'abort']:
4617 # Treat all invalid / unrecognized values as 'none'.
4618 # Treat all invalid / unrecognized values as 'none'.
4618 markcheck = False
4619 markcheck = False
4619 for f in ms:
4620 for f in ms:
4620 if not m(f):
4621 if not m(f):
4621 continue
4622 continue
4622
4623
4623 didwork = True
4624 didwork = True
4624
4625
4625 # don't let driver-resolved files be marked, and run the conclude
4626 # don't let driver-resolved files be marked, and run the conclude
4626 # step if asked to resolve
4627 # step if asked to resolve
4627 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4628 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4628 exact = m.exact(f)
4629 exact = m.exact(f)
4629 if mark:
4630 if mark:
4630 if exact:
4631 if exact:
4631 ui.warn(_('not marking %s as it is driver-resolved\n')
4632 ui.warn(_('not marking %s as it is driver-resolved\n')
4632 % f)
4633 % f)
4633 elif unmark:
4634 elif unmark:
4634 if exact:
4635 if exact:
4635 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4636 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4636 % f)
4637 % f)
4637 else:
4638 else:
4638 runconclude = True
4639 runconclude = True
4639 continue
4640 continue
4640
4641
4641 # path conflicts must be resolved manually
4642 # path conflicts must be resolved manually
4642 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4643 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4643 mergemod.MERGE_RECORD_RESOLVED_PATH):
4644 mergemod.MERGE_RECORD_RESOLVED_PATH):
4644 if mark:
4645 if mark:
4645 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4646 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4646 elif unmark:
4647 elif unmark:
4647 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4648 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4648 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4649 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4649 ui.warn(_('%s: path conflict must be resolved manually\n')
4650 ui.warn(_('%s: path conflict must be resolved manually\n')
4650 % f)
4651 % f)
4651 continue
4652 continue
4652
4653
4653 if mark:
4654 if mark:
4654 if markcheck:
4655 if markcheck:
4655 with repo.wvfs(f) as fobj:
4656 with repo.wvfs(f) as fobj:
4656 fdata = fobj.read()
4657 fdata = fobj.read()
4657 if filemerge.hasconflictmarkers(fdata) and \
4658 if filemerge.hasconflictmarkers(fdata) and \
4658 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4659 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4659 hasconflictmarkers.append(f)
4660 hasconflictmarkers.append(f)
4660 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4661 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4661 elif unmark:
4662 elif unmark:
4662 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4663 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4663 else:
4664 else:
4664 # backup pre-resolve (merge uses .orig for its own purposes)
4665 # backup pre-resolve (merge uses .orig for its own purposes)
4665 a = repo.wjoin(f)
4666 a = repo.wjoin(f)
4666 try:
4667 try:
4667 util.copyfile(a, a + ".resolve")
4668 util.copyfile(a, a + ".resolve")
4668 except (IOError, OSError) as inst:
4669 except (IOError, OSError) as inst:
4669 if inst.errno != errno.ENOENT:
4670 if inst.errno != errno.ENOENT:
4670 raise
4671 raise
4671
4672
4672 try:
4673 try:
4673 # preresolve file
4674 # preresolve file
4674 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4675 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4675 with ui.configoverride(overrides, 'resolve'):
4676 with ui.configoverride(overrides, 'resolve'):
4676 complete, r = ms.preresolve(f, wctx)
4677 complete, r = ms.preresolve(f, wctx)
4677 if not complete:
4678 if not complete:
4678 tocomplete.append(f)
4679 tocomplete.append(f)
4679 elif r:
4680 elif r:
4680 ret = 1
4681 ret = 1
4681 finally:
4682 finally:
4682 ms.commit()
4683 ms.commit()
4683
4684
4684 # replace filemerge's .orig file with our resolve file, but only
4685 # replace filemerge's .orig file with our resolve file, but only
4685 # for merges that are complete
4686 # for merges that are complete
4686 if complete:
4687 if complete:
4687 try:
4688 try:
4688 util.rename(a + ".resolve",
4689 util.rename(a + ".resolve",
4689 scmutil.origpath(ui, repo, a))
4690 scmutil.origpath(ui, repo, a))
4690 except OSError as inst:
4691 except OSError as inst:
4691 if inst.errno != errno.ENOENT:
4692 if inst.errno != errno.ENOENT:
4692 raise
4693 raise
4693
4694
4694 if hasconflictmarkers:
4695 if hasconflictmarkers:
4695 ui.warn(_('warning: the following files still have conflict '
4696 ui.warn(_('warning: the following files still have conflict '
4696 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4697 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4697 if markcheck == 'abort' and not all:
4698 if markcheck == 'abort' and not all:
4698 raise error.Abort(_('conflict markers detected'),
4699 raise error.Abort(_('conflict markers detected'),
4699 hint=_('use --all to mark anyway'))
4700 hint=_('use --all to mark anyway'))
4700
4701
4701 for f in tocomplete:
4702 for f in tocomplete:
4702 try:
4703 try:
4703 # resolve file
4704 # resolve file
4704 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4705 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4705 with ui.configoverride(overrides, 'resolve'):
4706 with ui.configoverride(overrides, 'resolve'):
4706 r = ms.resolve(f, wctx)
4707 r = ms.resolve(f, wctx)
4707 if r:
4708 if r:
4708 ret = 1
4709 ret = 1
4709 finally:
4710 finally:
4710 ms.commit()
4711 ms.commit()
4711
4712
4712 # replace filemerge's .orig file with our resolve file
4713 # replace filemerge's .orig file with our resolve file
4713 a = repo.wjoin(f)
4714 a = repo.wjoin(f)
4714 try:
4715 try:
4715 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4716 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4716 except OSError as inst:
4717 except OSError as inst:
4717 if inst.errno != errno.ENOENT:
4718 if inst.errno != errno.ENOENT:
4718 raise
4719 raise
4719
4720
4720 ms.commit()
4721 ms.commit()
4721 ms.recordactions()
4722 ms.recordactions()
4722
4723
4723 if not didwork and pats:
4724 if not didwork and pats:
4724 hint = None
4725 hint = None
4725 if not any([p for p in pats if p.find(':') >= 0]):
4726 if not any([p for p in pats if p.find(':') >= 0]):
4726 pats = ['path:%s' % p for p in pats]
4727 pats = ['path:%s' % p for p in pats]
4727 m = scmutil.match(wctx, pats, opts)
4728 m = scmutil.match(wctx, pats, opts)
4728 for f in ms:
4729 for f in ms:
4729 if not m(f):
4730 if not m(f):
4730 continue
4731 continue
4731 flags = ''.join(['-%s ' % o[0:1] for o in flaglist
4732 flags = ''.join(['-%s ' % o[0:1] for o in flaglist
4732 if opts.get(o)])
4733 if opts.get(o)])
4733 hint = _("(try: hg resolve %s%s)\n") % (
4734 hint = _("(try: hg resolve %s%s)\n") % (
4734 flags,
4735 flags,
4735 ' '.join(pats))
4736 ' '.join(pats))
4736 break
4737 break
4737 ui.warn(_("arguments do not match paths that need resolving\n"))
4738 ui.warn(_("arguments do not match paths that need resolving\n"))
4738 if hint:
4739 if hint:
4739 ui.warn(hint)
4740 ui.warn(hint)
4740 elif ms.mergedriver and ms.mdstate() != 's':
4741 elif ms.mergedriver and ms.mdstate() != 's':
4741 # run conclude step when either a driver-resolved file is requested
4742 # run conclude step when either a driver-resolved file is requested
4742 # or there are no driver-resolved files
4743 # or there are no driver-resolved files
4743 # we can't use 'ret' to determine whether any files are unresolved
4744 # we can't use 'ret' to determine whether any files are unresolved
4744 # because we might not have tried to resolve some
4745 # because we might not have tried to resolve some
4745 if ((runconclude or not list(ms.driverresolved()))
4746 if ((runconclude or not list(ms.driverresolved()))
4746 and not list(ms.unresolved())):
4747 and not list(ms.unresolved())):
4747 proceed = mergemod.driverconclude(repo, ms, wctx)
4748 proceed = mergemod.driverconclude(repo, ms, wctx)
4748 ms.commit()
4749 ms.commit()
4749 if not proceed:
4750 if not proceed:
4750 return 1
4751 return 1
4751
4752
4752 # Nudge users into finishing an unfinished operation
4753 # Nudge users into finishing an unfinished operation
4753 unresolvedf = list(ms.unresolved())
4754 unresolvedf = list(ms.unresolved())
4754 driverresolvedf = list(ms.driverresolved())
4755 driverresolvedf = list(ms.driverresolved())
4755 if not unresolvedf and not driverresolvedf:
4756 if not unresolvedf and not driverresolvedf:
4756 ui.status(_('(no more unresolved files)\n'))
4757 ui.status(_('(no more unresolved files)\n'))
4757 cmdutil.checkafterresolved(repo)
4758 cmdutil.checkafterresolved(repo)
4758 elif not unresolvedf:
4759 elif not unresolvedf:
4759 ui.status(_('(no more unresolved files -- '
4760 ui.status(_('(no more unresolved files -- '
4760 'run "hg resolve --all" to conclude)\n'))
4761 'run "hg resolve --all" to conclude)\n'))
4761
4762
4762 return ret
4763 return ret
4763
4764
4764 @command('revert',
4765 @command('revert',
4765 [('a', 'all', None, _('revert all changes when no arguments given')),
4766 [('a', 'all', None, _('revert all changes when no arguments given')),
4766 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4767 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4767 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4768 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4768 ('C', 'no-backup', None, _('do not save backup copies of files')),
4769 ('C', 'no-backup', None, _('do not save backup copies of files')),
4769 ('i', 'interactive', None, _('interactively select the changes')),
4770 ('i', 'interactive', None, _('interactively select the changes')),
4770 ] + walkopts + dryrunopts,
4771 ] + walkopts + dryrunopts,
4771 _('[OPTION]... [-r REV] [NAME]...'))
4772 _('[OPTION]... [-r REV] [NAME]...'))
4772 def revert(ui, repo, *pats, **opts):
4773 def revert(ui, repo, *pats, **opts):
4773 """restore files to their checkout state
4774 """restore files to their checkout state
4774
4775
4775 .. note::
4776 .. note::
4776
4777
4777 To check out earlier revisions, you should use :hg:`update REV`.
4778 To check out earlier revisions, you should use :hg:`update REV`.
4778 To cancel an uncommitted merge (and lose your changes),
4779 To cancel an uncommitted merge (and lose your changes),
4779 use :hg:`merge --abort`.
4780 use :hg:`merge --abort`.
4780
4781
4781 With no revision specified, revert the specified files or directories
4782 With no revision specified, revert the specified files or directories
4782 to the contents they had in the parent of the working directory.
4783 to the contents they had in the parent of the working directory.
4783 This restores the contents of files to an unmodified
4784 This restores the contents of files to an unmodified
4784 state and unschedules adds, removes, copies, and renames. If the
4785 state and unschedules adds, removes, copies, and renames. If the
4785 working directory has two parents, you must explicitly specify a
4786 working directory has two parents, you must explicitly specify a
4786 revision.
4787 revision.
4787
4788
4788 Using the -r/--rev or -d/--date options, revert the given files or
4789 Using the -r/--rev or -d/--date options, revert the given files or
4789 directories to their states as of a specific revision. Because
4790 directories to their states as of a specific revision. Because
4790 revert does not change the working directory parents, this will
4791 revert does not change the working directory parents, this will
4791 cause these files to appear modified. This can be helpful to "back
4792 cause these files to appear modified. This can be helpful to "back
4792 out" some or all of an earlier change. See :hg:`backout` for a
4793 out" some or all of an earlier change. See :hg:`backout` for a
4793 related method.
4794 related method.
4794
4795
4795 Modified files are saved with a .orig suffix before reverting.
4796 Modified files are saved with a .orig suffix before reverting.
4796 To disable these backups, use --no-backup. It is possible to store
4797 To disable these backups, use --no-backup. It is possible to store
4797 the backup files in a custom directory relative to the root of the
4798 the backup files in a custom directory relative to the root of the
4798 repository by setting the ``ui.origbackuppath`` configuration
4799 repository by setting the ``ui.origbackuppath`` configuration
4799 option.
4800 option.
4800
4801
4801 See :hg:`help dates` for a list of formats valid for -d/--date.
4802 See :hg:`help dates` for a list of formats valid for -d/--date.
4802
4803
4803 See :hg:`help backout` for a way to reverse the effect of an
4804 See :hg:`help backout` for a way to reverse the effect of an
4804 earlier changeset.
4805 earlier changeset.
4805
4806
4806 Returns 0 on success.
4807 Returns 0 on success.
4807 """
4808 """
4808
4809
4809 opts = pycompat.byteskwargs(opts)
4810 opts = pycompat.byteskwargs(opts)
4810 if opts.get("date"):
4811 if opts.get("date"):
4811 if opts.get("rev"):
4812 if opts.get("rev"):
4812 raise error.Abort(_("you can't specify a revision and a date"))
4813 raise error.Abort(_("you can't specify a revision and a date"))
4813 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4814 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4814
4815
4815 parent, p2 = repo.dirstate.parents()
4816 parent, p2 = repo.dirstate.parents()
4816 if not opts.get('rev') and p2 != nullid:
4817 if not opts.get('rev') and p2 != nullid:
4817 # revert after merge is a trap for new users (issue2915)
4818 # revert after merge is a trap for new users (issue2915)
4818 raise error.Abort(_('uncommitted merge with no revision specified'),
4819 raise error.Abort(_('uncommitted merge with no revision specified'),
4819 hint=_("use 'hg update' or see 'hg help revert'"))
4820 hint=_("use 'hg update' or see 'hg help revert'"))
4820
4821
4821 rev = opts.get('rev')
4822 rev = opts.get('rev')
4822 if rev:
4823 if rev:
4823 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4824 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4824 ctx = scmutil.revsingle(repo, rev)
4825 ctx = scmutil.revsingle(repo, rev)
4825
4826
4826 if (not (pats or opts.get('include') or opts.get('exclude') or
4827 if (not (pats or opts.get('include') or opts.get('exclude') or
4827 opts.get('all') or opts.get('interactive'))):
4828 opts.get('all') or opts.get('interactive'))):
4828 msg = _("no files or directories specified")
4829 msg = _("no files or directories specified")
4829 if p2 != nullid:
4830 if p2 != nullid:
4830 hint = _("uncommitted merge, use --all to discard all changes,"
4831 hint = _("uncommitted merge, use --all to discard all changes,"
4831 " or 'hg update -C .' to abort the merge")
4832 " or 'hg update -C .' to abort the merge")
4832 raise error.Abort(msg, hint=hint)
4833 raise error.Abort(msg, hint=hint)
4833 dirty = any(repo.status())
4834 dirty = any(repo.status())
4834 node = ctx.node()
4835 node = ctx.node()
4835 if node != parent:
4836 if node != parent:
4836 if dirty:
4837 if dirty:
4837 hint = _("uncommitted changes, use --all to discard all"
4838 hint = _("uncommitted changes, use --all to discard all"
4838 " changes, or 'hg update %s' to update") % ctx.rev()
4839 " changes, or 'hg update %s' to update") % ctx.rev()
4839 else:
4840 else:
4840 hint = _("use --all to revert all files,"
4841 hint = _("use --all to revert all files,"
4841 " or 'hg update %s' to update") % ctx.rev()
4842 " or 'hg update %s' to update") % ctx.rev()
4842 elif dirty:
4843 elif dirty:
4843 hint = _("uncommitted changes, use --all to discard all changes")
4844 hint = _("uncommitted changes, use --all to discard all changes")
4844 else:
4845 else:
4845 hint = _("use --all to revert all files")
4846 hint = _("use --all to revert all files")
4846 raise error.Abort(msg, hint=hint)
4847 raise error.Abort(msg, hint=hint)
4847
4848
4848 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4849 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4849 **pycompat.strkwargs(opts))
4850 **pycompat.strkwargs(opts))
4850
4851
4851 @command('rollback', dryrunopts +
4852 @command('rollback', dryrunopts +
4852 [('f', 'force', False, _('ignore safety measures'))])
4853 [('f', 'force', False, _('ignore safety measures'))])
4853 def rollback(ui, repo, **opts):
4854 def rollback(ui, repo, **opts):
4854 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4855 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4855
4856
4856 Please use :hg:`commit --amend` instead of rollback to correct
4857 Please use :hg:`commit --amend` instead of rollback to correct
4857 mistakes in the last commit.
4858 mistakes in the last commit.
4858
4859
4859 This command should be used with care. There is only one level of
4860 This command should be used with care. There is only one level of
4860 rollback, and there is no way to undo a rollback. It will also
4861 rollback, and there is no way to undo a rollback. It will also
4861 restore the dirstate at the time of the last transaction, losing
4862 restore the dirstate at the time of the last transaction, losing
4862 any dirstate changes since that time. This command does not alter
4863 any dirstate changes since that time. This command does not alter
4863 the working directory.
4864 the working directory.
4864
4865
4865 Transactions are used to encapsulate the effects of all commands
4866 Transactions are used to encapsulate the effects of all commands
4866 that create new changesets or propagate existing changesets into a
4867 that create new changesets or propagate existing changesets into a
4867 repository.
4868 repository.
4868
4869
4869 .. container:: verbose
4870 .. container:: verbose
4870
4871
4871 For example, the following commands are transactional, and their
4872 For example, the following commands are transactional, and their
4872 effects can be rolled back:
4873 effects can be rolled back:
4873
4874
4874 - commit
4875 - commit
4875 - import
4876 - import
4876 - pull
4877 - pull
4877 - push (with this repository as the destination)
4878 - push (with this repository as the destination)
4878 - unbundle
4879 - unbundle
4879
4880
4880 To avoid permanent data loss, rollback will refuse to rollback a
4881 To avoid permanent data loss, rollback will refuse to rollback a
4881 commit transaction if it isn't checked out. Use --force to
4882 commit transaction if it isn't checked out. Use --force to
4882 override this protection.
4883 override this protection.
4883
4884
4884 The rollback command can be entirely disabled by setting the
4885 The rollback command can be entirely disabled by setting the
4885 ``ui.rollback`` configuration setting to false. If you're here
4886 ``ui.rollback`` configuration setting to false. If you're here
4886 because you want to use rollback and it's disabled, you can
4887 because you want to use rollback and it's disabled, you can
4887 re-enable the command by setting ``ui.rollback`` to true.
4888 re-enable the command by setting ``ui.rollback`` to true.
4888
4889
4889 This command is not intended for use on public repositories. Once
4890 This command is not intended for use on public repositories. Once
4890 changes are visible for pull by other users, rolling a transaction
4891 changes are visible for pull by other users, rolling a transaction
4891 back locally is ineffective (someone else may already have pulled
4892 back locally is ineffective (someone else may already have pulled
4892 the changes). Furthermore, a race is possible with readers of the
4893 the changes). Furthermore, a race is possible with readers of the
4893 repository; for example an in-progress pull from the repository
4894 repository; for example an in-progress pull from the repository
4894 may fail if a rollback is performed.
4895 may fail if a rollback is performed.
4895
4896
4896 Returns 0 on success, 1 if no rollback data is available.
4897 Returns 0 on success, 1 if no rollback data is available.
4897 """
4898 """
4898 if not ui.configbool('ui', 'rollback'):
4899 if not ui.configbool('ui', 'rollback'):
4899 raise error.Abort(_('rollback is disabled because it is unsafe'),
4900 raise error.Abort(_('rollback is disabled because it is unsafe'),
4900 hint=('see `hg help -v rollback` for information'))
4901 hint=('see `hg help -v rollback` for information'))
4901 return repo.rollback(dryrun=opts.get(r'dry_run'),
4902 return repo.rollback(dryrun=opts.get(r'dry_run'),
4902 force=opts.get(r'force'))
4903 force=opts.get(r'force'))
4903
4904
4904 @command('root', [], intents={INTENT_READONLY})
4905 @command('root', [], intents={INTENT_READONLY})
4905 def root(ui, repo):
4906 def root(ui, repo):
4906 """print the root (top) of the current working directory
4907 """print the root (top) of the current working directory
4907
4908
4908 Print the root directory of the current repository.
4909 Print the root directory of the current repository.
4909
4910
4910 Returns 0 on success.
4911 Returns 0 on success.
4911 """
4912 """
4912 ui.write(repo.root + "\n")
4913 ui.write(repo.root + "\n")
4913
4914
4914 @command('^serve',
4915 @command('^serve',
4915 [('A', 'accesslog', '', _('name of access log file to write to'),
4916 [('A', 'accesslog', '', _('name of access log file to write to'),
4916 _('FILE')),
4917 _('FILE')),
4917 ('d', 'daemon', None, _('run server in background')),
4918 ('d', 'daemon', None, _('run server in background')),
4918 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4919 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4919 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4920 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4920 # use string type, then we can check if something was passed
4921 # use string type, then we can check if something was passed
4921 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4922 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4922 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4923 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4923 _('ADDR')),
4924 _('ADDR')),
4924 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4925 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4925 _('PREFIX')),
4926 _('PREFIX')),
4926 ('n', 'name', '',
4927 ('n', 'name', '',
4927 _('name to show in web pages (default: working directory)'), _('NAME')),
4928 _('name to show in web pages (default: working directory)'), _('NAME')),
4928 ('', 'web-conf', '',
4929 ('', 'web-conf', '',
4929 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4930 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4930 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4931 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4931 _('FILE')),
4932 _('FILE')),
4932 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4933 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4933 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4934 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4934 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4935 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4935 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4936 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4936 ('', 'style', '', _('template style to use'), _('STYLE')),
4937 ('', 'style', '', _('template style to use'), _('STYLE')),
4937 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4938 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4938 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
4939 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
4939 ('', 'print-url', None, _('start and print only the URL'))]
4940 ('', 'print-url', None, _('start and print only the URL'))]
4940 + subrepoopts,
4941 + subrepoopts,
4941 _('[OPTION]...'),
4942 _('[OPTION]...'),
4942 optionalrepo=True)
4943 optionalrepo=True)
4943 def serve(ui, repo, **opts):
4944 def serve(ui, repo, **opts):
4944 """start stand-alone webserver
4945 """start stand-alone webserver
4945
4946
4946 Start a local HTTP repository browser and pull server. You can use
4947 Start a local HTTP repository browser and pull server. You can use
4947 this for ad-hoc sharing and browsing of repositories. It is
4948 this for ad-hoc sharing and browsing of repositories. It is
4948 recommended to use a real web server to serve a repository for
4949 recommended to use a real web server to serve a repository for
4949 longer periods of time.
4950 longer periods of time.
4950
4951
4951 Please note that the server does not implement access control.
4952 Please note that the server does not implement access control.
4952 This means that, by default, anybody can read from the server and
4953 This means that, by default, anybody can read from the server and
4953 nobody can write to it by default. Set the ``web.allow-push``
4954 nobody can write to it by default. Set the ``web.allow-push``
4954 option to ``*`` to allow everybody to push to the server. You
4955 option to ``*`` to allow everybody to push to the server. You
4955 should use a real web server if you need to authenticate users.
4956 should use a real web server if you need to authenticate users.
4956
4957
4957 By default, the server logs accesses to stdout and errors to
4958 By default, the server logs accesses to stdout and errors to
4958 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4959 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4959 files.
4960 files.
4960
4961
4961 To have the server choose a free port number to listen on, specify
4962 To have the server choose a free port number to listen on, specify
4962 a port number of 0; in this case, the server will print the port
4963 a port number of 0; in this case, the server will print the port
4963 number it uses.
4964 number it uses.
4964
4965
4965 Returns 0 on success.
4966 Returns 0 on success.
4966 """
4967 """
4967
4968
4968 opts = pycompat.byteskwargs(opts)
4969 opts = pycompat.byteskwargs(opts)
4969 if opts["stdio"] and opts["cmdserver"]:
4970 if opts["stdio"] and opts["cmdserver"]:
4970 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4971 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4971 if opts["print_url"] and ui.verbose:
4972 if opts["print_url"] and ui.verbose:
4972 raise error.Abort(_("cannot use --print-url with --verbose"))
4973 raise error.Abort(_("cannot use --print-url with --verbose"))
4973
4974
4974 if opts["stdio"]:
4975 if opts["stdio"]:
4975 if repo is None:
4976 if repo is None:
4976 raise error.RepoError(_("there is no Mercurial repository here"
4977 raise error.RepoError(_("there is no Mercurial repository here"
4977 " (.hg not found)"))
4978 " (.hg not found)"))
4978 s = wireprotoserver.sshserver(ui, repo)
4979 s = wireprotoserver.sshserver(ui, repo)
4979 s.serve_forever()
4980 s.serve_forever()
4980
4981
4981 service = server.createservice(ui, repo, opts)
4982 service = server.createservice(ui, repo, opts)
4982 return server.runservice(opts, initfn=service.init, runfn=service.run)
4983 return server.runservice(opts, initfn=service.init, runfn=service.run)
4983
4984
4984 _NOTTERSE = 'nothing'
4985 _NOTTERSE = 'nothing'
4985
4986
4986 @command('^status|st',
4987 @command('^status|st',
4987 [('A', 'all', None, _('show status of all files')),
4988 [('A', 'all', None, _('show status of all files')),
4988 ('m', 'modified', None, _('show only modified files')),
4989 ('m', 'modified', None, _('show only modified files')),
4989 ('a', 'added', None, _('show only added files')),
4990 ('a', 'added', None, _('show only added files')),
4990 ('r', 'removed', None, _('show only removed files')),
4991 ('r', 'removed', None, _('show only removed files')),
4991 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4992 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4992 ('c', 'clean', None, _('show only files without changes')),
4993 ('c', 'clean', None, _('show only files without changes')),
4993 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4994 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4994 ('i', 'ignored', None, _('show only ignored files')),
4995 ('i', 'ignored', None, _('show only ignored files')),
4995 ('n', 'no-status', None, _('hide status prefix')),
4996 ('n', 'no-status', None, _('hide status prefix')),
4996 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
4997 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
4997 ('C', 'copies', None, _('show source of copied files')),
4998 ('C', 'copies', None, _('show source of copied files')),
4998 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4999 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4999 ('', 'rev', [], _('show difference from revision'), _('REV')),
5000 ('', 'rev', [], _('show difference from revision'), _('REV')),
5000 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5001 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5001 ] + walkopts + subrepoopts + formatteropts,
5002 ] + walkopts + subrepoopts + formatteropts,
5002 _('[OPTION]... [FILE]...'),
5003 _('[OPTION]... [FILE]...'),
5003 inferrepo=True,
5004 inferrepo=True,
5004 intents={INTENT_READONLY})
5005 intents={INTENT_READONLY})
5005 def status(ui, repo, *pats, **opts):
5006 def status(ui, repo, *pats, **opts):
5006 """show changed files in the working directory
5007 """show changed files in the working directory
5007
5008
5008 Show status of files in the repository. If names are given, only
5009 Show status of files in the repository. If names are given, only
5009 files that match are shown. Files that are clean or ignored or
5010 files that match are shown. Files that are clean or ignored or
5010 the source of a copy/move operation, are not listed unless
5011 the source of a copy/move operation, are not listed unless
5011 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5012 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5012 Unless options described with "show only ..." are given, the
5013 Unless options described with "show only ..." are given, the
5013 options -mardu are used.
5014 options -mardu are used.
5014
5015
5015 Option -q/--quiet hides untracked (unknown and ignored) files
5016 Option -q/--quiet hides untracked (unknown and ignored) files
5016 unless explicitly requested with -u/--unknown or -i/--ignored.
5017 unless explicitly requested with -u/--unknown or -i/--ignored.
5017
5018
5018 .. note::
5019 .. note::
5019
5020
5020 :hg:`status` may appear to disagree with diff if permissions have
5021 :hg:`status` may appear to disagree with diff if permissions have
5021 changed or a merge has occurred. The standard diff format does
5022 changed or a merge has occurred. The standard diff format does
5022 not report permission changes and diff only reports changes
5023 not report permission changes and diff only reports changes
5023 relative to one merge parent.
5024 relative to one merge parent.
5024
5025
5025 If one revision is given, it is used as the base revision.
5026 If one revision is given, it is used as the base revision.
5026 If two revisions are given, the differences between them are
5027 If two revisions are given, the differences between them are
5027 shown. The --change option can also be used as a shortcut to list
5028 shown. The --change option can also be used as a shortcut to list
5028 the changed files of a revision from its first parent.
5029 the changed files of a revision from its first parent.
5029
5030
5030 The codes used to show the status of files are::
5031 The codes used to show the status of files are::
5031
5032
5032 M = modified
5033 M = modified
5033 A = added
5034 A = added
5034 R = removed
5035 R = removed
5035 C = clean
5036 C = clean
5036 ! = missing (deleted by non-hg command, but still tracked)
5037 ! = missing (deleted by non-hg command, but still tracked)
5037 ? = not tracked
5038 ? = not tracked
5038 I = ignored
5039 I = ignored
5039 = origin of the previous file (with --copies)
5040 = origin of the previous file (with --copies)
5040
5041
5041 .. container:: verbose
5042 .. container:: verbose
5042
5043
5043 The -t/--terse option abbreviates the output by showing only the directory
5044 The -t/--terse option abbreviates the output by showing only the directory
5044 name if all the files in it share the same status. The option takes an
5045 name if all the files in it share the same status. The option takes an
5045 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5046 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5046 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5047 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5047 for 'ignored' and 'c' for clean.
5048 for 'ignored' and 'c' for clean.
5048
5049
5049 It abbreviates only those statuses which are passed. Note that clean and
5050 It abbreviates only those statuses which are passed. Note that clean and
5050 ignored files are not displayed with '--terse ic' unless the -c/--clean
5051 ignored files are not displayed with '--terse ic' unless the -c/--clean
5051 and -i/--ignored options are also used.
5052 and -i/--ignored options are also used.
5052
5053
5053 The -v/--verbose option shows information when the repository is in an
5054 The -v/--verbose option shows information when the repository is in an
5054 unfinished merge, shelve, rebase state etc. You can have this behavior
5055 unfinished merge, shelve, rebase state etc. You can have this behavior
5055 turned on by default by enabling the ``commands.status.verbose`` option.
5056 turned on by default by enabling the ``commands.status.verbose`` option.
5056
5057
5057 You can skip displaying some of these states by setting
5058 You can skip displaying some of these states by setting
5058 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5059 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5059 'histedit', 'merge', 'rebase', or 'unshelve'.
5060 'histedit', 'merge', 'rebase', or 'unshelve'.
5060
5061
5061 Examples:
5062 Examples:
5062
5063
5063 - show changes in the working directory relative to a
5064 - show changes in the working directory relative to a
5064 changeset::
5065 changeset::
5065
5066
5066 hg status --rev 9353
5067 hg status --rev 9353
5067
5068
5068 - show changes in the working directory relative to the
5069 - show changes in the working directory relative to the
5069 current directory (see :hg:`help patterns` for more information)::
5070 current directory (see :hg:`help patterns` for more information)::
5070
5071
5071 hg status re:
5072 hg status re:
5072
5073
5073 - show all changes including copies in an existing changeset::
5074 - show all changes including copies in an existing changeset::
5074
5075
5075 hg status --copies --change 9353
5076 hg status --copies --change 9353
5076
5077
5077 - get a NUL separated list of added files, suitable for xargs::
5078 - get a NUL separated list of added files, suitable for xargs::
5078
5079
5079 hg status -an0
5080 hg status -an0
5080
5081
5081 - show more information about the repository status, abbreviating
5082 - show more information about the repository status, abbreviating
5082 added, removed, modified, deleted, and untracked paths::
5083 added, removed, modified, deleted, and untracked paths::
5083
5084
5084 hg status -v -t mardu
5085 hg status -v -t mardu
5085
5086
5086 Returns 0 on success.
5087 Returns 0 on success.
5087
5088
5088 """
5089 """
5089
5090
5090 opts = pycompat.byteskwargs(opts)
5091 opts = pycompat.byteskwargs(opts)
5091 revs = opts.get('rev')
5092 revs = opts.get('rev')
5092 change = opts.get('change')
5093 change = opts.get('change')
5093 terse = opts.get('terse')
5094 terse = opts.get('terse')
5094 if terse is _NOTTERSE:
5095 if terse is _NOTTERSE:
5095 if revs:
5096 if revs:
5096 terse = ''
5097 terse = ''
5097 else:
5098 else:
5098 terse = ui.config('commands', 'status.terse')
5099 terse = ui.config('commands', 'status.terse')
5099
5100
5100 if revs and change:
5101 if revs and change:
5101 msg = _('cannot specify --rev and --change at the same time')
5102 msg = _('cannot specify --rev and --change at the same time')
5102 raise error.Abort(msg)
5103 raise error.Abort(msg)
5103 elif revs and terse:
5104 elif revs and terse:
5104 msg = _('cannot use --terse with --rev')
5105 msg = _('cannot use --terse with --rev')
5105 raise error.Abort(msg)
5106 raise error.Abort(msg)
5106 elif change:
5107 elif change:
5107 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5108 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5108 ctx2 = scmutil.revsingle(repo, change, None)
5109 ctx2 = scmutil.revsingle(repo, change, None)
5109 ctx1 = ctx2.p1()
5110 ctx1 = ctx2.p1()
5110 else:
5111 else:
5111 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5112 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5112 ctx1, ctx2 = scmutil.revpair(repo, revs)
5113 ctx1, ctx2 = scmutil.revpair(repo, revs)
5113
5114
5114 if pats or ui.configbool('commands', 'status.relative'):
5115 if pats or ui.configbool('commands', 'status.relative'):
5115 cwd = repo.getcwd()
5116 cwd = repo.getcwd()
5116 else:
5117 else:
5117 cwd = ''
5118 cwd = ''
5118
5119
5119 if opts.get('print0'):
5120 if opts.get('print0'):
5120 end = '\0'
5121 end = '\0'
5121 else:
5122 else:
5122 end = '\n'
5123 end = '\n'
5123 copy = {}
5124 copy = {}
5124 states = 'modified added removed deleted unknown ignored clean'.split()
5125 states = 'modified added removed deleted unknown ignored clean'.split()
5125 show = [k for k in states if opts.get(k)]
5126 show = [k for k in states if opts.get(k)]
5126 if opts.get('all'):
5127 if opts.get('all'):
5127 show += ui.quiet and (states[:4] + ['clean']) or states
5128 show += ui.quiet and (states[:4] + ['clean']) or states
5128
5129
5129 if not show:
5130 if not show:
5130 if ui.quiet:
5131 if ui.quiet:
5131 show = states[:4]
5132 show = states[:4]
5132 else:
5133 else:
5133 show = states[:5]
5134 show = states[:5]
5134
5135
5135 m = scmutil.match(ctx2, pats, opts)
5136 m = scmutil.match(ctx2, pats, opts)
5136 if terse:
5137 if terse:
5137 # we need to compute clean and unknown to terse
5138 # we need to compute clean and unknown to terse
5138 stat = repo.status(ctx1.node(), ctx2.node(), m,
5139 stat = repo.status(ctx1.node(), ctx2.node(), m,
5139 'ignored' in show or 'i' in terse,
5140 'ignored' in show or 'i' in terse,
5140 clean=True, unknown=True,
5141 clean=True, unknown=True,
5141 listsubrepos=opts.get('subrepos'))
5142 listsubrepos=opts.get('subrepos'))
5142
5143
5143 stat = cmdutil.tersedir(stat, terse)
5144 stat = cmdutil.tersedir(stat, terse)
5144 else:
5145 else:
5145 stat = repo.status(ctx1.node(), ctx2.node(), m,
5146 stat = repo.status(ctx1.node(), ctx2.node(), m,
5146 'ignored' in show, 'clean' in show,
5147 'ignored' in show, 'clean' in show,
5147 'unknown' in show, opts.get('subrepos'))
5148 'unknown' in show, opts.get('subrepos'))
5148
5149
5149 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5150 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5150
5151
5151 if (opts.get('all') or opts.get('copies')
5152 if (opts.get('all') or opts.get('copies')
5152 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5153 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5153 copy = copies.pathcopies(ctx1, ctx2, m)
5154 copy = copies.pathcopies(ctx1, ctx2, m)
5154
5155
5155 ui.pager('status')
5156 ui.pager('status')
5156 fm = ui.formatter('status', opts)
5157 fm = ui.formatter('status', opts)
5157 fmt = '%s' + end
5158 fmt = '%s' + end
5158 showchar = not opts.get('no_status')
5159 showchar = not opts.get('no_status')
5159
5160
5160 for state, char, files in changestates:
5161 for state, char, files in changestates:
5161 if state in show:
5162 if state in show:
5162 label = 'status.' + state
5163 label = 'status.' + state
5163 for f in files:
5164 for f in files:
5164 fm.startitem()
5165 fm.startitem()
5165 fm.context(ctx=ctx2)
5166 fm.context(ctx=ctx2)
5166 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5167 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5167 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5168 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5168 if f in copy:
5169 if f in copy:
5169 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5170 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5170 label='status.copied')
5171 label='status.copied')
5171
5172
5172 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5173 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5173 and not ui.plain()):
5174 and not ui.plain()):
5174 cmdutil.morestatus(repo, fm)
5175 cmdutil.morestatus(repo, fm)
5175 fm.end()
5176 fm.end()
5176
5177
5177 @command('^summary|sum',
5178 @command('^summary|sum',
5178 [('', 'remote', None, _('check for push and pull'))],
5179 [('', 'remote', None, _('check for push and pull'))],
5179 '[--remote]',
5180 '[--remote]',
5180 intents={INTENT_READONLY})
5181 intents={INTENT_READONLY})
5181 def summary(ui, repo, **opts):
5182 def summary(ui, repo, **opts):
5182 """summarize working directory state
5183 """summarize working directory state
5183
5184
5184 This generates a brief summary of the working directory state,
5185 This generates a brief summary of the working directory state,
5185 including parents, branch, commit status, phase and available updates.
5186 including parents, branch, commit status, phase and available updates.
5186
5187
5187 With the --remote option, this will check the default paths for
5188 With the --remote option, this will check the default paths for
5188 incoming and outgoing changes. This can be time-consuming.
5189 incoming and outgoing changes. This can be time-consuming.
5189
5190
5190 Returns 0 on success.
5191 Returns 0 on success.
5191 """
5192 """
5192
5193
5193 opts = pycompat.byteskwargs(opts)
5194 opts = pycompat.byteskwargs(opts)
5194 ui.pager('summary')
5195 ui.pager('summary')
5195 ctx = repo[None]
5196 ctx = repo[None]
5196 parents = ctx.parents()
5197 parents = ctx.parents()
5197 pnode = parents[0].node()
5198 pnode = parents[0].node()
5198 marks = []
5199 marks = []
5199
5200
5200 ms = None
5201 ms = None
5201 try:
5202 try:
5202 ms = mergemod.mergestate.read(repo)
5203 ms = mergemod.mergestate.read(repo)
5203 except error.UnsupportedMergeRecords as e:
5204 except error.UnsupportedMergeRecords as e:
5204 s = ' '.join(e.recordtypes)
5205 s = ' '.join(e.recordtypes)
5205 ui.warn(
5206 ui.warn(
5206 _('warning: merge state has unsupported record types: %s\n') % s)
5207 _('warning: merge state has unsupported record types: %s\n') % s)
5207 unresolved = []
5208 unresolved = []
5208 else:
5209 else:
5209 unresolved = list(ms.unresolved())
5210 unresolved = list(ms.unresolved())
5210
5211
5211 for p in parents:
5212 for p in parents:
5212 # label with log.changeset (instead of log.parent) since this
5213 # label with log.changeset (instead of log.parent) since this
5213 # shows a working directory parent *changeset*:
5214 # shows a working directory parent *changeset*:
5214 # i18n: column positioning for "hg summary"
5215 # i18n: column positioning for "hg summary"
5215 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5216 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5216 label=logcmdutil.changesetlabels(p))
5217 label=logcmdutil.changesetlabels(p))
5217 ui.write(' '.join(p.tags()), label='log.tag')
5218 ui.write(' '.join(p.tags()), label='log.tag')
5218 if p.bookmarks():
5219 if p.bookmarks():
5219 marks.extend(p.bookmarks())
5220 marks.extend(p.bookmarks())
5220 if p.rev() == -1:
5221 if p.rev() == -1:
5221 if not len(repo):
5222 if not len(repo):
5222 ui.write(_(' (empty repository)'))
5223 ui.write(_(' (empty repository)'))
5223 else:
5224 else:
5224 ui.write(_(' (no revision checked out)'))
5225 ui.write(_(' (no revision checked out)'))
5225 if p.obsolete():
5226 if p.obsolete():
5226 ui.write(_(' (obsolete)'))
5227 ui.write(_(' (obsolete)'))
5227 if p.isunstable():
5228 if p.isunstable():
5228 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5229 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5229 for instability in p.instabilities())
5230 for instability in p.instabilities())
5230 ui.write(' ('
5231 ui.write(' ('
5231 + ', '.join(instabilities)
5232 + ', '.join(instabilities)
5232 + ')')
5233 + ')')
5233 ui.write('\n')
5234 ui.write('\n')
5234 if p.description():
5235 if p.description():
5235 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5236 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5236 label='log.summary')
5237 label='log.summary')
5237
5238
5238 branch = ctx.branch()
5239 branch = ctx.branch()
5239 bheads = repo.branchheads(branch)
5240 bheads = repo.branchheads(branch)
5240 # i18n: column positioning for "hg summary"
5241 # i18n: column positioning for "hg summary"
5241 m = _('branch: %s\n') % branch
5242 m = _('branch: %s\n') % branch
5242 if branch != 'default':
5243 if branch != 'default':
5243 ui.write(m, label='log.branch')
5244 ui.write(m, label='log.branch')
5244 else:
5245 else:
5245 ui.status(m, label='log.branch')
5246 ui.status(m, label='log.branch')
5246
5247
5247 if marks:
5248 if marks:
5248 active = repo._activebookmark
5249 active = repo._activebookmark
5249 # i18n: column positioning for "hg summary"
5250 # i18n: column positioning for "hg summary"
5250 ui.write(_('bookmarks:'), label='log.bookmark')
5251 ui.write(_('bookmarks:'), label='log.bookmark')
5251 if active is not None:
5252 if active is not None:
5252 if active in marks:
5253 if active in marks:
5253 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5254 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5254 marks.remove(active)
5255 marks.remove(active)
5255 else:
5256 else:
5256 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5257 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5257 for m in marks:
5258 for m in marks:
5258 ui.write(' ' + m, label='log.bookmark')
5259 ui.write(' ' + m, label='log.bookmark')
5259 ui.write('\n', label='log.bookmark')
5260 ui.write('\n', label='log.bookmark')
5260
5261
5261 status = repo.status(unknown=True)
5262 status = repo.status(unknown=True)
5262
5263
5263 c = repo.dirstate.copies()
5264 c = repo.dirstate.copies()
5264 copied, renamed = [], []
5265 copied, renamed = [], []
5265 for d, s in c.iteritems():
5266 for d, s in c.iteritems():
5266 if s in status.removed:
5267 if s in status.removed:
5267 status.removed.remove(s)
5268 status.removed.remove(s)
5268 renamed.append(d)
5269 renamed.append(d)
5269 else:
5270 else:
5270 copied.append(d)
5271 copied.append(d)
5271 if d in status.added:
5272 if d in status.added:
5272 status.added.remove(d)
5273 status.added.remove(d)
5273
5274
5274 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5275 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5275
5276
5276 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5277 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5277 (ui.label(_('%d added'), 'status.added'), status.added),
5278 (ui.label(_('%d added'), 'status.added'), status.added),
5278 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5279 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5279 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5280 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5280 (ui.label(_('%d copied'), 'status.copied'), copied),
5281 (ui.label(_('%d copied'), 'status.copied'), copied),
5281 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5282 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5282 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5283 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5283 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5284 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5284 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5285 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5285 t = []
5286 t = []
5286 for l, s in labels:
5287 for l, s in labels:
5287 if s:
5288 if s:
5288 t.append(l % len(s))
5289 t.append(l % len(s))
5289
5290
5290 t = ', '.join(t)
5291 t = ', '.join(t)
5291 cleanworkdir = False
5292 cleanworkdir = False
5292
5293
5293 if repo.vfs.exists('graftstate'):
5294 if repo.vfs.exists('graftstate'):
5294 t += _(' (graft in progress)')
5295 t += _(' (graft in progress)')
5295 if repo.vfs.exists('updatestate'):
5296 if repo.vfs.exists('updatestate'):
5296 t += _(' (interrupted update)')
5297 t += _(' (interrupted update)')
5297 elif len(parents) > 1:
5298 elif len(parents) > 1:
5298 t += _(' (merge)')
5299 t += _(' (merge)')
5299 elif branch != parents[0].branch():
5300 elif branch != parents[0].branch():
5300 t += _(' (new branch)')
5301 t += _(' (new branch)')
5301 elif (parents[0].closesbranch() and
5302 elif (parents[0].closesbranch() and
5302 pnode in repo.branchheads(branch, closed=True)):
5303 pnode in repo.branchheads(branch, closed=True)):
5303 t += _(' (head closed)')
5304 t += _(' (head closed)')
5304 elif not (status.modified or status.added or status.removed or renamed or
5305 elif not (status.modified or status.added or status.removed or renamed or
5305 copied or subs):
5306 copied or subs):
5306 t += _(' (clean)')
5307 t += _(' (clean)')
5307 cleanworkdir = True
5308 cleanworkdir = True
5308 elif pnode not in bheads:
5309 elif pnode not in bheads:
5309 t += _(' (new branch head)')
5310 t += _(' (new branch head)')
5310
5311
5311 if parents:
5312 if parents:
5312 pendingphase = max(p.phase() for p in parents)
5313 pendingphase = max(p.phase() for p in parents)
5313 else:
5314 else:
5314 pendingphase = phases.public
5315 pendingphase = phases.public
5315
5316
5316 if pendingphase > phases.newcommitphase(ui):
5317 if pendingphase > phases.newcommitphase(ui):
5317 t += ' (%s)' % phases.phasenames[pendingphase]
5318 t += ' (%s)' % phases.phasenames[pendingphase]
5318
5319
5319 if cleanworkdir:
5320 if cleanworkdir:
5320 # i18n: column positioning for "hg summary"
5321 # i18n: column positioning for "hg summary"
5321 ui.status(_('commit: %s\n') % t.strip())
5322 ui.status(_('commit: %s\n') % t.strip())
5322 else:
5323 else:
5323 # i18n: column positioning for "hg summary"
5324 # i18n: column positioning for "hg summary"
5324 ui.write(_('commit: %s\n') % t.strip())
5325 ui.write(_('commit: %s\n') % t.strip())
5325
5326
5326 # all ancestors of branch heads - all ancestors of parent = new csets
5327 # all ancestors of branch heads - all ancestors of parent = new csets
5327 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5328 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5328 bheads))
5329 bheads))
5329
5330
5330 if new == 0:
5331 if new == 0:
5331 # i18n: column positioning for "hg summary"
5332 # i18n: column positioning for "hg summary"
5332 ui.status(_('update: (current)\n'))
5333 ui.status(_('update: (current)\n'))
5333 elif pnode not in bheads:
5334 elif pnode not in bheads:
5334 # i18n: column positioning for "hg summary"
5335 # i18n: column positioning for "hg summary"
5335 ui.write(_('update: %d new changesets (update)\n') % new)
5336 ui.write(_('update: %d new changesets (update)\n') % new)
5336 else:
5337 else:
5337 # i18n: column positioning for "hg summary"
5338 # i18n: column positioning for "hg summary"
5338 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5339 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5339 (new, len(bheads)))
5340 (new, len(bheads)))
5340
5341
5341 t = []
5342 t = []
5342 draft = len(repo.revs('draft()'))
5343 draft = len(repo.revs('draft()'))
5343 if draft:
5344 if draft:
5344 t.append(_('%d draft') % draft)
5345 t.append(_('%d draft') % draft)
5345 secret = len(repo.revs('secret()'))
5346 secret = len(repo.revs('secret()'))
5346 if secret:
5347 if secret:
5347 t.append(_('%d secret') % secret)
5348 t.append(_('%d secret') % secret)
5348
5349
5349 if draft or secret:
5350 if draft or secret:
5350 ui.status(_('phases: %s\n') % ', '.join(t))
5351 ui.status(_('phases: %s\n') % ', '.join(t))
5351
5352
5352 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5353 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5353 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5354 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5354 numtrouble = len(repo.revs(trouble + "()"))
5355 numtrouble = len(repo.revs(trouble + "()"))
5355 # We write all the possibilities to ease translation
5356 # We write all the possibilities to ease translation
5356 troublemsg = {
5357 troublemsg = {
5357 "orphan": _("orphan: %d changesets"),
5358 "orphan": _("orphan: %d changesets"),
5358 "contentdivergent": _("content-divergent: %d changesets"),
5359 "contentdivergent": _("content-divergent: %d changesets"),
5359 "phasedivergent": _("phase-divergent: %d changesets"),
5360 "phasedivergent": _("phase-divergent: %d changesets"),
5360 }
5361 }
5361 if numtrouble > 0:
5362 if numtrouble > 0:
5362 ui.status(troublemsg[trouble] % numtrouble + "\n")
5363 ui.status(troublemsg[trouble] % numtrouble + "\n")
5363
5364
5364 cmdutil.summaryhooks(ui, repo)
5365 cmdutil.summaryhooks(ui, repo)
5365
5366
5366 if opts.get('remote'):
5367 if opts.get('remote'):
5367 needsincoming, needsoutgoing = True, True
5368 needsincoming, needsoutgoing = True, True
5368 else:
5369 else:
5369 needsincoming, needsoutgoing = False, False
5370 needsincoming, needsoutgoing = False, False
5370 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5371 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5371 if i:
5372 if i:
5372 needsincoming = True
5373 needsincoming = True
5373 if o:
5374 if o:
5374 needsoutgoing = True
5375 needsoutgoing = True
5375 if not needsincoming and not needsoutgoing:
5376 if not needsincoming and not needsoutgoing:
5376 return
5377 return
5377
5378
5378 def getincoming():
5379 def getincoming():
5379 source, branches = hg.parseurl(ui.expandpath('default'))
5380 source, branches = hg.parseurl(ui.expandpath('default'))
5380 sbranch = branches[0]
5381 sbranch = branches[0]
5381 try:
5382 try:
5382 other = hg.peer(repo, {}, source)
5383 other = hg.peer(repo, {}, source)
5383 except error.RepoError:
5384 except error.RepoError:
5384 if opts.get('remote'):
5385 if opts.get('remote'):
5385 raise
5386 raise
5386 return source, sbranch, None, None, None
5387 return source, sbranch, None, None, None
5387 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5388 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5388 if revs:
5389 if revs:
5389 revs = [other.lookup(rev) for rev in revs]
5390 revs = [other.lookup(rev) for rev in revs]
5390 ui.debug('comparing with %s\n' % util.hidepassword(source))
5391 ui.debug('comparing with %s\n' % util.hidepassword(source))
5391 repo.ui.pushbuffer()
5392 repo.ui.pushbuffer()
5392 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5393 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5393 repo.ui.popbuffer()
5394 repo.ui.popbuffer()
5394 return source, sbranch, other, commoninc, commoninc[1]
5395 return source, sbranch, other, commoninc, commoninc[1]
5395
5396
5396 if needsincoming:
5397 if needsincoming:
5397 source, sbranch, sother, commoninc, incoming = getincoming()
5398 source, sbranch, sother, commoninc, incoming = getincoming()
5398 else:
5399 else:
5399 source = sbranch = sother = commoninc = incoming = None
5400 source = sbranch = sother = commoninc = incoming = None
5400
5401
5401 def getoutgoing():
5402 def getoutgoing():
5402 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5403 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5403 dbranch = branches[0]
5404 dbranch = branches[0]
5404 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5405 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5405 if source != dest:
5406 if source != dest:
5406 try:
5407 try:
5407 dother = hg.peer(repo, {}, dest)
5408 dother = hg.peer(repo, {}, dest)
5408 except error.RepoError:
5409 except error.RepoError:
5409 if opts.get('remote'):
5410 if opts.get('remote'):
5410 raise
5411 raise
5411 return dest, dbranch, None, None
5412 return dest, dbranch, None, None
5412 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5413 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5413 elif sother is None:
5414 elif sother is None:
5414 # there is no explicit destination peer, but source one is invalid
5415 # there is no explicit destination peer, but source one is invalid
5415 return dest, dbranch, None, None
5416 return dest, dbranch, None, None
5416 else:
5417 else:
5417 dother = sother
5418 dother = sother
5418 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5419 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5419 common = None
5420 common = None
5420 else:
5421 else:
5421 common = commoninc
5422 common = commoninc
5422 if revs:
5423 if revs:
5423 revs = [repo.lookup(rev) for rev in revs]
5424 revs = [repo.lookup(rev) for rev in revs]
5424 repo.ui.pushbuffer()
5425 repo.ui.pushbuffer()
5425 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5426 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5426 commoninc=common)
5427 commoninc=common)
5427 repo.ui.popbuffer()
5428 repo.ui.popbuffer()
5428 return dest, dbranch, dother, outgoing
5429 return dest, dbranch, dother, outgoing
5429
5430
5430 if needsoutgoing:
5431 if needsoutgoing:
5431 dest, dbranch, dother, outgoing = getoutgoing()
5432 dest, dbranch, dother, outgoing = getoutgoing()
5432 else:
5433 else:
5433 dest = dbranch = dother = outgoing = None
5434 dest = dbranch = dother = outgoing = None
5434
5435
5435 if opts.get('remote'):
5436 if opts.get('remote'):
5436 t = []
5437 t = []
5437 if incoming:
5438 if incoming:
5438 t.append(_('1 or more incoming'))
5439 t.append(_('1 or more incoming'))
5439 o = outgoing.missing
5440 o = outgoing.missing
5440 if o:
5441 if o:
5441 t.append(_('%d outgoing') % len(o))
5442 t.append(_('%d outgoing') % len(o))
5442 other = dother or sother
5443 other = dother or sother
5443 if 'bookmarks' in other.listkeys('namespaces'):
5444 if 'bookmarks' in other.listkeys('namespaces'):
5444 counts = bookmarks.summary(repo, other)
5445 counts = bookmarks.summary(repo, other)
5445 if counts[0] > 0:
5446 if counts[0] > 0:
5446 t.append(_('%d incoming bookmarks') % counts[0])
5447 t.append(_('%d incoming bookmarks') % counts[0])
5447 if counts[1] > 0:
5448 if counts[1] > 0:
5448 t.append(_('%d outgoing bookmarks') % counts[1])
5449 t.append(_('%d outgoing bookmarks') % counts[1])
5449
5450
5450 if t:
5451 if t:
5451 # i18n: column positioning for "hg summary"
5452 # i18n: column positioning for "hg summary"
5452 ui.write(_('remote: %s\n') % (', '.join(t)))
5453 ui.write(_('remote: %s\n') % (', '.join(t)))
5453 else:
5454 else:
5454 # i18n: column positioning for "hg summary"
5455 # i18n: column positioning for "hg summary"
5455 ui.status(_('remote: (synced)\n'))
5456 ui.status(_('remote: (synced)\n'))
5456
5457
5457 cmdutil.summaryremotehooks(ui, repo, opts,
5458 cmdutil.summaryremotehooks(ui, repo, opts,
5458 ((source, sbranch, sother, commoninc),
5459 ((source, sbranch, sother, commoninc),
5459 (dest, dbranch, dother, outgoing)))
5460 (dest, dbranch, dother, outgoing)))
5460
5461
5461 @command('tag',
5462 @command('tag',
5462 [('f', 'force', None, _('force tag')),
5463 [('f', 'force', None, _('force tag')),
5463 ('l', 'local', None, _('make the tag local')),
5464 ('l', 'local', None, _('make the tag local')),
5464 ('r', 'rev', '', _('revision to tag'), _('REV')),
5465 ('r', 'rev', '', _('revision to tag'), _('REV')),
5465 ('', 'remove', None, _('remove a tag')),
5466 ('', 'remove', None, _('remove a tag')),
5466 # -l/--local is already there, commitopts cannot be used
5467 # -l/--local is already there, commitopts cannot be used
5467 ('e', 'edit', None, _('invoke editor on commit messages')),
5468 ('e', 'edit', None, _('invoke editor on commit messages')),
5468 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5469 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5469 ] + commitopts2,
5470 ] + commitopts2,
5470 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5471 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5471 def tag(ui, repo, name1, *names, **opts):
5472 def tag(ui, repo, name1, *names, **opts):
5472 """add one or more tags for the current or given revision
5473 """add one or more tags for the current or given revision
5473
5474
5474 Name a particular revision using <name>.
5475 Name a particular revision using <name>.
5475
5476
5476 Tags are used to name particular revisions of the repository and are
5477 Tags are used to name particular revisions of the repository and are
5477 very useful to compare different revisions, to go back to significant
5478 very useful to compare different revisions, to go back to significant
5478 earlier versions or to mark branch points as releases, etc. Changing
5479 earlier versions or to mark branch points as releases, etc. Changing
5479 an existing tag is normally disallowed; use -f/--force to override.
5480 an existing tag is normally disallowed; use -f/--force to override.
5480
5481
5481 If no revision is given, the parent of the working directory is
5482 If no revision is given, the parent of the working directory is
5482 used.
5483 used.
5483
5484
5484 To facilitate version control, distribution, and merging of tags,
5485 To facilitate version control, distribution, and merging of tags,
5485 they are stored as a file named ".hgtags" which is managed similarly
5486 they are stored as a file named ".hgtags" which is managed similarly
5486 to other project files and can be hand-edited if necessary. This
5487 to other project files and can be hand-edited if necessary. This
5487 also means that tagging creates a new commit. The file
5488 also means that tagging creates a new commit. The file
5488 ".hg/localtags" is used for local tags (not shared among
5489 ".hg/localtags" is used for local tags (not shared among
5489 repositories).
5490 repositories).
5490
5491
5491 Tag commits are usually made at the head of a branch. If the parent
5492 Tag commits are usually made at the head of a branch. If the parent
5492 of the working directory is not a branch head, :hg:`tag` aborts; use
5493 of the working directory is not a branch head, :hg:`tag` aborts; use
5493 -f/--force to force the tag commit to be based on a non-head
5494 -f/--force to force the tag commit to be based on a non-head
5494 changeset.
5495 changeset.
5495
5496
5496 See :hg:`help dates` for a list of formats valid for -d/--date.
5497 See :hg:`help dates` for a list of formats valid for -d/--date.
5497
5498
5498 Since tag names have priority over branch names during revision
5499 Since tag names have priority over branch names during revision
5499 lookup, using an existing branch name as a tag name is discouraged.
5500 lookup, using an existing branch name as a tag name is discouraged.
5500
5501
5501 Returns 0 on success.
5502 Returns 0 on success.
5502 """
5503 """
5503 opts = pycompat.byteskwargs(opts)
5504 opts = pycompat.byteskwargs(opts)
5504 with repo.wlock(), repo.lock():
5505 with repo.wlock(), repo.lock():
5505 rev_ = "."
5506 rev_ = "."
5506 names = [t.strip() for t in (name1,) + names]
5507 names = [t.strip() for t in (name1,) + names]
5507 if len(names) != len(set(names)):
5508 if len(names) != len(set(names)):
5508 raise error.Abort(_('tag names must be unique'))
5509 raise error.Abort(_('tag names must be unique'))
5509 for n in names:
5510 for n in names:
5510 scmutil.checknewlabel(repo, n, 'tag')
5511 scmutil.checknewlabel(repo, n, 'tag')
5511 if not n:
5512 if not n:
5512 raise error.Abort(_('tag names cannot consist entirely of '
5513 raise error.Abort(_('tag names cannot consist entirely of '
5513 'whitespace'))
5514 'whitespace'))
5514 if opts.get('rev') and opts.get('remove'):
5515 if opts.get('rev') and opts.get('remove'):
5515 raise error.Abort(_("--rev and --remove are incompatible"))
5516 raise error.Abort(_("--rev and --remove are incompatible"))
5516 if opts.get('rev'):
5517 if opts.get('rev'):
5517 rev_ = opts['rev']
5518 rev_ = opts['rev']
5518 message = opts.get('message')
5519 message = opts.get('message')
5519 if opts.get('remove'):
5520 if opts.get('remove'):
5520 if opts.get('local'):
5521 if opts.get('local'):
5521 expectedtype = 'local'
5522 expectedtype = 'local'
5522 else:
5523 else:
5523 expectedtype = 'global'
5524 expectedtype = 'global'
5524
5525
5525 for n in names:
5526 for n in names:
5526 if not repo.tagtype(n):
5527 if not repo.tagtype(n):
5527 raise error.Abort(_("tag '%s' does not exist") % n)
5528 raise error.Abort(_("tag '%s' does not exist") % n)
5528 if repo.tagtype(n) != expectedtype:
5529 if repo.tagtype(n) != expectedtype:
5529 if expectedtype == 'global':
5530 if expectedtype == 'global':
5530 raise error.Abort(_("tag '%s' is not a global tag") % n)
5531 raise error.Abort(_("tag '%s' is not a global tag") % n)
5531 else:
5532 else:
5532 raise error.Abort(_("tag '%s' is not a local tag") % n)
5533 raise error.Abort(_("tag '%s' is not a local tag") % n)
5533 rev_ = 'null'
5534 rev_ = 'null'
5534 if not message:
5535 if not message:
5535 # we don't translate commit messages
5536 # we don't translate commit messages
5536 message = 'Removed tag %s' % ', '.join(names)
5537 message = 'Removed tag %s' % ', '.join(names)
5537 elif not opts.get('force'):
5538 elif not opts.get('force'):
5538 for n in names:
5539 for n in names:
5539 if n in repo.tags():
5540 if n in repo.tags():
5540 raise error.Abort(_("tag '%s' already exists "
5541 raise error.Abort(_("tag '%s' already exists "
5541 "(use -f to force)") % n)
5542 "(use -f to force)") % n)
5542 if not opts.get('local'):
5543 if not opts.get('local'):
5543 p1, p2 = repo.dirstate.parents()
5544 p1, p2 = repo.dirstate.parents()
5544 if p2 != nullid:
5545 if p2 != nullid:
5545 raise error.Abort(_('uncommitted merge'))
5546 raise error.Abort(_('uncommitted merge'))
5546 bheads = repo.branchheads()
5547 bheads = repo.branchheads()
5547 if not opts.get('force') and bheads and p1 not in bheads:
5548 if not opts.get('force') and bheads and p1 not in bheads:
5548 raise error.Abort(_('working directory is not at a branch head '
5549 raise error.Abort(_('working directory is not at a branch head '
5549 '(use -f to force)'))
5550 '(use -f to force)'))
5550 node = scmutil.revsingle(repo, rev_).node()
5551 node = scmutil.revsingle(repo, rev_).node()
5551
5552
5552 if not message:
5553 if not message:
5553 # we don't translate commit messages
5554 # we don't translate commit messages
5554 message = ('Added tag %s for changeset %s' %
5555 message = ('Added tag %s for changeset %s' %
5555 (', '.join(names), short(node)))
5556 (', '.join(names), short(node)))
5556
5557
5557 date = opts.get('date')
5558 date = opts.get('date')
5558 if date:
5559 if date:
5559 date = dateutil.parsedate(date)
5560 date = dateutil.parsedate(date)
5560
5561
5561 if opts.get('remove'):
5562 if opts.get('remove'):
5562 editform = 'tag.remove'
5563 editform = 'tag.remove'
5563 else:
5564 else:
5564 editform = 'tag.add'
5565 editform = 'tag.add'
5565 editor = cmdutil.getcommiteditor(editform=editform,
5566 editor = cmdutil.getcommiteditor(editform=editform,
5566 **pycompat.strkwargs(opts))
5567 **pycompat.strkwargs(opts))
5567
5568
5568 # don't allow tagging the null rev
5569 # don't allow tagging the null rev
5569 if (not opts.get('remove') and
5570 if (not opts.get('remove') and
5570 scmutil.revsingle(repo, rev_).rev() == nullrev):
5571 scmutil.revsingle(repo, rev_).rev() == nullrev):
5571 raise error.Abort(_("cannot tag null revision"))
5572 raise error.Abort(_("cannot tag null revision"))
5572
5573
5573 tagsmod.tag(repo, names, node, message, opts.get('local'),
5574 tagsmod.tag(repo, names, node, message, opts.get('local'),
5574 opts.get('user'), date, editor=editor)
5575 opts.get('user'), date, editor=editor)
5575
5576
5576 @command('tags', formatteropts, '', intents={INTENT_READONLY})
5577 @command('tags', formatteropts, '', intents={INTENT_READONLY})
5577 def tags(ui, repo, **opts):
5578 def tags(ui, repo, **opts):
5578 """list repository tags
5579 """list repository tags
5579
5580
5580 This lists both regular and local tags. When the -v/--verbose
5581 This lists both regular and local tags. When the -v/--verbose
5581 switch is used, a third column "local" is printed for local tags.
5582 switch is used, a third column "local" is printed for local tags.
5582 When the -q/--quiet switch is used, only the tag name is printed.
5583 When the -q/--quiet switch is used, only the tag name is printed.
5583
5584
5584 Returns 0 on success.
5585 Returns 0 on success.
5585 """
5586 """
5586
5587
5587 opts = pycompat.byteskwargs(opts)
5588 opts = pycompat.byteskwargs(opts)
5588 ui.pager('tags')
5589 ui.pager('tags')
5589 fm = ui.formatter('tags', opts)
5590 fm = ui.formatter('tags', opts)
5590 contexthint = fm.contexthint('tag rev node type')
5591 contexthint = fm.contexthint('tag rev node type')
5591 hexfunc = fm.hexfunc
5592 hexfunc = fm.hexfunc
5592 tagtype = ""
5593 tagtype = ""
5593
5594
5594 for t, n in reversed(repo.tagslist()):
5595 for t, n in reversed(repo.tagslist()):
5595 hn = hexfunc(n)
5596 hn = hexfunc(n)
5596 label = 'tags.normal'
5597 label = 'tags.normal'
5597 tagtype = ''
5598 tagtype = ''
5598 if repo.tagtype(t) == 'local':
5599 if repo.tagtype(t) == 'local':
5599 label = 'tags.local'
5600 label = 'tags.local'
5600 tagtype = 'local'
5601 tagtype = 'local'
5601
5602
5602 fm.startitem()
5603 fm.startitem()
5603 if 'ctx' in contexthint:
5604 if 'ctx' in contexthint:
5604 fm.context(ctx=repo[n])
5605 fm.context(ctx=repo[n])
5605 fm.write('tag', '%s', t, label=label)
5606 fm.write('tag', '%s', t, label=label)
5606 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5607 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5607 fm.condwrite(not ui.quiet, 'rev node', fmt,
5608 fm.condwrite(not ui.quiet, 'rev node', fmt,
5608 repo.changelog.rev(n), hn, label=label)
5609 repo.changelog.rev(n), hn, label=label)
5609 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5610 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5610 tagtype, label=label)
5611 tagtype, label=label)
5611 fm.plain('\n')
5612 fm.plain('\n')
5612 fm.end()
5613 fm.end()
5613
5614
5614 @command('tip',
5615 @command('tip',
5615 [('p', 'patch', None, _('show patch')),
5616 [('p', 'patch', None, _('show patch')),
5616 ('g', 'git', None, _('use git extended diff format')),
5617 ('g', 'git', None, _('use git extended diff format')),
5617 ] + templateopts,
5618 ] + templateopts,
5618 _('[-p] [-g]'))
5619 _('[-p] [-g]'))
5619 def tip(ui, repo, **opts):
5620 def tip(ui, repo, **opts):
5620 """show the tip revision (DEPRECATED)
5621 """show the tip revision (DEPRECATED)
5621
5622
5622 The tip revision (usually just called the tip) is the changeset
5623 The tip revision (usually just called the tip) is the changeset
5623 most recently added to the repository (and therefore the most
5624 most recently added to the repository (and therefore the most
5624 recently changed head).
5625 recently changed head).
5625
5626
5626 If you have just made a commit, that commit will be the tip. If
5627 If you have just made a commit, that commit will be the tip. If
5627 you have just pulled changes from another repository, the tip of
5628 you have just pulled changes from another repository, the tip of
5628 that repository becomes the current tip. The "tip" tag is special
5629 that repository becomes the current tip. The "tip" tag is special
5629 and cannot be renamed or assigned to a different changeset.
5630 and cannot be renamed or assigned to a different changeset.
5630
5631
5631 This command is deprecated, please use :hg:`heads` instead.
5632 This command is deprecated, please use :hg:`heads` instead.
5632
5633
5633 Returns 0 on success.
5634 Returns 0 on success.
5634 """
5635 """
5635 opts = pycompat.byteskwargs(opts)
5636 opts = pycompat.byteskwargs(opts)
5636 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5637 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5637 displayer.show(repo['tip'])
5638 displayer.show(repo['tip'])
5638 displayer.close()
5639 displayer.close()
5639
5640
5640 @command('unbundle',
5641 @command('unbundle',
5641 [('u', 'update', None,
5642 [('u', 'update', None,
5642 _('update to new branch head if changesets were unbundled'))],
5643 _('update to new branch head if changesets were unbundled'))],
5643 _('[-u] FILE...'))
5644 _('[-u] FILE...'))
5644 def unbundle(ui, repo, fname1, *fnames, **opts):
5645 def unbundle(ui, repo, fname1, *fnames, **opts):
5645 """apply one or more bundle files
5646 """apply one or more bundle files
5646
5647
5647 Apply one or more bundle files generated by :hg:`bundle`.
5648 Apply one or more bundle files generated by :hg:`bundle`.
5648
5649
5649 Returns 0 on success, 1 if an update has unresolved files.
5650 Returns 0 on success, 1 if an update has unresolved files.
5650 """
5651 """
5651 fnames = (fname1,) + fnames
5652 fnames = (fname1,) + fnames
5652
5653
5653 with repo.lock():
5654 with repo.lock():
5654 for fname in fnames:
5655 for fname in fnames:
5655 f = hg.openpath(ui, fname)
5656 f = hg.openpath(ui, fname)
5656 gen = exchange.readbundle(ui, f, fname)
5657 gen = exchange.readbundle(ui, f, fname)
5657 if isinstance(gen, streamclone.streamcloneapplier):
5658 if isinstance(gen, streamclone.streamcloneapplier):
5658 raise error.Abort(
5659 raise error.Abort(
5659 _('packed bundles cannot be applied with '
5660 _('packed bundles cannot be applied with '
5660 '"hg unbundle"'),
5661 '"hg unbundle"'),
5661 hint=_('use "hg debugapplystreamclonebundle"'))
5662 hint=_('use "hg debugapplystreamclonebundle"'))
5662 url = 'bundle:' + fname
5663 url = 'bundle:' + fname
5663 try:
5664 try:
5664 txnname = 'unbundle'
5665 txnname = 'unbundle'
5665 if not isinstance(gen, bundle2.unbundle20):
5666 if not isinstance(gen, bundle2.unbundle20):
5666 txnname = 'unbundle\n%s' % util.hidepassword(url)
5667 txnname = 'unbundle\n%s' % util.hidepassword(url)
5667 with repo.transaction(txnname) as tr:
5668 with repo.transaction(txnname) as tr:
5668 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5669 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5669 url=url)
5670 url=url)
5670 except error.BundleUnknownFeatureError as exc:
5671 except error.BundleUnknownFeatureError as exc:
5671 raise error.Abort(
5672 raise error.Abort(
5672 _('%s: unknown bundle feature, %s') % (fname, exc),
5673 _('%s: unknown bundle feature, %s') % (fname, exc),
5673 hint=_("see https://mercurial-scm.org/"
5674 hint=_("see https://mercurial-scm.org/"
5674 "wiki/BundleFeature for more "
5675 "wiki/BundleFeature for more "
5675 "information"))
5676 "information"))
5676 modheads = bundle2.combinechangegroupresults(op)
5677 modheads = bundle2.combinechangegroupresults(op)
5677
5678
5678 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5679 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5679
5680
5680 @command('^update|up|checkout|co',
5681 @command('^update|up|checkout|co',
5681 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5682 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5682 ('c', 'check', None, _('require clean working directory')),
5683 ('c', 'check', None, _('require clean working directory')),
5683 ('m', 'merge', None, _('merge uncommitted changes')),
5684 ('m', 'merge', None, _('merge uncommitted changes')),
5684 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5685 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5685 ('r', 'rev', '', _('revision'), _('REV'))
5686 ('r', 'rev', '', _('revision'), _('REV'))
5686 ] + mergetoolopts,
5687 ] + mergetoolopts,
5687 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5688 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5688 def update(ui, repo, node=None, **opts):
5689 def update(ui, repo, node=None, **opts):
5689 """update working directory (or switch revisions)
5690 """update working directory (or switch revisions)
5690
5691
5691 Update the repository's working directory to the specified
5692 Update the repository's working directory to the specified
5692 changeset. If no changeset is specified, update to the tip of the
5693 changeset. If no changeset is specified, update to the tip of the
5693 current named branch and move the active bookmark (see :hg:`help
5694 current named branch and move the active bookmark (see :hg:`help
5694 bookmarks`).
5695 bookmarks`).
5695
5696
5696 Update sets the working directory's parent revision to the specified
5697 Update sets the working directory's parent revision to the specified
5697 changeset (see :hg:`help parents`).
5698 changeset (see :hg:`help parents`).
5698
5699
5699 If the changeset is not a descendant or ancestor of the working
5700 If the changeset is not a descendant or ancestor of the working
5700 directory's parent and there are uncommitted changes, the update is
5701 directory's parent and there are uncommitted changes, the update is
5701 aborted. With the -c/--check option, the working directory is checked
5702 aborted. With the -c/--check option, the working directory is checked
5702 for uncommitted changes; if none are found, the working directory is
5703 for uncommitted changes; if none are found, the working directory is
5703 updated to the specified changeset.
5704 updated to the specified changeset.
5704
5705
5705 .. container:: verbose
5706 .. container:: verbose
5706
5707
5707 The -C/--clean, -c/--check, and -m/--merge options control what
5708 The -C/--clean, -c/--check, and -m/--merge options control what
5708 happens if the working directory contains uncommitted changes.
5709 happens if the working directory contains uncommitted changes.
5709 At most of one of them can be specified.
5710 At most of one of them can be specified.
5710
5711
5711 1. If no option is specified, and if
5712 1. If no option is specified, and if
5712 the requested changeset is an ancestor or descendant of
5713 the requested changeset is an ancestor or descendant of
5713 the working directory's parent, the uncommitted changes
5714 the working directory's parent, the uncommitted changes
5714 are merged into the requested changeset and the merged
5715 are merged into the requested changeset and the merged
5715 result is left uncommitted. If the requested changeset is
5716 result is left uncommitted. If the requested changeset is
5716 not an ancestor or descendant (that is, it is on another
5717 not an ancestor or descendant (that is, it is on another
5717 branch), the update is aborted and the uncommitted changes
5718 branch), the update is aborted and the uncommitted changes
5718 are preserved.
5719 are preserved.
5719
5720
5720 2. With the -m/--merge option, the update is allowed even if the
5721 2. With the -m/--merge option, the update is allowed even if the
5721 requested changeset is not an ancestor or descendant of
5722 requested changeset is not an ancestor or descendant of
5722 the working directory's parent.
5723 the working directory's parent.
5723
5724
5724 3. With the -c/--check option, the update is aborted and the
5725 3. With the -c/--check option, the update is aborted and the
5725 uncommitted changes are preserved.
5726 uncommitted changes are preserved.
5726
5727
5727 4. With the -C/--clean option, uncommitted changes are discarded and
5728 4. With the -C/--clean option, uncommitted changes are discarded and
5728 the working directory is updated to the requested changeset.
5729 the working directory is updated to the requested changeset.
5729
5730
5730 To cancel an uncommitted merge (and lose your changes), use
5731 To cancel an uncommitted merge (and lose your changes), use
5731 :hg:`merge --abort`.
5732 :hg:`merge --abort`.
5732
5733
5733 Use null as the changeset to remove the working directory (like
5734 Use null as the changeset to remove the working directory (like
5734 :hg:`clone -U`).
5735 :hg:`clone -U`).
5735
5736
5736 If you want to revert just one file to an older revision, use
5737 If you want to revert just one file to an older revision, use
5737 :hg:`revert [-r REV] NAME`.
5738 :hg:`revert [-r REV] NAME`.
5738
5739
5739 See :hg:`help dates` for a list of formats valid for -d/--date.
5740 See :hg:`help dates` for a list of formats valid for -d/--date.
5740
5741
5741 Returns 0 on success, 1 if there are unresolved files.
5742 Returns 0 on success, 1 if there are unresolved files.
5742 """
5743 """
5743 rev = opts.get(r'rev')
5744 rev = opts.get(r'rev')
5744 date = opts.get(r'date')
5745 date = opts.get(r'date')
5745 clean = opts.get(r'clean')
5746 clean = opts.get(r'clean')
5746 check = opts.get(r'check')
5747 check = opts.get(r'check')
5747 merge = opts.get(r'merge')
5748 merge = opts.get(r'merge')
5748 if rev and node:
5749 if rev and node:
5749 raise error.Abort(_("please specify just one revision"))
5750 raise error.Abort(_("please specify just one revision"))
5750
5751
5751 if ui.configbool('commands', 'update.requiredest'):
5752 if ui.configbool('commands', 'update.requiredest'):
5752 if not node and not rev and not date:
5753 if not node and not rev and not date:
5753 raise error.Abort(_('you must specify a destination'),
5754 raise error.Abort(_('you must specify a destination'),
5754 hint=_('for example: hg update ".::"'))
5755 hint=_('for example: hg update ".::"'))
5755
5756
5756 if rev is None or rev == '':
5757 if rev is None or rev == '':
5757 rev = node
5758 rev = node
5758
5759
5759 if date and rev is not None:
5760 if date and rev is not None:
5760 raise error.Abort(_("you can't specify a revision and a date"))
5761 raise error.Abort(_("you can't specify a revision and a date"))
5761
5762
5762 if len([x for x in (clean, check, merge) if x]) > 1:
5763 if len([x for x in (clean, check, merge) if x]) > 1:
5763 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5764 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5764 "or -m/--merge"))
5765 "or -m/--merge"))
5765
5766
5766 updatecheck = None
5767 updatecheck = None
5767 if check:
5768 if check:
5768 updatecheck = 'abort'
5769 updatecheck = 'abort'
5769 elif merge:
5770 elif merge:
5770 updatecheck = 'none'
5771 updatecheck = 'none'
5771
5772
5772 with repo.wlock():
5773 with repo.wlock():
5773 cmdutil.clearunfinished(repo)
5774 cmdutil.clearunfinished(repo)
5774
5775
5775 if date:
5776 if date:
5776 rev = cmdutil.finddate(ui, repo, date)
5777 rev = cmdutil.finddate(ui, repo, date)
5777
5778
5778 # if we defined a bookmark, we have to remember the original name
5779 # if we defined a bookmark, we have to remember the original name
5779 brev = rev
5780 brev = rev
5780 if rev:
5781 if rev:
5781 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5782 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5782 ctx = scmutil.revsingle(repo, rev, rev)
5783 ctx = scmutil.revsingle(repo, rev, rev)
5783 rev = ctx.rev()
5784 rev = ctx.rev()
5784 hidden = ctx.hidden()
5785 hidden = ctx.hidden()
5785 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
5786 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
5786 with ui.configoverride(overrides, 'update'):
5787 with ui.configoverride(overrides, 'update'):
5787 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
5788 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
5788 updatecheck=updatecheck)
5789 updatecheck=updatecheck)
5789 if hidden:
5790 if hidden:
5790 ctxstr = ctx.hex()[:12]
5791 ctxstr = ctx.hex()[:12]
5791 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
5792 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
5792
5793
5793 if ctx.obsolete():
5794 if ctx.obsolete():
5794 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5795 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5795 ui.warn("(%s)\n" % obsfatemsg)
5796 ui.warn("(%s)\n" % obsfatemsg)
5796 return ret
5797 return ret
5797
5798
5798 @command('verify', [])
5799 @command('verify', [])
5799 def verify(ui, repo):
5800 def verify(ui, repo):
5800 """verify the integrity of the repository
5801 """verify the integrity of the repository
5801
5802
5802 Verify the integrity of the current repository.
5803 Verify the integrity of the current repository.
5803
5804
5804 This will perform an extensive check of the repository's
5805 This will perform an extensive check of the repository's
5805 integrity, validating the hashes and checksums of each entry in
5806 integrity, validating the hashes and checksums of each entry in
5806 the changelog, manifest, and tracked files, as well as the
5807 the changelog, manifest, and tracked files, as well as the
5807 integrity of their crosslinks and indices.
5808 integrity of their crosslinks and indices.
5808
5809
5809 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5810 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5810 for more information about recovery from corruption of the
5811 for more information about recovery from corruption of the
5811 repository.
5812 repository.
5812
5813
5813 Returns 0 on success, 1 if errors are encountered.
5814 Returns 0 on success, 1 if errors are encountered.
5814 """
5815 """
5815 return hg.verify(repo)
5816 return hg.verify(repo)
5816
5817
5817 @command('version', [] + formatteropts, norepo=True,
5818 @command('version', [] + formatteropts, norepo=True,
5818 intents={INTENT_READONLY})
5819 intents={INTENT_READONLY})
5819 def version_(ui, **opts):
5820 def version_(ui, **opts):
5820 """output version and copyright information"""
5821 """output version and copyright information"""
5821 opts = pycompat.byteskwargs(opts)
5822 opts = pycompat.byteskwargs(opts)
5822 if ui.verbose:
5823 if ui.verbose:
5823 ui.pager('version')
5824 ui.pager('version')
5824 fm = ui.formatter("version", opts)
5825 fm = ui.formatter("version", opts)
5825 fm.startitem()
5826 fm.startitem()
5826 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5827 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5827 util.version())
5828 util.version())
5828 license = _(
5829 license = _(
5829 "(see https://mercurial-scm.org for more information)\n"
5830 "(see https://mercurial-scm.org for more information)\n"
5830 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5831 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5831 "This is free software; see the source for copying conditions. "
5832 "This is free software; see the source for copying conditions. "
5832 "There is NO\nwarranty; "
5833 "There is NO\nwarranty; "
5833 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5834 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5834 )
5835 )
5835 if not ui.quiet:
5836 if not ui.quiet:
5836 fm.plain(license)
5837 fm.plain(license)
5837
5838
5838 if ui.verbose:
5839 if ui.verbose:
5839 fm.plain(_("\nEnabled extensions:\n\n"))
5840 fm.plain(_("\nEnabled extensions:\n\n"))
5840 # format names and versions into columns
5841 # format names and versions into columns
5841 names = []
5842 names = []
5842 vers = []
5843 vers = []
5843 isinternals = []
5844 isinternals = []
5844 for name, module in extensions.extensions():
5845 for name, module in extensions.extensions():
5845 names.append(name)
5846 names.append(name)
5846 vers.append(extensions.moduleversion(module) or None)
5847 vers.append(extensions.moduleversion(module) or None)
5847 isinternals.append(extensions.ismoduleinternal(module))
5848 isinternals.append(extensions.ismoduleinternal(module))
5848 fn = fm.nested("extensions", tmpl='{name}\n')
5849 fn = fm.nested("extensions", tmpl='{name}\n')
5849 if names:
5850 if names:
5850 namefmt = " %%-%ds " % max(len(n) for n in names)
5851 namefmt = " %%-%ds " % max(len(n) for n in names)
5851 places = [_("external"), _("internal")]
5852 places = [_("external"), _("internal")]
5852 for n, v, p in zip(names, vers, isinternals):
5853 for n, v, p in zip(names, vers, isinternals):
5853 fn.startitem()
5854 fn.startitem()
5854 fn.condwrite(ui.verbose, "name", namefmt, n)
5855 fn.condwrite(ui.verbose, "name", namefmt, n)
5855 if ui.verbose:
5856 if ui.verbose:
5856 fn.plain("%s " % places[p])
5857 fn.plain("%s " % places[p])
5857 fn.data(bundled=p)
5858 fn.data(bundled=p)
5858 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5859 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5859 if ui.verbose:
5860 if ui.verbose:
5860 fn.plain("\n")
5861 fn.plain("\n")
5861 fn.end()
5862 fn.end()
5862 fm.end()
5863 fm.end()
5863
5864
5864 def loadcmdtable(ui, name, cmdtable):
5865 def loadcmdtable(ui, name, cmdtable):
5865 """Load command functions from specified cmdtable
5866 """Load command functions from specified cmdtable
5866 """
5867 """
5867 overrides = [cmd for cmd in cmdtable if cmd in table]
5868 overrides = [cmd for cmd in cmdtable if cmd in table]
5868 if overrides:
5869 if overrides:
5869 ui.warn(_("extension '%s' overrides commands: %s\n")
5870 ui.warn(_("extension '%s' overrides commands: %s\n")
5870 % (name, " ".join(overrides)))
5871 % (name, " ".join(overrides)))
5871 table.update(cmdtable)
5872 table.update(cmdtable)
@@ -1,493 +1,503 b''
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3 $ echo import > port
3 $ echo import > port
4 $ hg add port
4 $ hg add port
5 $ hg commit -m 0 -u spam -d '0 0'
5 $ hg commit -m 0 -u spam -d '0 0'
6 $ echo export >> port
6 $ echo export >> port
7 $ hg commit -m 1 -u eggs -d '1 0'
7 $ hg commit -m 1 -u eggs -d '1 0'
8 $ echo export > port
8 $ echo export > port
9 $ echo vaportight >> port
9 $ echo vaportight >> port
10 $ echo 'import/export' >> port
10 $ echo 'import/export' >> port
11 $ hg commit -m 2 -u spam -d '2 0'
11 $ hg commit -m 2 -u spam -d '2 0'
12 $ echo 'import/export' >> port
12 $ echo 'import/export' >> port
13 $ hg commit -m 3 -u eggs -d '3 0'
13 $ hg commit -m 3 -u eggs -d '3 0'
14 $ head -n 3 port > port1
14 $ head -n 3 port > port1
15 $ mv port1 port
15 $ mv port1 port
16 $ hg commit -m 4 -u spam -d '4 0'
16 $ hg commit -m 4 -u spam -d '4 0'
17
17
18 pattern error
18 pattern error
19
19
20 $ hg grep '**test**'
20 $ hg grep '**test**'
21 grep: invalid match pattern: nothing to repeat
21 grep: invalid match pattern: nothing to repeat
22 [1]
22 [1]
23
23
24 simple
24 simple
25
25
26 $ hg grep -r tip:0 '.*'
26 $ hg grep -r tip:0 '.*'
27 port:4:export
27 port:4:export
28 port:4:vaportight
28 port:4:vaportight
29 port:4:import/export
29 port:4:import/export
30 $ hg grep -r tip:0 port port
30 $ hg grep -r tip:0 port port
31 port:4:export
31 port:4:export
32 port:4:vaportight
32 port:4:vaportight
33 port:4:import/export
33 port:4:import/export
34
34
35 simple with color
35 simple with color
36
36
37 $ hg --config extensions.color= grep --config color.mode=ansi \
37 $ hg --config extensions.color= grep --config color.mode=ansi \
38 > --color=always port port -r tip:0
38 > --color=always port port -r tip:0
39 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mex\x1b[0;31;1mport\x1b[0m (esc)
39 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mex\x1b[0;31;1mport\x1b[0m (esc)
40 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc)
40 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc)
41 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/ex\x1b[0;31;1mport\x1b[0m (esc)
41 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/ex\x1b[0;31;1mport\x1b[0m (esc)
42
42
43 simple templated
43 simple templated
44
44
45 $ hg grep port -r tip:0 \
45 $ hg grep port -r tip:0 \
46 > -T '{file}:{rev}:{node|short}:{texts % "{if(matched, text|upper, text)}"}\n'
46 > -T '{file}:{rev}:{node|short}:{texts % "{if(matched, text|upper, text)}"}\n'
47 port:4:914fa752cdea:exPORT
47 port:4:914fa752cdea:exPORT
48 port:4:914fa752cdea:vaPORTight
48 port:4:914fa752cdea:vaPORTight
49 port:4:914fa752cdea:imPORT/exPORT
49 port:4:914fa752cdea:imPORT/exPORT
50
50
51 $ hg grep port -r tip:0 -T '{file}:{rev}:{texts}\n'
51 $ hg grep port -r tip:0 -T '{file}:{rev}:{texts}\n'
52 port:4:export
52 port:4:export
53 port:4:vaportight
53 port:4:vaportight
54 port:4:import/export
54 port:4:import/export
55
55
56 $ hg grep port -r tip:0 -T '{file}:{tags}:{texts}\n'
56 $ hg grep port -r tip:0 -T '{file}:{tags}:{texts}\n'
57 port:tip:export
57 port:tip:export
58 port:tip:vaportight
58 port:tip:vaportight
59 port:tip:import/export
59 port:tip:import/export
60
60
61 simple JSON (no "change" field)
61 simple JSON (no "change" field)
62
62
63 $ hg grep -r tip:0 -Tjson port
63 $ hg grep -r tip:0 -Tjson port
64 [
64 [
65 {
65 {
66 "date": [4, 0],
66 "date": [4, 0],
67 "file": "port",
67 "file": "port",
68 "line_number": 1,
68 "line_number": 1,
69 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
69 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
70 "rev": 4,
70 "rev": 4,
71 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
71 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
72 "user": "spam"
72 "user": "spam"
73 },
73 },
74 {
74 {
75 "date": [4, 0],
75 "date": [4, 0],
76 "file": "port",
76 "file": "port",
77 "line_number": 2,
77 "line_number": 2,
78 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
78 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
79 "rev": 4,
79 "rev": 4,
80 "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
80 "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
81 "user": "spam"
81 "user": "spam"
82 },
82 },
83 {
83 {
84 "date": [4, 0],
84 "date": [4, 0],
85 "file": "port",
85 "file": "port",
86 "line_number": 3,
86 "line_number": 3,
87 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
87 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
88 "rev": 4,
88 "rev": 4,
89 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
89 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
90 "user": "spam"
90 "user": "spam"
91 }
91 }
92 ]
92 ]
93
93
94 simple JSON without matching lines
94 simple JSON without matching lines
95
95
96 $ hg grep -r tip:0 -Tjson -l port
96 $ hg grep -r tip:0 -Tjson -l port
97 [
97 [
98 {
98 {
99 "date": [4, 0],
99 "date": [4, 0],
100 "file": "port",
100 "file": "port",
101 "line_number": 1,
101 "line_number": 1,
102 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
102 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
103 "rev": 4,
103 "rev": 4,
104 "user": "spam"
104 "user": "spam"
105 }
105 }
106 ]
106 ]
107
107
108 all
108 all
109
109
110 $ hg grep --traceback --all -nu port port
110 $ hg grep --traceback --all -nu port port
111 port:4:4:-:spam:import/export
111 port:4:4:-:spam:import/export
112 port:3:4:+:eggs:import/export
112 port:3:4:+:eggs:import/export
113 port:2:1:-:spam:import
113 port:2:1:-:spam:import
114 port:2:2:-:spam:export
114 port:2:2:-:spam:export
115 port:2:1:+:spam:export
115 port:2:1:+:spam:export
116 port:2:2:+:spam:vaportight
116 port:2:2:+:spam:vaportight
117 port:2:3:+:spam:import/export
117 port:2:3:+:spam:import/export
118 port:1:2:+:eggs:export
118 port:1:2:+:eggs:export
119 port:0:1:+:spam:import
119 port:0:1:+:spam:import
120
120
121 all JSON
121 all JSON
122
122
123 $ hg grep --all -Tjson port port
123 $ hg grep --all -Tjson port port
124 [
124 [
125 {
125 {
126 "change": "-",
126 "change": "-",
127 "date": [4, 0],
127 "date": [4, 0],
128 "file": "port",
128 "file": "port",
129 "line_number": 4,
129 "line_number": 4,
130 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
130 "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c",
131 "rev": 4,
131 "rev": 4,
132 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
132 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
133 "user": "spam"
133 "user": "spam"
134 },
134 },
135 {
135 {
136 "change": "+",
136 "change": "+",
137 "date": [3, 0],
137 "date": [3, 0],
138 "file": "port",
138 "file": "port",
139 "line_number": 4,
139 "line_number": 4,
140 "node": "95040cfd017d658c536071c6290230a613c4c2a6",
140 "node": "95040cfd017d658c536071c6290230a613c4c2a6",
141 "rev": 3,
141 "rev": 3,
142 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
142 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
143 "user": "eggs"
143 "user": "eggs"
144 },
144 },
145 {
145 {
146 "change": "-",
146 "change": "-",
147 "date": [2, 0],
147 "date": [2, 0],
148 "file": "port",
148 "file": "port",
149 "line_number": 1,
149 "line_number": 1,
150 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
150 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
151 "rev": 2,
151 "rev": 2,
152 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
152 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
153 "user": "spam"
153 "user": "spam"
154 },
154 },
155 {
155 {
156 "change": "-",
156 "change": "-",
157 "date": [2, 0],
157 "date": [2, 0],
158 "file": "port",
158 "file": "port",
159 "line_number": 2,
159 "line_number": 2,
160 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
160 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
161 "rev": 2,
161 "rev": 2,
162 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
162 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
163 "user": "spam"
163 "user": "spam"
164 },
164 },
165 {
165 {
166 "change": "+",
166 "change": "+",
167 "date": [2, 0],
167 "date": [2, 0],
168 "file": "port",
168 "file": "port",
169 "line_number": 1,
169 "line_number": 1,
170 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
170 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
171 "rev": 2,
171 "rev": 2,
172 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
172 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
173 "user": "spam"
173 "user": "spam"
174 },
174 },
175 {
175 {
176 "change": "+",
176 "change": "+",
177 "date": [2, 0],
177 "date": [2, 0],
178 "file": "port",
178 "file": "port",
179 "line_number": 2,
179 "line_number": 2,
180 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
180 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
181 "rev": 2,
181 "rev": 2,
182 "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
182 "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}],
183 "user": "spam"
183 "user": "spam"
184 },
184 },
185 {
185 {
186 "change": "+",
186 "change": "+",
187 "date": [2, 0],
187 "date": [2, 0],
188 "file": "port",
188 "file": "port",
189 "line_number": 3,
189 "line_number": 3,
190 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
190 "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47",
191 "rev": 2,
191 "rev": 2,
192 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
192 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}],
193 "user": "spam"
193 "user": "spam"
194 },
194 },
195 {
195 {
196 "change": "+",
196 "change": "+",
197 "date": [1, 0],
197 "date": [1, 0],
198 "file": "port",
198 "file": "port",
199 "line_number": 2,
199 "line_number": 2,
200 "node": "8b20f75c158513ff5ac80bd0e5219bfb6f0eb587",
200 "node": "8b20f75c158513ff5ac80bd0e5219bfb6f0eb587",
201 "rev": 1,
201 "rev": 1,
202 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
202 "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}],
203 "user": "eggs"
203 "user": "eggs"
204 },
204 },
205 {
205 {
206 "change": "+",
206 "change": "+",
207 "date": [0, 0],
207 "date": [0, 0],
208 "file": "port",
208 "file": "port",
209 "line_number": 1,
209 "line_number": 1,
210 "node": "f31323c9217050ba245ee8b537c713ec2e8ab226",
210 "node": "f31323c9217050ba245ee8b537c713ec2e8ab226",
211 "rev": 0,
211 "rev": 0,
212 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
212 "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}],
213 "user": "spam"
213 "user": "spam"
214 }
214 }
215 ]
215 ]
216
216
217 other
217 other
218
218
219 $ hg grep -r tip:0 -l port port
219 $ hg grep -r tip:0 -l port port
220 port:4
220 port:4
221 $ hg grep -r tip:0 import port
221 $ hg grep -r tip:0 import port
222 port:4:import/export
222 port:4:import/export
223
223
224 $ hg cp port port2
224 $ hg cp port port2
225 $ hg commit -m 4 -u spam -d '5 0'
225 $ hg commit -m 4 -u spam -d '5 0'
226
226
227 follow
227 follow
228
228
229 $ hg grep -r tip:0 --traceback -f 'import\n\Z' port2
229 $ hg grep -r tip:0 --traceback -f 'import\n\Z' port2
230 port:0:import
230 port:0:import
231
231
232 $ echo deport >> port2
232 $ echo deport >> port2
233 $ hg commit -m 5 -u eggs -d '6 0'
233 $ hg commit -m 5 -u eggs -d '6 0'
234 $ hg grep -f --all -nu port port2
234 $ hg grep -f --all -nu port port2
235 port2:6:4:+:eggs:deport
235 port2:6:4:+:eggs:deport
236 port:4:4:-:spam:import/export
236 port:4:4:-:spam:import/export
237 port:3:4:+:eggs:import/export
237 port:3:4:+:eggs:import/export
238 port:2:1:-:spam:import
238 port:2:1:-:spam:import
239 port:2:2:-:spam:export
239 port:2:2:-:spam:export
240 port:2:1:+:spam:export
240 port:2:1:+:spam:export
241 port:2:2:+:spam:vaportight
241 port:2:2:+:spam:vaportight
242 port:2:3:+:spam:import/export
242 port:2:3:+:spam:import/export
243 port:1:2:+:eggs:export
243 port:1:2:+:eggs:export
244 port:0:1:+:spam:import
244 port:0:1:+:spam:import
245
245
246 $ hg up -q null
246 $ hg up -q null
247 $ hg grep -r 'reverse(:.)' -f port
247 $ hg grep -r 'reverse(:.)' -f port
248 port:0:import
248 port:0:import
249
249
250 Test wdir
250 Test wdir
251 (at least, this shouldn't crash)
251 (at least, this shouldn't crash)
252
252
253 $ hg up -q
253 $ hg up -q
254 $ echo wport >> port2
254 $ echo wport >> port2
255 $ hg stat
255 $ hg stat
256 M port2
256 M port2
257 $ hg grep -r 'wdir()' port
257 $ hg grep -r 'wdir()' port
258 port2:2147483647:export
258 port2:2147483647:export
259 port2:2147483647:vaportight
259 port2:2147483647:vaportight
260 port2:2147483647:import/export
260 port2:2147483647:import/export
261 port2:2147483647:deport
261 port2:2147483647:deport
262 port2:2147483647:wport
262 port2:2147483647:wport
263
263
264 $ cd ..
264 $ cd ..
265 $ hg init t2
265 $ hg init t2
266 $ cd t2
266 $ cd t2
267 $ hg grep -r tip:0 foobar foo
267 $ hg grep -r tip:0 foobar foo
268 [1]
268 [1]
269 $ hg grep -r tip:0 foobar
269 $ hg grep -r tip:0 foobar
270 [1]
270 [1]
271 $ echo blue >> color
271 $ echo blue >> color
272 $ echo black >> color
272 $ echo black >> color
273 $ hg add color
273 $ hg add color
274 $ hg ci -m 0
274 $ hg ci -m 0
275 $ echo orange >> color
275 $ echo orange >> color
276 $ hg ci -m 1
276 $ hg ci -m 1
277 $ echo black > color
277 $ echo black > color
278 $ hg ci -m 2
278 $ hg ci -m 2
279 $ echo orange >> color
279 $ echo orange >> color
280 $ echo blue >> color
280 $ echo blue >> color
281 $ hg ci -m 3
281 $ hg ci -m 3
282 $ hg grep -r tip:0 orange
282 $ hg grep -r tip:0 orange
283 color:3:orange
283 color:3:orange
284 $ hg grep --all orange
284 $ hg grep --all orange
285 color:3:+:orange
285 color:3:+:orange
286 color:2:-:orange
286 color:2:-:orange
287 color:1:+:orange
287 color:1:+:orange
288
288
289 $ hg grep --diff orange
289 $ hg grep --diff orange
290 color:3:+:orange
290 color:3:+:orange
291 color:2:-:orange
291 color:2:-:orange
292 color:1:+:orange
292 color:1:+:orange
293
293
294 test substring match: '^' should only match at the beginning
294 test substring match: '^' should only match at the beginning
295
295
296 $ hg grep -r tip:0 '^.' --config extensions.color= --color debug
296 $ hg grep -r tip:0 '^.' --config extensions.color= --color debug
297 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lack
297 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lack
298 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|o]range
298 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|o]range
299 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lue
299 [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lue
300
300
301 match in last "line" without newline
301 match in last "line" without newline
302
302
303 $ $PYTHON -c 'fp = open("noeol", "wb"); fp.write(b"no infinite loop"); fp.close();'
303 $ $PYTHON -c 'fp = open("noeol", "wb"); fp.write(b"no infinite loop"); fp.close();'
304 $ hg ci -Amnoeol
304 $ hg ci -Amnoeol
305 adding noeol
305 adding noeol
306 $ hg grep -r tip:0 loop
306 $ hg grep -r tip:0 loop
307 noeol:4:no infinite loop
307 noeol:4:no infinite loop
308
308
309 $ cd ..
309 $ cd ..
310
310
311 Issue685: traceback in grep -r after rename
311 Issue685: traceback in grep -r after rename
312
312
313 Got a traceback when using grep on a single
313 Got a traceback when using grep on a single
314 revision with renamed files.
314 revision with renamed files.
315
315
316 $ hg init issue685
316 $ hg init issue685
317 $ cd issue685
317 $ cd issue685
318 $ echo octarine > color
318 $ echo octarine > color
319 $ hg ci -Amcolor
319 $ hg ci -Amcolor
320 adding color
320 adding color
321 $ hg rename color colour
321 $ hg rename color colour
322 $ hg ci -Am rename
322 $ hg ci -Am rename
323 $ hg grep -r tip:0 octarine
323 $ hg grep -r tip:0 octarine
324 colour:1:octarine
324 colour:1:octarine
325 color:0:octarine
325 color:0:octarine
326
326
327 Used to crash here
327 Used to crash here
328
328
329 $ hg grep -r 1 octarine
329 $ hg grep -r 1 octarine
330 colour:1:octarine
330 colour:1:octarine
331 $ cd ..
331 $ cd ..
332
332
333
333
334 Issue337: test that grep follows parent-child relationships instead
334 Issue337: test that grep follows parent-child relationships instead
335 of just using revision numbers.
335 of just using revision numbers.
336
336
337 $ hg init issue337
337 $ hg init issue337
338 $ cd issue337
338 $ cd issue337
339
339
340 $ echo white > color
340 $ echo white > color
341 $ hg commit -A -m "0 white"
341 $ hg commit -A -m "0 white"
342 adding color
342 adding color
343
343
344 $ echo red > color
344 $ echo red > color
345 $ hg commit -A -m "1 red"
345 $ hg commit -A -m "1 red"
346
346
347 $ hg update 0
347 $ hg update 0
348 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 $ echo black > color
349 $ echo black > color
350 $ hg commit -A -m "2 black"
350 $ hg commit -A -m "2 black"
351 created new head
351 created new head
352
352
353 $ hg update --clean 1
353 $ hg update --clean 1
354 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 $ echo blue > color
355 $ echo blue > color
356 $ hg commit -A -m "3 blue"
356 $ hg commit -A -m "3 blue"
357
357
358 $ hg grep --all red
358 $ hg grep --all red
359 color:3:-:red
359 color:3:-:red
360 color:1:+:red
360 color:1:+:red
361
361
362 $ hg grep --diff red
362 $ hg grep --diff red
363 color:3:-:red
363 color:3:-:red
364 color:1:+:red
364 color:1:+:red
365
365
366 Issue3885: test that changing revision order does not alter the
366 Issue3885: test that changing revision order does not alter the
367 revisions printed, just their order.
367 revisions printed, just their order.
368
368
369 $ hg grep --all red -r "all()"
369 $ hg grep --all red -r "all()"
370 color:1:+:red
370 color:1:+:red
371 color:3:-:red
371 color:3:-:red
372
372
373 $ hg grep --all red -r "reverse(all())"
373 $ hg grep --all red -r "reverse(all())"
374 color:3:-:red
374 color:3:-:red
375 color:1:+:red
375 color:1:+:red
376
376
377 $ hg grep --diff red -r "all()"
377 $ hg grep --diff red -r "all()"
378 color:1:+:red
378 color:1:+:red
379 color:3:-:red
379 color:3:-:red
380
380
381 $ hg grep --diff red -r "reverse(all())"
381 $ hg grep --diff red -r "reverse(all())"
382 color:3:-:red
382 color:3:-:red
383 color:1:+:red
383 color:1:+:red
384
384
385 $ cd ..
385 $ cd ..
386
386
387 $ hg init a
387 $ hg init a
388 $ cd a
388 $ cd a
389 $ cp "$TESTDIR/binfile.bin" .
389 $ cp "$TESTDIR/binfile.bin" .
390 $ hg add binfile.bin
390 $ hg add binfile.bin
391 $ hg ci -m 'add binfile.bin'
391 $ hg ci -m 'add binfile.bin'
392 $ hg grep "MaCam" --all
392 $ hg grep "MaCam" --all
393 binfile.bin:0:+: Binary file matches
393 binfile.bin:0:+: Binary file matches
394
394
395 $ hg grep "MaCam" --diff
395 $ hg grep "MaCam" --diff
396 binfile.bin:0:+: Binary file matches
396 binfile.bin:0:+: Binary file matches
397
397
398 $ cd ..
398 $ cd ..
399
399
400 Test for showing working of allfiles flag
400 Test for showing working of allfiles flag
401
401
402 $ hg init sng
402 $ hg init sng
403 $ cd sng
403 $ cd sng
404 $ echo "unmod" >> um
404 $ echo "unmod" >> um
405 $ hg ci -A -m "adds unmod to um"
405 $ hg ci -A -m "adds unmod to um"
406 adding um
406 adding um
407 $ echo "something else" >> new
407 $ echo "something else" >> new
408 $ hg ci -A -m "second commit"
408 $ hg ci -A -m "second commit"
409 adding new
409 adding new
410 $ hg grep -r "." "unmod"
410 $ hg grep -r "." "unmod"
411 [1]
411 [1]
412 $ hg grep -r "." "unmod" --all-files
412 $ hg grep -r "." "unmod" --all-files
413 um:1:unmod
413 um:1:unmod
414
414
415 With --all-files, the working directory is searched by default
415 With --all-files, the working directory is searched by default
416
416
417 $ echo modified >> new
417 $ echo modified >> new
418 $ hg grep --all-files mod
418 $ hg grep --all-files mod
419 new:modified
419 new:modified
420 um:unmod
420 um:unmod
421
421
422 which can be overridden by -rREV
422 which can be overridden by -rREV
423
423
424 $ hg grep --all-files -r. mod
424 $ hg grep --all-files -r. mod
425 um:1:unmod
425 um:1:unmod
426
426
427 commands.all-files can be negated by --no-all-files
427 commands.all-files can be negated by --no-all-files
428
428
429 $ hg grep --config commands.grep.all-files=True mod
429 $ hg grep --config commands.grep.all-files=True mod
430 new:modified
430 new:modified
431 um:unmod
431 um:unmod
432 $ hg grep --config commands.grep.all-files=True --no-all-files mod
432 $ hg grep --config commands.grep.all-files=True --no-all-files mod
433 um:0:unmod
433 um:0:unmod
434
434
435 --diff --all-files makes no sense since --diff is the option to grep history
435 --diff --all-files makes no sense since --diff is the option to grep history
436
436
437 $ hg grep --diff --all-files um
437 $ hg grep --diff --all-files um
438 abort: --diff and --all-files are mutually exclusive
438 abort: --diff and --all-files are mutually exclusive
439 [255]
439 [255]
440
440
441 but --diff should precede the commands.grep.all-files option
441 but --diff should precede the commands.grep.all-files option
442
442
443 $ hg grep --config commands.grep.all-files=True --diff mod
443 $ hg grep --config commands.grep.all-files=True --diff mod
444 um:0:+:unmod
444 um:0:+:unmod
445
445
446 $ cd ..
446 $ cd ..
447
447
448 Fix_Wdir(): test that passing wdir() t -r flag does greps on the
448 Fix_Wdir(): test that passing wdir() t -r flag does greps on the
449 files modified in the working directory
449 files modified in the working directory
450
450
451 $ cd a
451 $ cd a
452 $ echo "abracadara" >> a
452 $ echo "abracadara" >> a
453 $ hg add a
453 $ hg add a
454 $ hg grep -r "wdir()" "abra"
454 $ hg grep -r "wdir()" "abra"
455 a:2147483647:abracadara
455 a:2147483647:abracadara
456
456
457 $ cd ..
457 $ cd ..
458
458
459 Change Default of grep by ui.tweakdefaults, that is, the files not in current
459 Change Default of grep by ui.tweakdefaults, that is, the files not in current
460 working directory should not be grepp-ed on
460 working directory should not be grepp-ed on
461
461
462 $ hg init ab
462 $ hg init ab
463 $ cd ab
463 $ cd ab
464 $ cat <<'EOF' >> .hg/hgrc
464 $ cat <<'EOF' >> .hg/hgrc
465 > [ui]
465 > [ui]
466 > tweakdefaults = True
466 > tweakdefaults = True
467 > EOF
467 > EOF
468 $ echo "some text">>file1
468 $ echo "some text">>file1
469 $ hg add file1
469 $ hg add file1
470 $ hg commit -m "adds file1"
470 $ hg commit -m "adds file1"
471 $ hg mv file1 file2
471 $ hg mv file1 file2
472
472
473 wdir revision is hidden by default:
473 wdir revision is hidden by default:
474
474
475 $ hg grep "some"
475 $ hg grep "some"
476 file2:some text
476 file2:some text
477
477
478 but it should be available in template dict:
478 but it should be available in template dict:
479
479
480 $ hg grep "some" -Tjson
480 $ hg grep "some" -Tjson
481 [
481 [
482 {
482 {
483 "date": [0, 0],
483 "date": [0, 0],
484 "file": "file2",
484 "file": "file2",
485 "line_number": 1,
485 "line_number": 1,
486 "node": "ffffffffffffffffffffffffffffffffffffffff",
486 "node": "ffffffffffffffffffffffffffffffffffffffff",
487 "rev": 2147483647,
487 "rev": 2147483647,
488 "texts": [{"matched": true, "text": "some"}, {"matched": false, "text": " text"}],
488 "texts": [{"matched": true, "text": "some"}, {"matched": false, "text": " text"}],
489 "user": "test"
489 "user": "test"
490 }
490 }
491 ]
491 ]
492
492
493 $ cd ..
493 $ cd ..
494
495 test -rMULTIREV with --all-files
496
497 $ cd sng
498 $ hg rm um
499 $ hg commit -m "deletes um"
500 $ hg grep -r "0:2" "unmod" --all-files
501 um:0:unmod
502 um:1:unmod
503 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now