##// END OF EJS Templates
status: let `--no-copies` override `ui.statuscopies`
Martin von Zweigbergk -
r50393:f2b1bc19 default
parent child Browse files
Show More
@@ -1,7981 +1,7983 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@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
8
9 import os
9 import os
10 import re
10 import re
11 import sys
11 import sys
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import (
14 from .node import (
15 hex,
15 hex,
16 nullrev,
16 nullrev,
17 short,
17 short,
18 wdirrev,
18 wdirrev,
19 )
19 )
20 from .pycompat import open
20 from .pycompat import open
21 from . import (
21 from . import (
22 archival,
22 archival,
23 bookmarks,
23 bookmarks,
24 bundle2,
24 bundle2,
25 bundlecaches,
25 bundlecaches,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 debugcommands as debugcommandsmod,
29 debugcommands as debugcommandsmod,
30 destutil,
30 destutil,
31 dirstateguard,
31 dirstateguard,
32 discovery,
32 discovery,
33 encoding,
33 encoding,
34 error,
34 error,
35 exchange,
35 exchange,
36 extensions,
36 extensions,
37 filemerge,
37 filemerge,
38 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 grep as grepmod,
40 grep as grepmod,
41 hbisect,
41 hbisect,
42 help,
42 help,
43 hg,
43 hg,
44 logcmdutil,
44 logcmdutil,
45 merge as mergemod,
45 merge as mergemod,
46 mergestate as mergestatemod,
46 mergestate as mergestatemod,
47 narrowspec,
47 narrowspec,
48 obsolete,
48 obsolete,
49 obsutil,
49 obsutil,
50 patch,
50 patch,
51 phases,
51 phases,
52 pycompat,
52 pycompat,
53 rcutil,
53 rcutil,
54 registrar,
54 registrar,
55 requirements,
55 requirements,
56 revsetlang,
56 revsetlang,
57 rewriteutil,
57 rewriteutil,
58 scmutil,
58 scmutil,
59 server,
59 server,
60 shelve as shelvemod,
60 shelve as shelvemod,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 ui as uimod,
64 ui as uimod,
65 util,
65 util,
66 verify as verifymod,
66 verify as verifymod,
67 vfs as vfsmod,
67 vfs as vfsmod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 urlutil,
73 urlutil,
74 )
74 )
75
75
76 table = {}
76 table = {}
77 table.update(debugcommandsmod.command._table)
77 table.update(debugcommandsmod.command._table)
78
78
79 command = registrar.command(table)
79 command = registrar.command(table)
80 INTENT_READONLY = registrar.INTENT_READONLY
80 INTENT_READONLY = registrar.INTENT_READONLY
81
81
82 # common command options
82 # common command options
83
83
84 globalopts = [
84 globalopts = [
85 (
85 (
86 b'R',
86 b'R',
87 b'repository',
87 b'repository',
88 b'',
88 b'',
89 _(b'repository root directory or name of overlay bundle file'),
89 _(b'repository root directory or name of overlay bundle file'),
90 _(b'REPO'),
90 _(b'REPO'),
91 ),
91 ),
92 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
93 (
93 (
94 b'y',
94 b'y',
95 b'noninteractive',
95 b'noninteractive',
96 None,
96 None,
97 _(
97 _(
98 b'do not prompt, automatically pick the first choice for all prompts'
98 b'do not prompt, automatically pick the first choice for all prompts'
99 ),
99 ),
100 ),
100 ),
101 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'q', b'quiet', None, _(b'suppress output')),
102 (b'v', b'verbose', None, _(b'enable additional output')),
102 (b'v', b'verbose', None, _(b'enable additional output')),
103 (
103 (
104 b'',
104 b'',
105 b'color',
105 b'color',
106 b'',
106 b'',
107 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
108 # and should not be translated
108 # and should not be translated
109 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b"when to colorize (boolean, always, auto, never, or debug)"),
110 _(b'TYPE'),
110 _(b'TYPE'),
111 ),
111 ),
112 (
112 (
113 b'',
113 b'',
114 b'config',
114 b'config',
115 [],
115 [],
116 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'set/override config option (use \'section.name=value\')'),
117 _(b'CONFIG'),
117 _(b'CONFIG'),
118 ),
118 ),
119 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debug', None, _(b'enable debugging output')),
120 (b'', b'debugger', None, _(b'start debugger')),
120 (b'', b'debugger', None, _(b'start debugger')),
121 (
121 (
122 b'',
122 b'',
123 b'encoding',
123 b'encoding',
124 encoding.encoding,
124 encoding.encoding,
125 _(b'set the charset encoding'),
125 _(b'set the charset encoding'),
126 _(b'ENCODE'),
126 _(b'ENCODE'),
127 ),
127 ),
128 (
128 (
129 b'',
129 b'',
130 b'encodingmode',
130 b'encodingmode',
131 encoding.encodingmode,
131 encoding.encodingmode,
132 _(b'set the charset encoding mode'),
132 _(b'set the charset encoding mode'),
133 _(b'MODE'),
133 _(b'MODE'),
134 ),
134 ),
135 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'traceback', None, _(b'always print a traceback on exception')),
136 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'time', None, _(b'time how long the command takes')),
137 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'profile', None, _(b'print command execution profile')),
138 (b'', b'version', None, _(b'output version information and exit')),
138 (b'', b'version', None, _(b'output version information and exit')),
139 (b'h', b'help', None, _(b'display help and exit')),
139 (b'h', b'help', None, _(b'display help and exit')),
140 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (b'', b'hidden', False, _(b'consider hidden changesets')),
141 (
141 (
142 b'',
142 b'',
143 b'pager',
143 b'pager',
144 b'auto',
144 b'auto',
145 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b"when to paginate (boolean, always, auto, or never)"),
146 _(b'TYPE'),
146 _(b'TYPE'),
147 ),
147 ),
148 ]
148 ]
149
149
150 dryrunopts = cmdutil.dryrunopts
150 dryrunopts = cmdutil.dryrunopts
151 remoteopts = cmdutil.remoteopts
151 remoteopts = cmdutil.remoteopts
152 walkopts = cmdutil.walkopts
152 walkopts = cmdutil.walkopts
153 commitopts = cmdutil.commitopts
153 commitopts = cmdutil.commitopts
154 commitopts2 = cmdutil.commitopts2
154 commitopts2 = cmdutil.commitopts2
155 commitopts3 = cmdutil.commitopts3
155 commitopts3 = cmdutil.commitopts3
156 formatteropts = cmdutil.formatteropts
156 formatteropts = cmdutil.formatteropts
157 templateopts = cmdutil.templateopts
157 templateopts = cmdutil.templateopts
158 logopts = cmdutil.logopts
158 logopts = cmdutil.logopts
159 diffopts = cmdutil.diffopts
159 diffopts = cmdutil.diffopts
160 diffwsopts = cmdutil.diffwsopts
160 diffwsopts = cmdutil.diffwsopts
161 diffopts2 = cmdutil.diffopts2
161 diffopts2 = cmdutil.diffopts2
162 mergetoolopts = cmdutil.mergetoolopts
162 mergetoolopts = cmdutil.mergetoolopts
163 similarityopts = cmdutil.similarityopts
163 similarityopts = cmdutil.similarityopts
164 subrepoopts = cmdutil.subrepoopts
164 subrepoopts = cmdutil.subrepoopts
165 debugrevlogopts = cmdutil.debugrevlogopts
165 debugrevlogopts = cmdutil.debugrevlogopts
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169
169
170 @command(
170 @command(
171 b'abort',
171 b'abort',
172 dryrunopts,
172 dryrunopts,
173 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
174 helpbasic=True,
174 helpbasic=True,
175 )
175 )
176 def abort(ui, repo, **opts):
176 def abort(ui, repo, **opts):
177 """abort an unfinished operation (EXPERIMENTAL)
177 """abort an unfinished operation (EXPERIMENTAL)
178
178
179 Aborts a multistep operation like graft, histedit, rebase, merge,
179 Aborts a multistep operation like graft, histedit, rebase, merge,
180 and unshelve if they are in an unfinished state.
180 and unshelve if they are in an unfinished state.
181
181
182 use --dry-run/-n to dry run the command.
182 use --dry-run/-n to dry run the command.
183 """
183 """
184 dryrun = opts.get('dry_run')
184 dryrun = opts.get('dry_run')
185 abortstate = cmdutil.getunfinishedstate(repo)
185 abortstate = cmdutil.getunfinishedstate(repo)
186 if not abortstate:
186 if not abortstate:
187 raise error.StateError(_(b'no operation in progress'))
187 raise error.StateError(_(b'no operation in progress'))
188 if not abortstate.abortfunc:
188 if not abortstate.abortfunc:
189 raise error.InputError(
189 raise error.InputError(
190 (
190 (
191 _(b"%s in progress but does not support 'hg abort'")
191 _(b"%s in progress but does not support 'hg abort'")
192 % (abortstate._opname)
192 % (abortstate._opname)
193 ),
193 ),
194 hint=abortstate.hint(),
194 hint=abortstate.hint(),
195 )
195 )
196 if dryrun:
196 if dryrun:
197 ui.status(
197 ui.status(
198 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
199 )
199 )
200 return
200 return
201 return abortstate.abortfunc(ui, repo)
201 return abortstate.abortfunc(ui, repo)
202
202
203
203
204 @command(
204 @command(
205 b'add',
205 b'add',
206 walkopts + subrepoopts + dryrunopts,
206 walkopts + subrepoopts + dryrunopts,
207 _(b'[OPTION]... [FILE]...'),
207 _(b'[OPTION]... [FILE]...'),
208 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
209 helpbasic=True,
209 helpbasic=True,
210 inferrepo=True,
210 inferrepo=True,
211 )
211 )
212 def add(ui, repo, *pats, **opts):
212 def add(ui, repo, *pats, **opts):
213 """add the specified files on the next commit
213 """add the specified files on the next commit
214
214
215 Schedule files to be version controlled and added to the
215 Schedule files to be version controlled and added to the
216 repository.
216 repository.
217
217
218 The files will be added to the repository at the next commit. To
218 The files will be added to the repository at the next commit. To
219 undo an add before that, see :hg:`forget`.
219 undo an add before that, see :hg:`forget`.
220
220
221 If no names are given, add all files to the repository (except
221 If no names are given, add all files to the repository (except
222 files matching ``.hgignore``).
222 files matching ``.hgignore``).
223
223
224 .. container:: verbose
224 .. container:: verbose
225
225
226 Examples:
226 Examples:
227
227
228 - New (unknown) files are added
228 - New (unknown) files are added
229 automatically by :hg:`add`::
229 automatically by :hg:`add`::
230
230
231 $ ls
231 $ ls
232 foo.c
232 foo.c
233 $ hg status
233 $ hg status
234 ? foo.c
234 ? foo.c
235 $ hg add
235 $ hg add
236 adding foo.c
236 adding foo.c
237 $ hg status
237 $ hg status
238 A foo.c
238 A foo.c
239
239
240 - Specific files to be added can be specified::
240 - Specific files to be added can be specified::
241
241
242 $ ls
242 $ ls
243 bar.c foo.c
243 bar.c foo.c
244 $ hg status
244 $ hg status
245 ? bar.c
245 ? bar.c
246 ? foo.c
246 ? foo.c
247 $ hg add bar.c
247 $ hg add bar.c
248 $ hg status
248 $ hg status
249 A bar.c
249 A bar.c
250 ? foo.c
250 ? foo.c
251
251
252 Returns 0 if all files are successfully added.
252 Returns 0 if all files are successfully added.
253 """
253 """
254
254
255 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
256 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
257 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
258 return rejected and 1 or 0
258 return rejected and 1 or 0
259
259
260
260
261 @command(
261 @command(
262 b'addremove',
262 b'addremove',
263 similarityopts + subrepoopts + walkopts + dryrunopts,
263 similarityopts + subrepoopts + walkopts + dryrunopts,
264 _(b'[OPTION]... [FILE]...'),
264 _(b'[OPTION]... [FILE]...'),
265 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
266 inferrepo=True,
266 inferrepo=True,
267 )
267 )
268 def addremove(ui, repo, *pats, **opts):
268 def addremove(ui, repo, *pats, **opts):
269 """add all new files, delete all missing files
269 """add all new files, delete all missing files
270
270
271 Add all new files and remove all missing files from the
271 Add all new files and remove all missing files from the
272 repository.
272 repository.
273
273
274 Unless names are given, new files are ignored if they match any of
274 Unless names are given, new files are ignored if they match any of
275 the patterns in ``.hgignore``. As with add, these changes take
275 the patterns in ``.hgignore``. As with add, these changes take
276 effect at the next commit.
276 effect at the next commit.
277
277
278 Use the -s/--similarity option to detect renamed files. This
278 Use the -s/--similarity option to detect renamed files. This
279 option takes a percentage between 0 (disabled) and 100 (files must
279 option takes a percentage between 0 (disabled) and 100 (files must
280 be identical) as its parameter. With a parameter greater than 0,
280 be identical) as its parameter. With a parameter greater than 0,
281 this compares every removed file with every added file and records
281 this compares every removed file with every added file and records
282 those similar enough as renames. Detecting renamed files this way
282 those similar enough as renames. Detecting renamed files this way
283 can be expensive. After using this option, :hg:`status -C` can be
283 can be expensive. After using this option, :hg:`status -C` can be
284 used to check which files were identified as moved or renamed. If
284 used to check which files were identified as moved or renamed. If
285 not specified, -s/--similarity defaults to 100 and only renames of
285 not specified, -s/--similarity defaults to 100 and only renames of
286 identical files are detected.
286 identical files are detected.
287
287
288 .. container:: verbose
288 .. container:: verbose
289
289
290 Examples:
290 Examples:
291
291
292 - A number of files (bar.c and foo.c) are new,
292 - A number of files (bar.c and foo.c) are new,
293 while foobar.c has been removed (without using :hg:`remove`)
293 while foobar.c has been removed (without using :hg:`remove`)
294 from the repository::
294 from the repository::
295
295
296 $ ls
296 $ ls
297 bar.c foo.c
297 bar.c foo.c
298 $ hg status
298 $ hg status
299 ! foobar.c
299 ! foobar.c
300 ? bar.c
300 ? bar.c
301 ? foo.c
301 ? foo.c
302 $ hg addremove
302 $ hg addremove
303 adding bar.c
303 adding bar.c
304 adding foo.c
304 adding foo.c
305 removing foobar.c
305 removing foobar.c
306 $ hg status
306 $ hg status
307 A bar.c
307 A bar.c
308 A foo.c
308 A foo.c
309 R foobar.c
309 R foobar.c
310
310
311 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 - A file foobar.c was moved to foo.c without using :hg:`rename`.
312 Afterwards, it was edited slightly::
312 Afterwards, it was edited slightly::
313
313
314 $ ls
314 $ ls
315 foo.c
315 foo.c
316 $ hg status
316 $ hg status
317 ! foobar.c
317 ! foobar.c
318 ? foo.c
318 ? foo.c
319 $ hg addremove --similarity 90
319 $ hg addremove --similarity 90
320 removing foobar.c
320 removing foobar.c
321 adding foo.c
321 adding foo.c
322 recording removal of foobar.c as rename to foo.c (94% similar)
322 recording removal of foobar.c as rename to foo.c (94% similar)
323 $ hg status -C
323 $ hg status -C
324 A foo.c
324 A foo.c
325 foobar.c
325 foobar.c
326 R foobar.c
326 R foobar.c
327
327
328 Returns 0 if all files are successfully added.
328 Returns 0 if all files are successfully added.
329 """
329 """
330 opts = pycompat.byteskwargs(opts)
330 opts = pycompat.byteskwargs(opts)
331 if not opts.get(b'similarity'):
331 if not opts.get(b'similarity'):
332 opts[b'similarity'] = b'100'
332 opts[b'similarity'] = b'100'
333 matcher = scmutil.match(repo[None], pats, opts)
333 matcher = scmutil.match(repo[None], pats, opts)
334 relative = scmutil.anypats(pats, opts)
334 relative = scmutil.anypats(pats, opts)
335 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
336 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
337
337
338
338
339 @command(
339 @command(
340 b'annotate|blame',
340 b'annotate|blame',
341 [
341 [
342 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
343 (
343 (
344 b'',
344 b'',
345 b'follow',
345 b'follow',
346 None,
346 None,
347 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 _(b'follow copies/renames and list the filename (DEPRECATED)'),
348 ),
348 ),
349 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
350 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'a', b'text', None, _(b'treat all files as text')),
351 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'u', b'user', None, _(b'list the author (long with -v)')),
352 (b'f', b'file', None, _(b'list the filename')),
352 (b'f', b'file', None, _(b'list the filename')),
353 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'd', b'date', None, _(b'list the date (short with -q)')),
354 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'n', b'number', None, _(b'list the revision number (default)')),
355 (b'c', b'changeset', None, _(b'list the changeset')),
355 (b'c', b'changeset', None, _(b'list the changeset')),
356 (
356 (
357 b'l',
357 b'l',
358 b'line-number',
358 b'line-number',
359 None,
359 None,
360 _(b'show line number at the first appearance'),
360 _(b'show line number at the first appearance'),
361 ),
361 ),
362 (
362 (
363 b'',
363 b'',
364 b'skip',
364 b'skip',
365 [],
365 [],
366 _(b'revset to not display (EXPERIMENTAL)'),
366 _(b'revset to not display (EXPERIMENTAL)'),
367 _(b'REV'),
367 _(b'REV'),
368 ),
368 ),
369 ]
369 ]
370 + diffwsopts
370 + diffwsopts
371 + walkopts
371 + walkopts
372 + formatteropts,
372 + formatteropts,
373 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
374 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpcategory=command.CATEGORY_FILE_CONTENTS,
375 helpbasic=True,
375 helpbasic=True,
376 inferrepo=True,
376 inferrepo=True,
377 )
377 )
378 def annotate(ui, repo, *pats, **opts):
378 def annotate(ui, repo, *pats, **opts):
379 """show changeset information by line for each file
379 """show changeset information by line for each file
380
380
381 List changes in files, showing the revision id responsible for
381 List changes in files, showing the revision id responsible for
382 each line.
382 each line.
383
383
384 This command is useful for discovering when a change was made and
384 This command is useful for discovering when a change was made and
385 by whom.
385 by whom.
386
386
387 If you include --file, --user, or --date, the revision number is
387 If you include --file, --user, or --date, the revision number is
388 suppressed unless you also include --number.
388 suppressed unless you also include --number.
389
389
390 Without the -a/--text option, annotate will avoid processing files
390 Without the -a/--text option, annotate will avoid processing files
391 it detects as binary. With -a, annotate will annotate the file
391 it detects as binary. With -a, annotate will annotate the file
392 anyway, although the results will probably be neither useful
392 anyway, although the results will probably be neither useful
393 nor desirable.
393 nor desirable.
394
394
395 .. container:: verbose
395 .. container:: verbose
396
396
397 Template:
397 Template:
398
398
399 The following keywords are supported in addition to the common template
399 The following keywords are supported in addition to the common template
400 keywords and functions. See also :hg:`help templates`.
400 keywords and functions. See also :hg:`help templates`.
401
401
402 :lines: List of lines with annotation data.
402 :lines: List of lines with annotation data.
403 :path: String. Repository-absolute path of the specified file.
403 :path: String. Repository-absolute path of the specified file.
404
404
405 And each entry of ``{lines}`` provides the following sub-keywords in
405 And each entry of ``{lines}`` provides the following sub-keywords in
406 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
407
407
408 :line: String. Line content.
408 :line: String. Line content.
409 :lineno: Integer. Line number at that revision.
409 :lineno: Integer. Line number at that revision.
410 :path: String. Repository-absolute path of the file at that revision.
410 :path: String. Repository-absolute path of the file at that revision.
411
411
412 See :hg:`help templates.operators` for the list expansion syntax.
412 See :hg:`help templates.operators` for the list expansion syntax.
413
413
414 Returns 0 on success.
414 Returns 0 on success.
415 """
415 """
416 opts = pycompat.byteskwargs(opts)
416 opts = pycompat.byteskwargs(opts)
417 if not pats:
417 if not pats:
418 raise error.InputError(
418 raise error.InputError(
419 _(b'at least one filename or pattern is required')
419 _(b'at least one filename or pattern is required')
420 )
420 )
421
421
422 if opts.get(b'follow'):
422 if opts.get(b'follow'):
423 # --follow is deprecated and now just an alias for -f/--file
423 # --follow is deprecated and now just an alias for -f/--file
424 # to mimic the behavior of Mercurial before version 1.5
424 # to mimic the behavior of Mercurial before version 1.5
425 opts[b'file'] = True
425 opts[b'file'] = True
426
426
427 if (
427 if (
428 not opts.get(b'user')
428 not opts.get(b'user')
429 and not opts.get(b'changeset')
429 and not opts.get(b'changeset')
430 and not opts.get(b'date')
430 and not opts.get(b'date')
431 and not opts.get(b'file')
431 and not opts.get(b'file')
432 ):
432 ):
433 opts[b'number'] = True
433 opts[b'number'] = True
434
434
435 linenumber = opts.get(b'line_number') is not None
435 linenumber = opts.get(b'line_number') is not None
436 if (
436 if (
437 linenumber
437 linenumber
438 and (not opts.get(b'changeset'))
438 and (not opts.get(b'changeset'))
439 and (not opts.get(b'number'))
439 and (not opts.get(b'number'))
440 ):
440 ):
441 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
441 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
442
442
443 rev = opts.get(b'rev')
443 rev = opts.get(b'rev')
444 if rev:
444 if rev:
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
445 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
446 ctx = logcmdutil.revsingle(repo, rev)
446 ctx = logcmdutil.revsingle(repo, rev)
447
447
448 ui.pager(b'annotate')
448 ui.pager(b'annotate')
449 rootfm = ui.formatter(b'annotate', opts)
449 rootfm = ui.formatter(b'annotate', opts)
450 if ui.debugflag:
450 if ui.debugflag:
451 shorthex = pycompat.identity
451 shorthex = pycompat.identity
452 else:
452 else:
453
453
454 def shorthex(h):
454 def shorthex(h):
455 return h[:12]
455 return h[:12]
456
456
457 if ui.quiet:
457 if ui.quiet:
458 datefunc = dateutil.shortdate
458 datefunc = dateutil.shortdate
459 else:
459 else:
460 datefunc = dateutil.datestr
460 datefunc = dateutil.datestr
461 if ctx.rev() is None:
461 if ctx.rev() is None:
462 if opts.get(b'changeset'):
462 if opts.get(b'changeset'):
463 # omit "+" suffix which is appended to node hex
463 # omit "+" suffix which is appended to node hex
464 def formatrev(rev):
464 def formatrev(rev):
465 if rev == wdirrev:
465 if rev == wdirrev:
466 return b'%d' % ctx.p1().rev()
466 return b'%d' % ctx.p1().rev()
467 else:
467 else:
468 return b'%d' % rev
468 return b'%d' % rev
469
469
470 else:
470 else:
471
471
472 def formatrev(rev):
472 def formatrev(rev):
473 if rev == wdirrev:
473 if rev == wdirrev:
474 return b'%d+' % ctx.p1().rev()
474 return b'%d+' % ctx.p1().rev()
475 else:
475 else:
476 return b'%d ' % rev
476 return b'%d ' % rev
477
477
478 def formathex(h):
478 def formathex(h):
479 if h == repo.nodeconstants.wdirhex:
479 if h == repo.nodeconstants.wdirhex:
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
480 return b'%s+' % shorthex(hex(ctx.p1().node()))
481 else:
481 else:
482 return b'%s ' % shorthex(h)
482 return b'%s ' % shorthex(h)
483
483
484 else:
484 else:
485 formatrev = b'%d'.__mod__
485 formatrev = b'%d'.__mod__
486 formathex = shorthex
486 formathex = shorthex
487
487
488 opmap = [
488 opmap = [
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
489 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
490 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
491 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
492 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
493 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
494 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
495 ]
495 ]
496 opnamemap = {
496 opnamemap = {
497 b'rev': b'number',
497 b'rev': b'number',
498 b'node': b'changeset',
498 b'node': b'changeset',
499 b'path': b'file',
499 b'path': b'file',
500 b'lineno': b'line_number',
500 b'lineno': b'line_number',
501 }
501 }
502
502
503 if rootfm.isplain():
503 if rootfm.isplain():
504
504
505 def makefunc(get, fmt):
505 def makefunc(get, fmt):
506 return lambda x: fmt(get(x))
506 return lambda x: fmt(get(x))
507
507
508 else:
508 else:
509
509
510 def makefunc(get, fmt):
510 def makefunc(get, fmt):
511 return get
511 return get
512
512
513 datahint = rootfm.datahint()
513 datahint = rootfm.datahint()
514 funcmap = [
514 funcmap = [
515 (makefunc(get, fmt), sep)
515 (makefunc(get, fmt), sep)
516 for fn, sep, get, fmt in opmap
516 for fn, sep, get, fmt in opmap
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
517 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
518 ]
518 ]
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
519 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
520 fields = b' '.join(
520 fields = b' '.join(
521 fn
521 fn
522 for fn, sep, get, fmt in opmap
522 for fn, sep, get, fmt in opmap
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
523 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
524 )
524 )
525
525
526 def bad(x, y):
526 def bad(x, y):
527 raise error.InputError(b"%s: %s" % (x, y))
527 raise error.InputError(b"%s: %s" % (x, y))
528
528
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
529 m = scmutil.match(ctx, pats, opts, badfn=bad)
530
530
531 follow = not opts.get(b'no_follow')
531 follow = not opts.get(b'no_follow')
532 diffopts = patch.difffeatureopts(
532 diffopts = patch.difffeatureopts(
533 ui, opts, section=b'annotate', whitespace=True
533 ui, opts, section=b'annotate', whitespace=True
534 )
534 )
535 skiprevs = opts.get(b'skip')
535 skiprevs = opts.get(b'skip')
536 if skiprevs:
536 if skiprevs:
537 skiprevs = logcmdutil.revrange(repo, skiprevs)
537 skiprevs = logcmdutil.revrange(repo, skiprevs)
538
538
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
539 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
540 for abs in ctx.walk(m):
540 for abs in ctx.walk(m):
541 fctx = ctx[abs]
541 fctx = ctx[abs]
542 rootfm.startitem()
542 rootfm.startitem()
543 rootfm.data(path=abs)
543 rootfm.data(path=abs)
544 if not opts.get(b'text') and fctx.isbinary():
544 if not opts.get(b'text') and fctx.isbinary():
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
545 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
546 continue
546 continue
547
547
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
548 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
549 lines = fctx.annotate(
549 lines = fctx.annotate(
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
550 follow=follow, skiprevs=skiprevs, diffopts=diffopts
551 )
551 )
552 if not lines:
552 if not lines:
553 fm.end()
553 fm.end()
554 continue
554 continue
555 formats = []
555 formats = []
556 pieces = []
556 pieces = []
557
557
558 for f, sep in funcmap:
558 for f, sep in funcmap:
559 l = [f(n) for n in lines]
559 l = [f(n) for n in lines]
560 if fm.isplain():
560 if fm.isplain():
561 sizes = [encoding.colwidth(x) for x in l]
561 sizes = [encoding.colwidth(x) for x in l]
562 ml = max(sizes)
562 ml = max(sizes)
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
563 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
564 else:
564 else:
565 formats.append([b'%s'] * len(l))
565 formats.append([b'%s'] * len(l))
566 pieces.append(l)
566 pieces.append(l)
567
567
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
568 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
569 fm.startitem()
569 fm.startitem()
570 fm.context(fctx=n.fctx)
570 fm.context(fctx=n.fctx)
571 fm.write(fields, b"".join(f), *p)
571 fm.write(fields, b"".join(f), *p)
572 if n.skip:
572 if n.skip:
573 fmt = b"* %s"
573 fmt = b"* %s"
574 else:
574 else:
575 fmt = b": %s"
575 fmt = b": %s"
576 fm.write(b'line', fmt, n.text)
576 fm.write(b'line', fmt, n.text)
577
577
578 if not lines[-1].text.endswith(b'\n'):
578 if not lines[-1].text.endswith(b'\n'):
579 fm.plain(b'\n')
579 fm.plain(b'\n')
580 fm.end()
580 fm.end()
581
581
582 rootfm.end()
582 rootfm.end()
583
583
584
584
585 @command(
585 @command(
586 b'archive',
586 b'archive',
587 [
587 [
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
588 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
589 (
589 (
590 b'p',
590 b'p',
591 b'prefix',
591 b'prefix',
592 b'',
592 b'',
593 _(b'directory prefix for files in archive'),
593 _(b'directory prefix for files in archive'),
594 _(b'PREFIX'),
594 _(b'PREFIX'),
595 ),
595 ),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
596 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
597 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
598 ]
598 ]
599 + subrepoopts
599 + subrepoopts
600 + walkopts,
600 + walkopts,
601 _(b'[OPTION]... DEST'),
601 _(b'[OPTION]... DEST'),
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
602 helpcategory=command.CATEGORY_IMPORT_EXPORT,
603 )
603 )
604 def archive(ui, repo, dest, **opts):
604 def archive(ui, repo, dest, **opts):
605 """create an unversioned archive of a repository revision
605 """create an unversioned archive of a repository revision
606
606
607 By default, the revision used is the parent of the working
607 By default, the revision used is the parent of the working
608 directory; use -r/--rev to specify a different revision.
608 directory; use -r/--rev to specify a different revision.
609
609
610 The archive type is automatically detected based on file
610 The archive type is automatically detected based on file
611 extension (to override, use -t/--type).
611 extension (to override, use -t/--type).
612
612
613 .. container:: verbose
613 .. container:: verbose
614
614
615 Examples:
615 Examples:
616
616
617 - create a zip file containing the 1.0 release::
617 - create a zip file containing the 1.0 release::
618
618
619 hg archive -r 1.0 project-1.0.zip
619 hg archive -r 1.0 project-1.0.zip
620
620
621 - create a tarball excluding .hg files::
621 - create a tarball excluding .hg files::
622
622
623 hg archive project.tar.gz -X ".hg*"
623 hg archive project.tar.gz -X ".hg*"
624
624
625 Valid types are:
625 Valid types are:
626
626
627 :``files``: a directory full of files (default)
627 :``files``: a directory full of files (default)
628 :``tar``: tar archive, uncompressed
628 :``tar``: tar archive, uncompressed
629 :``tbz2``: tar archive, compressed using bzip2
629 :``tbz2``: tar archive, compressed using bzip2
630 :``tgz``: tar archive, compressed using gzip
630 :``tgz``: tar archive, compressed using gzip
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
631 :``txz``: tar archive, compressed using lzma (only in Python 3)
632 :``uzip``: zip archive, uncompressed
632 :``uzip``: zip archive, uncompressed
633 :``zip``: zip archive, compressed using deflate
633 :``zip``: zip archive, compressed using deflate
634
634
635 The exact name of the destination archive or directory is given
635 The exact name of the destination archive or directory is given
636 using a format string; see :hg:`help export` for details.
636 using a format string; see :hg:`help export` for details.
637
637
638 Each member added to an archive file has a directory prefix
638 Each member added to an archive file has a directory prefix
639 prepended. Use -p/--prefix to specify a format string for the
639 prepended. Use -p/--prefix to specify a format string for the
640 prefix. The default is the basename of the archive, with suffixes
640 prefix. The default is the basename of the archive, with suffixes
641 removed.
641 removed.
642
642
643 Returns 0 on success.
643 Returns 0 on success.
644 """
644 """
645
645
646 opts = pycompat.byteskwargs(opts)
646 opts = pycompat.byteskwargs(opts)
647 rev = opts.get(b'rev')
647 rev = opts.get(b'rev')
648 if rev:
648 if rev:
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
649 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 ctx = logcmdutil.revsingle(repo, rev)
650 ctx = logcmdutil.revsingle(repo, rev)
651 if not ctx:
651 if not ctx:
652 raise error.InputError(
652 raise error.InputError(
653 _(b'no working directory: please specify a revision')
653 _(b'no working directory: please specify a revision')
654 )
654 )
655 node = ctx.node()
655 node = ctx.node()
656 dest = cmdutil.makefilename(ctx, dest)
656 dest = cmdutil.makefilename(ctx, dest)
657 if os.path.realpath(dest) == repo.root:
657 if os.path.realpath(dest) == repo.root:
658 raise error.InputError(_(b'repository root cannot be destination'))
658 raise error.InputError(_(b'repository root cannot be destination'))
659
659
660 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
660 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
661 prefix = opts.get(b'prefix')
661 prefix = opts.get(b'prefix')
662
662
663 if dest == b'-':
663 if dest == b'-':
664 if kind == b'files':
664 if kind == b'files':
665 raise error.InputError(_(b'cannot archive plain files to stdout'))
665 raise error.InputError(_(b'cannot archive plain files to stdout'))
666 dest = cmdutil.makefileobj(ctx, dest)
666 dest = cmdutil.makefileobj(ctx, dest)
667 if not prefix:
667 if not prefix:
668 prefix = os.path.basename(repo.root) + b'-%h'
668 prefix = os.path.basename(repo.root) + b'-%h'
669
669
670 prefix = cmdutil.makefilename(ctx, prefix)
670 prefix = cmdutil.makefilename(ctx, prefix)
671 match = scmutil.match(ctx, [], opts)
671 match = scmutil.match(ctx, [], opts)
672 archival.archive(
672 archival.archive(
673 repo,
673 repo,
674 dest,
674 dest,
675 node,
675 node,
676 kind,
676 kind,
677 not opts.get(b'no_decode'),
677 not opts.get(b'no_decode'),
678 match,
678 match,
679 prefix,
679 prefix,
680 subrepos=opts.get(b'subrepos'),
680 subrepos=opts.get(b'subrepos'),
681 )
681 )
682
682
683
683
684 @command(
684 @command(
685 b'backout',
685 b'backout',
686 [
686 [
687 (
687 (
688 b'',
688 b'',
689 b'merge',
689 b'merge',
690 None,
690 None,
691 _(b'merge with old dirstate parent after backout'),
691 _(b'merge with old dirstate parent after backout'),
692 ),
692 ),
693 (
693 (
694 b'',
694 b'',
695 b'commit',
695 b'commit',
696 None,
696 None,
697 _(b'commit if no conflicts were encountered (DEPRECATED)'),
697 _(b'commit if no conflicts were encountered (DEPRECATED)'),
698 ),
698 ),
699 (b'', b'no-commit', None, _(b'do not commit')),
699 (b'', b'no-commit', None, _(b'do not commit')),
700 (
700 (
701 b'',
701 b'',
702 b'parent',
702 b'parent',
703 b'',
703 b'',
704 _(b'parent to choose when backing out merge (DEPRECATED)'),
704 _(b'parent to choose when backing out merge (DEPRECATED)'),
705 _(b'REV'),
705 _(b'REV'),
706 ),
706 ),
707 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
707 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
708 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
708 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
709 ]
709 ]
710 + mergetoolopts
710 + mergetoolopts
711 + walkopts
711 + walkopts
712 + commitopts
712 + commitopts
713 + commitopts2,
713 + commitopts2,
714 _(b'[OPTION]... [-r] REV'),
714 _(b'[OPTION]... [-r] REV'),
715 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
715 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
716 )
716 )
717 def backout(ui, repo, node=None, rev=None, **opts):
717 def backout(ui, repo, node=None, rev=None, **opts):
718 """reverse effect of earlier changeset
718 """reverse effect of earlier changeset
719
719
720 Prepare a new changeset with the effect of REV undone in the
720 Prepare a new changeset with the effect of REV undone in the
721 current working directory. If no conflicts were encountered,
721 current working directory. If no conflicts were encountered,
722 it will be committed immediately.
722 it will be committed immediately.
723
723
724 If REV is the parent of the working directory, then this new changeset
724 If REV is the parent of the working directory, then this new changeset
725 is committed automatically (unless --no-commit is specified).
725 is committed automatically (unless --no-commit is specified).
726
726
727 .. note::
727 .. note::
728
728
729 :hg:`backout` cannot be used to fix either an unwanted or
729 :hg:`backout` cannot be used to fix either an unwanted or
730 incorrect merge.
730 incorrect merge.
731
731
732 .. container:: verbose
732 .. container:: verbose
733
733
734 Examples:
734 Examples:
735
735
736 - Reverse the effect of the parent of the working directory.
736 - Reverse the effect of the parent of the working directory.
737 This backout will be committed immediately::
737 This backout will be committed immediately::
738
738
739 hg backout -r .
739 hg backout -r .
740
740
741 - Reverse the effect of previous bad revision 23::
741 - Reverse the effect of previous bad revision 23::
742
742
743 hg backout -r 23
743 hg backout -r 23
744
744
745 - Reverse the effect of previous bad revision 23 and
745 - Reverse the effect of previous bad revision 23 and
746 leave changes uncommitted::
746 leave changes uncommitted::
747
747
748 hg backout -r 23 --no-commit
748 hg backout -r 23 --no-commit
749 hg commit -m "Backout revision 23"
749 hg commit -m "Backout revision 23"
750
750
751 By default, the pending changeset will have one parent,
751 By default, the pending changeset will have one parent,
752 maintaining a linear history. With --merge, the pending
752 maintaining a linear history. With --merge, the pending
753 changeset will instead have two parents: the old parent of the
753 changeset will instead have two parents: the old parent of the
754 working directory and a new child of REV that simply undoes REV.
754 working directory and a new child of REV that simply undoes REV.
755
755
756 Before version 1.7, the behavior without --merge was equivalent
756 Before version 1.7, the behavior without --merge was equivalent
757 to specifying --merge followed by :hg:`update --clean .` to
757 to specifying --merge followed by :hg:`update --clean .` to
758 cancel the merge and leave the child of REV as a head to be
758 cancel the merge and leave the child of REV as a head to be
759 merged separately.
759 merged separately.
760
760
761 See :hg:`help dates` for a list of formats valid for -d/--date.
761 See :hg:`help dates` for a list of formats valid for -d/--date.
762
762
763 See :hg:`help revert` for a way to restore files to the state
763 See :hg:`help revert` for a way to restore files to the state
764 of another revision.
764 of another revision.
765
765
766 Returns 0 on success, 1 if nothing to backout or there are unresolved
766 Returns 0 on success, 1 if nothing to backout or there are unresolved
767 files.
767 files.
768 """
768 """
769 with repo.wlock(), repo.lock():
769 with repo.wlock(), repo.lock():
770 return _dobackout(ui, repo, node, rev, **opts)
770 return _dobackout(ui, repo, node, rev, **opts)
771
771
772
772
773 def _dobackout(ui, repo, node=None, rev=None, **opts):
773 def _dobackout(ui, repo, node=None, rev=None, **opts):
774 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
774 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
775 opts = pycompat.byteskwargs(opts)
775 opts = pycompat.byteskwargs(opts)
776
776
777 if rev and node:
777 if rev and node:
778 raise error.InputError(_(b"please specify just one revision"))
778 raise error.InputError(_(b"please specify just one revision"))
779
779
780 if not rev:
780 if not rev:
781 rev = node
781 rev = node
782
782
783 if not rev:
783 if not rev:
784 raise error.InputError(_(b"please specify a revision to backout"))
784 raise error.InputError(_(b"please specify a revision to backout"))
785
785
786 date = opts.get(b'date')
786 date = opts.get(b'date')
787 if date:
787 if date:
788 opts[b'date'] = dateutil.parsedate(date)
788 opts[b'date'] = dateutil.parsedate(date)
789
789
790 cmdutil.checkunfinished(repo)
790 cmdutil.checkunfinished(repo)
791 cmdutil.bailifchanged(repo)
791 cmdutil.bailifchanged(repo)
792 ctx = logcmdutil.revsingle(repo, rev)
792 ctx = logcmdutil.revsingle(repo, rev)
793 node = ctx.node()
793 node = ctx.node()
794
794
795 op1, op2 = repo.dirstate.parents()
795 op1, op2 = repo.dirstate.parents()
796 if not repo.changelog.isancestor(node, op1):
796 if not repo.changelog.isancestor(node, op1):
797 raise error.InputError(
797 raise error.InputError(
798 _(b'cannot backout change that is not an ancestor')
798 _(b'cannot backout change that is not an ancestor')
799 )
799 )
800
800
801 p1, p2 = repo.changelog.parents(node)
801 p1, p2 = repo.changelog.parents(node)
802 if p1 == repo.nullid:
802 if p1 == repo.nullid:
803 raise error.InputError(_(b'cannot backout a change with no parents'))
803 raise error.InputError(_(b'cannot backout a change with no parents'))
804 if p2 != repo.nullid:
804 if p2 != repo.nullid:
805 if not opts.get(b'parent'):
805 if not opts.get(b'parent'):
806 raise error.InputError(_(b'cannot backout a merge changeset'))
806 raise error.InputError(_(b'cannot backout a merge changeset'))
807 p = repo.lookup(opts[b'parent'])
807 p = repo.lookup(opts[b'parent'])
808 if p not in (p1, p2):
808 if p not in (p1, p2):
809 raise error.InputError(
809 raise error.InputError(
810 _(b'%s is not a parent of %s') % (short(p), short(node))
810 _(b'%s is not a parent of %s') % (short(p), short(node))
811 )
811 )
812 parent = p
812 parent = p
813 else:
813 else:
814 if opts.get(b'parent'):
814 if opts.get(b'parent'):
815 raise error.InputError(
815 raise error.InputError(
816 _(b'cannot use --parent on non-merge changeset')
816 _(b'cannot use --parent on non-merge changeset')
817 )
817 )
818 parent = p1
818 parent = p1
819
819
820 # the backout should appear on the same branch
820 # the backout should appear on the same branch
821 branch = repo.dirstate.branch()
821 branch = repo.dirstate.branch()
822 bheads = repo.branchheads(branch)
822 bheads = repo.branchheads(branch)
823 rctx = scmutil.revsingle(repo, hex(parent))
823 rctx = scmutil.revsingle(repo, hex(parent))
824 if not opts.get(b'merge') and op1 != node:
824 if not opts.get(b'merge') and op1 != node:
825 with dirstateguard.dirstateguard(repo, b'backout'):
825 with dirstateguard.dirstateguard(repo, b'backout'):
826 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
826 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
827 with ui.configoverride(overrides, b'backout'):
827 with ui.configoverride(overrides, b'backout'):
828 stats = mergemod.back_out(ctx, parent=repo[parent])
828 stats = mergemod.back_out(ctx, parent=repo[parent])
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)
839 cmdutil.revert(ui, repo, rctx)
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 # save to detect changes
859 # save to detect changes
860 tip = repo.changelog.tip()
860 tip = repo.changelog.tip()
861
861
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
862 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
863 if not newnode:
863 if not newnode:
864 ui.status(_(b"nothing changed\n"))
864 ui.status(_(b"nothing changed\n"))
865 return 1
865 return 1
866 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
866 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
867
867
868 def nice(node):
868 def nice(node):
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
869 return b'%d:%s' % (repo.changelog.rev(node), short(node))
870
870
871 ui.status(
871 ui.status(
872 _(b'changeset %s backs out changeset %s\n')
872 _(b'changeset %s backs out changeset %s\n')
873 % (nice(newnode), nice(node))
873 % (nice(newnode), nice(node))
874 )
874 )
875 if opts.get(b'merge') and op1 != node:
875 if opts.get(b'merge') and op1 != node:
876 hg.clean(repo, op1, show_stats=False)
876 hg.clean(repo, op1, show_stats=False)
877 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
877 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
878 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
879 with ui.configoverride(overrides, b'backout'):
879 with ui.configoverride(overrides, b'backout'):
880 return hg.merge(repo[b'tip'])
880 return hg.merge(repo[b'tip'])
881 return 0
881 return 0
882
882
883
883
884 @command(
884 @command(
885 b'bisect',
885 b'bisect',
886 [
886 [
887 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'r', b'reset', False, _(b'reset bisect state')),
888 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'g', b'good', False, _(b'mark changeset good')),
889 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b'b', b'bad', False, _(b'mark changeset bad')),
890 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b's', b'skip', False, _(b'skip testing changeset')),
891 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (b'e', b'extend', False, _(b'extend the bisect range')),
892 (
892 (
893 b'c',
893 b'c',
894 b'command',
894 b'command',
895 b'',
895 b'',
896 _(b'use command to check changeset state'),
896 _(b'use command to check changeset state'),
897 _(b'CMD'),
897 _(b'CMD'),
898 ),
898 ),
899 (b'U', b'noupdate', False, _(b'do not update to target')),
899 (b'U', b'noupdate', False, _(b'do not update to target')),
900 ],
900 ],
901 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
902 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
903 )
903 )
904 def bisect(
904 def bisect(
905 ui,
905 ui,
906 repo,
906 repo,
907 positional_1=None,
907 positional_1=None,
908 positional_2=None,
908 positional_2=None,
909 command=None,
909 command=None,
910 reset=None,
910 reset=None,
911 good=None,
911 good=None,
912 bad=None,
912 bad=None,
913 skip=None,
913 skip=None,
914 extend=None,
914 extend=None,
915 noupdate=None,
915 noupdate=None,
916 ):
916 ):
917 """subdivision search of changesets
917 """subdivision search of changesets
918
918
919 This command helps to find changesets which introduce problems. To
919 This command helps to find changesets which introduce problems. To
920 use, mark the earliest changeset you know exhibits the problem as
920 use, mark the earliest changeset you know exhibits the problem as
921 bad, then mark the latest changeset which is free from the problem
921 bad, then mark the latest changeset which is free from the problem
922 as good. Bisect will update your working directory to a revision
922 as good. Bisect will update your working directory to a revision
923 for testing (unless the -U/--noupdate option is specified). Once
923 for testing (unless the -U/--noupdate option is specified). Once
924 you have performed tests, mark the working directory as good or
924 you have performed tests, mark the working directory as good or
925 bad, and bisect will either update to another candidate changeset
925 bad, and bisect will either update to another candidate changeset
926 or announce that it has found the bad revision.
926 or announce that it has found the bad revision.
927
927
928 As a shortcut, you can also use the revision argument to mark a
928 As a shortcut, you can also use the revision argument to mark a
929 revision as good or bad without checking it out first.
929 revision as good or bad without checking it out first.
930
930
931 If you supply a command, it will be used for automatic bisection.
931 If you supply a command, it will be used for automatic bisection.
932 The environment variable HG_NODE will contain the ID of the
932 The environment variable HG_NODE will contain the ID of the
933 changeset being tested. The exit status of the command will be
933 changeset being tested. The exit status of the command will be
934 used to mark revisions as good or bad: status 0 means good, 125
934 used to mark revisions as good or bad: status 0 means good, 125
935 means to skip the revision, 127 (command not found) will abort the
935 means to skip the revision, 127 (command not found) will abort the
936 bisection, and any other non-zero exit status means the revision
936 bisection, and any other non-zero exit status means the revision
937 is bad.
937 is bad.
938
938
939 .. container:: verbose
939 .. container:: verbose
940
940
941 Some examples:
941 Some examples:
942
942
943 - start a bisection with known bad revision 34, and good revision 12::
943 - start a bisection with known bad revision 34, and good revision 12::
944
944
945 hg bisect --bad 34
945 hg bisect --bad 34
946 hg bisect --good 12
946 hg bisect --good 12
947
947
948 - advance the current bisection by marking current revision as good or
948 - advance the current bisection by marking current revision as good or
949 bad::
949 bad::
950
950
951 hg bisect --good
951 hg bisect --good
952 hg bisect --bad
952 hg bisect --bad
953
953
954 - mark the current revision, or a known revision, to be skipped (e.g. if
954 - mark the current revision, or a known revision, to be skipped (e.g. if
955 that revision is not usable because of another issue)::
955 that revision is not usable because of another issue)::
956
956
957 hg bisect --skip
957 hg bisect --skip
958 hg bisect --skip 23
958 hg bisect --skip 23
959
959
960 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960 - skip all revisions that do not touch directories ``foo`` or ``bar``::
961
961
962 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
963
963
964 - forget the current bisection::
964 - forget the current bisection::
965
965
966 hg bisect --reset
966 hg bisect --reset
967
967
968 - use 'make && make tests' to automatically find the first broken
968 - use 'make && make tests' to automatically find the first broken
969 revision::
969 revision::
970
970
971 hg bisect --reset
971 hg bisect --reset
972 hg bisect --bad 34
972 hg bisect --bad 34
973 hg bisect --good 12
973 hg bisect --good 12
974 hg bisect --command "make && make tests"
974 hg bisect --command "make && make tests"
975
975
976 - see all changesets whose states are already known in the current
976 - see all changesets whose states are already known in the current
977 bisection::
977 bisection::
978
978
979 hg log -r "bisect(pruned)"
979 hg log -r "bisect(pruned)"
980
980
981 - see the changeset currently being bisected (especially useful
981 - see the changeset currently being bisected (especially useful
982 if running with -U/--noupdate)::
982 if running with -U/--noupdate)::
983
983
984 hg log -r "bisect(current)"
984 hg log -r "bisect(current)"
985
985
986 - see all changesets that took part in the current bisection::
986 - see all changesets that took part in the current bisection::
987
987
988 hg log -r "bisect(range)"
988 hg log -r "bisect(range)"
989
989
990 - you can even get a nice graph::
990 - you can even get a nice graph::
991
991
992 hg log --graph -r "bisect(range)"
992 hg log --graph -r "bisect(range)"
993
993
994 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
995
995
996 Returns 0 on success.
996 Returns 0 on success.
997 """
997 """
998 rev = []
998 rev = []
999 # backward compatibility
999 # backward compatibility
1000 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1000 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1001 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1001 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1002 cmd = positional_1
1002 cmd = positional_1
1003 rev.append(positional_2)
1003 rev.append(positional_2)
1004 if cmd == b"good":
1004 if cmd == b"good":
1005 good = True
1005 good = True
1006 elif cmd == b"bad":
1006 elif cmd == b"bad":
1007 bad = True
1007 bad = True
1008 else:
1008 else:
1009 reset = True
1009 reset = True
1010 elif positional_2:
1010 elif positional_2:
1011 raise error.InputError(_(b'incompatible arguments'))
1011 raise error.InputError(_(b'incompatible arguments'))
1012 elif positional_1 is not None:
1012 elif positional_1 is not None:
1013 rev.append(positional_1)
1013 rev.append(positional_1)
1014
1014
1015 incompatibles = {
1015 incompatibles = {
1016 b'--bad': bad,
1016 b'--bad': bad,
1017 b'--command': bool(command),
1017 b'--command': bool(command),
1018 b'--extend': extend,
1018 b'--extend': extend,
1019 b'--good': good,
1019 b'--good': good,
1020 b'--reset': reset,
1020 b'--reset': reset,
1021 b'--skip': skip,
1021 b'--skip': skip,
1022 }
1022 }
1023
1023
1024 enabled = [x for x in incompatibles if incompatibles[x]]
1024 enabled = [x for x in incompatibles if incompatibles[x]]
1025
1025
1026 if len(enabled) > 1:
1026 if len(enabled) > 1:
1027 raise error.InputError(
1027 raise error.InputError(
1028 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1028 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1029 )
1029 )
1030
1030
1031 if reset:
1031 if reset:
1032 hbisect.resetstate(repo)
1032 hbisect.resetstate(repo)
1033 return
1033 return
1034
1034
1035 state = hbisect.load_state(repo)
1035 state = hbisect.load_state(repo)
1036
1036
1037 if rev:
1037 if rev:
1038 revs = logcmdutil.revrange(repo, rev)
1038 revs = logcmdutil.revrange(repo, rev)
1039 goodnodes = state[b'good']
1039 goodnodes = state[b'good']
1040 badnodes = state[b'bad']
1040 badnodes = state[b'bad']
1041 if goodnodes and badnodes:
1041 if goodnodes and badnodes:
1042 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1042 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1043 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1043 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1044 revs = candidates & revs
1044 revs = candidates & revs
1045 nodes = [repo.changelog.node(i) for i in revs]
1045 nodes = [repo.changelog.node(i) for i in revs]
1046 else:
1046 else:
1047 nodes = [repo.lookup(b'.')]
1047 nodes = [repo.lookup(b'.')]
1048
1048
1049 # update state
1049 # update state
1050 if good or bad or skip:
1050 if good or bad or skip:
1051 if good:
1051 if good:
1052 state[b'good'] += nodes
1052 state[b'good'] += nodes
1053 elif bad:
1053 elif bad:
1054 state[b'bad'] += nodes
1054 state[b'bad'] += nodes
1055 elif skip:
1055 elif skip:
1056 state[b'skip'] += nodes
1056 state[b'skip'] += nodes
1057 hbisect.save_state(repo, state)
1057 hbisect.save_state(repo, state)
1058 if not (state[b'good'] and state[b'bad']):
1058 if not (state[b'good'] and state[b'bad']):
1059 return
1059 return
1060
1060
1061 def mayupdate(repo, node, show_stats=True):
1061 def mayupdate(repo, node, show_stats=True):
1062 """common used update sequence"""
1062 """common used update sequence"""
1063 if noupdate:
1063 if noupdate:
1064 return
1064 return
1065 cmdutil.checkunfinished(repo)
1065 cmdutil.checkunfinished(repo)
1066 cmdutil.bailifchanged(repo)
1066 cmdutil.bailifchanged(repo)
1067 return hg.clean(repo, node, show_stats=show_stats)
1067 return hg.clean(repo, node, show_stats=show_stats)
1068
1068
1069 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1069 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1070
1070
1071 if command:
1071 if command:
1072 changesets = 1
1072 changesets = 1
1073 if noupdate:
1073 if noupdate:
1074 try:
1074 try:
1075 node = state[b'current'][0]
1075 node = state[b'current'][0]
1076 except LookupError:
1076 except LookupError:
1077 raise error.StateError(
1077 raise error.StateError(
1078 _(
1078 _(
1079 b'current bisect revision is unknown - '
1079 b'current bisect revision is unknown - '
1080 b'start a new bisect to fix'
1080 b'start a new bisect to fix'
1081 )
1081 )
1082 )
1082 )
1083 else:
1083 else:
1084 node, p2 = repo.dirstate.parents()
1084 node, p2 = repo.dirstate.parents()
1085 if p2 != repo.nullid:
1085 if p2 != repo.nullid:
1086 raise error.StateError(_(b'current bisect revision is a merge'))
1086 raise error.StateError(_(b'current bisect revision is a merge'))
1087 if rev:
1087 if rev:
1088 if not nodes:
1088 if not nodes:
1089 raise error.InputError(_(b'empty revision set'))
1089 raise error.InputError(_(b'empty revision set'))
1090 node = repo[nodes[-1]].node()
1090 node = repo[nodes[-1]].node()
1091 with hbisect.restore_state(repo, state, node):
1091 with hbisect.restore_state(repo, state, node):
1092 while changesets:
1092 while changesets:
1093 # update state
1093 # update state
1094 state[b'current'] = [node]
1094 state[b'current'] = [node]
1095 hbisect.save_state(repo, state)
1095 hbisect.save_state(repo, state)
1096 status = ui.system(
1096 status = ui.system(
1097 command,
1097 command,
1098 environ={b'HG_NODE': hex(node)},
1098 environ={b'HG_NODE': hex(node)},
1099 blockedtag=b'bisect_check',
1099 blockedtag=b'bisect_check',
1100 )
1100 )
1101 if status == 125:
1101 if status == 125:
1102 transition = b"skip"
1102 transition = b"skip"
1103 elif status == 0:
1103 elif status == 0:
1104 transition = b"good"
1104 transition = b"good"
1105 # status < 0 means process was killed
1105 # status < 0 means process was killed
1106 elif status == 127:
1106 elif status == 127:
1107 raise error.Abort(_(b"failed to execute %s") % command)
1107 raise error.Abort(_(b"failed to execute %s") % command)
1108 elif status < 0:
1108 elif status < 0:
1109 raise error.Abort(_(b"%s killed") % command)
1109 raise error.Abort(_(b"%s killed") % command)
1110 else:
1110 else:
1111 transition = b"bad"
1111 transition = b"bad"
1112 state[transition].append(node)
1112 state[transition].append(node)
1113 ctx = repo[node]
1113 ctx = repo[node]
1114 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1114 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1115 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1115 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1116 hbisect.checkstate(state)
1116 hbisect.checkstate(state)
1117 # bisect
1117 # bisect
1118 nodes, changesets, bgood = hbisect.bisect(repo, state)
1118 nodes, changesets, bgood = hbisect.bisect(repo, state)
1119 # update to next check
1119 # update to next check
1120 node = nodes[0]
1120 node = nodes[0]
1121 mayupdate(repo, node, show_stats=False)
1121 mayupdate(repo, node, show_stats=False)
1122 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1122 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1123 return
1123 return
1124
1124
1125 hbisect.checkstate(state)
1125 hbisect.checkstate(state)
1126
1126
1127 # actually bisect
1127 # actually bisect
1128 nodes, changesets, good = hbisect.bisect(repo, state)
1128 nodes, changesets, good = hbisect.bisect(repo, state)
1129 if extend:
1129 if extend:
1130 if not changesets:
1130 if not changesets:
1131 extendctx = hbisect.extendrange(repo, state, nodes, good)
1131 extendctx = hbisect.extendrange(repo, state, nodes, good)
1132 if extendctx is not None:
1132 if extendctx is not None:
1133 ui.write(
1133 ui.write(
1134 _(b"Extending search to changeset %s\n")
1134 _(b"Extending search to changeset %s\n")
1135 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1135 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1136 )
1136 )
1137 state[b'current'] = [extendctx.node()]
1137 state[b'current'] = [extendctx.node()]
1138 hbisect.save_state(repo, state)
1138 hbisect.save_state(repo, state)
1139 return mayupdate(repo, extendctx.node())
1139 return mayupdate(repo, extendctx.node())
1140 raise error.StateError(_(b"nothing to extend"))
1140 raise error.StateError(_(b"nothing to extend"))
1141
1141
1142 if changesets == 0:
1142 if changesets == 0:
1143 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1143 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1144 else:
1144 else:
1145 assert len(nodes) == 1 # only a single node can be tested next
1145 assert len(nodes) == 1 # only a single node can be tested next
1146 node = nodes[0]
1146 node = nodes[0]
1147 # compute the approximate number of remaining tests
1147 # compute the approximate number of remaining tests
1148 tests, size = 0, 2
1148 tests, size = 0, 2
1149 while size <= changesets:
1149 while size <= changesets:
1150 tests, size = tests + 1, size * 2
1150 tests, size = tests + 1, size * 2
1151 rev = repo.changelog.rev(node)
1151 rev = repo.changelog.rev(node)
1152 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1152 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1153 ui.write(
1153 ui.write(
1154 _(
1154 _(
1155 b"Testing changeset %s "
1155 b"Testing changeset %s "
1156 b"(%d changesets remaining, ~%d tests)\n"
1156 b"(%d changesets remaining, ~%d tests)\n"
1157 )
1157 )
1158 % (summary, changesets, tests)
1158 % (summary, changesets, tests)
1159 )
1159 )
1160 state[b'current'] = [node]
1160 state[b'current'] = [node]
1161 hbisect.save_state(repo, state)
1161 hbisect.save_state(repo, state)
1162 return mayupdate(repo, node)
1162 return mayupdate(repo, node)
1163
1163
1164
1164
1165 @command(
1165 @command(
1166 b'bookmarks|bookmark',
1166 b'bookmarks|bookmark',
1167 [
1167 [
1168 (b'f', b'force', False, _(b'force')),
1168 (b'f', b'force', False, _(b'force')),
1169 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1169 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1170 (b'd', b'delete', False, _(b'delete a given bookmark')),
1170 (b'd', b'delete', False, _(b'delete a given bookmark')),
1171 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1171 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1172 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1172 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1173 (b'l', b'list', False, _(b'list existing bookmarks')),
1173 (b'l', b'list', False, _(b'list existing bookmarks')),
1174 ]
1174 ]
1175 + formatteropts,
1175 + formatteropts,
1176 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1176 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1177 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1177 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1178 )
1178 )
1179 def bookmark(ui, repo, *names, **opts):
1179 def bookmark(ui, repo, *names, **opts):
1180 """create a new bookmark or list existing bookmarks
1180 """create a new bookmark or list existing bookmarks
1181
1181
1182 Bookmarks are labels on changesets to help track lines of development.
1182 Bookmarks are labels on changesets to help track lines of development.
1183 Bookmarks are unversioned and can be moved, renamed and deleted.
1183 Bookmarks are unversioned and can be moved, renamed and deleted.
1184 Deleting or moving a bookmark has no effect on the associated changesets.
1184 Deleting or moving a bookmark has no effect on the associated changesets.
1185
1185
1186 Creating or updating to a bookmark causes it to be marked as 'active'.
1186 Creating or updating to a bookmark causes it to be marked as 'active'.
1187 The active bookmark is indicated with a '*'.
1187 The active bookmark is indicated with a '*'.
1188 When a commit is made, the active bookmark will advance to the new commit.
1188 When a commit is made, the active bookmark will advance to the new commit.
1189 A plain :hg:`update` will also advance an active bookmark, if possible.
1189 A plain :hg:`update` will also advance an active bookmark, if possible.
1190 Updating away from a bookmark will cause it to be deactivated.
1190 Updating away from a bookmark will cause it to be deactivated.
1191
1191
1192 Bookmarks can be pushed and pulled between repositories (see
1192 Bookmarks can be pushed and pulled between repositories (see
1193 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1193 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1194 diverged, a new 'divergent bookmark' of the form 'name@path' will
1194 diverged, a new 'divergent bookmark' of the form 'name@path' will
1195 be created. Using :hg:`merge` will resolve the divergence.
1195 be created. Using :hg:`merge` will resolve the divergence.
1196
1196
1197 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1197 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1198 the active bookmark's name.
1198 the active bookmark's name.
1199
1199
1200 A bookmark named '@' has the special property that :hg:`clone` will
1200 A bookmark named '@' has the special property that :hg:`clone` will
1201 check it out by default if it exists.
1201 check it out by default if it exists.
1202
1202
1203 .. container:: verbose
1203 .. container:: verbose
1204
1204
1205 Template:
1205 Template:
1206
1206
1207 The following keywords are supported in addition to the common template
1207 The following keywords are supported in addition to the common template
1208 keywords and functions such as ``{bookmark}``. See also
1208 keywords and functions such as ``{bookmark}``. See also
1209 :hg:`help templates`.
1209 :hg:`help templates`.
1210
1210
1211 :active: Boolean. True if the bookmark is active.
1211 :active: Boolean. True if the bookmark is active.
1212
1212
1213 Examples:
1213 Examples:
1214
1214
1215 - create an active bookmark for a new line of development::
1215 - create an active bookmark for a new line of development::
1216
1216
1217 hg book new-feature
1217 hg book new-feature
1218
1218
1219 - create an inactive bookmark as a place marker::
1219 - create an inactive bookmark as a place marker::
1220
1220
1221 hg book -i reviewed
1221 hg book -i reviewed
1222
1222
1223 - create an inactive bookmark on another changeset::
1223 - create an inactive bookmark on another changeset::
1224
1224
1225 hg book -r .^ tested
1225 hg book -r .^ tested
1226
1226
1227 - rename bookmark turkey to dinner::
1227 - rename bookmark turkey to dinner::
1228
1228
1229 hg book -m turkey dinner
1229 hg book -m turkey dinner
1230
1230
1231 - move the '@' bookmark from another branch::
1231 - move the '@' bookmark from another branch::
1232
1232
1233 hg book -f @
1233 hg book -f @
1234
1234
1235 - print only the active bookmark name::
1235 - print only the active bookmark name::
1236
1236
1237 hg book -ql .
1237 hg book -ql .
1238 """
1238 """
1239 opts = pycompat.byteskwargs(opts)
1239 opts = pycompat.byteskwargs(opts)
1240 force = opts.get(b'force')
1240 force = opts.get(b'force')
1241 rev = opts.get(b'rev')
1241 rev = opts.get(b'rev')
1242 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1242 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1243
1243
1244 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1244 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1245 if action:
1245 if action:
1246 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1246 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1247 elif names or rev:
1247 elif names or rev:
1248 action = b'add'
1248 action = b'add'
1249 elif inactive:
1249 elif inactive:
1250 action = b'inactive' # meaning deactivate
1250 action = b'inactive' # meaning deactivate
1251 else:
1251 else:
1252 action = b'list'
1252 action = b'list'
1253
1253
1254 cmdutil.check_incompatible_arguments(
1254 cmdutil.check_incompatible_arguments(
1255 opts, b'inactive', [b'delete', b'list']
1255 opts, b'inactive', [b'delete', b'list']
1256 )
1256 )
1257 if not names and action in {b'add', b'delete'}:
1257 if not names and action in {b'add', b'delete'}:
1258 raise error.InputError(_(b"bookmark name required"))
1258 raise error.InputError(_(b"bookmark name required"))
1259
1259
1260 if action in {b'add', b'delete', b'rename', b'inactive'}:
1260 if action in {b'add', b'delete', b'rename', b'inactive'}:
1261 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1261 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1262 if action == b'delete':
1262 if action == b'delete':
1263 names = pycompat.maplist(repo._bookmarks.expandname, names)
1263 names = pycompat.maplist(repo._bookmarks.expandname, names)
1264 bookmarks.delete(repo, tr, names)
1264 bookmarks.delete(repo, tr, names)
1265 elif action == b'rename':
1265 elif action == b'rename':
1266 if not names:
1266 if not names:
1267 raise error.InputError(_(b"new bookmark name required"))
1267 raise error.InputError(_(b"new bookmark name required"))
1268 elif len(names) > 1:
1268 elif len(names) > 1:
1269 raise error.InputError(
1269 raise error.InputError(
1270 _(b"only one new bookmark name allowed")
1270 _(b"only one new bookmark name allowed")
1271 )
1271 )
1272 oldname = repo._bookmarks.expandname(opts[b'rename'])
1272 oldname = repo._bookmarks.expandname(opts[b'rename'])
1273 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1273 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1274 elif action == b'add':
1274 elif action == b'add':
1275 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1275 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1276 elif action == b'inactive':
1276 elif action == b'inactive':
1277 if len(repo._bookmarks) == 0:
1277 if len(repo._bookmarks) == 0:
1278 ui.status(_(b"no bookmarks set\n"))
1278 ui.status(_(b"no bookmarks set\n"))
1279 elif not repo._activebookmark:
1279 elif not repo._activebookmark:
1280 ui.status(_(b"no active bookmark\n"))
1280 ui.status(_(b"no active bookmark\n"))
1281 else:
1281 else:
1282 bookmarks.deactivate(repo)
1282 bookmarks.deactivate(repo)
1283 elif action == b'list':
1283 elif action == b'list':
1284 names = pycompat.maplist(repo._bookmarks.expandname, names)
1284 names = pycompat.maplist(repo._bookmarks.expandname, names)
1285 with ui.formatter(b'bookmarks', opts) as fm:
1285 with ui.formatter(b'bookmarks', opts) as fm:
1286 bookmarks.printbookmarks(ui, repo, fm, names)
1286 bookmarks.printbookmarks(ui, repo, fm, names)
1287 else:
1287 else:
1288 raise error.ProgrammingError(b'invalid action: %s' % action)
1288 raise error.ProgrammingError(b'invalid action: %s' % action)
1289
1289
1290
1290
1291 @command(
1291 @command(
1292 b'branch',
1292 b'branch',
1293 [
1293 [
1294 (
1294 (
1295 b'f',
1295 b'f',
1296 b'force',
1296 b'force',
1297 None,
1297 None,
1298 _(b'set branch name even if it shadows an existing branch'),
1298 _(b'set branch name even if it shadows an existing branch'),
1299 ),
1299 ),
1300 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1300 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1301 (
1301 (
1302 b'r',
1302 b'r',
1303 b'rev',
1303 b'rev',
1304 [],
1304 [],
1305 _(b'change branches of the given revs (EXPERIMENTAL)'),
1305 _(b'change branches of the given revs (EXPERIMENTAL)'),
1306 ),
1306 ),
1307 ],
1307 ],
1308 _(b'[-fC] [NAME]'),
1308 _(b'[-fC] [NAME]'),
1309 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1309 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1310 )
1310 )
1311 def branch(ui, repo, label=None, **opts):
1311 def branch(ui, repo, label=None, **opts):
1312 """set or show the current branch name
1312 """set or show the current branch name
1313
1313
1314 .. note::
1314 .. note::
1315
1315
1316 Branch names are permanent and global. Use :hg:`bookmark` to create a
1316 Branch names are permanent and global. Use :hg:`bookmark` to create a
1317 light-weight bookmark instead. See :hg:`help glossary` for more
1317 light-weight bookmark instead. See :hg:`help glossary` for more
1318 information about named branches and bookmarks.
1318 information about named branches and bookmarks.
1319
1319
1320 With no argument, show the current branch name. With one argument,
1320 With no argument, show the current branch name. With one argument,
1321 set the working directory branch name (the branch will not exist
1321 set the working directory branch name (the branch will not exist
1322 in the repository until the next commit). Standard practice
1322 in the repository until the next commit). Standard practice
1323 recommends that primary development take place on the 'default'
1323 recommends that primary development take place on the 'default'
1324 branch.
1324 branch.
1325
1325
1326 Unless -f/--force is specified, branch will not let you set a
1326 Unless -f/--force is specified, branch will not let you set a
1327 branch name that already exists.
1327 branch name that already exists.
1328
1328
1329 Use -C/--clean to reset the working directory branch to that of
1329 Use -C/--clean to reset the working directory branch to that of
1330 the parent of the working directory, negating a previous branch
1330 the parent of the working directory, negating a previous branch
1331 change.
1331 change.
1332
1332
1333 Use the command :hg:`update` to switch to an existing branch. Use
1333 Use the command :hg:`update` to switch to an existing branch. Use
1334 :hg:`commit --close-branch` to mark this branch head as closed.
1334 :hg:`commit --close-branch` to mark this branch head as closed.
1335 When all heads of a branch are closed, the branch will be
1335 When all heads of a branch are closed, the branch will be
1336 considered closed.
1336 considered closed.
1337
1337
1338 Returns 0 on success.
1338 Returns 0 on success.
1339 """
1339 """
1340 opts = pycompat.byteskwargs(opts)
1340 opts = pycompat.byteskwargs(opts)
1341 revs = opts.get(b'rev')
1341 revs = opts.get(b'rev')
1342 if label:
1342 if label:
1343 label = label.strip()
1343 label = label.strip()
1344
1344
1345 if not opts.get(b'clean') and not label:
1345 if not opts.get(b'clean') and not label:
1346 if revs:
1346 if revs:
1347 raise error.InputError(
1347 raise error.InputError(
1348 _(b"no branch name specified for the revisions")
1348 _(b"no branch name specified for the revisions")
1349 )
1349 )
1350 ui.write(b"%s\n" % repo.dirstate.branch())
1350 ui.write(b"%s\n" % repo.dirstate.branch())
1351 return
1351 return
1352
1352
1353 with repo.wlock():
1353 with repo.wlock():
1354 if opts.get(b'clean'):
1354 if opts.get(b'clean'):
1355 label = repo[b'.'].branch()
1355 label = repo[b'.'].branch()
1356 repo.dirstate.setbranch(label)
1356 repo.dirstate.setbranch(label)
1357 ui.status(_(b'reset working directory to branch %s\n') % label)
1357 ui.status(_(b'reset working directory to branch %s\n') % label)
1358 elif label:
1358 elif label:
1359
1359
1360 scmutil.checknewlabel(repo, label, b'branch')
1360 scmutil.checknewlabel(repo, label, b'branch')
1361 if revs:
1361 if revs:
1362 return cmdutil.changebranch(ui, repo, revs, label, opts)
1362 return cmdutil.changebranch(ui, repo, revs, label, opts)
1363
1363
1364 if not opts.get(b'force') and label in repo.branchmap():
1364 if not opts.get(b'force') and label in repo.branchmap():
1365 if label not in [p.branch() for p in repo[None].parents()]:
1365 if label not in [p.branch() for p in repo[None].parents()]:
1366 raise error.InputError(
1366 raise error.InputError(
1367 _(b'a branch of the same name already exists'),
1367 _(b'a branch of the same name already exists'),
1368 # i18n: "it" refers to an existing branch
1368 # i18n: "it" refers to an existing branch
1369 hint=_(b"use 'hg update' to switch to it"),
1369 hint=_(b"use 'hg update' to switch to it"),
1370 )
1370 )
1371
1371
1372 repo.dirstate.setbranch(label)
1372 repo.dirstate.setbranch(label)
1373 ui.status(_(b'marked working directory as branch %s\n') % label)
1373 ui.status(_(b'marked working directory as branch %s\n') % label)
1374
1374
1375 # find any open named branches aside from default
1375 # find any open named branches aside from default
1376 for n, h, t, c in repo.branchmap().iterbranches():
1376 for n, h, t, c in repo.branchmap().iterbranches():
1377 if n != b"default" and not c:
1377 if n != b"default" and not c:
1378 return 0
1378 return 0
1379 ui.status(
1379 ui.status(
1380 _(
1380 _(
1381 b'(branches are permanent and global, '
1381 b'(branches are permanent and global, '
1382 b'did you want a bookmark?)\n'
1382 b'did you want a bookmark?)\n'
1383 )
1383 )
1384 )
1384 )
1385
1385
1386
1386
1387 @command(
1387 @command(
1388 b'branches',
1388 b'branches',
1389 [
1389 [
1390 (
1390 (
1391 b'a',
1391 b'a',
1392 b'active',
1392 b'active',
1393 False,
1393 False,
1394 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1394 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1395 ),
1395 ),
1396 (b'c', b'closed', False, _(b'show normal and closed branches')),
1396 (b'c', b'closed', False, _(b'show normal and closed branches')),
1397 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1397 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1398 ]
1398 ]
1399 + formatteropts,
1399 + formatteropts,
1400 _(b'[-c]'),
1400 _(b'[-c]'),
1401 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1401 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1402 intents={INTENT_READONLY},
1402 intents={INTENT_READONLY},
1403 )
1403 )
1404 def branches(ui, repo, active=False, closed=False, **opts):
1404 def branches(ui, repo, active=False, closed=False, **opts):
1405 """list repository named branches
1405 """list repository named branches
1406
1406
1407 List the repository's named branches, indicating which ones are
1407 List the repository's named branches, indicating which ones are
1408 inactive. If -c/--closed is specified, also list branches which have
1408 inactive. If -c/--closed is specified, also list branches which have
1409 been marked closed (see :hg:`commit --close-branch`).
1409 been marked closed (see :hg:`commit --close-branch`).
1410
1410
1411 Use the command :hg:`update` to switch to an existing branch.
1411 Use the command :hg:`update` to switch to an existing branch.
1412
1412
1413 .. container:: verbose
1413 .. container:: verbose
1414
1414
1415 Template:
1415 Template:
1416
1416
1417 The following keywords are supported in addition to the common template
1417 The following keywords are supported in addition to the common template
1418 keywords and functions such as ``{branch}``. See also
1418 keywords and functions such as ``{branch}``. See also
1419 :hg:`help templates`.
1419 :hg:`help templates`.
1420
1420
1421 :active: Boolean. True if the branch is active.
1421 :active: Boolean. True if the branch is active.
1422 :closed: Boolean. True if the branch is closed.
1422 :closed: Boolean. True if the branch is closed.
1423 :current: Boolean. True if it is the current branch.
1423 :current: Boolean. True if it is the current branch.
1424
1424
1425 Returns 0.
1425 Returns 0.
1426 """
1426 """
1427
1427
1428 opts = pycompat.byteskwargs(opts)
1428 opts = pycompat.byteskwargs(opts)
1429 revs = opts.get(b'rev')
1429 revs = opts.get(b'rev')
1430 selectedbranches = None
1430 selectedbranches = None
1431 if revs:
1431 if revs:
1432 revs = logcmdutil.revrange(repo, revs)
1432 revs = logcmdutil.revrange(repo, revs)
1433 getbi = repo.revbranchcache().branchinfo
1433 getbi = repo.revbranchcache().branchinfo
1434 selectedbranches = {getbi(r)[0] for r in revs}
1434 selectedbranches = {getbi(r)[0] for r in revs}
1435
1435
1436 ui.pager(b'branches')
1436 ui.pager(b'branches')
1437 fm = ui.formatter(b'branches', opts)
1437 fm = ui.formatter(b'branches', opts)
1438 hexfunc = fm.hexfunc
1438 hexfunc = fm.hexfunc
1439
1439
1440 allheads = set(repo.heads())
1440 allheads = set(repo.heads())
1441 branches = []
1441 branches = []
1442 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1442 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1443 if selectedbranches is not None and tag not in selectedbranches:
1443 if selectedbranches is not None and tag not in selectedbranches:
1444 continue
1444 continue
1445 isactive = False
1445 isactive = False
1446 if not isclosed:
1446 if not isclosed:
1447 openheads = set(repo.branchmap().iteropen(heads))
1447 openheads = set(repo.branchmap().iteropen(heads))
1448 isactive = bool(openheads & allheads)
1448 isactive = bool(openheads & allheads)
1449 branches.append((tag, repo[tip], isactive, not isclosed))
1449 branches.append((tag, repo[tip], isactive, not isclosed))
1450 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1450 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1451
1451
1452 for tag, ctx, isactive, isopen in branches:
1452 for tag, ctx, isactive, isopen in branches:
1453 if active and not isactive:
1453 if active and not isactive:
1454 continue
1454 continue
1455 if isactive:
1455 if isactive:
1456 label = b'branches.active'
1456 label = b'branches.active'
1457 notice = b''
1457 notice = b''
1458 elif not isopen:
1458 elif not isopen:
1459 if not closed:
1459 if not closed:
1460 continue
1460 continue
1461 label = b'branches.closed'
1461 label = b'branches.closed'
1462 notice = _(b' (closed)')
1462 notice = _(b' (closed)')
1463 else:
1463 else:
1464 label = b'branches.inactive'
1464 label = b'branches.inactive'
1465 notice = _(b' (inactive)')
1465 notice = _(b' (inactive)')
1466 current = tag == repo.dirstate.branch()
1466 current = tag == repo.dirstate.branch()
1467 if current:
1467 if current:
1468 label = b'branches.current'
1468 label = b'branches.current'
1469
1469
1470 fm.startitem()
1470 fm.startitem()
1471 fm.write(b'branch', b'%s', tag, label=label)
1471 fm.write(b'branch', b'%s', tag, label=label)
1472 rev = ctx.rev()
1472 rev = ctx.rev()
1473 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1473 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1474 fmt = b' ' * padsize + b' %d:%s'
1474 fmt = b' ' * padsize + b' %d:%s'
1475 fm.condwrite(
1475 fm.condwrite(
1476 not ui.quiet,
1476 not ui.quiet,
1477 b'rev node',
1477 b'rev node',
1478 fmt,
1478 fmt,
1479 rev,
1479 rev,
1480 hexfunc(ctx.node()),
1480 hexfunc(ctx.node()),
1481 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1481 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1482 )
1482 )
1483 fm.context(ctx=ctx)
1483 fm.context(ctx=ctx)
1484 fm.data(active=isactive, closed=not isopen, current=current)
1484 fm.data(active=isactive, closed=not isopen, current=current)
1485 if not ui.quiet:
1485 if not ui.quiet:
1486 fm.plain(notice)
1486 fm.plain(notice)
1487 fm.plain(b'\n')
1487 fm.plain(b'\n')
1488 fm.end()
1488 fm.end()
1489
1489
1490
1490
1491 @command(
1491 @command(
1492 b'bundle',
1492 b'bundle',
1493 [
1493 [
1494 (
1494 (
1495 b'',
1495 b'',
1496 b'exact',
1496 b'exact',
1497 None,
1497 None,
1498 _(b'compute the base from the revision specified'),
1498 _(b'compute the base from the revision specified'),
1499 ),
1499 ),
1500 (
1500 (
1501 b'f',
1501 b'f',
1502 b'force',
1502 b'force',
1503 None,
1503 None,
1504 _(b'run even when the destination is unrelated'),
1504 _(b'run even when the destination is unrelated'),
1505 ),
1505 ),
1506 (
1506 (
1507 b'r',
1507 b'r',
1508 b'rev',
1508 b'rev',
1509 [],
1509 [],
1510 _(b'a changeset intended to be added to the destination'),
1510 _(b'a changeset intended to be added to the destination'),
1511 _(b'REV'),
1511 _(b'REV'),
1512 ),
1512 ),
1513 (
1513 (
1514 b'b',
1514 b'b',
1515 b'branch',
1515 b'branch',
1516 [],
1516 [],
1517 _(b'a specific branch you would like to bundle'),
1517 _(b'a specific branch you would like to bundle'),
1518 _(b'BRANCH'),
1518 _(b'BRANCH'),
1519 ),
1519 ),
1520 (
1520 (
1521 b'',
1521 b'',
1522 b'base',
1522 b'base',
1523 [],
1523 [],
1524 _(b'a base changeset assumed to be available at the destination'),
1524 _(b'a base changeset assumed to be available at the destination'),
1525 _(b'REV'),
1525 _(b'REV'),
1526 ),
1526 ),
1527 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1527 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1528 (
1528 (
1529 b't',
1529 b't',
1530 b'type',
1530 b'type',
1531 b'bzip2',
1531 b'bzip2',
1532 _(b'bundle compression type to use'),
1532 _(b'bundle compression type to use'),
1533 _(b'TYPE'),
1533 _(b'TYPE'),
1534 ),
1534 ),
1535 ]
1535 ]
1536 + remoteopts,
1536 + remoteopts,
1537 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1537 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1538 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1538 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1539 )
1539 )
1540 def bundle(ui, repo, fname, *dests, **opts):
1540 def bundle(ui, repo, fname, *dests, **opts):
1541 """create a bundle file
1541 """create a bundle file
1542
1542
1543 Generate a bundle file containing data to be transferred to another
1543 Generate a bundle file containing data to be transferred to another
1544 repository.
1544 repository.
1545
1545
1546 To create a bundle containing all changesets, use -a/--all
1546 To create a bundle containing all changesets, use -a/--all
1547 (or --base null). Otherwise, hg assumes the destination will have
1547 (or --base null). Otherwise, hg assumes the destination will have
1548 all the nodes you specify with --base parameters. Otherwise, hg
1548 all the nodes you specify with --base parameters. Otherwise, hg
1549 will assume the repository has all the nodes in destination, or
1549 will assume the repository has all the nodes in destination, or
1550 default-push/default if no destination is specified, where destination
1550 default-push/default if no destination is specified, where destination
1551 is the repositories you provide through DEST option.
1551 is the repositories you provide through DEST option.
1552
1552
1553 You can change bundle format with the -t/--type option. See
1553 You can change bundle format with the -t/--type option. See
1554 :hg:`help bundlespec` for documentation on this format. By default,
1554 :hg:`help bundlespec` for documentation on this format. By default,
1555 the most appropriate format is used and compression defaults to
1555 the most appropriate format is used and compression defaults to
1556 bzip2.
1556 bzip2.
1557
1557
1558 The bundle file can then be transferred using conventional means
1558 The bundle file can then be transferred using conventional means
1559 and applied to another repository with the unbundle or pull
1559 and applied to another repository with the unbundle or pull
1560 command. This is useful when direct push and pull are not
1560 command. This is useful when direct push and pull are not
1561 available or when exporting an entire repository is undesirable.
1561 available or when exporting an entire repository is undesirable.
1562
1562
1563 Applying bundles preserves all changeset contents including
1563 Applying bundles preserves all changeset contents including
1564 permissions, copy/rename information, and revision history.
1564 permissions, copy/rename information, and revision history.
1565
1565
1566 Returns 0 on success, 1 if no changes found.
1566 Returns 0 on success, 1 if no changes found.
1567 """
1567 """
1568 opts = pycompat.byteskwargs(opts)
1568 opts = pycompat.byteskwargs(opts)
1569
1569
1570 revs = None
1570 revs = None
1571 if b'rev' in opts:
1571 if b'rev' in opts:
1572 revstrings = opts[b'rev']
1572 revstrings = opts[b'rev']
1573 revs = logcmdutil.revrange(repo, revstrings)
1573 revs = logcmdutil.revrange(repo, revstrings)
1574 if revstrings and not revs:
1574 if revstrings and not revs:
1575 raise error.InputError(_(b'no commits to bundle'))
1575 raise error.InputError(_(b'no commits to bundle'))
1576
1576
1577 bundletype = opts.get(b'type', b'bzip2').lower()
1577 bundletype = opts.get(b'type', b'bzip2').lower()
1578 try:
1578 try:
1579 bundlespec = bundlecaches.parsebundlespec(
1579 bundlespec = bundlecaches.parsebundlespec(
1580 repo, bundletype, strict=False
1580 repo, bundletype, strict=False
1581 )
1581 )
1582 except error.UnsupportedBundleSpecification as e:
1582 except error.UnsupportedBundleSpecification as e:
1583 raise error.InputError(
1583 raise error.InputError(
1584 pycompat.bytestr(e),
1584 pycompat.bytestr(e),
1585 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1585 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1586 )
1586 )
1587 cgversion = bundlespec.params[b"cg.version"]
1587 cgversion = bundlespec.params[b"cg.version"]
1588
1588
1589 # Packed bundles are a pseudo bundle format for now.
1589 # Packed bundles are a pseudo bundle format for now.
1590 if cgversion == b's1':
1590 if cgversion == b's1':
1591 raise error.InputError(
1591 raise error.InputError(
1592 _(b'packed bundles cannot be produced by "hg bundle"'),
1592 _(b'packed bundles cannot be produced by "hg bundle"'),
1593 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1593 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1594 )
1594 )
1595
1595
1596 if opts.get(b'all'):
1596 if opts.get(b'all'):
1597 if dests:
1597 if dests:
1598 raise error.InputError(
1598 raise error.InputError(
1599 _(b"--all is incompatible with specifying destinations")
1599 _(b"--all is incompatible with specifying destinations")
1600 )
1600 )
1601 if opts.get(b'base'):
1601 if opts.get(b'base'):
1602 ui.warn(_(b"ignoring --base because --all was specified\n"))
1602 ui.warn(_(b"ignoring --base because --all was specified\n"))
1603 if opts.get(b'exact'):
1603 if opts.get(b'exact'):
1604 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1604 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1605 base = [nullrev]
1605 base = [nullrev]
1606 elif opts.get(b'exact'):
1606 elif opts.get(b'exact'):
1607 if dests:
1607 if dests:
1608 raise error.InputError(
1608 raise error.InputError(
1609 _(b"--exact is incompatible with specifying destinations")
1609 _(b"--exact is incompatible with specifying destinations")
1610 )
1610 )
1611 if opts.get(b'base'):
1611 if opts.get(b'base'):
1612 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1612 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1613 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1613 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1614 if not base:
1614 if not base:
1615 base = [nullrev]
1615 base = [nullrev]
1616 else:
1616 else:
1617 base = logcmdutil.revrange(repo, opts.get(b'base'))
1617 base = logcmdutil.revrange(repo, opts.get(b'base'))
1618 if cgversion not in changegroup.supportedoutgoingversions(repo):
1618 if cgversion not in changegroup.supportedoutgoingversions(repo):
1619 raise error.Abort(
1619 raise error.Abort(
1620 _(b"repository does not support bundle version %s") % cgversion
1620 _(b"repository does not support bundle version %s") % cgversion
1621 )
1621 )
1622
1622
1623 if base:
1623 if base:
1624 if dests:
1624 if dests:
1625 raise error.InputError(
1625 raise error.InputError(
1626 _(b"--base is incompatible with specifying destinations")
1626 _(b"--base is incompatible with specifying destinations")
1627 )
1627 )
1628 cl = repo.changelog
1628 cl = repo.changelog
1629 common = [cl.node(rev) for rev in base]
1629 common = [cl.node(rev) for rev in base]
1630 heads = [cl.node(r) for r in revs] if revs else None
1630 heads = [cl.node(r) for r in revs] if revs else None
1631 outgoing = discovery.outgoing(repo, common, heads)
1631 outgoing = discovery.outgoing(repo, common, heads)
1632 missing = outgoing.missing
1632 missing = outgoing.missing
1633 excluded = outgoing.excluded
1633 excluded = outgoing.excluded
1634 else:
1634 else:
1635 missing = set()
1635 missing = set()
1636 excluded = set()
1636 excluded = set()
1637 for path in urlutil.get_push_paths(repo, ui, dests):
1637 for path in urlutil.get_push_paths(repo, ui, dests):
1638 other = hg.peer(repo, opts, path.rawloc)
1638 other = hg.peer(repo, opts, path.rawloc)
1639 if revs is not None:
1639 if revs is not None:
1640 hex_revs = [repo[r].hex() for r in revs]
1640 hex_revs = [repo[r].hex() for r in revs]
1641 else:
1641 else:
1642 hex_revs = None
1642 hex_revs = None
1643 branches = (path.branch, [])
1643 branches = (path.branch, [])
1644 head_revs, checkout = hg.addbranchrevs(
1644 head_revs, checkout = hg.addbranchrevs(
1645 repo, repo, branches, hex_revs
1645 repo, repo, branches, hex_revs
1646 )
1646 )
1647 heads = (
1647 heads = (
1648 head_revs
1648 head_revs
1649 and pycompat.maplist(repo.lookup, head_revs)
1649 and pycompat.maplist(repo.lookup, head_revs)
1650 or head_revs
1650 or head_revs
1651 )
1651 )
1652 outgoing = discovery.findcommonoutgoing(
1652 outgoing = discovery.findcommonoutgoing(
1653 repo,
1653 repo,
1654 other,
1654 other,
1655 onlyheads=heads,
1655 onlyheads=heads,
1656 force=opts.get(b'force'),
1656 force=opts.get(b'force'),
1657 portable=True,
1657 portable=True,
1658 )
1658 )
1659 missing.update(outgoing.missing)
1659 missing.update(outgoing.missing)
1660 excluded.update(outgoing.excluded)
1660 excluded.update(outgoing.excluded)
1661
1661
1662 if not missing:
1662 if not missing:
1663 scmutil.nochangesfound(ui, repo, not base and excluded)
1663 scmutil.nochangesfound(ui, repo, not base and excluded)
1664 return 1
1664 return 1
1665
1665
1666 if heads:
1666 if heads:
1667 outgoing = discovery.outgoing(
1667 outgoing = discovery.outgoing(
1668 repo, missingroots=missing, ancestorsof=heads
1668 repo, missingroots=missing, ancestorsof=heads
1669 )
1669 )
1670 else:
1670 else:
1671 outgoing = discovery.outgoing(repo, missingroots=missing)
1671 outgoing = discovery.outgoing(repo, missingroots=missing)
1672 outgoing.excluded = sorted(excluded)
1672 outgoing.excluded = sorted(excluded)
1673
1673
1674 if cgversion == b'01': # bundle1
1674 if cgversion == b'01': # bundle1
1675 bversion = b'HG10' + bundlespec.wirecompression
1675 bversion = b'HG10' + bundlespec.wirecompression
1676 bcompression = None
1676 bcompression = None
1677 elif cgversion in (b'02', b'03'):
1677 elif cgversion in (b'02', b'03'):
1678 bversion = b'HG20'
1678 bversion = b'HG20'
1679 bcompression = bundlespec.wirecompression
1679 bcompression = bundlespec.wirecompression
1680 else:
1680 else:
1681 raise error.ProgrammingError(
1681 raise error.ProgrammingError(
1682 b'bundle: unexpected changegroup version %s' % cgversion
1682 b'bundle: unexpected changegroup version %s' % cgversion
1683 )
1683 )
1684
1684
1685 # TODO compression options should be derived from bundlespec parsing.
1685 # TODO compression options should be derived from bundlespec parsing.
1686 # This is a temporary hack to allow adjusting bundle compression
1686 # This is a temporary hack to allow adjusting bundle compression
1687 # level without a) formalizing the bundlespec changes to declare it
1687 # level without a) formalizing the bundlespec changes to declare it
1688 # b) introducing a command flag.
1688 # b) introducing a command flag.
1689 compopts = {}
1689 compopts = {}
1690 complevel = ui.configint(
1690 complevel = ui.configint(
1691 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1691 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1692 )
1692 )
1693 if complevel is None:
1693 if complevel is None:
1694 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1694 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1695 if complevel is not None:
1695 if complevel is not None:
1696 compopts[b'level'] = complevel
1696 compopts[b'level'] = complevel
1697
1697
1698 compthreads = ui.configint(
1698 compthreads = ui.configint(
1699 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1699 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1700 )
1700 )
1701 if compthreads is None:
1701 if compthreads is None:
1702 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1702 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1703 if compthreads is not None:
1703 if compthreads is not None:
1704 compopts[b'threads'] = compthreads
1704 compopts[b'threads'] = compthreads
1705
1705
1706 # Bundling of obsmarker and phases is optional as not all clients
1706 # Bundling of obsmarker and phases is optional as not all clients
1707 # support the necessary features.
1707 # support the necessary features.
1708 cfg = ui.configbool
1708 cfg = ui.configbool
1709 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1709 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1710 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1710 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1711 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1711 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1712 bundlespec.set_param(
1712 bundlespec.set_param(
1713 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1713 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1714 )
1714 )
1715 phases_cfg = cfg(b'experimental', b'bundle-phases')
1715 phases_cfg = cfg(b'experimental', b'bundle-phases')
1716 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1716 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1717
1717
1718 bundle2.writenewbundle(
1718 bundle2.writenewbundle(
1719 ui,
1719 ui,
1720 repo,
1720 repo,
1721 b'bundle',
1721 b'bundle',
1722 fname,
1722 fname,
1723 bversion,
1723 bversion,
1724 outgoing,
1724 outgoing,
1725 bundlespec.params,
1725 bundlespec.params,
1726 compression=bcompression,
1726 compression=bcompression,
1727 compopts=compopts,
1727 compopts=compopts,
1728 )
1728 )
1729
1729
1730
1730
1731 @command(
1731 @command(
1732 b'cat',
1732 b'cat',
1733 [
1733 [
1734 (
1734 (
1735 b'o',
1735 b'o',
1736 b'output',
1736 b'output',
1737 b'',
1737 b'',
1738 _(b'print output to file with formatted name'),
1738 _(b'print output to file with formatted name'),
1739 _(b'FORMAT'),
1739 _(b'FORMAT'),
1740 ),
1740 ),
1741 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1741 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1742 (b'', b'decode', None, _(b'apply any matching decode filter')),
1742 (b'', b'decode', None, _(b'apply any matching decode filter')),
1743 ]
1743 ]
1744 + walkopts
1744 + walkopts
1745 + formatteropts,
1745 + formatteropts,
1746 _(b'[OPTION]... FILE...'),
1746 _(b'[OPTION]... FILE...'),
1747 helpcategory=command.CATEGORY_FILE_CONTENTS,
1747 helpcategory=command.CATEGORY_FILE_CONTENTS,
1748 inferrepo=True,
1748 inferrepo=True,
1749 intents={INTENT_READONLY},
1749 intents={INTENT_READONLY},
1750 )
1750 )
1751 def cat(ui, repo, file1, *pats, **opts):
1751 def cat(ui, repo, file1, *pats, **opts):
1752 """output the current or given revision of files
1752 """output the current or given revision of files
1753
1753
1754 Print the specified files as they were at the given revision. If
1754 Print the specified files as they were at the given revision. If
1755 no revision is given, the parent of the working directory is used.
1755 no revision is given, the parent of the working directory is used.
1756
1756
1757 Output may be to a file, in which case the name of the file is
1757 Output may be to a file, in which case the name of the file is
1758 given using a template string. See :hg:`help templates`. In addition
1758 given using a template string. See :hg:`help templates`. In addition
1759 to the common template keywords, the following formatting rules are
1759 to the common template keywords, the following formatting rules are
1760 supported:
1760 supported:
1761
1761
1762 :``%%``: literal "%" character
1762 :``%%``: literal "%" character
1763 :``%s``: basename of file being printed
1763 :``%s``: basename of file being printed
1764 :``%d``: dirname of file being printed, or '.' if in repository root
1764 :``%d``: dirname of file being printed, or '.' if in repository root
1765 :``%p``: root-relative path name of file being printed
1765 :``%p``: root-relative path name of file being printed
1766 :``%H``: changeset hash (40 hexadecimal digits)
1766 :``%H``: changeset hash (40 hexadecimal digits)
1767 :``%R``: changeset revision number
1767 :``%R``: changeset revision number
1768 :``%h``: short-form changeset hash (12 hexadecimal digits)
1768 :``%h``: short-form changeset hash (12 hexadecimal digits)
1769 :``%r``: zero-padded changeset revision number
1769 :``%r``: zero-padded changeset revision number
1770 :``%b``: basename of the exporting repository
1770 :``%b``: basename of the exporting repository
1771 :``\\``: literal "\\" character
1771 :``\\``: literal "\\" character
1772
1772
1773 .. container:: verbose
1773 .. container:: verbose
1774
1774
1775 Template:
1775 Template:
1776
1776
1777 The following keywords are supported in addition to the common template
1777 The following keywords are supported in addition to the common template
1778 keywords and functions. See also :hg:`help templates`.
1778 keywords and functions. See also :hg:`help templates`.
1779
1779
1780 :data: String. File content.
1780 :data: String. File content.
1781 :path: String. Repository-absolute path of the file.
1781 :path: String. Repository-absolute path of the file.
1782
1782
1783 Returns 0 on success.
1783 Returns 0 on success.
1784 """
1784 """
1785 opts = pycompat.byteskwargs(opts)
1785 opts = pycompat.byteskwargs(opts)
1786 rev = opts.get(b'rev')
1786 rev = opts.get(b'rev')
1787 if rev:
1787 if rev:
1788 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1788 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1789 ctx = logcmdutil.revsingle(repo, rev)
1789 ctx = logcmdutil.revsingle(repo, rev)
1790 m = scmutil.match(ctx, (file1,) + pats, opts)
1790 m = scmutil.match(ctx, (file1,) + pats, opts)
1791 fntemplate = opts.pop(b'output', b'')
1791 fntemplate = opts.pop(b'output', b'')
1792 if cmdutil.isstdiofilename(fntemplate):
1792 if cmdutil.isstdiofilename(fntemplate):
1793 fntemplate = b''
1793 fntemplate = b''
1794
1794
1795 if fntemplate:
1795 if fntemplate:
1796 fm = formatter.nullformatter(ui, b'cat', opts)
1796 fm = formatter.nullformatter(ui, b'cat', opts)
1797 else:
1797 else:
1798 ui.pager(b'cat')
1798 ui.pager(b'cat')
1799 fm = ui.formatter(b'cat', opts)
1799 fm = ui.formatter(b'cat', opts)
1800 with fm:
1800 with fm:
1801 return cmdutil.cat(
1801 return cmdutil.cat(
1802 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1802 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1803 )
1803 )
1804
1804
1805
1805
1806 @command(
1806 @command(
1807 b'clone',
1807 b'clone',
1808 [
1808 [
1809 (
1809 (
1810 b'U',
1810 b'U',
1811 b'noupdate',
1811 b'noupdate',
1812 None,
1812 None,
1813 _(
1813 _(
1814 b'the clone will include an empty working '
1814 b'the clone will include an empty working '
1815 b'directory (only a repository)'
1815 b'directory (only a repository)'
1816 ),
1816 ),
1817 ),
1817 ),
1818 (
1818 (
1819 b'u',
1819 b'u',
1820 b'updaterev',
1820 b'updaterev',
1821 b'',
1821 b'',
1822 _(b'revision, tag, or branch to check out'),
1822 _(b'revision, tag, or branch to check out'),
1823 _(b'REV'),
1823 _(b'REV'),
1824 ),
1824 ),
1825 (
1825 (
1826 b'r',
1826 b'r',
1827 b'rev',
1827 b'rev',
1828 [],
1828 [],
1829 _(
1829 _(
1830 b'do not clone everything, but include this changeset'
1830 b'do not clone everything, but include this changeset'
1831 b' and its ancestors'
1831 b' and its ancestors'
1832 ),
1832 ),
1833 _(b'REV'),
1833 _(b'REV'),
1834 ),
1834 ),
1835 (
1835 (
1836 b'b',
1836 b'b',
1837 b'branch',
1837 b'branch',
1838 [],
1838 [],
1839 _(
1839 _(
1840 b'do not clone everything, but include this branch\'s'
1840 b'do not clone everything, but include this branch\'s'
1841 b' changesets and their ancestors'
1841 b' changesets and their ancestors'
1842 ),
1842 ),
1843 _(b'BRANCH'),
1843 _(b'BRANCH'),
1844 ),
1844 ),
1845 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1845 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1846 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1846 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1847 (b'', b'stream', None, _(b'clone with minimal data processing')),
1847 (b'', b'stream', None, _(b'clone with minimal data processing')),
1848 ]
1848 ]
1849 + remoteopts,
1849 + remoteopts,
1850 _(b'[OPTION]... SOURCE [DEST]'),
1850 _(b'[OPTION]... SOURCE [DEST]'),
1851 helpcategory=command.CATEGORY_REPO_CREATION,
1851 helpcategory=command.CATEGORY_REPO_CREATION,
1852 helpbasic=True,
1852 helpbasic=True,
1853 norepo=True,
1853 norepo=True,
1854 )
1854 )
1855 def clone(ui, source, dest=None, **opts):
1855 def clone(ui, source, dest=None, **opts):
1856 """make a copy of an existing repository
1856 """make a copy of an existing repository
1857
1857
1858 Create a copy of an existing repository in a new directory.
1858 Create a copy of an existing repository in a new directory.
1859
1859
1860 If no destination directory name is specified, it defaults to the
1860 If no destination directory name is specified, it defaults to the
1861 basename of the source.
1861 basename of the source.
1862
1862
1863 The location of the source is added to the new repository's
1863 The location of the source is added to the new repository's
1864 ``.hg/hgrc`` file, as the default to be used for future pulls.
1864 ``.hg/hgrc`` file, as the default to be used for future pulls.
1865
1865
1866 Only local paths and ``ssh://`` URLs are supported as
1866 Only local paths and ``ssh://`` URLs are supported as
1867 destinations. For ``ssh://`` destinations, no working directory or
1867 destinations. For ``ssh://`` destinations, no working directory or
1868 ``.hg/hgrc`` will be created on the remote side.
1868 ``.hg/hgrc`` will be created on the remote side.
1869
1869
1870 If the source repository has a bookmark called '@' set, that
1870 If the source repository has a bookmark called '@' set, that
1871 revision will be checked out in the new repository by default.
1871 revision will be checked out in the new repository by default.
1872
1872
1873 To check out a particular version, use -u/--update, or
1873 To check out a particular version, use -u/--update, or
1874 -U/--noupdate to create a clone with no working directory.
1874 -U/--noupdate to create a clone with no working directory.
1875
1875
1876 To pull only a subset of changesets, specify one or more revisions
1876 To pull only a subset of changesets, specify one or more revisions
1877 identifiers with -r/--rev or branches with -b/--branch. The
1877 identifiers with -r/--rev or branches with -b/--branch. The
1878 resulting clone will contain only the specified changesets and
1878 resulting clone will contain only the specified changesets and
1879 their ancestors. These options (or 'clone src#rev dest') imply
1879 their ancestors. These options (or 'clone src#rev dest') imply
1880 --pull, even for local source repositories.
1880 --pull, even for local source repositories.
1881
1881
1882 In normal clone mode, the remote normalizes repository data into a common
1882 In normal clone mode, the remote normalizes repository data into a common
1883 exchange format and the receiving end translates this data into its local
1883 exchange format and the receiving end translates this data into its local
1884 storage format. --stream activates a different clone mode that essentially
1884 storage format. --stream activates a different clone mode that essentially
1885 copies repository files from the remote with minimal data processing. This
1885 copies repository files from the remote with minimal data processing. This
1886 significantly reduces the CPU cost of a clone both remotely and locally.
1886 significantly reduces the CPU cost of a clone both remotely and locally.
1887 However, it often increases the transferred data size by 30-40%. This can
1887 However, it often increases the transferred data size by 30-40%. This can
1888 result in substantially faster clones where I/O throughput is plentiful,
1888 result in substantially faster clones where I/O throughput is plentiful,
1889 especially for larger repositories. A side-effect of --stream clones is
1889 especially for larger repositories. A side-effect of --stream clones is
1890 that storage settings and requirements on the remote are applied locally:
1890 that storage settings and requirements on the remote are applied locally:
1891 a modern client may inherit legacy or inefficient storage used by the
1891 a modern client may inherit legacy or inefficient storage used by the
1892 remote or a legacy Mercurial client may not be able to clone from a
1892 remote or a legacy Mercurial client may not be able to clone from a
1893 modern Mercurial remote.
1893 modern Mercurial remote.
1894
1894
1895 .. note::
1895 .. note::
1896
1896
1897 Specifying a tag will include the tagged changeset but not the
1897 Specifying a tag will include the tagged changeset but not the
1898 changeset containing the tag.
1898 changeset containing the tag.
1899
1899
1900 .. container:: verbose
1900 .. container:: verbose
1901
1901
1902 For efficiency, hardlinks are used for cloning whenever the
1902 For efficiency, hardlinks are used for cloning whenever the
1903 source and destination are on the same filesystem (note this
1903 source and destination are on the same filesystem (note this
1904 applies only to the repository data, not to the working
1904 applies only to the repository data, not to the working
1905 directory). Some filesystems, such as AFS, implement hardlinking
1905 directory). Some filesystems, such as AFS, implement hardlinking
1906 incorrectly, but do not report errors. In these cases, use the
1906 incorrectly, but do not report errors. In these cases, use the
1907 --pull option to avoid hardlinking.
1907 --pull option to avoid hardlinking.
1908
1908
1909 Mercurial will update the working directory to the first applicable
1909 Mercurial will update the working directory to the first applicable
1910 revision from this list:
1910 revision from this list:
1911
1911
1912 a) null if -U or the source repository has no changesets
1912 a) null if -U or the source repository has no changesets
1913 b) if -u . and the source repository is local, the first parent of
1913 b) if -u . and the source repository is local, the first parent of
1914 the source repository's working directory
1914 the source repository's working directory
1915 c) the changeset specified with -u (if a branch name, this means the
1915 c) the changeset specified with -u (if a branch name, this means the
1916 latest head of that branch)
1916 latest head of that branch)
1917 d) the changeset specified with -r
1917 d) the changeset specified with -r
1918 e) the tipmost head specified with -b
1918 e) the tipmost head specified with -b
1919 f) the tipmost head specified with the url#branch source syntax
1919 f) the tipmost head specified with the url#branch source syntax
1920 g) the revision marked with the '@' bookmark, if present
1920 g) the revision marked with the '@' bookmark, if present
1921 h) the tipmost head of the default branch
1921 h) the tipmost head of the default branch
1922 i) tip
1922 i) tip
1923
1923
1924 When cloning from servers that support it, Mercurial may fetch
1924 When cloning from servers that support it, Mercurial may fetch
1925 pre-generated data from a server-advertised URL or inline from the
1925 pre-generated data from a server-advertised URL or inline from the
1926 same stream. When this is done, hooks operating on incoming changesets
1926 same stream. When this is done, hooks operating on incoming changesets
1927 and changegroups may fire more than once, once for each pre-generated
1927 and changegroups may fire more than once, once for each pre-generated
1928 bundle and as well as for any additional remaining data. In addition,
1928 bundle and as well as for any additional remaining data. In addition,
1929 if an error occurs, the repository may be rolled back to a partial
1929 if an error occurs, the repository may be rolled back to a partial
1930 clone. This behavior may change in future releases.
1930 clone. This behavior may change in future releases.
1931 See :hg:`help -e clonebundles` for more.
1931 See :hg:`help -e clonebundles` for more.
1932
1932
1933 Examples:
1933 Examples:
1934
1934
1935 - clone a remote repository to a new directory named hg/::
1935 - clone a remote repository to a new directory named hg/::
1936
1936
1937 hg clone https://www.mercurial-scm.org/repo/hg/
1937 hg clone https://www.mercurial-scm.org/repo/hg/
1938
1938
1939 - create a lightweight local clone::
1939 - create a lightweight local clone::
1940
1940
1941 hg clone project/ project-feature/
1941 hg clone project/ project-feature/
1942
1942
1943 - clone from an absolute path on an ssh server (note double-slash)::
1943 - clone from an absolute path on an ssh server (note double-slash)::
1944
1944
1945 hg clone ssh://user@server//home/projects/alpha/
1945 hg clone ssh://user@server//home/projects/alpha/
1946
1946
1947 - do a streaming clone while checking out a specified version::
1947 - do a streaming clone while checking out a specified version::
1948
1948
1949 hg clone --stream http://server/repo -u 1.5
1949 hg clone --stream http://server/repo -u 1.5
1950
1950
1951 - create a repository without changesets after a particular revision::
1951 - create a repository without changesets after a particular revision::
1952
1952
1953 hg clone -r 04e544 experimental/ good/
1953 hg clone -r 04e544 experimental/ good/
1954
1954
1955 - clone (and track) a particular named branch::
1955 - clone (and track) a particular named branch::
1956
1956
1957 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1957 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1958
1958
1959 See :hg:`help urls` for details on specifying URLs.
1959 See :hg:`help urls` for details on specifying URLs.
1960
1960
1961 Returns 0 on success.
1961 Returns 0 on success.
1962 """
1962 """
1963 opts = pycompat.byteskwargs(opts)
1963 opts = pycompat.byteskwargs(opts)
1964 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1964 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1965
1965
1966 # --include/--exclude can come from narrow or sparse.
1966 # --include/--exclude can come from narrow or sparse.
1967 includepats, excludepats = None, None
1967 includepats, excludepats = None, None
1968
1968
1969 # hg.clone() differentiates between None and an empty set. So make sure
1969 # hg.clone() differentiates between None and an empty set. So make sure
1970 # patterns are sets if narrow is requested without patterns.
1970 # patterns are sets if narrow is requested without patterns.
1971 if opts.get(b'narrow'):
1971 if opts.get(b'narrow'):
1972 includepats = set()
1972 includepats = set()
1973 excludepats = set()
1973 excludepats = set()
1974
1974
1975 if opts.get(b'include'):
1975 if opts.get(b'include'):
1976 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1976 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1977 if opts.get(b'exclude'):
1977 if opts.get(b'exclude'):
1978 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1978 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1979
1979
1980 r = hg.clone(
1980 r = hg.clone(
1981 ui,
1981 ui,
1982 opts,
1982 opts,
1983 source,
1983 source,
1984 dest,
1984 dest,
1985 pull=opts.get(b'pull'),
1985 pull=opts.get(b'pull'),
1986 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1986 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1987 revs=opts.get(b'rev'),
1987 revs=opts.get(b'rev'),
1988 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1988 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1989 branch=opts.get(b'branch'),
1989 branch=opts.get(b'branch'),
1990 shareopts=opts.get(b'shareopts'),
1990 shareopts=opts.get(b'shareopts'),
1991 storeincludepats=includepats,
1991 storeincludepats=includepats,
1992 storeexcludepats=excludepats,
1992 storeexcludepats=excludepats,
1993 depth=opts.get(b'depth') or None,
1993 depth=opts.get(b'depth') or None,
1994 )
1994 )
1995
1995
1996 return r is None
1996 return r is None
1997
1997
1998
1998
1999 @command(
1999 @command(
2000 b'commit|ci',
2000 b'commit|ci',
2001 [
2001 [
2002 (
2002 (
2003 b'A',
2003 b'A',
2004 b'addremove',
2004 b'addremove',
2005 None,
2005 None,
2006 _(b'mark new/missing files as added/removed before committing'),
2006 _(b'mark new/missing files as added/removed before committing'),
2007 ),
2007 ),
2008 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2008 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2009 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2009 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2010 (b's', b'secret', None, _(b'use the secret phase for committing')),
2010 (b's', b'secret', None, _(b'use the secret phase for committing')),
2011 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2011 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2012 (
2012 (
2013 b'',
2013 b'',
2014 b'force-close-branch',
2014 b'force-close-branch',
2015 None,
2015 None,
2016 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2016 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2017 ),
2017 ),
2018 (b'i', b'interactive', None, _(b'use interactive mode')),
2018 (b'i', b'interactive', None, _(b'use interactive mode')),
2019 ]
2019 ]
2020 + walkopts
2020 + walkopts
2021 + commitopts
2021 + commitopts
2022 + commitopts2
2022 + commitopts2
2023 + subrepoopts,
2023 + subrepoopts,
2024 _(b'[OPTION]... [FILE]...'),
2024 _(b'[OPTION]... [FILE]...'),
2025 helpcategory=command.CATEGORY_COMMITTING,
2025 helpcategory=command.CATEGORY_COMMITTING,
2026 helpbasic=True,
2026 helpbasic=True,
2027 inferrepo=True,
2027 inferrepo=True,
2028 )
2028 )
2029 def commit(ui, repo, *pats, **opts):
2029 def commit(ui, repo, *pats, **opts):
2030 """commit the specified files or all outstanding changes
2030 """commit the specified files or all outstanding changes
2031
2031
2032 Commit changes to the given files into the repository. Unlike a
2032 Commit changes to the given files into the repository. Unlike a
2033 centralized SCM, this operation is a local operation. See
2033 centralized SCM, this operation is a local operation. See
2034 :hg:`push` for a way to actively distribute your changes.
2034 :hg:`push` for a way to actively distribute your changes.
2035
2035
2036 If a list of files is omitted, all changes reported by :hg:`status`
2036 If a list of files is omitted, all changes reported by :hg:`status`
2037 will be committed.
2037 will be committed.
2038
2038
2039 If you are committing the result of a merge, do not provide any
2039 If you are committing the result of a merge, do not provide any
2040 filenames or -I/-X filters.
2040 filenames or -I/-X filters.
2041
2041
2042 If no commit message is specified, Mercurial starts your
2042 If no commit message is specified, Mercurial starts your
2043 configured editor where you can enter a message. In case your
2043 configured editor where you can enter a message. In case your
2044 commit fails, you will find a backup of your message in
2044 commit fails, you will find a backup of your message in
2045 ``.hg/last-message.txt``.
2045 ``.hg/last-message.txt``.
2046
2046
2047 The --close-branch flag can be used to mark the current branch
2047 The --close-branch flag can be used to mark the current branch
2048 head closed. When all heads of a branch are closed, the branch
2048 head closed. When all heads of a branch are closed, the branch
2049 will be considered closed and no longer listed.
2049 will be considered closed and no longer listed.
2050
2050
2051 The --amend flag can be used to amend the parent of the
2051 The --amend flag can be used to amend the parent of the
2052 working directory with a new commit that contains the changes
2052 working directory with a new commit that contains the changes
2053 in the parent in addition to those currently reported by :hg:`status`,
2053 in the parent in addition to those currently reported by :hg:`status`,
2054 if there are any. The old commit is stored in a backup bundle in
2054 if there are any. The old commit is stored in a backup bundle in
2055 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2055 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2056 on how to restore it).
2056 on how to restore it).
2057
2057
2058 Message, user and date are taken from the amended commit unless
2058 Message, user and date are taken from the amended commit unless
2059 specified. When a message isn't specified on the command line,
2059 specified. When a message isn't specified on the command line,
2060 the editor will open with the message of the amended commit.
2060 the editor will open with the message of the amended commit.
2061
2061
2062 It is not possible to amend public changesets (see :hg:`help phases`)
2062 It is not possible to amend public changesets (see :hg:`help phases`)
2063 or changesets that have children.
2063 or changesets that have children.
2064
2064
2065 See :hg:`help dates` for a list of formats valid for -d/--date.
2065 See :hg:`help dates` for a list of formats valid for -d/--date.
2066
2066
2067 Returns 0 on success, 1 if nothing changed.
2067 Returns 0 on success, 1 if nothing changed.
2068
2068
2069 .. container:: verbose
2069 .. container:: verbose
2070
2070
2071 Examples:
2071 Examples:
2072
2072
2073 - commit all files ending in .py::
2073 - commit all files ending in .py::
2074
2074
2075 hg commit --include "set:**.py"
2075 hg commit --include "set:**.py"
2076
2076
2077 - commit all non-binary files::
2077 - commit all non-binary files::
2078
2078
2079 hg commit --exclude "set:binary()"
2079 hg commit --exclude "set:binary()"
2080
2080
2081 - amend the current commit and set the date to now::
2081 - amend the current commit and set the date to now::
2082
2082
2083 hg commit --amend --date now
2083 hg commit --amend --date now
2084 """
2084 """
2085 with repo.wlock(), repo.lock():
2085 with repo.wlock(), repo.lock():
2086 return _docommit(ui, repo, *pats, **opts)
2086 return _docommit(ui, repo, *pats, **opts)
2087
2087
2088
2088
2089 def _docommit(ui, repo, *pats, **opts):
2089 def _docommit(ui, repo, *pats, **opts):
2090 if opts.get('interactive'):
2090 if opts.get('interactive'):
2091 opts.pop('interactive')
2091 opts.pop('interactive')
2092 ret = cmdutil.dorecord(
2092 ret = cmdutil.dorecord(
2093 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2093 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2094 )
2094 )
2095 # ret can be 0 (no changes to record) or the value returned by
2095 # ret can be 0 (no changes to record) or the value returned by
2096 # commit(), 1 if nothing changed or None on success.
2096 # commit(), 1 if nothing changed or None on success.
2097 return 1 if ret == 0 else ret
2097 return 1 if ret == 0 else ret
2098
2098
2099 if opts.get('subrepos'):
2099 if opts.get('subrepos'):
2100 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2100 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2101 # Let --subrepos on the command line override config setting.
2101 # Let --subrepos on the command line override config setting.
2102 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2102 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2103
2103
2104 cmdutil.checkunfinished(repo, commit=True)
2104 cmdutil.checkunfinished(repo, commit=True)
2105
2105
2106 branch = repo[None].branch()
2106 branch = repo[None].branch()
2107 bheads = repo.branchheads(branch)
2107 bheads = repo.branchheads(branch)
2108 tip = repo.changelog.tip()
2108 tip = repo.changelog.tip()
2109
2109
2110 extra = {}
2110 extra = {}
2111 if opts.get('close_branch') or opts.get('force_close_branch'):
2111 if opts.get('close_branch') or opts.get('force_close_branch'):
2112 extra[b'close'] = b'1'
2112 extra[b'close'] = b'1'
2113
2113
2114 if repo[b'.'].closesbranch():
2114 if repo[b'.'].closesbranch():
2115 # Not ideal, but let us do an extra status early to prevent early
2115 # Not ideal, but let us do an extra status early to prevent early
2116 # bail out.
2116 # bail out.
2117 matcher = scmutil.match(repo[None], pats, opts)
2117 matcher = scmutil.match(repo[None], pats, opts)
2118 s = repo.status(match=matcher)
2118 s = repo.status(match=matcher)
2119 if s.modified or s.added or s.removed:
2119 if s.modified or s.added or s.removed:
2120 bheads = repo.branchheads(branch, closed=True)
2120 bheads = repo.branchheads(branch, closed=True)
2121 else:
2121 else:
2122 msg = _(b'current revision is already a branch closing head')
2122 msg = _(b'current revision is already a branch closing head')
2123 raise error.InputError(msg)
2123 raise error.InputError(msg)
2124
2124
2125 if not bheads:
2125 if not bheads:
2126 raise error.InputError(
2126 raise error.InputError(
2127 _(b'branch "%s" has no heads to close') % branch
2127 _(b'branch "%s" has no heads to close') % branch
2128 )
2128 )
2129 elif (
2129 elif (
2130 branch == repo[b'.'].branch()
2130 branch == repo[b'.'].branch()
2131 and repo[b'.'].node() not in bheads
2131 and repo[b'.'].node() not in bheads
2132 and not opts.get('force_close_branch')
2132 and not opts.get('force_close_branch')
2133 ):
2133 ):
2134 hint = _(
2134 hint = _(
2135 b'use --force-close-branch to close branch from a non-head'
2135 b'use --force-close-branch to close branch from a non-head'
2136 b' changeset'
2136 b' changeset'
2137 )
2137 )
2138 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2138 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2139 elif opts.get('amend'):
2139 elif opts.get('amend'):
2140 if (
2140 if (
2141 repo[b'.'].p1().branch() != branch
2141 repo[b'.'].p1().branch() != branch
2142 and repo[b'.'].p2().branch() != branch
2142 and repo[b'.'].p2().branch() != branch
2143 ):
2143 ):
2144 raise error.InputError(_(b'can only close branch heads'))
2144 raise error.InputError(_(b'can only close branch heads'))
2145
2145
2146 if opts.get('amend'):
2146 if opts.get('amend'):
2147 if ui.configbool(b'ui', b'commitsubrepos'):
2147 if ui.configbool(b'ui', b'commitsubrepos'):
2148 raise error.InputError(
2148 raise error.InputError(
2149 _(b'cannot amend with ui.commitsubrepos enabled')
2149 _(b'cannot amend with ui.commitsubrepos enabled')
2150 )
2150 )
2151
2151
2152 old = repo[b'.']
2152 old = repo[b'.']
2153 rewriteutil.precheck(repo, [old.rev()], b'amend')
2153 rewriteutil.precheck(repo, [old.rev()], b'amend')
2154
2154
2155 # Currently histedit gets confused if an amend happens while histedit
2155 # Currently histedit gets confused if an amend happens while histedit
2156 # is in progress. Since we have a checkunfinished command, we are
2156 # is in progress. Since we have a checkunfinished command, we are
2157 # temporarily honoring it.
2157 # temporarily honoring it.
2158 #
2158 #
2159 # Note: eventually this guard will be removed. Please do not expect
2159 # Note: eventually this guard will be removed. Please do not expect
2160 # this behavior to remain.
2160 # this behavior to remain.
2161 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2161 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2162 cmdutil.checkunfinished(repo)
2162 cmdutil.checkunfinished(repo)
2163
2163
2164 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2164 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2165 opts = pycompat.byteskwargs(opts)
2165 opts = pycompat.byteskwargs(opts)
2166 if node == old.node():
2166 if node == old.node():
2167 ui.status(_(b"nothing changed\n"))
2167 ui.status(_(b"nothing changed\n"))
2168 return 1
2168 return 1
2169 else:
2169 else:
2170
2170
2171 def commitfunc(ui, repo, message, match, opts):
2171 def commitfunc(ui, repo, message, match, opts):
2172 overrides = {}
2172 overrides = {}
2173 if opts.get(b'secret'):
2173 if opts.get(b'secret'):
2174 overrides[(b'phases', b'new-commit')] = b'secret'
2174 overrides[(b'phases', b'new-commit')] = b'secret'
2175
2175
2176 baseui = repo.baseui
2176 baseui = repo.baseui
2177 with baseui.configoverride(overrides, b'commit'):
2177 with baseui.configoverride(overrides, b'commit'):
2178 with ui.configoverride(overrides, b'commit'):
2178 with ui.configoverride(overrides, b'commit'):
2179 editform = cmdutil.mergeeditform(
2179 editform = cmdutil.mergeeditform(
2180 repo[None], b'commit.normal'
2180 repo[None], b'commit.normal'
2181 )
2181 )
2182 editor = cmdutil.getcommiteditor(
2182 editor = cmdutil.getcommiteditor(
2183 editform=editform, **pycompat.strkwargs(opts)
2183 editform=editform, **pycompat.strkwargs(opts)
2184 )
2184 )
2185 return repo.commit(
2185 return repo.commit(
2186 message,
2186 message,
2187 opts.get(b'user'),
2187 opts.get(b'user'),
2188 opts.get(b'date'),
2188 opts.get(b'date'),
2189 match,
2189 match,
2190 editor=editor,
2190 editor=editor,
2191 extra=extra,
2191 extra=extra,
2192 )
2192 )
2193
2193
2194 opts = pycompat.byteskwargs(opts)
2194 opts = pycompat.byteskwargs(opts)
2195 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2195 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2196
2196
2197 if not node:
2197 if not node:
2198 stat = cmdutil.postcommitstatus(repo, pats, opts)
2198 stat = cmdutil.postcommitstatus(repo, pats, opts)
2199 if stat.deleted:
2199 if stat.deleted:
2200 ui.status(
2200 ui.status(
2201 _(
2201 _(
2202 b"nothing changed (%d missing files, see "
2202 b"nothing changed (%d missing files, see "
2203 b"'hg status')\n"
2203 b"'hg status')\n"
2204 )
2204 )
2205 % len(stat.deleted)
2205 % len(stat.deleted)
2206 )
2206 )
2207 else:
2207 else:
2208 ui.status(_(b"nothing changed\n"))
2208 ui.status(_(b"nothing changed\n"))
2209 return 1
2209 return 1
2210
2210
2211 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2211 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2212
2212
2213 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2213 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2214 status(
2214 status(
2215 ui,
2215 ui,
2216 repo,
2216 repo,
2217 modified=True,
2217 modified=True,
2218 added=True,
2218 added=True,
2219 removed=True,
2219 removed=True,
2220 deleted=True,
2220 deleted=True,
2221 unknown=True,
2221 unknown=True,
2222 subrepos=opts.get(b'subrepos'),
2222 subrepos=opts.get(b'subrepos'),
2223 )
2223 )
2224
2224
2225
2225
2226 @command(
2226 @command(
2227 b'config|showconfig|debugconfig',
2227 b'config|showconfig|debugconfig',
2228 [
2228 [
2229 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2229 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2230 # This is experimental because we need
2230 # This is experimental because we need
2231 # * reasonable behavior around aliases,
2231 # * reasonable behavior around aliases,
2232 # * decide if we display [debug] [experimental] and [devel] section par
2232 # * decide if we display [debug] [experimental] and [devel] section par
2233 # default
2233 # default
2234 # * some way to display "generic" config entry (the one matching
2234 # * some way to display "generic" config entry (the one matching
2235 # regexp,
2235 # regexp,
2236 # * proper display of the different value type
2236 # * proper display of the different value type
2237 # * a better way to handle <DYNAMIC> values (and variable types),
2237 # * a better way to handle <DYNAMIC> values (and variable types),
2238 # * maybe some type information ?
2238 # * maybe some type information ?
2239 (
2239 (
2240 b'',
2240 b'',
2241 b'exp-all-known',
2241 b'exp-all-known',
2242 None,
2242 None,
2243 _(b'show all known config option (EXPERIMENTAL)'),
2243 _(b'show all known config option (EXPERIMENTAL)'),
2244 ),
2244 ),
2245 (b'e', b'edit', None, _(b'edit user config')),
2245 (b'e', b'edit', None, _(b'edit user config')),
2246 (b'l', b'local', None, _(b'edit repository config')),
2246 (b'l', b'local', None, _(b'edit repository config')),
2247 (b'', b'source', None, _(b'show source of configuration value')),
2247 (b'', b'source', None, _(b'show source of configuration value')),
2248 (
2248 (
2249 b'',
2249 b'',
2250 b'shared',
2250 b'shared',
2251 None,
2251 None,
2252 _(b'edit shared source repository config (EXPERIMENTAL)'),
2252 _(b'edit shared source repository config (EXPERIMENTAL)'),
2253 ),
2253 ),
2254 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2254 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2255 (b'g', b'global', None, _(b'edit global config')),
2255 (b'g', b'global', None, _(b'edit global config')),
2256 ]
2256 ]
2257 + formatteropts,
2257 + formatteropts,
2258 _(b'[-u] [NAME]...'),
2258 _(b'[-u] [NAME]...'),
2259 helpcategory=command.CATEGORY_HELP,
2259 helpcategory=command.CATEGORY_HELP,
2260 optionalrepo=True,
2260 optionalrepo=True,
2261 intents={INTENT_READONLY},
2261 intents={INTENT_READONLY},
2262 )
2262 )
2263 def config(ui, repo, *values, **opts):
2263 def config(ui, repo, *values, **opts):
2264 """show combined config settings from all hgrc files
2264 """show combined config settings from all hgrc files
2265
2265
2266 With no arguments, print names and values of all config items.
2266 With no arguments, print names and values of all config items.
2267
2267
2268 With one argument of the form section.name, print just the value
2268 With one argument of the form section.name, print just the value
2269 of that config item.
2269 of that config item.
2270
2270
2271 With multiple arguments, print names and values of all config
2271 With multiple arguments, print names and values of all config
2272 items with matching section names or section.names.
2272 items with matching section names or section.names.
2273
2273
2274 With --edit, start an editor on the user-level config file. With
2274 With --edit, start an editor on the user-level config file. With
2275 --global, edit the system-wide config file. With --local, edit the
2275 --global, edit the system-wide config file. With --local, edit the
2276 repository-level config file.
2276 repository-level config file.
2277
2277
2278 With --source, the source (filename and line number) is printed
2278 With --source, the source (filename and line number) is printed
2279 for each config item.
2279 for each config item.
2280
2280
2281 See :hg:`help config` for more information about config files.
2281 See :hg:`help config` for more information about config files.
2282
2282
2283 .. container:: verbose
2283 .. container:: verbose
2284
2284
2285 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2285 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2286 This file is not shared across shares when in share-safe mode.
2286 This file is not shared across shares when in share-safe mode.
2287
2287
2288 Template:
2288 Template:
2289
2289
2290 The following keywords are supported. See also :hg:`help templates`.
2290 The following keywords are supported. See also :hg:`help templates`.
2291
2291
2292 :name: String. Config name.
2292 :name: String. Config name.
2293 :source: String. Filename and line number where the item is defined.
2293 :source: String. Filename and line number where the item is defined.
2294 :value: String. Config value.
2294 :value: String. Config value.
2295
2295
2296 The --shared flag can be used to edit the config file of shared source
2296 The --shared flag can be used to edit the config file of shared source
2297 repository. It only works when you have shared using the experimental
2297 repository. It only works when you have shared using the experimental
2298 share safe feature.
2298 share safe feature.
2299
2299
2300 Returns 0 on success, 1 if NAME does not exist.
2300 Returns 0 on success, 1 if NAME does not exist.
2301
2301
2302 """
2302 """
2303
2303
2304 opts = pycompat.byteskwargs(opts)
2304 opts = pycompat.byteskwargs(opts)
2305 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2305 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2306 if any(opts.get(o) for o in editopts):
2306 if any(opts.get(o) for o in editopts):
2307 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2307 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2308 if opts.get(b'local'):
2308 if opts.get(b'local'):
2309 if not repo:
2309 if not repo:
2310 raise error.InputError(
2310 raise error.InputError(
2311 _(b"can't use --local outside a repository")
2311 _(b"can't use --local outside a repository")
2312 )
2312 )
2313 paths = [repo.vfs.join(b'hgrc')]
2313 paths = [repo.vfs.join(b'hgrc')]
2314 elif opts.get(b'global'):
2314 elif opts.get(b'global'):
2315 paths = rcutil.systemrcpath()
2315 paths = rcutil.systemrcpath()
2316 elif opts.get(b'shared'):
2316 elif opts.get(b'shared'):
2317 if not repo.shared():
2317 if not repo.shared():
2318 raise error.InputError(
2318 raise error.InputError(
2319 _(b"repository is not shared; can't use --shared")
2319 _(b"repository is not shared; can't use --shared")
2320 )
2320 )
2321 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2321 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2322 raise error.InputError(
2322 raise error.InputError(
2323 _(
2323 _(
2324 b"share safe feature not enabled; "
2324 b"share safe feature not enabled; "
2325 b"unable to edit shared source repository config"
2325 b"unable to edit shared source repository config"
2326 )
2326 )
2327 )
2327 )
2328 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2328 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2329 elif opts.get(b'non_shared'):
2329 elif opts.get(b'non_shared'):
2330 paths = [repo.vfs.join(b'hgrc-not-shared')]
2330 paths = [repo.vfs.join(b'hgrc-not-shared')]
2331 else:
2331 else:
2332 paths = rcutil.userrcpath()
2332 paths = rcutil.userrcpath()
2333
2333
2334 for f in paths:
2334 for f in paths:
2335 if os.path.exists(f):
2335 if os.path.exists(f):
2336 break
2336 break
2337 else:
2337 else:
2338 if opts.get(b'global'):
2338 if opts.get(b'global'):
2339 samplehgrc = uimod.samplehgrcs[b'global']
2339 samplehgrc = uimod.samplehgrcs[b'global']
2340 elif opts.get(b'local'):
2340 elif opts.get(b'local'):
2341 samplehgrc = uimod.samplehgrcs[b'local']
2341 samplehgrc = uimod.samplehgrcs[b'local']
2342 else:
2342 else:
2343 samplehgrc = uimod.samplehgrcs[b'user']
2343 samplehgrc = uimod.samplehgrcs[b'user']
2344
2344
2345 f = paths[0]
2345 f = paths[0]
2346 fp = open(f, b"wb")
2346 fp = open(f, b"wb")
2347 fp.write(util.tonativeeol(samplehgrc))
2347 fp.write(util.tonativeeol(samplehgrc))
2348 fp.close()
2348 fp.close()
2349
2349
2350 editor = ui.geteditor()
2350 editor = ui.geteditor()
2351 ui.system(
2351 ui.system(
2352 b"%s \"%s\"" % (editor, f),
2352 b"%s \"%s\"" % (editor, f),
2353 onerr=error.InputError,
2353 onerr=error.InputError,
2354 errprefix=_(b"edit failed"),
2354 errprefix=_(b"edit failed"),
2355 blockedtag=b'config_edit',
2355 blockedtag=b'config_edit',
2356 )
2356 )
2357 return
2357 return
2358 ui.pager(b'config')
2358 ui.pager(b'config')
2359 fm = ui.formatter(b'config', opts)
2359 fm = ui.formatter(b'config', opts)
2360 for t, f in rcutil.rccomponents():
2360 for t, f in rcutil.rccomponents():
2361 if t == b'path':
2361 if t == b'path':
2362 ui.debug(b'read config from: %s\n' % f)
2362 ui.debug(b'read config from: %s\n' % f)
2363 elif t == b'resource':
2363 elif t == b'resource':
2364 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2364 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2365 elif t == b'items':
2365 elif t == b'items':
2366 # Don't print anything for 'items'.
2366 # Don't print anything for 'items'.
2367 pass
2367 pass
2368 else:
2368 else:
2369 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2369 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2370 untrusted = bool(opts.get(b'untrusted'))
2370 untrusted = bool(opts.get(b'untrusted'))
2371
2371
2372 selsections = selentries = []
2372 selsections = selentries = []
2373 if values:
2373 if values:
2374 selsections = [v for v in values if b'.' not in v]
2374 selsections = [v for v in values if b'.' not in v]
2375 selentries = [v for v in values if b'.' in v]
2375 selentries = [v for v in values if b'.' in v]
2376 uniquesel = len(selentries) == 1 and not selsections
2376 uniquesel = len(selentries) == 1 and not selsections
2377 selsections = set(selsections)
2377 selsections = set(selsections)
2378 selentries = set(selentries)
2378 selentries = set(selentries)
2379
2379
2380 matched = False
2380 matched = False
2381 all_known = opts[b'exp_all_known']
2381 all_known = opts[b'exp_all_known']
2382 show_source = ui.debugflag or opts.get(b'source')
2382 show_source = ui.debugflag or opts.get(b'source')
2383 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2383 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2384 for section, name, value in entries:
2384 for section, name, value in entries:
2385 source = ui.configsource(section, name, untrusted)
2385 source = ui.configsource(section, name, untrusted)
2386 value = pycompat.bytestr(value)
2386 value = pycompat.bytestr(value)
2387 defaultvalue = ui.configdefault(section, name)
2387 defaultvalue = ui.configdefault(section, name)
2388 if fm.isplain():
2388 if fm.isplain():
2389 source = source or b'none'
2389 source = source or b'none'
2390 value = value.replace(b'\n', b'\\n')
2390 value = value.replace(b'\n', b'\\n')
2391 entryname = section + b'.' + name
2391 entryname = section + b'.' + name
2392 if values and not (section in selsections or entryname in selentries):
2392 if values and not (section in selsections or entryname in selentries):
2393 continue
2393 continue
2394 fm.startitem()
2394 fm.startitem()
2395 fm.condwrite(show_source, b'source', b'%s: ', source)
2395 fm.condwrite(show_source, b'source', b'%s: ', source)
2396 if uniquesel:
2396 if uniquesel:
2397 fm.data(name=entryname)
2397 fm.data(name=entryname)
2398 fm.write(b'value', b'%s\n', value)
2398 fm.write(b'value', b'%s\n', value)
2399 else:
2399 else:
2400 fm.write(b'name value', b'%s=%s\n', entryname, value)
2400 fm.write(b'name value', b'%s=%s\n', entryname, value)
2401 if formatter.isprintable(defaultvalue):
2401 if formatter.isprintable(defaultvalue):
2402 fm.data(defaultvalue=defaultvalue)
2402 fm.data(defaultvalue=defaultvalue)
2403 elif isinstance(defaultvalue, list) and all(
2403 elif isinstance(defaultvalue, list) and all(
2404 formatter.isprintable(e) for e in defaultvalue
2404 formatter.isprintable(e) for e in defaultvalue
2405 ):
2405 ):
2406 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2406 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2407 # TODO: no idea how to process unsupported defaultvalue types
2407 # TODO: no idea how to process unsupported defaultvalue types
2408 matched = True
2408 matched = True
2409 fm.end()
2409 fm.end()
2410 if matched:
2410 if matched:
2411 return 0
2411 return 0
2412 return 1
2412 return 1
2413
2413
2414
2414
2415 @command(
2415 @command(
2416 b'continue',
2416 b'continue',
2417 dryrunopts,
2417 dryrunopts,
2418 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2418 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2419 helpbasic=True,
2419 helpbasic=True,
2420 )
2420 )
2421 def continuecmd(ui, repo, **opts):
2421 def continuecmd(ui, repo, **opts):
2422 """resumes an interrupted operation (EXPERIMENTAL)
2422 """resumes an interrupted operation (EXPERIMENTAL)
2423
2423
2424 Finishes a multistep operation like graft, histedit, rebase, merge,
2424 Finishes a multistep operation like graft, histedit, rebase, merge,
2425 and unshelve if they are in an interrupted state.
2425 and unshelve if they are in an interrupted state.
2426
2426
2427 use --dry-run/-n to dry run the command.
2427 use --dry-run/-n to dry run the command.
2428 """
2428 """
2429 dryrun = opts.get('dry_run')
2429 dryrun = opts.get('dry_run')
2430 contstate = cmdutil.getunfinishedstate(repo)
2430 contstate = cmdutil.getunfinishedstate(repo)
2431 if not contstate:
2431 if not contstate:
2432 raise error.StateError(_(b'no operation in progress'))
2432 raise error.StateError(_(b'no operation in progress'))
2433 if not contstate.continuefunc:
2433 if not contstate.continuefunc:
2434 raise error.StateError(
2434 raise error.StateError(
2435 (
2435 (
2436 _(b"%s in progress but does not support 'hg continue'")
2436 _(b"%s in progress but does not support 'hg continue'")
2437 % (contstate._opname)
2437 % (contstate._opname)
2438 ),
2438 ),
2439 hint=contstate.continuemsg(),
2439 hint=contstate.continuemsg(),
2440 )
2440 )
2441 if dryrun:
2441 if dryrun:
2442 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2442 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2443 return
2443 return
2444 return contstate.continuefunc(ui, repo)
2444 return contstate.continuefunc(ui, repo)
2445
2445
2446
2446
2447 @command(
2447 @command(
2448 b'copy|cp',
2448 b'copy|cp',
2449 [
2449 [
2450 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2450 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2451 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2451 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2452 (
2452 (
2453 b'',
2453 b'',
2454 b'at-rev',
2454 b'at-rev',
2455 b'',
2455 b'',
2456 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2456 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2457 _(b'REV'),
2457 _(b'REV'),
2458 ),
2458 ),
2459 (
2459 (
2460 b'f',
2460 b'f',
2461 b'force',
2461 b'force',
2462 None,
2462 None,
2463 _(b'forcibly copy over an existing managed file'),
2463 _(b'forcibly copy over an existing managed file'),
2464 ),
2464 ),
2465 ]
2465 ]
2466 + walkopts
2466 + walkopts
2467 + dryrunopts,
2467 + dryrunopts,
2468 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2468 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2469 helpcategory=command.CATEGORY_FILE_CONTENTS,
2469 helpcategory=command.CATEGORY_FILE_CONTENTS,
2470 )
2470 )
2471 def copy(ui, repo, *pats, **opts):
2471 def copy(ui, repo, *pats, **opts):
2472 """mark files as copied for the next commit
2472 """mark files as copied for the next commit
2473
2473
2474 Mark dest as having copies of source files. If dest is a
2474 Mark dest as having copies of source files. If dest is a
2475 directory, copies are put in that directory. If dest is a file,
2475 directory, copies are put in that directory. If dest is a file,
2476 the source must be a single file.
2476 the source must be a single file.
2477
2477
2478 By default, this command copies the contents of files as they
2478 By default, this command copies the contents of files as they
2479 exist in the working directory. If invoked with -A/--after, the
2479 exist in the working directory. If invoked with -A/--after, the
2480 operation is recorded, but no copying is performed.
2480 operation is recorded, but no copying is performed.
2481
2481
2482 To undo marking a destination file as copied, use --forget. With that
2482 To undo marking a destination file as copied, use --forget. With that
2483 option, all given (positional) arguments are unmarked as copies. The
2483 option, all given (positional) arguments are unmarked as copies. The
2484 destination file(s) will be left in place (still tracked). Note that
2484 destination file(s) will be left in place (still tracked). Note that
2485 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2485 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2486
2486
2487 This command takes effect with the next commit by default.
2487 This command takes effect with the next commit by default.
2488
2488
2489 Returns 0 on success, 1 if errors are encountered.
2489 Returns 0 on success, 1 if errors are encountered.
2490 """
2490 """
2491 opts = pycompat.byteskwargs(opts)
2491 opts = pycompat.byteskwargs(opts)
2492 with repo.wlock():
2492 with repo.wlock():
2493 return cmdutil.copy(ui, repo, pats, opts)
2493 return cmdutil.copy(ui, repo, pats, opts)
2494
2494
2495
2495
2496 @command(
2496 @command(
2497 b'debugcommands',
2497 b'debugcommands',
2498 [],
2498 [],
2499 _(b'[COMMAND]'),
2499 _(b'[COMMAND]'),
2500 helpcategory=command.CATEGORY_HELP,
2500 helpcategory=command.CATEGORY_HELP,
2501 norepo=True,
2501 norepo=True,
2502 )
2502 )
2503 def debugcommands(ui, cmd=b'', *args):
2503 def debugcommands(ui, cmd=b'', *args):
2504 """list all available commands and options"""
2504 """list all available commands and options"""
2505 for cmd, vals in sorted(table.items()):
2505 for cmd, vals in sorted(table.items()):
2506 cmd = cmd.split(b'|')[0]
2506 cmd = cmd.split(b'|')[0]
2507 opts = b', '.join([i[1] for i in vals[1]])
2507 opts = b', '.join([i[1] for i in vals[1]])
2508 ui.write(b'%s: %s\n' % (cmd, opts))
2508 ui.write(b'%s: %s\n' % (cmd, opts))
2509
2509
2510
2510
2511 @command(
2511 @command(
2512 b'debugcomplete',
2512 b'debugcomplete',
2513 [(b'o', b'options', None, _(b'show the command options'))],
2513 [(b'o', b'options', None, _(b'show the command options'))],
2514 _(b'[-o] CMD'),
2514 _(b'[-o] CMD'),
2515 helpcategory=command.CATEGORY_HELP,
2515 helpcategory=command.CATEGORY_HELP,
2516 norepo=True,
2516 norepo=True,
2517 )
2517 )
2518 def debugcomplete(ui, cmd=b'', **opts):
2518 def debugcomplete(ui, cmd=b'', **opts):
2519 """returns the completion list associated with the given command"""
2519 """returns the completion list associated with the given command"""
2520
2520
2521 if opts.get('options'):
2521 if opts.get('options'):
2522 options = []
2522 options = []
2523 otables = [globalopts]
2523 otables = [globalopts]
2524 if cmd:
2524 if cmd:
2525 aliases, entry = cmdutil.findcmd(cmd, table, False)
2525 aliases, entry = cmdutil.findcmd(cmd, table, False)
2526 otables.append(entry[1])
2526 otables.append(entry[1])
2527 for t in otables:
2527 for t in otables:
2528 for o in t:
2528 for o in t:
2529 if b"(DEPRECATED)" in o[3]:
2529 if b"(DEPRECATED)" in o[3]:
2530 continue
2530 continue
2531 if o[0]:
2531 if o[0]:
2532 options.append(b'-%s' % o[0])
2532 options.append(b'-%s' % o[0])
2533 options.append(b'--%s' % o[1])
2533 options.append(b'--%s' % o[1])
2534 ui.write(b"%s\n" % b"\n".join(options))
2534 ui.write(b"%s\n" % b"\n".join(options))
2535 return
2535 return
2536
2536
2537 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2537 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2538 if ui.verbose:
2538 if ui.verbose:
2539 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2539 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2540 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2540 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2541
2541
2542
2542
2543 @command(
2543 @command(
2544 b'diff',
2544 b'diff',
2545 [
2545 [
2546 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2546 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2547 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2547 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2548 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2548 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2549 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2549 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2550 ]
2550 ]
2551 + diffopts
2551 + diffopts
2552 + diffopts2
2552 + diffopts2
2553 + walkopts
2553 + walkopts
2554 + subrepoopts,
2554 + subrepoopts,
2555 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2555 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2556 helpcategory=command.CATEGORY_FILE_CONTENTS,
2556 helpcategory=command.CATEGORY_FILE_CONTENTS,
2557 helpbasic=True,
2557 helpbasic=True,
2558 inferrepo=True,
2558 inferrepo=True,
2559 intents={INTENT_READONLY},
2559 intents={INTENT_READONLY},
2560 )
2560 )
2561 def diff(ui, repo, *pats, **opts):
2561 def diff(ui, repo, *pats, **opts):
2562 """diff repository (or selected files)
2562 """diff repository (or selected files)
2563
2563
2564 Show differences between revisions for the specified files.
2564 Show differences between revisions for the specified files.
2565
2565
2566 Differences between files are shown using the unified diff format.
2566 Differences between files are shown using the unified diff format.
2567
2567
2568 .. note::
2568 .. note::
2569
2569
2570 :hg:`diff` may generate unexpected results for merges, as it will
2570 :hg:`diff` may generate unexpected results for merges, as it will
2571 default to comparing against the working directory's first
2571 default to comparing against the working directory's first
2572 parent changeset if no revisions are specified. To diff against the
2572 parent changeset if no revisions are specified. To diff against the
2573 conflict regions, you can use `--config diff.merge=yes`.
2573 conflict regions, you can use `--config diff.merge=yes`.
2574
2574
2575 By default, the working directory files are compared to its first parent. To
2575 By default, the working directory files are compared to its first parent. To
2576 see the differences from another revision, use --from. To see the difference
2576 see the differences from another revision, use --from. To see the difference
2577 to another revision, use --to. For example, :hg:`diff --from .^` will show
2577 to another revision, use --to. For example, :hg:`diff --from .^` will show
2578 the differences from the working copy's grandparent to the working copy,
2578 the differences from the working copy's grandparent to the working copy,
2579 :hg:`diff --to .` will show the diff from the working copy to its parent
2579 :hg:`diff --to .` will show the diff from the working copy to its parent
2580 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2580 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2581 show the diff between those two revisions.
2581 show the diff between those two revisions.
2582
2582
2583 Alternatively you can specify -c/--change with a revision to see the changes
2583 Alternatively you can specify -c/--change with a revision to see the changes
2584 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2584 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2585 equivalent to :hg:`diff --from 42^ --to 42`)
2585 equivalent to :hg:`diff --from 42^ --to 42`)
2586
2586
2587 Without the -a/--text option, diff will avoid generating diffs of
2587 Without the -a/--text option, diff will avoid generating diffs of
2588 files it detects as binary. With -a, diff will generate a diff
2588 files it detects as binary. With -a, diff will generate a diff
2589 anyway, probably with undesirable results.
2589 anyway, probably with undesirable results.
2590
2590
2591 Use the -g/--git option to generate diffs in the git extended diff
2591 Use the -g/--git option to generate diffs in the git extended diff
2592 format. For more information, read :hg:`help diffs`.
2592 format. For more information, read :hg:`help diffs`.
2593
2593
2594 .. container:: verbose
2594 .. container:: verbose
2595
2595
2596 Examples:
2596 Examples:
2597
2597
2598 - compare a file in the current working directory to its parent::
2598 - compare a file in the current working directory to its parent::
2599
2599
2600 hg diff foo.c
2600 hg diff foo.c
2601
2601
2602 - compare two historical versions of a directory, with rename info::
2602 - compare two historical versions of a directory, with rename info::
2603
2603
2604 hg diff --git --from 1.0 --to 1.2 lib/
2604 hg diff --git --from 1.0 --to 1.2 lib/
2605
2605
2606 - get change stats relative to the last change on some date::
2606 - get change stats relative to the last change on some date::
2607
2607
2608 hg diff --stat --from "date('may 2')"
2608 hg diff --stat --from "date('may 2')"
2609
2609
2610 - diff all newly-added files that contain a keyword::
2610 - diff all newly-added files that contain a keyword::
2611
2611
2612 hg diff "set:added() and grep(GNU)"
2612 hg diff "set:added() and grep(GNU)"
2613
2613
2614 - compare a revision and its parents::
2614 - compare a revision and its parents::
2615
2615
2616 hg diff -c 9353 # compare against first parent
2616 hg diff -c 9353 # compare against first parent
2617 hg diff --from 9353^ --to 9353 # same using revset syntax
2617 hg diff --from 9353^ --to 9353 # same using revset syntax
2618 hg diff --from 9353^2 --to 9353 # compare against the second parent
2618 hg diff --from 9353^2 --to 9353 # compare against the second parent
2619
2619
2620 Returns 0 on success.
2620 Returns 0 on success.
2621 """
2621 """
2622
2622
2623 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2623 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2624 opts = pycompat.byteskwargs(opts)
2624 opts = pycompat.byteskwargs(opts)
2625 revs = opts.get(b'rev')
2625 revs = opts.get(b'rev')
2626 change = opts.get(b'change')
2626 change = opts.get(b'change')
2627 from_rev = opts.get(b'from')
2627 from_rev = opts.get(b'from')
2628 to_rev = opts.get(b'to')
2628 to_rev = opts.get(b'to')
2629 stat = opts.get(b'stat')
2629 stat = opts.get(b'stat')
2630 reverse = opts.get(b'reverse')
2630 reverse = opts.get(b'reverse')
2631
2631
2632 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2632 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2633 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2633 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2634 if change:
2634 if change:
2635 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2635 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2636 ctx2 = logcmdutil.revsingle(repo, change, None)
2636 ctx2 = logcmdutil.revsingle(repo, change, None)
2637 ctx1 = logcmdutil.diff_parent(ctx2)
2637 ctx1 = logcmdutil.diff_parent(ctx2)
2638 elif from_rev or to_rev:
2638 elif from_rev or to_rev:
2639 repo = scmutil.unhidehashlikerevs(
2639 repo = scmutil.unhidehashlikerevs(
2640 repo, [from_rev] + [to_rev], b'nowarn'
2640 repo, [from_rev] + [to_rev], b'nowarn'
2641 )
2641 )
2642 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2642 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2643 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2643 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2644 else:
2644 else:
2645 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2645 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2646 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2646 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2647
2647
2648 if reverse:
2648 if reverse:
2649 ctxleft = ctx2
2649 ctxleft = ctx2
2650 ctxright = ctx1
2650 ctxright = ctx1
2651 else:
2651 else:
2652 ctxleft = ctx1
2652 ctxleft = ctx1
2653 ctxright = ctx2
2653 ctxright = ctx2
2654
2654
2655 diffopts = patch.diffallopts(ui, opts)
2655 diffopts = patch.diffallopts(ui, opts)
2656 m = scmutil.match(ctx2, pats, opts)
2656 m = scmutil.match(ctx2, pats, opts)
2657 m = repo.narrowmatch(m)
2657 m = repo.narrowmatch(m)
2658 ui.pager(b'diff')
2658 ui.pager(b'diff')
2659 logcmdutil.diffordiffstat(
2659 logcmdutil.diffordiffstat(
2660 ui,
2660 ui,
2661 repo,
2661 repo,
2662 diffopts,
2662 diffopts,
2663 ctxleft,
2663 ctxleft,
2664 ctxright,
2664 ctxright,
2665 m,
2665 m,
2666 stat=stat,
2666 stat=stat,
2667 listsubrepos=opts.get(b'subrepos'),
2667 listsubrepos=opts.get(b'subrepos'),
2668 root=opts.get(b'root'),
2668 root=opts.get(b'root'),
2669 )
2669 )
2670
2670
2671
2671
2672 @command(
2672 @command(
2673 b'export',
2673 b'export',
2674 [
2674 [
2675 (
2675 (
2676 b'B',
2676 b'B',
2677 b'bookmark',
2677 b'bookmark',
2678 b'',
2678 b'',
2679 _(b'export changes only reachable by given bookmark'),
2679 _(b'export changes only reachable by given bookmark'),
2680 _(b'BOOKMARK'),
2680 _(b'BOOKMARK'),
2681 ),
2681 ),
2682 (
2682 (
2683 b'o',
2683 b'o',
2684 b'output',
2684 b'output',
2685 b'',
2685 b'',
2686 _(b'print output to file with formatted name'),
2686 _(b'print output to file with formatted name'),
2687 _(b'FORMAT'),
2687 _(b'FORMAT'),
2688 ),
2688 ),
2689 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2689 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2690 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2690 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2691 ]
2691 ]
2692 + diffopts
2692 + diffopts
2693 + formatteropts,
2693 + formatteropts,
2694 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2694 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2695 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2695 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2696 helpbasic=True,
2696 helpbasic=True,
2697 intents={INTENT_READONLY},
2697 intents={INTENT_READONLY},
2698 )
2698 )
2699 def export(ui, repo, *changesets, **opts):
2699 def export(ui, repo, *changesets, **opts):
2700 """dump the header and diffs for one or more changesets
2700 """dump the header and diffs for one or more changesets
2701
2701
2702 Print the changeset header and diffs for one or more revisions.
2702 Print the changeset header and diffs for one or more revisions.
2703 If no revision is given, the parent of the working directory is used.
2703 If no revision is given, the parent of the working directory is used.
2704
2704
2705 The information shown in the changeset header is: author, date,
2705 The information shown in the changeset header is: author, date,
2706 branch name (if non-default), changeset hash, parent(s) and commit
2706 branch name (if non-default), changeset hash, parent(s) and commit
2707 comment.
2707 comment.
2708
2708
2709 .. note::
2709 .. note::
2710
2710
2711 :hg:`export` may generate unexpected diff output for merge
2711 :hg:`export` may generate unexpected diff output for merge
2712 changesets, as it will compare the merge changeset against its
2712 changesets, as it will compare the merge changeset against its
2713 first parent only.
2713 first parent only.
2714
2714
2715 Output may be to a file, in which case the name of the file is
2715 Output may be to a file, in which case the name of the file is
2716 given using a template string. See :hg:`help templates`. In addition
2716 given using a template string. See :hg:`help templates`. In addition
2717 to the common template keywords, the following formatting rules are
2717 to the common template keywords, the following formatting rules are
2718 supported:
2718 supported:
2719
2719
2720 :``%%``: literal "%" character
2720 :``%%``: literal "%" character
2721 :``%H``: changeset hash (40 hexadecimal digits)
2721 :``%H``: changeset hash (40 hexadecimal digits)
2722 :``%N``: number of patches being generated
2722 :``%N``: number of patches being generated
2723 :``%R``: changeset revision number
2723 :``%R``: changeset revision number
2724 :``%b``: basename of the exporting repository
2724 :``%b``: basename of the exporting repository
2725 :``%h``: short-form changeset hash (12 hexadecimal digits)
2725 :``%h``: short-form changeset hash (12 hexadecimal digits)
2726 :``%m``: first line of the commit message (only alphanumeric characters)
2726 :``%m``: first line of the commit message (only alphanumeric characters)
2727 :``%n``: zero-padded sequence number, starting at 1
2727 :``%n``: zero-padded sequence number, starting at 1
2728 :``%r``: zero-padded changeset revision number
2728 :``%r``: zero-padded changeset revision number
2729 :``\\``: literal "\\" character
2729 :``\\``: literal "\\" character
2730
2730
2731 Without the -a/--text option, export will avoid generating diffs
2731 Without the -a/--text option, export will avoid generating diffs
2732 of files it detects as binary. With -a, export will generate a
2732 of files it detects as binary. With -a, export will generate a
2733 diff anyway, probably with undesirable results.
2733 diff anyway, probably with undesirable results.
2734
2734
2735 With -B/--bookmark changesets reachable by the given bookmark are
2735 With -B/--bookmark changesets reachable by the given bookmark are
2736 selected.
2736 selected.
2737
2737
2738 Use the -g/--git option to generate diffs in the git extended diff
2738 Use the -g/--git option to generate diffs in the git extended diff
2739 format. See :hg:`help diffs` for more information.
2739 format. See :hg:`help diffs` for more information.
2740
2740
2741 With the --switch-parent option, the diff will be against the
2741 With the --switch-parent option, the diff will be against the
2742 second parent. It can be useful to review a merge.
2742 second parent. It can be useful to review a merge.
2743
2743
2744 .. container:: verbose
2744 .. container:: verbose
2745
2745
2746 Template:
2746 Template:
2747
2747
2748 The following keywords are supported in addition to the common template
2748 The following keywords are supported in addition to the common template
2749 keywords and functions. See also :hg:`help templates`.
2749 keywords and functions. See also :hg:`help templates`.
2750
2750
2751 :diff: String. Diff content.
2751 :diff: String. Diff content.
2752 :parents: List of strings. Parent nodes of the changeset.
2752 :parents: List of strings. Parent nodes of the changeset.
2753
2753
2754 Examples:
2754 Examples:
2755
2755
2756 - use export and import to transplant a bugfix to the current
2756 - use export and import to transplant a bugfix to the current
2757 branch::
2757 branch::
2758
2758
2759 hg export -r 9353 | hg import -
2759 hg export -r 9353 | hg import -
2760
2760
2761 - export all the changesets between two revisions to a file with
2761 - export all the changesets between two revisions to a file with
2762 rename information::
2762 rename information::
2763
2763
2764 hg export --git -r 123:150 > changes.txt
2764 hg export --git -r 123:150 > changes.txt
2765
2765
2766 - split outgoing changes into a series of patches with
2766 - split outgoing changes into a series of patches with
2767 descriptive names::
2767 descriptive names::
2768
2768
2769 hg export -r "outgoing()" -o "%n-%m.patch"
2769 hg export -r "outgoing()" -o "%n-%m.patch"
2770
2770
2771 Returns 0 on success.
2771 Returns 0 on success.
2772 """
2772 """
2773 opts = pycompat.byteskwargs(opts)
2773 opts = pycompat.byteskwargs(opts)
2774 bookmark = opts.get(b'bookmark')
2774 bookmark = opts.get(b'bookmark')
2775 changesets += tuple(opts.get(b'rev', []))
2775 changesets += tuple(opts.get(b'rev', []))
2776
2776
2777 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2777 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2778
2778
2779 if bookmark:
2779 if bookmark:
2780 if bookmark not in repo._bookmarks:
2780 if bookmark not in repo._bookmarks:
2781 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2781 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2782
2782
2783 revs = scmutil.bookmarkrevs(repo, bookmark)
2783 revs = scmutil.bookmarkrevs(repo, bookmark)
2784 else:
2784 else:
2785 if not changesets:
2785 if not changesets:
2786 changesets = [b'.']
2786 changesets = [b'.']
2787
2787
2788 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2788 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2789 revs = logcmdutil.revrange(repo, changesets)
2789 revs = logcmdutil.revrange(repo, changesets)
2790
2790
2791 if not revs:
2791 if not revs:
2792 raise error.InputError(_(b"export requires at least one changeset"))
2792 raise error.InputError(_(b"export requires at least one changeset"))
2793 if len(revs) > 1:
2793 if len(revs) > 1:
2794 ui.note(_(b'exporting patches:\n'))
2794 ui.note(_(b'exporting patches:\n'))
2795 else:
2795 else:
2796 ui.note(_(b'exporting patch:\n'))
2796 ui.note(_(b'exporting patch:\n'))
2797
2797
2798 fntemplate = opts.get(b'output')
2798 fntemplate = opts.get(b'output')
2799 if cmdutil.isstdiofilename(fntemplate):
2799 if cmdutil.isstdiofilename(fntemplate):
2800 fntemplate = b''
2800 fntemplate = b''
2801
2801
2802 if fntemplate:
2802 if fntemplate:
2803 fm = formatter.nullformatter(ui, b'export', opts)
2803 fm = formatter.nullformatter(ui, b'export', opts)
2804 else:
2804 else:
2805 ui.pager(b'export')
2805 ui.pager(b'export')
2806 fm = ui.formatter(b'export', opts)
2806 fm = ui.formatter(b'export', opts)
2807 with fm:
2807 with fm:
2808 cmdutil.export(
2808 cmdutil.export(
2809 repo,
2809 repo,
2810 revs,
2810 revs,
2811 fm,
2811 fm,
2812 fntemplate=fntemplate,
2812 fntemplate=fntemplate,
2813 switch_parent=opts.get(b'switch_parent'),
2813 switch_parent=opts.get(b'switch_parent'),
2814 opts=patch.diffallopts(ui, opts),
2814 opts=patch.diffallopts(ui, opts),
2815 )
2815 )
2816
2816
2817
2817
2818 @command(
2818 @command(
2819 b'files',
2819 b'files',
2820 [
2820 [
2821 (
2821 (
2822 b'r',
2822 b'r',
2823 b'rev',
2823 b'rev',
2824 b'',
2824 b'',
2825 _(b'search the repository as it is in REV'),
2825 _(b'search the repository as it is in REV'),
2826 _(b'REV'),
2826 _(b'REV'),
2827 ),
2827 ),
2828 (
2828 (
2829 b'0',
2829 b'0',
2830 b'print0',
2830 b'print0',
2831 None,
2831 None,
2832 _(b'end filenames with NUL, for use with xargs'),
2832 _(b'end filenames with NUL, for use with xargs'),
2833 ),
2833 ),
2834 ]
2834 ]
2835 + walkopts
2835 + walkopts
2836 + formatteropts
2836 + formatteropts
2837 + subrepoopts,
2837 + subrepoopts,
2838 _(b'[OPTION]... [FILE]...'),
2838 _(b'[OPTION]... [FILE]...'),
2839 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2839 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2840 intents={INTENT_READONLY},
2840 intents={INTENT_READONLY},
2841 )
2841 )
2842 def files(ui, repo, *pats, **opts):
2842 def files(ui, repo, *pats, **opts):
2843 """list tracked files
2843 """list tracked files
2844
2844
2845 Print files under Mercurial control in the working directory or
2845 Print files under Mercurial control in the working directory or
2846 specified revision for given files (excluding removed files).
2846 specified revision for given files (excluding removed files).
2847 Files can be specified as filenames or filesets.
2847 Files can be specified as filenames or filesets.
2848
2848
2849 If no files are given to match, this command prints the names
2849 If no files are given to match, this command prints the names
2850 of all files under Mercurial control.
2850 of all files under Mercurial control.
2851
2851
2852 .. container:: verbose
2852 .. container:: verbose
2853
2853
2854 Template:
2854 Template:
2855
2855
2856 The following keywords are supported in addition to the common template
2856 The following keywords are supported in addition to the common template
2857 keywords and functions. See also :hg:`help templates`.
2857 keywords and functions. See also :hg:`help templates`.
2858
2858
2859 :flags: String. Character denoting file's symlink and executable bits.
2859 :flags: String. Character denoting file's symlink and executable bits.
2860 :path: String. Repository-absolute path of the file.
2860 :path: String. Repository-absolute path of the file.
2861 :size: Integer. Size of the file in bytes.
2861 :size: Integer. Size of the file in bytes.
2862
2862
2863 Examples:
2863 Examples:
2864
2864
2865 - list all files under the current directory::
2865 - list all files under the current directory::
2866
2866
2867 hg files .
2867 hg files .
2868
2868
2869 - shows sizes and flags for current revision::
2869 - shows sizes and flags for current revision::
2870
2870
2871 hg files -vr .
2871 hg files -vr .
2872
2872
2873 - list all files named README::
2873 - list all files named README::
2874
2874
2875 hg files -I "**/README"
2875 hg files -I "**/README"
2876
2876
2877 - list all binary files::
2877 - list all binary files::
2878
2878
2879 hg files "set:binary()"
2879 hg files "set:binary()"
2880
2880
2881 - find files containing a regular expression::
2881 - find files containing a regular expression::
2882
2882
2883 hg files "set:grep('bob')"
2883 hg files "set:grep('bob')"
2884
2884
2885 - search tracked file contents with xargs and grep::
2885 - search tracked file contents with xargs and grep::
2886
2886
2887 hg files -0 | xargs -0 grep foo
2887 hg files -0 | xargs -0 grep foo
2888
2888
2889 See :hg:`help patterns` and :hg:`help filesets` for more information
2889 See :hg:`help patterns` and :hg:`help filesets` for more information
2890 on specifying file patterns.
2890 on specifying file patterns.
2891
2891
2892 Returns 0 if a match is found, 1 otherwise.
2892 Returns 0 if a match is found, 1 otherwise.
2893
2893
2894 """
2894 """
2895
2895
2896 opts = pycompat.byteskwargs(opts)
2896 opts = pycompat.byteskwargs(opts)
2897 rev = opts.get(b'rev')
2897 rev = opts.get(b'rev')
2898 if rev:
2898 if rev:
2899 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2899 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2900 ctx = logcmdutil.revsingle(repo, rev, None)
2900 ctx = logcmdutil.revsingle(repo, rev, None)
2901
2901
2902 end = b'\n'
2902 end = b'\n'
2903 if opts.get(b'print0'):
2903 if opts.get(b'print0'):
2904 end = b'\0'
2904 end = b'\0'
2905 fmt = b'%s' + end
2905 fmt = b'%s' + end
2906
2906
2907 m = scmutil.match(ctx, pats, opts)
2907 m = scmutil.match(ctx, pats, opts)
2908 ui.pager(b'files')
2908 ui.pager(b'files')
2909 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2909 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2910 with ui.formatter(b'files', opts) as fm:
2910 with ui.formatter(b'files', opts) as fm:
2911 return cmdutil.files(
2911 return cmdutil.files(
2912 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2912 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2913 )
2913 )
2914
2914
2915
2915
2916 @command(
2916 @command(
2917 b'forget',
2917 b'forget',
2918 [
2918 [
2919 (b'i', b'interactive', None, _(b'use interactive mode')),
2919 (b'i', b'interactive', None, _(b'use interactive mode')),
2920 ]
2920 ]
2921 + walkopts
2921 + walkopts
2922 + dryrunopts,
2922 + dryrunopts,
2923 _(b'[OPTION]... FILE...'),
2923 _(b'[OPTION]... FILE...'),
2924 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2924 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2925 helpbasic=True,
2925 helpbasic=True,
2926 inferrepo=True,
2926 inferrepo=True,
2927 )
2927 )
2928 def forget(ui, repo, *pats, **opts):
2928 def forget(ui, repo, *pats, **opts):
2929 """forget the specified files on the next commit
2929 """forget the specified files on the next commit
2930
2930
2931 Mark the specified files so they will no longer be tracked
2931 Mark the specified files so they will no longer be tracked
2932 after the next commit.
2932 after the next commit.
2933
2933
2934 This only removes files from the current branch, not from the
2934 This only removes files from the current branch, not from the
2935 entire project history, and it does not delete them from the
2935 entire project history, and it does not delete them from the
2936 working directory.
2936 working directory.
2937
2937
2938 To delete the file from the working directory, see :hg:`remove`.
2938 To delete the file from the working directory, see :hg:`remove`.
2939
2939
2940 To undo a forget before the next commit, see :hg:`add`.
2940 To undo a forget before the next commit, see :hg:`add`.
2941
2941
2942 .. container:: verbose
2942 .. container:: verbose
2943
2943
2944 Examples:
2944 Examples:
2945
2945
2946 - forget newly-added binary files::
2946 - forget newly-added binary files::
2947
2947
2948 hg forget "set:added() and binary()"
2948 hg forget "set:added() and binary()"
2949
2949
2950 - forget files that would be excluded by .hgignore::
2950 - forget files that would be excluded by .hgignore::
2951
2951
2952 hg forget "set:hgignore()"
2952 hg forget "set:hgignore()"
2953
2953
2954 Returns 0 on success.
2954 Returns 0 on success.
2955 """
2955 """
2956
2956
2957 opts = pycompat.byteskwargs(opts)
2957 opts = pycompat.byteskwargs(opts)
2958 if not pats:
2958 if not pats:
2959 raise error.InputError(_(b'no files specified'))
2959 raise error.InputError(_(b'no files specified'))
2960
2960
2961 m = scmutil.match(repo[None], pats, opts)
2961 m = scmutil.match(repo[None], pats, opts)
2962 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2962 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2963 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2963 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2964 rejected = cmdutil.forget(
2964 rejected = cmdutil.forget(
2965 ui,
2965 ui,
2966 repo,
2966 repo,
2967 m,
2967 m,
2968 prefix=b"",
2968 prefix=b"",
2969 uipathfn=uipathfn,
2969 uipathfn=uipathfn,
2970 explicitonly=False,
2970 explicitonly=False,
2971 dryrun=dryrun,
2971 dryrun=dryrun,
2972 interactive=interactive,
2972 interactive=interactive,
2973 )[0]
2973 )[0]
2974 return rejected and 1 or 0
2974 return rejected and 1 or 0
2975
2975
2976
2976
2977 @command(
2977 @command(
2978 b'graft',
2978 b'graft',
2979 [
2979 [
2980 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2980 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2981 (
2981 (
2982 b'',
2982 b'',
2983 b'base',
2983 b'base',
2984 b'',
2984 b'',
2985 _(b'base revision when doing the graft merge (ADVANCED)'),
2985 _(b'base revision when doing the graft merge (ADVANCED)'),
2986 _(b'REV'),
2986 _(b'REV'),
2987 ),
2987 ),
2988 (b'c', b'continue', False, _(b'resume interrupted graft')),
2988 (b'c', b'continue', False, _(b'resume interrupted graft')),
2989 (b'', b'stop', False, _(b'stop interrupted graft')),
2989 (b'', b'stop', False, _(b'stop interrupted graft')),
2990 (b'', b'abort', False, _(b'abort interrupted graft')),
2990 (b'', b'abort', False, _(b'abort interrupted graft')),
2991 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2991 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2992 (b'', b'log', None, _(b'append graft info to log message')),
2992 (b'', b'log', None, _(b'append graft info to log message')),
2993 (
2993 (
2994 b'',
2994 b'',
2995 b'no-commit',
2995 b'no-commit',
2996 None,
2996 None,
2997 _(b"don't commit, just apply the changes in working directory"),
2997 _(b"don't commit, just apply the changes in working directory"),
2998 ),
2998 ),
2999 (b'f', b'force', False, _(b'force graft')),
2999 (b'f', b'force', False, _(b'force graft')),
3000 (
3000 (
3001 b'D',
3001 b'D',
3002 b'currentdate',
3002 b'currentdate',
3003 False,
3003 False,
3004 _(b'record the current date as commit date'),
3004 _(b'record the current date as commit date'),
3005 ),
3005 ),
3006 (
3006 (
3007 b'U',
3007 b'U',
3008 b'currentuser',
3008 b'currentuser',
3009 False,
3009 False,
3010 _(b'record the current user as committer'),
3010 _(b'record the current user as committer'),
3011 ),
3011 ),
3012 ]
3012 ]
3013 + commitopts2
3013 + commitopts2
3014 + mergetoolopts
3014 + mergetoolopts
3015 + dryrunopts,
3015 + dryrunopts,
3016 _(b'[OPTION]... [-r REV]... REV...'),
3016 _(b'[OPTION]... [-r REV]... REV...'),
3017 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3017 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3018 )
3018 )
3019 def graft(ui, repo, *revs, **opts):
3019 def graft(ui, repo, *revs, **opts):
3020 """copy changes from other branches onto the current branch
3020 """copy changes from other branches onto the current branch
3021
3021
3022 This command uses Mercurial's merge logic to copy individual
3022 This command uses Mercurial's merge logic to copy individual
3023 changes from other branches without merging branches in the
3023 changes from other branches without merging branches in the
3024 history graph. This is sometimes known as 'backporting' or
3024 history graph. This is sometimes known as 'backporting' or
3025 'cherry-picking'. By default, graft will copy user, date, and
3025 'cherry-picking'. By default, graft will copy user, date, and
3026 description from the source changesets.
3026 description from the source changesets.
3027
3027
3028 Changesets that are ancestors of the current revision, that have
3028 Changesets that are ancestors of the current revision, that have
3029 already been grafted, or that are merges will be skipped.
3029 already been grafted, or that are merges will be skipped.
3030
3030
3031 If --log is specified, log messages will have a comment appended
3031 If --log is specified, log messages will have a comment appended
3032 of the form::
3032 of the form::
3033
3033
3034 (grafted from CHANGESETHASH)
3034 (grafted from CHANGESETHASH)
3035
3035
3036 If --force is specified, revisions will be grafted even if they
3036 If --force is specified, revisions will be grafted even if they
3037 are already ancestors of, or have been grafted to, the destination.
3037 are already ancestors of, or have been grafted to, the destination.
3038 This is useful when the revisions have since been backed out.
3038 This is useful when the revisions have since been backed out.
3039
3039
3040 If a graft merge results in conflicts, the graft process is
3040 If a graft merge results in conflicts, the graft process is
3041 interrupted so that the current merge can be manually resolved.
3041 interrupted so that the current merge can be manually resolved.
3042 Once all conflicts are addressed, the graft process can be
3042 Once all conflicts are addressed, the graft process can be
3043 continued with the -c/--continue option.
3043 continued with the -c/--continue option.
3044
3044
3045 The -c/--continue option reapplies all the earlier options.
3045 The -c/--continue option reapplies all the earlier options.
3046
3046
3047 .. container:: verbose
3047 .. container:: verbose
3048
3048
3049 The --base option exposes more of how graft internally uses merge with a
3049 The --base option exposes more of how graft internally uses merge with a
3050 custom base revision. --base can be used to specify another ancestor than
3050 custom base revision. --base can be used to specify another ancestor than
3051 the first and only parent.
3051 the first and only parent.
3052
3052
3053 The command::
3053 The command::
3054
3054
3055 hg graft -r 345 --base 234
3055 hg graft -r 345 --base 234
3056
3056
3057 is thus pretty much the same as::
3057 is thus pretty much the same as::
3058
3058
3059 hg diff --from 234 --to 345 | hg import
3059 hg diff --from 234 --to 345 | hg import
3060
3060
3061 but using merge to resolve conflicts and track moved files.
3061 but using merge to resolve conflicts and track moved files.
3062
3062
3063 The result of a merge can thus be backported as a single commit by
3063 The result of a merge can thus be backported as a single commit by
3064 specifying one of the merge parents as base, and thus effectively
3064 specifying one of the merge parents as base, and thus effectively
3065 grafting the changes from the other side.
3065 grafting the changes from the other side.
3066
3066
3067 It is also possible to collapse multiple changesets and clean up history
3067 It is also possible to collapse multiple changesets and clean up history
3068 by specifying another ancestor as base, much like rebase --collapse
3068 by specifying another ancestor as base, much like rebase --collapse
3069 --keep.
3069 --keep.
3070
3070
3071 The commit message can be tweaked after the fact using commit --amend .
3071 The commit message can be tweaked after the fact using commit --amend .
3072
3072
3073 For using non-ancestors as the base to backout changes, see the backout
3073 For using non-ancestors as the base to backout changes, see the backout
3074 command and the hidden --parent option.
3074 command and the hidden --parent option.
3075
3075
3076 .. container:: verbose
3076 .. container:: verbose
3077
3077
3078 Examples:
3078 Examples:
3079
3079
3080 - copy a single change to the stable branch and edit its description::
3080 - copy a single change to the stable branch and edit its description::
3081
3081
3082 hg update stable
3082 hg update stable
3083 hg graft --edit 9393
3083 hg graft --edit 9393
3084
3084
3085 - graft a range of changesets with one exception, updating dates::
3085 - graft a range of changesets with one exception, updating dates::
3086
3086
3087 hg graft -D "2085::2093 and not 2091"
3087 hg graft -D "2085::2093 and not 2091"
3088
3088
3089 - continue a graft after resolving conflicts::
3089 - continue a graft after resolving conflicts::
3090
3090
3091 hg graft -c
3091 hg graft -c
3092
3092
3093 - show the source of a grafted changeset::
3093 - show the source of a grafted changeset::
3094
3094
3095 hg log --debug -r .
3095 hg log --debug -r .
3096
3096
3097 - show revisions sorted by date::
3097 - show revisions sorted by date::
3098
3098
3099 hg log -r "sort(all(), date)"
3099 hg log -r "sort(all(), date)"
3100
3100
3101 - backport the result of a merge as a single commit::
3101 - backport the result of a merge as a single commit::
3102
3102
3103 hg graft -r 123 --base 123^
3103 hg graft -r 123 --base 123^
3104
3104
3105 - land a feature branch as one changeset::
3105 - land a feature branch as one changeset::
3106
3106
3107 hg up -cr default
3107 hg up -cr default
3108 hg graft -r featureX --base "ancestor('featureX', 'default')"
3108 hg graft -r featureX --base "ancestor('featureX', 'default')"
3109
3109
3110 See :hg:`help revisions` for more about specifying revisions.
3110 See :hg:`help revisions` for more about specifying revisions.
3111
3111
3112 Returns 0 on successful completion, 1 if there are unresolved files.
3112 Returns 0 on successful completion, 1 if there are unresolved files.
3113 """
3113 """
3114 with repo.wlock():
3114 with repo.wlock():
3115 return _dograft(ui, repo, *revs, **opts)
3115 return _dograft(ui, repo, *revs, **opts)
3116
3116
3117
3117
3118 def _dograft(ui, repo, *revs, **opts):
3118 def _dograft(ui, repo, *revs, **opts):
3119 if revs and opts.get('rev'):
3119 if revs and opts.get('rev'):
3120 ui.warn(
3120 ui.warn(
3121 _(
3121 _(
3122 b'warning: inconsistent use of --rev might give unexpected '
3122 b'warning: inconsistent use of --rev might give unexpected '
3123 b'revision ordering!\n'
3123 b'revision ordering!\n'
3124 )
3124 )
3125 )
3125 )
3126
3126
3127 revs = list(revs)
3127 revs = list(revs)
3128 revs.extend(opts.get('rev'))
3128 revs.extend(opts.get('rev'))
3129 # a dict of data to be stored in state file
3129 # a dict of data to be stored in state file
3130 statedata = {}
3130 statedata = {}
3131 # list of new nodes created by ongoing graft
3131 # list of new nodes created by ongoing graft
3132 statedata[b'newnodes'] = []
3132 statedata[b'newnodes'] = []
3133
3133
3134 cmdutil.resolve_commit_options(ui, opts)
3134 cmdutil.resolve_commit_options(ui, opts)
3135
3135
3136 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3136 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3137
3137
3138 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3138 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3139
3139
3140 cont = False
3140 cont = False
3141 if opts.get('no_commit'):
3141 if opts.get('no_commit'):
3142 cmdutil.check_incompatible_arguments(
3142 cmdutil.check_incompatible_arguments(
3143 opts,
3143 opts,
3144 'no_commit',
3144 'no_commit',
3145 ['edit', 'currentuser', 'currentdate', 'log'],
3145 ['edit', 'currentuser', 'currentdate', 'log'],
3146 )
3146 )
3147
3147
3148 graftstate = statemod.cmdstate(repo, b'graftstate')
3148 graftstate = statemod.cmdstate(repo, b'graftstate')
3149
3149
3150 if opts.get('stop'):
3150 if opts.get('stop'):
3151 cmdutil.check_incompatible_arguments(
3151 cmdutil.check_incompatible_arguments(
3152 opts,
3152 opts,
3153 'stop',
3153 'stop',
3154 [
3154 [
3155 'edit',
3155 'edit',
3156 'log',
3156 'log',
3157 'user',
3157 'user',
3158 'date',
3158 'date',
3159 'currentdate',
3159 'currentdate',
3160 'currentuser',
3160 'currentuser',
3161 'rev',
3161 'rev',
3162 ],
3162 ],
3163 )
3163 )
3164 return _stopgraft(ui, repo, graftstate)
3164 return _stopgraft(ui, repo, graftstate)
3165 elif opts.get('abort'):
3165 elif opts.get('abort'):
3166 cmdutil.check_incompatible_arguments(
3166 cmdutil.check_incompatible_arguments(
3167 opts,
3167 opts,
3168 'abort',
3168 'abort',
3169 [
3169 [
3170 'edit',
3170 'edit',
3171 'log',
3171 'log',
3172 'user',
3172 'user',
3173 'date',
3173 'date',
3174 'currentdate',
3174 'currentdate',
3175 'currentuser',
3175 'currentuser',
3176 'rev',
3176 'rev',
3177 ],
3177 ],
3178 )
3178 )
3179 return cmdutil.abortgraft(ui, repo, graftstate)
3179 return cmdutil.abortgraft(ui, repo, graftstate)
3180 elif opts.get('continue'):
3180 elif opts.get('continue'):
3181 cont = True
3181 cont = True
3182 if revs:
3182 if revs:
3183 raise error.InputError(_(b"can't specify --continue and revisions"))
3183 raise error.InputError(_(b"can't specify --continue and revisions"))
3184 # read in unfinished revisions
3184 # read in unfinished revisions
3185 if graftstate.exists():
3185 if graftstate.exists():
3186 statedata = cmdutil.readgraftstate(repo, graftstate)
3186 statedata = cmdutil.readgraftstate(repo, graftstate)
3187 if statedata.get(b'date'):
3187 if statedata.get(b'date'):
3188 opts['date'] = statedata[b'date']
3188 opts['date'] = statedata[b'date']
3189 if statedata.get(b'user'):
3189 if statedata.get(b'user'):
3190 opts['user'] = statedata[b'user']
3190 opts['user'] = statedata[b'user']
3191 if statedata.get(b'log'):
3191 if statedata.get(b'log'):
3192 opts['log'] = True
3192 opts['log'] = True
3193 if statedata.get(b'no_commit'):
3193 if statedata.get(b'no_commit'):
3194 opts['no_commit'] = statedata.get(b'no_commit')
3194 opts['no_commit'] = statedata.get(b'no_commit')
3195 if statedata.get(b'base'):
3195 if statedata.get(b'base'):
3196 opts['base'] = statedata.get(b'base')
3196 opts['base'] = statedata.get(b'base')
3197 nodes = statedata[b'nodes']
3197 nodes = statedata[b'nodes']
3198 revs = [repo[node].rev() for node in nodes]
3198 revs = [repo[node].rev() for node in nodes]
3199 else:
3199 else:
3200 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3200 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3201 else:
3201 else:
3202 if not revs:
3202 if not revs:
3203 raise error.InputError(_(b'no revisions specified'))
3203 raise error.InputError(_(b'no revisions specified'))
3204 cmdutil.checkunfinished(repo)
3204 cmdutil.checkunfinished(repo)
3205 cmdutil.bailifchanged(repo)
3205 cmdutil.bailifchanged(repo)
3206 revs = logcmdutil.revrange(repo, revs)
3206 revs = logcmdutil.revrange(repo, revs)
3207
3207
3208 skipped = set()
3208 skipped = set()
3209 basectx = None
3209 basectx = None
3210 if opts.get('base'):
3210 if opts.get('base'):
3211 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3211 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3212 if basectx is None:
3212 if basectx is None:
3213 # check for merges
3213 # check for merges
3214 for rev in repo.revs(b'%ld and merge()', revs):
3214 for rev in repo.revs(b'%ld and merge()', revs):
3215 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3215 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3216 skipped.add(rev)
3216 skipped.add(rev)
3217 revs = [r for r in revs if r not in skipped]
3217 revs = [r for r in revs if r not in skipped]
3218 if not revs:
3218 if not revs:
3219 return -1
3219 return -1
3220 if basectx is not None and len(revs) != 1:
3220 if basectx is not None and len(revs) != 1:
3221 raise error.InputError(_(b'only one revision allowed with --base '))
3221 raise error.InputError(_(b'only one revision allowed with --base '))
3222
3222
3223 # Don't check in the --continue case, in effect retaining --force across
3223 # Don't check in the --continue case, in effect retaining --force across
3224 # --continues. That's because without --force, any revisions we decided to
3224 # --continues. That's because without --force, any revisions we decided to
3225 # skip would have been filtered out here, so they wouldn't have made their
3225 # skip would have been filtered out here, so they wouldn't have made their
3226 # way to the graftstate. With --force, any revisions we would have otherwise
3226 # way to the graftstate. With --force, any revisions we would have otherwise
3227 # skipped would not have been filtered out, and if they hadn't been applied
3227 # skipped would not have been filtered out, and if they hadn't been applied
3228 # already, they'd have been in the graftstate.
3228 # already, they'd have been in the graftstate.
3229 if not (cont or opts.get('force')) and basectx is None:
3229 if not (cont or opts.get('force')) and basectx is None:
3230 # check for ancestors of dest branch
3230 # check for ancestors of dest branch
3231 ancestors = repo.revs(b'%ld & (::.)', revs)
3231 ancestors = repo.revs(b'%ld & (::.)', revs)
3232 for rev in ancestors:
3232 for rev in ancestors:
3233 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3233 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3234
3234
3235 revs = [r for r in revs if r not in ancestors]
3235 revs = [r for r in revs if r not in ancestors]
3236
3236
3237 if not revs:
3237 if not revs:
3238 return -1
3238 return -1
3239
3239
3240 # analyze revs for earlier grafts
3240 # analyze revs for earlier grafts
3241 ids = {}
3241 ids = {}
3242 for ctx in repo.set(b"%ld", revs):
3242 for ctx in repo.set(b"%ld", revs):
3243 ids[ctx.hex()] = ctx.rev()
3243 ids[ctx.hex()] = ctx.rev()
3244 n = ctx.extra().get(b'source')
3244 n = ctx.extra().get(b'source')
3245 if n:
3245 if n:
3246 ids[n] = ctx.rev()
3246 ids[n] = ctx.rev()
3247
3247
3248 # check ancestors for earlier grafts
3248 # check ancestors for earlier grafts
3249 ui.debug(b'scanning for duplicate grafts\n')
3249 ui.debug(b'scanning for duplicate grafts\n')
3250
3250
3251 # The only changesets we can be sure doesn't contain grafts of any
3251 # The only changesets we can be sure doesn't contain grafts of any
3252 # revs, are the ones that are common ancestors of *all* revs:
3252 # revs, are the ones that are common ancestors of *all* revs:
3253 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3253 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3254 ctx = repo[rev]
3254 ctx = repo[rev]
3255 n = ctx.extra().get(b'source')
3255 n = ctx.extra().get(b'source')
3256 if n in ids:
3256 if n in ids:
3257 try:
3257 try:
3258 r = repo[n].rev()
3258 r = repo[n].rev()
3259 except error.RepoLookupError:
3259 except error.RepoLookupError:
3260 r = None
3260 r = None
3261 if r in revs:
3261 if r in revs:
3262 ui.warn(
3262 ui.warn(
3263 _(
3263 _(
3264 b'skipping revision %d:%s '
3264 b'skipping revision %d:%s '
3265 b'(already grafted to %d:%s)\n'
3265 b'(already grafted to %d:%s)\n'
3266 )
3266 )
3267 % (r, repo[r], rev, ctx)
3267 % (r, repo[r], rev, ctx)
3268 )
3268 )
3269 revs.remove(r)
3269 revs.remove(r)
3270 elif ids[n] in revs:
3270 elif ids[n] in revs:
3271 if r is None:
3271 if r is None:
3272 ui.warn(
3272 ui.warn(
3273 _(
3273 _(
3274 b'skipping already grafted revision %d:%s '
3274 b'skipping already grafted revision %d:%s '
3275 b'(%d:%s also has unknown origin %s)\n'
3275 b'(%d:%s also has unknown origin %s)\n'
3276 )
3276 )
3277 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3277 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3278 )
3278 )
3279 else:
3279 else:
3280 ui.warn(
3280 ui.warn(
3281 _(
3281 _(
3282 b'skipping already grafted revision %d:%s '
3282 b'skipping already grafted revision %d:%s '
3283 b'(%d:%s also has origin %d:%s)\n'
3283 b'(%d:%s also has origin %d:%s)\n'
3284 )
3284 )
3285 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3285 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3286 )
3286 )
3287 revs.remove(ids[n])
3287 revs.remove(ids[n])
3288 elif ctx.hex() in ids:
3288 elif ctx.hex() in ids:
3289 r = ids[ctx.hex()]
3289 r = ids[ctx.hex()]
3290 if r in revs:
3290 if r in revs:
3291 ui.warn(
3291 ui.warn(
3292 _(
3292 _(
3293 b'skipping already grafted revision %d:%s '
3293 b'skipping already grafted revision %d:%s '
3294 b'(was grafted from %d:%s)\n'
3294 b'(was grafted from %d:%s)\n'
3295 )
3295 )
3296 % (r, repo[r], rev, ctx)
3296 % (r, repo[r], rev, ctx)
3297 )
3297 )
3298 revs.remove(r)
3298 revs.remove(r)
3299 if not revs:
3299 if not revs:
3300 return -1
3300 return -1
3301
3301
3302 if opts.get('no_commit'):
3302 if opts.get('no_commit'):
3303 statedata[b'no_commit'] = True
3303 statedata[b'no_commit'] = True
3304 if opts.get('base'):
3304 if opts.get('base'):
3305 statedata[b'base'] = opts['base']
3305 statedata[b'base'] = opts['base']
3306 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3306 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3307 desc = b'%d:%s "%s"' % (
3307 desc = b'%d:%s "%s"' % (
3308 ctx.rev(),
3308 ctx.rev(),
3309 ctx,
3309 ctx,
3310 ctx.description().split(b'\n', 1)[0],
3310 ctx.description().split(b'\n', 1)[0],
3311 )
3311 )
3312 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3312 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3313 if names:
3313 if names:
3314 desc += b' (%s)' % b' '.join(names)
3314 desc += b' (%s)' % b' '.join(names)
3315 ui.status(_(b'grafting %s\n') % desc)
3315 ui.status(_(b'grafting %s\n') % desc)
3316 if opts.get('dry_run'):
3316 if opts.get('dry_run'):
3317 continue
3317 continue
3318
3318
3319 source = ctx.extra().get(b'source')
3319 source = ctx.extra().get(b'source')
3320 extra = {}
3320 extra = {}
3321 if source:
3321 if source:
3322 extra[b'source'] = source
3322 extra[b'source'] = source
3323 extra[b'intermediate-source'] = ctx.hex()
3323 extra[b'intermediate-source'] = ctx.hex()
3324 else:
3324 else:
3325 extra[b'source'] = ctx.hex()
3325 extra[b'source'] = ctx.hex()
3326 user = ctx.user()
3326 user = ctx.user()
3327 if opts.get('user'):
3327 if opts.get('user'):
3328 user = opts['user']
3328 user = opts['user']
3329 statedata[b'user'] = user
3329 statedata[b'user'] = user
3330 date = ctx.date()
3330 date = ctx.date()
3331 if opts.get('date'):
3331 if opts.get('date'):
3332 date = opts['date']
3332 date = opts['date']
3333 statedata[b'date'] = date
3333 statedata[b'date'] = date
3334 message = ctx.description()
3334 message = ctx.description()
3335 if opts.get('log'):
3335 if opts.get('log'):
3336 message += b'\n(grafted from %s)' % ctx.hex()
3336 message += b'\n(grafted from %s)' % ctx.hex()
3337 statedata[b'log'] = True
3337 statedata[b'log'] = True
3338
3338
3339 # we don't merge the first commit when continuing
3339 # we don't merge the first commit when continuing
3340 if not cont:
3340 if not cont:
3341 # perform the graft merge with p1(rev) as 'ancestor'
3341 # perform the graft merge with p1(rev) as 'ancestor'
3342 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3342 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3343 base = ctx.p1() if basectx is None else basectx
3343 base = ctx.p1() if basectx is None else basectx
3344 with ui.configoverride(overrides, b'graft'):
3344 with ui.configoverride(overrides, b'graft'):
3345 stats = mergemod.graft(
3345 stats = mergemod.graft(
3346 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3346 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3347 )
3347 )
3348 # report any conflicts
3348 # report any conflicts
3349 if stats.unresolvedcount > 0:
3349 if stats.unresolvedcount > 0:
3350 # write out state for --continue
3350 # write out state for --continue
3351 nodes = [repo[rev].hex() for rev in revs[pos:]]
3351 nodes = [repo[rev].hex() for rev in revs[pos:]]
3352 statedata[b'nodes'] = nodes
3352 statedata[b'nodes'] = nodes
3353 stateversion = 1
3353 stateversion = 1
3354 graftstate.save(stateversion, statedata)
3354 graftstate.save(stateversion, statedata)
3355 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3355 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3356 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3356 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3357 return 1
3357 return 1
3358 else:
3358 else:
3359 cont = False
3359 cont = False
3360
3360
3361 # commit if --no-commit is false
3361 # commit if --no-commit is false
3362 if not opts.get('no_commit'):
3362 if not opts.get('no_commit'):
3363 node = repo.commit(
3363 node = repo.commit(
3364 text=message, user=user, date=date, extra=extra, editor=editor
3364 text=message, user=user, date=date, extra=extra, editor=editor
3365 )
3365 )
3366 if node is None:
3366 if node is None:
3367 ui.warn(
3367 ui.warn(
3368 _(b'note: graft of %d:%s created no changes to commit\n')
3368 _(b'note: graft of %d:%s created no changes to commit\n')
3369 % (ctx.rev(), ctx)
3369 % (ctx.rev(), ctx)
3370 )
3370 )
3371 # checking that newnodes exist because old state files won't have it
3371 # checking that newnodes exist because old state files won't have it
3372 elif statedata.get(b'newnodes') is not None:
3372 elif statedata.get(b'newnodes') is not None:
3373 nn = statedata[b'newnodes']
3373 nn = statedata[b'newnodes']
3374 assert isinstance(nn, list) # list of bytes
3374 assert isinstance(nn, list) # list of bytes
3375 nn.append(node)
3375 nn.append(node)
3376
3376
3377 # remove state when we complete successfully
3377 # remove state when we complete successfully
3378 if not opts.get('dry_run'):
3378 if not opts.get('dry_run'):
3379 graftstate.delete()
3379 graftstate.delete()
3380
3380
3381 return 0
3381 return 0
3382
3382
3383
3383
3384 def _stopgraft(ui, repo, graftstate):
3384 def _stopgraft(ui, repo, graftstate):
3385 """stop the interrupted graft"""
3385 """stop the interrupted graft"""
3386 if not graftstate.exists():
3386 if not graftstate.exists():
3387 raise error.StateError(_(b"no interrupted graft found"))
3387 raise error.StateError(_(b"no interrupted graft found"))
3388 pctx = repo[b'.']
3388 pctx = repo[b'.']
3389 mergemod.clean_update(pctx)
3389 mergemod.clean_update(pctx)
3390 graftstate.delete()
3390 graftstate.delete()
3391 ui.status(_(b"stopped the interrupted graft\n"))
3391 ui.status(_(b"stopped the interrupted graft\n"))
3392 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3392 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3393 return 0
3393 return 0
3394
3394
3395
3395
3396 statemod.addunfinished(
3396 statemod.addunfinished(
3397 b'graft',
3397 b'graft',
3398 fname=b'graftstate',
3398 fname=b'graftstate',
3399 clearable=True,
3399 clearable=True,
3400 stopflag=True,
3400 stopflag=True,
3401 continueflag=True,
3401 continueflag=True,
3402 abortfunc=cmdutil.hgabortgraft,
3402 abortfunc=cmdutil.hgabortgraft,
3403 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3403 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3404 )
3404 )
3405
3405
3406
3406
3407 @command(
3407 @command(
3408 b'grep',
3408 b'grep',
3409 [
3409 [
3410 (b'0', b'print0', None, _(b'end fields with NUL')),
3410 (b'0', b'print0', None, _(b'end fields with NUL')),
3411 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3411 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3412 (
3412 (
3413 b'',
3413 b'',
3414 b'diff',
3414 b'diff',
3415 None,
3415 None,
3416 _(
3416 _(
3417 b'search revision differences for when the pattern was added '
3417 b'search revision differences for when the pattern was added '
3418 b'or removed'
3418 b'or removed'
3419 ),
3419 ),
3420 ),
3420 ),
3421 (b'a', b'text', None, _(b'treat all files as text')),
3421 (b'a', b'text', None, _(b'treat all files as text')),
3422 (
3422 (
3423 b'f',
3423 b'f',
3424 b'follow',
3424 b'follow',
3425 None,
3425 None,
3426 _(
3426 _(
3427 b'follow changeset history,'
3427 b'follow changeset history,'
3428 b' or file history across copies and renames'
3428 b' or file history across copies and renames'
3429 ),
3429 ),
3430 ),
3430 ),
3431 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3431 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3432 (
3432 (
3433 b'l',
3433 b'l',
3434 b'files-with-matches',
3434 b'files-with-matches',
3435 None,
3435 None,
3436 _(b'print only filenames and revisions that match'),
3436 _(b'print only filenames and revisions that match'),
3437 ),
3437 ),
3438 (b'n', b'line-number', None, _(b'print matching line numbers')),
3438 (b'n', b'line-number', None, _(b'print matching line numbers')),
3439 (
3439 (
3440 b'r',
3440 b'r',
3441 b'rev',
3441 b'rev',
3442 [],
3442 [],
3443 _(b'search files changed within revision range'),
3443 _(b'search files changed within revision range'),
3444 _(b'REV'),
3444 _(b'REV'),
3445 ),
3445 ),
3446 (
3446 (
3447 b'',
3447 b'',
3448 b'all-files',
3448 b'all-files',
3449 None,
3449 None,
3450 _(
3450 _(
3451 b'include all files in the changeset while grepping (DEPRECATED)'
3451 b'include all files in the changeset while grepping (DEPRECATED)'
3452 ),
3452 ),
3453 ),
3453 ),
3454 (b'u', b'user', None, _(b'list the author (long with -v)')),
3454 (b'u', b'user', None, _(b'list the author (long with -v)')),
3455 (b'd', b'date', None, _(b'list the date (short with -q)')),
3455 (b'd', b'date', None, _(b'list the date (short with -q)')),
3456 ]
3456 ]
3457 + formatteropts
3457 + formatteropts
3458 + walkopts,
3458 + walkopts,
3459 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3459 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3460 helpcategory=command.CATEGORY_FILE_CONTENTS,
3460 helpcategory=command.CATEGORY_FILE_CONTENTS,
3461 inferrepo=True,
3461 inferrepo=True,
3462 intents={INTENT_READONLY},
3462 intents={INTENT_READONLY},
3463 )
3463 )
3464 def grep(ui, repo, pattern, *pats, **opts):
3464 def grep(ui, repo, pattern, *pats, **opts):
3465 """search for a pattern in specified files
3465 """search for a pattern in specified files
3466
3466
3467 Search the working directory or revision history for a regular
3467 Search the working directory or revision history for a regular
3468 expression in the specified files for the entire repository.
3468 expression in the specified files for the entire repository.
3469
3469
3470 By default, grep searches the repository files in the working
3470 By default, grep searches the repository files in the working
3471 directory and prints the files where it finds a match. To specify
3471 directory and prints the files where it finds a match. To specify
3472 historical revisions instead of the working directory, use the
3472 historical revisions instead of the working directory, use the
3473 --rev flag.
3473 --rev flag.
3474
3474
3475 To search instead historical revision differences that contains a
3475 To search instead historical revision differences that contains a
3476 change in match status ("-" for a match that becomes a non-match,
3476 change in match status ("-" for a match that becomes a non-match,
3477 or "+" for a non-match that becomes a match), use the --diff flag.
3477 or "+" for a non-match that becomes a match), use the --diff flag.
3478
3478
3479 PATTERN can be any Python (roughly Perl-compatible) regular
3479 PATTERN can be any Python (roughly Perl-compatible) regular
3480 expression.
3480 expression.
3481
3481
3482 If no FILEs are specified and the --rev flag isn't supplied, all
3482 If no FILEs are specified and the --rev flag isn't supplied, all
3483 files in the working directory are searched. When using the --rev
3483 files in the working directory are searched. When using the --rev
3484 flag and specifying FILEs, use the --follow argument to also
3484 flag and specifying FILEs, use the --follow argument to also
3485 follow the specified FILEs across renames and copies.
3485 follow the specified FILEs across renames and copies.
3486
3486
3487 .. container:: verbose
3487 .. container:: verbose
3488
3488
3489 Template:
3489 Template:
3490
3490
3491 The following keywords are supported in addition to the common template
3491 The following keywords are supported in addition to the common template
3492 keywords and functions. See also :hg:`help templates`.
3492 keywords and functions. See also :hg:`help templates`.
3493
3493
3494 :change: String. Character denoting insertion ``+`` or removal ``-``.
3494 :change: String. Character denoting insertion ``+`` or removal ``-``.
3495 Available if ``--diff`` is specified.
3495 Available if ``--diff`` is specified.
3496 :lineno: Integer. Line number of the match.
3496 :lineno: Integer. Line number of the match.
3497 :path: String. Repository-absolute path of the file.
3497 :path: String. Repository-absolute path of the file.
3498 :texts: List of text chunks.
3498 :texts: List of text chunks.
3499
3499
3500 And each entry of ``{texts}`` provides the following sub-keywords.
3500 And each entry of ``{texts}`` provides the following sub-keywords.
3501
3501
3502 :matched: Boolean. True if the chunk matches the specified pattern.
3502 :matched: Boolean. True if the chunk matches the specified pattern.
3503 :text: String. Chunk content.
3503 :text: String. Chunk content.
3504
3504
3505 See :hg:`help templates.operators` for the list expansion syntax.
3505 See :hg:`help templates.operators` for the list expansion syntax.
3506
3506
3507 Returns 0 if a match is found, 1 otherwise.
3507 Returns 0 if a match is found, 1 otherwise.
3508
3508
3509 """
3509 """
3510 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3510 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3511 opts = pycompat.byteskwargs(opts)
3511 opts = pycompat.byteskwargs(opts)
3512 diff = opts.get(b'all') or opts.get(b'diff')
3512 diff = opts.get(b'all') or opts.get(b'diff')
3513 follow = opts.get(b'follow')
3513 follow = opts.get(b'follow')
3514 if opts.get(b'all_files') is None and not diff:
3514 if opts.get(b'all_files') is None and not diff:
3515 opts[b'all_files'] = True
3515 opts[b'all_files'] = True
3516 plaingrep = (
3516 plaingrep = (
3517 opts.get(b'all_files')
3517 opts.get(b'all_files')
3518 and not opts.get(b'rev')
3518 and not opts.get(b'rev')
3519 and not opts.get(b'follow')
3519 and not opts.get(b'follow')
3520 )
3520 )
3521 all_files = opts.get(b'all_files')
3521 all_files = opts.get(b'all_files')
3522 if plaingrep:
3522 if plaingrep:
3523 opts[b'rev'] = [b'wdir()']
3523 opts[b'rev'] = [b'wdir()']
3524
3524
3525 reflags = re.M
3525 reflags = re.M
3526 if opts.get(b'ignore_case'):
3526 if opts.get(b'ignore_case'):
3527 reflags |= re.I
3527 reflags |= re.I
3528 try:
3528 try:
3529 regexp = util.re.compile(pattern, reflags)
3529 regexp = util.re.compile(pattern, reflags)
3530 except re.error as inst:
3530 except re.error as inst:
3531 ui.warn(
3531 ui.warn(
3532 _(b"grep: invalid match pattern: %s\n")
3532 _(b"grep: invalid match pattern: %s\n")
3533 % stringutil.forcebytestr(inst)
3533 % stringutil.forcebytestr(inst)
3534 )
3534 )
3535 return 1
3535 return 1
3536 sep, eol = b':', b'\n'
3536 sep, eol = b':', b'\n'
3537 if opts.get(b'print0'):
3537 if opts.get(b'print0'):
3538 sep = eol = b'\0'
3538 sep = eol = b'\0'
3539
3539
3540 searcher = grepmod.grepsearcher(
3540 searcher = grepmod.grepsearcher(
3541 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3541 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3542 )
3542 )
3543
3543
3544 getfile = searcher._getfile
3544 getfile = searcher._getfile
3545
3545
3546 uipathfn = scmutil.getuipathfn(repo)
3546 uipathfn = scmutil.getuipathfn(repo)
3547
3547
3548 def display(fm, fn, ctx, pstates, states):
3548 def display(fm, fn, ctx, pstates, states):
3549 rev = scmutil.intrev(ctx)
3549 rev = scmutil.intrev(ctx)
3550 if fm.isplain():
3550 if fm.isplain():
3551 formatuser = ui.shortuser
3551 formatuser = ui.shortuser
3552 else:
3552 else:
3553 formatuser = pycompat.bytestr
3553 formatuser = pycompat.bytestr
3554 if ui.quiet:
3554 if ui.quiet:
3555 datefmt = b'%Y-%m-%d'
3555 datefmt = b'%Y-%m-%d'
3556 else:
3556 else:
3557 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3557 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3558 found = False
3558 found = False
3559
3559
3560 @util.cachefunc
3560 @util.cachefunc
3561 def binary():
3561 def binary():
3562 flog = getfile(fn)
3562 flog = getfile(fn)
3563 try:
3563 try:
3564 return stringutil.binary(flog.read(ctx.filenode(fn)))
3564 return stringutil.binary(flog.read(ctx.filenode(fn)))
3565 except error.WdirUnsupported:
3565 except error.WdirUnsupported:
3566 return ctx[fn].isbinary()
3566 return ctx[fn].isbinary()
3567
3567
3568 fieldnamemap = {b'linenumber': b'lineno'}
3568 fieldnamemap = {b'linenumber': b'lineno'}
3569 if diff:
3569 if diff:
3570 iter = grepmod.difflinestates(pstates, states)
3570 iter = grepmod.difflinestates(pstates, states)
3571 else:
3571 else:
3572 iter = [(b'', l) for l in states]
3572 iter = [(b'', l) for l in states]
3573 for change, l in iter:
3573 for change, l in iter:
3574 fm.startitem()
3574 fm.startitem()
3575 fm.context(ctx=ctx)
3575 fm.context(ctx=ctx)
3576 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3576 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3577 fm.plain(uipathfn(fn), label=b'grep.filename')
3577 fm.plain(uipathfn(fn), label=b'grep.filename')
3578
3578
3579 cols = [
3579 cols = [
3580 (b'rev', b'%d', rev, not plaingrep, b''),
3580 (b'rev', b'%d', rev, not plaingrep, b''),
3581 (
3581 (
3582 b'linenumber',
3582 b'linenumber',
3583 b'%d',
3583 b'%d',
3584 l.linenum,
3584 l.linenum,
3585 opts.get(b'line_number'),
3585 opts.get(b'line_number'),
3586 b'',
3586 b'',
3587 ),
3587 ),
3588 ]
3588 ]
3589 if diff:
3589 if diff:
3590 cols.append(
3590 cols.append(
3591 (
3591 (
3592 b'change',
3592 b'change',
3593 b'%s',
3593 b'%s',
3594 change,
3594 change,
3595 True,
3595 True,
3596 b'grep.inserted '
3596 b'grep.inserted '
3597 if change == b'+'
3597 if change == b'+'
3598 else b'grep.deleted ',
3598 else b'grep.deleted ',
3599 )
3599 )
3600 )
3600 )
3601 cols.extend(
3601 cols.extend(
3602 [
3602 [
3603 (
3603 (
3604 b'user',
3604 b'user',
3605 b'%s',
3605 b'%s',
3606 formatuser(ctx.user()),
3606 formatuser(ctx.user()),
3607 opts.get(b'user'),
3607 opts.get(b'user'),
3608 b'',
3608 b'',
3609 ),
3609 ),
3610 (
3610 (
3611 b'date',
3611 b'date',
3612 b'%s',
3612 b'%s',
3613 fm.formatdate(ctx.date(), datefmt),
3613 fm.formatdate(ctx.date(), datefmt),
3614 opts.get(b'date'),
3614 opts.get(b'date'),
3615 b'',
3615 b'',
3616 ),
3616 ),
3617 ]
3617 ]
3618 )
3618 )
3619 for name, fmt, data, cond, extra_label in cols:
3619 for name, fmt, data, cond, extra_label in cols:
3620 if cond:
3620 if cond:
3621 fm.plain(sep, label=b'grep.sep')
3621 fm.plain(sep, label=b'grep.sep')
3622 field = fieldnamemap.get(name, name)
3622 field = fieldnamemap.get(name, name)
3623 label = extra_label + (b'grep.%s' % name)
3623 label = extra_label + (b'grep.%s' % name)
3624 fm.condwrite(cond, field, fmt, data, label=label)
3624 fm.condwrite(cond, field, fmt, data, label=label)
3625 if not opts.get(b'files_with_matches'):
3625 if not opts.get(b'files_with_matches'):
3626 fm.plain(sep, label=b'grep.sep')
3626 fm.plain(sep, label=b'grep.sep')
3627 if not opts.get(b'text') and binary():
3627 if not opts.get(b'text') and binary():
3628 fm.plain(_(b" Binary file matches"))
3628 fm.plain(_(b" Binary file matches"))
3629 else:
3629 else:
3630 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3630 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3631 fm.plain(eol)
3631 fm.plain(eol)
3632 found = True
3632 found = True
3633 if opts.get(b'files_with_matches'):
3633 if opts.get(b'files_with_matches'):
3634 break
3634 break
3635 return found
3635 return found
3636
3636
3637 def displaymatches(fm, l):
3637 def displaymatches(fm, l):
3638 p = 0
3638 p = 0
3639 for s, e in l.findpos(regexp):
3639 for s, e in l.findpos(regexp):
3640 if p < s:
3640 if p < s:
3641 fm.startitem()
3641 fm.startitem()
3642 fm.write(b'text', b'%s', l.line[p:s])
3642 fm.write(b'text', b'%s', l.line[p:s])
3643 fm.data(matched=False)
3643 fm.data(matched=False)
3644 fm.startitem()
3644 fm.startitem()
3645 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3645 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3646 fm.data(matched=True)
3646 fm.data(matched=True)
3647 p = e
3647 p = e
3648 if p < len(l.line):
3648 if p < len(l.line):
3649 fm.startitem()
3649 fm.startitem()
3650 fm.write(b'text', b'%s', l.line[p:])
3650 fm.write(b'text', b'%s', l.line[p:])
3651 fm.data(matched=False)
3651 fm.data(matched=False)
3652 fm.end()
3652 fm.end()
3653
3653
3654 found = False
3654 found = False
3655
3655
3656 wopts = logcmdutil.walkopts(
3656 wopts = logcmdutil.walkopts(
3657 pats=pats,
3657 pats=pats,
3658 opts=opts,
3658 opts=opts,
3659 revspec=opts[b'rev'],
3659 revspec=opts[b'rev'],
3660 include_pats=opts[b'include'],
3660 include_pats=opts[b'include'],
3661 exclude_pats=opts[b'exclude'],
3661 exclude_pats=opts[b'exclude'],
3662 follow=follow,
3662 follow=follow,
3663 force_changelog_traversal=all_files,
3663 force_changelog_traversal=all_files,
3664 filter_revisions_by_pats=not all_files,
3664 filter_revisions_by_pats=not all_files,
3665 )
3665 )
3666 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3666 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3667
3667
3668 ui.pager(b'grep')
3668 ui.pager(b'grep')
3669 fm = ui.formatter(b'grep', opts)
3669 fm = ui.formatter(b'grep', opts)
3670 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3670 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3671 r = display(fm, fn, ctx, pstates, states)
3671 r = display(fm, fn, ctx, pstates, states)
3672 found = found or r
3672 found = found or r
3673 if r and not diff and not all_files:
3673 if r and not diff and not all_files:
3674 searcher.skipfile(fn, ctx.rev())
3674 searcher.skipfile(fn, ctx.rev())
3675 fm.end()
3675 fm.end()
3676
3676
3677 return not found
3677 return not found
3678
3678
3679
3679
3680 @command(
3680 @command(
3681 b'heads',
3681 b'heads',
3682 [
3682 [
3683 (
3683 (
3684 b'r',
3684 b'r',
3685 b'rev',
3685 b'rev',
3686 b'',
3686 b'',
3687 _(b'show only heads which are descendants of STARTREV'),
3687 _(b'show only heads which are descendants of STARTREV'),
3688 _(b'STARTREV'),
3688 _(b'STARTREV'),
3689 ),
3689 ),
3690 (b't', b'topo', False, _(b'show topological heads only')),
3690 (b't', b'topo', False, _(b'show topological heads only')),
3691 (
3691 (
3692 b'a',
3692 b'a',
3693 b'active',
3693 b'active',
3694 False,
3694 False,
3695 _(b'show active branchheads only (DEPRECATED)'),
3695 _(b'show active branchheads only (DEPRECATED)'),
3696 ),
3696 ),
3697 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3697 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3698 ]
3698 ]
3699 + templateopts,
3699 + templateopts,
3700 _(b'[-ct] [-r STARTREV] [REV]...'),
3700 _(b'[-ct] [-r STARTREV] [REV]...'),
3701 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3701 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3702 intents={INTENT_READONLY},
3702 intents={INTENT_READONLY},
3703 )
3703 )
3704 def heads(ui, repo, *branchrevs, **opts):
3704 def heads(ui, repo, *branchrevs, **opts):
3705 """show branch heads
3705 """show branch heads
3706
3706
3707 With no arguments, show all open branch heads in the repository.
3707 With no arguments, show all open branch heads in the repository.
3708 Branch heads are changesets that have no descendants on the
3708 Branch heads are changesets that have no descendants on the
3709 same branch. They are where development generally takes place and
3709 same branch. They are where development generally takes place and
3710 are the usual targets for update and merge operations.
3710 are the usual targets for update and merge operations.
3711
3711
3712 If one or more REVs are given, only open branch heads on the
3712 If one or more REVs are given, only open branch heads on the
3713 branches associated with the specified changesets are shown. This
3713 branches associated with the specified changesets are shown. This
3714 means that you can use :hg:`heads .` to see the heads on the
3714 means that you can use :hg:`heads .` to see the heads on the
3715 currently checked-out branch.
3715 currently checked-out branch.
3716
3716
3717 If -c/--closed is specified, also show branch heads marked closed
3717 If -c/--closed is specified, also show branch heads marked closed
3718 (see :hg:`commit --close-branch`).
3718 (see :hg:`commit --close-branch`).
3719
3719
3720 If STARTREV is specified, only those heads that are descendants of
3720 If STARTREV is specified, only those heads that are descendants of
3721 STARTREV will be displayed.
3721 STARTREV will be displayed.
3722
3722
3723 If -t/--topo is specified, named branch mechanics will be ignored and only
3723 If -t/--topo is specified, named branch mechanics will be ignored and only
3724 topological heads (changesets with no children) will be shown.
3724 topological heads (changesets with no children) will be shown.
3725
3725
3726 Returns 0 if matching heads are found, 1 if not.
3726 Returns 0 if matching heads are found, 1 if not.
3727 """
3727 """
3728
3728
3729 opts = pycompat.byteskwargs(opts)
3729 opts = pycompat.byteskwargs(opts)
3730 start = None
3730 start = None
3731 rev = opts.get(b'rev')
3731 rev = opts.get(b'rev')
3732 if rev:
3732 if rev:
3733 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3733 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3734 start = logcmdutil.revsingle(repo, rev, None).node()
3734 start = logcmdutil.revsingle(repo, rev, None).node()
3735
3735
3736 if opts.get(b'topo'):
3736 if opts.get(b'topo'):
3737 heads = [repo[h] for h in repo.heads(start)]
3737 heads = [repo[h] for h in repo.heads(start)]
3738 else:
3738 else:
3739 heads = []
3739 heads = []
3740 for branch in repo.branchmap():
3740 for branch in repo.branchmap():
3741 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3741 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3742 heads = [repo[h] for h in heads]
3742 heads = [repo[h] for h in heads]
3743
3743
3744 if branchrevs:
3744 if branchrevs:
3745 branches = {
3745 branches = {
3746 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3746 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3747 }
3747 }
3748 heads = [h for h in heads if h.branch() in branches]
3748 heads = [h for h in heads if h.branch() in branches]
3749
3749
3750 if opts.get(b'active') and branchrevs:
3750 if opts.get(b'active') and branchrevs:
3751 dagheads = repo.heads(start)
3751 dagheads = repo.heads(start)
3752 heads = [h for h in heads if h.node() in dagheads]
3752 heads = [h for h in heads if h.node() in dagheads]
3753
3753
3754 if branchrevs:
3754 if branchrevs:
3755 haveheads = {h.branch() for h in heads}
3755 haveheads = {h.branch() for h in heads}
3756 if branches - haveheads:
3756 if branches - haveheads:
3757 headless = b', '.join(b for b in branches - haveheads)
3757 headless = b', '.join(b for b in branches - haveheads)
3758 msg = _(b'no open branch heads found on branches %s')
3758 msg = _(b'no open branch heads found on branches %s')
3759 if opts.get(b'rev'):
3759 if opts.get(b'rev'):
3760 msg += _(b' (started at %s)') % opts[b'rev']
3760 msg += _(b' (started at %s)') % opts[b'rev']
3761 ui.warn((msg + b'\n') % headless)
3761 ui.warn((msg + b'\n') % headless)
3762
3762
3763 if not heads:
3763 if not heads:
3764 return 1
3764 return 1
3765
3765
3766 ui.pager(b'heads')
3766 ui.pager(b'heads')
3767 heads = sorted(heads, key=lambda x: -(x.rev()))
3767 heads = sorted(heads, key=lambda x: -(x.rev()))
3768 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3768 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3769 for ctx in heads:
3769 for ctx in heads:
3770 displayer.show(ctx)
3770 displayer.show(ctx)
3771 displayer.close()
3771 displayer.close()
3772
3772
3773
3773
3774 @command(
3774 @command(
3775 b'help',
3775 b'help',
3776 [
3776 [
3777 (b'e', b'extension', None, _(b'show only help for extensions')),
3777 (b'e', b'extension', None, _(b'show only help for extensions')),
3778 (b'c', b'command', None, _(b'show only help for commands')),
3778 (b'c', b'command', None, _(b'show only help for commands')),
3779 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3779 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3780 (
3780 (
3781 b's',
3781 b's',
3782 b'system',
3782 b'system',
3783 [],
3783 [],
3784 _(b'show help for specific platform(s)'),
3784 _(b'show help for specific platform(s)'),
3785 _(b'PLATFORM'),
3785 _(b'PLATFORM'),
3786 ),
3786 ),
3787 ],
3787 ],
3788 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3788 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3789 helpcategory=command.CATEGORY_HELP,
3789 helpcategory=command.CATEGORY_HELP,
3790 norepo=True,
3790 norepo=True,
3791 intents={INTENT_READONLY},
3791 intents={INTENT_READONLY},
3792 )
3792 )
3793 def help_(ui, name=None, **opts):
3793 def help_(ui, name=None, **opts):
3794 """show help for a given topic or a help overview
3794 """show help for a given topic or a help overview
3795
3795
3796 With no arguments, print a list of commands with short help messages.
3796 With no arguments, print a list of commands with short help messages.
3797
3797
3798 Given a topic, extension, or command name, print help for that
3798 Given a topic, extension, or command name, print help for that
3799 topic.
3799 topic.
3800
3800
3801 Returns 0 if successful.
3801 Returns 0 if successful.
3802 """
3802 """
3803
3803
3804 keep = opts.get('system') or []
3804 keep = opts.get('system') or []
3805 if len(keep) == 0:
3805 if len(keep) == 0:
3806 if pycompat.sysplatform.startswith(b'win'):
3806 if pycompat.sysplatform.startswith(b'win'):
3807 keep.append(b'windows')
3807 keep.append(b'windows')
3808 elif pycompat.sysplatform == b'OpenVMS':
3808 elif pycompat.sysplatform == b'OpenVMS':
3809 keep.append(b'vms')
3809 keep.append(b'vms')
3810 elif pycompat.sysplatform == b'plan9':
3810 elif pycompat.sysplatform == b'plan9':
3811 keep.append(b'plan9')
3811 keep.append(b'plan9')
3812 else:
3812 else:
3813 keep.append(b'unix')
3813 keep.append(b'unix')
3814 keep.append(pycompat.sysplatform.lower())
3814 keep.append(pycompat.sysplatform.lower())
3815 if ui.verbose:
3815 if ui.verbose:
3816 keep.append(b'verbose')
3816 keep.append(b'verbose')
3817
3817
3818 commands = sys.modules[__name__]
3818 commands = sys.modules[__name__]
3819 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3819 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3820 ui.pager(b'help')
3820 ui.pager(b'help')
3821 ui.write(formatted)
3821 ui.write(formatted)
3822
3822
3823
3823
3824 @command(
3824 @command(
3825 b'identify|id',
3825 b'identify|id',
3826 [
3826 [
3827 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3827 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3828 (b'n', b'num', None, _(b'show local revision number')),
3828 (b'n', b'num', None, _(b'show local revision number')),
3829 (b'i', b'id', None, _(b'show global revision id')),
3829 (b'i', b'id', None, _(b'show global revision id')),
3830 (b'b', b'branch', None, _(b'show branch')),
3830 (b'b', b'branch', None, _(b'show branch')),
3831 (b't', b'tags', None, _(b'show tags')),
3831 (b't', b'tags', None, _(b'show tags')),
3832 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3832 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3833 ]
3833 ]
3834 + remoteopts
3834 + remoteopts
3835 + formatteropts,
3835 + formatteropts,
3836 _(b'[-nibtB] [-r REV] [SOURCE]'),
3836 _(b'[-nibtB] [-r REV] [SOURCE]'),
3837 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3837 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3838 optionalrepo=True,
3838 optionalrepo=True,
3839 intents={INTENT_READONLY},
3839 intents={INTENT_READONLY},
3840 )
3840 )
3841 def identify(
3841 def identify(
3842 ui,
3842 ui,
3843 repo,
3843 repo,
3844 source=None,
3844 source=None,
3845 rev=None,
3845 rev=None,
3846 num=None,
3846 num=None,
3847 id=None,
3847 id=None,
3848 branch=None,
3848 branch=None,
3849 tags=None,
3849 tags=None,
3850 bookmarks=None,
3850 bookmarks=None,
3851 **opts
3851 **opts
3852 ):
3852 ):
3853 """identify the working directory or specified revision
3853 """identify the working directory or specified revision
3854
3854
3855 Print a summary identifying the repository state at REV using one or
3855 Print a summary identifying the repository state at REV using one or
3856 two parent hash identifiers, followed by a "+" if the working
3856 two parent hash identifiers, followed by a "+" if the working
3857 directory has uncommitted changes, the branch name (if not default),
3857 directory has uncommitted changes, the branch name (if not default),
3858 a list of tags, and a list of bookmarks.
3858 a list of tags, and a list of bookmarks.
3859
3859
3860 When REV is not given, print a summary of the current state of the
3860 When REV is not given, print a summary of the current state of the
3861 repository including the working directory. Specify -r. to get information
3861 repository including the working directory. Specify -r. to get information
3862 of the working directory parent without scanning uncommitted changes.
3862 of the working directory parent without scanning uncommitted changes.
3863
3863
3864 Specifying a path to a repository root or Mercurial bundle will
3864 Specifying a path to a repository root or Mercurial bundle will
3865 cause lookup to operate on that repository/bundle.
3865 cause lookup to operate on that repository/bundle.
3866
3866
3867 .. container:: verbose
3867 .. container:: verbose
3868
3868
3869 Template:
3869 Template:
3870
3870
3871 The following keywords are supported in addition to the common template
3871 The following keywords are supported in addition to the common template
3872 keywords and functions. See also :hg:`help templates`.
3872 keywords and functions. See also :hg:`help templates`.
3873
3873
3874 :dirty: String. Character ``+`` denoting if the working directory has
3874 :dirty: String. Character ``+`` denoting if the working directory has
3875 uncommitted changes.
3875 uncommitted changes.
3876 :id: String. One or two nodes, optionally followed by ``+``.
3876 :id: String. One or two nodes, optionally followed by ``+``.
3877 :parents: List of strings. Parent nodes of the changeset.
3877 :parents: List of strings. Parent nodes of the changeset.
3878
3878
3879 Examples:
3879 Examples:
3880
3880
3881 - generate a build identifier for the working directory::
3881 - generate a build identifier for the working directory::
3882
3882
3883 hg id --id > build-id.dat
3883 hg id --id > build-id.dat
3884
3884
3885 - find the revision corresponding to a tag::
3885 - find the revision corresponding to a tag::
3886
3886
3887 hg id -n -r 1.3
3887 hg id -n -r 1.3
3888
3888
3889 - check the most recent revision of a remote repository::
3889 - check the most recent revision of a remote repository::
3890
3890
3891 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3891 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3892
3892
3893 See :hg:`log` for generating more information about specific revisions,
3893 See :hg:`log` for generating more information about specific revisions,
3894 including full hash identifiers.
3894 including full hash identifiers.
3895
3895
3896 Returns 0 if successful.
3896 Returns 0 if successful.
3897 """
3897 """
3898
3898
3899 opts = pycompat.byteskwargs(opts)
3899 opts = pycompat.byteskwargs(opts)
3900 if not repo and not source:
3900 if not repo and not source:
3901 raise error.InputError(
3901 raise error.InputError(
3902 _(b"there is no Mercurial repository here (.hg not found)")
3902 _(b"there is no Mercurial repository here (.hg not found)")
3903 )
3903 )
3904
3904
3905 default = not (num or id or branch or tags or bookmarks)
3905 default = not (num or id or branch or tags or bookmarks)
3906 output = []
3906 output = []
3907 revs = []
3907 revs = []
3908
3908
3909 peer = None
3909 peer = None
3910 try:
3910 try:
3911 if source:
3911 if source:
3912 source, branches = urlutil.get_unique_pull_path(
3912 source, branches = urlutil.get_unique_pull_path(
3913 b'identify', repo, ui, source
3913 b'identify', repo, ui, source
3914 )
3914 )
3915 # only pass ui when no repo
3915 # only pass ui when no repo
3916 peer = hg.peer(repo or ui, opts, source)
3916 peer = hg.peer(repo or ui, opts, source)
3917 repo = peer.local()
3917 repo = peer.local()
3918 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3918 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3919
3919
3920 fm = ui.formatter(b'identify', opts)
3920 fm = ui.formatter(b'identify', opts)
3921 fm.startitem()
3921 fm.startitem()
3922
3922
3923 if not repo:
3923 if not repo:
3924 if num or branch or tags:
3924 if num or branch or tags:
3925 raise error.InputError(
3925 raise error.InputError(
3926 _(b"can't query remote revision number, branch, or tags")
3926 _(b"can't query remote revision number, branch, or tags")
3927 )
3927 )
3928 if not rev and revs:
3928 if not rev and revs:
3929 rev = revs[0]
3929 rev = revs[0]
3930 if not rev:
3930 if not rev:
3931 rev = b"tip"
3931 rev = b"tip"
3932
3932
3933 remoterev = peer.lookup(rev)
3933 remoterev = peer.lookup(rev)
3934 hexrev = fm.hexfunc(remoterev)
3934 hexrev = fm.hexfunc(remoterev)
3935 if default or id:
3935 if default or id:
3936 output = [hexrev]
3936 output = [hexrev]
3937 fm.data(id=hexrev)
3937 fm.data(id=hexrev)
3938
3938
3939 @util.cachefunc
3939 @util.cachefunc
3940 def getbms():
3940 def getbms():
3941 bms = []
3941 bms = []
3942
3942
3943 if b'bookmarks' in peer.listkeys(b'namespaces'):
3943 if b'bookmarks' in peer.listkeys(b'namespaces'):
3944 hexremoterev = hex(remoterev)
3944 hexremoterev = hex(remoterev)
3945 bms = [
3945 bms = [
3946 bm
3946 bm
3947 for bm, bmr in peer.listkeys(b'bookmarks').items()
3947 for bm, bmr in peer.listkeys(b'bookmarks').items()
3948 if bmr == hexremoterev
3948 if bmr == hexremoterev
3949 ]
3949 ]
3950
3950
3951 return sorted(bms)
3951 return sorted(bms)
3952
3952
3953 if fm.isplain():
3953 if fm.isplain():
3954 if bookmarks:
3954 if bookmarks:
3955 output.extend(getbms())
3955 output.extend(getbms())
3956 elif default and not ui.quiet:
3956 elif default and not ui.quiet:
3957 # multiple bookmarks for a single parent separated by '/'
3957 # multiple bookmarks for a single parent separated by '/'
3958 bm = b'/'.join(getbms())
3958 bm = b'/'.join(getbms())
3959 if bm:
3959 if bm:
3960 output.append(bm)
3960 output.append(bm)
3961 else:
3961 else:
3962 fm.data(node=hex(remoterev))
3962 fm.data(node=hex(remoterev))
3963 if bookmarks or b'bookmarks' in fm.datahint():
3963 if bookmarks or b'bookmarks' in fm.datahint():
3964 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3964 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3965 else:
3965 else:
3966 if rev:
3966 if rev:
3967 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3967 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3968 ctx = logcmdutil.revsingle(repo, rev, None)
3968 ctx = logcmdutil.revsingle(repo, rev, None)
3969
3969
3970 if ctx.rev() is None:
3970 if ctx.rev() is None:
3971 ctx = repo[None]
3971 ctx = repo[None]
3972 parents = ctx.parents()
3972 parents = ctx.parents()
3973 taglist = []
3973 taglist = []
3974 for p in parents:
3974 for p in parents:
3975 taglist.extend(p.tags())
3975 taglist.extend(p.tags())
3976
3976
3977 dirty = b""
3977 dirty = b""
3978 if ctx.dirty(missing=True, merge=False, branch=False):
3978 if ctx.dirty(missing=True, merge=False, branch=False):
3979 dirty = b'+'
3979 dirty = b'+'
3980 fm.data(dirty=dirty)
3980 fm.data(dirty=dirty)
3981
3981
3982 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3982 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3983 if default or id:
3983 if default or id:
3984 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3984 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3985 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3985 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3986
3986
3987 if num:
3987 if num:
3988 numoutput = [b"%d" % p.rev() for p in parents]
3988 numoutput = [b"%d" % p.rev() for p in parents]
3989 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3989 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3990
3990
3991 fm.data(
3991 fm.data(
3992 parents=fm.formatlist(
3992 parents=fm.formatlist(
3993 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3993 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3994 )
3994 )
3995 )
3995 )
3996 else:
3996 else:
3997 hexoutput = fm.hexfunc(ctx.node())
3997 hexoutput = fm.hexfunc(ctx.node())
3998 if default or id:
3998 if default or id:
3999 output = [hexoutput]
3999 output = [hexoutput]
4000 fm.data(id=hexoutput)
4000 fm.data(id=hexoutput)
4001
4001
4002 if num:
4002 if num:
4003 output.append(pycompat.bytestr(ctx.rev()))
4003 output.append(pycompat.bytestr(ctx.rev()))
4004 taglist = ctx.tags()
4004 taglist = ctx.tags()
4005
4005
4006 if default and not ui.quiet:
4006 if default and not ui.quiet:
4007 b = ctx.branch()
4007 b = ctx.branch()
4008 if b != b'default':
4008 if b != b'default':
4009 output.append(b"(%s)" % b)
4009 output.append(b"(%s)" % b)
4010
4010
4011 # multiple tags for a single parent separated by '/'
4011 # multiple tags for a single parent separated by '/'
4012 t = b'/'.join(taglist)
4012 t = b'/'.join(taglist)
4013 if t:
4013 if t:
4014 output.append(t)
4014 output.append(t)
4015
4015
4016 # multiple bookmarks for a single parent separated by '/'
4016 # multiple bookmarks for a single parent separated by '/'
4017 bm = b'/'.join(ctx.bookmarks())
4017 bm = b'/'.join(ctx.bookmarks())
4018 if bm:
4018 if bm:
4019 output.append(bm)
4019 output.append(bm)
4020 else:
4020 else:
4021 if branch:
4021 if branch:
4022 output.append(ctx.branch())
4022 output.append(ctx.branch())
4023
4023
4024 if tags:
4024 if tags:
4025 output.extend(taglist)
4025 output.extend(taglist)
4026
4026
4027 if bookmarks:
4027 if bookmarks:
4028 output.extend(ctx.bookmarks())
4028 output.extend(ctx.bookmarks())
4029
4029
4030 fm.data(node=ctx.hex())
4030 fm.data(node=ctx.hex())
4031 fm.data(branch=ctx.branch())
4031 fm.data(branch=ctx.branch())
4032 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4032 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4033 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4033 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4034 fm.context(ctx=ctx)
4034 fm.context(ctx=ctx)
4035
4035
4036 fm.plain(b"%s\n" % b' '.join(output))
4036 fm.plain(b"%s\n" % b' '.join(output))
4037 fm.end()
4037 fm.end()
4038 finally:
4038 finally:
4039 if peer:
4039 if peer:
4040 peer.close()
4040 peer.close()
4041
4041
4042
4042
4043 @command(
4043 @command(
4044 b'import|patch',
4044 b'import|patch',
4045 [
4045 [
4046 (
4046 (
4047 b'p',
4047 b'p',
4048 b'strip',
4048 b'strip',
4049 1,
4049 1,
4050 _(
4050 _(
4051 b'directory strip option for patch. This has the same '
4051 b'directory strip option for patch. This has the same '
4052 b'meaning as the corresponding patch option'
4052 b'meaning as the corresponding patch option'
4053 ),
4053 ),
4054 _(b'NUM'),
4054 _(b'NUM'),
4055 ),
4055 ),
4056 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4056 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4057 (b'', b'secret', None, _(b'use the secret phase for committing')),
4057 (b'', b'secret', None, _(b'use the secret phase for committing')),
4058 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4058 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4059 (
4059 (
4060 b'f',
4060 b'f',
4061 b'force',
4061 b'force',
4062 None,
4062 None,
4063 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4063 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4064 ),
4064 ),
4065 (
4065 (
4066 b'',
4066 b'',
4067 b'no-commit',
4067 b'no-commit',
4068 None,
4068 None,
4069 _(b"don't commit, just update the working directory"),
4069 _(b"don't commit, just update the working directory"),
4070 ),
4070 ),
4071 (
4071 (
4072 b'',
4072 b'',
4073 b'bypass',
4073 b'bypass',
4074 None,
4074 None,
4075 _(b"apply patch without touching the working directory"),
4075 _(b"apply patch without touching the working directory"),
4076 ),
4076 ),
4077 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4077 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4078 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4078 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4079 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4079 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4080 (
4080 (
4081 b'',
4081 b'',
4082 b'import-branch',
4082 b'import-branch',
4083 None,
4083 None,
4084 _(b'use any branch information in patch (implied by --exact)'),
4084 _(b'use any branch information in patch (implied by --exact)'),
4085 ),
4085 ),
4086 ]
4086 ]
4087 + commitopts
4087 + commitopts
4088 + commitopts2
4088 + commitopts2
4089 + similarityopts,
4089 + similarityopts,
4090 _(b'[OPTION]... PATCH...'),
4090 _(b'[OPTION]... PATCH...'),
4091 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4091 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4092 )
4092 )
4093 def import_(ui, repo, patch1=None, *patches, **opts):
4093 def import_(ui, repo, patch1=None, *patches, **opts):
4094 """import an ordered set of patches
4094 """import an ordered set of patches
4095
4095
4096 Import a list of patches and commit them individually (unless
4096 Import a list of patches and commit them individually (unless
4097 --no-commit is specified).
4097 --no-commit is specified).
4098
4098
4099 To read a patch from standard input (stdin), use "-" as the patch
4099 To read a patch from standard input (stdin), use "-" as the patch
4100 name. If a URL is specified, the patch will be downloaded from
4100 name. If a URL is specified, the patch will be downloaded from
4101 there.
4101 there.
4102
4102
4103 Import first applies changes to the working directory (unless
4103 Import first applies changes to the working directory (unless
4104 --bypass is specified), import will abort if there are outstanding
4104 --bypass is specified), import will abort if there are outstanding
4105 changes.
4105 changes.
4106
4106
4107 Use --bypass to apply and commit patches directly to the
4107 Use --bypass to apply and commit patches directly to the
4108 repository, without affecting the working directory. Without
4108 repository, without affecting the working directory. Without
4109 --exact, patches will be applied on top of the working directory
4109 --exact, patches will be applied on top of the working directory
4110 parent revision.
4110 parent revision.
4111
4111
4112 You can import a patch straight from a mail message. Even patches
4112 You can import a patch straight from a mail message. Even patches
4113 as attachments work (to use the body part, it must have type
4113 as attachments work (to use the body part, it must have type
4114 text/plain or text/x-patch). From and Subject headers of email
4114 text/plain or text/x-patch). From and Subject headers of email
4115 message are used as default committer and commit message. All
4115 message are used as default committer and commit message. All
4116 text/plain body parts before first diff are added to the commit
4116 text/plain body parts before first diff are added to the commit
4117 message.
4117 message.
4118
4118
4119 If the imported patch was generated by :hg:`export`, user and
4119 If the imported patch was generated by :hg:`export`, user and
4120 description from patch override values from message headers and
4120 description from patch override values from message headers and
4121 body. Values given on command line with -m/--message and -u/--user
4121 body. Values given on command line with -m/--message and -u/--user
4122 override these.
4122 override these.
4123
4123
4124 If --exact is specified, import will set the working directory to
4124 If --exact is specified, import will set the working directory to
4125 the parent of each patch before applying it, and will abort if the
4125 the parent of each patch before applying it, and will abort if the
4126 resulting changeset has a different ID than the one recorded in
4126 resulting changeset has a different ID than the one recorded in
4127 the patch. This will guard against various ways that portable
4127 the patch. This will guard against various ways that portable
4128 patch formats and mail systems might fail to transfer Mercurial
4128 patch formats and mail systems might fail to transfer Mercurial
4129 data or metadata. See :hg:`bundle` for lossless transmission.
4129 data or metadata. See :hg:`bundle` for lossless transmission.
4130
4130
4131 Use --partial to ensure a changeset will be created from the patch
4131 Use --partial to ensure a changeset will be created from the patch
4132 even if some hunks fail to apply. Hunks that fail to apply will be
4132 even if some hunks fail to apply. Hunks that fail to apply will be
4133 written to a <target-file>.rej file. Conflicts can then be resolved
4133 written to a <target-file>.rej file. Conflicts can then be resolved
4134 by hand before :hg:`commit --amend` is run to update the created
4134 by hand before :hg:`commit --amend` is run to update the created
4135 changeset. This flag exists to let people import patches that
4135 changeset. This flag exists to let people import patches that
4136 partially apply without losing the associated metadata (author,
4136 partially apply without losing the associated metadata (author,
4137 date, description, ...).
4137 date, description, ...).
4138
4138
4139 .. note::
4139 .. note::
4140
4140
4141 When no hunks apply cleanly, :hg:`import --partial` will create
4141 When no hunks apply cleanly, :hg:`import --partial` will create
4142 an empty changeset, importing only the patch metadata.
4142 an empty changeset, importing only the patch metadata.
4143
4143
4144 With -s/--similarity, hg will attempt to discover renames and
4144 With -s/--similarity, hg will attempt to discover renames and
4145 copies in the patch in the same way as :hg:`addremove`.
4145 copies in the patch in the same way as :hg:`addremove`.
4146
4146
4147 It is possible to use external patch programs to perform the patch
4147 It is possible to use external patch programs to perform the patch
4148 by setting the ``ui.patch`` configuration option. For the default
4148 by setting the ``ui.patch`` configuration option. For the default
4149 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4149 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4150 See :hg:`help config` for more information about configuration
4150 See :hg:`help config` for more information about configuration
4151 files and how to use these options.
4151 files and how to use these options.
4152
4152
4153 See :hg:`help dates` for a list of formats valid for -d/--date.
4153 See :hg:`help dates` for a list of formats valid for -d/--date.
4154
4154
4155 .. container:: verbose
4155 .. container:: verbose
4156
4156
4157 Examples:
4157 Examples:
4158
4158
4159 - import a traditional patch from a website and detect renames::
4159 - import a traditional patch from a website and detect renames::
4160
4160
4161 hg import -s 80 http://example.com/bugfix.patch
4161 hg import -s 80 http://example.com/bugfix.patch
4162
4162
4163 - import a changeset from an hgweb server::
4163 - import a changeset from an hgweb server::
4164
4164
4165 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4165 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4166
4166
4167 - import all the patches in an Unix-style mbox::
4167 - import all the patches in an Unix-style mbox::
4168
4168
4169 hg import incoming-patches.mbox
4169 hg import incoming-patches.mbox
4170
4170
4171 - import patches from stdin::
4171 - import patches from stdin::
4172
4172
4173 hg import -
4173 hg import -
4174
4174
4175 - attempt to exactly restore an exported changeset (not always
4175 - attempt to exactly restore an exported changeset (not always
4176 possible)::
4176 possible)::
4177
4177
4178 hg import --exact proposed-fix.patch
4178 hg import --exact proposed-fix.patch
4179
4179
4180 - use an external tool to apply a patch which is too fuzzy for
4180 - use an external tool to apply a patch which is too fuzzy for
4181 the default internal tool.
4181 the default internal tool.
4182
4182
4183 hg import --config ui.patch="patch --merge" fuzzy.patch
4183 hg import --config ui.patch="patch --merge" fuzzy.patch
4184
4184
4185 - change the default fuzzing from 2 to a less strict 7
4185 - change the default fuzzing from 2 to a less strict 7
4186
4186
4187 hg import --config ui.fuzz=7 fuzz.patch
4187 hg import --config ui.fuzz=7 fuzz.patch
4188
4188
4189 Returns 0 on success, 1 on partial success (see --partial).
4189 Returns 0 on success, 1 on partial success (see --partial).
4190 """
4190 """
4191
4191
4192 cmdutil.check_incompatible_arguments(
4192 cmdutil.check_incompatible_arguments(
4193 opts, 'no_commit', ['bypass', 'secret']
4193 opts, 'no_commit', ['bypass', 'secret']
4194 )
4194 )
4195 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4195 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4196 opts = pycompat.byteskwargs(opts)
4196 opts = pycompat.byteskwargs(opts)
4197 if not patch1:
4197 if not patch1:
4198 raise error.InputError(_(b'need at least one patch to import'))
4198 raise error.InputError(_(b'need at least one patch to import'))
4199
4199
4200 patches = (patch1,) + patches
4200 patches = (patch1,) + patches
4201
4201
4202 date = opts.get(b'date')
4202 date = opts.get(b'date')
4203 if date:
4203 if date:
4204 opts[b'date'] = dateutil.parsedate(date)
4204 opts[b'date'] = dateutil.parsedate(date)
4205
4205
4206 exact = opts.get(b'exact')
4206 exact = opts.get(b'exact')
4207 update = not opts.get(b'bypass')
4207 update = not opts.get(b'bypass')
4208 try:
4208 try:
4209 sim = float(opts.get(b'similarity') or 0)
4209 sim = float(opts.get(b'similarity') or 0)
4210 except ValueError:
4210 except ValueError:
4211 raise error.InputError(_(b'similarity must be a number'))
4211 raise error.InputError(_(b'similarity must be a number'))
4212 if sim < 0 or sim > 100:
4212 if sim < 0 or sim > 100:
4213 raise error.InputError(_(b'similarity must be between 0 and 100'))
4213 raise error.InputError(_(b'similarity must be between 0 and 100'))
4214 if sim and not update:
4214 if sim and not update:
4215 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4215 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4216
4216
4217 base = opts[b"base"]
4217 base = opts[b"base"]
4218 msgs = []
4218 msgs = []
4219 ret = 0
4219 ret = 0
4220
4220
4221 with repo.wlock():
4221 with repo.wlock():
4222 if update:
4222 if update:
4223 cmdutil.checkunfinished(repo)
4223 cmdutil.checkunfinished(repo)
4224 if exact or not opts.get(b'force'):
4224 if exact or not opts.get(b'force'):
4225 cmdutil.bailifchanged(repo)
4225 cmdutil.bailifchanged(repo)
4226
4226
4227 if not opts.get(b'no_commit'):
4227 if not opts.get(b'no_commit'):
4228 lock = repo.lock
4228 lock = repo.lock
4229 tr = lambda: repo.transaction(b'import')
4229 tr = lambda: repo.transaction(b'import')
4230 dsguard = util.nullcontextmanager
4230 dsguard = util.nullcontextmanager
4231 else:
4231 else:
4232 lock = util.nullcontextmanager
4232 lock = util.nullcontextmanager
4233 tr = util.nullcontextmanager
4233 tr = util.nullcontextmanager
4234 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4234 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4235 with lock(), tr(), dsguard():
4235 with lock(), tr(), dsguard():
4236 parents = repo[None].parents()
4236 parents = repo[None].parents()
4237 for patchurl in patches:
4237 for patchurl in patches:
4238 if patchurl == b'-':
4238 if patchurl == b'-':
4239 ui.status(_(b'applying patch from stdin\n'))
4239 ui.status(_(b'applying patch from stdin\n'))
4240 patchfile = ui.fin
4240 patchfile = ui.fin
4241 patchurl = b'stdin' # for error message
4241 patchurl = b'stdin' # for error message
4242 else:
4242 else:
4243 patchurl = os.path.join(base, patchurl)
4243 patchurl = os.path.join(base, patchurl)
4244 ui.status(_(b'applying %s\n') % patchurl)
4244 ui.status(_(b'applying %s\n') % patchurl)
4245 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4245 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4246
4246
4247 haspatch = False
4247 haspatch = False
4248 for hunk in patch.split(patchfile):
4248 for hunk in patch.split(patchfile):
4249 with patch.extract(ui, hunk) as patchdata:
4249 with patch.extract(ui, hunk) as patchdata:
4250 msg, node, rej = cmdutil.tryimportone(
4250 msg, node, rej = cmdutil.tryimportone(
4251 ui, repo, patchdata, parents, opts, msgs, hg.clean
4251 ui, repo, patchdata, parents, opts, msgs, hg.clean
4252 )
4252 )
4253 if msg:
4253 if msg:
4254 haspatch = True
4254 haspatch = True
4255 ui.note(msg + b'\n')
4255 ui.note(msg + b'\n')
4256 if update or exact:
4256 if update or exact:
4257 parents = repo[None].parents()
4257 parents = repo[None].parents()
4258 else:
4258 else:
4259 parents = [repo[node]]
4259 parents = [repo[node]]
4260 if rej:
4260 if rej:
4261 ui.write_err(_(b"patch applied partially\n"))
4261 ui.write_err(_(b"patch applied partially\n"))
4262 ui.write_err(
4262 ui.write_err(
4263 _(
4263 _(
4264 b"(fix the .rej files and run "
4264 b"(fix the .rej files and run "
4265 b"`hg commit --amend`)\n"
4265 b"`hg commit --amend`)\n"
4266 )
4266 )
4267 )
4267 )
4268 ret = 1
4268 ret = 1
4269 break
4269 break
4270
4270
4271 if not haspatch:
4271 if not haspatch:
4272 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4272 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4273
4273
4274 if msgs:
4274 if msgs:
4275 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4275 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4276 return ret
4276 return ret
4277
4277
4278
4278
4279 @command(
4279 @command(
4280 b'incoming|in',
4280 b'incoming|in',
4281 [
4281 [
4282 (
4282 (
4283 b'f',
4283 b'f',
4284 b'force',
4284 b'force',
4285 None,
4285 None,
4286 _(b'run even if remote repository is unrelated'),
4286 _(b'run even if remote repository is unrelated'),
4287 ),
4287 ),
4288 (b'n', b'newest-first', None, _(b'show newest record first')),
4288 (b'n', b'newest-first', None, _(b'show newest record first')),
4289 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4289 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4290 (
4290 (
4291 b'r',
4291 b'r',
4292 b'rev',
4292 b'rev',
4293 [],
4293 [],
4294 _(b'a remote changeset intended to be added'),
4294 _(b'a remote changeset intended to be added'),
4295 _(b'REV'),
4295 _(b'REV'),
4296 ),
4296 ),
4297 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4297 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4298 (
4298 (
4299 b'b',
4299 b'b',
4300 b'branch',
4300 b'branch',
4301 [],
4301 [],
4302 _(b'a specific branch you would like to pull'),
4302 _(b'a specific branch you would like to pull'),
4303 _(b'BRANCH'),
4303 _(b'BRANCH'),
4304 ),
4304 ),
4305 ]
4305 ]
4306 + logopts
4306 + logopts
4307 + remoteopts
4307 + remoteopts
4308 + subrepoopts,
4308 + subrepoopts,
4309 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4309 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4310 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4310 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4311 )
4311 )
4312 def incoming(ui, repo, source=b"default", **opts):
4312 def incoming(ui, repo, source=b"default", **opts):
4313 """show new changesets found in source
4313 """show new changesets found in source
4314
4314
4315 Show new changesets found in the specified path/URL or the default
4315 Show new changesets found in the specified path/URL or the default
4316 pull location. These are the changesets that would have been pulled
4316 pull location. These are the changesets that would have been pulled
4317 by :hg:`pull` at the time you issued this command.
4317 by :hg:`pull` at the time you issued this command.
4318
4318
4319 See pull for valid source format details.
4319 See pull for valid source format details.
4320
4320
4321 .. container:: verbose
4321 .. container:: verbose
4322
4322
4323 With -B/--bookmarks, the result of bookmark comparison between
4323 With -B/--bookmarks, the result of bookmark comparison between
4324 local and remote repositories is displayed. With -v/--verbose,
4324 local and remote repositories is displayed. With -v/--verbose,
4325 status is also displayed for each bookmark like below::
4325 status is also displayed for each bookmark like below::
4326
4326
4327 BM1 01234567890a added
4327 BM1 01234567890a added
4328 BM2 1234567890ab advanced
4328 BM2 1234567890ab advanced
4329 BM3 234567890abc diverged
4329 BM3 234567890abc diverged
4330 BM4 34567890abcd changed
4330 BM4 34567890abcd changed
4331
4331
4332 The action taken locally when pulling depends on the
4332 The action taken locally when pulling depends on the
4333 status of each bookmark:
4333 status of each bookmark:
4334
4334
4335 :``added``: pull will create it
4335 :``added``: pull will create it
4336 :``advanced``: pull will update it
4336 :``advanced``: pull will update it
4337 :``diverged``: pull will create a divergent bookmark
4337 :``diverged``: pull will create a divergent bookmark
4338 :``changed``: result depends on remote changesets
4338 :``changed``: result depends on remote changesets
4339
4339
4340 From the point of view of pulling behavior, bookmark
4340 From the point of view of pulling behavior, bookmark
4341 existing only in the remote repository are treated as ``added``,
4341 existing only in the remote repository are treated as ``added``,
4342 even if it is in fact locally deleted.
4342 even if it is in fact locally deleted.
4343
4343
4344 .. container:: verbose
4344 .. container:: verbose
4345
4345
4346 For remote repository, using --bundle avoids downloading the
4346 For remote repository, using --bundle avoids downloading the
4347 changesets twice if the incoming is followed by a pull.
4347 changesets twice if the incoming is followed by a pull.
4348
4348
4349 Examples:
4349 Examples:
4350
4350
4351 - show incoming changes with patches and full description::
4351 - show incoming changes with patches and full description::
4352
4352
4353 hg incoming -vp
4353 hg incoming -vp
4354
4354
4355 - show incoming changes excluding merges, store a bundle::
4355 - show incoming changes excluding merges, store a bundle::
4356
4356
4357 hg in -vpM --bundle incoming.hg
4357 hg in -vpM --bundle incoming.hg
4358 hg pull incoming.hg
4358 hg pull incoming.hg
4359
4359
4360 - briefly list changes inside a bundle::
4360 - briefly list changes inside a bundle::
4361
4361
4362 hg in changes.hg -T "{desc|firstline}\\n"
4362 hg in changes.hg -T "{desc|firstline}\\n"
4363
4363
4364 Returns 0 if there are incoming changes, 1 otherwise.
4364 Returns 0 if there are incoming changes, 1 otherwise.
4365 """
4365 """
4366 opts = pycompat.byteskwargs(opts)
4366 opts = pycompat.byteskwargs(opts)
4367 if opts.get(b'graph'):
4367 if opts.get(b'graph'):
4368 logcmdutil.checkunsupportedgraphflags([], opts)
4368 logcmdutil.checkunsupportedgraphflags([], opts)
4369
4369
4370 def display(other, chlist, displayer):
4370 def display(other, chlist, displayer):
4371 revdag = logcmdutil.graphrevs(other, chlist, opts)
4371 revdag = logcmdutil.graphrevs(other, chlist, opts)
4372 logcmdutil.displaygraph(
4372 logcmdutil.displaygraph(
4373 ui, repo, revdag, displayer, graphmod.asciiedges
4373 ui, repo, revdag, displayer, graphmod.asciiedges
4374 )
4374 )
4375
4375
4376 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4376 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4377 return 0
4377 return 0
4378
4378
4379 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4379 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4380
4380
4381 if opts.get(b'bookmarks'):
4381 if opts.get(b'bookmarks'):
4382 srcs = urlutil.get_pull_paths(repo, ui, [source])
4382 srcs = urlutil.get_pull_paths(repo, ui, [source])
4383 for path in srcs:
4383 for path in srcs:
4384 source, branches = urlutil.parseurl(
4384 source, branches = urlutil.parseurl(
4385 path.rawloc, opts.get(b'branch')
4385 path.rawloc, opts.get(b'branch')
4386 )
4386 )
4387 other = hg.peer(repo, opts, source)
4387 other = hg.peer(repo, opts, source)
4388 try:
4388 try:
4389 if b'bookmarks' not in other.listkeys(b'namespaces'):
4389 if b'bookmarks' not in other.listkeys(b'namespaces'):
4390 ui.warn(_(b"remote doesn't support bookmarks\n"))
4390 ui.warn(_(b"remote doesn't support bookmarks\n"))
4391 return 0
4391 return 0
4392 ui.pager(b'incoming')
4392 ui.pager(b'incoming')
4393 ui.status(
4393 ui.status(
4394 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4394 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4395 )
4395 )
4396 return bookmarks.incoming(
4396 return bookmarks.incoming(
4397 ui, repo, other, mode=path.bookmarks_mode
4397 ui, repo, other, mode=path.bookmarks_mode
4398 )
4398 )
4399 finally:
4399 finally:
4400 other.close()
4400 other.close()
4401
4401
4402 return hg.incoming(ui, repo, source, opts)
4402 return hg.incoming(ui, repo, source, opts)
4403
4403
4404
4404
4405 @command(
4405 @command(
4406 b'init',
4406 b'init',
4407 remoteopts,
4407 remoteopts,
4408 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4408 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4409 helpcategory=command.CATEGORY_REPO_CREATION,
4409 helpcategory=command.CATEGORY_REPO_CREATION,
4410 helpbasic=True,
4410 helpbasic=True,
4411 norepo=True,
4411 norepo=True,
4412 )
4412 )
4413 def init(ui, dest=b".", **opts):
4413 def init(ui, dest=b".", **opts):
4414 """create a new repository in the given directory
4414 """create a new repository in the given directory
4415
4415
4416 Initialize a new repository in the given directory. If the given
4416 Initialize a new repository in the given directory. If the given
4417 directory does not exist, it will be created.
4417 directory does not exist, it will be created.
4418
4418
4419 If no directory is given, the current directory is used.
4419 If no directory is given, the current directory is used.
4420
4420
4421 It is possible to specify an ``ssh://`` URL as the destination.
4421 It is possible to specify an ``ssh://`` URL as the destination.
4422 See :hg:`help urls` for more information.
4422 See :hg:`help urls` for more information.
4423
4423
4424 Returns 0 on success.
4424 Returns 0 on success.
4425 """
4425 """
4426 opts = pycompat.byteskwargs(opts)
4426 opts = pycompat.byteskwargs(opts)
4427 path = urlutil.get_clone_path(ui, dest)[1]
4427 path = urlutil.get_clone_path(ui, dest)[1]
4428 peer = hg.peer(ui, opts, path, create=True)
4428 peer = hg.peer(ui, opts, path, create=True)
4429 peer.close()
4429 peer.close()
4430
4430
4431
4431
4432 @command(
4432 @command(
4433 b'locate',
4433 b'locate',
4434 [
4434 [
4435 (
4435 (
4436 b'r',
4436 b'r',
4437 b'rev',
4437 b'rev',
4438 b'',
4438 b'',
4439 _(b'search the repository as it is in REV'),
4439 _(b'search the repository as it is in REV'),
4440 _(b'REV'),
4440 _(b'REV'),
4441 ),
4441 ),
4442 (
4442 (
4443 b'0',
4443 b'0',
4444 b'print0',
4444 b'print0',
4445 None,
4445 None,
4446 _(b'end filenames with NUL, for use with xargs'),
4446 _(b'end filenames with NUL, for use with xargs'),
4447 ),
4447 ),
4448 (
4448 (
4449 b'f',
4449 b'f',
4450 b'fullpath',
4450 b'fullpath',
4451 None,
4451 None,
4452 _(b'print complete paths from the filesystem root'),
4452 _(b'print complete paths from the filesystem root'),
4453 ),
4453 ),
4454 ]
4454 ]
4455 + walkopts,
4455 + walkopts,
4456 _(b'[OPTION]... [PATTERN]...'),
4456 _(b'[OPTION]... [PATTERN]...'),
4457 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4457 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4458 )
4458 )
4459 def locate(ui, repo, *pats, **opts):
4459 def locate(ui, repo, *pats, **opts):
4460 """locate files matching specific patterns (DEPRECATED)
4460 """locate files matching specific patterns (DEPRECATED)
4461
4461
4462 Print files under Mercurial control in the working directory whose
4462 Print files under Mercurial control in the working directory whose
4463 names match the given patterns.
4463 names match the given patterns.
4464
4464
4465 By default, this command searches all directories in the working
4465 By default, this command searches all directories in the working
4466 directory. To search just the current directory and its
4466 directory. To search just the current directory and its
4467 subdirectories, use "--include .".
4467 subdirectories, use "--include .".
4468
4468
4469 If no patterns are given to match, this command prints the names
4469 If no patterns are given to match, this command prints the names
4470 of all files under Mercurial control in the working directory.
4470 of all files under Mercurial control in the working directory.
4471
4471
4472 If you want to feed the output of this command into the "xargs"
4472 If you want to feed the output of this command into the "xargs"
4473 command, use the -0 option to both this command and "xargs". This
4473 command, use the -0 option to both this command and "xargs". This
4474 will avoid the problem of "xargs" treating single filenames that
4474 will avoid the problem of "xargs" treating single filenames that
4475 contain whitespace as multiple filenames.
4475 contain whitespace as multiple filenames.
4476
4476
4477 See :hg:`help files` for a more versatile command.
4477 See :hg:`help files` for a more versatile command.
4478
4478
4479 Returns 0 if a match is found, 1 otherwise.
4479 Returns 0 if a match is found, 1 otherwise.
4480 """
4480 """
4481 opts = pycompat.byteskwargs(opts)
4481 opts = pycompat.byteskwargs(opts)
4482 if opts.get(b'print0'):
4482 if opts.get(b'print0'):
4483 end = b'\0'
4483 end = b'\0'
4484 else:
4484 else:
4485 end = b'\n'
4485 end = b'\n'
4486 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4486 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4487
4487
4488 ret = 1
4488 ret = 1
4489 m = scmutil.match(
4489 m = scmutil.match(
4490 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4490 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4491 )
4491 )
4492
4492
4493 ui.pager(b'locate')
4493 ui.pager(b'locate')
4494 if ctx.rev() is None:
4494 if ctx.rev() is None:
4495 # When run on the working copy, "locate" includes removed files, so
4495 # When run on the working copy, "locate" includes removed files, so
4496 # we get the list of files from the dirstate.
4496 # we get the list of files from the dirstate.
4497 filesgen = sorted(repo.dirstate.matches(m))
4497 filesgen = sorted(repo.dirstate.matches(m))
4498 else:
4498 else:
4499 filesgen = ctx.matches(m)
4499 filesgen = ctx.matches(m)
4500 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4500 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4501 for abs in filesgen:
4501 for abs in filesgen:
4502 if opts.get(b'fullpath'):
4502 if opts.get(b'fullpath'):
4503 ui.write(repo.wjoin(abs), end)
4503 ui.write(repo.wjoin(abs), end)
4504 else:
4504 else:
4505 ui.write(uipathfn(abs), end)
4505 ui.write(uipathfn(abs), end)
4506 ret = 0
4506 ret = 0
4507
4507
4508 return ret
4508 return ret
4509
4509
4510
4510
4511 @command(
4511 @command(
4512 b'log|history',
4512 b'log|history',
4513 [
4513 [
4514 (
4514 (
4515 b'f',
4515 b'f',
4516 b'follow',
4516 b'follow',
4517 None,
4517 None,
4518 _(
4518 _(
4519 b'follow changeset history, or file history across copies and renames'
4519 b'follow changeset history, or file history across copies and renames'
4520 ),
4520 ),
4521 ),
4521 ),
4522 (
4522 (
4523 b'',
4523 b'',
4524 b'follow-first',
4524 b'follow-first',
4525 None,
4525 None,
4526 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4526 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4527 ),
4527 ),
4528 (
4528 (
4529 b'd',
4529 b'd',
4530 b'date',
4530 b'date',
4531 b'',
4531 b'',
4532 _(b'show revisions matching date spec'),
4532 _(b'show revisions matching date spec'),
4533 _(b'DATE'),
4533 _(b'DATE'),
4534 ),
4534 ),
4535 (b'C', b'copies', None, _(b'show copied files')),
4535 (b'C', b'copies', None, _(b'show copied files')),
4536 (
4536 (
4537 b'k',
4537 b'k',
4538 b'keyword',
4538 b'keyword',
4539 [],
4539 [],
4540 _(b'do case-insensitive search for a given text'),
4540 _(b'do case-insensitive search for a given text'),
4541 _(b'TEXT'),
4541 _(b'TEXT'),
4542 ),
4542 ),
4543 (
4543 (
4544 b'r',
4544 b'r',
4545 b'rev',
4545 b'rev',
4546 [],
4546 [],
4547 _(b'revisions to select or follow from'),
4547 _(b'revisions to select or follow from'),
4548 _(b'REV'),
4548 _(b'REV'),
4549 ),
4549 ),
4550 (
4550 (
4551 b'L',
4551 b'L',
4552 b'line-range',
4552 b'line-range',
4553 [],
4553 [],
4554 _(b'follow line range of specified file (EXPERIMENTAL)'),
4554 _(b'follow line range of specified file (EXPERIMENTAL)'),
4555 _(b'FILE,RANGE'),
4555 _(b'FILE,RANGE'),
4556 ),
4556 ),
4557 (
4557 (
4558 b'',
4558 b'',
4559 b'removed',
4559 b'removed',
4560 None,
4560 None,
4561 _(b'include revisions where files were removed'),
4561 _(b'include revisions where files were removed'),
4562 ),
4562 ),
4563 (
4563 (
4564 b'm',
4564 b'm',
4565 b'only-merges',
4565 b'only-merges',
4566 None,
4566 None,
4567 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4567 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4568 ),
4568 ),
4569 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4569 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4570 (
4570 (
4571 b'',
4571 b'',
4572 b'only-branch',
4572 b'only-branch',
4573 [],
4573 [],
4574 _(
4574 _(
4575 b'show only changesets within the given named branch (DEPRECATED)'
4575 b'show only changesets within the given named branch (DEPRECATED)'
4576 ),
4576 ),
4577 _(b'BRANCH'),
4577 _(b'BRANCH'),
4578 ),
4578 ),
4579 (
4579 (
4580 b'b',
4580 b'b',
4581 b'branch',
4581 b'branch',
4582 [],
4582 [],
4583 _(b'show changesets within the given named branch'),
4583 _(b'show changesets within the given named branch'),
4584 _(b'BRANCH'),
4584 _(b'BRANCH'),
4585 ),
4585 ),
4586 (
4586 (
4587 b'B',
4587 b'B',
4588 b'bookmark',
4588 b'bookmark',
4589 [],
4589 [],
4590 _(b"show changesets within the given bookmark"),
4590 _(b"show changesets within the given bookmark"),
4591 _(b'BOOKMARK'),
4591 _(b'BOOKMARK'),
4592 ),
4592 ),
4593 (
4593 (
4594 b'P',
4594 b'P',
4595 b'prune',
4595 b'prune',
4596 [],
4596 [],
4597 _(b'do not display revision or any of its ancestors'),
4597 _(b'do not display revision or any of its ancestors'),
4598 _(b'REV'),
4598 _(b'REV'),
4599 ),
4599 ),
4600 ]
4600 ]
4601 + logopts
4601 + logopts
4602 + walkopts,
4602 + walkopts,
4603 _(b'[OPTION]... [FILE]'),
4603 _(b'[OPTION]... [FILE]'),
4604 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4604 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4605 helpbasic=True,
4605 helpbasic=True,
4606 inferrepo=True,
4606 inferrepo=True,
4607 intents={INTENT_READONLY},
4607 intents={INTENT_READONLY},
4608 )
4608 )
4609 def log(ui, repo, *pats, **opts):
4609 def log(ui, repo, *pats, **opts):
4610 """show revision history of entire repository or files
4610 """show revision history of entire repository or files
4611
4611
4612 Print the revision history of the specified files or the entire
4612 Print the revision history of the specified files or the entire
4613 project.
4613 project.
4614
4614
4615 If no revision range is specified, the default is ``tip:0`` unless
4615 If no revision range is specified, the default is ``tip:0`` unless
4616 --follow is set.
4616 --follow is set.
4617
4617
4618 File history is shown without following rename or copy history of
4618 File history is shown without following rename or copy history of
4619 files. Use -f/--follow with a filename to follow history across
4619 files. Use -f/--follow with a filename to follow history across
4620 renames and copies. --follow without a filename will only show
4620 renames and copies. --follow without a filename will only show
4621 ancestors of the starting revisions. The starting revisions can be
4621 ancestors of the starting revisions. The starting revisions can be
4622 specified by -r/--rev, which default to the working directory parent.
4622 specified by -r/--rev, which default to the working directory parent.
4623
4623
4624 By default this command prints revision number and changeset id,
4624 By default this command prints revision number and changeset id,
4625 tags, non-trivial parents, user, date and time, and a summary for
4625 tags, non-trivial parents, user, date and time, and a summary for
4626 each commit. When the -v/--verbose switch is used, the list of
4626 each commit. When the -v/--verbose switch is used, the list of
4627 changed files and full commit message are shown.
4627 changed files and full commit message are shown.
4628
4628
4629 With --graph the revisions are shown as an ASCII art DAG with the most
4629 With --graph the revisions are shown as an ASCII art DAG with the most
4630 recent changeset at the top.
4630 recent changeset at the top.
4631 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4631 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4632 involved in an unresolved merge conflict, '_' closes a branch,
4632 involved in an unresolved merge conflict, '_' closes a branch,
4633 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4633 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4634 changeset from the lines below is a parent of the 'o' merge on the same
4634 changeset from the lines below is a parent of the 'o' merge on the same
4635 line.
4635 line.
4636 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4636 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4637 of a '|' indicates one or more revisions in a path are omitted.
4637 of a '|' indicates one or more revisions in a path are omitted.
4638
4638
4639 .. container:: verbose
4639 .. container:: verbose
4640
4640
4641 Use -L/--line-range FILE,M:N options to follow the history of lines
4641 Use -L/--line-range FILE,M:N options to follow the history of lines
4642 from M to N in FILE. With -p/--patch only diff hunks affecting
4642 from M to N in FILE. With -p/--patch only diff hunks affecting
4643 specified line range will be shown. This option requires --follow;
4643 specified line range will be shown. This option requires --follow;
4644 it can be specified multiple times. Currently, this option is not
4644 it can be specified multiple times. Currently, this option is not
4645 compatible with --graph. This option is experimental.
4645 compatible with --graph. This option is experimental.
4646
4646
4647 .. note::
4647 .. note::
4648
4648
4649 :hg:`log --patch` may generate unexpected diff output for merge
4649 :hg:`log --patch` may generate unexpected diff output for merge
4650 changesets, as it will only compare the merge changeset against
4650 changesets, as it will only compare the merge changeset against
4651 its first parent. Also, only files different from BOTH parents
4651 its first parent. Also, only files different from BOTH parents
4652 will appear in files:.
4652 will appear in files:.
4653
4653
4654 .. note::
4654 .. note::
4655
4655
4656 For performance reasons, :hg:`log FILE` may omit duplicate changes
4656 For performance reasons, :hg:`log FILE` may omit duplicate changes
4657 made on branches and will not show removals or mode changes. To
4657 made on branches and will not show removals or mode changes. To
4658 see all such changes, use the --removed switch.
4658 see all such changes, use the --removed switch.
4659
4659
4660 .. container:: verbose
4660 .. container:: verbose
4661
4661
4662 .. note::
4662 .. note::
4663
4663
4664 The history resulting from -L/--line-range options depends on diff
4664 The history resulting from -L/--line-range options depends on diff
4665 options; for instance if white-spaces are ignored, respective changes
4665 options; for instance if white-spaces are ignored, respective changes
4666 with only white-spaces in specified line range will not be listed.
4666 with only white-spaces in specified line range will not be listed.
4667
4667
4668 .. container:: verbose
4668 .. container:: verbose
4669
4669
4670 Some examples:
4670 Some examples:
4671
4671
4672 - changesets with full descriptions and file lists::
4672 - changesets with full descriptions and file lists::
4673
4673
4674 hg log -v
4674 hg log -v
4675
4675
4676 - changesets ancestral to the working directory::
4676 - changesets ancestral to the working directory::
4677
4677
4678 hg log -f
4678 hg log -f
4679
4679
4680 - last 10 commits on the current branch::
4680 - last 10 commits on the current branch::
4681
4681
4682 hg log -l 10 -b .
4682 hg log -l 10 -b .
4683
4683
4684 - changesets showing all modifications of a file, including removals::
4684 - changesets showing all modifications of a file, including removals::
4685
4685
4686 hg log --removed file.c
4686 hg log --removed file.c
4687
4687
4688 - all changesets that touch a directory, with diffs, excluding merges::
4688 - all changesets that touch a directory, with diffs, excluding merges::
4689
4689
4690 hg log -Mp lib/
4690 hg log -Mp lib/
4691
4691
4692 - all revision numbers that match a keyword::
4692 - all revision numbers that match a keyword::
4693
4693
4694 hg log -k bug --template "{rev}\\n"
4694 hg log -k bug --template "{rev}\\n"
4695
4695
4696 - the full hash identifier of the working directory parent::
4696 - the full hash identifier of the working directory parent::
4697
4697
4698 hg log -r . --template "{node}\\n"
4698 hg log -r . --template "{node}\\n"
4699
4699
4700 - list available log templates::
4700 - list available log templates::
4701
4701
4702 hg log -T list
4702 hg log -T list
4703
4703
4704 - check if a given changeset is included in a tagged release::
4704 - check if a given changeset is included in a tagged release::
4705
4705
4706 hg log -r "a21ccf and ancestor(1.9)"
4706 hg log -r "a21ccf and ancestor(1.9)"
4707
4707
4708 - find all changesets by some user in a date range::
4708 - find all changesets by some user in a date range::
4709
4709
4710 hg log -k alice -d "may 2008 to jul 2008"
4710 hg log -k alice -d "may 2008 to jul 2008"
4711
4711
4712 - summary of all changesets after the last tag::
4712 - summary of all changesets after the last tag::
4713
4713
4714 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4714 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4715
4715
4716 - changesets touching lines 13 to 23 for file.c::
4716 - changesets touching lines 13 to 23 for file.c::
4717
4717
4718 hg log -L file.c,13:23
4718 hg log -L file.c,13:23
4719
4719
4720 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4720 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4721 main.c with patch::
4721 main.c with patch::
4722
4722
4723 hg log -L file.c,13:23 -L main.c,2:6 -p
4723 hg log -L file.c,13:23 -L main.c,2:6 -p
4724
4724
4725 See :hg:`help dates` for a list of formats valid for -d/--date.
4725 See :hg:`help dates` for a list of formats valid for -d/--date.
4726
4726
4727 See :hg:`help revisions` for more about specifying and ordering
4727 See :hg:`help revisions` for more about specifying and ordering
4728 revisions.
4728 revisions.
4729
4729
4730 See :hg:`help templates` for more about pre-packaged styles and
4730 See :hg:`help templates` for more about pre-packaged styles and
4731 specifying custom templates. The default template used by the log
4731 specifying custom templates. The default template used by the log
4732 command can be customized via the ``command-templates.log`` configuration
4732 command can be customized via the ``command-templates.log`` configuration
4733 setting.
4733 setting.
4734
4734
4735 Returns 0 on success.
4735 Returns 0 on success.
4736
4736
4737 """
4737 """
4738 opts = pycompat.byteskwargs(opts)
4738 opts = pycompat.byteskwargs(opts)
4739 linerange = opts.get(b'line_range')
4739 linerange = opts.get(b'line_range')
4740
4740
4741 if linerange and not opts.get(b'follow'):
4741 if linerange and not opts.get(b'follow'):
4742 raise error.InputError(_(b'--line-range requires --follow'))
4742 raise error.InputError(_(b'--line-range requires --follow'))
4743
4743
4744 if linerange and pats:
4744 if linerange and pats:
4745 # TODO: take pats as patterns with no line-range filter
4745 # TODO: take pats as patterns with no line-range filter
4746 raise error.InputError(
4746 raise error.InputError(
4747 _(b'FILE arguments are not compatible with --line-range option')
4747 _(b'FILE arguments are not compatible with --line-range option')
4748 )
4748 )
4749
4749
4750 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4750 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4751 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4751 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4752 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4752 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4753 if linerange:
4753 if linerange:
4754 # TODO: should follow file history from logcmdutil._initialrevs(),
4754 # TODO: should follow file history from logcmdutil._initialrevs(),
4755 # then filter the result by logcmdutil._makerevset() and --limit
4755 # then filter the result by logcmdutil._makerevset() and --limit
4756 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4756 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4757
4757
4758 getcopies = None
4758 getcopies = None
4759 if opts.get(b'copies'):
4759 if opts.get(b'copies'):
4760 endrev = None
4760 endrev = None
4761 if revs:
4761 if revs:
4762 endrev = revs.max() + 1
4762 endrev = revs.max() + 1
4763 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4763 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4764
4764
4765 ui.pager(b'log')
4765 ui.pager(b'log')
4766 displayer = logcmdutil.changesetdisplayer(
4766 displayer = logcmdutil.changesetdisplayer(
4767 ui, repo, opts, differ, buffered=True
4767 ui, repo, opts, differ, buffered=True
4768 )
4768 )
4769 if opts.get(b'graph'):
4769 if opts.get(b'graph'):
4770 displayfn = logcmdutil.displaygraphrevs
4770 displayfn = logcmdutil.displaygraphrevs
4771 else:
4771 else:
4772 displayfn = logcmdutil.displayrevs
4772 displayfn = logcmdutil.displayrevs
4773 displayfn(ui, repo, revs, displayer, getcopies)
4773 displayfn(ui, repo, revs, displayer, getcopies)
4774
4774
4775
4775
4776 @command(
4776 @command(
4777 b'manifest',
4777 b'manifest',
4778 [
4778 [
4779 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4779 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4780 (b'', b'all', False, _(b"list files from all revisions")),
4780 (b'', b'all', False, _(b"list files from all revisions")),
4781 ]
4781 ]
4782 + formatteropts,
4782 + formatteropts,
4783 _(b'[-r REV]'),
4783 _(b'[-r REV]'),
4784 helpcategory=command.CATEGORY_MAINTENANCE,
4784 helpcategory=command.CATEGORY_MAINTENANCE,
4785 intents={INTENT_READONLY},
4785 intents={INTENT_READONLY},
4786 )
4786 )
4787 def manifest(ui, repo, node=None, rev=None, **opts):
4787 def manifest(ui, repo, node=None, rev=None, **opts):
4788 """output the current or given revision of the project manifest
4788 """output the current or given revision of the project manifest
4789
4789
4790 Print a list of version controlled files for the given revision.
4790 Print a list of version controlled files for the given revision.
4791 If no revision is given, the first parent of the working directory
4791 If no revision is given, the first parent of the working directory
4792 is used, or the null revision if no revision is checked out.
4792 is used, or the null revision if no revision is checked out.
4793
4793
4794 With -v, print file permissions, symlink and executable bits.
4794 With -v, print file permissions, symlink and executable bits.
4795 With --debug, print file revision hashes.
4795 With --debug, print file revision hashes.
4796
4796
4797 If option --all is specified, the list of all files from all revisions
4797 If option --all is specified, the list of all files from all revisions
4798 is printed. This includes deleted and renamed files.
4798 is printed. This includes deleted and renamed files.
4799
4799
4800 Returns 0 on success.
4800 Returns 0 on success.
4801 """
4801 """
4802 opts = pycompat.byteskwargs(opts)
4802 opts = pycompat.byteskwargs(opts)
4803 fm = ui.formatter(b'manifest', opts)
4803 fm = ui.formatter(b'manifest', opts)
4804
4804
4805 if opts.get(b'all'):
4805 if opts.get(b'all'):
4806 if rev or node:
4806 if rev or node:
4807 raise error.InputError(_(b"can't specify a revision with --all"))
4807 raise error.InputError(_(b"can't specify a revision with --all"))
4808
4808
4809 res = set()
4809 res = set()
4810 for rev in repo:
4810 for rev in repo:
4811 ctx = repo[rev]
4811 ctx = repo[rev]
4812 res |= set(ctx.files())
4812 res |= set(ctx.files())
4813
4813
4814 ui.pager(b'manifest')
4814 ui.pager(b'manifest')
4815 for f in sorted(res):
4815 for f in sorted(res):
4816 fm.startitem()
4816 fm.startitem()
4817 fm.write(b"path", b'%s\n', f)
4817 fm.write(b"path", b'%s\n', f)
4818 fm.end()
4818 fm.end()
4819 return
4819 return
4820
4820
4821 if rev and node:
4821 if rev and node:
4822 raise error.InputError(_(b"please specify just one revision"))
4822 raise error.InputError(_(b"please specify just one revision"))
4823
4823
4824 if not node:
4824 if not node:
4825 node = rev
4825 node = rev
4826
4826
4827 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4827 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4828 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4828 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4829 if node:
4829 if node:
4830 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4830 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4831 ctx = logcmdutil.revsingle(repo, node)
4831 ctx = logcmdutil.revsingle(repo, node)
4832 mf = ctx.manifest()
4832 mf = ctx.manifest()
4833 ui.pager(b'manifest')
4833 ui.pager(b'manifest')
4834 for f in ctx:
4834 for f in ctx:
4835 fm.startitem()
4835 fm.startitem()
4836 fm.context(ctx=ctx)
4836 fm.context(ctx=ctx)
4837 fl = ctx[f].flags()
4837 fl = ctx[f].flags()
4838 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4838 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4839 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4839 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4840 fm.write(b'path', b'%s\n', f)
4840 fm.write(b'path', b'%s\n', f)
4841 fm.end()
4841 fm.end()
4842
4842
4843
4843
4844 @command(
4844 @command(
4845 b'merge',
4845 b'merge',
4846 [
4846 [
4847 (
4847 (
4848 b'f',
4848 b'f',
4849 b'force',
4849 b'force',
4850 None,
4850 None,
4851 _(b'force a merge including outstanding changes (DEPRECATED)'),
4851 _(b'force a merge including outstanding changes (DEPRECATED)'),
4852 ),
4852 ),
4853 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4853 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4854 (
4854 (
4855 b'P',
4855 b'P',
4856 b'preview',
4856 b'preview',
4857 None,
4857 None,
4858 _(b'review revisions to merge (no merge is performed)'),
4858 _(b'review revisions to merge (no merge is performed)'),
4859 ),
4859 ),
4860 (b'', b'abort', None, _(b'abort the ongoing merge')),
4860 (b'', b'abort', None, _(b'abort the ongoing merge')),
4861 ]
4861 ]
4862 + mergetoolopts,
4862 + mergetoolopts,
4863 _(b'[-P] [[-r] REV]'),
4863 _(b'[-P] [[-r] REV]'),
4864 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4864 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4865 helpbasic=True,
4865 helpbasic=True,
4866 )
4866 )
4867 def merge(ui, repo, node=None, **opts):
4867 def merge(ui, repo, node=None, **opts):
4868 """merge another revision into working directory
4868 """merge another revision into working directory
4869
4869
4870 The current working directory is updated with all changes made in
4870 The current working directory is updated with all changes made in
4871 the requested revision since the last common predecessor revision.
4871 the requested revision since the last common predecessor revision.
4872
4872
4873 Files that changed between either parent are marked as changed for
4873 Files that changed between either parent are marked as changed for
4874 the next commit and a commit must be performed before any further
4874 the next commit and a commit must be performed before any further
4875 updates to the repository are allowed. The next commit will have
4875 updates to the repository are allowed. The next commit will have
4876 two parents.
4876 two parents.
4877
4877
4878 ``--tool`` can be used to specify the merge tool used for file
4878 ``--tool`` can be used to specify the merge tool used for file
4879 merges. It overrides the HGMERGE environment variable and your
4879 merges. It overrides the HGMERGE environment variable and your
4880 configuration files. See :hg:`help merge-tools` for options.
4880 configuration files. See :hg:`help merge-tools` for options.
4881
4881
4882 If no revision is specified, the working directory's parent is a
4882 If no revision is specified, the working directory's parent is a
4883 head revision, and the current branch contains exactly one other
4883 head revision, and the current branch contains exactly one other
4884 head, the other head is merged with by default. Otherwise, an
4884 head, the other head is merged with by default. Otherwise, an
4885 explicit revision with which to merge must be provided.
4885 explicit revision with which to merge must be provided.
4886
4886
4887 See :hg:`help resolve` for information on handling file conflicts.
4887 See :hg:`help resolve` for information on handling file conflicts.
4888
4888
4889 To undo an uncommitted merge, use :hg:`merge --abort` which
4889 To undo an uncommitted merge, use :hg:`merge --abort` which
4890 will check out a clean copy of the original merge parent, losing
4890 will check out a clean copy of the original merge parent, losing
4891 all changes.
4891 all changes.
4892
4892
4893 Returns 0 on success, 1 if there are unresolved files.
4893 Returns 0 on success, 1 if there are unresolved files.
4894 """
4894 """
4895
4895
4896 opts = pycompat.byteskwargs(opts)
4896 opts = pycompat.byteskwargs(opts)
4897 abort = opts.get(b'abort')
4897 abort = opts.get(b'abort')
4898 if abort and repo.dirstate.p2() == repo.nullid:
4898 if abort and repo.dirstate.p2() == repo.nullid:
4899 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4899 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4900 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4900 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4901 if abort:
4901 if abort:
4902 state = cmdutil.getunfinishedstate(repo)
4902 state = cmdutil.getunfinishedstate(repo)
4903 if state and state._opname != b'merge':
4903 if state and state._opname != b'merge':
4904 raise error.StateError(
4904 raise error.StateError(
4905 _(b'cannot abort merge with %s in progress') % (state._opname),
4905 _(b'cannot abort merge with %s in progress') % (state._opname),
4906 hint=state.hint(),
4906 hint=state.hint(),
4907 )
4907 )
4908 if node:
4908 if node:
4909 raise error.InputError(_(b"cannot specify a node with --abort"))
4909 raise error.InputError(_(b"cannot specify a node with --abort"))
4910 return hg.abortmerge(repo.ui, repo)
4910 return hg.abortmerge(repo.ui, repo)
4911
4911
4912 if opts.get(b'rev') and node:
4912 if opts.get(b'rev') and node:
4913 raise error.InputError(_(b"please specify just one revision"))
4913 raise error.InputError(_(b"please specify just one revision"))
4914 if not node:
4914 if not node:
4915 node = opts.get(b'rev')
4915 node = opts.get(b'rev')
4916
4916
4917 if node:
4917 if node:
4918 ctx = logcmdutil.revsingle(repo, node)
4918 ctx = logcmdutil.revsingle(repo, node)
4919 else:
4919 else:
4920 if ui.configbool(b'commands', b'merge.require-rev'):
4920 if ui.configbool(b'commands', b'merge.require-rev'):
4921 raise error.InputError(
4921 raise error.InputError(
4922 _(
4922 _(
4923 b'configuration requires specifying revision to merge '
4923 b'configuration requires specifying revision to merge '
4924 b'with'
4924 b'with'
4925 )
4925 )
4926 )
4926 )
4927 ctx = repo[destutil.destmerge(repo)]
4927 ctx = repo[destutil.destmerge(repo)]
4928
4928
4929 if ctx.node() is None:
4929 if ctx.node() is None:
4930 raise error.InputError(
4930 raise error.InputError(
4931 _(b'merging with the working copy has no effect')
4931 _(b'merging with the working copy has no effect')
4932 )
4932 )
4933
4933
4934 if opts.get(b'preview'):
4934 if opts.get(b'preview'):
4935 # find nodes that are ancestors of p2 but not of p1
4935 # find nodes that are ancestors of p2 but not of p1
4936 p1 = repo[b'.'].node()
4936 p1 = repo[b'.'].node()
4937 p2 = ctx.node()
4937 p2 = ctx.node()
4938 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4938 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4939
4939
4940 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4940 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4941 for node in nodes:
4941 for node in nodes:
4942 displayer.show(repo[node])
4942 displayer.show(repo[node])
4943 displayer.close()
4943 displayer.close()
4944 return 0
4944 return 0
4945
4945
4946 # ui.forcemerge is an internal variable, do not document
4946 # ui.forcemerge is an internal variable, do not document
4947 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4947 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4948 with ui.configoverride(overrides, b'merge'):
4948 with ui.configoverride(overrides, b'merge'):
4949 force = opts.get(b'force')
4949 force = opts.get(b'force')
4950 labels = [b'working copy', b'merge rev', b'common ancestor']
4950 labels = [b'working copy', b'merge rev', b'common ancestor']
4951 return hg.merge(ctx, force=force, labels=labels)
4951 return hg.merge(ctx, force=force, labels=labels)
4952
4952
4953
4953
4954 statemod.addunfinished(
4954 statemod.addunfinished(
4955 b'merge',
4955 b'merge',
4956 fname=None,
4956 fname=None,
4957 clearable=True,
4957 clearable=True,
4958 allowcommit=True,
4958 allowcommit=True,
4959 cmdmsg=_(b'outstanding uncommitted merge'),
4959 cmdmsg=_(b'outstanding uncommitted merge'),
4960 abortfunc=hg.abortmerge,
4960 abortfunc=hg.abortmerge,
4961 statushint=_(
4961 statushint=_(
4962 b'To continue: hg commit\nTo abort: hg merge --abort'
4962 b'To continue: hg commit\nTo abort: hg merge --abort'
4963 ),
4963 ),
4964 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4964 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4965 )
4965 )
4966
4966
4967
4967
4968 @command(
4968 @command(
4969 b'outgoing|out',
4969 b'outgoing|out',
4970 [
4970 [
4971 (
4971 (
4972 b'f',
4972 b'f',
4973 b'force',
4973 b'force',
4974 None,
4974 None,
4975 _(b'run even when the destination is unrelated'),
4975 _(b'run even when the destination is unrelated'),
4976 ),
4976 ),
4977 (
4977 (
4978 b'r',
4978 b'r',
4979 b'rev',
4979 b'rev',
4980 [],
4980 [],
4981 _(b'a changeset intended to be included in the destination'),
4981 _(b'a changeset intended to be included in the destination'),
4982 _(b'REV'),
4982 _(b'REV'),
4983 ),
4983 ),
4984 (b'n', b'newest-first', None, _(b'show newest record first')),
4984 (b'n', b'newest-first', None, _(b'show newest record first')),
4985 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4985 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4986 (
4986 (
4987 b'b',
4987 b'b',
4988 b'branch',
4988 b'branch',
4989 [],
4989 [],
4990 _(b'a specific branch you would like to push'),
4990 _(b'a specific branch you would like to push'),
4991 _(b'BRANCH'),
4991 _(b'BRANCH'),
4992 ),
4992 ),
4993 ]
4993 ]
4994 + logopts
4994 + logopts
4995 + remoteopts
4995 + remoteopts
4996 + subrepoopts,
4996 + subrepoopts,
4997 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4997 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4998 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4998 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4999 )
4999 )
5000 def outgoing(ui, repo, *dests, **opts):
5000 def outgoing(ui, repo, *dests, **opts):
5001 """show changesets not found in the destination
5001 """show changesets not found in the destination
5002
5002
5003 Show changesets not found in the specified destination repository
5003 Show changesets not found in the specified destination repository
5004 or the default push location. These are the changesets that would
5004 or the default push location. These are the changesets that would
5005 be pushed if a push was requested.
5005 be pushed if a push was requested.
5006
5006
5007 See pull for details of valid destination formats.
5007 See pull for details of valid destination formats.
5008
5008
5009 .. container:: verbose
5009 .. container:: verbose
5010
5010
5011 With -B/--bookmarks, the result of bookmark comparison between
5011 With -B/--bookmarks, the result of bookmark comparison between
5012 local and remote repositories is displayed. With -v/--verbose,
5012 local and remote repositories is displayed. With -v/--verbose,
5013 status is also displayed for each bookmark like below::
5013 status is also displayed for each bookmark like below::
5014
5014
5015 BM1 01234567890a added
5015 BM1 01234567890a added
5016 BM2 deleted
5016 BM2 deleted
5017 BM3 234567890abc advanced
5017 BM3 234567890abc advanced
5018 BM4 34567890abcd diverged
5018 BM4 34567890abcd diverged
5019 BM5 4567890abcde changed
5019 BM5 4567890abcde changed
5020
5020
5021 The action taken when pushing depends on the
5021 The action taken when pushing depends on the
5022 status of each bookmark:
5022 status of each bookmark:
5023
5023
5024 :``added``: push with ``-B`` will create it
5024 :``added``: push with ``-B`` will create it
5025 :``deleted``: push with ``-B`` will delete it
5025 :``deleted``: push with ``-B`` will delete it
5026 :``advanced``: push will update it
5026 :``advanced``: push will update it
5027 :``diverged``: push with ``-B`` will update it
5027 :``diverged``: push with ``-B`` will update it
5028 :``changed``: push with ``-B`` will update it
5028 :``changed``: push with ``-B`` will update it
5029
5029
5030 From the point of view of pushing behavior, bookmarks
5030 From the point of view of pushing behavior, bookmarks
5031 existing only in the remote repository are treated as
5031 existing only in the remote repository are treated as
5032 ``deleted``, even if it is in fact added remotely.
5032 ``deleted``, even if it is in fact added remotely.
5033
5033
5034 Returns 0 if there are outgoing changes, 1 otherwise.
5034 Returns 0 if there are outgoing changes, 1 otherwise.
5035 """
5035 """
5036 opts = pycompat.byteskwargs(opts)
5036 opts = pycompat.byteskwargs(opts)
5037 if opts.get(b'bookmarks'):
5037 if opts.get(b'bookmarks'):
5038 for path in urlutil.get_push_paths(repo, ui, dests):
5038 for path in urlutil.get_push_paths(repo, ui, dests):
5039 dest = path.pushloc or path.loc
5039 dest = path.pushloc or path.loc
5040 other = hg.peer(repo, opts, dest)
5040 other = hg.peer(repo, opts, dest)
5041 try:
5041 try:
5042 if b'bookmarks' not in other.listkeys(b'namespaces'):
5042 if b'bookmarks' not in other.listkeys(b'namespaces'):
5043 ui.warn(_(b"remote doesn't support bookmarks\n"))
5043 ui.warn(_(b"remote doesn't support bookmarks\n"))
5044 return 0
5044 return 0
5045 ui.status(
5045 ui.status(
5046 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5046 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5047 )
5047 )
5048 ui.pager(b'outgoing')
5048 ui.pager(b'outgoing')
5049 return bookmarks.outgoing(ui, repo, other)
5049 return bookmarks.outgoing(ui, repo, other)
5050 finally:
5050 finally:
5051 other.close()
5051 other.close()
5052
5052
5053 return hg.outgoing(ui, repo, dests, opts)
5053 return hg.outgoing(ui, repo, dests, opts)
5054
5054
5055
5055
5056 @command(
5056 @command(
5057 b'parents',
5057 b'parents',
5058 [
5058 [
5059 (
5059 (
5060 b'r',
5060 b'r',
5061 b'rev',
5061 b'rev',
5062 b'',
5062 b'',
5063 _(b'show parents of the specified revision'),
5063 _(b'show parents of the specified revision'),
5064 _(b'REV'),
5064 _(b'REV'),
5065 ),
5065 ),
5066 ]
5066 ]
5067 + templateopts,
5067 + templateopts,
5068 _(b'[-r REV] [FILE]'),
5068 _(b'[-r REV] [FILE]'),
5069 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5069 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5070 inferrepo=True,
5070 inferrepo=True,
5071 )
5071 )
5072 def parents(ui, repo, file_=None, **opts):
5072 def parents(ui, repo, file_=None, **opts):
5073 """show the parents of the working directory or revision (DEPRECATED)
5073 """show the parents of the working directory or revision (DEPRECATED)
5074
5074
5075 Print the working directory's parent revisions. If a revision is
5075 Print the working directory's parent revisions. If a revision is
5076 given via -r/--rev, the parent of that revision will be printed.
5076 given via -r/--rev, the parent of that revision will be printed.
5077 If a file argument is given, the revision in which the file was
5077 If a file argument is given, the revision in which the file was
5078 last changed (before the working directory revision or the
5078 last changed (before the working directory revision or the
5079 argument to --rev if given) is printed.
5079 argument to --rev if given) is printed.
5080
5080
5081 This command is equivalent to::
5081 This command is equivalent to::
5082
5082
5083 hg log -r "p1()+p2()" or
5083 hg log -r "p1()+p2()" or
5084 hg log -r "p1(REV)+p2(REV)" or
5084 hg log -r "p1(REV)+p2(REV)" or
5085 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5085 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5086 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5086 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5087
5087
5088 See :hg:`summary` and :hg:`help revsets` for related information.
5088 See :hg:`summary` and :hg:`help revsets` for related information.
5089
5089
5090 Returns 0 on success.
5090 Returns 0 on success.
5091 """
5091 """
5092
5092
5093 opts = pycompat.byteskwargs(opts)
5093 opts = pycompat.byteskwargs(opts)
5094 rev = opts.get(b'rev')
5094 rev = opts.get(b'rev')
5095 if rev:
5095 if rev:
5096 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5096 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5097 ctx = logcmdutil.revsingle(repo, rev, None)
5097 ctx = logcmdutil.revsingle(repo, rev, None)
5098
5098
5099 if file_:
5099 if file_:
5100 m = scmutil.match(ctx, (file_,), opts)
5100 m = scmutil.match(ctx, (file_,), opts)
5101 if m.anypats() or len(m.files()) != 1:
5101 if m.anypats() or len(m.files()) != 1:
5102 raise error.InputError(_(b'can only specify an explicit filename'))
5102 raise error.InputError(_(b'can only specify an explicit filename'))
5103 file_ = m.files()[0]
5103 file_ = m.files()[0]
5104 filenodes = []
5104 filenodes = []
5105 for cp in ctx.parents():
5105 for cp in ctx.parents():
5106 if not cp:
5106 if not cp:
5107 continue
5107 continue
5108 try:
5108 try:
5109 filenodes.append(cp.filenode(file_))
5109 filenodes.append(cp.filenode(file_))
5110 except error.LookupError:
5110 except error.LookupError:
5111 pass
5111 pass
5112 if not filenodes:
5112 if not filenodes:
5113 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5113 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5114 p = []
5114 p = []
5115 for fn in filenodes:
5115 for fn in filenodes:
5116 fctx = repo.filectx(file_, fileid=fn)
5116 fctx = repo.filectx(file_, fileid=fn)
5117 p.append(fctx.node())
5117 p.append(fctx.node())
5118 else:
5118 else:
5119 p = [cp.node() for cp in ctx.parents()]
5119 p = [cp.node() for cp in ctx.parents()]
5120
5120
5121 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5121 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5122 for n in p:
5122 for n in p:
5123 if n != repo.nullid:
5123 if n != repo.nullid:
5124 displayer.show(repo[n])
5124 displayer.show(repo[n])
5125 displayer.close()
5125 displayer.close()
5126
5126
5127
5127
5128 @command(
5128 @command(
5129 b'paths',
5129 b'paths',
5130 formatteropts,
5130 formatteropts,
5131 _(b'[NAME]'),
5131 _(b'[NAME]'),
5132 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5132 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5133 optionalrepo=True,
5133 optionalrepo=True,
5134 intents={INTENT_READONLY},
5134 intents={INTENT_READONLY},
5135 )
5135 )
5136 def paths(ui, repo, search=None, **opts):
5136 def paths(ui, repo, search=None, **opts):
5137 """show aliases for remote repositories
5137 """show aliases for remote repositories
5138
5138
5139 Show definition of symbolic path name NAME. If no name is given,
5139 Show definition of symbolic path name NAME. If no name is given,
5140 show definition of all available names.
5140 show definition of all available names.
5141
5141
5142 Option -q/--quiet suppresses all output when searching for NAME
5142 Option -q/--quiet suppresses all output when searching for NAME
5143 and shows only the path names when listing all definitions.
5143 and shows only the path names when listing all definitions.
5144
5144
5145 Path names are defined in the [paths] section of your
5145 Path names are defined in the [paths] section of your
5146 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5146 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5147 repository, ``.hg/hgrc`` is used, too.
5147 repository, ``.hg/hgrc`` is used, too.
5148
5148
5149 The path names ``default`` and ``default-push`` have a special
5149 The path names ``default`` and ``default-push`` have a special
5150 meaning. When performing a push or pull operation, they are used
5150 meaning. When performing a push or pull operation, they are used
5151 as fallbacks if no location is specified on the command-line.
5151 as fallbacks if no location is specified on the command-line.
5152 When ``default-push`` is set, it will be used for push and
5152 When ``default-push`` is set, it will be used for push and
5153 ``default`` will be used for pull; otherwise ``default`` is used
5153 ``default`` will be used for pull; otherwise ``default`` is used
5154 as the fallback for both. When cloning a repository, the clone
5154 as the fallback for both. When cloning a repository, the clone
5155 source is written as ``default`` in ``.hg/hgrc``.
5155 source is written as ``default`` in ``.hg/hgrc``.
5156
5156
5157 .. note::
5157 .. note::
5158
5158
5159 ``default`` and ``default-push`` apply to all inbound (e.g.
5159 ``default`` and ``default-push`` apply to all inbound (e.g.
5160 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5160 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5161 and :hg:`bundle`) operations.
5161 and :hg:`bundle`) operations.
5162
5162
5163 See :hg:`help urls` for more information.
5163 See :hg:`help urls` for more information.
5164
5164
5165 .. container:: verbose
5165 .. container:: verbose
5166
5166
5167 Template:
5167 Template:
5168
5168
5169 The following keywords are supported. See also :hg:`help templates`.
5169 The following keywords are supported. See also :hg:`help templates`.
5170
5170
5171 :name: String. Symbolic name of the path alias.
5171 :name: String. Symbolic name of the path alias.
5172 :pushurl: String. URL for push operations.
5172 :pushurl: String. URL for push operations.
5173 :url: String. URL or directory path for the other operations.
5173 :url: String. URL or directory path for the other operations.
5174
5174
5175 Returns 0 on success.
5175 Returns 0 on success.
5176 """
5176 """
5177
5177
5178 opts = pycompat.byteskwargs(opts)
5178 opts = pycompat.byteskwargs(opts)
5179
5179
5180 pathitems = urlutil.list_paths(ui, search)
5180 pathitems = urlutil.list_paths(ui, search)
5181 ui.pager(b'paths')
5181 ui.pager(b'paths')
5182
5182
5183 fm = ui.formatter(b'paths', opts)
5183 fm = ui.formatter(b'paths', opts)
5184 if fm.isplain():
5184 if fm.isplain():
5185 hidepassword = urlutil.hidepassword
5185 hidepassword = urlutil.hidepassword
5186 else:
5186 else:
5187 hidepassword = bytes
5187 hidepassword = bytes
5188 if ui.quiet:
5188 if ui.quiet:
5189 namefmt = b'%s\n'
5189 namefmt = b'%s\n'
5190 else:
5190 else:
5191 namefmt = b'%s = '
5191 namefmt = b'%s = '
5192 showsubopts = not search and not ui.quiet
5192 showsubopts = not search and not ui.quiet
5193
5193
5194 for name, path in pathitems:
5194 for name, path in pathitems:
5195 fm.startitem()
5195 fm.startitem()
5196 fm.condwrite(not search, b'name', namefmt, name)
5196 fm.condwrite(not search, b'name', namefmt, name)
5197 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5197 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5198 for subopt, value in sorted(path.suboptions.items()):
5198 for subopt, value in sorted(path.suboptions.items()):
5199 assert subopt not in (b'name', b'url')
5199 assert subopt not in (b'name', b'url')
5200 if showsubopts:
5200 if showsubopts:
5201 fm.plain(b'%s:%s = ' % (name, subopt))
5201 fm.plain(b'%s:%s = ' % (name, subopt))
5202 if isinstance(value, bool):
5202 if isinstance(value, bool):
5203 if value:
5203 if value:
5204 value = b'yes'
5204 value = b'yes'
5205 else:
5205 else:
5206 value = b'no'
5206 value = b'no'
5207 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5207 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5208
5208
5209 fm.end()
5209 fm.end()
5210
5210
5211 if search and not pathitems:
5211 if search and not pathitems:
5212 if not ui.quiet:
5212 if not ui.quiet:
5213 ui.warn(_(b"not found!\n"))
5213 ui.warn(_(b"not found!\n"))
5214 return 1
5214 return 1
5215 else:
5215 else:
5216 return 0
5216 return 0
5217
5217
5218
5218
5219 @command(
5219 @command(
5220 b'phase',
5220 b'phase',
5221 [
5221 [
5222 (b'p', b'public', False, _(b'set changeset phase to public')),
5222 (b'p', b'public', False, _(b'set changeset phase to public')),
5223 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5223 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5224 (b's', b'secret', False, _(b'set changeset phase to secret')),
5224 (b's', b'secret', False, _(b'set changeset phase to secret')),
5225 (b'f', b'force', False, _(b'allow to move boundary backward')),
5225 (b'f', b'force', False, _(b'allow to move boundary backward')),
5226 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5226 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5227 ],
5227 ],
5228 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5228 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5229 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5229 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5230 )
5230 )
5231 def phase(ui, repo, *revs, **opts):
5231 def phase(ui, repo, *revs, **opts):
5232 """set or show the current phase name
5232 """set or show the current phase name
5233
5233
5234 With no argument, show the phase name of the current revision(s).
5234 With no argument, show the phase name of the current revision(s).
5235
5235
5236 With one of -p/--public, -d/--draft or -s/--secret, change the
5236 With one of -p/--public, -d/--draft or -s/--secret, change the
5237 phase value of the specified revisions.
5237 phase value of the specified revisions.
5238
5238
5239 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5239 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5240 lower phase to a higher phase. Phases are ordered as follows::
5240 lower phase to a higher phase. Phases are ordered as follows::
5241
5241
5242 public < draft < secret
5242 public < draft < secret
5243
5243
5244 Returns 0 on success, 1 if some phases could not be changed.
5244 Returns 0 on success, 1 if some phases could not be changed.
5245
5245
5246 (For more information about the phases concept, see :hg:`help phases`.)
5246 (For more information about the phases concept, see :hg:`help phases`.)
5247 """
5247 """
5248 opts = pycompat.byteskwargs(opts)
5248 opts = pycompat.byteskwargs(opts)
5249 # search for a unique phase argument
5249 # search for a unique phase argument
5250 targetphase = None
5250 targetphase = None
5251 for idx, name in enumerate(phases.cmdphasenames):
5251 for idx, name in enumerate(phases.cmdphasenames):
5252 if opts[name]:
5252 if opts[name]:
5253 if targetphase is not None:
5253 if targetphase is not None:
5254 raise error.InputError(_(b'only one phase can be specified'))
5254 raise error.InputError(_(b'only one phase can be specified'))
5255 targetphase = idx
5255 targetphase = idx
5256
5256
5257 # look for specified revision
5257 # look for specified revision
5258 revs = list(revs)
5258 revs = list(revs)
5259 revs.extend(opts[b'rev'])
5259 revs.extend(opts[b'rev'])
5260 if revs:
5260 if revs:
5261 revs = logcmdutil.revrange(repo, revs)
5261 revs = logcmdutil.revrange(repo, revs)
5262 else:
5262 else:
5263 # display both parents as the second parent phase can influence
5263 # display both parents as the second parent phase can influence
5264 # the phase of a merge commit
5264 # the phase of a merge commit
5265 revs = [c.rev() for c in repo[None].parents()]
5265 revs = [c.rev() for c in repo[None].parents()]
5266
5266
5267 ret = 0
5267 ret = 0
5268 if targetphase is None:
5268 if targetphase is None:
5269 # display
5269 # display
5270 for r in revs:
5270 for r in revs:
5271 ctx = repo[r]
5271 ctx = repo[r]
5272 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5272 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5273 else:
5273 else:
5274 with repo.lock(), repo.transaction(b"phase") as tr:
5274 with repo.lock(), repo.transaction(b"phase") as tr:
5275 # set phase
5275 # set phase
5276 if not revs:
5276 if not revs:
5277 raise error.InputError(_(b'empty revision set'))
5277 raise error.InputError(_(b'empty revision set'))
5278 nodes = [repo[r].node() for r in revs]
5278 nodes = [repo[r].node() for r in revs]
5279 # moving revision from public to draft may hide them
5279 # moving revision from public to draft may hide them
5280 # We have to check result on an unfiltered repository
5280 # We have to check result on an unfiltered repository
5281 unfi = repo.unfiltered()
5281 unfi = repo.unfiltered()
5282 getphase = unfi._phasecache.phase
5282 getphase = unfi._phasecache.phase
5283 olddata = [getphase(unfi, r) for r in unfi]
5283 olddata = [getphase(unfi, r) for r in unfi]
5284 phases.advanceboundary(repo, tr, targetphase, nodes)
5284 phases.advanceboundary(repo, tr, targetphase, nodes)
5285 if opts[b'force']:
5285 if opts[b'force']:
5286 phases.retractboundary(repo, tr, targetphase, nodes)
5286 phases.retractboundary(repo, tr, targetphase, nodes)
5287 getphase = unfi._phasecache.phase
5287 getphase = unfi._phasecache.phase
5288 newdata = [getphase(unfi, r) for r in unfi]
5288 newdata = [getphase(unfi, r) for r in unfi]
5289 changes = sum(newdata[r] != olddata[r] for r in unfi)
5289 changes = sum(newdata[r] != olddata[r] for r in unfi)
5290 cl = unfi.changelog
5290 cl = unfi.changelog
5291 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5291 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5292 if rejected:
5292 if rejected:
5293 ui.warn(
5293 ui.warn(
5294 _(
5294 _(
5295 b'cannot move %i changesets to a higher '
5295 b'cannot move %i changesets to a higher '
5296 b'phase, use --force\n'
5296 b'phase, use --force\n'
5297 )
5297 )
5298 % len(rejected)
5298 % len(rejected)
5299 )
5299 )
5300 ret = 1
5300 ret = 1
5301 if changes:
5301 if changes:
5302 msg = _(b'phase changed for %i changesets\n') % changes
5302 msg = _(b'phase changed for %i changesets\n') % changes
5303 if ret:
5303 if ret:
5304 ui.status(msg)
5304 ui.status(msg)
5305 else:
5305 else:
5306 ui.note(msg)
5306 ui.note(msg)
5307 else:
5307 else:
5308 ui.warn(_(b'no phases changed\n'))
5308 ui.warn(_(b'no phases changed\n'))
5309 return ret
5309 return ret
5310
5310
5311
5311
5312 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5312 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5313 """Run after a changegroup has been added via pull/unbundle
5313 """Run after a changegroup has been added via pull/unbundle
5314
5314
5315 This takes arguments below:
5315 This takes arguments below:
5316
5316
5317 :modheads: change of heads by pull/unbundle
5317 :modheads: change of heads by pull/unbundle
5318 :optupdate: updating working directory is needed or not
5318 :optupdate: updating working directory is needed or not
5319 :checkout: update destination revision (or None to default destination)
5319 :checkout: update destination revision (or None to default destination)
5320 :brev: a name, which might be a bookmark to be activated after updating
5320 :brev: a name, which might be a bookmark to be activated after updating
5321
5321
5322 return True if update raise any conflict, False otherwise.
5322 return True if update raise any conflict, False otherwise.
5323 """
5323 """
5324 if modheads == 0:
5324 if modheads == 0:
5325 return False
5325 return False
5326 if optupdate:
5326 if optupdate:
5327 try:
5327 try:
5328 return hg.updatetotally(ui, repo, checkout, brev)
5328 return hg.updatetotally(ui, repo, checkout, brev)
5329 except error.UpdateAbort as inst:
5329 except error.UpdateAbort as inst:
5330 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5330 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5331 hint = inst.hint
5331 hint = inst.hint
5332 raise error.UpdateAbort(msg, hint=hint)
5332 raise error.UpdateAbort(msg, hint=hint)
5333 if modheads is not None and modheads > 1:
5333 if modheads is not None and modheads > 1:
5334 currentbranchheads = len(repo.branchheads())
5334 currentbranchheads = len(repo.branchheads())
5335 if currentbranchheads == modheads:
5335 if currentbranchheads == modheads:
5336 ui.status(
5336 ui.status(
5337 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5337 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5338 )
5338 )
5339 elif currentbranchheads > 1:
5339 elif currentbranchheads > 1:
5340 ui.status(
5340 ui.status(
5341 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5341 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5342 )
5342 )
5343 else:
5343 else:
5344 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5344 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5345 elif not ui.configbool(b'commands', b'update.requiredest'):
5345 elif not ui.configbool(b'commands', b'update.requiredest'):
5346 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5346 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5347 return False
5347 return False
5348
5348
5349
5349
5350 @command(
5350 @command(
5351 b'pull',
5351 b'pull',
5352 [
5352 [
5353 (
5353 (
5354 b'u',
5354 b'u',
5355 b'update',
5355 b'update',
5356 None,
5356 None,
5357 _(b'update to new branch head if new descendants were pulled'),
5357 _(b'update to new branch head if new descendants were pulled'),
5358 ),
5358 ),
5359 (
5359 (
5360 b'f',
5360 b'f',
5361 b'force',
5361 b'force',
5362 None,
5362 None,
5363 _(b'run even when remote repository is unrelated'),
5363 _(b'run even when remote repository is unrelated'),
5364 ),
5364 ),
5365 (
5365 (
5366 b'',
5366 b'',
5367 b'confirm',
5367 b'confirm',
5368 None,
5368 None,
5369 _(b'confirm pull before applying changes'),
5369 _(b'confirm pull before applying changes'),
5370 ),
5370 ),
5371 (
5371 (
5372 b'r',
5372 b'r',
5373 b'rev',
5373 b'rev',
5374 [],
5374 [],
5375 _(b'a remote changeset intended to be added'),
5375 _(b'a remote changeset intended to be added'),
5376 _(b'REV'),
5376 _(b'REV'),
5377 ),
5377 ),
5378 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5378 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5379 (
5379 (
5380 b'b',
5380 b'b',
5381 b'branch',
5381 b'branch',
5382 [],
5382 [],
5383 _(b'a specific branch you would like to pull'),
5383 _(b'a specific branch you would like to pull'),
5384 _(b'BRANCH'),
5384 _(b'BRANCH'),
5385 ),
5385 ),
5386 ]
5386 ]
5387 + remoteopts,
5387 + remoteopts,
5388 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5388 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5389 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5389 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5390 helpbasic=True,
5390 helpbasic=True,
5391 )
5391 )
5392 def pull(ui, repo, *sources, **opts):
5392 def pull(ui, repo, *sources, **opts):
5393 """pull changes from the specified source
5393 """pull changes from the specified source
5394
5394
5395 Pull changes from a remote repository to a local one.
5395 Pull changes from a remote repository to a local one.
5396
5396
5397 This finds all changes from the repository at the specified path
5397 This finds all changes from the repository at the specified path
5398 or URL and adds them to a local repository (the current one unless
5398 or URL and adds them to a local repository (the current one unless
5399 -R is specified). By default, this does not update the copy of the
5399 -R is specified). By default, this does not update the copy of the
5400 project in the working directory.
5400 project in the working directory.
5401
5401
5402 When cloning from servers that support it, Mercurial may fetch
5402 When cloning from servers that support it, Mercurial may fetch
5403 pre-generated data. When this is done, hooks operating on incoming
5403 pre-generated data. When this is done, hooks operating on incoming
5404 changesets and changegroups may fire more than once, once for each
5404 changesets and changegroups may fire more than once, once for each
5405 pre-generated bundle and as well as for any additional remaining
5405 pre-generated bundle and as well as for any additional remaining
5406 data. See :hg:`help -e clonebundles` for more.
5406 data. See :hg:`help -e clonebundles` for more.
5407
5407
5408 Use :hg:`incoming` if you want to see what would have been added
5408 Use :hg:`incoming` if you want to see what would have been added
5409 by a pull at the time you issued this command. If you then decide
5409 by a pull at the time you issued this command. If you then decide
5410 to add those changes to the repository, you should use :hg:`pull
5410 to add those changes to the repository, you should use :hg:`pull
5411 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5411 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5412
5412
5413 If SOURCE is omitted, the 'default' path will be used.
5413 If SOURCE is omitted, the 'default' path will be used.
5414 See :hg:`help urls` for more information.
5414 See :hg:`help urls` for more information.
5415
5415
5416 If multiple sources are specified, they will be pulled sequentially as if
5416 If multiple sources are specified, they will be pulled sequentially as if
5417 the command was run multiple time. If --update is specify and the command
5417 the command was run multiple time. If --update is specify and the command
5418 will stop at the first failed --update.
5418 will stop at the first failed --update.
5419
5419
5420 Specifying bookmark as ``.`` is equivalent to specifying the active
5420 Specifying bookmark as ``.`` is equivalent to specifying the active
5421 bookmark's name.
5421 bookmark's name.
5422
5422
5423 Returns 0 on success, 1 if an update had unresolved files.
5423 Returns 0 on success, 1 if an update had unresolved files.
5424 """
5424 """
5425
5425
5426 opts = pycompat.byteskwargs(opts)
5426 opts = pycompat.byteskwargs(opts)
5427 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5427 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5428 b'update'
5428 b'update'
5429 ):
5429 ):
5430 msg = _(b'update destination required by configuration')
5430 msg = _(b'update destination required by configuration')
5431 hint = _(b'use hg pull followed by hg update DEST')
5431 hint = _(b'use hg pull followed by hg update DEST')
5432 raise error.InputError(msg, hint=hint)
5432 raise error.InputError(msg, hint=hint)
5433
5433
5434 for path in urlutil.get_pull_paths(repo, ui, sources):
5434 for path in urlutil.get_pull_paths(repo, ui, sources):
5435 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5435 source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5436 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5436 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5437 ui.flush()
5437 ui.flush()
5438 other = hg.peer(repo, opts, source)
5438 other = hg.peer(repo, opts, source)
5439 update_conflict = None
5439 update_conflict = None
5440 try:
5440 try:
5441 revs, checkout = hg.addbranchrevs(
5441 revs, checkout = hg.addbranchrevs(
5442 repo, other, branches, opts.get(b'rev')
5442 repo, other, branches, opts.get(b'rev')
5443 )
5443 )
5444
5444
5445 pullopargs = {}
5445 pullopargs = {}
5446
5446
5447 nodes = None
5447 nodes = None
5448 if opts.get(b'bookmark') or revs:
5448 if opts.get(b'bookmark') or revs:
5449 # The list of bookmark used here is the same used to actually update
5449 # The list of bookmark used here is the same used to actually update
5450 # the bookmark names, to avoid the race from issue 4689 and we do
5450 # the bookmark names, to avoid the race from issue 4689 and we do
5451 # all lookup and bookmark queries in one go so they see the same
5451 # all lookup and bookmark queries in one go so they see the same
5452 # version of the server state (issue 4700).
5452 # version of the server state (issue 4700).
5453 nodes = []
5453 nodes = []
5454 fnodes = []
5454 fnodes = []
5455 revs = revs or []
5455 revs = revs or []
5456 if revs and not other.capable(b'lookup'):
5456 if revs and not other.capable(b'lookup'):
5457 err = _(
5457 err = _(
5458 b"other repository doesn't support revision lookup, "
5458 b"other repository doesn't support revision lookup, "
5459 b"so a rev cannot be specified."
5459 b"so a rev cannot be specified."
5460 )
5460 )
5461 raise error.Abort(err)
5461 raise error.Abort(err)
5462 with other.commandexecutor() as e:
5462 with other.commandexecutor() as e:
5463 fremotebookmarks = e.callcommand(
5463 fremotebookmarks = e.callcommand(
5464 b'listkeys', {b'namespace': b'bookmarks'}
5464 b'listkeys', {b'namespace': b'bookmarks'}
5465 )
5465 )
5466 for r in revs:
5466 for r in revs:
5467 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5467 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5468 remotebookmarks = fremotebookmarks.result()
5468 remotebookmarks = fremotebookmarks.result()
5469 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5469 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5470 pullopargs[b'remotebookmarks'] = remotebookmarks
5470 pullopargs[b'remotebookmarks'] = remotebookmarks
5471 for b in opts.get(b'bookmark', []):
5471 for b in opts.get(b'bookmark', []):
5472 b = repo._bookmarks.expandname(b)
5472 b = repo._bookmarks.expandname(b)
5473 if b not in remotebookmarks:
5473 if b not in remotebookmarks:
5474 raise error.InputError(
5474 raise error.InputError(
5475 _(b'remote bookmark %s not found!') % b
5475 _(b'remote bookmark %s not found!') % b
5476 )
5476 )
5477 nodes.append(remotebookmarks[b])
5477 nodes.append(remotebookmarks[b])
5478 for i, rev in enumerate(revs):
5478 for i, rev in enumerate(revs):
5479 node = fnodes[i].result()
5479 node = fnodes[i].result()
5480 nodes.append(node)
5480 nodes.append(node)
5481 if rev == checkout:
5481 if rev == checkout:
5482 checkout = node
5482 checkout = node
5483
5483
5484 wlock = util.nullcontextmanager()
5484 wlock = util.nullcontextmanager()
5485 if opts.get(b'update'):
5485 if opts.get(b'update'):
5486 wlock = repo.wlock()
5486 wlock = repo.wlock()
5487 with wlock:
5487 with wlock:
5488 pullopargs.update(opts.get(b'opargs', {}))
5488 pullopargs.update(opts.get(b'opargs', {}))
5489 modheads = exchange.pull(
5489 modheads = exchange.pull(
5490 repo,
5490 repo,
5491 other,
5491 other,
5492 path=path,
5492 path=path,
5493 heads=nodes,
5493 heads=nodes,
5494 force=opts.get(b'force'),
5494 force=opts.get(b'force'),
5495 bookmarks=opts.get(b'bookmark', ()),
5495 bookmarks=opts.get(b'bookmark', ()),
5496 opargs=pullopargs,
5496 opargs=pullopargs,
5497 confirm=opts.get(b'confirm'),
5497 confirm=opts.get(b'confirm'),
5498 ).cgresult
5498 ).cgresult
5499
5499
5500 # brev is a name, which might be a bookmark to be activated at
5500 # brev is a name, which might be a bookmark to be activated at
5501 # the end of the update. In other words, it is an explicit
5501 # the end of the update. In other words, it is an explicit
5502 # destination of the update
5502 # destination of the update
5503 brev = None
5503 brev = None
5504
5504
5505 if checkout:
5505 if checkout:
5506 checkout = repo.unfiltered().changelog.rev(checkout)
5506 checkout = repo.unfiltered().changelog.rev(checkout)
5507
5507
5508 # order below depends on implementation of
5508 # order below depends on implementation of
5509 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5509 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5510 # because 'checkout' is determined without it.
5510 # because 'checkout' is determined without it.
5511 if opts.get(b'rev'):
5511 if opts.get(b'rev'):
5512 brev = opts[b'rev'][0]
5512 brev = opts[b'rev'][0]
5513 elif opts.get(b'branch'):
5513 elif opts.get(b'branch'):
5514 brev = opts[b'branch'][0]
5514 brev = opts[b'branch'][0]
5515 else:
5515 else:
5516 brev = branches[0]
5516 brev = branches[0]
5517 repo._subtoppath = source
5517 repo._subtoppath = source
5518 try:
5518 try:
5519 update_conflict = postincoming(
5519 update_conflict = postincoming(
5520 ui, repo, modheads, opts.get(b'update'), checkout, brev
5520 ui, repo, modheads, opts.get(b'update'), checkout, brev
5521 )
5521 )
5522 except error.FilteredRepoLookupError as exc:
5522 except error.FilteredRepoLookupError as exc:
5523 msg = _(b'cannot update to target: %s') % exc.args[0]
5523 msg = _(b'cannot update to target: %s') % exc.args[0]
5524 exc.args = (msg,) + exc.args[1:]
5524 exc.args = (msg,) + exc.args[1:]
5525 raise
5525 raise
5526 finally:
5526 finally:
5527 del repo._subtoppath
5527 del repo._subtoppath
5528
5528
5529 finally:
5529 finally:
5530 other.close()
5530 other.close()
5531 # skip the remaining pull source if they are some conflict.
5531 # skip the remaining pull source if they are some conflict.
5532 if update_conflict:
5532 if update_conflict:
5533 break
5533 break
5534 if update_conflict:
5534 if update_conflict:
5535 return 1
5535 return 1
5536 else:
5536 else:
5537 return 0
5537 return 0
5538
5538
5539
5539
5540 @command(
5540 @command(
5541 b'purge|clean',
5541 b'purge|clean',
5542 [
5542 [
5543 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5543 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5544 (b'', b'all', None, _(b'purge ignored files too')),
5544 (b'', b'all', None, _(b'purge ignored files too')),
5545 (b'i', b'ignored', None, _(b'purge only ignored files')),
5545 (b'i', b'ignored', None, _(b'purge only ignored files')),
5546 (b'', b'dirs', None, _(b'purge empty directories')),
5546 (b'', b'dirs', None, _(b'purge empty directories')),
5547 (b'', b'files', None, _(b'purge files')),
5547 (b'', b'files', None, _(b'purge files')),
5548 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5548 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5549 (
5549 (
5550 b'0',
5550 b'0',
5551 b'print0',
5551 b'print0',
5552 None,
5552 None,
5553 _(
5553 _(
5554 b'end filenames with NUL, for use with xargs'
5554 b'end filenames with NUL, for use with xargs'
5555 b' (implies -p/--print)'
5555 b' (implies -p/--print)'
5556 ),
5556 ),
5557 ),
5557 ),
5558 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5558 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5559 ]
5559 ]
5560 + cmdutil.walkopts,
5560 + cmdutil.walkopts,
5561 _(b'hg purge [OPTION]... [DIR]...'),
5561 _(b'hg purge [OPTION]... [DIR]...'),
5562 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5562 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5563 )
5563 )
5564 def purge(ui, repo, *dirs, **opts):
5564 def purge(ui, repo, *dirs, **opts):
5565 """removes files not tracked by Mercurial
5565 """removes files not tracked by Mercurial
5566
5566
5567 Delete files not known to Mercurial. This is useful to test local
5567 Delete files not known to Mercurial. This is useful to test local
5568 and uncommitted changes in an otherwise-clean source tree.
5568 and uncommitted changes in an otherwise-clean source tree.
5569
5569
5570 This means that purge will delete the following by default:
5570 This means that purge will delete the following by default:
5571
5571
5572 - Unknown files: files marked with "?" by :hg:`status`
5572 - Unknown files: files marked with "?" by :hg:`status`
5573 - Empty directories: in fact Mercurial ignores directories unless
5573 - Empty directories: in fact Mercurial ignores directories unless
5574 they contain files under source control management
5574 they contain files under source control management
5575
5575
5576 But it will leave untouched:
5576 But it will leave untouched:
5577
5577
5578 - Modified and unmodified tracked files
5578 - Modified and unmodified tracked files
5579 - Ignored files (unless -i or --all is specified)
5579 - Ignored files (unless -i or --all is specified)
5580 - New files added to the repository (with :hg:`add`)
5580 - New files added to the repository (with :hg:`add`)
5581
5581
5582 The --files and --dirs options can be used to direct purge to delete
5582 The --files and --dirs options can be used to direct purge to delete
5583 only files, only directories, or both. If neither option is given,
5583 only files, only directories, or both. If neither option is given,
5584 both will be deleted.
5584 both will be deleted.
5585
5585
5586 If directories are given on the command line, only files in these
5586 If directories are given on the command line, only files in these
5587 directories are considered.
5587 directories are considered.
5588
5588
5589 Be careful with purge, as you could irreversibly delete some files
5589 Be careful with purge, as you could irreversibly delete some files
5590 you forgot to add to the repository. If you only want to print the
5590 you forgot to add to the repository. If you only want to print the
5591 list of files that this program would delete, use the --print
5591 list of files that this program would delete, use the --print
5592 option.
5592 option.
5593 """
5593 """
5594 opts = pycompat.byteskwargs(opts)
5594 opts = pycompat.byteskwargs(opts)
5595 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5595 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5596
5596
5597 act = not opts.get(b'print')
5597 act = not opts.get(b'print')
5598 eol = b'\n'
5598 eol = b'\n'
5599 if opts.get(b'print0'):
5599 if opts.get(b'print0'):
5600 eol = b'\0'
5600 eol = b'\0'
5601 act = False # --print0 implies --print
5601 act = False # --print0 implies --print
5602 if opts.get(b'all', False):
5602 if opts.get(b'all', False):
5603 ignored = True
5603 ignored = True
5604 unknown = True
5604 unknown = True
5605 else:
5605 else:
5606 ignored = opts.get(b'ignored', False)
5606 ignored = opts.get(b'ignored', False)
5607 unknown = not ignored
5607 unknown = not ignored
5608
5608
5609 removefiles = opts.get(b'files')
5609 removefiles = opts.get(b'files')
5610 removedirs = opts.get(b'dirs')
5610 removedirs = opts.get(b'dirs')
5611 confirm = opts.get(b'confirm')
5611 confirm = opts.get(b'confirm')
5612 if confirm is None:
5612 if confirm is None:
5613 try:
5613 try:
5614 extensions.find(b'purge')
5614 extensions.find(b'purge')
5615 confirm = False
5615 confirm = False
5616 except KeyError:
5616 except KeyError:
5617 confirm = True
5617 confirm = True
5618
5618
5619 if not removefiles and not removedirs:
5619 if not removefiles and not removedirs:
5620 removefiles = True
5620 removefiles = True
5621 removedirs = True
5621 removedirs = True
5622
5622
5623 match = scmutil.match(repo[None], dirs, opts)
5623 match = scmutil.match(repo[None], dirs, opts)
5624
5624
5625 paths = mergemod.purge(
5625 paths = mergemod.purge(
5626 repo,
5626 repo,
5627 match,
5627 match,
5628 unknown=unknown,
5628 unknown=unknown,
5629 ignored=ignored,
5629 ignored=ignored,
5630 removeemptydirs=removedirs,
5630 removeemptydirs=removedirs,
5631 removefiles=removefiles,
5631 removefiles=removefiles,
5632 abortonerror=opts.get(b'abort_on_err'),
5632 abortonerror=opts.get(b'abort_on_err'),
5633 noop=not act,
5633 noop=not act,
5634 confirm=confirm,
5634 confirm=confirm,
5635 )
5635 )
5636
5636
5637 for path in paths:
5637 for path in paths:
5638 if not act:
5638 if not act:
5639 ui.write(b'%s%s' % (path, eol))
5639 ui.write(b'%s%s' % (path, eol))
5640
5640
5641
5641
5642 @command(
5642 @command(
5643 b'push',
5643 b'push',
5644 [
5644 [
5645 (b'f', b'force', None, _(b'force push')),
5645 (b'f', b'force', None, _(b'force push')),
5646 (
5646 (
5647 b'r',
5647 b'r',
5648 b'rev',
5648 b'rev',
5649 [],
5649 [],
5650 _(b'a changeset intended to be included in the destination'),
5650 _(b'a changeset intended to be included in the destination'),
5651 _(b'REV'),
5651 _(b'REV'),
5652 ),
5652 ),
5653 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5653 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5654 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5654 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5655 (
5655 (
5656 b'b',
5656 b'b',
5657 b'branch',
5657 b'branch',
5658 [],
5658 [],
5659 _(b'a specific branch you would like to push'),
5659 _(b'a specific branch you would like to push'),
5660 _(b'BRANCH'),
5660 _(b'BRANCH'),
5661 ),
5661 ),
5662 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5662 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5663 (
5663 (
5664 b'',
5664 b'',
5665 b'pushvars',
5665 b'pushvars',
5666 [],
5666 [],
5667 _(b'variables that can be sent to server (ADVANCED)'),
5667 _(b'variables that can be sent to server (ADVANCED)'),
5668 ),
5668 ),
5669 (
5669 (
5670 b'',
5670 b'',
5671 b'publish',
5671 b'publish',
5672 False,
5672 False,
5673 _(b'push the changeset as public (EXPERIMENTAL)'),
5673 _(b'push the changeset as public (EXPERIMENTAL)'),
5674 ),
5674 ),
5675 ]
5675 ]
5676 + remoteopts,
5676 + remoteopts,
5677 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5677 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5678 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5678 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5679 helpbasic=True,
5679 helpbasic=True,
5680 )
5680 )
5681 def push(ui, repo, *dests, **opts):
5681 def push(ui, repo, *dests, **opts):
5682 """push changes to the specified destination
5682 """push changes to the specified destination
5683
5683
5684 Push changesets from the local repository to the specified
5684 Push changesets from the local repository to the specified
5685 destination.
5685 destination.
5686
5686
5687 This operation is symmetrical to pull: it is identical to a pull
5687 This operation is symmetrical to pull: it is identical to a pull
5688 in the destination repository from the current one.
5688 in the destination repository from the current one.
5689
5689
5690 By default, push will not allow creation of new heads at the
5690 By default, push will not allow creation of new heads at the
5691 destination, since multiple heads would make it unclear which head
5691 destination, since multiple heads would make it unclear which head
5692 to use. In this situation, it is recommended to pull and merge
5692 to use. In this situation, it is recommended to pull and merge
5693 before pushing.
5693 before pushing.
5694
5694
5695 Use --new-branch if you want to allow push to create a new named
5695 Use --new-branch if you want to allow push to create a new named
5696 branch that is not present at the destination. This allows you to
5696 branch that is not present at the destination. This allows you to
5697 only create a new branch without forcing other changes.
5697 only create a new branch without forcing other changes.
5698
5698
5699 .. note::
5699 .. note::
5700
5700
5701 Extra care should be taken with the -f/--force option,
5701 Extra care should be taken with the -f/--force option,
5702 which will push all new heads on all branches, an action which will
5702 which will push all new heads on all branches, an action which will
5703 almost always cause confusion for collaborators.
5703 almost always cause confusion for collaborators.
5704
5704
5705 If -r/--rev is used, the specified revision and all its ancestors
5705 If -r/--rev is used, the specified revision and all its ancestors
5706 will be pushed to the remote repository.
5706 will be pushed to the remote repository.
5707
5707
5708 If -B/--bookmark is used, the specified bookmarked revision, its
5708 If -B/--bookmark is used, the specified bookmarked revision, its
5709 ancestors, and the bookmark will be pushed to the remote
5709 ancestors, and the bookmark will be pushed to the remote
5710 repository. Specifying ``.`` is equivalent to specifying the active
5710 repository. Specifying ``.`` is equivalent to specifying the active
5711 bookmark's name. Use the --all-bookmarks option for pushing all
5711 bookmark's name. Use the --all-bookmarks option for pushing all
5712 current bookmarks.
5712 current bookmarks.
5713
5713
5714 Please see :hg:`help urls` for important details about ``ssh://``
5714 Please see :hg:`help urls` for important details about ``ssh://``
5715 URLs. If DESTINATION is omitted, a default path will be used.
5715 URLs. If DESTINATION is omitted, a default path will be used.
5716
5716
5717 When passed multiple destinations, push will process them one after the
5717 When passed multiple destinations, push will process them one after the
5718 other, but stop should an error occur.
5718 other, but stop should an error occur.
5719
5719
5720 .. container:: verbose
5720 .. container:: verbose
5721
5721
5722 The --pushvars option sends strings to the server that become
5722 The --pushvars option sends strings to the server that become
5723 environment variables prepended with ``HG_USERVAR_``. For example,
5723 environment variables prepended with ``HG_USERVAR_``. For example,
5724 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5724 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5725 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5725 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5726
5726
5727 pushvars can provide for user-overridable hooks as well as set debug
5727 pushvars can provide for user-overridable hooks as well as set debug
5728 levels. One example is having a hook that blocks commits containing
5728 levels. One example is having a hook that blocks commits containing
5729 conflict markers, but enables the user to override the hook if the file
5729 conflict markers, but enables the user to override the hook if the file
5730 is using conflict markers for testing purposes or the file format has
5730 is using conflict markers for testing purposes or the file format has
5731 strings that look like conflict markers.
5731 strings that look like conflict markers.
5732
5732
5733 By default, servers will ignore `--pushvars`. To enable it add the
5733 By default, servers will ignore `--pushvars`. To enable it add the
5734 following to your configuration file::
5734 following to your configuration file::
5735
5735
5736 [push]
5736 [push]
5737 pushvars.server = true
5737 pushvars.server = true
5738
5738
5739 Returns 0 if push was successful, 1 if nothing to push.
5739 Returns 0 if push was successful, 1 if nothing to push.
5740 """
5740 """
5741
5741
5742 opts = pycompat.byteskwargs(opts)
5742 opts = pycompat.byteskwargs(opts)
5743
5743
5744 if opts.get(b'all_bookmarks'):
5744 if opts.get(b'all_bookmarks'):
5745 cmdutil.check_incompatible_arguments(
5745 cmdutil.check_incompatible_arguments(
5746 opts,
5746 opts,
5747 b'all_bookmarks',
5747 b'all_bookmarks',
5748 [b'bookmark', b'rev'],
5748 [b'bookmark', b'rev'],
5749 )
5749 )
5750 opts[b'bookmark'] = list(repo._bookmarks)
5750 opts[b'bookmark'] = list(repo._bookmarks)
5751
5751
5752 if opts.get(b'bookmark'):
5752 if opts.get(b'bookmark'):
5753 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5753 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5754 for b in opts[b'bookmark']:
5754 for b in opts[b'bookmark']:
5755 # translate -B options to -r so changesets get pushed
5755 # translate -B options to -r so changesets get pushed
5756 b = repo._bookmarks.expandname(b)
5756 b = repo._bookmarks.expandname(b)
5757 if b in repo._bookmarks:
5757 if b in repo._bookmarks:
5758 opts.setdefault(b'rev', []).append(b)
5758 opts.setdefault(b'rev', []).append(b)
5759 else:
5759 else:
5760 # if we try to push a deleted bookmark, translate it to null
5760 # if we try to push a deleted bookmark, translate it to null
5761 # this lets simultaneous -r, -b options continue working
5761 # this lets simultaneous -r, -b options continue working
5762 opts.setdefault(b'rev', []).append(b"null")
5762 opts.setdefault(b'rev', []).append(b"null")
5763
5763
5764 some_pushed = False
5764 some_pushed = False
5765 result = 0
5765 result = 0
5766 for path in urlutil.get_push_paths(repo, ui, dests):
5766 for path in urlutil.get_push_paths(repo, ui, dests):
5767 dest = path.pushloc or path.loc
5767 dest = path.pushloc or path.loc
5768 branches = (path.branch, opts.get(b'branch') or [])
5768 branches = (path.branch, opts.get(b'branch') or [])
5769 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5769 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5770 revs, checkout = hg.addbranchrevs(
5770 revs, checkout = hg.addbranchrevs(
5771 repo, repo, branches, opts.get(b'rev')
5771 repo, repo, branches, opts.get(b'rev')
5772 )
5772 )
5773 other = hg.peer(repo, opts, dest)
5773 other = hg.peer(repo, opts, dest)
5774
5774
5775 try:
5775 try:
5776 if revs:
5776 if revs:
5777 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5777 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5778 if not revs:
5778 if not revs:
5779 raise error.InputError(
5779 raise error.InputError(
5780 _(b"specified revisions evaluate to an empty set"),
5780 _(b"specified revisions evaluate to an empty set"),
5781 hint=_(b"use different revision arguments"),
5781 hint=_(b"use different revision arguments"),
5782 )
5782 )
5783 elif path.pushrev:
5783 elif path.pushrev:
5784 # It doesn't make any sense to specify ancestor revisions. So limit
5784 # It doesn't make any sense to specify ancestor revisions. So limit
5785 # to DAG heads to make discovery simpler.
5785 # to DAG heads to make discovery simpler.
5786 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5786 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5787 revs = scmutil.revrange(repo, [expr])
5787 revs = scmutil.revrange(repo, [expr])
5788 revs = [repo[rev].node() for rev in revs]
5788 revs = [repo[rev].node() for rev in revs]
5789 if not revs:
5789 if not revs:
5790 raise error.InputError(
5790 raise error.InputError(
5791 _(
5791 _(
5792 b'default push revset for path evaluates to an empty set'
5792 b'default push revset for path evaluates to an empty set'
5793 )
5793 )
5794 )
5794 )
5795 elif ui.configbool(b'commands', b'push.require-revs'):
5795 elif ui.configbool(b'commands', b'push.require-revs'):
5796 raise error.InputError(
5796 raise error.InputError(
5797 _(b'no revisions specified to push'),
5797 _(b'no revisions specified to push'),
5798 hint=_(b'did you mean "hg push -r ."?'),
5798 hint=_(b'did you mean "hg push -r ."?'),
5799 )
5799 )
5800
5800
5801 repo._subtoppath = dest
5801 repo._subtoppath = dest
5802 try:
5802 try:
5803 # push subrepos depth-first for coherent ordering
5803 # push subrepos depth-first for coherent ordering
5804 c = repo[b'.']
5804 c = repo[b'.']
5805 subs = c.substate # only repos that are committed
5805 subs = c.substate # only repos that are committed
5806 for s in sorted(subs):
5806 for s in sorted(subs):
5807 sub_result = c.sub(s).push(opts)
5807 sub_result = c.sub(s).push(opts)
5808 if sub_result == 0:
5808 if sub_result == 0:
5809 return 1
5809 return 1
5810 finally:
5810 finally:
5811 del repo._subtoppath
5811 del repo._subtoppath
5812
5812
5813 opargs = dict(
5813 opargs = dict(
5814 opts.get(b'opargs', {})
5814 opts.get(b'opargs', {})
5815 ) # copy opargs since we may mutate it
5815 ) # copy opargs since we may mutate it
5816 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5816 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5817
5817
5818 pushop = exchange.push(
5818 pushop = exchange.push(
5819 repo,
5819 repo,
5820 other,
5820 other,
5821 opts.get(b'force'),
5821 opts.get(b'force'),
5822 revs=revs,
5822 revs=revs,
5823 newbranch=opts.get(b'new_branch'),
5823 newbranch=opts.get(b'new_branch'),
5824 bookmarks=opts.get(b'bookmark', ()),
5824 bookmarks=opts.get(b'bookmark', ()),
5825 publish=opts.get(b'publish'),
5825 publish=opts.get(b'publish'),
5826 opargs=opargs,
5826 opargs=opargs,
5827 )
5827 )
5828
5828
5829 if pushop.cgresult == 0:
5829 if pushop.cgresult == 0:
5830 result = 1
5830 result = 1
5831 elif pushop.cgresult is not None:
5831 elif pushop.cgresult is not None:
5832 some_pushed = True
5832 some_pushed = True
5833
5833
5834 if pushop.bkresult is not None:
5834 if pushop.bkresult is not None:
5835 if pushop.bkresult == 2:
5835 if pushop.bkresult == 2:
5836 result = 2
5836 result = 2
5837 elif not result and pushop.bkresult:
5837 elif not result and pushop.bkresult:
5838 result = 2
5838 result = 2
5839
5839
5840 if result:
5840 if result:
5841 break
5841 break
5842
5842
5843 finally:
5843 finally:
5844 other.close()
5844 other.close()
5845 if result == 0 and not some_pushed:
5845 if result == 0 and not some_pushed:
5846 result = 1
5846 result = 1
5847 return result
5847 return result
5848
5848
5849
5849
5850 @command(
5850 @command(
5851 b'recover',
5851 b'recover',
5852 [
5852 [
5853 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5853 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5854 ],
5854 ],
5855 helpcategory=command.CATEGORY_MAINTENANCE,
5855 helpcategory=command.CATEGORY_MAINTENANCE,
5856 )
5856 )
5857 def recover(ui, repo, **opts):
5857 def recover(ui, repo, **opts):
5858 """roll back an interrupted transaction
5858 """roll back an interrupted transaction
5859
5859
5860 Recover from an interrupted commit or pull.
5860 Recover from an interrupted commit or pull.
5861
5861
5862 This command tries to fix the repository status after an
5862 This command tries to fix the repository status after an
5863 interrupted operation. It should only be necessary when Mercurial
5863 interrupted operation. It should only be necessary when Mercurial
5864 suggests it.
5864 suggests it.
5865
5865
5866 Returns 0 if successful, 1 if nothing to recover or verify fails.
5866 Returns 0 if successful, 1 if nothing to recover or verify fails.
5867 """
5867 """
5868 ret = repo.recover()
5868 ret = repo.recover()
5869 if ret:
5869 if ret:
5870 if opts['verify']:
5870 if opts['verify']:
5871 return hg.verify(repo)
5871 return hg.verify(repo)
5872 else:
5872 else:
5873 msg = _(
5873 msg = _(
5874 b"(verify step skipped, run `hg verify` to check your "
5874 b"(verify step skipped, run `hg verify` to check your "
5875 b"repository content)\n"
5875 b"repository content)\n"
5876 )
5876 )
5877 ui.warn(msg)
5877 ui.warn(msg)
5878 return 0
5878 return 0
5879 return 1
5879 return 1
5880
5880
5881
5881
5882 @command(
5882 @command(
5883 b'remove|rm',
5883 b'remove|rm',
5884 [
5884 [
5885 (b'A', b'after', None, _(b'record delete for missing files')),
5885 (b'A', b'after', None, _(b'record delete for missing files')),
5886 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5886 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5887 ]
5887 ]
5888 + subrepoopts
5888 + subrepoopts
5889 + walkopts
5889 + walkopts
5890 + dryrunopts,
5890 + dryrunopts,
5891 _(b'[OPTION]... FILE...'),
5891 _(b'[OPTION]... FILE...'),
5892 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5892 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5893 helpbasic=True,
5893 helpbasic=True,
5894 inferrepo=True,
5894 inferrepo=True,
5895 )
5895 )
5896 def remove(ui, repo, *pats, **opts):
5896 def remove(ui, repo, *pats, **opts):
5897 """remove the specified files on the next commit
5897 """remove the specified files on the next commit
5898
5898
5899 Schedule the indicated files for removal from the current branch.
5899 Schedule the indicated files for removal from the current branch.
5900
5900
5901 This command schedules the files to be removed at the next commit.
5901 This command schedules the files to be removed at the next commit.
5902 To undo a remove before that, see :hg:`revert`. To undo added
5902 To undo a remove before that, see :hg:`revert`. To undo added
5903 files, see :hg:`forget`.
5903 files, see :hg:`forget`.
5904
5904
5905 .. container:: verbose
5905 .. container:: verbose
5906
5906
5907 -A/--after can be used to remove only files that have already
5907 -A/--after can be used to remove only files that have already
5908 been deleted, -f/--force can be used to force deletion, and -Af
5908 been deleted, -f/--force can be used to force deletion, and -Af
5909 can be used to remove files from the next revision without
5909 can be used to remove files from the next revision without
5910 deleting them from the working directory.
5910 deleting them from the working directory.
5911
5911
5912 The following table details the behavior of remove for different
5912 The following table details the behavior of remove for different
5913 file states (columns) and option combinations (rows). The file
5913 file states (columns) and option combinations (rows). The file
5914 states are Added [A], Clean [C], Modified [M] and Missing [!]
5914 states are Added [A], Clean [C], Modified [M] and Missing [!]
5915 (as reported by :hg:`status`). The actions are Warn, Remove
5915 (as reported by :hg:`status`). The actions are Warn, Remove
5916 (from branch) and Delete (from disk):
5916 (from branch) and Delete (from disk):
5917
5917
5918 ========= == == == ==
5918 ========= == == == ==
5919 opt/state A C M !
5919 opt/state A C M !
5920 ========= == == == ==
5920 ========= == == == ==
5921 none W RD W R
5921 none W RD W R
5922 -f R RD RD R
5922 -f R RD RD R
5923 -A W W W R
5923 -A W W W R
5924 -Af R R R R
5924 -Af R R R R
5925 ========= == == == ==
5925 ========= == == == ==
5926
5926
5927 .. note::
5927 .. note::
5928
5928
5929 :hg:`remove` never deletes files in Added [A] state from the
5929 :hg:`remove` never deletes files in Added [A] state from the
5930 working directory, not even if ``--force`` is specified.
5930 working directory, not even if ``--force`` is specified.
5931
5931
5932 Returns 0 on success, 1 if any warnings encountered.
5932 Returns 0 on success, 1 if any warnings encountered.
5933 """
5933 """
5934
5934
5935 opts = pycompat.byteskwargs(opts)
5935 opts = pycompat.byteskwargs(opts)
5936 after, force = opts.get(b'after'), opts.get(b'force')
5936 after, force = opts.get(b'after'), opts.get(b'force')
5937 dryrun = opts.get(b'dry_run')
5937 dryrun = opts.get(b'dry_run')
5938 if not pats and not after:
5938 if not pats and not after:
5939 raise error.InputError(_(b'no files specified'))
5939 raise error.InputError(_(b'no files specified'))
5940
5940
5941 m = scmutil.match(repo[None], pats, opts)
5941 m = scmutil.match(repo[None], pats, opts)
5942 subrepos = opts.get(b'subrepos')
5942 subrepos = opts.get(b'subrepos')
5943 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5943 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5944 return cmdutil.remove(
5944 return cmdutil.remove(
5945 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5945 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5946 )
5946 )
5947
5947
5948
5948
5949 @command(
5949 @command(
5950 b'rename|move|mv',
5950 b'rename|move|mv',
5951 [
5951 [
5952 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5952 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5953 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5953 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5954 (
5954 (
5955 b'',
5955 b'',
5956 b'at-rev',
5956 b'at-rev',
5957 b'',
5957 b'',
5958 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5958 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5959 _(b'REV'),
5959 _(b'REV'),
5960 ),
5960 ),
5961 (
5961 (
5962 b'f',
5962 b'f',
5963 b'force',
5963 b'force',
5964 None,
5964 None,
5965 _(b'forcibly move over an existing managed file'),
5965 _(b'forcibly move over an existing managed file'),
5966 ),
5966 ),
5967 ]
5967 ]
5968 + walkopts
5968 + walkopts
5969 + dryrunopts,
5969 + dryrunopts,
5970 _(b'[OPTION]... SOURCE... DEST'),
5970 _(b'[OPTION]... SOURCE... DEST'),
5971 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5971 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5972 )
5972 )
5973 def rename(ui, repo, *pats, **opts):
5973 def rename(ui, repo, *pats, **opts):
5974 """rename files; equivalent of copy + remove
5974 """rename files; equivalent of copy + remove
5975
5975
5976 Mark dest as copies of sources; mark sources for deletion. If dest
5976 Mark dest as copies of sources; mark sources for deletion. If dest
5977 is a directory, copies are put in that directory. If dest is a
5977 is a directory, copies are put in that directory. If dest is a
5978 file, there can only be one source.
5978 file, there can only be one source.
5979
5979
5980 By default, this command copies the contents of files as they
5980 By default, this command copies the contents of files as they
5981 exist in the working directory. If invoked with -A/--after, the
5981 exist in the working directory. If invoked with -A/--after, the
5982 operation is recorded, but no copying is performed.
5982 operation is recorded, but no copying is performed.
5983
5983
5984 To undo marking a destination file as renamed, use --forget. With that
5984 To undo marking a destination file as renamed, use --forget. With that
5985 option, all given (positional) arguments are unmarked as renames. The
5985 option, all given (positional) arguments are unmarked as renames. The
5986 destination file(s) will be left in place (still tracked). The source
5986 destination file(s) will be left in place (still tracked). The source
5987 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5987 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5988 the same way as :hg:`copy --forget`.
5988 the same way as :hg:`copy --forget`.
5989
5989
5990 This command takes effect with the next commit by default.
5990 This command takes effect with the next commit by default.
5991
5991
5992 Returns 0 on success, 1 if errors are encountered.
5992 Returns 0 on success, 1 if errors are encountered.
5993 """
5993 """
5994 opts = pycompat.byteskwargs(opts)
5994 opts = pycompat.byteskwargs(opts)
5995 with repo.wlock():
5995 with repo.wlock():
5996 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5996 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5997
5997
5998
5998
5999 @command(
5999 @command(
6000 b'resolve',
6000 b'resolve',
6001 [
6001 [
6002 (b'a', b'all', None, _(b'select all unresolved files')),
6002 (b'a', b'all', None, _(b'select all unresolved files')),
6003 (b'l', b'list', None, _(b'list state of files needing merge')),
6003 (b'l', b'list', None, _(b'list state of files needing merge')),
6004 (b'm', b'mark', None, _(b'mark files as resolved')),
6004 (b'm', b'mark', None, _(b'mark files as resolved')),
6005 (b'u', b'unmark', None, _(b'mark files as unresolved')),
6005 (b'u', b'unmark', None, _(b'mark files as unresolved')),
6006 (b'n', b'no-status', None, _(b'hide status prefix')),
6006 (b'n', b'no-status', None, _(b'hide status prefix')),
6007 (b'', b're-merge', None, _(b're-merge files')),
6007 (b'', b're-merge', None, _(b're-merge files')),
6008 ]
6008 ]
6009 + mergetoolopts
6009 + mergetoolopts
6010 + walkopts
6010 + walkopts
6011 + formatteropts,
6011 + formatteropts,
6012 _(b'[OPTION]... [FILE]...'),
6012 _(b'[OPTION]... [FILE]...'),
6013 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6013 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6014 inferrepo=True,
6014 inferrepo=True,
6015 )
6015 )
6016 def resolve(ui, repo, *pats, **opts):
6016 def resolve(ui, repo, *pats, **opts):
6017 """redo merges or set/view the merge status of files
6017 """redo merges or set/view the merge status of files
6018
6018
6019 Merges with unresolved conflicts are often the result of
6019 Merges with unresolved conflicts are often the result of
6020 non-interactive merging using the ``internal:merge`` configuration
6020 non-interactive merging using the ``internal:merge`` configuration
6021 setting, or a command-line merge tool like ``diff3``. The resolve
6021 setting, or a command-line merge tool like ``diff3``. The resolve
6022 command is used to manage the files involved in a merge, after
6022 command is used to manage the files involved in a merge, after
6023 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6023 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6024 working directory must have two parents). See :hg:`help
6024 working directory must have two parents). See :hg:`help
6025 merge-tools` for information on configuring merge tools.
6025 merge-tools` for information on configuring merge tools.
6026
6026
6027 The resolve command can be used in the following ways:
6027 The resolve command can be used in the following ways:
6028
6028
6029 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6029 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6030 the specified files, discarding any previous merge attempts. Re-merging
6030 the specified files, discarding any previous merge attempts. Re-merging
6031 is not performed for files already marked as resolved. Use ``--all/-a``
6031 is not performed for files already marked as resolved. Use ``--all/-a``
6032 to select all unresolved files. ``--tool`` can be used to specify
6032 to select all unresolved files. ``--tool`` can be used to specify
6033 the merge tool used for the given files. It overrides the HGMERGE
6033 the merge tool used for the given files. It overrides the HGMERGE
6034 environment variable and your configuration files. Previous file
6034 environment variable and your configuration files. Previous file
6035 contents are saved with a ``.orig`` suffix.
6035 contents are saved with a ``.orig`` suffix.
6036
6036
6037 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6037 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6038 (e.g. after having manually fixed-up the files). The default is
6038 (e.g. after having manually fixed-up the files). The default is
6039 to mark all unresolved files.
6039 to mark all unresolved files.
6040
6040
6041 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6041 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6042 default is to mark all resolved files.
6042 default is to mark all resolved files.
6043
6043
6044 - :hg:`resolve -l`: list files which had or still have conflicts.
6044 - :hg:`resolve -l`: list files which had or still have conflicts.
6045 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6045 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6046 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6046 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6047 the list. See :hg:`help filesets` for details.
6047 the list. See :hg:`help filesets` for details.
6048
6048
6049 .. note::
6049 .. note::
6050
6050
6051 Mercurial will not let you commit files with unresolved merge
6051 Mercurial will not let you commit files with unresolved merge
6052 conflicts. You must use :hg:`resolve -m ...` before you can
6052 conflicts. You must use :hg:`resolve -m ...` before you can
6053 commit after a conflicting merge.
6053 commit after a conflicting merge.
6054
6054
6055 .. container:: verbose
6055 .. container:: verbose
6056
6056
6057 Template:
6057 Template:
6058
6058
6059 The following keywords are supported in addition to the common template
6059 The following keywords are supported in addition to the common template
6060 keywords and functions. See also :hg:`help templates`.
6060 keywords and functions. See also :hg:`help templates`.
6061
6061
6062 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6062 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6063 :path: String. Repository-absolute path of the file.
6063 :path: String. Repository-absolute path of the file.
6064
6064
6065 Returns 0 on success, 1 if any files fail a resolve attempt.
6065 Returns 0 on success, 1 if any files fail a resolve attempt.
6066 """
6066 """
6067
6067
6068 opts = pycompat.byteskwargs(opts)
6068 opts = pycompat.byteskwargs(opts)
6069 confirm = ui.configbool(b'commands', b'resolve.confirm')
6069 confirm = ui.configbool(b'commands', b'resolve.confirm')
6070 flaglist = b'all mark unmark list no_status re_merge'.split()
6070 flaglist = b'all mark unmark list no_status re_merge'.split()
6071 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6071 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6072
6072
6073 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6073 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6074 if actioncount > 1:
6074 if actioncount > 1:
6075 raise error.InputError(_(b"too many actions specified"))
6075 raise error.InputError(_(b"too many actions specified"))
6076 elif actioncount == 0 and ui.configbool(
6076 elif actioncount == 0 and ui.configbool(
6077 b'commands', b'resolve.explicit-re-merge'
6077 b'commands', b'resolve.explicit-re-merge'
6078 ):
6078 ):
6079 hint = _(b'use --mark, --unmark, --list or --re-merge')
6079 hint = _(b'use --mark, --unmark, --list or --re-merge')
6080 raise error.InputError(_(b'no action specified'), hint=hint)
6080 raise error.InputError(_(b'no action specified'), hint=hint)
6081 if pats and all:
6081 if pats and all:
6082 raise error.InputError(_(b"can't specify --all and patterns"))
6082 raise error.InputError(_(b"can't specify --all and patterns"))
6083 if not (all or pats or show or mark or unmark):
6083 if not (all or pats or show or mark or unmark):
6084 raise error.InputError(
6084 raise error.InputError(
6085 _(b'no files or directories specified'),
6085 _(b'no files or directories specified'),
6086 hint=b'use --all to re-merge all unresolved files',
6086 hint=b'use --all to re-merge all unresolved files',
6087 )
6087 )
6088
6088
6089 if confirm:
6089 if confirm:
6090 if all:
6090 if all:
6091 if ui.promptchoice(
6091 if ui.promptchoice(
6092 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6092 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6093 ):
6093 ):
6094 raise error.CanceledError(_(b'user quit'))
6094 raise error.CanceledError(_(b'user quit'))
6095 if mark and not pats:
6095 if mark and not pats:
6096 if ui.promptchoice(
6096 if ui.promptchoice(
6097 _(
6097 _(
6098 b'mark all unresolved files as resolved (yn)?'
6098 b'mark all unresolved files as resolved (yn)?'
6099 b'$$ &Yes $$ &No'
6099 b'$$ &Yes $$ &No'
6100 )
6100 )
6101 ):
6101 ):
6102 raise error.CanceledError(_(b'user quit'))
6102 raise error.CanceledError(_(b'user quit'))
6103 if unmark and not pats:
6103 if unmark and not pats:
6104 if ui.promptchoice(
6104 if ui.promptchoice(
6105 _(
6105 _(
6106 b'mark all resolved files as unresolved (yn)?'
6106 b'mark all resolved files as unresolved (yn)?'
6107 b'$$ &Yes $$ &No'
6107 b'$$ &Yes $$ &No'
6108 )
6108 )
6109 ):
6109 ):
6110 raise error.CanceledError(_(b'user quit'))
6110 raise error.CanceledError(_(b'user quit'))
6111
6111
6112 uipathfn = scmutil.getuipathfn(repo)
6112 uipathfn = scmutil.getuipathfn(repo)
6113
6113
6114 if show:
6114 if show:
6115 ui.pager(b'resolve')
6115 ui.pager(b'resolve')
6116 fm = ui.formatter(b'resolve', opts)
6116 fm = ui.formatter(b'resolve', opts)
6117 ms = mergestatemod.mergestate.read(repo)
6117 ms = mergestatemod.mergestate.read(repo)
6118 wctx = repo[None]
6118 wctx = repo[None]
6119 m = scmutil.match(wctx, pats, opts)
6119 m = scmutil.match(wctx, pats, opts)
6120
6120
6121 # Labels and keys based on merge state. Unresolved path conflicts show
6121 # Labels and keys based on merge state. Unresolved path conflicts show
6122 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6122 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6123 # resolved conflicts.
6123 # resolved conflicts.
6124 mergestateinfo = {
6124 mergestateinfo = {
6125 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6125 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6126 b'resolve.unresolved',
6126 b'resolve.unresolved',
6127 b'U',
6127 b'U',
6128 ),
6128 ),
6129 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6129 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6130 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6130 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6131 b'resolve.unresolved',
6131 b'resolve.unresolved',
6132 b'P',
6132 b'P',
6133 ),
6133 ),
6134 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6134 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6135 b'resolve.resolved',
6135 b'resolve.resolved',
6136 b'R',
6136 b'R',
6137 ),
6137 ),
6138 }
6138 }
6139
6139
6140 for f in ms:
6140 for f in ms:
6141 if not m(f):
6141 if not m(f):
6142 continue
6142 continue
6143
6143
6144 label, key = mergestateinfo[ms[f]]
6144 label, key = mergestateinfo[ms[f]]
6145 fm.startitem()
6145 fm.startitem()
6146 fm.context(ctx=wctx)
6146 fm.context(ctx=wctx)
6147 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6147 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6148 fm.data(path=f)
6148 fm.data(path=f)
6149 fm.plain(b'%s\n' % uipathfn(f), label=label)
6149 fm.plain(b'%s\n' % uipathfn(f), label=label)
6150 fm.end()
6150 fm.end()
6151 return 0
6151 return 0
6152
6152
6153 with repo.wlock():
6153 with repo.wlock():
6154 ms = mergestatemod.mergestate.read(repo)
6154 ms = mergestatemod.mergestate.read(repo)
6155
6155
6156 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6156 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6157 raise error.StateError(
6157 raise error.StateError(
6158 _(b'resolve command not applicable when not merging')
6158 _(b'resolve command not applicable when not merging')
6159 )
6159 )
6160
6160
6161 wctx = repo[None]
6161 wctx = repo[None]
6162 m = scmutil.match(wctx, pats, opts)
6162 m = scmutil.match(wctx, pats, opts)
6163 ret = 0
6163 ret = 0
6164 didwork = False
6164 didwork = False
6165
6165
6166 hasconflictmarkers = []
6166 hasconflictmarkers = []
6167 if mark:
6167 if mark:
6168 markcheck = ui.config(b'commands', b'resolve.mark-check')
6168 markcheck = ui.config(b'commands', b'resolve.mark-check')
6169 if markcheck not in [b'warn', b'abort']:
6169 if markcheck not in [b'warn', b'abort']:
6170 # Treat all invalid / unrecognized values as 'none'.
6170 # Treat all invalid / unrecognized values as 'none'.
6171 markcheck = False
6171 markcheck = False
6172 for f in ms:
6172 for f in ms:
6173 if not m(f):
6173 if not m(f):
6174 continue
6174 continue
6175
6175
6176 didwork = True
6176 didwork = True
6177
6177
6178 # path conflicts must be resolved manually
6178 # path conflicts must be resolved manually
6179 if ms[f] in (
6179 if ms[f] in (
6180 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6180 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6181 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6181 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6182 ):
6182 ):
6183 if mark:
6183 if mark:
6184 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6184 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6185 elif unmark:
6185 elif unmark:
6186 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6186 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6187 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6187 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6188 ui.warn(
6188 ui.warn(
6189 _(b'%s: path conflict must be resolved manually\n')
6189 _(b'%s: path conflict must be resolved manually\n')
6190 % uipathfn(f)
6190 % uipathfn(f)
6191 )
6191 )
6192 continue
6192 continue
6193
6193
6194 if mark:
6194 if mark:
6195 if markcheck:
6195 if markcheck:
6196 fdata = repo.wvfs.tryread(f)
6196 fdata = repo.wvfs.tryread(f)
6197 if (
6197 if (
6198 filemerge.hasconflictmarkers(fdata)
6198 filemerge.hasconflictmarkers(fdata)
6199 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6199 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6200 ):
6200 ):
6201 hasconflictmarkers.append(f)
6201 hasconflictmarkers.append(f)
6202 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6202 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6203 elif unmark:
6203 elif unmark:
6204 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6204 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6205 else:
6205 else:
6206 # backup pre-resolve (merge uses .orig for its own purposes)
6206 # backup pre-resolve (merge uses .orig for its own purposes)
6207 a = repo.wjoin(f)
6207 a = repo.wjoin(f)
6208 try:
6208 try:
6209 util.copyfile(a, a + b".resolve")
6209 util.copyfile(a, a + b".resolve")
6210 except FileNotFoundError:
6210 except FileNotFoundError:
6211 pass
6211 pass
6212
6212
6213 try:
6213 try:
6214 # preresolve file
6214 # preresolve file
6215 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6215 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6216 with ui.configoverride(overrides, b'resolve'):
6216 with ui.configoverride(overrides, b'resolve'):
6217 r = ms.resolve(f, wctx)
6217 r = ms.resolve(f, wctx)
6218 if r:
6218 if r:
6219 ret = 1
6219 ret = 1
6220 finally:
6220 finally:
6221 ms.commit()
6221 ms.commit()
6222
6222
6223 # replace filemerge's .orig file with our resolve file
6223 # replace filemerge's .orig file with our resolve file
6224 try:
6224 try:
6225 util.rename(
6225 util.rename(
6226 a + b".resolve", scmutil.backuppath(ui, repo, f)
6226 a + b".resolve", scmutil.backuppath(ui, repo, f)
6227 )
6227 )
6228 except FileNotFoundError:
6228 except FileNotFoundError:
6229 pass
6229 pass
6230
6230
6231 if hasconflictmarkers:
6231 if hasconflictmarkers:
6232 ui.warn(
6232 ui.warn(
6233 _(
6233 _(
6234 b'warning: the following files still have conflict '
6234 b'warning: the following files still have conflict '
6235 b'markers:\n'
6235 b'markers:\n'
6236 )
6236 )
6237 + b''.join(
6237 + b''.join(
6238 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6238 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6239 )
6239 )
6240 )
6240 )
6241 if markcheck == b'abort' and not all and not pats:
6241 if markcheck == b'abort' and not all and not pats:
6242 raise error.StateError(
6242 raise error.StateError(
6243 _(b'conflict markers detected'),
6243 _(b'conflict markers detected'),
6244 hint=_(b'use --all to mark anyway'),
6244 hint=_(b'use --all to mark anyway'),
6245 )
6245 )
6246
6246
6247 ms.commit()
6247 ms.commit()
6248 branchmerge = repo.dirstate.p2() != repo.nullid
6248 branchmerge = repo.dirstate.p2() != repo.nullid
6249 # resolve is not doing a parent change here, however, `record updates`
6249 # resolve is not doing a parent change here, however, `record updates`
6250 # will call some dirstate API that at intended for parent changes call.
6250 # will call some dirstate API that at intended for parent changes call.
6251 # Ideally we would not need this and could implement a lighter version
6251 # Ideally we would not need this and could implement a lighter version
6252 # of the recordupdateslogic that will not have to deal with the part
6252 # of the recordupdateslogic that will not have to deal with the part
6253 # related to parent changes. However this would requires that:
6253 # related to parent changes. However this would requires that:
6254 # - we are sure we passed around enough information at update/merge
6254 # - we are sure we passed around enough information at update/merge
6255 # time to no longer needs it at `hg resolve time`
6255 # time to no longer needs it at `hg resolve time`
6256 # - we are sure we store that information well enough to be able to reuse it
6256 # - we are sure we store that information well enough to be able to reuse it
6257 # - we are the necessary logic to reuse it right.
6257 # - we are the necessary logic to reuse it right.
6258 #
6258 #
6259 # All this should eventually happens, but in the mean time, we use this
6259 # All this should eventually happens, but in the mean time, we use this
6260 # context manager slightly out of the context it should be.
6260 # context manager slightly out of the context it should be.
6261 with repo.dirstate.parentchange():
6261 with repo.dirstate.parentchange():
6262 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6262 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6263
6263
6264 if not didwork and pats:
6264 if not didwork and pats:
6265 hint = None
6265 hint = None
6266 if not any([p for p in pats if p.find(b':') >= 0]):
6266 if not any([p for p in pats if p.find(b':') >= 0]):
6267 pats = [b'path:%s' % p for p in pats]
6267 pats = [b'path:%s' % p for p in pats]
6268 m = scmutil.match(wctx, pats, opts)
6268 m = scmutil.match(wctx, pats, opts)
6269 for f in ms:
6269 for f in ms:
6270 if not m(f):
6270 if not m(f):
6271 continue
6271 continue
6272
6272
6273 def flag(o):
6273 def flag(o):
6274 if o == b're_merge':
6274 if o == b're_merge':
6275 return b'--re-merge '
6275 return b'--re-merge '
6276 return b'-%s ' % o[0:1]
6276 return b'-%s ' % o[0:1]
6277
6277
6278 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6278 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6279 hint = _(b"(try: hg resolve %s%s)\n") % (
6279 hint = _(b"(try: hg resolve %s%s)\n") % (
6280 flags,
6280 flags,
6281 b' '.join(pats),
6281 b' '.join(pats),
6282 )
6282 )
6283 break
6283 break
6284 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6284 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6285 if hint:
6285 if hint:
6286 ui.warn(hint)
6286 ui.warn(hint)
6287
6287
6288 unresolvedf = ms.unresolvedcount()
6288 unresolvedf = ms.unresolvedcount()
6289 if not unresolvedf:
6289 if not unresolvedf:
6290 ui.status(_(b'(no more unresolved files)\n'))
6290 ui.status(_(b'(no more unresolved files)\n'))
6291 cmdutil.checkafterresolved(repo)
6291 cmdutil.checkafterresolved(repo)
6292
6292
6293 return ret
6293 return ret
6294
6294
6295
6295
6296 @command(
6296 @command(
6297 b'revert',
6297 b'revert',
6298 [
6298 [
6299 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6299 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6300 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6300 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6301 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6301 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6302 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6302 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6303 (b'i', b'interactive', None, _(b'interactively select the changes')),
6303 (b'i', b'interactive', None, _(b'interactively select the changes')),
6304 ]
6304 ]
6305 + walkopts
6305 + walkopts
6306 + dryrunopts,
6306 + dryrunopts,
6307 _(b'[OPTION]... [-r REV] [NAME]...'),
6307 _(b'[OPTION]... [-r REV] [NAME]...'),
6308 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6308 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6309 )
6309 )
6310 def revert(ui, repo, *pats, **opts):
6310 def revert(ui, repo, *pats, **opts):
6311 """restore files to their checkout state
6311 """restore files to their checkout state
6312
6312
6313 .. note::
6313 .. note::
6314
6314
6315 To check out earlier revisions, you should use :hg:`update REV`.
6315 To check out earlier revisions, you should use :hg:`update REV`.
6316 To cancel an uncommitted merge (and lose your changes),
6316 To cancel an uncommitted merge (and lose your changes),
6317 use :hg:`merge --abort`.
6317 use :hg:`merge --abort`.
6318
6318
6319 With no revision specified, revert the specified files or directories
6319 With no revision specified, revert the specified files or directories
6320 to the contents they had in the parent of the working directory.
6320 to the contents they had in the parent of the working directory.
6321 This restores the contents of files to an unmodified
6321 This restores the contents of files to an unmodified
6322 state and unschedules adds, removes, copies, and renames. If the
6322 state and unschedules adds, removes, copies, and renames. If the
6323 working directory has two parents, you must explicitly specify a
6323 working directory has two parents, you must explicitly specify a
6324 revision.
6324 revision.
6325
6325
6326 Using the -r/--rev or -d/--date options, revert the given files or
6326 Using the -r/--rev or -d/--date options, revert the given files or
6327 directories to their states as of a specific revision. Because
6327 directories to their states as of a specific revision. Because
6328 revert does not change the working directory parents, this will
6328 revert does not change the working directory parents, this will
6329 cause these files to appear modified. This can be helpful to "back
6329 cause these files to appear modified. This can be helpful to "back
6330 out" some or all of an earlier change. See :hg:`backout` for a
6330 out" some or all of an earlier change. See :hg:`backout` for a
6331 related method.
6331 related method.
6332
6332
6333 Modified files are saved with a .orig suffix before reverting.
6333 Modified files are saved with a .orig suffix before reverting.
6334 To disable these backups, use --no-backup. It is possible to store
6334 To disable these backups, use --no-backup. It is possible to store
6335 the backup files in a custom directory relative to the root of the
6335 the backup files in a custom directory relative to the root of the
6336 repository by setting the ``ui.origbackuppath`` configuration
6336 repository by setting the ``ui.origbackuppath`` configuration
6337 option.
6337 option.
6338
6338
6339 See :hg:`help dates` for a list of formats valid for -d/--date.
6339 See :hg:`help dates` for a list of formats valid for -d/--date.
6340
6340
6341 See :hg:`help backout` for a way to reverse the effect of an
6341 See :hg:`help backout` for a way to reverse the effect of an
6342 earlier changeset.
6342 earlier changeset.
6343
6343
6344 Returns 0 on success.
6344 Returns 0 on success.
6345 """
6345 """
6346
6346
6347 opts = pycompat.byteskwargs(opts)
6347 opts = pycompat.byteskwargs(opts)
6348 if opts.get(b"date"):
6348 if opts.get(b"date"):
6349 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6349 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6350 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6350 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6351
6351
6352 parent, p2 = repo.dirstate.parents()
6352 parent, p2 = repo.dirstate.parents()
6353 if not opts.get(b'rev') and p2 != repo.nullid:
6353 if not opts.get(b'rev') and p2 != repo.nullid:
6354 # revert after merge is a trap for new users (issue2915)
6354 # revert after merge is a trap for new users (issue2915)
6355 raise error.InputError(
6355 raise error.InputError(
6356 _(b'uncommitted merge with no revision specified'),
6356 _(b'uncommitted merge with no revision specified'),
6357 hint=_(b"use 'hg update' or see 'hg help revert'"),
6357 hint=_(b"use 'hg update' or see 'hg help revert'"),
6358 )
6358 )
6359
6359
6360 rev = opts.get(b'rev')
6360 rev = opts.get(b'rev')
6361 if rev:
6361 if rev:
6362 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6362 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6363 ctx = logcmdutil.revsingle(repo, rev)
6363 ctx = logcmdutil.revsingle(repo, rev)
6364
6364
6365 if not (
6365 if not (
6366 pats
6366 pats
6367 or opts.get(b'include')
6367 or opts.get(b'include')
6368 or opts.get(b'exclude')
6368 or opts.get(b'exclude')
6369 or opts.get(b'all')
6369 or opts.get(b'all')
6370 or opts.get(b'interactive')
6370 or opts.get(b'interactive')
6371 ):
6371 ):
6372 msg = _(b"no files or directories specified")
6372 msg = _(b"no files or directories specified")
6373 if p2 != repo.nullid:
6373 if p2 != repo.nullid:
6374 hint = _(
6374 hint = _(
6375 b"uncommitted merge, use --all to discard all changes,"
6375 b"uncommitted merge, use --all to discard all changes,"
6376 b" or 'hg update -C .' to abort the merge"
6376 b" or 'hg update -C .' to abort the merge"
6377 )
6377 )
6378 raise error.InputError(msg, hint=hint)
6378 raise error.InputError(msg, hint=hint)
6379 dirty = any(repo.status())
6379 dirty = any(repo.status())
6380 node = ctx.node()
6380 node = ctx.node()
6381 if node != parent:
6381 if node != parent:
6382 if dirty:
6382 if dirty:
6383 hint = (
6383 hint = (
6384 _(
6384 _(
6385 b"uncommitted changes, use --all to discard all"
6385 b"uncommitted changes, use --all to discard all"
6386 b" changes, or 'hg update %d' to update"
6386 b" changes, or 'hg update %d' to update"
6387 )
6387 )
6388 % ctx.rev()
6388 % ctx.rev()
6389 )
6389 )
6390 else:
6390 else:
6391 hint = (
6391 hint = (
6392 _(
6392 _(
6393 b"use --all to revert all files,"
6393 b"use --all to revert all files,"
6394 b" or 'hg update %d' to update"
6394 b" or 'hg update %d' to update"
6395 )
6395 )
6396 % ctx.rev()
6396 % ctx.rev()
6397 )
6397 )
6398 elif dirty:
6398 elif dirty:
6399 hint = _(b"uncommitted changes, use --all to discard all changes")
6399 hint = _(b"uncommitted changes, use --all to discard all changes")
6400 else:
6400 else:
6401 hint = _(b"use --all to revert all files")
6401 hint = _(b"use --all to revert all files")
6402 raise error.InputError(msg, hint=hint)
6402 raise error.InputError(msg, hint=hint)
6403
6403
6404 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6404 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6405
6405
6406
6406
6407 @command(
6407 @command(
6408 b'rollback',
6408 b'rollback',
6409 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6409 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6410 helpcategory=command.CATEGORY_MAINTENANCE,
6410 helpcategory=command.CATEGORY_MAINTENANCE,
6411 )
6411 )
6412 def rollback(ui, repo, **opts):
6412 def rollback(ui, repo, **opts):
6413 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6413 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6414
6414
6415 Please use :hg:`commit --amend` instead of rollback to correct
6415 Please use :hg:`commit --amend` instead of rollback to correct
6416 mistakes in the last commit.
6416 mistakes in the last commit.
6417
6417
6418 This command should be used with care. There is only one level of
6418 This command should be used with care. There is only one level of
6419 rollback, and there is no way to undo a rollback. It will also
6419 rollback, and there is no way to undo a rollback. It will also
6420 restore the dirstate at the time of the last transaction, losing
6420 restore the dirstate at the time of the last transaction, losing
6421 any dirstate changes since that time. This command does not alter
6421 any dirstate changes since that time. This command does not alter
6422 the working directory.
6422 the working directory.
6423
6423
6424 Transactions are used to encapsulate the effects of all commands
6424 Transactions are used to encapsulate the effects of all commands
6425 that create new changesets or propagate existing changesets into a
6425 that create new changesets or propagate existing changesets into a
6426 repository.
6426 repository.
6427
6427
6428 .. container:: verbose
6428 .. container:: verbose
6429
6429
6430 For example, the following commands are transactional, and their
6430 For example, the following commands are transactional, and their
6431 effects can be rolled back:
6431 effects can be rolled back:
6432
6432
6433 - commit
6433 - commit
6434 - import
6434 - import
6435 - pull
6435 - pull
6436 - push (with this repository as the destination)
6436 - push (with this repository as the destination)
6437 - unbundle
6437 - unbundle
6438
6438
6439 To avoid permanent data loss, rollback will refuse to rollback a
6439 To avoid permanent data loss, rollback will refuse to rollback a
6440 commit transaction if it isn't checked out. Use --force to
6440 commit transaction if it isn't checked out. Use --force to
6441 override this protection.
6441 override this protection.
6442
6442
6443 The rollback command can be entirely disabled by setting the
6443 The rollback command can be entirely disabled by setting the
6444 ``ui.rollback`` configuration setting to false. If you're here
6444 ``ui.rollback`` configuration setting to false. If you're here
6445 because you want to use rollback and it's disabled, you can
6445 because you want to use rollback and it's disabled, you can
6446 re-enable the command by setting ``ui.rollback`` to true.
6446 re-enable the command by setting ``ui.rollback`` to true.
6447
6447
6448 This command is not intended for use on public repositories. Once
6448 This command is not intended for use on public repositories. Once
6449 changes are visible for pull by other users, rolling a transaction
6449 changes are visible for pull by other users, rolling a transaction
6450 back locally is ineffective (someone else may already have pulled
6450 back locally is ineffective (someone else may already have pulled
6451 the changes). Furthermore, a race is possible with readers of the
6451 the changes). Furthermore, a race is possible with readers of the
6452 repository; for example an in-progress pull from the repository
6452 repository; for example an in-progress pull from the repository
6453 may fail if a rollback is performed.
6453 may fail if a rollback is performed.
6454
6454
6455 Returns 0 on success, 1 if no rollback data is available.
6455 Returns 0 on success, 1 if no rollback data is available.
6456 """
6456 """
6457 if not ui.configbool(b'ui', b'rollback'):
6457 if not ui.configbool(b'ui', b'rollback'):
6458 raise error.Abort(
6458 raise error.Abort(
6459 _(b'rollback is disabled because it is unsafe'),
6459 _(b'rollback is disabled because it is unsafe'),
6460 hint=b'see `hg help -v rollback` for information',
6460 hint=b'see `hg help -v rollback` for information',
6461 )
6461 )
6462 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6462 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6463
6463
6464
6464
6465 @command(
6465 @command(
6466 b'root',
6466 b'root',
6467 [] + formatteropts,
6467 [] + formatteropts,
6468 intents={INTENT_READONLY},
6468 intents={INTENT_READONLY},
6469 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6469 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6470 )
6470 )
6471 def root(ui, repo, **opts):
6471 def root(ui, repo, **opts):
6472 """print the root (top) of the current working directory
6472 """print the root (top) of the current working directory
6473
6473
6474 Print the root directory of the current repository.
6474 Print the root directory of the current repository.
6475
6475
6476 .. container:: verbose
6476 .. container:: verbose
6477
6477
6478 Template:
6478 Template:
6479
6479
6480 The following keywords are supported in addition to the common template
6480 The following keywords are supported in addition to the common template
6481 keywords and functions. See also :hg:`help templates`.
6481 keywords and functions. See also :hg:`help templates`.
6482
6482
6483 :hgpath: String. Path to the .hg directory.
6483 :hgpath: String. Path to the .hg directory.
6484 :storepath: String. Path to the directory holding versioned data.
6484 :storepath: String. Path to the directory holding versioned data.
6485
6485
6486 Returns 0 on success.
6486 Returns 0 on success.
6487 """
6487 """
6488 opts = pycompat.byteskwargs(opts)
6488 opts = pycompat.byteskwargs(opts)
6489 with ui.formatter(b'root', opts) as fm:
6489 with ui.formatter(b'root', opts) as fm:
6490 fm.startitem()
6490 fm.startitem()
6491 fm.write(b'reporoot', b'%s\n', repo.root)
6491 fm.write(b'reporoot', b'%s\n', repo.root)
6492 fm.data(hgpath=repo.path, storepath=repo.spath)
6492 fm.data(hgpath=repo.path, storepath=repo.spath)
6493
6493
6494
6494
6495 @command(
6495 @command(
6496 b'serve',
6496 b'serve',
6497 [
6497 [
6498 (
6498 (
6499 b'A',
6499 b'A',
6500 b'accesslog',
6500 b'accesslog',
6501 b'',
6501 b'',
6502 _(b'name of access log file to write to'),
6502 _(b'name of access log file to write to'),
6503 _(b'FILE'),
6503 _(b'FILE'),
6504 ),
6504 ),
6505 (b'd', b'daemon', None, _(b'run server in background')),
6505 (b'd', b'daemon', None, _(b'run server in background')),
6506 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6506 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6507 (
6507 (
6508 b'E',
6508 b'E',
6509 b'errorlog',
6509 b'errorlog',
6510 b'',
6510 b'',
6511 _(b'name of error log file to write to'),
6511 _(b'name of error log file to write to'),
6512 _(b'FILE'),
6512 _(b'FILE'),
6513 ),
6513 ),
6514 # use string type, then we can check if something was passed
6514 # use string type, then we can check if something was passed
6515 (
6515 (
6516 b'p',
6516 b'p',
6517 b'port',
6517 b'port',
6518 b'',
6518 b'',
6519 _(b'port to listen on (default: 8000)'),
6519 _(b'port to listen on (default: 8000)'),
6520 _(b'PORT'),
6520 _(b'PORT'),
6521 ),
6521 ),
6522 (
6522 (
6523 b'a',
6523 b'a',
6524 b'address',
6524 b'address',
6525 b'',
6525 b'',
6526 _(b'address to listen on (default: all interfaces)'),
6526 _(b'address to listen on (default: all interfaces)'),
6527 _(b'ADDR'),
6527 _(b'ADDR'),
6528 ),
6528 ),
6529 (
6529 (
6530 b'',
6530 b'',
6531 b'prefix',
6531 b'prefix',
6532 b'',
6532 b'',
6533 _(b'prefix path to serve from (default: server root)'),
6533 _(b'prefix path to serve from (default: server root)'),
6534 _(b'PREFIX'),
6534 _(b'PREFIX'),
6535 ),
6535 ),
6536 (
6536 (
6537 b'n',
6537 b'n',
6538 b'name',
6538 b'name',
6539 b'',
6539 b'',
6540 _(b'name to show in web pages (default: working directory)'),
6540 _(b'name to show in web pages (default: working directory)'),
6541 _(b'NAME'),
6541 _(b'NAME'),
6542 ),
6542 ),
6543 (
6543 (
6544 b'',
6544 b'',
6545 b'web-conf',
6545 b'web-conf',
6546 b'',
6546 b'',
6547 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6547 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6548 _(b'FILE'),
6548 _(b'FILE'),
6549 ),
6549 ),
6550 (
6550 (
6551 b'',
6551 b'',
6552 b'webdir-conf',
6552 b'webdir-conf',
6553 b'',
6553 b'',
6554 _(b'name of the hgweb config file (DEPRECATED)'),
6554 _(b'name of the hgweb config file (DEPRECATED)'),
6555 _(b'FILE'),
6555 _(b'FILE'),
6556 ),
6556 ),
6557 (
6557 (
6558 b'',
6558 b'',
6559 b'pid-file',
6559 b'pid-file',
6560 b'',
6560 b'',
6561 _(b'name of file to write process ID to'),
6561 _(b'name of file to write process ID to'),
6562 _(b'FILE'),
6562 _(b'FILE'),
6563 ),
6563 ),
6564 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6564 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6565 (
6565 (
6566 b'',
6566 b'',
6567 b'cmdserver',
6567 b'cmdserver',
6568 b'',
6568 b'',
6569 _(b'for remote clients (ADVANCED)'),
6569 _(b'for remote clients (ADVANCED)'),
6570 _(b'MODE'),
6570 _(b'MODE'),
6571 ),
6571 ),
6572 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6572 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6573 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6573 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6574 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6574 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6575 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6575 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6576 (b'', b'print-url', None, _(b'start and print only the URL')),
6576 (b'', b'print-url', None, _(b'start and print only the URL')),
6577 ]
6577 ]
6578 + subrepoopts,
6578 + subrepoopts,
6579 _(b'[OPTION]...'),
6579 _(b'[OPTION]...'),
6580 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6580 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6581 helpbasic=True,
6581 helpbasic=True,
6582 optionalrepo=True,
6582 optionalrepo=True,
6583 )
6583 )
6584 def serve(ui, repo, **opts):
6584 def serve(ui, repo, **opts):
6585 """start stand-alone webserver
6585 """start stand-alone webserver
6586
6586
6587 Start a local HTTP repository browser and pull server. You can use
6587 Start a local HTTP repository browser and pull server. You can use
6588 this for ad-hoc sharing and browsing of repositories. It is
6588 this for ad-hoc sharing and browsing of repositories. It is
6589 recommended to use a real web server to serve a repository for
6589 recommended to use a real web server to serve a repository for
6590 longer periods of time.
6590 longer periods of time.
6591
6591
6592 Please note that the server does not implement access control.
6592 Please note that the server does not implement access control.
6593 This means that, by default, anybody can read from the server and
6593 This means that, by default, anybody can read from the server and
6594 nobody can write to it by default. Set the ``web.allow-push``
6594 nobody can write to it by default. Set the ``web.allow-push``
6595 option to ``*`` to allow everybody to push to the server. You
6595 option to ``*`` to allow everybody to push to the server. You
6596 should use a real web server if you need to authenticate users.
6596 should use a real web server if you need to authenticate users.
6597
6597
6598 By default, the server logs accesses to stdout and errors to
6598 By default, the server logs accesses to stdout and errors to
6599 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6599 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6600 files.
6600 files.
6601
6601
6602 To have the server choose a free port number to listen on, specify
6602 To have the server choose a free port number to listen on, specify
6603 a port number of 0; in this case, the server will print the port
6603 a port number of 0; in this case, the server will print the port
6604 number it uses.
6604 number it uses.
6605
6605
6606 Returns 0 on success.
6606 Returns 0 on success.
6607 """
6607 """
6608
6608
6609 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6609 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6610 opts = pycompat.byteskwargs(opts)
6610 opts = pycompat.byteskwargs(opts)
6611 if opts[b"print_url"] and ui.verbose:
6611 if opts[b"print_url"] and ui.verbose:
6612 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6612 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6613
6613
6614 if opts[b"stdio"]:
6614 if opts[b"stdio"]:
6615 if repo is None:
6615 if repo is None:
6616 raise error.RepoError(
6616 raise error.RepoError(
6617 _(b"there is no Mercurial repository here (.hg not found)")
6617 _(b"there is no Mercurial repository here (.hg not found)")
6618 )
6618 )
6619 s = wireprotoserver.sshserver(ui, repo)
6619 s = wireprotoserver.sshserver(ui, repo)
6620 s.serve_forever()
6620 s.serve_forever()
6621 return
6621 return
6622
6622
6623 service = server.createservice(ui, repo, opts)
6623 service = server.createservice(ui, repo, opts)
6624 return server.runservice(opts, initfn=service.init, runfn=service.run)
6624 return server.runservice(opts, initfn=service.init, runfn=service.run)
6625
6625
6626
6626
6627 @command(
6627 @command(
6628 b'shelve',
6628 b'shelve',
6629 [
6629 [
6630 (
6630 (
6631 b'A',
6631 b'A',
6632 b'addremove',
6632 b'addremove',
6633 None,
6633 None,
6634 _(b'mark new/missing files as added/removed before shelving'),
6634 _(b'mark new/missing files as added/removed before shelving'),
6635 ),
6635 ),
6636 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6636 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6637 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6637 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6638 (
6638 (
6639 b'',
6639 b'',
6640 b'date',
6640 b'date',
6641 b'',
6641 b'',
6642 _(b'shelve with the specified commit date'),
6642 _(b'shelve with the specified commit date'),
6643 _(b'DATE'),
6643 _(b'DATE'),
6644 ),
6644 ),
6645 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6645 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6646 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6646 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6647 (
6647 (
6648 b'k',
6648 b'k',
6649 b'keep',
6649 b'keep',
6650 False,
6650 False,
6651 _(b'shelve, but keep changes in the working directory'),
6651 _(b'shelve, but keep changes in the working directory'),
6652 ),
6652 ),
6653 (b'l', b'list', None, _(b'list current shelves')),
6653 (b'l', b'list', None, _(b'list current shelves')),
6654 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6654 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6655 (
6655 (
6656 b'n',
6656 b'n',
6657 b'name',
6657 b'name',
6658 b'',
6658 b'',
6659 _(b'use the given name for the shelved commit'),
6659 _(b'use the given name for the shelved commit'),
6660 _(b'NAME'),
6660 _(b'NAME'),
6661 ),
6661 ),
6662 (
6662 (
6663 b'p',
6663 b'p',
6664 b'patch',
6664 b'patch',
6665 None,
6665 None,
6666 _(
6666 _(
6667 b'output patches for changes (provide the names of the shelved '
6667 b'output patches for changes (provide the names of the shelved '
6668 b'changes as positional arguments)'
6668 b'changes as positional arguments)'
6669 ),
6669 ),
6670 ),
6670 ),
6671 (b'i', b'interactive', None, _(b'interactive mode')),
6671 (b'i', b'interactive', None, _(b'interactive mode')),
6672 (
6672 (
6673 b'',
6673 b'',
6674 b'stat',
6674 b'stat',
6675 None,
6675 None,
6676 _(
6676 _(
6677 b'output diffstat-style summary of changes (provide the names of '
6677 b'output diffstat-style summary of changes (provide the names of '
6678 b'the shelved changes as positional arguments)'
6678 b'the shelved changes as positional arguments)'
6679 ),
6679 ),
6680 ),
6680 ),
6681 ]
6681 ]
6682 + cmdutil.walkopts,
6682 + cmdutil.walkopts,
6683 _(b'hg shelve [OPTION]... [FILE]...'),
6683 _(b'hg shelve [OPTION]... [FILE]...'),
6684 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6684 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6685 )
6685 )
6686 def shelve(ui, repo, *pats, **opts):
6686 def shelve(ui, repo, *pats, **opts):
6687 """save and set aside changes from the working directory
6687 """save and set aside changes from the working directory
6688
6688
6689 Shelving takes files that "hg status" reports as not clean, saves
6689 Shelving takes files that "hg status" reports as not clean, saves
6690 the modifications to a bundle (a shelved change), and reverts the
6690 the modifications to a bundle (a shelved change), and reverts the
6691 files so that their state in the working directory becomes clean.
6691 files so that their state in the working directory becomes clean.
6692
6692
6693 To restore these changes to the working directory, using "hg
6693 To restore these changes to the working directory, using "hg
6694 unshelve"; this will work even if you switch to a different
6694 unshelve"; this will work even if you switch to a different
6695 commit.
6695 commit.
6696
6696
6697 When no files are specified, "hg shelve" saves all not-clean
6697 When no files are specified, "hg shelve" saves all not-clean
6698 files. If specific files or directories are named, only changes to
6698 files. If specific files or directories are named, only changes to
6699 those files are shelved.
6699 those files are shelved.
6700
6700
6701 In bare shelve (when no files are specified, without interactive,
6701 In bare shelve (when no files are specified, without interactive,
6702 include and exclude option), shelving remembers information if the
6702 include and exclude option), shelving remembers information if the
6703 working directory was on newly created branch, in other words working
6703 working directory was on newly created branch, in other words working
6704 directory was on different branch than its first parent. In this
6704 directory was on different branch than its first parent. In this
6705 situation unshelving restores branch information to the working directory.
6705 situation unshelving restores branch information to the working directory.
6706
6706
6707 Each shelved change has a name that makes it easier to find later.
6707 Each shelved change has a name that makes it easier to find later.
6708 The name of a shelved change defaults to being based on the active
6708 The name of a shelved change defaults to being based on the active
6709 bookmark, or if there is no active bookmark, the current named
6709 bookmark, or if there is no active bookmark, the current named
6710 branch. To specify a different name, use ``--name``.
6710 branch. To specify a different name, use ``--name``.
6711
6711
6712 To see a list of existing shelved changes, use the ``--list``
6712 To see a list of existing shelved changes, use the ``--list``
6713 option. For each shelved change, this will print its name, age,
6713 option. For each shelved change, this will print its name, age,
6714 and description; use ``--patch`` or ``--stat`` for more details.
6714 and description; use ``--patch`` or ``--stat`` for more details.
6715
6715
6716 To delete specific shelved changes, use ``--delete``. To delete
6716 To delete specific shelved changes, use ``--delete``. To delete
6717 all shelved changes, use ``--cleanup``.
6717 all shelved changes, use ``--cleanup``.
6718 """
6718 """
6719 opts = pycompat.byteskwargs(opts)
6719 opts = pycompat.byteskwargs(opts)
6720 allowables = [
6720 allowables = [
6721 (b'addremove', {b'create'}), # 'create' is pseudo action
6721 (b'addremove', {b'create'}), # 'create' is pseudo action
6722 (b'unknown', {b'create'}),
6722 (b'unknown', {b'create'}),
6723 (b'cleanup', {b'cleanup'}),
6723 (b'cleanup', {b'cleanup'}),
6724 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6724 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6725 (b'delete', {b'delete'}),
6725 (b'delete', {b'delete'}),
6726 (b'edit', {b'create'}),
6726 (b'edit', {b'create'}),
6727 (b'keep', {b'create'}),
6727 (b'keep', {b'create'}),
6728 (b'list', {b'list'}),
6728 (b'list', {b'list'}),
6729 (b'message', {b'create'}),
6729 (b'message', {b'create'}),
6730 (b'name', {b'create'}),
6730 (b'name', {b'create'}),
6731 (b'patch', {b'patch', b'list'}),
6731 (b'patch', {b'patch', b'list'}),
6732 (b'stat', {b'stat', b'list'}),
6732 (b'stat', {b'stat', b'list'}),
6733 ]
6733 ]
6734
6734
6735 def checkopt(opt):
6735 def checkopt(opt):
6736 if opts.get(opt):
6736 if opts.get(opt):
6737 for i, allowable in allowables:
6737 for i, allowable in allowables:
6738 if opts[i] and opt not in allowable:
6738 if opts[i] and opt not in allowable:
6739 raise error.InputError(
6739 raise error.InputError(
6740 _(
6740 _(
6741 b"options '--%s' and '--%s' may not be "
6741 b"options '--%s' and '--%s' may not be "
6742 b"used together"
6742 b"used together"
6743 )
6743 )
6744 % (opt, i)
6744 % (opt, i)
6745 )
6745 )
6746 return True
6746 return True
6747
6747
6748 if checkopt(b'cleanup'):
6748 if checkopt(b'cleanup'):
6749 if pats:
6749 if pats:
6750 raise error.InputError(
6750 raise error.InputError(
6751 _(b"cannot specify names when using '--cleanup'")
6751 _(b"cannot specify names when using '--cleanup'")
6752 )
6752 )
6753 return shelvemod.cleanupcmd(ui, repo)
6753 return shelvemod.cleanupcmd(ui, repo)
6754 elif checkopt(b'delete'):
6754 elif checkopt(b'delete'):
6755 return shelvemod.deletecmd(ui, repo, pats)
6755 return shelvemod.deletecmd(ui, repo, pats)
6756 elif checkopt(b'list'):
6756 elif checkopt(b'list'):
6757 return shelvemod.listcmd(ui, repo, pats, opts)
6757 return shelvemod.listcmd(ui, repo, pats, opts)
6758 elif checkopt(b'patch') or checkopt(b'stat'):
6758 elif checkopt(b'patch') or checkopt(b'stat'):
6759 return shelvemod.patchcmds(ui, repo, pats, opts)
6759 return shelvemod.patchcmds(ui, repo, pats, opts)
6760 else:
6760 else:
6761 return shelvemod.createcmd(ui, repo, pats, opts)
6761 return shelvemod.createcmd(ui, repo, pats, opts)
6762
6762
6763
6763
6764 _NOTTERSE = b'nothing'
6764 _NOTTERSE = b'nothing'
6765
6765
6766
6766
6767 @command(
6767 @command(
6768 b'status|st',
6768 b'status|st',
6769 [
6769 [
6770 (b'A', b'all', None, _(b'show status of all files')),
6770 (b'A', b'all', None, _(b'show status of all files')),
6771 (b'm', b'modified', None, _(b'show only modified files')),
6771 (b'm', b'modified', None, _(b'show only modified files')),
6772 (b'a', b'added', None, _(b'show only added files')),
6772 (b'a', b'added', None, _(b'show only added files')),
6773 (b'r', b'removed', None, _(b'show only removed files')),
6773 (b'r', b'removed', None, _(b'show only removed files')),
6774 (b'd', b'deleted', None, _(b'show only missing files')),
6774 (b'd', b'deleted', None, _(b'show only missing files')),
6775 (b'c', b'clean', None, _(b'show only files without changes')),
6775 (b'c', b'clean', None, _(b'show only files without changes')),
6776 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6776 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6777 (b'i', b'ignored', None, _(b'show only ignored files')),
6777 (b'i', b'ignored', None, _(b'show only ignored files')),
6778 (b'n', b'no-status', None, _(b'hide status prefix')),
6778 (b'n', b'no-status', None, _(b'hide status prefix')),
6779 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6779 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6780 (
6780 (
6781 b'C',
6781 b'C',
6782 b'copies',
6782 b'copies',
6783 None,
6783 None,
6784 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6784 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6785 ),
6785 ),
6786 (
6786 (
6787 b'0',
6787 b'0',
6788 b'print0',
6788 b'print0',
6789 None,
6789 None,
6790 _(b'end filenames with NUL, for use with xargs'),
6790 _(b'end filenames with NUL, for use with xargs'),
6791 ),
6791 ),
6792 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6792 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6793 (
6793 (
6794 b'',
6794 b'',
6795 b'change',
6795 b'change',
6796 b'',
6796 b'',
6797 _(b'list the changed files of a revision'),
6797 _(b'list the changed files of a revision'),
6798 _(b'REV'),
6798 _(b'REV'),
6799 ),
6799 ),
6800 ]
6800 ]
6801 + walkopts
6801 + walkopts
6802 + subrepoopts
6802 + subrepoopts
6803 + formatteropts,
6803 + formatteropts,
6804 _(b'[OPTION]... [FILE]...'),
6804 _(b'[OPTION]... [FILE]...'),
6805 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6805 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6806 helpbasic=True,
6806 helpbasic=True,
6807 inferrepo=True,
6807 inferrepo=True,
6808 intents={INTENT_READONLY},
6808 intents={INTENT_READONLY},
6809 )
6809 )
6810 def status(ui, repo, *pats, **opts):
6810 def status(ui, repo, *pats, **opts):
6811 """show changed files in the working directory
6811 """show changed files in the working directory
6812
6812
6813 Show status of files in the repository. If names are given, only
6813 Show status of files in the repository. If names are given, only
6814 files that match are shown. Files that are clean or ignored or
6814 files that match are shown. Files that are clean or ignored or
6815 the source of a copy/move operation, are not listed unless
6815 the source of a copy/move operation, are not listed unless
6816 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6816 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6817 Unless options described with "show only ..." are given, the
6817 Unless options described with "show only ..." are given, the
6818 options -mardu are used.
6818 options -mardu are used.
6819
6819
6820 Option -q/--quiet hides untracked (unknown and ignored) files
6820 Option -q/--quiet hides untracked (unknown and ignored) files
6821 unless explicitly requested with -u/--unknown or -i/--ignored.
6821 unless explicitly requested with -u/--unknown or -i/--ignored.
6822
6822
6823 .. note::
6823 .. note::
6824
6824
6825 :hg:`status` may appear to disagree with diff if permissions have
6825 :hg:`status` may appear to disagree with diff if permissions have
6826 changed or a merge has occurred. The standard diff format does
6826 changed or a merge has occurred. The standard diff format does
6827 not report permission changes and diff only reports changes
6827 not report permission changes and diff only reports changes
6828 relative to one merge parent.
6828 relative to one merge parent.
6829
6829
6830 If one revision is given, it is used as the base revision.
6830 If one revision is given, it is used as the base revision.
6831 If two revisions are given, the differences between them are
6831 If two revisions are given, the differences between them are
6832 shown. The --change option can also be used as a shortcut to list
6832 shown. The --change option can also be used as a shortcut to list
6833 the changed files of a revision from its first parent.
6833 the changed files of a revision from its first parent.
6834
6834
6835 The codes used to show the status of files are::
6835 The codes used to show the status of files are::
6836
6836
6837 M = modified
6837 M = modified
6838 A = added
6838 A = added
6839 R = removed
6839 R = removed
6840 C = clean
6840 C = clean
6841 ! = missing (deleted by non-hg command, but still tracked)
6841 ! = missing (deleted by non-hg command, but still tracked)
6842 ? = not tracked
6842 ? = not tracked
6843 I = ignored
6843 I = ignored
6844 = origin of the previous file (with --copies)
6844 = origin of the previous file (with --copies)
6845
6845
6846 .. container:: verbose
6846 .. container:: verbose
6847
6847
6848 The -t/--terse option abbreviates the output by showing only the directory
6848 The -t/--terse option abbreviates the output by showing only the directory
6849 name if all the files in it share the same status. The option takes an
6849 name if all the files in it share the same status. The option takes an
6850 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6850 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6851 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6851 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6852 for 'ignored' and 'c' for clean.
6852 for 'ignored' and 'c' for clean.
6853
6853
6854 It abbreviates only those statuses which are passed. Note that clean and
6854 It abbreviates only those statuses which are passed. Note that clean and
6855 ignored files are not displayed with '--terse ic' unless the -c/--clean
6855 ignored files are not displayed with '--terse ic' unless the -c/--clean
6856 and -i/--ignored options are also used.
6856 and -i/--ignored options are also used.
6857
6857
6858 The -v/--verbose option shows information when the repository is in an
6858 The -v/--verbose option shows information when the repository is in an
6859 unfinished merge, shelve, rebase state etc. You can have this behavior
6859 unfinished merge, shelve, rebase state etc. You can have this behavior
6860 turned on by default by enabling the ``commands.status.verbose`` option.
6860 turned on by default by enabling the ``commands.status.verbose`` option.
6861
6861
6862 You can skip displaying some of these states by setting
6862 You can skip displaying some of these states by setting
6863 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6863 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6864 'histedit', 'merge', 'rebase', or 'unshelve'.
6864 'histedit', 'merge', 'rebase', or 'unshelve'.
6865
6865
6866 Template:
6866 Template:
6867
6867
6868 The following keywords are supported in addition to the common template
6868 The following keywords are supported in addition to the common template
6869 keywords and functions. See also :hg:`help templates`.
6869 keywords and functions. See also :hg:`help templates`.
6870
6870
6871 :path: String. Repository-absolute path of the file.
6871 :path: String. Repository-absolute path of the file.
6872 :source: String. Repository-absolute path of the file originated from.
6872 :source: String. Repository-absolute path of the file originated from.
6873 Available if ``--copies`` is specified.
6873 Available if ``--copies`` is specified.
6874 :status: String. Character denoting file's status.
6874 :status: String. Character denoting file's status.
6875
6875
6876 Examples:
6876 Examples:
6877
6877
6878 - show changes in the working directory relative to a
6878 - show changes in the working directory relative to a
6879 changeset::
6879 changeset::
6880
6880
6881 hg status --rev 9353
6881 hg status --rev 9353
6882
6882
6883 - show changes in the working directory relative to the
6883 - show changes in the working directory relative to the
6884 current directory (see :hg:`help patterns` for more information)::
6884 current directory (see :hg:`help patterns` for more information)::
6885
6885
6886 hg status re:
6886 hg status re:
6887
6887
6888 - show all changes including copies in an existing changeset::
6888 - show all changes including copies in an existing changeset::
6889
6889
6890 hg status --copies --change 9353
6890 hg status --copies --change 9353
6891
6891
6892 - get a NUL separated list of added files, suitable for xargs::
6892 - get a NUL separated list of added files, suitable for xargs::
6893
6893
6894 hg status -an0
6894 hg status -an0
6895
6895
6896 - show more information about the repository status, abbreviating
6896 - show more information about the repository status, abbreviating
6897 added, removed, modified, deleted, and untracked paths::
6897 added, removed, modified, deleted, and untracked paths::
6898
6898
6899 hg status -v -t mardu
6899 hg status -v -t mardu
6900
6900
6901 Returns 0 on success.
6901 Returns 0 on success.
6902
6902
6903 """
6903 """
6904
6904
6905 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6905 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6906 opts = pycompat.byteskwargs(opts)
6906 opts = pycompat.byteskwargs(opts)
6907 revs = opts.get(b'rev', [])
6907 revs = opts.get(b'rev', [])
6908 change = opts.get(b'change', b'')
6908 change = opts.get(b'change', b'')
6909 terse = opts.get(b'terse', _NOTTERSE)
6909 terse = opts.get(b'terse', _NOTTERSE)
6910 if terse is _NOTTERSE:
6910 if terse is _NOTTERSE:
6911 if revs:
6911 if revs:
6912 terse = b''
6912 terse = b''
6913 else:
6913 else:
6914 terse = ui.config(b'commands', b'status.terse')
6914 terse = ui.config(b'commands', b'status.terse')
6915
6915
6916 if revs and terse:
6916 if revs and terse:
6917 msg = _(b'cannot use --terse with --rev')
6917 msg = _(b'cannot use --terse with --rev')
6918 raise error.InputError(msg)
6918 raise error.InputError(msg)
6919 elif change:
6919 elif change:
6920 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6920 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6921 ctx2 = logcmdutil.revsingle(repo, change, None)
6921 ctx2 = logcmdutil.revsingle(repo, change, None)
6922 ctx1 = ctx2.p1()
6922 ctx1 = ctx2.p1()
6923 else:
6923 else:
6924 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6924 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6925 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6925 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6926
6926
6927 forcerelativevalue = None
6927 forcerelativevalue = None
6928 if ui.hasconfig(b'commands', b'status.relative'):
6928 if ui.hasconfig(b'commands', b'status.relative'):
6929 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6929 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6930 uipathfn = scmutil.getuipathfn(
6930 uipathfn = scmutil.getuipathfn(
6931 repo,
6931 repo,
6932 legacyrelativevalue=bool(pats),
6932 legacyrelativevalue=bool(pats),
6933 forcerelativevalue=forcerelativevalue,
6933 forcerelativevalue=forcerelativevalue,
6934 )
6934 )
6935
6935
6936 if opts.get(b'print0'):
6936 if opts.get(b'print0'):
6937 end = b'\0'
6937 end = b'\0'
6938 else:
6938 else:
6939 end = b'\n'
6939 end = b'\n'
6940 states = b'modified added removed deleted unknown ignored clean'.split()
6940 states = b'modified added removed deleted unknown ignored clean'.split()
6941 show = [k for k in states if opts.get(k)]
6941 show = [k for k in states if opts.get(k)]
6942 if opts.get(b'all'):
6942 if opts.get(b'all'):
6943 show += ui.quiet and (states[:4] + [b'clean']) or states
6943 show += ui.quiet and (states[:4] + [b'clean']) or states
6944
6944
6945 if not show:
6945 if not show:
6946 if ui.quiet:
6946 if ui.quiet:
6947 show = states[:4]
6947 show = states[:4]
6948 else:
6948 else:
6949 show = states[:5]
6949 show = states[:5]
6950
6950
6951 m = scmutil.match(ctx2, pats, opts)
6951 m = scmutil.match(ctx2, pats, opts)
6952 if terse:
6952 if terse:
6953 # we need to compute clean and unknown to terse
6953 # we need to compute clean and unknown to terse
6954 stat = repo.status(
6954 stat = repo.status(
6955 ctx1.node(),
6955 ctx1.node(),
6956 ctx2.node(),
6956 ctx2.node(),
6957 m,
6957 m,
6958 b'ignored' in show or b'i' in terse,
6958 b'ignored' in show or b'i' in terse,
6959 clean=True,
6959 clean=True,
6960 unknown=True,
6960 unknown=True,
6961 listsubrepos=opts.get(b'subrepos'),
6961 listsubrepos=opts.get(b'subrepos'),
6962 )
6962 )
6963
6963
6964 stat = cmdutil.tersedir(stat, terse)
6964 stat = cmdutil.tersedir(stat, terse)
6965 else:
6965 else:
6966 stat = repo.status(
6966 stat = repo.status(
6967 ctx1.node(),
6967 ctx1.node(),
6968 ctx2.node(),
6968 ctx2.node(),
6969 m,
6969 m,
6970 b'ignored' in show,
6970 b'ignored' in show,
6971 b'clean' in show,
6971 b'clean' in show,
6972 b'unknown' in show,
6972 b'unknown' in show,
6973 opts.get(b'subrepos'),
6973 opts.get(b'subrepos'),
6974 )
6974 )
6975
6975
6976 changestates = zip(
6976 changestates = zip(
6977 states,
6977 states,
6978 pycompat.iterbytestr(b'MAR!?IC'),
6978 pycompat.iterbytestr(b'MAR!?IC'),
6979 [getattr(stat, s.decode('utf8')) for s in states],
6979 [getattr(stat, s.decode('utf8')) for s in states],
6980 )
6980 )
6981
6981
6982 copy = {}
6982 copy = {}
6983 if (
6983 show_copies = ui.configbool(b'ui', b'statuscopies')
6984 opts.get(b'all')
6984 if opts.get(b'copies') is not None:
6985 or opts.get(b'copies')
6985 show_copies = opts.get(b'copies')
6986 or ui.configbool(b'ui', b'statuscopies')
6986 show_copies = (show_copies or opts.get(b'all')) and not opts.get(
6987 ) and not opts.get(b'no_status'):
6987 b'no_status'
6988 )
6989 if show_copies:
6988 copy = copies.pathcopies(ctx1, ctx2, m)
6990 copy = copies.pathcopies(ctx1, ctx2, m)
6989
6991
6990 morestatus = None
6992 morestatus = None
6991 if (
6993 if (
6992 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6994 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6993 and not ui.plain()
6995 and not ui.plain()
6994 and not opts.get(b'print0')
6996 and not opts.get(b'print0')
6995 ):
6997 ):
6996 morestatus = cmdutil.readmorestatus(repo)
6998 morestatus = cmdutil.readmorestatus(repo)
6997
6999
6998 ui.pager(b'status')
7000 ui.pager(b'status')
6999 fm = ui.formatter(b'status', opts)
7001 fm = ui.formatter(b'status', opts)
7000 fmt = b'%s' + end
7002 fmt = b'%s' + end
7001 showchar = not opts.get(b'no_status')
7003 showchar = not opts.get(b'no_status')
7002
7004
7003 for state, char, files in changestates:
7005 for state, char, files in changestates:
7004 if state in show:
7006 if state in show:
7005 label = b'status.' + state
7007 label = b'status.' + state
7006 for f in files:
7008 for f in files:
7007 fm.startitem()
7009 fm.startitem()
7008 fm.context(ctx=ctx2)
7010 fm.context(ctx=ctx2)
7009 fm.data(itemtype=b'file', path=f)
7011 fm.data(itemtype=b'file', path=f)
7010 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7012 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7011 fm.plain(fmt % uipathfn(f), label=label)
7013 fm.plain(fmt % uipathfn(f), label=label)
7012 if f in copy:
7014 if f in copy:
7013 fm.data(source=copy[f])
7015 fm.data(source=copy[f])
7014 fm.plain(
7016 fm.plain(
7015 (b' %s' + end) % uipathfn(copy[f]),
7017 (b' %s' + end) % uipathfn(copy[f]),
7016 label=b'status.copied',
7018 label=b'status.copied',
7017 )
7019 )
7018 if morestatus:
7020 if morestatus:
7019 morestatus.formatfile(f, fm)
7021 morestatus.formatfile(f, fm)
7020
7022
7021 if morestatus:
7023 if morestatus:
7022 morestatus.formatfooter(fm)
7024 morestatus.formatfooter(fm)
7023 fm.end()
7025 fm.end()
7024
7026
7025
7027
7026 @command(
7028 @command(
7027 b'summary|sum',
7029 b'summary|sum',
7028 [(b'', b'remote', None, _(b'check for push and pull'))],
7030 [(b'', b'remote', None, _(b'check for push and pull'))],
7029 b'[--remote]',
7031 b'[--remote]',
7030 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7032 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7031 helpbasic=True,
7033 helpbasic=True,
7032 intents={INTENT_READONLY},
7034 intents={INTENT_READONLY},
7033 )
7035 )
7034 def summary(ui, repo, **opts):
7036 def summary(ui, repo, **opts):
7035 """summarize working directory state
7037 """summarize working directory state
7036
7038
7037 This generates a brief summary of the working directory state,
7039 This generates a brief summary of the working directory state,
7038 including parents, branch, commit status, phase and available updates.
7040 including parents, branch, commit status, phase and available updates.
7039
7041
7040 With the --remote option, this will check the default paths for
7042 With the --remote option, this will check the default paths for
7041 incoming and outgoing changes. This can be time-consuming.
7043 incoming and outgoing changes. This can be time-consuming.
7042
7044
7043 Returns 0 on success.
7045 Returns 0 on success.
7044 """
7046 """
7045
7047
7046 opts = pycompat.byteskwargs(opts)
7048 opts = pycompat.byteskwargs(opts)
7047 ui.pager(b'summary')
7049 ui.pager(b'summary')
7048 ctx = repo[None]
7050 ctx = repo[None]
7049 parents = ctx.parents()
7051 parents = ctx.parents()
7050 pnode = parents[0].node()
7052 pnode = parents[0].node()
7051 marks = []
7053 marks = []
7052
7054
7053 try:
7055 try:
7054 ms = mergestatemod.mergestate.read(repo)
7056 ms = mergestatemod.mergestate.read(repo)
7055 except error.UnsupportedMergeRecords as e:
7057 except error.UnsupportedMergeRecords as e:
7056 s = b' '.join(e.recordtypes)
7058 s = b' '.join(e.recordtypes)
7057 ui.warn(
7059 ui.warn(
7058 _(b'warning: merge state has unsupported record types: %s\n') % s
7060 _(b'warning: merge state has unsupported record types: %s\n') % s
7059 )
7061 )
7060 unresolved = []
7062 unresolved = []
7061 else:
7063 else:
7062 unresolved = list(ms.unresolved())
7064 unresolved = list(ms.unresolved())
7063
7065
7064 for p in parents:
7066 for p in parents:
7065 # label with log.changeset (instead of log.parent) since this
7067 # label with log.changeset (instead of log.parent) since this
7066 # shows a working directory parent *changeset*:
7068 # shows a working directory parent *changeset*:
7067 # i18n: column positioning for "hg summary"
7069 # i18n: column positioning for "hg summary"
7068 ui.write(
7070 ui.write(
7069 _(b'parent: %d:%s ') % (p.rev(), p),
7071 _(b'parent: %d:%s ') % (p.rev(), p),
7070 label=logcmdutil.changesetlabels(p),
7072 label=logcmdutil.changesetlabels(p),
7071 )
7073 )
7072 ui.write(b' '.join(p.tags()), label=b'log.tag')
7074 ui.write(b' '.join(p.tags()), label=b'log.tag')
7073 if p.bookmarks():
7075 if p.bookmarks():
7074 marks.extend(p.bookmarks())
7076 marks.extend(p.bookmarks())
7075 if p.rev() == -1:
7077 if p.rev() == -1:
7076 if not len(repo):
7078 if not len(repo):
7077 ui.write(_(b' (empty repository)'))
7079 ui.write(_(b' (empty repository)'))
7078 else:
7080 else:
7079 ui.write(_(b' (no revision checked out)'))
7081 ui.write(_(b' (no revision checked out)'))
7080 if p.obsolete():
7082 if p.obsolete():
7081 ui.write(_(b' (obsolete)'))
7083 ui.write(_(b' (obsolete)'))
7082 if p.isunstable():
7084 if p.isunstable():
7083 instabilities = (
7085 instabilities = (
7084 ui.label(instability, b'trouble.%s' % instability)
7086 ui.label(instability, b'trouble.%s' % instability)
7085 for instability in p.instabilities()
7087 for instability in p.instabilities()
7086 )
7088 )
7087 ui.write(b' (' + b', '.join(instabilities) + b')')
7089 ui.write(b' (' + b', '.join(instabilities) + b')')
7088 ui.write(b'\n')
7090 ui.write(b'\n')
7089 if p.description():
7091 if p.description():
7090 ui.status(
7092 ui.status(
7091 b' ' + p.description().splitlines()[0].strip() + b'\n',
7093 b' ' + p.description().splitlines()[0].strip() + b'\n',
7092 label=b'log.summary',
7094 label=b'log.summary',
7093 )
7095 )
7094
7096
7095 branch = ctx.branch()
7097 branch = ctx.branch()
7096 bheads = repo.branchheads(branch)
7098 bheads = repo.branchheads(branch)
7097 # i18n: column positioning for "hg summary"
7099 # i18n: column positioning for "hg summary"
7098 m = _(b'branch: %s\n') % branch
7100 m = _(b'branch: %s\n') % branch
7099 if branch != b'default':
7101 if branch != b'default':
7100 ui.write(m, label=b'log.branch')
7102 ui.write(m, label=b'log.branch')
7101 else:
7103 else:
7102 ui.status(m, label=b'log.branch')
7104 ui.status(m, label=b'log.branch')
7103
7105
7104 if marks:
7106 if marks:
7105 active = repo._activebookmark
7107 active = repo._activebookmark
7106 # i18n: column positioning for "hg summary"
7108 # i18n: column positioning for "hg summary"
7107 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7109 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7108 if active is not None:
7110 if active is not None:
7109 if active in marks:
7111 if active in marks:
7110 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7112 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7111 marks.remove(active)
7113 marks.remove(active)
7112 else:
7114 else:
7113 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7115 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7114 for m in marks:
7116 for m in marks:
7115 ui.write(b' ' + m, label=b'log.bookmark')
7117 ui.write(b' ' + m, label=b'log.bookmark')
7116 ui.write(b'\n', label=b'log.bookmark')
7118 ui.write(b'\n', label=b'log.bookmark')
7117
7119
7118 status = repo.status(unknown=True)
7120 status = repo.status(unknown=True)
7119
7121
7120 c = repo.dirstate.copies()
7122 c = repo.dirstate.copies()
7121 copied, renamed = [], []
7123 copied, renamed = [], []
7122 for d, s in c.items():
7124 for d, s in c.items():
7123 if s in status.removed:
7125 if s in status.removed:
7124 status.removed.remove(s)
7126 status.removed.remove(s)
7125 renamed.append(d)
7127 renamed.append(d)
7126 else:
7128 else:
7127 copied.append(d)
7129 copied.append(d)
7128 if d in status.added:
7130 if d in status.added:
7129 status.added.remove(d)
7131 status.added.remove(d)
7130
7132
7131 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7133 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7132
7134
7133 labels = [
7135 labels = [
7134 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7136 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7135 (ui.label(_(b'%d added'), b'status.added'), status.added),
7137 (ui.label(_(b'%d added'), b'status.added'), status.added),
7136 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7138 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7137 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7139 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7138 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7140 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7139 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7141 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7140 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7142 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7141 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7143 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7142 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7144 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7143 ]
7145 ]
7144 t = []
7146 t = []
7145 for l, s in labels:
7147 for l, s in labels:
7146 if s:
7148 if s:
7147 t.append(l % len(s))
7149 t.append(l % len(s))
7148
7150
7149 t = b', '.join(t)
7151 t = b', '.join(t)
7150 cleanworkdir = False
7152 cleanworkdir = False
7151
7153
7152 if repo.vfs.exists(b'graftstate'):
7154 if repo.vfs.exists(b'graftstate'):
7153 t += _(b' (graft in progress)')
7155 t += _(b' (graft in progress)')
7154 if repo.vfs.exists(b'updatestate'):
7156 if repo.vfs.exists(b'updatestate'):
7155 t += _(b' (interrupted update)')
7157 t += _(b' (interrupted update)')
7156 elif len(parents) > 1:
7158 elif len(parents) > 1:
7157 t += _(b' (merge)')
7159 t += _(b' (merge)')
7158 elif branch != parents[0].branch():
7160 elif branch != parents[0].branch():
7159 t += _(b' (new branch)')
7161 t += _(b' (new branch)')
7160 elif parents[0].closesbranch() and pnode in repo.branchheads(
7162 elif parents[0].closesbranch() and pnode in repo.branchheads(
7161 branch, closed=True
7163 branch, closed=True
7162 ):
7164 ):
7163 t += _(b' (head closed)')
7165 t += _(b' (head closed)')
7164 elif not (
7166 elif not (
7165 status.modified
7167 status.modified
7166 or status.added
7168 or status.added
7167 or status.removed
7169 or status.removed
7168 or renamed
7170 or renamed
7169 or copied
7171 or copied
7170 or subs
7172 or subs
7171 ):
7173 ):
7172 t += _(b' (clean)')
7174 t += _(b' (clean)')
7173 cleanworkdir = True
7175 cleanworkdir = True
7174 elif pnode not in bheads:
7176 elif pnode not in bheads:
7175 t += _(b' (new branch head)')
7177 t += _(b' (new branch head)')
7176
7178
7177 if parents:
7179 if parents:
7178 pendingphase = max(p.phase() for p in parents)
7180 pendingphase = max(p.phase() for p in parents)
7179 else:
7181 else:
7180 pendingphase = phases.public
7182 pendingphase = phases.public
7181
7183
7182 if pendingphase > phases.newcommitphase(ui):
7184 if pendingphase > phases.newcommitphase(ui):
7183 t += b' (%s)' % phases.phasenames[pendingphase]
7185 t += b' (%s)' % phases.phasenames[pendingphase]
7184
7186
7185 if cleanworkdir:
7187 if cleanworkdir:
7186 # i18n: column positioning for "hg summary"
7188 # i18n: column positioning for "hg summary"
7187 ui.status(_(b'commit: %s\n') % t.strip())
7189 ui.status(_(b'commit: %s\n') % t.strip())
7188 else:
7190 else:
7189 # i18n: column positioning for "hg summary"
7191 # i18n: column positioning for "hg summary"
7190 ui.write(_(b'commit: %s\n') % t.strip())
7192 ui.write(_(b'commit: %s\n') % t.strip())
7191
7193
7192 # all ancestors of branch heads - all ancestors of parent = new csets
7194 # all ancestors of branch heads - all ancestors of parent = new csets
7193 new = len(
7195 new = len(
7194 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7196 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7195 )
7197 )
7196
7198
7197 if new == 0:
7199 if new == 0:
7198 # i18n: column positioning for "hg summary"
7200 # i18n: column positioning for "hg summary"
7199 ui.status(_(b'update: (current)\n'))
7201 ui.status(_(b'update: (current)\n'))
7200 elif pnode not in bheads:
7202 elif pnode not in bheads:
7201 # i18n: column positioning for "hg summary"
7203 # i18n: column positioning for "hg summary"
7202 ui.write(_(b'update: %d new changesets (update)\n') % new)
7204 ui.write(_(b'update: %d new changesets (update)\n') % new)
7203 else:
7205 else:
7204 # i18n: column positioning for "hg summary"
7206 # i18n: column positioning for "hg summary"
7205 ui.write(
7207 ui.write(
7206 _(b'update: %d new changesets, %d branch heads (merge)\n')
7208 _(b'update: %d new changesets, %d branch heads (merge)\n')
7207 % (new, len(bheads))
7209 % (new, len(bheads))
7208 )
7210 )
7209
7211
7210 t = []
7212 t = []
7211 draft = len(repo.revs(b'draft()'))
7213 draft = len(repo.revs(b'draft()'))
7212 if draft:
7214 if draft:
7213 t.append(_(b'%d draft') % draft)
7215 t.append(_(b'%d draft') % draft)
7214 secret = len(repo.revs(b'secret()'))
7216 secret = len(repo.revs(b'secret()'))
7215 if secret:
7217 if secret:
7216 t.append(_(b'%d secret') % secret)
7218 t.append(_(b'%d secret') % secret)
7217
7219
7218 if draft or secret:
7220 if draft or secret:
7219 ui.status(_(b'phases: %s\n') % b', '.join(t))
7221 ui.status(_(b'phases: %s\n') % b', '.join(t))
7220
7222
7221 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7223 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7222 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7224 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7223 numtrouble = len(repo.revs(trouble + b"()"))
7225 numtrouble = len(repo.revs(trouble + b"()"))
7224 # We write all the possibilities to ease translation
7226 # We write all the possibilities to ease translation
7225 troublemsg = {
7227 troublemsg = {
7226 b"orphan": _(b"orphan: %d changesets"),
7228 b"orphan": _(b"orphan: %d changesets"),
7227 b"contentdivergent": _(b"content-divergent: %d changesets"),
7229 b"contentdivergent": _(b"content-divergent: %d changesets"),
7228 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7230 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7229 }
7231 }
7230 if numtrouble > 0:
7232 if numtrouble > 0:
7231 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7233 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7232
7234
7233 cmdutil.summaryhooks(ui, repo)
7235 cmdutil.summaryhooks(ui, repo)
7234
7236
7235 if opts.get(b'remote'):
7237 if opts.get(b'remote'):
7236 needsincoming, needsoutgoing = True, True
7238 needsincoming, needsoutgoing = True, True
7237 else:
7239 else:
7238 needsincoming, needsoutgoing = False, False
7240 needsincoming, needsoutgoing = False, False
7239 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7241 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7240 if i:
7242 if i:
7241 needsincoming = True
7243 needsincoming = True
7242 if o:
7244 if o:
7243 needsoutgoing = True
7245 needsoutgoing = True
7244 if not needsincoming and not needsoutgoing:
7246 if not needsincoming and not needsoutgoing:
7245 return
7247 return
7246
7248
7247 def getincoming():
7249 def getincoming():
7248 # XXX We should actually skip this if no default is specified, instead
7250 # XXX We should actually skip this if no default is specified, instead
7249 # of passing "default" which will resolve as "./default/" if no default
7251 # of passing "default" which will resolve as "./default/" if no default
7250 # path is defined.
7252 # path is defined.
7251 source, branches = urlutil.get_unique_pull_path(
7253 source, branches = urlutil.get_unique_pull_path(
7252 b'summary', repo, ui, b'default'
7254 b'summary', repo, ui, b'default'
7253 )
7255 )
7254 sbranch = branches[0]
7256 sbranch = branches[0]
7255 try:
7257 try:
7256 other = hg.peer(repo, {}, source)
7258 other = hg.peer(repo, {}, source)
7257 except error.RepoError:
7259 except error.RepoError:
7258 if opts.get(b'remote'):
7260 if opts.get(b'remote'):
7259 raise
7261 raise
7260 return source, sbranch, None, None, None
7262 return source, sbranch, None, None, None
7261 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7263 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7262 if revs:
7264 if revs:
7263 revs = [other.lookup(rev) for rev in revs]
7265 revs = [other.lookup(rev) for rev in revs]
7264 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7266 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7265 with repo.ui.silent():
7267 with repo.ui.silent():
7266 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7268 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7267 return source, sbranch, other, commoninc, commoninc[1]
7269 return source, sbranch, other, commoninc, commoninc[1]
7268
7270
7269 if needsincoming:
7271 if needsincoming:
7270 source, sbranch, sother, commoninc, incoming = getincoming()
7272 source, sbranch, sother, commoninc, incoming = getincoming()
7271 else:
7273 else:
7272 source = sbranch = sother = commoninc = incoming = None
7274 source = sbranch = sother = commoninc = incoming = None
7273
7275
7274 def getoutgoing():
7276 def getoutgoing():
7275 # XXX We should actually skip this if no default is specified, instead
7277 # XXX We should actually skip this if no default is specified, instead
7276 # of passing "default" which will resolve as "./default/" if no default
7278 # of passing "default" which will resolve as "./default/" if no default
7277 # path is defined.
7279 # path is defined.
7278 d = None
7280 d = None
7279 if b'default-push' in ui.paths:
7281 if b'default-push' in ui.paths:
7280 d = b'default-push'
7282 d = b'default-push'
7281 elif b'default' in ui.paths:
7283 elif b'default' in ui.paths:
7282 d = b'default'
7284 d = b'default'
7283 if d is not None:
7285 if d is not None:
7284 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7286 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7285 dest = path.pushloc or path.loc
7287 dest = path.pushloc or path.loc
7286 dbranch = path.branch
7288 dbranch = path.branch
7287 else:
7289 else:
7288 dest = b'default'
7290 dest = b'default'
7289 dbranch = None
7291 dbranch = None
7290 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7292 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7291 if source != dest:
7293 if source != dest:
7292 try:
7294 try:
7293 dother = hg.peer(repo, {}, dest)
7295 dother = hg.peer(repo, {}, dest)
7294 except error.RepoError:
7296 except error.RepoError:
7295 if opts.get(b'remote'):
7297 if opts.get(b'remote'):
7296 raise
7298 raise
7297 return dest, dbranch, None, None
7299 return dest, dbranch, None, None
7298 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7300 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7299 elif sother is None:
7301 elif sother is None:
7300 # there is no explicit destination peer, but source one is invalid
7302 # there is no explicit destination peer, but source one is invalid
7301 return dest, dbranch, None, None
7303 return dest, dbranch, None, None
7302 else:
7304 else:
7303 dother = sother
7305 dother = sother
7304 if source != dest or (sbranch is not None and sbranch != dbranch):
7306 if source != dest or (sbranch is not None and sbranch != dbranch):
7305 common = None
7307 common = None
7306 else:
7308 else:
7307 common = commoninc
7309 common = commoninc
7308 if revs:
7310 if revs:
7309 revs = [repo.lookup(rev) for rev in revs]
7311 revs = [repo.lookup(rev) for rev in revs]
7310 with repo.ui.silent():
7312 with repo.ui.silent():
7311 outgoing = discovery.findcommonoutgoing(
7313 outgoing = discovery.findcommonoutgoing(
7312 repo, dother, onlyheads=revs, commoninc=common
7314 repo, dother, onlyheads=revs, commoninc=common
7313 )
7315 )
7314 return dest, dbranch, dother, outgoing
7316 return dest, dbranch, dother, outgoing
7315
7317
7316 if needsoutgoing:
7318 if needsoutgoing:
7317 dest, dbranch, dother, outgoing = getoutgoing()
7319 dest, dbranch, dother, outgoing = getoutgoing()
7318 else:
7320 else:
7319 dest = dbranch = dother = outgoing = None
7321 dest = dbranch = dother = outgoing = None
7320
7322
7321 if opts.get(b'remote'):
7323 if opts.get(b'remote'):
7322 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7324 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7323 # The former always sets `sother` (or raises an exception if it can't);
7325 # The former always sets `sother` (or raises an exception if it can't);
7324 # the latter always sets `outgoing`.
7326 # the latter always sets `outgoing`.
7325 assert sother is not None
7327 assert sother is not None
7326 assert outgoing is not None
7328 assert outgoing is not None
7327
7329
7328 t = []
7330 t = []
7329 if incoming:
7331 if incoming:
7330 t.append(_(b'1 or more incoming'))
7332 t.append(_(b'1 or more incoming'))
7331 o = outgoing.missing
7333 o = outgoing.missing
7332 if o:
7334 if o:
7333 t.append(_(b'%d outgoing') % len(o))
7335 t.append(_(b'%d outgoing') % len(o))
7334 other = dother or sother
7336 other = dother or sother
7335 if b'bookmarks' in other.listkeys(b'namespaces'):
7337 if b'bookmarks' in other.listkeys(b'namespaces'):
7336 counts = bookmarks.summary(repo, other)
7338 counts = bookmarks.summary(repo, other)
7337 if counts[0] > 0:
7339 if counts[0] > 0:
7338 t.append(_(b'%d incoming bookmarks') % counts[0])
7340 t.append(_(b'%d incoming bookmarks') % counts[0])
7339 if counts[1] > 0:
7341 if counts[1] > 0:
7340 t.append(_(b'%d outgoing bookmarks') % counts[1])
7342 t.append(_(b'%d outgoing bookmarks') % counts[1])
7341
7343
7342 if t:
7344 if t:
7343 # i18n: column positioning for "hg summary"
7345 # i18n: column positioning for "hg summary"
7344 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7346 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7345 else:
7347 else:
7346 # i18n: column positioning for "hg summary"
7348 # i18n: column positioning for "hg summary"
7347 ui.status(_(b'remote: (synced)\n'))
7349 ui.status(_(b'remote: (synced)\n'))
7348
7350
7349 cmdutil.summaryremotehooks(
7351 cmdutil.summaryremotehooks(
7350 ui,
7352 ui,
7351 repo,
7353 repo,
7352 opts,
7354 opts,
7353 (
7355 (
7354 (source, sbranch, sother, commoninc),
7356 (source, sbranch, sother, commoninc),
7355 (dest, dbranch, dother, outgoing),
7357 (dest, dbranch, dother, outgoing),
7356 ),
7358 ),
7357 )
7359 )
7358
7360
7359
7361
7360 @command(
7362 @command(
7361 b'tag',
7363 b'tag',
7362 [
7364 [
7363 (b'f', b'force', None, _(b'force tag')),
7365 (b'f', b'force', None, _(b'force tag')),
7364 (b'l', b'local', None, _(b'make the tag local')),
7366 (b'l', b'local', None, _(b'make the tag local')),
7365 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7367 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7366 (b'', b'remove', None, _(b'remove a tag')),
7368 (b'', b'remove', None, _(b'remove a tag')),
7367 # -l/--local is already there, commitopts cannot be used
7369 # -l/--local is already there, commitopts cannot be used
7368 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7370 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7369 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7371 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7370 ]
7372 ]
7371 + commitopts2,
7373 + commitopts2,
7372 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7374 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7373 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7375 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7374 )
7376 )
7375 def tag(ui, repo, name1, *names, **opts):
7377 def tag(ui, repo, name1, *names, **opts):
7376 """add one or more tags for the current or given revision
7378 """add one or more tags for the current or given revision
7377
7379
7378 Name a particular revision using <name>.
7380 Name a particular revision using <name>.
7379
7381
7380 Tags are used to name particular revisions of the repository and are
7382 Tags are used to name particular revisions of the repository and are
7381 very useful to compare different revisions, to go back to significant
7383 very useful to compare different revisions, to go back to significant
7382 earlier versions or to mark branch points as releases, etc. Changing
7384 earlier versions or to mark branch points as releases, etc. Changing
7383 an existing tag is normally disallowed; use -f/--force to override.
7385 an existing tag is normally disallowed; use -f/--force to override.
7384
7386
7385 If no revision is given, the parent of the working directory is
7387 If no revision is given, the parent of the working directory is
7386 used.
7388 used.
7387
7389
7388 To facilitate version control, distribution, and merging of tags,
7390 To facilitate version control, distribution, and merging of tags,
7389 they are stored as a file named ".hgtags" which is managed similarly
7391 they are stored as a file named ".hgtags" which is managed similarly
7390 to other project files and can be hand-edited if necessary. This
7392 to other project files and can be hand-edited if necessary. This
7391 also means that tagging creates a new commit. The file
7393 also means that tagging creates a new commit. The file
7392 ".hg/localtags" is used for local tags (not shared among
7394 ".hg/localtags" is used for local tags (not shared among
7393 repositories).
7395 repositories).
7394
7396
7395 Tag commits are usually made at the head of a branch. If the parent
7397 Tag commits are usually made at the head of a branch. If the parent
7396 of the working directory is not a branch head, :hg:`tag` aborts; use
7398 of the working directory is not a branch head, :hg:`tag` aborts; use
7397 -f/--force to force the tag commit to be based on a non-head
7399 -f/--force to force the tag commit to be based on a non-head
7398 changeset.
7400 changeset.
7399
7401
7400 See :hg:`help dates` for a list of formats valid for -d/--date.
7402 See :hg:`help dates` for a list of formats valid for -d/--date.
7401
7403
7402 Since tag names have priority over branch names during revision
7404 Since tag names have priority over branch names during revision
7403 lookup, using an existing branch name as a tag name is discouraged.
7405 lookup, using an existing branch name as a tag name is discouraged.
7404
7406
7405 Returns 0 on success.
7407 Returns 0 on success.
7406 """
7408 """
7407 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7409 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7408 opts = pycompat.byteskwargs(opts)
7410 opts = pycompat.byteskwargs(opts)
7409 with repo.wlock(), repo.lock():
7411 with repo.wlock(), repo.lock():
7410 rev_ = b"."
7412 rev_ = b"."
7411 names = [t.strip() for t in (name1,) + names]
7413 names = [t.strip() for t in (name1,) + names]
7412 if len(names) != len(set(names)):
7414 if len(names) != len(set(names)):
7413 raise error.InputError(_(b'tag names must be unique'))
7415 raise error.InputError(_(b'tag names must be unique'))
7414 for n in names:
7416 for n in names:
7415 scmutil.checknewlabel(repo, n, b'tag')
7417 scmutil.checknewlabel(repo, n, b'tag')
7416 if not n:
7418 if not n:
7417 raise error.InputError(
7419 raise error.InputError(
7418 _(b'tag names cannot consist entirely of whitespace')
7420 _(b'tag names cannot consist entirely of whitespace')
7419 )
7421 )
7420 if opts.get(b'rev'):
7422 if opts.get(b'rev'):
7421 rev_ = opts[b'rev']
7423 rev_ = opts[b'rev']
7422 message = opts.get(b'message')
7424 message = opts.get(b'message')
7423 if opts.get(b'remove'):
7425 if opts.get(b'remove'):
7424 if opts.get(b'local'):
7426 if opts.get(b'local'):
7425 expectedtype = b'local'
7427 expectedtype = b'local'
7426 else:
7428 else:
7427 expectedtype = b'global'
7429 expectedtype = b'global'
7428
7430
7429 for n in names:
7431 for n in names:
7430 if repo.tagtype(n) == b'global':
7432 if repo.tagtype(n) == b'global':
7431 alltags = tagsmod.findglobaltags(ui, repo)
7433 alltags = tagsmod.findglobaltags(ui, repo)
7432 if alltags[n][0] == repo.nullid:
7434 if alltags[n][0] == repo.nullid:
7433 raise error.InputError(
7435 raise error.InputError(
7434 _(b"tag '%s' is already removed") % n
7436 _(b"tag '%s' is already removed") % n
7435 )
7437 )
7436 if not repo.tagtype(n):
7438 if not repo.tagtype(n):
7437 raise error.InputError(_(b"tag '%s' does not exist") % n)
7439 raise error.InputError(_(b"tag '%s' does not exist") % n)
7438 if repo.tagtype(n) != expectedtype:
7440 if repo.tagtype(n) != expectedtype:
7439 if expectedtype == b'global':
7441 if expectedtype == b'global':
7440 raise error.InputError(
7442 raise error.InputError(
7441 _(b"tag '%s' is not a global tag") % n
7443 _(b"tag '%s' is not a global tag") % n
7442 )
7444 )
7443 else:
7445 else:
7444 raise error.InputError(
7446 raise error.InputError(
7445 _(b"tag '%s' is not a local tag") % n
7447 _(b"tag '%s' is not a local tag") % n
7446 )
7448 )
7447 rev_ = b'null'
7449 rev_ = b'null'
7448 if not message:
7450 if not message:
7449 # we don't translate commit messages
7451 # we don't translate commit messages
7450 message = b'Removed tag %s' % b', '.join(names)
7452 message = b'Removed tag %s' % b', '.join(names)
7451 elif not opts.get(b'force'):
7453 elif not opts.get(b'force'):
7452 for n in names:
7454 for n in names:
7453 if n in repo.tags():
7455 if n in repo.tags():
7454 raise error.InputError(
7456 raise error.InputError(
7455 _(b"tag '%s' already exists (use -f to force)") % n
7457 _(b"tag '%s' already exists (use -f to force)") % n
7456 )
7458 )
7457 if not opts.get(b'local'):
7459 if not opts.get(b'local'):
7458 p1, p2 = repo.dirstate.parents()
7460 p1, p2 = repo.dirstate.parents()
7459 if p2 != repo.nullid:
7461 if p2 != repo.nullid:
7460 raise error.StateError(_(b'uncommitted merge'))
7462 raise error.StateError(_(b'uncommitted merge'))
7461 bheads = repo.branchheads()
7463 bheads = repo.branchheads()
7462 if not opts.get(b'force') and bheads and p1 not in bheads:
7464 if not opts.get(b'force') and bheads and p1 not in bheads:
7463 raise error.InputError(
7465 raise error.InputError(
7464 _(
7466 _(
7465 b'working directory is not at a branch head '
7467 b'working directory is not at a branch head '
7466 b'(use -f to force)'
7468 b'(use -f to force)'
7467 )
7469 )
7468 )
7470 )
7469 node = logcmdutil.revsingle(repo, rev_).node()
7471 node = logcmdutil.revsingle(repo, rev_).node()
7470
7472
7471 if not message:
7473 if not message:
7472 # we don't translate commit messages
7474 # we don't translate commit messages
7473 message = b'Added tag %s for changeset %s' % (
7475 message = b'Added tag %s for changeset %s' % (
7474 b', '.join(names),
7476 b', '.join(names),
7475 short(node),
7477 short(node),
7476 )
7478 )
7477
7479
7478 date = opts.get(b'date')
7480 date = opts.get(b'date')
7479 if date:
7481 if date:
7480 date = dateutil.parsedate(date)
7482 date = dateutil.parsedate(date)
7481
7483
7482 if opts.get(b'remove'):
7484 if opts.get(b'remove'):
7483 editform = b'tag.remove'
7485 editform = b'tag.remove'
7484 else:
7486 else:
7485 editform = b'tag.add'
7487 editform = b'tag.add'
7486 editor = cmdutil.getcommiteditor(
7488 editor = cmdutil.getcommiteditor(
7487 editform=editform, **pycompat.strkwargs(opts)
7489 editform=editform, **pycompat.strkwargs(opts)
7488 )
7490 )
7489
7491
7490 # don't allow tagging the null rev
7492 # don't allow tagging the null rev
7491 if (
7493 if (
7492 not opts.get(b'remove')
7494 not opts.get(b'remove')
7493 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7495 and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7494 ):
7496 ):
7495 raise error.InputError(_(b"cannot tag null revision"))
7497 raise error.InputError(_(b"cannot tag null revision"))
7496
7498
7497 tagsmod.tag(
7499 tagsmod.tag(
7498 repo,
7500 repo,
7499 names,
7501 names,
7500 node,
7502 node,
7501 message,
7503 message,
7502 opts.get(b'local'),
7504 opts.get(b'local'),
7503 opts.get(b'user'),
7505 opts.get(b'user'),
7504 date,
7506 date,
7505 editor=editor,
7507 editor=editor,
7506 )
7508 )
7507
7509
7508
7510
7509 @command(
7511 @command(
7510 b'tags',
7512 b'tags',
7511 formatteropts,
7513 formatteropts,
7512 b'',
7514 b'',
7513 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7515 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7514 intents={INTENT_READONLY},
7516 intents={INTENT_READONLY},
7515 )
7517 )
7516 def tags(ui, repo, **opts):
7518 def tags(ui, repo, **opts):
7517 """list repository tags
7519 """list repository tags
7518
7520
7519 This lists both regular and local tags. When the -v/--verbose
7521 This lists both regular and local tags. When the -v/--verbose
7520 switch is used, a third column "local" is printed for local tags.
7522 switch is used, a third column "local" is printed for local tags.
7521 When the -q/--quiet switch is used, only the tag name is printed.
7523 When the -q/--quiet switch is used, only the tag name is printed.
7522
7524
7523 .. container:: verbose
7525 .. container:: verbose
7524
7526
7525 Template:
7527 Template:
7526
7528
7527 The following keywords are supported in addition to the common template
7529 The following keywords are supported in addition to the common template
7528 keywords and functions such as ``{tag}``. See also
7530 keywords and functions such as ``{tag}``. See also
7529 :hg:`help templates`.
7531 :hg:`help templates`.
7530
7532
7531 :type: String. ``local`` for local tags.
7533 :type: String. ``local`` for local tags.
7532
7534
7533 Returns 0 on success.
7535 Returns 0 on success.
7534 """
7536 """
7535
7537
7536 opts = pycompat.byteskwargs(opts)
7538 opts = pycompat.byteskwargs(opts)
7537 ui.pager(b'tags')
7539 ui.pager(b'tags')
7538 fm = ui.formatter(b'tags', opts)
7540 fm = ui.formatter(b'tags', opts)
7539 hexfunc = fm.hexfunc
7541 hexfunc = fm.hexfunc
7540
7542
7541 for t, n in reversed(repo.tagslist()):
7543 for t, n in reversed(repo.tagslist()):
7542 hn = hexfunc(n)
7544 hn = hexfunc(n)
7543 label = b'tags.normal'
7545 label = b'tags.normal'
7544 tagtype = repo.tagtype(t)
7546 tagtype = repo.tagtype(t)
7545 if not tagtype or tagtype == b'global':
7547 if not tagtype or tagtype == b'global':
7546 tagtype = b''
7548 tagtype = b''
7547 else:
7549 else:
7548 label = b'tags.' + tagtype
7550 label = b'tags.' + tagtype
7549
7551
7550 fm.startitem()
7552 fm.startitem()
7551 fm.context(repo=repo)
7553 fm.context(repo=repo)
7552 fm.write(b'tag', b'%s', t, label=label)
7554 fm.write(b'tag', b'%s', t, label=label)
7553 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7555 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7554 fm.condwrite(
7556 fm.condwrite(
7555 not ui.quiet,
7557 not ui.quiet,
7556 b'rev node',
7558 b'rev node',
7557 fmt,
7559 fmt,
7558 repo.changelog.rev(n),
7560 repo.changelog.rev(n),
7559 hn,
7561 hn,
7560 label=label,
7562 label=label,
7561 )
7563 )
7562 fm.condwrite(
7564 fm.condwrite(
7563 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7565 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7564 )
7566 )
7565 fm.plain(b'\n')
7567 fm.plain(b'\n')
7566 fm.end()
7568 fm.end()
7567
7569
7568
7570
7569 @command(
7571 @command(
7570 b'tip',
7572 b'tip',
7571 [
7573 [
7572 (b'p', b'patch', None, _(b'show patch')),
7574 (b'p', b'patch', None, _(b'show patch')),
7573 (b'g', b'git', None, _(b'use git extended diff format')),
7575 (b'g', b'git', None, _(b'use git extended diff format')),
7574 ]
7576 ]
7575 + templateopts,
7577 + templateopts,
7576 _(b'[-p] [-g]'),
7578 _(b'[-p] [-g]'),
7577 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7579 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7578 )
7580 )
7579 def tip(ui, repo, **opts):
7581 def tip(ui, repo, **opts):
7580 """show the tip revision (DEPRECATED)
7582 """show the tip revision (DEPRECATED)
7581
7583
7582 The tip revision (usually just called the tip) is the changeset
7584 The tip revision (usually just called the tip) is the changeset
7583 most recently added to the repository (and therefore the most
7585 most recently added to the repository (and therefore the most
7584 recently changed head).
7586 recently changed head).
7585
7587
7586 If you have just made a commit, that commit will be the tip. If
7588 If you have just made a commit, that commit will be the tip. If
7587 you have just pulled changes from another repository, the tip of
7589 you have just pulled changes from another repository, the tip of
7588 that repository becomes the current tip. The "tip" tag is special
7590 that repository becomes the current tip. The "tip" tag is special
7589 and cannot be renamed or assigned to a different changeset.
7591 and cannot be renamed or assigned to a different changeset.
7590
7592
7591 This command is deprecated, please use :hg:`heads` instead.
7593 This command is deprecated, please use :hg:`heads` instead.
7592
7594
7593 Returns 0 on success.
7595 Returns 0 on success.
7594 """
7596 """
7595 opts = pycompat.byteskwargs(opts)
7597 opts = pycompat.byteskwargs(opts)
7596 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7598 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7597 displayer.show(repo[b'tip'])
7599 displayer.show(repo[b'tip'])
7598 displayer.close()
7600 displayer.close()
7599
7601
7600
7602
7601 @command(
7603 @command(
7602 b'unbundle',
7604 b'unbundle',
7603 [
7605 [
7604 (
7606 (
7605 b'u',
7607 b'u',
7606 b'update',
7608 b'update',
7607 None,
7609 None,
7608 _(b'update to new branch head if changesets were unbundled'),
7610 _(b'update to new branch head if changesets were unbundled'),
7609 )
7611 )
7610 ],
7612 ],
7611 _(b'[-u] FILE...'),
7613 _(b'[-u] FILE...'),
7612 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7614 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7613 )
7615 )
7614 def unbundle(ui, repo, fname1, *fnames, **opts):
7616 def unbundle(ui, repo, fname1, *fnames, **opts):
7615 """apply one or more bundle files
7617 """apply one or more bundle files
7616
7618
7617 Apply one or more bundle files generated by :hg:`bundle`.
7619 Apply one or more bundle files generated by :hg:`bundle`.
7618
7620
7619 Returns 0 on success, 1 if an update has unresolved files.
7621 Returns 0 on success, 1 if an update has unresolved files.
7620 """
7622 """
7621 fnames = (fname1,) + fnames
7623 fnames = (fname1,) + fnames
7622
7624
7623 with repo.lock():
7625 with repo.lock():
7624 for fname in fnames:
7626 for fname in fnames:
7625 f = hg.openpath(ui, fname)
7627 f = hg.openpath(ui, fname)
7626 gen = exchange.readbundle(ui, f, fname)
7628 gen = exchange.readbundle(ui, f, fname)
7627 if isinstance(gen, streamclone.streamcloneapplier):
7629 if isinstance(gen, streamclone.streamcloneapplier):
7628 raise error.InputError(
7630 raise error.InputError(
7629 _(
7631 _(
7630 b'packed bundles cannot be applied with '
7632 b'packed bundles cannot be applied with '
7631 b'"hg unbundle"'
7633 b'"hg unbundle"'
7632 ),
7634 ),
7633 hint=_(b'use "hg debugapplystreamclonebundle"'),
7635 hint=_(b'use "hg debugapplystreamclonebundle"'),
7634 )
7636 )
7635 url = b'bundle:' + fname
7637 url = b'bundle:' + fname
7636 try:
7638 try:
7637 txnname = b'unbundle'
7639 txnname = b'unbundle'
7638 if not isinstance(gen, bundle2.unbundle20):
7640 if not isinstance(gen, bundle2.unbundle20):
7639 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7641 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7640 with repo.transaction(txnname) as tr:
7642 with repo.transaction(txnname) as tr:
7641 op = bundle2.applybundle(
7643 op = bundle2.applybundle(
7642 repo, gen, tr, source=b'unbundle', url=url
7644 repo, gen, tr, source=b'unbundle', url=url
7643 )
7645 )
7644 except error.BundleUnknownFeatureError as exc:
7646 except error.BundleUnknownFeatureError as exc:
7645 raise error.Abort(
7647 raise error.Abort(
7646 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7648 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7647 hint=_(
7649 hint=_(
7648 b"see https://mercurial-scm.org/"
7650 b"see https://mercurial-scm.org/"
7649 b"wiki/BundleFeature for more "
7651 b"wiki/BundleFeature for more "
7650 b"information"
7652 b"information"
7651 ),
7653 ),
7652 )
7654 )
7653 modheads = bundle2.combinechangegroupresults(op)
7655 modheads = bundle2.combinechangegroupresults(op)
7654
7656
7655 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7657 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7656 return 1
7658 return 1
7657 else:
7659 else:
7658 return 0
7660 return 0
7659
7661
7660
7662
7661 @command(
7663 @command(
7662 b'unshelve',
7664 b'unshelve',
7663 [
7665 [
7664 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7666 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7665 (
7667 (
7666 b'c',
7668 b'c',
7667 b'continue',
7669 b'continue',
7668 None,
7670 None,
7669 _(b'continue an incomplete unshelve operation'),
7671 _(b'continue an incomplete unshelve operation'),
7670 ),
7672 ),
7671 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7673 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7672 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7674 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7673 (
7675 (
7674 b'n',
7676 b'n',
7675 b'name',
7677 b'name',
7676 b'',
7678 b'',
7677 _(b'restore shelved change with given name'),
7679 _(b'restore shelved change with given name'),
7678 _(b'NAME'),
7680 _(b'NAME'),
7679 ),
7681 ),
7680 (b't', b'tool', b'', _(b'specify merge tool')),
7682 (b't', b'tool', b'', _(b'specify merge tool')),
7681 (
7683 (
7682 b'',
7684 b'',
7683 b'date',
7685 b'date',
7684 b'',
7686 b'',
7685 _(b'set date for temporary commits (DEPRECATED)'),
7687 _(b'set date for temporary commits (DEPRECATED)'),
7686 _(b'DATE'),
7688 _(b'DATE'),
7687 ),
7689 ),
7688 ],
7690 ],
7689 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7691 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7690 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7692 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7691 )
7693 )
7692 def unshelve(ui, repo, *shelved, **opts):
7694 def unshelve(ui, repo, *shelved, **opts):
7693 """restore a shelved change to the working directory
7695 """restore a shelved change to the working directory
7694
7696
7695 This command accepts an optional name of a shelved change to
7697 This command accepts an optional name of a shelved change to
7696 restore. If none is given, the most recent shelved change is used.
7698 restore. If none is given, the most recent shelved change is used.
7697
7699
7698 If a shelved change is applied successfully, the bundle that
7700 If a shelved change is applied successfully, the bundle that
7699 contains the shelved changes is moved to a backup location
7701 contains the shelved changes is moved to a backup location
7700 (.hg/shelve-backup).
7702 (.hg/shelve-backup).
7701
7703
7702 Since you can restore a shelved change on top of an arbitrary
7704 Since you can restore a shelved change on top of an arbitrary
7703 commit, it is possible that unshelving will result in a conflict
7705 commit, it is possible that unshelving will result in a conflict
7704 between your changes and the commits you are unshelving onto. If
7706 between your changes and the commits you are unshelving onto. If
7705 this occurs, you must resolve the conflict, then use
7707 this occurs, you must resolve the conflict, then use
7706 ``--continue`` to complete the unshelve operation. (The bundle
7708 ``--continue`` to complete the unshelve operation. (The bundle
7707 will not be moved until you successfully complete the unshelve.)
7709 will not be moved until you successfully complete the unshelve.)
7708
7710
7709 (Alternatively, you can use ``--abort`` to abandon an unshelve
7711 (Alternatively, you can use ``--abort`` to abandon an unshelve
7710 that causes a conflict. This reverts the unshelved changes, and
7712 that causes a conflict. This reverts the unshelved changes, and
7711 leaves the bundle in place.)
7713 leaves the bundle in place.)
7712
7714
7713 If bare shelved change (without interactive, include and exclude
7715 If bare shelved change (without interactive, include and exclude
7714 option) was done on newly created branch it would restore branch
7716 option) was done on newly created branch it would restore branch
7715 information to the working directory.
7717 information to the working directory.
7716
7718
7717 After a successful unshelve, the shelved changes are stored in a
7719 After a successful unshelve, the shelved changes are stored in a
7718 backup directory. Only the N most recent backups are kept. N
7720 backup directory. Only the N most recent backups are kept. N
7719 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7721 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7720 configuration option.
7722 configuration option.
7721
7723
7722 .. container:: verbose
7724 .. container:: verbose
7723
7725
7724 Timestamp in seconds is used to decide order of backups. More
7726 Timestamp in seconds is used to decide order of backups. More
7725 than ``maxbackups`` backups are kept, if same timestamp
7727 than ``maxbackups`` backups are kept, if same timestamp
7726 prevents from deciding exact order of them, for safety.
7728 prevents from deciding exact order of them, for safety.
7727
7729
7728 Selected changes can be unshelved with ``--interactive`` flag.
7730 Selected changes can be unshelved with ``--interactive`` flag.
7729 The working directory is updated with the selected changes, and
7731 The working directory is updated with the selected changes, and
7730 only the unselected changes remain shelved.
7732 only the unselected changes remain shelved.
7731 Note: The whole shelve is applied to working directory first before
7733 Note: The whole shelve is applied to working directory first before
7732 running interactively. So, this will bring up all the conflicts between
7734 running interactively. So, this will bring up all the conflicts between
7733 working directory and the shelve, irrespective of which changes will be
7735 working directory and the shelve, irrespective of which changes will be
7734 unshelved.
7736 unshelved.
7735 """
7737 """
7736 with repo.wlock():
7738 with repo.wlock():
7737 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7739 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7738
7740
7739
7741
7740 statemod.addunfinished(
7742 statemod.addunfinished(
7741 b'unshelve',
7743 b'unshelve',
7742 fname=b'shelvedstate',
7744 fname=b'shelvedstate',
7743 continueflag=True,
7745 continueflag=True,
7744 abortfunc=shelvemod.hgabortunshelve,
7746 abortfunc=shelvemod.hgabortunshelve,
7745 continuefunc=shelvemod.hgcontinueunshelve,
7747 continuefunc=shelvemod.hgcontinueunshelve,
7746 cmdmsg=_(b'unshelve already in progress'),
7748 cmdmsg=_(b'unshelve already in progress'),
7747 )
7749 )
7748
7750
7749
7751
7750 @command(
7752 @command(
7751 b'update|up|checkout|co',
7753 b'update|up|checkout|co',
7752 [
7754 [
7753 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7755 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7754 (b'c', b'check', None, _(b'require clean working directory')),
7756 (b'c', b'check', None, _(b'require clean working directory')),
7755 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7757 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7756 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7758 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7757 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7759 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7758 ]
7760 ]
7759 + mergetoolopts,
7761 + mergetoolopts,
7760 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7762 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7761 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7763 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7762 helpbasic=True,
7764 helpbasic=True,
7763 )
7765 )
7764 def update(ui, repo, node=None, **opts):
7766 def update(ui, repo, node=None, **opts):
7765 """update working directory (or switch revisions)
7767 """update working directory (or switch revisions)
7766
7768
7767 Update the repository's working directory to the specified
7769 Update the repository's working directory to the specified
7768 changeset. If no changeset is specified, update to the tip of the
7770 changeset. If no changeset is specified, update to the tip of the
7769 current named branch and move the active bookmark (see :hg:`help
7771 current named branch and move the active bookmark (see :hg:`help
7770 bookmarks`).
7772 bookmarks`).
7771
7773
7772 Update sets the working directory's parent revision to the specified
7774 Update sets the working directory's parent revision to the specified
7773 changeset (see :hg:`help parents`).
7775 changeset (see :hg:`help parents`).
7774
7776
7775 If the changeset is not a descendant or ancestor of the working
7777 If the changeset is not a descendant or ancestor of the working
7776 directory's parent and there are uncommitted changes, the update is
7778 directory's parent and there are uncommitted changes, the update is
7777 aborted. With the -c/--check option, the working directory is checked
7779 aborted. With the -c/--check option, the working directory is checked
7778 for uncommitted changes; if none are found, the working directory is
7780 for uncommitted changes; if none are found, the working directory is
7779 updated to the specified changeset.
7781 updated to the specified changeset.
7780
7782
7781 .. container:: verbose
7783 .. container:: verbose
7782
7784
7783 The -C/--clean, -c/--check, and -m/--merge options control what
7785 The -C/--clean, -c/--check, and -m/--merge options control what
7784 happens if the working directory contains uncommitted changes.
7786 happens if the working directory contains uncommitted changes.
7785 At most of one of them can be specified.
7787 At most of one of them can be specified.
7786
7788
7787 1. If no option is specified, and if
7789 1. If no option is specified, and if
7788 the requested changeset is an ancestor or descendant of
7790 the requested changeset is an ancestor or descendant of
7789 the working directory's parent, the uncommitted changes
7791 the working directory's parent, the uncommitted changes
7790 are merged into the requested changeset and the merged
7792 are merged into the requested changeset and the merged
7791 result is left uncommitted. If the requested changeset is
7793 result is left uncommitted. If the requested changeset is
7792 not an ancestor or descendant (that is, it is on another
7794 not an ancestor or descendant (that is, it is on another
7793 branch), the update is aborted and the uncommitted changes
7795 branch), the update is aborted and the uncommitted changes
7794 are preserved.
7796 are preserved.
7795
7797
7796 2. With the -m/--merge option, the update is allowed even if the
7798 2. With the -m/--merge option, the update is allowed even if the
7797 requested changeset is not an ancestor or descendant of
7799 requested changeset is not an ancestor or descendant of
7798 the working directory's parent.
7800 the working directory's parent.
7799
7801
7800 3. With the -c/--check option, the update is aborted and the
7802 3. With the -c/--check option, the update is aborted and the
7801 uncommitted changes are preserved.
7803 uncommitted changes are preserved.
7802
7804
7803 4. With the -C/--clean option, uncommitted changes are discarded and
7805 4. With the -C/--clean option, uncommitted changes are discarded and
7804 the working directory is updated to the requested changeset.
7806 the working directory is updated to the requested changeset.
7805
7807
7806 To cancel an uncommitted merge (and lose your changes), use
7808 To cancel an uncommitted merge (and lose your changes), use
7807 :hg:`merge --abort`.
7809 :hg:`merge --abort`.
7808
7810
7809 Use null as the changeset to remove the working directory (like
7811 Use null as the changeset to remove the working directory (like
7810 :hg:`clone -U`).
7812 :hg:`clone -U`).
7811
7813
7812 If you want to revert just one file to an older revision, use
7814 If you want to revert just one file to an older revision, use
7813 :hg:`revert [-r REV] NAME`.
7815 :hg:`revert [-r REV] NAME`.
7814
7816
7815 See :hg:`help dates` for a list of formats valid for -d/--date.
7817 See :hg:`help dates` for a list of formats valid for -d/--date.
7816
7818
7817 Returns 0 on success, 1 if there are unresolved files.
7819 Returns 0 on success, 1 if there are unresolved files.
7818 """
7820 """
7819 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7821 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7820 rev = opts.get('rev')
7822 rev = opts.get('rev')
7821 date = opts.get('date')
7823 date = opts.get('date')
7822 clean = opts.get('clean')
7824 clean = opts.get('clean')
7823 check = opts.get('check')
7825 check = opts.get('check')
7824 merge = opts.get('merge')
7826 merge = opts.get('merge')
7825 if rev and node:
7827 if rev and node:
7826 raise error.InputError(_(b"please specify just one revision"))
7828 raise error.InputError(_(b"please specify just one revision"))
7827
7829
7828 if ui.configbool(b'commands', b'update.requiredest'):
7830 if ui.configbool(b'commands', b'update.requiredest'):
7829 if not node and not rev and not date:
7831 if not node and not rev and not date:
7830 raise error.InputError(
7832 raise error.InputError(
7831 _(b'you must specify a destination'),
7833 _(b'you must specify a destination'),
7832 hint=_(b'for example: hg update ".::"'),
7834 hint=_(b'for example: hg update ".::"'),
7833 )
7835 )
7834
7836
7835 if rev is None or rev == b'':
7837 if rev is None or rev == b'':
7836 rev = node
7838 rev = node
7837
7839
7838 if date and rev is not None:
7840 if date and rev is not None:
7839 raise error.InputError(_(b"you can't specify a revision and a date"))
7841 raise error.InputError(_(b"you can't specify a revision and a date"))
7840
7842
7841 updatecheck = None
7843 updatecheck = None
7842 if check or merge is not None and not merge:
7844 if check or merge is not None and not merge:
7843 updatecheck = b'abort'
7845 updatecheck = b'abort'
7844 elif merge or check is not None and not check:
7846 elif merge or check is not None and not check:
7845 updatecheck = b'none'
7847 updatecheck = b'none'
7846
7848
7847 with repo.wlock():
7849 with repo.wlock():
7848 cmdutil.clearunfinished(repo)
7850 cmdutil.clearunfinished(repo)
7849 if date:
7851 if date:
7850 rev = cmdutil.finddate(ui, repo, date)
7852 rev = cmdutil.finddate(ui, repo, date)
7851
7853
7852 # if we defined a bookmark, we have to remember the original name
7854 # if we defined a bookmark, we have to remember the original name
7853 brev = rev
7855 brev = rev
7854 if rev:
7856 if rev:
7855 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7857 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7856 ctx = logcmdutil.revsingle(repo, rev, default=None)
7858 ctx = logcmdutil.revsingle(repo, rev, default=None)
7857 rev = ctx.rev()
7859 rev = ctx.rev()
7858 hidden = ctx.hidden()
7860 hidden = ctx.hidden()
7859 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7861 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7860 with ui.configoverride(overrides, b'update'):
7862 with ui.configoverride(overrides, b'update'):
7861 ret = hg.updatetotally(
7863 ret = hg.updatetotally(
7862 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7864 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7863 )
7865 )
7864 if hidden:
7866 if hidden:
7865 ctxstr = ctx.hex()[:12]
7867 ctxstr = ctx.hex()[:12]
7866 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7868 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7867
7869
7868 if ctx.obsolete():
7870 if ctx.obsolete():
7869 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7871 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7870 ui.warn(b"(%s)\n" % obsfatemsg)
7872 ui.warn(b"(%s)\n" % obsfatemsg)
7871 return ret
7873 return ret
7872
7874
7873
7875
7874 @command(
7876 @command(
7875 b'verify',
7877 b'verify',
7876 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7878 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7877 helpcategory=command.CATEGORY_MAINTENANCE,
7879 helpcategory=command.CATEGORY_MAINTENANCE,
7878 )
7880 )
7879 def verify(ui, repo, **opts):
7881 def verify(ui, repo, **opts):
7880 """verify the integrity of the repository
7882 """verify the integrity of the repository
7881
7883
7882 Verify the integrity of the current repository.
7884 Verify the integrity of the current repository.
7883
7885
7884 This will perform an extensive check of the repository's
7886 This will perform an extensive check of the repository's
7885 integrity, validating the hashes and checksums of each entry in
7887 integrity, validating the hashes and checksums of each entry in
7886 the changelog, manifest, and tracked files, as well as the
7888 the changelog, manifest, and tracked files, as well as the
7887 integrity of their crosslinks and indices.
7889 integrity of their crosslinks and indices.
7888
7890
7889 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7891 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7890 for more information about recovery from corruption of the
7892 for more information about recovery from corruption of the
7891 repository.
7893 repository.
7892
7894
7893 Returns 0 on success, 1 if errors are encountered.
7895 Returns 0 on success, 1 if errors are encountered.
7894 """
7896 """
7895 opts = pycompat.byteskwargs(opts)
7897 opts = pycompat.byteskwargs(opts)
7896
7898
7897 level = None
7899 level = None
7898 if opts[b'full']:
7900 if opts[b'full']:
7899 level = verifymod.VERIFY_FULL
7901 level = verifymod.VERIFY_FULL
7900 return hg.verify(repo, level)
7902 return hg.verify(repo, level)
7901
7903
7902
7904
7903 @command(
7905 @command(
7904 b'version',
7906 b'version',
7905 [] + formatteropts,
7907 [] + formatteropts,
7906 helpcategory=command.CATEGORY_HELP,
7908 helpcategory=command.CATEGORY_HELP,
7907 norepo=True,
7909 norepo=True,
7908 intents={INTENT_READONLY},
7910 intents={INTENT_READONLY},
7909 )
7911 )
7910 def version_(ui, **opts):
7912 def version_(ui, **opts):
7911 """output version and copyright information
7913 """output version and copyright information
7912
7914
7913 .. container:: verbose
7915 .. container:: verbose
7914
7916
7915 Template:
7917 Template:
7916
7918
7917 The following keywords are supported. See also :hg:`help templates`.
7919 The following keywords are supported. See also :hg:`help templates`.
7918
7920
7919 :extensions: List of extensions.
7921 :extensions: List of extensions.
7920 :ver: String. Version number.
7922 :ver: String. Version number.
7921
7923
7922 And each entry of ``{extensions}`` provides the following sub-keywords
7924 And each entry of ``{extensions}`` provides the following sub-keywords
7923 in addition to ``{ver}``.
7925 in addition to ``{ver}``.
7924
7926
7925 :bundled: Boolean. True if included in the release.
7927 :bundled: Boolean. True if included in the release.
7926 :name: String. Extension name.
7928 :name: String. Extension name.
7927 """
7929 """
7928 opts = pycompat.byteskwargs(opts)
7930 opts = pycompat.byteskwargs(opts)
7929 if ui.verbose:
7931 if ui.verbose:
7930 ui.pager(b'version')
7932 ui.pager(b'version')
7931 fm = ui.formatter(b"version", opts)
7933 fm = ui.formatter(b"version", opts)
7932 fm.startitem()
7934 fm.startitem()
7933 fm.write(
7935 fm.write(
7934 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7936 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7935 )
7937 )
7936 license = _(
7938 license = _(
7937 b"(see https://mercurial-scm.org for more information)\n"
7939 b"(see https://mercurial-scm.org for more information)\n"
7938 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7940 b"\nCopyright (C) 2005-2022 Olivia Mackall and others\n"
7939 b"This is free software; see the source for copying conditions. "
7941 b"This is free software; see the source for copying conditions. "
7940 b"There is NO\nwarranty; "
7942 b"There is NO\nwarranty; "
7941 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7943 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7942 )
7944 )
7943 if not ui.quiet:
7945 if not ui.quiet:
7944 fm.plain(license)
7946 fm.plain(license)
7945
7947
7946 if ui.verbose:
7948 if ui.verbose:
7947 fm.plain(_(b"\nEnabled extensions:\n\n"))
7949 fm.plain(_(b"\nEnabled extensions:\n\n"))
7948 # format names and versions into columns
7950 # format names and versions into columns
7949 names = []
7951 names = []
7950 vers = []
7952 vers = []
7951 isinternals = []
7953 isinternals = []
7952 for name, module in sorted(extensions.extensions()):
7954 for name, module in sorted(extensions.extensions()):
7953 names.append(name)
7955 names.append(name)
7954 vers.append(extensions.moduleversion(module) or None)
7956 vers.append(extensions.moduleversion(module) or None)
7955 isinternals.append(extensions.ismoduleinternal(module))
7957 isinternals.append(extensions.ismoduleinternal(module))
7956 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7958 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7957 if names:
7959 if names:
7958 namefmt = b" %%-%ds " % max(len(n) for n in names)
7960 namefmt = b" %%-%ds " % max(len(n) for n in names)
7959 places = [_(b"external"), _(b"internal")]
7961 places = [_(b"external"), _(b"internal")]
7960 for n, v, p in zip(names, vers, isinternals):
7962 for n, v, p in zip(names, vers, isinternals):
7961 fn.startitem()
7963 fn.startitem()
7962 fn.condwrite(ui.verbose, b"name", namefmt, n)
7964 fn.condwrite(ui.verbose, b"name", namefmt, n)
7963 if ui.verbose:
7965 if ui.verbose:
7964 fn.plain(b"%s " % places[p])
7966 fn.plain(b"%s " % places[p])
7965 fn.data(bundled=p)
7967 fn.data(bundled=p)
7966 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7968 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7967 if ui.verbose:
7969 if ui.verbose:
7968 fn.plain(b"\n")
7970 fn.plain(b"\n")
7969 fn.end()
7971 fn.end()
7970 fm.end()
7972 fm.end()
7971
7973
7972
7974
7973 def loadcmdtable(ui, name, cmdtable):
7975 def loadcmdtable(ui, name, cmdtable):
7974 """Load command functions from specified cmdtable"""
7976 """Load command functions from specified cmdtable"""
7975 overrides = [cmd for cmd in cmdtable if cmd in table]
7977 overrides = [cmd for cmd in cmdtable if cmd in table]
7976 if overrides:
7978 if overrides:
7977 ui.warn(
7979 ui.warn(
7978 _(b"extension '%s' overrides commands: %s\n")
7980 _(b"extension '%s' overrides commands: %s\n")
7979 % (name, b" ".join(overrides))
7981 % (name, b" ".join(overrides))
7980 )
7982 )
7981 table.update(cmdtable)
7983 table.update(cmdtable)
@@ -1,972 +1,979 b''
1 #testcases dirstate-v1 dirstate-v2
1 #testcases dirstate-v1 dirstate-v2
2
2
3 #if dirstate-v2
3 #if dirstate-v2
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [format]
5 > [format]
6 > use-dirstate-v2=1
6 > use-dirstate-v2=1
7 > [storage]
7 > [storage]
8 > dirstate-v2.slow-path=allow
8 > dirstate-v2.slow-path=allow
9 > EOF
9 > EOF
10 #endif
10 #endif
11
11
12 $ hg init repo1
12 $ hg init repo1
13 $ cd repo1
13 $ cd repo1
14 $ mkdir a b a/1 b/1 b/2
14 $ mkdir a b a/1 b/1 b/2
15 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
15 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
16
16
17 hg status in repo root:
17 hg status in repo root:
18
18
19 $ hg status
19 $ hg status
20 ? a/1/in_a_1
20 ? a/1/in_a_1
21 ? a/in_a
21 ? a/in_a
22 ? b/1/in_b_1
22 ? b/1/in_b_1
23 ? b/2/in_b_2
23 ? b/2/in_b_2
24 ? b/in_b
24 ? b/in_b
25 ? in_root
25 ? in_root
26
26
27 hg status . in repo root:
27 hg status . in repo root:
28
28
29 $ hg status .
29 $ hg status .
30 ? a/1/in_a_1
30 ? a/1/in_a_1
31 ? a/in_a
31 ? a/in_a
32 ? b/1/in_b_1
32 ? b/1/in_b_1
33 ? b/2/in_b_2
33 ? b/2/in_b_2
34 ? b/in_b
34 ? b/in_b
35 ? in_root
35 ? in_root
36
36
37 $ hg status --cwd a
37 $ hg status --cwd a
38 ? a/1/in_a_1
38 ? a/1/in_a_1
39 ? a/in_a
39 ? a/in_a
40 ? b/1/in_b_1
40 ? b/1/in_b_1
41 ? b/2/in_b_2
41 ? b/2/in_b_2
42 ? b/in_b
42 ? b/in_b
43 ? in_root
43 ? in_root
44 $ hg status --cwd a .
44 $ hg status --cwd a .
45 ? 1/in_a_1
45 ? 1/in_a_1
46 ? in_a
46 ? in_a
47 $ hg status --cwd a ..
47 $ hg status --cwd a ..
48 ? 1/in_a_1
48 ? 1/in_a_1
49 ? in_a
49 ? in_a
50 ? ../b/1/in_b_1
50 ? ../b/1/in_b_1
51 ? ../b/2/in_b_2
51 ? ../b/2/in_b_2
52 ? ../b/in_b
52 ? ../b/in_b
53 ? ../in_root
53 ? ../in_root
54
54
55 $ hg status --cwd b
55 $ hg status --cwd b
56 ? a/1/in_a_1
56 ? a/1/in_a_1
57 ? a/in_a
57 ? a/in_a
58 ? b/1/in_b_1
58 ? b/1/in_b_1
59 ? b/2/in_b_2
59 ? b/2/in_b_2
60 ? b/in_b
60 ? b/in_b
61 ? in_root
61 ? in_root
62 $ hg status --cwd b .
62 $ hg status --cwd b .
63 ? 1/in_b_1
63 ? 1/in_b_1
64 ? 2/in_b_2
64 ? 2/in_b_2
65 ? in_b
65 ? in_b
66 $ hg status --cwd b ..
66 $ hg status --cwd b ..
67 ? ../a/1/in_a_1
67 ? ../a/1/in_a_1
68 ? ../a/in_a
68 ? ../a/in_a
69 ? 1/in_b_1
69 ? 1/in_b_1
70 ? 2/in_b_2
70 ? 2/in_b_2
71 ? in_b
71 ? in_b
72 ? ../in_root
72 ? ../in_root
73
73
74 $ hg status --cwd a/1
74 $ hg status --cwd a/1
75 ? a/1/in_a_1
75 ? a/1/in_a_1
76 ? a/in_a
76 ? a/in_a
77 ? b/1/in_b_1
77 ? b/1/in_b_1
78 ? b/2/in_b_2
78 ? b/2/in_b_2
79 ? b/in_b
79 ? b/in_b
80 ? in_root
80 ? in_root
81 $ hg status --cwd a/1 .
81 $ hg status --cwd a/1 .
82 ? in_a_1
82 ? in_a_1
83 $ hg status --cwd a/1 ..
83 $ hg status --cwd a/1 ..
84 ? in_a_1
84 ? in_a_1
85 ? ../in_a
85 ? ../in_a
86
86
87 $ hg status --cwd b/1
87 $ hg status --cwd b/1
88 ? a/1/in_a_1
88 ? a/1/in_a_1
89 ? a/in_a
89 ? a/in_a
90 ? b/1/in_b_1
90 ? b/1/in_b_1
91 ? b/2/in_b_2
91 ? b/2/in_b_2
92 ? b/in_b
92 ? b/in_b
93 ? in_root
93 ? in_root
94 $ hg status --cwd b/1 .
94 $ hg status --cwd b/1 .
95 ? in_b_1
95 ? in_b_1
96 $ hg status --cwd b/1 ..
96 $ hg status --cwd b/1 ..
97 ? in_b_1
97 ? in_b_1
98 ? ../2/in_b_2
98 ? ../2/in_b_2
99 ? ../in_b
99 ? ../in_b
100
100
101 $ hg status --cwd b/2
101 $ hg status --cwd b/2
102 ? a/1/in_a_1
102 ? a/1/in_a_1
103 ? a/in_a
103 ? a/in_a
104 ? b/1/in_b_1
104 ? b/1/in_b_1
105 ? b/2/in_b_2
105 ? b/2/in_b_2
106 ? b/in_b
106 ? b/in_b
107 ? in_root
107 ? in_root
108 $ hg status --cwd b/2 .
108 $ hg status --cwd b/2 .
109 ? in_b_2
109 ? in_b_2
110 $ hg status --cwd b/2 ..
110 $ hg status --cwd b/2 ..
111 ? ../1/in_b_1
111 ? ../1/in_b_1
112 ? in_b_2
112 ? in_b_2
113 ? ../in_b
113 ? ../in_b
114
114
115 combining patterns with root and patterns without a root works
115 combining patterns with root and patterns without a root works
116
116
117 $ hg st a/in_a re:.*b$
117 $ hg st a/in_a re:.*b$
118 ? a/in_a
118 ? a/in_a
119 ? b/in_b
119 ? b/in_b
120
120
121 tweaking defaults works
121 tweaking defaults works
122 $ hg status --cwd a --config ui.tweakdefaults=yes
122 $ hg status --cwd a --config ui.tweakdefaults=yes
123 ? 1/in_a_1
123 ? 1/in_a_1
124 ? in_a
124 ? in_a
125 ? ../b/1/in_b_1
125 ? ../b/1/in_b_1
126 ? ../b/2/in_b_2
126 ? ../b/2/in_b_2
127 ? ../b/in_b
127 ? ../b/in_b
128 ? ../in_root
128 ? ../in_root
129 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
129 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
130 ? a/1/in_a_1 (glob)
130 ? a/1/in_a_1 (glob)
131 ? a/in_a (glob)
131 ? a/in_a (glob)
132 ? b/1/in_b_1 (glob)
132 ? b/1/in_b_1 (glob)
133 ? b/2/in_b_2 (glob)
133 ? b/2/in_b_2 (glob)
134 ? b/in_b (glob)
134 ? b/in_b (glob)
135 ? in_root
135 ? in_root
136 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
136 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
137 ? 1/in_a_1
137 ? 1/in_a_1
138 ? in_a
138 ? in_a
139 ? ../b/1/in_b_1
139 ? ../b/1/in_b_1
140 ? ../b/2/in_b_2
140 ? ../b/2/in_b_2
141 ? ../b/in_b
141 ? ../b/in_b
142 ? ../in_root (glob)
142 ? ../in_root (glob)
143
143
144 relative paths can be requested
144 relative paths can be requested
145
145
146 $ hg status --cwd a --config ui.relative-paths=yes
146 $ hg status --cwd a --config ui.relative-paths=yes
147 ? 1/in_a_1
147 ? 1/in_a_1
148 ? in_a
148 ? in_a
149 ? ../b/1/in_b_1
149 ? ../b/1/in_b_1
150 ? ../b/2/in_b_2
150 ? ../b/2/in_b_2
151 ? ../b/in_b
151 ? ../b/in_b
152 ? ../in_root
152 ? ../in_root
153
153
154 $ hg status --cwd a . --config ui.relative-paths=legacy
154 $ hg status --cwd a . --config ui.relative-paths=legacy
155 ? 1/in_a_1
155 ? 1/in_a_1
156 ? in_a
156 ? in_a
157 $ hg status --cwd a . --config ui.relative-paths=no
157 $ hg status --cwd a . --config ui.relative-paths=no
158 ? a/1/in_a_1
158 ? a/1/in_a_1
159 ? a/in_a
159 ? a/in_a
160
160
161 commands.status.relative overrides ui.relative-paths
161 commands.status.relative overrides ui.relative-paths
162
162
163 $ cat >> $HGRCPATH <<EOF
163 $ cat >> $HGRCPATH <<EOF
164 > [ui]
164 > [ui]
165 > relative-paths = False
165 > relative-paths = False
166 > [commands]
166 > [commands]
167 > status.relative = True
167 > status.relative = True
168 > EOF
168 > EOF
169 $ hg status --cwd a
169 $ hg status --cwd a
170 ? 1/in_a_1
170 ? 1/in_a_1
171 ? in_a
171 ? in_a
172 ? ../b/1/in_b_1
172 ? ../b/1/in_b_1
173 ? ../b/2/in_b_2
173 ? ../b/2/in_b_2
174 ? ../b/in_b
174 ? ../b/in_b
175 ? ../in_root
175 ? ../in_root
176 $ HGPLAIN=1 hg status --cwd a
176 $ HGPLAIN=1 hg status --cwd a
177 ? a/1/in_a_1 (glob)
177 ? a/1/in_a_1 (glob)
178 ? a/in_a (glob)
178 ? a/in_a (glob)
179 ? b/1/in_b_1 (glob)
179 ? b/1/in_b_1 (glob)
180 ? b/2/in_b_2 (glob)
180 ? b/2/in_b_2 (glob)
181 ? b/in_b (glob)
181 ? b/in_b (glob)
182 ? in_root
182 ? in_root
183
183
184 if relative paths are explicitly off, tweakdefaults doesn't change it
184 if relative paths are explicitly off, tweakdefaults doesn't change it
185 $ cat >> $HGRCPATH <<EOF
185 $ cat >> $HGRCPATH <<EOF
186 > [commands]
186 > [commands]
187 > status.relative = False
187 > status.relative = False
188 > EOF
188 > EOF
189 $ hg status --cwd a --config ui.tweakdefaults=yes
189 $ hg status --cwd a --config ui.tweakdefaults=yes
190 ? a/1/in_a_1
190 ? a/1/in_a_1
191 ? a/in_a
191 ? a/in_a
192 ? b/1/in_b_1
192 ? b/1/in_b_1
193 ? b/2/in_b_2
193 ? b/2/in_b_2
194 ? b/in_b
194 ? b/in_b
195 ? in_root
195 ? in_root
196
196
197 $ cd ..
197 $ cd ..
198
198
199 $ hg init repo2
199 $ hg init repo2
200 $ cd repo2
200 $ cd repo2
201 $ touch modified removed deleted ignored
201 $ touch modified removed deleted ignored
202 $ echo "^ignored$" > .hgignore
202 $ echo "^ignored$" > .hgignore
203 $ hg ci -A -m 'initial checkin'
203 $ hg ci -A -m 'initial checkin'
204 adding .hgignore
204 adding .hgignore
205 adding deleted
205 adding deleted
206 adding modified
206 adding modified
207 adding removed
207 adding removed
208 $ touch modified added unknown ignored
208 $ touch modified added unknown ignored
209 $ hg add added
209 $ hg add added
210 $ hg remove removed
210 $ hg remove removed
211 $ rm deleted
211 $ rm deleted
212
212
213 hg status:
213 hg status:
214
214
215 $ hg status
215 $ hg status
216 A added
216 A added
217 R removed
217 R removed
218 ! deleted
218 ! deleted
219 ? unknown
219 ? unknown
220
220
221 hg status -n:
221 hg status -n:
222 $ env RHG_ON_UNSUPPORTED=abort hg status -n
222 $ env RHG_ON_UNSUPPORTED=abort hg status -n
223 added
223 added
224 removed
224 removed
225 deleted
225 deleted
226 unknown
226 unknown
227
227
228 hg status modified added removed deleted unknown never-existed ignored:
228 hg status modified added removed deleted unknown never-existed ignored:
229
229
230 $ hg status modified added removed deleted unknown never-existed ignored
230 $ hg status modified added removed deleted unknown never-existed ignored
231 never-existed: * (glob)
231 never-existed: * (glob)
232 A added
232 A added
233 R removed
233 R removed
234 ! deleted
234 ! deleted
235 ? unknown
235 ? unknown
236
236
237 $ hg copy modified copied
237 $ hg copy modified copied
238
238
239 hg status -C:
239 hg status -C:
240
240
241 $ hg status -C
241 $ hg status -C
242 A added
242 A added
243 A copied
243 A copied
244 modified
244 modified
245 R removed
245 R removed
246 ! deleted
246 ! deleted
247 ? unknown
247 ? unknown
248
248
249 hg status -A:
249 hg status -A:
250
250
251 $ hg status -A
251 $ hg status -A
252 A added
252 A added
253 A copied
253 A copied
254 modified
254 modified
255 R removed
255 R removed
256 ! deleted
256 ! deleted
257 ? unknown
257 ? unknown
258 I ignored
258 I ignored
259 C .hgignore
259 C .hgignore
260 C modified
260 C modified
261
261
262 $ hg status -A -T '{status} {path} {node|shortest}\n'
262 $ hg status -A -T '{status} {path} {node|shortest}\n'
263 A added ffff
263 A added ffff
264 A copied ffff
264 A copied ffff
265 R removed ffff
265 R removed ffff
266 ! deleted ffff
266 ! deleted ffff
267 ? unknown ffff
267 ? unknown ffff
268 I ignored ffff
268 I ignored ffff
269 C .hgignore ffff
269 C .hgignore ffff
270 C modified ffff
270 C modified ffff
271
271
272 $ hg status -A -Tjson
272 $ hg status -A -Tjson
273 [
273 [
274 {
274 {
275 "itemtype": "file",
275 "itemtype": "file",
276 "path": "added",
276 "path": "added",
277 "status": "A"
277 "status": "A"
278 },
278 },
279 {
279 {
280 "itemtype": "file",
280 "itemtype": "file",
281 "path": "copied",
281 "path": "copied",
282 "source": "modified",
282 "source": "modified",
283 "status": "A"
283 "status": "A"
284 },
284 },
285 {
285 {
286 "itemtype": "file",
286 "itemtype": "file",
287 "path": "removed",
287 "path": "removed",
288 "status": "R"
288 "status": "R"
289 },
289 },
290 {
290 {
291 "itemtype": "file",
291 "itemtype": "file",
292 "path": "deleted",
292 "path": "deleted",
293 "status": "!"
293 "status": "!"
294 },
294 },
295 {
295 {
296 "itemtype": "file",
296 "itemtype": "file",
297 "path": "unknown",
297 "path": "unknown",
298 "status": "?"
298 "status": "?"
299 },
299 },
300 {
300 {
301 "itemtype": "file",
301 "itemtype": "file",
302 "path": "ignored",
302 "path": "ignored",
303 "status": "I"
303 "status": "I"
304 },
304 },
305 {
305 {
306 "itemtype": "file",
306 "itemtype": "file",
307 "path": ".hgignore",
307 "path": ".hgignore",
308 "status": "C"
308 "status": "C"
309 },
309 },
310 {
310 {
311 "itemtype": "file",
311 "itemtype": "file",
312 "path": "modified",
312 "path": "modified",
313 "status": "C"
313 "status": "C"
314 }
314 }
315 ]
315 ]
316
316
317 $ hg status -A -Tpickle > pickle
317 $ hg status -A -Tpickle > pickle
318 >>> import pickle
318 >>> import pickle
319 >>> from mercurial import util
319 >>> from mercurial import util
320 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
320 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
321 >>> for s, p in data: print("%s %s" % (s, p))
321 >>> for s, p in data: print("%s %s" % (s, p))
322 ! deleted
322 ! deleted
323 ? pickle
323 ? pickle
324 ? unknown
324 ? unknown
325 A added
325 A added
326 A copied
326 A copied
327 C .hgignore
327 C .hgignore
328 C modified
328 C modified
329 I ignored
329 I ignored
330 R removed
330 R removed
331 $ rm pickle
331 $ rm pickle
332
332
333 $ echo "^ignoreddir$" > .hgignore
333 $ echo "^ignoreddir$" > .hgignore
334 $ mkdir ignoreddir
334 $ mkdir ignoreddir
335 $ touch ignoreddir/file
335 $ touch ignoreddir/file
336
336
337 Test templater support:
337 Test templater support:
338
338
339 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
339 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
340 [M] .hgignore
340 [M] .hgignore
341 [A] added
341 [A] added
342 [A] modified -> copied
342 [A] modified -> copied
343 [R] removed
343 [R] removed
344 [!] deleted
344 [!] deleted
345 [?] ignored
345 [?] ignored
346 [?] unknown
346 [?] unknown
347 [I] ignoreddir/file
347 [I] ignoreddir/file
348 [C] modified
348 [C] modified
349 $ hg status -AT default
349 $ hg status -AT default
350 M .hgignore
350 M .hgignore
351 A added
351 A added
352 A copied
352 A copied
353 modified
353 modified
354 R removed
354 R removed
355 ! deleted
355 ! deleted
356 ? ignored
356 ? ignored
357 ? unknown
357 ? unknown
358 I ignoreddir/file
358 I ignoreddir/file
359 C modified
359 C modified
360 $ hg status -T compact
360 $ hg status -T compact
361 abort: "status" not in template map
361 abort: "status" not in template map
362 [255]
362 [255]
363
363
364 hg status ignoreddir/file:
364 hg status ignoreddir/file:
365
365
366 $ hg status ignoreddir/file
366 $ hg status ignoreddir/file
367
367
368 hg status -i ignoreddir/file:
368 hg status -i ignoreddir/file:
369
369
370 $ hg status -i ignoreddir/file
370 $ hg status -i ignoreddir/file
371 I ignoreddir/file
371 I ignoreddir/file
372 $ cd ..
372 $ cd ..
373
373
374 Check 'status -q' and some combinations
374 Check 'status -q' and some combinations
375
375
376 $ hg init repo3
376 $ hg init repo3
377 $ cd repo3
377 $ cd repo3
378 $ touch modified removed deleted ignored
378 $ touch modified removed deleted ignored
379 $ echo "^ignored$" > .hgignore
379 $ echo "^ignored$" > .hgignore
380 $ hg commit -A -m 'initial checkin'
380 $ hg commit -A -m 'initial checkin'
381 adding .hgignore
381 adding .hgignore
382 adding deleted
382 adding deleted
383 adding modified
383 adding modified
384 adding removed
384 adding removed
385 $ touch added unknown ignored
385 $ touch added unknown ignored
386 $ hg add added
386 $ hg add added
387 $ echo "test" >> modified
387 $ echo "test" >> modified
388 $ hg remove removed
388 $ hg remove removed
389 $ rm deleted
389 $ rm deleted
390 $ hg copy modified copied
390 $ hg copy modified copied
391
391
392 Specify working directory revision explicitly, that should be the same as
392 Specify working directory revision explicitly, that should be the same as
393 "hg status"
393 "hg status"
394
394
395 $ hg status --change "wdir()"
395 $ hg status --change "wdir()"
396 M modified
396 M modified
397 A added
397 A added
398 A copied
398 A copied
399 R removed
399 R removed
400 ! deleted
400 ! deleted
401 ? unknown
401 ? unknown
402
402
403 Run status with 2 different flags.
403 Run status with 2 different flags.
404 Check if result is the same or different.
404 Check if result is the same or different.
405 If result is not as expected, raise error
405 If result is not as expected, raise error
406
406
407 $ assert() {
407 $ assert() {
408 > hg status $1 > ../a
408 > hg status $1 > ../a
409 > hg status $2 > ../b
409 > hg status $2 > ../b
410 > if diff ../a ../b > /dev/null; then
410 > if diff ../a ../b > /dev/null; then
411 > out=0
411 > out=0
412 > else
412 > else
413 > out=1
413 > out=1
414 > fi
414 > fi
415 > if [ $3 -eq 0 ]; then
415 > if [ $3 -eq 0 ]; then
416 > df="same"
416 > df="same"
417 > else
417 > else
418 > df="different"
418 > df="different"
419 > fi
419 > fi
420 > if [ $out -ne $3 ]; then
420 > if [ $out -ne $3 ]; then
421 > echo "Error on $1 and $2, should be $df."
421 > echo "Error on $1 and $2, should be $df."
422 > fi
422 > fi
423 > }
423 > }
424
424
425 Assert flag1 flag2 [0-same | 1-different]
425 Assert flag1 flag2 [0-same | 1-different]
426
426
427 $ assert "-q" "-mard" 0
427 $ assert "-q" "-mard" 0
428 $ assert "-A" "-marduicC" 0
428 $ assert "-A" "-marduicC" 0
429 $ assert "-qA" "-mardcC" 0
429 $ assert "-qA" "-mardcC" 0
430 $ assert "-qAui" "-A" 0
430 $ assert "-qAui" "-A" 0
431 $ assert "-qAu" "-marducC" 0
431 $ assert "-qAu" "-marducC" 0
432 $ assert "-qAi" "-mardicC" 0
432 $ assert "-qAi" "-mardicC" 0
433 $ assert "-qu" "-u" 0
433 $ assert "-qu" "-u" 0
434 $ assert "-q" "-u" 1
434 $ assert "-q" "-u" 1
435 $ assert "-m" "-a" 1
435 $ assert "-m" "-a" 1
436 $ assert "-r" "-d" 1
436 $ assert "-r" "-d" 1
437 $ cd ..
437 $ cd ..
438
438
439 $ hg init repo4
439 $ hg init repo4
440 $ cd repo4
440 $ cd repo4
441 $ touch modified removed deleted
441 $ touch modified removed deleted
442 $ hg ci -q -A -m 'initial checkin'
442 $ hg ci -q -A -m 'initial checkin'
443 $ touch added unknown
443 $ touch added unknown
444 $ hg add added
444 $ hg add added
445 $ hg remove removed
445 $ hg remove removed
446 $ rm deleted
446 $ rm deleted
447 $ echo x > modified
447 $ echo x > modified
448 $ hg copy modified copied
448 $ hg copy modified copied
449 $ hg ci -m 'test checkin' -d "1000001 0"
449 $ hg ci -m 'test checkin' -d "1000001 0"
450 $ rm *
450 $ rm *
451 $ touch unrelated
451 $ touch unrelated
452 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
452 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
453
453
454 hg status --change 1:
454 hg status --change 1:
455
455
456 $ hg status --change 1
456 $ hg status --change 1
457 M modified
457 M modified
458 A added
458 A added
459 A copied
459 A copied
460 R removed
460 R removed
461
461
462 hg status --change 1 unrelated:
462 hg status --change 1 unrelated:
463
463
464 $ hg status --change 1 unrelated
464 $ hg status --change 1 unrelated
465
465
466 hg status -C --change 1 added modified copied removed deleted:
466 hg status -C --change 1 added modified copied removed deleted:
467
467
468 $ hg status -C --change 1 added modified copied removed deleted
468 $ hg status -C --change 1 added modified copied removed deleted
469 M modified
469 M modified
470 A added
470 A added
471 A copied
471 A copied
472 modified
472 modified
473 R removed
473 R removed
474
474
475 hg status -A --change 1 and revset:
475 hg status -A --change 1 and revset:
476
476
477 $ hg status -A --change '1|1'
477 $ hg status -A --change '1|1'
478 M modified
478 M modified
479 A added
479 A added
480 A copied
480 A copied
481 modified
481 modified
482 R removed
482 R removed
483 C deleted
483 C deleted
484
484
485 $ cd ..
485 $ cd ..
486
486
487 hg status with --rev and reverted changes:
487 hg status with --rev and reverted changes:
488
488
489 $ hg init reverted-changes-repo
489 $ hg init reverted-changes-repo
490 $ cd reverted-changes-repo
490 $ cd reverted-changes-repo
491 $ echo a > file
491 $ echo a > file
492 $ hg add file
492 $ hg add file
493 $ hg ci -m a
493 $ hg ci -m a
494 $ echo b > file
494 $ echo b > file
495 $ hg ci -m b
495 $ hg ci -m b
496
496
497 reverted file should appear clean
497 reverted file should appear clean
498
498
499 $ hg revert -r 0 .
499 $ hg revert -r 0 .
500 reverting file
500 reverting file
501 $ hg status -A --rev 0
501 $ hg status -A --rev 0
502 C file
502 C file
503
503
504 #if execbit
504 #if execbit
505 reverted file with changed flag should appear modified
505 reverted file with changed flag should appear modified
506
506
507 $ chmod +x file
507 $ chmod +x file
508 $ hg status -A --rev 0
508 $ hg status -A --rev 0
509 M file
509 M file
510
510
511 $ hg revert -r 0 .
511 $ hg revert -r 0 .
512 reverting file
512 reverting file
513
513
514 reverted and committed file with changed flag should appear modified
514 reverted and committed file with changed flag should appear modified
515
515
516 $ hg co -C .
516 $ hg co -C .
517 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
518 $ chmod +x file
518 $ chmod +x file
519 $ hg ci -m 'change flag'
519 $ hg ci -m 'change flag'
520 $ hg status -A --rev 1 --rev 2
520 $ hg status -A --rev 1 --rev 2
521 M file
521 M file
522 $ hg diff -r 1 -r 2
522 $ hg diff -r 1 -r 2
523
523
524 #endif
524 #endif
525
525
526 $ cd ..
526 $ cd ..
527
527
528 hg status of binary file starting with '\1\n', a separator for metadata:
528 hg status of binary file starting with '\1\n', a separator for metadata:
529
529
530 $ hg init repo5
530 $ hg init repo5
531 $ cd repo5
531 $ cd repo5
532 >>> open("010a", r"wb").write(b"\1\nfoo") and None
532 >>> open("010a", r"wb").write(b"\1\nfoo") and None
533 $ hg ci -q -A -m 'initial checkin'
533 $ hg ci -q -A -m 'initial checkin'
534 $ hg status -A
534 $ hg status -A
535 C 010a
535 C 010a
536
536
537 >>> open("010a", r"wb").write(b"\1\nbar") and None
537 >>> open("010a", r"wb").write(b"\1\nbar") and None
538 $ hg status -A
538 $ hg status -A
539 M 010a
539 M 010a
540 $ hg ci -q -m 'modify 010a'
540 $ hg ci -q -m 'modify 010a'
541 $ hg status -A --rev 0:1
541 $ hg status -A --rev 0:1
542 M 010a
542 M 010a
543
543
544 $ touch empty
544 $ touch empty
545 $ hg ci -q -A -m 'add another file'
545 $ hg ci -q -A -m 'add another file'
546 $ hg status -A --rev 1:2 010a
546 $ hg status -A --rev 1:2 010a
547 C 010a
547 C 010a
548
548
549 $ cd ..
549 $ cd ..
550
550
551 test "hg status" with "directory pattern" which matches against files
551 test "hg status" with "directory pattern" which matches against files
552 only known on target revision.
552 only known on target revision.
553
553
554 $ hg init repo6
554 $ hg init repo6
555 $ cd repo6
555 $ cd repo6
556
556
557 $ echo a > a.txt
557 $ echo a > a.txt
558 $ hg add a.txt
558 $ hg add a.txt
559 $ hg commit -m '#0'
559 $ hg commit -m '#0'
560 $ mkdir -p 1/2/3/4/5
560 $ mkdir -p 1/2/3/4/5
561 $ echo b > 1/2/3/4/5/b.txt
561 $ echo b > 1/2/3/4/5/b.txt
562 $ hg add 1/2/3/4/5/b.txt
562 $ hg add 1/2/3/4/5/b.txt
563 $ hg commit -m '#1'
563 $ hg commit -m '#1'
564
564
565 $ hg update -C 0 > /dev/null
565 $ hg update -C 0 > /dev/null
566 $ hg status -A
566 $ hg status -A
567 C a.txt
567 C a.txt
568
568
569 the directory matching against specified pattern should be removed,
569 the directory matching against specified pattern should be removed,
570 because directory existence prevents 'dirstate.walk()' from showing
570 because directory existence prevents 'dirstate.walk()' from showing
571 warning message about such pattern.
571 warning message about such pattern.
572
572
573 $ test ! -d 1
573 $ test ! -d 1
574 $ hg status -A --rev 1 1/2/3/4/5/b.txt
574 $ hg status -A --rev 1 1/2/3/4/5/b.txt
575 R 1/2/3/4/5/b.txt
575 R 1/2/3/4/5/b.txt
576 $ hg status -A --rev 1 1/2/3/4/5
576 $ hg status -A --rev 1 1/2/3/4/5
577 R 1/2/3/4/5/b.txt
577 R 1/2/3/4/5/b.txt
578 $ hg status -A --rev 1 1/2/3
578 $ hg status -A --rev 1 1/2/3
579 R 1/2/3/4/5/b.txt
579 R 1/2/3/4/5/b.txt
580 $ hg status -A --rev 1 1
580 $ hg status -A --rev 1 1
581 R 1/2/3/4/5/b.txt
581 R 1/2/3/4/5/b.txt
582
582
583 $ hg status --config ui.formatdebug=True --rev 1 1
583 $ hg status --config ui.formatdebug=True --rev 1 1
584 status = [
584 status = [
585 {
585 {
586 'itemtype': 'file',
586 'itemtype': 'file',
587 'path': '1/2/3/4/5/b.txt',
587 'path': '1/2/3/4/5/b.txt',
588 'status': 'R'
588 'status': 'R'
589 },
589 },
590 ]
590 ]
591
591
592 #if windows
592 #if windows
593 $ hg --config ui.slash=false status -A --rev 1 1
593 $ hg --config ui.slash=false status -A --rev 1 1
594 R 1\2\3\4\5\b.txt
594 R 1\2\3\4\5\b.txt
595 #endif
595 #endif
596
596
597 $ cd ..
597 $ cd ..
598
598
599 Status after move overwriting a file (issue4458)
599 Status after move overwriting a file (issue4458)
600 =================================================
600 =================================================
601
601
602
602
603 $ hg init issue4458
603 $ hg init issue4458
604 $ cd issue4458
604 $ cd issue4458
605 $ echo a > a
605 $ echo a > a
606 $ echo b > b
606 $ echo b > b
607 $ hg commit -Am base
607 $ hg commit -Am base
608 adding a
608 adding a
609 adding b
609 adding b
610
610
611
611
612 with --force
612 with --force
613
613
614 $ hg mv b --force a
614 $ hg mv b --force a
615 $ hg st --copies
615 $ hg st --copies
616 M a
616 M a
617 b
617 b
618 R b
618 R b
619 $ hg revert --all
619 $ hg revert --all
620 reverting a
620 reverting a
621 undeleting b
621 undeleting b
622 $ rm *.orig
622 $ rm *.orig
623
623
624 without force
624 without force
625
625
626 $ hg rm a
626 $ hg rm a
627 $ hg st --copies
627 $ hg st --copies
628 R a
628 R a
629 $ hg mv b a
629 $ hg mv b a
630 $ hg st --copies
630 $ hg st --copies
631 M a
631 M a
632 b
632 b
633 R b
633 R b
634
634
635 using ui.statuscopies setting
635 using ui.statuscopies setting
636 $ hg st --config ui.statuscopies=true
636 $ hg st --config ui.statuscopies=true
637 M a
637 M a
638 b
638 b
639 R b
639 R b
640 $ hg st --config ui.statuscopies=true --no-copies
641 M a
642 R b
640 $ hg st --config ui.statuscopies=false
643 $ hg st --config ui.statuscopies=false
641 M a
644 M a
642 R b
645 R b
646 $ hg st --config ui.statuscopies=false --copies
647 M a
648 b
649 R b
643 $ hg st --config ui.tweakdefaults=yes
650 $ hg st --config ui.tweakdefaults=yes
644 M a
651 M a
645 b
652 b
646 R b
653 R b
647
654
648 using log status template (issue5155)
655 using log status template (issue5155)
649 $ hg log -Tstatus -r 'wdir()' -C
656 $ hg log -Tstatus -r 'wdir()' -C
650 changeset: 2147483647:ffffffffffff
657 changeset: 2147483647:ffffffffffff
651 parent: 0:8c55c58b4c0e
658 parent: 0:8c55c58b4c0e
652 user: test
659 user: test
653 date: * (glob)
660 date: * (glob)
654 files:
661 files:
655 M a
662 M a
656 b
663 b
657 R b
664 R b
658
665
659 $ hg log -GTstatus -r 'wdir()' -C
666 $ hg log -GTstatus -r 'wdir()' -C
660 o changeset: 2147483647:ffffffffffff
667 o changeset: 2147483647:ffffffffffff
661 | parent: 0:8c55c58b4c0e
668 | parent: 0:8c55c58b4c0e
662 ~ user: test
669 ~ user: test
663 date: * (glob)
670 date: * (glob)
664 files:
671 files:
665 M a
672 M a
666 b
673 b
667 R b
674 R b
668
675
669
676
670 Other "bug" highlight, the revision status does not report the copy information.
677 Other "bug" highlight, the revision status does not report the copy information.
671 This is buggy behavior.
678 This is buggy behavior.
672
679
673 $ hg commit -m 'blah'
680 $ hg commit -m 'blah'
674 $ hg st --copies --change .
681 $ hg st --copies --change .
675 M a
682 M a
676 R b
683 R b
677
684
678 using log status template, the copy information is displayed correctly.
685 using log status template, the copy information is displayed correctly.
679 $ hg log -Tstatus -r. -C
686 $ hg log -Tstatus -r. -C
680 changeset: 1:6685fde43d21
687 changeset: 1:6685fde43d21
681 tag: tip
688 tag: tip
682 user: test
689 user: test
683 date: * (glob)
690 date: * (glob)
684 summary: blah
691 summary: blah
685 files:
692 files:
686 M a
693 M a
687 b
694 b
688 R b
695 R b
689
696
690
697
691 $ cd ..
698 $ cd ..
692
699
693 Make sure .hg doesn't show up even as a symlink
700 Make sure .hg doesn't show up even as a symlink
694
701
695 $ hg init repo0
702 $ hg init repo0
696 $ mkdir symlink-repo0
703 $ mkdir symlink-repo0
697 $ cd symlink-repo0
704 $ cd symlink-repo0
698 $ ln -s ../repo0/.hg
705 $ ln -s ../repo0/.hg
699 $ hg status
706 $ hg status
700
707
701 If the size hasnt changed but mtime has, status needs to read the contents
708 If the size hasnt changed but mtime has, status needs to read the contents
702 of the file to check whether it has changed
709 of the file to check whether it has changed
703
710
704 $ echo 1 > a
711 $ echo 1 > a
705 $ echo 1 > b
712 $ echo 1 > b
706 $ touch -t 200102030000 a b
713 $ touch -t 200102030000 a b
707 $ hg commit -Aqm '#0'
714 $ hg commit -Aqm '#0'
708 $ echo 2 > a
715 $ echo 2 > a
709 $ touch -t 200102040000 a b
716 $ touch -t 200102040000 a b
710 $ hg status
717 $ hg status
711 M a
718 M a
712
719
713 Asking specifically for the status of a deleted/removed file
720 Asking specifically for the status of a deleted/removed file
714
721
715 $ rm a
722 $ rm a
716 $ rm b
723 $ rm b
717 $ hg status a
724 $ hg status a
718 ! a
725 ! a
719 $ hg rm a
726 $ hg rm a
720 $ hg rm b
727 $ hg rm b
721 $ hg status a
728 $ hg status a
722 R a
729 R a
723 $ hg commit -qm '#1'
730 $ hg commit -qm '#1'
724 $ hg status a
731 $ hg status a
725 a: $ENOENT$
732 a: $ENOENT$
726
733
727 Check using include flag with pattern when status does not need to traverse
734 Check using include flag with pattern when status does not need to traverse
728 the working directory (issue6483)
735 the working directory (issue6483)
729
736
730 $ cd ..
737 $ cd ..
731 $ hg init issue6483
738 $ hg init issue6483
732 $ cd issue6483
739 $ cd issue6483
733 $ touch a.py b.rs
740 $ touch a.py b.rs
734 $ hg add a.py b.rs
741 $ hg add a.py b.rs
735 $ hg st -aI "*.py"
742 $ hg st -aI "*.py"
736 A a.py
743 A a.py
737
744
738 Also check exclude pattern
745 Also check exclude pattern
739
746
740 $ hg st -aX "*.rs"
747 $ hg st -aX "*.rs"
741 A a.py
748 A a.py
742
749
743 issue6335
750 issue6335
744 When a directory containing a tracked file gets symlinked, as of 5.8
751 When a directory containing a tracked file gets symlinked, as of 5.8
745 `hg st` only gives the correct answer about clean (or deleted) files
752 `hg st` only gives the correct answer about clean (or deleted) files
746 if also listing unknowns.
753 if also listing unknowns.
747 The tree-based dirstate and status algorithm fix this:
754 The tree-based dirstate and status algorithm fix this:
748
755
749 #if symlink no-dirstate-v1 rust
756 #if symlink no-dirstate-v1 rust
750
757
751 $ cd ..
758 $ cd ..
752 $ hg init issue6335
759 $ hg init issue6335
753 $ cd issue6335
760 $ cd issue6335
754 $ mkdir foo
761 $ mkdir foo
755 $ touch foo/a
762 $ touch foo/a
756 $ hg ci -Ama
763 $ hg ci -Ama
757 adding foo/a
764 adding foo/a
758 $ mv foo bar
765 $ mv foo bar
759 $ ln -s bar foo
766 $ ln -s bar foo
760 $ hg status
767 $ hg status
761 ! foo/a
768 ! foo/a
762 ? bar/a
769 ? bar/a
763 ? foo
770 ? foo
764
771
765 $ hg status -c # incorrect output without the Rust implementation
772 $ hg status -c # incorrect output without the Rust implementation
766 $ hg status -cu
773 $ hg status -cu
767 ? bar/a
774 ? bar/a
768 ? foo
775 ? foo
769 $ hg status -d # incorrect output without the Rust implementation
776 $ hg status -d # incorrect output without the Rust implementation
770 ! foo/a
777 ! foo/a
771 $ hg status -du
778 $ hg status -du
772 ! foo/a
779 ! foo/a
773 ? bar/a
780 ? bar/a
774 ? foo
781 ? foo
775
782
776 #endif
783 #endif
777
784
778
785
779 Create a repo with files in each possible status
786 Create a repo with files in each possible status
780
787
781 $ cd ..
788 $ cd ..
782 $ hg init repo7
789 $ hg init repo7
783 $ cd repo7
790 $ cd repo7
784 $ mkdir subdir
791 $ mkdir subdir
785 $ touch clean modified deleted removed
792 $ touch clean modified deleted removed
786 $ touch subdir/clean subdir/modified subdir/deleted subdir/removed
793 $ touch subdir/clean subdir/modified subdir/deleted subdir/removed
787 $ echo ignored > .hgignore
794 $ echo ignored > .hgignore
788 $ hg ci -Aqm '#0'
795 $ hg ci -Aqm '#0'
789 $ echo 1 > modified
796 $ echo 1 > modified
790 $ echo 1 > subdir/modified
797 $ echo 1 > subdir/modified
791 $ rm deleted
798 $ rm deleted
792 $ rm subdir/deleted
799 $ rm subdir/deleted
793 $ hg rm removed
800 $ hg rm removed
794 $ hg rm subdir/removed
801 $ hg rm subdir/removed
795 $ touch unknown ignored
802 $ touch unknown ignored
796 $ touch subdir/unknown subdir/ignored
803 $ touch subdir/unknown subdir/ignored
797
804
798 Check the output
805 Check the output
799
806
800 $ hg status
807 $ hg status
801 M modified
808 M modified
802 M subdir/modified
809 M subdir/modified
803 R removed
810 R removed
804 R subdir/removed
811 R subdir/removed
805 ! deleted
812 ! deleted
806 ! subdir/deleted
813 ! subdir/deleted
807 ? subdir/unknown
814 ? subdir/unknown
808 ? unknown
815 ? unknown
809
816
810 $ hg status -mard
817 $ hg status -mard
811 M modified
818 M modified
812 M subdir/modified
819 M subdir/modified
813 R removed
820 R removed
814 R subdir/removed
821 R subdir/removed
815 ! deleted
822 ! deleted
816 ! subdir/deleted
823 ! subdir/deleted
817
824
818 $ hg status -A
825 $ hg status -A
819 M modified
826 M modified
820 M subdir/modified
827 M subdir/modified
821 R removed
828 R removed
822 R subdir/removed
829 R subdir/removed
823 ! deleted
830 ! deleted
824 ! subdir/deleted
831 ! subdir/deleted
825 ? subdir/unknown
832 ? subdir/unknown
826 ? unknown
833 ? unknown
827 I ignored
834 I ignored
828 I subdir/ignored
835 I subdir/ignored
829 C .hgignore
836 C .hgignore
830 C clean
837 C clean
831 C subdir/clean
838 C subdir/clean
832
839
833 Note: `hg status some-name` creates a patternmatcher which is not supported
840 Note: `hg status some-name` creates a patternmatcher which is not supported
834 yet by the Rust implementation of status, but includematcher is supported.
841 yet by the Rust implementation of status, but includematcher is supported.
835 --include is used below for that reason
842 --include is used below for that reason
836
843
837 #if unix-permissions
844 #if unix-permissions
838
845
839 Not having permission to read a directory that contains tracked files makes
846 Not having permission to read a directory that contains tracked files makes
840 status emit a warning then behave as if the directory was empty or removed
847 status emit a warning then behave as if the directory was empty or removed
841 entirely:
848 entirely:
842
849
843 $ chmod 0 subdir
850 $ chmod 0 subdir
844 $ hg status --include subdir
851 $ hg status --include subdir
845 subdir: Permission denied
852 subdir: Permission denied
846 R subdir/removed
853 R subdir/removed
847 ! subdir/clean
854 ! subdir/clean
848 ! subdir/deleted
855 ! subdir/deleted
849 ! subdir/modified
856 ! subdir/modified
850 $ chmod 755 subdir
857 $ chmod 755 subdir
851
858
852 #endif
859 #endif
853
860
854 Remove a directory that contains tracked files
861 Remove a directory that contains tracked files
855
862
856 $ rm -r subdir
863 $ rm -r subdir
857 $ hg status --include subdir
864 $ hg status --include subdir
858 R subdir/removed
865 R subdir/removed
859 ! subdir/clean
866 ! subdir/clean
860 ! subdir/deleted
867 ! subdir/deleted
861 ! subdir/modified
868 ! subdir/modified
862
869
863 and replace it by a file
870 and replace it by a file
864
871
865 $ touch subdir
872 $ touch subdir
866 $ hg status --include subdir
873 $ hg status --include subdir
867 R subdir/removed
874 R subdir/removed
868 ! subdir/clean
875 ! subdir/clean
869 ! subdir/deleted
876 ! subdir/deleted
870 ! subdir/modified
877 ! subdir/modified
871 ? subdir
878 ? subdir
872
879
873 Replaced a deleted or removed file with a directory
880 Replaced a deleted or removed file with a directory
874
881
875 $ mkdir deleted removed
882 $ mkdir deleted removed
876 $ touch deleted/1 removed/1
883 $ touch deleted/1 removed/1
877 $ hg status --include deleted --include removed
884 $ hg status --include deleted --include removed
878 R removed
885 R removed
879 ! deleted
886 ! deleted
880 ? deleted/1
887 ? deleted/1
881 ? removed/1
888 ? removed/1
882 $ hg add removed/1
889 $ hg add removed/1
883 $ hg status --include deleted --include removed
890 $ hg status --include deleted --include removed
884 A removed/1
891 A removed/1
885 R removed
892 R removed
886 ! deleted
893 ! deleted
887 ? deleted/1
894 ? deleted/1
888
895
889 Deeply nested files in an ignored directory are still listed on request
896 Deeply nested files in an ignored directory are still listed on request
890
897
891 $ echo ignored-dir >> .hgignore
898 $ echo ignored-dir >> .hgignore
892 $ mkdir ignored-dir
899 $ mkdir ignored-dir
893 $ mkdir ignored-dir/subdir
900 $ mkdir ignored-dir/subdir
894 $ touch ignored-dir/subdir/1
901 $ touch ignored-dir/subdir/1
895 $ hg status --ignored
902 $ hg status --ignored
896 I ignored
903 I ignored
897 I ignored-dir/subdir/1
904 I ignored-dir/subdir/1
898
905
899 Check using include flag while listing ignored composes correctly (issue6514)
906 Check using include flag while listing ignored composes correctly (issue6514)
900
907
901 $ cd ..
908 $ cd ..
902 $ hg init issue6514
909 $ hg init issue6514
903 $ cd issue6514
910 $ cd issue6514
904 $ mkdir ignored-folder
911 $ mkdir ignored-folder
905 $ touch A.hs B.hs C.hs ignored-folder/other.txt ignored-folder/ctest.hs
912 $ touch A.hs B.hs C.hs ignored-folder/other.txt ignored-folder/ctest.hs
906 $ cat >.hgignore <<EOF
913 $ cat >.hgignore <<EOF
907 > A.hs
914 > A.hs
908 > B.hs
915 > B.hs
909 > ignored-folder/
916 > ignored-folder/
910 > EOF
917 > EOF
911 $ hg st -i -I 're:.*\.hs$'
918 $ hg st -i -I 're:.*\.hs$'
912 I A.hs
919 I A.hs
913 I B.hs
920 I B.hs
914 I ignored-folder/ctest.hs
921 I ignored-folder/ctest.hs
915
922
916 #if rust dirstate-v2
923 #if rust dirstate-v2
917
924
918 Check read_dir caching
925 Check read_dir caching
919
926
920 $ cd ..
927 $ cd ..
921 $ hg init repo8
928 $ hg init repo8
922 $ cd repo8
929 $ cd repo8
923 $ mkdir subdir
930 $ mkdir subdir
924 $ touch subdir/a subdir/b
931 $ touch subdir/a subdir/b
925 $ hg ci -Aqm '#0'
932 $ hg ci -Aqm '#0'
926
933
927 The cached mtime is initially unset
934 The cached mtime is initially unset
928
935
929 $ hg debugdirstate --all --no-dates | grep '^ '
936 $ hg debugdirstate --all --no-dates | grep '^ '
930 0 -1 unset subdir
937 0 -1 unset subdir
931
938
932 It is still not set when there are unknown files
939 It is still not set when there are unknown files
933
940
934 $ touch subdir/unknown
941 $ touch subdir/unknown
935 $ hg status
942 $ hg status
936 ? subdir/unknown
943 ? subdir/unknown
937 $ hg debugdirstate --all --no-dates | grep '^ '
944 $ hg debugdirstate --all --no-dates | grep '^ '
938 0 -1 unset subdir
945 0 -1 unset subdir
939
946
940 Now the directory is eligible for caching, so its mtime is save in the dirstate
947 Now the directory is eligible for caching, so its mtime is save in the dirstate
941
948
942 $ rm subdir/unknown
949 $ rm subdir/unknown
943 $ sleep 0.1 # ensure the kernels internal clock for mtimes has ticked
950 $ sleep 0.1 # ensure the kernels internal clock for mtimes has ticked
944 $ hg status
951 $ hg status
945 $ hg debugdirstate --all --no-dates | grep '^ '
952 $ hg debugdirstate --all --no-dates | grep '^ '
946 0 -1 set subdir
953 0 -1 set subdir
947
954
948 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
955 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
949
956
950 $ hg status
957 $ hg status
951
958
952 Creating a new file changes the directorys mtime, invalidating the cache
959 Creating a new file changes the directorys mtime, invalidating the cache
953
960
954 $ touch subdir/unknown
961 $ touch subdir/unknown
955 $ hg status
962 $ hg status
956 ? subdir/unknown
963 ? subdir/unknown
957
964
958 $ rm subdir/unknown
965 $ rm subdir/unknown
959 $ hg status
966 $ hg status
960
967
961 Removing a node from the dirstate resets the cache for its parent directory
968 Removing a node from the dirstate resets the cache for its parent directory
962
969
963 $ hg forget subdir/a
970 $ hg forget subdir/a
964 $ hg debugdirstate --all --no-dates | grep '^ '
971 $ hg debugdirstate --all --no-dates | grep '^ '
965 0 -1 set subdir
972 0 -1 set subdir
966 $ hg ci -qm '#1'
973 $ hg ci -qm '#1'
967 $ hg debugdirstate --all --no-dates | grep '^ '
974 $ hg debugdirstate --all --no-dates | grep '^ '
968 0 -1 unset subdir
975 0 -1 unset subdir
969 $ hg status
976 $ hg status
970 ? subdir/a
977 ? subdir/a
971
978
972 #endif
979 #endif
General Comments 0
You need to be logged in to leave comments. Login now