##// END OF EJS Templates
copy: add option to unmark file as copied...
Martin von Zweigbergk -
r44844:8be0c635 default
parent child Browse files
Show More
@@ -1,4075 +1,4096 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 copy as copymod
10 import copy as copymod
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from .pycompat import (
22 from .pycompat import (
23 getattr,
23 getattr,
24 open,
24 open,
25 setattr,
25 setattr,
26 )
26 )
27 from .thirdparty import attr
27 from .thirdparty import attr
28
28
29 from . import (
29 from . import (
30 bookmarks,
30 bookmarks,
31 changelog,
31 changelog,
32 copies,
32 copies,
33 crecord as crecordmod,
33 crecord as crecordmod,
34 dirstateguard,
34 dirstateguard,
35 encoding,
35 encoding,
36 error,
36 error,
37 formatter,
37 formatter,
38 logcmdutil,
38 logcmdutil,
39 match as matchmod,
39 match as matchmod,
40 merge as mergemod,
40 merge as mergemod,
41 mergeutil,
41 mergeutil,
42 obsolete,
42 obsolete,
43 patch,
43 patch,
44 pathutil,
44 pathutil,
45 phases,
45 phases,
46 pycompat,
46 pycompat,
47 repair,
47 repair,
48 revlog,
48 revlog,
49 rewriteutil,
49 rewriteutil,
50 scmutil,
50 scmutil,
51 smartset,
51 smartset,
52 state as statemod,
52 state as statemod,
53 subrepoutil,
53 subrepoutil,
54 templatekw,
54 templatekw,
55 templater,
55 templater,
56 util,
56 util,
57 vfs as vfsmod,
57 vfs as vfsmod,
58 )
58 )
59
59
60 from .utils import (
60 from .utils import (
61 dateutil,
61 dateutil,
62 stringutil,
62 stringutil,
63 )
63 )
64
64
65 if pycompat.TYPE_CHECKING:
65 if pycompat.TYPE_CHECKING:
66 from typing import (
66 from typing import (
67 Any,
67 Any,
68 Dict,
68 Dict,
69 )
69 )
70
70
71 for t in (Any, Dict):
71 for t in (Any, Dict):
72 assert t
72 assert t
73
73
74 stringio = util.stringio
74 stringio = util.stringio
75
75
76 # templates of common command options
76 # templates of common command options
77
77
78 dryrunopts = [
78 dryrunopts = [
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
80 ]
80 ]
81
81
82 confirmopts = [
82 confirmopts = [
83 (b'', b'confirm', None, _(b'ask before applying actions')),
83 (b'', b'confirm', None, _(b'ask before applying actions')),
84 ]
84 ]
85
85
86 remoteopts = [
86 remoteopts = [
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
88 (
88 (
89 b'',
89 b'',
90 b'remotecmd',
90 b'remotecmd',
91 b'',
91 b'',
92 _(b'specify hg command to run on the remote side'),
92 _(b'specify hg command to run on the remote side'),
93 _(b'CMD'),
93 _(b'CMD'),
94 ),
94 ),
95 (
95 (
96 b'',
96 b'',
97 b'insecure',
97 b'insecure',
98 None,
98 None,
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 _(b'do not verify server certificate (ignoring web.cacerts config)'),
100 ),
100 ),
101 ]
101 ]
102
102
103 walkopts = [
103 walkopts = [
104 (
104 (
105 b'I',
105 b'I',
106 b'include',
106 b'include',
107 [],
107 [],
108 _(b'include names matching the given patterns'),
108 _(b'include names matching the given patterns'),
109 _(b'PATTERN'),
109 _(b'PATTERN'),
110 ),
110 ),
111 (
111 (
112 b'X',
112 b'X',
113 b'exclude',
113 b'exclude',
114 [],
114 [],
115 _(b'exclude names matching the given patterns'),
115 _(b'exclude names matching the given patterns'),
116 _(b'PATTERN'),
116 _(b'PATTERN'),
117 ),
117 ),
118 ]
118 ]
119
119
120 commitopts = [
120 commitopts = [
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
123 ]
123 ]
124
124
125 commitopts2 = [
125 commitopts2 = [
126 (
126 (
127 b'd',
127 b'd',
128 b'date',
128 b'date',
129 b'',
129 b'',
130 _(b'record the specified date as commit date'),
130 _(b'record the specified date as commit date'),
131 _(b'DATE'),
131 _(b'DATE'),
132 ),
132 ),
133 (
133 (
134 b'u',
134 b'u',
135 b'user',
135 b'user',
136 b'',
136 b'',
137 _(b'record the specified user as committer'),
137 _(b'record the specified user as committer'),
138 _(b'USER'),
138 _(b'USER'),
139 ),
139 ),
140 ]
140 ]
141
141
142 commitopts3 = [
142 commitopts3 = [
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 (b'U', b'currentuser', None, _(b'record the current user as committer')),
145 ]
145 ]
146
146
147 formatteropts = [
147 formatteropts = [
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
149 ]
149 ]
150
150
151 templateopts = [
151 templateopts = [
152 (
152 (
153 b'',
153 b'',
154 b'style',
154 b'style',
155 b'',
155 b'',
156 _(b'display using template map file (DEPRECATED)'),
156 _(b'display using template map file (DEPRECATED)'),
157 _(b'STYLE'),
157 _(b'STYLE'),
158 ),
158 ),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
160 ]
160 ]
161
161
162 logopts = [
162 logopts = [
163 (b'p', b'patch', None, _(b'show patch')),
163 (b'p', b'patch', None, _(b'show patch')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
164 (b'g', b'git', None, _(b'use git extended diff format')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
166 (b'M', b'no-merges', None, _(b'do not show merges')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
168 (b'G', b'graph', None, _(b"show the revision DAG")),
169 ] + templateopts
169 ] + templateopts
170
170
171 diffopts = [
171 diffopts = [
172 (b'a', b'text', None, _(b'treat all files as text')),
172 (b'a', b'text', None, _(b'treat all files as text')),
173 (
173 (
174 b'g',
174 b'g',
175 b'git',
175 b'git',
176 None,
176 None,
177 _(b'use git extended diff format (DEFAULT: diff.git)'),
177 _(b'use git extended diff format (DEFAULT: diff.git)'),
178 ),
178 ),
179 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
179 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
180 (b'', b'nodates', None, _(b'omit dates from diff headers')),
180 (b'', b'nodates', None, _(b'omit dates from diff headers')),
181 ]
181 ]
182
182
183 diffwsopts = [
183 diffwsopts = [
184 (
184 (
185 b'w',
185 b'w',
186 b'ignore-all-space',
186 b'ignore-all-space',
187 None,
187 None,
188 _(b'ignore white space when comparing lines'),
188 _(b'ignore white space when comparing lines'),
189 ),
189 ),
190 (
190 (
191 b'b',
191 b'b',
192 b'ignore-space-change',
192 b'ignore-space-change',
193 None,
193 None,
194 _(b'ignore changes in the amount of white space'),
194 _(b'ignore changes in the amount of white space'),
195 ),
195 ),
196 (
196 (
197 b'B',
197 b'B',
198 b'ignore-blank-lines',
198 b'ignore-blank-lines',
199 None,
199 None,
200 _(b'ignore changes whose lines are all blank'),
200 _(b'ignore changes whose lines are all blank'),
201 ),
201 ),
202 (
202 (
203 b'Z',
203 b'Z',
204 b'ignore-space-at-eol',
204 b'ignore-space-at-eol',
205 None,
205 None,
206 _(b'ignore changes in whitespace at EOL'),
206 _(b'ignore changes in whitespace at EOL'),
207 ),
207 ),
208 ]
208 ]
209
209
210 diffopts2 = (
210 diffopts2 = (
211 [
211 [
212 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
212 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
213 (
213 (
214 b'p',
214 b'p',
215 b'show-function',
215 b'show-function',
216 None,
216 None,
217 _(
217 _(
218 b'show which function each change is in (DEFAULT: diff.showfunc)'
218 b'show which function each change is in (DEFAULT: diff.showfunc)'
219 ),
219 ),
220 ),
220 ),
221 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
221 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
222 ]
222 ]
223 + diffwsopts
223 + diffwsopts
224 + [
224 + [
225 (
225 (
226 b'U',
226 b'U',
227 b'unified',
227 b'unified',
228 b'',
228 b'',
229 _(b'number of lines of context to show'),
229 _(b'number of lines of context to show'),
230 _(b'NUM'),
230 _(b'NUM'),
231 ),
231 ),
232 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
232 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
233 (
233 (
234 b'',
234 b'',
235 b'root',
235 b'root',
236 b'',
236 b'',
237 _(b'produce diffs relative to subdirectory'),
237 _(b'produce diffs relative to subdirectory'),
238 _(b'DIR'),
238 _(b'DIR'),
239 ),
239 ),
240 ]
240 ]
241 )
241 )
242
242
243 mergetoolopts = [
243 mergetoolopts = [
244 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
244 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
245 ]
245 ]
246
246
247 similarityopts = [
247 similarityopts = [
248 (
248 (
249 b's',
249 b's',
250 b'similarity',
250 b'similarity',
251 b'',
251 b'',
252 _(b'guess renamed files by similarity (0<=s<=100)'),
252 _(b'guess renamed files by similarity (0<=s<=100)'),
253 _(b'SIMILARITY'),
253 _(b'SIMILARITY'),
254 )
254 )
255 ]
255 ]
256
256
257 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
257 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
258
258
259 debugrevlogopts = [
259 debugrevlogopts = [
260 (b'c', b'changelog', False, _(b'open changelog')),
260 (b'c', b'changelog', False, _(b'open changelog')),
261 (b'm', b'manifest', False, _(b'open manifest')),
261 (b'm', b'manifest', False, _(b'open manifest')),
262 (b'', b'dir', b'', _(b'open directory manifest')),
262 (b'', b'dir', b'', _(b'open directory manifest')),
263 ]
263 ]
264
264
265 # special string such that everything below this line will be ingored in the
265 # special string such that everything below this line will be ingored in the
266 # editor text
266 # editor text
267 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
267 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
268
268
269
269
270 def check_at_most_one_arg(opts, *args):
270 def check_at_most_one_arg(opts, *args):
271 """abort if more than one of the arguments are in opts
271 """abort if more than one of the arguments are in opts
272
272
273 Returns the unique argument or None if none of them were specified.
273 Returns the unique argument or None if none of them were specified.
274 """
274 """
275
275
276 def to_display(name):
276 def to_display(name):
277 return pycompat.sysbytes(name).replace(b'_', b'-')
277 return pycompat.sysbytes(name).replace(b'_', b'-')
278
278
279 previous = None
279 previous = None
280 for x in args:
280 for x in args:
281 if opts.get(x):
281 if opts.get(x):
282 if previous:
282 if previous:
283 raise error.Abort(
283 raise error.Abort(
284 _(b'cannot specify both --%s and --%s')
284 _(b'cannot specify both --%s and --%s')
285 % (to_display(previous), to_display(x))
285 % (to_display(previous), to_display(x))
286 )
286 )
287 previous = x
287 previous = x
288 return previous
288 return previous
289
289
290
290
291 def check_incompatible_arguments(opts, first, others):
291 def check_incompatible_arguments(opts, first, others):
292 """abort if the first argument is given along with any of the others
292 """abort if the first argument is given along with any of the others
293
293
294 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
294 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
295 among themselves, and they're passed as a single collection.
295 among themselves, and they're passed as a single collection.
296 """
296 """
297 for other in others:
297 for other in others:
298 check_at_most_one_arg(opts, first, other)
298 check_at_most_one_arg(opts, first, other)
299
299
300
300
301 def resolvecommitoptions(ui, opts):
301 def resolvecommitoptions(ui, opts):
302 """modify commit options dict to handle related options
302 """modify commit options dict to handle related options
303
303
304 The return value indicates that ``rewrite.update-timestamp`` is the reason
304 The return value indicates that ``rewrite.update-timestamp`` is the reason
305 the ``date`` option is set.
305 the ``date`` option is set.
306 """
306 """
307 check_at_most_one_arg(opts, b'date', b'currentdate')
307 check_at_most_one_arg(opts, b'date', b'currentdate')
308 check_at_most_one_arg(opts, b'user', b'currentuser')
308 check_at_most_one_arg(opts, b'user', b'currentuser')
309
309
310 datemaydiffer = False # date-only change should be ignored?
310 datemaydiffer = False # date-only change should be ignored?
311
311
312 if opts.get(b'currentdate'):
312 if opts.get(b'currentdate'):
313 opts[b'date'] = b'%d %d' % dateutil.makedate()
313 opts[b'date'] = b'%d %d' % dateutil.makedate()
314 elif (
314 elif (
315 not opts.get(b'date')
315 not opts.get(b'date')
316 and ui.configbool(b'rewrite', b'update-timestamp')
316 and ui.configbool(b'rewrite', b'update-timestamp')
317 and opts.get(b'currentdate') is None
317 and opts.get(b'currentdate') is None
318 ):
318 ):
319 opts[b'date'] = b'%d %d' % dateutil.makedate()
319 opts[b'date'] = b'%d %d' % dateutil.makedate()
320 datemaydiffer = True
320 datemaydiffer = True
321
321
322 if opts.get(b'currentuser'):
322 if opts.get(b'currentuser'):
323 opts[b'user'] = ui.username()
323 opts[b'user'] = ui.username()
324
324
325 return datemaydiffer
325 return datemaydiffer
326
326
327
327
328 def checknotesize(ui, opts):
328 def checknotesize(ui, opts):
329 """ make sure note is of valid format """
329 """ make sure note is of valid format """
330
330
331 note = opts.get(b'note')
331 note = opts.get(b'note')
332 if not note:
332 if not note:
333 return
333 return
334
334
335 if len(note) > 255:
335 if len(note) > 255:
336 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
336 raise error.Abort(_(b"cannot store a note of more than 255 bytes"))
337 if b'\n' in note:
337 if b'\n' in note:
338 raise error.Abort(_(b"note cannot contain a newline"))
338 raise error.Abort(_(b"note cannot contain a newline"))
339
339
340
340
341 def ishunk(x):
341 def ishunk(x):
342 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
342 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
343 return isinstance(x, hunkclasses)
343 return isinstance(x, hunkclasses)
344
344
345
345
346 def newandmodified(chunks, originalchunks):
346 def newandmodified(chunks, originalchunks):
347 newlyaddedandmodifiedfiles = set()
347 newlyaddedandmodifiedfiles = set()
348 alsorestore = set()
348 alsorestore = set()
349 for chunk in chunks:
349 for chunk in chunks:
350 if (
350 if (
351 ishunk(chunk)
351 ishunk(chunk)
352 and chunk.header.isnewfile()
352 and chunk.header.isnewfile()
353 and chunk not in originalchunks
353 and chunk not in originalchunks
354 ):
354 ):
355 newlyaddedandmodifiedfiles.add(chunk.header.filename())
355 newlyaddedandmodifiedfiles.add(chunk.header.filename())
356 alsorestore.update(
356 alsorestore.update(
357 set(chunk.header.files()) - {chunk.header.filename()}
357 set(chunk.header.files()) - {chunk.header.filename()}
358 )
358 )
359 return newlyaddedandmodifiedfiles, alsorestore
359 return newlyaddedandmodifiedfiles, alsorestore
360
360
361
361
362 def parsealiases(cmd):
362 def parsealiases(cmd):
363 return cmd.split(b"|")
363 return cmd.split(b"|")
364
364
365
365
366 def setupwrapcolorwrite(ui):
366 def setupwrapcolorwrite(ui):
367 # wrap ui.write so diff output can be labeled/colorized
367 # wrap ui.write so diff output can be labeled/colorized
368 def wrapwrite(orig, *args, **kw):
368 def wrapwrite(orig, *args, **kw):
369 label = kw.pop('label', b'')
369 label = kw.pop('label', b'')
370 for chunk, l in patch.difflabel(lambda: args):
370 for chunk, l in patch.difflabel(lambda: args):
371 orig(chunk, label=label + l)
371 orig(chunk, label=label + l)
372
372
373 oldwrite = ui.write
373 oldwrite = ui.write
374
374
375 def wrap(*args, **kwargs):
375 def wrap(*args, **kwargs):
376 return wrapwrite(oldwrite, *args, **kwargs)
376 return wrapwrite(oldwrite, *args, **kwargs)
377
377
378 setattr(ui, 'write', wrap)
378 setattr(ui, 'write', wrap)
379 return oldwrite
379 return oldwrite
380
380
381
381
382 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
382 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
383 try:
383 try:
384 if usecurses:
384 if usecurses:
385 if testfile:
385 if testfile:
386 recordfn = crecordmod.testdecorator(
386 recordfn = crecordmod.testdecorator(
387 testfile, crecordmod.testchunkselector
387 testfile, crecordmod.testchunkselector
388 )
388 )
389 else:
389 else:
390 recordfn = crecordmod.chunkselector
390 recordfn = crecordmod.chunkselector
391
391
392 return crecordmod.filterpatch(
392 return crecordmod.filterpatch(
393 ui, originalhunks, recordfn, operation
393 ui, originalhunks, recordfn, operation
394 )
394 )
395 except crecordmod.fallbackerror as e:
395 except crecordmod.fallbackerror as e:
396 ui.warn(b'%s\n' % e)
396 ui.warn(b'%s\n' % e)
397 ui.warn(_(b'falling back to text mode\n'))
397 ui.warn(_(b'falling back to text mode\n'))
398
398
399 return patch.filterpatch(ui, originalhunks, match, operation)
399 return patch.filterpatch(ui, originalhunks, match, operation)
400
400
401
401
402 def recordfilter(ui, originalhunks, match, operation=None):
402 def recordfilter(ui, originalhunks, match, operation=None):
403 """ Prompts the user to filter the originalhunks and return a list of
403 """ Prompts the user to filter the originalhunks and return a list of
404 selected hunks.
404 selected hunks.
405 *operation* is used for to build ui messages to indicate the user what
405 *operation* is used for to build ui messages to indicate the user what
406 kind of filtering they are doing: reverting, committing, shelving, etc.
406 kind of filtering they are doing: reverting, committing, shelving, etc.
407 (see patch.filterpatch).
407 (see patch.filterpatch).
408 """
408 """
409 usecurses = crecordmod.checkcurses(ui)
409 usecurses = crecordmod.checkcurses(ui)
410 testfile = ui.config(b'experimental', b'crecordtest')
410 testfile = ui.config(b'experimental', b'crecordtest')
411 oldwrite = setupwrapcolorwrite(ui)
411 oldwrite = setupwrapcolorwrite(ui)
412 try:
412 try:
413 newchunks, newopts = filterchunks(
413 newchunks, newopts = filterchunks(
414 ui, originalhunks, usecurses, testfile, match, operation
414 ui, originalhunks, usecurses, testfile, match, operation
415 )
415 )
416 finally:
416 finally:
417 ui.write = oldwrite
417 ui.write = oldwrite
418 return newchunks, newopts
418 return newchunks, newopts
419
419
420
420
421 def dorecord(
421 def dorecord(
422 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
422 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
423 ):
423 ):
424 opts = pycompat.byteskwargs(opts)
424 opts = pycompat.byteskwargs(opts)
425 if not ui.interactive():
425 if not ui.interactive():
426 if cmdsuggest:
426 if cmdsuggest:
427 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
427 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
428 else:
428 else:
429 msg = _(b'running non-interactively')
429 msg = _(b'running non-interactively')
430 raise error.Abort(msg)
430 raise error.Abort(msg)
431
431
432 # make sure username is set before going interactive
432 # make sure username is set before going interactive
433 if not opts.get(b'user'):
433 if not opts.get(b'user'):
434 ui.username() # raise exception, username not provided
434 ui.username() # raise exception, username not provided
435
435
436 def recordfunc(ui, repo, message, match, opts):
436 def recordfunc(ui, repo, message, match, opts):
437 """This is generic record driver.
437 """This is generic record driver.
438
438
439 Its job is to interactively filter local changes, and
439 Its job is to interactively filter local changes, and
440 accordingly prepare working directory into a state in which the
440 accordingly prepare working directory into a state in which the
441 job can be delegated to a non-interactive commit command such as
441 job can be delegated to a non-interactive commit command such as
442 'commit' or 'qrefresh'.
442 'commit' or 'qrefresh'.
443
443
444 After the actual job is done by non-interactive command, the
444 After the actual job is done by non-interactive command, the
445 working directory is restored to its original state.
445 working directory is restored to its original state.
446
446
447 In the end we'll record interesting changes, and everything else
447 In the end we'll record interesting changes, and everything else
448 will be left in place, so the user can continue working.
448 will be left in place, so the user can continue working.
449 """
449 """
450 if not opts.get(b'interactive-unshelve'):
450 if not opts.get(b'interactive-unshelve'):
451 checkunfinished(repo, commit=True)
451 checkunfinished(repo, commit=True)
452 wctx = repo[None]
452 wctx = repo[None]
453 merge = len(wctx.parents()) > 1
453 merge = len(wctx.parents()) > 1
454 if merge:
454 if merge:
455 raise error.Abort(
455 raise error.Abort(
456 _(
456 _(
457 b'cannot partially commit a merge '
457 b'cannot partially commit a merge '
458 b'(use "hg commit" instead)'
458 b'(use "hg commit" instead)'
459 )
459 )
460 )
460 )
461
461
462 def fail(f, msg):
462 def fail(f, msg):
463 raise error.Abort(b'%s: %s' % (f, msg))
463 raise error.Abort(b'%s: %s' % (f, msg))
464
464
465 force = opts.get(b'force')
465 force = opts.get(b'force')
466 if not force:
466 if not force:
467 match = matchmod.badmatch(match, fail)
467 match = matchmod.badmatch(match, fail)
468
468
469 status = repo.status(match=match)
469 status = repo.status(match=match)
470
470
471 overrides = {(b'ui', b'commitsubrepos'): True}
471 overrides = {(b'ui', b'commitsubrepos'): True}
472
472
473 with repo.ui.configoverride(overrides, b'record'):
473 with repo.ui.configoverride(overrides, b'record'):
474 # subrepoutil.precommit() modifies the status
474 # subrepoutil.precommit() modifies the status
475 tmpstatus = scmutil.status(
475 tmpstatus = scmutil.status(
476 copymod.copy(status.modified),
476 copymod.copy(status.modified),
477 copymod.copy(status.added),
477 copymod.copy(status.added),
478 copymod.copy(status.removed),
478 copymod.copy(status.removed),
479 copymod.copy(status.deleted),
479 copymod.copy(status.deleted),
480 copymod.copy(status.unknown),
480 copymod.copy(status.unknown),
481 copymod.copy(status.ignored),
481 copymod.copy(status.ignored),
482 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
482 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
483 )
483 )
484
484
485 # Force allows -X subrepo to skip the subrepo.
485 # Force allows -X subrepo to skip the subrepo.
486 subs, commitsubs, newstate = subrepoutil.precommit(
486 subs, commitsubs, newstate = subrepoutil.precommit(
487 repo.ui, wctx, tmpstatus, match, force=True
487 repo.ui, wctx, tmpstatus, match, force=True
488 )
488 )
489 for s in subs:
489 for s in subs:
490 if s in commitsubs:
490 if s in commitsubs:
491 dirtyreason = wctx.sub(s).dirtyreason(True)
491 dirtyreason = wctx.sub(s).dirtyreason(True)
492 raise error.Abort(dirtyreason)
492 raise error.Abort(dirtyreason)
493
493
494 if not force:
494 if not force:
495 repo.checkcommitpatterns(wctx, match, status, fail)
495 repo.checkcommitpatterns(wctx, match, status, fail)
496 diffopts = patch.difffeatureopts(
496 diffopts = patch.difffeatureopts(
497 ui,
497 ui,
498 opts=opts,
498 opts=opts,
499 whitespace=True,
499 whitespace=True,
500 section=b'commands',
500 section=b'commands',
501 configprefix=b'commit.interactive.',
501 configprefix=b'commit.interactive.',
502 )
502 )
503 diffopts.nodates = True
503 diffopts.nodates = True
504 diffopts.git = True
504 diffopts.git = True
505 diffopts.showfunc = True
505 diffopts.showfunc = True
506 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
506 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
507 originalchunks = patch.parsepatch(originaldiff)
507 originalchunks = patch.parsepatch(originaldiff)
508 match = scmutil.match(repo[None], pats)
508 match = scmutil.match(repo[None], pats)
509
509
510 # 1. filter patch, since we are intending to apply subset of it
510 # 1. filter patch, since we are intending to apply subset of it
511 try:
511 try:
512 chunks, newopts = filterfn(ui, originalchunks, match)
512 chunks, newopts = filterfn(ui, originalchunks, match)
513 except error.PatchError as err:
513 except error.PatchError as err:
514 raise error.Abort(_(b'error parsing patch: %s') % err)
514 raise error.Abort(_(b'error parsing patch: %s') % err)
515 opts.update(newopts)
515 opts.update(newopts)
516
516
517 # We need to keep a backup of files that have been newly added and
517 # We need to keep a backup of files that have been newly added and
518 # modified during the recording process because there is a previous
518 # modified during the recording process because there is a previous
519 # version without the edit in the workdir. We also will need to restore
519 # version without the edit in the workdir. We also will need to restore
520 # files that were the sources of renames so that the patch application
520 # files that were the sources of renames so that the patch application
521 # works.
521 # works.
522 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
522 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
523 chunks, originalchunks
523 chunks, originalchunks
524 )
524 )
525 contenders = set()
525 contenders = set()
526 for h in chunks:
526 for h in chunks:
527 try:
527 try:
528 contenders.update(set(h.files()))
528 contenders.update(set(h.files()))
529 except AttributeError:
529 except AttributeError:
530 pass
530 pass
531
531
532 changed = status.modified + status.added + status.removed
532 changed = status.modified + status.added + status.removed
533 newfiles = [f for f in changed if f in contenders]
533 newfiles = [f for f in changed if f in contenders]
534 if not newfiles:
534 if not newfiles:
535 ui.status(_(b'no changes to record\n'))
535 ui.status(_(b'no changes to record\n'))
536 return 0
536 return 0
537
537
538 modified = set(status.modified)
538 modified = set(status.modified)
539
539
540 # 2. backup changed files, so we can restore them in the end
540 # 2. backup changed files, so we can restore them in the end
541
541
542 if backupall:
542 if backupall:
543 tobackup = changed
543 tobackup = changed
544 else:
544 else:
545 tobackup = [
545 tobackup = [
546 f
546 f
547 for f in newfiles
547 for f in newfiles
548 if f in modified or f in newlyaddedandmodifiedfiles
548 if f in modified or f in newlyaddedandmodifiedfiles
549 ]
549 ]
550 backups = {}
550 backups = {}
551 if tobackup:
551 if tobackup:
552 backupdir = repo.vfs.join(b'record-backups')
552 backupdir = repo.vfs.join(b'record-backups')
553 try:
553 try:
554 os.mkdir(backupdir)
554 os.mkdir(backupdir)
555 except OSError as err:
555 except OSError as err:
556 if err.errno != errno.EEXIST:
556 if err.errno != errno.EEXIST:
557 raise
557 raise
558 try:
558 try:
559 # backup continues
559 # backup continues
560 for f in tobackup:
560 for f in tobackup:
561 fd, tmpname = pycompat.mkstemp(
561 fd, tmpname = pycompat.mkstemp(
562 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
562 prefix=f.replace(b'/', b'_') + b'.', dir=backupdir
563 )
563 )
564 os.close(fd)
564 os.close(fd)
565 ui.debug(b'backup %r as %r\n' % (f, tmpname))
565 ui.debug(b'backup %r as %r\n' % (f, tmpname))
566 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
566 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
567 backups[f] = tmpname
567 backups[f] = tmpname
568
568
569 fp = stringio()
569 fp = stringio()
570 for c in chunks:
570 for c in chunks:
571 fname = c.filename()
571 fname = c.filename()
572 if fname in backups:
572 if fname in backups:
573 c.write(fp)
573 c.write(fp)
574 dopatch = fp.tell()
574 dopatch = fp.tell()
575 fp.seek(0)
575 fp.seek(0)
576
576
577 # 2.5 optionally review / modify patch in text editor
577 # 2.5 optionally review / modify patch in text editor
578 if opts.get(b'review', False):
578 if opts.get(b'review', False):
579 patchtext = (
579 patchtext = (
580 crecordmod.diffhelptext
580 crecordmod.diffhelptext
581 + crecordmod.patchhelptext
581 + crecordmod.patchhelptext
582 + fp.read()
582 + fp.read()
583 )
583 )
584 reviewedpatch = ui.edit(
584 reviewedpatch = ui.edit(
585 patchtext, b"", action=b"diff", repopath=repo.path
585 patchtext, b"", action=b"diff", repopath=repo.path
586 )
586 )
587 fp.truncate(0)
587 fp.truncate(0)
588 fp.write(reviewedpatch)
588 fp.write(reviewedpatch)
589 fp.seek(0)
589 fp.seek(0)
590
590
591 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
591 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
592 # 3a. apply filtered patch to clean repo (clean)
592 # 3a. apply filtered patch to clean repo (clean)
593 if backups:
593 if backups:
594 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
594 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
595 mergemod.revert_to(repo[b'.'], matcher=m)
595 mergemod.revert_to(repo[b'.'], matcher=m)
596
596
597 # 3b. (apply)
597 # 3b. (apply)
598 if dopatch:
598 if dopatch:
599 try:
599 try:
600 ui.debug(b'applying patch\n')
600 ui.debug(b'applying patch\n')
601 ui.debug(fp.getvalue())
601 ui.debug(fp.getvalue())
602 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
602 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
603 except error.PatchError as err:
603 except error.PatchError as err:
604 raise error.Abort(pycompat.bytestr(err))
604 raise error.Abort(pycompat.bytestr(err))
605 del fp
605 del fp
606
606
607 # 4. We prepared working directory according to filtered
607 # 4. We prepared working directory according to filtered
608 # patch. Now is the time to delegate the job to
608 # patch. Now is the time to delegate the job to
609 # commit/qrefresh or the like!
609 # commit/qrefresh or the like!
610
610
611 # Make all of the pathnames absolute.
611 # Make all of the pathnames absolute.
612 newfiles = [repo.wjoin(nf) for nf in newfiles]
612 newfiles = [repo.wjoin(nf) for nf in newfiles]
613 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
613 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
614 finally:
614 finally:
615 # 5. finally restore backed-up files
615 # 5. finally restore backed-up files
616 try:
616 try:
617 dirstate = repo.dirstate
617 dirstate = repo.dirstate
618 for realname, tmpname in pycompat.iteritems(backups):
618 for realname, tmpname in pycompat.iteritems(backups):
619 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
619 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
620
620
621 if dirstate[realname] == b'n':
621 if dirstate[realname] == b'n':
622 # without normallookup, restoring timestamp
622 # without normallookup, restoring timestamp
623 # may cause partially committed files
623 # may cause partially committed files
624 # to be treated as unmodified
624 # to be treated as unmodified
625 dirstate.normallookup(realname)
625 dirstate.normallookup(realname)
626
626
627 # copystat=True here and above are a hack to trick any
627 # copystat=True here and above are a hack to trick any
628 # editors that have f open that we haven't modified them.
628 # editors that have f open that we haven't modified them.
629 #
629 #
630 # Also note that this racy as an editor could notice the
630 # Also note that this racy as an editor could notice the
631 # file's mtime before we've finished writing it.
631 # file's mtime before we've finished writing it.
632 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
632 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
633 os.unlink(tmpname)
633 os.unlink(tmpname)
634 if tobackup:
634 if tobackup:
635 os.rmdir(backupdir)
635 os.rmdir(backupdir)
636 except OSError:
636 except OSError:
637 pass
637 pass
638
638
639 def recordinwlock(ui, repo, message, match, opts):
639 def recordinwlock(ui, repo, message, match, opts):
640 with repo.wlock():
640 with repo.wlock():
641 return recordfunc(ui, repo, message, match, opts)
641 return recordfunc(ui, repo, message, match, opts)
642
642
643 return commit(ui, repo, recordinwlock, pats, opts)
643 return commit(ui, repo, recordinwlock, pats, opts)
644
644
645
645
646 class dirnode(object):
646 class dirnode(object):
647 """
647 """
648 Represent a directory in user working copy with information required for
648 Represent a directory in user working copy with information required for
649 the purpose of tersing its status.
649 the purpose of tersing its status.
650
650
651 path is the path to the directory, without a trailing '/'
651 path is the path to the directory, without a trailing '/'
652
652
653 statuses is a set of statuses of all files in this directory (this includes
653 statuses is a set of statuses of all files in this directory (this includes
654 all the files in all the subdirectories too)
654 all the files in all the subdirectories too)
655
655
656 files is a list of files which are direct child of this directory
656 files is a list of files which are direct child of this directory
657
657
658 subdirs is a dictionary of sub-directory name as the key and it's own
658 subdirs is a dictionary of sub-directory name as the key and it's own
659 dirnode object as the value
659 dirnode object as the value
660 """
660 """
661
661
662 def __init__(self, dirpath):
662 def __init__(self, dirpath):
663 self.path = dirpath
663 self.path = dirpath
664 self.statuses = set()
664 self.statuses = set()
665 self.files = []
665 self.files = []
666 self.subdirs = {}
666 self.subdirs = {}
667
667
668 def _addfileindir(self, filename, status):
668 def _addfileindir(self, filename, status):
669 """Add a file in this directory as a direct child."""
669 """Add a file in this directory as a direct child."""
670 self.files.append((filename, status))
670 self.files.append((filename, status))
671
671
672 def addfile(self, filename, status):
672 def addfile(self, filename, status):
673 """
673 """
674 Add a file to this directory or to its direct parent directory.
674 Add a file to this directory or to its direct parent directory.
675
675
676 If the file is not direct child of this directory, we traverse to the
676 If the file is not direct child of this directory, we traverse to the
677 directory of which this file is a direct child of and add the file
677 directory of which this file is a direct child of and add the file
678 there.
678 there.
679 """
679 """
680
680
681 # the filename contains a path separator, it means it's not the direct
681 # the filename contains a path separator, it means it's not the direct
682 # child of this directory
682 # child of this directory
683 if b'/' in filename:
683 if b'/' in filename:
684 subdir, filep = filename.split(b'/', 1)
684 subdir, filep = filename.split(b'/', 1)
685
685
686 # does the dirnode object for subdir exists
686 # does the dirnode object for subdir exists
687 if subdir not in self.subdirs:
687 if subdir not in self.subdirs:
688 subdirpath = pathutil.join(self.path, subdir)
688 subdirpath = pathutil.join(self.path, subdir)
689 self.subdirs[subdir] = dirnode(subdirpath)
689 self.subdirs[subdir] = dirnode(subdirpath)
690
690
691 # try adding the file in subdir
691 # try adding the file in subdir
692 self.subdirs[subdir].addfile(filep, status)
692 self.subdirs[subdir].addfile(filep, status)
693
693
694 else:
694 else:
695 self._addfileindir(filename, status)
695 self._addfileindir(filename, status)
696
696
697 if status not in self.statuses:
697 if status not in self.statuses:
698 self.statuses.add(status)
698 self.statuses.add(status)
699
699
700 def iterfilepaths(self):
700 def iterfilepaths(self):
701 """Yield (status, path) for files directly under this directory."""
701 """Yield (status, path) for files directly under this directory."""
702 for f, st in self.files:
702 for f, st in self.files:
703 yield st, pathutil.join(self.path, f)
703 yield st, pathutil.join(self.path, f)
704
704
705 def tersewalk(self, terseargs):
705 def tersewalk(self, terseargs):
706 """
706 """
707 Yield (status, path) obtained by processing the status of this
707 Yield (status, path) obtained by processing the status of this
708 dirnode.
708 dirnode.
709
709
710 terseargs is the string of arguments passed by the user with `--terse`
710 terseargs is the string of arguments passed by the user with `--terse`
711 flag.
711 flag.
712
712
713 Following are the cases which can happen:
713 Following are the cases which can happen:
714
714
715 1) All the files in the directory (including all the files in its
715 1) All the files in the directory (including all the files in its
716 subdirectories) share the same status and the user has asked us to terse
716 subdirectories) share the same status and the user has asked us to terse
717 that status. -> yield (status, dirpath). dirpath will end in '/'.
717 that status. -> yield (status, dirpath). dirpath will end in '/'.
718
718
719 2) Otherwise, we do following:
719 2) Otherwise, we do following:
720
720
721 a) Yield (status, filepath) for all the files which are in this
721 a) Yield (status, filepath) for all the files which are in this
722 directory (only the ones in this directory, not the subdirs)
722 directory (only the ones in this directory, not the subdirs)
723
723
724 b) Recurse the function on all the subdirectories of this
724 b) Recurse the function on all the subdirectories of this
725 directory
725 directory
726 """
726 """
727
727
728 if len(self.statuses) == 1:
728 if len(self.statuses) == 1:
729 onlyst = self.statuses.pop()
729 onlyst = self.statuses.pop()
730
730
731 # Making sure we terse only when the status abbreviation is
731 # Making sure we terse only when the status abbreviation is
732 # passed as terse argument
732 # passed as terse argument
733 if onlyst in terseargs:
733 if onlyst in terseargs:
734 yield onlyst, self.path + b'/'
734 yield onlyst, self.path + b'/'
735 return
735 return
736
736
737 # add the files to status list
737 # add the files to status list
738 for st, fpath in self.iterfilepaths():
738 for st, fpath in self.iterfilepaths():
739 yield st, fpath
739 yield st, fpath
740
740
741 # recurse on the subdirs
741 # recurse on the subdirs
742 for dirobj in self.subdirs.values():
742 for dirobj in self.subdirs.values():
743 for st, fpath in dirobj.tersewalk(terseargs):
743 for st, fpath in dirobj.tersewalk(terseargs):
744 yield st, fpath
744 yield st, fpath
745
745
746
746
747 def tersedir(statuslist, terseargs):
747 def tersedir(statuslist, terseargs):
748 """
748 """
749 Terse the status if all the files in a directory shares the same status.
749 Terse the status if all the files in a directory shares the same status.
750
750
751 statuslist is scmutil.status() object which contains a list of files for
751 statuslist is scmutil.status() object which contains a list of files for
752 each status.
752 each status.
753 terseargs is string which is passed by the user as the argument to `--terse`
753 terseargs is string which is passed by the user as the argument to `--terse`
754 flag.
754 flag.
755
755
756 The function makes a tree of objects of dirnode class, and at each node it
756 The function makes a tree of objects of dirnode class, and at each node it
757 stores the information required to know whether we can terse a certain
757 stores the information required to know whether we can terse a certain
758 directory or not.
758 directory or not.
759 """
759 """
760 # the order matters here as that is used to produce final list
760 # the order matters here as that is used to produce final list
761 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
761 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
762
762
763 # checking the argument validity
763 # checking the argument validity
764 for s in pycompat.bytestr(terseargs):
764 for s in pycompat.bytestr(terseargs):
765 if s not in allst:
765 if s not in allst:
766 raise error.Abort(_(b"'%s' not recognized") % s)
766 raise error.Abort(_(b"'%s' not recognized") % s)
767
767
768 # creating a dirnode object for the root of the repo
768 # creating a dirnode object for the root of the repo
769 rootobj = dirnode(b'')
769 rootobj = dirnode(b'')
770 pstatus = (
770 pstatus = (
771 b'modified',
771 b'modified',
772 b'added',
772 b'added',
773 b'deleted',
773 b'deleted',
774 b'clean',
774 b'clean',
775 b'unknown',
775 b'unknown',
776 b'ignored',
776 b'ignored',
777 b'removed',
777 b'removed',
778 )
778 )
779
779
780 tersedict = {}
780 tersedict = {}
781 for attrname in pstatus:
781 for attrname in pstatus:
782 statuschar = attrname[0:1]
782 statuschar = attrname[0:1]
783 for f in getattr(statuslist, attrname):
783 for f in getattr(statuslist, attrname):
784 rootobj.addfile(f, statuschar)
784 rootobj.addfile(f, statuschar)
785 tersedict[statuschar] = []
785 tersedict[statuschar] = []
786
786
787 # we won't be tersing the root dir, so add files in it
787 # we won't be tersing the root dir, so add files in it
788 for st, fpath in rootobj.iterfilepaths():
788 for st, fpath in rootobj.iterfilepaths():
789 tersedict[st].append(fpath)
789 tersedict[st].append(fpath)
790
790
791 # process each sub-directory and build tersedict
791 # process each sub-directory and build tersedict
792 for subdir in rootobj.subdirs.values():
792 for subdir in rootobj.subdirs.values():
793 for st, f in subdir.tersewalk(terseargs):
793 for st, f in subdir.tersewalk(terseargs):
794 tersedict[st].append(f)
794 tersedict[st].append(f)
795
795
796 tersedlist = []
796 tersedlist = []
797 for st in allst:
797 for st in allst:
798 tersedict[st].sort()
798 tersedict[st].sort()
799 tersedlist.append(tersedict[st])
799 tersedlist.append(tersedict[st])
800
800
801 return scmutil.status(*tersedlist)
801 return scmutil.status(*tersedlist)
802
802
803
803
804 def _commentlines(raw):
804 def _commentlines(raw):
805 '''Surround lineswith a comment char and a new line'''
805 '''Surround lineswith a comment char and a new line'''
806 lines = raw.splitlines()
806 lines = raw.splitlines()
807 commentedlines = [b'# %s' % line for line in lines]
807 commentedlines = [b'# %s' % line for line in lines]
808 return b'\n'.join(commentedlines) + b'\n'
808 return b'\n'.join(commentedlines) + b'\n'
809
809
810
810
811 @attr.s(frozen=True)
811 @attr.s(frozen=True)
812 class morestatus(object):
812 class morestatus(object):
813 reporoot = attr.ib()
813 reporoot = attr.ib()
814 unfinishedop = attr.ib()
814 unfinishedop = attr.ib()
815 unfinishedmsg = attr.ib()
815 unfinishedmsg = attr.ib()
816 activemerge = attr.ib()
816 activemerge = attr.ib()
817 unresolvedpaths = attr.ib()
817 unresolvedpaths = attr.ib()
818 _formattedpaths = attr.ib(init=False, default=set())
818 _formattedpaths = attr.ib(init=False, default=set())
819 _label = b'status.morestatus'
819 _label = b'status.morestatus'
820
820
821 def formatfile(self, path, fm):
821 def formatfile(self, path, fm):
822 self._formattedpaths.add(path)
822 self._formattedpaths.add(path)
823 if self.activemerge and path in self.unresolvedpaths:
823 if self.activemerge and path in self.unresolvedpaths:
824 fm.data(unresolved=True)
824 fm.data(unresolved=True)
825
825
826 def formatfooter(self, fm):
826 def formatfooter(self, fm):
827 if self.unfinishedop or self.unfinishedmsg:
827 if self.unfinishedop or self.unfinishedmsg:
828 fm.startitem()
828 fm.startitem()
829 fm.data(itemtype=b'morestatus')
829 fm.data(itemtype=b'morestatus')
830
830
831 if self.unfinishedop:
831 if self.unfinishedop:
832 fm.data(unfinished=self.unfinishedop)
832 fm.data(unfinished=self.unfinishedop)
833 statemsg = (
833 statemsg = (
834 _(b'The repository is in an unfinished *%s* state.')
834 _(b'The repository is in an unfinished *%s* state.')
835 % self.unfinishedop
835 % self.unfinishedop
836 )
836 )
837 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
837 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
838 if self.unfinishedmsg:
838 if self.unfinishedmsg:
839 fm.data(unfinishedmsg=self.unfinishedmsg)
839 fm.data(unfinishedmsg=self.unfinishedmsg)
840
840
841 # May also start new data items.
841 # May also start new data items.
842 self._formatconflicts(fm)
842 self._formatconflicts(fm)
843
843
844 if self.unfinishedmsg:
844 if self.unfinishedmsg:
845 fm.plain(
845 fm.plain(
846 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
846 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
847 )
847 )
848
848
849 def _formatconflicts(self, fm):
849 def _formatconflicts(self, fm):
850 if not self.activemerge:
850 if not self.activemerge:
851 return
851 return
852
852
853 if self.unresolvedpaths:
853 if self.unresolvedpaths:
854 mergeliststr = b'\n'.join(
854 mergeliststr = b'\n'.join(
855 [
855 [
856 b' %s'
856 b' %s'
857 % util.pathto(self.reporoot, encoding.getcwd(), path)
857 % util.pathto(self.reporoot, encoding.getcwd(), path)
858 for path in self.unresolvedpaths
858 for path in self.unresolvedpaths
859 ]
859 ]
860 )
860 )
861 msg = (
861 msg = (
862 _(
862 _(
863 '''Unresolved merge conflicts:
863 '''Unresolved merge conflicts:
864
864
865 %s
865 %s
866
866
867 To mark files as resolved: hg resolve --mark FILE'''
867 To mark files as resolved: hg resolve --mark FILE'''
868 )
868 )
869 % mergeliststr
869 % mergeliststr
870 )
870 )
871
871
872 # If any paths with unresolved conflicts were not previously
872 # If any paths with unresolved conflicts were not previously
873 # formatted, output them now.
873 # formatted, output them now.
874 for f in self.unresolvedpaths:
874 for f in self.unresolvedpaths:
875 if f in self._formattedpaths:
875 if f in self._formattedpaths:
876 # Already output.
876 # Already output.
877 continue
877 continue
878 fm.startitem()
878 fm.startitem()
879 # We can't claim to know the status of the file - it may just
879 # We can't claim to know the status of the file - it may just
880 # have been in one of the states that were not requested for
880 # have been in one of the states that were not requested for
881 # display, so it could be anything.
881 # display, so it could be anything.
882 fm.data(itemtype=b'file', path=f, unresolved=True)
882 fm.data(itemtype=b'file', path=f, unresolved=True)
883
883
884 else:
884 else:
885 msg = _(b'No unresolved merge conflicts.')
885 msg = _(b'No unresolved merge conflicts.')
886
886
887 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
887 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
888
888
889
889
890 def readmorestatus(repo):
890 def readmorestatus(repo):
891 """Returns a morestatus object if the repo has unfinished state."""
891 """Returns a morestatus object if the repo has unfinished state."""
892 statetuple = statemod.getrepostate(repo)
892 statetuple = statemod.getrepostate(repo)
893 mergestate = mergemod.mergestate.read(repo)
893 mergestate = mergemod.mergestate.read(repo)
894 activemerge = mergestate.active()
894 activemerge = mergestate.active()
895 if not statetuple and not activemerge:
895 if not statetuple and not activemerge:
896 return None
896 return None
897
897
898 unfinishedop = unfinishedmsg = unresolved = None
898 unfinishedop = unfinishedmsg = unresolved = None
899 if statetuple:
899 if statetuple:
900 unfinishedop, unfinishedmsg = statetuple
900 unfinishedop, unfinishedmsg = statetuple
901 if activemerge:
901 if activemerge:
902 unresolved = sorted(mergestate.unresolved())
902 unresolved = sorted(mergestate.unresolved())
903 return morestatus(
903 return morestatus(
904 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
904 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
905 )
905 )
906
906
907
907
908 def findpossible(cmd, table, strict=False):
908 def findpossible(cmd, table, strict=False):
909 """
909 """
910 Return cmd -> (aliases, command table entry)
910 Return cmd -> (aliases, command table entry)
911 for each matching command.
911 for each matching command.
912 Return debug commands (or their aliases) only if no normal command matches.
912 Return debug commands (or their aliases) only if no normal command matches.
913 """
913 """
914 choice = {}
914 choice = {}
915 debugchoice = {}
915 debugchoice = {}
916
916
917 if cmd in table:
917 if cmd in table:
918 # short-circuit exact matches, "log" alias beats "log|history"
918 # short-circuit exact matches, "log" alias beats "log|history"
919 keys = [cmd]
919 keys = [cmd]
920 else:
920 else:
921 keys = table.keys()
921 keys = table.keys()
922
922
923 allcmds = []
923 allcmds = []
924 for e in keys:
924 for e in keys:
925 aliases = parsealiases(e)
925 aliases = parsealiases(e)
926 allcmds.extend(aliases)
926 allcmds.extend(aliases)
927 found = None
927 found = None
928 if cmd in aliases:
928 if cmd in aliases:
929 found = cmd
929 found = cmd
930 elif not strict:
930 elif not strict:
931 for a in aliases:
931 for a in aliases:
932 if a.startswith(cmd):
932 if a.startswith(cmd):
933 found = a
933 found = a
934 break
934 break
935 if found is not None:
935 if found is not None:
936 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
936 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
937 debugchoice[found] = (aliases, table[e])
937 debugchoice[found] = (aliases, table[e])
938 else:
938 else:
939 choice[found] = (aliases, table[e])
939 choice[found] = (aliases, table[e])
940
940
941 if not choice and debugchoice:
941 if not choice and debugchoice:
942 choice = debugchoice
942 choice = debugchoice
943
943
944 return choice, allcmds
944 return choice, allcmds
945
945
946
946
947 def findcmd(cmd, table, strict=True):
947 def findcmd(cmd, table, strict=True):
948 """Return (aliases, command table entry) for command string."""
948 """Return (aliases, command table entry) for command string."""
949 choice, allcmds = findpossible(cmd, table, strict)
949 choice, allcmds = findpossible(cmd, table, strict)
950
950
951 if cmd in choice:
951 if cmd in choice:
952 return choice[cmd]
952 return choice[cmd]
953
953
954 if len(choice) > 1:
954 if len(choice) > 1:
955 clist = sorted(choice)
955 clist = sorted(choice)
956 raise error.AmbiguousCommand(cmd, clist)
956 raise error.AmbiguousCommand(cmd, clist)
957
957
958 if choice:
958 if choice:
959 return list(choice.values())[0]
959 return list(choice.values())[0]
960
960
961 raise error.UnknownCommand(cmd, allcmds)
961 raise error.UnknownCommand(cmd, allcmds)
962
962
963
963
964 def changebranch(ui, repo, revs, label):
964 def changebranch(ui, repo, revs, label):
965 """ Change the branch name of given revs to label """
965 """ Change the branch name of given revs to label """
966
966
967 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
967 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
968 # abort in case of uncommitted merge or dirty wdir
968 # abort in case of uncommitted merge or dirty wdir
969 bailifchanged(repo)
969 bailifchanged(repo)
970 revs = scmutil.revrange(repo, revs)
970 revs = scmutil.revrange(repo, revs)
971 if not revs:
971 if not revs:
972 raise error.Abort(b"empty revision set")
972 raise error.Abort(b"empty revision set")
973 roots = repo.revs(b'roots(%ld)', revs)
973 roots = repo.revs(b'roots(%ld)', revs)
974 if len(roots) > 1:
974 if len(roots) > 1:
975 raise error.Abort(
975 raise error.Abort(
976 _(b"cannot change branch of non-linear revisions")
976 _(b"cannot change branch of non-linear revisions")
977 )
977 )
978 rewriteutil.precheck(repo, revs, b'change branch of')
978 rewriteutil.precheck(repo, revs, b'change branch of')
979
979
980 root = repo[roots.first()]
980 root = repo[roots.first()]
981 rpb = {parent.branch() for parent in root.parents()}
981 rpb = {parent.branch() for parent in root.parents()}
982 if label not in rpb and label in repo.branchmap():
982 if label not in rpb and label in repo.branchmap():
983 raise error.Abort(_(b"a branch of the same name already exists"))
983 raise error.Abort(_(b"a branch of the same name already exists"))
984
984
985 if repo.revs(b'obsolete() and %ld', revs):
985 if repo.revs(b'obsolete() and %ld', revs):
986 raise error.Abort(
986 raise error.Abort(
987 _(b"cannot change branch of a obsolete changeset")
987 _(b"cannot change branch of a obsolete changeset")
988 )
988 )
989
989
990 # make sure only topological heads
990 # make sure only topological heads
991 if repo.revs(b'heads(%ld) - head()', revs):
991 if repo.revs(b'heads(%ld) - head()', revs):
992 raise error.Abort(_(b"cannot change branch in middle of a stack"))
992 raise error.Abort(_(b"cannot change branch in middle of a stack"))
993
993
994 replacements = {}
994 replacements = {}
995 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
995 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
996 # mercurial.subrepo -> mercurial.cmdutil
996 # mercurial.subrepo -> mercurial.cmdutil
997 from . import context
997 from . import context
998
998
999 for rev in revs:
999 for rev in revs:
1000 ctx = repo[rev]
1000 ctx = repo[rev]
1001 oldbranch = ctx.branch()
1001 oldbranch = ctx.branch()
1002 # check if ctx has same branch
1002 # check if ctx has same branch
1003 if oldbranch == label:
1003 if oldbranch == label:
1004 continue
1004 continue
1005
1005
1006 def filectxfn(repo, newctx, path):
1006 def filectxfn(repo, newctx, path):
1007 try:
1007 try:
1008 return ctx[path]
1008 return ctx[path]
1009 except error.ManifestLookupError:
1009 except error.ManifestLookupError:
1010 return None
1010 return None
1011
1011
1012 ui.debug(
1012 ui.debug(
1013 b"changing branch of '%s' from '%s' to '%s'\n"
1013 b"changing branch of '%s' from '%s' to '%s'\n"
1014 % (hex(ctx.node()), oldbranch, label)
1014 % (hex(ctx.node()), oldbranch, label)
1015 )
1015 )
1016 extra = ctx.extra()
1016 extra = ctx.extra()
1017 extra[b'branch_change'] = hex(ctx.node())
1017 extra[b'branch_change'] = hex(ctx.node())
1018 # While changing branch of set of linear commits, make sure that
1018 # While changing branch of set of linear commits, make sure that
1019 # we base our commits on new parent rather than old parent which
1019 # we base our commits on new parent rather than old parent which
1020 # was obsoleted while changing the branch
1020 # was obsoleted while changing the branch
1021 p1 = ctx.p1().node()
1021 p1 = ctx.p1().node()
1022 p2 = ctx.p2().node()
1022 p2 = ctx.p2().node()
1023 if p1 in replacements:
1023 if p1 in replacements:
1024 p1 = replacements[p1][0]
1024 p1 = replacements[p1][0]
1025 if p2 in replacements:
1025 if p2 in replacements:
1026 p2 = replacements[p2][0]
1026 p2 = replacements[p2][0]
1027
1027
1028 mc = context.memctx(
1028 mc = context.memctx(
1029 repo,
1029 repo,
1030 (p1, p2),
1030 (p1, p2),
1031 ctx.description(),
1031 ctx.description(),
1032 ctx.files(),
1032 ctx.files(),
1033 filectxfn,
1033 filectxfn,
1034 user=ctx.user(),
1034 user=ctx.user(),
1035 date=ctx.date(),
1035 date=ctx.date(),
1036 extra=extra,
1036 extra=extra,
1037 branch=label,
1037 branch=label,
1038 )
1038 )
1039
1039
1040 newnode = repo.commitctx(mc)
1040 newnode = repo.commitctx(mc)
1041 replacements[ctx.node()] = (newnode,)
1041 replacements[ctx.node()] = (newnode,)
1042 ui.debug(b'new node id is %s\n' % hex(newnode))
1042 ui.debug(b'new node id is %s\n' % hex(newnode))
1043
1043
1044 # create obsmarkers and move bookmarks
1044 # create obsmarkers and move bookmarks
1045 scmutil.cleanupnodes(
1045 scmutil.cleanupnodes(
1046 repo, replacements, b'branch-change', fixphase=True
1046 repo, replacements, b'branch-change', fixphase=True
1047 )
1047 )
1048
1048
1049 # move the working copy too
1049 # move the working copy too
1050 wctx = repo[None]
1050 wctx = repo[None]
1051 # in-progress merge is a bit too complex for now.
1051 # in-progress merge is a bit too complex for now.
1052 if len(wctx.parents()) == 1:
1052 if len(wctx.parents()) == 1:
1053 newid = replacements.get(wctx.p1().node())
1053 newid = replacements.get(wctx.p1().node())
1054 if newid is not None:
1054 if newid is not None:
1055 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1055 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1056 # mercurial.cmdutil
1056 # mercurial.cmdutil
1057 from . import hg
1057 from . import hg
1058
1058
1059 hg.update(repo, newid[0], quietempty=True)
1059 hg.update(repo, newid[0], quietempty=True)
1060
1060
1061 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1061 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1062
1062
1063
1063
1064 def findrepo(p):
1064 def findrepo(p):
1065 while not os.path.isdir(os.path.join(p, b".hg")):
1065 while not os.path.isdir(os.path.join(p, b".hg")):
1066 oldp, p = p, os.path.dirname(p)
1066 oldp, p = p, os.path.dirname(p)
1067 if p == oldp:
1067 if p == oldp:
1068 return None
1068 return None
1069
1069
1070 return p
1070 return p
1071
1071
1072
1072
1073 def bailifchanged(repo, merge=True, hint=None):
1073 def bailifchanged(repo, merge=True, hint=None):
1074 """ enforce the precondition that working directory must be clean.
1074 """ enforce the precondition that working directory must be clean.
1075
1075
1076 'merge' can be set to false if a pending uncommitted merge should be
1076 'merge' can be set to false if a pending uncommitted merge should be
1077 ignored (such as when 'update --check' runs).
1077 ignored (such as when 'update --check' runs).
1078
1078
1079 'hint' is the usual hint given to Abort exception.
1079 'hint' is the usual hint given to Abort exception.
1080 """
1080 """
1081
1081
1082 if merge and repo.dirstate.p2() != nullid:
1082 if merge and repo.dirstate.p2() != nullid:
1083 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1083 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1084 st = repo.status()
1084 st = repo.status()
1085 if st.modified or st.added or st.removed or st.deleted:
1085 if st.modified or st.added or st.removed or st.deleted:
1086 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1086 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1087 ctx = repo[None]
1087 ctx = repo[None]
1088 for s in sorted(ctx.substate):
1088 for s in sorted(ctx.substate):
1089 ctx.sub(s).bailifchanged(hint=hint)
1089 ctx.sub(s).bailifchanged(hint=hint)
1090
1090
1091
1091
1092 def logmessage(ui, opts):
1092 def logmessage(ui, opts):
1093 """ get the log message according to -m and -l option """
1093 """ get the log message according to -m and -l option """
1094
1094
1095 check_at_most_one_arg(opts, b'message', b'logfile')
1095 check_at_most_one_arg(opts, b'message', b'logfile')
1096
1096
1097 message = opts.get(b'message')
1097 message = opts.get(b'message')
1098 logfile = opts.get(b'logfile')
1098 logfile = opts.get(b'logfile')
1099
1099
1100 if not message and logfile:
1100 if not message and logfile:
1101 try:
1101 try:
1102 if isstdiofilename(logfile):
1102 if isstdiofilename(logfile):
1103 message = ui.fin.read()
1103 message = ui.fin.read()
1104 else:
1104 else:
1105 message = b'\n'.join(util.readfile(logfile).splitlines())
1105 message = b'\n'.join(util.readfile(logfile).splitlines())
1106 except IOError as inst:
1106 except IOError as inst:
1107 raise error.Abort(
1107 raise error.Abort(
1108 _(b"can't read commit message '%s': %s")
1108 _(b"can't read commit message '%s': %s")
1109 % (logfile, encoding.strtolocal(inst.strerror))
1109 % (logfile, encoding.strtolocal(inst.strerror))
1110 )
1110 )
1111 return message
1111 return message
1112
1112
1113
1113
1114 def mergeeditform(ctxorbool, baseformname):
1114 def mergeeditform(ctxorbool, baseformname):
1115 """return appropriate editform name (referencing a committemplate)
1115 """return appropriate editform name (referencing a committemplate)
1116
1116
1117 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1117 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1118 merging is committed.
1118 merging is committed.
1119
1119
1120 This returns baseformname with '.merge' appended if it is a merge,
1120 This returns baseformname with '.merge' appended if it is a merge,
1121 otherwise '.normal' is appended.
1121 otherwise '.normal' is appended.
1122 """
1122 """
1123 if isinstance(ctxorbool, bool):
1123 if isinstance(ctxorbool, bool):
1124 if ctxorbool:
1124 if ctxorbool:
1125 return baseformname + b".merge"
1125 return baseformname + b".merge"
1126 elif len(ctxorbool.parents()) > 1:
1126 elif len(ctxorbool.parents()) > 1:
1127 return baseformname + b".merge"
1127 return baseformname + b".merge"
1128
1128
1129 return baseformname + b".normal"
1129 return baseformname + b".normal"
1130
1130
1131
1131
1132 def getcommiteditor(
1132 def getcommiteditor(
1133 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1133 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1134 ):
1134 ):
1135 """get appropriate commit message editor according to '--edit' option
1135 """get appropriate commit message editor according to '--edit' option
1136
1136
1137 'finishdesc' is a function to be called with edited commit message
1137 'finishdesc' is a function to be called with edited commit message
1138 (= 'description' of the new changeset) just after editing, but
1138 (= 'description' of the new changeset) just after editing, but
1139 before checking empty-ness. It should return actual text to be
1139 before checking empty-ness. It should return actual text to be
1140 stored into history. This allows to change description before
1140 stored into history. This allows to change description before
1141 storing.
1141 storing.
1142
1142
1143 'extramsg' is a extra message to be shown in the editor instead of
1143 'extramsg' is a extra message to be shown in the editor instead of
1144 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1144 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1145 is automatically added.
1145 is automatically added.
1146
1146
1147 'editform' is a dot-separated list of names, to distinguish
1147 'editform' is a dot-separated list of names, to distinguish
1148 the purpose of commit text editing.
1148 the purpose of commit text editing.
1149
1149
1150 'getcommiteditor' returns 'commitforceeditor' regardless of
1150 'getcommiteditor' returns 'commitforceeditor' regardless of
1151 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1151 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1152 they are specific for usage in MQ.
1152 they are specific for usage in MQ.
1153 """
1153 """
1154 if edit or finishdesc or extramsg:
1154 if edit or finishdesc or extramsg:
1155 return lambda r, c, s: commitforceeditor(
1155 return lambda r, c, s: commitforceeditor(
1156 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1156 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1157 )
1157 )
1158 elif editform:
1158 elif editform:
1159 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1159 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1160 else:
1160 else:
1161 return commiteditor
1161 return commiteditor
1162
1162
1163
1163
1164 def _escapecommandtemplate(tmpl):
1164 def _escapecommandtemplate(tmpl):
1165 parts = []
1165 parts = []
1166 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1166 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1167 if typ == b'string':
1167 if typ == b'string':
1168 parts.append(stringutil.escapestr(tmpl[start:end]))
1168 parts.append(stringutil.escapestr(tmpl[start:end]))
1169 else:
1169 else:
1170 parts.append(tmpl[start:end])
1170 parts.append(tmpl[start:end])
1171 return b''.join(parts)
1171 return b''.join(parts)
1172
1172
1173
1173
1174 def rendercommandtemplate(ui, tmpl, props):
1174 def rendercommandtemplate(ui, tmpl, props):
1175 r"""Expand a literal template 'tmpl' in a way suitable for command line
1175 r"""Expand a literal template 'tmpl' in a way suitable for command line
1176
1176
1177 '\' in outermost string is not taken as an escape character because it
1177 '\' in outermost string is not taken as an escape character because it
1178 is a directory separator on Windows.
1178 is a directory separator on Windows.
1179
1179
1180 >>> from . import ui as uimod
1180 >>> from . import ui as uimod
1181 >>> ui = uimod.ui()
1181 >>> ui = uimod.ui()
1182 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1182 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1183 'c:\\foo'
1183 'c:\\foo'
1184 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1184 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1185 'c:{path}'
1185 'c:{path}'
1186 """
1186 """
1187 if not tmpl:
1187 if not tmpl:
1188 return tmpl
1188 return tmpl
1189 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1189 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1190 return t.renderdefault(props)
1190 return t.renderdefault(props)
1191
1191
1192
1192
1193 def rendertemplate(ctx, tmpl, props=None):
1193 def rendertemplate(ctx, tmpl, props=None):
1194 """Expand a literal template 'tmpl' byte-string against one changeset
1194 """Expand a literal template 'tmpl' byte-string against one changeset
1195
1195
1196 Each props item must be a stringify-able value or a callable returning
1196 Each props item must be a stringify-able value or a callable returning
1197 such value, i.e. no bare list nor dict should be passed.
1197 such value, i.e. no bare list nor dict should be passed.
1198 """
1198 """
1199 repo = ctx.repo()
1199 repo = ctx.repo()
1200 tres = formatter.templateresources(repo.ui, repo)
1200 tres = formatter.templateresources(repo.ui, repo)
1201 t = formatter.maketemplater(
1201 t = formatter.maketemplater(
1202 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1202 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1203 )
1203 )
1204 mapping = {b'ctx': ctx}
1204 mapping = {b'ctx': ctx}
1205 if props:
1205 if props:
1206 mapping.update(props)
1206 mapping.update(props)
1207 return t.renderdefault(mapping)
1207 return t.renderdefault(mapping)
1208
1208
1209
1209
1210 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1210 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1211 r"""Convert old-style filename format string to template string
1211 r"""Convert old-style filename format string to template string
1212
1212
1213 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1213 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1214 'foo-{reporoot|basename}-{seqno}.patch'
1214 'foo-{reporoot|basename}-{seqno}.patch'
1215 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1215 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1216 '{rev}{tags % "{tag}"}{node}'
1216 '{rev}{tags % "{tag}"}{node}'
1217
1217
1218 '\' in outermost strings has to be escaped because it is a directory
1218 '\' in outermost strings has to be escaped because it is a directory
1219 separator on Windows:
1219 separator on Windows:
1220
1220
1221 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1221 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1222 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1222 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1223 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1223 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1224 '\\\\\\\\foo\\\\bar.patch'
1224 '\\\\\\\\foo\\\\bar.patch'
1225 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1225 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1226 '\\\\{tags % "{tag}"}'
1226 '\\\\{tags % "{tag}"}'
1227
1227
1228 but inner strings follow the template rules (i.e. '\' is taken as an
1228 but inner strings follow the template rules (i.e. '\' is taken as an
1229 escape character):
1229 escape character):
1230
1230
1231 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1231 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1232 '{"c:\\tmp"}'
1232 '{"c:\\tmp"}'
1233 """
1233 """
1234 expander = {
1234 expander = {
1235 b'H': b'{node}',
1235 b'H': b'{node}',
1236 b'R': b'{rev}',
1236 b'R': b'{rev}',
1237 b'h': b'{node|short}',
1237 b'h': b'{node|short}',
1238 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1238 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1239 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1239 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1240 b'%': b'%',
1240 b'%': b'%',
1241 b'b': b'{reporoot|basename}',
1241 b'b': b'{reporoot|basename}',
1242 }
1242 }
1243 if total is not None:
1243 if total is not None:
1244 expander[b'N'] = b'{total}'
1244 expander[b'N'] = b'{total}'
1245 if seqno is not None:
1245 if seqno is not None:
1246 expander[b'n'] = b'{seqno}'
1246 expander[b'n'] = b'{seqno}'
1247 if total is not None and seqno is not None:
1247 if total is not None and seqno is not None:
1248 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1248 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1249 if pathname is not None:
1249 if pathname is not None:
1250 expander[b's'] = b'{pathname|basename}'
1250 expander[b's'] = b'{pathname|basename}'
1251 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1251 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1252 expander[b'p'] = b'{pathname}'
1252 expander[b'p'] = b'{pathname}'
1253
1253
1254 newname = []
1254 newname = []
1255 for typ, start, end in templater.scantemplate(pat, raw=True):
1255 for typ, start, end in templater.scantemplate(pat, raw=True):
1256 if typ != b'string':
1256 if typ != b'string':
1257 newname.append(pat[start:end])
1257 newname.append(pat[start:end])
1258 continue
1258 continue
1259 i = start
1259 i = start
1260 while i < end:
1260 while i < end:
1261 n = pat.find(b'%', i, end)
1261 n = pat.find(b'%', i, end)
1262 if n < 0:
1262 if n < 0:
1263 newname.append(stringutil.escapestr(pat[i:end]))
1263 newname.append(stringutil.escapestr(pat[i:end]))
1264 break
1264 break
1265 newname.append(stringutil.escapestr(pat[i:n]))
1265 newname.append(stringutil.escapestr(pat[i:n]))
1266 if n + 2 > end:
1266 if n + 2 > end:
1267 raise error.Abort(
1267 raise error.Abort(
1268 _(b"incomplete format spec in output filename")
1268 _(b"incomplete format spec in output filename")
1269 )
1269 )
1270 c = pat[n + 1 : n + 2]
1270 c = pat[n + 1 : n + 2]
1271 i = n + 2
1271 i = n + 2
1272 try:
1272 try:
1273 newname.append(expander[c])
1273 newname.append(expander[c])
1274 except KeyError:
1274 except KeyError:
1275 raise error.Abort(
1275 raise error.Abort(
1276 _(b"invalid format spec '%%%s' in output filename") % c
1276 _(b"invalid format spec '%%%s' in output filename") % c
1277 )
1277 )
1278 return b''.join(newname)
1278 return b''.join(newname)
1279
1279
1280
1280
1281 def makefilename(ctx, pat, **props):
1281 def makefilename(ctx, pat, **props):
1282 if not pat:
1282 if not pat:
1283 return pat
1283 return pat
1284 tmpl = _buildfntemplate(pat, **props)
1284 tmpl = _buildfntemplate(pat, **props)
1285 # BUG: alias expansion shouldn't be made against template fragments
1285 # BUG: alias expansion shouldn't be made against template fragments
1286 # rewritten from %-format strings, but we have no easy way to partially
1286 # rewritten from %-format strings, but we have no easy way to partially
1287 # disable the expansion.
1287 # disable the expansion.
1288 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1288 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1289
1289
1290
1290
1291 def isstdiofilename(pat):
1291 def isstdiofilename(pat):
1292 """True if the given pat looks like a filename denoting stdin/stdout"""
1292 """True if the given pat looks like a filename denoting stdin/stdout"""
1293 return not pat or pat == b'-'
1293 return not pat or pat == b'-'
1294
1294
1295
1295
1296 class _unclosablefile(object):
1296 class _unclosablefile(object):
1297 def __init__(self, fp):
1297 def __init__(self, fp):
1298 self._fp = fp
1298 self._fp = fp
1299
1299
1300 def close(self):
1300 def close(self):
1301 pass
1301 pass
1302
1302
1303 def __iter__(self):
1303 def __iter__(self):
1304 return iter(self._fp)
1304 return iter(self._fp)
1305
1305
1306 def __getattr__(self, attr):
1306 def __getattr__(self, attr):
1307 return getattr(self._fp, attr)
1307 return getattr(self._fp, attr)
1308
1308
1309 def __enter__(self):
1309 def __enter__(self):
1310 return self
1310 return self
1311
1311
1312 def __exit__(self, exc_type, exc_value, exc_tb):
1312 def __exit__(self, exc_type, exc_value, exc_tb):
1313 pass
1313 pass
1314
1314
1315
1315
1316 def makefileobj(ctx, pat, mode=b'wb', **props):
1316 def makefileobj(ctx, pat, mode=b'wb', **props):
1317 writable = mode not in (b'r', b'rb')
1317 writable = mode not in (b'r', b'rb')
1318
1318
1319 if isstdiofilename(pat):
1319 if isstdiofilename(pat):
1320 repo = ctx.repo()
1320 repo = ctx.repo()
1321 if writable:
1321 if writable:
1322 fp = repo.ui.fout
1322 fp = repo.ui.fout
1323 else:
1323 else:
1324 fp = repo.ui.fin
1324 fp = repo.ui.fin
1325 return _unclosablefile(fp)
1325 return _unclosablefile(fp)
1326 fn = makefilename(ctx, pat, **props)
1326 fn = makefilename(ctx, pat, **props)
1327 return open(fn, mode)
1327 return open(fn, mode)
1328
1328
1329
1329
1330 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1330 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1331 """opens the changelog, manifest, a filelog or a given revlog"""
1331 """opens the changelog, manifest, a filelog or a given revlog"""
1332 cl = opts[b'changelog']
1332 cl = opts[b'changelog']
1333 mf = opts[b'manifest']
1333 mf = opts[b'manifest']
1334 dir = opts[b'dir']
1334 dir = opts[b'dir']
1335 msg = None
1335 msg = None
1336 if cl and mf:
1336 if cl and mf:
1337 msg = _(b'cannot specify --changelog and --manifest at the same time')
1337 msg = _(b'cannot specify --changelog and --manifest at the same time')
1338 elif cl and dir:
1338 elif cl and dir:
1339 msg = _(b'cannot specify --changelog and --dir at the same time')
1339 msg = _(b'cannot specify --changelog and --dir at the same time')
1340 elif cl or mf or dir:
1340 elif cl or mf or dir:
1341 if file_:
1341 if file_:
1342 msg = _(b'cannot specify filename with --changelog or --manifest')
1342 msg = _(b'cannot specify filename with --changelog or --manifest')
1343 elif not repo:
1343 elif not repo:
1344 msg = _(
1344 msg = _(
1345 b'cannot specify --changelog or --manifest or --dir '
1345 b'cannot specify --changelog or --manifest or --dir '
1346 b'without a repository'
1346 b'without a repository'
1347 )
1347 )
1348 if msg:
1348 if msg:
1349 raise error.Abort(msg)
1349 raise error.Abort(msg)
1350
1350
1351 r = None
1351 r = None
1352 if repo:
1352 if repo:
1353 if cl:
1353 if cl:
1354 r = repo.unfiltered().changelog
1354 r = repo.unfiltered().changelog
1355 elif dir:
1355 elif dir:
1356 if b'treemanifest' not in repo.requirements:
1356 if b'treemanifest' not in repo.requirements:
1357 raise error.Abort(
1357 raise error.Abort(
1358 _(
1358 _(
1359 b"--dir can only be used on repos with "
1359 b"--dir can only be used on repos with "
1360 b"treemanifest enabled"
1360 b"treemanifest enabled"
1361 )
1361 )
1362 )
1362 )
1363 if not dir.endswith(b'/'):
1363 if not dir.endswith(b'/'):
1364 dir = dir + b'/'
1364 dir = dir + b'/'
1365 dirlog = repo.manifestlog.getstorage(dir)
1365 dirlog = repo.manifestlog.getstorage(dir)
1366 if len(dirlog):
1366 if len(dirlog):
1367 r = dirlog
1367 r = dirlog
1368 elif mf:
1368 elif mf:
1369 r = repo.manifestlog.getstorage(b'')
1369 r = repo.manifestlog.getstorage(b'')
1370 elif file_:
1370 elif file_:
1371 filelog = repo.file(file_)
1371 filelog = repo.file(file_)
1372 if len(filelog):
1372 if len(filelog):
1373 r = filelog
1373 r = filelog
1374
1374
1375 # Not all storage may be revlogs. If requested, try to return an actual
1375 # Not all storage may be revlogs. If requested, try to return an actual
1376 # revlog instance.
1376 # revlog instance.
1377 if returnrevlog:
1377 if returnrevlog:
1378 if isinstance(r, revlog.revlog):
1378 if isinstance(r, revlog.revlog):
1379 pass
1379 pass
1380 elif util.safehasattr(r, b'_revlog'):
1380 elif util.safehasattr(r, b'_revlog'):
1381 r = r._revlog # pytype: disable=attribute-error
1381 r = r._revlog # pytype: disable=attribute-error
1382 elif r is not None:
1382 elif r is not None:
1383 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1383 raise error.Abort(_(b'%r does not appear to be a revlog') % r)
1384
1384
1385 if not r:
1385 if not r:
1386 if not returnrevlog:
1386 if not returnrevlog:
1387 raise error.Abort(_(b'cannot give path to non-revlog'))
1387 raise error.Abort(_(b'cannot give path to non-revlog'))
1388
1388
1389 if not file_:
1389 if not file_:
1390 raise error.CommandError(cmd, _(b'invalid arguments'))
1390 raise error.CommandError(cmd, _(b'invalid arguments'))
1391 if not os.path.isfile(file_):
1391 if not os.path.isfile(file_):
1392 raise error.Abort(_(b"revlog '%s' not found") % file_)
1392 raise error.Abort(_(b"revlog '%s' not found") % file_)
1393 r = revlog.revlog(
1393 r = revlog.revlog(
1394 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1394 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1395 )
1395 )
1396 return r
1396 return r
1397
1397
1398
1398
1399 def openrevlog(repo, cmd, file_, opts):
1399 def openrevlog(repo, cmd, file_, opts):
1400 """Obtain a revlog backing storage of an item.
1400 """Obtain a revlog backing storage of an item.
1401
1401
1402 This is similar to ``openstorage()`` except it always returns a revlog.
1402 This is similar to ``openstorage()`` except it always returns a revlog.
1403
1403
1404 In most cases, a caller cares about the main storage object - not the
1404 In most cases, a caller cares about the main storage object - not the
1405 revlog backing it. Therefore, this function should only be used by code
1405 revlog backing it. Therefore, this function should only be used by code
1406 that needs to examine low-level revlog implementation details. e.g. debug
1406 that needs to examine low-level revlog implementation details. e.g. debug
1407 commands.
1407 commands.
1408 """
1408 """
1409 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1409 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1410
1410
1411
1411
1412 def copy(ui, repo, pats, opts, rename=False):
1412 def copy(ui, repo, pats, opts, rename=False):
1413 check_incompatible_arguments(opts, b'forget', [b'dry_run'])
1414
1413 # called with the repo lock held
1415 # called with the repo lock held
1414 #
1416 #
1415 # hgsep => pathname that uses "/" to separate directories
1417 # hgsep => pathname that uses "/" to separate directories
1416 # ossep => pathname that uses os.sep to separate directories
1418 # ossep => pathname that uses os.sep to separate directories
1417 cwd = repo.getcwd()
1419 cwd = repo.getcwd()
1418 targets = {}
1420 targets = {}
1421 forget = opts.get(b"forget")
1419 after = opts.get(b"after")
1422 after = opts.get(b"after")
1420 dryrun = opts.get(b"dry_run")
1423 dryrun = opts.get(b"dry_run")
1421 ctx = repo[None]
1424 ctx = repo[None]
1422 pctx = ctx.p1()
1425 pctx = ctx.p1()
1423
1426
1424 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1427 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1425
1428
1429 if forget:
1430 match = scmutil.match(wctx, pats, opts)
1431
1432 current_copies = wctx.p1copies()
1433 current_copies.update(wctx.p2copies())
1434
1435 for f in wctx.walk(match):
1436 if f in current_copies:
1437 wctx[f].markcopied(None)
1438 elif match.exact(f):
1439 ui.warn(
1440 _(
1441 b'%s: not unmarking as copy - file is not marked as copied\n'
1442 )
1443 % uipathfn(f)
1444 )
1445 return
1446
1426 def walkpat(pat):
1447 def walkpat(pat):
1427 srcs = []
1448 srcs = []
1428 m = scmutil.match(ctx, [pat], opts, globbed=True)
1449 m = scmutil.match(ctx, [pat], opts, globbed=True)
1429 for abs in ctx.walk(m):
1450 for abs in ctx.walk(m):
1430 rel = uipathfn(abs)
1451 rel = uipathfn(abs)
1431 exact = m.exact(abs)
1452 exact = m.exact(abs)
1432 if abs not in ctx:
1453 if abs not in ctx:
1433 if abs in pctx:
1454 if abs in pctx:
1434 if not after:
1455 if not after:
1435 if exact:
1456 if exact:
1436 ui.warn(
1457 ui.warn(
1437 _(
1458 _(
1438 b'%s: not copying - file has been marked '
1459 b'%s: not copying - file has been marked '
1439 b'for remove\n'
1460 b'for remove\n'
1440 )
1461 )
1441 % rel
1462 % rel
1442 )
1463 )
1443 continue
1464 continue
1444 else:
1465 else:
1445 if exact:
1466 if exact:
1446 ui.warn(
1467 ui.warn(
1447 _(b'%s: not copying - file is not managed\n') % rel
1468 _(b'%s: not copying - file is not managed\n') % rel
1448 )
1469 )
1449 continue
1470 continue
1450
1471
1451 # abs: hgsep
1472 # abs: hgsep
1452 # rel: ossep
1473 # rel: ossep
1453 srcs.append((abs, rel, exact))
1474 srcs.append((abs, rel, exact))
1454 return srcs
1475 return srcs
1455
1476
1456 # abssrc: hgsep
1477 # abssrc: hgsep
1457 # relsrc: ossep
1478 # relsrc: ossep
1458 # otarget: ossep
1479 # otarget: ossep
1459 def copyfile(abssrc, relsrc, otarget, exact):
1480 def copyfile(abssrc, relsrc, otarget, exact):
1460 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1481 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1461 if b'/' in abstarget:
1482 if b'/' in abstarget:
1462 # We cannot normalize abstarget itself, this would prevent
1483 # We cannot normalize abstarget itself, this would prevent
1463 # case only renames, like a => A.
1484 # case only renames, like a => A.
1464 abspath, absname = abstarget.rsplit(b'/', 1)
1485 abspath, absname = abstarget.rsplit(b'/', 1)
1465 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1486 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1466 reltarget = repo.pathto(abstarget, cwd)
1487 reltarget = repo.pathto(abstarget, cwd)
1467 target = repo.wjoin(abstarget)
1488 target = repo.wjoin(abstarget)
1468 src = repo.wjoin(abssrc)
1489 src = repo.wjoin(abssrc)
1469 state = repo.dirstate[abstarget]
1490 state = repo.dirstate[abstarget]
1470
1491
1471 scmutil.checkportable(ui, abstarget)
1492 scmutil.checkportable(ui, abstarget)
1472
1493
1473 # check for collisions
1494 # check for collisions
1474 prevsrc = targets.get(abstarget)
1495 prevsrc = targets.get(abstarget)
1475 if prevsrc is not None:
1496 if prevsrc is not None:
1476 ui.warn(
1497 ui.warn(
1477 _(b'%s: not overwriting - %s collides with %s\n')
1498 _(b'%s: not overwriting - %s collides with %s\n')
1478 % (
1499 % (
1479 reltarget,
1500 reltarget,
1480 repo.pathto(abssrc, cwd),
1501 repo.pathto(abssrc, cwd),
1481 repo.pathto(prevsrc, cwd),
1502 repo.pathto(prevsrc, cwd),
1482 )
1503 )
1483 )
1504 )
1484 return True # report a failure
1505 return True # report a failure
1485
1506
1486 # check for overwrites
1507 # check for overwrites
1487 exists = os.path.lexists(target)
1508 exists = os.path.lexists(target)
1488 samefile = False
1509 samefile = False
1489 if exists and abssrc != abstarget:
1510 if exists and abssrc != abstarget:
1490 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1511 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1491 abstarget
1512 abstarget
1492 ):
1513 ):
1493 if not rename:
1514 if not rename:
1494 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1515 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1495 return True # report a failure
1516 return True # report a failure
1496 exists = False
1517 exists = False
1497 samefile = True
1518 samefile = True
1498
1519
1499 if not after and exists or after and state in b'mn':
1520 if not after and exists or after and state in b'mn':
1500 if not opts[b'force']:
1521 if not opts[b'force']:
1501 if state in b'mn':
1522 if state in b'mn':
1502 msg = _(b'%s: not overwriting - file already committed\n')
1523 msg = _(b'%s: not overwriting - file already committed\n')
1503 if after:
1524 if after:
1504 flags = b'--after --force'
1525 flags = b'--after --force'
1505 else:
1526 else:
1506 flags = b'--force'
1527 flags = b'--force'
1507 if rename:
1528 if rename:
1508 hint = (
1529 hint = (
1509 _(
1530 _(
1510 b"('hg rename %s' to replace the file by "
1531 b"('hg rename %s' to replace the file by "
1511 b'recording a rename)\n'
1532 b'recording a rename)\n'
1512 )
1533 )
1513 % flags
1534 % flags
1514 )
1535 )
1515 else:
1536 else:
1516 hint = (
1537 hint = (
1517 _(
1538 _(
1518 b"('hg copy %s' to replace the file by "
1539 b"('hg copy %s' to replace the file by "
1519 b'recording a copy)\n'
1540 b'recording a copy)\n'
1520 )
1541 )
1521 % flags
1542 % flags
1522 )
1543 )
1523 else:
1544 else:
1524 msg = _(b'%s: not overwriting - file exists\n')
1545 msg = _(b'%s: not overwriting - file exists\n')
1525 if rename:
1546 if rename:
1526 hint = _(
1547 hint = _(
1527 b"('hg rename --after' to record the rename)\n"
1548 b"('hg rename --after' to record the rename)\n"
1528 )
1549 )
1529 else:
1550 else:
1530 hint = _(b"('hg copy --after' to record the copy)\n")
1551 hint = _(b"('hg copy --after' to record the copy)\n")
1531 ui.warn(msg % reltarget)
1552 ui.warn(msg % reltarget)
1532 ui.warn(hint)
1553 ui.warn(hint)
1533 return True # report a failure
1554 return True # report a failure
1534
1555
1535 if after:
1556 if after:
1536 if not exists:
1557 if not exists:
1537 if rename:
1558 if rename:
1538 ui.warn(
1559 ui.warn(
1539 _(b'%s: not recording move - %s does not exist\n')
1560 _(b'%s: not recording move - %s does not exist\n')
1540 % (relsrc, reltarget)
1561 % (relsrc, reltarget)
1541 )
1562 )
1542 else:
1563 else:
1543 ui.warn(
1564 ui.warn(
1544 _(b'%s: not recording copy - %s does not exist\n')
1565 _(b'%s: not recording copy - %s does not exist\n')
1545 % (relsrc, reltarget)
1566 % (relsrc, reltarget)
1546 )
1567 )
1547 return True # report a failure
1568 return True # report a failure
1548 elif not dryrun:
1569 elif not dryrun:
1549 try:
1570 try:
1550 if exists:
1571 if exists:
1551 os.unlink(target)
1572 os.unlink(target)
1552 targetdir = os.path.dirname(target) or b'.'
1573 targetdir = os.path.dirname(target) or b'.'
1553 if not os.path.isdir(targetdir):
1574 if not os.path.isdir(targetdir):
1554 os.makedirs(targetdir)
1575 os.makedirs(targetdir)
1555 if samefile:
1576 if samefile:
1556 tmp = target + b"~hgrename"
1577 tmp = target + b"~hgrename"
1557 os.rename(src, tmp)
1578 os.rename(src, tmp)
1558 os.rename(tmp, target)
1579 os.rename(tmp, target)
1559 else:
1580 else:
1560 # Preserve stat info on renames, not on copies; this matches
1581 # Preserve stat info on renames, not on copies; this matches
1561 # Linux CLI behavior.
1582 # Linux CLI behavior.
1562 util.copyfile(src, target, copystat=rename)
1583 util.copyfile(src, target, copystat=rename)
1563 srcexists = True
1584 srcexists = True
1564 except IOError as inst:
1585 except IOError as inst:
1565 if inst.errno == errno.ENOENT:
1586 if inst.errno == errno.ENOENT:
1566 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1587 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1567 srcexists = False
1588 srcexists = False
1568 else:
1589 else:
1569 ui.warn(
1590 ui.warn(
1570 _(b'%s: cannot copy - %s\n')
1591 _(b'%s: cannot copy - %s\n')
1571 % (relsrc, encoding.strtolocal(inst.strerror))
1592 % (relsrc, encoding.strtolocal(inst.strerror))
1572 )
1593 )
1573 return True # report a failure
1594 return True # report a failure
1574
1595
1575 if ui.verbose or not exact:
1596 if ui.verbose or not exact:
1576 if rename:
1597 if rename:
1577 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1598 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1578 else:
1599 else:
1579 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1600 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1580
1601
1581 targets[abstarget] = abssrc
1602 targets[abstarget] = abssrc
1582
1603
1583 # fix up dirstate
1604 # fix up dirstate
1584 scmutil.dirstatecopy(
1605 scmutil.dirstatecopy(
1585 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1606 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1586 )
1607 )
1587 if rename and not dryrun:
1608 if rename and not dryrun:
1588 if not after and srcexists and not samefile:
1609 if not after and srcexists and not samefile:
1589 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1610 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1590 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1611 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1591 ctx.forget([abssrc])
1612 ctx.forget([abssrc])
1592
1613
1593 # pat: ossep
1614 # pat: ossep
1594 # dest ossep
1615 # dest ossep
1595 # srcs: list of (hgsep, hgsep, ossep, bool)
1616 # srcs: list of (hgsep, hgsep, ossep, bool)
1596 # return: function that takes hgsep and returns ossep
1617 # return: function that takes hgsep and returns ossep
1597 def targetpathfn(pat, dest, srcs):
1618 def targetpathfn(pat, dest, srcs):
1598 if os.path.isdir(pat):
1619 if os.path.isdir(pat):
1599 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1620 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1600 abspfx = util.localpath(abspfx)
1621 abspfx = util.localpath(abspfx)
1601 if destdirexists:
1622 if destdirexists:
1602 striplen = len(os.path.split(abspfx)[0])
1623 striplen = len(os.path.split(abspfx)[0])
1603 else:
1624 else:
1604 striplen = len(abspfx)
1625 striplen = len(abspfx)
1605 if striplen:
1626 if striplen:
1606 striplen += len(pycompat.ossep)
1627 striplen += len(pycompat.ossep)
1607 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1628 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1608 elif destdirexists:
1629 elif destdirexists:
1609 res = lambda p: os.path.join(
1630 res = lambda p: os.path.join(
1610 dest, os.path.basename(util.localpath(p))
1631 dest, os.path.basename(util.localpath(p))
1611 )
1632 )
1612 else:
1633 else:
1613 res = lambda p: dest
1634 res = lambda p: dest
1614 return res
1635 return res
1615
1636
1616 # pat: ossep
1637 # pat: ossep
1617 # dest ossep
1638 # dest ossep
1618 # srcs: list of (hgsep, hgsep, ossep, bool)
1639 # srcs: list of (hgsep, hgsep, ossep, bool)
1619 # return: function that takes hgsep and returns ossep
1640 # return: function that takes hgsep and returns ossep
1620 def targetpathafterfn(pat, dest, srcs):
1641 def targetpathafterfn(pat, dest, srcs):
1621 if matchmod.patkind(pat):
1642 if matchmod.patkind(pat):
1622 # a mercurial pattern
1643 # a mercurial pattern
1623 res = lambda p: os.path.join(
1644 res = lambda p: os.path.join(
1624 dest, os.path.basename(util.localpath(p))
1645 dest, os.path.basename(util.localpath(p))
1625 )
1646 )
1626 else:
1647 else:
1627 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1648 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1628 if len(abspfx) < len(srcs[0][0]):
1649 if len(abspfx) < len(srcs[0][0]):
1629 # A directory. Either the target path contains the last
1650 # A directory. Either the target path contains the last
1630 # component of the source path or it does not.
1651 # component of the source path or it does not.
1631 def evalpath(striplen):
1652 def evalpath(striplen):
1632 score = 0
1653 score = 0
1633 for s in srcs:
1654 for s in srcs:
1634 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1655 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1635 if os.path.lexists(t):
1656 if os.path.lexists(t):
1636 score += 1
1657 score += 1
1637 return score
1658 return score
1638
1659
1639 abspfx = util.localpath(abspfx)
1660 abspfx = util.localpath(abspfx)
1640 striplen = len(abspfx)
1661 striplen = len(abspfx)
1641 if striplen:
1662 if striplen:
1642 striplen += len(pycompat.ossep)
1663 striplen += len(pycompat.ossep)
1643 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1664 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1644 score = evalpath(striplen)
1665 score = evalpath(striplen)
1645 striplen1 = len(os.path.split(abspfx)[0])
1666 striplen1 = len(os.path.split(abspfx)[0])
1646 if striplen1:
1667 if striplen1:
1647 striplen1 += len(pycompat.ossep)
1668 striplen1 += len(pycompat.ossep)
1648 if evalpath(striplen1) > score:
1669 if evalpath(striplen1) > score:
1649 striplen = striplen1
1670 striplen = striplen1
1650 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1671 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1651 else:
1672 else:
1652 # a file
1673 # a file
1653 if destdirexists:
1674 if destdirexists:
1654 res = lambda p: os.path.join(
1675 res = lambda p: os.path.join(
1655 dest, os.path.basename(util.localpath(p))
1676 dest, os.path.basename(util.localpath(p))
1656 )
1677 )
1657 else:
1678 else:
1658 res = lambda p: dest
1679 res = lambda p: dest
1659 return res
1680 return res
1660
1681
1661 pats = scmutil.expandpats(pats)
1682 pats = scmutil.expandpats(pats)
1662 if not pats:
1683 if not pats:
1663 raise error.Abort(_(b'no source or destination specified'))
1684 raise error.Abort(_(b'no source or destination specified'))
1664 if len(pats) == 1:
1685 if len(pats) == 1:
1665 raise error.Abort(_(b'no destination specified'))
1686 raise error.Abort(_(b'no destination specified'))
1666 dest = pats.pop()
1687 dest = pats.pop()
1667 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1688 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1668 if not destdirexists:
1689 if not destdirexists:
1669 if len(pats) > 1 or matchmod.patkind(pats[0]):
1690 if len(pats) > 1 or matchmod.patkind(pats[0]):
1670 raise error.Abort(
1691 raise error.Abort(
1671 _(
1692 _(
1672 b'with multiple sources, destination must be an '
1693 b'with multiple sources, destination must be an '
1673 b'existing directory'
1694 b'existing directory'
1674 )
1695 )
1675 )
1696 )
1676 if util.endswithsep(dest):
1697 if util.endswithsep(dest):
1677 raise error.Abort(_(b'destination %s is not a directory') % dest)
1698 raise error.Abort(_(b'destination %s is not a directory') % dest)
1678
1699
1679 tfn = targetpathfn
1700 tfn = targetpathfn
1680 if after:
1701 if after:
1681 tfn = targetpathafterfn
1702 tfn = targetpathafterfn
1682 copylist = []
1703 copylist = []
1683 for pat in pats:
1704 for pat in pats:
1684 srcs = walkpat(pat)
1705 srcs = walkpat(pat)
1685 if not srcs:
1706 if not srcs:
1686 continue
1707 continue
1687 copylist.append((tfn(pat, dest, srcs), srcs))
1708 copylist.append((tfn(pat, dest, srcs), srcs))
1688 if not copylist:
1709 if not copylist:
1689 raise error.Abort(_(b'no files to copy'))
1710 raise error.Abort(_(b'no files to copy'))
1690
1711
1691 errors = 0
1712 errors = 0
1692 for targetpath, srcs in copylist:
1713 for targetpath, srcs in copylist:
1693 for abssrc, relsrc, exact in srcs:
1714 for abssrc, relsrc, exact in srcs:
1694 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1715 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1695 errors += 1
1716 errors += 1
1696
1717
1697 return errors != 0
1718 return errors != 0
1698
1719
1699
1720
1700 ## facility to let extension process additional data into an import patch
1721 ## facility to let extension process additional data into an import patch
1701 # list of identifier to be executed in order
1722 # list of identifier to be executed in order
1702 extrapreimport = [] # run before commit
1723 extrapreimport = [] # run before commit
1703 extrapostimport = [] # run after commit
1724 extrapostimport = [] # run after commit
1704 # mapping from identifier to actual import function
1725 # mapping from identifier to actual import function
1705 #
1726 #
1706 # 'preimport' are run before the commit is made and are provided the following
1727 # 'preimport' are run before the commit is made and are provided the following
1707 # arguments:
1728 # arguments:
1708 # - repo: the localrepository instance,
1729 # - repo: the localrepository instance,
1709 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1730 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1710 # - extra: the future extra dictionary of the changeset, please mutate it,
1731 # - extra: the future extra dictionary of the changeset, please mutate it,
1711 # - opts: the import options.
1732 # - opts: the import options.
1712 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1733 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1713 # mutation of in memory commit and more. Feel free to rework the code to get
1734 # mutation of in memory commit and more. Feel free to rework the code to get
1714 # there.
1735 # there.
1715 extrapreimportmap = {}
1736 extrapreimportmap = {}
1716 # 'postimport' are run after the commit is made and are provided the following
1737 # 'postimport' are run after the commit is made and are provided the following
1717 # argument:
1738 # argument:
1718 # - ctx: the changectx created by import.
1739 # - ctx: the changectx created by import.
1719 extrapostimportmap = {}
1740 extrapostimportmap = {}
1720
1741
1721
1742
1722 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1743 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1723 """Utility function used by commands.import to import a single patch
1744 """Utility function used by commands.import to import a single patch
1724
1745
1725 This function is explicitly defined here to help the evolve extension to
1746 This function is explicitly defined here to help the evolve extension to
1726 wrap this part of the import logic.
1747 wrap this part of the import logic.
1727
1748
1728 The API is currently a bit ugly because it a simple code translation from
1749 The API is currently a bit ugly because it a simple code translation from
1729 the import command. Feel free to make it better.
1750 the import command. Feel free to make it better.
1730
1751
1731 :patchdata: a dictionary containing parsed patch data (such as from
1752 :patchdata: a dictionary containing parsed patch data (such as from
1732 ``patch.extract()``)
1753 ``patch.extract()``)
1733 :parents: nodes that will be parent of the created commit
1754 :parents: nodes that will be parent of the created commit
1734 :opts: the full dict of option passed to the import command
1755 :opts: the full dict of option passed to the import command
1735 :msgs: list to save commit message to.
1756 :msgs: list to save commit message to.
1736 (used in case we need to save it when failing)
1757 (used in case we need to save it when failing)
1737 :updatefunc: a function that update a repo to a given node
1758 :updatefunc: a function that update a repo to a given node
1738 updatefunc(<repo>, <node>)
1759 updatefunc(<repo>, <node>)
1739 """
1760 """
1740 # avoid cycle context -> subrepo -> cmdutil
1761 # avoid cycle context -> subrepo -> cmdutil
1741 from . import context
1762 from . import context
1742
1763
1743 tmpname = patchdata.get(b'filename')
1764 tmpname = patchdata.get(b'filename')
1744 message = patchdata.get(b'message')
1765 message = patchdata.get(b'message')
1745 user = opts.get(b'user') or patchdata.get(b'user')
1766 user = opts.get(b'user') or patchdata.get(b'user')
1746 date = opts.get(b'date') or patchdata.get(b'date')
1767 date = opts.get(b'date') or patchdata.get(b'date')
1747 branch = patchdata.get(b'branch')
1768 branch = patchdata.get(b'branch')
1748 nodeid = patchdata.get(b'nodeid')
1769 nodeid = patchdata.get(b'nodeid')
1749 p1 = patchdata.get(b'p1')
1770 p1 = patchdata.get(b'p1')
1750 p2 = patchdata.get(b'p2')
1771 p2 = patchdata.get(b'p2')
1751
1772
1752 nocommit = opts.get(b'no_commit')
1773 nocommit = opts.get(b'no_commit')
1753 importbranch = opts.get(b'import_branch')
1774 importbranch = opts.get(b'import_branch')
1754 update = not opts.get(b'bypass')
1775 update = not opts.get(b'bypass')
1755 strip = opts[b"strip"]
1776 strip = opts[b"strip"]
1756 prefix = opts[b"prefix"]
1777 prefix = opts[b"prefix"]
1757 sim = float(opts.get(b'similarity') or 0)
1778 sim = float(opts.get(b'similarity') or 0)
1758
1779
1759 if not tmpname:
1780 if not tmpname:
1760 return None, None, False
1781 return None, None, False
1761
1782
1762 rejects = False
1783 rejects = False
1763
1784
1764 cmdline_message = logmessage(ui, opts)
1785 cmdline_message = logmessage(ui, opts)
1765 if cmdline_message:
1786 if cmdline_message:
1766 # pickup the cmdline msg
1787 # pickup the cmdline msg
1767 message = cmdline_message
1788 message = cmdline_message
1768 elif message:
1789 elif message:
1769 # pickup the patch msg
1790 # pickup the patch msg
1770 message = message.strip()
1791 message = message.strip()
1771 else:
1792 else:
1772 # launch the editor
1793 # launch the editor
1773 message = None
1794 message = None
1774 ui.debug(b'message:\n%s\n' % (message or b''))
1795 ui.debug(b'message:\n%s\n' % (message or b''))
1775
1796
1776 if len(parents) == 1:
1797 if len(parents) == 1:
1777 parents.append(repo[nullid])
1798 parents.append(repo[nullid])
1778 if opts.get(b'exact'):
1799 if opts.get(b'exact'):
1779 if not nodeid or not p1:
1800 if not nodeid or not p1:
1780 raise error.Abort(_(b'not a Mercurial patch'))
1801 raise error.Abort(_(b'not a Mercurial patch'))
1781 p1 = repo[p1]
1802 p1 = repo[p1]
1782 p2 = repo[p2 or nullid]
1803 p2 = repo[p2 or nullid]
1783 elif p2:
1804 elif p2:
1784 try:
1805 try:
1785 p1 = repo[p1]
1806 p1 = repo[p1]
1786 p2 = repo[p2]
1807 p2 = repo[p2]
1787 # Without any options, consider p2 only if the
1808 # Without any options, consider p2 only if the
1788 # patch is being applied on top of the recorded
1809 # patch is being applied on top of the recorded
1789 # first parent.
1810 # first parent.
1790 if p1 != parents[0]:
1811 if p1 != parents[0]:
1791 p1 = parents[0]
1812 p1 = parents[0]
1792 p2 = repo[nullid]
1813 p2 = repo[nullid]
1793 except error.RepoError:
1814 except error.RepoError:
1794 p1, p2 = parents
1815 p1, p2 = parents
1795 if p2.node() == nullid:
1816 if p2.node() == nullid:
1796 ui.warn(
1817 ui.warn(
1797 _(
1818 _(
1798 b"warning: import the patch as a normal revision\n"
1819 b"warning: import the patch as a normal revision\n"
1799 b"(use --exact to import the patch as a merge)\n"
1820 b"(use --exact to import the patch as a merge)\n"
1800 )
1821 )
1801 )
1822 )
1802 else:
1823 else:
1803 p1, p2 = parents
1824 p1, p2 = parents
1804
1825
1805 n = None
1826 n = None
1806 if update:
1827 if update:
1807 if p1 != parents[0]:
1828 if p1 != parents[0]:
1808 updatefunc(repo, p1.node())
1829 updatefunc(repo, p1.node())
1809 if p2 != parents[1]:
1830 if p2 != parents[1]:
1810 repo.setparents(p1.node(), p2.node())
1831 repo.setparents(p1.node(), p2.node())
1811
1832
1812 if opts.get(b'exact') or importbranch:
1833 if opts.get(b'exact') or importbranch:
1813 repo.dirstate.setbranch(branch or b'default')
1834 repo.dirstate.setbranch(branch or b'default')
1814
1835
1815 partial = opts.get(b'partial', False)
1836 partial = opts.get(b'partial', False)
1816 files = set()
1837 files = set()
1817 try:
1838 try:
1818 patch.patch(
1839 patch.patch(
1819 ui,
1840 ui,
1820 repo,
1841 repo,
1821 tmpname,
1842 tmpname,
1822 strip=strip,
1843 strip=strip,
1823 prefix=prefix,
1844 prefix=prefix,
1824 files=files,
1845 files=files,
1825 eolmode=None,
1846 eolmode=None,
1826 similarity=sim / 100.0,
1847 similarity=sim / 100.0,
1827 )
1848 )
1828 except error.PatchError as e:
1849 except error.PatchError as e:
1829 if not partial:
1850 if not partial:
1830 raise error.Abort(pycompat.bytestr(e))
1851 raise error.Abort(pycompat.bytestr(e))
1831 if partial:
1852 if partial:
1832 rejects = True
1853 rejects = True
1833
1854
1834 files = list(files)
1855 files = list(files)
1835 if nocommit:
1856 if nocommit:
1836 if message:
1857 if message:
1837 msgs.append(message)
1858 msgs.append(message)
1838 else:
1859 else:
1839 if opts.get(b'exact') or p2:
1860 if opts.get(b'exact') or p2:
1840 # If you got here, you either use --force and know what
1861 # If you got here, you either use --force and know what
1841 # you are doing or used --exact or a merge patch while
1862 # you are doing or used --exact or a merge patch while
1842 # being updated to its first parent.
1863 # being updated to its first parent.
1843 m = None
1864 m = None
1844 else:
1865 else:
1845 m = scmutil.matchfiles(repo, files or [])
1866 m = scmutil.matchfiles(repo, files or [])
1846 editform = mergeeditform(repo[None], b'import.normal')
1867 editform = mergeeditform(repo[None], b'import.normal')
1847 if opts.get(b'exact'):
1868 if opts.get(b'exact'):
1848 editor = None
1869 editor = None
1849 else:
1870 else:
1850 editor = getcommiteditor(
1871 editor = getcommiteditor(
1851 editform=editform, **pycompat.strkwargs(opts)
1872 editform=editform, **pycompat.strkwargs(opts)
1852 )
1873 )
1853 extra = {}
1874 extra = {}
1854 for idfunc in extrapreimport:
1875 for idfunc in extrapreimport:
1855 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1876 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
1856 overrides = {}
1877 overrides = {}
1857 if partial:
1878 if partial:
1858 overrides[(b'ui', b'allowemptycommit')] = True
1879 overrides[(b'ui', b'allowemptycommit')] = True
1859 if opts.get(b'secret'):
1880 if opts.get(b'secret'):
1860 overrides[(b'phases', b'new-commit')] = b'secret'
1881 overrides[(b'phases', b'new-commit')] = b'secret'
1861 with repo.ui.configoverride(overrides, b'import'):
1882 with repo.ui.configoverride(overrides, b'import'):
1862 n = repo.commit(
1883 n = repo.commit(
1863 message, user, date, match=m, editor=editor, extra=extra
1884 message, user, date, match=m, editor=editor, extra=extra
1864 )
1885 )
1865 for idfunc in extrapostimport:
1886 for idfunc in extrapostimport:
1866 extrapostimportmap[idfunc](repo[n])
1887 extrapostimportmap[idfunc](repo[n])
1867 else:
1888 else:
1868 if opts.get(b'exact') or importbranch:
1889 if opts.get(b'exact') or importbranch:
1869 branch = branch or b'default'
1890 branch = branch or b'default'
1870 else:
1891 else:
1871 branch = p1.branch()
1892 branch = p1.branch()
1872 store = patch.filestore()
1893 store = patch.filestore()
1873 try:
1894 try:
1874 files = set()
1895 files = set()
1875 try:
1896 try:
1876 patch.patchrepo(
1897 patch.patchrepo(
1877 ui,
1898 ui,
1878 repo,
1899 repo,
1879 p1,
1900 p1,
1880 store,
1901 store,
1881 tmpname,
1902 tmpname,
1882 strip,
1903 strip,
1883 prefix,
1904 prefix,
1884 files,
1905 files,
1885 eolmode=None,
1906 eolmode=None,
1886 )
1907 )
1887 except error.PatchError as e:
1908 except error.PatchError as e:
1888 raise error.Abort(stringutil.forcebytestr(e))
1909 raise error.Abort(stringutil.forcebytestr(e))
1889 if opts.get(b'exact'):
1910 if opts.get(b'exact'):
1890 editor = None
1911 editor = None
1891 else:
1912 else:
1892 editor = getcommiteditor(editform=b'import.bypass')
1913 editor = getcommiteditor(editform=b'import.bypass')
1893 memctx = context.memctx(
1914 memctx = context.memctx(
1894 repo,
1915 repo,
1895 (p1.node(), p2.node()),
1916 (p1.node(), p2.node()),
1896 message,
1917 message,
1897 files=files,
1918 files=files,
1898 filectxfn=store,
1919 filectxfn=store,
1899 user=user,
1920 user=user,
1900 date=date,
1921 date=date,
1901 branch=branch,
1922 branch=branch,
1902 editor=editor,
1923 editor=editor,
1903 )
1924 )
1904 n = memctx.commit()
1925 n = memctx.commit()
1905 finally:
1926 finally:
1906 store.close()
1927 store.close()
1907 if opts.get(b'exact') and nocommit:
1928 if opts.get(b'exact') and nocommit:
1908 # --exact with --no-commit is still useful in that it does merge
1929 # --exact with --no-commit is still useful in that it does merge
1909 # and branch bits
1930 # and branch bits
1910 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1931 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
1911 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1932 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
1912 raise error.Abort(_(b'patch is damaged or loses information'))
1933 raise error.Abort(_(b'patch is damaged or loses information'))
1913 msg = _(b'applied to working directory')
1934 msg = _(b'applied to working directory')
1914 if n:
1935 if n:
1915 # i18n: refers to a short changeset id
1936 # i18n: refers to a short changeset id
1916 msg = _(b'created %s') % short(n)
1937 msg = _(b'created %s') % short(n)
1917 return msg, n, rejects
1938 return msg, n, rejects
1918
1939
1919
1940
1920 # facility to let extensions include additional data in an exported patch
1941 # facility to let extensions include additional data in an exported patch
1921 # list of identifiers to be executed in order
1942 # list of identifiers to be executed in order
1922 extraexport = []
1943 extraexport = []
1923 # mapping from identifier to actual export function
1944 # mapping from identifier to actual export function
1924 # function as to return a string to be added to the header or None
1945 # function as to return a string to be added to the header or None
1925 # it is given two arguments (sequencenumber, changectx)
1946 # it is given two arguments (sequencenumber, changectx)
1926 extraexportmap = {}
1947 extraexportmap = {}
1927
1948
1928
1949
1929 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1950 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
1930 node = scmutil.binnode(ctx)
1951 node = scmutil.binnode(ctx)
1931 parents = [p.node() for p in ctx.parents() if p]
1952 parents = [p.node() for p in ctx.parents() if p]
1932 branch = ctx.branch()
1953 branch = ctx.branch()
1933 if switch_parent:
1954 if switch_parent:
1934 parents.reverse()
1955 parents.reverse()
1935
1956
1936 if parents:
1957 if parents:
1937 prev = parents[0]
1958 prev = parents[0]
1938 else:
1959 else:
1939 prev = nullid
1960 prev = nullid
1940
1961
1941 fm.context(ctx=ctx)
1962 fm.context(ctx=ctx)
1942 fm.plain(b'# HG changeset patch\n')
1963 fm.plain(b'# HG changeset patch\n')
1943 fm.write(b'user', b'# User %s\n', ctx.user())
1964 fm.write(b'user', b'# User %s\n', ctx.user())
1944 fm.plain(b'# Date %d %d\n' % ctx.date())
1965 fm.plain(b'# Date %d %d\n' % ctx.date())
1945 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1966 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
1946 fm.condwrite(
1967 fm.condwrite(
1947 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1968 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
1948 )
1969 )
1949 fm.write(b'node', b'# Node ID %s\n', hex(node))
1970 fm.write(b'node', b'# Node ID %s\n', hex(node))
1950 fm.plain(b'# Parent %s\n' % hex(prev))
1971 fm.plain(b'# Parent %s\n' % hex(prev))
1951 if len(parents) > 1:
1972 if len(parents) > 1:
1952 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1973 fm.plain(b'# Parent %s\n' % hex(parents[1]))
1953 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1974 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
1954
1975
1955 # TODO: redesign extraexportmap function to support formatter
1976 # TODO: redesign extraexportmap function to support formatter
1956 for headerid in extraexport:
1977 for headerid in extraexport:
1957 header = extraexportmap[headerid](seqno, ctx)
1978 header = extraexportmap[headerid](seqno, ctx)
1958 if header is not None:
1979 if header is not None:
1959 fm.plain(b'# %s\n' % header)
1980 fm.plain(b'# %s\n' % header)
1960
1981
1961 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1982 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
1962 fm.plain(b'\n')
1983 fm.plain(b'\n')
1963
1984
1964 if fm.isplain():
1985 if fm.isplain():
1965 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1986 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
1966 for chunk, label in chunkiter:
1987 for chunk, label in chunkiter:
1967 fm.plain(chunk, label=label)
1988 fm.plain(chunk, label=label)
1968 else:
1989 else:
1969 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1990 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
1970 # TODO: make it structured?
1991 # TODO: make it structured?
1971 fm.data(diff=b''.join(chunkiter))
1992 fm.data(diff=b''.join(chunkiter))
1972
1993
1973
1994
1974 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1995 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
1975 """Export changesets to stdout or a single file"""
1996 """Export changesets to stdout or a single file"""
1976 for seqno, rev in enumerate(revs, 1):
1997 for seqno, rev in enumerate(revs, 1):
1977 ctx = repo[rev]
1998 ctx = repo[rev]
1978 if not dest.startswith(b'<'):
1999 if not dest.startswith(b'<'):
1979 repo.ui.note(b"%s\n" % dest)
2000 repo.ui.note(b"%s\n" % dest)
1980 fm.startitem()
2001 fm.startitem()
1981 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
2002 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
1982
2003
1983
2004
1984 def _exportfntemplate(
2005 def _exportfntemplate(
1985 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
2006 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
1986 ):
2007 ):
1987 """Export changesets to possibly multiple files"""
2008 """Export changesets to possibly multiple files"""
1988 total = len(revs)
2009 total = len(revs)
1989 revwidth = max(len(str(rev)) for rev in revs)
2010 revwidth = max(len(str(rev)) for rev in revs)
1990 filemap = util.sortdict() # filename: [(seqno, rev), ...]
2011 filemap = util.sortdict() # filename: [(seqno, rev), ...]
1991
2012
1992 for seqno, rev in enumerate(revs, 1):
2013 for seqno, rev in enumerate(revs, 1):
1993 ctx = repo[rev]
2014 ctx = repo[rev]
1994 dest = makefilename(
2015 dest = makefilename(
1995 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
2016 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
1996 )
2017 )
1997 filemap.setdefault(dest, []).append((seqno, rev))
2018 filemap.setdefault(dest, []).append((seqno, rev))
1998
2019
1999 for dest in filemap:
2020 for dest in filemap:
2000 with formatter.maybereopen(basefm, dest) as fm:
2021 with formatter.maybereopen(basefm, dest) as fm:
2001 repo.ui.note(b"%s\n" % dest)
2022 repo.ui.note(b"%s\n" % dest)
2002 for seqno, rev in filemap[dest]:
2023 for seqno, rev in filemap[dest]:
2003 fm.startitem()
2024 fm.startitem()
2004 ctx = repo[rev]
2025 ctx = repo[rev]
2005 _exportsingle(
2026 _exportsingle(
2006 repo, ctx, fm, match, switch_parent, seqno, diffopts
2027 repo, ctx, fm, match, switch_parent, seqno, diffopts
2007 )
2028 )
2008
2029
2009
2030
2010 def _prefetchchangedfiles(repo, revs, match):
2031 def _prefetchchangedfiles(repo, revs, match):
2011 allfiles = set()
2032 allfiles = set()
2012 for rev in revs:
2033 for rev in revs:
2013 for file in repo[rev].files():
2034 for file in repo[rev].files():
2014 if not match or match(file):
2035 if not match or match(file):
2015 allfiles.add(file)
2036 allfiles.add(file)
2016 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
2037 scmutil.prefetchfiles(repo, revs, scmutil.matchfiles(repo, allfiles))
2017
2038
2018
2039
2019 def export(
2040 def export(
2020 repo,
2041 repo,
2021 revs,
2042 revs,
2022 basefm,
2043 basefm,
2023 fntemplate=b'hg-%h.patch',
2044 fntemplate=b'hg-%h.patch',
2024 switch_parent=False,
2045 switch_parent=False,
2025 opts=None,
2046 opts=None,
2026 match=None,
2047 match=None,
2027 ):
2048 ):
2028 '''export changesets as hg patches
2049 '''export changesets as hg patches
2029
2050
2030 Args:
2051 Args:
2031 repo: The repository from which we're exporting revisions.
2052 repo: The repository from which we're exporting revisions.
2032 revs: A list of revisions to export as revision numbers.
2053 revs: A list of revisions to export as revision numbers.
2033 basefm: A formatter to which patches should be written.
2054 basefm: A formatter to which patches should be written.
2034 fntemplate: An optional string to use for generating patch file names.
2055 fntemplate: An optional string to use for generating patch file names.
2035 switch_parent: If True, show diffs against second parent when not nullid.
2056 switch_parent: If True, show diffs against second parent when not nullid.
2036 Default is false, which always shows diff against p1.
2057 Default is false, which always shows diff against p1.
2037 opts: diff options to use for generating the patch.
2058 opts: diff options to use for generating the patch.
2038 match: If specified, only export changes to files matching this matcher.
2059 match: If specified, only export changes to files matching this matcher.
2039
2060
2040 Returns:
2061 Returns:
2041 Nothing.
2062 Nothing.
2042
2063
2043 Side Effect:
2064 Side Effect:
2044 "HG Changeset Patch" data is emitted to one of the following
2065 "HG Changeset Patch" data is emitted to one of the following
2045 destinations:
2066 destinations:
2046 fntemplate specified: Each rev is written to a unique file named using
2067 fntemplate specified: Each rev is written to a unique file named using
2047 the given template.
2068 the given template.
2048 Otherwise: All revs will be written to basefm.
2069 Otherwise: All revs will be written to basefm.
2049 '''
2070 '''
2050 _prefetchchangedfiles(repo, revs, match)
2071 _prefetchchangedfiles(repo, revs, match)
2051
2072
2052 if not fntemplate:
2073 if not fntemplate:
2053 _exportfile(
2074 _exportfile(
2054 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2075 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2055 )
2076 )
2056 else:
2077 else:
2057 _exportfntemplate(
2078 _exportfntemplate(
2058 repo, revs, basefm, fntemplate, switch_parent, opts, match
2079 repo, revs, basefm, fntemplate, switch_parent, opts, match
2059 )
2080 )
2060
2081
2061
2082
2062 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2083 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2063 """Export changesets to the given file stream"""
2084 """Export changesets to the given file stream"""
2064 _prefetchchangedfiles(repo, revs, match)
2085 _prefetchchangedfiles(repo, revs, match)
2065
2086
2066 dest = getattr(fp, 'name', b'<unnamed>')
2087 dest = getattr(fp, 'name', b'<unnamed>')
2067 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2088 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2068 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2089 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2069
2090
2070
2091
2071 def showmarker(fm, marker, index=None):
2092 def showmarker(fm, marker, index=None):
2072 """utility function to display obsolescence marker in a readable way
2093 """utility function to display obsolescence marker in a readable way
2073
2094
2074 To be used by debug function."""
2095 To be used by debug function."""
2075 if index is not None:
2096 if index is not None:
2076 fm.write(b'index', b'%i ', index)
2097 fm.write(b'index', b'%i ', index)
2077 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2098 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2078 succs = marker.succnodes()
2099 succs = marker.succnodes()
2079 fm.condwrite(
2100 fm.condwrite(
2080 succs,
2101 succs,
2081 b'succnodes',
2102 b'succnodes',
2082 b'%s ',
2103 b'%s ',
2083 fm.formatlist(map(hex, succs), name=b'node'),
2104 fm.formatlist(map(hex, succs), name=b'node'),
2084 )
2105 )
2085 fm.write(b'flag', b'%X ', marker.flags())
2106 fm.write(b'flag', b'%X ', marker.flags())
2086 parents = marker.parentnodes()
2107 parents = marker.parentnodes()
2087 if parents is not None:
2108 if parents is not None:
2088 fm.write(
2109 fm.write(
2089 b'parentnodes',
2110 b'parentnodes',
2090 b'{%s} ',
2111 b'{%s} ',
2091 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2112 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2092 )
2113 )
2093 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2114 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2094 meta = marker.metadata().copy()
2115 meta = marker.metadata().copy()
2095 meta.pop(b'date', None)
2116 meta.pop(b'date', None)
2096 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2117 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2097 fm.write(
2118 fm.write(
2098 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2119 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2099 )
2120 )
2100 fm.plain(b'\n')
2121 fm.plain(b'\n')
2101
2122
2102
2123
2103 def finddate(ui, repo, date):
2124 def finddate(ui, repo, date):
2104 """Find the tipmost changeset that matches the given date spec"""
2125 """Find the tipmost changeset that matches the given date spec"""
2105
2126
2106 df = dateutil.matchdate(date)
2127 df = dateutil.matchdate(date)
2107 m = scmutil.matchall(repo)
2128 m = scmutil.matchall(repo)
2108 results = {}
2129 results = {}
2109
2130
2110 def prep(ctx, fns):
2131 def prep(ctx, fns):
2111 d = ctx.date()
2132 d = ctx.date()
2112 if df(d[0]):
2133 if df(d[0]):
2113 results[ctx.rev()] = d
2134 results[ctx.rev()] = d
2114
2135
2115 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2136 for ctx in walkchangerevs(repo, m, {b'rev': None}, prep):
2116 rev = ctx.rev()
2137 rev = ctx.rev()
2117 if rev in results:
2138 if rev in results:
2118 ui.status(
2139 ui.status(
2119 _(b"found revision %d from %s\n")
2140 _(b"found revision %d from %s\n")
2120 % (rev, dateutil.datestr(results[rev]))
2141 % (rev, dateutil.datestr(results[rev]))
2121 )
2142 )
2122 return b'%d' % rev
2143 return b'%d' % rev
2123
2144
2124 raise error.Abort(_(b"revision matching date not found"))
2145 raise error.Abort(_(b"revision matching date not found"))
2125
2146
2126
2147
2127 def increasingwindows(windowsize=8, sizelimit=512):
2148 def increasingwindows(windowsize=8, sizelimit=512):
2128 while True:
2149 while True:
2129 yield windowsize
2150 yield windowsize
2130 if windowsize < sizelimit:
2151 if windowsize < sizelimit:
2131 windowsize *= 2
2152 windowsize *= 2
2132
2153
2133
2154
2134 def _walkrevs(repo, opts):
2155 def _walkrevs(repo, opts):
2135 # Default --rev value depends on --follow but --follow behavior
2156 # Default --rev value depends on --follow but --follow behavior
2136 # depends on revisions resolved from --rev...
2157 # depends on revisions resolved from --rev...
2137 follow = opts.get(b'follow') or opts.get(b'follow_first')
2158 follow = opts.get(b'follow') or opts.get(b'follow_first')
2138 if opts.get(b'rev'):
2159 if opts.get(b'rev'):
2139 revs = scmutil.revrange(repo, opts[b'rev'])
2160 revs = scmutil.revrange(repo, opts[b'rev'])
2140 elif follow and repo.dirstate.p1() == nullid:
2161 elif follow and repo.dirstate.p1() == nullid:
2141 revs = smartset.baseset()
2162 revs = smartset.baseset()
2142 elif follow:
2163 elif follow:
2143 revs = repo.revs(b'reverse(:.)')
2164 revs = repo.revs(b'reverse(:.)')
2144 else:
2165 else:
2145 revs = smartset.spanset(repo)
2166 revs = smartset.spanset(repo)
2146 revs.reverse()
2167 revs.reverse()
2147 return revs
2168 return revs
2148
2169
2149
2170
2150 class FileWalkError(Exception):
2171 class FileWalkError(Exception):
2151 pass
2172 pass
2152
2173
2153
2174
2154 def walkfilerevs(repo, match, follow, revs, fncache):
2175 def walkfilerevs(repo, match, follow, revs, fncache):
2155 '''Walks the file history for the matched files.
2176 '''Walks the file history for the matched files.
2156
2177
2157 Returns the changeset revs that are involved in the file history.
2178 Returns the changeset revs that are involved in the file history.
2158
2179
2159 Throws FileWalkError if the file history can't be walked using
2180 Throws FileWalkError if the file history can't be walked using
2160 filelogs alone.
2181 filelogs alone.
2161 '''
2182 '''
2162 wanted = set()
2183 wanted = set()
2163 copies = []
2184 copies = []
2164 minrev, maxrev = min(revs), max(revs)
2185 minrev, maxrev = min(revs), max(revs)
2165
2186
2166 def filerevs(filelog, last):
2187 def filerevs(filelog, last):
2167 """
2188 """
2168 Only files, no patterns. Check the history of each file.
2189 Only files, no patterns. Check the history of each file.
2169
2190
2170 Examines filelog entries within minrev, maxrev linkrev range
2191 Examines filelog entries within minrev, maxrev linkrev range
2171 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2192 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2172 tuples in backwards order
2193 tuples in backwards order
2173 """
2194 """
2174 cl_count = len(repo)
2195 cl_count = len(repo)
2175 revs = []
2196 revs = []
2176 for j in pycompat.xrange(0, last + 1):
2197 for j in pycompat.xrange(0, last + 1):
2177 linkrev = filelog.linkrev(j)
2198 linkrev = filelog.linkrev(j)
2178 if linkrev < minrev:
2199 if linkrev < minrev:
2179 continue
2200 continue
2180 # only yield rev for which we have the changelog, it can
2201 # only yield rev for which we have the changelog, it can
2181 # happen while doing "hg log" during a pull or commit
2202 # happen while doing "hg log" during a pull or commit
2182 if linkrev >= cl_count:
2203 if linkrev >= cl_count:
2183 break
2204 break
2184
2205
2185 parentlinkrevs = []
2206 parentlinkrevs = []
2186 for p in filelog.parentrevs(j):
2207 for p in filelog.parentrevs(j):
2187 if p != nullrev:
2208 if p != nullrev:
2188 parentlinkrevs.append(filelog.linkrev(p))
2209 parentlinkrevs.append(filelog.linkrev(p))
2189 n = filelog.node(j)
2210 n = filelog.node(j)
2190 revs.append(
2211 revs.append(
2191 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2212 (linkrev, parentlinkrevs, follow and filelog.renamed(n))
2192 )
2213 )
2193
2214
2194 return reversed(revs)
2215 return reversed(revs)
2195
2216
2196 def iterfiles():
2217 def iterfiles():
2197 pctx = repo[b'.']
2218 pctx = repo[b'.']
2198 for filename in match.files():
2219 for filename in match.files():
2199 if follow:
2220 if follow:
2200 if filename not in pctx:
2221 if filename not in pctx:
2201 raise error.Abort(
2222 raise error.Abort(
2202 _(
2223 _(
2203 b'cannot follow file not in parent '
2224 b'cannot follow file not in parent '
2204 b'revision: "%s"'
2225 b'revision: "%s"'
2205 )
2226 )
2206 % filename
2227 % filename
2207 )
2228 )
2208 yield filename, pctx[filename].filenode()
2229 yield filename, pctx[filename].filenode()
2209 else:
2230 else:
2210 yield filename, None
2231 yield filename, None
2211 for filename_node in copies:
2232 for filename_node in copies:
2212 yield filename_node
2233 yield filename_node
2213
2234
2214 for file_, node in iterfiles():
2235 for file_, node in iterfiles():
2215 filelog = repo.file(file_)
2236 filelog = repo.file(file_)
2216 if not len(filelog):
2237 if not len(filelog):
2217 if node is None:
2238 if node is None:
2218 # A zero count may be a directory or deleted file, so
2239 # A zero count may be a directory or deleted file, so
2219 # try to find matching entries on the slow path.
2240 # try to find matching entries on the slow path.
2220 if follow:
2241 if follow:
2221 raise error.Abort(
2242 raise error.Abort(
2222 _(b'cannot follow nonexistent file: "%s"') % file_
2243 _(b'cannot follow nonexistent file: "%s"') % file_
2223 )
2244 )
2224 raise FileWalkError(b"Cannot walk via filelog")
2245 raise FileWalkError(b"Cannot walk via filelog")
2225 else:
2246 else:
2226 continue
2247 continue
2227
2248
2228 if node is None:
2249 if node is None:
2229 last = len(filelog) - 1
2250 last = len(filelog) - 1
2230 else:
2251 else:
2231 last = filelog.rev(node)
2252 last = filelog.rev(node)
2232
2253
2233 # keep track of all ancestors of the file
2254 # keep track of all ancestors of the file
2234 ancestors = {filelog.linkrev(last)}
2255 ancestors = {filelog.linkrev(last)}
2235
2256
2236 # iterate from latest to oldest revision
2257 # iterate from latest to oldest revision
2237 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2258 for rev, flparentlinkrevs, copied in filerevs(filelog, last):
2238 if not follow:
2259 if not follow:
2239 if rev > maxrev:
2260 if rev > maxrev:
2240 continue
2261 continue
2241 else:
2262 else:
2242 # Note that last might not be the first interesting
2263 # Note that last might not be the first interesting
2243 # rev to us:
2264 # rev to us:
2244 # if the file has been changed after maxrev, we'll
2265 # if the file has been changed after maxrev, we'll
2245 # have linkrev(last) > maxrev, and we still need
2266 # have linkrev(last) > maxrev, and we still need
2246 # to explore the file graph
2267 # to explore the file graph
2247 if rev not in ancestors:
2268 if rev not in ancestors:
2248 continue
2269 continue
2249 # XXX insert 1327 fix here
2270 # XXX insert 1327 fix here
2250 if flparentlinkrevs:
2271 if flparentlinkrevs:
2251 ancestors.update(flparentlinkrevs)
2272 ancestors.update(flparentlinkrevs)
2252
2273
2253 fncache.setdefault(rev, []).append(file_)
2274 fncache.setdefault(rev, []).append(file_)
2254 wanted.add(rev)
2275 wanted.add(rev)
2255 if copied:
2276 if copied:
2256 copies.append(copied)
2277 copies.append(copied)
2257
2278
2258 return wanted
2279 return wanted
2259
2280
2260
2281
2261 class _followfilter(object):
2282 class _followfilter(object):
2262 def __init__(self, repo, onlyfirst=False):
2283 def __init__(self, repo, onlyfirst=False):
2263 self.repo = repo
2284 self.repo = repo
2264 self.startrev = nullrev
2285 self.startrev = nullrev
2265 self.roots = set()
2286 self.roots = set()
2266 self.onlyfirst = onlyfirst
2287 self.onlyfirst = onlyfirst
2267
2288
2268 def match(self, rev):
2289 def match(self, rev):
2269 def realparents(rev):
2290 def realparents(rev):
2270 if self.onlyfirst:
2291 if self.onlyfirst:
2271 return self.repo.changelog.parentrevs(rev)[0:1]
2292 return self.repo.changelog.parentrevs(rev)[0:1]
2272 else:
2293 else:
2273 return filter(
2294 return filter(
2274 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2295 lambda x: x != nullrev, self.repo.changelog.parentrevs(rev)
2275 )
2296 )
2276
2297
2277 if self.startrev == nullrev:
2298 if self.startrev == nullrev:
2278 self.startrev = rev
2299 self.startrev = rev
2279 return True
2300 return True
2280
2301
2281 if rev > self.startrev:
2302 if rev > self.startrev:
2282 # forward: all descendants
2303 # forward: all descendants
2283 if not self.roots:
2304 if not self.roots:
2284 self.roots.add(self.startrev)
2305 self.roots.add(self.startrev)
2285 for parent in realparents(rev):
2306 for parent in realparents(rev):
2286 if parent in self.roots:
2307 if parent in self.roots:
2287 self.roots.add(rev)
2308 self.roots.add(rev)
2288 return True
2309 return True
2289 else:
2310 else:
2290 # backwards: all parents
2311 # backwards: all parents
2291 if not self.roots:
2312 if not self.roots:
2292 self.roots.update(realparents(self.startrev))
2313 self.roots.update(realparents(self.startrev))
2293 if rev in self.roots:
2314 if rev in self.roots:
2294 self.roots.remove(rev)
2315 self.roots.remove(rev)
2295 self.roots.update(realparents(rev))
2316 self.roots.update(realparents(rev))
2296 return True
2317 return True
2297
2318
2298 return False
2319 return False
2299
2320
2300
2321
2301 def walkchangerevs(repo, match, opts, prepare):
2322 def walkchangerevs(repo, match, opts, prepare):
2302 '''Iterate over files and the revs in which they changed.
2323 '''Iterate over files and the revs in which they changed.
2303
2324
2304 Callers most commonly need to iterate backwards over the history
2325 Callers most commonly need to iterate backwards over the history
2305 in which they are interested. Doing so has awful (quadratic-looking)
2326 in which they are interested. Doing so has awful (quadratic-looking)
2306 performance, so we use iterators in a "windowed" way.
2327 performance, so we use iterators in a "windowed" way.
2307
2328
2308 We walk a window of revisions in the desired order. Within the
2329 We walk a window of revisions in the desired order. Within the
2309 window, we first walk forwards to gather data, then in the desired
2330 window, we first walk forwards to gather data, then in the desired
2310 order (usually backwards) to display it.
2331 order (usually backwards) to display it.
2311
2332
2312 This function returns an iterator yielding contexts. Before
2333 This function returns an iterator yielding contexts. Before
2313 yielding each context, the iterator will first call the prepare
2334 yielding each context, the iterator will first call the prepare
2314 function on each context in the window in forward order.'''
2335 function on each context in the window in forward order.'''
2315
2336
2316 allfiles = opts.get(b'all_files')
2337 allfiles = opts.get(b'all_files')
2317 follow = opts.get(b'follow') or opts.get(b'follow_first')
2338 follow = opts.get(b'follow') or opts.get(b'follow_first')
2318 revs = _walkrevs(repo, opts)
2339 revs = _walkrevs(repo, opts)
2319 if not revs:
2340 if not revs:
2320 return []
2341 return []
2321 wanted = set()
2342 wanted = set()
2322 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2343 slowpath = match.anypats() or (not match.always() and opts.get(b'removed'))
2323 fncache = {}
2344 fncache = {}
2324 change = repo.__getitem__
2345 change = repo.__getitem__
2325
2346
2326 # First step is to fill wanted, the set of revisions that we want to yield.
2347 # First step is to fill wanted, the set of revisions that we want to yield.
2327 # When it does not induce extra cost, we also fill fncache for revisions in
2348 # When it does not induce extra cost, we also fill fncache for revisions in
2328 # wanted: a cache of filenames that were changed (ctx.files()) and that
2349 # wanted: a cache of filenames that were changed (ctx.files()) and that
2329 # match the file filtering conditions.
2350 # match the file filtering conditions.
2330
2351
2331 if match.always() or allfiles:
2352 if match.always() or allfiles:
2332 # No files, no patterns. Display all revs.
2353 # No files, no patterns. Display all revs.
2333 wanted = revs
2354 wanted = revs
2334 elif not slowpath:
2355 elif not slowpath:
2335 # We only have to read through the filelog to find wanted revisions
2356 # We only have to read through the filelog to find wanted revisions
2336
2357
2337 try:
2358 try:
2338 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2359 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2339 except FileWalkError:
2360 except FileWalkError:
2340 slowpath = True
2361 slowpath = True
2341
2362
2342 # We decided to fall back to the slowpath because at least one
2363 # We decided to fall back to the slowpath because at least one
2343 # of the paths was not a file. Check to see if at least one of them
2364 # of the paths was not a file. Check to see if at least one of them
2344 # existed in history, otherwise simply return
2365 # existed in history, otherwise simply return
2345 for path in match.files():
2366 for path in match.files():
2346 if path == b'.' or path in repo.store:
2367 if path == b'.' or path in repo.store:
2347 break
2368 break
2348 else:
2369 else:
2349 return []
2370 return []
2350
2371
2351 if slowpath:
2372 if slowpath:
2352 # We have to read the changelog to match filenames against
2373 # We have to read the changelog to match filenames against
2353 # changed files
2374 # changed files
2354
2375
2355 if follow:
2376 if follow:
2356 raise error.Abort(
2377 raise error.Abort(
2357 _(b'can only follow copies/renames for explicit filenames')
2378 _(b'can only follow copies/renames for explicit filenames')
2358 )
2379 )
2359
2380
2360 # The slow path checks files modified in every changeset.
2381 # The slow path checks files modified in every changeset.
2361 # This is really slow on large repos, so compute the set lazily.
2382 # This is really slow on large repos, so compute the set lazily.
2362 class lazywantedset(object):
2383 class lazywantedset(object):
2363 def __init__(self):
2384 def __init__(self):
2364 self.set = set()
2385 self.set = set()
2365 self.revs = set(revs)
2386 self.revs = set(revs)
2366
2387
2367 # No need to worry about locality here because it will be accessed
2388 # No need to worry about locality here because it will be accessed
2368 # in the same order as the increasing window below.
2389 # in the same order as the increasing window below.
2369 def __contains__(self, value):
2390 def __contains__(self, value):
2370 if value in self.set:
2391 if value in self.set:
2371 return True
2392 return True
2372 elif not value in self.revs:
2393 elif not value in self.revs:
2373 return False
2394 return False
2374 else:
2395 else:
2375 self.revs.discard(value)
2396 self.revs.discard(value)
2376 ctx = change(value)
2397 ctx = change(value)
2377 if allfiles:
2398 if allfiles:
2378 matches = list(ctx.manifest().walk(match))
2399 matches = list(ctx.manifest().walk(match))
2379 else:
2400 else:
2380 matches = [f for f in ctx.files() if match(f)]
2401 matches = [f for f in ctx.files() if match(f)]
2381 if matches:
2402 if matches:
2382 fncache[value] = matches
2403 fncache[value] = matches
2383 self.set.add(value)
2404 self.set.add(value)
2384 return True
2405 return True
2385 return False
2406 return False
2386
2407
2387 def discard(self, value):
2408 def discard(self, value):
2388 self.revs.discard(value)
2409 self.revs.discard(value)
2389 self.set.discard(value)
2410 self.set.discard(value)
2390
2411
2391 wanted = lazywantedset()
2412 wanted = lazywantedset()
2392
2413
2393 # it might be worthwhile to do this in the iterator if the rev range
2414 # it might be worthwhile to do this in the iterator if the rev range
2394 # is descending and the prune args are all within that range
2415 # is descending and the prune args are all within that range
2395 for rev in opts.get(b'prune', ()):
2416 for rev in opts.get(b'prune', ()):
2396 rev = repo[rev].rev()
2417 rev = repo[rev].rev()
2397 ff = _followfilter(repo)
2418 ff = _followfilter(repo)
2398 stop = min(revs[0], revs[-1])
2419 stop = min(revs[0], revs[-1])
2399 for x in pycompat.xrange(rev, stop - 1, -1):
2420 for x in pycompat.xrange(rev, stop - 1, -1):
2400 if ff.match(x):
2421 if ff.match(x):
2401 wanted = wanted - [x]
2422 wanted = wanted - [x]
2402
2423
2403 # Now that wanted is correctly initialized, we can iterate over the
2424 # Now that wanted is correctly initialized, we can iterate over the
2404 # revision range, yielding only revisions in wanted.
2425 # revision range, yielding only revisions in wanted.
2405 def iterate():
2426 def iterate():
2406 if follow and match.always():
2427 if follow and match.always():
2407 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2428 ff = _followfilter(repo, onlyfirst=opts.get(b'follow_first'))
2408
2429
2409 def want(rev):
2430 def want(rev):
2410 return ff.match(rev) and rev in wanted
2431 return ff.match(rev) and rev in wanted
2411
2432
2412 else:
2433 else:
2413
2434
2414 def want(rev):
2435 def want(rev):
2415 return rev in wanted
2436 return rev in wanted
2416
2437
2417 it = iter(revs)
2438 it = iter(revs)
2418 stopiteration = False
2439 stopiteration = False
2419 for windowsize in increasingwindows():
2440 for windowsize in increasingwindows():
2420 nrevs = []
2441 nrevs = []
2421 for i in pycompat.xrange(windowsize):
2442 for i in pycompat.xrange(windowsize):
2422 rev = next(it, None)
2443 rev = next(it, None)
2423 if rev is None:
2444 if rev is None:
2424 stopiteration = True
2445 stopiteration = True
2425 break
2446 break
2426 elif want(rev):
2447 elif want(rev):
2427 nrevs.append(rev)
2448 nrevs.append(rev)
2428 for rev in sorted(nrevs):
2449 for rev in sorted(nrevs):
2429 fns = fncache.get(rev)
2450 fns = fncache.get(rev)
2430 ctx = change(rev)
2451 ctx = change(rev)
2431 if not fns:
2452 if not fns:
2432
2453
2433 def fns_generator():
2454 def fns_generator():
2434 if allfiles:
2455 if allfiles:
2435
2456
2436 def bad(f, msg):
2457 def bad(f, msg):
2437 pass
2458 pass
2438
2459
2439 for f in ctx.matches(matchmod.badmatch(match, bad)):
2460 for f in ctx.matches(matchmod.badmatch(match, bad)):
2440 yield f
2461 yield f
2441 else:
2462 else:
2442 for f in ctx.files():
2463 for f in ctx.files():
2443 if match(f):
2464 if match(f):
2444 yield f
2465 yield f
2445
2466
2446 fns = fns_generator()
2467 fns = fns_generator()
2447 prepare(ctx, fns)
2468 prepare(ctx, fns)
2448 for rev in nrevs:
2469 for rev in nrevs:
2449 yield change(rev)
2470 yield change(rev)
2450
2471
2451 if stopiteration:
2472 if stopiteration:
2452 break
2473 break
2453
2474
2454 return iterate()
2475 return iterate()
2455
2476
2456
2477
2457 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2478 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2458 bad = []
2479 bad = []
2459
2480
2460 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2481 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2461 names = []
2482 names = []
2462 wctx = repo[None]
2483 wctx = repo[None]
2463 cca = None
2484 cca = None
2464 abort, warn = scmutil.checkportabilityalert(ui)
2485 abort, warn = scmutil.checkportabilityalert(ui)
2465 if abort or warn:
2486 if abort or warn:
2466 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2487 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2467
2488
2468 match = repo.narrowmatch(match, includeexact=True)
2489 match = repo.narrowmatch(match, includeexact=True)
2469 badmatch = matchmod.badmatch(match, badfn)
2490 badmatch = matchmod.badmatch(match, badfn)
2470 dirstate = repo.dirstate
2491 dirstate = repo.dirstate
2471 # We don't want to just call wctx.walk here, since it would return a lot of
2492 # We don't want to just call wctx.walk here, since it would return a lot of
2472 # clean files, which we aren't interested in and takes time.
2493 # clean files, which we aren't interested in and takes time.
2473 for f in sorted(
2494 for f in sorted(
2474 dirstate.walk(
2495 dirstate.walk(
2475 badmatch,
2496 badmatch,
2476 subrepos=sorted(wctx.substate),
2497 subrepos=sorted(wctx.substate),
2477 unknown=True,
2498 unknown=True,
2478 ignored=False,
2499 ignored=False,
2479 full=False,
2500 full=False,
2480 )
2501 )
2481 ):
2502 ):
2482 exact = match.exact(f)
2503 exact = match.exact(f)
2483 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2504 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2484 if cca:
2505 if cca:
2485 cca(f)
2506 cca(f)
2486 names.append(f)
2507 names.append(f)
2487 if ui.verbose or not exact:
2508 if ui.verbose or not exact:
2488 ui.status(
2509 ui.status(
2489 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2510 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2490 )
2511 )
2491
2512
2492 for subpath in sorted(wctx.substate):
2513 for subpath in sorted(wctx.substate):
2493 sub = wctx.sub(subpath)
2514 sub = wctx.sub(subpath)
2494 try:
2515 try:
2495 submatch = matchmod.subdirmatcher(subpath, match)
2516 submatch = matchmod.subdirmatcher(subpath, match)
2496 subprefix = repo.wvfs.reljoin(prefix, subpath)
2517 subprefix = repo.wvfs.reljoin(prefix, subpath)
2497 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2518 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2498 if opts.get('subrepos'):
2519 if opts.get('subrepos'):
2499 bad.extend(
2520 bad.extend(
2500 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2521 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2501 )
2522 )
2502 else:
2523 else:
2503 bad.extend(
2524 bad.extend(
2504 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2525 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2505 )
2526 )
2506 except error.LookupError:
2527 except error.LookupError:
2507 ui.status(
2528 ui.status(
2508 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2529 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2509 )
2530 )
2510
2531
2511 if not opts.get('dry_run'):
2532 if not opts.get('dry_run'):
2512 rejected = wctx.add(names, prefix)
2533 rejected = wctx.add(names, prefix)
2513 bad.extend(f for f in rejected if f in match.files())
2534 bad.extend(f for f in rejected if f in match.files())
2514 return bad
2535 return bad
2515
2536
2516
2537
2517 def addwebdirpath(repo, serverpath, webconf):
2538 def addwebdirpath(repo, serverpath, webconf):
2518 webconf[serverpath] = repo.root
2539 webconf[serverpath] = repo.root
2519 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2540 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2520
2541
2521 for r in repo.revs(b'filelog("path:.hgsub")'):
2542 for r in repo.revs(b'filelog("path:.hgsub")'):
2522 ctx = repo[r]
2543 ctx = repo[r]
2523 for subpath in ctx.substate:
2544 for subpath in ctx.substate:
2524 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2545 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2525
2546
2526
2547
2527 def forget(
2548 def forget(
2528 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2549 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2529 ):
2550 ):
2530 if dryrun and interactive:
2551 if dryrun and interactive:
2531 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2552 raise error.Abort(_(b"cannot specify both --dry-run and --interactive"))
2532 bad = []
2553 bad = []
2533 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2554 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2534 wctx = repo[None]
2555 wctx = repo[None]
2535 forgot = []
2556 forgot = []
2536
2557
2537 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2558 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2538 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2559 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2539 if explicitonly:
2560 if explicitonly:
2540 forget = [f for f in forget if match.exact(f)]
2561 forget = [f for f in forget if match.exact(f)]
2541
2562
2542 for subpath in sorted(wctx.substate):
2563 for subpath in sorted(wctx.substate):
2543 sub = wctx.sub(subpath)
2564 sub = wctx.sub(subpath)
2544 submatch = matchmod.subdirmatcher(subpath, match)
2565 submatch = matchmod.subdirmatcher(subpath, match)
2545 subprefix = repo.wvfs.reljoin(prefix, subpath)
2566 subprefix = repo.wvfs.reljoin(prefix, subpath)
2546 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2567 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2547 try:
2568 try:
2548 subbad, subforgot = sub.forget(
2569 subbad, subforgot = sub.forget(
2549 submatch,
2570 submatch,
2550 subprefix,
2571 subprefix,
2551 subuipathfn,
2572 subuipathfn,
2552 dryrun=dryrun,
2573 dryrun=dryrun,
2553 interactive=interactive,
2574 interactive=interactive,
2554 )
2575 )
2555 bad.extend([subpath + b'/' + f for f in subbad])
2576 bad.extend([subpath + b'/' + f for f in subbad])
2556 forgot.extend([subpath + b'/' + f for f in subforgot])
2577 forgot.extend([subpath + b'/' + f for f in subforgot])
2557 except error.LookupError:
2578 except error.LookupError:
2558 ui.status(
2579 ui.status(
2559 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2580 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2560 )
2581 )
2561
2582
2562 if not explicitonly:
2583 if not explicitonly:
2563 for f in match.files():
2584 for f in match.files():
2564 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2585 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2565 if f not in forgot:
2586 if f not in forgot:
2566 if repo.wvfs.exists(f):
2587 if repo.wvfs.exists(f):
2567 # Don't complain if the exact case match wasn't given.
2588 # Don't complain if the exact case match wasn't given.
2568 # But don't do this until after checking 'forgot', so
2589 # But don't do this until after checking 'forgot', so
2569 # that subrepo files aren't normalized, and this op is
2590 # that subrepo files aren't normalized, and this op is
2570 # purely from data cached by the status walk above.
2591 # purely from data cached by the status walk above.
2571 if repo.dirstate.normalize(f) in repo.dirstate:
2592 if repo.dirstate.normalize(f) in repo.dirstate:
2572 continue
2593 continue
2573 ui.warn(
2594 ui.warn(
2574 _(
2595 _(
2575 b'not removing %s: '
2596 b'not removing %s: '
2576 b'file is already untracked\n'
2597 b'file is already untracked\n'
2577 )
2598 )
2578 % uipathfn(f)
2599 % uipathfn(f)
2579 )
2600 )
2580 bad.append(f)
2601 bad.append(f)
2581
2602
2582 if interactive:
2603 if interactive:
2583 responses = _(
2604 responses = _(
2584 b'[Ynsa?]'
2605 b'[Ynsa?]'
2585 b'$$ &Yes, forget this file'
2606 b'$$ &Yes, forget this file'
2586 b'$$ &No, skip this file'
2607 b'$$ &No, skip this file'
2587 b'$$ &Skip remaining files'
2608 b'$$ &Skip remaining files'
2588 b'$$ Include &all remaining files'
2609 b'$$ Include &all remaining files'
2589 b'$$ &? (display help)'
2610 b'$$ &? (display help)'
2590 )
2611 )
2591 for filename in forget[:]:
2612 for filename in forget[:]:
2592 r = ui.promptchoice(
2613 r = ui.promptchoice(
2593 _(b'forget %s %s') % (uipathfn(filename), responses)
2614 _(b'forget %s %s') % (uipathfn(filename), responses)
2594 )
2615 )
2595 if r == 4: # ?
2616 if r == 4: # ?
2596 while r == 4:
2617 while r == 4:
2597 for c, t in ui.extractchoices(responses)[1]:
2618 for c, t in ui.extractchoices(responses)[1]:
2598 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2619 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2599 r = ui.promptchoice(
2620 r = ui.promptchoice(
2600 _(b'forget %s %s') % (uipathfn(filename), responses)
2621 _(b'forget %s %s') % (uipathfn(filename), responses)
2601 )
2622 )
2602 if r == 0: # yes
2623 if r == 0: # yes
2603 continue
2624 continue
2604 elif r == 1: # no
2625 elif r == 1: # no
2605 forget.remove(filename)
2626 forget.remove(filename)
2606 elif r == 2: # Skip
2627 elif r == 2: # Skip
2607 fnindex = forget.index(filename)
2628 fnindex = forget.index(filename)
2608 del forget[fnindex:]
2629 del forget[fnindex:]
2609 break
2630 break
2610 elif r == 3: # All
2631 elif r == 3: # All
2611 break
2632 break
2612
2633
2613 for f in forget:
2634 for f in forget:
2614 if ui.verbose or not match.exact(f) or interactive:
2635 if ui.verbose or not match.exact(f) or interactive:
2615 ui.status(
2636 ui.status(
2616 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2637 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2617 )
2638 )
2618
2639
2619 if not dryrun:
2640 if not dryrun:
2620 rejected = wctx.forget(forget, prefix)
2641 rejected = wctx.forget(forget, prefix)
2621 bad.extend(f for f in rejected if f in match.files())
2642 bad.extend(f for f in rejected if f in match.files())
2622 forgot.extend(f for f in forget if f not in rejected)
2643 forgot.extend(f for f in forget if f not in rejected)
2623 return bad, forgot
2644 return bad, forgot
2624
2645
2625
2646
2626 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2647 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2627 ret = 1
2648 ret = 1
2628
2649
2629 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2650 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2630 for f in ctx.matches(m):
2651 for f in ctx.matches(m):
2631 fm.startitem()
2652 fm.startitem()
2632 fm.context(ctx=ctx)
2653 fm.context(ctx=ctx)
2633 if needsfctx:
2654 if needsfctx:
2634 fc = ctx[f]
2655 fc = ctx[f]
2635 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2656 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2636 fm.data(path=f)
2657 fm.data(path=f)
2637 fm.plain(fmt % uipathfn(f))
2658 fm.plain(fmt % uipathfn(f))
2638 ret = 0
2659 ret = 0
2639
2660
2640 for subpath in sorted(ctx.substate):
2661 for subpath in sorted(ctx.substate):
2641 submatch = matchmod.subdirmatcher(subpath, m)
2662 submatch = matchmod.subdirmatcher(subpath, m)
2642 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2663 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2643 if subrepos or m.exact(subpath) or any(submatch.files()):
2664 if subrepos or m.exact(subpath) or any(submatch.files()):
2644 sub = ctx.sub(subpath)
2665 sub = ctx.sub(subpath)
2645 try:
2666 try:
2646 recurse = m.exact(subpath) or subrepos
2667 recurse = m.exact(subpath) or subrepos
2647 if (
2668 if (
2648 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2669 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2649 == 0
2670 == 0
2650 ):
2671 ):
2651 ret = 0
2672 ret = 0
2652 except error.LookupError:
2673 except error.LookupError:
2653 ui.status(
2674 ui.status(
2654 _(b"skipping missing subrepository: %s\n")
2675 _(b"skipping missing subrepository: %s\n")
2655 % uipathfn(subpath)
2676 % uipathfn(subpath)
2656 )
2677 )
2657
2678
2658 return ret
2679 return ret
2659
2680
2660
2681
2661 def remove(
2682 def remove(
2662 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2683 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2663 ):
2684 ):
2664 ret = 0
2685 ret = 0
2665 s = repo.status(match=m, clean=True)
2686 s = repo.status(match=m, clean=True)
2666 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2687 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2667
2688
2668 wctx = repo[None]
2689 wctx = repo[None]
2669
2690
2670 if warnings is None:
2691 if warnings is None:
2671 warnings = []
2692 warnings = []
2672 warn = True
2693 warn = True
2673 else:
2694 else:
2674 warn = False
2695 warn = False
2675
2696
2676 subs = sorted(wctx.substate)
2697 subs = sorted(wctx.substate)
2677 progress = ui.makeprogress(
2698 progress = ui.makeprogress(
2678 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2699 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2679 )
2700 )
2680 for subpath in subs:
2701 for subpath in subs:
2681 submatch = matchmod.subdirmatcher(subpath, m)
2702 submatch = matchmod.subdirmatcher(subpath, m)
2682 subprefix = repo.wvfs.reljoin(prefix, subpath)
2703 subprefix = repo.wvfs.reljoin(prefix, subpath)
2683 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2704 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2684 if subrepos or m.exact(subpath) or any(submatch.files()):
2705 if subrepos or m.exact(subpath) or any(submatch.files()):
2685 progress.increment()
2706 progress.increment()
2686 sub = wctx.sub(subpath)
2707 sub = wctx.sub(subpath)
2687 try:
2708 try:
2688 if sub.removefiles(
2709 if sub.removefiles(
2689 submatch,
2710 submatch,
2690 subprefix,
2711 subprefix,
2691 subuipathfn,
2712 subuipathfn,
2692 after,
2713 after,
2693 force,
2714 force,
2694 subrepos,
2715 subrepos,
2695 dryrun,
2716 dryrun,
2696 warnings,
2717 warnings,
2697 ):
2718 ):
2698 ret = 1
2719 ret = 1
2699 except error.LookupError:
2720 except error.LookupError:
2700 warnings.append(
2721 warnings.append(
2701 _(b"skipping missing subrepository: %s\n")
2722 _(b"skipping missing subrepository: %s\n")
2702 % uipathfn(subpath)
2723 % uipathfn(subpath)
2703 )
2724 )
2704 progress.complete()
2725 progress.complete()
2705
2726
2706 # warn about failure to delete explicit files/dirs
2727 # warn about failure to delete explicit files/dirs
2707 deleteddirs = pathutil.dirs(deleted)
2728 deleteddirs = pathutil.dirs(deleted)
2708 files = m.files()
2729 files = m.files()
2709 progress = ui.makeprogress(
2730 progress = ui.makeprogress(
2710 _(b'deleting'), total=len(files), unit=_(b'files')
2731 _(b'deleting'), total=len(files), unit=_(b'files')
2711 )
2732 )
2712 for f in files:
2733 for f in files:
2713
2734
2714 def insubrepo():
2735 def insubrepo():
2715 for subpath in wctx.substate:
2736 for subpath in wctx.substate:
2716 if f.startswith(subpath + b'/'):
2737 if f.startswith(subpath + b'/'):
2717 return True
2738 return True
2718 return False
2739 return False
2719
2740
2720 progress.increment()
2741 progress.increment()
2721 isdir = f in deleteddirs or wctx.hasdir(f)
2742 isdir = f in deleteddirs or wctx.hasdir(f)
2722 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2743 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2723 continue
2744 continue
2724
2745
2725 if repo.wvfs.exists(f):
2746 if repo.wvfs.exists(f):
2726 if repo.wvfs.isdir(f):
2747 if repo.wvfs.isdir(f):
2727 warnings.append(
2748 warnings.append(
2728 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2749 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2729 )
2750 )
2730 else:
2751 else:
2731 warnings.append(
2752 warnings.append(
2732 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2753 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2733 )
2754 )
2734 # missing files will generate a warning elsewhere
2755 # missing files will generate a warning elsewhere
2735 ret = 1
2756 ret = 1
2736 progress.complete()
2757 progress.complete()
2737
2758
2738 if force:
2759 if force:
2739 list = modified + deleted + clean + added
2760 list = modified + deleted + clean + added
2740 elif after:
2761 elif after:
2741 list = deleted
2762 list = deleted
2742 remaining = modified + added + clean
2763 remaining = modified + added + clean
2743 progress = ui.makeprogress(
2764 progress = ui.makeprogress(
2744 _(b'skipping'), total=len(remaining), unit=_(b'files')
2765 _(b'skipping'), total=len(remaining), unit=_(b'files')
2745 )
2766 )
2746 for f in remaining:
2767 for f in remaining:
2747 progress.increment()
2768 progress.increment()
2748 if ui.verbose or (f in files):
2769 if ui.verbose or (f in files):
2749 warnings.append(
2770 warnings.append(
2750 _(b'not removing %s: file still exists\n') % uipathfn(f)
2771 _(b'not removing %s: file still exists\n') % uipathfn(f)
2751 )
2772 )
2752 ret = 1
2773 ret = 1
2753 progress.complete()
2774 progress.complete()
2754 else:
2775 else:
2755 list = deleted + clean
2776 list = deleted + clean
2756 progress = ui.makeprogress(
2777 progress = ui.makeprogress(
2757 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2778 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2758 )
2779 )
2759 for f in modified:
2780 for f in modified:
2760 progress.increment()
2781 progress.increment()
2761 warnings.append(
2782 warnings.append(
2762 _(
2783 _(
2763 b'not removing %s: file is modified (use -f'
2784 b'not removing %s: file is modified (use -f'
2764 b' to force removal)\n'
2785 b' to force removal)\n'
2765 )
2786 )
2766 % uipathfn(f)
2787 % uipathfn(f)
2767 )
2788 )
2768 ret = 1
2789 ret = 1
2769 for f in added:
2790 for f in added:
2770 progress.increment()
2791 progress.increment()
2771 warnings.append(
2792 warnings.append(
2772 _(
2793 _(
2773 b"not removing %s: file has been marked for add"
2794 b"not removing %s: file has been marked for add"
2774 b" (use 'hg forget' to undo add)\n"
2795 b" (use 'hg forget' to undo add)\n"
2775 )
2796 )
2776 % uipathfn(f)
2797 % uipathfn(f)
2777 )
2798 )
2778 ret = 1
2799 ret = 1
2779 progress.complete()
2800 progress.complete()
2780
2801
2781 list = sorted(list)
2802 list = sorted(list)
2782 progress = ui.makeprogress(
2803 progress = ui.makeprogress(
2783 _(b'deleting'), total=len(list), unit=_(b'files')
2804 _(b'deleting'), total=len(list), unit=_(b'files')
2784 )
2805 )
2785 for f in list:
2806 for f in list:
2786 if ui.verbose or not m.exact(f):
2807 if ui.verbose or not m.exact(f):
2787 progress.increment()
2808 progress.increment()
2788 ui.status(
2809 ui.status(
2789 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2810 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2790 )
2811 )
2791 progress.complete()
2812 progress.complete()
2792
2813
2793 if not dryrun:
2814 if not dryrun:
2794 with repo.wlock():
2815 with repo.wlock():
2795 if not after:
2816 if not after:
2796 for f in list:
2817 for f in list:
2797 if f in added:
2818 if f in added:
2798 continue # we never unlink added files on remove
2819 continue # we never unlink added files on remove
2799 rmdir = repo.ui.configbool(
2820 rmdir = repo.ui.configbool(
2800 b'experimental', b'removeemptydirs'
2821 b'experimental', b'removeemptydirs'
2801 )
2822 )
2802 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2823 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2803 repo[None].forget(list)
2824 repo[None].forget(list)
2804
2825
2805 if warn:
2826 if warn:
2806 for warning in warnings:
2827 for warning in warnings:
2807 ui.warn(warning)
2828 ui.warn(warning)
2808
2829
2809 return ret
2830 return ret
2810
2831
2811
2832
2812 def _catfmtneedsdata(fm):
2833 def _catfmtneedsdata(fm):
2813 return not fm.datahint() or b'data' in fm.datahint()
2834 return not fm.datahint() or b'data' in fm.datahint()
2814
2835
2815
2836
2816 def _updatecatformatter(fm, ctx, matcher, path, decode):
2837 def _updatecatformatter(fm, ctx, matcher, path, decode):
2817 """Hook for adding data to the formatter used by ``hg cat``.
2838 """Hook for adding data to the formatter used by ``hg cat``.
2818
2839
2819 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2840 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2820 this method first."""
2841 this method first."""
2821
2842
2822 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2843 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2823 # wasn't requested.
2844 # wasn't requested.
2824 data = b''
2845 data = b''
2825 if _catfmtneedsdata(fm):
2846 if _catfmtneedsdata(fm):
2826 data = ctx[path].data()
2847 data = ctx[path].data()
2827 if decode:
2848 if decode:
2828 data = ctx.repo().wwritedata(path, data)
2849 data = ctx.repo().wwritedata(path, data)
2829 fm.startitem()
2850 fm.startitem()
2830 fm.context(ctx=ctx)
2851 fm.context(ctx=ctx)
2831 fm.write(b'data', b'%s', data)
2852 fm.write(b'data', b'%s', data)
2832 fm.data(path=path)
2853 fm.data(path=path)
2833
2854
2834
2855
2835 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2856 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2836 err = 1
2857 err = 1
2837 opts = pycompat.byteskwargs(opts)
2858 opts = pycompat.byteskwargs(opts)
2838
2859
2839 def write(path):
2860 def write(path):
2840 filename = None
2861 filename = None
2841 if fntemplate:
2862 if fntemplate:
2842 filename = makefilename(
2863 filename = makefilename(
2843 ctx, fntemplate, pathname=os.path.join(prefix, path)
2864 ctx, fntemplate, pathname=os.path.join(prefix, path)
2844 )
2865 )
2845 # attempt to create the directory if it does not already exist
2866 # attempt to create the directory if it does not already exist
2846 try:
2867 try:
2847 os.makedirs(os.path.dirname(filename))
2868 os.makedirs(os.path.dirname(filename))
2848 except OSError:
2869 except OSError:
2849 pass
2870 pass
2850 with formatter.maybereopen(basefm, filename) as fm:
2871 with formatter.maybereopen(basefm, filename) as fm:
2851 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2872 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2852
2873
2853 # Automation often uses hg cat on single files, so special case it
2874 # Automation often uses hg cat on single files, so special case it
2854 # for performance to avoid the cost of parsing the manifest.
2875 # for performance to avoid the cost of parsing the manifest.
2855 if len(matcher.files()) == 1 and not matcher.anypats():
2876 if len(matcher.files()) == 1 and not matcher.anypats():
2856 file = matcher.files()[0]
2877 file = matcher.files()[0]
2857 mfl = repo.manifestlog
2878 mfl = repo.manifestlog
2858 mfnode = ctx.manifestnode()
2879 mfnode = ctx.manifestnode()
2859 try:
2880 try:
2860 if mfnode and mfl[mfnode].find(file)[0]:
2881 if mfnode and mfl[mfnode].find(file)[0]:
2861 if _catfmtneedsdata(basefm):
2882 if _catfmtneedsdata(basefm):
2862 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2883 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2863 write(file)
2884 write(file)
2864 return 0
2885 return 0
2865 except KeyError:
2886 except KeyError:
2866 pass
2887 pass
2867
2888
2868 if _catfmtneedsdata(basefm):
2889 if _catfmtneedsdata(basefm):
2869 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2890 scmutil.prefetchfiles(repo, [ctx.rev()], matcher)
2870
2891
2871 for abs in ctx.walk(matcher):
2892 for abs in ctx.walk(matcher):
2872 write(abs)
2893 write(abs)
2873 err = 0
2894 err = 0
2874
2895
2875 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2896 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2876 for subpath in sorted(ctx.substate):
2897 for subpath in sorted(ctx.substate):
2877 sub = ctx.sub(subpath)
2898 sub = ctx.sub(subpath)
2878 try:
2899 try:
2879 submatch = matchmod.subdirmatcher(subpath, matcher)
2900 submatch = matchmod.subdirmatcher(subpath, matcher)
2880 subprefix = os.path.join(prefix, subpath)
2901 subprefix = os.path.join(prefix, subpath)
2881 if not sub.cat(
2902 if not sub.cat(
2882 submatch,
2903 submatch,
2883 basefm,
2904 basefm,
2884 fntemplate,
2905 fntemplate,
2885 subprefix,
2906 subprefix,
2886 **pycompat.strkwargs(opts)
2907 **pycompat.strkwargs(opts)
2887 ):
2908 ):
2888 err = 0
2909 err = 0
2889 except error.RepoLookupError:
2910 except error.RepoLookupError:
2890 ui.status(
2911 ui.status(
2891 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2912 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2892 )
2913 )
2893
2914
2894 return err
2915 return err
2895
2916
2896
2917
2897 def commit(ui, repo, commitfunc, pats, opts):
2918 def commit(ui, repo, commitfunc, pats, opts):
2898 '''commit the specified files or all outstanding changes'''
2919 '''commit the specified files or all outstanding changes'''
2899 date = opts.get(b'date')
2920 date = opts.get(b'date')
2900 if date:
2921 if date:
2901 opts[b'date'] = dateutil.parsedate(date)
2922 opts[b'date'] = dateutil.parsedate(date)
2902 message = logmessage(ui, opts)
2923 message = logmessage(ui, opts)
2903 matcher = scmutil.match(repo[None], pats, opts)
2924 matcher = scmutil.match(repo[None], pats, opts)
2904
2925
2905 dsguard = None
2926 dsguard = None
2906 # extract addremove carefully -- this function can be called from a command
2927 # extract addremove carefully -- this function can be called from a command
2907 # that doesn't support addremove
2928 # that doesn't support addremove
2908 if opts.get(b'addremove'):
2929 if opts.get(b'addremove'):
2909 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2930 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2910 with dsguard or util.nullcontextmanager():
2931 with dsguard or util.nullcontextmanager():
2911 if dsguard:
2932 if dsguard:
2912 relative = scmutil.anypats(pats, opts)
2933 relative = scmutil.anypats(pats, opts)
2913 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2934 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2914 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2935 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2915 raise error.Abort(
2936 raise error.Abort(
2916 _(b"failed to mark all new/missing files as added/removed")
2937 _(b"failed to mark all new/missing files as added/removed")
2917 )
2938 )
2918
2939
2919 return commitfunc(ui, repo, message, matcher, opts)
2940 return commitfunc(ui, repo, message, matcher, opts)
2920
2941
2921
2942
2922 def samefile(f, ctx1, ctx2):
2943 def samefile(f, ctx1, ctx2):
2923 if f in ctx1.manifest():
2944 if f in ctx1.manifest():
2924 a = ctx1.filectx(f)
2945 a = ctx1.filectx(f)
2925 if f in ctx2.manifest():
2946 if f in ctx2.manifest():
2926 b = ctx2.filectx(f)
2947 b = ctx2.filectx(f)
2927 return not a.cmp(b) and a.flags() == b.flags()
2948 return not a.cmp(b) and a.flags() == b.flags()
2928 else:
2949 else:
2929 return False
2950 return False
2930 else:
2951 else:
2931 return f not in ctx2.manifest()
2952 return f not in ctx2.manifest()
2932
2953
2933
2954
2934 def amend(ui, repo, old, extra, pats, opts):
2955 def amend(ui, repo, old, extra, pats, opts):
2935 # avoid cycle context -> subrepo -> cmdutil
2956 # avoid cycle context -> subrepo -> cmdutil
2936 from . import context
2957 from . import context
2937
2958
2938 # amend will reuse the existing user if not specified, but the obsolete
2959 # amend will reuse the existing user if not specified, but the obsolete
2939 # marker creation requires that the current user's name is specified.
2960 # marker creation requires that the current user's name is specified.
2940 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2961 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2941 ui.username() # raise exception if username not set
2962 ui.username() # raise exception if username not set
2942
2963
2943 ui.note(_(b'amending changeset %s\n') % old)
2964 ui.note(_(b'amending changeset %s\n') % old)
2944 base = old.p1()
2965 base = old.p1()
2945
2966
2946 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2967 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2947 # Participating changesets:
2968 # Participating changesets:
2948 #
2969 #
2949 # wctx o - workingctx that contains changes from working copy
2970 # wctx o - workingctx that contains changes from working copy
2950 # | to go into amending commit
2971 # | to go into amending commit
2951 # |
2972 # |
2952 # old o - changeset to amend
2973 # old o - changeset to amend
2953 # |
2974 # |
2954 # base o - first parent of the changeset to amend
2975 # base o - first parent of the changeset to amend
2955 wctx = repo[None]
2976 wctx = repo[None]
2956
2977
2957 # Copy to avoid mutating input
2978 # Copy to avoid mutating input
2958 extra = extra.copy()
2979 extra = extra.copy()
2959 # Update extra dict from amended commit (e.g. to preserve graft
2980 # Update extra dict from amended commit (e.g. to preserve graft
2960 # source)
2981 # source)
2961 extra.update(old.extra())
2982 extra.update(old.extra())
2962
2983
2963 # Also update it from the from the wctx
2984 # Also update it from the from the wctx
2964 extra.update(wctx.extra())
2985 extra.update(wctx.extra())
2965
2986
2966 # date-only change should be ignored?
2987 # date-only change should be ignored?
2967 datemaydiffer = resolvecommitoptions(ui, opts)
2988 datemaydiffer = resolvecommitoptions(ui, opts)
2968
2989
2969 date = old.date()
2990 date = old.date()
2970 if opts.get(b'date'):
2991 if opts.get(b'date'):
2971 date = dateutil.parsedate(opts.get(b'date'))
2992 date = dateutil.parsedate(opts.get(b'date'))
2972 user = opts.get(b'user') or old.user()
2993 user = opts.get(b'user') or old.user()
2973
2994
2974 if len(old.parents()) > 1:
2995 if len(old.parents()) > 1:
2975 # ctx.files() isn't reliable for merges, so fall back to the
2996 # ctx.files() isn't reliable for merges, so fall back to the
2976 # slower repo.status() method
2997 # slower repo.status() method
2977 st = base.status(old)
2998 st = base.status(old)
2978 files = set(st.modified) | set(st.added) | set(st.removed)
2999 files = set(st.modified) | set(st.added) | set(st.removed)
2979 else:
3000 else:
2980 files = set(old.files())
3001 files = set(old.files())
2981
3002
2982 # add/remove the files to the working copy if the "addremove" option
3003 # add/remove the files to the working copy if the "addremove" option
2983 # was specified.
3004 # was specified.
2984 matcher = scmutil.match(wctx, pats, opts)
3005 matcher = scmutil.match(wctx, pats, opts)
2985 relative = scmutil.anypats(pats, opts)
3006 relative = scmutil.anypats(pats, opts)
2986 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
3007 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2987 if opts.get(b'addremove') and scmutil.addremove(
3008 if opts.get(b'addremove') and scmutil.addremove(
2988 repo, matcher, b"", uipathfn, opts
3009 repo, matcher, b"", uipathfn, opts
2989 ):
3010 ):
2990 raise error.Abort(
3011 raise error.Abort(
2991 _(b"failed to mark all new/missing files as added/removed")
3012 _(b"failed to mark all new/missing files as added/removed")
2992 )
3013 )
2993
3014
2994 # Check subrepos. This depends on in-place wctx._status update in
3015 # Check subrepos. This depends on in-place wctx._status update in
2995 # subrepo.precommit(). To minimize the risk of this hack, we do
3016 # subrepo.precommit(). To minimize the risk of this hack, we do
2996 # nothing if .hgsub does not exist.
3017 # nothing if .hgsub does not exist.
2997 if b'.hgsub' in wctx or b'.hgsub' in old:
3018 if b'.hgsub' in wctx or b'.hgsub' in old:
2998 subs, commitsubs, newsubstate = subrepoutil.precommit(
3019 subs, commitsubs, newsubstate = subrepoutil.precommit(
2999 ui, wctx, wctx._status, matcher
3020 ui, wctx, wctx._status, matcher
3000 )
3021 )
3001 # amend should abort if commitsubrepos is enabled
3022 # amend should abort if commitsubrepos is enabled
3002 assert not commitsubs
3023 assert not commitsubs
3003 if subs:
3024 if subs:
3004 subrepoutil.writestate(repo, newsubstate)
3025 subrepoutil.writestate(repo, newsubstate)
3005
3026
3006 ms = mergemod.mergestate.read(repo)
3027 ms = mergemod.mergestate.read(repo)
3007 mergeutil.checkunresolved(ms)
3028 mergeutil.checkunresolved(ms)
3008
3029
3009 filestoamend = set(f for f in wctx.files() if matcher(f))
3030 filestoamend = set(f for f in wctx.files() if matcher(f))
3010
3031
3011 changes = len(filestoamend) > 0
3032 changes = len(filestoamend) > 0
3012 if changes:
3033 if changes:
3013 # Recompute copies (avoid recording a -> b -> a)
3034 # Recompute copies (avoid recording a -> b -> a)
3014 copied = copies.pathcopies(base, wctx, matcher)
3035 copied = copies.pathcopies(base, wctx, matcher)
3015 if old.p2:
3036 if old.p2:
3016 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3037 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
3017
3038
3018 # Prune files which were reverted by the updates: if old
3039 # Prune files which were reverted by the updates: if old
3019 # introduced file X and the file was renamed in the working
3040 # introduced file X and the file was renamed in the working
3020 # copy, then those two files are the same and
3041 # copy, then those two files are the same and
3021 # we can discard X from our list of files. Likewise if X
3042 # we can discard X from our list of files. Likewise if X
3022 # was removed, it's no longer relevant. If X is missing (aka
3043 # was removed, it's no longer relevant. If X is missing (aka
3023 # deleted), old X must be preserved.
3044 # deleted), old X must be preserved.
3024 files.update(filestoamend)
3045 files.update(filestoamend)
3025 files = [
3046 files = [
3026 f
3047 f
3027 for f in files
3048 for f in files
3028 if (f not in filestoamend or not samefile(f, wctx, base))
3049 if (f not in filestoamend or not samefile(f, wctx, base))
3029 ]
3050 ]
3030
3051
3031 def filectxfn(repo, ctx_, path):
3052 def filectxfn(repo, ctx_, path):
3032 try:
3053 try:
3033 # If the file being considered is not amongst the files
3054 # If the file being considered is not amongst the files
3034 # to be amended, we should return the file context from the
3055 # to be amended, we should return the file context from the
3035 # old changeset. This avoids issues when only some files in
3056 # old changeset. This avoids issues when only some files in
3036 # the working copy are being amended but there are also
3057 # the working copy are being amended but there are also
3037 # changes to other files from the old changeset.
3058 # changes to other files from the old changeset.
3038 if path not in filestoamend:
3059 if path not in filestoamend:
3039 return old.filectx(path)
3060 return old.filectx(path)
3040
3061
3041 # Return None for removed files.
3062 # Return None for removed files.
3042 if path in wctx.removed():
3063 if path in wctx.removed():
3043 return None
3064 return None
3044
3065
3045 fctx = wctx[path]
3066 fctx = wctx[path]
3046 flags = fctx.flags()
3067 flags = fctx.flags()
3047 mctx = context.memfilectx(
3068 mctx = context.memfilectx(
3048 repo,
3069 repo,
3049 ctx_,
3070 ctx_,
3050 fctx.path(),
3071 fctx.path(),
3051 fctx.data(),
3072 fctx.data(),
3052 islink=b'l' in flags,
3073 islink=b'l' in flags,
3053 isexec=b'x' in flags,
3074 isexec=b'x' in flags,
3054 copysource=copied.get(path),
3075 copysource=copied.get(path),
3055 )
3076 )
3056 return mctx
3077 return mctx
3057 except KeyError:
3078 except KeyError:
3058 return None
3079 return None
3059
3080
3060 else:
3081 else:
3061 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
3082 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
3062
3083
3063 # Use version of files as in the old cset
3084 # Use version of files as in the old cset
3064 def filectxfn(repo, ctx_, path):
3085 def filectxfn(repo, ctx_, path):
3065 try:
3086 try:
3066 return old.filectx(path)
3087 return old.filectx(path)
3067 except KeyError:
3088 except KeyError:
3068 return None
3089 return None
3069
3090
3070 # See if we got a message from -m or -l, if not, open the editor with
3091 # See if we got a message from -m or -l, if not, open the editor with
3071 # the message of the changeset to amend.
3092 # the message of the changeset to amend.
3072 message = logmessage(ui, opts)
3093 message = logmessage(ui, opts)
3073
3094
3074 editform = mergeeditform(old, b'commit.amend')
3095 editform = mergeeditform(old, b'commit.amend')
3075
3096
3076 if not message:
3097 if not message:
3077 message = old.description()
3098 message = old.description()
3078 # Default if message isn't provided and --edit is not passed is to
3099 # Default if message isn't provided and --edit is not passed is to
3079 # invoke editor, but allow --no-edit. If somehow we don't have any
3100 # invoke editor, but allow --no-edit. If somehow we don't have any
3080 # description, let's always start the editor.
3101 # description, let's always start the editor.
3081 doedit = not message or opts.get(b'edit') in [True, None]
3102 doedit = not message or opts.get(b'edit') in [True, None]
3082 else:
3103 else:
3083 # Default if message is provided is to not invoke editor, but allow
3104 # Default if message is provided is to not invoke editor, but allow
3084 # --edit.
3105 # --edit.
3085 doedit = opts.get(b'edit') is True
3106 doedit = opts.get(b'edit') is True
3086 editor = getcommiteditor(edit=doedit, editform=editform)
3107 editor = getcommiteditor(edit=doedit, editform=editform)
3087
3108
3088 pureextra = extra.copy()
3109 pureextra = extra.copy()
3089 extra[b'amend_source'] = old.hex()
3110 extra[b'amend_source'] = old.hex()
3090
3111
3091 new = context.memctx(
3112 new = context.memctx(
3092 repo,
3113 repo,
3093 parents=[base.node(), old.p2().node()],
3114 parents=[base.node(), old.p2().node()],
3094 text=message,
3115 text=message,
3095 files=files,
3116 files=files,
3096 filectxfn=filectxfn,
3117 filectxfn=filectxfn,
3097 user=user,
3118 user=user,
3098 date=date,
3119 date=date,
3099 extra=extra,
3120 extra=extra,
3100 editor=editor,
3121 editor=editor,
3101 )
3122 )
3102
3123
3103 newdesc = changelog.stripdesc(new.description())
3124 newdesc = changelog.stripdesc(new.description())
3104 if (
3125 if (
3105 (not changes)
3126 (not changes)
3106 and newdesc == old.description()
3127 and newdesc == old.description()
3107 and user == old.user()
3128 and user == old.user()
3108 and (date == old.date() or datemaydiffer)
3129 and (date == old.date() or datemaydiffer)
3109 and pureextra == old.extra()
3130 and pureextra == old.extra()
3110 ):
3131 ):
3111 # nothing changed. continuing here would create a new node
3132 # nothing changed. continuing here would create a new node
3112 # anyway because of the amend_source noise.
3133 # anyway because of the amend_source noise.
3113 #
3134 #
3114 # This not what we expect from amend.
3135 # This not what we expect from amend.
3115 return old.node()
3136 return old.node()
3116
3137
3117 commitphase = None
3138 commitphase = None
3118 if opts.get(b'secret'):
3139 if opts.get(b'secret'):
3119 commitphase = phases.secret
3140 commitphase = phases.secret
3120 newid = repo.commitctx(new)
3141 newid = repo.commitctx(new)
3121
3142
3122 # Reroute the working copy parent to the new changeset
3143 # Reroute the working copy parent to the new changeset
3123 repo.setparents(newid, nullid)
3144 repo.setparents(newid, nullid)
3124 mapping = {old.node(): (newid,)}
3145 mapping = {old.node(): (newid,)}
3125 obsmetadata = None
3146 obsmetadata = None
3126 if opts.get(b'note'):
3147 if opts.get(b'note'):
3127 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3148 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
3128 backup = ui.configbool(b'rewrite', b'backup-bundle')
3149 backup = ui.configbool(b'rewrite', b'backup-bundle')
3129 scmutil.cleanupnodes(
3150 scmutil.cleanupnodes(
3130 repo,
3151 repo,
3131 mapping,
3152 mapping,
3132 b'amend',
3153 b'amend',
3133 metadata=obsmetadata,
3154 metadata=obsmetadata,
3134 fixphase=True,
3155 fixphase=True,
3135 targetphase=commitphase,
3156 targetphase=commitphase,
3136 backup=backup,
3157 backup=backup,
3137 )
3158 )
3138
3159
3139 # Fixing the dirstate because localrepo.commitctx does not update
3160 # Fixing the dirstate because localrepo.commitctx does not update
3140 # it. This is rather convenient because we did not need to update
3161 # it. This is rather convenient because we did not need to update
3141 # the dirstate for all the files in the new commit which commitctx
3162 # the dirstate for all the files in the new commit which commitctx
3142 # could have done if it updated the dirstate. Now, we can
3163 # could have done if it updated the dirstate. Now, we can
3143 # selectively update the dirstate only for the amended files.
3164 # selectively update the dirstate only for the amended files.
3144 dirstate = repo.dirstate
3165 dirstate = repo.dirstate
3145
3166
3146 # Update the state of the files which were added and modified in the
3167 # Update the state of the files which were added and modified in the
3147 # amend to "normal" in the dirstate. We need to use "normallookup" since
3168 # amend to "normal" in the dirstate. We need to use "normallookup" since
3148 # the files may have changed since the command started; using "normal"
3169 # the files may have changed since the command started; using "normal"
3149 # would mark them as clean but with uncommitted contents.
3170 # would mark them as clean but with uncommitted contents.
3150 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3171 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
3151 for f in normalfiles:
3172 for f in normalfiles:
3152 dirstate.normallookup(f)
3173 dirstate.normallookup(f)
3153
3174
3154 # Update the state of files which were removed in the amend
3175 # Update the state of files which were removed in the amend
3155 # to "removed" in the dirstate.
3176 # to "removed" in the dirstate.
3156 removedfiles = set(wctx.removed()) & filestoamend
3177 removedfiles = set(wctx.removed()) & filestoamend
3157 for f in removedfiles:
3178 for f in removedfiles:
3158 dirstate.drop(f)
3179 dirstate.drop(f)
3159
3180
3160 return newid
3181 return newid
3161
3182
3162
3183
3163 def commiteditor(repo, ctx, subs, editform=b''):
3184 def commiteditor(repo, ctx, subs, editform=b''):
3164 if ctx.description():
3185 if ctx.description():
3165 return ctx.description()
3186 return ctx.description()
3166 return commitforceeditor(
3187 return commitforceeditor(
3167 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3188 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3168 )
3189 )
3169
3190
3170
3191
3171 def commitforceeditor(
3192 def commitforceeditor(
3172 repo,
3193 repo,
3173 ctx,
3194 ctx,
3174 subs,
3195 subs,
3175 finishdesc=None,
3196 finishdesc=None,
3176 extramsg=None,
3197 extramsg=None,
3177 editform=b'',
3198 editform=b'',
3178 unchangedmessagedetection=False,
3199 unchangedmessagedetection=False,
3179 ):
3200 ):
3180 if not extramsg:
3201 if not extramsg:
3181 extramsg = _(b"Leave message empty to abort commit.")
3202 extramsg = _(b"Leave message empty to abort commit.")
3182
3203
3183 forms = [e for e in editform.split(b'.') if e]
3204 forms = [e for e in editform.split(b'.') if e]
3184 forms.insert(0, b'changeset')
3205 forms.insert(0, b'changeset')
3185 templatetext = None
3206 templatetext = None
3186 while forms:
3207 while forms:
3187 ref = b'.'.join(forms)
3208 ref = b'.'.join(forms)
3188 if repo.ui.config(b'committemplate', ref):
3209 if repo.ui.config(b'committemplate', ref):
3189 templatetext = committext = buildcommittemplate(
3210 templatetext = committext = buildcommittemplate(
3190 repo, ctx, subs, extramsg, ref
3211 repo, ctx, subs, extramsg, ref
3191 )
3212 )
3192 break
3213 break
3193 forms.pop()
3214 forms.pop()
3194 else:
3215 else:
3195 committext = buildcommittext(repo, ctx, subs, extramsg)
3216 committext = buildcommittext(repo, ctx, subs, extramsg)
3196
3217
3197 # run editor in the repository root
3218 # run editor in the repository root
3198 olddir = encoding.getcwd()
3219 olddir = encoding.getcwd()
3199 os.chdir(repo.root)
3220 os.chdir(repo.root)
3200
3221
3201 # make in-memory changes visible to external process
3222 # make in-memory changes visible to external process
3202 tr = repo.currenttransaction()
3223 tr = repo.currenttransaction()
3203 repo.dirstate.write(tr)
3224 repo.dirstate.write(tr)
3204 pending = tr and tr.writepending() and repo.root
3225 pending = tr and tr.writepending() and repo.root
3205
3226
3206 editortext = repo.ui.edit(
3227 editortext = repo.ui.edit(
3207 committext,
3228 committext,
3208 ctx.user(),
3229 ctx.user(),
3209 ctx.extra(),
3230 ctx.extra(),
3210 editform=editform,
3231 editform=editform,
3211 pending=pending,
3232 pending=pending,
3212 repopath=repo.path,
3233 repopath=repo.path,
3213 action=b'commit',
3234 action=b'commit',
3214 )
3235 )
3215 text = editortext
3236 text = editortext
3216
3237
3217 # strip away anything below this special string (used for editors that want
3238 # strip away anything below this special string (used for editors that want
3218 # to display the diff)
3239 # to display the diff)
3219 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3240 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3220 if stripbelow:
3241 if stripbelow:
3221 text = text[: stripbelow.start()]
3242 text = text[: stripbelow.start()]
3222
3243
3223 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3244 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3224 os.chdir(olddir)
3245 os.chdir(olddir)
3225
3246
3226 if finishdesc:
3247 if finishdesc:
3227 text = finishdesc(text)
3248 text = finishdesc(text)
3228 if not text.strip():
3249 if not text.strip():
3229 raise error.Abort(_(b"empty commit message"))
3250 raise error.Abort(_(b"empty commit message"))
3230 if unchangedmessagedetection and editortext == templatetext:
3251 if unchangedmessagedetection and editortext == templatetext:
3231 raise error.Abort(_(b"commit message unchanged"))
3252 raise error.Abort(_(b"commit message unchanged"))
3232
3253
3233 return text
3254 return text
3234
3255
3235
3256
3236 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3257 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3237 ui = repo.ui
3258 ui = repo.ui
3238 spec = formatter.templatespec(ref, None, None)
3259 spec = formatter.templatespec(ref, None, None)
3239 t = logcmdutil.changesettemplater(ui, repo, spec)
3260 t = logcmdutil.changesettemplater(ui, repo, spec)
3240 t.t.cache.update(
3261 t.t.cache.update(
3241 (k, templater.unquotestring(v))
3262 (k, templater.unquotestring(v))
3242 for k, v in repo.ui.configitems(b'committemplate')
3263 for k, v in repo.ui.configitems(b'committemplate')
3243 )
3264 )
3244
3265
3245 if not extramsg:
3266 if not extramsg:
3246 extramsg = b'' # ensure that extramsg is string
3267 extramsg = b'' # ensure that extramsg is string
3247
3268
3248 ui.pushbuffer()
3269 ui.pushbuffer()
3249 t.show(ctx, extramsg=extramsg)
3270 t.show(ctx, extramsg=extramsg)
3250 return ui.popbuffer()
3271 return ui.popbuffer()
3251
3272
3252
3273
3253 def hgprefix(msg):
3274 def hgprefix(msg):
3254 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3275 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3255
3276
3256
3277
3257 def buildcommittext(repo, ctx, subs, extramsg):
3278 def buildcommittext(repo, ctx, subs, extramsg):
3258 edittext = []
3279 edittext = []
3259 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3280 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3260 if ctx.description():
3281 if ctx.description():
3261 edittext.append(ctx.description())
3282 edittext.append(ctx.description())
3262 edittext.append(b"")
3283 edittext.append(b"")
3263 edittext.append(b"") # Empty line between message and comments.
3284 edittext.append(b"") # Empty line between message and comments.
3264 edittext.append(
3285 edittext.append(
3265 hgprefix(
3286 hgprefix(
3266 _(
3287 _(
3267 b"Enter commit message."
3288 b"Enter commit message."
3268 b" Lines beginning with 'HG:' are removed."
3289 b" Lines beginning with 'HG:' are removed."
3269 )
3290 )
3270 )
3291 )
3271 )
3292 )
3272 edittext.append(hgprefix(extramsg))
3293 edittext.append(hgprefix(extramsg))
3273 edittext.append(b"HG: --")
3294 edittext.append(b"HG: --")
3274 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3295 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3275 if ctx.p2():
3296 if ctx.p2():
3276 edittext.append(hgprefix(_(b"branch merge")))
3297 edittext.append(hgprefix(_(b"branch merge")))
3277 if ctx.branch():
3298 if ctx.branch():
3278 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3299 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3279 if bookmarks.isactivewdirparent(repo):
3300 if bookmarks.isactivewdirparent(repo):
3280 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3301 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3281 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3302 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3282 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3303 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3283 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3304 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3284 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3305 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3285 if not added and not modified and not removed:
3306 if not added and not modified and not removed:
3286 edittext.append(hgprefix(_(b"no files changed")))
3307 edittext.append(hgprefix(_(b"no files changed")))
3287 edittext.append(b"")
3308 edittext.append(b"")
3288
3309
3289 return b"\n".join(edittext)
3310 return b"\n".join(edittext)
3290
3311
3291
3312
3292 def commitstatus(repo, node, branch, bheads=None, opts=None):
3313 def commitstatus(repo, node, branch, bheads=None, opts=None):
3293 if opts is None:
3314 if opts is None:
3294 opts = {}
3315 opts = {}
3295 ctx = repo[node]
3316 ctx = repo[node]
3296 parents = ctx.parents()
3317 parents = ctx.parents()
3297
3318
3298 if (
3319 if (
3299 not opts.get(b'amend')
3320 not opts.get(b'amend')
3300 and bheads
3321 and bheads
3301 and node not in bheads
3322 and node not in bheads
3302 and not [
3323 and not [
3303 x for x in parents if x.node() in bheads and x.branch() == branch
3324 x for x in parents if x.node() in bheads and x.branch() == branch
3304 ]
3325 ]
3305 ):
3326 ):
3306 repo.ui.status(_(b'created new head\n'))
3327 repo.ui.status(_(b'created new head\n'))
3307 # The message is not printed for initial roots. For the other
3328 # The message is not printed for initial roots. For the other
3308 # changesets, it is printed in the following situations:
3329 # changesets, it is printed in the following situations:
3309 #
3330 #
3310 # Par column: for the 2 parents with ...
3331 # Par column: for the 2 parents with ...
3311 # N: null or no parent
3332 # N: null or no parent
3312 # B: parent is on another named branch
3333 # B: parent is on another named branch
3313 # C: parent is a regular non head changeset
3334 # C: parent is a regular non head changeset
3314 # H: parent was a branch head of the current branch
3335 # H: parent was a branch head of the current branch
3315 # Msg column: whether we print "created new head" message
3336 # Msg column: whether we print "created new head" message
3316 # In the following, it is assumed that there already exists some
3337 # In the following, it is assumed that there already exists some
3317 # initial branch heads of the current branch, otherwise nothing is
3338 # initial branch heads of the current branch, otherwise nothing is
3318 # printed anyway.
3339 # printed anyway.
3319 #
3340 #
3320 # Par Msg Comment
3341 # Par Msg Comment
3321 # N N y additional topo root
3342 # N N y additional topo root
3322 #
3343 #
3323 # B N y additional branch root
3344 # B N y additional branch root
3324 # C N y additional topo head
3345 # C N y additional topo head
3325 # H N n usual case
3346 # H N n usual case
3326 #
3347 #
3327 # B B y weird additional branch root
3348 # B B y weird additional branch root
3328 # C B y branch merge
3349 # C B y branch merge
3329 # H B n merge with named branch
3350 # H B n merge with named branch
3330 #
3351 #
3331 # C C y additional head from merge
3352 # C C y additional head from merge
3332 # C H n merge with a head
3353 # C H n merge with a head
3333 #
3354 #
3334 # H H n head merge: head count decreases
3355 # H H n head merge: head count decreases
3335
3356
3336 if not opts.get(b'close_branch'):
3357 if not opts.get(b'close_branch'):
3337 for r in parents:
3358 for r in parents:
3338 if r.closesbranch() and r.branch() == branch:
3359 if r.closesbranch() and r.branch() == branch:
3339 repo.ui.status(
3360 repo.ui.status(
3340 _(b'reopening closed branch head %d\n') % r.rev()
3361 _(b'reopening closed branch head %d\n') % r.rev()
3341 )
3362 )
3342
3363
3343 if repo.ui.debugflag:
3364 if repo.ui.debugflag:
3344 repo.ui.write(
3365 repo.ui.write(
3345 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3366 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3346 )
3367 )
3347 elif repo.ui.verbose:
3368 elif repo.ui.verbose:
3348 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3369 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3349
3370
3350
3371
3351 def postcommitstatus(repo, pats, opts):
3372 def postcommitstatus(repo, pats, opts):
3352 return repo.status(match=scmutil.match(repo[None], pats, opts))
3373 return repo.status(match=scmutil.match(repo[None], pats, opts))
3353
3374
3354
3375
3355 def revert(ui, repo, ctx, parents, *pats, **opts):
3376 def revert(ui, repo, ctx, parents, *pats, **opts):
3356 opts = pycompat.byteskwargs(opts)
3377 opts = pycompat.byteskwargs(opts)
3357 parent, p2 = parents
3378 parent, p2 = parents
3358 node = ctx.node()
3379 node = ctx.node()
3359
3380
3360 mf = ctx.manifest()
3381 mf = ctx.manifest()
3361 if node == p2:
3382 if node == p2:
3362 parent = p2
3383 parent = p2
3363
3384
3364 # need all matching names in dirstate and manifest of target rev,
3385 # need all matching names in dirstate and manifest of target rev,
3365 # so have to walk both. do not print errors if files exist in one
3386 # so have to walk both. do not print errors if files exist in one
3366 # but not other. in both cases, filesets should be evaluated against
3387 # but not other. in both cases, filesets should be evaluated against
3367 # workingctx to get consistent result (issue4497). this means 'set:**'
3388 # workingctx to get consistent result (issue4497). this means 'set:**'
3368 # cannot be used to select missing files from target rev.
3389 # cannot be used to select missing files from target rev.
3369
3390
3370 # `names` is a mapping for all elements in working copy and target revision
3391 # `names` is a mapping for all elements in working copy and target revision
3371 # The mapping is in the form:
3392 # The mapping is in the form:
3372 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3393 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3373 names = {}
3394 names = {}
3374 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3395 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3375
3396
3376 with repo.wlock():
3397 with repo.wlock():
3377 ## filling of the `names` mapping
3398 ## filling of the `names` mapping
3378 # walk dirstate to fill `names`
3399 # walk dirstate to fill `names`
3379
3400
3380 interactive = opts.get(b'interactive', False)
3401 interactive = opts.get(b'interactive', False)
3381 wctx = repo[None]
3402 wctx = repo[None]
3382 m = scmutil.match(wctx, pats, opts)
3403 m = scmutil.match(wctx, pats, opts)
3383
3404
3384 # we'll need this later
3405 # we'll need this later
3385 targetsubs = sorted(s for s in wctx.substate if m(s))
3406 targetsubs = sorted(s for s in wctx.substate if m(s))
3386
3407
3387 if not m.always():
3408 if not m.always():
3388 matcher = matchmod.badmatch(m, lambda x, y: False)
3409 matcher = matchmod.badmatch(m, lambda x, y: False)
3389 for abs in wctx.walk(matcher):
3410 for abs in wctx.walk(matcher):
3390 names[abs] = m.exact(abs)
3411 names[abs] = m.exact(abs)
3391
3412
3392 # walk target manifest to fill `names`
3413 # walk target manifest to fill `names`
3393
3414
3394 def badfn(path, msg):
3415 def badfn(path, msg):
3395 if path in names:
3416 if path in names:
3396 return
3417 return
3397 if path in ctx.substate:
3418 if path in ctx.substate:
3398 return
3419 return
3399 path_ = path + b'/'
3420 path_ = path + b'/'
3400 for f in names:
3421 for f in names:
3401 if f.startswith(path_):
3422 if f.startswith(path_):
3402 return
3423 return
3403 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3424 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3404
3425
3405 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3426 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3406 if abs not in names:
3427 if abs not in names:
3407 names[abs] = m.exact(abs)
3428 names[abs] = m.exact(abs)
3408
3429
3409 # Find status of all file in `names`.
3430 # Find status of all file in `names`.
3410 m = scmutil.matchfiles(repo, names)
3431 m = scmutil.matchfiles(repo, names)
3411
3432
3412 changes = repo.status(
3433 changes = repo.status(
3413 node1=node, match=m, unknown=True, ignored=True, clean=True
3434 node1=node, match=m, unknown=True, ignored=True, clean=True
3414 )
3435 )
3415 else:
3436 else:
3416 changes = repo.status(node1=node, match=m)
3437 changes = repo.status(node1=node, match=m)
3417 for kind in changes:
3438 for kind in changes:
3418 for abs in kind:
3439 for abs in kind:
3419 names[abs] = m.exact(abs)
3440 names[abs] = m.exact(abs)
3420
3441
3421 m = scmutil.matchfiles(repo, names)
3442 m = scmutil.matchfiles(repo, names)
3422
3443
3423 modified = set(changes.modified)
3444 modified = set(changes.modified)
3424 added = set(changes.added)
3445 added = set(changes.added)
3425 removed = set(changes.removed)
3446 removed = set(changes.removed)
3426 _deleted = set(changes.deleted)
3447 _deleted = set(changes.deleted)
3427 unknown = set(changes.unknown)
3448 unknown = set(changes.unknown)
3428 unknown.update(changes.ignored)
3449 unknown.update(changes.ignored)
3429 clean = set(changes.clean)
3450 clean = set(changes.clean)
3430 modadded = set()
3451 modadded = set()
3431
3452
3432 # We need to account for the state of the file in the dirstate,
3453 # We need to account for the state of the file in the dirstate,
3433 # even when we revert against something else than parent. This will
3454 # even when we revert against something else than parent. This will
3434 # slightly alter the behavior of revert (doing back up or not, delete
3455 # slightly alter the behavior of revert (doing back up or not, delete
3435 # or just forget etc).
3456 # or just forget etc).
3436 if parent == node:
3457 if parent == node:
3437 dsmodified = modified
3458 dsmodified = modified
3438 dsadded = added
3459 dsadded = added
3439 dsremoved = removed
3460 dsremoved = removed
3440 # store all local modifications, useful later for rename detection
3461 # store all local modifications, useful later for rename detection
3441 localchanges = dsmodified | dsadded
3462 localchanges = dsmodified | dsadded
3442 modified, added, removed = set(), set(), set()
3463 modified, added, removed = set(), set(), set()
3443 else:
3464 else:
3444 changes = repo.status(node1=parent, match=m)
3465 changes = repo.status(node1=parent, match=m)
3445 dsmodified = set(changes.modified)
3466 dsmodified = set(changes.modified)
3446 dsadded = set(changes.added)
3467 dsadded = set(changes.added)
3447 dsremoved = set(changes.removed)
3468 dsremoved = set(changes.removed)
3448 # store all local modifications, useful later for rename detection
3469 # store all local modifications, useful later for rename detection
3449 localchanges = dsmodified | dsadded
3470 localchanges = dsmodified | dsadded
3450
3471
3451 # only take into account for removes between wc and target
3472 # only take into account for removes between wc and target
3452 clean |= dsremoved - removed
3473 clean |= dsremoved - removed
3453 dsremoved &= removed
3474 dsremoved &= removed
3454 # distinct between dirstate remove and other
3475 # distinct between dirstate remove and other
3455 removed -= dsremoved
3476 removed -= dsremoved
3456
3477
3457 modadded = added & dsmodified
3478 modadded = added & dsmodified
3458 added -= modadded
3479 added -= modadded
3459
3480
3460 # tell newly modified apart.
3481 # tell newly modified apart.
3461 dsmodified &= modified
3482 dsmodified &= modified
3462 dsmodified |= modified & dsadded # dirstate added may need backup
3483 dsmodified |= modified & dsadded # dirstate added may need backup
3463 modified -= dsmodified
3484 modified -= dsmodified
3464
3485
3465 # We need to wait for some post-processing to update this set
3486 # We need to wait for some post-processing to update this set
3466 # before making the distinction. The dirstate will be used for
3487 # before making the distinction. The dirstate will be used for
3467 # that purpose.
3488 # that purpose.
3468 dsadded = added
3489 dsadded = added
3469
3490
3470 # in case of merge, files that are actually added can be reported as
3491 # in case of merge, files that are actually added can be reported as
3471 # modified, we need to post process the result
3492 # modified, we need to post process the result
3472 if p2 != nullid:
3493 if p2 != nullid:
3473 mergeadd = set(dsmodified)
3494 mergeadd = set(dsmodified)
3474 for path in dsmodified:
3495 for path in dsmodified:
3475 if path in mf:
3496 if path in mf:
3476 mergeadd.remove(path)
3497 mergeadd.remove(path)
3477 dsadded |= mergeadd
3498 dsadded |= mergeadd
3478 dsmodified -= mergeadd
3499 dsmodified -= mergeadd
3479
3500
3480 # if f is a rename, update `names` to also revert the source
3501 # if f is a rename, update `names` to also revert the source
3481 for f in localchanges:
3502 for f in localchanges:
3482 src = repo.dirstate.copied(f)
3503 src = repo.dirstate.copied(f)
3483 # XXX should we check for rename down to target node?
3504 # XXX should we check for rename down to target node?
3484 if src and src not in names and repo.dirstate[src] == b'r':
3505 if src and src not in names and repo.dirstate[src] == b'r':
3485 dsremoved.add(src)
3506 dsremoved.add(src)
3486 names[src] = True
3507 names[src] = True
3487
3508
3488 # determine the exact nature of the deleted changesets
3509 # determine the exact nature of the deleted changesets
3489 deladded = set(_deleted)
3510 deladded = set(_deleted)
3490 for path in _deleted:
3511 for path in _deleted:
3491 if path in mf:
3512 if path in mf:
3492 deladded.remove(path)
3513 deladded.remove(path)
3493 deleted = _deleted - deladded
3514 deleted = _deleted - deladded
3494
3515
3495 # distinguish between file to forget and the other
3516 # distinguish between file to forget and the other
3496 added = set()
3517 added = set()
3497 for abs in dsadded:
3518 for abs in dsadded:
3498 if repo.dirstate[abs] != b'a':
3519 if repo.dirstate[abs] != b'a':
3499 added.add(abs)
3520 added.add(abs)
3500 dsadded -= added
3521 dsadded -= added
3501
3522
3502 for abs in deladded:
3523 for abs in deladded:
3503 if repo.dirstate[abs] == b'a':
3524 if repo.dirstate[abs] == b'a':
3504 dsadded.add(abs)
3525 dsadded.add(abs)
3505 deladded -= dsadded
3526 deladded -= dsadded
3506
3527
3507 # For files marked as removed, we check if an unknown file is present at
3528 # For files marked as removed, we check if an unknown file is present at
3508 # the same path. If a such file exists it may need to be backed up.
3529 # the same path. If a such file exists it may need to be backed up.
3509 # Making the distinction at this stage helps have simpler backup
3530 # Making the distinction at this stage helps have simpler backup
3510 # logic.
3531 # logic.
3511 removunk = set()
3532 removunk = set()
3512 for abs in removed:
3533 for abs in removed:
3513 target = repo.wjoin(abs)
3534 target = repo.wjoin(abs)
3514 if os.path.lexists(target):
3535 if os.path.lexists(target):
3515 removunk.add(abs)
3536 removunk.add(abs)
3516 removed -= removunk
3537 removed -= removunk
3517
3538
3518 dsremovunk = set()
3539 dsremovunk = set()
3519 for abs in dsremoved:
3540 for abs in dsremoved:
3520 target = repo.wjoin(abs)
3541 target = repo.wjoin(abs)
3521 if os.path.lexists(target):
3542 if os.path.lexists(target):
3522 dsremovunk.add(abs)
3543 dsremovunk.add(abs)
3523 dsremoved -= dsremovunk
3544 dsremoved -= dsremovunk
3524
3545
3525 # action to be actually performed by revert
3546 # action to be actually performed by revert
3526 # (<list of file>, message>) tuple
3547 # (<list of file>, message>) tuple
3527 actions = {
3548 actions = {
3528 b'revert': ([], _(b'reverting %s\n')),
3549 b'revert': ([], _(b'reverting %s\n')),
3529 b'add': ([], _(b'adding %s\n')),
3550 b'add': ([], _(b'adding %s\n')),
3530 b'remove': ([], _(b'removing %s\n')),
3551 b'remove': ([], _(b'removing %s\n')),
3531 b'drop': ([], _(b'removing %s\n')),
3552 b'drop': ([], _(b'removing %s\n')),
3532 b'forget': ([], _(b'forgetting %s\n')),
3553 b'forget': ([], _(b'forgetting %s\n')),
3533 b'undelete': ([], _(b'undeleting %s\n')),
3554 b'undelete': ([], _(b'undeleting %s\n')),
3534 b'noop': (None, _(b'no changes needed to %s\n')),
3555 b'noop': (None, _(b'no changes needed to %s\n')),
3535 b'unknown': (None, _(b'file not managed: %s\n')),
3556 b'unknown': (None, _(b'file not managed: %s\n')),
3536 }
3557 }
3537
3558
3538 # "constant" that convey the backup strategy.
3559 # "constant" that convey the backup strategy.
3539 # All set to `discard` if `no-backup` is set do avoid checking
3560 # All set to `discard` if `no-backup` is set do avoid checking
3540 # no_backup lower in the code.
3561 # no_backup lower in the code.
3541 # These values are ordered for comparison purposes
3562 # These values are ordered for comparison purposes
3542 backupinteractive = 3 # do backup if interactively modified
3563 backupinteractive = 3 # do backup if interactively modified
3543 backup = 2 # unconditionally do backup
3564 backup = 2 # unconditionally do backup
3544 check = 1 # check if the existing file differs from target
3565 check = 1 # check if the existing file differs from target
3545 discard = 0 # never do backup
3566 discard = 0 # never do backup
3546 if opts.get(b'no_backup'):
3567 if opts.get(b'no_backup'):
3547 backupinteractive = backup = check = discard
3568 backupinteractive = backup = check = discard
3548 if interactive:
3569 if interactive:
3549 dsmodifiedbackup = backupinteractive
3570 dsmodifiedbackup = backupinteractive
3550 else:
3571 else:
3551 dsmodifiedbackup = backup
3572 dsmodifiedbackup = backup
3552 tobackup = set()
3573 tobackup = set()
3553
3574
3554 backupanddel = actions[b'remove']
3575 backupanddel = actions[b'remove']
3555 if not opts.get(b'no_backup'):
3576 if not opts.get(b'no_backup'):
3556 backupanddel = actions[b'drop']
3577 backupanddel = actions[b'drop']
3557
3578
3558 disptable = (
3579 disptable = (
3559 # dispatch table:
3580 # dispatch table:
3560 # file state
3581 # file state
3561 # action
3582 # action
3562 # make backup
3583 # make backup
3563 ## Sets that results that will change file on disk
3584 ## Sets that results that will change file on disk
3564 # Modified compared to target, no local change
3585 # Modified compared to target, no local change
3565 (modified, actions[b'revert'], discard),
3586 (modified, actions[b'revert'], discard),
3566 # Modified compared to target, but local file is deleted
3587 # Modified compared to target, but local file is deleted
3567 (deleted, actions[b'revert'], discard),
3588 (deleted, actions[b'revert'], discard),
3568 # Modified compared to target, local change
3589 # Modified compared to target, local change
3569 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3590 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3570 # Added since target
3591 # Added since target
3571 (added, actions[b'remove'], discard),
3592 (added, actions[b'remove'], discard),
3572 # Added in working directory
3593 # Added in working directory
3573 (dsadded, actions[b'forget'], discard),
3594 (dsadded, actions[b'forget'], discard),
3574 # Added since target, have local modification
3595 # Added since target, have local modification
3575 (modadded, backupanddel, backup),
3596 (modadded, backupanddel, backup),
3576 # Added since target but file is missing in working directory
3597 # Added since target but file is missing in working directory
3577 (deladded, actions[b'drop'], discard),
3598 (deladded, actions[b'drop'], discard),
3578 # Removed since target, before working copy parent
3599 # Removed since target, before working copy parent
3579 (removed, actions[b'add'], discard),
3600 (removed, actions[b'add'], discard),
3580 # Same as `removed` but an unknown file exists at the same path
3601 # Same as `removed` but an unknown file exists at the same path
3581 (removunk, actions[b'add'], check),
3602 (removunk, actions[b'add'], check),
3582 # Removed since targe, marked as such in working copy parent
3603 # Removed since targe, marked as such in working copy parent
3583 (dsremoved, actions[b'undelete'], discard),
3604 (dsremoved, actions[b'undelete'], discard),
3584 # Same as `dsremoved` but an unknown file exists at the same path
3605 # Same as `dsremoved` but an unknown file exists at the same path
3585 (dsremovunk, actions[b'undelete'], check),
3606 (dsremovunk, actions[b'undelete'], check),
3586 ## the following sets does not result in any file changes
3607 ## the following sets does not result in any file changes
3587 # File with no modification
3608 # File with no modification
3588 (clean, actions[b'noop'], discard),
3609 (clean, actions[b'noop'], discard),
3589 # Existing file, not tracked anywhere
3610 # Existing file, not tracked anywhere
3590 (unknown, actions[b'unknown'], discard),
3611 (unknown, actions[b'unknown'], discard),
3591 )
3612 )
3592
3613
3593 for abs, exact in sorted(names.items()):
3614 for abs, exact in sorted(names.items()):
3594 # target file to be touch on disk (relative to cwd)
3615 # target file to be touch on disk (relative to cwd)
3595 target = repo.wjoin(abs)
3616 target = repo.wjoin(abs)
3596 # search the entry in the dispatch table.
3617 # search the entry in the dispatch table.
3597 # if the file is in any of these sets, it was touched in the working
3618 # if the file is in any of these sets, it was touched in the working
3598 # directory parent and we are sure it needs to be reverted.
3619 # directory parent and we are sure it needs to be reverted.
3599 for table, (xlist, msg), dobackup in disptable:
3620 for table, (xlist, msg), dobackup in disptable:
3600 if abs not in table:
3621 if abs not in table:
3601 continue
3622 continue
3602 if xlist is not None:
3623 if xlist is not None:
3603 xlist.append(abs)
3624 xlist.append(abs)
3604 if dobackup:
3625 if dobackup:
3605 # If in interactive mode, don't automatically create
3626 # If in interactive mode, don't automatically create
3606 # .orig files (issue4793)
3627 # .orig files (issue4793)
3607 if dobackup == backupinteractive:
3628 if dobackup == backupinteractive:
3608 tobackup.add(abs)
3629 tobackup.add(abs)
3609 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3630 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3610 absbakname = scmutil.backuppath(ui, repo, abs)
3631 absbakname = scmutil.backuppath(ui, repo, abs)
3611 bakname = os.path.relpath(
3632 bakname = os.path.relpath(
3612 absbakname, start=repo.root
3633 absbakname, start=repo.root
3613 )
3634 )
3614 ui.note(
3635 ui.note(
3615 _(b'saving current version of %s as %s\n')
3636 _(b'saving current version of %s as %s\n')
3616 % (uipathfn(abs), uipathfn(bakname))
3637 % (uipathfn(abs), uipathfn(bakname))
3617 )
3638 )
3618 if not opts.get(b'dry_run'):
3639 if not opts.get(b'dry_run'):
3619 if interactive:
3640 if interactive:
3620 util.copyfile(target, absbakname)
3641 util.copyfile(target, absbakname)
3621 else:
3642 else:
3622 util.rename(target, absbakname)
3643 util.rename(target, absbakname)
3623 if opts.get(b'dry_run'):
3644 if opts.get(b'dry_run'):
3624 if ui.verbose or not exact:
3645 if ui.verbose or not exact:
3625 ui.status(msg % uipathfn(abs))
3646 ui.status(msg % uipathfn(abs))
3626 elif exact:
3647 elif exact:
3627 ui.warn(msg % uipathfn(abs))
3648 ui.warn(msg % uipathfn(abs))
3628 break
3649 break
3629
3650
3630 if not opts.get(b'dry_run'):
3651 if not opts.get(b'dry_run'):
3631 needdata = (b'revert', b'add', b'undelete')
3652 needdata = (b'revert', b'add', b'undelete')
3632 oplist = [actions[name][0] for name in needdata]
3653 oplist = [actions[name][0] for name in needdata]
3633 prefetch = scmutil.prefetchfiles
3654 prefetch = scmutil.prefetchfiles
3634 matchfiles = scmutil.matchfiles
3655 matchfiles = scmutil.matchfiles
3635 prefetch(
3656 prefetch(
3636 repo,
3657 repo,
3637 [ctx.rev()],
3658 [ctx.rev()],
3638 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3659 matchfiles(repo, [f for sublist in oplist for f in sublist]),
3639 )
3660 )
3640 match = scmutil.match(repo[None], pats)
3661 match = scmutil.match(repo[None], pats)
3641 _performrevert(
3662 _performrevert(
3642 repo,
3663 repo,
3643 parents,
3664 parents,
3644 ctx,
3665 ctx,
3645 names,
3666 names,
3646 uipathfn,
3667 uipathfn,
3647 actions,
3668 actions,
3648 match,
3669 match,
3649 interactive,
3670 interactive,
3650 tobackup,
3671 tobackup,
3651 )
3672 )
3652
3673
3653 if targetsubs:
3674 if targetsubs:
3654 # Revert the subrepos on the revert list
3675 # Revert the subrepos on the revert list
3655 for sub in targetsubs:
3676 for sub in targetsubs:
3656 try:
3677 try:
3657 wctx.sub(sub).revert(
3678 wctx.sub(sub).revert(
3658 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3679 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3659 )
3680 )
3660 except KeyError:
3681 except KeyError:
3661 raise error.Abort(
3682 raise error.Abort(
3662 b"subrepository '%s' does not exist in %s!"
3683 b"subrepository '%s' does not exist in %s!"
3663 % (sub, short(ctx.node()))
3684 % (sub, short(ctx.node()))
3664 )
3685 )
3665
3686
3666
3687
3667 def _performrevert(
3688 def _performrevert(
3668 repo,
3689 repo,
3669 parents,
3690 parents,
3670 ctx,
3691 ctx,
3671 names,
3692 names,
3672 uipathfn,
3693 uipathfn,
3673 actions,
3694 actions,
3674 match,
3695 match,
3675 interactive=False,
3696 interactive=False,
3676 tobackup=None,
3697 tobackup=None,
3677 ):
3698 ):
3678 """function that actually perform all the actions computed for revert
3699 """function that actually perform all the actions computed for revert
3679
3700
3680 This is an independent function to let extension to plug in and react to
3701 This is an independent function to let extension to plug in and react to
3681 the imminent revert.
3702 the imminent revert.
3682
3703
3683 Make sure you have the working directory locked when calling this function.
3704 Make sure you have the working directory locked when calling this function.
3684 """
3705 """
3685 parent, p2 = parents
3706 parent, p2 = parents
3686 node = ctx.node()
3707 node = ctx.node()
3687 excluded_files = []
3708 excluded_files = []
3688
3709
3689 def checkout(f):
3710 def checkout(f):
3690 fc = ctx[f]
3711 fc = ctx[f]
3691 repo.wwrite(f, fc.data(), fc.flags())
3712 repo.wwrite(f, fc.data(), fc.flags())
3692
3713
3693 def doremove(f):
3714 def doremove(f):
3694 try:
3715 try:
3695 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3716 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3696 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3717 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3697 except OSError:
3718 except OSError:
3698 pass
3719 pass
3699 repo.dirstate.remove(f)
3720 repo.dirstate.remove(f)
3700
3721
3701 def prntstatusmsg(action, f):
3722 def prntstatusmsg(action, f):
3702 exact = names[f]
3723 exact = names[f]
3703 if repo.ui.verbose or not exact:
3724 if repo.ui.verbose or not exact:
3704 repo.ui.status(actions[action][1] % uipathfn(f))
3725 repo.ui.status(actions[action][1] % uipathfn(f))
3705
3726
3706 audit_path = pathutil.pathauditor(repo.root, cached=True)
3727 audit_path = pathutil.pathauditor(repo.root, cached=True)
3707 for f in actions[b'forget'][0]:
3728 for f in actions[b'forget'][0]:
3708 if interactive:
3729 if interactive:
3709 choice = repo.ui.promptchoice(
3730 choice = repo.ui.promptchoice(
3710 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3731 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3711 )
3732 )
3712 if choice == 0:
3733 if choice == 0:
3713 prntstatusmsg(b'forget', f)
3734 prntstatusmsg(b'forget', f)
3714 repo.dirstate.drop(f)
3735 repo.dirstate.drop(f)
3715 else:
3736 else:
3716 excluded_files.append(f)
3737 excluded_files.append(f)
3717 else:
3738 else:
3718 prntstatusmsg(b'forget', f)
3739 prntstatusmsg(b'forget', f)
3719 repo.dirstate.drop(f)
3740 repo.dirstate.drop(f)
3720 for f in actions[b'remove'][0]:
3741 for f in actions[b'remove'][0]:
3721 audit_path(f)
3742 audit_path(f)
3722 if interactive:
3743 if interactive:
3723 choice = repo.ui.promptchoice(
3744 choice = repo.ui.promptchoice(
3724 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3745 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3725 )
3746 )
3726 if choice == 0:
3747 if choice == 0:
3727 prntstatusmsg(b'remove', f)
3748 prntstatusmsg(b'remove', f)
3728 doremove(f)
3749 doremove(f)
3729 else:
3750 else:
3730 excluded_files.append(f)
3751 excluded_files.append(f)
3731 else:
3752 else:
3732 prntstatusmsg(b'remove', f)
3753 prntstatusmsg(b'remove', f)
3733 doremove(f)
3754 doremove(f)
3734 for f in actions[b'drop'][0]:
3755 for f in actions[b'drop'][0]:
3735 audit_path(f)
3756 audit_path(f)
3736 prntstatusmsg(b'drop', f)
3757 prntstatusmsg(b'drop', f)
3737 repo.dirstate.remove(f)
3758 repo.dirstate.remove(f)
3738
3759
3739 normal = None
3760 normal = None
3740 if node == parent:
3761 if node == parent:
3741 # We're reverting to our parent. If possible, we'd like status
3762 # We're reverting to our parent. If possible, we'd like status
3742 # to report the file as clean. We have to use normallookup for
3763 # to report the file as clean. We have to use normallookup for
3743 # merges to avoid losing information about merged/dirty files.
3764 # merges to avoid losing information about merged/dirty files.
3744 if p2 != nullid:
3765 if p2 != nullid:
3745 normal = repo.dirstate.normallookup
3766 normal = repo.dirstate.normallookup
3746 else:
3767 else:
3747 normal = repo.dirstate.normal
3768 normal = repo.dirstate.normal
3748
3769
3749 newlyaddedandmodifiedfiles = set()
3770 newlyaddedandmodifiedfiles = set()
3750 if interactive:
3771 if interactive:
3751 # Prompt the user for changes to revert
3772 # Prompt the user for changes to revert
3752 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3773 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3753 m = scmutil.matchfiles(repo, torevert)
3774 m = scmutil.matchfiles(repo, torevert)
3754 diffopts = patch.difffeatureopts(
3775 diffopts = patch.difffeatureopts(
3755 repo.ui,
3776 repo.ui,
3756 whitespace=True,
3777 whitespace=True,
3757 section=b'commands',
3778 section=b'commands',
3758 configprefix=b'revert.interactive.',
3779 configprefix=b'revert.interactive.',
3759 )
3780 )
3760 diffopts.nodates = True
3781 diffopts.nodates = True
3761 diffopts.git = True
3782 diffopts.git = True
3762 operation = b'apply'
3783 operation = b'apply'
3763 if node == parent:
3784 if node == parent:
3764 if repo.ui.configbool(
3785 if repo.ui.configbool(
3765 b'experimental', b'revert.interactive.select-to-keep'
3786 b'experimental', b'revert.interactive.select-to-keep'
3766 ):
3787 ):
3767 operation = b'keep'
3788 operation = b'keep'
3768 else:
3789 else:
3769 operation = b'discard'
3790 operation = b'discard'
3770
3791
3771 if operation == b'apply':
3792 if operation == b'apply':
3772 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3793 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3773 else:
3794 else:
3774 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3795 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3775 originalchunks = patch.parsepatch(diff)
3796 originalchunks = patch.parsepatch(diff)
3776
3797
3777 try:
3798 try:
3778
3799
3779 chunks, opts = recordfilter(
3800 chunks, opts = recordfilter(
3780 repo.ui, originalchunks, match, operation=operation
3801 repo.ui, originalchunks, match, operation=operation
3781 )
3802 )
3782 if operation == b'discard':
3803 if operation == b'discard':
3783 chunks = patch.reversehunks(chunks)
3804 chunks = patch.reversehunks(chunks)
3784
3805
3785 except error.PatchError as err:
3806 except error.PatchError as err:
3786 raise error.Abort(_(b'error parsing patch: %s') % err)
3807 raise error.Abort(_(b'error parsing patch: %s') % err)
3787
3808
3788 # FIXME: when doing an interactive revert of a copy, there's no way of
3809 # FIXME: when doing an interactive revert of a copy, there's no way of
3789 # performing a partial revert of the added file, the only option is
3810 # performing a partial revert of the added file, the only option is
3790 # "remove added file <name> (Yn)?", so we don't need to worry about the
3811 # "remove added file <name> (Yn)?", so we don't need to worry about the
3791 # alsorestore value. Ideally we'd be able to partially revert
3812 # alsorestore value. Ideally we'd be able to partially revert
3792 # copied/renamed files.
3813 # copied/renamed files.
3793 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3814 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3794 chunks, originalchunks
3815 chunks, originalchunks
3795 )
3816 )
3796 if tobackup is None:
3817 if tobackup is None:
3797 tobackup = set()
3818 tobackup = set()
3798 # Apply changes
3819 # Apply changes
3799 fp = stringio()
3820 fp = stringio()
3800 # chunks are serialized per file, but files aren't sorted
3821 # chunks are serialized per file, but files aren't sorted
3801 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3822 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3802 prntstatusmsg(b'revert', f)
3823 prntstatusmsg(b'revert', f)
3803 files = set()
3824 files = set()
3804 for c in chunks:
3825 for c in chunks:
3805 if ishunk(c):
3826 if ishunk(c):
3806 abs = c.header.filename()
3827 abs = c.header.filename()
3807 # Create a backup file only if this hunk should be backed up
3828 # Create a backup file only if this hunk should be backed up
3808 if c.header.filename() in tobackup:
3829 if c.header.filename() in tobackup:
3809 target = repo.wjoin(abs)
3830 target = repo.wjoin(abs)
3810 bakname = scmutil.backuppath(repo.ui, repo, abs)
3831 bakname = scmutil.backuppath(repo.ui, repo, abs)
3811 util.copyfile(target, bakname)
3832 util.copyfile(target, bakname)
3812 tobackup.remove(abs)
3833 tobackup.remove(abs)
3813 if abs not in files:
3834 if abs not in files:
3814 files.add(abs)
3835 files.add(abs)
3815 if operation == b'keep':
3836 if operation == b'keep':
3816 checkout(abs)
3837 checkout(abs)
3817 c.write(fp)
3838 c.write(fp)
3818 dopatch = fp.tell()
3839 dopatch = fp.tell()
3819 fp.seek(0)
3840 fp.seek(0)
3820 if dopatch:
3841 if dopatch:
3821 try:
3842 try:
3822 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3843 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3823 except error.PatchError as err:
3844 except error.PatchError as err:
3824 raise error.Abort(pycompat.bytestr(err))
3845 raise error.Abort(pycompat.bytestr(err))
3825 del fp
3846 del fp
3826 else:
3847 else:
3827 for f in actions[b'revert'][0]:
3848 for f in actions[b'revert'][0]:
3828 prntstatusmsg(b'revert', f)
3849 prntstatusmsg(b'revert', f)
3829 checkout(f)
3850 checkout(f)
3830 if normal:
3851 if normal:
3831 normal(f)
3852 normal(f)
3832
3853
3833 for f in actions[b'add'][0]:
3854 for f in actions[b'add'][0]:
3834 # Don't checkout modified files, they are already created by the diff
3855 # Don't checkout modified files, they are already created by the diff
3835 if f not in newlyaddedandmodifiedfiles:
3856 if f not in newlyaddedandmodifiedfiles:
3836 prntstatusmsg(b'add', f)
3857 prntstatusmsg(b'add', f)
3837 checkout(f)
3858 checkout(f)
3838 repo.dirstate.add(f)
3859 repo.dirstate.add(f)
3839
3860
3840 normal = repo.dirstate.normallookup
3861 normal = repo.dirstate.normallookup
3841 if node == parent and p2 == nullid:
3862 if node == parent and p2 == nullid:
3842 normal = repo.dirstate.normal
3863 normal = repo.dirstate.normal
3843 for f in actions[b'undelete'][0]:
3864 for f in actions[b'undelete'][0]:
3844 if interactive:
3865 if interactive:
3845 choice = repo.ui.promptchoice(
3866 choice = repo.ui.promptchoice(
3846 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3867 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3847 )
3868 )
3848 if choice == 0:
3869 if choice == 0:
3849 prntstatusmsg(b'undelete', f)
3870 prntstatusmsg(b'undelete', f)
3850 checkout(f)
3871 checkout(f)
3851 normal(f)
3872 normal(f)
3852 else:
3873 else:
3853 excluded_files.append(f)
3874 excluded_files.append(f)
3854 else:
3875 else:
3855 prntstatusmsg(b'undelete', f)
3876 prntstatusmsg(b'undelete', f)
3856 checkout(f)
3877 checkout(f)
3857 normal(f)
3878 normal(f)
3858
3879
3859 copied = copies.pathcopies(repo[parent], ctx)
3880 copied = copies.pathcopies(repo[parent], ctx)
3860
3881
3861 for f in (
3882 for f in (
3862 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3883 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3863 ):
3884 ):
3864 if f in copied:
3885 if f in copied:
3865 repo.dirstate.copy(copied[f], f)
3886 repo.dirstate.copy(copied[f], f)
3866
3887
3867
3888
3868 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3889 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3869 # commands.outgoing. "missing" is "missing" of the result of
3890 # commands.outgoing. "missing" is "missing" of the result of
3870 # "findcommonoutgoing()"
3891 # "findcommonoutgoing()"
3871 outgoinghooks = util.hooks()
3892 outgoinghooks = util.hooks()
3872
3893
3873 # a list of (ui, repo) functions called by commands.summary
3894 # a list of (ui, repo) functions called by commands.summary
3874 summaryhooks = util.hooks()
3895 summaryhooks = util.hooks()
3875
3896
3876 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3897 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3877 #
3898 #
3878 # functions should return tuple of booleans below, if 'changes' is None:
3899 # functions should return tuple of booleans below, if 'changes' is None:
3879 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3900 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3880 #
3901 #
3881 # otherwise, 'changes' is a tuple of tuples below:
3902 # otherwise, 'changes' is a tuple of tuples below:
3882 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3903 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3883 # - (desturl, destbranch, destpeer, outgoing)
3904 # - (desturl, destbranch, destpeer, outgoing)
3884 summaryremotehooks = util.hooks()
3905 summaryremotehooks = util.hooks()
3885
3906
3886
3907
3887 def checkunfinished(repo, commit=False, skipmerge=False):
3908 def checkunfinished(repo, commit=False, skipmerge=False):
3888 '''Look for an unfinished multistep operation, like graft, and abort
3909 '''Look for an unfinished multistep operation, like graft, and abort
3889 if found. It's probably good to check this right before
3910 if found. It's probably good to check this right before
3890 bailifchanged().
3911 bailifchanged().
3891 '''
3912 '''
3892 # Check for non-clearable states first, so things like rebase will take
3913 # Check for non-clearable states first, so things like rebase will take
3893 # precedence over update.
3914 # precedence over update.
3894 for state in statemod._unfinishedstates:
3915 for state in statemod._unfinishedstates:
3895 if (
3916 if (
3896 state._clearable
3917 state._clearable
3897 or (commit and state._allowcommit)
3918 or (commit and state._allowcommit)
3898 or state._reportonly
3919 or state._reportonly
3899 ):
3920 ):
3900 continue
3921 continue
3901 if state.isunfinished(repo):
3922 if state.isunfinished(repo):
3902 raise error.Abort(state.msg(), hint=state.hint())
3923 raise error.Abort(state.msg(), hint=state.hint())
3903
3924
3904 for s in statemod._unfinishedstates:
3925 for s in statemod._unfinishedstates:
3905 if (
3926 if (
3906 not s._clearable
3927 not s._clearable
3907 or (commit and s._allowcommit)
3928 or (commit and s._allowcommit)
3908 or (s._opname == b'merge' and skipmerge)
3929 or (s._opname == b'merge' and skipmerge)
3909 or s._reportonly
3930 or s._reportonly
3910 ):
3931 ):
3911 continue
3932 continue
3912 if s.isunfinished(repo):
3933 if s.isunfinished(repo):
3913 raise error.Abort(s.msg(), hint=s.hint())
3934 raise error.Abort(s.msg(), hint=s.hint())
3914
3935
3915
3936
3916 def clearunfinished(repo):
3937 def clearunfinished(repo):
3917 '''Check for unfinished operations (as above), and clear the ones
3938 '''Check for unfinished operations (as above), and clear the ones
3918 that are clearable.
3939 that are clearable.
3919 '''
3940 '''
3920 for state in statemod._unfinishedstates:
3941 for state in statemod._unfinishedstates:
3921 if state._reportonly:
3942 if state._reportonly:
3922 continue
3943 continue
3923 if not state._clearable and state.isunfinished(repo):
3944 if not state._clearable and state.isunfinished(repo):
3924 raise error.Abort(state.msg(), hint=state.hint())
3945 raise error.Abort(state.msg(), hint=state.hint())
3925
3946
3926 for s in statemod._unfinishedstates:
3947 for s in statemod._unfinishedstates:
3927 if s._opname == b'merge' or state._reportonly:
3948 if s._opname == b'merge' or state._reportonly:
3928 continue
3949 continue
3929 if s._clearable and s.isunfinished(repo):
3950 if s._clearable and s.isunfinished(repo):
3930 util.unlink(repo.vfs.join(s._fname))
3951 util.unlink(repo.vfs.join(s._fname))
3931
3952
3932
3953
3933 def getunfinishedstate(repo):
3954 def getunfinishedstate(repo):
3934 ''' Checks for unfinished operations and returns statecheck object
3955 ''' Checks for unfinished operations and returns statecheck object
3935 for it'''
3956 for it'''
3936 for state in statemod._unfinishedstates:
3957 for state in statemod._unfinishedstates:
3937 if state.isunfinished(repo):
3958 if state.isunfinished(repo):
3938 return state
3959 return state
3939 return None
3960 return None
3940
3961
3941
3962
3942 def howtocontinue(repo):
3963 def howtocontinue(repo):
3943 '''Check for an unfinished operation and return the command to finish
3964 '''Check for an unfinished operation and return the command to finish
3944 it.
3965 it.
3945
3966
3946 statemod._unfinishedstates list is checked for an unfinished operation
3967 statemod._unfinishedstates list is checked for an unfinished operation
3947 and the corresponding message to finish it is generated if a method to
3968 and the corresponding message to finish it is generated if a method to
3948 continue is supported by the operation.
3969 continue is supported by the operation.
3949
3970
3950 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3971 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3951 a boolean.
3972 a boolean.
3952 '''
3973 '''
3953 contmsg = _(b"continue: %s")
3974 contmsg = _(b"continue: %s")
3954 for state in statemod._unfinishedstates:
3975 for state in statemod._unfinishedstates:
3955 if not state._continueflag:
3976 if not state._continueflag:
3956 continue
3977 continue
3957 if state.isunfinished(repo):
3978 if state.isunfinished(repo):
3958 return contmsg % state.continuemsg(), True
3979 return contmsg % state.continuemsg(), True
3959 if repo[None].dirty(missing=True, merge=False, branch=False):
3980 if repo[None].dirty(missing=True, merge=False, branch=False):
3960 return contmsg % _(b"hg commit"), False
3981 return contmsg % _(b"hg commit"), False
3961 return None, None
3982 return None, None
3962
3983
3963
3984
3964 def checkafterresolved(repo):
3985 def checkafterresolved(repo):
3965 '''Inform the user about the next action after completing hg resolve
3986 '''Inform the user about the next action after completing hg resolve
3966
3987
3967 If there's a an unfinished operation that supports continue flag,
3988 If there's a an unfinished operation that supports continue flag,
3968 howtocontinue will yield repo.ui.warn as the reporter.
3989 howtocontinue will yield repo.ui.warn as the reporter.
3969
3990
3970 Otherwise, it will yield repo.ui.note.
3991 Otherwise, it will yield repo.ui.note.
3971 '''
3992 '''
3972 msg, warning = howtocontinue(repo)
3993 msg, warning = howtocontinue(repo)
3973 if msg is not None:
3994 if msg is not None:
3974 if warning:
3995 if warning:
3975 repo.ui.warn(b"%s\n" % msg)
3996 repo.ui.warn(b"%s\n" % msg)
3976 else:
3997 else:
3977 repo.ui.note(b"%s\n" % msg)
3998 repo.ui.note(b"%s\n" % msg)
3978
3999
3979
4000
3980 def wrongtooltocontinue(repo, task):
4001 def wrongtooltocontinue(repo, task):
3981 '''Raise an abort suggesting how to properly continue if there is an
4002 '''Raise an abort suggesting how to properly continue if there is an
3982 active task.
4003 active task.
3983
4004
3984 Uses howtocontinue() to find the active task.
4005 Uses howtocontinue() to find the active task.
3985
4006
3986 If there's no task (repo.ui.note for 'hg commit'), it does not offer
4007 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3987 a hint.
4008 a hint.
3988 '''
4009 '''
3989 after = howtocontinue(repo)
4010 after = howtocontinue(repo)
3990 hint = None
4011 hint = None
3991 if after[1]:
4012 if after[1]:
3992 hint = after[0]
4013 hint = after[0]
3993 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
4014 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3994
4015
3995
4016
3996 def abortgraft(ui, repo, graftstate):
4017 def abortgraft(ui, repo, graftstate):
3997 """abort the interrupted graft and rollbacks to the state before interrupted
4018 """abort the interrupted graft and rollbacks to the state before interrupted
3998 graft"""
4019 graft"""
3999 if not graftstate.exists():
4020 if not graftstate.exists():
4000 raise error.Abort(_(b"no interrupted graft to abort"))
4021 raise error.Abort(_(b"no interrupted graft to abort"))
4001 statedata = readgraftstate(repo, graftstate)
4022 statedata = readgraftstate(repo, graftstate)
4002 newnodes = statedata.get(b'newnodes')
4023 newnodes = statedata.get(b'newnodes')
4003 if newnodes is None:
4024 if newnodes is None:
4004 # and old graft state which does not have all the data required to abort
4025 # and old graft state which does not have all the data required to abort
4005 # the graft
4026 # the graft
4006 raise error.Abort(_(b"cannot abort using an old graftstate"))
4027 raise error.Abort(_(b"cannot abort using an old graftstate"))
4007
4028
4008 # changeset from which graft operation was started
4029 # changeset from which graft operation was started
4009 if len(newnodes) > 0:
4030 if len(newnodes) > 0:
4010 startctx = repo[newnodes[0]].p1()
4031 startctx = repo[newnodes[0]].p1()
4011 else:
4032 else:
4012 startctx = repo[b'.']
4033 startctx = repo[b'.']
4013 # whether to strip or not
4034 # whether to strip or not
4014 cleanup = False
4035 cleanup = False
4015 from . import hg
4036 from . import hg
4016
4037
4017 if newnodes:
4038 if newnodes:
4018 newnodes = [repo[r].rev() for r in newnodes]
4039 newnodes = [repo[r].rev() for r in newnodes]
4019 cleanup = True
4040 cleanup = True
4020 # checking that none of the newnodes turned public or is public
4041 # checking that none of the newnodes turned public or is public
4021 immutable = [c for c in newnodes if not repo[c].mutable()]
4042 immutable = [c for c in newnodes if not repo[c].mutable()]
4022 if immutable:
4043 if immutable:
4023 repo.ui.warn(
4044 repo.ui.warn(
4024 _(b"cannot clean up public changesets %s\n")
4045 _(b"cannot clean up public changesets %s\n")
4025 % b', '.join(bytes(repo[r]) for r in immutable),
4046 % b', '.join(bytes(repo[r]) for r in immutable),
4026 hint=_(b"see 'hg help phases' for details"),
4047 hint=_(b"see 'hg help phases' for details"),
4027 )
4048 )
4028 cleanup = False
4049 cleanup = False
4029
4050
4030 # checking that no new nodes are created on top of grafted revs
4051 # checking that no new nodes are created on top of grafted revs
4031 desc = set(repo.changelog.descendants(newnodes))
4052 desc = set(repo.changelog.descendants(newnodes))
4032 if desc - set(newnodes):
4053 if desc - set(newnodes):
4033 repo.ui.warn(
4054 repo.ui.warn(
4034 _(
4055 _(
4035 b"new changesets detected on destination "
4056 b"new changesets detected on destination "
4036 b"branch, can't strip\n"
4057 b"branch, can't strip\n"
4037 )
4058 )
4038 )
4059 )
4039 cleanup = False
4060 cleanup = False
4040
4061
4041 if cleanup:
4062 if cleanup:
4042 with repo.wlock(), repo.lock():
4063 with repo.wlock(), repo.lock():
4043 hg.updaterepo(repo, startctx.node(), overwrite=True)
4064 hg.updaterepo(repo, startctx.node(), overwrite=True)
4044 # stripping the new nodes created
4065 # stripping the new nodes created
4045 strippoints = [
4066 strippoints = [
4046 c.node() for c in repo.set(b"roots(%ld)", newnodes)
4067 c.node() for c in repo.set(b"roots(%ld)", newnodes)
4047 ]
4068 ]
4048 repair.strip(repo.ui, repo, strippoints, backup=False)
4069 repair.strip(repo.ui, repo, strippoints, backup=False)
4049
4070
4050 if not cleanup:
4071 if not cleanup:
4051 # we don't update to the startnode if we can't strip
4072 # we don't update to the startnode if we can't strip
4052 startctx = repo[b'.']
4073 startctx = repo[b'.']
4053 hg.updaterepo(repo, startctx.node(), overwrite=True)
4074 hg.updaterepo(repo, startctx.node(), overwrite=True)
4054
4075
4055 ui.status(_(b"graft aborted\n"))
4076 ui.status(_(b"graft aborted\n"))
4056 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
4077 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
4057 graftstate.delete()
4078 graftstate.delete()
4058 return 0
4079 return 0
4059
4080
4060
4081
4061 def readgraftstate(repo, graftstate):
4082 def readgraftstate(repo, graftstate):
4062 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
4083 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
4063 """read the graft state file and return a dict of the data stored in it"""
4084 """read the graft state file and return a dict of the data stored in it"""
4064 try:
4085 try:
4065 return graftstate.read()
4086 return graftstate.read()
4066 except error.CorruptedState:
4087 except error.CorruptedState:
4067 nodes = repo.vfs.read(b'graftstate').splitlines()
4088 nodes = repo.vfs.read(b'graftstate').splitlines()
4068 return {b'nodes': nodes}
4089 return {b'nodes': nodes}
4069
4090
4070
4091
4071 def hgabortgraft(ui, repo):
4092 def hgabortgraft(ui, repo):
4072 """ abort logic for aborting graft using 'hg abort'"""
4093 """ abort logic for aborting graft using 'hg abort'"""
4073 with repo.wlock():
4094 with repo.wlock():
4074 graftstate = statemod.cmdstate(repo, b'graftstate')
4095 graftstate = statemod.cmdstate(repo, b'graftstate')
4075 return abortgraft(ui, repo, graftstate)
4096 return abortgraft(ui, repo, graftstate)
@@ -1,7829 +1,7833 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 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get('dry_run')
183 dryrun = opts.get('dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revset to not display (EXPERIMENTAL)'),
365 _(b'revset to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s'] * len(l))
562 formats.append([b'%s'] * len(l))
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo, hex(repo.changelog.tip()))
879 return hg.merge(repo, hex(repo.changelog.tip()))
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 with hbisect.restore_state(repo, state, node):
1076 with hbisect.restore_state(repo, state, node):
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1109 return
1109 return
1110
1110
1111 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1112
1112
1113 # actually bisect
1113 # actually bisect
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1115 if extend:
1115 if extend:
1116 if not changesets:
1116 if not changesets:
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1118 if extendnode is not None:
1118 if extendnode is not None:
1119 ui.write(
1119 ui.write(
1120 _(b"Extending search to changeset %d:%s\n")
1120 _(b"Extending search to changeset %d:%s\n")
1121 % (extendnode.rev(), extendnode)
1121 % (extendnode.rev(), extendnode)
1122 )
1122 )
1123 state[b'current'] = [extendnode.node()]
1123 state[b'current'] = [extendnode.node()]
1124 hbisect.save_state(repo, state)
1124 hbisect.save_state(repo, state)
1125 return mayupdate(repo, extendnode.node())
1125 return mayupdate(repo, extendnode.node())
1126 raise error.Abort(_(b"nothing to extend"))
1126 raise error.Abort(_(b"nothing to extend"))
1127
1127
1128 if changesets == 0:
1128 if changesets == 0:
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1130 else:
1130 else:
1131 assert len(nodes) == 1 # only a single node can be tested next
1131 assert len(nodes) == 1 # only a single node can be tested next
1132 node = nodes[0]
1132 node = nodes[0]
1133 # compute the approximate number of remaining tests
1133 # compute the approximate number of remaining tests
1134 tests, size = 0, 2
1134 tests, size = 0, 2
1135 while size <= changesets:
1135 while size <= changesets:
1136 tests, size = tests + 1, size * 2
1136 tests, size = tests + 1, size * 2
1137 rev = repo.changelog.rev(node)
1137 rev = repo.changelog.rev(node)
1138 ui.write(
1138 ui.write(
1139 _(
1139 _(
1140 b"Testing changeset %d:%s "
1140 b"Testing changeset %d:%s "
1141 b"(%d changesets remaining, ~%d tests)\n"
1141 b"(%d changesets remaining, ~%d tests)\n"
1142 )
1142 )
1143 % (rev, short(node), changesets, tests)
1143 % (rev, short(node), changesets, tests)
1144 )
1144 )
1145 state[b'current'] = [node]
1145 state[b'current'] = [node]
1146 hbisect.save_state(repo, state)
1146 hbisect.save_state(repo, state)
1147 return mayupdate(repo, node)
1147 return mayupdate(repo, node)
1148
1148
1149
1149
1150 @command(
1150 @command(
1151 b'bookmarks|bookmark',
1151 b'bookmarks|bookmark',
1152 [
1152 [
1153 (b'f', b'force', False, _(b'force')),
1153 (b'f', b'force', False, _(b'force')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1159 ]
1159 ]
1160 + formatteropts,
1160 + formatteropts,
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1163 )
1163 )
1164 def bookmark(ui, repo, *names, **opts):
1164 def bookmark(ui, repo, *names, **opts):
1165 '''create a new bookmark or list existing bookmarks
1165 '''create a new bookmark or list existing bookmarks
1166
1166
1167 Bookmarks are labels on changesets to help track lines of development.
1167 Bookmarks are labels on changesets to help track lines of development.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1170
1170
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1172 The active bookmark is indicated with a '*'.
1172 The active bookmark is indicated with a '*'.
1173 When a commit is made, the active bookmark will advance to the new commit.
1173 When a commit is made, the active bookmark will advance to the new commit.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1175 Updating away from a bookmark will cause it to be deactivated.
1175 Updating away from a bookmark will cause it to be deactivated.
1176
1176
1177 Bookmarks can be pushed and pulled between repositories (see
1177 Bookmarks can be pushed and pulled between repositories (see
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1180 be created. Using :hg:`merge` will resolve the divergence.
1180 be created. Using :hg:`merge` will resolve the divergence.
1181
1181
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1183 the active bookmark's name.
1183 the active bookmark's name.
1184
1184
1185 A bookmark named '@' has the special property that :hg:`clone` will
1185 A bookmark named '@' has the special property that :hg:`clone` will
1186 check it out by default if it exists.
1186 check it out by default if it exists.
1187
1187
1188 .. container:: verbose
1188 .. container:: verbose
1189
1189
1190 Template:
1190 Template:
1191
1191
1192 The following keywords are supported in addition to the common template
1192 The following keywords are supported in addition to the common template
1193 keywords and functions such as ``{bookmark}``. See also
1193 keywords and functions such as ``{bookmark}``. See also
1194 :hg:`help templates`.
1194 :hg:`help templates`.
1195
1195
1196 :active: Boolean. True if the bookmark is active.
1196 :active: Boolean. True if the bookmark is active.
1197
1197
1198 Examples:
1198 Examples:
1199
1199
1200 - create an active bookmark for a new line of development::
1200 - create an active bookmark for a new line of development::
1201
1201
1202 hg book new-feature
1202 hg book new-feature
1203
1203
1204 - create an inactive bookmark as a place marker::
1204 - create an inactive bookmark as a place marker::
1205
1205
1206 hg book -i reviewed
1206 hg book -i reviewed
1207
1207
1208 - create an inactive bookmark on another changeset::
1208 - create an inactive bookmark on another changeset::
1209
1209
1210 hg book -r .^ tested
1210 hg book -r .^ tested
1211
1211
1212 - rename bookmark turkey to dinner::
1212 - rename bookmark turkey to dinner::
1213
1213
1214 hg book -m turkey dinner
1214 hg book -m turkey dinner
1215
1215
1216 - move the '@' bookmark from another branch::
1216 - move the '@' bookmark from another branch::
1217
1217
1218 hg book -f @
1218 hg book -f @
1219
1219
1220 - print only the active bookmark name::
1220 - print only the active bookmark name::
1221
1221
1222 hg book -ql .
1222 hg book -ql .
1223 '''
1223 '''
1224 opts = pycompat.byteskwargs(opts)
1224 opts = pycompat.byteskwargs(opts)
1225 force = opts.get(b'force')
1225 force = opts.get(b'force')
1226 rev = opts.get(b'rev')
1226 rev = opts.get(b'rev')
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1228
1228
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1229 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1230 if action:
1230 if action:
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1231 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1232 elif names or rev:
1232 elif names or rev:
1233 action = b'add'
1233 action = b'add'
1234 elif inactive:
1234 elif inactive:
1235 action = b'inactive' # meaning deactivate
1235 action = b'inactive' # meaning deactivate
1236 else:
1236 else:
1237 action = b'list'
1237 action = b'list'
1238
1238
1239 cmdutil.check_incompatible_arguments(
1239 cmdutil.check_incompatible_arguments(
1240 opts, b'inactive', [b'delete', b'list']
1240 opts, b'inactive', [b'delete', b'list']
1241 )
1241 )
1242 if not names and action in {b'add', b'delete'}:
1242 if not names and action in {b'add', b'delete'}:
1243 raise error.Abort(_(b"bookmark name required"))
1243 raise error.Abort(_(b"bookmark name required"))
1244
1244
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1245 if action in {b'add', b'delete', b'rename', b'inactive'}:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1246 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1247 if action == b'delete':
1247 if action == b'delete':
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1248 names = pycompat.maplist(repo._bookmarks.expandname, names)
1249 bookmarks.delete(repo, tr, names)
1249 bookmarks.delete(repo, tr, names)
1250 elif action == b'rename':
1250 elif action == b'rename':
1251 if not names:
1251 if not names:
1252 raise error.Abort(_(b"new bookmark name required"))
1252 raise error.Abort(_(b"new bookmark name required"))
1253 elif len(names) > 1:
1253 elif len(names) > 1:
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1254 raise error.Abort(_(b"only one new bookmark name allowed"))
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1255 oldname = repo._bookmarks.expandname(opts[b'rename'])
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1256 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1257 elif action == b'add':
1257 elif action == b'add':
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1258 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1259 elif action == b'inactive':
1259 elif action == b'inactive':
1260 if len(repo._bookmarks) == 0:
1260 if len(repo._bookmarks) == 0:
1261 ui.status(_(b"no bookmarks set\n"))
1261 ui.status(_(b"no bookmarks set\n"))
1262 elif not repo._activebookmark:
1262 elif not repo._activebookmark:
1263 ui.status(_(b"no active bookmark\n"))
1263 ui.status(_(b"no active bookmark\n"))
1264 else:
1264 else:
1265 bookmarks.deactivate(repo)
1265 bookmarks.deactivate(repo)
1266 elif action == b'list':
1266 elif action == b'list':
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1267 names = pycompat.maplist(repo._bookmarks.expandname, names)
1268 with ui.formatter(b'bookmarks', opts) as fm:
1268 with ui.formatter(b'bookmarks', opts) as fm:
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1269 bookmarks.printbookmarks(ui, repo, fm, names)
1270 else:
1270 else:
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1271 raise error.ProgrammingError(b'invalid action: %s' % action)
1272
1272
1273
1273
1274 @command(
1274 @command(
1275 b'branch',
1275 b'branch',
1276 [
1276 [
1277 (
1277 (
1278 b'f',
1278 b'f',
1279 b'force',
1279 b'force',
1280 None,
1280 None,
1281 _(b'set branch name even if it shadows an existing branch'),
1281 _(b'set branch name even if it shadows an existing branch'),
1282 ),
1282 ),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1283 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1284 (
1284 (
1285 b'r',
1285 b'r',
1286 b'rev',
1286 b'rev',
1287 [],
1287 [],
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1288 _(b'change branches of the given revs (EXPERIMENTAL)'),
1289 ),
1289 ),
1290 ],
1290 ],
1291 _(b'[-fC] [NAME]'),
1291 _(b'[-fC] [NAME]'),
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1292 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1293 )
1293 )
1294 def branch(ui, repo, label=None, **opts):
1294 def branch(ui, repo, label=None, **opts):
1295 """set or show the current branch name
1295 """set or show the current branch name
1296
1296
1297 .. note::
1297 .. note::
1298
1298
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1299 Branch names are permanent and global. Use :hg:`bookmark` to create a
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1300 light-weight bookmark instead. See :hg:`help glossary` for more
1301 information about named branches and bookmarks.
1301 information about named branches and bookmarks.
1302
1302
1303 With no argument, show the current branch name. With one argument,
1303 With no argument, show the current branch name. With one argument,
1304 set the working directory branch name (the branch will not exist
1304 set the working directory branch name (the branch will not exist
1305 in the repository until the next commit). Standard practice
1305 in the repository until the next commit). Standard practice
1306 recommends that primary development take place on the 'default'
1306 recommends that primary development take place on the 'default'
1307 branch.
1307 branch.
1308
1308
1309 Unless -f/--force is specified, branch will not let you set a
1309 Unless -f/--force is specified, branch will not let you set a
1310 branch name that already exists.
1310 branch name that already exists.
1311
1311
1312 Use -C/--clean to reset the working directory branch to that of
1312 Use -C/--clean to reset the working directory branch to that of
1313 the parent of the working directory, negating a previous branch
1313 the parent of the working directory, negating a previous branch
1314 change.
1314 change.
1315
1315
1316 Use the command :hg:`update` to switch to an existing branch. Use
1316 Use the command :hg:`update` to switch to an existing branch. Use
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1317 :hg:`commit --close-branch` to mark this branch head as closed.
1318 When all heads of a branch are closed, the branch will be
1318 When all heads of a branch are closed, the branch will be
1319 considered closed.
1319 considered closed.
1320
1320
1321 Returns 0 on success.
1321 Returns 0 on success.
1322 """
1322 """
1323 opts = pycompat.byteskwargs(opts)
1323 opts = pycompat.byteskwargs(opts)
1324 revs = opts.get(b'rev')
1324 revs = opts.get(b'rev')
1325 if label:
1325 if label:
1326 label = label.strip()
1326 label = label.strip()
1327
1327
1328 if not opts.get(b'clean') and not label:
1328 if not opts.get(b'clean') and not label:
1329 if revs:
1329 if revs:
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1330 raise error.Abort(_(b"no branch name specified for the revisions"))
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1331 ui.write(b"%s\n" % repo.dirstate.branch())
1332 return
1332 return
1333
1333
1334 with repo.wlock():
1334 with repo.wlock():
1335 if opts.get(b'clean'):
1335 if opts.get(b'clean'):
1336 label = repo[b'.'].branch()
1336 label = repo[b'.'].branch()
1337 repo.dirstate.setbranch(label)
1337 repo.dirstate.setbranch(label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1338 ui.status(_(b'reset working directory to branch %s\n') % label)
1339 elif label:
1339 elif label:
1340
1340
1341 scmutil.checknewlabel(repo, label, b'branch')
1341 scmutil.checknewlabel(repo, label, b'branch')
1342 if revs:
1342 if revs:
1343 return cmdutil.changebranch(ui, repo, revs, label)
1343 return cmdutil.changebranch(ui, repo, revs, label)
1344
1344
1345 if not opts.get(b'force') and label in repo.branchmap():
1345 if not opts.get(b'force') and label in repo.branchmap():
1346 if label not in [p.branch() for p in repo[None].parents()]:
1346 if label not in [p.branch() for p in repo[None].parents()]:
1347 raise error.Abort(
1347 raise error.Abort(
1348 _(b'a branch of the same name already exists'),
1348 _(b'a branch of the same name already exists'),
1349 # i18n: "it" refers to an existing branch
1349 # i18n: "it" refers to an existing branch
1350 hint=_(b"use 'hg update' to switch to it"),
1350 hint=_(b"use 'hg update' to switch to it"),
1351 )
1351 )
1352
1352
1353 repo.dirstate.setbranch(label)
1353 repo.dirstate.setbranch(label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1354 ui.status(_(b'marked working directory as branch %s\n') % label)
1355
1355
1356 # find any open named branches aside from default
1356 # find any open named branches aside from default
1357 for n, h, t, c in repo.branchmap().iterbranches():
1357 for n, h, t, c in repo.branchmap().iterbranches():
1358 if n != b"default" and not c:
1358 if n != b"default" and not c:
1359 return 0
1359 return 0
1360 ui.status(
1360 ui.status(
1361 _(
1361 _(
1362 b'(branches are permanent and global, '
1362 b'(branches are permanent and global, '
1363 b'did you want a bookmark?)\n'
1363 b'did you want a bookmark?)\n'
1364 )
1364 )
1365 )
1365 )
1366
1366
1367
1367
1368 @command(
1368 @command(
1369 b'branches',
1369 b'branches',
1370 [
1370 [
1371 (
1371 (
1372 b'a',
1372 b'a',
1373 b'active',
1373 b'active',
1374 False,
1374 False,
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1375 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1376 ),
1376 ),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1377 (b'c', b'closed', False, _(b'show normal and closed branches')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1378 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1379 ]
1379 ]
1380 + formatteropts,
1380 + formatteropts,
1381 _(b'[-c]'),
1381 _(b'[-c]'),
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1382 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1383 intents={INTENT_READONLY},
1383 intents={INTENT_READONLY},
1384 )
1384 )
1385 def branches(ui, repo, active=False, closed=False, **opts):
1385 def branches(ui, repo, active=False, closed=False, **opts):
1386 """list repository named branches
1386 """list repository named branches
1387
1387
1388 List the repository's named branches, indicating which ones are
1388 List the repository's named branches, indicating which ones are
1389 inactive. If -c/--closed is specified, also list branches which have
1389 inactive. If -c/--closed is specified, also list branches which have
1390 been marked closed (see :hg:`commit --close-branch`).
1390 been marked closed (see :hg:`commit --close-branch`).
1391
1391
1392 Use the command :hg:`update` to switch to an existing branch.
1392 Use the command :hg:`update` to switch to an existing branch.
1393
1393
1394 .. container:: verbose
1394 .. container:: verbose
1395
1395
1396 Template:
1396 Template:
1397
1397
1398 The following keywords are supported in addition to the common template
1398 The following keywords are supported in addition to the common template
1399 keywords and functions such as ``{branch}``. See also
1399 keywords and functions such as ``{branch}``. See also
1400 :hg:`help templates`.
1400 :hg:`help templates`.
1401
1401
1402 :active: Boolean. True if the branch is active.
1402 :active: Boolean. True if the branch is active.
1403 :closed: Boolean. True if the branch is closed.
1403 :closed: Boolean. True if the branch is closed.
1404 :current: Boolean. True if it is the current branch.
1404 :current: Boolean. True if it is the current branch.
1405
1405
1406 Returns 0.
1406 Returns 0.
1407 """
1407 """
1408
1408
1409 opts = pycompat.byteskwargs(opts)
1409 opts = pycompat.byteskwargs(opts)
1410 revs = opts.get(b'rev')
1410 revs = opts.get(b'rev')
1411 selectedbranches = None
1411 selectedbranches = None
1412 if revs:
1412 if revs:
1413 revs = scmutil.revrange(repo, revs)
1413 revs = scmutil.revrange(repo, revs)
1414 getbi = repo.revbranchcache().branchinfo
1414 getbi = repo.revbranchcache().branchinfo
1415 selectedbranches = {getbi(r)[0] for r in revs}
1415 selectedbranches = {getbi(r)[0] for r in revs}
1416
1416
1417 ui.pager(b'branches')
1417 ui.pager(b'branches')
1418 fm = ui.formatter(b'branches', opts)
1418 fm = ui.formatter(b'branches', opts)
1419 hexfunc = fm.hexfunc
1419 hexfunc = fm.hexfunc
1420
1420
1421 allheads = set(repo.heads())
1421 allheads = set(repo.heads())
1422 branches = []
1422 branches = []
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1423 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1424 if selectedbranches is not None and tag not in selectedbranches:
1424 if selectedbranches is not None and tag not in selectedbranches:
1425 continue
1425 continue
1426 isactive = False
1426 isactive = False
1427 if not isclosed:
1427 if not isclosed:
1428 openheads = set(repo.branchmap().iteropen(heads))
1428 openheads = set(repo.branchmap().iteropen(heads))
1429 isactive = bool(openheads & allheads)
1429 isactive = bool(openheads & allheads)
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1430 branches.append((tag, repo[tip], isactive, not isclosed))
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1431 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1432
1432
1433 for tag, ctx, isactive, isopen in branches:
1433 for tag, ctx, isactive, isopen in branches:
1434 if active and not isactive:
1434 if active and not isactive:
1435 continue
1435 continue
1436 if isactive:
1436 if isactive:
1437 label = b'branches.active'
1437 label = b'branches.active'
1438 notice = b''
1438 notice = b''
1439 elif not isopen:
1439 elif not isopen:
1440 if not closed:
1440 if not closed:
1441 continue
1441 continue
1442 label = b'branches.closed'
1442 label = b'branches.closed'
1443 notice = _(b' (closed)')
1443 notice = _(b' (closed)')
1444 else:
1444 else:
1445 label = b'branches.inactive'
1445 label = b'branches.inactive'
1446 notice = _(b' (inactive)')
1446 notice = _(b' (inactive)')
1447 current = tag == repo.dirstate.branch()
1447 current = tag == repo.dirstate.branch()
1448 if current:
1448 if current:
1449 label = b'branches.current'
1449 label = b'branches.current'
1450
1450
1451 fm.startitem()
1451 fm.startitem()
1452 fm.write(b'branch', b'%s', tag, label=label)
1452 fm.write(b'branch', b'%s', tag, label=label)
1453 rev = ctx.rev()
1453 rev = ctx.rev()
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1454 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1455 fmt = b' ' * padsize + b' %d:%s'
1455 fmt = b' ' * padsize + b' %d:%s'
1456 fm.condwrite(
1456 fm.condwrite(
1457 not ui.quiet,
1457 not ui.quiet,
1458 b'rev node',
1458 b'rev node',
1459 fmt,
1459 fmt,
1460 rev,
1460 rev,
1461 hexfunc(ctx.node()),
1461 hexfunc(ctx.node()),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1462 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1463 )
1463 )
1464 fm.context(ctx=ctx)
1464 fm.context(ctx=ctx)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1465 fm.data(active=isactive, closed=not isopen, current=current)
1466 if not ui.quiet:
1466 if not ui.quiet:
1467 fm.plain(notice)
1467 fm.plain(notice)
1468 fm.plain(b'\n')
1468 fm.plain(b'\n')
1469 fm.end()
1469 fm.end()
1470
1470
1471
1471
1472 @command(
1472 @command(
1473 b'bundle',
1473 b'bundle',
1474 [
1474 [
1475 (
1475 (
1476 b'f',
1476 b'f',
1477 b'force',
1477 b'force',
1478 None,
1478 None,
1479 _(b'run even when the destination is unrelated'),
1479 _(b'run even when the destination is unrelated'),
1480 ),
1480 ),
1481 (
1481 (
1482 b'r',
1482 b'r',
1483 b'rev',
1483 b'rev',
1484 [],
1484 [],
1485 _(b'a changeset intended to be added to the destination'),
1485 _(b'a changeset intended to be added to the destination'),
1486 _(b'REV'),
1486 _(b'REV'),
1487 ),
1487 ),
1488 (
1488 (
1489 b'b',
1489 b'b',
1490 b'branch',
1490 b'branch',
1491 [],
1491 [],
1492 _(b'a specific branch you would like to bundle'),
1492 _(b'a specific branch you would like to bundle'),
1493 _(b'BRANCH'),
1493 _(b'BRANCH'),
1494 ),
1494 ),
1495 (
1495 (
1496 b'',
1496 b'',
1497 b'base',
1497 b'base',
1498 [],
1498 [],
1499 _(b'a base changeset assumed to be available at the destination'),
1499 _(b'a base changeset assumed to be available at the destination'),
1500 _(b'REV'),
1500 _(b'REV'),
1501 ),
1501 ),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1502 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1503 (
1503 (
1504 b't',
1504 b't',
1505 b'type',
1505 b'type',
1506 b'bzip2',
1506 b'bzip2',
1507 _(b'bundle compression type to use'),
1507 _(b'bundle compression type to use'),
1508 _(b'TYPE'),
1508 _(b'TYPE'),
1509 ),
1509 ),
1510 ]
1510 ]
1511 + remoteopts,
1511 + remoteopts,
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1512 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1513 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1514 )
1514 )
1515 def bundle(ui, repo, fname, dest=None, **opts):
1515 def bundle(ui, repo, fname, dest=None, **opts):
1516 """create a bundle file
1516 """create a bundle file
1517
1517
1518 Generate a bundle file containing data to be transferred to another
1518 Generate a bundle file containing data to be transferred to another
1519 repository.
1519 repository.
1520
1520
1521 To create a bundle containing all changesets, use -a/--all
1521 To create a bundle containing all changesets, use -a/--all
1522 (or --base null). Otherwise, hg assumes the destination will have
1522 (or --base null). Otherwise, hg assumes the destination will have
1523 all the nodes you specify with --base parameters. Otherwise, hg
1523 all the nodes you specify with --base parameters. Otherwise, hg
1524 will assume the repository has all the nodes in destination, or
1524 will assume the repository has all the nodes in destination, or
1525 default-push/default if no destination is specified, where destination
1525 default-push/default if no destination is specified, where destination
1526 is the repository you provide through DEST option.
1526 is the repository you provide through DEST option.
1527
1527
1528 You can change bundle format with the -t/--type option. See
1528 You can change bundle format with the -t/--type option. See
1529 :hg:`help bundlespec` for documentation on this format. By default,
1529 :hg:`help bundlespec` for documentation on this format. By default,
1530 the most appropriate format is used and compression defaults to
1530 the most appropriate format is used and compression defaults to
1531 bzip2.
1531 bzip2.
1532
1532
1533 The bundle file can then be transferred using conventional means
1533 The bundle file can then be transferred using conventional means
1534 and applied to another repository with the unbundle or pull
1534 and applied to another repository with the unbundle or pull
1535 command. This is useful when direct push and pull are not
1535 command. This is useful when direct push and pull are not
1536 available or when exporting an entire repository is undesirable.
1536 available or when exporting an entire repository is undesirable.
1537
1537
1538 Applying bundles preserves all changeset contents including
1538 Applying bundles preserves all changeset contents including
1539 permissions, copy/rename information, and revision history.
1539 permissions, copy/rename information, and revision history.
1540
1540
1541 Returns 0 on success, 1 if no changes found.
1541 Returns 0 on success, 1 if no changes found.
1542 """
1542 """
1543 opts = pycompat.byteskwargs(opts)
1543 opts = pycompat.byteskwargs(opts)
1544 revs = None
1544 revs = None
1545 if b'rev' in opts:
1545 if b'rev' in opts:
1546 revstrings = opts[b'rev']
1546 revstrings = opts[b'rev']
1547 revs = scmutil.revrange(repo, revstrings)
1547 revs = scmutil.revrange(repo, revstrings)
1548 if revstrings and not revs:
1548 if revstrings and not revs:
1549 raise error.Abort(_(b'no commits to bundle'))
1549 raise error.Abort(_(b'no commits to bundle'))
1550
1550
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1551 bundletype = opts.get(b'type', b'bzip2').lower()
1552 try:
1552 try:
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1553 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1554 except error.UnsupportedBundleSpecification as e:
1554 except error.UnsupportedBundleSpecification as e:
1555 raise error.Abort(
1555 raise error.Abort(
1556 pycompat.bytestr(e),
1556 pycompat.bytestr(e),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1557 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1558 )
1558 )
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1559 cgversion = bundlespec.contentopts[b"cg.version"]
1560
1560
1561 # Packed bundles are a pseudo bundle format for now.
1561 # Packed bundles are a pseudo bundle format for now.
1562 if cgversion == b's1':
1562 if cgversion == b's1':
1563 raise error.Abort(
1563 raise error.Abort(
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1564 _(b'packed bundles cannot be produced by "hg bundle"'),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1565 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1566 )
1566 )
1567
1567
1568 if opts.get(b'all'):
1568 if opts.get(b'all'):
1569 if dest:
1569 if dest:
1570 raise error.Abort(
1570 raise error.Abort(
1571 _(b"--all is incompatible with specifying a destination")
1571 _(b"--all is incompatible with specifying a destination")
1572 )
1572 )
1573 if opts.get(b'base'):
1573 if opts.get(b'base'):
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1574 ui.warn(_(b"ignoring --base because --all was specified\n"))
1575 base = [nullrev]
1575 base = [nullrev]
1576 else:
1576 else:
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1577 base = scmutil.revrange(repo, opts.get(b'base'))
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1578 if cgversion not in changegroup.supportedoutgoingversions(repo):
1579 raise error.Abort(
1579 raise error.Abort(
1580 _(b"repository does not support bundle version %s") % cgversion
1580 _(b"repository does not support bundle version %s") % cgversion
1581 )
1581 )
1582
1582
1583 if base:
1583 if base:
1584 if dest:
1584 if dest:
1585 raise error.Abort(
1585 raise error.Abort(
1586 _(b"--base is incompatible with specifying a destination")
1586 _(b"--base is incompatible with specifying a destination")
1587 )
1587 )
1588 common = [repo[rev].node() for rev in base]
1588 common = [repo[rev].node() for rev in base]
1589 heads = [repo[r].node() for r in revs] if revs else None
1589 heads = [repo[r].node() for r in revs] if revs else None
1590 outgoing = discovery.outgoing(repo, common, heads)
1590 outgoing = discovery.outgoing(repo, common, heads)
1591 else:
1591 else:
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1592 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1593 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1594 other = hg.peer(repo, opts, dest)
1594 other = hg.peer(repo, opts, dest)
1595 revs = [repo[r].hex() for r in revs]
1595 revs = [repo[r].hex() for r in revs]
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1596 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1597 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1598 outgoing = discovery.findcommonoutgoing(
1598 outgoing = discovery.findcommonoutgoing(
1599 repo,
1599 repo,
1600 other,
1600 other,
1601 onlyheads=heads,
1601 onlyheads=heads,
1602 force=opts.get(b'force'),
1602 force=opts.get(b'force'),
1603 portable=True,
1603 portable=True,
1604 )
1604 )
1605
1605
1606 if not outgoing.missing:
1606 if not outgoing.missing:
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1607 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1608 return 1
1608 return 1
1609
1609
1610 if cgversion == b'01': # bundle1
1610 if cgversion == b'01': # bundle1
1611 bversion = b'HG10' + bundlespec.wirecompression
1611 bversion = b'HG10' + bundlespec.wirecompression
1612 bcompression = None
1612 bcompression = None
1613 elif cgversion in (b'02', b'03'):
1613 elif cgversion in (b'02', b'03'):
1614 bversion = b'HG20'
1614 bversion = b'HG20'
1615 bcompression = bundlespec.wirecompression
1615 bcompression = bundlespec.wirecompression
1616 else:
1616 else:
1617 raise error.ProgrammingError(
1617 raise error.ProgrammingError(
1618 b'bundle: unexpected changegroup version %s' % cgversion
1618 b'bundle: unexpected changegroup version %s' % cgversion
1619 )
1619 )
1620
1620
1621 # TODO compression options should be derived from bundlespec parsing.
1621 # TODO compression options should be derived from bundlespec parsing.
1622 # This is a temporary hack to allow adjusting bundle compression
1622 # This is a temporary hack to allow adjusting bundle compression
1623 # level without a) formalizing the bundlespec changes to declare it
1623 # level without a) formalizing the bundlespec changes to declare it
1624 # b) introducing a command flag.
1624 # b) introducing a command flag.
1625 compopts = {}
1625 compopts = {}
1626 complevel = ui.configint(
1626 complevel = ui.configint(
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1627 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1628 )
1628 )
1629 if complevel is None:
1629 if complevel is None:
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1630 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1631 if complevel is not None:
1631 if complevel is not None:
1632 compopts[b'level'] = complevel
1632 compopts[b'level'] = complevel
1633
1633
1634 # Allow overriding the bundling of obsmarker in phases through
1634 # Allow overriding the bundling of obsmarker in phases through
1635 # configuration while we don't have a bundle version that include them
1635 # configuration while we don't have a bundle version that include them
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1636 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1637 bundlespec.contentopts[b'obsolescence'] = True
1637 bundlespec.contentopts[b'obsolescence'] = True
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1638 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1639 bundlespec.contentopts[b'phases'] = True
1639 bundlespec.contentopts[b'phases'] = True
1640
1640
1641 bundle2.writenewbundle(
1641 bundle2.writenewbundle(
1642 ui,
1642 ui,
1643 repo,
1643 repo,
1644 b'bundle',
1644 b'bundle',
1645 fname,
1645 fname,
1646 bversion,
1646 bversion,
1647 outgoing,
1647 outgoing,
1648 bundlespec.contentopts,
1648 bundlespec.contentopts,
1649 compression=bcompression,
1649 compression=bcompression,
1650 compopts=compopts,
1650 compopts=compopts,
1651 )
1651 )
1652
1652
1653
1653
1654 @command(
1654 @command(
1655 b'cat',
1655 b'cat',
1656 [
1656 [
1657 (
1657 (
1658 b'o',
1658 b'o',
1659 b'output',
1659 b'output',
1660 b'',
1660 b'',
1661 _(b'print output to file with formatted name'),
1661 _(b'print output to file with formatted name'),
1662 _(b'FORMAT'),
1662 _(b'FORMAT'),
1663 ),
1663 ),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1664 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1665 (b'', b'decode', None, _(b'apply any matching decode filter')),
1666 ]
1666 ]
1667 + walkopts
1667 + walkopts
1668 + formatteropts,
1668 + formatteropts,
1669 _(b'[OPTION]... FILE...'),
1669 _(b'[OPTION]... FILE...'),
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1670 helpcategory=command.CATEGORY_FILE_CONTENTS,
1671 inferrepo=True,
1671 inferrepo=True,
1672 intents={INTENT_READONLY},
1672 intents={INTENT_READONLY},
1673 )
1673 )
1674 def cat(ui, repo, file1, *pats, **opts):
1674 def cat(ui, repo, file1, *pats, **opts):
1675 """output the current or given revision of files
1675 """output the current or given revision of files
1676
1676
1677 Print the specified files as they were at the given revision. If
1677 Print the specified files as they were at the given revision. If
1678 no revision is given, the parent of the working directory is used.
1678 no revision is given, the parent of the working directory is used.
1679
1679
1680 Output may be to a file, in which case the name of the file is
1680 Output may be to a file, in which case the name of the file is
1681 given using a template string. See :hg:`help templates`. In addition
1681 given using a template string. See :hg:`help templates`. In addition
1682 to the common template keywords, the following formatting rules are
1682 to the common template keywords, the following formatting rules are
1683 supported:
1683 supported:
1684
1684
1685 :``%%``: literal "%" character
1685 :``%%``: literal "%" character
1686 :``%s``: basename of file being printed
1686 :``%s``: basename of file being printed
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1687 :``%d``: dirname of file being printed, or '.' if in repository root
1688 :``%p``: root-relative path name of file being printed
1688 :``%p``: root-relative path name of file being printed
1689 :``%H``: changeset hash (40 hexadecimal digits)
1689 :``%H``: changeset hash (40 hexadecimal digits)
1690 :``%R``: changeset revision number
1690 :``%R``: changeset revision number
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1691 :``%h``: short-form changeset hash (12 hexadecimal digits)
1692 :``%r``: zero-padded changeset revision number
1692 :``%r``: zero-padded changeset revision number
1693 :``%b``: basename of the exporting repository
1693 :``%b``: basename of the exporting repository
1694 :``\\``: literal "\\" character
1694 :``\\``: literal "\\" character
1695
1695
1696 .. container:: verbose
1696 .. container:: verbose
1697
1697
1698 Template:
1698 Template:
1699
1699
1700 The following keywords are supported in addition to the common template
1700 The following keywords are supported in addition to the common template
1701 keywords and functions. See also :hg:`help templates`.
1701 keywords and functions. See also :hg:`help templates`.
1702
1702
1703 :data: String. File content.
1703 :data: String. File content.
1704 :path: String. Repository-absolute path of the file.
1704 :path: String. Repository-absolute path of the file.
1705
1705
1706 Returns 0 on success.
1706 Returns 0 on success.
1707 """
1707 """
1708 opts = pycompat.byteskwargs(opts)
1708 opts = pycompat.byteskwargs(opts)
1709 rev = opts.get(b'rev')
1709 rev = opts.get(b'rev')
1710 if rev:
1710 if rev:
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1711 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1712 ctx = scmutil.revsingle(repo, rev)
1712 ctx = scmutil.revsingle(repo, rev)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1713 m = scmutil.match(ctx, (file1,) + pats, opts)
1714 fntemplate = opts.pop(b'output', b'')
1714 fntemplate = opts.pop(b'output', b'')
1715 if cmdutil.isstdiofilename(fntemplate):
1715 if cmdutil.isstdiofilename(fntemplate):
1716 fntemplate = b''
1716 fntemplate = b''
1717
1717
1718 if fntemplate:
1718 if fntemplate:
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1719 fm = formatter.nullformatter(ui, b'cat', opts)
1720 else:
1720 else:
1721 ui.pager(b'cat')
1721 ui.pager(b'cat')
1722 fm = ui.formatter(b'cat', opts)
1722 fm = ui.formatter(b'cat', opts)
1723 with fm:
1723 with fm:
1724 return cmdutil.cat(
1724 return cmdutil.cat(
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1725 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1726 )
1726 )
1727
1727
1728
1728
1729 @command(
1729 @command(
1730 b'clone',
1730 b'clone',
1731 [
1731 [
1732 (
1732 (
1733 b'U',
1733 b'U',
1734 b'noupdate',
1734 b'noupdate',
1735 None,
1735 None,
1736 _(
1736 _(
1737 b'the clone will include an empty working '
1737 b'the clone will include an empty working '
1738 b'directory (only a repository)'
1738 b'directory (only a repository)'
1739 ),
1739 ),
1740 ),
1740 ),
1741 (
1741 (
1742 b'u',
1742 b'u',
1743 b'updaterev',
1743 b'updaterev',
1744 b'',
1744 b'',
1745 _(b'revision, tag, or branch to check out'),
1745 _(b'revision, tag, or branch to check out'),
1746 _(b'REV'),
1746 _(b'REV'),
1747 ),
1747 ),
1748 (
1748 (
1749 b'r',
1749 b'r',
1750 b'rev',
1750 b'rev',
1751 [],
1751 [],
1752 _(
1752 _(
1753 b'do not clone everything, but include this changeset'
1753 b'do not clone everything, but include this changeset'
1754 b' and its ancestors'
1754 b' and its ancestors'
1755 ),
1755 ),
1756 _(b'REV'),
1756 _(b'REV'),
1757 ),
1757 ),
1758 (
1758 (
1759 b'b',
1759 b'b',
1760 b'branch',
1760 b'branch',
1761 [],
1761 [],
1762 _(
1762 _(
1763 b'do not clone everything, but include this branch\'s'
1763 b'do not clone everything, but include this branch\'s'
1764 b' changesets and their ancestors'
1764 b' changesets and their ancestors'
1765 ),
1765 ),
1766 _(b'BRANCH'),
1766 _(b'BRANCH'),
1767 ),
1767 ),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1768 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1769 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1770 (b'', b'stream', None, _(b'clone with minimal data processing')),
1771 ]
1771 ]
1772 + remoteopts,
1772 + remoteopts,
1773 _(b'[OPTION]... SOURCE [DEST]'),
1773 _(b'[OPTION]... SOURCE [DEST]'),
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1774 helpcategory=command.CATEGORY_REPO_CREATION,
1775 helpbasic=True,
1775 helpbasic=True,
1776 norepo=True,
1776 norepo=True,
1777 )
1777 )
1778 def clone(ui, source, dest=None, **opts):
1778 def clone(ui, source, dest=None, **opts):
1779 """make a copy of an existing repository
1779 """make a copy of an existing repository
1780
1780
1781 Create a copy of an existing repository in a new directory.
1781 Create a copy of an existing repository in a new directory.
1782
1782
1783 If no destination directory name is specified, it defaults to the
1783 If no destination directory name is specified, it defaults to the
1784 basename of the source.
1784 basename of the source.
1785
1785
1786 The location of the source is added to the new repository's
1786 The location of the source is added to the new repository's
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1787 ``.hg/hgrc`` file, as the default to be used for future pulls.
1788
1788
1789 Only local paths and ``ssh://`` URLs are supported as
1789 Only local paths and ``ssh://`` URLs are supported as
1790 destinations. For ``ssh://`` destinations, no working directory or
1790 destinations. For ``ssh://`` destinations, no working directory or
1791 ``.hg/hgrc`` will be created on the remote side.
1791 ``.hg/hgrc`` will be created on the remote side.
1792
1792
1793 If the source repository has a bookmark called '@' set, that
1793 If the source repository has a bookmark called '@' set, that
1794 revision will be checked out in the new repository by default.
1794 revision will be checked out in the new repository by default.
1795
1795
1796 To check out a particular version, use -u/--update, or
1796 To check out a particular version, use -u/--update, or
1797 -U/--noupdate to create a clone with no working directory.
1797 -U/--noupdate to create a clone with no working directory.
1798
1798
1799 To pull only a subset of changesets, specify one or more revisions
1799 To pull only a subset of changesets, specify one or more revisions
1800 identifiers with -r/--rev or branches with -b/--branch. The
1800 identifiers with -r/--rev or branches with -b/--branch. The
1801 resulting clone will contain only the specified changesets and
1801 resulting clone will contain only the specified changesets and
1802 their ancestors. These options (or 'clone src#rev dest') imply
1802 their ancestors. These options (or 'clone src#rev dest') imply
1803 --pull, even for local source repositories.
1803 --pull, even for local source repositories.
1804
1804
1805 In normal clone mode, the remote normalizes repository data into a common
1805 In normal clone mode, the remote normalizes repository data into a common
1806 exchange format and the receiving end translates this data into its local
1806 exchange format and the receiving end translates this data into its local
1807 storage format. --stream activates a different clone mode that essentially
1807 storage format. --stream activates a different clone mode that essentially
1808 copies repository files from the remote with minimal data processing. This
1808 copies repository files from the remote with minimal data processing. This
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1809 significantly reduces the CPU cost of a clone both remotely and locally.
1810 However, it often increases the transferred data size by 30-40%. This can
1810 However, it often increases the transferred data size by 30-40%. This can
1811 result in substantially faster clones where I/O throughput is plentiful,
1811 result in substantially faster clones where I/O throughput is plentiful,
1812 especially for larger repositories. A side-effect of --stream clones is
1812 especially for larger repositories. A side-effect of --stream clones is
1813 that storage settings and requirements on the remote are applied locally:
1813 that storage settings and requirements on the remote are applied locally:
1814 a modern client may inherit legacy or inefficient storage used by the
1814 a modern client may inherit legacy or inefficient storage used by the
1815 remote or a legacy Mercurial client may not be able to clone from a
1815 remote or a legacy Mercurial client may not be able to clone from a
1816 modern Mercurial remote.
1816 modern Mercurial remote.
1817
1817
1818 .. note::
1818 .. note::
1819
1819
1820 Specifying a tag will include the tagged changeset but not the
1820 Specifying a tag will include the tagged changeset but not the
1821 changeset containing the tag.
1821 changeset containing the tag.
1822
1822
1823 .. container:: verbose
1823 .. container:: verbose
1824
1824
1825 For efficiency, hardlinks are used for cloning whenever the
1825 For efficiency, hardlinks are used for cloning whenever the
1826 source and destination are on the same filesystem (note this
1826 source and destination are on the same filesystem (note this
1827 applies only to the repository data, not to the working
1827 applies only to the repository data, not to the working
1828 directory). Some filesystems, such as AFS, implement hardlinking
1828 directory). Some filesystems, such as AFS, implement hardlinking
1829 incorrectly, but do not report errors. In these cases, use the
1829 incorrectly, but do not report errors. In these cases, use the
1830 --pull option to avoid hardlinking.
1830 --pull option to avoid hardlinking.
1831
1831
1832 Mercurial will update the working directory to the first applicable
1832 Mercurial will update the working directory to the first applicable
1833 revision from this list:
1833 revision from this list:
1834
1834
1835 a) null if -U or the source repository has no changesets
1835 a) null if -U or the source repository has no changesets
1836 b) if -u . and the source repository is local, the first parent of
1836 b) if -u . and the source repository is local, the first parent of
1837 the source repository's working directory
1837 the source repository's working directory
1838 c) the changeset specified with -u (if a branch name, this means the
1838 c) the changeset specified with -u (if a branch name, this means the
1839 latest head of that branch)
1839 latest head of that branch)
1840 d) the changeset specified with -r
1840 d) the changeset specified with -r
1841 e) the tipmost head specified with -b
1841 e) the tipmost head specified with -b
1842 f) the tipmost head specified with the url#branch source syntax
1842 f) the tipmost head specified with the url#branch source syntax
1843 g) the revision marked with the '@' bookmark, if present
1843 g) the revision marked with the '@' bookmark, if present
1844 h) the tipmost head of the default branch
1844 h) the tipmost head of the default branch
1845 i) tip
1845 i) tip
1846
1846
1847 When cloning from servers that support it, Mercurial may fetch
1847 When cloning from servers that support it, Mercurial may fetch
1848 pre-generated data from a server-advertised URL or inline from the
1848 pre-generated data from a server-advertised URL or inline from the
1849 same stream. When this is done, hooks operating on incoming changesets
1849 same stream. When this is done, hooks operating on incoming changesets
1850 and changegroups may fire more than once, once for each pre-generated
1850 and changegroups may fire more than once, once for each pre-generated
1851 bundle and as well as for any additional remaining data. In addition,
1851 bundle and as well as for any additional remaining data. In addition,
1852 if an error occurs, the repository may be rolled back to a partial
1852 if an error occurs, the repository may be rolled back to a partial
1853 clone. This behavior may change in future releases.
1853 clone. This behavior may change in future releases.
1854 See :hg:`help -e clonebundles` for more.
1854 See :hg:`help -e clonebundles` for more.
1855
1855
1856 Examples:
1856 Examples:
1857
1857
1858 - clone a remote repository to a new directory named hg/::
1858 - clone a remote repository to a new directory named hg/::
1859
1859
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1860 hg clone https://www.mercurial-scm.org/repo/hg/
1861
1861
1862 - create a lightweight local clone::
1862 - create a lightweight local clone::
1863
1863
1864 hg clone project/ project-feature/
1864 hg clone project/ project-feature/
1865
1865
1866 - clone from an absolute path on an ssh server (note double-slash)::
1866 - clone from an absolute path on an ssh server (note double-slash)::
1867
1867
1868 hg clone ssh://user@server//home/projects/alpha/
1868 hg clone ssh://user@server//home/projects/alpha/
1869
1869
1870 - do a streaming clone while checking out a specified version::
1870 - do a streaming clone while checking out a specified version::
1871
1871
1872 hg clone --stream http://server/repo -u 1.5
1872 hg clone --stream http://server/repo -u 1.5
1873
1873
1874 - create a repository without changesets after a particular revision::
1874 - create a repository without changesets after a particular revision::
1875
1875
1876 hg clone -r 04e544 experimental/ good/
1876 hg clone -r 04e544 experimental/ good/
1877
1877
1878 - clone (and track) a particular named branch::
1878 - clone (and track) a particular named branch::
1879
1879
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1880 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1881
1881
1882 See :hg:`help urls` for details on specifying URLs.
1882 See :hg:`help urls` for details on specifying URLs.
1883
1883
1884 Returns 0 on success.
1884 Returns 0 on success.
1885 """
1885 """
1886 opts = pycompat.byteskwargs(opts)
1886 opts = pycompat.byteskwargs(opts)
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1887 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1888
1888
1889 # --include/--exclude can come from narrow or sparse.
1889 # --include/--exclude can come from narrow or sparse.
1890 includepats, excludepats = None, None
1890 includepats, excludepats = None, None
1891
1891
1892 # hg.clone() differentiates between None and an empty set. So make sure
1892 # hg.clone() differentiates between None and an empty set. So make sure
1893 # patterns are sets if narrow is requested without patterns.
1893 # patterns are sets if narrow is requested without patterns.
1894 if opts.get(b'narrow'):
1894 if opts.get(b'narrow'):
1895 includepats = set()
1895 includepats = set()
1896 excludepats = set()
1896 excludepats = set()
1897
1897
1898 if opts.get(b'include'):
1898 if opts.get(b'include'):
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1899 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1900 if opts.get(b'exclude'):
1900 if opts.get(b'exclude'):
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1901 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1902
1902
1903 r = hg.clone(
1903 r = hg.clone(
1904 ui,
1904 ui,
1905 opts,
1905 opts,
1906 source,
1906 source,
1907 dest,
1907 dest,
1908 pull=opts.get(b'pull'),
1908 pull=opts.get(b'pull'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1909 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1910 revs=opts.get(b'rev'),
1910 revs=opts.get(b'rev'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1911 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1912 branch=opts.get(b'branch'),
1912 branch=opts.get(b'branch'),
1913 shareopts=opts.get(b'shareopts'),
1913 shareopts=opts.get(b'shareopts'),
1914 storeincludepats=includepats,
1914 storeincludepats=includepats,
1915 storeexcludepats=excludepats,
1915 storeexcludepats=excludepats,
1916 depth=opts.get(b'depth') or None,
1916 depth=opts.get(b'depth') or None,
1917 )
1917 )
1918
1918
1919 return r is None
1919 return r is None
1920
1920
1921
1921
1922 @command(
1922 @command(
1923 b'commit|ci',
1923 b'commit|ci',
1924 [
1924 [
1925 (
1925 (
1926 b'A',
1926 b'A',
1927 b'addremove',
1927 b'addremove',
1928 None,
1928 None,
1929 _(b'mark new/missing files as added/removed before committing'),
1929 _(b'mark new/missing files as added/removed before committing'),
1930 ),
1930 ),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1931 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1932 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1933 (b's', b'secret', None, _(b'use the secret phase for committing')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1934 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1935 (
1935 (
1936 b'',
1936 b'',
1937 b'force-close-branch',
1937 b'force-close-branch',
1938 None,
1938 None,
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1939 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1940 ),
1940 ),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1941 (b'i', b'interactive', None, _(b'use interactive mode')),
1942 ]
1942 ]
1943 + walkopts
1943 + walkopts
1944 + commitopts
1944 + commitopts
1945 + commitopts2
1945 + commitopts2
1946 + subrepoopts,
1946 + subrepoopts,
1947 _(b'[OPTION]... [FILE]...'),
1947 _(b'[OPTION]... [FILE]...'),
1948 helpcategory=command.CATEGORY_COMMITTING,
1948 helpcategory=command.CATEGORY_COMMITTING,
1949 helpbasic=True,
1949 helpbasic=True,
1950 inferrepo=True,
1950 inferrepo=True,
1951 )
1951 )
1952 def commit(ui, repo, *pats, **opts):
1952 def commit(ui, repo, *pats, **opts):
1953 """commit the specified files or all outstanding changes
1953 """commit the specified files or all outstanding changes
1954
1954
1955 Commit changes to the given files into the repository. Unlike a
1955 Commit changes to the given files into the repository. Unlike a
1956 centralized SCM, this operation is a local operation. See
1956 centralized SCM, this operation is a local operation. See
1957 :hg:`push` for a way to actively distribute your changes.
1957 :hg:`push` for a way to actively distribute your changes.
1958
1958
1959 If a list of files is omitted, all changes reported by :hg:`status`
1959 If a list of files is omitted, all changes reported by :hg:`status`
1960 will be committed.
1960 will be committed.
1961
1961
1962 If you are committing the result of a merge, do not provide any
1962 If you are committing the result of a merge, do not provide any
1963 filenames or -I/-X filters.
1963 filenames or -I/-X filters.
1964
1964
1965 If no commit message is specified, Mercurial starts your
1965 If no commit message is specified, Mercurial starts your
1966 configured editor where you can enter a message. In case your
1966 configured editor where you can enter a message. In case your
1967 commit fails, you will find a backup of your message in
1967 commit fails, you will find a backup of your message in
1968 ``.hg/last-message.txt``.
1968 ``.hg/last-message.txt``.
1969
1969
1970 The --close-branch flag can be used to mark the current branch
1970 The --close-branch flag can be used to mark the current branch
1971 head closed. When all heads of a branch are closed, the branch
1971 head closed. When all heads of a branch are closed, the branch
1972 will be considered closed and no longer listed.
1972 will be considered closed and no longer listed.
1973
1973
1974 The --amend flag can be used to amend the parent of the
1974 The --amend flag can be used to amend the parent of the
1975 working directory with a new commit that contains the changes
1975 working directory with a new commit that contains the changes
1976 in the parent in addition to those currently reported by :hg:`status`,
1976 in the parent in addition to those currently reported by :hg:`status`,
1977 if there are any. The old commit is stored in a backup bundle in
1977 if there are any. The old commit is stored in a backup bundle in
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1978 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1979 on how to restore it).
1979 on how to restore it).
1980
1980
1981 Message, user and date are taken from the amended commit unless
1981 Message, user and date are taken from the amended commit unless
1982 specified. When a message isn't specified on the command line,
1982 specified. When a message isn't specified on the command line,
1983 the editor will open with the message of the amended commit.
1983 the editor will open with the message of the amended commit.
1984
1984
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1985 It is not possible to amend public changesets (see :hg:`help phases`)
1986 or changesets that have children.
1986 or changesets that have children.
1987
1987
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1988 See :hg:`help dates` for a list of formats valid for -d/--date.
1989
1989
1990 Returns 0 on success, 1 if nothing changed.
1990 Returns 0 on success, 1 if nothing changed.
1991
1991
1992 .. container:: verbose
1992 .. container:: verbose
1993
1993
1994 Examples:
1994 Examples:
1995
1995
1996 - commit all files ending in .py::
1996 - commit all files ending in .py::
1997
1997
1998 hg commit --include "set:**.py"
1998 hg commit --include "set:**.py"
1999
1999
2000 - commit all non-binary files::
2000 - commit all non-binary files::
2001
2001
2002 hg commit --exclude "set:binary()"
2002 hg commit --exclude "set:binary()"
2003
2003
2004 - amend the current commit and set the date to now::
2004 - amend the current commit and set the date to now::
2005
2005
2006 hg commit --amend --date now
2006 hg commit --amend --date now
2007 """
2007 """
2008 with repo.wlock(), repo.lock():
2008 with repo.wlock(), repo.lock():
2009 return _docommit(ui, repo, *pats, **opts)
2009 return _docommit(ui, repo, *pats, **opts)
2010
2010
2011
2011
2012 def _docommit(ui, repo, *pats, **opts):
2012 def _docommit(ui, repo, *pats, **opts):
2013 if opts.get('interactive'):
2013 if opts.get('interactive'):
2014 opts.pop('interactive')
2014 opts.pop('interactive')
2015 ret = cmdutil.dorecord(
2015 ret = cmdutil.dorecord(
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2016 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2017 )
2017 )
2018 # ret can be 0 (no changes to record) or the value returned by
2018 # ret can be 0 (no changes to record) or the value returned by
2019 # commit(), 1 if nothing changed or None on success.
2019 # commit(), 1 if nothing changed or None on success.
2020 return 1 if ret == 0 else ret
2020 return 1 if ret == 0 else ret
2021
2021
2022 opts = pycompat.byteskwargs(opts)
2022 opts = pycompat.byteskwargs(opts)
2023 if opts.get(b'subrepos'):
2023 if opts.get(b'subrepos'):
2024 if opts.get(b'amend'):
2024 if opts.get(b'amend'):
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2025 raise error.Abort(_(b'cannot amend with --subrepos'))
2026 # Let --subrepos on the command line override config setting.
2026 # Let --subrepos on the command line override config setting.
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2027 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2028
2028
2029 cmdutil.checkunfinished(repo, commit=True)
2029 cmdutil.checkunfinished(repo, commit=True)
2030
2030
2031 branch = repo[None].branch()
2031 branch = repo[None].branch()
2032 bheads = repo.branchheads(branch)
2032 bheads = repo.branchheads(branch)
2033
2033
2034 extra = {}
2034 extra = {}
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2035 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2036 extra[b'close'] = b'1'
2036 extra[b'close'] = b'1'
2037
2037
2038 if repo[b'.'].closesbranch():
2038 if repo[b'.'].closesbranch():
2039 raise error.Abort(
2039 raise error.Abort(
2040 _(b'current revision is already a branch closing head')
2040 _(b'current revision is already a branch closing head')
2041 )
2041 )
2042 elif not bheads:
2042 elif not bheads:
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2043 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2044 elif (
2044 elif (
2045 branch == repo[b'.'].branch()
2045 branch == repo[b'.'].branch()
2046 and repo[b'.'].node() not in bheads
2046 and repo[b'.'].node() not in bheads
2047 and not opts.get(b'force_close_branch')
2047 and not opts.get(b'force_close_branch')
2048 ):
2048 ):
2049 hint = _(
2049 hint = _(
2050 b'use --force-close-branch to close branch from a non-head'
2050 b'use --force-close-branch to close branch from a non-head'
2051 b' changeset'
2051 b' changeset'
2052 )
2052 )
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2053 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2054 elif opts.get(b'amend'):
2054 elif opts.get(b'amend'):
2055 if (
2055 if (
2056 repo[b'.'].p1().branch() != branch
2056 repo[b'.'].p1().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2057 and repo[b'.'].p2().branch() != branch
2058 ):
2058 ):
2059 raise error.Abort(_(b'can only close branch heads'))
2059 raise error.Abort(_(b'can only close branch heads'))
2060
2060
2061 if opts.get(b'amend'):
2061 if opts.get(b'amend'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2062 if ui.configbool(b'ui', b'commitsubrepos'):
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2063 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2064
2064
2065 old = repo[b'.']
2065 old = repo[b'.']
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2066 rewriteutil.precheck(repo, [old.rev()], b'amend')
2067
2067
2068 # Currently histedit gets confused if an amend happens while histedit
2068 # Currently histedit gets confused if an amend happens while histedit
2069 # is in progress. Since we have a checkunfinished command, we are
2069 # is in progress. Since we have a checkunfinished command, we are
2070 # temporarily honoring it.
2070 # temporarily honoring it.
2071 #
2071 #
2072 # Note: eventually this guard will be removed. Please do not expect
2072 # Note: eventually this guard will be removed. Please do not expect
2073 # this behavior to remain.
2073 # this behavior to remain.
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2074 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2075 cmdutil.checkunfinished(repo)
2075 cmdutil.checkunfinished(repo)
2076
2076
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2077 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2078 if node == old.node():
2078 if node == old.node():
2079 ui.status(_(b"nothing changed\n"))
2079 ui.status(_(b"nothing changed\n"))
2080 return 1
2080 return 1
2081 else:
2081 else:
2082
2082
2083 def commitfunc(ui, repo, message, match, opts):
2083 def commitfunc(ui, repo, message, match, opts):
2084 overrides = {}
2084 overrides = {}
2085 if opts.get(b'secret'):
2085 if opts.get(b'secret'):
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2086 overrides[(b'phases', b'new-commit')] = b'secret'
2087
2087
2088 baseui = repo.baseui
2088 baseui = repo.baseui
2089 with baseui.configoverride(overrides, b'commit'):
2089 with baseui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2090 with ui.configoverride(overrides, b'commit'):
2091 editform = cmdutil.mergeeditform(
2091 editform = cmdutil.mergeeditform(
2092 repo[None], b'commit.normal'
2092 repo[None], b'commit.normal'
2093 )
2093 )
2094 editor = cmdutil.getcommiteditor(
2094 editor = cmdutil.getcommiteditor(
2095 editform=editform, **pycompat.strkwargs(opts)
2095 editform=editform, **pycompat.strkwargs(opts)
2096 )
2096 )
2097 return repo.commit(
2097 return repo.commit(
2098 message,
2098 message,
2099 opts.get(b'user'),
2099 opts.get(b'user'),
2100 opts.get(b'date'),
2100 opts.get(b'date'),
2101 match,
2101 match,
2102 editor=editor,
2102 editor=editor,
2103 extra=extra,
2103 extra=extra,
2104 )
2104 )
2105
2105
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2106 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2107
2107
2108 if not node:
2108 if not node:
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2109 stat = cmdutil.postcommitstatus(repo, pats, opts)
2110 if stat.deleted:
2110 if stat.deleted:
2111 ui.status(
2111 ui.status(
2112 _(
2112 _(
2113 b"nothing changed (%d missing files, see "
2113 b"nothing changed (%d missing files, see "
2114 b"'hg status')\n"
2114 b"'hg status')\n"
2115 )
2115 )
2116 % len(stat.deleted)
2116 % len(stat.deleted)
2117 )
2117 )
2118 else:
2118 else:
2119 ui.status(_(b"nothing changed\n"))
2119 ui.status(_(b"nothing changed\n"))
2120 return 1
2120 return 1
2121
2121
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2122 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2123
2123
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2124 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2125 status(
2125 status(
2126 ui,
2126 ui,
2127 repo,
2127 repo,
2128 modified=True,
2128 modified=True,
2129 added=True,
2129 added=True,
2130 removed=True,
2130 removed=True,
2131 deleted=True,
2131 deleted=True,
2132 unknown=True,
2132 unknown=True,
2133 subrepos=opts.get(b'subrepos'),
2133 subrepos=opts.get(b'subrepos'),
2134 )
2134 )
2135
2135
2136
2136
2137 @command(
2137 @command(
2138 b'config|showconfig|debugconfig',
2138 b'config|showconfig|debugconfig',
2139 [
2139 [
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2140 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2141 (b'e', b'edit', None, _(b'edit user config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2142 (b'l', b'local', None, _(b'edit repository config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2143 (b'g', b'global', None, _(b'edit global config')),
2144 ]
2144 ]
2145 + formatteropts,
2145 + formatteropts,
2146 _(b'[-u] [NAME]...'),
2146 _(b'[-u] [NAME]...'),
2147 helpcategory=command.CATEGORY_HELP,
2147 helpcategory=command.CATEGORY_HELP,
2148 optionalrepo=True,
2148 optionalrepo=True,
2149 intents={INTENT_READONLY},
2149 intents={INTENT_READONLY},
2150 )
2150 )
2151 def config(ui, repo, *values, **opts):
2151 def config(ui, repo, *values, **opts):
2152 """show combined config settings from all hgrc files
2152 """show combined config settings from all hgrc files
2153
2153
2154 With no arguments, print names and values of all config items.
2154 With no arguments, print names and values of all config items.
2155
2155
2156 With one argument of the form section.name, print just the value
2156 With one argument of the form section.name, print just the value
2157 of that config item.
2157 of that config item.
2158
2158
2159 With multiple arguments, print names and values of all config
2159 With multiple arguments, print names and values of all config
2160 items with matching section names or section.names.
2160 items with matching section names or section.names.
2161
2161
2162 With --edit, start an editor on the user-level config file. With
2162 With --edit, start an editor on the user-level config file. With
2163 --global, edit the system-wide config file. With --local, edit the
2163 --global, edit the system-wide config file. With --local, edit the
2164 repository-level config file.
2164 repository-level config file.
2165
2165
2166 With --debug, the source (filename and line number) is printed
2166 With --debug, the source (filename and line number) is printed
2167 for each config item.
2167 for each config item.
2168
2168
2169 See :hg:`help config` for more information about config files.
2169 See :hg:`help config` for more information about config files.
2170
2170
2171 .. container:: verbose
2171 .. container:: verbose
2172
2172
2173 Template:
2173 Template:
2174
2174
2175 The following keywords are supported. See also :hg:`help templates`.
2175 The following keywords are supported. See also :hg:`help templates`.
2176
2176
2177 :name: String. Config name.
2177 :name: String. Config name.
2178 :source: String. Filename and line number where the item is defined.
2178 :source: String. Filename and line number where the item is defined.
2179 :value: String. Config value.
2179 :value: String. Config value.
2180
2180
2181 Returns 0 on success, 1 if NAME does not exist.
2181 Returns 0 on success, 1 if NAME does not exist.
2182
2182
2183 """
2183 """
2184
2184
2185 opts = pycompat.byteskwargs(opts)
2185 opts = pycompat.byteskwargs(opts)
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2186 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2187 if opts.get(b'local') and opts.get(b'global'):
2188 raise error.Abort(_(b"can't use --local and --global together"))
2188 raise error.Abort(_(b"can't use --local and --global together"))
2189
2189
2190 if opts.get(b'local'):
2190 if opts.get(b'local'):
2191 if not repo:
2191 if not repo:
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2192 raise error.Abort(_(b"can't use --local outside a repository"))
2193 paths = [repo.vfs.join(b'hgrc')]
2193 paths = [repo.vfs.join(b'hgrc')]
2194 elif opts.get(b'global'):
2194 elif opts.get(b'global'):
2195 paths = rcutil.systemrcpath()
2195 paths = rcutil.systemrcpath()
2196 else:
2196 else:
2197 paths = rcutil.userrcpath()
2197 paths = rcutil.userrcpath()
2198
2198
2199 for f in paths:
2199 for f in paths:
2200 if os.path.exists(f):
2200 if os.path.exists(f):
2201 break
2201 break
2202 else:
2202 else:
2203 if opts.get(b'global'):
2203 if opts.get(b'global'):
2204 samplehgrc = uimod.samplehgrcs[b'global']
2204 samplehgrc = uimod.samplehgrcs[b'global']
2205 elif opts.get(b'local'):
2205 elif opts.get(b'local'):
2206 samplehgrc = uimod.samplehgrcs[b'local']
2206 samplehgrc = uimod.samplehgrcs[b'local']
2207 else:
2207 else:
2208 samplehgrc = uimod.samplehgrcs[b'user']
2208 samplehgrc = uimod.samplehgrcs[b'user']
2209
2209
2210 f = paths[0]
2210 f = paths[0]
2211 fp = open(f, b"wb")
2211 fp = open(f, b"wb")
2212 fp.write(util.tonativeeol(samplehgrc))
2212 fp.write(util.tonativeeol(samplehgrc))
2213 fp.close()
2213 fp.close()
2214
2214
2215 editor = ui.geteditor()
2215 editor = ui.geteditor()
2216 ui.system(
2216 ui.system(
2217 b"%s \"%s\"" % (editor, f),
2217 b"%s \"%s\"" % (editor, f),
2218 onerr=error.Abort,
2218 onerr=error.Abort,
2219 errprefix=_(b"edit failed"),
2219 errprefix=_(b"edit failed"),
2220 blockedtag=b'config_edit',
2220 blockedtag=b'config_edit',
2221 )
2221 )
2222 return
2222 return
2223 ui.pager(b'config')
2223 ui.pager(b'config')
2224 fm = ui.formatter(b'config', opts)
2224 fm = ui.formatter(b'config', opts)
2225 for t, f in rcutil.rccomponents():
2225 for t, f in rcutil.rccomponents():
2226 if t == b'path':
2226 if t == b'path':
2227 ui.debug(b'read config from: %s\n' % f)
2227 ui.debug(b'read config from: %s\n' % f)
2228 elif t == b'resource':
2228 elif t == b'resource':
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2229 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2230 elif t == b'items':
2230 elif t == b'items':
2231 # Don't print anything for 'items'.
2231 # Don't print anything for 'items'.
2232 pass
2232 pass
2233 else:
2233 else:
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2234 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2235 untrusted = bool(opts.get(b'untrusted'))
2235 untrusted = bool(opts.get(b'untrusted'))
2236
2236
2237 selsections = selentries = []
2237 selsections = selentries = []
2238 if values:
2238 if values:
2239 selsections = [v for v in values if b'.' not in v]
2239 selsections = [v for v in values if b'.' not in v]
2240 selentries = [v for v in values if b'.' in v]
2240 selentries = [v for v in values if b'.' in v]
2241 uniquesel = len(selentries) == 1 and not selsections
2241 uniquesel = len(selentries) == 1 and not selsections
2242 selsections = set(selsections)
2242 selsections = set(selsections)
2243 selentries = set(selentries)
2243 selentries = set(selentries)
2244
2244
2245 matched = False
2245 matched = False
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2246 for section, name, value in ui.walkconfig(untrusted=untrusted):
2247 source = ui.configsource(section, name, untrusted)
2247 source = ui.configsource(section, name, untrusted)
2248 value = pycompat.bytestr(value)
2248 value = pycompat.bytestr(value)
2249 defaultvalue = ui.configdefault(section, name)
2249 defaultvalue = ui.configdefault(section, name)
2250 if fm.isplain():
2250 if fm.isplain():
2251 source = source or b'none'
2251 source = source or b'none'
2252 value = value.replace(b'\n', b'\\n')
2252 value = value.replace(b'\n', b'\\n')
2253 entryname = section + b'.' + name
2253 entryname = section + b'.' + name
2254 if values and not (section in selsections or entryname in selentries):
2254 if values and not (section in selsections or entryname in selentries):
2255 continue
2255 continue
2256 fm.startitem()
2256 fm.startitem()
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2257 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2258 if uniquesel:
2258 if uniquesel:
2259 fm.data(name=entryname)
2259 fm.data(name=entryname)
2260 fm.write(b'value', b'%s\n', value)
2260 fm.write(b'value', b'%s\n', value)
2261 else:
2261 else:
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2262 fm.write(b'name value', b'%s=%s\n', entryname, value)
2263 if formatter.isprintable(defaultvalue):
2263 if formatter.isprintable(defaultvalue):
2264 fm.data(defaultvalue=defaultvalue)
2264 fm.data(defaultvalue=defaultvalue)
2265 elif isinstance(defaultvalue, list) and all(
2265 elif isinstance(defaultvalue, list) and all(
2266 formatter.isprintable(e) for e in defaultvalue
2266 formatter.isprintable(e) for e in defaultvalue
2267 ):
2267 ):
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2268 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2269 # TODO: no idea how to process unsupported defaultvalue types
2269 # TODO: no idea how to process unsupported defaultvalue types
2270 matched = True
2270 matched = True
2271 fm.end()
2271 fm.end()
2272 if matched:
2272 if matched:
2273 return 0
2273 return 0
2274 return 1
2274 return 1
2275
2275
2276
2276
2277 @command(
2277 @command(
2278 b'continue',
2278 b'continue',
2279 dryrunopts,
2279 dryrunopts,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2280 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2281 helpbasic=True,
2281 helpbasic=True,
2282 )
2282 )
2283 def continuecmd(ui, repo, **opts):
2283 def continuecmd(ui, repo, **opts):
2284 """resumes an interrupted operation (EXPERIMENTAL)
2284 """resumes an interrupted operation (EXPERIMENTAL)
2285
2285
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2286 Finishes a multistep operation like graft, histedit, rebase, merge,
2287 and unshelve if they are in an interrupted state.
2287 and unshelve if they are in an interrupted state.
2288
2288
2289 use --dry-run/-n to dry run the command.
2289 use --dry-run/-n to dry run the command.
2290 """
2290 """
2291 dryrun = opts.get('dry_run')
2291 dryrun = opts.get('dry_run')
2292 contstate = cmdutil.getunfinishedstate(repo)
2292 contstate = cmdutil.getunfinishedstate(repo)
2293 if not contstate:
2293 if not contstate:
2294 raise error.Abort(_(b'no operation in progress'))
2294 raise error.Abort(_(b'no operation in progress'))
2295 if not contstate.continuefunc:
2295 if not contstate.continuefunc:
2296 raise error.Abort(
2296 raise error.Abort(
2297 (
2297 (
2298 _(b"%s in progress but does not support 'hg continue'")
2298 _(b"%s in progress but does not support 'hg continue'")
2299 % (contstate._opname)
2299 % (contstate._opname)
2300 ),
2300 ),
2301 hint=contstate.continuemsg(),
2301 hint=contstate.continuemsg(),
2302 )
2302 )
2303 if dryrun:
2303 if dryrun:
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2304 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2305 return
2305 return
2306 return contstate.continuefunc(ui, repo)
2306 return contstate.continuefunc(ui, repo)
2307
2307
2308
2308
2309 @command(
2309 @command(
2310 b'copy|cp',
2310 b'copy|cp',
2311 [
2311 [
2312 (b'', b'forget', None, _(b'unmark a file as copied')),
2312 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2313 (
2314 (
2314 b'f',
2315 b'f',
2315 b'force',
2316 b'force',
2316 None,
2317 None,
2317 _(b'forcibly copy over an existing managed file'),
2318 _(b'forcibly copy over an existing managed file'),
2318 ),
2319 ),
2319 ]
2320 ]
2320 + walkopts
2321 + walkopts
2321 + dryrunopts,
2322 + dryrunopts,
2322 _(b'[OPTION]... SOURCE... DEST'),
2323 _(b'[OPTION]... SOURCE... DEST'),
2323 helpcategory=command.CATEGORY_FILE_CONTENTS,
2324 helpcategory=command.CATEGORY_FILE_CONTENTS,
2324 )
2325 )
2325 def copy(ui, repo, *pats, **opts):
2326 def copy(ui, repo, *pats, **opts):
2326 """mark files as copied for the next commit
2327 """mark files as copied for the next commit
2327
2328
2328 Mark dest as having copies of source files. If dest is a
2329 Mark dest as having copies of source files. If dest is a
2329 directory, copies are put in that directory. If dest is a file,
2330 directory, copies are put in that directory. If dest is a file,
2330 the source must be a single file.
2331 the source must be a single file.
2331
2332
2332 By default, this command copies the contents of files as they
2333 By default, this command copies the contents of files as they
2333 exist in the working directory. If invoked with -A/--after, the
2334 exist in the working directory. If invoked with -A/--after, the
2334 operation is recorded, but no copying is performed.
2335 operation is recorded, but no copying is performed.
2335
2336
2336 This command takes effect with the next commit. To undo a copy
2337 To undo marking a file as copied, use --forget. With that option,
2337 before that, see :hg:`revert`.
2338 all given (positional) arguments are unmarked as copies. The destination
2339 file(s) will be left in place (still tracked).
2340
2341 This command takes effect with the next commit.
2338
2342
2339 Returns 0 on success, 1 if errors are encountered.
2343 Returns 0 on success, 1 if errors are encountered.
2340 """
2344 """
2341 opts = pycompat.byteskwargs(opts)
2345 opts = pycompat.byteskwargs(opts)
2342 with repo.wlock(False):
2346 with repo.wlock(False):
2343 return cmdutil.copy(ui, repo, pats, opts)
2347 return cmdutil.copy(ui, repo, pats, opts)
2344
2348
2345
2349
2346 @command(
2350 @command(
2347 b'debugcommands',
2351 b'debugcommands',
2348 [],
2352 [],
2349 _(b'[COMMAND]'),
2353 _(b'[COMMAND]'),
2350 helpcategory=command.CATEGORY_HELP,
2354 helpcategory=command.CATEGORY_HELP,
2351 norepo=True,
2355 norepo=True,
2352 )
2356 )
2353 def debugcommands(ui, cmd=b'', *args):
2357 def debugcommands(ui, cmd=b'', *args):
2354 """list all available commands and options"""
2358 """list all available commands and options"""
2355 for cmd, vals in sorted(pycompat.iteritems(table)):
2359 for cmd, vals in sorted(pycompat.iteritems(table)):
2356 cmd = cmd.split(b'|')[0]
2360 cmd = cmd.split(b'|')[0]
2357 opts = b', '.join([i[1] for i in vals[1]])
2361 opts = b', '.join([i[1] for i in vals[1]])
2358 ui.write(b'%s: %s\n' % (cmd, opts))
2362 ui.write(b'%s: %s\n' % (cmd, opts))
2359
2363
2360
2364
2361 @command(
2365 @command(
2362 b'debugcomplete',
2366 b'debugcomplete',
2363 [(b'o', b'options', None, _(b'show the command options'))],
2367 [(b'o', b'options', None, _(b'show the command options'))],
2364 _(b'[-o] CMD'),
2368 _(b'[-o] CMD'),
2365 helpcategory=command.CATEGORY_HELP,
2369 helpcategory=command.CATEGORY_HELP,
2366 norepo=True,
2370 norepo=True,
2367 )
2371 )
2368 def debugcomplete(ui, cmd=b'', **opts):
2372 def debugcomplete(ui, cmd=b'', **opts):
2369 """returns the completion list associated with the given command"""
2373 """returns the completion list associated with the given command"""
2370
2374
2371 if opts.get('options'):
2375 if opts.get('options'):
2372 options = []
2376 options = []
2373 otables = [globalopts]
2377 otables = [globalopts]
2374 if cmd:
2378 if cmd:
2375 aliases, entry = cmdutil.findcmd(cmd, table, False)
2379 aliases, entry = cmdutil.findcmd(cmd, table, False)
2376 otables.append(entry[1])
2380 otables.append(entry[1])
2377 for t in otables:
2381 for t in otables:
2378 for o in t:
2382 for o in t:
2379 if b"(DEPRECATED)" in o[3]:
2383 if b"(DEPRECATED)" in o[3]:
2380 continue
2384 continue
2381 if o[0]:
2385 if o[0]:
2382 options.append(b'-%s' % o[0])
2386 options.append(b'-%s' % o[0])
2383 options.append(b'--%s' % o[1])
2387 options.append(b'--%s' % o[1])
2384 ui.write(b"%s\n" % b"\n".join(options))
2388 ui.write(b"%s\n" % b"\n".join(options))
2385 return
2389 return
2386
2390
2387 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2391 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2388 if ui.verbose:
2392 if ui.verbose:
2389 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2393 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2390 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2394 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2391
2395
2392
2396
2393 @command(
2397 @command(
2394 b'diff',
2398 b'diff',
2395 [
2399 [
2396 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2400 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2397 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2401 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2398 ]
2402 ]
2399 + diffopts
2403 + diffopts
2400 + diffopts2
2404 + diffopts2
2401 + walkopts
2405 + walkopts
2402 + subrepoopts,
2406 + subrepoopts,
2403 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2407 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2404 helpcategory=command.CATEGORY_FILE_CONTENTS,
2408 helpcategory=command.CATEGORY_FILE_CONTENTS,
2405 helpbasic=True,
2409 helpbasic=True,
2406 inferrepo=True,
2410 inferrepo=True,
2407 intents={INTENT_READONLY},
2411 intents={INTENT_READONLY},
2408 )
2412 )
2409 def diff(ui, repo, *pats, **opts):
2413 def diff(ui, repo, *pats, **opts):
2410 """diff repository (or selected files)
2414 """diff repository (or selected files)
2411
2415
2412 Show differences between revisions for the specified files.
2416 Show differences between revisions for the specified files.
2413
2417
2414 Differences between files are shown using the unified diff format.
2418 Differences between files are shown using the unified diff format.
2415
2419
2416 .. note::
2420 .. note::
2417
2421
2418 :hg:`diff` may generate unexpected results for merges, as it will
2422 :hg:`diff` may generate unexpected results for merges, as it will
2419 default to comparing against the working directory's first
2423 default to comparing against the working directory's first
2420 parent changeset if no revisions are specified.
2424 parent changeset if no revisions are specified.
2421
2425
2422 When two revision arguments are given, then changes are shown
2426 When two revision arguments are given, then changes are shown
2423 between those revisions. If only one revision is specified then
2427 between those revisions. If only one revision is specified then
2424 that revision is compared to the working directory, and, when no
2428 that revision is compared to the working directory, and, when no
2425 revisions are specified, the working directory files are compared
2429 revisions are specified, the working directory files are compared
2426 to its first parent.
2430 to its first parent.
2427
2431
2428 Alternatively you can specify -c/--change with a revision to see
2432 Alternatively you can specify -c/--change with a revision to see
2429 the changes in that changeset relative to its first parent.
2433 the changes in that changeset relative to its first parent.
2430
2434
2431 Without the -a/--text option, diff will avoid generating diffs of
2435 Without the -a/--text option, diff will avoid generating diffs of
2432 files it detects as binary. With -a, diff will generate a diff
2436 files it detects as binary. With -a, diff will generate a diff
2433 anyway, probably with undesirable results.
2437 anyway, probably with undesirable results.
2434
2438
2435 Use the -g/--git option to generate diffs in the git extended diff
2439 Use the -g/--git option to generate diffs in the git extended diff
2436 format. For more information, read :hg:`help diffs`.
2440 format. For more information, read :hg:`help diffs`.
2437
2441
2438 .. container:: verbose
2442 .. container:: verbose
2439
2443
2440 Examples:
2444 Examples:
2441
2445
2442 - compare a file in the current working directory to its parent::
2446 - compare a file in the current working directory to its parent::
2443
2447
2444 hg diff foo.c
2448 hg diff foo.c
2445
2449
2446 - compare two historical versions of a directory, with rename info::
2450 - compare two historical versions of a directory, with rename info::
2447
2451
2448 hg diff --git -r 1.0:1.2 lib/
2452 hg diff --git -r 1.0:1.2 lib/
2449
2453
2450 - get change stats relative to the last change on some date::
2454 - get change stats relative to the last change on some date::
2451
2455
2452 hg diff --stat -r "date('may 2')"
2456 hg diff --stat -r "date('may 2')"
2453
2457
2454 - diff all newly-added files that contain a keyword::
2458 - diff all newly-added files that contain a keyword::
2455
2459
2456 hg diff "set:added() and grep(GNU)"
2460 hg diff "set:added() and grep(GNU)"
2457
2461
2458 - compare a revision and its parents::
2462 - compare a revision and its parents::
2459
2463
2460 hg diff -c 9353 # compare against first parent
2464 hg diff -c 9353 # compare against first parent
2461 hg diff -r 9353^:9353 # same using revset syntax
2465 hg diff -r 9353^:9353 # same using revset syntax
2462 hg diff -r 9353^2:9353 # compare against the second parent
2466 hg diff -r 9353^2:9353 # compare against the second parent
2463
2467
2464 Returns 0 on success.
2468 Returns 0 on success.
2465 """
2469 """
2466
2470
2467 opts = pycompat.byteskwargs(opts)
2471 opts = pycompat.byteskwargs(opts)
2468 revs = opts.get(b'rev')
2472 revs = opts.get(b'rev')
2469 change = opts.get(b'change')
2473 change = opts.get(b'change')
2470 stat = opts.get(b'stat')
2474 stat = opts.get(b'stat')
2471 reverse = opts.get(b'reverse')
2475 reverse = opts.get(b'reverse')
2472
2476
2473 if revs and change:
2477 if revs and change:
2474 msg = _(b'cannot specify --rev and --change at the same time')
2478 msg = _(b'cannot specify --rev and --change at the same time')
2475 raise error.Abort(msg)
2479 raise error.Abort(msg)
2476 elif change:
2480 elif change:
2477 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2481 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2478 ctx2 = scmutil.revsingle(repo, change, None)
2482 ctx2 = scmutil.revsingle(repo, change, None)
2479 ctx1 = ctx2.p1()
2483 ctx1 = ctx2.p1()
2480 else:
2484 else:
2481 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2485 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2482 ctx1, ctx2 = scmutil.revpair(repo, revs)
2486 ctx1, ctx2 = scmutil.revpair(repo, revs)
2483 node1, node2 = ctx1.node(), ctx2.node()
2487 node1, node2 = ctx1.node(), ctx2.node()
2484
2488
2485 if reverse:
2489 if reverse:
2486 node1, node2 = node2, node1
2490 node1, node2 = node2, node1
2487
2491
2488 diffopts = patch.diffallopts(ui, opts)
2492 diffopts = patch.diffallopts(ui, opts)
2489 m = scmutil.match(ctx2, pats, opts)
2493 m = scmutil.match(ctx2, pats, opts)
2490 m = repo.narrowmatch(m)
2494 m = repo.narrowmatch(m)
2491 ui.pager(b'diff')
2495 ui.pager(b'diff')
2492 logcmdutil.diffordiffstat(
2496 logcmdutil.diffordiffstat(
2493 ui,
2497 ui,
2494 repo,
2498 repo,
2495 diffopts,
2499 diffopts,
2496 node1,
2500 node1,
2497 node2,
2501 node2,
2498 m,
2502 m,
2499 stat=stat,
2503 stat=stat,
2500 listsubrepos=opts.get(b'subrepos'),
2504 listsubrepos=opts.get(b'subrepos'),
2501 root=opts.get(b'root'),
2505 root=opts.get(b'root'),
2502 )
2506 )
2503
2507
2504
2508
2505 @command(
2509 @command(
2506 b'export',
2510 b'export',
2507 [
2511 [
2508 (
2512 (
2509 b'B',
2513 b'B',
2510 b'bookmark',
2514 b'bookmark',
2511 b'',
2515 b'',
2512 _(b'export changes only reachable by given bookmark'),
2516 _(b'export changes only reachable by given bookmark'),
2513 _(b'BOOKMARK'),
2517 _(b'BOOKMARK'),
2514 ),
2518 ),
2515 (
2519 (
2516 b'o',
2520 b'o',
2517 b'output',
2521 b'output',
2518 b'',
2522 b'',
2519 _(b'print output to file with formatted name'),
2523 _(b'print output to file with formatted name'),
2520 _(b'FORMAT'),
2524 _(b'FORMAT'),
2521 ),
2525 ),
2522 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2526 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2523 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2527 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2524 ]
2528 ]
2525 + diffopts
2529 + diffopts
2526 + formatteropts,
2530 + formatteropts,
2527 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2531 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2528 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2532 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2529 helpbasic=True,
2533 helpbasic=True,
2530 intents={INTENT_READONLY},
2534 intents={INTENT_READONLY},
2531 )
2535 )
2532 def export(ui, repo, *changesets, **opts):
2536 def export(ui, repo, *changesets, **opts):
2533 """dump the header and diffs for one or more changesets
2537 """dump the header and diffs for one or more changesets
2534
2538
2535 Print the changeset header and diffs for one or more revisions.
2539 Print the changeset header and diffs for one or more revisions.
2536 If no revision is given, the parent of the working directory is used.
2540 If no revision is given, the parent of the working directory is used.
2537
2541
2538 The information shown in the changeset header is: author, date,
2542 The information shown in the changeset header is: author, date,
2539 branch name (if non-default), changeset hash, parent(s) and commit
2543 branch name (if non-default), changeset hash, parent(s) and commit
2540 comment.
2544 comment.
2541
2545
2542 .. note::
2546 .. note::
2543
2547
2544 :hg:`export` may generate unexpected diff output for merge
2548 :hg:`export` may generate unexpected diff output for merge
2545 changesets, as it will compare the merge changeset against its
2549 changesets, as it will compare the merge changeset against its
2546 first parent only.
2550 first parent only.
2547
2551
2548 Output may be to a file, in which case the name of the file is
2552 Output may be to a file, in which case the name of the file is
2549 given using a template string. See :hg:`help templates`. In addition
2553 given using a template string. See :hg:`help templates`. In addition
2550 to the common template keywords, the following formatting rules are
2554 to the common template keywords, the following formatting rules are
2551 supported:
2555 supported:
2552
2556
2553 :``%%``: literal "%" character
2557 :``%%``: literal "%" character
2554 :``%H``: changeset hash (40 hexadecimal digits)
2558 :``%H``: changeset hash (40 hexadecimal digits)
2555 :``%N``: number of patches being generated
2559 :``%N``: number of patches being generated
2556 :``%R``: changeset revision number
2560 :``%R``: changeset revision number
2557 :``%b``: basename of the exporting repository
2561 :``%b``: basename of the exporting repository
2558 :``%h``: short-form changeset hash (12 hexadecimal digits)
2562 :``%h``: short-form changeset hash (12 hexadecimal digits)
2559 :``%m``: first line of the commit message (only alphanumeric characters)
2563 :``%m``: first line of the commit message (only alphanumeric characters)
2560 :``%n``: zero-padded sequence number, starting at 1
2564 :``%n``: zero-padded sequence number, starting at 1
2561 :``%r``: zero-padded changeset revision number
2565 :``%r``: zero-padded changeset revision number
2562 :``\\``: literal "\\" character
2566 :``\\``: literal "\\" character
2563
2567
2564 Without the -a/--text option, export will avoid generating diffs
2568 Without the -a/--text option, export will avoid generating diffs
2565 of files it detects as binary. With -a, export will generate a
2569 of files it detects as binary. With -a, export will generate a
2566 diff anyway, probably with undesirable results.
2570 diff anyway, probably with undesirable results.
2567
2571
2568 With -B/--bookmark changesets reachable by the given bookmark are
2572 With -B/--bookmark changesets reachable by the given bookmark are
2569 selected.
2573 selected.
2570
2574
2571 Use the -g/--git option to generate diffs in the git extended diff
2575 Use the -g/--git option to generate diffs in the git extended diff
2572 format. See :hg:`help diffs` for more information.
2576 format. See :hg:`help diffs` for more information.
2573
2577
2574 With the --switch-parent option, the diff will be against the
2578 With the --switch-parent option, the diff will be against the
2575 second parent. It can be useful to review a merge.
2579 second parent. It can be useful to review a merge.
2576
2580
2577 .. container:: verbose
2581 .. container:: verbose
2578
2582
2579 Template:
2583 Template:
2580
2584
2581 The following keywords are supported in addition to the common template
2585 The following keywords are supported in addition to the common template
2582 keywords and functions. See also :hg:`help templates`.
2586 keywords and functions. See also :hg:`help templates`.
2583
2587
2584 :diff: String. Diff content.
2588 :diff: String. Diff content.
2585 :parents: List of strings. Parent nodes of the changeset.
2589 :parents: List of strings. Parent nodes of the changeset.
2586
2590
2587 Examples:
2591 Examples:
2588
2592
2589 - use export and import to transplant a bugfix to the current
2593 - use export and import to transplant a bugfix to the current
2590 branch::
2594 branch::
2591
2595
2592 hg export -r 9353 | hg import -
2596 hg export -r 9353 | hg import -
2593
2597
2594 - export all the changesets between two revisions to a file with
2598 - export all the changesets between two revisions to a file with
2595 rename information::
2599 rename information::
2596
2600
2597 hg export --git -r 123:150 > changes.txt
2601 hg export --git -r 123:150 > changes.txt
2598
2602
2599 - split outgoing changes into a series of patches with
2603 - split outgoing changes into a series of patches with
2600 descriptive names::
2604 descriptive names::
2601
2605
2602 hg export -r "outgoing()" -o "%n-%m.patch"
2606 hg export -r "outgoing()" -o "%n-%m.patch"
2603
2607
2604 Returns 0 on success.
2608 Returns 0 on success.
2605 """
2609 """
2606 opts = pycompat.byteskwargs(opts)
2610 opts = pycompat.byteskwargs(opts)
2607 bookmark = opts.get(b'bookmark')
2611 bookmark = opts.get(b'bookmark')
2608 changesets += tuple(opts.get(b'rev', []))
2612 changesets += tuple(opts.get(b'rev', []))
2609
2613
2610 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2614 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2611
2615
2612 if bookmark:
2616 if bookmark:
2613 if bookmark not in repo._bookmarks:
2617 if bookmark not in repo._bookmarks:
2614 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2618 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2615
2619
2616 revs = scmutil.bookmarkrevs(repo, bookmark)
2620 revs = scmutil.bookmarkrevs(repo, bookmark)
2617 else:
2621 else:
2618 if not changesets:
2622 if not changesets:
2619 changesets = [b'.']
2623 changesets = [b'.']
2620
2624
2621 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2625 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2622 revs = scmutil.revrange(repo, changesets)
2626 revs = scmutil.revrange(repo, changesets)
2623
2627
2624 if not revs:
2628 if not revs:
2625 raise error.Abort(_(b"export requires at least one changeset"))
2629 raise error.Abort(_(b"export requires at least one changeset"))
2626 if len(revs) > 1:
2630 if len(revs) > 1:
2627 ui.note(_(b'exporting patches:\n'))
2631 ui.note(_(b'exporting patches:\n'))
2628 else:
2632 else:
2629 ui.note(_(b'exporting patch:\n'))
2633 ui.note(_(b'exporting patch:\n'))
2630
2634
2631 fntemplate = opts.get(b'output')
2635 fntemplate = opts.get(b'output')
2632 if cmdutil.isstdiofilename(fntemplate):
2636 if cmdutil.isstdiofilename(fntemplate):
2633 fntemplate = b''
2637 fntemplate = b''
2634
2638
2635 if fntemplate:
2639 if fntemplate:
2636 fm = formatter.nullformatter(ui, b'export', opts)
2640 fm = formatter.nullformatter(ui, b'export', opts)
2637 else:
2641 else:
2638 ui.pager(b'export')
2642 ui.pager(b'export')
2639 fm = ui.formatter(b'export', opts)
2643 fm = ui.formatter(b'export', opts)
2640 with fm:
2644 with fm:
2641 cmdutil.export(
2645 cmdutil.export(
2642 repo,
2646 repo,
2643 revs,
2647 revs,
2644 fm,
2648 fm,
2645 fntemplate=fntemplate,
2649 fntemplate=fntemplate,
2646 switch_parent=opts.get(b'switch_parent'),
2650 switch_parent=opts.get(b'switch_parent'),
2647 opts=patch.diffallopts(ui, opts),
2651 opts=patch.diffallopts(ui, opts),
2648 )
2652 )
2649
2653
2650
2654
2651 @command(
2655 @command(
2652 b'files',
2656 b'files',
2653 [
2657 [
2654 (
2658 (
2655 b'r',
2659 b'r',
2656 b'rev',
2660 b'rev',
2657 b'',
2661 b'',
2658 _(b'search the repository as it is in REV'),
2662 _(b'search the repository as it is in REV'),
2659 _(b'REV'),
2663 _(b'REV'),
2660 ),
2664 ),
2661 (
2665 (
2662 b'0',
2666 b'0',
2663 b'print0',
2667 b'print0',
2664 None,
2668 None,
2665 _(b'end filenames with NUL, for use with xargs'),
2669 _(b'end filenames with NUL, for use with xargs'),
2666 ),
2670 ),
2667 ]
2671 ]
2668 + walkopts
2672 + walkopts
2669 + formatteropts
2673 + formatteropts
2670 + subrepoopts,
2674 + subrepoopts,
2671 _(b'[OPTION]... [FILE]...'),
2675 _(b'[OPTION]... [FILE]...'),
2672 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2676 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2673 intents={INTENT_READONLY},
2677 intents={INTENT_READONLY},
2674 )
2678 )
2675 def files(ui, repo, *pats, **opts):
2679 def files(ui, repo, *pats, **opts):
2676 """list tracked files
2680 """list tracked files
2677
2681
2678 Print files under Mercurial control in the working directory or
2682 Print files under Mercurial control in the working directory or
2679 specified revision for given files (excluding removed files).
2683 specified revision for given files (excluding removed files).
2680 Files can be specified as filenames or filesets.
2684 Files can be specified as filenames or filesets.
2681
2685
2682 If no files are given to match, this command prints the names
2686 If no files are given to match, this command prints the names
2683 of all files under Mercurial control.
2687 of all files under Mercurial control.
2684
2688
2685 .. container:: verbose
2689 .. container:: verbose
2686
2690
2687 Template:
2691 Template:
2688
2692
2689 The following keywords are supported in addition to the common template
2693 The following keywords are supported in addition to the common template
2690 keywords and functions. See also :hg:`help templates`.
2694 keywords and functions. See also :hg:`help templates`.
2691
2695
2692 :flags: String. Character denoting file's symlink and executable bits.
2696 :flags: String. Character denoting file's symlink and executable bits.
2693 :path: String. Repository-absolute path of the file.
2697 :path: String. Repository-absolute path of the file.
2694 :size: Integer. Size of the file in bytes.
2698 :size: Integer. Size of the file in bytes.
2695
2699
2696 Examples:
2700 Examples:
2697
2701
2698 - list all files under the current directory::
2702 - list all files under the current directory::
2699
2703
2700 hg files .
2704 hg files .
2701
2705
2702 - shows sizes and flags for current revision::
2706 - shows sizes and flags for current revision::
2703
2707
2704 hg files -vr .
2708 hg files -vr .
2705
2709
2706 - list all files named README::
2710 - list all files named README::
2707
2711
2708 hg files -I "**/README"
2712 hg files -I "**/README"
2709
2713
2710 - list all binary files::
2714 - list all binary files::
2711
2715
2712 hg files "set:binary()"
2716 hg files "set:binary()"
2713
2717
2714 - find files containing a regular expression::
2718 - find files containing a regular expression::
2715
2719
2716 hg files "set:grep('bob')"
2720 hg files "set:grep('bob')"
2717
2721
2718 - search tracked file contents with xargs and grep::
2722 - search tracked file contents with xargs and grep::
2719
2723
2720 hg files -0 | xargs -0 grep foo
2724 hg files -0 | xargs -0 grep foo
2721
2725
2722 See :hg:`help patterns` and :hg:`help filesets` for more information
2726 See :hg:`help patterns` and :hg:`help filesets` for more information
2723 on specifying file patterns.
2727 on specifying file patterns.
2724
2728
2725 Returns 0 if a match is found, 1 otherwise.
2729 Returns 0 if a match is found, 1 otherwise.
2726
2730
2727 """
2731 """
2728
2732
2729 opts = pycompat.byteskwargs(opts)
2733 opts = pycompat.byteskwargs(opts)
2730 rev = opts.get(b'rev')
2734 rev = opts.get(b'rev')
2731 if rev:
2735 if rev:
2732 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2736 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2733 ctx = scmutil.revsingle(repo, rev, None)
2737 ctx = scmutil.revsingle(repo, rev, None)
2734
2738
2735 end = b'\n'
2739 end = b'\n'
2736 if opts.get(b'print0'):
2740 if opts.get(b'print0'):
2737 end = b'\0'
2741 end = b'\0'
2738 fmt = b'%s' + end
2742 fmt = b'%s' + end
2739
2743
2740 m = scmutil.match(ctx, pats, opts)
2744 m = scmutil.match(ctx, pats, opts)
2741 ui.pager(b'files')
2745 ui.pager(b'files')
2742 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2746 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2743 with ui.formatter(b'files', opts) as fm:
2747 with ui.formatter(b'files', opts) as fm:
2744 return cmdutil.files(
2748 return cmdutil.files(
2745 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2749 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2746 )
2750 )
2747
2751
2748
2752
2749 @command(
2753 @command(
2750 b'forget',
2754 b'forget',
2751 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2755 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2752 + walkopts
2756 + walkopts
2753 + dryrunopts,
2757 + dryrunopts,
2754 _(b'[OPTION]... FILE...'),
2758 _(b'[OPTION]... FILE...'),
2755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2759 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2756 helpbasic=True,
2760 helpbasic=True,
2757 inferrepo=True,
2761 inferrepo=True,
2758 )
2762 )
2759 def forget(ui, repo, *pats, **opts):
2763 def forget(ui, repo, *pats, **opts):
2760 """forget the specified files on the next commit
2764 """forget the specified files on the next commit
2761
2765
2762 Mark the specified files so they will no longer be tracked
2766 Mark the specified files so they will no longer be tracked
2763 after the next commit.
2767 after the next commit.
2764
2768
2765 This only removes files from the current branch, not from the
2769 This only removes files from the current branch, not from the
2766 entire project history, and it does not delete them from the
2770 entire project history, and it does not delete them from the
2767 working directory.
2771 working directory.
2768
2772
2769 To delete the file from the working directory, see :hg:`remove`.
2773 To delete the file from the working directory, see :hg:`remove`.
2770
2774
2771 To undo a forget before the next commit, see :hg:`add`.
2775 To undo a forget before the next commit, see :hg:`add`.
2772
2776
2773 .. container:: verbose
2777 .. container:: verbose
2774
2778
2775 Examples:
2779 Examples:
2776
2780
2777 - forget newly-added binary files::
2781 - forget newly-added binary files::
2778
2782
2779 hg forget "set:added() and binary()"
2783 hg forget "set:added() and binary()"
2780
2784
2781 - forget files that would be excluded by .hgignore::
2785 - forget files that would be excluded by .hgignore::
2782
2786
2783 hg forget "set:hgignore()"
2787 hg forget "set:hgignore()"
2784
2788
2785 Returns 0 on success.
2789 Returns 0 on success.
2786 """
2790 """
2787
2791
2788 opts = pycompat.byteskwargs(opts)
2792 opts = pycompat.byteskwargs(opts)
2789 if not pats:
2793 if not pats:
2790 raise error.Abort(_(b'no files specified'))
2794 raise error.Abort(_(b'no files specified'))
2791
2795
2792 m = scmutil.match(repo[None], pats, opts)
2796 m = scmutil.match(repo[None], pats, opts)
2793 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2797 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2794 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2798 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2795 rejected = cmdutil.forget(
2799 rejected = cmdutil.forget(
2796 ui,
2800 ui,
2797 repo,
2801 repo,
2798 m,
2802 m,
2799 prefix=b"",
2803 prefix=b"",
2800 uipathfn=uipathfn,
2804 uipathfn=uipathfn,
2801 explicitonly=False,
2805 explicitonly=False,
2802 dryrun=dryrun,
2806 dryrun=dryrun,
2803 interactive=interactive,
2807 interactive=interactive,
2804 )[0]
2808 )[0]
2805 return rejected and 1 or 0
2809 return rejected and 1 or 0
2806
2810
2807
2811
2808 @command(
2812 @command(
2809 b'graft',
2813 b'graft',
2810 [
2814 [
2811 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2815 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2812 (
2816 (
2813 b'',
2817 b'',
2814 b'base',
2818 b'base',
2815 b'',
2819 b'',
2816 _(b'base revision when doing the graft merge (ADVANCED)'),
2820 _(b'base revision when doing the graft merge (ADVANCED)'),
2817 _(b'REV'),
2821 _(b'REV'),
2818 ),
2822 ),
2819 (b'c', b'continue', False, _(b'resume interrupted graft')),
2823 (b'c', b'continue', False, _(b'resume interrupted graft')),
2820 (b'', b'stop', False, _(b'stop interrupted graft')),
2824 (b'', b'stop', False, _(b'stop interrupted graft')),
2821 (b'', b'abort', False, _(b'abort interrupted graft')),
2825 (b'', b'abort', False, _(b'abort interrupted graft')),
2822 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2826 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2823 (b'', b'log', None, _(b'append graft info to log message')),
2827 (b'', b'log', None, _(b'append graft info to log message')),
2824 (
2828 (
2825 b'',
2829 b'',
2826 b'no-commit',
2830 b'no-commit',
2827 None,
2831 None,
2828 _(b"don't commit, just apply the changes in working directory"),
2832 _(b"don't commit, just apply the changes in working directory"),
2829 ),
2833 ),
2830 (b'f', b'force', False, _(b'force graft')),
2834 (b'f', b'force', False, _(b'force graft')),
2831 (
2835 (
2832 b'D',
2836 b'D',
2833 b'currentdate',
2837 b'currentdate',
2834 False,
2838 False,
2835 _(b'record the current date as commit date'),
2839 _(b'record the current date as commit date'),
2836 ),
2840 ),
2837 (
2841 (
2838 b'U',
2842 b'U',
2839 b'currentuser',
2843 b'currentuser',
2840 False,
2844 False,
2841 _(b'record the current user as committer'),
2845 _(b'record the current user as committer'),
2842 ),
2846 ),
2843 ]
2847 ]
2844 + commitopts2
2848 + commitopts2
2845 + mergetoolopts
2849 + mergetoolopts
2846 + dryrunopts,
2850 + dryrunopts,
2847 _(b'[OPTION]... [-r REV]... REV...'),
2851 _(b'[OPTION]... [-r REV]... REV...'),
2848 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2852 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2849 )
2853 )
2850 def graft(ui, repo, *revs, **opts):
2854 def graft(ui, repo, *revs, **opts):
2851 '''copy changes from other branches onto the current branch
2855 '''copy changes from other branches onto the current branch
2852
2856
2853 This command uses Mercurial's merge logic to copy individual
2857 This command uses Mercurial's merge logic to copy individual
2854 changes from other branches without merging branches in the
2858 changes from other branches without merging branches in the
2855 history graph. This is sometimes known as 'backporting' or
2859 history graph. This is sometimes known as 'backporting' or
2856 'cherry-picking'. By default, graft will copy user, date, and
2860 'cherry-picking'. By default, graft will copy user, date, and
2857 description from the source changesets.
2861 description from the source changesets.
2858
2862
2859 Changesets that are ancestors of the current revision, that have
2863 Changesets that are ancestors of the current revision, that have
2860 already been grafted, or that are merges will be skipped.
2864 already been grafted, or that are merges will be skipped.
2861
2865
2862 If --log is specified, log messages will have a comment appended
2866 If --log is specified, log messages will have a comment appended
2863 of the form::
2867 of the form::
2864
2868
2865 (grafted from CHANGESETHASH)
2869 (grafted from CHANGESETHASH)
2866
2870
2867 If --force is specified, revisions will be grafted even if they
2871 If --force is specified, revisions will be grafted even if they
2868 are already ancestors of, or have been grafted to, the destination.
2872 are already ancestors of, or have been grafted to, the destination.
2869 This is useful when the revisions have since been backed out.
2873 This is useful when the revisions have since been backed out.
2870
2874
2871 If a graft merge results in conflicts, the graft process is
2875 If a graft merge results in conflicts, the graft process is
2872 interrupted so that the current merge can be manually resolved.
2876 interrupted so that the current merge can be manually resolved.
2873 Once all conflicts are addressed, the graft process can be
2877 Once all conflicts are addressed, the graft process can be
2874 continued with the -c/--continue option.
2878 continued with the -c/--continue option.
2875
2879
2876 The -c/--continue option reapplies all the earlier options.
2880 The -c/--continue option reapplies all the earlier options.
2877
2881
2878 .. container:: verbose
2882 .. container:: verbose
2879
2883
2880 The --base option exposes more of how graft internally uses merge with a
2884 The --base option exposes more of how graft internally uses merge with a
2881 custom base revision. --base can be used to specify another ancestor than
2885 custom base revision. --base can be used to specify another ancestor than
2882 the first and only parent.
2886 the first and only parent.
2883
2887
2884 The command::
2888 The command::
2885
2889
2886 hg graft -r 345 --base 234
2890 hg graft -r 345 --base 234
2887
2891
2888 is thus pretty much the same as::
2892 is thus pretty much the same as::
2889
2893
2890 hg diff -r 234 -r 345 | hg import
2894 hg diff -r 234 -r 345 | hg import
2891
2895
2892 but using merge to resolve conflicts and track moved files.
2896 but using merge to resolve conflicts and track moved files.
2893
2897
2894 The result of a merge can thus be backported as a single commit by
2898 The result of a merge can thus be backported as a single commit by
2895 specifying one of the merge parents as base, and thus effectively
2899 specifying one of the merge parents as base, and thus effectively
2896 grafting the changes from the other side.
2900 grafting the changes from the other side.
2897
2901
2898 It is also possible to collapse multiple changesets and clean up history
2902 It is also possible to collapse multiple changesets and clean up history
2899 by specifying another ancestor as base, much like rebase --collapse
2903 by specifying another ancestor as base, much like rebase --collapse
2900 --keep.
2904 --keep.
2901
2905
2902 The commit message can be tweaked after the fact using commit --amend .
2906 The commit message can be tweaked after the fact using commit --amend .
2903
2907
2904 For using non-ancestors as the base to backout changes, see the backout
2908 For using non-ancestors as the base to backout changes, see the backout
2905 command and the hidden --parent option.
2909 command and the hidden --parent option.
2906
2910
2907 .. container:: verbose
2911 .. container:: verbose
2908
2912
2909 Examples:
2913 Examples:
2910
2914
2911 - copy a single change to the stable branch and edit its description::
2915 - copy a single change to the stable branch and edit its description::
2912
2916
2913 hg update stable
2917 hg update stable
2914 hg graft --edit 9393
2918 hg graft --edit 9393
2915
2919
2916 - graft a range of changesets with one exception, updating dates::
2920 - graft a range of changesets with one exception, updating dates::
2917
2921
2918 hg graft -D "2085::2093 and not 2091"
2922 hg graft -D "2085::2093 and not 2091"
2919
2923
2920 - continue a graft after resolving conflicts::
2924 - continue a graft after resolving conflicts::
2921
2925
2922 hg graft -c
2926 hg graft -c
2923
2927
2924 - show the source of a grafted changeset::
2928 - show the source of a grafted changeset::
2925
2929
2926 hg log --debug -r .
2930 hg log --debug -r .
2927
2931
2928 - show revisions sorted by date::
2932 - show revisions sorted by date::
2929
2933
2930 hg log -r "sort(all(), date)"
2934 hg log -r "sort(all(), date)"
2931
2935
2932 - backport the result of a merge as a single commit::
2936 - backport the result of a merge as a single commit::
2933
2937
2934 hg graft -r 123 --base 123^
2938 hg graft -r 123 --base 123^
2935
2939
2936 - land a feature branch as one changeset::
2940 - land a feature branch as one changeset::
2937
2941
2938 hg up -cr default
2942 hg up -cr default
2939 hg graft -r featureX --base "ancestor('featureX', 'default')"
2943 hg graft -r featureX --base "ancestor('featureX', 'default')"
2940
2944
2941 See :hg:`help revisions` for more about specifying revisions.
2945 See :hg:`help revisions` for more about specifying revisions.
2942
2946
2943 Returns 0 on successful completion.
2947 Returns 0 on successful completion.
2944 '''
2948 '''
2945 with repo.wlock():
2949 with repo.wlock():
2946 return _dograft(ui, repo, *revs, **opts)
2950 return _dograft(ui, repo, *revs, **opts)
2947
2951
2948
2952
2949 def _dograft(ui, repo, *revs, **opts):
2953 def _dograft(ui, repo, *revs, **opts):
2950 opts = pycompat.byteskwargs(opts)
2954 opts = pycompat.byteskwargs(opts)
2951 if revs and opts.get(b'rev'):
2955 if revs and opts.get(b'rev'):
2952 ui.warn(
2956 ui.warn(
2953 _(
2957 _(
2954 b'warning: inconsistent use of --rev might give unexpected '
2958 b'warning: inconsistent use of --rev might give unexpected '
2955 b'revision ordering!\n'
2959 b'revision ordering!\n'
2956 )
2960 )
2957 )
2961 )
2958
2962
2959 revs = list(revs)
2963 revs = list(revs)
2960 revs.extend(opts.get(b'rev'))
2964 revs.extend(opts.get(b'rev'))
2961 basectx = None
2965 basectx = None
2962 if opts.get(b'base'):
2966 if opts.get(b'base'):
2963 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2967 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2964 # a dict of data to be stored in state file
2968 # a dict of data to be stored in state file
2965 statedata = {}
2969 statedata = {}
2966 # list of new nodes created by ongoing graft
2970 # list of new nodes created by ongoing graft
2967 statedata[b'newnodes'] = []
2971 statedata[b'newnodes'] = []
2968
2972
2969 cmdutil.resolvecommitoptions(ui, opts)
2973 cmdutil.resolvecommitoptions(ui, opts)
2970
2974
2971 editor = cmdutil.getcommiteditor(
2975 editor = cmdutil.getcommiteditor(
2972 editform=b'graft', **pycompat.strkwargs(opts)
2976 editform=b'graft', **pycompat.strkwargs(opts)
2973 )
2977 )
2974
2978
2975 cont = False
2979 cont = False
2976 if opts.get(b'no_commit'):
2980 if opts.get(b'no_commit'):
2977 if opts.get(b'edit'):
2981 if opts.get(b'edit'):
2978 raise error.Abort(
2982 raise error.Abort(
2979 _(b"cannot specify --no-commit and --edit together")
2983 _(b"cannot specify --no-commit and --edit together")
2980 )
2984 )
2981 if opts.get(b'currentuser'):
2985 if opts.get(b'currentuser'):
2982 raise error.Abort(
2986 raise error.Abort(
2983 _(b"cannot specify --no-commit and --currentuser together")
2987 _(b"cannot specify --no-commit and --currentuser together")
2984 )
2988 )
2985 if opts.get(b'currentdate'):
2989 if opts.get(b'currentdate'):
2986 raise error.Abort(
2990 raise error.Abort(
2987 _(b"cannot specify --no-commit and --currentdate together")
2991 _(b"cannot specify --no-commit and --currentdate together")
2988 )
2992 )
2989 if opts.get(b'log'):
2993 if opts.get(b'log'):
2990 raise error.Abort(
2994 raise error.Abort(
2991 _(b"cannot specify --no-commit and --log together")
2995 _(b"cannot specify --no-commit and --log together")
2992 )
2996 )
2993
2997
2994 graftstate = statemod.cmdstate(repo, b'graftstate')
2998 graftstate = statemod.cmdstate(repo, b'graftstate')
2995
2999
2996 if opts.get(b'stop'):
3000 if opts.get(b'stop'):
2997 if opts.get(b'continue'):
3001 if opts.get(b'continue'):
2998 raise error.Abort(
3002 raise error.Abort(
2999 _(b"cannot use '--continue' and '--stop' together")
3003 _(b"cannot use '--continue' and '--stop' together")
3000 )
3004 )
3001 if opts.get(b'abort'):
3005 if opts.get(b'abort'):
3002 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3006 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3003
3007
3004 if any(
3008 if any(
3005 (
3009 (
3006 opts.get(b'edit'),
3010 opts.get(b'edit'),
3007 opts.get(b'log'),
3011 opts.get(b'log'),
3008 opts.get(b'user'),
3012 opts.get(b'user'),
3009 opts.get(b'date'),
3013 opts.get(b'date'),
3010 opts.get(b'currentdate'),
3014 opts.get(b'currentdate'),
3011 opts.get(b'currentuser'),
3015 opts.get(b'currentuser'),
3012 opts.get(b'rev'),
3016 opts.get(b'rev'),
3013 )
3017 )
3014 ):
3018 ):
3015 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3019 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3016 return _stopgraft(ui, repo, graftstate)
3020 return _stopgraft(ui, repo, graftstate)
3017 elif opts.get(b'abort'):
3021 elif opts.get(b'abort'):
3018 if opts.get(b'continue'):
3022 if opts.get(b'continue'):
3019 raise error.Abort(
3023 raise error.Abort(
3020 _(b"cannot use '--continue' and '--abort' together")
3024 _(b"cannot use '--continue' and '--abort' together")
3021 )
3025 )
3022 if any(
3026 if any(
3023 (
3027 (
3024 opts.get(b'edit'),
3028 opts.get(b'edit'),
3025 opts.get(b'log'),
3029 opts.get(b'log'),
3026 opts.get(b'user'),
3030 opts.get(b'user'),
3027 opts.get(b'date'),
3031 opts.get(b'date'),
3028 opts.get(b'currentdate'),
3032 opts.get(b'currentdate'),
3029 opts.get(b'currentuser'),
3033 opts.get(b'currentuser'),
3030 opts.get(b'rev'),
3034 opts.get(b'rev'),
3031 )
3035 )
3032 ):
3036 ):
3033 raise error.Abort(
3037 raise error.Abort(
3034 _(b"cannot specify any other flag with '--abort'")
3038 _(b"cannot specify any other flag with '--abort'")
3035 )
3039 )
3036
3040
3037 return cmdutil.abortgraft(ui, repo, graftstate)
3041 return cmdutil.abortgraft(ui, repo, graftstate)
3038 elif opts.get(b'continue'):
3042 elif opts.get(b'continue'):
3039 cont = True
3043 cont = True
3040 if revs:
3044 if revs:
3041 raise error.Abort(_(b"can't specify --continue and revisions"))
3045 raise error.Abort(_(b"can't specify --continue and revisions"))
3042 # read in unfinished revisions
3046 # read in unfinished revisions
3043 if graftstate.exists():
3047 if graftstate.exists():
3044 statedata = cmdutil.readgraftstate(repo, graftstate)
3048 statedata = cmdutil.readgraftstate(repo, graftstate)
3045 if statedata.get(b'date'):
3049 if statedata.get(b'date'):
3046 opts[b'date'] = statedata[b'date']
3050 opts[b'date'] = statedata[b'date']
3047 if statedata.get(b'user'):
3051 if statedata.get(b'user'):
3048 opts[b'user'] = statedata[b'user']
3052 opts[b'user'] = statedata[b'user']
3049 if statedata.get(b'log'):
3053 if statedata.get(b'log'):
3050 opts[b'log'] = True
3054 opts[b'log'] = True
3051 if statedata.get(b'no_commit'):
3055 if statedata.get(b'no_commit'):
3052 opts[b'no_commit'] = statedata.get(b'no_commit')
3056 opts[b'no_commit'] = statedata.get(b'no_commit')
3053 nodes = statedata[b'nodes']
3057 nodes = statedata[b'nodes']
3054 revs = [repo[node].rev() for node in nodes]
3058 revs = [repo[node].rev() for node in nodes]
3055 else:
3059 else:
3056 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3060 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3057 else:
3061 else:
3058 if not revs:
3062 if not revs:
3059 raise error.Abort(_(b'no revisions specified'))
3063 raise error.Abort(_(b'no revisions specified'))
3060 cmdutil.checkunfinished(repo)
3064 cmdutil.checkunfinished(repo)
3061 cmdutil.bailifchanged(repo)
3065 cmdutil.bailifchanged(repo)
3062 revs = scmutil.revrange(repo, revs)
3066 revs = scmutil.revrange(repo, revs)
3063
3067
3064 skipped = set()
3068 skipped = set()
3065 if basectx is None:
3069 if basectx is None:
3066 # check for merges
3070 # check for merges
3067 for rev in repo.revs(b'%ld and merge()', revs):
3071 for rev in repo.revs(b'%ld and merge()', revs):
3068 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3072 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3069 skipped.add(rev)
3073 skipped.add(rev)
3070 revs = [r for r in revs if r not in skipped]
3074 revs = [r for r in revs if r not in skipped]
3071 if not revs:
3075 if not revs:
3072 return -1
3076 return -1
3073 if basectx is not None and len(revs) != 1:
3077 if basectx is not None and len(revs) != 1:
3074 raise error.Abort(_(b'only one revision allowed with --base '))
3078 raise error.Abort(_(b'only one revision allowed with --base '))
3075
3079
3076 # Don't check in the --continue case, in effect retaining --force across
3080 # Don't check in the --continue case, in effect retaining --force across
3077 # --continues. That's because without --force, any revisions we decided to
3081 # --continues. That's because without --force, any revisions we decided to
3078 # skip would have been filtered out here, so they wouldn't have made their
3082 # skip would have been filtered out here, so they wouldn't have made their
3079 # way to the graftstate. With --force, any revisions we would have otherwise
3083 # way to the graftstate. With --force, any revisions we would have otherwise
3080 # skipped would not have been filtered out, and if they hadn't been applied
3084 # skipped would not have been filtered out, and if they hadn't been applied
3081 # already, they'd have been in the graftstate.
3085 # already, they'd have been in the graftstate.
3082 if not (cont or opts.get(b'force')) and basectx is None:
3086 if not (cont or opts.get(b'force')) and basectx is None:
3083 # check for ancestors of dest branch
3087 # check for ancestors of dest branch
3084 ancestors = repo.revs(b'%ld & (::.)', revs)
3088 ancestors = repo.revs(b'%ld & (::.)', revs)
3085 for rev in ancestors:
3089 for rev in ancestors:
3086 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3090 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3087
3091
3088 revs = [r for r in revs if r not in ancestors]
3092 revs = [r for r in revs if r not in ancestors]
3089
3093
3090 if not revs:
3094 if not revs:
3091 return -1
3095 return -1
3092
3096
3093 # analyze revs for earlier grafts
3097 # analyze revs for earlier grafts
3094 ids = {}
3098 ids = {}
3095 for ctx in repo.set(b"%ld", revs):
3099 for ctx in repo.set(b"%ld", revs):
3096 ids[ctx.hex()] = ctx.rev()
3100 ids[ctx.hex()] = ctx.rev()
3097 n = ctx.extra().get(b'source')
3101 n = ctx.extra().get(b'source')
3098 if n:
3102 if n:
3099 ids[n] = ctx.rev()
3103 ids[n] = ctx.rev()
3100
3104
3101 # check ancestors for earlier grafts
3105 # check ancestors for earlier grafts
3102 ui.debug(b'scanning for duplicate grafts\n')
3106 ui.debug(b'scanning for duplicate grafts\n')
3103
3107
3104 # The only changesets we can be sure doesn't contain grafts of any
3108 # The only changesets we can be sure doesn't contain grafts of any
3105 # revs, are the ones that are common ancestors of *all* revs:
3109 # revs, are the ones that are common ancestors of *all* revs:
3106 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3110 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3107 ctx = repo[rev]
3111 ctx = repo[rev]
3108 n = ctx.extra().get(b'source')
3112 n = ctx.extra().get(b'source')
3109 if n in ids:
3113 if n in ids:
3110 try:
3114 try:
3111 r = repo[n].rev()
3115 r = repo[n].rev()
3112 except error.RepoLookupError:
3116 except error.RepoLookupError:
3113 r = None
3117 r = None
3114 if r in revs:
3118 if r in revs:
3115 ui.warn(
3119 ui.warn(
3116 _(
3120 _(
3117 b'skipping revision %d:%s '
3121 b'skipping revision %d:%s '
3118 b'(already grafted to %d:%s)\n'
3122 b'(already grafted to %d:%s)\n'
3119 )
3123 )
3120 % (r, repo[r], rev, ctx)
3124 % (r, repo[r], rev, ctx)
3121 )
3125 )
3122 revs.remove(r)
3126 revs.remove(r)
3123 elif ids[n] in revs:
3127 elif ids[n] in revs:
3124 if r is None:
3128 if r is None:
3125 ui.warn(
3129 ui.warn(
3126 _(
3130 _(
3127 b'skipping already grafted revision %d:%s '
3131 b'skipping already grafted revision %d:%s '
3128 b'(%d:%s also has unknown origin %s)\n'
3132 b'(%d:%s also has unknown origin %s)\n'
3129 )
3133 )
3130 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3134 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3131 )
3135 )
3132 else:
3136 else:
3133 ui.warn(
3137 ui.warn(
3134 _(
3138 _(
3135 b'skipping already grafted revision %d:%s '
3139 b'skipping already grafted revision %d:%s '
3136 b'(%d:%s also has origin %d:%s)\n'
3140 b'(%d:%s also has origin %d:%s)\n'
3137 )
3141 )
3138 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3142 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3139 )
3143 )
3140 revs.remove(ids[n])
3144 revs.remove(ids[n])
3141 elif ctx.hex() in ids:
3145 elif ctx.hex() in ids:
3142 r = ids[ctx.hex()]
3146 r = ids[ctx.hex()]
3143 if r in revs:
3147 if r in revs:
3144 ui.warn(
3148 ui.warn(
3145 _(
3149 _(
3146 b'skipping already grafted revision %d:%s '
3150 b'skipping already grafted revision %d:%s '
3147 b'(was grafted from %d:%s)\n'
3151 b'(was grafted from %d:%s)\n'
3148 )
3152 )
3149 % (r, repo[r], rev, ctx)
3153 % (r, repo[r], rev, ctx)
3150 )
3154 )
3151 revs.remove(r)
3155 revs.remove(r)
3152 if not revs:
3156 if not revs:
3153 return -1
3157 return -1
3154
3158
3155 if opts.get(b'no_commit'):
3159 if opts.get(b'no_commit'):
3156 statedata[b'no_commit'] = True
3160 statedata[b'no_commit'] = True
3157 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3161 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3158 desc = b'%d:%s "%s"' % (
3162 desc = b'%d:%s "%s"' % (
3159 ctx.rev(),
3163 ctx.rev(),
3160 ctx,
3164 ctx,
3161 ctx.description().split(b'\n', 1)[0],
3165 ctx.description().split(b'\n', 1)[0],
3162 )
3166 )
3163 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3167 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3164 if names:
3168 if names:
3165 desc += b' (%s)' % b' '.join(names)
3169 desc += b' (%s)' % b' '.join(names)
3166 ui.status(_(b'grafting %s\n') % desc)
3170 ui.status(_(b'grafting %s\n') % desc)
3167 if opts.get(b'dry_run'):
3171 if opts.get(b'dry_run'):
3168 continue
3172 continue
3169
3173
3170 source = ctx.extra().get(b'source')
3174 source = ctx.extra().get(b'source')
3171 extra = {}
3175 extra = {}
3172 if source:
3176 if source:
3173 extra[b'source'] = source
3177 extra[b'source'] = source
3174 extra[b'intermediate-source'] = ctx.hex()
3178 extra[b'intermediate-source'] = ctx.hex()
3175 else:
3179 else:
3176 extra[b'source'] = ctx.hex()
3180 extra[b'source'] = ctx.hex()
3177 user = ctx.user()
3181 user = ctx.user()
3178 if opts.get(b'user'):
3182 if opts.get(b'user'):
3179 user = opts[b'user']
3183 user = opts[b'user']
3180 statedata[b'user'] = user
3184 statedata[b'user'] = user
3181 date = ctx.date()
3185 date = ctx.date()
3182 if opts.get(b'date'):
3186 if opts.get(b'date'):
3183 date = opts[b'date']
3187 date = opts[b'date']
3184 statedata[b'date'] = date
3188 statedata[b'date'] = date
3185 message = ctx.description()
3189 message = ctx.description()
3186 if opts.get(b'log'):
3190 if opts.get(b'log'):
3187 message += b'\n(grafted from %s)' % ctx.hex()
3191 message += b'\n(grafted from %s)' % ctx.hex()
3188 statedata[b'log'] = True
3192 statedata[b'log'] = True
3189
3193
3190 # we don't merge the first commit when continuing
3194 # we don't merge the first commit when continuing
3191 if not cont:
3195 if not cont:
3192 # perform the graft merge with p1(rev) as 'ancestor'
3196 # perform the graft merge with p1(rev) as 'ancestor'
3193 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3197 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3194 base = ctx.p1() if basectx is None else basectx
3198 base = ctx.p1() if basectx is None else basectx
3195 with ui.configoverride(overrides, b'graft'):
3199 with ui.configoverride(overrides, b'graft'):
3196 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3200 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3197 # report any conflicts
3201 # report any conflicts
3198 if stats.unresolvedcount > 0:
3202 if stats.unresolvedcount > 0:
3199 # write out state for --continue
3203 # write out state for --continue
3200 nodes = [repo[rev].hex() for rev in revs[pos:]]
3204 nodes = [repo[rev].hex() for rev in revs[pos:]]
3201 statedata[b'nodes'] = nodes
3205 statedata[b'nodes'] = nodes
3202 stateversion = 1
3206 stateversion = 1
3203 graftstate.save(stateversion, statedata)
3207 graftstate.save(stateversion, statedata)
3204 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3208 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3205 raise error.Abort(
3209 raise error.Abort(
3206 _(b"unresolved conflicts, can't continue"), hint=hint
3210 _(b"unresolved conflicts, can't continue"), hint=hint
3207 )
3211 )
3208 else:
3212 else:
3209 cont = False
3213 cont = False
3210
3214
3211 # commit if --no-commit is false
3215 # commit if --no-commit is false
3212 if not opts.get(b'no_commit'):
3216 if not opts.get(b'no_commit'):
3213 node = repo.commit(
3217 node = repo.commit(
3214 text=message, user=user, date=date, extra=extra, editor=editor
3218 text=message, user=user, date=date, extra=extra, editor=editor
3215 )
3219 )
3216 if node is None:
3220 if node is None:
3217 ui.warn(
3221 ui.warn(
3218 _(b'note: graft of %d:%s created no changes to commit\n')
3222 _(b'note: graft of %d:%s created no changes to commit\n')
3219 % (ctx.rev(), ctx)
3223 % (ctx.rev(), ctx)
3220 )
3224 )
3221 # checking that newnodes exist because old state files won't have it
3225 # checking that newnodes exist because old state files won't have it
3222 elif statedata.get(b'newnodes') is not None:
3226 elif statedata.get(b'newnodes') is not None:
3223 statedata[b'newnodes'].append(node)
3227 statedata[b'newnodes'].append(node)
3224
3228
3225 # remove state when we complete successfully
3229 # remove state when we complete successfully
3226 if not opts.get(b'dry_run'):
3230 if not opts.get(b'dry_run'):
3227 graftstate.delete()
3231 graftstate.delete()
3228
3232
3229 return 0
3233 return 0
3230
3234
3231
3235
3232 def _stopgraft(ui, repo, graftstate):
3236 def _stopgraft(ui, repo, graftstate):
3233 """stop the interrupted graft"""
3237 """stop the interrupted graft"""
3234 if not graftstate.exists():
3238 if not graftstate.exists():
3235 raise error.Abort(_(b"no interrupted graft found"))
3239 raise error.Abort(_(b"no interrupted graft found"))
3236 pctx = repo[b'.']
3240 pctx = repo[b'.']
3237 hg.updaterepo(repo, pctx.node(), overwrite=True)
3241 hg.updaterepo(repo, pctx.node(), overwrite=True)
3238 graftstate.delete()
3242 graftstate.delete()
3239 ui.status(_(b"stopped the interrupted graft\n"))
3243 ui.status(_(b"stopped the interrupted graft\n"))
3240 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3244 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3241 return 0
3245 return 0
3242
3246
3243
3247
3244 statemod.addunfinished(
3248 statemod.addunfinished(
3245 b'graft',
3249 b'graft',
3246 fname=b'graftstate',
3250 fname=b'graftstate',
3247 clearable=True,
3251 clearable=True,
3248 stopflag=True,
3252 stopflag=True,
3249 continueflag=True,
3253 continueflag=True,
3250 abortfunc=cmdutil.hgabortgraft,
3254 abortfunc=cmdutil.hgabortgraft,
3251 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3255 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3252 )
3256 )
3253
3257
3254
3258
3255 @command(
3259 @command(
3256 b'grep',
3260 b'grep',
3257 [
3261 [
3258 (b'0', b'print0', None, _(b'end fields with NUL')),
3262 (b'0', b'print0', None, _(b'end fields with NUL')),
3259 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3263 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3260 (
3264 (
3261 b'',
3265 b'',
3262 b'diff',
3266 b'diff',
3263 None,
3267 None,
3264 _(
3268 _(
3265 b'search revision differences for when the pattern was added '
3269 b'search revision differences for when the pattern was added '
3266 b'or removed'
3270 b'or removed'
3267 ),
3271 ),
3268 ),
3272 ),
3269 (b'a', b'text', None, _(b'treat all files as text')),
3273 (b'a', b'text', None, _(b'treat all files as text')),
3270 (
3274 (
3271 b'f',
3275 b'f',
3272 b'follow',
3276 b'follow',
3273 None,
3277 None,
3274 _(
3278 _(
3275 b'follow changeset history,'
3279 b'follow changeset history,'
3276 b' or file history across copies and renames'
3280 b' or file history across copies and renames'
3277 ),
3281 ),
3278 ),
3282 ),
3279 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3283 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3280 (
3284 (
3281 b'l',
3285 b'l',
3282 b'files-with-matches',
3286 b'files-with-matches',
3283 None,
3287 None,
3284 _(b'print only filenames and revisions that match'),
3288 _(b'print only filenames and revisions that match'),
3285 ),
3289 ),
3286 (b'n', b'line-number', None, _(b'print matching line numbers')),
3290 (b'n', b'line-number', None, _(b'print matching line numbers')),
3287 (
3291 (
3288 b'r',
3292 b'r',
3289 b'rev',
3293 b'rev',
3290 [],
3294 [],
3291 _(b'search files changed within revision range'),
3295 _(b'search files changed within revision range'),
3292 _(b'REV'),
3296 _(b'REV'),
3293 ),
3297 ),
3294 (
3298 (
3295 b'',
3299 b'',
3296 b'all-files',
3300 b'all-files',
3297 None,
3301 None,
3298 _(
3302 _(
3299 b'include all files in the changeset while grepping (DEPRECATED)'
3303 b'include all files in the changeset while grepping (DEPRECATED)'
3300 ),
3304 ),
3301 ),
3305 ),
3302 (b'u', b'user', None, _(b'list the author (long with -v)')),
3306 (b'u', b'user', None, _(b'list the author (long with -v)')),
3303 (b'd', b'date', None, _(b'list the date (short with -q)')),
3307 (b'd', b'date', None, _(b'list the date (short with -q)')),
3304 ]
3308 ]
3305 + formatteropts
3309 + formatteropts
3306 + walkopts,
3310 + walkopts,
3307 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3311 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3308 helpcategory=command.CATEGORY_FILE_CONTENTS,
3312 helpcategory=command.CATEGORY_FILE_CONTENTS,
3309 inferrepo=True,
3313 inferrepo=True,
3310 intents={INTENT_READONLY},
3314 intents={INTENT_READONLY},
3311 )
3315 )
3312 def grep(ui, repo, pattern, *pats, **opts):
3316 def grep(ui, repo, pattern, *pats, **opts):
3313 """search for a pattern in specified files
3317 """search for a pattern in specified files
3314
3318
3315 Search the working directory or revision history for a regular
3319 Search the working directory or revision history for a regular
3316 expression in the specified files for the entire repository.
3320 expression in the specified files for the entire repository.
3317
3321
3318 By default, grep searches the repository files in the working
3322 By default, grep searches the repository files in the working
3319 directory and prints the files where it finds a match. To specify
3323 directory and prints the files where it finds a match. To specify
3320 historical revisions instead of the working directory, use the
3324 historical revisions instead of the working directory, use the
3321 --rev flag.
3325 --rev flag.
3322
3326
3323 To search instead historical revision differences that contains a
3327 To search instead historical revision differences that contains a
3324 change in match status ("-" for a match that becomes a non-match,
3328 change in match status ("-" for a match that becomes a non-match,
3325 or "+" for a non-match that becomes a match), use the --diff flag.
3329 or "+" for a non-match that becomes a match), use the --diff flag.
3326
3330
3327 PATTERN can be any Python (roughly Perl-compatible) regular
3331 PATTERN can be any Python (roughly Perl-compatible) regular
3328 expression.
3332 expression.
3329
3333
3330 If no FILEs are specified and the --rev flag isn't supplied, all
3334 If no FILEs are specified and the --rev flag isn't supplied, all
3331 files in the working directory are searched. When using the --rev
3335 files in the working directory are searched. When using the --rev
3332 flag and specifying FILEs, use the --follow argument to also
3336 flag and specifying FILEs, use the --follow argument to also
3333 follow the specified FILEs across renames and copies.
3337 follow the specified FILEs across renames and copies.
3334
3338
3335 .. container:: verbose
3339 .. container:: verbose
3336
3340
3337 Template:
3341 Template:
3338
3342
3339 The following keywords are supported in addition to the common template
3343 The following keywords are supported in addition to the common template
3340 keywords and functions. See also :hg:`help templates`.
3344 keywords and functions. See also :hg:`help templates`.
3341
3345
3342 :change: String. Character denoting insertion ``+`` or removal ``-``.
3346 :change: String. Character denoting insertion ``+`` or removal ``-``.
3343 Available if ``--diff`` is specified.
3347 Available if ``--diff`` is specified.
3344 :lineno: Integer. Line number of the match.
3348 :lineno: Integer. Line number of the match.
3345 :path: String. Repository-absolute path of the file.
3349 :path: String. Repository-absolute path of the file.
3346 :texts: List of text chunks.
3350 :texts: List of text chunks.
3347
3351
3348 And each entry of ``{texts}`` provides the following sub-keywords.
3352 And each entry of ``{texts}`` provides the following sub-keywords.
3349
3353
3350 :matched: Boolean. True if the chunk matches the specified pattern.
3354 :matched: Boolean. True if the chunk matches the specified pattern.
3351 :text: String. Chunk content.
3355 :text: String. Chunk content.
3352
3356
3353 See :hg:`help templates.operators` for the list expansion syntax.
3357 See :hg:`help templates.operators` for the list expansion syntax.
3354
3358
3355 Returns 0 if a match is found, 1 otherwise.
3359 Returns 0 if a match is found, 1 otherwise.
3356
3360
3357 """
3361 """
3358 opts = pycompat.byteskwargs(opts)
3362 opts = pycompat.byteskwargs(opts)
3359 diff = opts.get(b'all') or opts.get(b'diff')
3363 diff = opts.get(b'all') or opts.get(b'diff')
3360 if diff and opts.get(b'all_files'):
3364 if diff and opts.get(b'all_files'):
3361 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3365 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3362 if opts.get(b'all_files') is None and not diff:
3366 if opts.get(b'all_files') is None and not diff:
3363 opts[b'all_files'] = True
3367 opts[b'all_files'] = True
3364 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3368 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3365 all_files = opts.get(b'all_files')
3369 all_files = opts.get(b'all_files')
3366 if plaingrep:
3370 if plaingrep:
3367 opts[b'rev'] = [b'wdir()']
3371 opts[b'rev'] = [b'wdir()']
3368
3372
3369 reflags = re.M
3373 reflags = re.M
3370 if opts.get(b'ignore_case'):
3374 if opts.get(b'ignore_case'):
3371 reflags |= re.I
3375 reflags |= re.I
3372 try:
3376 try:
3373 regexp = util.re.compile(pattern, reflags)
3377 regexp = util.re.compile(pattern, reflags)
3374 except re.error as inst:
3378 except re.error as inst:
3375 ui.warn(
3379 ui.warn(
3376 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3380 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3377 )
3381 )
3378 return 1
3382 return 1
3379 sep, eol = b':', b'\n'
3383 sep, eol = b':', b'\n'
3380 if opts.get(b'print0'):
3384 if opts.get(b'print0'):
3381 sep = eol = b'\0'
3385 sep = eol = b'\0'
3382
3386
3383 getfile = util.lrucachefunc(repo.file)
3387 getfile = util.lrucachefunc(repo.file)
3384
3388
3385 def matchlines(body):
3389 def matchlines(body):
3386 begin = 0
3390 begin = 0
3387 linenum = 0
3391 linenum = 0
3388 while begin < len(body):
3392 while begin < len(body):
3389 match = regexp.search(body, begin)
3393 match = regexp.search(body, begin)
3390 if not match:
3394 if not match:
3391 break
3395 break
3392 mstart, mend = match.span()
3396 mstart, mend = match.span()
3393 linenum += body.count(b'\n', begin, mstart) + 1
3397 linenum += body.count(b'\n', begin, mstart) + 1
3394 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3398 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3395 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3399 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3396 lend = begin - 1
3400 lend = begin - 1
3397 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3401 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3398
3402
3399 class linestate(object):
3403 class linestate(object):
3400 def __init__(self, line, linenum, colstart, colend):
3404 def __init__(self, line, linenum, colstart, colend):
3401 self.line = line
3405 self.line = line
3402 self.linenum = linenum
3406 self.linenum = linenum
3403 self.colstart = colstart
3407 self.colstart = colstart
3404 self.colend = colend
3408 self.colend = colend
3405
3409
3406 def __hash__(self):
3410 def __hash__(self):
3407 return hash((self.linenum, self.line))
3411 return hash((self.linenum, self.line))
3408
3412
3409 def __eq__(self, other):
3413 def __eq__(self, other):
3410 return self.line == other.line
3414 return self.line == other.line
3411
3415
3412 def findpos(self):
3416 def findpos(self):
3413 """Iterate all (start, end) indices of matches"""
3417 """Iterate all (start, end) indices of matches"""
3414 yield self.colstart, self.colend
3418 yield self.colstart, self.colend
3415 p = self.colend
3419 p = self.colend
3416 while p < len(self.line):
3420 while p < len(self.line):
3417 m = regexp.search(self.line, p)
3421 m = regexp.search(self.line, p)
3418 if not m:
3422 if not m:
3419 break
3423 break
3420 yield m.span()
3424 yield m.span()
3421 p = m.end()
3425 p = m.end()
3422
3426
3423 matches = {}
3427 matches = {}
3424 copies = {}
3428 copies = {}
3425
3429
3426 def grepbody(fn, rev, body):
3430 def grepbody(fn, rev, body):
3427 matches[rev].setdefault(fn, [])
3431 matches[rev].setdefault(fn, [])
3428 m = matches[rev][fn]
3432 m = matches[rev][fn]
3429 if body is None:
3433 if body is None:
3430 return
3434 return
3431
3435
3432 for lnum, cstart, cend, line in matchlines(body):
3436 for lnum, cstart, cend, line in matchlines(body):
3433 s = linestate(line, lnum, cstart, cend)
3437 s = linestate(line, lnum, cstart, cend)
3434 m.append(s)
3438 m.append(s)
3435
3439
3436 def difflinestates(a, b):
3440 def difflinestates(a, b):
3437 sm = difflib.SequenceMatcher(None, a, b)
3441 sm = difflib.SequenceMatcher(None, a, b)
3438 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3442 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3439 if tag == 'insert':
3443 if tag == 'insert':
3440 for i in pycompat.xrange(blo, bhi):
3444 for i in pycompat.xrange(blo, bhi):
3441 yield (b'+', b[i])
3445 yield (b'+', b[i])
3442 elif tag == 'delete':
3446 elif tag == 'delete':
3443 for i in pycompat.xrange(alo, ahi):
3447 for i in pycompat.xrange(alo, ahi):
3444 yield (b'-', a[i])
3448 yield (b'-', a[i])
3445 elif tag == 'replace':
3449 elif tag == 'replace':
3446 for i in pycompat.xrange(alo, ahi):
3450 for i in pycompat.xrange(alo, ahi):
3447 yield (b'-', a[i])
3451 yield (b'-', a[i])
3448 for i in pycompat.xrange(blo, bhi):
3452 for i in pycompat.xrange(blo, bhi):
3449 yield (b'+', b[i])
3453 yield (b'+', b[i])
3450
3454
3451 uipathfn = scmutil.getuipathfn(repo)
3455 uipathfn = scmutil.getuipathfn(repo)
3452
3456
3453 def display(fm, fn, ctx, pstates, states):
3457 def display(fm, fn, ctx, pstates, states):
3454 rev = scmutil.intrev(ctx)
3458 rev = scmutil.intrev(ctx)
3455 if fm.isplain():
3459 if fm.isplain():
3456 formatuser = ui.shortuser
3460 formatuser = ui.shortuser
3457 else:
3461 else:
3458 formatuser = pycompat.bytestr
3462 formatuser = pycompat.bytestr
3459 if ui.quiet:
3463 if ui.quiet:
3460 datefmt = b'%Y-%m-%d'
3464 datefmt = b'%Y-%m-%d'
3461 else:
3465 else:
3462 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3466 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3463 found = False
3467 found = False
3464
3468
3465 @util.cachefunc
3469 @util.cachefunc
3466 def binary():
3470 def binary():
3467 flog = getfile(fn)
3471 flog = getfile(fn)
3468 try:
3472 try:
3469 return stringutil.binary(flog.read(ctx.filenode(fn)))
3473 return stringutil.binary(flog.read(ctx.filenode(fn)))
3470 except error.WdirUnsupported:
3474 except error.WdirUnsupported:
3471 return ctx[fn].isbinary()
3475 return ctx[fn].isbinary()
3472
3476
3473 fieldnamemap = {b'linenumber': b'lineno'}
3477 fieldnamemap = {b'linenumber': b'lineno'}
3474 if diff:
3478 if diff:
3475 iter = difflinestates(pstates, states)
3479 iter = difflinestates(pstates, states)
3476 else:
3480 else:
3477 iter = [(b'', l) for l in states]
3481 iter = [(b'', l) for l in states]
3478 for change, l in iter:
3482 for change, l in iter:
3479 fm.startitem()
3483 fm.startitem()
3480 fm.context(ctx=ctx)
3484 fm.context(ctx=ctx)
3481 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3485 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3482 fm.plain(uipathfn(fn), label=b'grep.filename')
3486 fm.plain(uipathfn(fn), label=b'grep.filename')
3483
3487
3484 cols = [
3488 cols = [
3485 (b'rev', b'%d', rev, not plaingrep, b''),
3489 (b'rev', b'%d', rev, not plaingrep, b''),
3486 (
3490 (
3487 b'linenumber',
3491 b'linenumber',
3488 b'%d',
3492 b'%d',
3489 l.linenum,
3493 l.linenum,
3490 opts.get(b'line_number'),
3494 opts.get(b'line_number'),
3491 b'',
3495 b'',
3492 ),
3496 ),
3493 ]
3497 ]
3494 if diff:
3498 if diff:
3495 cols.append(
3499 cols.append(
3496 (
3500 (
3497 b'change',
3501 b'change',
3498 b'%s',
3502 b'%s',
3499 change,
3503 change,
3500 True,
3504 True,
3501 b'grep.inserted '
3505 b'grep.inserted '
3502 if change == b'+'
3506 if change == b'+'
3503 else b'grep.deleted ',
3507 else b'grep.deleted ',
3504 )
3508 )
3505 )
3509 )
3506 cols.extend(
3510 cols.extend(
3507 [
3511 [
3508 (
3512 (
3509 b'user',
3513 b'user',
3510 b'%s',
3514 b'%s',
3511 formatuser(ctx.user()),
3515 formatuser(ctx.user()),
3512 opts.get(b'user'),
3516 opts.get(b'user'),
3513 b'',
3517 b'',
3514 ),
3518 ),
3515 (
3519 (
3516 b'date',
3520 b'date',
3517 b'%s',
3521 b'%s',
3518 fm.formatdate(ctx.date(), datefmt),
3522 fm.formatdate(ctx.date(), datefmt),
3519 opts.get(b'date'),
3523 opts.get(b'date'),
3520 b'',
3524 b'',
3521 ),
3525 ),
3522 ]
3526 ]
3523 )
3527 )
3524 for name, fmt, data, cond, extra_label in cols:
3528 for name, fmt, data, cond, extra_label in cols:
3525 if cond:
3529 if cond:
3526 fm.plain(sep, label=b'grep.sep')
3530 fm.plain(sep, label=b'grep.sep')
3527 field = fieldnamemap.get(name, name)
3531 field = fieldnamemap.get(name, name)
3528 label = extra_label + (b'grep.%s' % name)
3532 label = extra_label + (b'grep.%s' % name)
3529 fm.condwrite(cond, field, fmt, data, label=label)
3533 fm.condwrite(cond, field, fmt, data, label=label)
3530 if not opts.get(b'files_with_matches'):
3534 if not opts.get(b'files_with_matches'):
3531 fm.plain(sep, label=b'grep.sep')
3535 fm.plain(sep, label=b'grep.sep')
3532 if not opts.get(b'text') and binary():
3536 if not opts.get(b'text') and binary():
3533 fm.plain(_(b" Binary file matches"))
3537 fm.plain(_(b" Binary file matches"))
3534 else:
3538 else:
3535 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3539 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3536 fm.plain(eol)
3540 fm.plain(eol)
3537 found = True
3541 found = True
3538 if opts.get(b'files_with_matches'):
3542 if opts.get(b'files_with_matches'):
3539 break
3543 break
3540 return found
3544 return found
3541
3545
3542 def displaymatches(fm, l):
3546 def displaymatches(fm, l):
3543 p = 0
3547 p = 0
3544 for s, e in l.findpos():
3548 for s, e in l.findpos():
3545 if p < s:
3549 if p < s:
3546 fm.startitem()
3550 fm.startitem()
3547 fm.write(b'text', b'%s', l.line[p:s])
3551 fm.write(b'text', b'%s', l.line[p:s])
3548 fm.data(matched=False)
3552 fm.data(matched=False)
3549 fm.startitem()
3553 fm.startitem()
3550 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3554 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3551 fm.data(matched=True)
3555 fm.data(matched=True)
3552 p = e
3556 p = e
3553 if p < len(l.line):
3557 if p < len(l.line):
3554 fm.startitem()
3558 fm.startitem()
3555 fm.write(b'text', b'%s', l.line[p:])
3559 fm.write(b'text', b'%s', l.line[p:])
3556 fm.data(matched=False)
3560 fm.data(matched=False)
3557 fm.end()
3561 fm.end()
3558
3562
3559 skip = set()
3563 skip = set()
3560 revfiles = {}
3564 revfiles = {}
3561 match = scmutil.match(repo[None], pats, opts)
3565 match = scmutil.match(repo[None], pats, opts)
3562 found = False
3566 found = False
3563 follow = opts.get(b'follow')
3567 follow = opts.get(b'follow')
3564
3568
3565 getrenamed = scmutil.getrenamedfn(repo)
3569 getrenamed = scmutil.getrenamedfn(repo)
3566
3570
3567 def get_file_content(filename, filelog, filenode, context, revision):
3571 def get_file_content(filename, filelog, filenode, context, revision):
3568 try:
3572 try:
3569 content = filelog.read(filenode)
3573 content = filelog.read(filenode)
3570 except error.WdirUnsupported:
3574 except error.WdirUnsupported:
3571 content = context[filename].data()
3575 content = context[filename].data()
3572 except error.CensoredNodeError:
3576 except error.CensoredNodeError:
3573 content = None
3577 content = None
3574 ui.warn(
3578 ui.warn(
3575 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3579 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3576 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3580 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3577 )
3581 )
3578 return content
3582 return content
3579
3583
3580 def prep(ctx, fns):
3584 def prep(ctx, fns):
3581 rev = ctx.rev()
3585 rev = ctx.rev()
3582 pctx = ctx.p1()
3586 pctx = ctx.p1()
3583 parent = pctx.rev()
3587 parent = pctx.rev()
3584 matches.setdefault(rev, {})
3588 matches.setdefault(rev, {})
3585 matches.setdefault(parent, {})
3589 matches.setdefault(parent, {})
3586 files = revfiles.setdefault(rev, [])
3590 files = revfiles.setdefault(rev, [])
3587 for fn in fns:
3591 for fn in fns:
3588 flog = getfile(fn)
3592 flog = getfile(fn)
3589 try:
3593 try:
3590 fnode = ctx.filenode(fn)
3594 fnode = ctx.filenode(fn)
3591 except error.LookupError:
3595 except error.LookupError:
3592 continue
3596 continue
3593
3597
3594 copy = None
3598 copy = None
3595 if follow:
3599 if follow:
3596 copy = getrenamed(fn, rev)
3600 copy = getrenamed(fn, rev)
3597 if copy:
3601 if copy:
3598 copies.setdefault(rev, {})[fn] = copy
3602 copies.setdefault(rev, {})[fn] = copy
3599 if fn in skip:
3603 if fn in skip:
3600 skip.add(copy)
3604 skip.add(copy)
3601 if fn in skip:
3605 if fn in skip:
3602 continue
3606 continue
3603 files.append(fn)
3607 files.append(fn)
3604
3608
3605 if fn not in matches[rev]:
3609 if fn not in matches[rev]:
3606 content = get_file_content(fn, flog, fnode, ctx, rev)
3610 content = get_file_content(fn, flog, fnode, ctx, rev)
3607 grepbody(fn, rev, content)
3611 grepbody(fn, rev, content)
3608
3612
3609 pfn = copy or fn
3613 pfn = copy or fn
3610 if pfn not in matches[parent]:
3614 if pfn not in matches[parent]:
3611 try:
3615 try:
3612 pfnode = pctx.filenode(pfn)
3616 pfnode = pctx.filenode(pfn)
3613 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3617 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3614 grepbody(pfn, parent, pcontent)
3618 grepbody(pfn, parent, pcontent)
3615 except error.LookupError:
3619 except error.LookupError:
3616 pass
3620 pass
3617
3621
3618 ui.pager(b'grep')
3622 ui.pager(b'grep')
3619 fm = ui.formatter(b'grep', opts)
3623 fm = ui.formatter(b'grep', opts)
3620 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3624 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3621 rev = ctx.rev()
3625 rev = ctx.rev()
3622 parent = ctx.p1().rev()
3626 parent = ctx.p1().rev()
3623 for fn in sorted(revfiles.get(rev, [])):
3627 for fn in sorted(revfiles.get(rev, [])):
3624 states = matches[rev][fn]
3628 states = matches[rev][fn]
3625 copy = copies.get(rev, {}).get(fn)
3629 copy = copies.get(rev, {}).get(fn)
3626 if fn in skip:
3630 if fn in skip:
3627 if copy:
3631 if copy:
3628 skip.add(copy)
3632 skip.add(copy)
3629 continue
3633 continue
3630 pstates = matches.get(parent, {}).get(copy or fn, [])
3634 pstates = matches.get(parent, {}).get(copy or fn, [])
3631 if pstates or states:
3635 if pstates or states:
3632 r = display(fm, fn, ctx, pstates, states)
3636 r = display(fm, fn, ctx, pstates, states)
3633 found = found or r
3637 found = found or r
3634 if r and not diff and not all_files:
3638 if r and not diff and not all_files:
3635 skip.add(fn)
3639 skip.add(fn)
3636 if copy:
3640 if copy:
3637 skip.add(copy)
3641 skip.add(copy)
3638 del revfiles[rev]
3642 del revfiles[rev]
3639 # We will keep the matches dict for the duration of the window
3643 # We will keep the matches dict for the duration of the window
3640 # clear the matches dict once the window is over
3644 # clear the matches dict once the window is over
3641 if not revfiles:
3645 if not revfiles:
3642 matches.clear()
3646 matches.clear()
3643 fm.end()
3647 fm.end()
3644
3648
3645 return not found
3649 return not found
3646
3650
3647
3651
3648 @command(
3652 @command(
3649 b'heads',
3653 b'heads',
3650 [
3654 [
3651 (
3655 (
3652 b'r',
3656 b'r',
3653 b'rev',
3657 b'rev',
3654 b'',
3658 b'',
3655 _(b'show only heads which are descendants of STARTREV'),
3659 _(b'show only heads which are descendants of STARTREV'),
3656 _(b'STARTREV'),
3660 _(b'STARTREV'),
3657 ),
3661 ),
3658 (b't', b'topo', False, _(b'show topological heads only')),
3662 (b't', b'topo', False, _(b'show topological heads only')),
3659 (
3663 (
3660 b'a',
3664 b'a',
3661 b'active',
3665 b'active',
3662 False,
3666 False,
3663 _(b'show active branchheads only (DEPRECATED)'),
3667 _(b'show active branchheads only (DEPRECATED)'),
3664 ),
3668 ),
3665 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3669 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3666 ]
3670 ]
3667 + templateopts,
3671 + templateopts,
3668 _(b'[-ct] [-r STARTREV] [REV]...'),
3672 _(b'[-ct] [-r STARTREV] [REV]...'),
3669 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3673 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3670 intents={INTENT_READONLY},
3674 intents={INTENT_READONLY},
3671 )
3675 )
3672 def heads(ui, repo, *branchrevs, **opts):
3676 def heads(ui, repo, *branchrevs, **opts):
3673 """show branch heads
3677 """show branch heads
3674
3678
3675 With no arguments, show all open branch heads in the repository.
3679 With no arguments, show all open branch heads in the repository.
3676 Branch heads are changesets that have no descendants on the
3680 Branch heads are changesets that have no descendants on the
3677 same branch. They are where development generally takes place and
3681 same branch. They are where development generally takes place and
3678 are the usual targets for update and merge operations.
3682 are the usual targets for update and merge operations.
3679
3683
3680 If one or more REVs are given, only open branch heads on the
3684 If one or more REVs are given, only open branch heads on the
3681 branches associated with the specified changesets are shown. This
3685 branches associated with the specified changesets are shown. This
3682 means that you can use :hg:`heads .` to see the heads on the
3686 means that you can use :hg:`heads .` to see the heads on the
3683 currently checked-out branch.
3687 currently checked-out branch.
3684
3688
3685 If -c/--closed is specified, also show branch heads marked closed
3689 If -c/--closed is specified, also show branch heads marked closed
3686 (see :hg:`commit --close-branch`).
3690 (see :hg:`commit --close-branch`).
3687
3691
3688 If STARTREV is specified, only those heads that are descendants of
3692 If STARTREV is specified, only those heads that are descendants of
3689 STARTREV will be displayed.
3693 STARTREV will be displayed.
3690
3694
3691 If -t/--topo is specified, named branch mechanics will be ignored and only
3695 If -t/--topo is specified, named branch mechanics will be ignored and only
3692 topological heads (changesets with no children) will be shown.
3696 topological heads (changesets with no children) will be shown.
3693
3697
3694 Returns 0 if matching heads are found, 1 if not.
3698 Returns 0 if matching heads are found, 1 if not.
3695 """
3699 """
3696
3700
3697 opts = pycompat.byteskwargs(opts)
3701 opts = pycompat.byteskwargs(opts)
3698 start = None
3702 start = None
3699 rev = opts.get(b'rev')
3703 rev = opts.get(b'rev')
3700 if rev:
3704 if rev:
3701 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3705 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3702 start = scmutil.revsingle(repo, rev, None).node()
3706 start = scmutil.revsingle(repo, rev, None).node()
3703
3707
3704 if opts.get(b'topo'):
3708 if opts.get(b'topo'):
3705 heads = [repo[h] for h in repo.heads(start)]
3709 heads = [repo[h] for h in repo.heads(start)]
3706 else:
3710 else:
3707 heads = []
3711 heads = []
3708 for branch in repo.branchmap():
3712 for branch in repo.branchmap():
3709 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3713 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3710 heads = [repo[h] for h in heads]
3714 heads = [repo[h] for h in heads]
3711
3715
3712 if branchrevs:
3716 if branchrevs:
3713 branches = set(
3717 branches = set(
3714 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3718 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3715 )
3719 )
3716 heads = [h for h in heads if h.branch() in branches]
3720 heads = [h for h in heads if h.branch() in branches]
3717
3721
3718 if opts.get(b'active') and branchrevs:
3722 if opts.get(b'active') and branchrevs:
3719 dagheads = repo.heads(start)
3723 dagheads = repo.heads(start)
3720 heads = [h for h in heads if h.node() in dagheads]
3724 heads = [h for h in heads if h.node() in dagheads]
3721
3725
3722 if branchrevs:
3726 if branchrevs:
3723 haveheads = set(h.branch() for h in heads)
3727 haveheads = set(h.branch() for h in heads)
3724 if branches - haveheads:
3728 if branches - haveheads:
3725 headless = b', '.join(b for b in branches - haveheads)
3729 headless = b', '.join(b for b in branches - haveheads)
3726 msg = _(b'no open branch heads found on branches %s')
3730 msg = _(b'no open branch heads found on branches %s')
3727 if opts.get(b'rev'):
3731 if opts.get(b'rev'):
3728 msg += _(b' (started at %s)') % opts[b'rev']
3732 msg += _(b' (started at %s)') % opts[b'rev']
3729 ui.warn((msg + b'\n') % headless)
3733 ui.warn((msg + b'\n') % headless)
3730
3734
3731 if not heads:
3735 if not heads:
3732 return 1
3736 return 1
3733
3737
3734 ui.pager(b'heads')
3738 ui.pager(b'heads')
3735 heads = sorted(heads, key=lambda x: -(x.rev()))
3739 heads = sorted(heads, key=lambda x: -(x.rev()))
3736 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3740 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3737 for ctx in heads:
3741 for ctx in heads:
3738 displayer.show(ctx)
3742 displayer.show(ctx)
3739 displayer.close()
3743 displayer.close()
3740
3744
3741
3745
3742 @command(
3746 @command(
3743 b'help',
3747 b'help',
3744 [
3748 [
3745 (b'e', b'extension', None, _(b'show only help for extensions')),
3749 (b'e', b'extension', None, _(b'show only help for extensions')),
3746 (b'c', b'command', None, _(b'show only help for commands')),
3750 (b'c', b'command', None, _(b'show only help for commands')),
3747 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3751 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3748 (
3752 (
3749 b's',
3753 b's',
3750 b'system',
3754 b'system',
3751 [],
3755 [],
3752 _(b'show help for specific platform(s)'),
3756 _(b'show help for specific platform(s)'),
3753 _(b'PLATFORM'),
3757 _(b'PLATFORM'),
3754 ),
3758 ),
3755 ],
3759 ],
3756 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3760 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3757 helpcategory=command.CATEGORY_HELP,
3761 helpcategory=command.CATEGORY_HELP,
3758 norepo=True,
3762 norepo=True,
3759 intents={INTENT_READONLY},
3763 intents={INTENT_READONLY},
3760 )
3764 )
3761 def help_(ui, name=None, **opts):
3765 def help_(ui, name=None, **opts):
3762 """show help for a given topic or a help overview
3766 """show help for a given topic or a help overview
3763
3767
3764 With no arguments, print a list of commands with short help messages.
3768 With no arguments, print a list of commands with short help messages.
3765
3769
3766 Given a topic, extension, or command name, print help for that
3770 Given a topic, extension, or command name, print help for that
3767 topic.
3771 topic.
3768
3772
3769 Returns 0 if successful.
3773 Returns 0 if successful.
3770 """
3774 """
3771
3775
3772 keep = opts.get('system') or []
3776 keep = opts.get('system') or []
3773 if len(keep) == 0:
3777 if len(keep) == 0:
3774 if pycompat.sysplatform.startswith(b'win'):
3778 if pycompat.sysplatform.startswith(b'win'):
3775 keep.append(b'windows')
3779 keep.append(b'windows')
3776 elif pycompat.sysplatform == b'OpenVMS':
3780 elif pycompat.sysplatform == b'OpenVMS':
3777 keep.append(b'vms')
3781 keep.append(b'vms')
3778 elif pycompat.sysplatform == b'plan9':
3782 elif pycompat.sysplatform == b'plan9':
3779 keep.append(b'plan9')
3783 keep.append(b'plan9')
3780 else:
3784 else:
3781 keep.append(b'unix')
3785 keep.append(b'unix')
3782 keep.append(pycompat.sysplatform.lower())
3786 keep.append(pycompat.sysplatform.lower())
3783 if ui.verbose:
3787 if ui.verbose:
3784 keep.append(b'verbose')
3788 keep.append(b'verbose')
3785
3789
3786 commands = sys.modules[__name__]
3790 commands = sys.modules[__name__]
3787 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3791 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3788 ui.pager(b'help')
3792 ui.pager(b'help')
3789 ui.write(formatted)
3793 ui.write(formatted)
3790
3794
3791
3795
3792 @command(
3796 @command(
3793 b'identify|id',
3797 b'identify|id',
3794 [
3798 [
3795 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3799 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3796 (b'n', b'num', None, _(b'show local revision number')),
3800 (b'n', b'num', None, _(b'show local revision number')),
3797 (b'i', b'id', None, _(b'show global revision id')),
3801 (b'i', b'id', None, _(b'show global revision id')),
3798 (b'b', b'branch', None, _(b'show branch')),
3802 (b'b', b'branch', None, _(b'show branch')),
3799 (b't', b'tags', None, _(b'show tags')),
3803 (b't', b'tags', None, _(b'show tags')),
3800 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3804 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3801 ]
3805 ]
3802 + remoteopts
3806 + remoteopts
3803 + formatteropts,
3807 + formatteropts,
3804 _(b'[-nibtB] [-r REV] [SOURCE]'),
3808 _(b'[-nibtB] [-r REV] [SOURCE]'),
3805 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3809 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3806 optionalrepo=True,
3810 optionalrepo=True,
3807 intents={INTENT_READONLY},
3811 intents={INTENT_READONLY},
3808 )
3812 )
3809 def identify(
3813 def identify(
3810 ui,
3814 ui,
3811 repo,
3815 repo,
3812 source=None,
3816 source=None,
3813 rev=None,
3817 rev=None,
3814 num=None,
3818 num=None,
3815 id=None,
3819 id=None,
3816 branch=None,
3820 branch=None,
3817 tags=None,
3821 tags=None,
3818 bookmarks=None,
3822 bookmarks=None,
3819 **opts
3823 **opts
3820 ):
3824 ):
3821 """identify the working directory or specified revision
3825 """identify the working directory or specified revision
3822
3826
3823 Print a summary identifying the repository state at REV using one or
3827 Print a summary identifying the repository state at REV using one or
3824 two parent hash identifiers, followed by a "+" if the working
3828 two parent hash identifiers, followed by a "+" if the working
3825 directory has uncommitted changes, the branch name (if not default),
3829 directory has uncommitted changes, the branch name (if not default),
3826 a list of tags, and a list of bookmarks.
3830 a list of tags, and a list of bookmarks.
3827
3831
3828 When REV is not given, print a summary of the current state of the
3832 When REV is not given, print a summary of the current state of the
3829 repository including the working directory. Specify -r. to get information
3833 repository including the working directory. Specify -r. to get information
3830 of the working directory parent without scanning uncommitted changes.
3834 of the working directory parent without scanning uncommitted changes.
3831
3835
3832 Specifying a path to a repository root or Mercurial bundle will
3836 Specifying a path to a repository root or Mercurial bundle will
3833 cause lookup to operate on that repository/bundle.
3837 cause lookup to operate on that repository/bundle.
3834
3838
3835 .. container:: verbose
3839 .. container:: verbose
3836
3840
3837 Template:
3841 Template:
3838
3842
3839 The following keywords are supported in addition to the common template
3843 The following keywords are supported in addition to the common template
3840 keywords and functions. See also :hg:`help templates`.
3844 keywords and functions. See also :hg:`help templates`.
3841
3845
3842 :dirty: String. Character ``+`` denoting if the working directory has
3846 :dirty: String. Character ``+`` denoting if the working directory has
3843 uncommitted changes.
3847 uncommitted changes.
3844 :id: String. One or two nodes, optionally followed by ``+``.
3848 :id: String. One or two nodes, optionally followed by ``+``.
3845 :parents: List of strings. Parent nodes of the changeset.
3849 :parents: List of strings. Parent nodes of the changeset.
3846
3850
3847 Examples:
3851 Examples:
3848
3852
3849 - generate a build identifier for the working directory::
3853 - generate a build identifier for the working directory::
3850
3854
3851 hg id --id > build-id.dat
3855 hg id --id > build-id.dat
3852
3856
3853 - find the revision corresponding to a tag::
3857 - find the revision corresponding to a tag::
3854
3858
3855 hg id -n -r 1.3
3859 hg id -n -r 1.3
3856
3860
3857 - check the most recent revision of a remote repository::
3861 - check the most recent revision of a remote repository::
3858
3862
3859 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3863 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3860
3864
3861 See :hg:`log` for generating more information about specific revisions,
3865 See :hg:`log` for generating more information about specific revisions,
3862 including full hash identifiers.
3866 including full hash identifiers.
3863
3867
3864 Returns 0 if successful.
3868 Returns 0 if successful.
3865 """
3869 """
3866
3870
3867 opts = pycompat.byteskwargs(opts)
3871 opts = pycompat.byteskwargs(opts)
3868 if not repo and not source:
3872 if not repo and not source:
3869 raise error.Abort(
3873 raise error.Abort(
3870 _(b"there is no Mercurial repository here (.hg not found)")
3874 _(b"there is no Mercurial repository here (.hg not found)")
3871 )
3875 )
3872
3876
3873 default = not (num or id or branch or tags or bookmarks)
3877 default = not (num or id or branch or tags or bookmarks)
3874 output = []
3878 output = []
3875 revs = []
3879 revs = []
3876
3880
3877 if source:
3881 if source:
3878 source, branches = hg.parseurl(ui.expandpath(source))
3882 source, branches = hg.parseurl(ui.expandpath(source))
3879 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3883 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3880 repo = peer.local()
3884 repo = peer.local()
3881 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3885 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3882
3886
3883 fm = ui.formatter(b'identify', opts)
3887 fm = ui.formatter(b'identify', opts)
3884 fm.startitem()
3888 fm.startitem()
3885
3889
3886 if not repo:
3890 if not repo:
3887 if num or branch or tags:
3891 if num or branch or tags:
3888 raise error.Abort(
3892 raise error.Abort(
3889 _(b"can't query remote revision number, branch, or tags")
3893 _(b"can't query remote revision number, branch, or tags")
3890 )
3894 )
3891 if not rev and revs:
3895 if not rev and revs:
3892 rev = revs[0]
3896 rev = revs[0]
3893 if not rev:
3897 if not rev:
3894 rev = b"tip"
3898 rev = b"tip"
3895
3899
3896 remoterev = peer.lookup(rev)
3900 remoterev = peer.lookup(rev)
3897 hexrev = fm.hexfunc(remoterev)
3901 hexrev = fm.hexfunc(remoterev)
3898 if default or id:
3902 if default or id:
3899 output = [hexrev]
3903 output = [hexrev]
3900 fm.data(id=hexrev)
3904 fm.data(id=hexrev)
3901
3905
3902 @util.cachefunc
3906 @util.cachefunc
3903 def getbms():
3907 def getbms():
3904 bms = []
3908 bms = []
3905
3909
3906 if b'bookmarks' in peer.listkeys(b'namespaces'):
3910 if b'bookmarks' in peer.listkeys(b'namespaces'):
3907 hexremoterev = hex(remoterev)
3911 hexremoterev = hex(remoterev)
3908 bms = [
3912 bms = [
3909 bm
3913 bm
3910 for bm, bmr in pycompat.iteritems(
3914 for bm, bmr in pycompat.iteritems(
3911 peer.listkeys(b'bookmarks')
3915 peer.listkeys(b'bookmarks')
3912 )
3916 )
3913 if bmr == hexremoterev
3917 if bmr == hexremoterev
3914 ]
3918 ]
3915
3919
3916 return sorted(bms)
3920 return sorted(bms)
3917
3921
3918 if fm.isplain():
3922 if fm.isplain():
3919 if bookmarks:
3923 if bookmarks:
3920 output.extend(getbms())
3924 output.extend(getbms())
3921 elif default and not ui.quiet:
3925 elif default and not ui.quiet:
3922 # multiple bookmarks for a single parent separated by '/'
3926 # multiple bookmarks for a single parent separated by '/'
3923 bm = b'/'.join(getbms())
3927 bm = b'/'.join(getbms())
3924 if bm:
3928 if bm:
3925 output.append(bm)
3929 output.append(bm)
3926 else:
3930 else:
3927 fm.data(node=hex(remoterev))
3931 fm.data(node=hex(remoterev))
3928 if bookmarks or b'bookmarks' in fm.datahint():
3932 if bookmarks or b'bookmarks' in fm.datahint():
3929 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3933 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3930 else:
3934 else:
3931 if rev:
3935 if rev:
3932 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3936 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3933 ctx = scmutil.revsingle(repo, rev, None)
3937 ctx = scmutil.revsingle(repo, rev, None)
3934
3938
3935 if ctx.rev() is None:
3939 if ctx.rev() is None:
3936 ctx = repo[None]
3940 ctx = repo[None]
3937 parents = ctx.parents()
3941 parents = ctx.parents()
3938 taglist = []
3942 taglist = []
3939 for p in parents:
3943 for p in parents:
3940 taglist.extend(p.tags())
3944 taglist.extend(p.tags())
3941
3945
3942 dirty = b""
3946 dirty = b""
3943 if ctx.dirty(missing=True, merge=False, branch=False):
3947 if ctx.dirty(missing=True, merge=False, branch=False):
3944 dirty = b'+'
3948 dirty = b'+'
3945 fm.data(dirty=dirty)
3949 fm.data(dirty=dirty)
3946
3950
3947 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3951 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3948 if default or id:
3952 if default or id:
3949 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3953 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3950 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3954 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3951
3955
3952 if num:
3956 if num:
3953 numoutput = [b"%d" % p.rev() for p in parents]
3957 numoutput = [b"%d" % p.rev() for p in parents]
3954 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3958 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3955
3959
3956 fm.data(
3960 fm.data(
3957 parents=fm.formatlist(
3961 parents=fm.formatlist(
3958 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3962 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3959 )
3963 )
3960 )
3964 )
3961 else:
3965 else:
3962 hexoutput = fm.hexfunc(ctx.node())
3966 hexoutput = fm.hexfunc(ctx.node())
3963 if default or id:
3967 if default or id:
3964 output = [hexoutput]
3968 output = [hexoutput]
3965 fm.data(id=hexoutput)
3969 fm.data(id=hexoutput)
3966
3970
3967 if num:
3971 if num:
3968 output.append(pycompat.bytestr(ctx.rev()))
3972 output.append(pycompat.bytestr(ctx.rev()))
3969 taglist = ctx.tags()
3973 taglist = ctx.tags()
3970
3974
3971 if default and not ui.quiet:
3975 if default and not ui.quiet:
3972 b = ctx.branch()
3976 b = ctx.branch()
3973 if b != b'default':
3977 if b != b'default':
3974 output.append(b"(%s)" % b)
3978 output.append(b"(%s)" % b)
3975
3979
3976 # multiple tags for a single parent separated by '/'
3980 # multiple tags for a single parent separated by '/'
3977 t = b'/'.join(taglist)
3981 t = b'/'.join(taglist)
3978 if t:
3982 if t:
3979 output.append(t)
3983 output.append(t)
3980
3984
3981 # multiple bookmarks for a single parent separated by '/'
3985 # multiple bookmarks for a single parent separated by '/'
3982 bm = b'/'.join(ctx.bookmarks())
3986 bm = b'/'.join(ctx.bookmarks())
3983 if bm:
3987 if bm:
3984 output.append(bm)
3988 output.append(bm)
3985 else:
3989 else:
3986 if branch:
3990 if branch:
3987 output.append(ctx.branch())
3991 output.append(ctx.branch())
3988
3992
3989 if tags:
3993 if tags:
3990 output.extend(taglist)
3994 output.extend(taglist)
3991
3995
3992 if bookmarks:
3996 if bookmarks:
3993 output.extend(ctx.bookmarks())
3997 output.extend(ctx.bookmarks())
3994
3998
3995 fm.data(node=ctx.hex())
3999 fm.data(node=ctx.hex())
3996 fm.data(branch=ctx.branch())
4000 fm.data(branch=ctx.branch())
3997 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4001 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3998 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4002 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3999 fm.context(ctx=ctx)
4003 fm.context(ctx=ctx)
4000
4004
4001 fm.plain(b"%s\n" % b' '.join(output))
4005 fm.plain(b"%s\n" % b' '.join(output))
4002 fm.end()
4006 fm.end()
4003
4007
4004
4008
4005 @command(
4009 @command(
4006 b'import|patch',
4010 b'import|patch',
4007 [
4011 [
4008 (
4012 (
4009 b'p',
4013 b'p',
4010 b'strip',
4014 b'strip',
4011 1,
4015 1,
4012 _(
4016 _(
4013 b'directory strip option for patch. This has the same '
4017 b'directory strip option for patch. This has the same '
4014 b'meaning as the corresponding patch option'
4018 b'meaning as the corresponding patch option'
4015 ),
4019 ),
4016 _(b'NUM'),
4020 _(b'NUM'),
4017 ),
4021 ),
4018 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4022 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4019 (b'', b'secret', None, _(b'use the secret phase for committing')),
4023 (b'', b'secret', None, _(b'use the secret phase for committing')),
4020 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4024 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4021 (
4025 (
4022 b'f',
4026 b'f',
4023 b'force',
4027 b'force',
4024 None,
4028 None,
4025 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4029 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4026 ),
4030 ),
4027 (
4031 (
4028 b'',
4032 b'',
4029 b'no-commit',
4033 b'no-commit',
4030 None,
4034 None,
4031 _(b"don't commit, just update the working directory"),
4035 _(b"don't commit, just update the working directory"),
4032 ),
4036 ),
4033 (
4037 (
4034 b'',
4038 b'',
4035 b'bypass',
4039 b'bypass',
4036 None,
4040 None,
4037 _(b"apply patch without touching the working directory"),
4041 _(b"apply patch without touching the working directory"),
4038 ),
4042 ),
4039 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4043 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4040 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4044 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4041 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4045 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4042 (
4046 (
4043 b'',
4047 b'',
4044 b'import-branch',
4048 b'import-branch',
4045 None,
4049 None,
4046 _(b'use any branch information in patch (implied by --exact)'),
4050 _(b'use any branch information in patch (implied by --exact)'),
4047 ),
4051 ),
4048 ]
4052 ]
4049 + commitopts
4053 + commitopts
4050 + commitopts2
4054 + commitopts2
4051 + similarityopts,
4055 + similarityopts,
4052 _(b'[OPTION]... PATCH...'),
4056 _(b'[OPTION]... PATCH...'),
4053 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4057 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4054 )
4058 )
4055 def import_(ui, repo, patch1=None, *patches, **opts):
4059 def import_(ui, repo, patch1=None, *patches, **opts):
4056 """import an ordered set of patches
4060 """import an ordered set of patches
4057
4061
4058 Import a list of patches and commit them individually (unless
4062 Import a list of patches and commit them individually (unless
4059 --no-commit is specified).
4063 --no-commit is specified).
4060
4064
4061 To read a patch from standard input (stdin), use "-" as the patch
4065 To read a patch from standard input (stdin), use "-" as the patch
4062 name. If a URL is specified, the patch will be downloaded from
4066 name. If a URL is specified, the patch will be downloaded from
4063 there.
4067 there.
4064
4068
4065 Import first applies changes to the working directory (unless
4069 Import first applies changes to the working directory (unless
4066 --bypass is specified), import will abort if there are outstanding
4070 --bypass is specified), import will abort if there are outstanding
4067 changes.
4071 changes.
4068
4072
4069 Use --bypass to apply and commit patches directly to the
4073 Use --bypass to apply and commit patches directly to the
4070 repository, without affecting the working directory. Without
4074 repository, without affecting the working directory. Without
4071 --exact, patches will be applied on top of the working directory
4075 --exact, patches will be applied on top of the working directory
4072 parent revision.
4076 parent revision.
4073
4077
4074 You can import a patch straight from a mail message. Even patches
4078 You can import a patch straight from a mail message. Even patches
4075 as attachments work (to use the body part, it must have type
4079 as attachments work (to use the body part, it must have type
4076 text/plain or text/x-patch). From and Subject headers of email
4080 text/plain or text/x-patch). From and Subject headers of email
4077 message are used as default committer and commit message. All
4081 message are used as default committer and commit message. All
4078 text/plain body parts before first diff are added to the commit
4082 text/plain body parts before first diff are added to the commit
4079 message.
4083 message.
4080
4084
4081 If the imported patch was generated by :hg:`export`, user and
4085 If the imported patch was generated by :hg:`export`, user and
4082 description from patch override values from message headers and
4086 description from patch override values from message headers and
4083 body. Values given on command line with -m/--message and -u/--user
4087 body. Values given on command line with -m/--message and -u/--user
4084 override these.
4088 override these.
4085
4089
4086 If --exact is specified, import will set the working directory to
4090 If --exact is specified, import will set the working directory to
4087 the parent of each patch before applying it, and will abort if the
4091 the parent of each patch before applying it, and will abort if the
4088 resulting changeset has a different ID than the one recorded in
4092 resulting changeset has a different ID than the one recorded in
4089 the patch. This will guard against various ways that portable
4093 the patch. This will guard against various ways that portable
4090 patch formats and mail systems might fail to transfer Mercurial
4094 patch formats and mail systems might fail to transfer Mercurial
4091 data or metadata. See :hg:`bundle` for lossless transmission.
4095 data or metadata. See :hg:`bundle` for lossless transmission.
4092
4096
4093 Use --partial to ensure a changeset will be created from the patch
4097 Use --partial to ensure a changeset will be created from the patch
4094 even if some hunks fail to apply. Hunks that fail to apply will be
4098 even if some hunks fail to apply. Hunks that fail to apply will be
4095 written to a <target-file>.rej file. Conflicts can then be resolved
4099 written to a <target-file>.rej file. Conflicts can then be resolved
4096 by hand before :hg:`commit --amend` is run to update the created
4100 by hand before :hg:`commit --amend` is run to update the created
4097 changeset. This flag exists to let people import patches that
4101 changeset. This flag exists to let people import patches that
4098 partially apply without losing the associated metadata (author,
4102 partially apply without losing the associated metadata (author,
4099 date, description, ...).
4103 date, description, ...).
4100
4104
4101 .. note::
4105 .. note::
4102
4106
4103 When no hunks apply cleanly, :hg:`import --partial` will create
4107 When no hunks apply cleanly, :hg:`import --partial` will create
4104 an empty changeset, importing only the patch metadata.
4108 an empty changeset, importing only the patch metadata.
4105
4109
4106 With -s/--similarity, hg will attempt to discover renames and
4110 With -s/--similarity, hg will attempt to discover renames and
4107 copies in the patch in the same way as :hg:`addremove`.
4111 copies in the patch in the same way as :hg:`addremove`.
4108
4112
4109 It is possible to use external patch programs to perform the patch
4113 It is possible to use external patch programs to perform the patch
4110 by setting the ``ui.patch`` configuration option. For the default
4114 by setting the ``ui.patch`` configuration option. For the default
4111 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4115 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4112 See :hg:`help config` for more information about configuration
4116 See :hg:`help config` for more information about configuration
4113 files and how to use these options.
4117 files and how to use these options.
4114
4118
4115 See :hg:`help dates` for a list of formats valid for -d/--date.
4119 See :hg:`help dates` for a list of formats valid for -d/--date.
4116
4120
4117 .. container:: verbose
4121 .. container:: verbose
4118
4122
4119 Examples:
4123 Examples:
4120
4124
4121 - import a traditional patch from a website and detect renames::
4125 - import a traditional patch from a website and detect renames::
4122
4126
4123 hg import -s 80 http://example.com/bugfix.patch
4127 hg import -s 80 http://example.com/bugfix.patch
4124
4128
4125 - import a changeset from an hgweb server::
4129 - import a changeset from an hgweb server::
4126
4130
4127 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4131 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4128
4132
4129 - import all the patches in an Unix-style mbox::
4133 - import all the patches in an Unix-style mbox::
4130
4134
4131 hg import incoming-patches.mbox
4135 hg import incoming-patches.mbox
4132
4136
4133 - import patches from stdin::
4137 - import patches from stdin::
4134
4138
4135 hg import -
4139 hg import -
4136
4140
4137 - attempt to exactly restore an exported changeset (not always
4141 - attempt to exactly restore an exported changeset (not always
4138 possible)::
4142 possible)::
4139
4143
4140 hg import --exact proposed-fix.patch
4144 hg import --exact proposed-fix.patch
4141
4145
4142 - use an external tool to apply a patch which is too fuzzy for
4146 - use an external tool to apply a patch which is too fuzzy for
4143 the default internal tool.
4147 the default internal tool.
4144
4148
4145 hg import --config ui.patch="patch --merge" fuzzy.patch
4149 hg import --config ui.patch="patch --merge" fuzzy.patch
4146
4150
4147 - change the default fuzzing from 2 to a less strict 7
4151 - change the default fuzzing from 2 to a less strict 7
4148
4152
4149 hg import --config ui.fuzz=7 fuzz.patch
4153 hg import --config ui.fuzz=7 fuzz.patch
4150
4154
4151 Returns 0 on success, 1 on partial success (see --partial).
4155 Returns 0 on success, 1 on partial success (see --partial).
4152 """
4156 """
4153
4157
4154 opts = pycompat.byteskwargs(opts)
4158 opts = pycompat.byteskwargs(opts)
4155 if not patch1:
4159 if not patch1:
4156 raise error.Abort(_(b'need at least one patch to import'))
4160 raise error.Abort(_(b'need at least one patch to import'))
4157
4161
4158 patches = (patch1,) + patches
4162 patches = (patch1,) + patches
4159
4163
4160 date = opts.get(b'date')
4164 date = opts.get(b'date')
4161 if date:
4165 if date:
4162 opts[b'date'] = dateutil.parsedate(date)
4166 opts[b'date'] = dateutil.parsedate(date)
4163
4167
4164 exact = opts.get(b'exact')
4168 exact = opts.get(b'exact')
4165 update = not opts.get(b'bypass')
4169 update = not opts.get(b'bypass')
4166 if not update and opts.get(b'no_commit'):
4170 if not update and opts.get(b'no_commit'):
4167 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4171 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4168 if opts.get(b'secret') and opts.get(b'no_commit'):
4172 if opts.get(b'secret') and opts.get(b'no_commit'):
4169 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4173 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4170 try:
4174 try:
4171 sim = float(opts.get(b'similarity') or 0)
4175 sim = float(opts.get(b'similarity') or 0)
4172 except ValueError:
4176 except ValueError:
4173 raise error.Abort(_(b'similarity must be a number'))
4177 raise error.Abort(_(b'similarity must be a number'))
4174 if sim < 0 or sim > 100:
4178 if sim < 0 or sim > 100:
4175 raise error.Abort(_(b'similarity must be between 0 and 100'))
4179 raise error.Abort(_(b'similarity must be between 0 and 100'))
4176 if sim and not update:
4180 if sim and not update:
4177 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4181 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4178 if exact:
4182 if exact:
4179 if opts.get(b'edit'):
4183 if opts.get(b'edit'):
4180 raise error.Abort(_(b'cannot use --exact with --edit'))
4184 raise error.Abort(_(b'cannot use --exact with --edit'))
4181 if opts.get(b'prefix'):
4185 if opts.get(b'prefix'):
4182 raise error.Abort(_(b'cannot use --exact with --prefix'))
4186 raise error.Abort(_(b'cannot use --exact with --prefix'))
4183
4187
4184 base = opts[b"base"]
4188 base = opts[b"base"]
4185 msgs = []
4189 msgs = []
4186 ret = 0
4190 ret = 0
4187
4191
4188 with repo.wlock():
4192 with repo.wlock():
4189 if update:
4193 if update:
4190 cmdutil.checkunfinished(repo)
4194 cmdutil.checkunfinished(repo)
4191 if exact or not opts.get(b'force'):
4195 if exact or not opts.get(b'force'):
4192 cmdutil.bailifchanged(repo)
4196 cmdutil.bailifchanged(repo)
4193
4197
4194 if not opts.get(b'no_commit'):
4198 if not opts.get(b'no_commit'):
4195 lock = repo.lock
4199 lock = repo.lock
4196 tr = lambda: repo.transaction(b'import')
4200 tr = lambda: repo.transaction(b'import')
4197 dsguard = util.nullcontextmanager
4201 dsguard = util.nullcontextmanager
4198 else:
4202 else:
4199 lock = util.nullcontextmanager
4203 lock = util.nullcontextmanager
4200 tr = util.nullcontextmanager
4204 tr = util.nullcontextmanager
4201 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4205 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4202 with lock(), tr(), dsguard():
4206 with lock(), tr(), dsguard():
4203 parents = repo[None].parents()
4207 parents = repo[None].parents()
4204 for patchurl in patches:
4208 for patchurl in patches:
4205 if patchurl == b'-':
4209 if patchurl == b'-':
4206 ui.status(_(b'applying patch from stdin\n'))
4210 ui.status(_(b'applying patch from stdin\n'))
4207 patchfile = ui.fin
4211 patchfile = ui.fin
4208 patchurl = b'stdin' # for error message
4212 patchurl = b'stdin' # for error message
4209 else:
4213 else:
4210 patchurl = os.path.join(base, patchurl)
4214 patchurl = os.path.join(base, patchurl)
4211 ui.status(_(b'applying %s\n') % patchurl)
4215 ui.status(_(b'applying %s\n') % patchurl)
4212 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4216 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4213
4217
4214 haspatch = False
4218 haspatch = False
4215 for hunk in patch.split(patchfile):
4219 for hunk in patch.split(patchfile):
4216 with patch.extract(ui, hunk) as patchdata:
4220 with patch.extract(ui, hunk) as patchdata:
4217 msg, node, rej = cmdutil.tryimportone(
4221 msg, node, rej = cmdutil.tryimportone(
4218 ui, repo, patchdata, parents, opts, msgs, hg.clean
4222 ui, repo, patchdata, parents, opts, msgs, hg.clean
4219 )
4223 )
4220 if msg:
4224 if msg:
4221 haspatch = True
4225 haspatch = True
4222 ui.note(msg + b'\n')
4226 ui.note(msg + b'\n')
4223 if update or exact:
4227 if update or exact:
4224 parents = repo[None].parents()
4228 parents = repo[None].parents()
4225 else:
4229 else:
4226 parents = [repo[node]]
4230 parents = [repo[node]]
4227 if rej:
4231 if rej:
4228 ui.write_err(_(b"patch applied partially\n"))
4232 ui.write_err(_(b"patch applied partially\n"))
4229 ui.write_err(
4233 ui.write_err(
4230 _(
4234 _(
4231 b"(fix the .rej files and run "
4235 b"(fix the .rej files and run "
4232 b"`hg commit --amend`)\n"
4236 b"`hg commit --amend`)\n"
4233 )
4237 )
4234 )
4238 )
4235 ret = 1
4239 ret = 1
4236 break
4240 break
4237
4241
4238 if not haspatch:
4242 if not haspatch:
4239 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4243 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4240
4244
4241 if msgs:
4245 if msgs:
4242 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4246 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4243 return ret
4247 return ret
4244
4248
4245
4249
4246 @command(
4250 @command(
4247 b'incoming|in',
4251 b'incoming|in',
4248 [
4252 [
4249 (
4253 (
4250 b'f',
4254 b'f',
4251 b'force',
4255 b'force',
4252 None,
4256 None,
4253 _(b'run even if remote repository is unrelated'),
4257 _(b'run even if remote repository is unrelated'),
4254 ),
4258 ),
4255 (b'n', b'newest-first', None, _(b'show newest record first')),
4259 (b'n', b'newest-first', None, _(b'show newest record first')),
4256 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4260 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4257 (
4261 (
4258 b'r',
4262 b'r',
4259 b'rev',
4263 b'rev',
4260 [],
4264 [],
4261 _(b'a remote changeset intended to be added'),
4265 _(b'a remote changeset intended to be added'),
4262 _(b'REV'),
4266 _(b'REV'),
4263 ),
4267 ),
4264 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4268 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4265 (
4269 (
4266 b'b',
4270 b'b',
4267 b'branch',
4271 b'branch',
4268 [],
4272 [],
4269 _(b'a specific branch you would like to pull'),
4273 _(b'a specific branch you would like to pull'),
4270 _(b'BRANCH'),
4274 _(b'BRANCH'),
4271 ),
4275 ),
4272 ]
4276 ]
4273 + logopts
4277 + logopts
4274 + remoteopts
4278 + remoteopts
4275 + subrepoopts,
4279 + subrepoopts,
4276 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4280 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4277 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4281 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4278 )
4282 )
4279 def incoming(ui, repo, source=b"default", **opts):
4283 def incoming(ui, repo, source=b"default", **opts):
4280 """show new changesets found in source
4284 """show new changesets found in source
4281
4285
4282 Show new changesets found in the specified path/URL or the default
4286 Show new changesets found in the specified path/URL or the default
4283 pull location. These are the changesets that would have been pulled
4287 pull location. These are the changesets that would have been pulled
4284 by :hg:`pull` at the time you issued this command.
4288 by :hg:`pull` at the time you issued this command.
4285
4289
4286 See pull for valid source format details.
4290 See pull for valid source format details.
4287
4291
4288 .. container:: verbose
4292 .. container:: verbose
4289
4293
4290 With -B/--bookmarks, the result of bookmark comparison between
4294 With -B/--bookmarks, the result of bookmark comparison between
4291 local and remote repositories is displayed. With -v/--verbose,
4295 local and remote repositories is displayed. With -v/--verbose,
4292 status is also displayed for each bookmark like below::
4296 status is also displayed for each bookmark like below::
4293
4297
4294 BM1 01234567890a added
4298 BM1 01234567890a added
4295 BM2 1234567890ab advanced
4299 BM2 1234567890ab advanced
4296 BM3 234567890abc diverged
4300 BM3 234567890abc diverged
4297 BM4 34567890abcd changed
4301 BM4 34567890abcd changed
4298
4302
4299 The action taken locally when pulling depends on the
4303 The action taken locally when pulling depends on the
4300 status of each bookmark:
4304 status of each bookmark:
4301
4305
4302 :``added``: pull will create it
4306 :``added``: pull will create it
4303 :``advanced``: pull will update it
4307 :``advanced``: pull will update it
4304 :``diverged``: pull will create a divergent bookmark
4308 :``diverged``: pull will create a divergent bookmark
4305 :``changed``: result depends on remote changesets
4309 :``changed``: result depends on remote changesets
4306
4310
4307 From the point of view of pulling behavior, bookmark
4311 From the point of view of pulling behavior, bookmark
4308 existing only in the remote repository are treated as ``added``,
4312 existing only in the remote repository are treated as ``added``,
4309 even if it is in fact locally deleted.
4313 even if it is in fact locally deleted.
4310
4314
4311 .. container:: verbose
4315 .. container:: verbose
4312
4316
4313 For remote repository, using --bundle avoids downloading the
4317 For remote repository, using --bundle avoids downloading the
4314 changesets twice if the incoming is followed by a pull.
4318 changesets twice if the incoming is followed by a pull.
4315
4319
4316 Examples:
4320 Examples:
4317
4321
4318 - show incoming changes with patches and full description::
4322 - show incoming changes with patches and full description::
4319
4323
4320 hg incoming -vp
4324 hg incoming -vp
4321
4325
4322 - show incoming changes excluding merges, store a bundle::
4326 - show incoming changes excluding merges, store a bundle::
4323
4327
4324 hg in -vpM --bundle incoming.hg
4328 hg in -vpM --bundle incoming.hg
4325 hg pull incoming.hg
4329 hg pull incoming.hg
4326
4330
4327 - briefly list changes inside a bundle::
4331 - briefly list changes inside a bundle::
4328
4332
4329 hg in changes.hg -T "{desc|firstline}\\n"
4333 hg in changes.hg -T "{desc|firstline}\\n"
4330
4334
4331 Returns 0 if there are incoming changes, 1 otherwise.
4335 Returns 0 if there are incoming changes, 1 otherwise.
4332 """
4336 """
4333 opts = pycompat.byteskwargs(opts)
4337 opts = pycompat.byteskwargs(opts)
4334 if opts.get(b'graph'):
4338 if opts.get(b'graph'):
4335 logcmdutil.checkunsupportedgraphflags([], opts)
4339 logcmdutil.checkunsupportedgraphflags([], opts)
4336
4340
4337 def display(other, chlist, displayer):
4341 def display(other, chlist, displayer):
4338 revdag = logcmdutil.graphrevs(other, chlist, opts)
4342 revdag = logcmdutil.graphrevs(other, chlist, opts)
4339 logcmdutil.displaygraph(
4343 logcmdutil.displaygraph(
4340 ui, repo, revdag, displayer, graphmod.asciiedges
4344 ui, repo, revdag, displayer, graphmod.asciiedges
4341 )
4345 )
4342
4346
4343 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4347 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4344 return 0
4348 return 0
4345
4349
4346 if opts.get(b'bundle') and opts.get(b'subrepos'):
4350 if opts.get(b'bundle') and opts.get(b'subrepos'):
4347 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4351 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4348
4352
4349 if opts.get(b'bookmarks'):
4353 if opts.get(b'bookmarks'):
4350 source, branches = hg.parseurl(
4354 source, branches = hg.parseurl(
4351 ui.expandpath(source), opts.get(b'branch')
4355 ui.expandpath(source), opts.get(b'branch')
4352 )
4356 )
4353 other = hg.peer(repo, opts, source)
4357 other = hg.peer(repo, opts, source)
4354 if b'bookmarks' not in other.listkeys(b'namespaces'):
4358 if b'bookmarks' not in other.listkeys(b'namespaces'):
4355 ui.warn(_(b"remote doesn't support bookmarks\n"))
4359 ui.warn(_(b"remote doesn't support bookmarks\n"))
4356 return 0
4360 return 0
4357 ui.pager(b'incoming')
4361 ui.pager(b'incoming')
4358 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4362 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4359 return bookmarks.incoming(ui, repo, other)
4363 return bookmarks.incoming(ui, repo, other)
4360
4364
4361 repo._subtoppath = ui.expandpath(source)
4365 repo._subtoppath = ui.expandpath(source)
4362 try:
4366 try:
4363 return hg.incoming(ui, repo, source, opts)
4367 return hg.incoming(ui, repo, source, opts)
4364 finally:
4368 finally:
4365 del repo._subtoppath
4369 del repo._subtoppath
4366
4370
4367
4371
4368 @command(
4372 @command(
4369 b'init',
4373 b'init',
4370 remoteopts,
4374 remoteopts,
4371 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4375 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4372 helpcategory=command.CATEGORY_REPO_CREATION,
4376 helpcategory=command.CATEGORY_REPO_CREATION,
4373 helpbasic=True,
4377 helpbasic=True,
4374 norepo=True,
4378 norepo=True,
4375 )
4379 )
4376 def init(ui, dest=b".", **opts):
4380 def init(ui, dest=b".", **opts):
4377 """create a new repository in the given directory
4381 """create a new repository in the given directory
4378
4382
4379 Initialize a new repository in the given directory. If the given
4383 Initialize a new repository in the given directory. If the given
4380 directory does not exist, it will be created.
4384 directory does not exist, it will be created.
4381
4385
4382 If no directory is given, the current directory is used.
4386 If no directory is given, the current directory is used.
4383
4387
4384 It is possible to specify an ``ssh://`` URL as the destination.
4388 It is possible to specify an ``ssh://`` URL as the destination.
4385 See :hg:`help urls` for more information.
4389 See :hg:`help urls` for more information.
4386
4390
4387 Returns 0 on success.
4391 Returns 0 on success.
4388 """
4392 """
4389 opts = pycompat.byteskwargs(opts)
4393 opts = pycompat.byteskwargs(opts)
4390 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4394 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4391
4395
4392
4396
4393 @command(
4397 @command(
4394 b'locate',
4398 b'locate',
4395 [
4399 [
4396 (
4400 (
4397 b'r',
4401 b'r',
4398 b'rev',
4402 b'rev',
4399 b'',
4403 b'',
4400 _(b'search the repository as it is in REV'),
4404 _(b'search the repository as it is in REV'),
4401 _(b'REV'),
4405 _(b'REV'),
4402 ),
4406 ),
4403 (
4407 (
4404 b'0',
4408 b'0',
4405 b'print0',
4409 b'print0',
4406 None,
4410 None,
4407 _(b'end filenames with NUL, for use with xargs'),
4411 _(b'end filenames with NUL, for use with xargs'),
4408 ),
4412 ),
4409 (
4413 (
4410 b'f',
4414 b'f',
4411 b'fullpath',
4415 b'fullpath',
4412 None,
4416 None,
4413 _(b'print complete paths from the filesystem root'),
4417 _(b'print complete paths from the filesystem root'),
4414 ),
4418 ),
4415 ]
4419 ]
4416 + walkopts,
4420 + walkopts,
4417 _(b'[OPTION]... [PATTERN]...'),
4421 _(b'[OPTION]... [PATTERN]...'),
4418 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4422 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4419 )
4423 )
4420 def locate(ui, repo, *pats, **opts):
4424 def locate(ui, repo, *pats, **opts):
4421 """locate files matching specific patterns (DEPRECATED)
4425 """locate files matching specific patterns (DEPRECATED)
4422
4426
4423 Print files under Mercurial control in the working directory whose
4427 Print files under Mercurial control in the working directory whose
4424 names match the given patterns.
4428 names match the given patterns.
4425
4429
4426 By default, this command searches all directories in the working
4430 By default, this command searches all directories in the working
4427 directory. To search just the current directory and its
4431 directory. To search just the current directory and its
4428 subdirectories, use "--include .".
4432 subdirectories, use "--include .".
4429
4433
4430 If no patterns are given to match, this command prints the names
4434 If no patterns are given to match, this command prints the names
4431 of all files under Mercurial control in the working directory.
4435 of all files under Mercurial control in the working directory.
4432
4436
4433 If you want to feed the output of this command into the "xargs"
4437 If you want to feed the output of this command into the "xargs"
4434 command, use the -0 option to both this command and "xargs". This
4438 command, use the -0 option to both this command and "xargs". This
4435 will avoid the problem of "xargs" treating single filenames that
4439 will avoid the problem of "xargs" treating single filenames that
4436 contain whitespace as multiple filenames.
4440 contain whitespace as multiple filenames.
4437
4441
4438 See :hg:`help files` for a more versatile command.
4442 See :hg:`help files` for a more versatile command.
4439
4443
4440 Returns 0 if a match is found, 1 otherwise.
4444 Returns 0 if a match is found, 1 otherwise.
4441 """
4445 """
4442 opts = pycompat.byteskwargs(opts)
4446 opts = pycompat.byteskwargs(opts)
4443 if opts.get(b'print0'):
4447 if opts.get(b'print0'):
4444 end = b'\0'
4448 end = b'\0'
4445 else:
4449 else:
4446 end = b'\n'
4450 end = b'\n'
4447 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4451 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4448
4452
4449 ret = 1
4453 ret = 1
4450 m = scmutil.match(
4454 m = scmutil.match(
4451 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4455 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4452 )
4456 )
4453
4457
4454 ui.pager(b'locate')
4458 ui.pager(b'locate')
4455 if ctx.rev() is None:
4459 if ctx.rev() is None:
4456 # When run on the working copy, "locate" includes removed files, so
4460 # When run on the working copy, "locate" includes removed files, so
4457 # we get the list of files from the dirstate.
4461 # we get the list of files from the dirstate.
4458 filesgen = sorted(repo.dirstate.matches(m))
4462 filesgen = sorted(repo.dirstate.matches(m))
4459 else:
4463 else:
4460 filesgen = ctx.matches(m)
4464 filesgen = ctx.matches(m)
4461 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4465 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4462 for abs in filesgen:
4466 for abs in filesgen:
4463 if opts.get(b'fullpath'):
4467 if opts.get(b'fullpath'):
4464 ui.write(repo.wjoin(abs), end)
4468 ui.write(repo.wjoin(abs), end)
4465 else:
4469 else:
4466 ui.write(uipathfn(abs), end)
4470 ui.write(uipathfn(abs), end)
4467 ret = 0
4471 ret = 0
4468
4472
4469 return ret
4473 return ret
4470
4474
4471
4475
4472 @command(
4476 @command(
4473 b'log|history',
4477 b'log|history',
4474 [
4478 [
4475 (
4479 (
4476 b'f',
4480 b'f',
4477 b'follow',
4481 b'follow',
4478 None,
4482 None,
4479 _(
4483 _(
4480 b'follow changeset history, or file history across copies and renames'
4484 b'follow changeset history, or file history across copies and renames'
4481 ),
4485 ),
4482 ),
4486 ),
4483 (
4487 (
4484 b'',
4488 b'',
4485 b'follow-first',
4489 b'follow-first',
4486 None,
4490 None,
4487 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4491 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4488 ),
4492 ),
4489 (
4493 (
4490 b'd',
4494 b'd',
4491 b'date',
4495 b'date',
4492 b'',
4496 b'',
4493 _(b'show revisions matching date spec'),
4497 _(b'show revisions matching date spec'),
4494 _(b'DATE'),
4498 _(b'DATE'),
4495 ),
4499 ),
4496 (b'C', b'copies', None, _(b'show copied files')),
4500 (b'C', b'copies', None, _(b'show copied files')),
4497 (
4501 (
4498 b'k',
4502 b'k',
4499 b'keyword',
4503 b'keyword',
4500 [],
4504 [],
4501 _(b'do case-insensitive search for a given text'),
4505 _(b'do case-insensitive search for a given text'),
4502 _(b'TEXT'),
4506 _(b'TEXT'),
4503 ),
4507 ),
4504 (
4508 (
4505 b'r',
4509 b'r',
4506 b'rev',
4510 b'rev',
4507 [],
4511 [],
4508 _(b'show the specified revision or revset'),
4512 _(b'show the specified revision or revset'),
4509 _(b'REV'),
4513 _(b'REV'),
4510 ),
4514 ),
4511 (
4515 (
4512 b'L',
4516 b'L',
4513 b'line-range',
4517 b'line-range',
4514 [],
4518 [],
4515 _(b'follow line range of specified file (EXPERIMENTAL)'),
4519 _(b'follow line range of specified file (EXPERIMENTAL)'),
4516 _(b'FILE,RANGE'),
4520 _(b'FILE,RANGE'),
4517 ),
4521 ),
4518 (
4522 (
4519 b'',
4523 b'',
4520 b'removed',
4524 b'removed',
4521 None,
4525 None,
4522 _(b'include revisions where files were removed'),
4526 _(b'include revisions where files were removed'),
4523 ),
4527 ),
4524 (
4528 (
4525 b'm',
4529 b'm',
4526 b'only-merges',
4530 b'only-merges',
4527 None,
4531 None,
4528 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4532 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4529 ),
4533 ),
4530 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4534 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4531 (
4535 (
4532 b'',
4536 b'',
4533 b'only-branch',
4537 b'only-branch',
4534 [],
4538 [],
4535 _(
4539 _(
4536 b'show only changesets within the given named branch (DEPRECATED)'
4540 b'show only changesets within the given named branch (DEPRECATED)'
4537 ),
4541 ),
4538 _(b'BRANCH'),
4542 _(b'BRANCH'),
4539 ),
4543 ),
4540 (
4544 (
4541 b'b',
4545 b'b',
4542 b'branch',
4546 b'branch',
4543 [],
4547 [],
4544 _(b'show changesets within the given named branch'),
4548 _(b'show changesets within the given named branch'),
4545 _(b'BRANCH'),
4549 _(b'BRANCH'),
4546 ),
4550 ),
4547 (
4551 (
4548 b'P',
4552 b'P',
4549 b'prune',
4553 b'prune',
4550 [],
4554 [],
4551 _(b'do not display revision or any of its ancestors'),
4555 _(b'do not display revision or any of its ancestors'),
4552 _(b'REV'),
4556 _(b'REV'),
4553 ),
4557 ),
4554 ]
4558 ]
4555 + logopts
4559 + logopts
4556 + walkopts,
4560 + walkopts,
4557 _(b'[OPTION]... [FILE]'),
4561 _(b'[OPTION]... [FILE]'),
4558 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4562 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4559 helpbasic=True,
4563 helpbasic=True,
4560 inferrepo=True,
4564 inferrepo=True,
4561 intents={INTENT_READONLY},
4565 intents={INTENT_READONLY},
4562 )
4566 )
4563 def log(ui, repo, *pats, **opts):
4567 def log(ui, repo, *pats, **opts):
4564 """show revision history of entire repository or files
4568 """show revision history of entire repository or files
4565
4569
4566 Print the revision history of the specified files or the entire
4570 Print the revision history of the specified files or the entire
4567 project.
4571 project.
4568
4572
4569 If no revision range is specified, the default is ``tip:0`` unless
4573 If no revision range is specified, the default is ``tip:0`` unless
4570 --follow is set, in which case the working directory parent is
4574 --follow is set, in which case the working directory parent is
4571 used as the starting revision.
4575 used as the starting revision.
4572
4576
4573 File history is shown without following rename or copy history of
4577 File history is shown without following rename or copy history of
4574 files. Use -f/--follow with a filename to follow history across
4578 files. Use -f/--follow with a filename to follow history across
4575 renames and copies. --follow without a filename will only show
4579 renames and copies. --follow without a filename will only show
4576 ancestors of the starting revision.
4580 ancestors of the starting revision.
4577
4581
4578 By default this command prints revision number and changeset id,
4582 By default this command prints revision number and changeset id,
4579 tags, non-trivial parents, user, date and time, and a summary for
4583 tags, non-trivial parents, user, date and time, and a summary for
4580 each commit. When the -v/--verbose switch is used, the list of
4584 each commit. When the -v/--verbose switch is used, the list of
4581 changed files and full commit message are shown.
4585 changed files and full commit message are shown.
4582
4586
4583 With --graph the revisions are shown as an ASCII art DAG with the most
4587 With --graph the revisions are shown as an ASCII art DAG with the most
4584 recent changeset at the top.
4588 recent changeset at the top.
4585 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4589 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4586 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4590 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4587 changeset from the lines below is a parent of the 'o' merge on the same
4591 changeset from the lines below is a parent of the 'o' merge on the same
4588 line.
4592 line.
4589 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4593 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4590 of a '|' indicates one or more revisions in a path are omitted.
4594 of a '|' indicates one or more revisions in a path are omitted.
4591
4595
4592 .. container:: verbose
4596 .. container:: verbose
4593
4597
4594 Use -L/--line-range FILE,M:N options to follow the history of lines
4598 Use -L/--line-range FILE,M:N options to follow the history of lines
4595 from M to N in FILE. With -p/--patch only diff hunks affecting
4599 from M to N in FILE. With -p/--patch only diff hunks affecting
4596 specified line range will be shown. This option requires --follow;
4600 specified line range will be shown. This option requires --follow;
4597 it can be specified multiple times. Currently, this option is not
4601 it can be specified multiple times. Currently, this option is not
4598 compatible with --graph. This option is experimental.
4602 compatible with --graph. This option is experimental.
4599
4603
4600 .. note::
4604 .. note::
4601
4605
4602 :hg:`log --patch` may generate unexpected diff output for merge
4606 :hg:`log --patch` may generate unexpected diff output for merge
4603 changesets, as it will only compare the merge changeset against
4607 changesets, as it will only compare the merge changeset against
4604 its first parent. Also, only files different from BOTH parents
4608 its first parent. Also, only files different from BOTH parents
4605 will appear in files:.
4609 will appear in files:.
4606
4610
4607 .. note::
4611 .. note::
4608
4612
4609 For performance reasons, :hg:`log FILE` may omit duplicate changes
4613 For performance reasons, :hg:`log FILE` may omit duplicate changes
4610 made on branches and will not show removals or mode changes. To
4614 made on branches and will not show removals or mode changes. To
4611 see all such changes, use the --removed switch.
4615 see all such changes, use the --removed switch.
4612
4616
4613 .. container:: verbose
4617 .. container:: verbose
4614
4618
4615 .. note::
4619 .. note::
4616
4620
4617 The history resulting from -L/--line-range options depends on diff
4621 The history resulting from -L/--line-range options depends on diff
4618 options; for instance if white-spaces are ignored, respective changes
4622 options; for instance if white-spaces are ignored, respective changes
4619 with only white-spaces in specified line range will not be listed.
4623 with only white-spaces in specified line range will not be listed.
4620
4624
4621 .. container:: verbose
4625 .. container:: verbose
4622
4626
4623 Some examples:
4627 Some examples:
4624
4628
4625 - changesets with full descriptions and file lists::
4629 - changesets with full descriptions and file lists::
4626
4630
4627 hg log -v
4631 hg log -v
4628
4632
4629 - changesets ancestral to the working directory::
4633 - changesets ancestral to the working directory::
4630
4634
4631 hg log -f
4635 hg log -f
4632
4636
4633 - last 10 commits on the current branch::
4637 - last 10 commits on the current branch::
4634
4638
4635 hg log -l 10 -b .
4639 hg log -l 10 -b .
4636
4640
4637 - changesets showing all modifications of a file, including removals::
4641 - changesets showing all modifications of a file, including removals::
4638
4642
4639 hg log --removed file.c
4643 hg log --removed file.c
4640
4644
4641 - all changesets that touch a directory, with diffs, excluding merges::
4645 - all changesets that touch a directory, with diffs, excluding merges::
4642
4646
4643 hg log -Mp lib/
4647 hg log -Mp lib/
4644
4648
4645 - all revision numbers that match a keyword::
4649 - all revision numbers that match a keyword::
4646
4650
4647 hg log -k bug --template "{rev}\\n"
4651 hg log -k bug --template "{rev}\\n"
4648
4652
4649 - the full hash identifier of the working directory parent::
4653 - the full hash identifier of the working directory parent::
4650
4654
4651 hg log -r . --template "{node}\\n"
4655 hg log -r . --template "{node}\\n"
4652
4656
4653 - list available log templates::
4657 - list available log templates::
4654
4658
4655 hg log -T list
4659 hg log -T list
4656
4660
4657 - check if a given changeset is included in a tagged release::
4661 - check if a given changeset is included in a tagged release::
4658
4662
4659 hg log -r "a21ccf and ancestor(1.9)"
4663 hg log -r "a21ccf and ancestor(1.9)"
4660
4664
4661 - find all changesets by some user in a date range::
4665 - find all changesets by some user in a date range::
4662
4666
4663 hg log -k alice -d "may 2008 to jul 2008"
4667 hg log -k alice -d "may 2008 to jul 2008"
4664
4668
4665 - summary of all changesets after the last tag::
4669 - summary of all changesets after the last tag::
4666
4670
4667 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4671 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4668
4672
4669 - changesets touching lines 13 to 23 for file.c::
4673 - changesets touching lines 13 to 23 for file.c::
4670
4674
4671 hg log -L file.c,13:23
4675 hg log -L file.c,13:23
4672
4676
4673 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4677 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4674 main.c with patch::
4678 main.c with patch::
4675
4679
4676 hg log -L file.c,13:23 -L main.c,2:6 -p
4680 hg log -L file.c,13:23 -L main.c,2:6 -p
4677
4681
4678 See :hg:`help dates` for a list of formats valid for -d/--date.
4682 See :hg:`help dates` for a list of formats valid for -d/--date.
4679
4683
4680 See :hg:`help revisions` for more about specifying and ordering
4684 See :hg:`help revisions` for more about specifying and ordering
4681 revisions.
4685 revisions.
4682
4686
4683 See :hg:`help templates` for more about pre-packaged styles and
4687 See :hg:`help templates` for more about pre-packaged styles and
4684 specifying custom templates. The default template used by the log
4688 specifying custom templates. The default template used by the log
4685 command can be customized via the ``ui.logtemplate`` configuration
4689 command can be customized via the ``ui.logtemplate`` configuration
4686 setting.
4690 setting.
4687
4691
4688 Returns 0 on success.
4692 Returns 0 on success.
4689
4693
4690 """
4694 """
4691 opts = pycompat.byteskwargs(opts)
4695 opts = pycompat.byteskwargs(opts)
4692 linerange = opts.get(b'line_range')
4696 linerange = opts.get(b'line_range')
4693
4697
4694 if linerange and not opts.get(b'follow'):
4698 if linerange and not opts.get(b'follow'):
4695 raise error.Abort(_(b'--line-range requires --follow'))
4699 raise error.Abort(_(b'--line-range requires --follow'))
4696
4700
4697 if linerange and pats:
4701 if linerange and pats:
4698 # TODO: take pats as patterns with no line-range filter
4702 # TODO: take pats as patterns with no line-range filter
4699 raise error.Abort(
4703 raise error.Abort(
4700 _(b'FILE arguments are not compatible with --line-range option')
4704 _(b'FILE arguments are not compatible with --line-range option')
4701 )
4705 )
4702
4706
4703 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4707 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4704 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4708 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4705 if linerange:
4709 if linerange:
4706 # TODO: should follow file history from logcmdutil._initialrevs(),
4710 # TODO: should follow file history from logcmdutil._initialrevs(),
4707 # then filter the result by logcmdutil._makerevset() and --limit
4711 # then filter the result by logcmdutil._makerevset() and --limit
4708 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4712 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4709
4713
4710 getcopies = None
4714 getcopies = None
4711 if opts.get(b'copies'):
4715 if opts.get(b'copies'):
4712 endrev = None
4716 endrev = None
4713 if revs:
4717 if revs:
4714 endrev = revs.max() + 1
4718 endrev = revs.max() + 1
4715 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4719 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4716
4720
4717 ui.pager(b'log')
4721 ui.pager(b'log')
4718 displayer = logcmdutil.changesetdisplayer(
4722 displayer = logcmdutil.changesetdisplayer(
4719 ui, repo, opts, differ, buffered=True
4723 ui, repo, opts, differ, buffered=True
4720 )
4724 )
4721 if opts.get(b'graph'):
4725 if opts.get(b'graph'):
4722 displayfn = logcmdutil.displaygraphrevs
4726 displayfn = logcmdutil.displaygraphrevs
4723 else:
4727 else:
4724 displayfn = logcmdutil.displayrevs
4728 displayfn = logcmdutil.displayrevs
4725 displayfn(ui, repo, revs, displayer, getcopies)
4729 displayfn(ui, repo, revs, displayer, getcopies)
4726
4730
4727
4731
4728 @command(
4732 @command(
4729 b'manifest',
4733 b'manifest',
4730 [
4734 [
4731 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4735 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4732 (b'', b'all', False, _(b"list files from all revisions")),
4736 (b'', b'all', False, _(b"list files from all revisions")),
4733 ]
4737 ]
4734 + formatteropts,
4738 + formatteropts,
4735 _(b'[-r REV]'),
4739 _(b'[-r REV]'),
4736 helpcategory=command.CATEGORY_MAINTENANCE,
4740 helpcategory=command.CATEGORY_MAINTENANCE,
4737 intents={INTENT_READONLY},
4741 intents={INTENT_READONLY},
4738 )
4742 )
4739 def manifest(ui, repo, node=None, rev=None, **opts):
4743 def manifest(ui, repo, node=None, rev=None, **opts):
4740 """output the current or given revision of the project manifest
4744 """output the current or given revision of the project manifest
4741
4745
4742 Print a list of version controlled files for the given revision.
4746 Print a list of version controlled files for the given revision.
4743 If no revision is given, the first parent of the working directory
4747 If no revision is given, the first parent of the working directory
4744 is used, or the null revision if no revision is checked out.
4748 is used, or the null revision if no revision is checked out.
4745
4749
4746 With -v, print file permissions, symlink and executable bits.
4750 With -v, print file permissions, symlink and executable bits.
4747 With --debug, print file revision hashes.
4751 With --debug, print file revision hashes.
4748
4752
4749 If option --all is specified, the list of all files from all revisions
4753 If option --all is specified, the list of all files from all revisions
4750 is printed. This includes deleted and renamed files.
4754 is printed. This includes deleted and renamed files.
4751
4755
4752 Returns 0 on success.
4756 Returns 0 on success.
4753 """
4757 """
4754 opts = pycompat.byteskwargs(opts)
4758 opts = pycompat.byteskwargs(opts)
4755 fm = ui.formatter(b'manifest', opts)
4759 fm = ui.formatter(b'manifest', opts)
4756
4760
4757 if opts.get(b'all'):
4761 if opts.get(b'all'):
4758 if rev or node:
4762 if rev or node:
4759 raise error.Abort(_(b"can't specify a revision with --all"))
4763 raise error.Abort(_(b"can't specify a revision with --all"))
4760
4764
4761 res = set()
4765 res = set()
4762 for rev in repo:
4766 for rev in repo:
4763 ctx = repo[rev]
4767 ctx = repo[rev]
4764 res |= set(ctx.files())
4768 res |= set(ctx.files())
4765
4769
4766 ui.pager(b'manifest')
4770 ui.pager(b'manifest')
4767 for f in sorted(res):
4771 for f in sorted(res):
4768 fm.startitem()
4772 fm.startitem()
4769 fm.write(b"path", b'%s\n', f)
4773 fm.write(b"path", b'%s\n', f)
4770 fm.end()
4774 fm.end()
4771 return
4775 return
4772
4776
4773 if rev and node:
4777 if rev and node:
4774 raise error.Abort(_(b"please specify just one revision"))
4778 raise error.Abort(_(b"please specify just one revision"))
4775
4779
4776 if not node:
4780 if not node:
4777 node = rev
4781 node = rev
4778
4782
4779 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4783 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4780 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4784 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4781 if node:
4785 if node:
4782 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4786 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4783 ctx = scmutil.revsingle(repo, node)
4787 ctx = scmutil.revsingle(repo, node)
4784 mf = ctx.manifest()
4788 mf = ctx.manifest()
4785 ui.pager(b'manifest')
4789 ui.pager(b'manifest')
4786 for f in ctx:
4790 for f in ctx:
4787 fm.startitem()
4791 fm.startitem()
4788 fm.context(ctx=ctx)
4792 fm.context(ctx=ctx)
4789 fl = ctx[f].flags()
4793 fl = ctx[f].flags()
4790 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4794 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4791 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4795 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4792 fm.write(b'path', b'%s\n', f)
4796 fm.write(b'path', b'%s\n', f)
4793 fm.end()
4797 fm.end()
4794
4798
4795
4799
4796 @command(
4800 @command(
4797 b'merge',
4801 b'merge',
4798 [
4802 [
4799 (
4803 (
4800 b'f',
4804 b'f',
4801 b'force',
4805 b'force',
4802 None,
4806 None,
4803 _(b'force a merge including outstanding changes (DEPRECATED)'),
4807 _(b'force a merge including outstanding changes (DEPRECATED)'),
4804 ),
4808 ),
4805 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4809 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4806 (
4810 (
4807 b'P',
4811 b'P',
4808 b'preview',
4812 b'preview',
4809 None,
4813 None,
4810 _(b'review revisions to merge (no merge is performed)'),
4814 _(b'review revisions to merge (no merge is performed)'),
4811 ),
4815 ),
4812 (b'', b'abort', None, _(b'abort the ongoing merge')),
4816 (b'', b'abort', None, _(b'abort the ongoing merge')),
4813 ]
4817 ]
4814 + mergetoolopts,
4818 + mergetoolopts,
4815 _(b'[-P] [[-r] REV]'),
4819 _(b'[-P] [[-r] REV]'),
4816 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4820 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4817 helpbasic=True,
4821 helpbasic=True,
4818 )
4822 )
4819 def merge(ui, repo, node=None, **opts):
4823 def merge(ui, repo, node=None, **opts):
4820 """merge another revision into working directory
4824 """merge another revision into working directory
4821
4825
4822 The current working directory is updated with all changes made in
4826 The current working directory is updated with all changes made in
4823 the requested revision since the last common predecessor revision.
4827 the requested revision since the last common predecessor revision.
4824
4828
4825 Files that changed between either parent are marked as changed for
4829 Files that changed between either parent are marked as changed for
4826 the next commit and a commit must be performed before any further
4830 the next commit and a commit must be performed before any further
4827 updates to the repository are allowed. The next commit will have
4831 updates to the repository are allowed. The next commit will have
4828 two parents.
4832 two parents.
4829
4833
4830 ``--tool`` can be used to specify the merge tool used for file
4834 ``--tool`` can be used to specify the merge tool used for file
4831 merges. It overrides the HGMERGE environment variable and your
4835 merges. It overrides the HGMERGE environment variable and your
4832 configuration files. See :hg:`help merge-tools` for options.
4836 configuration files. See :hg:`help merge-tools` for options.
4833
4837
4834 If no revision is specified, the working directory's parent is a
4838 If no revision is specified, the working directory's parent is a
4835 head revision, and the current branch contains exactly one other
4839 head revision, and the current branch contains exactly one other
4836 head, the other head is merged with by default. Otherwise, an
4840 head, the other head is merged with by default. Otherwise, an
4837 explicit revision with which to merge must be provided.
4841 explicit revision with which to merge must be provided.
4838
4842
4839 See :hg:`help resolve` for information on handling file conflicts.
4843 See :hg:`help resolve` for information on handling file conflicts.
4840
4844
4841 To undo an uncommitted merge, use :hg:`merge --abort` which
4845 To undo an uncommitted merge, use :hg:`merge --abort` which
4842 will check out a clean copy of the original merge parent, losing
4846 will check out a clean copy of the original merge parent, losing
4843 all changes.
4847 all changes.
4844
4848
4845 Returns 0 on success, 1 if there are unresolved files.
4849 Returns 0 on success, 1 if there are unresolved files.
4846 """
4850 """
4847
4851
4848 opts = pycompat.byteskwargs(opts)
4852 opts = pycompat.byteskwargs(opts)
4849 abort = opts.get(b'abort')
4853 abort = opts.get(b'abort')
4850 if abort and repo.dirstate.p2() == nullid:
4854 if abort and repo.dirstate.p2() == nullid:
4851 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4855 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4852 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4856 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4853 if abort:
4857 if abort:
4854 state = cmdutil.getunfinishedstate(repo)
4858 state = cmdutil.getunfinishedstate(repo)
4855 if state and state._opname != b'merge':
4859 if state and state._opname != b'merge':
4856 raise error.Abort(
4860 raise error.Abort(
4857 _(b'cannot abort merge with %s in progress') % (state._opname),
4861 _(b'cannot abort merge with %s in progress') % (state._opname),
4858 hint=state.hint(),
4862 hint=state.hint(),
4859 )
4863 )
4860 if node:
4864 if node:
4861 raise error.Abort(_(b"cannot specify a node with --abort"))
4865 raise error.Abort(_(b"cannot specify a node with --abort"))
4862 return hg.abortmerge(repo.ui, repo)
4866 return hg.abortmerge(repo.ui, repo)
4863
4867
4864 if opts.get(b'rev') and node:
4868 if opts.get(b'rev') and node:
4865 raise error.Abort(_(b"please specify just one revision"))
4869 raise error.Abort(_(b"please specify just one revision"))
4866 if not node:
4870 if not node:
4867 node = opts.get(b'rev')
4871 node = opts.get(b'rev')
4868
4872
4869 if node:
4873 if node:
4870 node = scmutil.revsingle(repo, node).node()
4874 node = scmutil.revsingle(repo, node).node()
4871 else:
4875 else:
4872 if ui.configbool(b'commands', b'merge.require-rev'):
4876 if ui.configbool(b'commands', b'merge.require-rev'):
4873 raise error.Abort(
4877 raise error.Abort(
4874 _(
4878 _(
4875 b'configuration requires specifying revision to merge '
4879 b'configuration requires specifying revision to merge '
4876 b'with'
4880 b'with'
4877 )
4881 )
4878 )
4882 )
4879 node = repo[destutil.destmerge(repo)].node()
4883 node = repo[destutil.destmerge(repo)].node()
4880
4884
4881 if node is None:
4885 if node is None:
4882 raise error.Abort(_(b'merging with the working copy has no effect'))
4886 raise error.Abort(_(b'merging with the working copy has no effect'))
4883
4887
4884 if opts.get(b'preview'):
4888 if opts.get(b'preview'):
4885 # find nodes that are ancestors of p2 but not of p1
4889 # find nodes that are ancestors of p2 but not of p1
4886 p1 = repo[b'.'].node()
4890 p1 = repo[b'.'].node()
4887 p2 = node
4891 p2 = node
4888 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4892 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4889
4893
4890 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4894 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4891 for node in nodes:
4895 for node in nodes:
4892 displayer.show(repo[node])
4896 displayer.show(repo[node])
4893 displayer.close()
4897 displayer.close()
4894 return 0
4898 return 0
4895
4899
4896 # ui.forcemerge is an internal variable, do not document
4900 # ui.forcemerge is an internal variable, do not document
4897 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4901 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4898 with ui.configoverride(overrides, b'merge'):
4902 with ui.configoverride(overrides, b'merge'):
4899 force = opts.get(b'force')
4903 force = opts.get(b'force')
4900 labels = [b'working copy', b'merge rev']
4904 labels = [b'working copy', b'merge rev']
4901 return hg.merge(
4905 return hg.merge(
4902 repo, node, force=force, mergeforce=force, labels=labels
4906 repo, node, force=force, mergeforce=force, labels=labels
4903 )
4907 )
4904
4908
4905
4909
4906 statemod.addunfinished(
4910 statemod.addunfinished(
4907 b'merge',
4911 b'merge',
4908 fname=None,
4912 fname=None,
4909 clearable=True,
4913 clearable=True,
4910 allowcommit=True,
4914 allowcommit=True,
4911 cmdmsg=_(b'outstanding uncommitted merge'),
4915 cmdmsg=_(b'outstanding uncommitted merge'),
4912 abortfunc=hg.abortmerge,
4916 abortfunc=hg.abortmerge,
4913 statushint=_(
4917 statushint=_(
4914 b'To continue: hg commit\nTo abort: hg merge --abort'
4918 b'To continue: hg commit\nTo abort: hg merge --abort'
4915 ),
4919 ),
4916 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4920 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4917 )
4921 )
4918
4922
4919
4923
4920 @command(
4924 @command(
4921 b'outgoing|out',
4925 b'outgoing|out',
4922 [
4926 [
4923 (
4927 (
4924 b'f',
4928 b'f',
4925 b'force',
4929 b'force',
4926 None,
4930 None,
4927 _(b'run even when the destination is unrelated'),
4931 _(b'run even when the destination is unrelated'),
4928 ),
4932 ),
4929 (
4933 (
4930 b'r',
4934 b'r',
4931 b'rev',
4935 b'rev',
4932 [],
4936 [],
4933 _(b'a changeset intended to be included in the destination'),
4937 _(b'a changeset intended to be included in the destination'),
4934 _(b'REV'),
4938 _(b'REV'),
4935 ),
4939 ),
4936 (b'n', b'newest-first', None, _(b'show newest record first')),
4940 (b'n', b'newest-first', None, _(b'show newest record first')),
4937 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4941 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4938 (
4942 (
4939 b'b',
4943 b'b',
4940 b'branch',
4944 b'branch',
4941 [],
4945 [],
4942 _(b'a specific branch you would like to push'),
4946 _(b'a specific branch you would like to push'),
4943 _(b'BRANCH'),
4947 _(b'BRANCH'),
4944 ),
4948 ),
4945 ]
4949 ]
4946 + logopts
4950 + logopts
4947 + remoteopts
4951 + remoteopts
4948 + subrepoopts,
4952 + subrepoopts,
4949 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4953 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4950 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4954 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4951 )
4955 )
4952 def outgoing(ui, repo, dest=None, **opts):
4956 def outgoing(ui, repo, dest=None, **opts):
4953 """show changesets not found in the destination
4957 """show changesets not found in the destination
4954
4958
4955 Show changesets not found in the specified destination repository
4959 Show changesets not found in the specified destination repository
4956 or the default push location. These are the changesets that would
4960 or the default push location. These are the changesets that would
4957 be pushed if a push was requested.
4961 be pushed if a push was requested.
4958
4962
4959 See pull for details of valid destination formats.
4963 See pull for details of valid destination formats.
4960
4964
4961 .. container:: verbose
4965 .. container:: verbose
4962
4966
4963 With -B/--bookmarks, the result of bookmark comparison between
4967 With -B/--bookmarks, the result of bookmark comparison between
4964 local and remote repositories is displayed. With -v/--verbose,
4968 local and remote repositories is displayed. With -v/--verbose,
4965 status is also displayed for each bookmark like below::
4969 status is also displayed for each bookmark like below::
4966
4970
4967 BM1 01234567890a added
4971 BM1 01234567890a added
4968 BM2 deleted
4972 BM2 deleted
4969 BM3 234567890abc advanced
4973 BM3 234567890abc advanced
4970 BM4 34567890abcd diverged
4974 BM4 34567890abcd diverged
4971 BM5 4567890abcde changed
4975 BM5 4567890abcde changed
4972
4976
4973 The action taken when pushing depends on the
4977 The action taken when pushing depends on the
4974 status of each bookmark:
4978 status of each bookmark:
4975
4979
4976 :``added``: push with ``-B`` will create it
4980 :``added``: push with ``-B`` will create it
4977 :``deleted``: push with ``-B`` will delete it
4981 :``deleted``: push with ``-B`` will delete it
4978 :``advanced``: push will update it
4982 :``advanced``: push will update it
4979 :``diverged``: push with ``-B`` will update it
4983 :``diverged``: push with ``-B`` will update it
4980 :``changed``: push with ``-B`` will update it
4984 :``changed``: push with ``-B`` will update it
4981
4985
4982 From the point of view of pushing behavior, bookmarks
4986 From the point of view of pushing behavior, bookmarks
4983 existing only in the remote repository are treated as
4987 existing only in the remote repository are treated as
4984 ``deleted``, even if it is in fact added remotely.
4988 ``deleted``, even if it is in fact added remotely.
4985
4989
4986 Returns 0 if there are outgoing changes, 1 otherwise.
4990 Returns 0 if there are outgoing changes, 1 otherwise.
4987 """
4991 """
4988 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4992 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4989 # style URLs, so don't overwrite dest.
4993 # style URLs, so don't overwrite dest.
4990 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4994 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4991 if not path:
4995 if not path:
4992 raise error.Abort(
4996 raise error.Abort(
4993 _(b'default repository not configured!'),
4997 _(b'default repository not configured!'),
4994 hint=_(b"see 'hg help config.paths'"),
4998 hint=_(b"see 'hg help config.paths'"),
4995 )
4999 )
4996
5000
4997 opts = pycompat.byteskwargs(opts)
5001 opts = pycompat.byteskwargs(opts)
4998 if opts.get(b'graph'):
5002 if opts.get(b'graph'):
4999 logcmdutil.checkunsupportedgraphflags([], opts)
5003 logcmdutil.checkunsupportedgraphflags([], opts)
5000 o, other = hg._outgoing(ui, repo, dest, opts)
5004 o, other = hg._outgoing(ui, repo, dest, opts)
5001 if not o:
5005 if not o:
5002 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5006 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5003 return
5007 return
5004
5008
5005 revdag = logcmdutil.graphrevs(repo, o, opts)
5009 revdag = logcmdutil.graphrevs(repo, o, opts)
5006 ui.pager(b'outgoing')
5010 ui.pager(b'outgoing')
5007 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5011 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5008 logcmdutil.displaygraph(
5012 logcmdutil.displaygraph(
5009 ui, repo, revdag, displayer, graphmod.asciiedges
5013 ui, repo, revdag, displayer, graphmod.asciiedges
5010 )
5014 )
5011 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5015 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5012 return 0
5016 return 0
5013
5017
5014 if opts.get(b'bookmarks'):
5018 if opts.get(b'bookmarks'):
5015 dest = path.pushloc or path.loc
5019 dest = path.pushloc or path.loc
5016 other = hg.peer(repo, opts, dest)
5020 other = hg.peer(repo, opts, dest)
5017 if b'bookmarks' not in other.listkeys(b'namespaces'):
5021 if b'bookmarks' not in other.listkeys(b'namespaces'):
5018 ui.warn(_(b"remote doesn't support bookmarks\n"))
5022 ui.warn(_(b"remote doesn't support bookmarks\n"))
5019 return 0
5023 return 0
5020 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5024 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5021 ui.pager(b'outgoing')
5025 ui.pager(b'outgoing')
5022 return bookmarks.outgoing(ui, repo, other)
5026 return bookmarks.outgoing(ui, repo, other)
5023
5027
5024 repo._subtoppath = path.pushloc or path.loc
5028 repo._subtoppath = path.pushloc or path.loc
5025 try:
5029 try:
5026 return hg.outgoing(ui, repo, dest, opts)
5030 return hg.outgoing(ui, repo, dest, opts)
5027 finally:
5031 finally:
5028 del repo._subtoppath
5032 del repo._subtoppath
5029
5033
5030
5034
5031 @command(
5035 @command(
5032 b'parents',
5036 b'parents',
5033 [
5037 [
5034 (
5038 (
5035 b'r',
5039 b'r',
5036 b'rev',
5040 b'rev',
5037 b'',
5041 b'',
5038 _(b'show parents of the specified revision'),
5042 _(b'show parents of the specified revision'),
5039 _(b'REV'),
5043 _(b'REV'),
5040 ),
5044 ),
5041 ]
5045 ]
5042 + templateopts,
5046 + templateopts,
5043 _(b'[-r REV] [FILE]'),
5047 _(b'[-r REV] [FILE]'),
5044 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5048 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5045 inferrepo=True,
5049 inferrepo=True,
5046 )
5050 )
5047 def parents(ui, repo, file_=None, **opts):
5051 def parents(ui, repo, file_=None, **opts):
5048 """show the parents of the working directory or revision (DEPRECATED)
5052 """show the parents of the working directory or revision (DEPRECATED)
5049
5053
5050 Print the working directory's parent revisions. If a revision is
5054 Print the working directory's parent revisions. If a revision is
5051 given via -r/--rev, the parent of that revision will be printed.
5055 given via -r/--rev, the parent of that revision will be printed.
5052 If a file argument is given, the revision in which the file was
5056 If a file argument is given, the revision in which the file was
5053 last changed (before the working directory revision or the
5057 last changed (before the working directory revision or the
5054 argument to --rev if given) is printed.
5058 argument to --rev if given) is printed.
5055
5059
5056 This command is equivalent to::
5060 This command is equivalent to::
5057
5061
5058 hg log -r "p1()+p2()" or
5062 hg log -r "p1()+p2()" or
5059 hg log -r "p1(REV)+p2(REV)" or
5063 hg log -r "p1(REV)+p2(REV)" or
5060 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5064 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5061 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5065 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5062
5066
5063 See :hg:`summary` and :hg:`help revsets` for related information.
5067 See :hg:`summary` and :hg:`help revsets` for related information.
5064
5068
5065 Returns 0 on success.
5069 Returns 0 on success.
5066 """
5070 """
5067
5071
5068 opts = pycompat.byteskwargs(opts)
5072 opts = pycompat.byteskwargs(opts)
5069 rev = opts.get(b'rev')
5073 rev = opts.get(b'rev')
5070 if rev:
5074 if rev:
5071 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5075 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5072 ctx = scmutil.revsingle(repo, rev, None)
5076 ctx = scmutil.revsingle(repo, rev, None)
5073
5077
5074 if file_:
5078 if file_:
5075 m = scmutil.match(ctx, (file_,), opts)
5079 m = scmutil.match(ctx, (file_,), opts)
5076 if m.anypats() or len(m.files()) != 1:
5080 if m.anypats() or len(m.files()) != 1:
5077 raise error.Abort(_(b'can only specify an explicit filename'))
5081 raise error.Abort(_(b'can only specify an explicit filename'))
5078 file_ = m.files()[0]
5082 file_ = m.files()[0]
5079 filenodes = []
5083 filenodes = []
5080 for cp in ctx.parents():
5084 for cp in ctx.parents():
5081 if not cp:
5085 if not cp:
5082 continue
5086 continue
5083 try:
5087 try:
5084 filenodes.append(cp.filenode(file_))
5088 filenodes.append(cp.filenode(file_))
5085 except error.LookupError:
5089 except error.LookupError:
5086 pass
5090 pass
5087 if not filenodes:
5091 if not filenodes:
5088 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5092 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5089 p = []
5093 p = []
5090 for fn in filenodes:
5094 for fn in filenodes:
5091 fctx = repo.filectx(file_, fileid=fn)
5095 fctx = repo.filectx(file_, fileid=fn)
5092 p.append(fctx.node())
5096 p.append(fctx.node())
5093 else:
5097 else:
5094 p = [cp.node() for cp in ctx.parents()]
5098 p = [cp.node() for cp in ctx.parents()]
5095
5099
5096 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5100 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5097 for n in p:
5101 for n in p:
5098 if n != nullid:
5102 if n != nullid:
5099 displayer.show(repo[n])
5103 displayer.show(repo[n])
5100 displayer.close()
5104 displayer.close()
5101
5105
5102
5106
5103 @command(
5107 @command(
5104 b'paths',
5108 b'paths',
5105 formatteropts,
5109 formatteropts,
5106 _(b'[NAME]'),
5110 _(b'[NAME]'),
5107 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5111 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5108 optionalrepo=True,
5112 optionalrepo=True,
5109 intents={INTENT_READONLY},
5113 intents={INTENT_READONLY},
5110 )
5114 )
5111 def paths(ui, repo, search=None, **opts):
5115 def paths(ui, repo, search=None, **opts):
5112 """show aliases for remote repositories
5116 """show aliases for remote repositories
5113
5117
5114 Show definition of symbolic path name NAME. If no name is given,
5118 Show definition of symbolic path name NAME. If no name is given,
5115 show definition of all available names.
5119 show definition of all available names.
5116
5120
5117 Option -q/--quiet suppresses all output when searching for NAME
5121 Option -q/--quiet suppresses all output when searching for NAME
5118 and shows only the path names when listing all definitions.
5122 and shows only the path names when listing all definitions.
5119
5123
5120 Path names are defined in the [paths] section of your
5124 Path names are defined in the [paths] section of your
5121 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5125 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5122 repository, ``.hg/hgrc`` is used, too.
5126 repository, ``.hg/hgrc`` is used, too.
5123
5127
5124 The path names ``default`` and ``default-push`` have a special
5128 The path names ``default`` and ``default-push`` have a special
5125 meaning. When performing a push or pull operation, they are used
5129 meaning. When performing a push or pull operation, they are used
5126 as fallbacks if no location is specified on the command-line.
5130 as fallbacks if no location is specified on the command-line.
5127 When ``default-push`` is set, it will be used for push and
5131 When ``default-push`` is set, it will be used for push and
5128 ``default`` will be used for pull; otherwise ``default`` is used
5132 ``default`` will be used for pull; otherwise ``default`` is used
5129 as the fallback for both. When cloning a repository, the clone
5133 as the fallback for both. When cloning a repository, the clone
5130 source is written as ``default`` in ``.hg/hgrc``.
5134 source is written as ``default`` in ``.hg/hgrc``.
5131
5135
5132 .. note::
5136 .. note::
5133
5137
5134 ``default`` and ``default-push`` apply to all inbound (e.g.
5138 ``default`` and ``default-push`` apply to all inbound (e.g.
5135 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5139 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5136 and :hg:`bundle`) operations.
5140 and :hg:`bundle`) operations.
5137
5141
5138 See :hg:`help urls` for more information.
5142 See :hg:`help urls` for more information.
5139
5143
5140 .. container:: verbose
5144 .. container:: verbose
5141
5145
5142 Template:
5146 Template:
5143
5147
5144 The following keywords are supported. See also :hg:`help templates`.
5148 The following keywords are supported. See also :hg:`help templates`.
5145
5149
5146 :name: String. Symbolic name of the path alias.
5150 :name: String. Symbolic name of the path alias.
5147 :pushurl: String. URL for push operations.
5151 :pushurl: String. URL for push operations.
5148 :url: String. URL or directory path for the other operations.
5152 :url: String. URL or directory path for the other operations.
5149
5153
5150 Returns 0 on success.
5154 Returns 0 on success.
5151 """
5155 """
5152
5156
5153 opts = pycompat.byteskwargs(opts)
5157 opts = pycompat.byteskwargs(opts)
5154 ui.pager(b'paths')
5158 ui.pager(b'paths')
5155 if search:
5159 if search:
5156 pathitems = [
5160 pathitems = [
5157 (name, path)
5161 (name, path)
5158 for name, path in pycompat.iteritems(ui.paths)
5162 for name, path in pycompat.iteritems(ui.paths)
5159 if name == search
5163 if name == search
5160 ]
5164 ]
5161 else:
5165 else:
5162 pathitems = sorted(pycompat.iteritems(ui.paths))
5166 pathitems = sorted(pycompat.iteritems(ui.paths))
5163
5167
5164 fm = ui.formatter(b'paths', opts)
5168 fm = ui.formatter(b'paths', opts)
5165 if fm.isplain():
5169 if fm.isplain():
5166 hidepassword = util.hidepassword
5170 hidepassword = util.hidepassword
5167 else:
5171 else:
5168 hidepassword = bytes
5172 hidepassword = bytes
5169 if ui.quiet:
5173 if ui.quiet:
5170 namefmt = b'%s\n'
5174 namefmt = b'%s\n'
5171 else:
5175 else:
5172 namefmt = b'%s = '
5176 namefmt = b'%s = '
5173 showsubopts = not search and not ui.quiet
5177 showsubopts = not search and not ui.quiet
5174
5178
5175 for name, path in pathitems:
5179 for name, path in pathitems:
5176 fm.startitem()
5180 fm.startitem()
5177 fm.condwrite(not search, b'name', namefmt, name)
5181 fm.condwrite(not search, b'name', namefmt, name)
5178 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5182 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5179 for subopt, value in sorted(path.suboptions.items()):
5183 for subopt, value in sorted(path.suboptions.items()):
5180 assert subopt not in (b'name', b'url')
5184 assert subopt not in (b'name', b'url')
5181 if showsubopts:
5185 if showsubopts:
5182 fm.plain(b'%s:%s = ' % (name, subopt))
5186 fm.plain(b'%s:%s = ' % (name, subopt))
5183 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5187 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5184
5188
5185 fm.end()
5189 fm.end()
5186
5190
5187 if search and not pathitems:
5191 if search and not pathitems:
5188 if not ui.quiet:
5192 if not ui.quiet:
5189 ui.warn(_(b"not found!\n"))
5193 ui.warn(_(b"not found!\n"))
5190 return 1
5194 return 1
5191 else:
5195 else:
5192 return 0
5196 return 0
5193
5197
5194
5198
5195 @command(
5199 @command(
5196 b'phase',
5200 b'phase',
5197 [
5201 [
5198 (b'p', b'public', False, _(b'set changeset phase to public')),
5202 (b'p', b'public', False, _(b'set changeset phase to public')),
5199 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5203 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5200 (b's', b'secret', False, _(b'set changeset phase to secret')),
5204 (b's', b'secret', False, _(b'set changeset phase to secret')),
5201 (b'f', b'force', False, _(b'allow to move boundary backward')),
5205 (b'f', b'force', False, _(b'allow to move boundary backward')),
5202 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5206 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5203 ],
5207 ],
5204 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5208 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5205 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5209 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5206 )
5210 )
5207 def phase(ui, repo, *revs, **opts):
5211 def phase(ui, repo, *revs, **opts):
5208 """set or show the current phase name
5212 """set or show the current phase name
5209
5213
5210 With no argument, show the phase name of the current revision(s).
5214 With no argument, show the phase name of the current revision(s).
5211
5215
5212 With one of -p/--public, -d/--draft or -s/--secret, change the
5216 With one of -p/--public, -d/--draft or -s/--secret, change the
5213 phase value of the specified revisions.
5217 phase value of the specified revisions.
5214
5218
5215 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5219 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5216 lower phase to a higher phase. Phases are ordered as follows::
5220 lower phase to a higher phase. Phases are ordered as follows::
5217
5221
5218 public < draft < secret
5222 public < draft < secret
5219
5223
5220 Returns 0 on success, 1 if some phases could not be changed.
5224 Returns 0 on success, 1 if some phases could not be changed.
5221
5225
5222 (For more information about the phases concept, see :hg:`help phases`.)
5226 (For more information about the phases concept, see :hg:`help phases`.)
5223 """
5227 """
5224 opts = pycompat.byteskwargs(opts)
5228 opts = pycompat.byteskwargs(opts)
5225 # search for a unique phase argument
5229 # search for a unique phase argument
5226 targetphase = None
5230 targetphase = None
5227 for idx, name in enumerate(phases.cmdphasenames):
5231 for idx, name in enumerate(phases.cmdphasenames):
5228 if opts[name]:
5232 if opts[name]:
5229 if targetphase is not None:
5233 if targetphase is not None:
5230 raise error.Abort(_(b'only one phase can be specified'))
5234 raise error.Abort(_(b'only one phase can be specified'))
5231 targetphase = idx
5235 targetphase = idx
5232
5236
5233 # look for specified revision
5237 # look for specified revision
5234 revs = list(revs)
5238 revs = list(revs)
5235 revs.extend(opts[b'rev'])
5239 revs.extend(opts[b'rev'])
5236 if not revs:
5240 if not revs:
5237 # display both parents as the second parent phase can influence
5241 # display both parents as the second parent phase can influence
5238 # the phase of a merge commit
5242 # the phase of a merge commit
5239 revs = [c.rev() for c in repo[None].parents()]
5243 revs = [c.rev() for c in repo[None].parents()]
5240
5244
5241 revs = scmutil.revrange(repo, revs)
5245 revs = scmutil.revrange(repo, revs)
5242
5246
5243 ret = 0
5247 ret = 0
5244 if targetphase is None:
5248 if targetphase is None:
5245 # display
5249 # display
5246 for r in revs:
5250 for r in revs:
5247 ctx = repo[r]
5251 ctx = repo[r]
5248 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5252 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5249 else:
5253 else:
5250 with repo.lock(), repo.transaction(b"phase") as tr:
5254 with repo.lock(), repo.transaction(b"phase") as tr:
5251 # set phase
5255 # set phase
5252 if not revs:
5256 if not revs:
5253 raise error.Abort(_(b'empty revision set'))
5257 raise error.Abort(_(b'empty revision set'))
5254 nodes = [repo[r].node() for r in revs]
5258 nodes = [repo[r].node() for r in revs]
5255 # moving revision from public to draft may hide them
5259 # moving revision from public to draft may hide them
5256 # We have to check result on an unfiltered repository
5260 # We have to check result on an unfiltered repository
5257 unfi = repo.unfiltered()
5261 unfi = repo.unfiltered()
5258 getphase = unfi._phasecache.phase
5262 getphase = unfi._phasecache.phase
5259 olddata = [getphase(unfi, r) for r in unfi]
5263 olddata = [getphase(unfi, r) for r in unfi]
5260 phases.advanceboundary(repo, tr, targetphase, nodes)
5264 phases.advanceboundary(repo, tr, targetphase, nodes)
5261 if opts[b'force']:
5265 if opts[b'force']:
5262 phases.retractboundary(repo, tr, targetphase, nodes)
5266 phases.retractboundary(repo, tr, targetphase, nodes)
5263 getphase = unfi._phasecache.phase
5267 getphase = unfi._phasecache.phase
5264 newdata = [getphase(unfi, r) for r in unfi]
5268 newdata = [getphase(unfi, r) for r in unfi]
5265 changes = sum(newdata[r] != olddata[r] for r in unfi)
5269 changes = sum(newdata[r] != olddata[r] for r in unfi)
5266 cl = unfi.changelog
5270 cl = unfi.changelog
5267 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5271 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5268 if rejected:
5272 if rejected:
5269 ui.warn(
5273 ui.warn(
5270 _(
5274 _(
5271 b'cannot move %i changesets to a higher '
5275 b'cannot move %i changesets to a higher '
5272 b'phase, use --force\n'
5276 b'phase, use --force\n'
5273 )
5277 )
5274 % len(rejected)
5278 % len(rejected)
5275 )
5279 )
5276 ret = 1
5280 ret = 1
5277 if changes:
5281 if changes:
5278 msg = _(b'phase changed for %i changesets\n') % changes
5282 msg = _(b'phase changed for %i changesets\n') % changes
5279 if ret:
5283 if ret:
5280 ui.status(msg)
5284 ui.status(msg)
5281 else:
5285 else:
5282 ui.note(msg)
5286 ui.note(msg)
5283 else:
5287 else:
5284 ui.warn(_(b'no phases changed\n'))
5288 ui.warn(_(b'no phases changed\n'))
5285 return ret
5289 return ret
5286
5290
5287
5291
5288 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5292 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5289 """Run after a changegroup has been added via pull/unbundle
5293 """Run after a changegroup has been added via pull/unbundle
5290
5294
5291 This takes arguments below:
5295 This takes arguments below:
5292
5296
5293 :modheads: change of heads by pull/unbundle
5297 :modheads: change of heads by pull/unbundle
5294 :optupdate: updating working directory is needed or not
5298 :optupdate: updating working directory is needed or not
5295 :checkout: update destination revision (or None to default destination)
5299 :checkout: update destination revision (or None to default destination)
5296 :brev: a name, which might be a bookmark to be activated after updating
5300 :brev: a name, which might be a bookmark to be activated after updating
5297 """
5301 """
5298 if modheads == 0:
5302 if modheads == 0:
5299 return
5303 return
5300 if optupdate:
5304 if optupdate:
5301 try:
5305 try:
5302 return hg.updatetotally(ui, repo, checkout, brev)
5306 return hg.updatetotally(ui, repo, checkout, brev)
5303 except error.UpdateAbort as inst:
5307 except error.UpdateAbort as inst:
5304 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5308 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5305 hint = inst.hint
5309 hint = inst.hint
5306 raise error.UpdateAbort(msg, hint=hint)
5310 raise error.UpdateAbort(msg, hint=hint)
5307 if modheads is not None and modheads > 1:
5311 if modheads is not None and modheads > 1:
5308 currentbranchheads = len(repo.branchheads())
5312 currentbranchheads = len(repo.branchheads())
5309 if currentbranchheads == modheads:
5313 if currentbranchheads == modheads:
5310 ui.status(
5314 ui.status(
5311 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5315 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5312 )
5316 )
5313 elif currentbranchheads > 1:
5317 elif currentbranchheads > 1:
5314 ui.status(
5318 ui.status(
5315 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5319 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5316 )
5320 )
5317 else:
5321 else:
5318 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5322 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5319 elif not ui.configbool(b'commands', b'update.requiredest'):
5323 elif not ui.configbool(b'commands', b'update.requiredest'):
5320 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5324 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5321
5325
5322
5326
5323 @command(
5327 @command(
5324 b'pull',
5328 b'pull',
5325 [
5329 [
5326 (
5330 (
5327 b'u',
5331 b'u',
5328 b'update',
5332 b'update',
5329 None,
5333 None,
5330 _(b'update to new branch head if new descendants were pulled'),
5334 _(b'update to new branch head if new descendants were pulled'),
5331 ),
5335 ),
5332 (
5336 (
5333 b'f',
5337 b'f',
5334 b'force',
5338 b'force',
5335 None,
5339 None,
5336 _(b'run even when remote repository is unrelated'),
5340 _(b'run even when remote repository is unrelated'),
5337 ),
5341 ),
5338 (
5342 (
5339 b'r',
5343 b'r',
5340 b'rev',
5344 b'rev',
5341 [],
5345 [],
5342 _(b'a remote changeset intended to be added'),
5346 _(b'a remote changeset intended to be added'),
5343 _(b'REV'),
5347 _(b'REV'),
5344 ),
5348 ),
5345 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5349 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5346 (
5350 (
5347 b'b',
5351 b'b',
5348 b'branch',
5352 b'branch',
5349 [],
5353 [],
5350 _(b'a specific branch you would like to pull'),
5354 _(b'a specific branch you would like to pull'),
5351 _(b'BRANCH'),
5355 _(b'BRANCH'),
5352 ),
5356 ),
5353 ]
5357 ]
5354 + remoteopts,
5358 + remoteopts,
5355 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5359 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5356 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5360 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5357 helpbasic=True,
5361 helpbasic=True,
5358 )
5362 )
5359 def pull(ui, repo, source=b"default", **opts):
5363 def pull(ui, repo, source=b"default", **opts):
5360 """pull changes from the specified source
5364 """pull changes from the specified source
5361
5365
5362 Pull changes from a remote repository to a local one.
5366 Pull changes from a remote repository to a local one.
5363
5367
5364 This finds all changes from the repository at the specified path
5368 This finds all changes from the repository at the specified path
5365 or URL and adds them to a local repository (the current one unless
5369 or URL and adds them to a local repository (the current one unless
5366 -R is specified). By default, this does not update the copy of the
5370 -R is specified). By default, this does not update the copy of the
5367 project in the working directory.
5371 project in the working directory.
5368
5372
5369 When cloning from servers that support it, Mercurial may fetch
5373 When cloning from servers that support it, Mercurial may fetch
5370 pre-generated data. When this is done, hooks operating on incoming
5374 pre-generated data. When this is done, hooks operating on incoming
5371 changesets and changegroups may fire more than once, once for each
5375 changesets and changegroups may fire more than once, once for each
5372 pre-generated bundle and as well as for any additional remaining
5376 pre-generated bundle and as well as for any additional remaining
5373 data. See :hg:`help -e clonebundles` for more.
5377 data. See :hg:`help -e clonebundles` for more.
5374
5378
5375 Use :hg:`incoming` if you want to see what would have been added
5379 Use :hg:`incoming` if you want to see what would have been added
5376 by a pull at the time you issued this command. If you then decide
5380 by a pull at the time you issued this command. If you then decide
5377 to add those changes to the repository, you should use :hg:`pull
5381 to add those changes to the repository, you should use :hg:`pull
5378 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5382 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5379
5383
5380 If SOURCE is omitted, the 'default' path will be used.
5384 If SOURCE is omitted, the 'default' path will be used.
5381 See :hg:`help urls` for more information.
5385 See :hg:`help urls` for more information.
5382
5386
5383 Specifying bookmark as ``.`` is equivalent to specifying the active
5387 Specifying bookmark as ``.`` is equivalent to specifying the active
5384 bookmark's name.
5388 bookmark's name.
5385
5389
5386 Returns 0 on success, 1 if an update had unresolved files.
5390 Returns 0 on success, 1 if an update had unresolved files.
5387 """
5391 """
5388
5392
5389 opts = pycompat.byteskwargs(opts)
5393 opts = pycompat.byteskwargs(opts)
5390 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5394 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5391 b'update'
5395 b'update'
5392 ):
5396 ):
5393 msg = _(b'update destination required by configuration')
5397 msg = _(b'update destination required by configuration')
5394 hint = _(b'use hg pull followed by hg update DEST')
5398 hint = _(b'use hg pull followed by hg update DEST')
5395 raise error.Abort(msg, hint=hint)
5399 raise error.Abort(msg, hint=hint)
5396
5400
5397 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5401 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5398 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5402 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5399 other = hg.peer(repo, opts, source)
5403 other = hg.peer(repo, opts, source)
5400 try:
5404 try:
5401 revs, checkout = hg.addbranchrevs(
5405 revs, checkout = hg.addbranchrevs(
5402 repo, other, branches, opts.get(b'rev')
5406 repo, other, branches, opts.get(b'rev')
5403 )
5407 )
5404
5408
5405 pullopargs = {}
5409 pullopargs = {}
5406
5410
5407 nodes = None
5411 nodes = None
5408 if opts.get(b'bookmark') or revs:
5412 if opts.get(b'bookmark') or revs:
5409 # The list of bookmark used here is the same used to actually update
5413 # The list of bookmark used here is the same used to actually update
5410 # the bookmark names, to avoid the race from issue 4689 and we do
5414 # the bookmark names, to avoid the race from issue 4689 and we do
5411 # all lookup and bookmark queries in one go so they see the same
5415 # all lookup and bookmark queries in one go so they see the same
5412 # version of the server state (issue 4700).
5416 # version of the server state (issue 4700).
5413 nodes = []
5417 nodes = []
5414 fnodes = []
5418 fnodes = []
5415 revs = revs or []
5419 revs = revs or []
5416 if revs and not other.capable(b'lookup'):
5420 if revs and not other.capable(b'lookup'):
5417 err = _(
5421 err = _(
5418 b"other repository doesn't support revision lookup, "
5422 b"other repository doesn't support revision lookup, "
5419 b"so a rev cannot be specified."
5423 b"so a rev cannot be specified."
5420 )
5424 )
5421 raise error.Abort(err)
5425 raise error.Abort(err)
5422 with other.commandexecutor() as e:
5426 with other.commandexecutor() as e:
5423 fremotebookmarks = e.callcommand(
5427 fremotebookmarks = e.callcommand(
5424 b'listkeys', {b'namespace': b'bookmarks'}
5428 b'listkeys', {b'namespace': b'bookmarks'}
5425 )
5429 )
5426 for r in revs:
5430 for r in revs:
5427 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5431 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5428 remotebookmarks = fremotebookmarks.result()
5432 remotebookmarks = fremotebookmarks.result()
5429 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5433 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5430 pullopargs[b'remotebookmarks'] = remotebookmarks
5434 pullopargs[b'remotebookmarks'] = remotebookmarks
5431 for b in opts.get(b'bookmark', []):
5435 for b in opts.get(b'bookmark', []):
5432 b = repo._bookmarks.expandname(b)
5436 b = repo._bookmarks.expandname(b)
5433 if b not in remotebookmarks:
5437 if b not in remotebookmarks:
5434 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5438 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5435 nodes.append(remotebookmarks[b])
5439 nodes.append(remotebookmarks[b])
5436 for i, rev in enumerate(revs):
5440 for i, rev in enumerate(revs):
5437 node = fnodes[i].result()
5441 node = fnodes[i].result()
5438 nodes.append(node)
5442 nodes.append(node)
5439 if rev == checkout:
5443 if rev == checkout:
5440 checkout = node
5444 checkout = node
5441
5445
5442 wlock = util.nullcontextmanager()
5446 wlock = util.nullcontextmanager()
5443 if opts.get(b'update'):
5447 if opts.get(b'update'):
5444 wlock = repo.wlock()
5448 wlock = repo.wlock()
5445 with wlock:
5449 with wlock:
5446 pullopargs.update(opts.get(b'opargs', {}))
5450 pullopargs.update(opts.get(b'opargs', {}))
5447 modheads = exchange.pull(
5451 modheads = exchange.pull(
5448 repo,
5452 repo,
5449 other,
5453 other,
5450 heads=nodes,
5454 heads=nodes,
5451 force=opts.get(b'force'),
5455 force=opts.get(b'force'),
5452 bookmarks=opts.get(b'bookmark', ()),
5456 bookmarks=opts.get(b'bookmark', ()),
5453 opargs=pullopargs,
5457 opargs=pullopargs,
5454 ).cgresult
5458 ).cgresult
5455
5459
5456 # brev is a name, which might be a bookmark to be activated at
5460 # brev is a name, which might be a bookmark to be activated at
5457 # the end of the update. In other words, it is an explicit
5461 # the end of the update. In other words, it is an explicit
5458 # destination of the update
5462 # destination of the update
5459 brev = None
5463 brev = None
5460
5464
5461 if checkout:
5465 if checkout:
5462 checkout = repo.unfiltered().changelog.rev(checkout)
5466 checkout = repo.unfiltered().changelog.rev(checkout)
5463
5467
5464 # order below depends on implementation of
5468 # order below depends on implementation of
5465 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5469 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5466 # because 'checkout' is determined without it.
5470 # because 'checkout' is determined without it.
5467 if opts.get(b'rev'):
5471 if opts.get(b'rev'):
5468 brev = opts[b'rev'][0]
5472 brev = opts[b'rev'][0]
5469 elif opts.get(b'branch'):
5473 elif opts.get(b'branch'):
5470 brev = opts[b'branch'][0]
5474 brev = opts[b'branch'][0]
5471 else:
5475 else:
5472 brev = branches[0]
5476 brev = branches[0]
5473 repo._subtoppath = source
5477 repo._subtoppath = source
5474 try:
5478 try:
5475 ret = postincoming(
5479 ret = postincoming(
5476 ui, repo, modheads, opts.get(b'update'), checkout, brev
5480 ui, repo, modheads, opts.get(b'update'), checkout, brev
5477 )
5481 )
5478 except error.FilteredRepoLookupError as exc:
5482 except error.FilteredRepoLookupError as exc:
5479 msg = _(b'cannot update to target: %s') % exc.args[0]
5483 msg = _(b'cannot update to target: %s') % exc.args[0]
5480 exc.args = (msg,) + exc.args[1:]
5484 exc.args = (msg,) + exc.args[1:]
5481 raise
5485 raise
5482 finally:
5486 finally:
5483 del repo._subtoppath
5487 del repo._subtoppath
5484
5488
5485 finally:
5489 finally:
5486 other.close()
5490 other.close()
5487 return ret
5491 return ret
5488
5492
5489
5493
5490 @command(
5494 @command(
5491 b'push',
5495 b'push',
5492 [
5496 [
5493 (b'f', b'force', None, _(b'force push')),
5497 (b'f', b'force', None, _(b'force push')),
5494 (
5498 (
5495 b'r',
5499 b'r',
5496 b'rev',
5500 b'rev',
5497 [],
5501 [],
5498 _(b'a changeset intended to be included in the destination'),
5502 _(b'a changeset intended to be included in the destination'),
5499 _(b'REV'),
5503 _(b'REV'),
5500 ),
5504 ),
5501 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5505 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5502 (
5506 (
5503 b'b',
5507 b'b',
5504 b'branch',
5508 b'branch',
5505 [],
5509 [],
5506 _(b'a specific branch you would like to push'),
5510 _(b'a specific branch you would like to push'),
5507 _(b'BRANCH'),
5511 _(b'BRANCH'),
5508 ),
5512 ),
5509 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5513 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5510 (
5514 (
5511 b'',
5515 b'',
5512 b'pushvars',
5516 b'pushvars',
5513 [],
5517 [],
5514 _(b'variables that can be sent to server (ADVANCED)'),
5518 _(b'variables that can be sent to server (ADVANCED)'),
5515 ),
5519 ),
5516 (
5520 (
5517 b'',
5521 b'',
5518 b'publish',
5522 b'publish',
5519 False,
5523 False,
5520 _(b'push the changeset as public (EXPERIMENTAL)'),
5524 _(b'push the changeset as public (EXPERIMENTAL)'),
5521 ),
5525 ),
5522 ]
5526 ]
5523 + remoteopts,
5527 + remoteopts,
5524 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5528 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5525 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5529 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5526 helpbasic=True,
5530 helpbasic=True,
5527 )
5531 )
5528 def push(ui, repo, dest=None, **opts):
5532 def push(ui, repo, dest=None, **opts):
5529 """push changes to the specified destination
5533 """push changes to the specified destination
5530
5534
5531 Push changesets from the local repository to the specified
5535 Push changesets from the local repository to the specified
5532 destination.
5536 destination.
5533
5537
5534 This operation is symmetrical to pull: it is identical to a pull
5538 This operation is symmetrical to pull: it is identical to a pull
5535 in the destination repository from the current one.
5539 in the destination repository from the current one.
5536
5540
5537 By default, push will not allow creation of new heads at the
5541 By default, push will not allow creation of new heads at the
5538 destination, since multiple heads would make it unclear which head
5542 destination, since multiple heads would make it unclear which head
5539 to use. In this situation, it is recommended to pull and merge
5543 to use. In this situation, it is recommended to pull and merge
5540 before pushing.
5544 before pushing.
5541
5545
5542 Use --new-branch if you want to allow push to create a new named
5546 Use --new-branch if you want to allow push to create a new named
5543 branch that is not present at the destination. This allows you to
5547 branch that is not present at the destination. This allows you to
5544 only create a new branch without forcing other changes.
5548 only create a new branch without forcing other changes.
5545
5549
5546 .. note::
5550 .. note::
5547
5551
5548 Extra care should be taken with the -f/--force option,
5552 Extra care should be taken with the -f/--force option,
5549 which will push all new heads on all branches, an action which will
5553 which will push all new heads on all branches, an action which will
5550 almost always cause confusion for collaborators.
5554 almost always cause confusion for collaborators.
5551
5555
5552 If -r/--rev is used, the specified revision and all its ancestors
5556 If -r/--rev is used, the specified revision and all its ancestors
5553 will be pushed to the remote repository.
5557 will be pushed to the remote repository.
5554
5558
5555 If -B/--bookmark is used, the specified bookmarked revision, its
5559 If -B/--bookmark is used, the specified bookmarked revision, its
5556 ancestors, and the bookmark will be pushed to the remote
5560 ancestors, and the bookmark will be pushed to the remote
5557 repository. Specifying ``.`` is equivalent to specifying the active
5561 repository. Specifying ``.`` is equivalent to specifying the active
5558 bookmark's name.
5562 bookmark's name.
5559
5563
5560 Please see :hg:`help urls` for important details about ``ssh://``
5564 Please see :hg:`help urls` for important details about ``ssh://``
5561 URLs. If DESTINATION is omitted, a default path will be used.
5565 URLs. If DESTINATION is omitted, a default path will be used.
5562
5566
5563 .. container:: verbose
5567 .. container:: verbose
5564
5568
5565 The --pushvars option sends strings to the server that become
5569 The --pushvars option sends strings to the server that become
5566 environment variables prepended with ``HG_USERVAR_``. For example,
5570 environment variables prepended with ``HG_USERVAR_``. For example,
5567 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5571 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5568 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5572 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5569
5573
5570 pushvars can provide for user-overridable hooks as well as set debug
5574 pushvars can provide for user-overridable hooks as well as set debug
5571 levels. One example is having a hook that blocks commits containing
5575 levels. One example is having a hook that blocks commits containing
5572 conflict markers, but enables the user to override the hook if the file
5576 conflict markers, but enables the user to override the hook if the file
5573 is using conflict markers for testing purposes or the file format has
5577 is using conflict markers for testing purposes or the file format has
5574 strings that look like conflict markers.
5578 strings that look like conflict markers.
5575
5579
5576 By default, servers will ignore `--pushvars`. To enable it add the
5580 By default, servers will ignore `--pushvars`. To enable it add the
5577 following to your configuration file::
5581 following to your configuration file::
5578
5582
5579 [push]
5583 [push]
5580 pushvars.server = true
5584 pushvars.server = true
5581
5585
5582 Returns 0 if push was successful, 1 if nothing to push.
5586 Returns 0 if push was successful, 1 if nothing to push.
5583 """
5587 """
5584
5588
5585 opts = pycompat.byteskwargs(opts)
5589 opts = pycompat.byteskwargs(opts)
5586 if opts.get(b'bookmark'):
5590 if opts.get(b'bookmark'):
5587 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5591 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5588 for b in opts[b'bookmark']:
5592 for b in opts[b'bookmark']:
5589 # translate -B options to -r so changesets get pushed
5593 # translate -B options to -r so changesets get pushed
5590 b = repo._bookmarks.expandname(b)
5594 b = repo._bookmarks.expandname(b)
5591 if b in repo._bookmarks:
5595 if b in repo._bookmarks:
5592 opts.setdefault(b'rev', []).append(b)
5596 opts.setdefault(b'rev', []).append(b)
5593 else:
5597 else:
5594 # if we try to push a deleted bookmark, translate it to null
5598 # if we try to push a deleted bookmark, translate it to null
5595 # this lets simultaneous -r, -b options continue working
5599 # this lets simultaneous -r, -b options continue working
5596 opts.setdefault(b'rev', []).append(b"null")
5600 opts.setdefault(b'rev', []).append(b"null")
5597
5601
5598 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5602 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5599 if not path:
5603 if not path:
5600 raise error.Abort(
5604 raise error.Abort(
5601 _(b'default repository not configured!'),
5605 _(b'default repository not configured!'),
5602 hint=_(b"see 'hg help config.paths'"),
5606 hint=_(b"see 'hg help config.paths'"),
5603 )
5607 )
5604 dest = path.pushloc or path.loc
5608 dest = path.pushloc or path.loc
5605 branches = (path.branch, opts.get(b'branch') or [])
5609 branches = (path.branch, opts.get(b'branch') or [])
5606 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5610 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5607 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5611 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5608 other = hg.peer(repo, opts, dest)
5612 other = hg.peer(repo, opts, dest)
5609
5613
5610 if revs:
5614 if revs:
5611 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5615 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5612 if not revs:
5616 if not revs:
5613 raise error.Abort(
5617 raise error.Abort(
5614 _(b"specified revisions evaluate to an empty set"),
5618 _(b"specified revisions evaluate to an empty set"),
5615 hint=_(b"use different revision arguments"),
5619 hint=_(b"use different revision arguments"),
5616 )
5620 )
5617 elif path.pushrev:
5621 elif path.pushrev:
5618 # It doesn't make any sense to specify ancestor revisions. So limit
5622 # It doesn't make any sense to specify ancestor revisions. So limit
5619 # to DAG heads to make discovery simpler.
5623 # to DAG heads to make discovery simpler.
5620 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5624 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5621 revs = scmutil.revrange(repo, [expr])
5625 revs = scmutil.revrange(repo, [expr])
5622 revs = [repo[rev].node() for rev in revs]
5626 revs = [repo[rev].node() for rev in revs]
5623 if not revs:
5627 if not revs:
5624 raise error.Abort(
5628 raise error.Abort(
5625 _(b'default push revset for path evaluates to an empty set')
5629 _(b'default push revset for path evaluates to an empty set')
5626 )
5630 )
5627 elif ui.configbool(b'commands', b'push.require-revs'):
5631 elif ui.configbool(b'commands', b'push.require-revs'):
5628 raise error.Abort(
5632 raise error.Abort(
5629 _(b'no revisions specified to push'),
5633 _(b'no revisions specified to push'),
5630 hint=_(b'did you mean "hg push -r ."?'),
5634 hint=_(b'did you mean "hg push -r ."?'),
5631 )
5635 )
5632
5636
5633 repo._subtoppath = dest
5637 repo._subtoppath = dest
5634 try:
5638 try:
5635 # push subrepos depth-first for coherent ordering
5639 # push subrepos depth-first for coherent ordering
5636 c = repo[b'.']
5640 c = repo[b'.']
5637 subs = c.substate # only repos that are committed
5641 subs = c.substate # only repos that are committed
5638 for s in sorted(subs):
5642 for s in sorted(subs):
5639 result = c.sub(s).push(opts)
5643 result = c.sub(s).push(opts)
5640 if result == 0:
5644 if result == 0:
5641 return not result
5645 return not result
5642 finally:
5646 finally:
5643 del repo._subtoppath
5647 del repo._subtoppath
5644
5648
5645 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5649 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5646 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5650 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5647
5651
5648 pushop = exchange.push(
5652 pushop = exchange.push(
5649 repo,
5653 repo,
5650 other,
5654 other,
5651 opts.get(b'force'),
5655 opts.get(b'force'),
5652 revs=revs,
5656 revs=revs,
5653 newbranch=opts.get(b'new_branch'),
5657 newbranch=opts.get(b'new_branch'),
5654 bookmarks=opts.get(b'bookmark', ()),
5658 bookmarks=opts.get(b'bookmark', ()),
5655 publish=opts.get(b'publish'),
5659 publish=opts.get(b'publish'),
5656 opargs=opargs,
5660 opargs=opargs,
5657 )
5661 )
5658
5662
5659 result = not pushop.cgresult
5663 result = not pushop.cgresult
5660
5664
5661 if pushop.bkresult is not None:
5665 if pushop.bkresult is not None:
5662 if pushop.bkresult == 2:
5666 if pushop.bkresult == 2:
5663 result = 2
5667 result = 2
5664 elif not result and pushop.bkresult:
5668 elif not result and pushop.bkresult:
5665 result = 2
5669 result = 2
5666
5670
5667 return result
5671 return result
5668
5672
5669
5673
5670 @command(
5674 @command(
5671 b'recover',
5675 b'recover',
5672 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5676 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5673 helpcategory=command.CATEGORY_MAINTENANCE,
5677 helpcategory=command.CATEGORY_MAINTENANCE,
5674 )
5678 )
5675 def recover(ui, repo, **opts):
5679 def recover(ui, repo, **opts):
5676 """roll back an interrupted transaction
5680 """roll back an interrupted transaction
5677
5681
5678 Recover from an interrupted commit or pull.
5682 Recover from an interrupted commit or pull.
5679
5683
5680 This command tries to fix the repository status after an
5684 This command tries to fix the repository status after an
5681 interrupted operation. It should only be necessary when Mercurial
5685 interrupted operation. It should only be necessary when Mercurial
5682 suggests it.
5686 suggests it.
5683
5687
5684 Returns 0 if successful, 1 if nothing to recover or verify fails.
5688 Returns 0 if successful, 1 if nothing to recover or verify fails.
5685 """
5689 """
5686 ret = repo.recover()
5690 ret = repo.recover()
5687 if ret:
5691 if ret:
5688 if opts['verify']:
5692 if opts['verify']:
5689 return hg.verify(repo)
5693 return hg.verify(repo)
5690 else:
5694 else:
5691 msg = _(
5695 msg = _(
5692 b"(verify step skipped, run `hg verify` to check your "
5696 b"(verify step skipped, run `hg verify` to check your "
5693 b"repository content)\n"
5697 b"repository content)\n"
5694 )
5698 )
5695 ui.warn(msg)
5699 ui.warn(msg)
5696 return 0
5700 return 0
5697 return 1
5701 return 1
5698
5702
5699
5703
5700 @command(
5704 @command(
5701 b'remove|rm',
5705 b'remove|rm',
5702 [
5706 [
5703 (b'A', b'after', None, _(b'record delete for missing files')),
5707 (b'A', b'after', None, _(b'record delete for missing files')),
5704 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5708 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5705 ]
5709 ]
5706 + subrepoopts
5710 + subrepoopts
5707 + walkopts
5711 + walkopts
5708 + dryrunopts,
5712 + dryrunopts,
5709 _(b'[OPTION]... FILE...'),
5713 _(b'[OPTION]... FILE...'),
5710 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5714 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5711 helpbasic=True,
5715 helpbasic=True,
5712 inferrepo=True,
5716 inferrepo=True,
5713 )
5717 )
5714 def remove(ui, repo, *pats, **opts):
5718 def remove(ui, repo, *pats, **opts):
5715 """remove the specified files on the next commit
5719 """remove the specified files on the next commit
5716
5720
5717 Schedule the indicated files for removal from the current branch.
5721 Schedule the indicated files for removal from the current branch.
5718
5722
5719 This command schedules the files to be removed at the next commit.
5723 This command schedules the files to be removed at the next commit.
5720 To undo a remove before that, see :hg:`revert`. To undo added
5724 To undo a remove before that, see :hg:`revert`. To undo added
5721 files, see :hg:`forget`.
5725 files, see :hg:`forget`.
5722
5726
5723 .. container:: verbose
5727 .. container:: verbose
5724
5728
5725 -A/--after can be used to remove only files that have already
5729 -A/--after can be used to remove only files that have already
5726 been deleted, -f/--force can be used to force deletion, and -Af
5730 been deleted, -f/--force can be used to force deletion, and -Af
5727 can be used to remove files from the next revision without
5731 can be used to remove files from the next revision without
5728 deleting them from the working directory.
5732 deleting them from the working directory.
5729
5733
5730 The following table details the behavior of remove for different
5734 The following table details the behavior of remove for different
5731 file states (columns) and option combinations (rows). The file
5735 file states (columns) and option combinations (rows). The file
5732 states are Added [A], Clean [C], Modified [M] and Missing [!]
5736 states are Added [A], Clean [C], Modified [M] and Missing [!]
5733 (as reported by :hg:`status`). The actions are Warn, Remove
5737 (as reported by :hg:`status`). The actions are Warn, Remove
5734 (from branch) and Delete (from disk):
5738 (from branch) and Delete (from disk):
5735
5739
5736 ========= == == == ==
5740 ========= == == == ==
5737 opt/state A C M !
5741 opt/state A C M !
5738 ========= == == == ==
5742 ========= == == == ==
5739 none W RD W R
5743 none W RD W R
5740 -f R RD RD R
5744 -f R RD RD R
5741 -A W W W R
5745 -A W W W R
5742 -Af R R R R
5746 -Af R R R R
5743 ========= == == == ==
5747 ========= == == == ==
5744
5748
5745 .. note::
5749 .. note::
5746
5750
5747 :hg:`remove` never deletes files in Added [A] state from the
5751 :hg:`remove` never deletes files in Added [A] state from the
5748 working directory, not even if ``--force`` is specified.
5752 working directory, not even if ``--force`` is specified.
5749
5753
5750 Returns 0 on success, 1 if any warnings encountered.
5754 Returns 0 on success, 1 if any warnings encountered.
5751 """
5755 """
5752
5756
5753 opts = pycompat.byteskwargs(opts)
5757 opts = pycompat.byteskwargs(opts)
5754 after, force = opts.get(b'after'), opts.get(b'force')
5758 after, force = opts.get(b'after'), opts.get(b'force')
5755 dryrun = opts.get(b'dry_run')
5759 dryrun = opts.get(b'dry_run')
5756 if not pats and not after:
5760 if not pats and not after:
5757 raise error.Abort(_(b'no files specified'))
5761 raise error.Abort(_(b'no files specified'))
5758
5762
5759 m = scmutil.match(repo[None], pats, opts)
5763 m = scmutil.match(repo[None], pats, opts)
5760 subrepos = opts.get(b'subrepos')
5764 subrepos = opts.get(b'subrepos')
5761 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5765 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5762 return cmdutil.remove(
5766 return cmdutil.remove(
5763 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5767 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5764 )
5768 )
5765
5769
5766
5770
5767 @command(
5771 @command(
5768 b'rename|move|mv',
5772 b'rename|move|mv',
5769 [
5773 [
5770 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5774 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5771 (
5775 (
5772 b'f',
5776 b'f',
5773 b'force',
5777 b'force',
5774 None,
5778 None,
5775 _(b'forcibly move over an existing managed file'),
5779 _(b'forcibly move over an existing managed file'),
5776 ),
5780 ),
5777 ]
5781 ]
5778 + walkopts
5782 + walkopts
5779 + dryrunopts,
5783 + dryrunopts,
5780 _(b'[OPTION]... SOURCE... DEST'),
5784 _(b'[OPTION]... SOURCE... DEST'),
5781 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5785 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5782 )
5786 )
5783 def rename(ui, repo, *pats, **opts):
5787 def rename(ui, repo, *pats, **opts):
5784 """rename files; equivalent of copy + remove
5788 """rename files; equivalent of copy + remove
5785
5789
5786 Mark dest as copies of sources; mark sources for deletion. If dest
5790 Mark dest as copies of sources; mark sources for deletion. If dest
5787 is a directory, copies are put in that directory. If dest is a
5791 is a directory, copies are put in that directory. If dest is a
5788 file, there can only be one source.
5792 file, there can only be one source.
5789
5793
5790 By default, this command copies the contents of files as they
5794 By default, this command copies the contents of files as they
5791 exist in the working directory. If invoked with -A/--after, the
5795 exist in the working directory. If invoked with -A/--after, the
5792 operation is recorded, but no copying is performed.
5796 operation is recorded, but no copying is performed.
5793
5797
5794 This command takes effect at the next commit. To undo a rename
5798 This command takes effect at the next commit. To undo a rename
5795 before that, see :hg:`revert`.
5799 before that, see :hg:`revert`.
5796
5800
5797 Returns 0 on success, 1 if errors are encountered.
5801 Returns 0 on success, 1 if errors are encountered.
5798 """
5802 """
5799 opts = pycompat.byteskwargs(opts)
5803 opts = pycompat.byteskwargs(opts)
5800 with repo.wlock(False):
5804 with repo.wlock(False):
5801 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5805 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5802
5806
5803
5807
5804 @command(
5808 @command(
5805 b'resolve',
5809 b'resolve',
5806 [
5810 [
5807 (b'a', b'all', None, _(b'select all unresolved files')),
5811 (b'a', b'all', None, _(b'select all unresolved files')),
5808 (b'l', b'list', None, _(b'list state of files needing merge')),
5812 (b'l', b'list', None, _(b'list state of files needing merge')),
5809 (b'm', b'mark', None, _(b'mark files as resolved')),
5813 (b'm', b'mark', None, _(b'mark files as resolved')),
5810 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5814 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5811 (b'n', b'no-status', None, _(b'hide status prefix')),
5815 (b'n', b'no-status', None, _(b'hide status prefix')),
5812 (b'', b're-merge', None, _(b're-merge files')),
5816 (b'', b're-merge', None, _(b're-merge files')),
5813 ]
5817 ]
5814 + mergetoolopts
5818 + mergetoolopts
5815 + walkopts
5819 + walkopts
5816 + formatteropts,
5820 + formatteropts,
5817 _(b'[OPTION]... [FILE]...'),
5821 _(b'[OPTION]... [FILE]...'),
5818 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5822 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5819 inferrepo=True,
5823 inferrepo=True,
5820 )
5824 )
5821 def resolve(ui, repo, *pats, **opts):
5825 def resolve(ui, repo, *pats, **opts):
5822 """redo merges or set/view the merge status of files
5826 """redo merges or set/view the merge status of files
5823
5827
5824 Merges with unresolved conflicts are often the result of
5828 Merges with unresolved conflicts are often the result of
5825 non-interactive merging using the ``internal:merge`` configuration
5829 non-interactive merging using the ``internal:merge`` configuration
5826 setting, or a command-line merge tool like ``diff3``. The resolve
5830 setting, or a command-line merge tool like ``diff3``. The resolve
5827 command is used to manage the files involved in a merge, after
5831 command is used to manage the files involved in a merge, after
5828 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5832 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5829 working directory must have two parents). See :hg:`help
5833 working directory must have two parents). See :hg:`help
5830 merge-tools` for information on configuring merge tools.
5834 merge-tools` for information on configuring merge tools.
5831
5835
5832 The resolve command can be used in the following ways:
5836 The resolve command can be used in the following ways:
5833
5837
5834 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5838 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5835 the specified files, discarding any previous merge attempts. Re-merging
5839 the specified files, discarding any previous merge attempts. Re-merging
5836 is not performed for files already marked as resolved. Use ``--all/-a``
5840 is not performed for files already marked as resolved. Use ``--all/-a``
5837 to select all unresolved files. ``--tool`` can be used to specify
5841 to select all unresolved files. ``--tool`` can be used to specify
5838 the merge tool used for the given files. It overrides the HGMERGE
5842 the merge tool used for the given files. It overrides the HGMERGE
5839 environment variable and your configuration files. Previous file
5843 environment variable and your configuration files. Previous file
5840 contents are saved with a ``.orig`` suffix.
5844 contents are saved with a ``.orig`` suffix.
5841
5845
5842 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5846 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5843 (e.g. after having manually fixed-up the files). The default is
5847 (e.g. after having manually fixed-up the files). The default is
5844 to mark all unresolved files.
5848 to mark all unresolved files.
5845
5849
5846 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5850 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5847 default is to mark all resolved files.
5851 default is to mark all resolved files.
5848
5852
5849 - :hg:`resolve -l`: list files which had or still have conflicts.
5853 - :hg:`resolve -l`: list files which had or still have conflicts.
5850 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5854 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5851 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5855 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5852 the list. See :hg:`help filesets` for details.
5856 the list. See :hg:`help filesets` for details.
5853
5857
5854 .. note::
5858 .. note::
5855
5859
5856 Mercurial will not let you commit files with unresolved merge
5860 Mercurial will not let you commit files with unresolved merge
5857 conflicts. You must use :hg:`resolve -m ...` before you can
5861 conflicts. You must use :hg:`resolve -m ...` before you can
5858 commit after a conflicting merge.
5862 commit after a conflicting merge.
5859
5863
5860 .. container:: verbose
5864 .. container:: verbose
5861
5865
5862 Template:
5866 Template:
5863
5867
5864 The following keywords are supported in addition to the common template
5868 The following keywords are supported in addition to the common template
5865 keywords and functions. See also :hg:`help templates`.
5869 keywords and functions. See also :hg:`help templates`.
5866
5870
5867 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5871 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5868 :path: String. Repository-absolute path of the file.
5872 :path: String. Repository-absolute path of the file.
5869
5873
5870 Returns 0 on success, 1 if any files fail a resolve attempt.
5874 Returns 0 on success, 1 if any files fail a resolve attempt.
5871 """
5875 """
5872
5876
5873 opts = pycompat.byteskwargs(opts)
5877 opts = pycompat.byteskwargs(opts)
5874 confirm = ui.configbool(b'commands', b'resolve.confirm')
5878 confirm = ui.configbool(b'commands', b'resolve.confirm')
5875 flaglist = b'all mark unmark list no_status re_merge'.split()
5879 flaglist = b'all mark unmark list no_status re_merge'.split()
5876 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5880 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5877
5881
5878 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5882 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5879 if actioncount > 1:
5883 if actioncount > 1:
5880 raise error.Abort(_(b"too many actions specified"))
5884 raise error.Abort(_(b"too many actions specified"))
5881 elif actioncount == 0 and ui.configbool(
5885 elif actioncount == 0 and ui.configbool(
5882 b'commands', b'resolve.explicit-re-merge'
5886 b'commands', b'resolve.explicit-re-merge'
5883 ):
5887 ):
5884 hint = _(b'use --mark, --unmark, --list or --re-merge')
5888 hint = _(b'use --mark, --unmark, --list or --re-merge')
5885 raise error.Abort(_(b'no action specified'), hint=hint)
5889 raise error.Abort(_(b'no action specified'), hint=hint)
5886 if pats and all:
5890 if pats and all:
5887 raise error.Abort(_(b"can't specify --all and patterns"))
5891 raise error.Abort(_(b"can't specify --all and patterns"))
5888 if not (all or pats or show or mark or unmark):
5892 if not (all or pats or show or mark or unmark):
5889 raise error.Abort(
5893 raise error.Abort(
5890 _(b'no files or directories specified'),
5894 _(b'no files or directories specified'),
5891 hint=b'use --all to re-merge all unresolved files',
5895 hint=b'use --all to re-merge all unresolved files',
5892 )
5896 )
5893
5897
5894 if confirm:
5898 if confirm:
5895 if all:
5899 if all:
5896 if ui.promptchoice(
5900 if ui.promptchoice(
5897 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5901 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5898 ):
5902 ):
5899 raise error.Abort(_(b'user quit'))
5903 raise error.Abort(_(b'user quit'))
5900 if mark and not pats:
5904 if mark and not pats:
5901 if ui.promptchoice(
5905 if ui.promptchoice(
5902 _(
5906 _(
5903 b'mark all unresolved files as resolved (yn)?'
5907 b'mark all unresolved files as resolved (yn)?'
5904 b'$$ &Yes $$ &No'
5908 b'$$ &Yes $$ &No'
5905 )
5909 )
5906 ):
5910 ):
5907 raise error.Abort(_(b'user quit'))
5911 raise error.Abort(_(b'user quit'))
5908 if unmark and not pats:
5912 if unmark and not pats:
5909 if ui.promptchoice(
5913 if ui.promptchoice(
5910 _(
5914 _(
5911 b'mark all resolved files as unresolved (yn)?'
5915 b'mark all resolved files as unresolved (yn)?'
5912 b'$$ &Yes $$ &No'
5916 b'$$ &Yes $$ &No'
5913 )
5917 )
5914 ):
5918 ):
5915 raise error.Abort(_(b'user quit'))
5919 raise error.Abort(_(b'user quit'))
5916
5920
5917 uipathfn = scmutil.getuipathfn(repo)
5921 uipathfn = scmutil.getuipathfn(repo)
5918
5922
5919 if show:
5923 if show:
5920 ui.pager(b'resolve')
5924 ui.pager(b'resolve')
5921 fm = ui.formatter(b'resolve', opts)
5925 fm = ui.formatter(b'resolve', opts)
5922 ms = mergemod.mergestate.read(repo)
5926 ms = mergemod.mergestate.read(repo)
5923 wctx = repo[None]
5927 wctx = repo[None]
5924 m = scmutil.match(wctx, pats, opts)
5928 m = scmutil.match(wctx, pats, opts)
5925
5929
5926 # Labels and keys based on merge state. Unresolved path conflicts show
5930 # Labels and keys based on merge state. Unresolved path conflicts show
5927 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5931 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5928 # resolved conflicts.
5932 # resolved conflicts.
5929 mergestateinfo = {
5933 mergestateinfo = {
5930 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5934 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5931 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5935 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5932 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5936 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5933 b'resolve.unresolved',
5937 b'resolve.unresolved',
5934 b'P',
5938 b'P',
5935 ),
5939 ),
5936 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5940 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5937 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5941 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5938 b'resolve.driverresolved',
5942 b'resolve.driverresolved',
5939 b'D',
5943 b'D',
5940 ),
5944 ),
5941 }
5945 }
5942
5946
5943 for f in ms:
5947 for f in ms:
5944 if not m(f):
5948 if not m(f):
5945 continue
5949 continue
5946
5950
5947 label, key = mergestateinfo[ms[f]]
5951 label, key = mergestateinfo[ms[f]]
5948 fm.startitem()
5952 fm.startitem()
5949 fm.context(ctx=wctx)
5953 fm.context(ctx=wctx)
5950 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5954 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5951 fm.data(path=f)
5955 fm.data(path=f)
5952 fm.plain(b'%s\n' % uipathfn(f), label=label)
5956 fm.plain(b'%s\n' % uipathfn(f), label=label)
5953 fm.end()
5957 fm.end()
5954 return 0
5958 return 0
5955
5959
5956 with repo.wlock():
5960 with repo.wlock():
5957 ms = mergemod.mergestate.read(repo)
5961 ms = mergemod.mergestate.read(repo)
5958
5962
5959 if not (ms.active() or repo.dirstate.p2() != nullid):
5963 if not (ms.active() or repo.dirstate.p2() != nullid):
5960 raise error.Abort(
5964 raise error.Abort(
5961 _(b'resolve command not applicable when not merging')
5965 _(b'resolve command not applicable when not merging')
5962 )
5966 )
5963
5967
5964 wctx = repo[None]
5968 wctx = repo[None]
5965
5969
5966 if (
5970 if (
5967 ms.mergedriver
5971 ms.mergedriver
5968 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5972 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5969 ):
5973 ):
5970 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5974 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5971 ms.commit()
5975 ms.commit()
5972 # allow mark and unmark to go through
5976 # allow mark and unmark to go through
5973 if not mark and not unmark and not proceed:
5977 if not mark and not unmark and not proceed:
5974 return 1
5978 return 1
5975
5979
5976 m = scmutil.match(wctx, pats, opts)
5980 m = scmutil.match(wctx, pats, opts)
5977 ret = 0
5981 ret = 0
5978 didwork = False
5982 didwork = False
5979 runconclude = False
5983 runconclude = False
5980
5984
5981 tocomplete = []
5985 tocomplete = []
5982 hasconflictmarkers = []
5986 hasconflictmarkers = []
5983 if mark:
5987 if mark:
5984 markcheck = ui.config(b'commands', b'resolve.mark-check')
5988 markcheck = ui.config(b'commands', b'resolve.mark-check')
5985 if markcheck not in [b'warn', b'abort']:
5989 if markcheck not in [b'warn', b'abort']:
5986 # Treat all invalid / unrecognized values as 'none'.
5990 # Treat all invalid / unrecognized values as 'none'.
5987 markcheck = False
5991 markcheck = False
5988 for f in ms:
5992 for f in ms:
5989 if not m(f):
5993 if not m(f):
5990 continue
5994 continue
5991
5995
5992 didwork = True
5996 didwork = True
5993
5997
5994 # don't let driver-resolved files be marked, and run the conclude
5998 # don't let driver-resolved files be marked, and run the conclude
5995 # step if asked to resolve
5999 # step if asked to resolve
5996 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6000 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
5997 exact = m.exact(f)
6001 exact = m.exact(f)
5998 if mark:
6002 if mark:
5999 if exact:
6003 if exact:
6000 ui.warn(
6004 ui.warn(
6001 _(b'not marking %s as it is driver-resolved\n')
6005 _(b'not marking %s as it is driver-resolved\n')
6002 % uipathfn(f)
6006 % uipathfn(f)
6003 )
6007 )
6004 elif unmark:
6008 elif unmark:
6005 if exact:
6009 if exact:
6006 ui.warn(
6010 ui.warn(
6007 _(b'not unmarking %s as it is driver-resolved\n')
6011 _(b'not unmarking %s as it is driver-resolved\n')
6008 % uipathfn(f)
6012 % uipathfn(f)
6009 )
6013 )
6010 else:
6014 else:
6011 runconclude = True
6015 runconclude = True
6012 continue
6016 continue
6013
6017
6014 # path conflicts must be resolved manually
6018 # path conflicts must be resolved manually
6015 if ms[f] in (
6019 if ms[f] in (
6016 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6020 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6017 mergemod.MERGE_RECORD_RESOLVED_PATH,
6021 mergemod.MERGE_RECORD_RESOLVED_PATH,
6018 ):
6022 ):
6019 if mark:
6023 if mark:
6020 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6024 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6021 elif unmark:
6025 elif unmark:
6022 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6026 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6023 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6027 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6024 ui.warn(
6028 ui.warn(
6025 _(b'%s: path conflict must be resolved manually\n')
6029 _(b'%s: path conflict must be resolved manually\n')
6026 % uipathfn(f)
6030 % uipathfn(f)
6027 )
6031 )
6028 continue
6032 continue
6029
6033
6030 if mark:
6034 if mark:
6031 if markcheck:
6035 if markcheck:
6032 fdata = repo.wvfs.tryread(f)
6036 fdata = repo.wvfs.tryread(f)
6033 if (
6037 if (
6034 filemerge.hasconflictmarkers(fdata)
6038 filemerge.hasconflictmarkers(fdata)
6035 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6039 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6036 ):
6040 ):
6037 hasconflictmarkers.append(f)
6041 hasconflictmarkers.append(f)
6038 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6042 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6039 elif unmark:
6043 elif unmark:
6040 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6044 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6041 else:
6045 else:
6042 # backup pre-resolve (merge uses .orig for its own purposes)
6046 # backup pre-resolve (merge uses .orig for its own purposes)
6043 a = repo.wjoin(f)
6047 a = repo.wjoin(f)
6044 try:
6048 try:
6045 util.copyfile(a, a + b".resolve")
6049 util.copyfile(a, a + b".resolve")
6046 except (IOError, OSError) as inst:
6050 except (IOError, OSError) as inst:
6047 if inst.errno != errno.ENOENT:
6051 if inst.errno != errno.ENOENT:
6048 raise
6052 raise
6049
6053
6050 try:
6054 try:
6051 # preresolve file
6055 # preresolve file
6052 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6056 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6053 with ui.configoverride(overrides, b'resolve'):
6057 with ui.configoverride(overrides, b'resolve'):
6054 complete, r = ms.preresolve(f, wctx)
6058 complete, r = ms.preresolve(f, wctx)
6055 if not complete:
6059 if not complete:
6056 tocomplete.append(f)
6060 tocomplete.append(f)
6057 elif r:
6061 elif r:
6058 ret = 1
6062 ret = 1
6059 finally:
6063 finally:
6060 ms.commit()
6064 ms.commit()
6061
6065
6062 # replace filemerge's .orig file with our resolve file, but only
6066 # replace filemerge's .orig file with our resolve file, but only
6063 # for merges that are complete
6067 # for merges that are complete
6064 if complete:
6068 if complete:
6065 try:
6069 try:
6066 util.rename(
6070 util.rename(
6067 a + b".resolve", scmutil.backuppath(ui, repo, f)
6071 a + b".resolve", scmutil.backuppath(ui, repo, f)
6068 )
6072 )
6069 except OSError as inst:
6073 except OSError as inst:
6070 if inst.errno != errno.ENOENT:
6074 if inst.errno != errno.ENOENT:
6071 raise
6075 raise
6072
6076
6073 if hasconflictmarkers:
6077 if hasconflictmarkers:
6074 ui.warn(
6078 ui.warn(
6075 _(
6079 _(
6076 b'warning: the following files still have conflict '
6080 b'warning: the following files still have conflict '
6077 b'markers:\n'
6081 b'markers:\n'
6078 )
6082 )
6079 + b''.join(
6083 + b''.join(
6080 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6084 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6081 )
6085 )
6082 )
6086 )
6083 if markcheck == b'abort' and not all and not pats:
6087 if markcheck == b'abort' and not all and not pats:
6084 raise error.Abort(
6088 raise error.Abort(
6085 _(b'conflict markers detected'),
6089 _(b'conflict markers detected'),
6086 hint=_(b'use --all to mark anyway'),
6090 hint=_(b'use --all to mark anyway'),
6087 )
6091 )
6088
6092
6089 for f in tocomplete:
6093 for f in tocomplete:
6090 try:
6094 try:
6091 # resolve file
6095 # resolve file
6092 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6096 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6093 with ui.configoverride(overrides, b'resolve'):
6097 with ui.configoverride(overrides, b'resolve'):
6094 r = ms.resolve(f, wctx)
6098 r = ms.resolve(f, wctx)
6095 if r:
6099 if r:
6096 ret = 1
6100 ret = 1
6097 finally:
6101 finally:
6098 ms.commit()
6102 ms.commit()
6099
6103
6100 # replace filemerge's .orig file with our resolve file
6104 # replace filemerge's .orig file with our resolve file
6101 a = repo.wjoin(f)
6105 a = repo.wjoin(f)
6102 try:
6106 try:
6103 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6107 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6104 except OSError as inst:
6108 except OSError as inst:
6105 if inst.errno != errno.ENOENT:
6109 if inst.errno != errno.ENOENT:
6106 raise
6110 raise
6107
6111
6108 ms.commit()
6112 ms.commit()
6109 ms.recordactions()
6113 ms.recordactions()
6110
6114
6111 if not didwork and pats:
6115 if not didwork and pats:
6112 hint = None
6116 hint = None
6113 if not any([p for p in pats if p.find(b':') >= 0]):
6117 if not any([p for p in pats if p.find(b':') >= 0]):
6114 pats = [b'path:%s' % p for p in pats]
6118 pats = [b'path:%s' % p for p in pats]
6115 m = scmutil.match(wctx, pats, opts)
6119 m = scmutil.match(wctx, pats, opts)
6116 for f in ms:
6120 for f in ms:
6117 if not m(f):
6121 if not m(f):
6118 continue
6122 continue
6119
6123
6120 def flag(o):
6124 def flag(o):
6121 if o == b're_merge':
6125 if o == b're_merge':
6122 return b'--re-merge '
6126 return b'--re-merge '
6123 return b'-%s ' % o[0:1]
6127 return b'-%s ' % o[0:1]
6124
6128
6125 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6129 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6126 hint = _(b"(try: hg resolve %s%s)\n") % (
6130 hint = _(b"(try: hg resolve %s%s)\n") % (
6127 flags,
6131 flags,
6128 b' '.join(pats),
6132 b' '.join(pats),
6129 )
6133 )
6130 break
6134 break
6131 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6135 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6132 if hint:
6136 if hint:
6133 ui.warn(hint)
6137 ui.warn(hint)
6134 elif ms.mergedriver and ms.mdstate() != b's':
6138 elif ms.mergedriver and ms.mdstate() != b's':
6135 # run conclude step when either a driver-resolved file is requested
6139 # run conclude step when either a driver-resolved file is requested
6136 # or there are no driver-resolved files
6140 # or there are no driver-resolved files
6137 # we can't use 'ret' to determine whether any files are unresolved
6141 # we can't use 'ret' to determine whether any files are unresolved
6138 # because we might not have tried to resolve some
6142 # because we might not have tried to resolve some
6139 if (runconclude or not list(ms.driverresolved())) and not list(
6143 if (runconclude or not list(ms.driverresolved())) and not list(
6140 ms.unresolved()
6144 ms.unresolved()
6141 ):
6145 ):
6142 proceed = mergemod.driverconclude(repo, ms, wctx)
6146 proceed = mergemod.driverconclude(repo, ms, wctx)
6143 ms.commit()
6147 ms.commit()
6144 if not proceed:
6148 if not proceed:
6145 return 1
6149 return 1
6146
6150
6147 # Nudge users into finishing an unfinished operation
6151 # Nudge users into finishing an unfinished operation
6148 unresolvedf = list(ms.unresolved())
6152 unresolvedf = list(ms.unresolved())
6149 driverresolvedf = list(ms.driverresolved())
6153 driverresolvedf = list(ms.driverresolved())
6150 if not unresolvedf and not driverresolvedf:
6154 if not unresolvedf and not driverresolvedf:
6151 ui.status(_(b'(no more unresolved files)\n'))
6155 ui.status(_(b'(no more unresolved files)\n'))
6152 cmdutil.checkafterresolved(repo)
6156 cmdutil.checkafterresolved(repo)
6153 elif not unresolvedf:
6157 elif not unresolvedf:
6154 ui.status(
6158 ui.status(
6155 _(
6159 _(
6156 b'(no more unresolved files -- '
6160 b'(no more unresolved files -- '
6157 b'run "hg resolve --all" to conclude)\n'
6161 b'run "hg resolve --all" to conclude)\n'
6158 )
6162 )
6159 )
6163 )
6160
6164
6161 return ret
6165 return ret
6162
6166
6163
6167
6164 @command(
6168 @command(
6165 b'revert',
6169 b'revert',
6166 [
6170 [
6167 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6171 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6168 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6172 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6169 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6173 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6170 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6174 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6171 (b'i', b'interactive', None, _(b'interactively select the changes')),
6175 (b'i', b'interactive', None, _(b'interactively select the changes')),
6172 ]
6176 ]
6173 + walkopts
6177 + walkopts
6174 + dryrunopts,
6178 + dryrunopts,
6175 _(b'[OPTION]... [-r REV] [NAME]...'),
6179 _(b'[OPTION]... [-r REV] [NAME]...'),
6176 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6180 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6177 )
6181 )
6178 def revert(ui, repo, *pats, **opts):
6182 def revert(ui, repo, *pats, **opts):
6179 """restore files to their checkout state
6183 """restore files to their checkout state
6180
6184
6181 .. note::
6185 .. note::
6182
6186
6183 To check out earlier revisions, you should use :hg:`update REV`.
6187 To check out earlier revisions, you should use :hg:`update REV`.
6184 To cancel an uncommitted merge (and lose your changes),
6188 To cancel an uncommitted merge (and lose your changes),
6185 use :hg:`merge --abort`.
6189 use :hg:`merge --abort`.
6186
6190
6187 With no revision specified, revert the specified files or directories
6191 With no revision specified, revert the specified files or directories
6188 to the contents they had in the parent of the working directory.
6192 to the contents they had in the parent of the working directory.
6189 This restores the contents of files to an unmodified
6193 This restores the contents of files to an unmodified
6190 state and unschedules adds, removes, copies, and renames. If the
6194 state and unschedules adds, removes, copies, and renames. If the
6191 working directory has two parents, you must explicitly specify a
6195 working directory has two parents, you must explicitly specify a
6192 revision.
6196 revision.
6193
6197
6194 Using the -r/--rev or -d/--date options, revert the given files or
6198 Using the -r/--rev or -d/--date options, revert the given files or
6195 directories to their states as of a specific revision. Because
6199 directories to their states as of a specific revision. Because
6196 revert does not change the working directory parents, this will
6200 revert does not change the working directory parents, this will
6197 cause these files to appear modified. This can be helpful to "back
6201 cause these files to appear modified. This can be helpful to "back
6198 out" some or all of an earlier change. See :hg:`backout` for a
6202 out" some or all of an earlier change. See :hg:`backout` for a
6199 related method.
6203 related method.
6200
6204
6201 Modified files are saved with a .orig suffix before reverting.
6205 Modified files are saved with a .orig suffix before reverting.
6202 To disable these backups, use --no-backup. It is possible to store
6206 To disable these backups, use --no-backup. It is possible to store
6203 the backup files in a custom directory relative to the root of the
6207 the backup files in a custom directory relative to the root of the
6204 repository by setting the ``ui.origbackuppath`` configuration
6208 repository by setting the ``ui.origbackuppath`` configuration
6205 option.
6209 option.
6206
6210
6207 See :hg:`help dates` for a list of formats valid for -d/--date.
6211 See :hg:`help dates` for a list of formats valid for -d/--date.
6208
6212
6209 See :hg:`help backout` for a way to reverse the effect of an
6213 See :hg:`help backout` for a way to reverse the effect of an
6210 earlier changeset.
6214 earlier changeset.
6211
6215
6212 Returns 0 on success.
6216 Returns 0 on success.
6213 """
6217 """
6214
6218
6215 opts = pycompat.byteskwargs(opts)
6219 opts = pycompat.byteskwargs(opts)
6216 if opts.get(b"date"):
6220 if opts.get(b"date"):
6217 if opts.get(b"rev"):
6221 if opts.get(b"rev"):
6218 raise error.Abort(_(b"you can't specify a revision and a date"))
6222 raise error.Abort(_(b"you can't specify a revision and a date"))
6219 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6223 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6220
6224
6221 parent, p2 = repo.dirstate.parents()
6225 parent, p2 = repo.dirstate.parents()
6222 if not opts.get(b'rev') and p2 != nullid:
6226 if not opts.get(b'rev') and p2 != nullid:
6223 # revert after merge is a trap for new users (issue2915)
6227 # revert after merge is a trap for new users (issue2915)
6224 raise error.Abort(
6228 raise error.Abort(
6225 _(b'uncommitted merge with no revision specified'),
6229 _(b'uncommitted merge with no revision specified'),
6226 hint=_(b"use 'hg update' or see 'hg help revert'"),
6230 hint=_(b"use 'hg update' or see 'hg help revert'"),
6227 )
6231 )
6228
6232
6229 rev = opts.get(b'rev')
6233 rev = opts.get(b'rev')
6230 if rev:
6234 if rev:
6231 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6235 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6232 ctx = scmutil.revsingle(repo, rev)
6236 ctx = scmutil.revsingle(repo, rev)
6233
6237
6234 if not (
6238 if not (
6235 pats
6239 pats
6236 or opts.get(b'include')
6240 or opts.get(b'include')
6237 or opts.get(b'exclude')
6241 or opts.get(b'exclude')
6238 or opts.get(b'all')
6242 or opts.get(b'all')
6239 or opts.get(b'interactive')
6243 or opts.get(b'interactive')
6240 ):
6244 ):
6241 msg = _(b"no files or directories specified")
6245 msg = _(b"no files or directories specified")
6242 if p2 != nullid:
6246 if p2 != nullid:
6243 hint = _(
6247 hint = _(
6244 b"uncommitted merge, use --all to discard all changes,"
6248 b"uncommitted merge, use --all to discard all changes,"
6245 b" or 'hg update -C .' to abort the merge"
6249 b" or 'hg update -C .' to abort the merge"
6246 )
6250 )
6247 raise error.Abort(msg, hint=hint)
6251 raise error.Abort(msg, hint=hint)
6248 dirty = any(repo.status())
6252 dirty = any(repo.status())
6249 node = ctx.node()
6253 node = ctx.node()
6250 if node != parent:
6254 if node != parent:
6251 if dirty:
6255 if dirty:
6252 hint = (
6256 hint = (
6253 _(
6257 _(
6254 b"uncommitted changes, use --all to discard all"
6258 b"uncommitted changes, use --all to discard all"
6255 b" changes, or 'hg update %d' to update"
6259 b" changes, or 'hg update %d' to update"
6256 )
6260 )
6257 % ctx.rev()
6261 % ctx.rev()
6258 )
6262 )
6259 else:
6263 else:
6260 hint = (
6264 hint = (
6261 _(
6265 _(
6262 b"use --all to revert all files,"
6266 b"use --all to revert all files,"
6263 b" or 'hg update %d' to update"
6267 b" or 'hg update %d' to update"
6264 )
6268 )
6265 % ctx.rev()
6269 % ctx.rev()
6266 )
6270 )
6267 elif dirty:
6271 elif dirty:
6268 hint = _(b"uncommitted changes, use --all to discard all changes")
6272 hint = _(b"uncommitted changes, use --all to discard all changes")
6269 else:
6273 else:
6270 hint = _(b"use --all to revert all files")
6274 hint = _(b"use --all to revert all files")
6271 raise error.Abort(msg, hint=hint)
6275 raise error.Abort(msg, hint=hint)
6272
6276
6273 return cmdutil.revert(
6277 return cmdutil.revert(
6274 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6278 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6275 )
6279 )
6276
6280
6277
6281
6278 @command(
6282 @command(
6279 b'rollback',
6283 b'rollback',
6280 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6284 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6281 helpcategory=command.CATEGORY_MAINTENANCE,
6285 helpcategory=command.CATEGORY_MAINTENANCE,
6282 )
6286 )
6283 def rollback(ui, repo, **opts):
6287 def rollback(ui, repo, **opts):
6284 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6288 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6285
6289
6286 Please use :hg:`commit --amend` instead of rollback to correct
6290 Please use :hg:`commit --amend` instead of rollback to correct
6287 mistakes in the last commit.
6291 mistakes in the last commit.
6288
6292
6289 This command should be used with care. There is only one level of
6293 This command should be used with care. There is only one level of
6290 rollback, and there is no way to undo a rollback. It will also
6294 rollback, and there is no way to undo a rollback. It will also
6291 restore the dirstate at the time of the last transaction, losing
6295 restore the dirstate at the time of the last transaction, losing
6292 any dirstate changes since that time. This command does not alter
6296 any dirstate changes since that time. This command does not alter
6293 the working directory.
6297 the working directory.
6294
6298
6295 Transactions are used to encapsulate the effects of all commands
6299 Transactions are used to encapsulate the effects of all commands
6296 that create new changesets or propagate existing changesets into a
6300 that create new changesets or propagate existing changesets into a
6297 repository.
6301 repository.
6298
6302
6299 .. container:: verbose
6303 .. container:: verbose
6300
6304
6301 For example, the following commands are transactional, and their
6305 For example, the following commands are transactional, and their
6302 effects can be rolled back:
6306 effects can be rolled back:
6303
6307
6304 - commit
6308 - commit
6305 - import
6309 - import
6306 - pull
6310 - pull
6307 - push (with this repository as the destination)
6311 - push (with this repository as the destination)
6308 - unbundle
6312 - unbundle
6309
6313
6310 To avoid permanent data loss, rollback will refuse to rollback a
6314 To avoid permanent data loss, rollback will refuse to rollback a
6311 commit transaction if it isn't checked out. Use --force to
6315 commit transaction if it isn't checked out. Use --force to
6312 override this protection.
6316 override this protection.
6313
6317
6314 The rollback command can be entirely disabled by setting the
6318 The rollback command can be entirely disabled by setting the
6315 ``ui.rollback`` configuration setting to false. If you're here
6319 ``ui.rollback`` configuration setting to false. If you're here
6316 because you want to use rollback and it's disabled, you can
6320 because you want to use rollback and it's disabled, you can
6317 re-enable the command by setting ``ui.rollback`` to true.
6321 re-enable the command by setting ``ui.rollback`` to true.
6318
6322
6319 This command is not intended for use on public repositories. Once
6323 This command is not intended for use on public repositories. Once
6320 changes are visible for pull by other users, rolling a transaction
6324 changes are visible for pull by other users, rolling a transaction
6321 back locally is ineffective (someone else may already have pulled
6325 back locally is ineffective (someone else may already have pulled
6322 the changes). Furthermore, a race is possible with readers of the
6326 the changes). Furthermore, a race is possible with readers of the
6323 repository; for example an in-progress pull from the repository
6327 repository; for example an in-progress pull from the repository
6324 may fail if a rollback is performed.
6328 may fail if a rollback is performed.
6325
6329
6326 Returns 0 on success, 1 if no rollback data is available.
6330 Returns 0 on success, 1 if no rollback data is available.
6327 """
6331 """
6328 if not ui.configbool(b'ui', b'rollback'):
6332 if not ui.configbool(b'ui', b'rollback'):
6329 raise error.Abort(
6333 raise error.Abort(
6330 _(b'rollback is disabled because it is unsafe'),
6334 _(b'rollback is disabled because it is unsafe'),
6331 hint=b'see `hg help -v rollback` for information',
6335 hint=b'see `hg help -v rollback` for information',
6332 )
6336 )
6333 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6337 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6334
6338
6335
6339
6336 @command(
6340 @command(
6337 b'root',
6341 b'root',
6338 [] + formatteropts,
6342 [] + formatteropts,
6339 intents={INTENT_READONLY},
6343 intents={INTENT_READONLY},
6340 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6344 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6341 )
6345 )
6342 def root(ui, repo, **opts):
6346 def root(ui, repo, **opts):
6343 """print the root (top) of the current working directory
6347 """print the root (top) of the current working directory
6344
6348
6345 Print the root directory of the current repository.
6349 Print the root directory of the current repository.
6346
6350
6347 .. container:: verbose
6351 .. container:: verbose
6348
6352
6349 Template:
6353 Template:
6350
6354
6351 The following keywords are supported in addition to the common template
6355 The following keywords are supported in addition to the common template
6352 keywords and functions. See also :hg:`help templates`.
6356 keywords and functions. See also :hg:`help templates`.
6353
6357
6354 :hgpath: String. Path to the .hg directory.
6358 :hgpath: String. Path to the .hg directory.
6355 :storepath: String. Path to the directory holding versioned data.
6359 :storepath: String. Path to the directory holding versioned data.
6356
6360
6357 Returns 0 on success.
6361 Returns 0 on success.
6358 """
6362 """
6359 opts = pycompat.byteskwargs(opts)
6363 opts = pycompat.byteskwargs(opts)
6360 with ui.formatter(b'root', opts) as fm:
6364 with ui.formatter(b'root', opts) as fm:
6361 fm.startitem()
6365 fm.startitem()
6362 fm.write(b'reporoot', b'%s\n', repo.root)
6366 fm.write(b'reporoot', b'%s\n', repo.root)
6363 fm.data(hgpath=repo.path, storepath=repo.spath)
6367 fm.data(hgpath=repo.path, storepath=repo.spath)
6364
6368
6365
6369
6366 @command(
6370 @command(
6367 b'serve',
6371 b'serve',
6368 [
6372 [
6369 (
6373 (
6370 b'A',
6374 b'A',
6371 b'accesslog',
6375 b'accesslog',
6372 b'',
6376 b'',
6373 _(b'name of access log file to write to'),
6377 _(b'name of access log file to write to'),
6374 _(b'FILE'),
6378 _(b'FILE'),
6375 ),
6379 ),
6376 (b'd', b'daemon', None, _(b'run server in background')),
6380 (b'd', b'daemon', None, _(b'run server in background')),
6377 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6381 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6378 (
6382 (
6379 b'E',
6383 b'E',
6380 b'errorlog',
6384 b'errorlog',
6381 b'',
6385 b'',
6382 _(b'name of error log file to write to'),
6386 _(b'name of error log file to write to'),
6383 _(b'FILE'),
6387 _(b'FILE'),
6384 ),
6388 ),
6385 # use string type, then we can check if something was passed
6389 # use string type, then we can check if something was passed
6386 (
6390 (
6387 b'p',
6391 b'p',
6388 b'port',
6392 b'port',
6389 b'',
6393 b'',
6390 _(b'port to listen on (default: 8000)'),
6394 _(b'port to listen on (default: 8000)'),
6391 _(b'PORT'),
6395 _(b'PORT'),
6392 ),
6396 ),
6393 (
6397 (
6394 b'a',
6398 b'a',
6395 b'address',
6399 b'address',
6396 b'',
6400 b'',
6397 _(b'address to listen on (default: all interfaces)'),
6401 _(b'address to listen on (default: all interfaces)'),
6398 _(b'ADDR'),
6402 _(b'ADDR'),
6399 ),
6403 ),
6400 (
6404 (
6401 b'',
6405 b'',
6402 b'prefix',
6406 b'prefix',
6403 b'',
6407 b'',
6404 _(b'prefix path to serve from (default: server root)'),
6408 _(b'prefix path to serve from (default: server root)'),
6405 _(b'PREFIX'),
6409 _(b'PREFIX'),
6406 ),
6410 ),
6407 (
6411 (
6408 b'n',
6412 b'n',
6409 b'name',
6413 b'name',
6410 b'',
6414 b'',
6411 _(b'name to show in web pages (default: working directory)'),
6415 _(b'name to show in web pages (default: working directory)'),
6412 _(b'NAME'),
6416 _(b'NAME'),
6413 ),
6417 ),
6414 (
6418 (
6415 b'',
6419 b'',
6416 b'web-conf',
6420 b'web-conf',
6417 b'',
6421 b'',
6418 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6422 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6419 _(b'FILE'),
6423 _(b'FILE'),
6420 ),
6424 ),
6421 (
6425 (
6422 b'',
6426 b'',
6423 b'webdir-conf',
6427 b'webdir-conf',
6424 b'',
6428 b'',
6425 _(b'name of the hgweb config file (DEPRECATED)'),
6429 _(b'name of the hgweb config file (DEPRECATED)'),
6426 _(b'FILE'),
6430 _(b'FILE'),
6427 ),
6431 ),
6428 (
6432 (
6429 b'',
6433 b'',
6430 b'pid-file',
6434 b'pid-file',
6431 b'',
6435 b'',
6432 _(b'name of file to write process ID to'),
6436 _(b'name of file to write process ID to'),
6433 _(b'FILE'),
6437 _(b'FILE'),
6434 ),
6438 ),
6435 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6439 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6436 (
6440 (
6437 b'',
6441 b'',
6438 b'cmdserver',
6442 b'cmdserver',
6439 b'',
6443 b'',
6440 _(b'for remote clients (ADVANCED)'),
6444 _(b'for remote clients (ADVANCED)'),
6441 _(b'MODE'),
6445 _(b'MODE'),
6442 ),
6446 ),
6443 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6447 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6444 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6448 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6445 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6449 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6446 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6450 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6447 (b'', b'print-url', None, _(b'start and print only the URL')),
6451 (b'', b'print-url', None, _(b'start and print only the URL')),
6448 ]
6452 ]
6449 + subrepoopts,
6453 + subrepoopts,
6450 _(b'[OPTION]...'),
6454 _(b'[OPTION]...'),
6451 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6455 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6452 helpbasic=True,
6456 helpbasic=True,
6453 optionalrepo=True,
6457 optionalrepo=True,
6454 )
6458 )
6455 def serve(ui, repo, **opts):
6459 def serve(ui, repo, **opts):
6456 """start stand-alone webserver
6460 """start stand-alone webserver
6457
6461
6458 Start a local HTTP repository browser and pull server. You can use
6462 Start a local HTTP repository browser and pull server. You can use
6459 this for ad-hoc sharing and browsing of repositories. It is
6463 this for ad-hoc sharing and browsing of repositories. It is
6460 recommended to use a real web server to serve a repository for
6464 recommended to use a real web server to serve a repository for
6461 longer periods of time.
6465 longer periods of time.
6462
6466
6463 Please note that the server does not implement access control.
6467 Please note that the server does not implement access control.
6464 This means that, by default, anybody can read from the server and
6468 This means that, by default, anybody can read from the server and
6465 nobody can write to it by default. Set the ``web.allow-push``
6469 nobody can write to it by default. Set the ``web.allow-push``
6466 option to ``*`` to allow everybody to push to the server. You
6470 option to ``*`` to allow everybody to push to the server. You
6467 should use a real web server if you need to authenticate users.
6471 should use a real web server if you need to authenticate users.
6468
6472
6469 By default, the server logs accesses to stdout and errors to
6473 By default, the server logs accesses to stdout and errors to
6470 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6474 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6471 files.
6475 files.
6472
6476
6473 To have the server choose a free port number to listen on, specify
6477 To have the server choose a free port number to listen on, specify
6474 a port number of 0; in this case, the server will print the port
6478 a port number of 0; in this case, the server will print the port
6475 number it uses.
6479 number it uses.
6476
6480
6477 Returns 0 on success.
6481 Returns 0 on success.
6478 """
6482 """
6479
6483
6480 opts = pycompat.byteskwargs(opts)
6484 opts = pycompat.byteskwargs(opts)
6481 if opts[b"stdio"] and opts[b"cmdserver"]:
6485 if opts[b"stdio"] and opts[b"cmdserver"]:
6482 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6486 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6483 if opts[b"print_url"] and ui.verbose:
6487 if opts[b"print_url"] and ui.verbose:
6484 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6488 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6485
6489
6486 if opts[b"stdio"]:
6490 if opts[b"stdio"]:
6487 if repo is None:
6491 if repo is None:
6488 raise error.RepoError(
6492 raise error.RepoError(
6489 _(b"there is no Mercurial repository here (.hg not found)")
6493 _(b"there is no Mercurial repository here (.hg not found)")
6490 )
6494 )
6491 s = wireprotoserver.sshserver(ui, repo)
6495 s = wireprotoserver.sshserver(ui, repo)
6492 s.serve_forever()
6496 s.serve_forever()
6493
6497
6494 service = server.createservice(ui, repo, opts)
6498 service = server.createservice(ui, repo, opts)
6495 return server.runservice(opts, initfn=service.init, runfn=service.run)
6499 return server.runservice(opts, initfn=service.init, runfn=service.run)
6496
6500
6497
6501
6498 @command(
6502 @command(
6499 b'shelve',
6503 b'shelve',
6500 [
6504 [
6501 (
6505 (
6502 b'A',
6506 b'A',
6503 b'addremove',
6507 b'addremove',
6504 None,
6508 None,
6505 _(b'mark new/missing files as added/removed before shelving'),
6509 _(b'mark new/missing files as added/removed before shelving'),
6506 ),
6510 ),
6507 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6511 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6508 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6512 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6509 (
6513 (
6510 b'',
6514 b'',
6511 b'date',
6515 b'date',
6512 b'',
6516 b'',
6513 _(b'shelve with the specified commit date'),
6517 _(b'shelve with the specified commit date'),
6514 _(b'DATE'),
6518 _(b'DATE'),
6515 ),
6519 ),
6516 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6520 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6517 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6521 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6518 (
6522 (
6519 b'k',
6523 b'k',
6520 b'keep',
6524 b'keep',
6521 False,
6525 False,
6522 _(b'shelve, but keep changes in the working directory'),
6526 _(b'shelve, but keep changes in the working directory'),
6523 ),
6527 ),
6524 (b'l', b'list', None, _(b'list current shelves')),
6528 (b'l', b'list', None, _(b'list current shelves')),
6525 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6529 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6526 (
6530 (
6527 b'n',
6531 b'n',
6528 b'name',
6532 b'name',
6529 b'',
6533 b'',
6530 _(b'use the given name for the shelved commit'),
6534 _(b'use the given name for the shelved commit'),
6531 _(b'NAME'),
6535 _(b'NAME'),
6532 ),
6536 ),
6533 (
6537 (
6534 b'p',
6538 b'p',
6535 b'patch',
6539 b'patch',
6536 None,
6540 None,
6537 _(
6541 _(
6538 b'output patches for changes (provide the names of the shelved '
6542 b'output patches for changes (provide the names of the shelved '
6539 b'changes as positional arguments)'
6543 b'changes as positional arguments)'
6540 ),
6544 ),
6541 ),
6545 ),
6542 (b'i', b'interactive', None, _(b'interactive mode')),
6546 (b'i', b'interactive', None, _(b'interactive mode')),
6543 (
6547 (
6544 b'',
6548 b'',
6545 b'stat',
6549 b'stat',
6546 None,
6550 None,
6547 _(
6551 _(
6548 b'output diffstat-style summary of changes (provide the names of '
6552 b'output diffstat-style summary of changes (provide the names of '
6549 b'the shelved changes as positional arguments)'
6553 b'the shelved changes as positional arguments)'
6550 ),
6554 ),
6551 ),
6555 ),
6552 ]
6556 ]
6553 + cmdutil.walkopts,
6557 + cmdutil.walkopts,
6554 _(b'hg shelve [OPTION]... [FILE]...'),
6558 _(b'hg shelve [OPTION]... [FILE]...'),
6555 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6559 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6556 )
6560 )
6557 def shelve(ui, repo, *pats, **opts):
6561 def shelve(ui, repo, *pats, **opts):
6558 '''save and set aside changes from the working directory
6562 '''save and set aside changes from the working directory
6559
6563
6560 Shelving takes files that "hg status" reports as not clean, saves
6564 Shelving takes files that "hg status" reports as not clean, saves
6561 the modifications to a bundle (a shelved change), and reverts the
6565 the modifications to a bundle (a shelved change), and reverts the
6562 files so that their state in the working directory becomes clean.
6566 files so that their state in the working directory becomes clean.
6563
6567
6564 To restore these changes to the working directory, using "hg
6568 To restore these changes to the working directory, using "hg
6565 unshelve"; this will work even if you switch to a different
6569 unshelve"; this will work even if you switch to a different
6566 commit.
6570 commit.
6567
6571
6568 When no files are specified, "hg shelve" saves all not-clean
6572 When no files are specified, "hg shelve" saves all not-clean
6569 files. If specific files or directories are named, only changes to
6573 files. If specific files or directories are named, only changes to
6570 those files are shelved.
6574 those files are shelved.
6571
6575
6572 In bare shelve (when no files are specified, without interactive,
6576 In bare shelve (when no files are specified, without interactive,
6573 include and exclude option), shelving remembers information if the
6577 include and exclude option), shelving remembers information if the
6574 working directory was on newly created branch, in other words working
6578 working directory was on newly created branch, in other words working
6575 directory was on different branch than its first parent. In this
6579 directory was on different branch than its first parent. In this
6576 situation unshelving restores branch information to the working directory.
6580 situation unshelving restores branch information to the working directory.
6577
6581
6578 Each shelved change has a name that makes it easier to find later.
6582 Each shelved change has a name that makes it easier to find later.
6579 The name of a shelved change defaults to being based on the active
6583 The name of a shelved change defaults to being based on the active
6580 bookmark, or if there is no active bookmark, the current named
6584 bookmark, or if there is no active bookmark, the current named
6581 branch. To specify a different name, use ``--name``.
6585 branch. To specify a different name, use ``--name``.
6582
6586
6583 To see a list of existing shelved changes, use the ``--list``
6587 To see a list of existing shelved changes, use the ``--list``
6584 option. For each shelved change, this will print its name, age,
6588 option. For each shelved change, this will print its name, age,
6585 and description; use ``--patch`` or ``--stat`` for more details.
6589 and description; use ``--patch`` or ``--stat`` for more details.
6586
6590
6587 To delete specific shelved changes, use ``--delete``. To delete
6591 To delete specific shelved changes, use ``--delete``. To delete
6588 all shelved changes, use ``--cleanup``.
6592 all shelved changes, use ``--cleanup``.
6589 '''
6593 '''
6590 opts = pycompat.byteskwargs(opts)
6594 opts = pycompat.byteskwargs(opts)
6591 allowables = [
6595 allowables = [
6592 (b'addremove', {b'create'}), # 'create' is pseudo action
6596 (b'addremove', {b'create'}), # 'create' is pseudo action
6593 (b'unknown', {b'create'}),
6597 (b'unknown', {b'create'}),
6594 (b'cleanup', {b'cleanup'}),
6598 (b'cleanup', {b'cleanup'}),
6595 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6599 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6596 (b'delete', {b'delete'}),
6600 (b'delete', {b'delete'}),
6597 (b'edit', {b'create'}),
6601 (b'edit', {b'create'}),
6598 (b'keep', {b'create'}),
6602 (b'keep', {b'create'}),
6599 (b'list', {b'list'}),
6603 (b'list', {b'list'}),
6600 (b'message', {b'create'}),
6604 (b'message', {b'create'}),
6601 (b'name', {b'create'}),
6605 (b'name', {b'create'}),
6602 (b'patch', {b'patch', b'list'}),
6606 (b'patch', {b'patch', b'list'}),
6603 (b'stat', {b'stat', b'list'}),
6607 (b'stat', {b'stat', b'list'}),
6604 ]
6608 ]
6605
6609
6606 def checkopt(opt):
6610 def checkopt(opt):
6607 if opts.get(opt):
6611 if opts.get(opt):
6608 for i, allowable in allowables:
6612 for i, allowable in allowables:
6609 if opts[i] and opt not in allowable:
6613 if opts[i] and opt not in allowable:
6610 raise error.Abort(
6614 raise error.Abort(
6611 _(
6615 _(
6612 b"options '--%s' and '--%s' may not be "
6616 b"options '--%s' and '--%s' may not be "
6613 b"used together"
6617 b"used together"
6614 )
6618 )
6615 % (opt, i)
6619 % (opt, i)
6616 )
6620 )
6617 return True
6621 return True
6618
6622
6619 if checkopt(b'cleanup'):
6623 if checkopt(b'cleanup'):
6620 if pats:
6624 if pats:
6621 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6625 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6622 return shelvemod.cleanupcmd(ui, repo)
6626 return shelvemod.cleanupcmd(ui, repo)
6623 elif checkopt(b'delete'):
6627 elif checkopt(b'delete'):
6624 return shelvemod.deletecmd(ui, repo, pats)
6628 return shelvemod.deletecmd(ui, repo, pats)
6625 elif checkopt(b'list'):
6629 elif checkopt(b'list'):
6626 return shelvemod.listcmd(ui, repo, pats, opts)
6630 return shelvemod.listcmd(ui, repo, pats, opts)
6627 elif checkopt(b'patch') or checkopt(b'stat'):
6631 elif checkopt(b'patch') or checkopt(b'stat'):
6628 return shelvemod.patchcmds(ui, repo, pats, opts)
6632 return shelvemod.patchcmds(ui, repo, pats, opts)
6629 else:
6633 else:
6630 return shelvemod.createcmd(ui, repo, pats, opts)
6634 return shelvemod.createcmd(ui, repo, pats, opts)
6631
6635
6632
6636
6633 _NOTTERSE = b'nothing'
6637 _NOTTERSE = b'nothing'
6634
6638
6635
6639
6636 @command(
6640 @command(
6637 b'status|st',
6641 b'status|st',
6638 [
6642 [
6639 (b'A', b'all', None, _(b'show status of all files')),
6643 (b'A', b'all', None, _(b'show status of all files')),
6640 (b'm', b'modified', None, _(b'show only modified files')),
6644 (b'm', b'modified', None, _(b'show only modified files')),
6641 (b'a', b'added', None, _(b'show only added files')),
6645 (b'a', b'added', None, _(b'show only added files')),
6642 (b'r', b'removed', None, _(b'show only removed files')),
6646 (b'r', b'removed', None, _(b'show only removed files')),
6643 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6647 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6644 (b'c', b'clean', None, _(b'show only files without changes')),
6648 (b'c', b'clean', None, _(b'show only files without changes')),
6645 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6649 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6646 (b'i', b'ignored', None, _(b'show only ignored files')),
6650 (b'i', b'ignored', None, _(b'show only ignored files')),
6647 (b'n', b'no-status', None, _(b'hide status prefix')),
6651 (b'n', b'no-status', None, _(b'hide status prefix')),
6648 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6652 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6649 (
6653 (
6650 b'C',
6654 b'C',
6651 b'copies',
6655 b'copies',
6652 None,
6656 None,
6653 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6657 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6654 ),
6658 ),
6655 (
6659 (
6656 b'0',
6660 b'0',
6657 b'print0',
6661 b'print0',
6658 None,
6662 None,
6659 _(b'end filenames with NUL, for use with xargs'),
6663 _(b'end filenames with NUL, for use with xargs'),
6660 ),
6664 ),
6661 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6665 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6662 (
6666 (
6663 b'',
6667 b'',
6664 b'change',
6668 b'change',
6665 b'',
6669 b'',
6666 _(b'list the changed files of a revision'),
6670 _(b'list the changed files of a revision'),
6667 _(b'REV'),
6671 _(b'REV'),
6668 ),
6672 ),
6669 ]
6673 ]
6670 + walkopts
6674 + walkopts
6671 + subrepoopts
6675 + subrepoopts
6672 + formatteropts,
6676 + formatteropts,
6673 _(b'[OPTION]... [FILE]...'),
6677 _(b'[OPTION]... [FILE]...'),
6674 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6678 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6675 helpbasic=True,
6679 helpbasic=True,
6676 inferrepo=True,
6680 inferrepo=True,
6677 intents={INTENT_READONLY},
6681 intents={INTENT_READONLY},
6678 )
6682 )
6679 def status(ui, repo, *pats, **opts):
6683 def status(ui, repo, *pats, **opts):
6680 """show changed files in the working directory
6684 """show changed files in the working directory
6681
6685
6682 Show status of files in the repository. If names are given, only
6686 Show status of files in the repository. If names are given, only
6683 files that match are shown. Files that are clean or ignored or
6687 files that match are shown. Files that are clean or ignored or
6684 the source of a copy/move operation, are not listed unless
6688 the source of a copy/move operation, are not listed unless
6685 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6689 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6686 Unless options described with "show only ..." are given, the
6690 Unless options described with "show only ..." are given, the
6687 options -mardu are used.
6691 options -mardu are used.
6688
6692
6689 Option -q/--quiet hides untracked (unknown and ignored) files
6693 Option -q/--quiet hides untracked (unknown and ignored) files
6690 unless explicitly requested with -u/--unknown or -i/--ignored.
6694 unless explicitly requested with -u/--unknown or -i/--ignored.
6691
6695
6692 .. note::
6696 .. note::
6693
6697
6694 :hg:`status` may appear to disagree with diff if permissions have
6698 :hg:`status` may appear to disagree with diff if permissions have
6695 changed or a merge has occurred. The standard diff format does
6699 changed or a merge has occurred. The standard diff format does
6696 not report permission changes and diff only reports changes
6700 not report permission changes and diff only reports changes
6697 relative to one merge parent.
6701 relative to one merge parent.
6698
6702
6699 If one revision is given, it is used as the base revision.
6703 If one revision is given, it is used as the base revision.
6700 If two revisions are given, the differences between them are
6704 If two revisions are given, the differences between them are
6701 shown. The --change option can also be used as a shortcut to list
6705 shown. The --change option can also be used as a shortcut to list
6702 the changed files of a revision from its first parent.
6706 the changed files of a revision from its first parent.
6703
6707
6704 The codes used to show the status of files are::
6708 The codes used to show the status of files are::
6705
6709
6706 M = modified
6710 M = modified
6707 A = added
6711 A = added
6708 R = removed
6712 R = removed
6709 C = clean
6713 C = clean
6710 ! = missing (deleted by non-hg command, but still tracked)
6714 ! = missing (deleted by non-hg command, but still tracked)
6711 ? = not tracked
6715 ? = not tracked
6712 I = ignored
6716 I = ignored
6713 = origin of the previous file (with --copies)
6717 = origin of the previous file (with --copies)
6714
6718
6715 .. container:: verbose
6719 .. container:: verbose
6716
6720
6717 The -t/--terse option abbreviates the output by showing only the directory
6721 The -t/--terse option abbreviates the output by showing only the directory
6718 name if all the files in it share the same status. The option takes an
6722 name if all the files in it share the same status. The option takes an
6719 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6723 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6720 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6724 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6721 for 'ignored' and 'c' for clean.
6725 for 'ignored' and 'c' for clean.
6722
6726
6723 It abbreviates only those statuses which are passed. Note that clean and
6727 It abbreviates only those statuses which are passed. Note that clean and
6724 ignored files are not displayed with '--terse ic' unless the -c/--clean
6728 ignored files are not displayed with '--terse ic' unless the -c/--clean
6725 and -i/--ignored options are also used.
6729 and -i/--ignored options are also used.
6726
6730
6727 The -v/--verbose option shows information when the repository is in an
6731 The -v/--verbose option shows information when the repository is in an
6728 unfinished merge, shelve, rebase state etc. You can have this behavior
6732 unfinished merge, shelve, rebase state etc. You can have this behavior
6729 turned on by default by enabling the ``commands.status.verbose`` option.
6733 turned on by default by enabling the ``commands.status.verbose`` option.
6730
6734
6731 You can skip displaying some of these states by setting
6735 You can skip displaying some of these states by setting
6732 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6736 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6733 'histedit', 'merge', 'rebase', or 'unshelve'.
6737 'histedit', 'merge', 'rebase', or 'unshelve'.
6734
6738
6735 Template:
6739 Template:
6736
6740
6737 The following keywords are supported in addition to the common template
6741 The following keywords are supported in addition to the common template
6738 keywords and functions. See also :hg:`help templates`.
6742 keywords and functions. See also :hg:`help templates`.
6739
6743
6740 :path: String. Repository-absolute path of the file.
6744 :path: String. Repository-absolute path of the file.
6741 :source: String. Repository-absolute path of the file originated from.
6745 :source: String. Repository-absolute path of the file originated from.
6742 Available if ``--copies`` is specified.
6746 Available if ``--copies`` is specified.
6743 :status: String. Character denoting file's status.
6747 :status: String. Character denoting file's status.
6744
6748
6745 Examples:
6749 Examples:
6746
6750
6747 - show changes in the working directory relative to a
6751 - show changes in the working directory relative to a
6748 changeset::
6752 changeset::
6749
6753
6750 hg status --rev 9353
6754 hg status --rev 9353
6751
6755
6752 - show changes in the working directory relative to the
6756 - show changes in the working directory relative to the
6753 current directory (see :hg:`help patterns` for more information)::
6757 current directory (see :hg:`help patterns` for more information)::
6754
6758
6755 hg status re:
6759 hg status re:
6756
6760
6757 - show all changes including copies in an existing changeset::
6761 - show all changes including copies in an existing changeset::
6758
6762
6759 hg status --copies --change 9353
6763 hg status --copies --change 9353
6760
6764
6761 - get a NUL separated list of added files, suitable for xargs::
6765 - get a NUL separated list of added files, suitable for xargs::
6762
6766
6763 hg status -an0
6767 hg status -an0
6764
6768
6765 - show more information about the repository status, abbreviating
6769 - show more information about the repository status, abbreviating
6766 added, removed, modified, deleted, and untracked paths::
6770 added, removed, modified, deleted, and untracked paths::
6767
6771
6768 hg status -v -t mardu
6772 hg status -v -t mardu
6769
6773
6770 Returns 0 on success.
6774 Returns 0 on success.
6771
6775
6772 """
6776 """
6773
6777
6774 opts = pycompat.byteskwargs(opts)
6778 opts = pycompat.byteskwargs(opts)
6775 revs = opts.get(b'rev')
6779 revs = opts.get(b'rev')
6776 change = opts.get(b'change')
6780 change = opts.get(b'change')
6777 terse = opts.get(b'terse')
6781 terse = opts.get(b'terse')
6778 if terse is _NOTTERSE:
6782 if terse is _NOTTERSE:
6779 if revs:
6783 if revs:
6780 terse = b''
6784 terse = b''
6781 else:
6785 else:
6782 terse = ui.config(b'commands', b'status.terse')
6786 terse = ui.config(b'commands', b'status.terse')
6783
6787
6784 if revs and change:
6788 if revs and change:
6785 msg = _(b'cannot specify --rev and --change at the same time')
6789 msg = _(b'cannot specify --rev and --change at the same time')
6786 raise error.Abort(msg)
6790 raise error.Abort(msg)
6787 elif revs and terse:
6791 elif revs and terse:
6788 msg = _(b'cannot use --terse with --rev')
6792 msg = _(b'cannot use --terse with --rev')
6789 raise error.Abort(msg)
6793 raise error.Abort(msg)
6790 elif change:
6794 elif change:
6791 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6795 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6792 ctx2 = scmutil.revsingle(repo, change, None)
6796 ctx2 = scmutil.revsingle(repo, change, None)
6793 ctx1 = ctx2.p1()
6797 ctx1 = ctx2.p1()
6794 else:
6798 else:
6795 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6799 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6796 ctx1, ctx2 = scmutil.revpair(repo, revs)
6800 ctx1, ctx2 = scmutil.revpair(repo, revs)
6797
6801
6798 forcerelativevalue = None
6802 forcerelativevalue = None
6799 if ui.hasconfig(b'commands', b'status.relative'):
6803 if ui.hasconfig(b'commands', b'status.relative'):
6800 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6804 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6801 uipathfn = scmutil.getuipathfn(
6805 uipathfn = scmutil.getuipathfn(
6802 repo,
6806 repo,
6803 legacyrelativevalue=bool(pats),
6807 legacyrelativevalue=bool(pats),
6804 forcerelativevalue=forcerelativevalue,
6808 forcerelativevalue=forcerelativevalue,
6805 )
6809 )
6806
6810
6807 if opts.get(b'print0'):
6811 if opts.get(b'print0'):
6808 end = b'\0'
6812 end = b'\0'
6809 else:
6813 else:
6810 end = b'\n'
6814 end = b'\n'
6811 states = b'modified added removed deleted unknown ignored clean'.split()
6815 states = b'modified added removed deleted unknown ignored clean'.split()
6812 show = [k for k in states if opts.get(k)]
6816 show = [k for k in states if opts.get(k)]
6813 if opts.get(b'all'):
6817 if opts.get(b'all'):
6814 show += ui.quiet and (states[:4] + [b'clean']) or states
6818 show += ui.quiet and (states[:4] + [b'clean']) or states
6815
6819
6816 if not show:
6820 if not show:
6817 if ui.quiet:
6821 if ui.quiet:
6818 show = states[:4]
6822 show = states[:4]
6819 else:
6823 else:
6820 show = states[:5]
6824 show = states[:5]
6821
6825
6822 m = scmutil.match(ctx2, pats, opts)
6826 m = scmutil.match(ctx2, pats, opts)
6823 if terse:
6827 if terse:
6824 # we need to compute clean and unknown to terse
6828 # we need to compute clean and unknown to terse
6825 stat = repo.status(
6829 stat = repo.status(
6826 ctx1.node(),
6830 ctx1.node(),
6827 ctx2.node(),
6831 ctx2.node(),
6828 m,
6832 m,
6829 b'ignored' in show or b'i' in terse,
6833 b'ignored' in show or b'i' in terse,
6830 clean=True,
6834 clean=True,
6831 unknown=True,
6835 unknown=True,
6832 listsubrepos=opts.get(b'subrepos'),
6836 listsubrepos=opts.get(b'subrepos'),
6833 )
6837 )
6834
6838
6835 stat = cmdutil.tersedir(stat, terse)
6839 stat = cmdutil.tersedir(stat, terse)
6836 else:
6840 else:
6837 stat = repo.status(
6841 stat = repo.status(
6838 ctx1.node(),
6842 ctx1.node(),
6839 ctx2.node(),
6843 ctx2.node(),
6840 m,
6844 m,
6841 b'ignored' in show,
6845 b'ignored' in show,
6842 b'clean' in show,
6846 b'clean' in show,
6843 b'unknown' in show,
6847 b'unknown' in show,
6844 opts.get(b'subrepos'),
6848 opts.get(b'subrepos'),
6845 )
6849 )
6846
6850
6847 changestates = zip(
6851 changestates = zip(
6848 states,
6852 states,
6849 pycompat.iterbytestr(b'MAR!?IC'),
6853 pycompat.iterbytestr(b'MAR!?IC'),
6850 [getattr(stat, s.decode('utf8')) for s in states],
6854 [getattr(stat, s.decode('utf8')) for s in states],
6851 )
6855 )
6852
6856
6853 copy = {}
6857 copy = {}
6854 if (
6858 if (
6855 opts.get(b'all')
6859 opts.get(b'all')
6856 or opts.get(b'copies')
6860 or opts.get(b'copies')
6857 or ui.configbool(b'ui', b'statuscopies')
6861 or ui.configbool(b'ui', b'statuscopies')
6858 ) and not opts.get(b'no_status'):
6862 ) and not opts.get(b'no_status'):
6859 copy = copies.pathcopies(ctx1, ctx2, m)
6863 copy = copies.pathcopies(ctx1, ctx2, m)
6860
6864
6861 morestatus = None
6865 morestatus = None
6862 if (
6866 if (
6863 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6867 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6864 ) and not ui.plain():
6868 ) and not ui.plain():
6865 morestatus = cmdutil.readmorestatus(repo)
6869 morestatus = cmdutil.readmorestatus(repo)
6866
6870
6867 ui.pager(b'status')
6871 ui.pager(b'status')
6868 fm = ui.formatter(b'status', opts)
6872 fm = ui.formatter(b'status', opts)
6869 fmt = b'%s' + end
6873 fmt = b'%s' + end
6870 showchar = not opts.get(b'no_status')
6874 showchar = not opts.get(b'no_status')
6871
6875
6872 for state, char, files in changestates:
6876 for state, char, files in changestates:
6873 if state in show:
6877 if state in show:
6874 label = b'status.' + state
6878 label = b'status.' + state
6875 for f in files:
6879 for f in files:
6876 fm.startitem()
6880 fm.startitem()
6877 fm.context(ctx=ctx2)
6881 fm.context(ctx=ctx2)
6878 fm.data(itemtype=b'file', path=f)
6882 fm.data(itemtype=b'file', path=f)
6879 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6883 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6880 fm.plain(fmt % uipathfn(f), label=label)
6884 fm.plain(fmt % uipathfn(f), label=label)
6881 if f in copy:
6885 if f in copy:
6882 fm.data(source=copy[f])
6886 fm.data(source=copy[f])
6883 fm.plain(
6887 fm.plain(
6884 (b' %s' + end) % uipathfn(copy[f]),
6888 (b' %s' + end) % uipathfn(copy[f]),
6885 label=b'status.copied',
6889 label=b'status.copied',
6886 )
6890 )
6887 if morestatus:
6891 if morestatus:
6888 morestatus.formatfile(f, fm)
6892 morestatus.formatfile(f, fm)
6889
6893
6890 if morestatus:
6894 if morestatus:
6891 morestatus.formatfooter(fm)
6895 morestatus.formatfooter(fm)
6892 fm.end()
6896 fm.end()
6893
6897
6894
6898
6895 @command(
6899 @command(
6896 b'summary|sum',
6900 b'summary|sum',
6897 [(b'', b'remote', None, _(b'check for push and pull'))],
6901 [(b'', b'remote', None, _(b'check for push and pull'))],
6898 b'[--remote]',
6902 b'[--remote]',
6899 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6903 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6900 helpbasic=True,
6904 helpbasic=True,
6901 intents={INTENT_READONLY},
6905 intents={INTENT_READONLY},
6902 )
6906 )
6903 def summary(ui, repo, **opts):
6907 def summary(ui, repo, **opts):
6904 """summarize working directory state
6908 """summarize working directory state
6905
6909
6906 This generates a brief summary of the working directory state,
6910 This generates a brief summary of the working directory state,
6907 including parents, branch, commit status, phase and available updates.
6911 including parents, branch, commit status, phase and available updates.
6908
6912
6909 With the --remote option, this will check the default paths for
6913 With the --remote option, this will check the default paths for
6910 incoming and outgoing changes. This can be time-consuming.
6914 incoming and outgoing changes. This can be time-consuming.
6911
6915
6912 Returns 0 on success.
6916 Returns 0 on success.
6913 """
6917 """
6914
6918
6915 opts = pycompat.byteskwargs(opts)
6919 opts = pycompat.byteskwargs(opts)
6916 ui.pager(b'summary')
6920 ui.pager(b'summary')
6917 ctx = repo[None]
6921 ctx = repo[None]
6918 parents = ctx.parents()
6922 parents = ctx.parents()
6919 pnode = parents[0].node()
6923 pnode = parents[0].node()
6920 marks = []
6924 marks = []
6921
6925
6922 try:
6926 try:
6923 ms = mergemod.mergestate.read(repo)
6927 ms = mergemod.mergestate.read(repo)
6924 except error.UnsupportedMergeRecords as e:
6928 except error.UnsupportedMergeRecords as e:
6925 s = b' '.join(e.recordtypes)
6929 s = b' '.join(e.recordtypes)
6926 ui.warn(
6930 ui.warn(
6927 _(b'warning: merge state has unsupported record types: %s\n') % s
6931 _(b'warning: merge state has unsupported record types: %s\n') % s
6928 )
6932 )
6929 unresolved = []
6933 unresolved = []
6930 else:
6934 else:
6931 unresolved = list(ms.unresolved())
6935 unresolved = list(ms.unresolved())
6932
6936
6933 for p in parents:
6937 for p in parents:
6934 # label with log.changeset (instead of log.parent) since this
6938 # label with log.changeset (instead of log.parent) since this
6935 # shows a working directory parent *changeset*:
6939 # shows a working directory parent *changeset*:
6936 # i18n: column positioning for "hg summary"
6940 # i18n: column positioning for "hg summary"
6937 ui.write(
6941 ui.write(
6938 _(b'parent: %d:%s ') % (p.rev(), p),
6942 _(b'parent: %d:%s ') % (p.rev(), p),
6939 label=logcmdutil.changesetlabels(p),
6943 label=logcmdutil.changesetlabels(p),
6940 )
6944 )
6941 ui.write(b' '.join(p.tags()), label=b'log.tag')
6945 ui.write(b' '.join(p.tags()), label=b'log.tag')
6942 if p.bookmarks():
6946 if p.bookmarks():
6943 marks.extend(p.bookmarks())
6947 marks.extend(p.bookmarks())
6944 if p.rev() == -1:
6948 if p.rev() == -1:
6945 if not len(repo):
6949 if not len(repo):
6946 ui.write(_(b' (empty repository)'))
6950 ui.write(_(b' (empty repository)'))
6947 else:
6951 else:
6948 ui.write(_(b' (no revision checked out)'))
6952 ui.write(_(b' (no revision checked out)'))
6949 if p.obsolete():
6953 if p.obsolete():
6950 ui.write(_(b' (obsolete)'))
6954 ui.write(_(b' (obsolete)'))
6951 if p.isunstable():
6955 if p.isunstable():
6952 instabilities = (
6956 instabilities = (
6953 ui.label(instability, b'trouble.%s' % instability)
6957 ui.label(instability, b'trouble.%s' % instability)
6954 for instability in p.instabilities()
6958 for instability in p.instabilities()
6955 )
6959 )
6956 ui.write(b' (' + b', '.join(instabilities) + b')')
6960 ui.write(b' (' + b', '.join(instabilities) + b')')
6957 ui.write(b'\n')
6961 ui.write(b'\n')
6958 if p.description():
6962 if p.description():
6959 ui.status(
6963 ui.status(
6960 b' ' + p.description().splitlines()[0].strip() + b'\n',
6964 b' ' + p.description().splitlines()[0].strip() + b'\n',
6961 label=b'log.summary',
6965 label=b'log.summary',
6962 )
6966 )
6963
6967
6964 branch = ctx.branch()
6968 branch = ctx.branch()
6965 bheads = repo.branchheads(branch)
6969 bheads = repo.branchheads(branch)
6966 # i18n: column positioning for "hg summary"
6970 # i18n: column positioning for "hg summary"
6967 m = _(b'branch: %s\n') % branch
6971 m = _(b'branch: %s\n') % branch
6968 if branch != b'default':
6972 if branch != b'default':
6969 ui.write(m, label=b'log.branch')
6973 ui.write(m, label=b'log.branch')
6970 else:
6974 else:
6971 ui.status(m, label=b'log.branch')
6975 ui.status(m, label=b'log.branch')
6972
6976
6973 if marks:
6977 if marks:
6974 active = repo._activebookmark
6978 active = repo._activebookmark
6975 # i18n: column positioning for "hg summary"
6979 # i18n: column positioning for "hg summary"
6976 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6980 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6977 if active is not None:
6981 if active is not None:
6978 if active in marks:
6982 if active in marks:
6979 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6983 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6980 marks.remove(active)
6984 marks.remove(active)
6981 else:
6985 else:
6982 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6986 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6983 for m in marks:
6987 for m in marks:
6984 ui.write(b' ' + m, label=b'log.bookmark')
6988 ui.write(b' ' + m, label=b'log.bookmark')
6985 ui.write(b'\n', label=b'log.bookmark')
6989 ui.write(b'\n', label=b'log.bookmark')
6986
6990
6987 status = repo.status(unknown=True)
6991 status = repo.status(unknown=True)
6988
6992
6989 c = repo.dirstate.copies()
6993 c = repo.dirstate.copies()
6990 copied, renamed = [], []
6994 copied, renamed = [], []
6991 for d, s in pycompat.iteritems(c):
6995 for d, s in pycompat.iteritems(c):
6992 if s in status.removed:
6996 if s in status.removed:
6993 status.removed.remove(s)
6997 status.removed.remove(s)
6994 renamed.append(d)
6998 renamed.append(d)
6995 else:
6999 else:
6996 copied.append(d)
7000 copied.append(d)
6997 if d in status.added:
7001 if d in status.added:
6998 status.added.remove(d)
7002 status.added.remove(d)
6999
7003
7000 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7004 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7001
7005
7002 labels = [
7006 labels = [
7003 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7007 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7004 (ui.label(_(b'%d added'), b'status.added'), status.added),
7008 (ui.label(_(b'%d added'), b'status.added'), status.added),
7005 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7009 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7006 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7010 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7007 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7011 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7008 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7012 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7009 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7013 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7010 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7014 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7011 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7015 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7012 ]
7016 ]
7013 t = []
7017 t = []
7014 for l, s in labels:
7018 for l, s in labels:
7015 if s:
7019 if s:
7016 t.append(l % len(s))
7020 t.append(l % len(s))
7017
7021
7018 t = b', '.join(t)
7022 t = b', '.join(t)
7019 cleanworkdir = False
7023 cleanworkdir = False
7020
7024
7021 if repo.vfs.exists(b'graftstate'):
7025 if repo.vfs.exists(b'graftstate'):
7022 t += _(b' (graft in progress)')
7026 t += _(b' (graft in progress)')
7023 if repo.vfs.exists(b'updatestate'):
7027 if repo.vfs.exists(b'updatestate'):
7024 t += _(b' (interrupted update)')
7028 t += _(b' (interrupted update)')
7025 elif len(parents) > 1:
7029 elif len(parents) > 1:
7026 t += _(b' (merge)')
7030 t += _(b' (merge)')
7027 elif branch != parents[0].branch():
7031 elif branch != parents[0].branch():
7028 t += _(b' (new branch)')
7032 t += _(b' (new branch)')
7029 elif parents[0].closesbranch() and pnode in repo.branchheads(
7033 elif parents[0].closesbranch() and pnode in repo.branchheads(
7030 branch, closed=True
7034 branch, closed=True
7031 ):
7035 ):
7032 t += _(b' (head closed)')
7036 t += _(b' (head closed)')
7033 elif not (
7037 elif not (
7034 status.modified
7038 status.modified
7035 or status.added
7039 or status.added
7036 or status.removed
7040 or status.removed
7037 or renamed
7041 or renamed
7038 or copied
7042 or copied
7039 or subs
7043 or subs
7040 ):
7044 ):
7041 t += _(b' (clean)')
7045 t += _(b' (clean)')
7042 cleanworkdir = True
7046 cleanworkdir = True
7043 elif pnode not in bheads:
7047 elif pnode not in bheads:
7044 t += _(b' (new branch head)')
7048 t += _(b' (new branch head)')
7045
7049
7046 if parents:
7050 if parents:
7047 pendingphase = max(p.phase() for p in parents)
7051 pendingphase = max(p.phase() for p in parents)
7048 else:
7052 else:
7049 pendingphase = phases.public
7053 pendingphase = phases.public
7050
7054
7051 if pendingphase > phases.newcommitphase(ui):
7055 if pendingphase > phases.newcommitphase(ui):
7052 t += b' (%s)' % phases.phasenames[pendingphase]
7056 t += b' (%s)' % phases.phasenames[pendingphase]
7053
7057
7054 if cleanworkdir:
7058 if cleanworkdir:
7055 # i18n: column positioning for "hg summary"
7059 # i18n: column positioning for "hg summary"
7056 ui.status(_(b'commit: %s\n') % t.strip())
7060 ui.status(_(b'commit: %s\n') % t.strip())
7057 else:
7061 else:
7058 # i18n: column positioning for "hg summary"
7062 # i18n: column positioning for "hg summary"
7059 ui.write(_(b'commit: %s\n') % t.strip())
7063 ui.write(_(b'commit: %s\n') % t.strip())
7060
7064
7061 # all ancestors of branch heads - all ancestors of parent = new csets
7065 # all ancestors of branch heads - all ancestors of parent = new csets
7062 new = len(
7066 new = len(
7063 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7067 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7064 )
7068 )
7065
7069
7066 if new == 0:
7070 if new == 0:
7067 # i18n: column positioning for "hg summary"
7071 # i18n: column positioning for "hg summary"
7068 ui.status(_(b'update: (current)\n'))
7072 ui.status(_(b'update: (current)\n'))
7069 elif pnode not in bheads:
7073 elif pnode not in bheads:
7070 # i18n: column positioning for "hg summary"
7074 # i18n: column positioning for "hg summary"
7071 ui.write(_(b'update: %d new changesets (update)\n') % new)
7075 ui.write(_(b'update: %d new changesets (update)\n') % new)
7072 else:
7076 else:
7073 # i18n: column positioning for "hg summary"
7077 # i18n: column positioning for "hg summary"
7074 ui.write(
7078 ui.write(
7075 _(b'update: %d new changesets, %d branch heads (merge)\n')
7079 _(b'update: %d new changesets, %d branch heads (merge)\n')
7076 % (new, len(bheads))
7080 % (new, len(bheads))
7077 )
7081 )
7078
7082
7079 t = []
7083 t = []
7080 draft = len(repo.revs(b'draft()'))
7084 draft = len(repo.revs(b'draft()'))
7081 if draft:
7085 if draft:
7082 t.append(_(b'%d draft') % draft)
7086 t.append(_(b'%d draft') % draft)
7083 secret = len(repo.revs(b'secret()'))
7087 secret = len(repo.revs(b'secret()'))
7084 if secret:
7088 if secret:
7085 t.append(_(b'%d secret') % secret)
7089 t.append(_(b'%d secret') % secret)
7086
7090
7087 if draft or secret:
7091 if draft or secret:
7088 ui.status(_(b'phases: %s\n') % b', '.join(t))
7092 ui.status(_(b'phases: %s\n') % b', '.join(t))
7089
7093
7090 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7094 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7091 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7095 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7092 numtrouble = len(repo.revs(trouble + b"()"))
7096 numtrouble = len(repo.revs(trouble + b"()"))
7093 # We write all the possibilities to ease translation
7097 # We write all the possibilities to ease translation
7094 troublemsg = {
7098 troublemsg = {
7095 b"orphan": _(b"orphan: %d changesets"),
7099 b"orphan": _(b"orphan: %d changesets"),
7096 b"contentdivergent": _(b"content-divergent: %d changesets"),
7100 b"contentdivergent": _(b"content-divergent: %d changesets"),
7097 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7101 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7098 }
7102 }
7099 if numtrouble > 0:
7103 if numtrouble > 0:
7100 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7104 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7101
7105
7102 cmdutil.summaryhooks(ui, repo)
7106 cmdutil.summaryhooks(ui, repo)
7103
7107
7104 if opts.get(b'remote'):
7108 if opts.get(b'remote'):
7105 needsincoming, needsoutgoing = True, True
7109 needsincoming, needsoutgoing = True, True
7106 else:
7110 else:
7107 needsincoming, needsoutgoing = False, False
7111 needsincoming, needsoutgoing = False, False
7108 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7112 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7109 if i:
7113 if i:
7110 needsincoming = True
7114 needsincoming = True
7111 if o:
7115 if o:
7112 needsoutgoing = True
7116 needsoutgoing = True
7113 if not needsincoming and not needsoutgoing:
7117 if not needsincoming and not needsoutgoing:
7114 return
7118 return
7115
7119
7116 def getincoming():
7120 def getincoming():
7117 source, branches = hg.parseurl(ui.expandpath(b'default'))
7121 source, branches = hg.parseurl(ui.expandpath(b'default'))
7118 sbranch = branches[0]
7122 sbranch = branches[0]
7119 try:
7123 try:
7120 other = hg.peer(repo, {}, source)
7124 other = hg.peer(repo, {}, source)
7121 except error.RepoError:
7125 except error.RepoError:
7122 if opts.get(b'remote'):
7126 if opts.get(b'remote'):
7123 raise
7127 raise
7124 return source, sbranch, None, None, None
7128 return source, sbranch, None, None, None
7125 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7129 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7126 if revs:
7130 if revs:
7127 revs = [other.lookup(rev) for rev in revs]
7131 revs = [other.lookup(rev) for rev in revs]
7128 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7132 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7129 repo.ui.pushbuffer()
7133 repo.ui.pushbuffer()
7130 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7134 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7131 repo.ui.popbuffer()
7135 repo.ui.popbuffer()
7132 return source, sbranch, other, commoninc, commoninc[1]
7136 return source, sbranch, other, commoninc, commoninc[1]
7133
7137
7134 if needsincoming:
7138 if needsincoming:
7135 source, sbranch, sother, commoninc, incoming = getincoming()
7139 source, sbranch, sother, commoninc, incoming = getincoming()
7136 else:
7140 else:
7137 source = sbranch = sother = commoninc = incoming = None
7141 source = sbranch = sother = commoninc = incoming = None
7138
7142
7139 def getoutgoing():
7143 def getoutgoing():
7140 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7144 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7141 dbranch = branches[0]
7145 dbranch = branches[0]
7142 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7146 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7143 if source != dest:
7147 if source != dest:
7144 try:
7148 try:
7145 dother = hg.peer(repo, {}, dest)
7149 dother = hg.peer(repo, {}, dest)
7146 except error.RepoError:
7150 except error.RepoError:
7147 if opts.get(b'remote'):
7151 if opts.get(b'remote'):
7148 raise
7152 raise
7149 return dest, dbranch, None, None
7153 return dest, dbranch, None, None
7150 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7154 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7151 elif sother is None:
7155 elif sother is None:
7152 # there is no explicit destination peer, but source one is invalid
7156 # there is no explicit destination peer, but source one is invalid
7153 return dest, dbranch, None, None
7157 return dest, dbranch, None, None
7154 else:
7158 else:
7155 dother = sother
7159 dother = sother
7156 if source != dest or (sbranch is not None and sbranch != dbranch):
7160 if source != dest or (sbranch is not None and sbranch != dbranch):
7157 common = None
7161 common = None
7158 else:
7162 else:
7159 common = commoninc
7163 common = commoninc
7160 if revs:
7164 if revs:
7161 revs = [repo.lookup(rev) for rev in revs]
7165 revs = [repo.lookup(rev) for rev in revs]
7162 repo.ui.pushbuffer()
7166 repo.ui.pushbuffer()
7163 outgoing = discovery.findcommonoutgoing(
7167 outgoing = discovery.findcommonoutgoing(
7164 repo, dother, onlyheads=revs, commoninc=common
7168 repo, dother, onlyheads=revs, commoninc=common
7165 )
7169 )
7166 repo.ui.popbuffer()
7170 repo.ui.popbuffer()
7167 return dest, dbranch, dother, outgoing
7171 return dest, dbranch, dother, outgoing
7168
7172
7169 if needsoutgoing:
7173 if needsoutgoing:
7170 dest, dbranch, dother, outgoing = getoutgoing()
7174 dest, dbranch, dother, outgoing = getoutgoing()
7171 else:
7175 else:
7172 dest = dbranch = dother = outgoing = None
7176 dest = dbranch = dother = outgoing = None
7173
7177
7174 if opts.get(b'remote'):
7178 if opts.get(b'remote'):
7175 t = []
7179 t = []
7176 if incoming:
7180 if incoming:
7177 t.append(_(b'1 or more incoming'))
7181 t.append(_(b'1 or more incoming'))
7178 o = outgoing.missing
7182 o = outgoing.missing
7179 if o:
7183 if o:
7180 t.append(_(b'%d outgoing') % len(o))
7184 t.append(_(b'%d outgoing') % len(o))
7181 other = dother or sother
7185 other = dother or sother
7182 if b'bookmarks' in other.listkeys(b'namespaces'):
7186 if b'bookmarks' in other.listkeys(b'namespaces'):
7183 counts = bookmarks.summary(repo, other)
7187 counts = bookmarks.summary(repo, other)
7184 if counts[0] > 0:
7188 if counts[0] > 0:
7185 t.append(_(b'%d incoming bookmarks') % counts[0])
7189 t.append(_(b'%d incoming bookmarks') % counts[0])
7186 if counts[1] > 0:
7190 if counts[1] > 0:
7187 t.append(_(b'%d outgoing bookmarks') % counts[1])
7191 t.append(_(b'%d outgoing bookmarks') % counts[1])
7188
7192
7189 if t:
7193 if t:
7190 # i18n: column positioning for "hg summary"
7194 # i18n: column positioning for "hg summary"
7191 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7195 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7192 else:
7196 else:
7193 # i18n: column positioning for "hg summary"
7197 # i18n: column positioning for "hg summary"
7194 ui.status(_(b'remote: (synced)\n'))
7198 ui.status(_(b'remote: (synced)\n'))
7195
7199
7196 cmdutil.summaryremotehooks(
7200 cmdutil.summaryremotehooks(
7197 ui,
7201 ui,
7198 repo,
7202 repo,
7199 opts,
7203 opts,
7200 (
7204 (
7201 (source, sbranch, sother, commoninc),
7205 (source, sbranch, sother, commoninc),
7202 (dest, dbranch, dother, outgoing),
7206 (dest, dbranch, dother, outgoing),
7203 ),
7207 ),
7204 )
7208 )
7205
7209
7206
7210
7207 @command(
7211 @command(
7208 b'tag',
7212 b'tag',
7209 [
7213 [
7210 (b'f', b'force', None, _(b'force tag')),
7214 (b'f', b'force', None, _(b'force tag')),
7211 (b'l', b'local', None, _(b'make the tag local')),
7215 (b'l', b'local', None, _(b'make the tag local')),
7212 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7216 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7213 (b'', b'remove', None, _(b'remove a tag')),
7217 (b'', b'remove', None, _(b'remove a tag')),
7214 # -l/--local is already there, commitopts cannot be used
7218 # -l/--local is already there, commitopts cannot be used
7215 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7219 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7216 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7220 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7217 ]
7221 ]
7218 + commitopts2,
7222 + commitopts2,
7219 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7223 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7220 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7224 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7221 )
7225 )
7222 def tag(ui, repo, name1, *names, **opts):
7226 def tag(ui, repo, name1, *names, **opts):
7223 """add one or more tags for the current or given revision
7227 """add one or more tags for the current or given revision
7224
7228
7225 Name a particular revision using <name>.
7229 Name a particular revision using <name>.
7226
7230
7227 Tags are used to name particular revisions of the repository and are
7231 Tags are used to name particular revisions of the repository and are
7228 very useful to compare different revisions, to go back to significant
7232 very useful to compare different revisions, to go back to significant
7229 earlier versions or to mark branch points as releases, etc. Changing
7233 earlier versions or to mark branch points as releases, etc. Changing
7230 an existing tag is normally disallowed; use -f/--force to override.
7234 an existing tag is normally disallowed; use -f/--force to override.
7231
7235
7232 If no revision is given, the parent of the working directory is
7236 If no revision is given, the parent of the working directory is
7233 used.
7237 used.
7234
7238
7235 To facilitate version control, distribution, and merging of tags,
7239 To facilitate version control, distribution, and merging of tags,
7236 they are stored as a file named ".hgtags" which is managed similarly
7240 they are stored as a file named ".hgtags" which is managed similarly
7237 to other project files and can be hand-edited if necessary. This
7241 to other project files and can be hand-edited if necessary. This
7238 also means that tagging creates a new commit. The file
7242 also means that tagging creates a new commit. The file
7239 ".hg/localtags" is used for local tags (not shared among
7243 ".hg/localtags" is used for local tags (not shared among
7240 repositories).
7244 repositories).
7241
7245
7242 Tag commits are usually made at the head of a branch. If the parent
7246 Tag commits are usually made at the head of a branch. If the parent
7243 of the working directory is not a branch head, :hg:`tag` aborts; use
7247 of the working directory is not a branch head, :hg:`tag` aborts; use
7244 -f/--force to force the tag commit to be based on a non-head
7248 -f/--force to force the tag commit to be based on a non-head
7245 changeset.
7249 changeset.
7246
7250
7247 See :hg:`help dates` for a list of formats valid for -d/--date.
7251 See :hg:`help dates` for a list of formats valid for -d/--date.
7248
7252
7249 Since tag names have priority over branch names during revision
7253 Since tag names have priority over branch names during revision
7250 lookup, using an existing branch name as a tag name is discouraged.
7254 lookup, using an existing branch name as a tag name is discouraged.
7251
7255
7252 Returns 0 on success.
7256 Returns 0 on success.
7253 """
7257 """
7254 opts = pycompat.byteskwargs(opts)
7258 opts = pycompat.byteskwargs(opts)
7255 with repo.wlock(), repo.lock():
7259 with repo.wlock(), repo.lock():
7256 rev_ = b"."
7260 rev_ = b"."
7257 names = [t.strip() for t in (name1,) + names]
7261 names = [t.strip() for t in (name1,) + names]
7258 if len(names) != len(set(names)):
7262 if len(names) != len(set(names)):
7259 raise error.Abort(_(b'tag names must be unique'))
7263 raise error.Abort(_(b'tag names must be unique'))
7260 for n in names:
7264 for n in names:
7261 scmutil.checknewlabel(repo, n, b'tag')
7265 scmutil.checknewlabel(repo, n, b'tag')
7262 if not n:
7266 if not n:
7263 raise error.Abort(
7267 raise error.Abort(
7264 _(b'tag names cannot consist entirely of whitespace')
7268 _(b'tag names cannot consist entirely of whitespace')
7265 )
7269 )
7266 if opts.get(b'rev') and opts.get(b'remove'):
7270 if opts.get(b'rev') and opts.get(b'remove'):
7267 raise error.Abort(_(b"--rev and --remove are incompatible"))
7271 raise error.Abort(_(b"--rev and --remove are incompatible"))
7268 if opts.get(b'rev'):
7272 if opts.get(b'rev'):
7269 rev_ = opts[b'rev']
7273 rev_ = opts[b'rev']
7270 message = opts.get(b'message')
7274 message = opts.get(b'message')
7271 if opts.get(b'remove'):
7275 if opts.get(b'remove'):
7272 if opts.get(b'local'):
7276 if opts.get(b'local'):
7273 expectedtype = b'local'
7277 expectedtype = b'local'
7274 else:
7278 else:
7275 expectedtype = b'global'
7279 expectedtype = b'global'
7276
7280
7277 for n in names:
7281 for n in names:
7278 if repo.tagtype(n) == b'global':
7282 if repo.tagtype(n) == b'global':
7279 alltags = tagsmod.findglobaltags(ui, repo)
7283 alltags = tagsmod.findglobaltags(ui, repo)
7280 if alltags[n][0] == nullid:
7284 if alltags[n][0] == nullid:
7281 raise error.Abort(_(b"tag '%s' is already removed") % n)
7285 raise error.Abort(_(b"tag '%s' is already removed") % n)
7282 if not repo.tagtype(n):
7286 if not repo.tagtype(n):
7283 raise error.Abort(_(b"tag '%s' does not exist") % n)
7287 raise error.Abort(_(b"tag '%s' does not exist") % n)
7284 if repo.tagtype(n) != expectedtype:
7288 if repo.tagtype(n) != expectedtype:
7285 if expectedtype == b'global':
7289 if expectedtype == b'global':
7286 raise error.Abort(
7290 raise error.Abort(
7287 _(b"tag '%s' is not a global tag") % n
7291 _(b"tag '%s' is not a global tag") % n
7288 )
7292 )
7289 else:
7293 else:
7290 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7294 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7291 rev_ = b'null'
7295 rev_ = b'null'
7292 if not message:
7296 if not message:
7293 # we don't translate commit messages
7297 # we don't translate commit messages
7294 message = b'Removed tag %s' % b', '.join(names)
7298 message = b'Removed tag %s' % b', '.join(names)
7295 elif not opts.get(b'force'):
7299 elif not opts.get(b'force'):
7296 for n in names:
7300 for n in names:
7297 if n in repo.tags():
7301 if n in repo.tags():
7298 raise error.Abort(
7302 raise error.Abort(
7299 _(b"tag '%s' already exists (use -f to force)") % n
7303 _(b"tag '%s' already exists (use -f to force)") % n
7300 )
7304 )
7301 if not opts.get(b'local'):
7305 if not opts.get(b'local'):
7302 p1, p2 = repo.dirstate.parents()
7306 p1, p2 = repo.dirstate.parents()
7303 if p2 != nullid:
7307 if p2 != nullid:
7304 raise error.Abort(_(b'uncommitted merge'))
7308 raise error.Abort(_(b'uncommitted merge'))
7305 bheads = repo.branchheads()
7309 bheads = repo.branchheads()
7306 if not opts.get(b'force') and bheads and p1 not in bheads:
7310 if not opts.get(b'force') and bheads and p1 not in bheads:
7307 raise error.Abort(
7311 raise error.Abort(
7308 _(
7312 _(
7309 b'working directory is not at a branch head '
7313 b'working directory is not at a branch head '
7310 b'(use -f to force)'
7314 b'(use -f to force)'
7311 )
7315 )
7312 )
7316 )
7313 node = scmutil.revsingle(repo, rev_).node()
7317 node = scmutil.revsingle(repo, rev_).node()
7314
7318
7315 if not message:
7319 if not message:
7316 # we don't translate commit messages
7320 # we don't translate commit messages
7317 message = b'Added tag %s for changeset %s' % (
7321 message = b'Added tag %s for changeset %s' % (
7318 b', '.join(names),
7322 b', '.join(names),
7319 short(node),
7323 short(node),
7320 )
7324 )
7321
7325
7322 date = opts.get(b'date')
7326 date = opts.get(b'date')
7323 if date:
7327 if date:
7324 date = dateutil.parsedate(date)
7328 date = dateutil.parsedate(date)
7325
7329
7326 if opts.get(b'remove'):
7330 if opts.get(b'remove'):
7327 editform = b'tag.remove'
7331 editform = b'tag.remove'
7328 else:
7332 else:
7329 editform = b'tag.add'
7333 editform = b'tag.add'
7330 editor = cmdutil.getcommiteditor(
7334 editor = cmdutil.getcommiteditor(
7331 editform=editform, **pycompat.strkwargs(opts)
7335 editform=editform, **pycompat.strkwargs(opts)
7332 )
7336 )
7333
7337
7334 # don't allow tagging the null rev
7338 # don't allow tagging the null rev
7335 if (
7339 if (
7336 not opts.get(b'remove')
7340 not opts.get(b'remove')
7337 and scmutil.revsingle(repo, rev_).rev() == nullrev
7341 and scmutil.revsingle(repo, rev_).rev() == nullrev
7338 ):
7342 ):
7339 raise error.Abort(_(b"cannot tag null revision"))
7343 raise error.Abort(_(b"cannot tag null revision"))
7340
7344
7341 tagsmod.tag(
7345 tagsmod.tag(
7342 repo,
7346 repo,
7343 names,
7347 names,
7344 node,
7348 node,
7345 message,
7349 message,
7346 opts.get(b'local'),
7350 opts.get(b'local'),
7347 opts.get(b'user'),
7351 opts.get(b'user'),
7348 date,
7352 date,
7349 editor=editor,
7353 editor=editor,
7350 )
7354 )
7351
7355
7352
7356
7353 @command(
7357 @command(
7354 b'tags',
7358 b'tags',
7355 formatteropts,
7359 formatteropts,
7356 b'',
7360 b'',
7357 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7361 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7358 intents={INTENT_READONLY},
7362 intents={INTENT_READONLY},
7359 )
7363 )
7360 def tags(ui, repo, **opts):
7364 def tags(ui, repo, **opts):
7361 """list repository tags
7365 """list repository tags
7362
7366
7363 This lists both regular and local tags. When the -v/--verbose
7367 This lists both regular and local tags. When the -v/--verbose
7364 switch is used, a third column "local" is printed for local tags.
7368 switch is used, a third column "local" is printed for local tags.
7365 When the -q/--quiet switch is used, only the tag name is printed.
7369 When the -q/--quiet switch is used, only the tag name is printed.
7366
7370
7367 .. container:: verbose
7371 .. container:: verbose
7368
7372
7369 Template:
7373 Template:
7370
7374
7371 The following keywords are supported in addition to the common template
7375 The following keywords are supported in addition to the common template
7372 keywords and functions such as ``{tag}``. See also
7376 keywords and functions such as ``{tag}``. See also
7373 :hg:`help templates`.
7377 :hg:`help templates`.
7374
7378
7375 :type: String. ``local`` for local tags.
7379 :type: String. ``local`` for local tags.
7376
7380
7377 Returns 0 on success.
7381 Returns 0 on success.
7378 """
7382 """
7379
7383
7380 opts = pycompat.byteskwargs(opts)
7384 opts = pycompat.byteskwargs(opts)
7381 ui.pager(b'tags')
7385 ui.pager(b'tags')
7382 fm = ui.formatter(b'tags', opts)
7386 fm = ui.formatter(b'tags', opts)
7383 hexfunc = fm.hexfunc
7387 hexfunc = fm.hexfunc
7384
7388
7385 for t, n in reversed(repo.tagslist()):
7389 for t, n in reversed(repo.tagslist()):
7386 hn = hexfunc(n)
7390 hn = hexfunc(n)
7387 label = b'tags.normal'
7391 label = b'tags.normal'
7388 tagtype = b''
7392 tagtype = b''
7389 if repo.tagtype(t) == b'local':
7393 if repo.tagtype(t) == b'local':
7390 label = b'tags.local'
7394 label = b'tags.local'
7391 tagtype = b'local'
7395 tagtype = b'local'
7392
7396
7393 fm.startitem()
7397 fm.startitem()
7394 fm.context(repo=repo)
7398 fm.context(repo=repo)
7395 fm.write(b'tag', b'%s', t, label=label)
7399 fm.write(b'tag', b'%s', t, label=label)
7396 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7400 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7397 fm.condwrite(
7401 fm.condwrite(
7398 not ui.quiet,
7402 not ui.quiet,
7399 b'rev node',
7403 b'rev node',
7400 fmt,
7404 fmt,
7401 repo.changelog.rev(n),
7405 repo.changelog.rev(n),
7402 hn,
7406 hn,
7403 label=label,
7407 label=label,
7404 )
7408 )
7405 fm.condwrite(
7409 fm.condwrite(
7406 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7410 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7407 )
7411 )
7408 fm.plain(b'\n')
7412 fm.plain(b'\n')
7409 fm.end()
7413 fm.end()
7410
7414
7411
7415
7412 @command(
7416 @command(
7413 b'tip',
7417 b'tip',
7414 [
7418 [
7415 (b'p', b'patch', None, _(b'show patch')),
7419 (b'p', b'patch', None, _(b'show patch')),
7416 (b'g', b'git', None, _(b'use git extended diff format')),
7420 (b'g', b'git', None, _(b'use git extended diff format')),
7417 ]
7421 ]
7418 + templateopts,
7422 + templateopts,
7419 _(b'[-p] [-g]'),
7423 _(b'[-p] [-g]'),
7420 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7424 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7421 )
7425 )
7422 def tip(ui, repo, **opts):
7426 def tip(ui, repo, **opts):
7423 """show the tip revision (DEPRECATED)
7427 """show the tip revision (DEPRECATED)
7424
7428
7425 The tip revision (usually just called the tip) is the changeset
7429 The tip revision (usually just called the tip) is the changeset
7426 most recently added to the repository (and therefore the most
7430 most recently added to the repository (and therefore the most
7427 recently changed head).
7431 recently changed head).
7428
7432
7429 If you have just made a commit, that commit will be the tip. If
7433 If you have just made a commit, that commit will be the tip. If
7430 you have just pulled changes from another repository, the tip of
7434 you have just pulled changes from another repository, the tip of
7431 that repository becomes the current tip. The "tip" tag is special
7435 that repository becomes the current tip. The "tip" tag is special
7432 and cannot be renamed or assigned to a different changeset.
7436 and cannot be renamed or assigned to a different changeset.
7433
7437
7434 This command is deprecated, please use :hg:`heads` instead.
7438 This command is deprecated, please use :hg:`heads` instead.
7435
7439
7436 Returns 0 on success.
7440 Returns 0 on success.
7437 """
7441 """
7438 opts = pycompat.byteskwargs(opts)
7442 opts = pycompat.byteskwargs(opts)
7439 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7443 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7440 displayer.show(repo[b'tip'])
7444 displayer.show(repo[b'tip'])
7441 displayer.close()
7445 displayer.close()
7442
7446
7443
7447
7444 @command(
7448 @command(
7445 b'unbundle',
7449 b'unbundle',
7446 [
7450 [
7447 (
7451 (
7448 b'u',
7452 b'u',
7449 b'update',
7453 b'update',
7450 None,
7454 None,
7451 _(b'update to new branch head if changesets were unbundled'),
7455 _(b'update to new branch head if changesets were unbundled'),
7452 )
7456 )
7453 ],
7457 ],
7454 _(b'[-u] FILE...'),
7458 _(b'[-u] FILE...'),
7455 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7459 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7456 )
7460 )
7457 def unbundle(ui, repo, fname1, *fnames, **opts):
7461 def unbundle(ui, repo, fname1, *fnames, **opts):
7458 """apply one or more bundle files
7462 """apply one or more bundle files
7459
7463
7460 Apply one or more bundle files generated by :hg:`bundle`.
7464 Apply one or more bundle files generated by :hg:`bundle`.
7461
7465
7462 Returns 0 on success, 1 if an update has unresolved files.
7466 Returns 0 on success, 1 if an update has unresolved files.
7463 """
7467 """
7464 fnames = (fname1,) + fnames
7468 fnames = (fname1,) + fnames
7465
7469
7466 with repo.lock():
7470 with repo.lock():
7467 for fname in fnames:
7471 for fname in fnames:
7468 f = hg.openpath(ui, fname)
7472 f = hg.openpath(ui, fname)
7469 gen = exchange.readbundle(ui, f, fname)
7473 gen = exchange.readbundle(ui, f, fname)
7470 if isinstance(gen, streamclone.streamcloneapplier):
7474 if isinstance(gen, streamclone.streamcloneapplier):
7471 raise error.Abort(
7475 raise error.Abort(
7472 _(
7476 _(
7473 b'packed bundles cannot be applied with '
7477 b'packed bundles cannot be applied with '
7474 b'"hg unbundle"'
7478 b'"hg unbundle"'
7475 ),
7479 ),
7476 hint=_(b'use "hg debugapplystreamclonebundle"'),
7480 hint=_(b'use "hg debugapplystreamclonebundle"'),
7477 )
7481 )
7478 url = b'bundle:' + fname
7482 url = b'bundle:' + fname
7479 try:
7483 try:
7480 txnname = b'unbundle'
7484 txnname = b'unbundle'
7481 if not isinstance(gen, bundle2.unbundle20):
7485 if not isinstance(gen, bundle2.unbundle20):
7482 txnname = b'unbundle\n%s' % util.hidepassword(url)
7486 txnname = b'unbundle\n%s' % util.hidepassword(url)
7483 with repo.transaction(txnname) as tr:
7487 with repo.transaction(txnname) as tr:
7484 op = bundle2.applybundle(
7488 op = bundle2.applybundle(
7485 repo, gen, tr, source=b'unbundle', url=url
7489 repo, gen, tr, source=b'unbundle', url=url
7486 )
7490 )
7487 except error.BundleUnknownFeatureError as exc:
7491 except error.BundleUnknownFeatureError as exc:
7488 raise error.Abort(
7492 raise error.Abort(
7489 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7493 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7490 hint=_(
7494 hint=_(
7491 b"see https://mercurial-scm.org/"
7495 b"see https://mercurial-scm.org/"
7492 b"wiki/BundleFeature for more "
7496 b"wiki/BundleFeature for more "
7493 b"information"
7497 b"information"
7494 ),
7498 ),
7495 )
7499 )
7496 modheads = bundle2.combinechangegroupresults(op)
7500 modheads = bundle2.combinechangegroupresults(op)
7497
7501
7498 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7502 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7499
7503
7500
7504
7501 @command(
7505 @command(
7502 b'unshelve',
7506 b'unshelve',
7503 [
7507 [
7504 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7508 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7505 (
7509 (
7506 b'c',
7510 b'c',
7507 b'continue',
7511 b'continue',
7508 None,
7512 None,
7509 _(b'continue an incomplete unshelve operation'),
7513 _(b'continue an incomplete unshelve operation'),
7510 ),
7514 ),
7511 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7515 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7512 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7516 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7513 (
7517 (
7514 b'n',
7518 b'n',
7515 b'name',
7519 b'name',
7516 b'',
7520 b'',
7517 _(b'restore shelved change with given name'),
7521 _(b'restore shelved change with given name'),
7518 _(b'NAME'),
7522 _(b'NAME'),
7519 ),
7523 ),
7520 (b't', b'tool', b'', _(b'specify merge tool')),
7524 (b't', b'tool', b'', _(b'specify merge tool')),
7521 (
7525 (
7522 b'',
7526 b'',
7523 b'date',
7527 b'date',
7524 b'',
7528 b'',
7525 _(b'set date for temporary commits (DEPRECATED)'),
7529 _(b'set date for temporary commits (DEPRECATED)'),
7526 _(b'DATE'),
7530 _(b'DATE'),
7527 ),
7531 ),
7528 ],
7532 ],
7529 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7533 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7530 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7534 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7531 )
7535 )
7532 def unshelve(ui, repo, *shelved, **opts):
7536 def unshelve(ui, repo, *shelved, **opts):
7533 """restore a shelved change to the working directory
7537 """restore a shelved change to the working directory
7534
7538
7535 This command accepts an optional name of a shelved change to
7539 This command accepts an optional name of a shelved change to
7536 restore. If none is given, the most recent shelved change is used.
7540 restore. If none is given, the most recent shelved change is used.
7537
7541
7538 If a shelved change is applied successfully, the bundle that
7542 If a shelved change is applied successfully, the bundle that
7539 contains the shelved changes is moved to a backup location
7543 contains the shelved changes is moved to a backup location
7540 (.hg/shelve-backup).
7544 (.hg/shelve-backup).
7541
7545
7542 Since you can restore a shelved change on top of an arbitrary
7546 Since you can restore a shelved change on top of an arbitrary
7543 commit, it is possible that unshelving will result in a conflict
7547 commit, it is possible that unshelving will result in a conflict
7544 between your changes and the commits you are unshelving onto. If
7548 between your changes and the commits you are unshelving onto. If
7545 this occurs, you must resolve the conflict, then use
7549 this occurs, you must resolve the conflict, then use
7546 ``--continue`` to complete the unshelve operation. (The bundle
7550 ``--continue`` to complete the unshelve operation. (The bundle
7547 will not be moved until you successfully complete the unshelve.)
7551 will not be moved until you successfully complete the unshelve.)
7548
7552
7549 (Alternatively, you can use ``--abort`` to abandon an unshelve
7553 (Alternatively, you can use ``--abort`` to abandon an unshelve
7550 that causes a conflict. This reverts the unshelved changes, and
7554 that causes a conflict. This reverts the unshelved changes, and
7551 leaves the bundle in place.)
7555 leaves the bundle in place.)
7552
7556
7553 If bare shelved change (without interactive, include and exclude
7557 If bare shelved change (without interactive, include and exclude
7554 option) was done on newly created branch it would restore branch
7558 option) was done on newly created branch it would restore branch
7555 information to the working directory.
7559 information to the working directory.
7556
7560
7557 After a successful unshelve, the shelved changes are stored in a
7561 After a successful unshelve, the shelved changes are stored in a
7558 backup directory. Only the N most recent backups are kept. N
7562 backup directory. Only the N most recent backups are kept. N
7559 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7563 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7560 configuration option.
7564 configuration option.
7561
7565
7562 .. container:: verbose
7566 .. container:: verbose
7563
7567
7564 Timestamp in seconds is used to decide order of backups. More
7568 Timestamp in seconds is used to decide order of backups. More
7565 than ``maxbackups`` backups are kept, if same timestamp
7569 than ``maxbackups`` backups are kept, if same timestamp
7566 prevents from deciding exact order of them, for safety.
7570 prevents from deciding exact order of them, for safety.
7567
7571
7568 Selected changes can be unshelved with ``--interactive`` flag.
7572 Selected changes can be unshelved with ``--interactive`` flag.
7569 The working directory is updated with the selected changes, and
7573 The working directory is updated with the selected changes, and
7570 only the unselected changes remain shelved.
7574 only the unselected changes remain shelved.
7571 Note: The whole shelve is applied to working directory first before
7575 Note: The whole shelve is applied to working directory first before
7572 running interactively. So, this will bring up all the conflicts between
7576 running interactively. So, this will bring up all the conflicts between
7573 working directory and the shelve, irrespective of which changes will be
7577 working directory and the shelve, irrespective of which changes will be
7574 unshelved.
7578 unshelved.
7575 """
7579 """
7576 with repo.wlock():
7580 with repo.wlock():
7577 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7581 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7578
7582
7579
7583
7580 statemod.addunfinished(
7584 statemod.addunfinished(
7581 b'unshelve',
7585 b'unshelve',
7582 fname=b'shelvedstate',
7586 fname=b'shelvedstate',
7583 continueflag=True,
7587 continueflag=True,
7584 abortfunc=shelvemod.hgabortunshelve,
7588 abortfunc=shelvemod.hgabortunshelve,
7585 continuefunc=shelvemod.hgcontinueunshelve,
7589 continuefunc=shelvemod.hgcontinueunshelve,
7586 cmdmsg=_(b'unshelve already in progress'),
7590 cmdmsg=_(b'unshelve already in progress'),
7587 )
7591 )
7588
7592
7589
7593
7590 @command(
7594 @command(
7591 b'update|up|checkout|co',
7595 b'update|up|checkout|co',
7592 [
7596 [
7593 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7597 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7594 (b'c', b'check', None, _(b'require clean working directory')),
7598 (b'c', b'check', None, _(b'require clean working directory')),
7595 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7599 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7596 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7600 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7597 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7601 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7598 ]
7602 ]
7599 + mergetoolopts,
7603 + mergetoolopts,
7600 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7604 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7601 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7605 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7602 helpbasic=True,
7606 helpbasic=True,
7603 )
7607 )
7604 def update(ui, repo, node=None, **opts):
7608 def update(ui, repo, node=None, **opts):
7605 """update working directory (or switch revisions)
7609 """update working directory (or switch revisions)
7606
7610
7607 Update the repository's working directory to the specified
7611 Update the repository's working directory to the specified
7608 changeset. If no changeset is specified, update to the tip of the
7612 changeset. If no changeset is specified, update to the tip of the
7609 current named branch and move the active bookmark (see :hg:`help
7613 current named branch and move the active bookmark (see :hg:`help
7610 bookmarks`).
7614 bookmarks`).
7611
7615
7612 Update sets the working directory's parent revision to the specified
7616 Update sets the working directory's parent revision to the specified
7613 changeset (see :hg:`help parents`).
7617 changeset (see :hg:`help parents`).
7614
7618
7615 If the changeset is not a descendant or ancestor of the working
7619 If the changeset is not a descendant or ancestor of the working
7616 directory's parent and there are uncommitted changes, the update is
7620 directory's parent and there are uncommitted changes, the update is
7617 aborted. With the -c/--check option, the working directory is checked
7621 aborted. With the -c/--check option, the working directory is checked
7618 for uncommitted changes; if none are found, the working directory is
7622 for uncommitted changes; if none are found, the working directory is
7619 updated to the specified changeset.
7623 updated to the specified changeset.
7620
7624
7621 .. container:: verbose
7625 .. container:: verbose
7622
7626
7623 The -C/--clean, -c/--check, and -m/--merge options control what
7627 The -C/--clean, -c/--check, and -m/--merge options control what
7624 happens if the working directory contains uncommitted changes.
7628 happens if the working directory contains uncommitted changes.
7625 At most of one of them can be specified.
7629 At most of one of them can be specified.
7626
7630
7627 1. If no option is specified, and if
7631 1. If no option is specified, and if
7628 the requested changeset is an ancestor or descendant of
7632 the requested changeset is an ancestor or descendant of
7629 the working directory's parent, the uncommitted changes
7633 the working directory's parent, the uncommitted changes
7630 are merged into the requested changeset and the merged
7634 are merged into the requested changeset and the merged
7631 result is left uncommitted. If the requested changeset is
7635 result is left uncommitted. If the requested changeset is
7632 not an ancestor or descendant (that is, it is on another
7636 not an ancestor or descendant (that is, it is on another
7633 branch), the update is aborted and the uncommitted changes
7637 branch), the update is aborted and the uncommitted changes
7634 are preserved.
7638 are preserved.
7635
7639
7636 2. With the -m/--merge option, the update is allowed even if the
7640 2. With the -m/--merge option, the update is allowed even if the
7637 requested changeset is not an ancestor or descendant of
7641 requested changeset is not an ancestor or descendant of
7638 the working directory's parent.
7642 the working directory's parent.
7639
7643
7640 3. With the -c/--check option, the update is aborted and the
7644 3. With the -c/--check option, the update is aborted and the
7641 uncommitted changes are preserved.
7645 uncommitted changes are preserved.
7642
7646
7643 4. With the -C/--clean option, uncommitted changes are discarded and
7647 4. With the -C/--clean option, uncommitted changes are discarded and
7644 the working directory is updated to the requested changeset.
7648 the working directory is updated to the requested changeset.
7645
7649
7646 To cancel an uncommitted merge (and lose your changes), use
7650 To cancel an uncommitted merge (and lose your changes), use
7647 :hg:`merge --abort`.
7651 :hg:`merge --abort`.
7648
7652
7649 Use null as the changeset to remove the working directory (like
7653 Use null as the changeset to remove the working directory (like
7650 :hg:`clone -U`).
7654 :hg:`clone -U`).
7651
7655
7652 If you want to revert just one file to an older revision, use
7656 If you want to revert just one file to an older revision, use
7653 :hg:`revert [-r REV] NAME`.
7657 :hg:`revert [-r REV] NAME`.
7654
7658
7655 See :hg:`help dates` for a list of formats valid for -d/--date.
7659 See :hg:`help dates` for a list of formats valid for -d/--date.
7656
7660
7657 Returns 0 on success, 1 if there are unresolved files.
7661 Returns 0 on success, 1 if there are unresolved files.
7658 """
7662 """
7659 rev = opts.get('rev')
7663 rev = opts.get('rev')
7660 date = opts.get('date')
7664 date = opts.get('date')
7661 clean = opts.get('clean')
7665 clean = opts.get('clean')
7662 check = opts.get('check')
7666 check = opts.get('check')
7663 merge = opts.get('merge')
7667 merge = opts.get('merge')
7664 if rev and node:
7668 if rev and node:
7665 raise error.Abort(_(b"please specify just one revision"))
7669 raise error.Abort(_(b"please specify just one revision"))
7666
7670
7667 if ui.configbool(b'commands', b'update.requiredest'):
7671 if ui.configbool(b'commands', b'update.requiredest'):
7668 if not node and not rev and not date:
7672 if not node and not rev and not date:
7669 raise error.Abort(
7673 raise error.Abort(
7670 _(b'you must specify a destination'),
7674 _(b'you must specify a destination'),
7671 hint=_(b'for example: hg update ".::"'),
7675 hint=_(b'for example: hg update ".::"'),
7672 )
7676 )
7673
7677
7674 if rev is None or rev == b'':
7678 if rev is None or rev == b'':
7675 rev = node
7679 rev = node
7676
7680
7677 if date and rev is not None:
7681 if date and rev is not None:
7678 raise error.Abort(_(b"you can't specify a revision and a date"))
7682 raise error.Abort(_(b"you can't specify a revision and a date"))
7679
7683
7680 if len([x for x in (clean, check, merge) if x]) > 1:
7684 if len([x for x in (clean, check, merge) if x]) > 1:
7681 raise error.Abort(
7685 raise error.Abort(
7682 _(
7686 _(
7683 b"can only specify one of -C/--clean, -c/--check, "
7687 b"can only specify one of -C/--clean, -c/--check, "
7684 b"or -m/--merge"
7688 b"or -m/--merge"
7685 )
7689 )
7686 )
7690 )
7687
7691
7688 updatecheck = None
7692 updatecheck = None
7689 if check:
7693 if check:
7690 updatecheck = b'abort'
7694 updatecheck = b'abort'
7691 elif merge:
7695 elif merge:
7692 updatecheck = b'none'
7696 updatecheck = b'none'
7693
7697
7694 with repo.wlock():
7698 with repo.wlock():
7695 cmdutil.clearunfinished(repo)
7699 cmdutil.clearunfinished(repo)
7696 if date:
7700 if date:
7697 rev = cmdutil.finddate(ui, repo, date)
7701 rev = cmdutil.finddate(ui, repo, date)
7698
7702
7699 # if we defined a bookmark, we have to remember the original name
7703 # if we defined a bookmark, we have to remember the original name
7700 brev = rev
7704 brev = rev
7701 if rev:
7705 if rev:
7702 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7706 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7703 ctx = scmutil.revsingle(repo, rev, default=None)
7707 ctx = scmutil.revsingle(repo, rev, default=None)
7704 rev = ctx.rev()
7708 rev = ctx.rev()
7705 hidden = ctx.hidden()
7709 hidden = ctx.hidden()
7706 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7710 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7707 with ui.configoverride(overrides, b'update'):
7711 with ui.configoverride(overrides, b'update'):
7708 ret = hg.updatetotally(
7712 ret = hg.updatetotally(
7709 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7713 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7710 )
7714 )
7711 if hidden:
7715 if hidden:
7712 ctxstr = ctx.hex()[:12]
7716 ctxstr = ctx.hex()[:12]
7713 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7717 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7714
7718
7715 if ctx.obsolete():
7719 if ctx.obsolete():
7716 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7720 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7717 ui.warn(b"(%s)\n" % obsfatemsg)
7721 ui.warn(b"(%s)\n" % obsfatemsg)
7718 return ret
7722 return ret
7719
7723
7720
7724
7721 @command(
7725 @command(
7722 b'verify',
7726 b'verify',
7723 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7727 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7724 helpcategory=command.CATEGORY_MAINTENANCE,
7728 helpcategory=command.CATEGORY_MAINTENANCE,
7725 )
7729 )
7726 def verify(ui, repo, **opts):
7730 def verify(ui, repo, **opts):
7727 """verify the integrity of the repository
7731 """verify the integrity of the repository
7728
7732
7729 Verify the integrity of the current repository.
7733 Verify the integrity of the current repository.
7730
7734
7731 This will perform an extensive check of the repository's
7735 This will perform an extensive check of the repository's
7732 integrity, validating the hashes and checksums of each entry in
7736 integrity, validating the hashes and checksums of each entry in
7733 the changelog, manifest, and tracked files, as well as the
7737 the changelog, manifest, and tracked files, as well as the
7734 integrity of their crosslinks and indices.
7738 integrity of their crosslinks and indices.
7735
7739
7736 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7740 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7737 for more information about recovery from corruption of the
7741 for more information about recovery from corruption of the
7738 repository.
7742 repository.
7739
7743
7740 Returns 0 on success, 1 if errors are encountered.
7744 Returns 0 on success, 1 if errors are encountered.
7741 """
7745 """
7742 opts = pycompat.byteskwargs(opts)
7746 opts = pycompat.byteskwargs(opts)
7743
7747
7744 level = None
7748 level = None
7745 if opts[b'full']:
7749 if opts[b'full']:
7746 level = verifymod.VERIFY_FULL
7750 level = verifymod.VERIFY_FULL
7747 return hg.verify(repo, level)
7751 return hg.verify(repo, level)
7748
7752
7749
7753
7750 @command(
7754 @command(
7751 b'version',
7755 b'version',
7752 [] + formatteropts,
7756 [] + formatteropts,
7753 helpcategory=command.CATEGORY_HELP,
7757 helpcategory=command.CATEGORY_HELP,
7754 norepo=True,
7758 norepo=True,
7755 intents={INTENT_READONLY},
7759 intents={INTENT_READONLY},
7756 )
7760 )
7757 def version_(ui, **opts):
7761 def version_(ui, **opts):
7758 """output version and copyright information
7762 """output version and copyright information
7759
7763
7760 .. container:: verbose
7764 .. container:: verbose
7761
7765
7762 Template:
7766 Template:
7763
7767
7764 The following keywords are supported. See also :hg:`help templates`.
7768 The following keywords are supported. See also :hg:`help templates`.
7765
7769
7766 :extensions: List of extensions.
7770 :extensions: List of extensions.
7767 :ver: String. Version number.
7771 :ver: String. Version number.
7768
7772
7769 And each entry of ``{extensions}`` provides the following sub-keywords
7773 And each entry of ``{extensions}`` provides the following sub-keywords
7770 in addition to ``{ver}``.
7774 in addition to ``{ver}``.
7771
7775
7772 :bundled: Boolean. True if included in the release.
7776 :bundled: Boolean. True if included in the release.
7773 :name: String. Extension name.
7777 :name: String. Extension name.
7774 """
7778 """
7775 opts = pycompat.byteskwargs(opts)
7779 opts = pycompat.byteskwargs(opts)
7776 if ui.verbose:
7780 if ui.verbose:
7777 ui.pager(b'version')
7781 ui.pager(b'version')
7778 fm = ui.formatter(b"version", opts)
7782 fm = ui.formatter(b"version", opts)
7779 fm.startitem()
7783 fm.startitem()
7780 fm.write(
7784 fm.write(
7781 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7785 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7782 )
7786 )
7783 license = _(
7787 license = _(
7784 b"(see https://mercurial-scm.org for more information)\n"
7788 b"(see https://mercurial-scm.org for more information)\n"
7785 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7789 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7786 b"This is free software; see the source for copying conditions. "
7790 b"This is free software; see the source for copying conditions. "
7787 b"There is NO\nwarranty; "
7791 b"There is NO\nwarranty; "
7788 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7792 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7789 )
7793 )
7790 if not ui.quiet:
7794 if not ui.quiet:
7791 fm.plain(license)
7795 fm.plain(license)
7792
7796
7793 if ui.verbose:
7797 if ui.verbose:
7794 fm.plain(_(b"\nEnabled extensions:\n\n"))
7798 fm.plain(_(b"\nEnabled extensions:\n\n"))
7795 # format names and versions into columns
7799 # format names and versions into columns
7796 names = []
7800 names = []
7797 vers = []
7801 vers = []
7798 isinternals = []
7802 isinternals = []
7799 for name, module in extensions.extensions():
7803 for name, module in extensions.extensions():
7800 names.append(name)
7804 names.append(name)
7801 vers.append(extensions.moduleversion(module) or None)
7805 vers.append(extensions.moduleversion(module) or None)
7802 isinternals.append(extensions.ismoduleinternal(module))
7806 isinternals.append(extensions.ismoduleinternal(module))
7803 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7807 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7804 if names:
7808 if names:
7805 namefmt = b" %%-%ds " % max(len(n) for n in names)
7809 namefmt = b" %%-%ds " % max(len(n) for n in names)
7806 places = [_(b"external"), _(b"internal")]
7810 places = [_(b"external"), _(b"internal")]
7807 for n, v, p in zip(names, vers, isinternals):
7811 for n, v, p in zip(names, vers, isinternals):
7808 fn.startitem()
7812 fn.startitem()
7809 fn.condwrite(ui.verbose, b"name", namefmt, n)
7813 fn.condwrite(ui.verbose, b"name", namefmt, n)
7810 if ui.verbose:
7814 if ui.verbose:
7811 fn.plain(b"%s " % places[p])
7815 fn.plain(b"%s " % places[p])
7812 fn.data(bundled=p)
7816 fn.data(bundled=p)
7813 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7817 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7814 if ui.verbose:
7818 if ui.verbose:
7815 fn.plain(b"\n")
7819 fn.plain(b"\n")
7816 fn.end()
7820 fn.end()
7817 fm.end()
7821 fm.end()
7818
7822
7819
7823
7820 def loadcmdtable(ui, name, cmdtable):
7824 def loadcmdtable(ui, name, cmdtable):
7821 """Load command functions from specified cmdtable
7825 """Load command functions from specified cmdtable
7822 """
7826 """
7823 overrides = [cmd for cmd in cmdtable if cmd in table]
7827 overrides = [cmd for cmd in cmdtable if cmd in table]
7824 if overrides:
7828 if overrides:
7825 ui.warn(
7829 ui.warn(
7826 _(b"extension '%s' overrides commands: %s\n")
7830 _(b"extension '%s' overrides commands: %s\n")
7827 % (name, b" ".join(overrides))
7831 % (name, b" ".join(overrides))
7828 )
7832 )
7829 table.update(cmdtable)
7833 table.update(cmdtable)
@@ -1,39 +1,41 b''
1 == New Features ==
1 == New Features ==
2
2
3 * `hg purge`/`hg clean` can now delete ignored files instead of
3 * `hg purge`/`hg clean` can now delete ignored files instead of
4 untracked files, with the new -i flag.
4 untracked files, with the new -i flag.
5
5
6 * `hg log` now defaults to using an '%' symbol for commits involved
6 * `hg log` now defaults to using an '%' symbol for commits involved
7 in unresolved merge conflicts. That includes unresolved conflicts
7 in unresolved merge conflicts. That includes unresolved conflicts
8 caused by e.g. `hg update --merge` and `hg graft`. '@' still takes
8 caused by e.g. `hg update --merge` and `hg graft`. '@' still takes
9 precedence, so what used to be marked '@' still is.
9 precedence, so what used to be marked '@' still is.
10
10
11 * New `conflictlocal()` and `conflictother()` revsets return the
11 * New `conflictlocal()` and `conflictother()` revsets return the
12 commits that are being merged, when there are conflicts. Also works
12 commits that are being merged, when there are conflicts. Also works
13 for conflicts caused by e.g. `hg graft`.
13 for conflicts caused by e.g. `hg graft`.
14
14
15 * `hg copy --forget` can be used to unmark a file as copied.
16
15
17
16 == New Experimental Features ==
18 == New Experimental Features ==
17
19
18
20
19 == Bug Fixes ==
21 == Bug Fixes ==
20
22
21
23
22 == Backwards Compatibility Changes ==
24 == Backwards Compatibility Changes ==
23
25
24 * When `hg rebase` pauses for merge conflict resolution, the working
26 * When `hg rebase` pauses for merge conflict resolution, the working
25 copy will no longer have the rebased node as a second parent. You
27 copy will no longer have the rebased node as a second parent. You
26 can use the new `conflictparents()` revset for finding the other
28 can use the new `conflictparents()` revset for finding the other
27 parent during a conflict.
29 parent during a conflict.
28
30
29
31
30 == Internal API Changes ==
32 == Internal API Changes ==
31
33
32 * The deprecated `ui.progress()` has now been deleted. Please use
34 * The deprecated `ui.progress()` has now been deleted. Please use
33 `ui.makeprogress()` instead.
35 `ui.makeprogress()` instead.
34
36
35 * `hg.merge()` has lost its `abort` argument. Please call
37 * `hg.merge()` has lost its `abort` argument. Please call
36 `hg.abortmerge()` directly instead.
38 `hg.abortmerge()` directly instead.
37
39
38 * The `*others` argument of `cmdutil.check_incompatible_arguments()`
40 * The `*others` argument of `cmdutil.check_incompatible_arguments()`
39 changed from being varargs argument to being a single collection.
41 changed from being varargs argument to being a single collection.
@@ -1,429 +1,429 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 abort
3 abort
4 add
4 add
5 addremove
5 addremove
6 annotate
6 annotate
7 archive
7 archive
8 backout
8 backout
9 bisect
9 bisect
10 bookmarks
10 bookmarks
11 branch
11 branch
12 branches
12 branches
13 bundle
13 bundle
14 cat
14 cat
15 clone
15 clone
16 commit
16 commit
17 config
17 config
18 continue
18 continue
19 copy
19 copy
20 diff
20 diff
21 export
21 export
22 files
22 files
23 forget
23 forget
24 graft
24 graft
25 grep
25 grep
26 heads
26 heads
27 help
27 help
28 identify
28 identify
29 import
29 import
30 incoming
30 incoming
31 init
31 init
32 locate
32 locate
33 log
33 log
34 manifest
34 manifest
35 merge
35 merge
36 outgoing
36 outgoing
37 parents
37 parents
38 paths
38 paths
39 phase
39 phase
40 pull
40 pull
41 push
41 push
42 recover
42 recover
43 remove
43 remove
44 rename
44 rename
45 resolve
45 resolve
46 revert
46 revert
47 rollback
47 rollback
48 root
48 root
49 serve
49 serve
50 shelve
50 shelve
51 status
51 status
52 summary
52 summary
53 tag
53 tag
54 tags
54 tags
55 tip
55 tip
56 unbundle
56 unbundle
57 unshelve
57 unshelve
58 update
58 update
59 verify
59 verify
60 version
60 version
61
61
62 Show all commands that start with "a"
62 Show all commands that start with "a"
63 $ hg debugcomplete a
63 $ hg debugcomplete a
64 abort
64 abort
65 add
65 add
66 addremove
66 addremove
67 annotate
67 annotate
68 archive
68 archive
69
69
70 Do not show debug commands if there are other candidates
70 Do not show debug commands if there are other candidates
71 $ hg debugcomplete d
71 $ hg debugcomplete d
72 diff
72 diff
73
73
74 Show debug commands if there are no other candidates
74 Show debug commands if there are no other candidates
75 $ hg debugcomplete debug
75 $ hg debugcomplete debug
76 debugancestor
76 debugancestor
77 debugapplystreamclonebundle
77 debugapplystreamclonebundle
78 debugbuilddag
78 debugbuilddag
79 debugbundle
79 debugbundle
80 debugcapabilities
80 debugcapabilities
81 debugcheckstate
81 debugcheckstate
82 debugcolor
82 debugcolor
83 debugcommands
83 debugcommands
84 debugcomplete
84 debugcomplete
85 debugconfig
85 debugconfig
86 debugcreatestreamclonebundle
86 debugcreatestreamclonebundle
87 debugdag
87 debugdag
88 debugdata
88 debugdata
89 debugdate
89 debugdate
90 debugdeltachain
90 debugdeltachain
91 debugdirstate
91 debugdirstate
92 debugdiscovery
92 debugdiscovery
93 debugdownload
93 debugdownload
94 debugextensions
94 debugextensions
95 debugfileset
95 debugfileset
96 debugformat
96 debugformat
97 debugfsinfo
97 debugfsinfo
98 debuggetbundle
98 debuggetbundle
99 debugignore
99 debugignore
100 debugindex
100 debugindex
101 debugindexdot
101 debugindexdot
102 debugindexstats
102 debugindexstats
103 debuginstall
103 debuginstall
104 debugknown
104 debugknown
105 debuglabelcomplete
105 debuglabelcomplete
106 debuglocks
106 debuglocks
107 debugmanifestfulltextcache
107 debugmanifestfulltextcache
108 debugmergestate
108 debugmergestate
109 debugnamecomplete
109 debugnamecomplete
110 debugnodemap
110 debugnodemap
111 debugobsolete
111 debugobsolete
112 debugp1copies
112 debugp1copies
113 debugp2copies
113 debugp2copies
114 debugpathcomplete
114 debugpathcomplete
115 debugpathcopies
115 debugpathcopies
116 debugpeer
116 debugpeer
117 debugpickmergetool
117 debugpickmergetool
118 debugpushkey
118 debugpushkey
119 debugpvec
119 debugpvec
120 debugrebuilddirstate
120 debugrebuilddirstate
121 debugrebuildfncache
121 debugrebuildfncache
122 debugrename
122 debugrename
123 debugrevlog
123 debugrevlog
124 debugrevlogindex
124 debugrevlogindex
125 debugrevspec
125 debugrevspec
126 debugserve
126 debugserve
127 debugsetparents
127 debugsetparents
128 debugsidedata
128 debugsidedata
129 debugssl
129 debugssl
130 debugsub
130 debugsub
131 debugsuccessorssets
131 debugsuccessorssets
132 debugtagscache
132 debugtagscache
133 debugtemplate
133 debugtemplate
134 debuguigetpass
134 debuguigetpass
135 debuguiprompt
135 debuguiprompt
136 debugupdatecaches
136 debugupdatecaches
137 debugupgraderepo
137 debugupgraderepo
138 debugwalk
138 debugwalk
139 debugwhyunstable
139 debugwhyunstable
140 debugwireargs
140 debugwireargs
141 debugwireproto
141 debugwireproto
142
142
143 Do not show the alias of a debug command if there are other candidates
143 Do not show the alias of a debug command if there are other candidates
144 (this should hide rawcommit)
144 (this should hide rawcommit)
145 $ hg debugcomplete r
145 $ hg debugcomplete r
146 recover
146 recover
147 remove
147 remove
148 rename
148 rename
149 resolve
149 resolve
150 revert
150 revert
151 rollback
151 rollback
152 root
152 root
153 Show the alias of a debug command if there are no other candidates
153 Show the alias of a debug command if there are no other candidates
154 $ hg debugcomplete rawc
154 $ hg debugcomplete rawc
155
155
156
156
157 Show the global options
157 Show the global options
158 $ hg debugcomplete --options | sort
158 $ hg debugcomplete --options | sort
159 --color
159 --color
160 --config
160 --config
161 --cwd
161 --cwd
162 --debug
162 --debug
163 --debugger
163 --debugger
164 --encoding
164 --encoding
165 --encodingmode
165 --encodingmode
166 --help
166 --help
167 --hidden
167 --hidden
168 --noninteractive
168 --noninteractive
169 --pager
169 --pager
170 --profile
170 --profile
171 --quiet
171 --quiet
172 --repository
172 --repository
173 --time
173 --time
174 --traceback
174 --traceback
175 --verbose
175 --verbose
176 --version
176 --version
177 -R
177 -R
178 -h
178 -h
179 -q
179 -q
180 -v
180 -v
181 -y
181 -y
182
182
183 Show the options for the "serve" command
183 Show the options for the "serve" command
184 $ hg debugcomplete --options serve | sort
184 $ hg debugcomplete --options serve | sort
185 --accesslog
185 --accesslog
186 --address
186 --address
187 --certificate
187 --certificate
188 --cmdserver
188 --cmdserver
189 --color
189 --color
190 --config
190 --config
191 --cwd
191 --cwd
192 --daemon
192 --daemon
193 --daemon-postexec
193 --daemon-postexec
194 --debug
194 --debug
195 --debugger
195 --debugger
196 --encoding
196 --encoding
197 --encodingmode
197 --encodingmode
198 --errorlog
198 --errorlog
199 --help
199 --help
200 --hidden
200 --hidden
201 --ipv6
201 --ipv6
202 --name
202 --name
203 --noninteractive
203 --noninteractive
204 --pager
204 --pager
205 --pid-file
205 --pid-file
206 --port
206 --port
207 --prefix
207 --prefix
208 --print-url
208 --print-url
209 --profile
209 --profile
210 --quiet
210 --quiet
211 --repository
211 --repository
212 --stdio
212 --stdio
213 --style
213 --style
214 --subrepos
214 --subrepos
215 --templates
215 --templates
216 --time
216 --time
217 --traceback
217 --traceback
218 --verbose
218 --verbose
219 --version
219 --version
220 --web-conf
220 --web-conf
221 -6
221 -6
222 -A
222 -A
223 -E
223 -E
224 -R
224 -R
225 -S
225 -S
226 -a
226 -a
227 -d
227 -d
228 -h
228 -h
229 -n
229 -n
230 -p
230 -p
231 -q
231 -q
232 -t
232 -t
233 -v
233 -v
234 -y
234 -y
235
235
236 Show an error if we use --options with an ambiguous abbreviation
236 Show an error if we use --options with an ambiguous abbreviation
237 $ hg debugcomplete --options s
237 $ hg debugcomplete --options s
238 hg: command 's' is ambiguous:
238 hg: command 's' is ambiguous:
239 serve shelve showconfig status summary
239 serve shelve showconfig status summary
240 [255]
240 [255]
241
241
242 Show all commands + options
242 Show all commands + options
243 $ hg debugcommands
243 $ hg debugcommands
244 abort: dry-run
244 abort: dry-run
245 add: include, exclude, subrepos, dry-run
245 add: include, exclude, subrepos, dry-run
246 addremove: similarity, subrepos, include, exclude, dry-run
246 addremove: similarity, subrepos, include, exclude, dry-run
247 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
247 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
248 archive: no-decode, prefix, rev, type, subrepos, include, exclude
248 archive: no-decode, prefix, rev, type, subrepos, include, exclude
249 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
249 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
250 bisect: reset, good, bad, skip, extend, command, noupdate
250 bisect: reset, good, bad, skip, extend, command, noupdate
251 bookmarks: force, rev, delete, rename, inactive, list, template
251 bookmarks: force, rev, delete, rename, inactive, list, template
252 branch: force, clean, rev
252 branch: force, clean, rev
253 branches: active, closed, rev, template
253 branches: active, closed, rev, template
254 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
254 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
255 cat: output, rev, decode, include, exclude, template
255 cat: output, rev, decode, include, exclude, template
256 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
256 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
257 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
257 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
258 config: untrusted, edit, local, global, template
258 config: untrusted, edit, local, global, template
259 continue: dry-run
259 continue: dry-run
260 copy: after, force, include, exclude, dry-run
260 copy: forget, after, force, include, exclude, dry-run
261 debugancestor:
261 debugancestor:
262 debugapplystreamclonebundle:
262 debugapplystreamclonebundle:
263 debugbuilddag: mergeable-file, overwritten-file, new-file
263 debugbuilddag: mergeable-file, overwritten-file, new-file
264 debugbundle: all, part-type, spec
264 debugbundle: all, part-type, spec
265 debugcapabilities:
265 debugcapabilities:
266 debugcheckstate:
266 debugcheckstate:
267 debugcolor: style
267 debugcolor: style
268 debugcommands:
268 debugcommands:
269 debugcomplete: options
269 debugcomplete: options
270 debugcreatestreamclonebundle:
270 debugcreatestreamclonebundle:
271 debugdag: tags, branches, dots, spaces
271 debugdag: tags, branches, dots, spaces
272 debugdata: changelog, manifest, dir
272 debugdata: changelog, manifest, dir
273 debugdate: extended
273 debugdate: extended
274 debugdeltachain: changelog, manifest, dir, template
274 debugdeltachain: changelog, manifest, dir, template
275 debugdirstate: nodates, dates, datesort
275 debugdirstate: nodates, dates, datesort
276 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
276 debugdiscovery: old, nonheads, rev, seed, ssh, remotecmd, insecure
277 debugdownload: output
277 debugdownload: output
278 debugextensions: template
278 debugextensions: template
279 debugfileset: rev, all-files, show-matcher, show-stage
279 debugfileset: rev, all-files, show-matcher, show-stage
280 debugformat: template
280 debugformat: template
281 debugfsinfo:
281 debugfsinfo:
282 debuggetbundle: head, common, type
282 debuggetbundle: head, common, type
283 debugignore:
283 debugignore:
284 debugindex: changelog, manifest, dir, template
284 debugindex: changelog, manifest, dir, template
285 debugindexdot: changelog, manifest, dir
285 debugindexdot: changelog, manifest, dir
286 debugindexstats:
286 debugindexstats:
287 debuginstall: template
287 debuginstall: template
288 debugknown:
288 debugknown:
289 debuglabelcomplete:
289 debuglabelcomplete:
290 debuglocks: force-lock, force-wlock, set-lock, set-wlock
290 debuglocks: force-lock, force-wlock, set-lock, set-wlock
291 debugmanifestfulltextcache: clear, add
291 debugmanifestfulltextcache: clear, add
292 debugmergestate:
292 debugmergestate:
293 debugnamecomplete:
293 debugnamecomplete:
294 debugnodemap: dump-new, dump-disk, check, metadata
294 debugnodemap: dump-new, dump-disk, check, metadata
295 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
295 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
296 debugp1copies: rev
296 debugp1copies: rev
297 debugp2copies: rev
297 debugp2copies: rev
298 debugpathcomplete: full, normal, added, removed
298 debugpathcomplete: full, normal, added, removed
299 debugpathcopies: include, exclude
299 debugpathcopies: include, exclude
300 debugpeer:
300 debugpeer:
301 debugpickmergetool: rev, changedelete, include, exclude, tool
301 debugpickmergetool: rev, changedelete, include, exclude, tool
302 debugpushkey:
302 debugpushkey:
303 debugpvec:
303 debugpvec:
304 debugrebuilddirstate: rev, minimal
304 debugrebuilddirstate: rev, minimal
305 debugrebuildfncache:
305 debugrebuildfncache:
306 debugrename: rev
306 debugrename: rev
307 debugrevlog: changelog, manifest, dir, dump
307 debugrevlog: changelog, manifest, dir, dump
308 debugrevlogindex: changelog, manifest, dir, format
308 debugrevlogindex: changelog, manifest, dir, format
309 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
309 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
310 debugserve: sshstdio, logiofd, logiofile
310 debugserve: sshstdio, logiofd, logiofile
311 debugsetparents:
311 debugsetparents:
312 debugsidedata: changelog, manifest, dir
312 debugsidedata: changelog, manifest, dir
313 debugssl:
313 debugssl:
314 debugsub: rev
314 debugsub: rev
315 debugsuccessorssets: closest
315 debugsuccessorssets: closest
316 debugtagscache:
316 debugtagscache:
317 debugtemplate: rev, define
317 debugtemplate: rev, define
318 debuguigetpass: prompt
318 debuguigetpass: prompt
319 debuguiprompt: prompt
319 debuguiprompt: prompt
320 debugupdatecaches:
320 debugupdatecaches:
321 debugupgraderepo: optimize, run, backup, changelog, manifest
321 debugupgraderepo: optimize, run, backup, changelog, manifest
322 debugwalk: include, exclude
322 debugwalk: include, exclude
323 debugwhyunstable:
323 debugwhyunstable:
324 debugwireargs: three, four, five, ssh, remotecmd, insecure
324 debugwireargs: three, four, five, ssh, remotecmd, insecure
325 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
325 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
326 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
326 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
327 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
327 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
328 files: rev, print0, include, exclude, template, subrepos
328 files: rev, print0, include, exclude, template, subrepos
329 forget: interactive, include, exclude, dry-run
329 forget: interactive, include, exclude, dry-run
330 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
330 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
331 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
331 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
332 heads: rev, topo, active, closed, style, template
332 heads: rev, topo, active, closed, style, template
333 help: extension, command, keyword, system
333 help: extension, command, keyword, system
334 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
334 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
335 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
335 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
336 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
336 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
337 init: ssh, remotecmd, insecure
337 init: ssh, remotecmd, insecure
338 locate: rev, print0, fullpath, include, exclude
338 locate: rev, print0, fullpath, include, exclude
339 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
339 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
340 manifest: rev, all, template
340 manifest: rev, all, template
341 merge: force, rev, preview, abort, tool
341 merge: force, rev, preview, abort, tool
342 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
342 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
343 parents: rev, style, template
343 parents: rev, style, template
344 paths: template
344 paths: template
345 phase: public, draft, secret, force, rev
345 phase: public, draft, secret, force, rev
346 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
346 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
347 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
347 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
348 recover: verify
348 recover: verify
349 remove: after, force, subrepos, include, exclude, dry-run
349 remove: after, force, subrepos, include, exclude, dry-run
350 rename: after, force, include, exclude, dry-run
350 rename: after, force, include, exclude, dry-run
351 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
351 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
352 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
352 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
353 rollback: dry-run, force
353 rollback: dry-run, force
354 root: template
354 root: template
355 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
355 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
356 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
356 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
357 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
357 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
358 summary: remote
358 summary: remote
359 tag: force, local, rev, remove, edit, message, date, user
359 tag: force, local, rev, remove, edit, message, date, user
360 tags: template
360 tags: template
361 tip: patch, git, style, template
361 tip: patch, git, style, template
362 unbundle: update
362 unbundle: update
363 unshelve: abort, continue, interactive, keep, name, tool, date
363 unshelve: abort, continue, interactive, keep, name, tool, date
364 update: clean, check, merge, date, rev, tool
364 update: clean, check, merge, date, rev, tool
365 verify: full
365 verify: full
366 version: template
366 version: template
367
367
368 $ hg init a
368 $ hg init a
369 $ cd a
369 $ cd a
370 $ echo fee > fee
370 $ echo fee > fee
371 $ hg ci -q -Amfee
371 $ hg ci -q -Amfee
372 $ hg tag fee
372 $ hg tag fee
373 $ mkdir fie
373 $ mkdir fie
374 $ echo dead > fie/dead
374 $ echo dead > fie/dead
375 $ echo live > fie/live
375 $ echo live > fie/live
376 $ hg bookmark fo
376 $ hg bookmark fo
377 $ hg branch -q fie
377 $ hg branch -q fie
378 $ hg ci -q -Amfie
378 $ hg ci -q -Amfie
379 $ echo fo > fo
379 $ echo fo > fo
380 $ hg branch -qf default
380 $ hg branch -qf default
381 $ hg ci -q -Amfo
381 $ hg ci -q -Amfo
382 $ echo Fum > Fum
382 $ echo Fum > Fum
383 $ hg ci -q -AmFum
383 $ hg ci -q -AmFum
384 $ hg bookmark Fum
384 $ hg bookmark Fum
385
385
386 Test debugpathcomplete
386 Test debugpathcomplete
387
387
388 $ hg debugpathcomplete f
388 $ hg debugpathcomplete f
389 fee
389 fee
390 fie
390 fie
391 fo
391 fo
392 $ hg debugpathcomplete -f f
392 $ hg debugpathcomplete -f f
393 fee
393 fee
394 fie/dead
394 fie/dead
395 fie/live
395 fie/live
396 fo
396 fo
397
397
398 $ hg rm Fum
398 $ hg rm Fum
399 $ hg debugpathcomplete -r F
399 $ hg debugpathcomplete -r F
400 Fum
400 Fum
401
401
402 Test debugnamecomplete
402 Test debugnamecomplete
403
403
404 $ hg debugnamecomplete
404 $ hg debugnamecomplete
405 Fum
405 Fum
406 default
406 default
407 fee
407 fee
408 fie
408 fie
409 fo
409 fo
410 tip
410 tip
411 $ hg debugnamecomplete f
411 $ hg debugnamecomplete f
412 fee
412 fee
413 fie
413 fie
414 fo
414 fo
415
415
416 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
416 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
417 used for completions in some shells.
417 used for completions in some shells.
418
418
419 $ hg debuglabelcomplete
419 $ hg debuglabelcomplete
420 Fum
420 Fum
421 default
421 default
422 fee
422 fee
423 fie
423 fie
424 fo
424 fo
425 tip
425 tip
426 $ hg debuglabelcomplete f
426 $ hg debuglabelcomplete f
427 fee
427 fee
428 fie
428 fie
429 fo
429 fo
@@ -1,266 +1,323 b''
1 $ mkdir part1
1 $ mkdir part1
2 $ cd part1
2 $ cd part1
3
3
4 $ hg init
4 $ hg init
5 $ echo a > a
5 $ echo a > a
6 $ hg add a
6 $ hg add a
7 $ hg commit -m "1"
7 $ hg commit -m "1"
8 $ hg status
8 $ hg status
9 $ hg copy a b
9 $ hg copy a b
10 $ hg --config ui.portablefilenames=abort copy a con.xml
10 $ hg --config ui.portablefilenames=abort copy a con.xml
11 abort: filename contains 'con', which is reserved on Windows: con.xml
11 abort: filename contains 'con', which is reserved on Windows: con.xml
12 [255]
12 [255]
13 $ hg status
13 $ hg status
14 A b
14 A b
15 $ hg sum
15 $ hg sum
16 parent: 0:c19d34741b0a tip
16 parent: 0:c19d34741b0a tip
17 1
17 1
18 branch: default
18 branch: default
19 commit: 1 copied
19 commit: 1 copied
20 update: (current)
20 update: (current)
21 phases: 1 draft
21 phases: 1 draft
22 $ hg --debug commit -m "2"
22 $ hg --debug commit -m "2"
23 committing files:
23 committing files:
24 b
24 b
25 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
25 b: copy a:b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
26 committing manifest
26 committing manifest
27 committing changelog
27 committing changelog
28 updating the branch cache
28 updating the branch cache
29 committed changeset 1:93580a2c28a50a56f63526fb305067e6fbf739c4
29 committed changeset 1:93580a2c28a50a56f63526fb305067e6fbf739c4
30
30
31 we should see two history entries
31 we should see two history entries
32
32
33 $ hg history -v
33 $ hg history -v
34 changeset: 1:93580a2c28a5
34 changeset: 1:93580a2c28a5
35 tag: tip
35 tag: tip
36 user: test
36 user: test
37 date: Thu Jan 01 00:00:00 1970 +0000
37 date: Thu Jan 01 00:00:00 1970 +0000
38 files: b
38 files: b
39 description:
39 description:
40 2
40 2
41
41
42
42
43 changeset: 0:c19d34741b0a
43 changeset: 0:c19d34741b0a
44 user: test
44 user: test
45 date: Thu Jan 01 00:00:00 1970 +0000
45 date: Thu Jan 01 00:00:00 1970 +0000
46 files: a
46 files: a
47 description:
47 description:
48 1
48 1
49
49
50
50
51
51
52 we should see one log entry for a
52 we should see one log entry for a
53
53
54 $ hg log a
54 $ hg log a
55 changeset: 0:c19d34741b0a
55 changeset: 0:c19d34741b0a
56 user: test
56 user: test
57 date: Thu Jan 01 00:00:00 1970 +0000
57 date: Thu Jan 01 00:00:00 1970 +0000
58 summary: 1
58 summary: 1
59
59
60
60
61 this should show a revision linked to changeset 0
61 this should show a revision linked to changeset 0
62
62
63 $ hg debugindex a
63 $ hg debugindex a
64 rev linkrev nodeid p1 p2
64 rev linkrev nodeid p1 p2
65 0 0 b789fdd96dc2 000000000000 000000000000
65 0 0 b789fdd96dc2 000000000000 000000000000
66
66
67 we should see one log entry for b
67 we should see one log entry for b
68
68
69 $ hg log b
69 $ hg log b
70 changeset: 1:93580a2c28a5
70 changeset: 1:93580a2c28a5
71 tag: tip
71 tag: tip
72 user: test
72 user: test
73 date: Thu Jan 01 00:00:00 1970 +0000
73 date: Thu Jan 01 00:00:00 1970 +0000
74 summary: 2
74 summary: 2
75
75
76
76
77 this should show a revision linked to changeset 1
77 this should show a revision linked to changeset 1
78
78
79 $ hg debugindex b
79 $ hg debugindex b
80 rev linkrev nodeid p1 p2
80 rev linkrev nodeid p1 p2
81 0 1 37d9b5d994ea 000000000000 000000000000
81 0 1 37d9b5d994ea 000000000000 000000000000
82
82
83 this should show the rename information in the metadata
83 this should show the rename information in the metadata
84
84
85 $ hg debugdata b 0 | head -3 | tail -2
85 $ hg debugdata b 0 | head -3 | tail -2
86 copy: a
86 copy: a
87 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
87 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
88
88
89 #if reporevlogstore
89 #if reporevlogstore
90 $ md5sum.py .hg/store/data/b.i
90 $ md5sum.py .hg/store/data/b.i
91 44913824c8f5890ae218f9829535922e .hg/store/data/b.i
91 44913824c8f5890ae218f9829535922e .hg/store/data/b.i
92 #endif
92 #endif
93 $ hg cat b > bsum
93 $ hg cat b > bsum
94 $ md5sum.py bsum
94 $ md5sum.py bsum
95 60b725f10c9c85c70d97880dfe8191b3 bsum
95 60b725f10c9c85c70d97880dfe8191b3 bsum
96 $ hg cat a > asum
96 $ hg cat a > asum
97 $ md5sum.py asum
97 $ md5sum.py asum
98 60b725f10c9c85c70d97880dfe8191b3 asum
98 60b725f10c9c85c70d97880dfe8191b3 asum
99 $ hg verify
99 $ hg verify
100 checking changesets
100 checking changesets
101 checking manifests
101 checking manifests
102 crosschecking files in changesets and manifests
102 crosschecking files in changesets and manifests
103 checking files
103 checking files
104 checked 2 changesets with 2 changes to 2 files
104 checked 2 changesets with 2 changes to 2 files
105
105
106 $ cd ..
106 $ cd ..
107
107
108
108
109 $ mkdir part2
109 $ mkdir part2
110 $ cd part2
110 $ cd part2
111
111
112 $ hg init
112 $ hg init
113 $ echo foo > foo
113 $ echo foo > foo
114 should fail - foo is not managed
114 should fail - foo is not managed
115 $ hg mv foo bar
115 $ hg mv foo bar
116 foo: not copying - file is not managed
116 foo: not copying - file is not managed
117 abort: no files to copy
117 abort: no files to copy
118 [255]
118 [255]
119 $ hg st -A
119 $ hg st -A
120 ? foo
120 ? foo
121 respects ui.relative-paths
121 respects ui.relative-paths
122 $ mkdir dir
122 $ mkdir dir
123 $ cd dir
123 $ cd dir
124 $ hg mv ../foo ../bar
124 $ hg mv ../foo ../bar
125 ../foo: not copying - file is not managed
125 ../foo: not copying - file is not managed
126 abort: no files to copy
126 abort: no files to copy
127 [255]
127 [255]
128 $ hg mv ../foo ../bar --config ui.relative-paths=yes
128 $ hg mv ../foo ../bar --config ui.relative-paths=yes
129 ../foo: not copying - file is not managed
129 ../foo: not copying - file is not managed
130 abort: no files to copy
130 abort: no files to copy
131 [255]
131 [255]
132 $ hg mv ../foo ../bar --config ui.relative-paths=no
132 $ hg mv ../foo ../bar --config ui.relative-paths=no
133 foo: not copying - file is not managed
133 foo: not copying - file is not managed
134 abort: no files to copy
134 abort: no files to copy
135 [255]
135 [255]
136 $ cd ..
136 $ cd ..
137 $ rmdir dir
137 $ rmdir dir
138 $ hg add foo
138 $ hg add foo
139 dry-run; print a warning that this is not a real copy; foo is added
139 dry-run; print a warning that this is not a real copy; foo is added
140 $ hg mv --dry-run foo bar
140 $ hg mv --dry-run foo bar
141 foo has not been committed yet, so no copy data will be stored for bar.
141 foo has not been committed yet, so no copy data will be stored for bar.
142 $ hg st -A
142 $ hg st -A
143 A foo
143 A foo
144 should print a warning that this is not a real copy; bar is added
144 should print a warning that this is not a real copy; bar is added
145 $ hg mv foo bar
145 $ hg mv foo bar
146 foo has not been committed yet, so no copy data will be stored for bar.
146 foo has not been committed yet, so no copy data will be stored for bar.
147 $ hg st -A
147 $ hg st -A
148 A bar
148 A bar
149 should print a warning that this is not a real copy; foo is added
149 should print a warning that this is not a real copy; foo is added
150 $ hg cp bar foo
150 $ hg cp bar foo
151 bar has not been committed yet, so no copy data will be stored for foo.
151 bar has not been committed yet, so no copy data will be stored for foo.
152 $ hg rm -f bar
152 $ hg rm -f bar
153 $ rm bar
153 $ rm bar
154 $ hg st -A
154 $ hg st -A
155 A foo
155 A foo
156 $ hg commit -m1
156 $ hg commit -m1
157
157
158 moving a missing file
158 moving a missing file
159 $ rm foo
159 $ rm foo
160 $ hg mv foo foo3
160 $ hg mv foo foo3
161 foo: deleted in working directory
161 foo: deleted in working directory
162 foo3 does not exist!
162 foo3 does not exist!
163 $ hg up -qC .
163 $ hg up -qC .
164
164
165 copy --after to a nonexistent target filename
165 copy --after to a nonexistent target filename
166 $ hg cp -A foo dummy
166 $ hg cp -A foo dummy
167 foo: not recording copy - dummy does not exist
167 foo: not recording copy - dummy does not exist
168 [1]
168 [1]
169
169
170 dry-run; should show that foo is clean
170 dry-run; should show that foo is clean
171 $ hg copy --dry-run foo bar
171 $ hg copy --dry-run foo bar
172 $ hg st -A
172 $ hg st -A
173 C foo
173 C foo
174 should show copy
174 should show copy
175 $ hg copy foo bar
175 $ hg copy foo bar
176 $ hg st -C
176 $ hg st -C
177 A bar
177 A bar
178 foo
178 foo
179
179
180 shouldn't show copy
180 shouldn't show copy
181 $ hg commit -m2
181 $ hg commit -m2
182 $ hg st -C
182 $ hg st -C
183
183
184 should match
184 should match
185 $ hg debugindex foo
185 $ hg debugindex foo
186 rev linkrev nodeid p1 p2
186 rev linkrev nodeid p1 p2
187 0 0 2ed2a3912a0b 000000000000 000000000000
187 0 0 2ed2a3912a0b 000000000000 000000000000
188 $ hg debugrename bar
188 $ hg debugrename bar
189 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
189 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
190
190
191 $ echo bleah > foo
191 $ echo bleah > foo
192 $ echo quux > bar
192 $ echo quux > bar
193 $ hg commit -m3
193 $ hg commit -m3
194
194
195 should not be renamed
195 should not be renamed
196 $ hg debugrename bar
196 $ hg debugrename bar
197 bar not renamed
197 bar not renamed
198
198
199 $ hg copy -f foo bar
199 $ hg copy -f foo bar
200 should show copy
200 should show copy
201 $ hg st -C
201 $ hg st -C
202 M bar
202 M bar
203 foo
203 foo
204
204
205 XXX: filtering lfilesrepo.status() in 3.3-rc causes the copy source to not be
205 XXX: filtering lfilesrepo.status() in 3.3-rc causes the copy source to not be
206 displayed.
206 displayed.
207 $ hg st -C --config extensions.largefiles=
207 $ hg st -C --config extensions.largefiles=
208 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
208 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
209 M bar
209 M bar
210 foo
210 foo
211
211
212 $ hg commit -m3
212 $ hg commit -m3
213
213
214 should show no parents for tip
214 should show no parents for tip
215 $ hg debugindex bar
215 $ hg debugindex bar
216 rev linkrev nodeid p1 p2
216 rev linkrev nodeid p1 p2
217 0 1 7711d36246cc 000000000000 000000000000
217 0 1 7711d36246cc 000000000000 000000000000
218 1 2 bdf70a2b8d03 7711d36246cc 000000000000
218 1 2 bdf70a2b8d03 7711d36246cc 000000000000
219 2 3 b2558327ea8d 000000000000 000000000000
219 2 3 b2558327ea8d 000000000000 000000000000
220 should match
220 should match
221 $ hg debugindex foo
221 $ hg debugindex foo
222 rev linkrev nodeid p1 p2
222 rev linkrev nodeid p1 p2
223 0 0 2ed2a3912a0b 000000000000 000000000000
223 0 0 2ed2a3912a0b 000000000000 000000000000
224 1 2 dd12c926cf16 2ed2a3912a0b 000000000000
224 1 2 dd12c926cf16 2ed2a3912a0b 000000000000
225 $ hg debugrename bar
225 $ hg debugrename bar
226 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
226 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
227
227
228 should show no copies
228 should show no copies
229 $ hg st -C
229 $ hg st -C
230
230
231 copy --after on an added file
231 copy --after on an added file
232 $ cp bar baz
232 $ cp bar baz
233 $ hg add baz
233 $ hg add baz
234 $ hg cp -A bar baz
234 $ hg cp -A bar baz
235 $ hg st -C
235 $ hg st -C
236 A baz
236 A baz
237 bar
237 bar
238
238
239 foo was clean:
239 foo was clean:
240 $ hg st -AC foo
240 $ hg st -AC foo
241 C foo
241 C foo
242 Trying to copy on top of an existing file fails,
242 Trying to copy on top of an existing file fails,
243 $ hg copy -A bar foo
243 $ hg copy -A bar foo
244 foo: not overwriting - file already committed
244 foo: not overwriting - file already committed
245 ('hg copy --after --force' to replace the file by recording a copy)
245 ('hg copy --after --force' to replace the file by recording a copy)
246 [1]
246 [1]
247 same error without the --after, so the user doesn't have to go through
247 same error without the --after, so the user doesn't have to go through
248 two hints:
248 two hints:
249 $ hg copy bar foo
249 $ hg copy bar foo
250 foo: not overwriting - file already committed
250 foo: not overwriting - file already committed
251 ('hg copy --force' to replace the file by recording a copy)
251 ('hg copy --force' to replace the file by recording a copy)
252 [1]
252 [1]
253 but it's considered modified after a copy --after --force
253 but it's considered modified after a copy --after --force
254 $ hg copy -Af bar foo
254 $ hg copy -Af bar foo
255 $ hg st -AC foo
255 $ hg st -AC foo
256 M foo
256 M foo
257 bar
257 bar
258 The hint for a file that exists but is not in file history doesn't
258 The hint for a file that exists but is not in file history doesn't
259 mention --force:
259 mention --force:
260 $ touch xyzzy
260 $ touch xyzzy
261 $ hg cp bar xyzzy
261 $ hg cp bar xyzzy
262 xyzzy: not overwriting - file exists
262 xyzzy: not overwriting - file exists
263 ('hg copy --after' to record the copy)
263 ('hg copy --after' to record the copy)
264 [1]
264 [1]
265 $ hg co -qC .
266 $ rm baz xyzzy
267
268
269 Test unmarking copy of a single file
270
271 # Set up by creating a copy
272 $ hg cp bar baz
273 # Test uncopying a non-existent file
274 $ hg copy --forget non-existent
275 non-existent: $ENOENT$
276 # Test uncopying an tracked but unrelated file
277 $ hg copy --forget foo
278 foo: not unmarking as copy - file is not marked as copied
279 # Test uncopying a copy source
280 $ hg copy --forget bar
281 bar: not unmarking as copy - file is not marked as copied
282 # baz should still be marked as a copy
283 $ hg st -C
284 A baz
285 bar
286 # Test the normal case
287 $ hg copy --forget baz
288 $ hg st -C
289 A baz
290 # Test uncopy with matching an non-matching patterns
291 $ hg cp bar baz --after
292 $ hg copy --forget bar baz
293 bar: not unmarking as copy - file is not marked as copied
294 $ hg st -C
295 A baz
296 # Test uncopy with no exact matches
297 $ hg cp bar baz --after
298 $ hg copy --forget .
299 $ hg st -C
300 A baz
301 $ hg forget baz
302 $ rm baz
303
304 Test unmarking copy of a directory
305
306 $ mkdir dir
307 $ echo foo > dir/foo
308 $ echo bar > dir/bar
309 $ hg add dir
310 adding dir/bar
311 adding dir/foo
312 $ hg ci -m 'add dir/'
313 $ hg cp dir dir2
314 copying dir/bar to dir2/bar
315 copying dir/foo to dir2/foo
316 $ touch dir2/untracked
317 $ hg copy --forget dir2
318 $ hg st -C
319 A dir2/bar
320 A dir2/foo
321 ? dir2/untracked
265
322
266 $ cd ..
323 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now