##// END OF EJS Templates
tag: migrate `opts` to native kwargs
Matt Harbison -
r51744:fa675fd6 default
parent child Browse files
Show More
@@ -1,8053 +1,8051 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 nullid,
16 nullid,
17 nullrev,
17 nullrev,
18 short,
18 short,
19 wdirrev,
19 wdirrev,
20 )
20 )
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 discovery,
31 discovery,
32 encoding,
32 encoding,
33 error,
33 error,
34 exchange,
34 exchange,
35 extensions,
35 extensions,
36 filemerge,
36 filemerge,
37 formatter,
37 formatter,
38 graphmod,
38 graphmod,
39 grep as grepmod,
39 grep as grepmod,
40 hbisect,
40 hbisect,
41 help,
41 help,
42 hg,
42 hg,
43 logcmdutil,
43 logcmdutil,
44 merge as mergemod,
44 merge as mergemod,
45 mergestate as mergestatemod,
45 mergestate as mergestatemod,
46 narrowspec,
46 narrowspec,
47 obsolete,
47 obsolete,
48 obsutil,
48 obsutil,
49 patch,
49 patch,
50 phases,
50 phases,
51 pycompat,
51 pycompat,
52 rcutil,
52 rcutil,
53 registrar,
53 registrar,
54 requirements,
54 requirements,
55 revsetlang,
55 revsetlang,
56 rewriteutil,
56 rewriteutil,
57 scmutil,
57 scmutil,
58 server,
58 server,
59 shelve as shelvemod,
59 shelve as shelvemod,
60 state as statemod,
60 state as statemod,
61 streamclone,
61 streamclone,
62 tags as tagsmod,
62 tags as tagsmod,
63 ui as uimod,
63 ui as uimod,
64 util,
64 util,
65 verify as verifymod,
65 verify as verifymod,
66 vfs as vfsmod,
66 vfs as vfsmod,
67 wireprotoserver,
67 wireprotoserver,
68 )
68 )
69 from .utils import (
69 from .utils import (
70 dateutil,
70 dateutil,
71 procutil,
71 procutil,
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 with repo.wlock(), repo.dirstate.changing_files(repo):
255 with repo.wlock(), repo.dirstate.changing_files(repo):
256 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
256 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
257 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
257 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
258 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
258 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
259 return rejected and 1 or 0
259 return rejected and 1 or 0
260
260
261
261
262 @command(
262 @command(
263 b'addremove',
263 b'addremove',
264 similarityopts + subrepoopts + walkopts + dryrunopts,
264 similarityopts + subrepoopts + walkopts + dryrunopts,
265 _(b'[OPTION]... [FILE]...'),
265 _(b'[OPTION]... [FILE]...'),
266 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
266 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
267 inferrepo=True,
267 inferrepo=True,
268 )
268 )
269 def addremove(ui, repo, *pats, **opts):
269 def addremove(ui, repo, *pats, **opts):
270 """add all new files, delete all missing files
270 """add all new files, delete all missing files
271
271
272 Add all new files and remove all missing files from the
272 Add all new files and remove all missing files from the
273 repository.
273 repository.
274
274
275 Unless names are given, new files are ignored if they match any of
275 Unless names are given, new files are ignored if they match any of
276 the patterns in ``.hgignore``. As with add, these changes take
276 the patterns in ``.hgignore``. As with add, these changes take
277 effect at the next commit.
277 effect at the next commit.
278
278
279 Use the -s/--similarity option to detect renamed files. This
279 Use the -s/--similarity option to detect renamed files. This
280 option takes a percentage between 0 (disabled) and 100 (files must
280 option takes a percentage between 0 (disabled) and 100 (files must
281 be identical) as its parameter. With a parameter greater than 0,
281 be identical) as its parameter. With a parameter greater than 0,
282 this compares every removed file with every added file and records
282 this compares every removed file with every added file and records
283 those similar enough as renames. Detecting renamed files this way
283 those similar enough as renames. Detecting renamed files this way
284 can be expensive. After using this option, :hg:`status -C` can be
284 can be expensive. After using this option, :hg:`status -C` can be
285 used to check which files were identified as moved or renamed. If
285 used to check which files were identified as moved or renamed. If
286 not specified, -s/--similarity defaults to 100 and only renames of
286 not specified, -s/--similarity defaults to 100 and only renames of
287 identical files are detected.
287 identical files are detected.
288
288
289 .. container:: verbose
289 .. container:: verbose
290
290
291 Examples:
291 Examples:
292
292
293 - A number of files (bar.c and foo.c) are new,
293 - A number of files (bar.c and foo.c) are new,
294 while foobar.c has been removed (without using :hg:`remove`)
294 while foobar.c has been removed (without using :hg:`remove`)
295 from the repository::
295 from the repository::
296
296
297 $ ls
297 $ ls
298 bar.c foo.c
298 bar.c foo.c
299 $ hg status
299 $ hg status
300 ! foobar.c
300 ! foobar.c
301 ? bar.c
301 ? bar.c
302 ? foo.c
302 ? foo.c
303 $ hg addremove
303 $ hg addremove
304 adding bar.c
304 adding bar.c
305 adding foo.c
305 adding foo.c
306 removing foobar.c
306 removing foobar.c
307 $ hg status
307 $ hg status
308 A bar.c
308 A bar.c
309 A foo.c
309 A foo.c
310 R foobar.c
310 R foobar.c
311
311
312 - A file foobar.c was moved to foo.c without using :hg:`rename`.
312 - A file foobar.c was moved to foo.c without using :hg:`rename`.
313 Afterwards, it was edited slightly::
313 Afterwards, it was edited slightly::
314
314
315 $ ls
315 $ ls
316 foo.c
316 foo.c
317 $ hg status
317 $ hg status
318 ! foobar.c
318 ! foobar.c
319 ? foo.c
319 ? foo.c
320 $ hg addremove --similarity 90
320 $ hg addremove --similarity 90
321 removing foobar.c
321 removing foobar.c
322 adding foo.c
322 adding foo.c
323 recording removal of foobar.c as rename to foo.c (94% similar)
323 recording removal of foobar.c as rename to foo.c (94% similar)
324 $ hg status -C
324 $ hg status -C
325 A foo.c
325 A foo.c
326 foobar.c
326 foobar.c
327 R foobar.c
327 R foobar.c
328
328
329 Returns 0 if all files are successfully added.
329 Returns 0 if all files are successfully added.
330 """
330 """
331 opts = pycompat.byteskwargs(opts)
331 opts = pycompat.byteskwargs(opts)
332 if not opts.get(b'similarity'):
332 if not opts.get(b'similarity'):
333 opts[b'similarity'] = b'100'
333 opts[b'similarity'] = b'100'
334 with repo.wlock(), repo.dirstate.changing_files(repo):
334 with repo.wlock(), repo.dirstate.changing_files(repo):
335 matcher = scmutil.match(repo[None], pats, opts)
335 matcher = scmutil.match(repo[None], pats, opts)
336 relative = scmutil.anypats(pats, opts)
336 relative = scmutil.anypats(pats, opts)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339
339
340
340
341 @command(
341 @command(
342 b'annotate|blame',
342 b'annotate|blame',
343 [
343 [
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (
345 (
346 b'',
346 b'',
347 b'follow',
347 b'follow',
348 None,
348 None,
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 ),
350 ),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'a', b'text', None, _(b'treat all files as text')),
352 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'f', b'file', None, _(b'list the filename')),
354 (b'f', b'file', None, _(b'list the filename')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
358 (
358 (
359 b'l',
359 b'l',
360 b'line-number',
360 b'line-number',
361 None,
361 None,
362 _(b'show line number at the first appearance'),
362 _(b'show line number at the first appearance'),
363 ),
363 ),
364 (
364 (
365 b'',
365 b'',
366 b'skip',
366 b'skip',
367 [],
367 [],
368 _(b'revset to not display (EXPERIMENTAL)'),
368 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'REV'),
369 _(b'REV'),
370 ),
370 ),
371 ]
371 ]
372 + diffwsopts
372 + diffwsopts
373 + walkopts
373 + walkopts
374 + formatteropts,
374 + formatteropts,
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpbasic=True,
377 helpbasic=True,
378 inferrepo=True,
378 inferrepo=True,
379 )
379 )
380 def annotate(ui, repo, *pats, **opts):
380 def annotate(ui, repo, *pats, **opts):
381 """show changeset information by line for each file
381 """show changeset information by line for each file
382
382
383 List changes in files, showing the revision id responsible for
383 List changes in files, showing the revision id responsible for
384 each line.
384 each line.
385
385
386 This command is useful for discovering when a change was made and
386 This command is useful for discovering when a change was made and
387 by whom.
387 by whom.
388
388
389 If you include --file, --user, or --date, the revision number is
389 If you include --file, --user, or --date, the revision number is
390 suppressed unless you also include --number.
390 suppressed unless you also include --number.
391
391
392 Without the -a/--text option, annotate will avoid processing files
392 Without the -a/--text option, annotate will avoid processing files
393 it detects as binary. With -a, annotate will annotate the file
393 it detects as binary. With -a, annotate will annotate the file
394 anyway, although the results will probably be neither useful
394 anyway, although the results will probably be neither useful
395 nor desirable.
395 nor desirable.
396
396
397 .. container:: verbose
397 .. container:: verbose
398
398
399 Template:
399 Template:
400
400
401 The following keywords are supported in addition to the common template
401 The following keywords are supported in addition to the common template
402 keywords and functions. See also :hg:`help templates`.
402 keywords and functions. See also :hg:`help templates`.
403
403
404 :lines: List of lines with annotation data.
404 :lines: List of lines with annotation data.
405 :path: String. Repository-absolute path of the specified file.
405 :path: String. Repository-absolute path of the specified file.
406
406
407 And each entry of ``{lines}`` provides the following sub-keywords in
407 And each entry of ``{lines}`` provides the following sub-keywords in
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409
409
410 :line: String. Line content.
410 :line: String. Line content.
411 :lineno: Integer. Line number at that revision.
411 :lineno: Integer. Line number at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
413
413
414 See :hg:`help templates.operators` for the list expansion syntax.
414 See :hg:`help templates.operators` for the list expansion syntax.
415
415
416 Returns 0 on success.
416 Returns 0 on success.
417 """
417 """
418 opts = pycompat.byteskwargs(opts)
418 opts = pycompat.byteskwargs(opts)
419 if not pats:
419 if not pats:
420 raise error.InputError(
420 raise error.InputError(
421 _(b'at least one filename or pattern is required')
421 _(b'at least one filename or pattern is required')
422 )
422 )
423
423
424 if opts.get(b'follow'):
424 if opts.get(b'follow'):
425 # --follow is deprecated and now just an alias for -f/--file
425 # --follow is deprecated and now just an alias for -f/--file
426 # to mimic the behavior of Mercurial before version 1.5
426 # to mimic the behavior of Mercurial before version 1.5
427 opts[b'file'] = True
427 opts[b'file'] = True
428
428
429 if (
429 if (
430 not opts.get(b'user')
430 not opts.get(b'user')
431 and not opts.get(b'changeset')
431 and not opts.get(b'changeset')
432 and not opts.get(b'date')
432 and not opts.get(b'date')
433 and not opts.get(b'file')
433 and not opts.get(b'file')
434 ):
434 ):
435 opts[b'number'] = True
435 opts[b'number'] = True
436
436
437 linenumber = opts.get(b'line_number') is not None
437 linenumber = opts.get(b'line_number') is not None
438 if (
438 if (
439 linenumber
439 linenumber
440 and (not opts.get(b'changeset'))
440 and (not opts.get(b'changeset'))
441 and (not opts.get(b'number'))
441 and (not opts.get(b'number'))
442 ):
442 ):
443 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
443 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
444
444
445 rev = opts.get(b'rev')
445 rev = opts.get(b'rev')
446 if rev:
446 if rev:
447 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
447 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
448 ctx = logcmdutil.revsingle(repo, rev)
448 ctx = logcmdutil.revsingle(repo, rev)
449
449
450 ui.pager(b'annotate')
450 ui.pager(b'annotate')
451 rootfm = ui.formatter(b'annotate', opts)
451 rootfm = ui.formatter(b'annotate', opts)
452 if ui.debugflag:
452 if ui.debugflag:
453 shorthex = pycompat.identity
453 shorthex = pycompat.identity
454 else:
454 else:
455
455
456 def shorthex(h):
456 def shorthex(h):
457 return h[:12]
457 return h[:12]
458
458
459 if ui.quiet:
459 if ui.quiet:
460 datefunc = dateutil.shortdate
460 datefunc = dateutil.shortdate
461 else:
461 else:
462 datefunc = dateutil.datestr
462 datefunc = dateutil.datestr
463 if ctx.rev() is None:
463 if ctx.rev() is None:
464 if opts.get(b'changeset'):
464 if opts.get(b'changeset'):
465 # omit "+" suffix which is appended to node hex
465 # omit "+" suffix which is appended to node hex
466 def formatrev(rev):
466 def formatrev(rev):
467 if rev == wdirrev:
467 if rev == wdirrev:
468 return b'%d' % ctx.p1().rev()
468 return b'%d' % ctx.p1().rev()
469 else:
469 else:
470 return b'%d' % rev
470 return b'%d' % rev
471
471
472 else:
472 else:
473
473
474 def formatrev(rev):
474 def formatrev(rev):
475 if rev == wdirrev:
475 if rev == wdirrev:
476 return b'%d+' % ctx.p1().rev()
476 return b'%d+' % ctx.p1().rev()
477 else:
477 else:
478 return b'%d ' % rev
478 return b'%d ' % rev
479
479
480 def formathex(h):
480 def formathex(h):
481 if h == repo.nodeconstants.wdirhex:
481 if h == repo.nodeconstants.wdirhex:
482 return b'%s+' % shorthex(hex(ctx.p1().node()))
482 return b'%s+' % shorthex(hex(ctx.p1().node()))
483 else:
483 else:
484 return b'%s ' % shorthex(h)
484 return b'%s ' % shorthex(h)
485
485
486 else:
486 else:
487 formatrev = b'%d'.__mod__
487 formatrev = b'%d'.__mod__
488 formathex = shorthex
488 formathex = shorthex
489
489
490 opmap = [
490 opmap = [
491 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
491 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
492 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
492 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
493 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
493 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
494 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
494 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
495 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
495 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
496 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
496 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
497 ]
497 ]
498 opnamemap = {
498 opnamemap = {
499 b'rev': b'number',
499 b'rev': b'number',
500 b'node': b'changeset',
500 b'node': b'changeset',
501 b'path': b'file',
501 b'path': b'file',
502 b'lineno': b'line_number',
502 b'lineno': b'line_number',
503 }
503 }
504
504
505 if rootfm.isplain():
505 if rootfm.isplain():
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return lambda x: fmt(get(x))
508 return lambda x: fmt(get(x))
509
509
510 else:
510 else:
511
511
512 def makefunc(get, fmt):
512 def makefunc(get, fmt):
513 return get
513 return get
514
514
515 datahint = rootfm.datahint()
515 datahint = rootfm.datahint()
516 funcmap = [
516 funcmap = [
517 (makefunc(get, fmt), sep)
517 (makefunc(get, fmt), sep)
518 for fn, sep, get, fmt in opmap
518 for fn, sep, get, fmt in opmap
519 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
519 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 ]
520 ]
521 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
521 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
522 fields = b' '.join(
522 fields = b' '.join(
523 fn
523 fn
524 for fn, sep, get, fmt in opmap
524 for fn, sep, get, fmt in opmap
525 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
525 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526 )
526 )
527
527
528 def bad(x, y):
528 def bad(x, y):
529 raise error.InputError(b"%s: %s" % (x, y))
529 raise error.InputError(b"%s: %s" % (x, y))
530
530
531 m = scmutil.match(ctx, pats, opts, badfn=bad)
531 m = scmutil.match(ctx, pats, opts, badfn=bad)
532
532
533 follow = not opts.get(b'no_follow')
533 follow = not opts.get(b'no_follow')
534 diffopts = patch.difffeatureopts(
534 diffopts = patch.difffeatureopts(
535 ui, opts, section=b'annotate', whitespace=True
535 ui, opts, section=b'annotate', whitespace=True
536 )
536 )
537 skiprevs = opts.get(b'skip')
537 skiprevs = opts.get(b'skip')
538 if skiprevs:
538 if skiprevs:
539 skiprevs = logcmdutil.revrange(repo, skiprevs)
539 skiprevs = logcmdutil.revrange(repo, skiprevs)
540
540
541 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
541 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
542 for abs in ctx.walk(m):
542 for abs in ctx.walk(m):
543 fctx = ctx[abs]
543 fctx = ctx[abs]
544 rootfm.startitem()
544 rootfm.startitem()
545 rootfm.data(path=abs)
545 rootfm.data(path=abs)
546 if not opts.get(b'text') and fctx.isbinary():
546 if not opts.get(b'text') and fctx.isbinary():
547 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
547 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
548 continue
548 continue
549
549
550 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
550 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
551 lines = fctx.annotate(
551 lines = fctx.annotate(
552 follow=follow, skiprevs=skiprevs, diffopts=diffopts
552 follow=follow, skiprevs=skiprevs, diffopts=diffopts
553 )
553 )
554 if not lines:
554 if not lines:
555 fm.end()
555 fm.end()
556 continue
556 continue
557 formats = []
557 formats = []
558 pieces = []
558 pieces = []
559
559
560 for f, sep in funcmap:
560 for f, sep in funcmap:
561 l = [f(n) for n in lines]
561 l = [f(n) for n in lines]
562 if fm.isplain():
562 if fm.isplain():
563 sizes = [encoding.colwidth(x) for x in l]
563 sizes = [encoding.colwidth(x) for x in l]
564 ml = max(sizes)
564 ml = max(sizes)
565 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
565 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
566 else:
566 else:
567 formats.append([b'%s'] * len(l))
567 formats.append([b'%s'] * len(l))
568 pieces.append(l)
568 pieces.append(l)
569
569
570 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
570 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
571 fm.startitem()
571 fm.startitem()
572 fm.context(fctx=n.fctx)
572 fm.context(fctx=n.fctx)
573 fm.write(fields, b"".join(f), *p)
573 fm.write(fields, b"".join(f), *p)
574 if n.skip:
574 if n.skip:
575 fmt = b"* %s"
575 fmt = b"* %s"
576 else:
576 else:
577 fmt = b": %s"
577 fmt = b": %s"
578 fm.write(b'line', fmt, n.text)
578 fm.write(b'line', fmt, n.text)
579
579
580 if not lines[-1].text.endswith(b'\n'):
580 if not lines[-1].text.endswith(b'\n'):
581 fm.plain(b'\n')
581 fm.plain(b'\n')
582 fm.end()
582 fm.end()
583
583
584 rootfm.end()
584 rootfm.end()
585
585
586
586
587 @command(
587 @command(
588 b'archive',
588 b'archive',
589 [
589 [
590 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
590 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
591 (
591 (
592 b'p',
592 b'p',
593 b'prefix',
593 b'prefix',
594 b'',
594 b'',
595 _(b'directory prefix for files in archive'),
595 _(b'directory prefix for files in archive'),
596 _(b'PREFIX'),
596 _(b'PREFIX'),
597 ),
597 ),
598 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
598 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
599 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
599 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
600 ]
600 ]
601 + subrepoopts
601 + subrepoopts
602 + walkopts,
602 + walkopts,
603 _(b'[OPTION]... DEST'),
603 _(b'[OPTION]... DEST'),
604 helpcategory=command.CATEGORY_IMPORT_EXPORT,
604 helpcategory=command.CATEGORY_IMPORT_EXPORT,
605 )
605 )
606 def archive(ui, repo, dest, **opts):
606 def archive(ui, repo, dest, **opts):
607 """create an unversioned archive of a repository revision
607 """create an unversioned archive of a repository revision
608
608
609 By default, the revision used is the parent of the working
609 By default, the revision used is the parent of the working
610 directory; use -r/--rev to specify a different revision.
610 directory; use -r/--rev to specify a different revision.
611
611
612 The archive type is automatically detected based on file
612 The archive type is automatically detected based on file
613 extension (to override, use -t/--type).
613 extension (to override, use -t/--type).
614
614
615 .. container:: verbose
615 .. container:: verbose
616
616
617 Examples:
617 Examples:
618
618
619 - create a zip file containing the 1.0 release::
619 - create a zip file containing the 1.0 release::
620
620
621 hg archive -r 1.0 project-1.0.zip
621 hg archive -r 1.0 project-1.0.zip
622
622
623 - create a tarball excluding .hg files::
623 - create a tarball excluding .hg files::
624
624
625 hg archive project.tar.gz -X ".hg*"
625 hg archive project.tar.gz -X ".hg*"
626
626
627 Valid types are:
627 Valid types are:
628
628
629 :``files``: a directory full of files (default)
629 :``files``: a directory full of files (default)
630 :``tar``: tar archive, uncompressed
630 :``tar``: tar archive, uncompressed
631 :``tbz2``: tar archive, compressed using bzip2
631 :``tbz2``: tar archive, compressed using bzip2
632 :``tgz``: tar archive, compressed using gzip
632 :``tgz``: tar archive, compressed using gzip
633 :``txz``: tar archive, compressed using lzma (only in Python 3)
633 :``txz``: tar archive, compressed using lzma (only in Python 3)
634 :``uzip``: zip archive, uncompressed
634 :``uzip``: zip archive, uncompressed
635 :``zip``: zip archive, compressed using deflate
635 :``zip``: zip archive, compressed using deflate
636
636
637 The exact name of the destination archive or directory is given
637 The exact name of the destination archive or directory is given
638 using a format string; see :hg:`help export` for details.
638 using a format string; see :hg:`help export` for details.
639
639
640 Each member added to an archive file has a directory prefix
640 Each member added to an archive file has a directory prefix
641 prepended. Use -p/--prefix to specify a format string for the
641 prepended. Use -p/--prefix to specify a format string for the
642 prefix. The default is the basename of the archive, with suffixes
642 prefix. The default is the basename of the archive, with suffixes
643 removed.
643 removed.
644
644
645 Returns 0 on success.
645 Returns 0 on success.
646 """
646 """
647
647
648 rev = opts.get('rev')
648 rev = opts.get('rev')
649 if rev:
649 if rev:
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
651 ctx = logcmdutil.revsingle(repo, rev)
651 ctx = logcmdutil.revsingle(repo, rev)
652 if not ctx:
652 if not ctx:
653 raise error.InputError(
653 raise error.InputError(
654 _(b'no working directory: please specify a revision')
654 _(b'no working directory: please specify a revision')
655 )
655 )
656 node = ctx.node()
656 node = ctx.node()
657 dest = cmdutil.makefilename(ctx, dest)
657 dest = cmdutil.makefilename(ctx, dest)
658 if os.path.realpath(dest) == repo.root:
658 if os.path.realpath(dest) == repo.root:
659 raise error.InputError(_(b'repository root cannot be destination'))
659 raise error.InputError(_(b'repository root cannot be destination'))
660
660
661 kind = opts.get('type') or archival.guesskind(dest) or b'files'
661 kind = opts.get('type') or archival.guesskind(dest) or b'files'
662 prefix = opts.get('prefix')
662 prefix = opts.get('prefix')
663
663
664 if dest == b'-':
664 if dest == b'-':
665 if kind == b'files':
665 if kind == b'files':
666 raise error.InputError(_(b'cannot archive plain files to stdout'))
666 raise error.InputError(_(b'cannot archive plain files to stdout'))
667 dest = cmdutil.makefileobj(ctx, dest)
667 dest = cmdutil.makefileobj(ctx, dest)
668 if not prefix:
668 if not prefix:
669 prefix = os.path.basename(repo.root) + b'-%h'
669 prefix = os.path.basename(repo.root) + b'-%h'
670
670
671 prefix = cmdutil.makefilename(ctx, prefix)
671 prefix = cmdutil.makefilename(ctx, prefix)
672 match = scmutil.match(ctx, [], pycompat.byteskwargs(opts))
672 match = scmutil.match(ctx, [], pycompat.byteskwargs(opts))
673 archival.archive(
673 archival.archive(
674 repo,
674 repo,
675 dest,
675 dest,
676 node,
676 node,
677 kind,
677 kind,
678 not opts.get('no_decode'),
678 not opts.get('no_decode'),
679 match,
679 match,
680 prefix,
680 prefix,
681 subrepos=opts.get('subrepos'),
681 subrepos=opts.get('subrepos'),
682 )
682 )
683
683
684
684
685 @command(
685 @command(
686 b'backout',
686 b'backout',
687 [
687 [
688 (
688 (
689 b'',
689 b'',
690 b'merge',
690 b'merge',
691 None,
691 None,
692 _(b'merge with old dirstate parent after backout'),
692 _(b'merge with old dirstate parent after backout'),
693 ),
693 ),
694 (
694 (
695 b'',
695 b'',
696 b'commit',
696 b'commit',
697 None,
697 None,
698 _(b'commit if no conflicts were encountered (DEPRECATED)'),
698 _(b'commit if no conflicts were encountered (DEPRECATED)'),
699 ),
699 ),
700 (b'', b'no-commit', None, _(b'do not commit')),
700 (b'', b'no-commit', None, _(b'do not commit')),
701 (
701 (
702 b'',
702 b'',
703 b'parent',
703 b'parent',
704 b'',
704 b'',
705 _(b'parent to choose when backing out merge (DEPRECATED)'),
705 _(b'parent to choose when backing out merge (DEPRECATED)'),
706 _(b'REV'),
706 _(b'REV'),
707 ),
707 ),
708 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
708 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
709 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
709 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
710 ]
710 ]
711 + mergetoolopts
711 + mergetoolopts
712 + walkopts
712 + walkopts
713 + commitopts
713 + commitopts
714 + commitopts2,
714 + commitopts2,
715 _(b'[OPTION]... [-r] REV'),
715 _(b'[OPTION]... [-r] REV'),
716 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
716 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
717 )
717 )
718 def backout(ui, repo, node=None, rev=None, **opts):
718 def backout(ui, repo, node=None, rev=None, **opts):
719 """reverse effect of earlier changeset
719 """reverse effect of earlier changeset
720
720
721 Prepare a new changeset with the effect of REV undone in the
721 Prepare a new changeset with the effect of REV undone in the
722 current working directory. If no conflicts were encountered,
722 current working directory. If no conflicts were encountered,
723 it will be committed immediately.
723 it will be committed immediately.
724
724
725 If REV is the parent of the working directory, then this new changeset
725 If REV is the parent of the working directory, then this new changeset
726 is committed automatically (unless --no-commit is specified).
726 is committed automatically (unless --no-commit is specified).
727
727
728 .. note::
728 .. note::
729
729
730 :hg:`backout` cannot be used to fix either an unwanted or
730 :hg:`backout` cannot be used to fix either an unwanted or
731 incorrect merge.
731 incorrect merge.
732
732
733 .. container:: verbose
733 .. container:: verbose
734
734
735 Examples:
735 Examples:
736
736
737 - Reverse the effect of the parent of the working directory.
737 - Reverse the effect of the parent of the working directory.
738 This backout will be committed immediately::
738 This backout will be committed immediately::
739
739
740 hg backout -r .
740 hg backout -r .
741
741
742 - Reverse the effect of previous bad revision 23::
742 - Reverse the effect of previous bad revision 23::
743
743
744 hg backout -r 23
744 hg backout -r 23
745
745
746 - Reverse the effect of previous bad revision 23 and
746 - Reverse the effect of previous bad revision 23 and
747 leave changes uncommitted::
747 leave changes uncommitted::
748
748
749 hg backout -r 23 --no-commit
749 hg backout -r 23 --no-commit
750 hg commit -m "Backout revision 23"
750 hg commit -m "Backout revision 23"
751
751
752 By default, the pending changeset will have one parent,
752 By default, the pending changeset will have one parent,
753 maintaining a linear history. With --merge, the pending
753 maintaining a linear history. With --merge, the pending
754 changeset will instead have two parents: the old parent of the
754 changeset will instead have two parents: the old parent of the
755 working directory and a new child of REV that simply undoes REV.
755 working directory and a new child of REV that simply undoes REV.
756
756
757 Before version 1.7, the behavior without --merge was equivalent
757 Before version 1.7, the behavior without --merge was equivalent
758 to specifying --merge followed by :hg:`update --clean .` to
758 to specifying --merge followed by :hg:`update --clean .` to
759 cancel the merge and leave the child of REV as a head to be
759 cancel the merge and leave the child of REV as a head to be
760 merged separately.
760 merged separately.
761
761
762 See :hg:`help dates` for a list of formats valid for -d/--date.
762 See :hg:`help dates` for a list of formats valid for -d/--date.
763
763
764 See :hg:`help revert` for a way to restore files to the state
764 See :hg:`help revert` for a way to restore files to the state
765 of another revision.
765 of another revision.
766
766
767 Returns 0 on success, 1 if nothing to backout or there are unresolved
767 Returns 0 on success, 1 if nothing to backout or there are unresolved
768 files.
768 files.
769 """
769 """
770 with repo.wlock(), repo.lock():
770 with repo.wlock(), repo.lock():
771 return _dobackout(ui, repo, node, rev, **opts)
771 return _dobackout(ui, repo, node, rev, **opts)
772
772
773
773
774 def _dobackout(ui, repo, node=None, rev=None, **opts):
774 def _dobackout(ui, repo, node=None, rev=None, **opts):
775 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
775 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
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('date')
786 date = opts.get('date')
787 if date:
787 if date:
788 opts['date'] = dateutil.parsedate(date)
788 opts['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('parent'):
805 if not opts.get('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['parent'])
807 p = repo.lookup(opts['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('parent'):
814 if opts.get('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('merge') and op1 != node:
824 if not opts.get('merge') and op1 != node:
825 with repo.transaction(b"backout"):
825 with repo.transaction(b"backout"):
826 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
826 overrides = {(b'ui', b'forcemerge'): opts.get('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, repo.currenttransaction())
838 repo.dirstate.setbranch(branch, repo.currenttransaction())
839 cmdutil.revert(ui, repo, rctx)
839 cmdutil.revert(ui, repo, rctx)
840
840
841 if opts.get('no_commit'):
841 if opts.get('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(
862 newnode = cmdutil.commit(
863 ui, repo, commitfunc, [], pycompat.byteskwargs(opts)
863 ui, repo, commitfunc, [], pycompat.byteskwargs(opts)
864 )
864 )
865 if not newnode:
865 if not newnode:
866 ui.status(_(b"nothing changed\n"))
866 ui.status(_(b"nothing changed\n"))
867 return 1
867 return 1
868 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
868 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
869
869
870 def nice(node):
870 def nice(node):
871 return b'%d:%s' % (repo.changelog.rev(node), short(node))
871 return b'%d:%s' % (repo.changelog.rev(node), short(node))
872
872
873 ui.status(
873 ui.status(
874 _(b'changeset %s backs out changeset %s\n')
874 _(b'changeset %s backs out changeset %s\n')
875 % (nice(newnode), nice(node))
875 % (nice(newnode), nice(node))
876 )
876 )
877 if opts.get('merge') and op1 != node:
877 if opts.get('merge') and op1 != node:
878 hg.clean(repo, op1, show_stats=False)
878 hg.clean(repo, op1, show_stats=False)
879 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
879 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
880 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
880 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
881 with ui.configoverride(overrides, b'backout'):
881 with ui.configoverride(overrides, b'backout'):
882 return hg.merge(repo[b'tip'])
882 return hg.merge(repo[b'tip'])
883 return 0
883 return 0
884
884
885
885
886 @command(
886 @command(
887 b'bisect',
887 b'bisect',
888 [
888 [
889 (b'r', b'reset', False, _(b'reset bisect state')),
889 (b'r', b'reset', False, _(b'reset bisect state')),
890 (b'g', b'good', False, _(b'mark changeset good')),
890 (b'g', b'good', False, _(b'mark changeset good')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
894 (
894 (
895 b'c',
895 b'c',
896 b'command',
896 b'command',
897 b'',
897 b'',
898 _(b'use command to check changeset state'),
898 _(b'use command to check changeset state'),
899 _(b'CMD'),
899 _(b'CMD'),
900 ),
900 ),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
902 ],
902 ],
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905 )
905 )
906 def bisect(
906 def bisect(
907 ui,
907 ui,
908 repo,
908 repo,
909 positional_1=None,
909 positional_1=None,
910 positional_2=None,
910 positional_2=None,
911 command=None,
911 command=None,
912 reset=None,
912 reset=None,
913 good=None,
913 good=None,
914 bad=None,
914 bad=None,
915 skip=None,
915 skip=None,
916 extend=None,
916 extend=None,
917 noupdate=None,
917 noupdate=None,
918 ):
918 ):
919 """subdivision search of changesets
919 """subdivision search of changesets
920
920
921 This command helps to find changesets which introduce problems. To
921 This command helps to find changesets which introduce problems. To
922 use, mark the earliest changeset you know exhibits the problem as
922 use, mark the earliest changeset you know exhibits the problem as
923 bad, then mark the latest changeset which is free from the problem
923 bad, then mark the latest changeset which is free from the problem
924 as good. Bisect will update your working directory to a revision
924 as good. Bisect will update your working directory to a revision
925 for testing (unless the -U/--noupdate option is specified). Once
925 for testing (unless the -U/--noupdate option is specified). Once
926 you have performed tests, mark the working directory as good or
926 you have performed tests, mark the working directory as good or
927 bad, and bisect will either update to another candidate changeset
927 bad, and bisect will either update to another candidate changeset
928 or announce that it has found the bad revision.
928 or announce that it has found the bad revision.
929
929
930 As a shortcut, you can also use the revision argument to mark a
930 As a shortcut, you can also use the revision argument to mark a
931 revision as good or bad without checking it out first.
931 revision as good or bad without checking it out first.
932
932
933 If you supply a command, it will be used for automatic bisection.
933 If you supply a command, it will be used for automatic bisection.
934 The environment variable HG_NODE will contain the ID of the
934 The environment variable HG_NODE will contain the ID of the
935 changeset being tested. The exit status of the command will be
935 changeset being tested. The exit status of the command will be
936 used to mark revisions as good or bad: status 0 means good, 125
936 used to mark revisions as good or bad: status 0 means good, 125
937 means to skip the revision, 127 (command not found) will abort the
937 means to skip the revision, 127 (command not found) will abort the
938 bisection, and any other non-zero exit status means the revision
938 bisection, and any other non-zero exit status means the revision
939 is bad.
939 is bad.
940
940
941 .. container:: verbose
941 .. container:: verbose
942
942
943 Some examples:
943 Some examples:
944
944
945 - start a bisection with known bad revision 34, and good revision 12::
945 - start a bisection with known bad revision 34, and good revision 12::
946
946
947 hg bisect --bad 34
947 hg bisect --bad 34
948 hg bisect --good 12
948 hg bisect --good 12
949
949
950 - advance the current bisection by marking current revision as good or
950 - advance the current bisection by marking current revision as good or
951 bad::
951 bad::
952
952
953 hg bisect --good
953 hg bisect --good
954 hg bisect --bad
954 hg bisect --bad
955
955
956 - mark the current revision, or a known revision, to be skipped (e.g. if
956 - mark the current revision, or a known revision, to be skipped (e.g. if
957 that revision is not usable because of another issue)::
957 that revision is not usable because of another issue)::
958
958
959 hg bisect --skip
959 hg bisect --skip
960 hg bisect --skip 23
960 hg bisect --skip 23
961
961
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
963
963
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965
965
966 - forget the current bisection::
966 - forget the current bisection::
967
967
968 hg bisect --reset
968 hg bisect --reset
969
969
970 - use 'make && make tests' to automatically find the first broken
970 - use 'make && make tests' to automatically find the first broken
971 revision::
971 revision::
972
972
973 hg bisect --reset
973 hg bisect --reset
974 hg bisect --bad 34
974 hg bisect --bad 34
975 hg bisect --good 12
975 hg bisect --good 12
976 hg bisect --command "make && make tests"
976 hg bisect --command "make && make tests"
977
977
978 - see all changesets whose states are already known in the current
978 - see all changesets whose states are already known in the current
979 bisection::
979 bisection::
980
980
981 hg log -r "bisect(pruned)"
981 hg log -r "bisect(pruned)"
982
982
983 - see the changeset currently being bisected (especially useful
983 - see the changeset currently being bisected (especially useful
984 if running with -U/--noupdate)::
984 if running with -U/--noupdate)::
985
985
986 hg log -r "bisect(current)"
986 hg log -r "bisect(current)"
987
987
988 - see all changesets that took part in the current bisection::
988 - see all changesets that took part in the current bisection::
989
989
990 hg log -r "bisect(range)"
990 hg log -r "bisect(range)"
991
991
992 - you can even get a nice graph::
992 - you can even get a nice graph::
993
993
994 hg log --graph -r "bisect(range)"
994 hg log --graph -r "bisect(range)"
995
995
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997
997
998 Returns 0 on success.
998 Returns 0 on success.
999 """
999 """
1000 rev = []
1000 rev = []
1001 # backward compatibility
1001 # backward compatibility
1002 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1002 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1003 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1003 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004 cmd = positional_1
1004 cmd = positional_1
1005 rev.append(positional_2)
1005 rev.append(positional_2)
1006 if cmd == b"good":
1006 if cmd == b"good":
1007 good = True
1007 good = True
1008 elif cmd == b"bad":
1008 elif cmd == b"bad":
1009 bad = True
1009 bad = True
1010 else:
1010 else:
1011 reset = True
1011 reset = True
1012 elif positional_2:
1012 elif positional_2:
1013 raise error.InputError(_(b'incompatible arguments'))
1013 raise error.InputError(_(b'incompatible arguments'))
1014 elif positional_1 is not None:
1014 elif positional_1 is not None:
1015 rev.append(positional_1)
1015 rev.append(positional_1)
1016
1016
1017 incompatibles = {
1017 incompatibles = {
1018 b'--bad': bad,
1018 b'--bad': bad,
1019 b'--command': bool(command),
1019 b'--command': bool(command),
1020 b'--extend': extend,
1020 b'--extend': extend,
1021 b'--good': good,
1021 b'--good': good,
1022 b'--reset': reset,
1022 b'--reset': reset,
1023 b'--skip': skip,
1023 b'--skip': skip,
1024 }
1024 }
1025
1025
1026 enabled = [x for x in incompatibles if incompatibles[x]]
1026 enabled = [x for x in incompatibles if incompatibles[x]]
1027
1027
1028 if len(enabled) > 1:
1028 if len(enabled) > 1:
1029 raise error.InputError(
1029 raise error.InputError(
1030 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1030 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1031 )
1031 )
1032
1032
1033 if reset:
1033 if reset:
1034 hbisect.resetstate(repo)
1034 hbisect.resetstate(repo)
1035 return
1035 return
1036
1036
1037 state = hbisect.load_state(repo)
1037 state = hbisect.load_state(repo)
1038
1038
1039 if rev:
1039 if rev:
1040 revs = logcmdutil.revrange(repo, rev)
1040 revs = logcmdutil.revrange(repo, rev)
1041 goodnodes = state[b'good']
1041 goodnodes = state[b'good']
1042 badnodes = state[b'bad']
1042 badnodes = state[b'bad']
1043 if goodnodes and badnodes:
1043 if goodnodes and badnodes:
1044 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1044 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1045 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1045 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1046 revs = candidates & revs
1046 revs = candidates & revs
1047 nodes = [repo.changelog.node(i) for i in revs]
1047 nodes = [repo.changelog.node(i) for i in revs]
1048 else:
1048 else:
1049 nodes = [repo.lookup(b'.')]
1049 nodes = [repo.lookup(b'.')]
1050
1050
1051 # update state
1051 # update state
1052 if good or bad or skip:
1052 if good or bad or skip:
1053 if good:
1053 if good:
1054 state[b'good'] += nodes
1054 state[b'good'] += nodes
1055 elif bad:
1055 elif bad:
1056 state[b'bad'] += nodes
1056 state[b'bad'] += nodes
1057 elif skip:
1057 elif skip:
1058 state[b'skip'] += nodes
1058 state[b'skip'] += nodes
1059 hbisect.save_state(repo, state)
1059 hbisect.save_state(repo, state)
1060 if not (state[b'good'] and state[b'bad']):
1060 if not (state[b'good'] and state[b'bad']):
1061 return
1061 return
1062
1062
1063 def mayupdate(repo, node, show_stats=True):
1063 def mayupdate(repo, node, show_stats=True):
1064 """common used update sequence"""
1064 """common used update sequence"""
1065 if noupdate:
1065 if noupdate:
1066 return
1066 return
1067 cmdutil.checkunfinished(repo)
1067 cmdutil.checkunfinished(repo)
1068 cmdutil.bailifchanged(repo)
1068 cmdutil.bailifchanged(repo)
1069 return hg.clean(repo, node, show_stats=show_stats)
1069 return hg.clean(repo, node, show_stats=show_stats)
1070
1070
1071 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1071 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1072
1072
1073 if command:
1073 if command:
1074 changesets = 1
1074 changesets = 1
1075 if noupdate:
1075 if noupdate:
1076 try:
1076 try:
1077 node = state[b'current'][0]
1077 node = state[b'current'][0]
1078 except LookupError:
1078 except LookupError:
1079 raise error.StateError(
1079 raise error.StateError(
1080 _(
1080 _(
1081 b'current bisect revision is unknown - '
1081 b'current bisect revision is unknown - '
1082 b'start a new bisect to fix'
1082 b'start a new bisect to fix'
1083 )
1083 )
1084 )
1084 )
1085 else:
1085 else:
1086 node, p2 = repo.dirstate.parents()
1086 node, p2 = repo.dirstate.parents()
1087 if p2 != repo.nullid:
1087 if p2 != repo.nullid:
1088 raise error.StateError(_(b'current bisect revision is a merge'))
1088 raise error.StateError(_(b'current bisect revision is a merge'))
1089 if rev:
1089 if rev:
1090 if not nodes:
1090 if not nodes:
1091 raise error.InputError(_(b'empty revision set'))
1091 raise error.InputError(_(b'empty revision set'))
1092 node = repo[nodes[-1]].node()
1092 node = repo[nodes[-1]].node()
1093 with hbisect.restore_state(repo, state, node):
1093 with hbisect.restore_state(repo, state, node):
1094 while changesets:
1094 while changesets:
1095 # update state
1095 # update state
1096 state[b'current'] = [node]
1096 state[b'current'] = [node]
1097 hbisect.save_state(repo, state)
1097 hbisect.save_state(repo, state)
1098 status = ui.system(
1098 status = ui.system(
1099 command,
1099 command,
1100 environ={b'HG_NODE': hex(node)},
1100 environ={b'HG_NODE': hex(node)},
1101 blockedtag=b'bisect_check',
1101 blockedtag=b'bisect_check',
1102 )
1102 )
1103 if status == 125:
1103 if status == 125:
1104 transition = b"skip"
1104 transition = b"skip"
1105 elif status == 0:
1105 elif status == 0:
1106 transition = b"good"
1106 transition = b"good"
1107 # status < 0 means process was killed
1107 # status < 0 means process was killed
1108 elif status == 127:
1108 elif status == 127:
1109 raise error.Abort(_(b"failed to execute %s") % command)
1109 raise error.Abort(_(b"failed to execute %s") % command)
1110 elif status < 0:
1110 elif status < 0:
1111 raise error.Abort(_(b"%s killed") % command)
1111 raise error.Abort(_(b"%s killed") % command)
1112 else:
1112 else:
1113 transition = b"bad"
1113 transition = b"bad"
1114 state[transition].append(node)
1114 state[transition].append(node)
1115 ctx = repo[node]
1115 ctx = repo[node]
1116 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1116 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1117 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1117 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1118 hbisect.checkstate(state)
1118 hbisect.checkstate(state)
1119 # bisect
1119 # bisect
1120 nodes, changesets, bgood = hbisect.bisect(repo, state)
1120 nodes, changesets, bgood = hbisect.bisect(repo, state)
1121 # update to next check
1121 # update to next check
1122 node = nodes[0]
1122 node = nodes[0]
1123 mayupdate(repo, node, show_stats=False)
1123 mayupdate(repo, node, show_stats=False)
1124 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1124 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1125 return
1125 return
1126
1126
1127 hbisect.checkstate(state)
1127 hbisect.checkstate(state)
1128
1128
1129 # actually bisect
1129 # actually bisect
1130 nodes, changesets, good = hbisect.bisect(repo, state)
1130 nodes, changesets, good = hbisect.bisect(repo, state)
1131 if extend:
1131 if extend:
1132 if not changesets:
1132 if not changesets:
1133 extendctx = hbisect.extendrange(repo, state, nodes, good)
1133 extendctx = hbisect.extendrange(repo, state, nodes, good)
1134 if extendctx is not None:
1134 if extendctx is not None:
1135 ui.write(
1135 ui.write(
1136 _(b"Extending search to changeset %s\n")
1136 _(b"Extending search to changeset %s\n")
1137 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1137 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1138 )
1138 )
1139 state[b'current'] = [extendctx.node()]
1139 state[b'current'] = [extendctx.node()]
1140 hbisect.save_state(repo, state)
1140 hbisect.save_state(repo, state)
1141 return mayupdate(repo, extendctx.node())
1141 return mayupdate(repo, extendctx.node())
1142 raise error.StateError(_(b"nothing to extend"))
1142 raise error.StateError(_(b"nothing to extend"))
1143
1143
1144 if changesets == 0:
1144 if changesets == 0:
1145 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1145 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1146 else:
1146 else:
1147 assert len(nodes) == 1 # only a single node can be tested next
1147 assert len(nodes) == 1 # only a single node can be tested next
1148 node = nodes[0]
1148 node = nodes[0]
1149 # compute the approximate number of remaining tests
1149 # compute the approximate number of remaining tests
1150 tests, size = 0, 2
1150 tests, size = 0, 2
1151 while size <= changesets:
1151 while size <= changesets:
1152 tests, size = tests + 1, size * 2
1152 tests, size = tests + 1, size * 2
1153 rev = repo.changelog.rev(node)
1153 rev = repo.changelog.rev(node)
1154 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1154 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1155 ui.write(
1155 ui.write(
1156 _(
1156 _(
1157 b"Testing changeset %s "
1157 b"Testing changeset %s "
1158 b"(%d changesets remaining, ~%d tests)\n"
1158 b"(%d changesets remaining, ~%d tests)\n"
1159 )
1159 )
1160 % (summary, changesets, tests)
1160 % (summary, changesets, tests)
1161 )
1161 )
1162 state[b'current'] = [node]
1162 state[b'current'] = [node]
1163 hbisect.save_state(repo, state)
1163 hbisect.save_state(repo, state)
1164 return mayupdate(repo, node)
1164 return mayupdate(repo, node)
1165
1165
1166
1166
1167 @command(
1167 @command(
1168 b'bookmarks|bookmark',
1168 b'bookmarks|bookmark',
1169 [
1169 [
1170 (b'f', b'force', False, _(b'force')),
1170 (b'f', b'force', False, _(b'force')),
1171 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1171 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1172 (b'd', b'delete', False, _(b'delete a given bookmark')),
1172 (b'd', b'delete', False, _(b'delete a given bookmark')),
1173 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1173 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1174 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1174 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1175 (b'l', b'list', False, _(b'list existing bookmarks')),
1175 (b'l', b'list', False, _(b'list existing bookmarks')),
1176 ]
1176 ]
1177 + formatteropts,
1177 + formatteropts,
1178 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1178 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1179 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1179 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1180 )
1180 )
1181 def bookmark(ui, repo, *names, **opts):
1181 def bookmark(ui, repo, *names, **opts):
1182 """create a new bookmark or list existing bookmarks
1182 """create a new bookmark or list existing bookmarks
1183
1183
1184 Bookmarks are labels on changesets to help track lines of development.
1184 Bookmarks are labels on changesets to help track lines of development.
1185 Bookmarks are unversioned and can be moved, renamed and deleted.
1185 Bookmarks are unversioned and can be moved, renamed and deleted.
1186 Deleting or moving a bookmark has no effect on the associated changesets.
1186 Deleting or moving a bookmark has no effect on the associated changesets.
1187
1187
1188 Creating or updating to a bookmark causes it to be marked as 'active'.
1188 Creating or updating to a bookmark causes it to be marked as 'active'.
1189 The active bookmark is indicated with a '*'.
1189 The active bookmark is indicated with a '*'.
1190 When a commit is made, the active bookmark will advance to the new commit.
1190 When a commit is made, the active bookmark will advance to the new commit.
1191 A plain :hg:`update` will also advance an active bookmark, if possible.
1191 A plain :hg:`update` will also advance an active bookmark, if possible.
1192 Updating away from a bookmark will cause it to be deactivated.
1192 Updating away from a bookmark will cause it to be deactivated.
1193
1193
1194 Bookmarks can be pushed and pulled between repositories (see
1194 Bookmarks can be pushed and pulled between repositories (see
1195 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1195 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1196 diverged, a new 'divergent bookmark' of the form 'name@path' will
1196 diverged, a new 'divergent bookmark' of the form 'name@path' will
1197 be created. Using :hg:`merge` will resolve the divergence.
1197 be created. Using :hg:`merge` will resolve the divergence.
1198
1198
1199 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1199 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1200 the active bookmark's name.
1200 the active bookmark's name.
1201
1201
1202 A bookmark named '@' has the special property that :hg:`clone` will
1202 A bookmark named '@' has the special property that :hg:`clone` will
1203 check it out by default if it exists.
1203 check it out by default if it exists.
1204
1204
1205 .. container:: verbose
1205 .. container:: verbose
1206
1206
1207 Template:
1207 Template:
1208
1208
1209 The following keywords are supported in addition to the common template
1209 The following keywords are supported in addition to the common template
1210 keywords and functions such as ``{bookmark}``. See also
1210 keywords and functions such as ``{bookmark}``. See also
1211 :hg:`help templates`.
1211 :hg:`help templates`.
1212
1212
1213 :active: Boolean. True if the bookmark is active.
1213 :active: Boolean. True if the bookmark is active.
1214
1214
1215 Examples:
1215 Examples:
1216
1216
1217 - create an active bookmark for a new line of development::
1217 - create an active bookmark for a new line of development::
1218
1218
1219 hg book new-feature
1219 hg book new-feature
1220
1220
1221 - create an inactive bookmark as a place marker::
1221 - create an inactive bookmark as a place marker::
1222
1222
1223 hg book -i reviewed
1223 hg book -i reviewed
1224
1224
1225 - create an inactive bookmark on another changeset::
1225 - create an inactive bookmark on another changeset::
1226
1226
1227 hg book -r .^ tested
1227 hg book -r .^ tested
1228
1228
1229 - rename bookmark turkey to dinner::
1229 - rename bookmark turkey to dinner::
1230
1230
1231 hg book -m turkey dinner
1231 hg book -m turkey dinner
1232
1232
1233 - move the '@' bookmark from another branch::
1233 - move the '@' bookmark from another branch::
1234
1234
1235 hg book -f @
1235 hg book -f @
1236
1236
1237 - print only the active bookmark name::
1237 - print only the active bookmark name::
1238
1238
1239 hg book -ql .
1239 hg book -ql .
1240 """
1240 """
1241 force = opts.get('force')
1241 force = opts.get('force')
1242 rev = opts.get('rev')
1242 rev = opts.get('rev')
1243 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1243 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1244
1244
1245 action = cmdutil.check_at_most_one_arg(opts, 'delete', 'rename', 'list')
1245 action = cmdutil.check_at_most_one_arg(opts, 'delete', 'rename', 'list')
1246 if action:
1246 if action:
1247 cmdutil.check_incompatible_arguments(opts, action, ['rev'])
1247 cmdutil.check_incompatible_arguments(opts, action, ['rev'])
1248 elif names or rev:
1248 elif names or rev:
1249 action = 'add'
1249 action = 'add'
1250 elif inactive:
1250 elif inactive:
1251 action = 'inactive' # meaning deactivate
1251 action = 'inactive' # meaning deactivate
1252 else:
1252 else:
1253 action = 'list'
1253 action = 'list'
1254
1254
1255 cmdutil.check_incompatible_arguments(opts, 'inactive', ['delete', 'list'])
1255 cmdutil.check_incompatible_arguments(opts, 'inactive', ['delete', 'list'])
1256 if not names and action in {'add', 'delete'}:
1256 if not names and action in {'add', 'delete'}:
1257 raise error.InputError(_(b"bookmark name required"))
1257 raise error.InputError(_(b"bookmark name required"))
1258
1258
1259 if action in {'add', 'delete', 'rename', 'inactive'}:
1259 if action in {'add', 'delete', 'rename', 'inactive'}:
1260 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1260 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1261 if action == 'delete':
1261 if action == 'delete':
1262 names = pycompat.maplist(repo._bookmarks.expandname, names)
1262 names = pycompat.maplist(repo._bookmarks.expandname, names)
1263 bookmarks.delete(repo, tr, names)
1263 bookmarks.delete(repo, tr, names)
1264 elif action == 'rename':
1264 elif action == 'rename':
1265 if not names:
1265 if not names:
1266 raise error.InputError(_(b"new bookmark name required"))
1266 raise error.InputError(_(b"new bookmark name required"))
1267 elif len(names) > 1:
1267 elif len(names) > 1:
1268 raise error.InputError(
1268 raise error.InputError(
1269 _(b"only one new bookmark name allowed")
1269 _(b"only one new bookmark name allowed")
1270 )
1270 )
1271 oldname = repo._bookmarks.expandname(opts['rename'])
1271 oldname = repo._bookmarks.expandname(opts['rename'])
1272 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1272 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1273 elif action == 'add':
1273 elif action == 'add':
1274 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1274 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1275 elif action == 'inactive':
1275 elif action == 'inactive':
1276 if len(repo._bookmarks) == 0:
1276 if len(repo._bookmarks) == 0:
1277 ui.status(_(b"no bookmarks set\n"))
1277 ui.status(_(b"no bookmarks set\n"))
1278 elif not repo._activebookmark:
1278 elif not repo._activebookmark:
1279 ui.status(_(b"no active bookmark\n"))
1279 ui.status(_(b"no active bookmark\n"))
1280 else:
1280 else:
1281 bookmarks.deactivate(repo)
1281 bookmarks.deactivate(repo)
1282 elif action == 'list':
1282 elif action == 'list':
1283 names = pycompat.maplist(repo._bookmarks.expandname, names)
1283 names = pycompat.maplist(repo._bookmarks.expandname, names)
1284 with ui.formatter(b'bookmarks', pycompat.byteskwargs(opts)) as fm:
1284 with ui.formatter(b'bookmarks', pycompat.byteskwargs(opts)) as fm:
1285 bookmarks.printbookmarks(ui, repo, fm, names)
1285 bookmarks.printbookmarks(ui, repo, fm, names)
1286 else:
1286 else:
1287 raise error.ProgrammingError(
1287 raise error.ProgrammingError(
1288 b'invalid action: %s' % pycompat.sysbytes(action)
1288 b'invalid action: %s' % pycompat.sysbytes(action)
1289 )
1289 )
1290
1290
1291
1291
1292 @command(
1292 @command(
1293 b'branch',
1293 b'branch',
1294 [
1294 [
1295 (
1295 (
1296 b'f',
1296 b'f',
1297 b'force',
1297 b'force',
1298 None,
1298 None,
1299 _(b'set branch name even if it shadows an existing branch'),
1299 _(b'set branch name even if it shadows an existing branch'),
1300 ),
1300 ),
1301 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1301 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1302 (
1302 (
1303 b'r',
1303 b'r',
1304 b'rev',
1304 b'rev',
1305 [],
1305 [],
1306 _(b'change branches of the given revs (EXPERIMENTAL)'),
1306 _(b'change branches of the given revs (EXPERIMENTAL)'),
1307 ),
1307 ),
1308 ],
1308 ],
1309 _(b'[-fC] [NAME]'),
1309 _(b'[-fC] [NAME]'),
1310 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1310 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1311 )
1311 )
1312 def branch(ui, repo, label=None, **opts):
1312 def branch(ui, repo, label=None, **opts):
1313 """set or show the current branch name
1313 """set or show the current branch name
1314
1314
1315 .. note::
1315 .. note::
1316
1316
1317 Branch names are permanent and global. Use :hg:`bookmark` to create a
1317 Branch names are permanent and global. Use :hg:`bookmark` to create a
1318 light-weight bookmark instead. See :hg:`help glossary` for more
1318 light-weight bookmark instead. See :hg:`help glossary` for more
1319 information about named branches and bookmarks.
1319 information about named branches and bookmarks.
1320
1320
1321 With no argument, show the current branch name. With one argument,
1321 With no argument, show the current branch name. With one argument,
1322 set the working directory branch name (the branch will not exist
1322 set the working directory branch name (the branch will not exist
1323 in the repository until the next commit). Standard practice
1323 in the repository until the next commit). Standard practice
1324 recommends that primary development take place on the 'default'
1324 recommends that primary development take place on the 'default'
1325 branch.
1325 branch.
1326
1326
1327 Unless -f/--force is specified, branch will not let you set a
1327 Unless -f/--force is specified, branch will not let you set a
1328 branch name that already exists.
1328 branch name that already exists.
1329
1329
1330 Use -C/--clean to reset the working directory branch to that of
1330 Use -C/--clean to reset the working directory branch to that of
1331 the parent of the working directory, negating a previous branch
1331 the parent of the working directory, negating a previous branch
1332 change.
1332 change.
1333
1333
1334 Use the command :hg:`update` to switch to an existing branch. Use
1334 Use the command :hg:`update` to switch to an existing branch. Use
1335 :hg:`commit --close-branch` to mark this branch head as closed.
1335 :hg:`commit --close-branch` to mark this branch head as closed.
1336 When all heads of a branch are closed, the branch will be
1336 When all heads of a branch are closed, the branch will be
1337 considered closed.
1337 considered closed.
1338
1338
1339 Returns 0 on success.
1339 Returns 0 on success.
1340 """
1340 """
1341 revs = opts.get('rev')
1341 revs = opts.get('rev')
1342 if label:
1342 if label:
1343 label = label.strip()
1343 label = label.strip()
1344
1344
1345 if not opts.get('clean') and not label:
1345 if not opts.get('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('clean'):
1354 if opts.get('clean'):
1355 label = repo[b'.'].branch()
1355 label = repo[b'.'].branch()
1356 repo.dirstate.setbranch(label, repo.currenttransaction())
1356 repo.dirstate.setbranch(label, repo.currenttransaction())
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('force') and label in repo.branchmap():
1364 if not opts.get('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, repo.currenttransaction())
1372 repo.dirstate.setbranch(label, repo.currenttransaction())
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 revs = opts.get('rev')
1428 revs = opts.get('rev')
1429 selectedbranches = None
1429 selectedbranches = None
1430 if revs:
1430 if revs:
1431 revs = logcmdutil.revrange(repo, revs)
1431 revs = logcmdutil.revrange(repo, revs)
1432 getbi = repo.revbranchcache().branchinfo
1432 getbi = repo.revbranchcache().branchinfo
1433 selectedbranches = {getbi(r)[0] for r in revs}
1433 selectedbranches = {getbi(r)[0] for r in revs}
1434
1434
1435 ui.pager(b'branches')
1435 ui.pager(b'branches')
1436 fm = ui.formatter(b'branches', pycompat.byteskwargs(opts))
1436 fm = ui.formatter(b'branches', pycompat.byteskwargs(opts))
1437 hexfunc = fm.hexfunc
1437 hexfunc = fm.hexfunc
1438
1438
1439 allheads = set(repo.heads())
1439 allheads = set(repo.heads())
1440 branches = []
1440 branches = []
1441 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1441 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1442 if selectedbranches is not None and tag not in selectedbranches:
1442 if selectedbranches is not None and tag not in selectedbranches:
1443 continue
1443 continue
1444 isactive = False
1444 isactive = False
1445 if not isclosed:
1445 if not isclosed:
1446 openheads = set(repo.branchmap().iteropen(heads))
1446 openheads = set(repo.branchmap().iteropen(heads))
1447 isactive = bool(openheads & allheads)
1447 isactive = bool(openheads & allheads)
1448 branches.append((tag, repo[tip], isactive, not isclosed))
1448 branches.append((tag, repo[tip], isactive, not isclosed))
1449 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1449 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1450
1450
1451 for tag, ctx, isactive, isopen in branches:
1451 for tag, ctx, isactive, isopen in branches:
1452 if active and not isactive:
1452 if active and not isactive:
1453 continue
1453 continue
1454 if isactive:
1454 if isactive:
1455 label = b'branches.active'
1455 label = b'branches.active'
1456 notice = b''
1456 notice = b''
1457 elif not isopen:
1457 elif not isopen:
1458 if not closed:
1458 if not closed:
1459 continue
1459 continue
1460 label = b'branches.closed'
1460 label = b'branches.closed'
1461 notice = _(b' (closed)')
1461 notice = _(b' (closed)')
1462 else:
1462 else:
1463 label = b'branches.inactive'
1463 label = b'branches.inactive'
1464 notice = _(b' (inactive)')
1464 notice = _(b' (inactive)')
1465 current = tag == repo.dirstate.branch()
1465 current = tag == repo.dirstate.branch()
1466 if current:
1466 if current:
1467 label = b'branches.current'
1467 label = b'branches.current'
1468
1468
1469 fm.startitem()
1469 fm.startitem()
1470 fm.write(b'branch', b'%s', tag, label=label)
1470 fm.write(b'branch', b'%s', tag, label=label)
1471 rev = ctx.rev()
1471 rev = ctx.rev()
1472 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1472 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1473 fmt = b' ' * padsize + b' %d:%s'
1473 fmt = b' ' * padsize + b' %d:%s'
1474 fm.condwrite(
1474 fm.condwrite(
1475 not ui.quiet,
1475 not ui.quiet,
1476 b'rev node',
1476 b'rev node',
1477 fmt,
1477 fmt,
1478 rev,
1478 rev,
1479 hexfunc(ctx.node()),
1479 hexfunc(ctx.node()),
1480 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1480 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1481 )
1481 )
1482 fm.context(ctx=ctx)
1482 fm.context(ctx=ctx)
1483 fm.data(active=isactive, closed=not isopen, current=current)
1483 fm.data(active=isactive, closed=not isopen, current=current)
1484 if not ui.quiet:
1484 if not ui.quiet:
1485 fm.plain(notice)
1485 fm.plain(notice)
1486 fm.plain(b'\n')
1486 fm.plain(b'\n')
1487 fm.end()
1487 fm.end()
1488
1488
1489
1489
1490 @command(
1490 @command(
1491 b'bundle',
1491 b'bundle',
1492 [
1492 [
1493 (
1493 (
1494 b'',
1494 b'',
1495 b'exact',
1495 b'exact',
1496 None,
1496 None,
1497 _(b'compute the base from the revision specified'),
1497 _(b'compute the base from the revision specified'),
1498 ),
1498 ),
1499 (
1499 (
1500 b'f',
1500 b'f',
1501 b'force',
1501 b'force',
1502 None,
1502 None,
1503 _(b'run even when the destination is unrelated'),
1503 _(b'run even when the destination is unrelated'),
1504 ),
1504 ),
1505 (
1505 (
1506 b'r',
1506 b'r',
1507 b'rev',
1507 b'rev',
1508 [],
1508 [],
1509 _(b'a changeset intended to be added to the destination'),
1509 _(b'a changeset intended to be added to the destination'),
1510 _(b'REV'),
1510 _(b'REV'),
1511 ),
1511 ),
1512 (
1512 (
1513 b'b',
1513 b'b',
1514 b'branch',
1514 b'branch',
1515 [],
1515 [],
1516 _(b'a specific branch you would like to bundle'),
1516 _(b'a specific branch you would like to bundle'),
1517 _(b'BRANCH'),
1517 _(b'BRANCH'),
1518 ),
1518 ),
1519 (
1519 (
1520 b'',
1520 b'',
1521 b'base',
1521 b'base',
1522 [],
1522 [],
1523 _(b'a base changeset assumed to be available at the destination'),
1523 _(b'a base changeset assumed to be available at the destination'),
1524 _(b'REV'),
1524 _(b'REV'),
1525 ),
1525 ),
1526 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1526 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1527 (
1527 (
1528 b't',
1528 b't',
1529 b'type',
1529 b'type',
1530 b'bzip2',
1530 b'bzip2',
1531 _(b'bundle compression type to use'),
1531 _(b'bundle compression type to use'),
1532 _(b'TYPE'),
1532 _(b'TYPE'),
1533 ),
1533 ),
1534 ]
1534 ]
1535 + remoteopts,
1535 + remoteopts,
1536 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1536 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1537 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1537 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1538 )
1538 )
1539 def bundle(ui, repo, fname, *dests, **opts):
1539 def bundle(ui, repo, fname, *dests, **opts):
1540 """create a bundle file
1540 """create a bundle file
1541
1541
1542 Generate a bundle file containing data to be transferred to another
1542 Generate a bundle file containing data to be transferred to another
1543 repository.
1543 repository.
1544
1544
1545 To create a bundle containing all changesets, use -a/--all
1545 To create a bundle containing all changesets, use -a/--all
1546 (or --base null). Otherwise, hg assumes the destination will have
1546 (or --base null). Otherwise, hg assumes the destination will have
1547 all the nodes you specify with --base parameters. Otherwise, hg
1547 all the nodes you specify with --base parameters. Otherwise, hg
1548 will assume the repository has all the nodes in destination, or
1548 will assume the repository has all the nodes in destination, or
1549 default-push/default if no destination is specified, where destination
1549 default-push/default if no destination is specified, where destination
1550 is the repositories you provide through DEST option.
1550 is the repositories you provide through DEST option.
1551
1551
1552 You can change bundle format with the -t/--type option. See
1552 You can change bundle format with the -t/--type option. See
1553 :hg:`help bundlespec` for documentation on this format. By default,
1553 :hg:`help bundlespec` for documentation on this format. By default,
1554 the most appropriate format is used and compression defaults to
1554 the most appropriate format is used and compression defaults to
1555 bzip2.
1555 bzip2.
1556
1556
1557 The bundle file can then be transferred using conventional means
1557 The bundle file can then be transferred using conventional means
1558 and applied to another repository with the unbundle or pull
1558 and applied to another repository with the unbundle or pull
1559 command. This is useful when direct push and pull are not
1559 command. This is useful when direct push and pull are not
1560 available or when exporting an entire repository is undesirable.
1560 available or when exporting an entire repository is undesirable.
1561
1561
1562 Applying bundles preserves all changeset contents including
1562 Applying bundles preserves all changeset contents including
1563 permissions, copy/rename information, and revision history.
1563 permissions, copy/rename information, and revision history.
1564
1564
1565 Returns 0 on success, 1 if no changes found.
1565 Returns 0 on success, 1 if no changes found.
1566 """
1566 """
1567
1567
1568 revs = None
1568 revs = None
1569 if 'rev' in opts:
1569 if 'rev' in opts:
1570 revstrings = opts['rev']
1570 revstrings = opts['rev']
1571 revs = logcmdutil.revrange(repo, revstrings)
1571 revs = logcmdutil.revrange(repo, revstrings)
1572 if revstrings and not revs:
1572 if revstrings and not revs:
1573 raise error.InputError(_(b'no commits to bundle'))
1573 raise error.InputError(_(b'no commits to bundle'))
1574
1574
1575 bundletype = opts.get('type', b'bzip2').lower()
1575 bundletype = opts.get('type', b'bzip2').lower()
1576 try:
1576 try:
1577 bundlespec = bundlecaches.parsebundlespec(
1577 bundlespec = bundlecaches.parsebundlespec(
1578 repo, bundletype, strict=False
1578 repo, bundletype, strict=False
1579 )
1579 )
1580 except error.UnsupportedBundleSpecification as e:
1580 except error.UnsupportedBundleSpecification as e:
1581 raise error.InputError(
1581 raise error.InputError(
1582 pycompat.bytestr(e),
1582 pycompat.bytestr(e),
1583 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1583 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1584 )
1584 )
1585 cgversion = bundlespec.params[b"cg.version"]
1585 cgversion = bundlespec.params[b"cg.version"]
1586
1586
1587 # Packed bundles are a pseudo bundle format for now.
1587 # Packed bundles are a pseudo bundle format for now.
1588 if cgversion == b's1':
1588 if cgversion == b's1':
1589 raise error.InputError(
1589 raise error.InputError(
1590 _(b'packed bundles cannot be produced by "hg bundle"'),
1590 _(b'packed bundles cannot be produced by "hg bundle"'),
1591 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1591 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1592 )
1592 )
1593
1593
1594 if opts.get('all'):
1594 if opts.get('all'):
1595 if dests:
1595 if dests:
1596 raise error.InputError(
1596 raise error.InputError(
1597 _(b"--all is incompatible with specifying destinations")
1597 _(b"--all is incompatible with specifying destinations")
1598 )
1598 )
1599 if opts.get('base'):
1599 if opts.get('base'):
1600 ui.warn(_(b"ignoring --base because --all was specified\n"))
1600 ui.warn(_(b"ignoring --base because --all was specified\n"))
1601 if opts.get('exact'):
1601 if opts.get('exact'):
1602 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1602 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1603 base = [nullrev]
1603 base = [nullrev]
1604 elif opts.get('exact'):
1604 elif opts.get('exact'):
1605 if dests:
1605 if dests:
1606 raise error.InputError(
1606 raise error.InputError(
1607 _(b"--exact is incompatible with specifying destinations")
1607 _(b"--exact is incompatible with specifying destinations")
1608 )
1608 )
1609 if opts.get('base'):
1609 if opts.get('base'):
1610 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1610 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1611 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1611 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1612 if not base:
1612 if not base:
1613 base = [nullrev]
1613 base = [nullrev]
1614 else:
1614 else:
1615 base = logcmdutil.revrange(repo, opts.get('base'))
1615 base = logcmdutil.revrange(repo, opts.get('base'))
1616 if cgversion not in changegroup.supportedoutgoingversions(repo):
1616 if cgversion not in changegroup.supportedoutgoingversions(repo):
1617 raise error.Abort(
1617 raise error.Abort(
1618 _(b"repository does not support bundle version %s") % cgversion
1618 _(b"repository does not support bundle version %s") % cgversion
1619 )
1619 )
1620
1620
1621 if base:
1621 if base:
1622 if dests:
1622 if dests:
1623 raise error.InputError(
1623 raise error.InputError(
1624 _(b"--base is incompatible with specifying destinations")
1624 _(b"--base is incompatible with specifying destinations")
1625 )
1625 )
1626 cl = repo.changelog
1626 cl = repo.changelog
1627 common = [cl.node(rev) for rev in base]
1627 common = [cl.node(rev) for rev in base]
1628 heads = [cl.node(r) for r in revs] if revs else None
1628 heads = [cl.node(r) for r in revs] if revs else None
1629 outgoing = discovery.outgoing(repo, common, heads)
1629 outgoing = discovery.outgoing(repo, common, heads)
1630 missing = outgoing.missing
1630 missing = outgoing.missing
1631 excluded = outgoing.excluded
1631 excluded = outgoing.excluded
1632 else:
1632 else:
1633 missing = set()
1633 missing = set()
1634 excluded = set()
1634 excluded = set()
1635 for path in urlutil.get_push_paths(repo, ui, dests):
1635 for path in urlutil.get_push_paths(repo, ui, dests):
1636 other = hg.peer(repo, pycompat.byteskwargs(opts), path)
1636 other = hg.peer(repo, pycompat.byteskwargs(opts), path)
1637 if revs is not None:
1637 if revs is not None:
1638 hex_revs = [repo[r].hex() for r in revs]
1638 hex_revs = [repo[r].hex() for r in revs]
1639 else:
1639 else:
1640 hex_revs = None
1640 hex_revs = None
1641 branches = (path.branch, [])
1641 branches = (path.branch, [])
1642 head_revs, checkout = hg.addbranchrevs(
1642 head_revs, checkout = hg.addbranchrevs(
1643 repo, repo, branches, hex_revs
1643 repo, repo, branches, hex_revs
1644 )
1644 )
1645 heads = (
1645 heads = (
1646 head_revs
1646 head_revs
1647 and pycompat.maplist(repo.lookup, head_revs)
1647 and pycompat.maplist(repo.lookup, head_revs)
1648 or head_revs
1648 or head_revs
1649 )
1649 )
1650 outgoing = discovery.findcommonoutgoing(
1650 outgoing = discovery.findcommonoutgoing(
1651 repo,
1651 repo,
1652 other,
1652 other,
1653 onlyheads=heads,
1653 onlyheads=heads,
1654 force=opts.get('force'),
1654 force=opts.get('force'),
1655 portable=True,
1655 portable=True,
1656 )
1656 )
1657 missing.update(outgoing.missing)
1657 missing.update(outgoing.missing)
1658 excluded.update(outgoing.excluded)
1658 excluded.update(outgoing.excluded)
1659
1659
1660 if not missing:
1660 if not missing:
1661 scmutil.nochangesfound(ui, repo, not base and excluded)
1661 scmutil.nochangesfound(ui, repo, not base and excluded)
1662 return 1
1662 return 1
1663
1663
1664 # internal changeset are internal implementation details that should not
1664 # internal changeset are internal implementation details that should not
1665 # leave the repository. Bundling with `hg bundle` create such risk.
1665 # leave the repository. Bundling with `hg bundle` create such risk.
1666 bundled_internal = repo.revs(b"%ln and _internal()", missing)
1666 bundled_internal = repo.revs(b"%ln and _internal()", missing)
1667 if bundled_internal:
1667 if bundled_internal:
1668 msg = _(b"cannot bundle internal changesets")
1668 msg = _(b"cannot bundle internal changesets")
1669 hint = _(b"%d internal changesets selected") % len(bundled_internal)
1669 hint = _(b"%d internal changesets selected") % len(bundled_internal)
1670 raise error.Abort(msg, hint=hint)
1670 raise error.Abort(msg, hint=hint)
1671
1671
1672 if heads:
1672 if heads:
1673 outgoing = discovery.outgoing(
1673 outgoing = discovery.outgoing(
1674 repo, missingroots=missing, ancestorsof=heads
1674 repo, missingroots=missing, ancestorsof=heads
1675 )
1675 )
1676 else:
1676 else:
1677 outgoing = discovery.outgoing(repo, missingroots=missing)
1677 outgoing = discovery.outgoing(repo, missingroots=missing)
1678 outgoing.excluded = sorted(excluded)
1678 outgoing.excluded = sorted(excluded)
1679
1679
1680 if cgversion == b'01': # bundle1
1680 if cgversion == b'01': # bundle1
1681 bversion = b'HG10' + bundlespec.wirecompression
1681 bversion = b'HG10' + bundlespec.wirecompression
1682 bcompression = None
1682 bcompression = None
1683 elif cgversion in (b'02', b'03'):
1683 elif cgversion in (b'02', b'03'):
1684 bversion = b'HG20'
1684 bversion = b'HG20'
1685 bcompression = bundlespec.wirecompression
1685 bcompression = bundlespec.wirecompression
1686 else:
1686 else:
1687 raise error.ProgrammingError(
1687 raise error.ProgrammingError(
1688 b'bundle: unexpected changegroup version %s' % cgversion
1688 b'bundle: unexpected changegroup version %s' % cgversion
1689 )
1689 )
1690
1690
1691 # TODO compression options should be derived from bundlespec parsing.
1691 # TODO compression options should be derived from bundlespec parsing.
1692 # This is a temporary hack to allow adjusting bundle compression
1692 # This is a temporary hack to allow adjusting bundle compression
1693 # level without a) formalizing the bundlespec changes to declare it
1693 # level without a) formalizing the bundlespec changes to declare it
1694 # b) introducing a command flag.
1694 # b) introducing a command flag.
1695 compopts = {}
1695 compopts = {}
1696 complevel = ui.configint(
1696 complevel = ui.configint(
1697 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1697 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1698 )
1698 )
1699 if complevel is None:
1699 if complevel is None:
1700 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1700 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1701 if complevel is not None:
1701 if complevel is not None:
1702 compopts[b'level'] = complevel
1702 compopts[b'level'] = complevel
1703
1703
1704 compthreads = ui.configint(
1704 compthreads = ui.configint(
1705 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1705 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1706 )
1706 )
1707 if compthreads is None:
1707 if compthreads is None:
1708 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1708 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1709 if compthreads is not None:
1709 if compthreads is not None:
1710 compopts[b'threads'] = compthreads
1710 compopts[b'threads'] = compthreads
1711
1711
1712 # Bundling of obsmarker and phases is optional as not all clients
1712 # Bundling of obsmarker and phases is optional as not all clients
1713 # support the necessary features.
1713 # support the necessary features.
1714 cfg = ui.configbool
1714 cfg = ui.configbool
1715 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1715 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1716 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1716 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1717 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1717 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1718 bundlespec.set_param(
1718 bundlespec.set_param(
1719 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1719 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1720 )
1720 )
1721 if not bundlespec.params.get(b'phases', False):
1721 if not bundlespec.params.get(b'phases', False):
1722 phases_cfg = cfg(b'experimental', b'bundle-phases')
1722 phases_cfg = cfg(b'experimental', b'bundle-phases')
1723 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1723 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1724
1724
1725 bundle2.writenewbundle(
1725 bundle2.writenewbundle(
1726 ui,
1726 ui,
1727 repo,
1727 repo,
1728 b'bundle',
1728 b'bundle',
1729 fname,
1729 fname,
1730 bversion,
1730 bversion,
1731 outgoing,
1731 outgoing,
1732 bundlespec.params,
1732 bundlespec.params,
1733 compression=bcompression,
1733 compression=bcompression,
1734 compopts=compopts,
1734 compopts=compopts,
1735 )
1735 )
1736
1736
1737
1737
1738 @command(
1738 @command(
1739 b'cat',
1739 b'cat',
1740 [
1740 [
1741 (
1741 (
1742 b'o',
1742 b'o',
1743 b'output',
1743 b'output',
1744 b'',
1744 b'',
1745 _(b'print output to file with formatted name'),
1745 _(b'print output to file with formatted name'),
1746 _(b'FORMAT'),
1746 _(b'FORMAT'),
1747 ),
1747 ),
1748 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1748 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1749 (b'', b'decode', None, _(b'apply any matching decode filter')),
1749 (b'', b'decode', None, _(b'apply any matching decode filter')),
1750 ]
1750 ]
1751 + walkopts
1751 + walkopts
1752 + formatteropts,
1752 + formatteropts,
1753 _(b'[OPTION]... FILE...'),
1753 _(b'[OPTION]... FILE...'),
1754 helpcategory=command.CATEGORY_FILE_CONTENTS,
1754 helpcategory=command.CATEGORY_FILE_CONTENTS,
1755 inferrepo=True,
1755 inferrepo=True,
1756 intents={INTENT_READONLY},
1756 intents={INTENT_READONLY},
1757 )
1757 )
1758 def cat(ui, repo, file1, *pats, **opts):
1758 def cat(ui, repo, file1, *pats, **opts):
1759 """output the current or given revision of files
1759 """output the current or given revision of files
1760
1760
1761 Print the specified files as they were at the given revision. If
1761 Print the specified files as they were at the given revision. If
1762 no revision is given, the parent of the working directory is used.
1762 no revision is given, the parent of the working directory is used.
1763
1763
1764 Output may be to a file, in which case the name of the file is
1764 Output may be to a file, in which case the name of the file is
1765 given using a template string. See :hg:`help templates`. In addition
1765 given using a template string. See :hg:`help templates`. In addition
1766 to the common template keywords, the following formatting rules are
1766 to the common template keywords, the following formatting rules are
1767 supported:
1767 supported:
1768
1768
1769 :``%%``: literal "%" character
1769 :``%%``: literal "%" character
1770 :``%s``: basename of file being printed
1770 :``%s``: basename of file being printed
1771 :``%d``: dirname of file being printed, or '.' if in repository root
1771 :``%d``: dirname of file being printed, or '.' if in repository root
1772 :``%p``: root-relative path name of file being printed
1772 :``%p``: root-relative path name of file being printed
1773 :``%H``: changeset hash (40 hexadecimal digits)
1773 :``%H``: changeset hash (40 hexadecimal digits)
1774 :``%R``: changeset revision number
1774 :``%R``: changeset revision number
1775 :``%h``: short-form changeset hash (12 hexadecimal digits)
1775 :``%h``: short-form changeset hash (12 hexadecimal digits)
1776 :``%r``: zero-padded changeset revision number
1776 :``%r``: zero-padded changeset revision number
1777 :``%b``: basename of the exporting repository
1777 :``%b``: basename of the exporting repository
1778 :``\\``: literal "\\" character
1778 :``\\``: literal "\\" character
1779
1779
1780 .. container:: verbose
1780 .. container:: verbose
1781
1781
1782 Template:
1782 Template:
1783
1783
1784 The following keywords are supported in addition to the common template
1784 The following keywords are supported in addition to the common template
1785 keywords and functions. See also :hg:`help templates`.
1785 keywords and functions. See also :hg:`help templates`.
1786
1786
1787 :data: String. File content.
1787 :data: String. File content.
1788 :path: String. Repository-absolute path of the file.
1788 :path: String. Repository-absolute path of the file.
1789
1789
1790 Returns 0 on success.
1790 Returns 0 on success.
1791 """
1791 """
1792 rev = opts.get('rev')
1792 rev = opts.get('rev')
1793 if rev:
1793 if rev:
1794 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1794 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1795 ctx = logcmdutil.revsingle(repo, rev)
1795 ctx = logcmdutil.revsingle(repo, rev)
1796 m = scmutil.match(ctx, (file1,) + pats, pycompat.byteskwargs(opts))
1796 m = scmutil.match(ctx, (file1,) + pats, pycompat.byteskwargs(opts))
1797 fntemplate = opts.pop('output', b'')
1797 fntemplate = opts.pop('output', b'')
1798 if cmdutil.isstdiofilename(fntemplate):
1798 if cmdutil.isstdiofilename(fntemplate):
1799 fntemplate = b''
1799 fntemplate = b''
1800
1800
1801 if fntemplate:
1801 if fntemplate:
1802 fm = formatter.nullformatter(ui, b'cat', pycompat.byteskwargs(opts))
1802 fm = formatter.nullformatter(ui, b'cat', pycompat.byteskwargs(opts))
1803 else:
1803 else:
1804 ui.pager(b'cat')
1804 ui.pager(b'cat')
1805 fm = ui.formatter(b'cat', pycompat.byteskwargs(opts))
1805 fm = ui.formatter(b'cat', pycompat.byteskwargs(opts))
1806 with fm:
1806 with fm:
1807 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, b'', **opts)
1807 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, b'', **opts)
1808
1808
1809
1809
1810 @command(
1810 @command(
1811 b'clone',
1811 b'clone',
1812 [
1812 [
1813 (
1813 (
1814 b'U',
1814 b'U',
1815 b'noupdate',
1815 b'noupdate',
1816 None,
1816 None,
1817 _(
1817 _(
1818 b'the clone will include an empty working '
1818 b'the clone will include an empty working '
1819 b'directory (only a repository)'
1819 b'directory (only a repository)'
1820 ),
1820 ),
1821 ),
1821 ),
1822 (
1822 (
1823 b'u',
1823 b'u',
1824 b'updaterev',
1824 b'updaterev',
1825 b'',
1825 b'',
1826 _(b'revision, tag, or branch to check out'),
1826 _(b'revision, tag, or branch to check out'),
1827 _(b'REV'),
1827 _(b'REV'),
1828 ),
1828 ),
1829 (
1829 (
1830 b'r',
1830 b'r',
1831 b'rev',
1831 b'rev',
1832 [],
1832 [],
1833 _(
1833 _(
1834 b'do not clone everything, but include this changeset'
1834 b'do not clone everything, but include this changeset'
1835 b' and its ancestors'
1835 b' and its ancestors'
1836 ),
1836 ),
1837 _(b'REV'),
1837 _(b'REV'),
1838 ),
1838 ),
1839 (
1839 (
1840 b'b',
1840 b'b',
1841 b'branch',
1841 b'branch',
1842 [],
1842 [],
1843 _(
1843 _(
1844 b'do not clone everything, but include this branch\'s'
1844 b'do not clone everything, but include this branch\'s'
1845 b' changesets and their ancestors'
1845 b' changesets and their ancestors'
1846 ),
1846 ),
1847 _(b'BRANCH'),
1847 _(b'BRANCH'),
1848 ),
1848 ),
1849 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1849 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1850 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1850 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1851 (b'', b'stream', None, _(b'clone with minimal data processing')),
1851 (b'', b'stream', None, _(b'clone with minimal data processing')),
1852 ]
1852 ]
1853 + remoteopts,
1853 + remoteopts,
1854 _(b'[OPTION]... SOURCE [DEST]'),
1854 _(b'[OPTION]... SOURCE [DEST]'),
1855 helpcategory=command.CATEGORY_REPO_CREATION,
1855 helpcategory=command.CATEGORY_REPO_CREATION,
1856 helpbasic=True,
1856 helpbasic=True,
1857 norepo=True,
1857 norepo=True,
1858 )
1858 )
1859 def clone(ui, source, dest=None, **opts):
1859 def clone(ui, source, dest=None, **opts):
1860 """make a copy of an existing repository
1860 """make a copy of an existing repository
1861
1861
1862 Create a copy of an existing repository in a new directory.
1862 Create a copy of an existing repository in a new directory.
1863
1863
1864 If no destination directory name is specified, it defaults to the
1864 If no destination directory name is specified, it defaults to the
1865 basename of the source.
1865 basename of the source.
1866
1866
1867 The location of the source is added to the new repository's
1867 The location of the source is added to the new repository's
1868 ``.hg/hgrc`` file, as the default to be used for future pulls.
1868 ``.hg/hgrc`` file, as the default to be used for future pulls.
1869
1869
1870 Only local paths and ``ssh://`` URLs are supported as
1870 Only local paths and ``ssh://`` URLs are supported as
1871 destinations. For ``ssh://`` destinations, no working directory or
1871 destinations. For ``ssh://`` destinations, no working directory or
1872 ``.hg/hgrc`` will be created on the remote side.
1872 ``.hg/hgrc`` will be created on the remote side.
1873
1873
1874 If the source repository has a bookmark called '@' set, that
1874 If the source repository has a bookmark called '@' set, that
1875 revision will be checked out in the new repository by default.
1875 revision will be checked out in the new repository by default.
1876
1876
1877 To check out a particular version, use -u/--update, or
1877 To check out a particular version, use -u/--update, or
1878 -U/--noupdate to create a clone with no working directory.
1878 -U/--noupdate to create a clone with no working directory.
1879
1879
1880 To pull only a subset of changesets, specify one or more revisions
1880 To pull only a subset of changesets, specify one or more revisions
1881 identifiers with -r/--rev or branches with -b/--branch. The
1881 identifiers with -r/--rev or branches with -b/--branch. The
1882 resulting clone will contain only the specified changesets and
1882 resulting clone will contain only the specified changesets and
1883 their ancestors. These options (or 'clone src#rev dest') imply
1883 their ancestors. These options (or 'clone src#rev dest') imply
1884 --pull, even for local source repositories.
1884 --pull, even for local source repositories.
1885
1885
1886 In normal clone mode, the remote normalizes repository data into a common
1886 In normal clone mode, the remote normalizes repository data into a common
1887 exchange format and the receiving end translates this data into its local
1887 exchange format and the receiving end translates this data into its local
1888 storage format. --stream activates a different clone mode that essentially
1888 storage format. --stream activates a different clone mode that essentially
1889 copies repository files from the remote with minimal data processing. This
1889 copies repository files from the remote with minimal data processing. This
1890 significantly reduces the CPU cost of a clone both remotely and locally.
1890 significantly reduces the CPU cost of a clone both remotely and locally.
1891 However, it often increases the transferred data size by 30-40%. This can
1891 However, it often increases the transferred data size by 30-40%. This can
1892 result in substantially faster clones where I/O throughput is plentiful,
1892 result in substantially faster clones where I/O throughput is plentiful,
1893 especially for larger repositories. A side-effect of --stream clones is
1893 especially for larger repositories. A side-effect of --stream clones is
1894 that storage settings and requirements on the remote are applied locally:
1894 that storage settings and requirements on the remote are applied locally:
1895 a modern client may inherit legacy or inefficient storage used by the
1895 a modern client may inherit legacy or inefficient storage used by the
1896 remote or a legacy Mercurial client may not be able to clone from a
1896 remote or a legacy Mercurial client may not be able to clone from a
1897 modern Mercurial remote.
1897 modern Mercurial remote.
1898
1898
1899 .. note::
1899 .. note::
1900
1900
1901 Specifying a tag will include the tagged changeset but not the
1901 Specifying a tag will include the tagged changeset but not the
1902 changeset containing the tag.
1902 changeset containing the tag.
1903
1903
1904 .. container:: verbose
1904 .. container:: verbose
1905
1905
1906 For efficiency, hardlinks are used for cloning whenever the
1906 For efficiency, hardlinks are used for cloning whenever the
1907 source and destination are on the same filesystem (note this
1907 source and destination are on the same filesystem (note this
1908 applies only to the repository data, not to the working
1908 applies only to the repository data, not to the working
1909 directory). Some filesystems, such as AFS, implement hardlinking
1909 directory). Some filesystems, such as AFS, implement hardlinking
1910 incorrectly, but do not report errors. In these cases, use the
1910 incorrectly, but do not report errors. In these cases, use the
1911 --pull option to avoid hardlinking.
1911 --pull option to avoid hardlinking.
1912
1912
1913 Mercurial will update the working directory to the first applicable
1913 Mercurial will update the working directory to the first applicable
1914 revision from this list:
1914 revision from this list:
1915
1915
1916 a) null if -U or the source repository has no changesets
1916 a) null if -U or the source repository has no changesets
1917 b) if -u . and the source repository is local, the first parent of
1917 b) if -u . and the source repository is local, the first parent of
1918 the source repository's working directory
1918 the source repository's working directory
1919 c) the changeset specified with -u (if a branch name, this means the
1919 c) the changeset specified with -u (if a branch name, this means the
1920 latest head of that branch)
1920 latest head of that branch)
1921 d) the changeset specified with -r
1921 d) the changeset specified with -r
1922 e) the tipmost head specified with -b
1922 e) the tipmost head specified with -b
1923 f) the tipmost head specified with the url#branch source syntax
1923 f) the tipmost head specified with the url#branch source syntax
1924 g) the revision marked with the '@' bookmark, if present
1924 g) the revision marked with the '@' bookmark, if present
1925 h) the tipmost head of the default branch
1925 h) the tipmost head of the default branch
1926 i) tip
1926 i) tip
1927
1927
1928 When cloning from servers that support it, Mercurial may fetch
1928 When cloning from servers that support it, Mercurial may fetch
1929 pre-generated data from a server-advertised URL or inline from the
1929 pre-generated data from a server-advertised URL or inline from the
1930 same stream. When this is done, hooks operating on incoming changesets
1930 same stream. When this is done, hooks operating on incoming changesets
1931 and changegroups may fire more than once, once for each pre-generated
1931 and changegroups may fire more than once, once for each pre-generated
1932 bundle and as well as for any additional remaining data. In addition,
1932 bundle and as well as for any additional remaining data. In addition,
1933 if an error occurs, the repository may be rolled back to a partial
1933 if an error occurs, the repository may be rolled back to a partial
1934 clone. This behavior may change in future releases.
1934 clone. This behavior may change in future releases.
1935 See :hg:`help -e clonebundles` for more.
1935 See :hg:`help -e clonebundles` for more.
1936
1936
1937 Examples:
1937 Examples:
1938
1938
1939 - clone a remote repository to a new directory named hg/::
1939 - clone a remote repository to a new directory named hg/::
1940
1940
1941 hg clone https://www.mercurial-scm.org/repo/hg/
1941 hg clone https://www.mercurial-scm.org/repo/hg/
1942
1942
1943 - create a lightweight local clone::
1943 - create a lightweight local clone::
1944
1944
1945 hg clone project/ project-feature/
1945 hg clone project/ project-feature/
1946
1946
1947 - clone from an absolute path on an ssh server (note double-slash)::
1947 - clone from an absolute path on an ssh server (note double-slash)::
1948
1948
1949 hg clone ssh://user@server//home/projects/alpha/
1949 hg clone ssh://user@server//home/projects/alpha/
1950
1950
1951 - do a streaming clone while checking out a specified version::
1951 - do a streaming clone while checking out a specified version::
1952
1952
1953 hg clone --stream http://server/repo -u 1.5
1953 hg clone --stream http://server/repo -u 1.5
1954
1954
1955 - create a repository without changesets after a particular revision::
1955 - create a repository without changesets after a particular revision::
1956
1956
1957 hg clone -r 04e544 experimental/ good/
1957 hg clone -r 04e544 experimental/ good/
1958
1958
1959 - clone (and track) a particular named branch::
1959 - clone (and track) a particular named branch::
1960
1960
1961 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1961 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1962
1962
1963 See :hg:`help urls` for details on specifying URLs.
1963 See :hg:`help urls` for details on specifying URLs.
1964
1964
1965 Returns 0 on success.
1965 Returns 0 on success.
1966 """
1966 """
1967 cmdutil.check_at_most_one_arg(opts, 'noupdate', 'updaterev')
1967 cmdutil.check_at_most_one_arg(opts, 'noupdate', 'updaterev')
1968
1968
1969 # --include/--exclude can come from narrow or sparse.
1969 # --include/--exclude can come from narrow or sparse.
1970 includepats, excludepats = None, None
1970 includepats, excludepats = None, None
1971
1971
1972 # hg.clone() differentiates between None and an empty set. So make sure
1972 # hg.clone() differentiates between None and an empty set. So make sure
1973 # patterns are sets if narrow is requested without patterns.
1973 # patterns are sets if narrow is requested without patterns.
1974 if opts.get('narrow'):
1974 if opts.get('narrow'):
1975 includepats = set()
1975 includepats = set()
1976 excludepats = set()
1976 excludepats = set()
1977
1977
1978 if opts.get('include'):
1978 if opts.get('include'):
1979 includepats = narrowspec.parsepatterns(opts.get('include'))
1979 includepats = narrowspec.parsepatterns(opts.get('include'))
1980 if opts.get('exclude'):
1980 if opts.get('exclude'):
1981 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1981 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1982
1982
1983 r = hg.clone(
1983 r = hg.clone(
1984 ui,
1984 ui,
1985 pycompat.byteskwargs(opts),
1985 pycompat.byteskwargs(opts),
1986 source,
1986 source,
1987 dest,
1987 dest,
1988 pull=opts.get('pull'),
1988 pull=opts.get('pull'),
1989 stream=opts.get('stream') or opts.get('uncompressed'),
1989 stream=opts.get('stream') or opts.get('uncompressed'),
1990 revs=opts.get('rev'),
1990 revs=opts.get('rev'),
1991 update=opts.get('updaterev') or not opts.get('noupdate'),
1991 update=opts.get('updaterev') or not opts.get('noupdate'),
1992 branch=opts.get('branch'),
1992 branch=opts.get('branch'),
1993 shareopts=opts.get('shareopts'),
1993 shareopts=opts.get('shareopts'),
1994 storeincludepats=includepats,
1994 storeincludepats=includepats,
1995 storeexcludepats=excludepats,
1995 storeexcludepats=excludepats,
1996 depth=opts.get('depth') or None,
1996 depth=opts.get('depth') or None,
1997 )
1997 )
1998
1998
1999 return r is None
1999 return r is None
2000
2000
2001
2001
2002 @command(
2002 @command(
2003 b'commit|ci',
2003 b'commit|ci',
2004 [
2004 [
2005 (
2005 (
2006 b'A',
2006 b'A',
2007 b'addremove',
2007 b'addremove',
2008 None,
2008 None,
2009 _(b'mark new/missing files as added/removed before committing'),
2009 _(b'mark new/missing files as added/removed before committing'),
2010 ),
2010 ),
2011 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2011 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2012 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2012 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2013 (b's', b'secret', None, _(b'use the secret phase for committing')),
2013 (b's', b'secret', None, _(b'use the secret phase for committing')),
2014 (b'', b'draft', None, _(b'use the draft phase for committing')),
2014 (b'', b'draft', None, _(b'use the draft phase for committing')),
2015 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2015 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2016 (
2016 (
2017 b'',
2017 b'',
2018 b'force-close-branch',
2018 b'force-close-branch',
2019 None,
2019 None,
2020 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2020 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2021 ),
2021 ),
2022 (b'i', b'interactive', None, _(b'use interactive mode')),
2022 (b'i', b'interactive', None, _(b'use interactive mode')),
2023 ]
2023 ]
2024 + walkopts
2024 + walkopts
2025 + commitopts
2025 + commitopts
2026 + commitopts2
2026 + commitopts2
2027 + subrepoopts,
2027 + subrepoopts,
2028 _(b'[OPTION]... [FILE]...'),
2028 _(b'[OPTION]... [FILE]...'),
2029 helpcategory=command.CATEGORY_COMMITTING,
2029 helpcategory=command.CATEGORY_COMMITTING,
2030 helpbasic=True,
2030 helpbasic=True,
2031 inferrepo=True,
2031 inferrepo=True,
2032 )
2032 )
2033 def commit(ui, repo, *pats, **opts):
2033 def commit(ui, repo, *pats, **opts):
2034 """commit the specified files or all outstanding changes
2034 """commit the specified files or all outstanding changes
2035
2035
2036 Commit changes to the given files into the repository. Unlike a
2036 Commit changes to the given files into the repository. Unlike a
2037 centralized SCM, this operation is a local operation. See
2037 centralized SCM, this operation is a local operation. See
2038 :hg:`push` for a way to actively distribute your changes.
2038 :hg:`push` for a way to actively distribute your changes.
2039
2039
2040 If a list of files is omitted, all changes reported by :hg:`status`
2040 If a list of files is omitted, all changes reported by :hg:`status`
2041 will be committed.
2041 will be committed.
2042
2042
2043 If you are committing the result of a merge, do not provide any
2043 If you are committing the result of a merge, do not provide any
2044 filenames or -I/-X filters.
2044 filenames or -I/-X filters.
2045
2045
2046 If no commit message is specified, Mercurial starts your
2046 If no commit message is specified, Mercurial starts your
2047 configured editor where you can enter a message. In case your
2047 configured editor where you can enter a message. In case your
2048 commit fails, you will find a backup of your message in
2048 commit fails, you will find a backup of your message in
2049 ``.hg/last-message.txt``.
2049 ``.hg/last-message.txt``.
2050
2050
2051 The --close-branch flag can be used to mark the current branch
2051 The --close-branch flag can be used to mark the current branch
2052 head closed. When all heads of a branch are closed, the branch
2052 head closed. When all heads of a branch are closed, the branch
2053 will be considered closed and no longer listed.
2053 will be considered closed and no longer listed.
2054
2054
2055 The --amend flag can be used to amend the parent of the
2055 The --amend flag can be used to amend the parent of the
2056 working directory with a new commit that contains the changes
2056 working directory with a new commit that contains the changes
2057 in the parent in addition to those currently reported by :hg:`status`,
2057 in the parent in addition to those currently reported by :hg:`status`,
2058 if there are any. The old commit is stored in a backup bundle in
2058 if there are any. The old commit is stored in a backup bundle in
2059 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2059 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2060 on how to restore it).
2060 on how to restore it).
2061
2061
2062 Message, user and date are taken from the amended commit unless
2062 Message, user and date are taken from the amended commit unless
2063 specified. When a message isn't specified on the command line,
2063 specified. When a message isn't specified on the command line,
2064 the editor will open with the message of the amended commit.
2064 the editor will open with the message of the amended commit.
2065
2065
2066 It is not possible to amend public changesets (see :hg:`help phases`)
2066 It is not possible to amend public changesets (see :hg:`help phases`)
2067 or changesets that have children.
2067 or changesets that have children.
2068
2068
2069 See :hg:`help dates` for a list of formats valid for -d/--date.
2069 See :hg:`help dates` for a list of formats valid for -d/--date.
2070
2070
2071 Returns 0 on success, 1 if nothing changed.
2071 Returns 0 on success, 1 if nothing changed.
2072
2072
2073 .. container:: verbose
2073 .. container:: verbose
2074
2074
2075 Examples:
2075 Examples:
2076
2076
2077 - commit all files ending in .py::
2077 - commit all files ending in .py::
2078
2078
2079 hg commit --include "set:**.py"
2079 hg commit --include "set:**.py"
2080
2080
2081 - commit all non-binary files::
2081 - commit all non-binary files::
2082
2082
2083 hg commit --exclude "set:binary()"
2083 hg commit --exclude "set:binary()"
2084
2084
2085 - amend the current commit and set the date to now::
2085 - amend the current commit and set the date to now::
2086
2086
2087 hg commit --amend --date now
2087 hg commit --amend --date now
2088 """
2088 """
2089 cmdutil.check_at_most_one_arg(opts, 'draft', 'secret')
2089 cmdutil.check_at_most_one_arg(opts, 'draft', 'secret')
2090 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2090 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2091 with repo.wlock(), repo.lock():
2091 with repo.wlock(), repo.lock():
2092 return _docommit(ui, repo, *pats, **opts)
2092 return _docommit(ui, repo, *pats, **opts)
2093
2093
2094
2094
2095 def _docommit(ui, repo, *pats, **opts):
2095 def _docommit(ui, repo, *pats, **opts):
2096 if opts.get('interactive'):
2096 if opts.get('interactive'):
2097 opts.pop('interactive')
2097 opts.pop('interactive')
2098 ret = cmdutil.dorecord(
2098 ret = cmdutil.dorecord(
2099 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2099 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2100 )
2100 )
2101 # ret can be 0 (no changes to record) or the value returned by
2101 # ret can be 0 (no changes to record) or the value returned by
2102 # commit(), 1 if nothing changed or None on success.
2102 # commit(), 1 if nothing changed or None on success.
2103 return 1 if ret == 0 else ret
2103 return 1 if ret == 0 else ret
2104
2104
2105 if opts.get('subrepos'):
2105 if opts.get('subrepos'):
2106 # Let --subrepos on the command line override config setting.
2106 # Let --subrepos on the command line override config setting.
2107 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2107 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2108
2108
2109 cmdutil.checkunfinished(repo, commit=True)
2109 cmdutil.checkunfinished(repo, commit=True)
2110
2110
2111 branch = repo[None].branch()
2111 branch = repo[None].branch()
2112 bheads = repo.branchheads(branch)
2112 bheads = repo.branchheads(branch)
2113 tip = repo.changelog.tip()
2113 tip = repo.changelog.tip()
2114
2114
2115 extra = {}
2115 extra = {}
2116 if opts.get('close_branch') or opts.get('force_close_branch'):
2116 if opts.get('close_branch') or opts.get('force_close_branch'):
2117 extra[b'close'] = b'1'
2117 extra[b'close'] = b'1'
2118
2118
2119 if repo[b'.'].closesbranch():
2119 if repo[b'.'].closesbranch():
2120 # Not ideal, but let us do an extra status early to prevent early
2120 # Not ideal, but let us do an extra status early to prevent early
2121 # bail out.
2121 # bail out.
2122 matcher = scmutil.match(
2122 matcher = scmutil.match(
2123 repo[None], pats, pycompat.byteskwargs(opts)
2123 repo[None], pats, pycompat.byteskwargs(opts)
2124 )
2124 )
2125 s = repo.status(match=matcher)
2125 s = repo.status(match=matcher)
2126 if s.modified or s.added or s.removed:
2126 if s.modified or s.added or s.removed:
2127 bheads = repo.branchheads(branch, closed=True)
2127 bheads = repo.branchheads(branch, closed=True)
2128 else:
2128 else:
2129 msg = _(b'current revision is already a branch closing head')
2129 msg = _(b'current revision is already a branch closing head')
2130 raise error.InputError(msg)
2130 raise error.InputError(msg)
2131
2131
2132 if not bheads:
2132 if not bheads:
2133 raise error.InputError(
2133 raise error.InputError(
2134 _(b'branch "%s" has no heads to close') % branch
2134 _(b'branch "%s" has no heads to close') % branch
2135 )
2135 )
2136 elif (
2136 elif (
2137 branch == repo[b'.'].branch()
2137 branch == repo[b'.'].branch()
2138 and repo[b'.'].node() not in bheads
2138 and repo[b'.'].node() not in bheads
2139 and not opts.get('force_close_branch')
2139 and not opts.get('force_close_branch')
2140 ):
2140 ):
2141 hint = _(
2141 hint = _(
2142 b'use --force-close-branch to close branch from a non-head'
2142 b'use --force-close-branch to close branch from a non-head'
2143 b' changeset'
2143 b' changeset'
2144 )
2144 )
2145 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2145 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2146 elif opts.get('amend'):
2146 elif opts.get('amend'):
2147 if (
2147 if (
2148 repo[b'.'].p1().branch() != branch
2148 repo[b'.'].p1().branch() != branch
2149 and repo[b'.'].p2().branch() != branch
2149 and repo[b'.'].p2().branch() != branch
2150 ):
2150 ):
2151 raise error.InputError(_(b'can only close branch heads'))
2151 raise error.InputError(_(b'can only close branch heads'))
2152
2152
2153 if opts.get('amend'):
2153 if opts.get('amend'):
2154 if ui.configbool(b'ui', b'commitsubrepos'):
2154 if ui.configbool(b'ui', b'commitsubrepos'):
2155 raise error.InputError(
2155 raise error.InputError(
2156 _(b'cannot amend with ui.commitsubrepos enabled')
2156 _(b'cannot amend with ui.commitsubrepos enabled')
2157 )
2157 )
2158
2158
2159 old = repo[b'.']
2159 old = repo[b'.']
2160 rewriteutil.precheck(repo, [old.rev()], b'amend')
2160 rewriteutil.precheck(repo, [old.rev()], b'amend')
2161
2161
2162 # Currently histedit gets confused if an amend happens while histedit
2162 # Currently histedit gets confused if an amend happens while histedit
2163 # is in progress. Since we have a checkunfinished command, we are
2163 # is in progress. Since we have a checkunfinished command, we are
2164 # temporarily honoring it.
2164 # temporarily honoring it.
2165 #
2165 #
2166 # Note: eventually this guard will be removed. Please do not expect
2166 # Note: eventually this guard will be removed. Please do not expect
2167 # this behavior to remain.
2167 # this behavior to remain.
2168 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2168 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2169 cmdutil.checkunfinished(repo)
2169 cmdutil.checkunfinished(repo)
2170
2170
2171 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2171 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2172 if node == old.node():
2172 if node == old.node():
2173 ui.status(_(b"nothing changed\n"))
2173 ui.status(_(b"nothing changed\n"))
2174 return 1
2174 return 1
2175 else:
2175 else:
2176
2176
2177 def commitfunc(ui, repo, message, match, opts):
2177 def commitfunc(ui, repo, message, match, opts):
2178 overrides = {}
2178 overrides = {}
2179 if opts.get(b'secret'):
2179 if opts.get(b'secret'):
2180 overrides[(b'phases', b'new-commit')] = b'secret'
2180 overrides[(b'phases', b'new-commit')] = b'secret'
2181 elif opts.get(b'draft'):
2181 elif opts.get(b'draft'):
2182 overrides[(b'phases', b'new-commit')] = b'draft'
2182 overrides[(b'phases', b'new-commit')] = b'draft'
2183
2183
2184 baseui = repo.baseui
2184 baseui = repo.baseui
2185 with baseui.configoverride(overrides, b'commit'):
2185 with baseui.configoverride(overrides, b'commit'):
2186 with ui.configoverride(overrides, b'commit'):
2186 with ui.configoverride(overrides, b'commit'):
2187 editform = cmdutil.mergeeditform(
2187 editform = cmdutil.mergeeditform(
2188 repo[None], b'commit.normal'
2188 repo[None], b'commit.normal'
2189 )
2189 )
2190 editor = cmdutil.getcommiteditor(
2190 editor = cmdutil.getcommiteditor(
2191 editform=editform, **pycompat.strkwargs(opts)
2191 editform=editform, **pycompat.strkwargs(opts)
2192 )
2192 )
2193 return repo.commit(
2193 return repo.commit(
2194 message,
2194 message,
2195 opts.get(b'user'),
2195 opts.get(b'user'),
2196 opts.get(b'date'),
2196 opts.get(b'date'),
2197 match,
2197 match,
2198 editor=editor,
2198 editor=editor,
2199 extra=extra,
2199 extra=extra,
2200 )
2200 )
2201
2201
2202 node = cmdutil.commit(
2202 node = cmdutil.commit(
2203 ui, repo, commitfunc, pats, pycompat.byteskwargs(opts)
2203 ui, repo, commitfunc, pats, pycompat.byteskwargs(opts)
2204 )
2204 )
2205
2205
2206 if not node:
2206 if not node:
2207 stat = cmdutil.postcommitstatus(
2207 stat = cmdutil.postcommitstatus(
2208 repo, pats, pycompat.byteskwargs(opts)
2208 repo, pats, pycompat.byteskwargs(opts)
2209 )
2209 )
2210 if stat.deleted:
2210 if stat.deleted:
2211 ui.status(
2211 ui.status(
2212 _(
2212 _(
2213 b"nothing changed (%d missing files, see "
2213 b"nothing changed (%d missing files, see "
2214 b"'hg status')\n"
2214 b"'hg status')\n"
2215 )
2215 )
2216 % len(stat.deleted)
2216 % len(stat.deleted)
2217 )
2217 )
2218 else:
2218 else:
2219 ui.status(_(b"nothing changed\n"))
2219 ui.status(_(b"nothing changed\n"))
2220 return 1
2220 return 1
2221
2221
2222 cmdutil.commitstatus(repo, node, branch, bheads, tip, **opts)
2222 cmdutil.commitstatus(repo, node, branch, bheads, tip, **opts)
2223
2223
2224 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2224 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2225 status(
2225 status(
2226 ui,
2226 ui,
2227 repo,
2227 repo,
2228 modified=True,
2228 modified=True,
2229 added=True,
2229 added=True,
2230 removed=True,
2230 removed=True,
2231 deleted=True,
2231 deleted=True,
2232 unknown=True,
2232 unknown=True,
2233 subrepos=opts.get('subrepos'),
2233 subrepos=opts.get('subrepos'),
2234 )
2234 )
2235
2235
2236
2236
2237 @command(
2237 @command(
2238 b'config|showconfig|debugconfig',
2238 b'config|showconfig|debugconfig',
2239 [
2239 [
2240 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2240 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2241 # This is experimental because we need
2241 # This is experimental because we need
2242 # * reasonable behavior around aliases,
2242 # * reasonable behavior around aliases,
2243 # * decide if we display [debug] [experimental] and [devel] section par
2243 # * decide if we display [debug] [experimental] and [devel] section par
2244 # default
2244 # default
2245 # * some way to display "generic" config entry (the one matching
2245 # * some way to display "generic" config entry (the one matching
2246 # regexp,
2246 # regexp,
2247 # * proper display of the different value type
2247 # * proper display of the different value type
2248 # * a better way to handle <DYNAMIC> values (and variable types),
2248 # * a better way to handle <DYNAMIC> values (and variable types),
2249 # * maybe some type information ?
2249 # * maybe some type information ?
2250 (
2250 (
2251 b'',
2251 b'',
2252 b'exp-all-known',
2252 b'exp-all-known',
2253 None,
2253 None,
2254 _(b'show all known config option (EXPERIMENTAL)'),
2254 _(b'show all known config option (EXPERIMENTAL)'),
2255 ),
2255 ),
2256 (b'e', b'edit', None, _(b'edit user config')),
2256 (b'e', b'edit', None, _(b'edit user config')),
2257 (b'l', b'local', None, _(b'edit repository config')),
2257 (b'l', b'local', None, _(b'edit repository config')),
2258 (b'', b'source', None, _(b'show source of configuration value')),
2258 (b'', b'source', None, _(b'show source of configuration value')),
2259 (
2259 (
2260 b'',
2260 b'',
2261 b'shared',
2261 b'shared',
2262 None,
2262 None,
2263 _(b'edit shared source repository config (EXPERIMENTAL)'),
2263 _(b'edit shared source repository config (EXPERIMENTAL)'),
2264 ),
2264 ),
2265 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2265 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2266 (b'g', b'global', None, _(b'edit global config')),
2266 (b'g', b'global', None, _(b'edit global config')),
2267 ]
2267 ]
2268 + formatteropts,
2268 + formatteropts,
2269 _(b'[-u] [NAME]...'),
2269 _(b'[-u] [NAME]...'),
2270 helpcategory=command.CATEGORY_HELP,
2270 helpcategory=command.CATEGORY_HELP,
2271 optionalrepo=True,
2271 optionalrepo=True,
2272 intents={INTENT_READONLY},
2272 intents={INTENT_READONLY},
2273 )
2273 )
2274 def config(ui, repo, *values, **opts):
2274 def config(ui, repo, *values, **opts):
2275 """show combined config settings from all hgrc files
2275 """show combined config settings from all hgrc files
2276
2276
2277 With no arguments, print names and values of all config items.
2277 With no arguments, print names and values of all config items.
2278
2278
2279 With one argument of the form section.name, print just the value
2279 With one argument of the form section.name, print just the value
2280 of that config item.
2280 of that config item.
2281
2281
2282 With multiple arguments, print names and values of all config
2282 With multiple arguments, print names and values of all config
2283 items with matching section names or section.names.
2283 items with matching section names or section.names.
2284
2284
2285 With --edit, start an editor on the user-level config file. With
2285 With --edit, start an editor on the user-level config file. With
2286 --global, edit the system-wide config file. With --local, edit the
2286 --global, edit the system-wide config file. With --local, edit the
2287 repository-level config file.
2287 repository-level config file.
2288
2288
2289 With --source, the source (filename and line number) is printed
2289 With --source, the source (filename and line number) is printed
2290 for each config item.
2290 for each config item.
2291
2291
2292 See :hg:`help config` for more information about config files.
2292 See :hg:`help config` for more information about config files.
2293
2293
2294 .. container:: verbose
2294 .. container:: verbose
2295
2295
2296 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2296 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2297 This file is not shared across shares when in share-safe mode.
2297 This file is not shared across shares when in share-safe mode.
2298
2298
2299 Template:
2299 Template:
2300
2300
2301 The following keywords are supported. See also :hg:`help templates`.
2301 The following keywords are supported. See also :hg:`help templates`.
2302
2302
2303 :name: String. Config name.
2303 :name: String. Config name.
2304 :source: String. Filename and line number where the item is defined.
2304 :source: String. Filename and line number where the item is defined.
2305 :value: String. Config value.
2305 :value: String. Config value.
2306
2306
2307 The --shared flag can be used to edit the config file of shared source
2307 The --shared flag can be used to edit the config file of shared source
2308 repository. It only works when you have shared using the experimental
2308 repository. It only works when you have shared using the experimental
2309 share safe feature.
2309 share safe feature.
2310
2310
2311 Returns 0 on success, 1 if NAME does not exist.
2311 Returns 0 on success, 1 if NAME does not exist.
2312
2312
2313 """
2313 """
2314
2314
2315 editopts = ('edit', 'local', 'global', 'shared', 'non_shared')
2315 editopts = ('edit', 'local', 'global', 'shared', 'non_shared')
2316 if any(opts.get(o) for o in editopts):
2316 if any(opts.get(o) for o in editopts):
2317 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2317 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2318 if opts.get('local'):
2318 if opts.get('local'):
2319 if not repo:
2319 if not repo:
2320 raise error.InputError(
2320 raise error.InputError(
2321 _(b"can't use --local outside a repository")
2321 _(b"can't use --local outside a repository")
2322 )
2322 )
2323 paths = [repo.vfs.join(b'hgrc')]
2323 paths = [repo.vfs.join(b'hgrc')]
2324 elif opts.get('global'):
2324 elif opts.get('global'):
2325 paths = rcutil.systemrcpath()
2325 paths = rcutil.systemrcpath()
2326 elif opts.get('shared'):
2326 elif opts.get('shared'):
2327 if not repo.shared():
2327 if not repo.shared():
2328 raise error.InputError(
2328 raise error.InputError(
2329 _(b"repository is not shared; can't use --shared")
2329 _(b"repository is not shared; can't use --shared")
2330 )
2330 )
2331 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2331 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2332 raise error.InputError(
2332 raise error.InputError(
2333 _(
2333 _(
2334 b"share safe feature not enabled; "
2334 b"share safe feature not enabled; "
2335 b"unable to edit shared source repository config"
2335 b"unable to edit shared source repository config"
2336 )
2336 )
2337 )
2337 )
2338 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2338 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2339 elif opts.get('non_shared'):
2339 elif opts.get('non_shared'):
2340 paths = [repo.vfs.join(b'hgrc-not-shared')]
2340 paths = [repo.vfs.join(b'hgrc-not-shared')]
2341 else:
2341 else:
2342 paths = rcutil.userrcpath()
2342 paths = rcutil.userrcpath()
2343
2343
2344 for f in paths:
2344 for f in paths:
2345 if os.path.exists(f):
2345 if os.path.exists(f):
2346 break
2346 break
2347 else:
2347 else:
2348 if opts.get('global'):
2348 if opts.get('global'):
2349 samplehgrc = uimod.samplehgrcs[b'global']
2349 samplehgrc = uimod.samplehgrcs[b'global']
2350 elif opts.get('local'):
2350 elif opts.get('local'):
2351 samplehgrc = uimod.samplehgrcs[b'local']
2351 samplehgrc = uimod.samplehgrcs[b'local']
2352 else:
2352 else:
2353 samplehgrc = uimod.samplehgrcs[b'user']
2353 samplehgrc = uimod.samplehgrcs[b'user']
2354
2354
2355 f = paths[0]
2355 f = paths[0]
2356 util.writefile(f, util.tonativeeol(samplehgrc))
2356 util.writefile(f, util.tonativeeol(samplehgrc))
2357
2357
2358 editor = ui.geteditor()
2358 editor = ui.geteditor()
2359 ui.system(
2359 ui.system(
2360 b"%s \"%s\"" % (editor, f),
2360 b"%s \"%s\"" % (editor, f),
2361 onerr=error.InputError,
2361 onerr=error.InputError,
2362 errprefix=_(b"edit failed"),
2362 errprefix=_(b"edit failed"),
2363 blockedtag=b'config_edit',
2363 blockedtag=b'config_edit',
2364 )
2364 )
2365 return
2365 return
2366 ui.pager(b'config')
2366 ui.pager(b'config')
2367 fm = ui.formatter(b'config', pycompat.byteskwargs(opts))
2367 fm = ui.formatter(b'config', pycompat.byteskwargs(opts))
2368 for t, f in rcutil.rccomponents():
2368 for t, f in rcutil.rccomponents():
2369 if t == b'path':
2369 if t == b'path':
2370 ui.debug(b'read config from: %s\n' % f)
2370 ui.debug(b'read config from: %s\n' % f)
2371 elif t == b'resource':
2371 elif t == b'resource':
2372 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2372 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2373 elif t == b'items':
2373 elif t == b'items':
2374 # Don't print anything for 'items'.
2374 # Don't print anything for 'items'.
2375 pass
2375 pass
2376 else:
2376 else:
2377 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2377 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2378 untrusted = bool(opts.get('untrusted'))
2378 untrusted = bool(opts.get('untrusted'))
2379
2379
2380 selsections = selentries = []
2380 selsections = selentries = []
2381 if values:
2381 if values:
2382 selsections = [v for v in values if b'.' not in v]
2382 selsections = [v for v in values if b'.' not in v]
2383 selentries = [v for v in values if b'.' in v]
2383 selentries = [v for v in values if b'.' in v]
2384 uniquesel = len(selentries) == 1 and not selsections
2384 uniquesel = len(selentries) == 1 and not selsections
2385 selsections = set(selsections)
2385 selsections = set(selsections)
2386 selentries = set(selentries)
2386 selentries = set(selentries)
2387
2387
2388 matched = False
2388 matched = False
2389 all_known = opts['exp_all_known']
2389 all_known = opts['exp_all_known']
2390 show_source = ui.debugflag or opts.get('source')
2390 show_source = ui.debugflag or opts.get('source')
2391 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2391 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2392 for section, name, value in entries:
2392 for section, name, value in entries:
2393 source = ui.configsource(section, name, untrusted)
2393 source = ui.configsource(section, name, untrusted)
2394 value = pycompat.bytestr(value)
2394 value = pycompat.bytestr(value)
2395 defaultvalue = ui.configdefault(section, name)
2395 defaultvalue = ui.configdefault(section, name)
2396 if fm.isplain():
2396 if fm.isplain():
2397 source = source or b'none'
2397 source = source or b'none'
2398 value = value.replace(b'\n', b'\\n')
2398 value = value.replace(b'\n', b'\\n')
2399 entryname = section + b'.' + name
2399 entryname = section + b'.' + name
2400 if values and not (section in selsections or entryname in selentries):
2400 if values and not (section in selsections or entryname in selentries):
2401 continue
2401 continue
2402 fm.startitem()
2402 fm.startitem()
2403 fm.condwrite(show_source, b'source', b'%s: ', source)
2403 fm.condwrite(show_source, b'source', b'%s: ', source)
2404 if uniquesel:
2404 if uniquesel:
2405 fm.data(name=entryname)
2405 fm.data(name=entryname)
2406 fm.write(b'value', b'%s\n', value)
2406 fm.write(b'value', b'%s\n', value)
2407 else:
2407 else:
2408 fm.write(b'name value', b'%s=%s\n', entryname, value)
2408 fm.write(b'name value', b'%s=%s\n', entryname, value)
2409 if formatter.isprintable(defaultvalue):
2409 if formatter.isprintable(defaultvalue):
2410 fm.data(defaultvalue=defaultvalue)
2410 fm.data(defaultvalue=defaultvalue)
2411 elif isinstance(defaultvalue, list) and all(
2411 elif isinstance(defaultvalue, list) and all(
2412 formatter.isprintable(e) for e in defaultvalue
2412 formatter.isprintable(e) for e in defaultvalue
2413 ):
2413 ):
2414 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2414 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2415 # TODO: no idea how to process unsupported defaultvalue types
2415 # TODO: no idea how to process unsupported defaultvalue types
2416 matched = True
2416 matched = True
2417 fm.end()
2417 fm.end()
2418 if matched:
2418 if matched:
2419 return 0
2419 return 0
2420 return 1
2420 return 1
2421
2421
2422
2422
2423 @command(
2423 @command(
2424 b'continue',
2424 b'continue',
2425 dryrunopts,
2425 dryrunopts,
2426 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2426 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2427 helpbasic=True,
2427 helpbasic=True,
2428 )
2428 )
2429 def continuecmd(ui, repo, **opts):
2429 def continuecmd(ui, repo, **opts):
2430 """resumes an interrupted operation (EXPERIMENTAL)
2430 """resumes an interrupted operation (EXPERIMENTAL)
2431
2431
2432 Finishes a multistep operation like graft, histedit, rebase, merge,
2432 Finishes a multistep operation like graft, histedit, rebase, merge,
2433 and unshelve if they are in an interrupted state.
2433 and unshelve if they are in an interrupted state.
2434
2434
2435 use --dry-run/-n to dry run the command.
2435 use --dry-run/-n to dry run the command.
2436 """
2436 """
2437 dryrun = opts.get('dry_run')
2437 dryrun = opts.get('dry_run')
2438 contstate = cmdutil.getunfinishedstate(repo)
2438 contstate = cmdutil.getunfinishedstate(repo)
2439 if not contstate:
2439 if not contstate:
2440 raise error.StateError(_(b'no operation in progress'))
2440 raise error.StateError(_(b'no operation in progress'))
2441 if not contstate.continuefunc:
2441 if not contstate.continuefunc:
2442 raise error.StateError(
2442 raise error.StateError(
2443 (
2443 (
2444 _(b"%s in progress but does not support 'hg continue'")
2444 _(b"%s in progress but does not support 'hg continue'")
2445 % (contstate._opname)
2445 % (contstate._opname)
2446 ),
2446 ),
2447 hint=contstate.continuemsg(),
2447 hint=contstate.continuemsg(),
2448 )
2448 )
2449 if dryrun:
2449 if dryrun:
2450 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2450 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2451 return
2451 return
2452 return contstate.continuefunc(ui, repo)
2452 return contstate.continuefunc(ui, repo)
2453
2453
2454
2454
2455 @command(
2455 @command(
2456 b'copy|cp',
2456 b'copy|cp',
2457 [
2457 [
2458 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2458 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2459 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2459 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2460 (
2460 (
2461 b'',
2461 b'',
2462 b'at-rev',
2462 b'at-rev',
2463 b'',
2463 b'',
2464 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2464 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2465 _(b'REV'),
2465 _(b'REV'),
2466 ),
2466 ),
2467 (
2467 (
2468 b'f',
2468 b'f',
2469 b'force',
2469 b'force',
2470 None,
2470 None,
2471 _(b'forcibly copy over an existing managed file'),
2471 _(b'forcibly copy over an existing managed file'),
2472 ),
2472 ),
2473 ]
2473 ]
2474 + walkopts
2474 + walkopts
2475 + dryrunopts,
2475 + dryrunopts,
2476 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2476 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2477 helpcategory=command.CATEGORY_FILE_CONTENTS,
2477 helpcategory=command.CATEGORY_FILE_CONTENTS,
2478 )
2478 )
2479 def copy(ui, repo, *pats, **opts):
2479 def copy(ui, repo, *pats, **opts):
2480 """mark files as copied for the next commit
2480 """mark files as copied for the next commit
2481
2481
2482 Mark dest as having copies of source files. If dest is a
2482 Mark dest as having copies of source files. If dest is a
2483 directory, copies are put in that directory. If dest is a file,
2483 directory, copies are put in that directory. If dest is a file,
2484 the source must be a single file.
2484 the source must be a single file.
2485
2485
2486 By default, this command copies the contents of files as they
2486 By default, this command copies the contents of files as they
2487 exist in the working directory. If invoked with -A/--after, the
2487 exist in the working directory. If invoked with -A/--after, the
2488 operation is recorded, but no copying is performed.
2488 operation is recorded, but no copying is performed.
2489
2489
2490 To undo marking a destination file as copied, use --forget. With that
2490 To undo marking a destination file as copied, use --forget. With that
2491 option, all given (positional) arguments are unmarked as copies. The
2491 option, all given (positional) arguments are unmarked as copies. The
2492 destination file(s) will be left in place (still tracked). Note that
2492 destination file(s) will be left in place (still tracked). Note that
2493 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2493 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2494
2494
2495 This command takes effect with the next commit by default.
2495 This command takes effect with the next commit by default.
2496
2496
2497 Returns 0 on success, 1 if errors are encountered.
2497 Returns 0 on success, 1 if errors are encountered.
2498 """
2498 """
2499
2499
2500 context = lambda repo: repo.dirstate.changing_files(repo)
2500 context = lambda repo: repo.dirstate.changing_files(repo)
2501 rev = opts.get('at_rev')
2501 rev = opts.get('at_rev')
2502
2502
2503 if rev:
2503 if rev:
2504 ctx = logcmdutil.revsingle(repo, rev)
2504 ctx = logcmdutil.revsingle(repo, rev)
2505 if ctx.rev() is not None:
2505 if ctx.rev() is not None:
2506
2506
2507 def context(repo):
2507 def context(repo):
2508 return util.nullcontextmanager()
2508 return util.nullcontextmanager()
2509
2509
2510 opts['at_rev'] = ctx.rev()
2510 opts['at_rev'] = ctx.rev()
2511 with repo.wlock(), context(repo):
2511 with repo.wlock(), context(repo):
2512 return cmdutil.copy(ui, repo, pats, pycompat.byteskwargs(opts))
2512 return cmdutil.copy(ui, repo, pats, pycompat.byteskwargs(opts))
2513
2513
2514
2514
2515 @command(
2515 @command(
2516 b'debugcommands',
2516 b'debugcommands',
2517 [],
2517 [],
2518 _(b'[COMMAND]'),
2518 _(b'[COMMAND]'),
2519 helpcategory=command.CATEGORY_HELP,
2519 helpcategory=command.CATEGORY_HELP,
2520 norepo=True,
2520 norepo=True,
2521 )
2521 )
2522 def debugcommands(ui, cmd=b'', *args):
2522 def debugcommands(ui, cmd=b'', *args):
2523 """list all available commands and options"""
2523 """list all available commands and options"""
2524 for cmd, vals in sorted(table.items()):
2524 for cmd, vals in sorted(table.items()):
2525 cmd = cmd.split(b'|')[0]
2525 cmd = cmd.split(b'|')[0]
2526 opts = b', '.join([i[1] for i in vals[1]])
2526 opts = b', '.join([i[1] for i in vals[1]])
2527 ui.write(b'%s: %s\n' % (cmd, opts))
2527 ui.write(b'%s: %s\n' % (cmd, opts))
2528
2528
2529
2529
2530 @command(
2530 @command(
2531 b'debugcomplete',
2531 b'debugcomplete',
2532 [(b'o', b'options', None, _(b'show the command options'))],
2532 [(b'o', b'options', None, _(b'show the command options'))],
2533 _(b'[-o] CMD'),
2533 _(b'[-o] CMD'),
2534 helpcategory=command.CATEGORY_HELP,
2534 helpcategory=command.CATEGORY_HELP,
2535 norepo=True,
2535 norepo=True,
2536 )
2536 )
2537 def debugcomplete(ui, cmd=b'', **opts):
2537 def debugcomplete(ui, cmd=b'', **opts):
2538 """returns the completion list associated with the given command"""
2538 """returns the completion list associated with the given command"""
2539
2539
2540 if opts.get('options'):
2540 if opts.get('options'):
2541 options = []
2541 options = []
2542 otables = [globalopts]
2542 otables = [globalopts]
2543 if cmd:
2543 if cmd:
2544 aliases, entry = cmdutil.findcmd(cmd, table, False)
2544 aliases, entry = cmdutil.findcmd(cmd, table, False)
2545 otables.append(entry[1])
2545 otables.append(entry[1])
2546 for t in otables:
2546 for t in otables:
2547 for o in t:
2547 for o in t:
2548 if b"(DEPRECATED)" in o[3]:
2548 if b"(DEPRECATED)" in o[3]:
2549 continue
2549 continue
2550 if o[0]:
2550 if o[0]:
2551 options.append(b'-%s' % o[0])
2551 options.append(b'-%s' % o[0])
2552 options.append(b'--%s' % o[1])
2552 options.append(b'--%s' % o[1])
2553 ui.write(b"%s\n" % b"\n".join(options))
2553 ui.write(b"%s\n" % b"\n".join(options))
2554 return
2554 return
2555
2555
2556 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2556 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2557 if ui.verbose:
2557 if ui.verbose:
2558 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2558 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2559 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2559 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2560
2560
2561
2561
2562 @command(
2562 @command(
2563 b'diff',
2563 b'diff',
2564 [
2564 [
2565 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2565 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2566 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2566 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2567 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2567 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2568 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2568 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2569 ]
2569 ]
2570 + diffopts
2570 + diffopts
2571 + diffopts2
2571 + diffopts2
2572 + walkopts
2572 + walkopts
2573 + subrepoopts,
2573 + subrepoopts,
2574 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2574 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2575 helpcategory=command.CATEGORY_FILE_CONTENTS,
2575 helpcategory=command.CATEGORY_FILE_CONTENTS,
2576 helpbasic=True,
2576 helpbasic=True,
2577 inferrepo=True,
2577 inferrepo=True,
2578 intents={INTENT_READONLY},
2578 intents={INTENT_READONLY},
2579 )
2579 )
2580 def diff(ui, repo, *pats, **opts):
2580 def diff(ui, repo, *pats, **opts):
2581 """diff repository (or selected files)
2581 """diff repository (or selected files)
2582
2582
2583 Show differences between revisions for the specified files.
2583 Show differences between revisions for the specified files.
2584
2584
2585 Differences between files are shown using the unified diff format.
2585 Differences between files are shown using the unified diff format.
2586
2586
2587 .. note::
2587 .. note::
2588
2588
2589 :hg:`diff` may generate unexpected results for merges, as it will
2589 :hg:`diff` may generate unexpected results for merges, as it will
2590 default to comparing against the working directory's first
2590 default to comparing against the working directory's first
2591 parent changeset if no revisions are specified. To diff against the
2591 parent changeset if no revisions are specified. To diff against the
2592 conflict regions, you can use `--config diff.merge=yes`.
2592 conflict regions, you can use `--config diff.merge=yes`.
2593
2593
2594 By default, the working directory files are compared to its first parent. To
2594 By default, the working directory files are compared to its first parent. To
2595 see the differences from another revision, use --from. To see the difference
2595 see the differences from another revision, use --from. To see the difference
2596 to another revision, use --to. For example, :hg:`diff --from .^` will show
2596 to another revision, use --to. For example, :hg:`diff --from .^` will show
2597 the differences from the working copy's grandparent to the working copy,
2597 the differences from the working copy's grandparent to the working copy,
2598 :hg:`diff --to .` will show the diff from the working copy to its parent
2598 :hg:`diff --to .` will show the diff from the working copy to its parent
2599 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2599 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2600 show the diff between those two revisions.
2600 show the diff between those two revisions.
2601
2601
2602 Alternatively you can specify -c/--change with a revision to see the changes
2602 Alternatively you can specify -c/--change with a revision to see the changes
2603 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2603 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2604 equivalent to :hg:`diff --from 42^ --to 42`)
2604 equivalent to :hg:`diff --from 42^ --to 42`)
2605
2605
2606 Without the -a/--text option, diff will avoid generating diffs of
2606 Without the -a/--text option, diff will avoid generating diffs of
2607 files it detects as binary. With -a, diff will generate a diff
2607 files it detects as binary. With -a, diff will generate a diff
2608 anyway, probably with undesirable results.
2608 anyway, probably with undesirable results.
2609
2609
2610 Use the -g/--git option to generate diffs in the git extended diff
2610 Use the -g/--git option to generate diffs in the git extended diff
2611 format. For more information, read :hg:`help diffs`.
2611 format. For more information, read :hg:`help diffs`.
2612
2612
2613 .. container:: verbose
2613 .. container:: verbose
2614
2614
2615 Examples:
2615 Examples:
2616
2616
2617 - compare a file in the current working directory to its parent::
2617 - compare a file in the current working directory to its parent::
2618
2618
2619 hg diff foo.c
2619 hg diff foo.c
2620
2620
2621 - compare two historical versions of a directory, with rename info::
2621 - compare two historical versions of a directory, with rename info::
2622
2622
2623 hg diff --git --from 1.0 --to 1.2 lib/
2623 hg diff --git --from 1.0 --to 1.2 lib/
2624
2624
2625 - get change stats relative to the last change on some date::
2625 - get change stats relative to the last change on some date::
2626
2626
2627 hg diff --stat --from "date('may 2')"
2627 hg diff --stat --from "date('may 2')"
2628
2628
2629 - diff all newly-added files that contain a keyword::
2629 - diff all newly-added files that contain a keyword::
2630
2630
2631 hg diff "set:added() and grep(GNU)"
2631 hg diff "set:added() and grep(GNU)"
2632
2632
2633 - compare a revision and its parents::
2633 - compare a revision and its parents::
2634
2634
2635 hg diff -c 9353 # compare against first parent
2635 hg diff -c 9353 # compare against first parent
2636 hg diff --from 9353^ --to 9353 # same using revset syntax
2636 hg diff --from 9353^ --to 9353 # same using revset syntax
2637 hg diff --from 9353^2 --to 9353 # compare against the second parent
2637 hg diff --from 9353^2 --to 9353 # compare against the second parent
2638
2638
2639 Returns 0 on success.
2639 Returns 0 on success.
2640 """
2640 """
2641
2641
2642 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2642 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2643 opts = pycompat.byteskwargs(opts)
2643 opts = pycompat.byteskwargs(opts)
2644 revs = opts.get(b'rev')
2644 revs = opts.get(b'rev')
2645 change = opts.get(b'change')
2645 change = opts.get(b'change')
2646 from_rev = opts.get(b'from')
2646 from_rev = opts.get(b'from')
2647 to_rev = opts.get(b'to')
2647 to_rev = opts.get(b'to')
2648 stat = opts.get(b'stat')
2648 stat = opts.get(b'stat')
2649 reverse = opts.get(b'reverse')
2649 reverse = opts.get(b'reverse')
2650
2650
2651 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2651 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2652 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2652 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2653 if change:
2653 if change:
2654 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2654 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2655 ctx2 = logcmdutil.revsingle(repo, change, None)
2655 ctx2 = logcmdutil.revsingle(repo, change, None)
2656 ctx1 = logcmdutil.diff_parent(ctx2)
2656 ctx1 = logcmdutil.diff_parent(ctx2)
2657 elif from_rev or to_rev:
2657 elif from_rev or to_rev:
2658 repo = scmutil.unhidehashlikerevs(
2658 repo = scmutil.unhidehashlikerevs(
2659 repo, [from_rev] + [to_rev], b'nowarn'
2659 repo, [from_rev] + [to_rev], b'nowarn'
2660 )
2660 )
2661 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2661 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2662 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2662 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2663 else:
2663 else:
2664 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2664 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2665 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2665 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2666
2666
2667 if reverse:
2667 if reverse:
2668 ctxleft = ctx2
2668 ctxleft = ctx2
2669 ctxright = ctx1
2669 ctxright = ctx1
2670 else:
2670 else:
2671 ctxleft = ctx1
2671 ctxleft = ctx1
2672 ctxright = ctx2
2672 ctxright = ctx2
2673
2673
2674 diffopts = patch.diffallopts(ui, opts)
2674 diffopts = patch.diffallopts(ui, opts)
2675 m = scmutil.match(ctx2, pats, opts)
2675 m = scmutil.match(ctx2, pats, opts)
2676 m = repo.narrowmatch(m)
2676 m = repo.narrowmatch(m)
2677 ui.pager(b'diff')
2677 ui.pager(b'diff')
2678 logcmdutil.diffordiffstat(
2678 logcmdutil.diffordiffstat(
2679 ui,
2679 ui,
2680 repo,
2680 repo,
2681 diffopts,
2681 diffopts,
2682 ctxleft,
2682 ctxleft,
2683 ctxright,
2683 ctxright,
2684 m,
2684 m,
2685 stat=stat,
2685 stat=stat,
2686 listsubrepos=opts.get(b'subrepos'),
2686 listsubrepos=opts.get(b'subrepos'),
2687 root=opts.get(b'root'),
2687 root=opts.get(b'root'),
2688 )
2688 )
2689
2689
2690
2690
2691 @command(
2691 @command(
2692 b'export',
2692 b'export',
2693 [
2693 [
2694 (
2694 (
2695 b'B',
2695 b'B',
2696 b'bookmark',
2696 b'bookmark',
2697 b'',
2697 b'',
2698 _(b'export changes only reachable by given bookmark'),
2698 _(b'export changes only reachable by given bookmark'),
2699 _(b'BOOKMARK'),
2699 _(b'BOOKMARK'),
2700 ),
2700 ),
2701 (
2701 (
2702 b'o',
2702 b'o',
2703 b'output',
2703 b'output',
2704 b'',
2704 b'',
2705 _(b'print output to file with formatted name'),
2705 _(b'print output to file with formatted name'),
2706 _(b'FORMAT'),
2706 _(b'FORMAT'),
2707 ),
2707 ),
2708 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2708 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2709 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2709 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2710 ]
2710 ]
2711 + diffopts
2711 + diffopts
2712 + formatteropts,
2712 + formatteropts,
2713 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2713 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2714 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2714 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2715 helpbasic=True,
2715 helpbasic=True,
2716 intents={INTENT_READONLY},
2716 intents={INTENT_READONLY},
2717 )
2717 )
2718 def export(ui, repo, *changesets, **opts):
2718 def export(ui, repo, *changesets, **opts):
2719 """dump the header and diffs for one or more changesets
2719 """dump the header and diffs for one or more changesets
2720
2720
2721 Print the changeset header and diffs for one or more revisions.
2721 Print the changeset header and diffs for one or more revisions.
2722 If no revision is given, the parent of the working directory is used.
2722 If no revision is given, the parent of the working directory is used.
2723
2723
2724 The information shown in the changeset header is: author, date,
2724 The information shown in the changeset header is: author, date,
2725 branch name (if non-default), changeset hash, parent(s) and commit
2725 branch name (if non-default), changeset hash, parent(s) and commit
2726 comment.
2726 comment.
2727
2727
2728 .. note::
2728 .. note::
2729
2729
2730 :hg:`export` may generate unexpected diff output for merge
2730 :hg:`export` may generate unexpected diff output for merge
2731 changesets, as it will compare the merge changeset against its
2731 changesets, as it will compare the merge changeset against its
2732 first parent only.
2732 first parent only.
2733
2733
2734 Output may be to a file, in which case the name of the file is
2734 Output may be to a file, in which case the name of the file is
2735 given using a template string. See :hg:`help templates`. In addition
2735 given using a template string. See :hg:`help templates`. In addition
2736 to the common template keywords, the following formatting rules are
2736 to the common template keywords, the following formatting rules are
2737 supported:
2737 supported:
2738
2738
2739 :``%%``: literal "%" character
2739 :``%%``: literal "%" character
2740 :``%H``: changeset hash (40 hexadecimal digits)
2740 :``%H``: changeset hash (40 hexadecimal digits)
2741 :``%N``: number of patches being generated
2741 :``%N``: number of patches being generated
2742 :``%R``: changeset revision number
2742 :``%R``: changeset revision number
2743 :``%b``: basename of the exporting repository
2743 :``%b``: basename of the exporting repository
2744 :``%h``: short-form changeset hash (12 hexadecimal digits)
2744 :``%h``: short-form changeset hash (12 hexadecimal digits)
2745 :``%m``: first line of the commit message (only alphanumeric characters)
2745 :``%m``: first line of the commit message (only alphanumeric characters)
2746 :``%n``: zero-padded sequence number, starting at 1
2746 :``%n``: zero-padded sequence number, starting at 1
2747 :``%r``: zero-padded changeset revision number
2747 :``%r``: zero-padded changeset revision number
2748 :``\\``: literal "\\" character
2748 :``\\``: literal "\\" character
2749
2749
2750 Without the -a/--text option, export will avoid generating diffs
2750 Without the -a/--text option, export will avoid generating diffs
2751 of files it detects as binary. With -a, export will generate a
2751 of files it detects as binary. With -a, export will generate a
2752 diff anyway, probably with undesirable results.
2752 diff anyway, probably with undesirable results.
2753
2753
2754 With -B/--bookmark changesets reachable by the given bookmark are
2754 With -B/--bookmark changesets reachable by the given bookmark are
2755 selected.
2755 selected.
2756
2756
2757 Use the -g/--git option to generate diffs in the git extended diff
2757 Use the -g/--git option to generate diffs in the git extended diff
2758 format. See :hg:`help diffs` for more information.
2758 format. See :hg:`help diffs` for more information.
2759
2759
2760 With the --switch-parent option, the diff will be against the
2760 With the --switch-parent option, the diff will be against the
2761 second parent. It can be useful to review a merge.
2761 second parent. It can be useful to review a merge.
2762
2762
2763 .. container:: verbose
2763 .. container:: verbose
2764
2764
2765 Template:
2765 Template:
2766
2766
2767 The following keywords are supported in addition to the common template
2767 The following keywords are supported in addition to the common template
2768 keywords and functions. See also :hg:`help templates`.
2768 keywords and functions. See also :hg:`help templates`.
2769
2769
2770 :diff: String. Diff content.
2770 :diff: String. Diff content.
2771 :parents: List of strings. Parent nodes of the changeset.
2771 :parents: List of strings. Parent nodes of the changeset.
2772
2772
2773 Examples:
2773 Examples:
2774
2774
2775 - use export and import to transplant a bugfix to the current
2775 - use export and import to transplant a bugfix to the current
2776 branch::
2776 branch::
2777
2777
2778 hg export -r 9353 | hg import -
2778 hg export -r 9353 | hg import -
2779
2779
2780 - export all the changesets between two revisions to a file with
2780 - export all the changesets between two revisions to a file with
2781 rename information::
2781 rename information::
2782
2782
2783 hg export --git -r 123:150 > changes.txt
2783 hg export --git -r 123:150 > changes.txt
2784
2784
2785 - split outgoing changes into a series of patches with
2785 - split outgoing changes into a series of patches with
2786 descriptive names::
2786 descriptive names::
2787
2787
2788 hg export -r "outgoing()" -o "%n-%m.patch"
2788 hg export -r "outgoing()" -o "%n-%m.patch"
2789
2789
2790 Returns 0 on success.
2790 Returns 0 on success.
2791 """
2791 """
2792 opts = pycompat.byteskwargs(opts)
2792 opts = pycompat.byteskwargs(opts)
2793 bookmark = opts.get(b'bookmark')
2793 bookmark = opts.get(b'bookmark')
2794 changesets += tuple(opts.get(b'rev', []))
2794 changesets += tuple(opts.get(b'rev', []))
2795
2795
2796 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2796 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2797
2797
2798 if bookmark:
2798 if bookmark:
2799 if bookmark not in repo._bookmarks:
2799 if bookmark not in repo._bookmarks:
2800 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2800 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2801
2801
2802 revs = scmutil.bookmarkrevs(repo, bookmark)
2802 revs = scmutil.bookmarkrevs(repo, bookmark)
2803 else:
2803 else:
2804 if not changesets:
2804 if not changesets:
2805 changesets = [b'.']
2805 changesets = [b'.']
2806
2806
2807 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2807 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2808 revs = logcmdutil.revrange(repo, changesets)
2808 revs = logcmdutil.revrange(repo, changesets)
2809
2809
2810 if not revs:
2810 if not revs:
2811 raise error.InputError(_(b"export requires at least one changeset"))
2811 raise error.InputError(_(b"export requires at least one changeset"))
2812 if len(revs) > 1:
2812 if len(revs) > 1:
2813 ui.note(_(b'exporting patches:\n'))
2813 ui.note(_(b'exporting patches:\n'))
2814 else:
2814 else:
2815 ui.note(_(b'exporting patch:\n'))
2815 ui.note(_(b'exporting patch:\n'))
2816
2816
2817 fntemplate = opts.get(b'output')
2817 fntemplate = opts.get(b'output')
2818 if cmdutil.isstdiofilename(fntemplate):
2818 if cmdutil.isstdiofilename(fntemplate):
2819 fntemplate = b''
2819 fntemplate = b''
2820
2820
2821 if fntemplate:
2821 if fntemplate:
2822 fm = formatter.nullformatter(ui, b'export', opts)
2822 fm = formatter.nullformatter(ui, b'export', opts)
2823 else:
2823 else:
2824 ui.pager(b'export')
2824 ui.pager(b'export')
2825 fm = ui.formatter(b'export', opts)
2825 fm = ui.formatter(b'export', opts)
2826 with fm:
2826 with fm:
2827 cmdutil.export(
2827 cmdutil.export(
2828 repo,
2828 repo,
2829 revs,
2829 revs,
2830 fm,
2830 fm,
2831 fntemplate=fntemplate,
2831 fntemplate=fntemplate,
2832 switch_parent=opts.get(b'switch_parent'),
2832 switch_parent=opts.get(b'switch_parent'),
2833 opts=patch.diffallopts(ui, opts),
2833 opts=patch.diffallopts(ui, opts),
2834 )
2834 )
2835
2835
2836
2836
2837 @command(
2837 @command(
2838 b'files',
2838 b'files',
2839 [
2839 [
2840 (
2840 (
2841 b'r',
2841 b'r',
2842 b'rev',
2842 b'rev',
2843 b'',
2843 b'',
2844 _(b'search the repository as it is in REV'),
2844 _(b'search the repository as it is in REV'),
2845 _(b'REV'),
2845 _(b'REV'),
2846 ),
2846 ),
2847 (
2847 (
2848 b'0',
2848 b'0',
2849 b'print0',
2849 b'print0',
2850 None,
2850 None,
2851 _(b'end filenames with NUL, for use with xargs'),
2851 _(b'end filenames with NUL, for use with xargs'),
2852 ),
2852 ),
2853 ]
2853 ]
2854 + walkopts
2854 + walkopts
2855 + formatteropts
2855 + formatteropts
2856 + subrepoopts,
2856 + subrepoopts,
2857 _(b'[OPTION]... [FILE]...'),
2857 _(b'[OPTION]... [FILE]...'),
2858 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2858 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2859 intents={INTENT_READONLY},
2859 intents={INTENT_READONLY},
2860 )
2860 )
2861 def files(ui, repo, *pats, **opts):
2861 def files(ui, repo, *pats, **opts):
2862 """list tracked files
2862 """list tracked files
2863
2863
2864 Print files under Mercurial control in the working directory or
2864 Print files under Mercurial control in the working directory or
2865 specified revision for given files (excluding removed files).
2865 specified revision for given files (excluding removed files).
2866 Files can be specified as filenames or filesets.
2866 Files can be specified as filenames or filesets.
2867
2867
2868 If no files are given to match, this command prints the names
2868 If no files are given to match, this command prints the names
2869 of all files under Mercurial control.
2869 of all files under Mercurial control.
2870
2870
2871 .. container:: verbose
2871 .. container:: verbose
2872
2872
2873 Template:
2873 Template:
2874
2874
2875 The following keywords are supported in addition to the common template
2875 The following keywords are supported in addition to the common template
2876 keywords and functions. See also :hg:`help templates`.
2876 keywords and functions. See also :hg:`help templates`.
2877
2877
2878 :flags: String. Character denoting file's symlink and executable bits.
2878 :flags: String. Character denoting file's symlink and executable bits.
2879 :path: String. Repository-absolute path of the file.
2879 :path: String. Repository-absolute path of the file.
2880 :size: Integer. Size of the file in bytes.
2880 :size: Integer. Size of the file in bytes.
2881
2881
2882 Examples:
2882 Examples:
2883
2883
2884 - list all files under the current directory::
2884 - list all files under the current directory::
2885
2885
2886 hg files .
2886 hg files .
2887
2887
2888 - shows sizes and flags for current revision::
2888 - shows sizes and flags for current revision::
2889
2889
2890 hg files -vr .
2890 hg files -vr .
2891
2891
2892 - list all files named README::
2892 - list all files named README::
2893
2893
2894 hg files -I "**/README"
2894 hg files -I "**/README"
2895
2895
2896 - list all binary files::
2896 - list all binary files::
2897
2897
2898 hg files "set:binary()"
2898 hg files "set:binary()"
2899
2899
2900 - find files containing a regular expression::
2900 - find files containing a regular expression::
2901
2901
2902 hg files "set:grep('bob')"
2902 hg files "set:grep('bob')"
2903
2903
2904 - search tracked file contents with xargs and grep::
2904 - search tracked file contents with xargs and grep::
2905
2905
2906 hg files -0 | xargs -0 grep foo
2906 hg files -0 | xargs -0 grep foo
2907
2907
2908 See :hg:`help patterns` and :hg:`help filesets` for more information
2908 See :hg:`help patterns` and :hg:`help filesets` for more information
2909 on specifying file patterns.
2909 on specifying file patterns.
2910
2910
2911 Returns 0 if a match is found, 1 otherwise.
2911 Returns 0 if a match is found, 1 otherwise.
2912
2912
2913 """
2913 """
2914
2914
2915 opts = pycompat.byteskwargs(opts)
2915 opts = pycompat.byteskwargs(opts)
2916 rev = opts.get(b'rev')
2916 rev = opts.get(b'rev')
2917 if rev:
2917 if rev:
2918 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2918 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2919 ctx = logcmdutil.revsingle(repo, rev, None)
2919 ctx = logcmdutil.revsingle(repo, rev, None)
2920
2920
2921 end = b'\n'
2921 end = b'\n'
2922 if opts.get(b'print0'):
2922 if opts.get(b'print0'):
2923 end = b'\0'
2923 end = b'\0'
2924 fmt = b'%s' + end
2924 fmt = b'%s' + end
2925
2925
2926 m = scmutil.match(ctx, pats, opts)
2926 m = scmutil.match(ctx, pats, opts)
2927 ui.pager(b'files')
2927 ui.pager(b'files')
2928 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2928 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2929 with ui.formatter(b'files', opts) as fm:
2929 with ui.formatter(b'files', opts) as fm:
2930 return cmdutil.files(
2930 return cmdutil.files(
2931 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2931 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2932 )
2932 )
2933
2933
2934
2934
2935 @command(
2935 @command(
2936 b'forget',
2936 b'forget',
2937 [
2937 [
2938 (b'i', b'interactive', None, _(b'use interactive mode')),
2938 (b'i', b'interactive', None, _(b'use interactive mode')),
2939 ]
2939 ]
2940 + walkopts
2940 + walkopts
2941 + dryrunopts,
2941 + dryrunopts,
2942 _(b'[OPTION]... FILE...'),
2942 _(b'[OPTION]... FILE...'),
2943 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2943 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2944 helpbasic=True,
2944 helpbasic=True,
2945 inferrepo=True,
2945 inferrepo=True,
2946 )
2946 )
2947 def forget(ui, repo, *pats, **opts):
2947 def forget(ui, repo, *pats, **opts):
2948 """forget the specified files on the next commit
2948 """forget the specified files on the next commit
2949
2949
2950 Mark the specified files so they will no longer be tracked
2950 Mark the specified files so they will no longer be tracked
2951 after the next commit.
2951 after the next commit.
2952
2952
2953 This only removes files from the current branch, not from the
2953 This only removes files from the current branch, not from the
2954 entire project history, and it does not delete them from the
2954 entire project history, and it does not delete them from the
2955 working directory.
2955 working directory.
2956
2956
2957 To delete the file from the working directory, see :hg:`remove`.
2957 To delete the file from the working directory, see :hg:`remove`.
2958
2958
2959 To undo a forget before the next commit, see :hg:`add`.
2959 To undo a forget before the next commit, see :hg:`add`.
2960
2960
2961 .. container:: verbose
2961 .. container:: verbose
2962
2962
2963 Examples:
2963 Examples:
2964
2964
2965 - forget newly-added binary files::
2965 - forget newly-added binary files::
2966
2966
2967 hg forget "set:added() and binary()"
2967 hg forget "set:added() and binary()"
2968
2968
2969 - forget files that would be excluded by .hgignore::
2969 - forget files that would be excluded by .hgignore::
2970
2970
2971 hg forget "set:hgignore()"
2971 hg forget "set:hgignore()"
2972
2972
2973 Returns 0 on success.
2973 Returns 0 on success.
2974 """
2974 """
2975
2975
2976 if not pats:
2976 if not pats:
2977 raise error.InputError(_(b'no files specified'))
2977 raise error.InputError(_(b'no files specified'))
2978
2978
2979 with repo.wlock(), repo.dirstate.changing_files(repo):
2979 with repo.wlock(), repo.dirstate.changing_files(repo):
2980 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
2980 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
2981 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2981 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2982 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2982 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2983 rejected = cmdutil.forget(
2983 rejected = cmdutil.forget(
2984 ui,
2984 ui,
2985 repo,
2985 repo,
2986 m,
2986 m,
2987 prefix=b"",
2987 prefix=b"",
2988 uipathfn=uipathfn,
2988 uipathfn=uipathfn,
2989 explicitonly=False,
2989 explicitonly=False,
2990 dryrun=dryrun,
2990 dryrun=dryrun,
2991 interactive=interactive,
2991 interactive=interactive,
2992 )[0]
2992 )[0]
2993 return rejected and 1 or 0
2993 return rejected and 1 or 0
2994
2994
2995
2995
2996 @command(
2996 @command(
2997 b'graft',
2997 b'graft',
2998 [
2998 [
2999 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2999 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
3000 (
3000 (
3001 b'',
3001 b'',
3002 b'base',
3002 b'base',
3003 b'',
3003 b'',
3004 _(b'base revision when doing the graft merge (ADVANCED)'),
3004 _(b'base revision when doing the graft merge (ADVANCED)'),
3005 _(b'REV'),
3005 _(b'REV'),
3006 ),
3006 ),
3007 (b'c', b'continue', False, _(b'resume interrupted graft')),
3007 (b'c', b'continue', False, _(b'resume interrupted graft')),
3008 (b'', b'stop', False, _(b'stop interrupted graft')),
3008 (b'', b'stop', False, _(b'stop interrupted graft')),
3009 (b'', b'abort', False, _(b'abort interrupted graft')),
3009 (b'', b'abort', False, _(b'abort interrupted graft')),
3010 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3010 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3011 (b'', b'log', None, _(b'append graft info to log message')),
3011 (b'', b'log', None, _(b'append graft info to log message')),
3012 (
3012 (
3013 b'',
3013 b'',
3014 b'no-commit',
3014 b'no-commit',
3015 None,
3015 None,
3016 _(b"don't commit, just apply the changes in working directory"),
3016 _(b"don't commit, just apply the changes in working directory"),
3017 ),
3017 ),
3018 (b'f', b'force', False, _(b'force graft')),
3018 (b'f', b'force', False, _(b'force graft')),
3019 (
3019 (
3020 b'D',
3020 b'D',
3021 b'currentdate',
3021 b'currentdate',
3022 False,
3022 False,
3023 _(b'record the current date as commit date'),
3023 _(b'record the current date as commit date'),
3024 ),
3024 ),
3025 (
3025 (
3026 b'U',
3026 b'U',
3027 b'currentuser',
3027 b'currentuser',
3028 False,
3028 False,
3029 _(b'record the current user as committer'),
3029 _(b'record the current user as committer'),
3030 ),
3030 ),
3031 ]
3031 ]
3032 + commitopts2
3032 + commitopts2
3033 + mergetoolopts
3033 + mergetoolopts
3034 + dryrunopts,
3034 + dryrunopts,
3035 _(b'[OPTION]... [-r REV]... REV...'),
3035 _(b'[OPTION]... [-r REV]... REV...'),
3036 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3036 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3037 )
3037 )
3038 def graft(ui, repo, *revs, **opts):
3038 def graft(ui, repo, *revs, **opts):
3039 """copy changes from other branches onto the current branch
3039 """copy changes from other branches onto the current branch
3040
3040
3041 This command uses Mercurial's merge logic to copy individual
3041 This command uses Mercurial's merge logic to copy individual
3042 changes from other branches without merging branches in the
3042 changes from other branches without merging branches in the
3043 history graph. This is sometimes known as 'backporting' or
3043 history graph. This is sometimes known as 'backporting' or
3044 'cherry-picking'. By default, graft will copy user, date, and
3044 'cherry-picking'. By default, graft will copy user, date, and
3045 description from the source changesets.
3045 description from the source changesets.
3046
3046
3047 Changesets that are ancestors of the current revision, that have
3047 Changesets that are ancestors of the current revision, that have
3048 already been grafted, or that are merges will be skipped.
3048 already been grafted, or that are merges will be skipped.
3049
3049
3050 If --log is specified, log messages will have a comment appended
3050 If --log is specified, log messages will have a comment appended
3051 of the form::
3051 of the form::
3052
3052
3053 (grafted from CHANGESETHASH)
3053 (grafted from CHANGESETHASH)
3054
3054
3055 If --force is specified, revisions will be grafted even if they
3055 If --force is specified, revisions will be grafted even if they
3056 are already ancestors of, or have been grafted to, the destination.
3056 are already ancestors of, or have been grafted to, the destination.
3057 This is useful when the revisions have since been backed out.
3057 This is useful when the revisions have since been backed out.
3058
3058
3059 If a graft merge results in conflicts, the graft process is
3059 If a graft merge results in conflicts, the graft process is
3060 interrupted so that the current merge can be manually resolved.
3060 interrupted so that the current merge can be manually resolved.
3061 Once all conflicts are addressed, the graft process can be
3061 Once all conflicts are addressed, the graft process can be
3062 continued with the -c/--continue option.
3062 continued with the -c/--continue option.
3063
3063
3064 The -c/--continue option reapplies all the earlier options.
3064 The -c/--continue option reapplies all the earlier options.
3065
3065
3066 .. container:: verbose
3066 .. container:: verbose
3067
3067
3068 The --base option exposes more of how graft internally uses merge with a
3068 The --base option exposes more of how graft internally uses merge with a
3069 custom base revision. --base can be used to specify another ancestor than
3069 custom base revision. --base can be used to specify another ancestor than
3070 the first and only parent.
3070 the first and only parent.
3071
3071
3072 The command::
3072 The command::
3073
3073
3074 hg graft -r 345 --base 234
3074 hg graft -r 345 --base 234
3075
3075
3076 is thus pretty much the same as::
3076 is thus pretty much the same as::
3077
3077
3078 hg diff --from 234 --to 345 | hg import
3078 hg diff --from 234 --to 345 | hg import
3079
3079
3080 but using merge to resolve conflicts and track moved files.
3080 but using merge to resolve conflicts and track moved files.
3081
3081
3082 The result of a merge can thus be backported as a single commit by
3082 The result of a merge can thus be backported as a single commit by
3083 specifying one of the merge parents as base, and thus effectively
3083 specifying one of the merge parents as base, and thus effectively
3084 grafting the changes from the other side.
3084 grafting the changes from the other side.
3085
3085
3086 It is also possible to collapse multiple changesets and clean up history
3086 It is also possible to collapse multiple changesets and clean up history
3087 by specifying another ancestor as base, much like rebase --collapse
3087 by specifying another ancestor as base, much like rebase --collapse
3088 --keep.
3088 --keep.
3089
3089
3090 The commit message can be tweaked after the fact using commit --amend .
3090 The commit message can be tweaked after the fact using commit --amend .
3091
3091
3092 For using non-ancestors as the base to backout changes, see the backout
3092 For using non-ancestors as the base to backout changes, see the backout
3093 command and the hidden --parent option.
3093 command and the hidden --parent option.
3094
3094
3095 .. container:: verbose
3095 .. container:: verbose
3096
3096
3097 Examples:
3097 Examples:
3098
3098
3099 - copy a single change to the stable branch and edit its description::
3099 - copy a single change to the stable branch and edit its description::
3100
3100
3101 hg update stable
3101 hg update stable
3102 hg graft --edit 9393
3102 hg graft --edit 9393
3103
3103
3104 - graft a range of changesets with one exception, updating dates::
3104 - graft a range of changesets with one exception, updating dates::
3105
3105
3106 hg graft -D "2085::2093 and not 2091"
3106 hg graft -D "2085::2093 and not 2091"
3107
3107
3108 - continue a graft after resolving conflicts::
3108 - continue a graft after resolving conflicts::
3109
3109
3110 hg graft -c
3110 hg graft -c
3111
3111
3112 - show the source of a grafted changeset::
3112 - show the source of a grafted changeset::
3113
3113
3114 hg log --debug -r .
3114 hg log --debug -r .
3115
3115
3116 - show revisions sorted by date::
3116 - show revisions sorted by date::
3117
3117
3118 hg log -r "sort(all(), date)"
3118 hg log -r "sort(all(), date)"
3119
3119
3120 - backport the result of a merge as a single commit::
3120 - backport the result of a merge as a single commit::
3121
3121
3122 hg graft -r 123 --base 123^
3122 hg graft -r 123 --base 123^
3123
3123
3124 - land a feature branch as one changeset::
3124 - land a feature branch as one changeset::
3125
3125
3126 hg up -cr default
3126 hg up -cr default
3127 hg graft -r featureX --base "ancestor('featureX', 'default')"
3127 hg graft -r featureX --base "ancestor('featureX', 'default')"
3128
3128
3129 See :hg:`help revisions` for more about specifying revisions.
3129 See :hg:`help revisions` for more about specifying revisions.
3130
3130
3131 Returns 0 on successful completion, 1 if there are unresolved files.
3131 Returns 0 on successful completion, 1 if there are unresolved files.
3132 """
3132 """
3133 with repo.wlock():
3133 with repo.wlock():
3134 return _dograft(ui, repo, *revs, **opts)
3134 return _dograft(ui, repo, *revs, **opts)
3135
3135
3136
3136
3137 def _dograft(ui, repo, *revs, **opts):
3137 def _dograft(ui, repo, *revs, **opts):
3138 if revs and opts.get('rev'):
3138 if revs and opts.get('rev'):
3139 ui.warn(
3139 ui.warn(
3140 _(
3140 _(
3141 b'warning: inconsistent use of --rev might give unexpected '
3141 b'warning: inconsistent use of --rev might give unexpected '
3142 b'revision ordering!\n'
3142 b'revision ordering!\n'
3143 )
3143 )
3144 )
3144 )
3145
3145
3146 revs = list(revs)
3146 revs = list(revs)
3147 revs.extend(opts.get('rev'))
3147 revs.extend(opts.get('rev'))
3148 # a dict of data to be stored in state file
3148 # a dict of data to be stored in state file
3149 statedata = {}
3149 statedata = {}
3150 # list of new nodes created by ongoing graft
3150 # list of new nodes created by ongoing graft
3151 statedata[b'newnodes'] = []
3151 statedata[b'newnodes'] = []
3152
3152
3153 cmdutil.resolve_commit_options(ui, opts)
3153 cmdutil.resolve_commit_options(ui, opts)
3154
3154
3155 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3155 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3156
3156
3157 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3157 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3158
3158
3159 cont = False
3159 cont = False
3160 if opts.get('no_commit'):
3160 if opts.get('no_commit'):
3161 cmdutil.check_incompatible_arguments(
3161 cmdutil.check_incompatible_arguments(
3162 opts,
3162 opts,
3163 'no_commit',
3163 'no_commit',
3164 ['edit', 'currentuser', 'currentdate', 'log'],
3164 ['edit', 'currentuser', 'currentdate', 'log'],
3165 )
3165 )
3166
3166
3167 graftstate = statemod.cmdstate(repo, b'graftstate')
3167 graftstate = statemod.cmdstate(repo, b'graftstate')
3168
3168
3169 if opts.get('stop'):
3169 if opts.get('stop'):
3170 cmdutil.check_incompatible_arguments(
3170 cmdutil.check_incompatible_arguments(
3171 opts,
3171 opts,
3172 'stop',
3172 'stop',
3173 [
3173 [
3174 'edit',
3174 'edit',
3175 'log',
3175 'log',
3176 'user',
3176 'user',
3177 'date',
3177 'date',
3178 'currentdate',
3178 'currentdate',
3179 'currentuser',
3179 'currentuser',
3180 'rev',
3180 'rev',
3181 ],
3181 ],
3182 )
3182 )
3183 return _stopgraft(ui, repo, graftstate)
3183 return _stopgraft(ui, repo, graftstate)
3184 elif opts.get('abort'):
3184 elif opts.get('abort'):
3185 cmdutil.check_incompatible_arguments(
3185 cmdutil.check_incompatible_arguments(
3186 opts,
3186 opts,
3187 'abort',
3187 'abort',
3188 [
3188 [
3189 'edit',
3189 'edit',
3190 'log',
3190 'log',
3191 'user',
3191 'user',
3192 'date',
3192 'date',
3193 'currentdate',
3193 'currentdate',
3194 'currentuser',
3194 'currentuser',
3195 'rev',
3195 'rev',
3196 ],
3196 ],
3197 )
3197 )
3198 return cmdutil.abortgraft(ui, repo, graftstate)
3198 return cmdutil.abortgraft(ui, repo, graftstate)
3199 elif opts.get('continue'):
3199 elif opts.get('continue'):
3200 cont = True
3200 cont = True
3201 if revs:
3201 if revs:
3202 raise error.InputError(_(b"can't specify --continue and revisions"))
3202 raise error.InputError(_(b"can't specify --continue and revisions"))
3203 # read in unfinished revisions
3203 # read in unfinished revisions
3204 if graftstate.exists():
3204 if graftstate.exists():
3205 statedata = cmdutil.readgraftstate(repo, graftstate)
3205 statedata = cmdutil.readgraftstate(repo, graftstate)
3206 if statedata.get(b'date'):
3206 if statedata.get(b'date'):
3207 opts['date'] = statedata[b'date']
3207 opts['date'] = statedata[b'date']
3208 if statedata.get(b'user'):
3208 if statedata.get(b'user'):
3209 opts['user'] = statedata[b'user']
3209 opts['user'] = statedata[b'user']
3210 if statedata.get(b'log'):
3210 if statedata.get(b'log'):
3211 opts['log'] = True
3211 opts['log'] = True
3212 if statedata.get(b'no_commit'):
3212 if statedata.get(b'no_commit'):
3213 opts['no_commit'] = statedata.get(b'no_commit')
3213 opts['no_commit'] = statedata.get(b'no_commit')
3214 if statedata.get(b'base'):
3214 if statedata.get(b'base'):
3215 opts['base'] = statedata.get(b'base')
3215 opts['base'] = statedata.get(b'base')
3216 nodes = statedata[b'nodes']
3216 nodes = statedata[b'nodes']
3217 revs = [repo[node].rev() for node in nodes]
3217 revs = [repo[node].rev() for node in nodes]
3218 else:
3218 else:
3219 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3219 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3220 else:
3220 else:
3221 if not revs:
3221 if not revs:
3222 raise error.InputError(_(b'no revisions specified'))
3222 raise error.InputError(_(b'no revisions specified'))
3223 cmdutil.checkunfinished(repo)
3223 cmdutil.checkunfinished(repo)
3224 cmdutil.bailifchanged(repo)
3224 cmdutil.bailifchanged(repo)
3225 revs = logcmdutil.revrange(repo, revs)
3225 revs = logcmdutil.revrange(repo, revs)
3226
3226
3227 skipped = set()
3227 skipped = set()
3228 basectx = None
3228 basectx = None
3229 if opts.get('base'):
3229 if opts.get('base'):
3230 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3230 basectx = logcmdutil.revsingle(repo, opts['base'], None)
3231 if basectx is None:
3231 if basectx is None:
3232 # check for merges
3232 # check for merges
3233 for rev in repo.revs(b'%ld and merge()', revs):
3233 for rev in repo.revs(b'%ld and merge()', revs):
3234 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3234 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3235 skipped.add(rev)
3235 skipped.add(rev)
3236 revs = [r for r in revs if r not in skipped]
3236 revs = [r for r in revs if r not in skipped]
3237 if not revs:
3237 if not revs:
3238 return -1
3238 return -1
3239 if basectx is not None and len(revs) != 1:
3239 if basectx is not None and len(revs) != 1:
3240 raise error.InputError(_(b'only one revision allowed with --base '))
3240 raise error.InputError(_(b'only one revision allowed with --base '))
3241
3241
3242 # Don't check in the --continue case, in effect retaining --force across
3242 # Don't check in the --continue case, in effect retaining --force across
3243 # --continues. That's because without --force, any revisions we decided to
3243 # --continues. That's because without --force, any revisions we decided to
3244 # skip would have been filtered out here, so they wouldn't have made their
3244 # skip would have been filtered out here, so they wouldn't have made their
3245 # way to the graftstate. With --force, any revisions we would have otherwise
3245 # way to the graftstate. With --force, any revisions we would have otherwise
3246 # skipped would not have been filtered out, and if they hadn't been applied
3246 # skipped would not have been filtered out, and if they hadn't been applied
3247 # already, they'd have been in the graftstate.
3247 # already, they'd have been in the graftstate.
3248 if not (cont or opts.get('force')) and basectx is None:
3248 if not (cont or opts.get('force')) and basectx is None:
3249 # check for ancestors of dest branch
3249 # check for ancestors of dest branch
3250 ancestors = repo.revs(b'%ld & (::.)', revs)
3250 ancestors = repo.revs(b'%ld & (::.)', revs)
3251 for rev in ancestors:
3251 for rev in ancestors:
3252 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3252 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3253
3253
3254 revs = [r for r in revs if r not in ancestors]
3254 revs = [r for r in revs if r not in ancestors]
3255
3255
3256 if not revs:
3256 if not revs:
3257 return -1
3257 return -1
3258
3258
3259 # analyze revs for earlier grafts
3259 # analyze revs for earlier grafts
3260 ids = {}
3260 ids = {}
3261 for ctx in repo.set(b"%ld", revs):
3261 for ctx in repo.set(b"%ld", revs):
3262 ids[ctx.hex()] = ctx.rev()
3262 ids[ctx.hex()] = ctx.rev()
3263 n = ctx.extra().get(b'source')
3263 n = ctx.extra().get(b'source')
3264 if n:
3264 if n:
3265 ids[n] = ctx.rev()
3265 ids[n] = ctx.rev()
3266
3266
3267 # check ancestors for earlier grafts
3267 # check ancestors for earlier grafts
3268 ui.debug(b'scanning for duplicate grafts\n')
3268 ui.debug(b'scanning for duplicate grafts\n')
3269
3269
3270 # The only changesets we can be sure doesn't contain grafts of any
3270 # The only changesets we can be sure doesn't contain grafts of any
3271 # revs, are the ones that are common ancestors of *all* revs:
3271 # revs, are the ones that are common ancestors of *all* revs:
3272 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3272 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3273 ctx = repo[rev]
3273 ctx = repo[rev]
3274 n = ctx.extra().get(b'source')
3274 n = ctx.extra().get(b'source')
3275 if n in ids:
3275 if n in ids:
3276 try:
3276 try:
3277 r = repo[n].rev()
3277 r = repo[n].rev()
3278 except error.RepoLookupError:
3278 except error.RepoLookupError:
3279 r = None
3279 r = None
3280 if r in revs:
3280 if r in revs:
3281 ui.warn(
3281 ui.warn(
3282 _(
3282 _(
3283 b'skipping revision %d:%s '
3283 b'skipping revision %d:%s '
3284 b'(already grafted to %d:%s)\n'
3284 b'(already grafted to %d:%s)\n'
3285 )
3285 )
3286 % (r, repo[r], rev, ctx)
3286 % (r, repo[r], rev, ctx)
3287 )
3287 )
3288 revs.remove(r)
3288 revs.remove(r)
3289 elif ids[n] in revs:
3289 elif ids[n] in revs:
3290 if r is None:
3290 if r is None:
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'(%d:%s also has unknown origin %s)\n'
3294 b'(%d:%s also has unknown origin %s)\n'
3295 )
3295 )
3296 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3296 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3297 )
3297 )
3298 else:
3298 else:
3299 ui.warn(
3299 ui.warn(
3300 _(
3300 _(
3301 b'skipping already grafted revision %d:%s '
3301 b'skipping already grafted revision %d:%s '
3302 b'(%d:%s also has origin %d:%s)\n'
3302 b'(%d:%s also has origin %d:%s)\n'
3303 )
3303 )
3304 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3304 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3305 )
3305 )
3306 revs.remove(ids[n])
3306 revs.remove(ids[n])
3307 elif ctx.hex() in ids:
3307 elif ctx.hex() in ids:
3308 r = ids[ctx.hex()]
3308 r = ids[ctx.hex()]
3309 if r in revs:
3309 if r in revs:
3310 ui.warn(
3310 ui.warn(
3311 _(
3311 _(
3312 b'skipping already grafted revision %d:%s '
3312 b'skipping already grafted revision %d:%s '
3313 b'(was grafted from %d:%s)\n'
3313 b'(was grafted from %d:%s)\n'
3314 )
3314 )
3315 % (r, repo[r], rev, ctx)
3315 % (r, repo[r], rev, ctx)
3316 )
3316 )
3317 revs.remove(r)
3317 revs.remove(r)
3318 if not revs:
3318 if not revs:
3319 return -1
3319 return -1
3320
3320
3321 if opts.get('no_commit'):
3321 if opts.get('no_commit'):
3322 statedata[b'no_commit'] = True
3322 statedata[b'no_commit'] = True
3323 if opts.get('base'):
3323 if opts.get('base'):
3324 statedata[b'base'] = opts['base']
3324 statedata[b'base'] = opts['base']
3325 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3325 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3326 desc = b'%d:%s "%s"' % (
3326 desc = b'%d:%s "%s"' % (
3327 ctx.rev(),
3327 ctx.rev(),
3328 ctx,
3328 ctx,
3329 ctx.description().split(b'\n', 1)[0],
3329 ctx.description().split(b'\n', 1)[0],
3330 )
3330 )
3331 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3331 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3332 if names:
3332 if names:
3333 desc += b' (%s)' % b' '.join(names)
3333 desc += b' (%s)' % b' '.join(names)
3334 ui.status(_(b'grafting %s\n') % desc)
3334 ui.status(_(b'grafting %s\n') % desc)
3335 if opts.get('dry_run'):
3335 if opts.get('dry_run'):
3336 continue
3336 continue
3337
3337
3338 source = ctx.extra().get(b'source')
3338 source = ctx.extra().get(b'source')
3339 extra = {}
3339 extra = {}
3340 if source:
3340 if source:
3341 extra[b'source'] = source
3341 extra[b'source'] = source
3342 extra[b'intermediate-source'] = ctx.hex()
3342 extra[b'intermediate-source'] = ctx.hex()
3343 else:
3343 else:
3344 extra[b'source'] = ctx.hex()
3344 extra[b'source'] = ctx.hex()
3345 user = ctx.user()
3345 user = ctx.user()
3346 if opts.get('user'):
3346 if opts.get('user'):
3347 user = opts['user']
3347 user = opts['user']
3348 statedata[b'user'] = user
3348 statedata[b'user'] = user
3349 date = ctx.date()
3349 date = ctx.date()
3350 if opts.get('date'):
3350 if opts.get('date'):
3351 date = opts['date']
3351 date = opts['date']
3352 statedata[b'date'] = date
3352 statedata[b'date'] = date
3353 message = ctx.description()
3353 message = ctx.description()
3354 if opts.get('log'):
3354 if opts.get('log'):
3355 message += b'\n(grafted from %s)' % ctx.hex()
3355 message += b'\n(grafted from %s)' % ctx.hex()
3356 statedata[b'log'] = True
3356 statedata[b'log'] = True
3357
3357
3358 # we don't merge the first commit when continuing
3358 # we don't merge the first commit when continuing
3359 if not cont:
3359 if not cont:
3360 # perform the graft merge with p1(rev) as 'ancestor'
3360 # perform the graft merge with p1(rev) as 'ancestor'
3361 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3361 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3362 base = ctx.p1() if basectx is None else basectx
3362 base = ctx.p1() if basectx is None else basectx
3363 with ui.configoverride(overrides, b'graft'):
3363 with ui.configoverride(overrides, b'graft'):
3364 stats = mergemod.graft(
3364 stats = mergemod.graft(
3365 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3365 repo, ctx, base, [b'local', b'graft', b'parent of graft']
3366 )
3366 )
3367 # report any conflicts
3367 # report any conflicts
3368 if stats.unresolvedcount > 0:
3368 if stats.unresolvedcount > 0:
3369 # write out state for --continue
3369 # write out state for --continue
3370 nodes = [repo[rev].hex() for rev in revs[pos:]]
3370 nodes = [repo[rev].hex() for rev in revs[pos:]]
3371 statedata[b'nodes'] = nodes
3371 statedata[b'nodes'] = nodes
3372 stateversion = 1
3372 stateversion = 1
3373 graftstate.save(stateversion, statedata)
3373 graftstate.save(stateversion, statedata)
3374 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3374 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3375 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3375 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3376 return 1
3376 return 1
3377 else:
3377 else:
3378 cont = False
3378 cont = False
3379
3379
3380 # commit if --no-commit is false
3380 # commit if --no-commit is false
3381 if not opts.get('no_commit'):
3381 if not opts.get('no_commit'):
3382 node = repo.commit(
3382 node = repo.commit(
3383 text=message, user=user, date=date, extra=extra, editor=editor
3383 text=message, user=user, date=date, extra=extra, editor=editor
3384 )
3384 )
3385 if node is None:
3385 if node is None:
3386 ui.warn(
3386 ui.warn(
3387 _(b'note: graft of %d:%s created no changes to commit\n')
3387 _(b'note: graft of %d:%s created no changes to commit\n')
3388 % (ctx.rev(), ctx)
3388 % (ctx.rev(), ctx)
3389 )
3389 )
3390 # checking that newnodes exist because old state files won't have it
3390 # checking that newnodes exist because old state files won't have it
3391 elif statedata.get(b'newnodes') is not None:
3391 elif statedata.get(b'newnodes') is not None:
3392 nn = statedata[b'newnodes']
3392 nn = statedata[b'newnodes']
3393 assert isinstance(nn, list) # list of bytes
3393 assert isinstance(nn, list) # list of bytes
3394 nn.append(node)
3394 nn.append(node)
3395
3395
3396 # remove state when we complete successfully
3396 # remove state when we complete successfully
3397 if not opts.get('dry_run'):
3397 if not opts.get('dry_run'):
3398 graftstate.delete()
3398 graftstate.delete()
3399
3399
3400 return 0
3400 return 0
3401
3401
3402
3402
3403 def _stopgraft(ui, repo, graftstate):
3403 def _stopgraft(ui, repo, graftstate):
3404 """stop the interrupted graft"""
3404 """stop the interrupted graft"""
3405 if not graftstate.exists():
3405 if not graftstate.exists():
3406 raise error.StateError(_(b"no interrupted graft found"))
3406 raise error.StateError(_(b"no interrupted graft found"))
3407 pctx = repo[b'.']
3407 pctx = repo[b'.']
3408 mergemod.clean_update(pctx)
3408 mergemod.clean_update(pctx)
3409 graftstate.delete()
3409 graftstate.delete()
3410 ui.status(_(b"stopped the interrupted graft\n"))
3410 ui.status(_(b"stopped the interrupted graft\n"))
3411 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3411 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3412 return 0
3412 return 0
3413
3413
3414
3414
3415 statemod.addunfinished(
3415 statemod.addunfinished(
3416 b'graft',
3416 b'graft',
3417 fname=b'graftstate',
3417 fname=b'graftstate',
3418 clearable=True,
3418 clearable=True,
3419 stopflag=True,
3419 stopflag=True,
3420 continueflag=True,
3420 continueflag=True,
3421 abortfunc=cmdutil.hgabortgraft,
3421 abortfunc=cmdutil.hgabortgraft,
3422 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3422 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3423 )
3423 )
3424
3424
3425
3425
3426 @command(
3426 @command(
3427 b'grep',
3427 b'grep',
3428 [
3428 [
3429 (b'0', b'print0', None, _(b'end fields with NUL')),
3429 (b'0', b'print0', None, _(b'end fields with NUL')),
3430 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3430 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3431 (
3431 (
3432 b'',
3432 b'',
3433 b'diff',
3433 b'diff',
3434 None,
3434 None,
3435 _(
3435 _(
3436 b'search revision differences for when the pattern was added '
3436 b'search revision differences for when the pattern was added '
3437 b'or removed'
3437 b'or removed'
3438 ),
3438 ),
3439 ),
3439 ),
3440 (b'a', b'text', None, _(b'treat all files as text')),
3440 (b'a', b'text', None, _(b'treat all files as text')),
3441 (
3441 (
3442 b'f',
3442 b'f',
3443 b'follow',
3443 b'follow',
3444 None,
3444 None,
3445 _(
3445 _(
3446 b'follow changeset history,'
3446 b'follow changeset history,'
3447 b' or file history across copies and renames'
3447 b' or file history across copies and renames'
3448 ),
3448 ),
3449 ),
3449 ),
3450 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3450 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3451 (
3451 (
3452 b'l',
3452 b'l',
3453 b'files-with-matches',
3453 b'files-with-matches',
3454 None,
3454 None,
3455 _(b'print only filenames and revisions that match'),
3455 _(b'print only filenames and revisions that match'),
3456 ),
3456 ),
3457 (b'n', b'line-number', None, _(b'print matching line numbers')),
3457 (b'n', b'line-number', None, _(b'print matching line numbers')),
3458 (
3458 (
3459 b'r',
3459 b'r',
3460 b'rev',
3460 b'rev',
3461 [],
3461 [],
3462 _(b'search files changed within revision range'),
3462 _(b'search files changed within revision range'),
3463 _(b'REV'),
3463 _(b'REV'),
3464 ),
3464 ),
3465 (
3465 (
3466 b'',
3466 b'',
3467 b'all-files',
3467 b'all-files',
3468 None,
3468 None,
3469 _(
3469 _(
3470 b'include all files in the changeset while grepping (DEPRECATED)'
3470 b'include all files in the changeset while grepping (DEPRECATED)'
3471 ),
3471 ),
3472 ),
3472 ),
3473 (b'u', b'user', None, _(b'list the author (long with -v)')),
3473 (b'u', b'user', None, _(b'list the author (long with -v)')),
3474 (b'd', b'date', None, _(b'list the date (short with -q)')),
3474 (b'd', b'date', None, _(b'list the date (short with -q)')),
3475 ]
3475 ]
3476 + formatteropts
3476 + formatteropts
3477 + walkopts,
3477 + walkopts,
3478 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3478 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3479 helpcategory=command.CATEGORY_FILE_CONTENTS,
3479 helpcategory=command.CATEGORY_FILE_CONTENTS,
3480 inferrepo=True,
3480 inferrepo=True,
3481 intents={INTENT_READONLY},
3481 intents={INTENT_READONLY},
3482 )
3482 )
3483 def grep(ui, repo, pattern, *pats, **opts):
3483 def grep(ui, repo, pattern, *pats, **opts):
3484 """search for a pattern in specified files
3484 """search for a pattern in specified files
3485
3485
3486 Search the working directory or revision history for a regular
3486 Search the working directory or revision history for a regular
3487 expression in the specified files for the entire repository.
3487 expression in the specified files for the entire repository.
3488
3488
3489 By default, grep searches the repository files in the working
3489 By default, grep searches the repository files in the working
3490 directory and prints the files where it finds a match. To specify
3490 directory and prints the files where it finds a match. To specify
3491 historical revisions instead of the working directory, use the
3491 historical revisions instead of the working directory, use the
3492 --rev flag.
3492 --rev flag.
3493
3493
3494 To search instead historical revision differences that contains a
3494 To search instead historical revision differences that contains a
3495 change in match status ("-" for a match that becomes a non-match,
3495 change in match status ("-" for a match that becomes a non-match,
3496 or "+" for a non-match that becomes a match), use the --diff flag.
3496 or "+" for a non-match that becomes a match), use the --diff flag.
3497
3497
3498 PATTERN can be any Python (roughly Perl-compatible) regular
3498 PATTERN can be any Python (roughly Perl-compatible) regular
3499 expression.
3499 expression.
3500
3500
3501 If no FILEs are specified and the --rev flag isn't supplied, all
3501 If no FILEs are specified and the --rev flag isn't supplied, all
3502 files in the working directory are searched. When using the --rev
3502 files in the working directory are searched. When using the --rev
3503 flag and specifying FILEs, use the --follow argument to also
3503 flag and specifying FILEs, use the --follow argument to also
3504 follow the specified FILEs across renames and copies.
3504 follow the specified FILEs across renames and copies.
3505
3505
3506 .. container:: verbose
3506 .. container:: verbose
3507
3507
3508 Template:
3508 Template:
3509
3509
3510 The following keywords are supported in addition to the common template
3510 The following keywords are supported in addition to the common template
3511 keywords and functions. See also :hg:`help templates`.
3511 keywords and functions. See also :hg:`help templates`.
3512
3512
3513 :change: String. Character denoting insertion ``+`` or removal ``-``.
3513 :change: String. Character denoting insertion ``+`` or removal ``-``.
3514 Available if ``--diff`` is specified.
3514 Available if ``--diff`` is specified.
3515 :lineno: Integer. Line number of the match.
3515 :lineno: Integer. Line number of the match.
3516 :path: String. Repository-absolute path of the file.
3516 :path: String. Repository-absolute path of the file.
3517 :texts: List of text chunks.
3517 :texts: List of text chunks.
3518
3518
3519 And each entry of ``{texts}`` provides the following sub-keywords.
3519 And each entry of ``{texts}`` provides the following sub-keywords.
3520
3520
3521 :matched: Boolean. True if the chunk matches the specified pattern.
3521 :matched: Boolean. True if the chunk matches the specified pattern.
3522 :text: String. Chunk content.
3522 :text: String. Chunk content.
3523
3523
3524 See :hg:`help templates.operators` for the list expansion syntax.
3524 See :hg:`help templates.operators` for the list expansion syntax.
3525
3525
3526 Returns 0 if a match is found, 1 otherwise.
3526 Returns 0 if a match is found, 1 otherwise.
3527
3527
3528 """
3528 """
3529 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3529 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3530
3530
3531 diff = opts.get('all') or opts.get('diff')
3531 diff = opts.get('all') or opts.get('diff')
3532 follow = opts.get('follow')
3532 follow = opts.get('follow')
3533 if opts.get('all_files') is None and not diff:
3533 if opts.get('all_files') is None and not diff:
3534 opts['all_files'] = True
3534 opts['all_files'] = True
3535 plaingrep = (
3535 plaingrep = (
3536 opts.get('all_files') and not opts.get('rev') and not opts.get('follow')
3536 opts.get('all_files') and not opts.get('rev') and not opts.get('follow')
3537 )
3537 )
3538 all_files = opts.get('all_files')
3538 all_files = opts.get('all_files')
3539 if plaingrep:
3539 if plaingrep:
3540 opts['rev'] = [b'wdir()']
3540 opts['rev'] = [b'wdir()']
3541
3541
3542 reflags = re.M
3542 reflags = re.M
3543 if opts.get('ignore_case'):
3543 if opts.get('ignore_case'):
3544 reflags |= re.I
3544 reflags |= re.I
3545 try:
3545 try:
3546 regexp = util.re.compile(pattern, reflags)
3546 regexp = util.re.compile(pattern, reflags)
3547 except re.error as inst:
3547 except re.error as inst:
3548 ui.warn(
3548 ui.warn(
3549 _(b"grep: invalid match pattern: %s\n")
3549 _(b"grep: invalid match pattern: %s\n")
3550 % stringutil.forcebytestr(inst)
3550 % stringutil.forcebytestr(inst)
3551 )
3551 )
3552 return 1
3552 return 1
3553 sep, eol = b':', b'\n'
3553 sep, eol = b':', b'\n'
3554 if opts.get('print0'):
3554 if opts.get('print0'):
3555 sep = eol = b'\0'
3555 sep = eol = b'\0'
3556
3556
3557 searcher = grepmod.grepsearcher(
3557 searcher = grepmod.grepsearcher(
3558 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3558 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3559 )
3559 )
3560
3560
3561 getfile = searcher._getfile
3561 getfile = searcher._getfile
3562
3562
3563 uipathfn = scmutil.getuipathfn(repo)
3563 uipathfn = scmutil.getuipathfn(repo)
3564
3564
3565 def display(fm, fn, ctx, pstates, states):
3565 def display(fm, fn, ctx, pstates, states):
3566 rev = scmutil.intrev(ctx)
3566 rev = scmutil.intrev(ctx)
3567 if fm.isplain():
3567 if fm.isplain():
3568 formatuser = ui.shortuser
3568 formatuser = ui.shortuser
3569 else:
3569 else:
3570 formatuser = pycompat.bytestr
3570 formatuser = pycompat.bytestr
3571 if ui.quiet:
3571 if ui.quiet:
3572 datefmt = b'%Y-%m-%d'
3572 datefmt = b'%Y-%m-%d'
3573 else:
3573 else:
3574 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3574 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3575 found = False
3575 found = False
3576
3576
3577 @util.cachefunc
3577 @util.cachefunc
3578 def binary():
3578 def binary():
3579 flog = getfile(fn)
3579 flog = getfile(fn)
3580 try:
3580 try:
3581 return stringutil.binary(flog.read(ctx.filenode(fn)))
3581 return stringutil.binary(flog.read(ctx.filenode(fn)))
3582 except error.WdirUnsupported:
3582 except error.WdirUnsupported:
3583 return ctx[fn].isbinary()
3583 return ctx[fn].isbinary()
3584
3584
3585 fieldnamemap = {b'linenumber': b'lineno'}
3585 fieldnamemap = {b'linenumber': b'lineno'}
3586 if diff:
3586 if diff:
3587 iter = grepmod.difflinestates(pstates, states)
3587 iter = grepmod.difflinestates(pstates, states)
3588 else:
3588 else:
3589 iter = [(b'', l) for l in states]
3589 iter = [(b'', l) for l in states]
3590 for change, l in iter:
3590 for change, l in iter:
3591 fm.startitem()
3591 fm.startitem()
3592 fm.context(ctx=ctx)
3592 fm.context(ctx=ctx)
3593 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3593 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3594 fm.plain(uipathfn(fn), label=b'grep.filename')
3594 fm.plain(uipathfn(fn), label=b'grep.filename')
3595
3595
3596 cols = [
3596 cols = [
3597 (b'rev', b'%d', rev, not plaingrep, b''),
3597 (b'rev', b'%d', rev, not plaingrep, b''),
3598 (
3598 (
3599 b'linenumber',
3599 b'linenumber',
3600 b'%d',
3600 b'%d',
3601 l.linenum,
3601 l.linenum,
3602 opts.get('line_number'),
3602 opts.get('line_number'),
3603 b'',
3603 b'',
3604 ),
3604 ),
3605 ]
3605 ]
3606 if diff:
3606 if diff:
3607 cols.append(
3607 cols.append(
3608 (
3608 (
3609 b'change',
3609 b'change',
3610 b'%s',
3610 b'%s',
3611 change,
3611 change,
3612 True,
3612 True,
3613 b'grep.inserted '
3613 b'grep.inserted '
3614 if change == b'+'
3614 if change == b'+'
3615 else b'grep.deleted ',
3615 else b'grep.deleted ',
3616 )
3616 )
3617 )
3617 )
3618 cols.extend(
3618 cols.extend(
3619 [
3619 [
3620 (
3620 (
3621 b'user',
3621 b'user',
3622 b'%s',
3622 b'%s',
3623 formatuser(ctx.user()),
3623 formatuser(ctx.user()),
3624 opts.get('user'),
3624 opts.get('user'),
3625 b'',
3625 b'',
3626 ),
3626 ),
3627 (
3627 (
3628 b'date',
3628 b'date',
3629 b'%s',
3629 b'%s',
3630 fm.formatdate(ctx.date(), datefmt),
3630 fm.formatdate(ctx.date(), datefmt),
3631 opts.get('date'),
3631 opts.get('date'),
3632 b'',
3632 b'',
3633 ),
3633 ),
3634 ]
3634 ]
3635 )
3635 )
3636 for name, fmt, data, cond, extra_label in cols:
3636 for name, fmt, data, cond, extra_label in cols:
3637 if cond:
3637 if cond:
3638 fm.plain(sep, label=b'grep.sep')
3638 fm.plain(sep, label=b'grep.sep')
3639 field = fieldnamemap.get(name, name)
3639 field = fieldnamemap.get(name, name)
3640 label = extra_label + (b'grep.%s' % name)
3640 label = extra_label + (b'grep.%s' % name)
3641 fm.condwrite(cond, field, fmt, data, label=label)
3641 fm.condwrite(cond, field, fmt, data, label=label)
3642 if not opts.get('files_with_matches'):
3642 if not opts.get('files_with_matches'):
3643 fm.plain(sep, label=b'grep.sep')
3643 fm.plain(sep, label=b'grep.sep')
3644 if not opts.get('text') and binary():
3644 if not opts.get('text') and binary():
3645 fm.plain(_(b" Binary file matches"))
3645 fm.plain(_(b" Binary file matches"))
3646 else:
3646 else:
3647 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3647 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3648 fm.plain(eol)
3648 fm.plain(eol)
3649 found = True
3649 found = True
3650 if opts.get('files_with_matches'):
3650 if opts.get('files_with_matches'):
3651 break
3651 break
3652 return found
3652 return found
3653
3653
3654 def displaymatches(fm, l):
3654 def displaymatches(fm, l):
3655 p = 0
3655 p = 0
3656 for s, e in l.findpos(regexp):
3656 for s, e in l.findpos(regexp):
3657 if p < s:
3657 if p < s:
3658 fm.startitem()
3658 fm.startitem()
3659 fm.write(b'text', b'%s', l.line[p:s])
3659 fm.write(b'text', b'%s', l.line[p:s])
3660 fm.data(matched=False)
3660 fm.data(matched=False)
3661 fm.startitem()
3661 fm.startitem()
3662 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3662 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3663 fm.data(matched=True)
3663 fm.data(matched=True)
3664 p = e
3664 p = e
3665 if p < len(l.line):
3665 if p < len(l.line):
3666 fm.startitem()
3666 fm.startitem()
3667 fm.write(b'text', b'%s', l.line[p:])
3667 fm.write(b'text', b'%s', l.line[p:])
3668 fm.data(matched=False)
3668 fm.data(matched=False)
3669 fm.end()
3669 fm.end()
3670
3670
3671 found = False
3671 found = False
3672
3672
3673 wopts = logcmdutil.walkopts(
3673 wopts = logcmdutil.walkopts(
3674 pats=pats,
3674 pats=pats,
3675 opts=opts,
3675 opts=opts,
3676 revspec=opts['rev'],
3676 revspec=opts['rev'],
3677 include_pats=opts['include'],
3677 include_pats=opts['include'],
3678 exclude_pats=opts['exclude'],
3678 exclude_pats=opts['exclude'],
3679 follow=follow,
3679 follow=follow,
3680 force_changelog_traversal=all_files,
3680 force_changelog_traversal=all_files,
3681 filter_revisions_by_pats=not all_files,
3681 filter_revisions_by_pats=not all_files,
3682 )
3682 )
3683 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3683 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3684
3684
3685 ui.pager(b'grep')
3685 ui.pager(b'grep')
3686 fm = ui.formatter(b'grep', pycompat.byteskwargs(opts))
3686 fm = ui.formatter(b'grep', pycompat.byteskwargs(opts))
3687 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3687 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3688 r = display(fm, fn, ctx, pstates, states)
3688 r = display(fm, fn, ctx, pstates, states)
3689 found = found or r
3689 found = found or r
3690 if r and not diff and not all_files:
3690 if r and not diff and not all_files:
3691 searcher.skipfile(fn, ctx.rev())
3691 searcher.skipfile(fn, ctx.rev())
3692 fm.end()
3692 fm.end()
3693
3693
3694 return not found
3694 return not found
3695
3695
3696
3696
3697 @command(
3697 @command(
3698 b'heads',
3698 b'heads',
3699 [
3699 [
3700 (
3700 (
3701 b'r',
3701 b'r',
3702 b'rev',
3702 b'rev',
3703 b'',
3703 b'',
3704 _(b'show only heads which are descendants of STARTREV'),
3704 _(b'show only heads which are descendants of STARTREV'),
3705 _(b'STARTREV'),
3705 _(b'STARTREV'),
3706 ),
3706 ),
3707 (b't', b'topo', False, _(b'show topological heads only')),
3707 (b't', b'topo', False, _(b'show topological heads only')),
3708 (
3708 (
3709 b'a',
3709 b'a',
3710 b'active',
3710 b'active',
3711 False,
3711 False,
3712 _(b'show active branchheads only (DEPRECATED)'),
3712 _(b'show active branchheads only (DEPRECATED)'),
3713 ),
3713 ),
3714 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3714 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3715 ]
3715 ]
3716 + templateopts,
3716 + templateopts,
3717 _(b'[-ct] [-r STARTREV] [REV]...'),
3717 _(b'[-ct] [-r STARTREV] [REV]...'),
3718 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3718 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3719 intents={INTENT_READONLY},
3719 intents={INTENT_READONLY},
3720 )
3720 )
3721 def heads(ui, repo, *branchrevs, **opts):
3721 def heads(ui, repo, *branchrevs, **opts):
3722 """show branch heads
3722 """show branch heads
3723
3723
3724 With no arguments, show all open branch heads in the repository.
3724 With no arguments, show all open branch heads in the repository.
3725 Branch heads are changesets that have no descendants on the
3725 Branch heads are changesets that have no descendants on the
3726 same branch. They are where development generally takes place and
3726 same branch. They are where development generally takes place and
3727 are the usual targets for update and merge operations.
3727 are the usual targets for update and merge operations.
3728
3728
3729 If one or more REVs are given, only open branch heads on the
3729 If one or more REVs are given, only open branch heads on the
3730 branches associated with the specified changesets are shown. This
3730 branches associated with the specified changesets are shown. This
3731 means that you can use :hg:`heads .` to see the heads on the
3731 means that you can use :hg:`heads .` to see the heads on the
3732 currently checked-out branch.
3732 currently checked-out branch.
3733
3733
3734 If -c/--closed is specified, also show branch heads marked closed
3734 If -c/--closed is specified, also show branch heads marked closed
3735 (see :hg:`commit --close-branch`).
3735 (see :hg:`commit --close-branch`).
3736
3736
3737 If STARTREV is specified, only those heads that are descendants of
3737 If STARTREV is specified, only those heads that are descendants of
3738 STARTREV will be displayed.
3738 STARTREV will be displayed.
3739
3739
3740 If -t/--topo is specified, named branch mechanics will be ignored and only
3740 If -t/--topo is specified, named branch mechanics will be ignored and only
3741 topological heads (changesets with no children) will be shown.
3741 topological heads (changesets with no children) will be shown.
3742
3742
3743 Returns 0 if matching heads are found, 1 if not.
3743 Returns 0 if matching heads are found, 1 if not.
3744 """
3744 """
3745
3745
3746 start = None
3746 start = None
3747 rev = opts.get('rev')
3747 rev = opts.get('rev')
3748 if rev:
3748 if rev:
3749 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3749 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3750 start = logcmdutil.revsingle(repo, rev, None).node()
3750 start = logcmdutil.revsingle(repo, rev, None).node()
3751
3751
3752 if opts.get('topo'):
3752 if opts.get('topo'):
3753 heads = [repo[h] for h in repo.heads(start)]
3753 heads = [repo[h] for h in repo.heads(start)]
3754 else:
3754 else:
3755 heads = []
3755 heads = []
3756 for branch in repo.branchmap():
3756 for branch in repo.branchmap():
3757 heads += repo.branchheads(branch, start, opts.get('closed'))
3757 heads += repo.branchheads(branch, start, opts.get('closed'))
3758 heads = [repo[h] for h in heads]
3758 heads = [repo[h] for h in heads]
3759
3759
3760 if branchrevs:
3760 if branchrevs:
3761 branches = {
3761 branches = {
3762 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3762 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3763 }
3763 }
3764 heads = [h for h in heads if h.branch() in branches]
3764 heads = [h for h in heads if h.branch() in branches]
3765
3765
3766 if opts.get('active') and branchrevs:
3766 if opts.get('active') and branchrevs:
3767 dagheads = repo.heads(start)
3767 dagheads = repo.heads(start)
3768 heads = [h for h in heads if h.node() in dagheads]
3768 heads = [h for h in heads if h.node() in dagheads]
3769
3769
3770 if branchrevs:
3770 if branchrevs:
3771 haveheads = {h.branch() for h in heads}
3771 haveheads = {h.branch() for h in heads}
3772 if branches - haveheads:
3772 if branches - haveheads:
3773 headless = b', '.join(b for b in branches - haveheads)
3773 headless = b', '.join(b for b in branches - haveheads)
3774 msg = _(b'no open branch heads found on branches %s')
3774 msg = _(b'no open branch heads found on branches %s')
3775 if opts.get('rev'):
3775 if opts.get('rev'):
3776 msg += _(b' (started at %s)') % opts['rev']
3776 msg += _(b' (started at %s)') % opts['rev']
3777 ui.warn((msg + b'\n') % headless)
3777 ui.warn((msg + b'\n') % headless)
3778
3778
3779 if not heads:
3779 if not heads:
3780 return 1
3780 return 1
3781
3781
3782 ui.pager(b'heads')
3782 ui.pager(b'heads')
3783 heads = sorted(heads, key=lambda x: -(x.rev()))
3783 heads = sorted(heads, key=lambda x: -(x.rev()))
3784 displayer = logcmdutil.changesetdisplayer(
3784 displayer = logcmdutil.changesetdisplayer(
3785 ui, repo, pycompat.byteskwargs(opts)
3785 ui, repo, pycompat.byteskwargs(opts)
3786 )
3786 )
3787 for ctx in heads:
3787 for ctx in heads:
3788 displayer.show(ctx)
3788 displayer.show(ctx)
3789 displayer.close()
3789 displayer.close()
3790
3790
3791
3791
3792 @command(
3792 @command(
3793 b'help',
3793 b'help',
3794 [
3794 [
3795 (b'e', b'extension', None, _(b'show only help for extensions')),
3795 (b'e', b'extension', None, _(b'show only help for extensions')),
3796 (b'c', b'command', None, _(b'show only help for commands')),
3796 (b'c', b'command', None, _(b'show only help for commands')),
3797 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3797 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3798 (
3798 (
3799 b's',
3799 b's',
3800 b'system',
3800 b'system',
3801 [],
3801 [],
3802 _(b'show help for specific platform(s)'),
3802 _(b'show help for specific platform(s)'),
3803 _(b'PLATFORM'),
3803 _(b'PLATFORM'),
3804 ),
3804 ),
3805 ],
3805 ],
3806 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3806 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3807 helpcategory=command.CATEGORY_HELP,
3807 helpcategory=command.CATEGORY_HELP,
3808 norepo=True,
3808 norepo=True,
3809 intents={INTENT_READONLY},
3809 intents={INTENT_READONLY},
3810 )
3810 )
3811 def help_(ui, name=None, **opts):
3811 def help_(ui, name=None, **opts):
3812 """show help for a given topic or a help overview
3812 """show help for a given topic or a help overview
3813
3813
3814 With no arguments, print a list of commands with short help messages.
3814 With no arguments, print a list of commands with short help messages.
3815
3815
3816 Given a topic, extension, or command name, print help for that
3816 Given a topic, extension, or command name, print help for that
3817 topic.
3817 topic.
3818
3818
3819 Returns 0 if successful.
3819 Returns 0 if successful.
3820 """
3820 """
3821
3821
3822 keep = opts.get('system') or []
3822 keep = opts.get('system') or []
3823 if len(keep) == 0:
3823 if len(keep) == 0:
3824 if pycompat.sysplatform.startswith(b'win'):
3824 if pycompat.sysplatform.startswith(b'win'):
3825 keep.append(b'windows')
3825 keep.append(b'windows')
3826 elif pycompat.sysplatform == b'OpenVMS':
3826 elif pycompat.sysplatform == b'OpenVMS':
3827 keep.append(b'vms')
3827 keep.append(b'vms')
3828 elif pycompat.sysplatform == b'plan9':
3828 elif pycompat.sysplatform == b'plan9':
3829 keep.append(b'plan9')
3829 keep.append(b'plan9')
3830 else:
3830 else:
3831 keep.append(b'unix')
3831 keep.append(b'unix')
3832 keep.append(pycompat.sysplatform.lower())
3832 keep.append(pycompat.sysplatform.lower())
3833 if ui.verbose:
3833 if ui.verbose:
3834 keep.append(b'verbose')
3834 keep.append(b'verbose')
3835
3835
3836 commands = sys.modules[__name__]
3836 commands = sys.modules[__name__]
3837 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3837 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3838 ui.pager(b'help')
3838 ui.pager(b'help')
3839 ui.write(formatted)
3839 ui.write(formatted)
3840
3840
3841
3841
3842 @command(
3842 @command(
3843 b'identify|id',
3843 b'identify|id',
3844 [
3844 [
3845 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3845 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3846 (b'n', b'num', None, _(b'show local revision number')),
3846 (b'n', b'num', None, _(b'show local revision number')),
3847 (b'i', b'id', None, _(b'show global revision id')),
3847 (b'i', b'id', None, _(b'show global revision id')),
3848 (b'b', b'branch', None, _(b'show branch')),
3848 (b'b', b'branch', None, _(b'show branch')),
3849 (b't', b'tags', None, _(b'show tags')),
3849 (b't', b'tags', None, _(b'show tags')),
3850 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3850 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3851 ]
3851 ]
3852 + remoteopts
3852 + remoteopts
3853 + formatteropts,
3853 + formatteropts,
3854 _(b'[-nibtB] [-r REV] [SOURCE]'),
3854 _(b'[-nibtB] [-r REV] [SOURCE]'),
3855 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3855 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3856 optionalrepo=True,
3856 optionalrepo=True,
3857 intents={INTENT_READONLY},
3857 intents={INTENT_READONLY},
3858 )
3858 )
3859 def identify(
3859 def identify(
3860 ui,
3860 ui,
3861 repo,
3861 repo,
3862 source=None,
3862 source=None,
3863 rev=None,
3863 rev=None,
3864 num=None,
3864 num=None,
3865 id=None,
3865 id=None,
3866 branch=None,
3866 branch=None,
3867 tags=None,
3867 tags=None,
3868 bookmarks=None,
3868 bookmarks=None,
3869 **opts
3869 **opts
3870 ):
3870 ):
3871 """identify the working directory or specified revision
3871 """identify the working directory or specified revision
3872
3872
3873 Print a summary identifying the repository state at REV using one or
3873 Print a summary identifying the repository state at REV using one or
3874 two parent hash identifiers, followed by a "+" if the working
3874 two parent hash identifiers, followed by a "+" if the working
3875 directory has uncommitted changes, the branch name (if not default),
3875 directory has uncommitted changes, the branch name (if not default),
3876 a list of tags, and a list of bookmarks.
3876 a list of tags, and a list of bookmarks.
3877
3877
3878 When REV is not given, print a summary of the current state of the
3878 When REV is not given, print a summary of the current state of the
3879 repository including the working directory. Specify -r. to get information
3879 repository including the working directory. Specify -r. to get information
3880 of the working directory parent without scanning uncommitted changes.
3880 of the working directory parent without scanning uncommitted changes.
3881
3881
3882 Specifying a path to a repository root or Mercurial bundle will
3882 Specifying a path to a repository root or Mercurial bundle will
3883 cause lookup to operate on that repository/bundle.
3883 cause lookup to operate on that repository/bundle.
3884
3884
3885 .. container:: verbose
3885 .. container:: verbose
3886
3886
3887 Template:
3887 Template:
3888
3888
3889 The following keywords are supported in addition to the common template
3889 The following keywords are supported in addition to the common template
3890 keywords and functions. See also :hg:`help templates`.
3890 keywords and functions. See also :hg:`help templates`.
3891
3891
3892 :dirty: String. Character ``+`` denoting if the working directory has
3892 :dirty: String. Character ``+`` denoting if the working directory has
3893 uncommitted changes.
3893 uncommitted changes.
3894 :id: String. One or two nodes, optionally followed by ``+``.
3894 :id: String. One or two nodes, optionally followed by ``+``.
3895 :parents: List of strings. Parent nodes of the changeset.
3895 :parents: List of strings. Parent nodes of the changeset.
3896
3896
3897 Examples:
3897 Examples:
3898
3898
3899 - generate a build identifier for the working directory::
3899 - generate a build identifier for the working directory::
3900
3900
3901 hg id --id > build-id.dat
3901 hg id --id > build-id.dat
3902
3902
3903 - find the revision corresponding to a tag::
3903 - find the revision corresponding to a tag::
3904
3904
3905 hg id -n -r 1.3
3905 hg id -n -r 1.3
3906
3906
3907 - check the most recent revision of a remote repository::
3907 - check the most recent revision of a remote repository::
3908
3908
3909 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3909 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3910
3910
3911 See :hg:`log` for generating more information about specific revisions,
3911 See :hg:`log` for generating more information about specific revisions,
3912 including full hash identifiers.
3912 including full hash identifiers.
3913
3913
3914 Returns 0 if successful.
3914 Returns 0 if successful.
3915 """
3915 """
3916
3916
3917 opts = pycompat.byteskwargs(opts)
3917 opts = pycompat.byteskwargs(opts)
3918 if not repo and not source:
3918 if not repo and not source:
3919 raise error.InputError(
3919 raise error.InputError(
3920 _(b"there is no Mercurial repository here (.hg not found)")
3920 _(b"there is no Mercurial repository here (.hg not found)")
3921 )
3921 )
3922
3922
3923 default = not (num or id or branch or tags or bookmarks)
3923 default = not (num or id or branch or tags or bookmarks)
3924 output = []
3924 output = []
3925 revs = []
3925 revs = []
3926
3926
3927 peer = None
3927 peer = None
3928 try:
3928 try:
3929 if source:
3929 if source:
3930 path = urlutil.get_unique_pull_path_obj(b'identify', ui, source)
3930 path = urlutil.get_unique_pull_path_obj(b'identify', ui, source)
3931 # only pass ui when no repo
3931 # only pass ui when no repo
3932 peer = hg.peer(repo or ui, opts, path)
3932 peer = hg.peer(repo or ui, opts, path)
3933 repo = peer.local()
3933 repo = peer.local()
3934 branches = (path.branch, [])
3934 branches = (path.branch, [])
3935 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3935 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3936
3936
3937 fm = ui.formatter(b'identify', opts)
3937 fm = ui.formatter(b'identify', opts)
3938 fm.startitem()
3938 fm.startitem()
3939
3939
3940 if not repo:
3940 if not repo:
3941 if num or branch or tags:
3941 if num or branch or tags:
3942 raise error.InputError(
3942 raise error.InputError(
3943 _(b"can't query remote revision number, branch, or tags")
3943 _(b"can't query remote revision number, branch, or tags")
3944 )
3944 )
3945 if not rev and revs:
3945 if not rev and revs:
3946 rev = revs[0]
3946 rev = revs[0]
3947 if not rev:
3947 if not rev:
3948 rev = b"tip"
3948 rev = b"tip"
3949
3949
3950 remoterev = peer.lookup(rev)
3950 remoterev = peer.lookup(rev)
3951 hexrev = fm.hexfunc(remoterev)
3951 hexrev = fm.hexfunc(remoterev)
3952 if default or id:
3952 if default or id:
3953 output = [hexrev]
3953 output = [hexrev]
3954 fm.data(id=hexrev)
3954 fm.data(id=hexrev)
3955
3955
3956 @util.cachefunc
3956 @util.cachefunc
3957 def getbms():
3957 def getbms():
3958 bms = []
3958 bms = []
3959
3959
3960 if b'bookmarks' in peer.listkeys(b'namespaces'):
3960 if b'bookmarks' in peer.listkeys(b'namespaces'):
3961 hexremoterev = hex(remoterev)
3961 hexremoterev = hex(remoterev)
3962 bms = [
3962 bms = [
3963 bm
3963 bm
3964 for bm, bmr in peer.listkeys(b'bookmarks').items()
3964 for bm, bmr in peer.listkeys(b'bookmarks').items()
3965 if bmr == hexremoterev
3965 if bmr == hexremoterev
3966 ]
3966 ]
3967
3967
3968 return sorted(bms)
3968 return sorted(bms)
3969
3969
3970 if fm.isplain():
3970 if fm.isplain():
3971 if bookmarks:
3971 if bookmarks:
3972 output.extend(getbms())
3972 output.extend(getbms())
3973 elif default and not ui.quiet:
3973 elif default and not ui.quiet:
3974 # multiple bookmarks for a single parent separated by '/'
3974 # multiple bookmarks for a single parent separated by '/'
3975 bm = b'/'.join(getbms())
3975 bm = b'/'.join(getbms())
3976 if bm:
3976 if bm:
3977 output.append(bm)
3977 output.append(bm)
3978 else:
3978 else:
3979 fm.data(node=hex(remoterev))
3979 fm.data(node=hex(remoterev))
3980 if bookmarks or b'bookmarks' in fm.datahint():
3980 if bookmarks or b'bookmarks' in fm.datahint():
3981 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3981 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3982 else:
3982 else:
3983 if rev:
3983 if rev:
3984 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3984 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3985 ctx = logcmdutil.revsingle(repo, rev, None)
3985 ctx = logcmdutil.revsingle(repo, rev, None)
3986
3986
3987 if ctx.rev() is None:
3987 if ctx.rev() is None:
3988 ctx = repo[None]
3988 ctx = repo[None]
3989 parents = ctx.parents()
3989 parents = ctx.parents()
3990 taglist = []
3990 taglist = []
3991 for p in parents:
3991 for p in parents:
3992 taglist.extend(p.tags())
3992 taglist.extend(p.tags())
3993
3993
3994 dirty = b""
3994 dirty = b""
3995 if ctx.dirty(missing=True, merge=False, branch=False):
3995 if ctx.dirty(missing=True, merge=False, branch=False):
3996 dirty = b'+'
3996 dirty = b'+'
3997 fm.data(dirty=dirty)
3997 fm.data(dirty=dirty)
3998
3998
3999 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3999 hexoutput = [fm.hexfunc(p.node()) for p in parents]
4000 if default or id:
4000 if default or id:
4001 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
4001 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
4002 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
4002 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
4003
4003
4004 if num:
4004 if num:
4005 numoutput = [b"%d" % p.rev() for p in parents]
4005 numoutput = [b"%d" % p.rev() for p in parents]
4006 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
4006 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
4007
4007
4008 fm.data(
4008 fm.data(
4009 parents=fm.formatlist(
4009 parents=fm.formatlist(
4010 [fm.hexfunc(p.node()) for p in parents], name=b'node'
4010 [fm.hexfunc(p.node()) for p in parents], name=b'node'
4011 )
4011 )
4012 )
4012 )
4013 else:
4013 else:
4014 hexoutput = fm.hexfunc(ctx.node())
4014 hexoutput = fm.hexfunc(ctx.node())
4015 if default or id:
4015 if default or id:
4016 output = [hexoutput]
4016 output = [hexoutput]
4017 fm.data(id=hexoutput)
4017 fm.data(id=hexoutput)
4018
4018
4019 if num:
4019 if num:
4020 output.append(pycompat.bytestr(ctx.rev()))
4020 output.append(pycompat.bytestr(ctx.rev()))
4021 taglist = ctx.tags()
4021 taglist = ctx.tags()
4022
4022
4023 if default and not ui.quiet:
4023 if default and not ui.quiet:
4024 b = ctx.branch()
4024 b = ctx.branch()
4025 if b != b'default':
4025 if b != b'default':
4026 output.append(b"(%s)" % b)
4026 output.append(b"(%s)" % b)
4027
4027
4028 # multiple tags for a single parent separated by '/'
4028 # multiple tags for a single parent separated by '/'
4029 t = b'/'.join(taglist)
4029 t = b'/'.join(taglist)
4030 if t:
4030 if t:
4031 output.append(t)
4031 output.append(t)
4032
4032
4033 # multiple bookmarks for a single parent separated by '/'
4033 # multiple bookmarks for a single parent separated by '/'
4034 bm = b'/'.join(ctx.bookmarks())
4034 bm = b'/'.join(ctx.bookmarks())
4035 if bm:
4035 if bm:
4036 output.append(bm)
4036 output.append(bm)
4037 else:
4037 else:
4038 if branch:
4038 if branch:
4039 output.append(ctx.branch())
4039 output.append(ctx.branch())
4040
4040
4041 if tags:
4041 if tags:
4042 output.extend(taglist)
4042 output.extend(taglist)
4043
4043
4044 if bookmarks:
4044 if bookmarks:
4045 output.extend(ctx.bookmarks())
4045 output.extend(ctx.bookmarks())
4046
4046
4047 fm.data(node=ctx.hex())
4047 fm.data(node=ctx.hex())
4048 fm.data(branch=ctx.branch())
4048 fm.data(branch=ctx.branch())
4049 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4049 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4050 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4050 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4051 fm.context(ctx=ctx)
4051 fm.context(ctx=ctx)
4052
4052
4053 fm.plain(b"%s\n" % b' '.join(output))
4053 fm.plain(b"%s\n" % b' '.join(output))
4054 fm.end()
4054 fm.end()
4055 finally:
4055 finally:
4056 if peer:
4056 if peer:
4057 peer.close()
4057 peer.close()
4058
4058
4059
4059
4060 @command(
4060 @command(
4061 b'import|patch',
4061 b'import|patch',
4062 [
4062 [
4063 (
4063 (
4064 b'p',
4064 b'p',
4065 b'strip',
4065 b'strip',
4066 1,
4066 1,
4067 _(
4067 _(
4068 b'directory strip option for patch. This has the same '
4068 b'directory strip option for patch. This has the same '
4069 b'meaning as the corresponding patch option'
4069 b'meaning as the corresponding patch option'
4070 ),
4070 ),
4071 _(b'NUM'),
4071 _(b'NUM'),
4072 ),
4072 ),
4073 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4073 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4074 (b'', b'secret', None, _(b'use the secret phase for committing')),
4074 (b'', b'secret', None, _(b'use the secret phase for committing')),
4075 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4075 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4076 (
4076 (
4077 b'f',
4077 b'f',
4078 b'force',
4078 b'force',
4079 None,
4079 None,
4080 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4080 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4081 ),
4081 ),
4082 (
4082 (
4083 b'',
4083 b'',
4084 b'no-commit',
4084 b'no-commit',
4085 None,
4085 None,
4086 _(b"don't commit, just update the working directory"),
4086 _(b"don't commit, just update the working directory"),
4087 ),
4087 ),
4088 (
4088 (
4089 b'',
4089 b'',
4090 b'bypass',
4090 b'bypass',
4091 None,
4091 None,
4092 _(b"apply patch without touching the working directory"),
4092 _(b"apply patch without touching the working directory"),
4093 ),
4093 ),
4094 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4094 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4095 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4095 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4096 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4096 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4097 (
4097 (
4098 b'',
4098 b'',
4099 b'import-branch',
4099 b'import-branch',
4100 None,
4100 None,
4101 _(b'use any branch information in patch (implied by --exact)'),
4101 _(b'use any branch information in patch (implied by --exact)'),
4102 ),
4102 ),
4103 ]
4103 ]
4104 + commitopts
4104 + commitopts
4105 + commitopts2
4105 + commitopts2
4106 + similarityopts,
4106 + similarityopts,
4107 _(b'[OPTION]... PATCH...'),
4107 _(b'[OPTION]... PATCH...'),
4108 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4108 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4109 )
4109 )
4110 def import_(ui, repo, patch1=None, *patches, **opts):
4110 def import_(ui, repo, patch1=None, *patches, **opts):
4111 """import an ordered set of patches
4111 """import an ordered set of patches
4112
4112
4113 Import a list of patches and commit them individually (unless
4113 Import a list of patches and commit them individually (unless
4114 --no-commit is specified).
4114 --no-commit is specified).
4115
4115
4116 To read a patch from standard input (stdin), use "-" as the patch
4116 To read a patch from standard input (stdin), use "-" as the patch
4117 name. If a URL is specified, the patch will be downloaded from
4117 name. If a URL is specified, the patch will be downloaded from
4118 there.
4118 there.
4119
4119
4120 Import first applies changes to the working directory (unless
4120 Import first applies changes to the working directory (unless
4121 --bypass is specified), import will abort if there are outstanding
4121 --bypass is specified), import will abort if there are outstanding
4122 changes.
4122 changes.
4123
4123
4124 Use --bypass to apply and commit patches directly to the
4124 Use --bypass to apply and commit patches directly to the
4125 repository, without affecting the working directory. Without
4125 repository, without affecting the working directory. Without
4126 --exact, patches will be applied on top of the working directory
4126 --exact, patches will be applied on top of the working directory
4127 parent revision.
4127 parent revision.
4128
4128
4129 You can import a patch straight from a mail message. Even patches
4129 You can import a patch straight from a mail message. Even patches
4130 as attachments work (to use the body part, it must have type
4130 as attachments work (to use the body part, it must have type
4131 text/plain or text/x-patch). From and Subject headers of email
4131 text/plain or text/x-patch). From and Subject headers of email
4132 message are used as default committer and commit message. All
4132 message are used as default committer and commit message. All
4133 text/plain body parts before first diff are added to the commit
4133 text/plain body parts before first diff are added to the commit
4134 message.
4134 message.
4135
4135
4136 If the imported patch was generated by :hg:`export`, user and
4136 If the imported patch was generated by :hg:`export`, user and
4137 description from patch override values from message headers and
4137 description from patch override values from message headers and
4138 body. Values given on command line with -m/--message and -u/--user
4138 body. Values given on command line with -m/--message and -u/--user
4139 override these.
4139 override these.
4140
4140
4141 If --exact is specified, import will set the working directory to
4141 If --exact is specified, import will set the working directory to
4142 the parent of each patch before applying it, and will abort if the
4142 the parent of each patch before applying it, and will abort if the
4143 resulting changeset has a different ID than the one recorded in
4143 resulting changeset has a different ID than the one recorded in
4144 the patch. This will guard against various ways that portable
4144 the patch. This will guard against various ways that portable
4145 patch formats and mail systems might fail to transfer Mercurial
4145 patch formats and mail systems might fail to transfer Mercurial
4146 data or metadata. See :hg:`bundle` for lossless transmission.
4146 data or metadata. See :hg:`bundle` for lossless transmission.
4147
4147
4148 Use --partial to ensure a changeset will be created from the patch
4148 Use --partial to ensure a changeset will be created from the patch
4149 even if some hunks fail to apply. Hunks that fail to apply will be
4149 even if some hunks fail to apply. Hunks that fail to apply will be
4150 written to a <target-file>.rej file. Conflicts can then be resolved
4150 written to a <target-file>.rej file. Conflicts can then be resolved
4151 by hand before :hg:`commit --amend` is run to update the created
4151 by hand before :hg:`commit --amend` is run to update the created
4152 changeset. This flag exists to let people import patches that
4152 changeset. This flag exists to let people import patches that
4153 partially apply without losing the associated metadata (author,
4153 partially apply without losing the associated metadata (author,
4154 date, description, ...).
4154 date, description, ...).
4155
4155
4156 .. note::
4156 .. note::
4157
4157
4158 When no hunks apply cleanly, :hg:`import --partial` will create
4158 When no hunks apply cleanly, :hg:`import --partial` will create
4159 an empty changeset, importing only the patch metadata.
4159 an empty changeset, importing only the patch metadata.
4160
4160
4161 With -s/--similarity, hg will attempt to discover renames and
4161 With -s/--similarity, hg will attempt to discover renames and
4162 copies in the patch in the same way as :hg:`addremove`.
4162 copies in the patch in the same way as :hg:`addremove`.
4163
4163
4164 It is possible to use external patch programs to perform the patch
4164 It is possible to use external patch programs to perform the patch
4165 by setting the ``ui.patch`` configuration option. For the default
4165 by setting the ``ui.patch`` configuration option. For the default
4166 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4166 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4167 See :hg:`help config` for more information about configuration
4167 See :hg:`help config` for more information about configuration
4168 files and how to use these options.
4168 files and how to use these options.
4169
4169
4170 See :hg:`help dates` for a list of formats valid for -d/--date.
4170 See :hg:`help dates` for a list of formats valid for -d/--date.
4171
4171
4172 .. container:: verbose
4172 .. container:: verbose
4173
4173
4174 Examples:
4174 Examples:
4175
4175
4176 - import a traditional patch from a website and detect renames::
4176 - import a traditional patch from a website and detect renames::
4177
4177
4178 hg import -s 80 http://example.com/bugfix.patch
4178 hg import -s 80 http://example.com/bugfix.patch
4179
4179
4180 - import a changeset from an hgweb server::
4180 - import a changeset from an hgweb server::
4181
4181
4182 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4182 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4183
4183
4184 - import all the patches in an Unix-style mbox::
4184 - import all the patches in an Unix-style mbox::
4185
4185
4186 hg import incoming-patches.mbox
4186 hg import incoming-patches.mbox
4187
4187
4188 - import patches from stdin::
4188 - import patches from stdin::
4189
4189
4190 hg import -
4190 hg import -
4191
4191
4192 - attempt to exactly restore an exported changeset (not always
4192 - attempt to exactly restore an exported changeset (not always
4193 possible)::
4193 possible)::
4194
4194
4195 hg import --exact proposed-fix.patch
4195 hg import --exact proposed-fix.patch
4196
4196
4197 - use an external tool to apply a patch which is too fuzzy for
4197 - use an external tool to apply a patch which is too fuzzy for
4198 the default internal tool.
4198 the default internal tool.
4199
4199
4200 hg import --config ui.patch="patch --merge" fuzzy.patch
4200 hg import --config ui.patch="patch --merge" fuzzy.patch
4201
4201
4202 - change the default fuzzing from 2 to a less strict 7
4202 - change the default fuzzing from 2 to a less strict 7
4203
4203
4204 hg import --config ui.fuzz=7 fuzz.patch
4204 hg import --config ui.fuzz=7 fuzz.patch
4205
4205
4206 Returns 0 on success, 1 on partial success (see --partial).
4206 Returns 0 on success, 1 on partial success (see --partial).
4207 """
4207 """
4208
4208
4209 cmdutil.check_incompatible_arguments(
4209 cmdutil.check_incompatible_arguments(
4210 opts, 'no_commit', ['bypass', 'secret']
4210 opts, 'no_commit', ['bypass', 'secret']
4211 )
4211 )
4212 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4212 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4213
4213
4214 if not patch1:
4214 if not patch1:
4215 raise error.InputError(_(b'need at least one patch to import'))
4215 raise error.InputError(_(b'need at least one patch to import'))
4216
4216
4217 patches = (patch1,) + patches
4217 patches = (patch1,) + patches
4218
4218
4219 date = opts.get('date')
4219 date = opts.get('date')
4220 if date:
4220 if date:
4221 opts['date'] = dateutil.parsedate(date)
4221 opts['date'] = dateutil.parsedate(date)
4222
4222
4223 exact = opts.get('exact')
4223 exact = opts.get('exact')
4224 update = not opts.get('bypass')
4224 update = not opts.get('bypass')
4225 try:
4225 try:
4226 sim = float(opts.get('similarity') or 0)
4226 sim = float(opts.get('similarity') or 0)
4227 except ValueError:
4227 except ValueError:
4228 raise error.InputError(_(b'similarity must be a number'))
4228 raise error.InputError(_(b'similarity must be a number'))
4229 if sim < 0 or sim > 100:
4229 if sim < 0 or sim > 100:
4230 raise error.InputError(_(b'similarity must be between 0 and 100'))
4230 raise error.InputError(_(b'similarity must be between 0 and 100'))
4231 if sim and not update:
4231 if sim and not update:
4232 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4232 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4233
4233
4234 base = opts["base"]
4234 base = opts["base"]
4235 msgs = []
4235 msgs = []
4236 ret = 0
4236 ret = 0
4237
4237
4238 with repo.wlock():
4238 with repo.wlock():
4239 if update:
4239 if update:
4240 cmdutil.checkunfinished(repo)
4240 cmdutil.checkunfinished(repo)
4241 if exact or not opts.get('force'):
4241 if exact or not opts.get('force'):
4242 cmdutil.bailifchanged(repo)
4242 cmdutil.bailifchanged(repo)
4243
4243
4244 if not opts.get('no_commit'):
4244 if not opts.get('no_commit'):
4245 lock = repo.lock
4245 lock = repo.lock
4246 tr = lambda: repo.transaction(b'import')
4246 tr = lambda: repo.transaction(b'import')
4247 else:
4247 else:
4248 lock = util.nullcontextmanager
4248 lock = util.nullcontextmanager
4249 tr = util.nullcontextmanager
4249 tr = util.nullcontextmanager
4250 with lock(), tr():
4250 with lock(), tr():
4251 parents = repo[None].parents()
4251 parents = repo[None].parents()
4252 for patchurl in patches:
4252 for patchurl in patches:
4253 if patchurl == b'-':
4253 if patchurl == b'-':
4254 ui.status(_(b'applying patch from stdin\n'))
4254 ui.status(_(b'applying patch from stdin\n'))
4255 patchfile = ui.fin
4255 patchfile = ui.fin
4256 patchurl = b'stdin' # for error message
4256 patchurl = b'stdin' # for error message
4257 else:
4257 else:
4258 patchurl = os.path.join(base, patchurl)
4258 patchurl = os.path.join(base, patchurl)
4259 ui.status(_(b'applying %s\n') % patchurl)
4259 ui.status(_(b'applying %s\n') % patchurl)
4260 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4260 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4261
4261
4262 haspatch = False
4262 haspatch = False
4263 for hunk in patch.split(patchfile):
4263 for hunk in patch.split(patchfile):
4264 with patch.extract(ui, hunk) as patchdata:
4264 with patch.extract(ui, hunk) as patchdata:
4265 msg, node, rej = cmdutil.tryimportone(
4265 msg, node, rej = cmdutil.tryimportone(
4266 ui,
4266 ui,
4267 repo,
4267 repo,
4268 patchdata,
4268 patchdata,
4269 parents,
4269 parents,
4270 pycompat.byteskwargs(opts),
4270 pycompat.byteskwargs(opts),
4271 msgs,
4271 msgs,
4272 hg.clean,
4272 hg.clean,
4273 )
4273 )
4274 if msg:
4274 if msg:
4275 haspatch = True
4275 haspatch = True
4276 ui.note(msg + b'\n')
4276 ui.note(msg + b'\n')
4277 if update or exact:
4277 if update or exact:
4278 parents = repo[None].parents()
4278 parents = repo[None].parents()
4279 else:
4279 else:
4280 parents = [repo[node]]
4280 parents = [repo[node]]
4281 if rej:
4281 if rej:
4282 ui.write_err(_(b"patch applied partially\n"))
4282 ui.write_err(_(b"patch applied partially\n"))
4283 ui.write_err(
4283 ui.write_err(
4284 _(
4284 _(
4285 b"(fix the .rej files and run "
4285 b"(fix the .rej files and run "
4286 b"`hg commit --amend`)\n"
4286 b"`hg commit --amend`)\n"
4287 )
4287 )
4288 )
4288 )
4289 ret = 1
4289 ret = 1
4290 break
4290 break
4291
4291
4292 if not haspatch:
4292 if not haspatch:
4293 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4293 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4294
4294
4295 if msgs:
4295 if msgs:
4296 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4296 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4297 return ret
4297 return ret
4298
4298
4299
4299
4300 @command(
4300 @command(
4301 b'incoming|in',
4301 b'incoming|in',
4302 [
4302 [
4303 (
4303 (
4304 b'f',
4304 b'f',
4305 b'force',
4305 b'force',
4306 None,
4306 None,
4307 _(b'run even if remote repository is unrelated'),
4307 _(b'run even if remote repository is unrelated'),
4308 ),
4308 ),
4309 (b'n', b'newest-first', None, _(b'show newest record first')),
4309 (b'n', b'newest-first', None, _(b'show newest record first')),
4310 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4310 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4311 (
4311 (
4312 b'r',
4312 b'r',
4313 b'rev',
4313 b'rev',
4314 [],
4314 [],
4315 _(b'a remote changeset intended to be added'),
4315 _(b'a remote changeset intended to be added'),
4316 _(b'REV'),
4316 _(b'REV'),
4317 ),
4317 ),
4318 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4318 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4319 (
4319 (
4320 b'b',
4320 b'b',
4321 b'branch',
4321 b'branch',
4322 [],
4322 [],
4323 _(b'a specific branch you would like to pull'),
4323 _(b'a specific branch you would like to pull'),
4324 _(b'BRANCH'),
4324 _(b'BRANCH'),
4325 ),
4325 ),
4326 ]
4326 ]
4327 + logopts
4327 + logopts
4328 + remoteopts
4328 + remoteopts
4329 + subrepoopts,
4329 + subrepoopts,
4330 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4330 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4331 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4331 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4332 )
4332 )
4333 def incoming(ui, repo, source=b"default", **opts):
4333 def incoming(ui, repo, source=b"default", **opts):
4334 """show new changesets found in source
4334 """show new changesets found in source
4335
4335
4336 Show new changesets found in the specified path/URL or the default
4336 Show new changesets found in the specified path/URL or the default
4337 pull location. These are the changesets that would have been pulled
4337 pull location. These are the changesets that would have been pulled
4338 by :hg:`pull` at the time you issued this command.
4338 by :hg:`pull` at the time you issued this command.
4339
4339
4340 See pull for valid source format details.
4340 See pull for valid source format details.
4341
4341
4342 .. container:: verbose
4342 .. container:: verbose
4343
4343
4344 With -B/--bookmarks, the result of bookmark comparison between
4344 With -B/--bookmarks, the result of bookmark comparison between
4345 local and remote repositories is displayed. With -v/--verbose,
4345 local and remote repositories is displayed. With -v/--verbose,
4346 status is also displayed for each bookmark like below::
4346 status is also displayed for each bookmark like below::
4347
4347
4348 BM1 01234567890a added
4348 BM1 01234567890a added
4349 BM2 1234567890ab advanced
4349 BM2 1234567890ab advanced
4350 BM3 234567890abc diverged
4350 BM3 234567890abc diverged
4351 BM4 34567890abcd changed
4351 BM4 34567890abcd changed
4352
4352
4353 The action taken locally when pulling depends on the
4353 The action taken locally when pulling depends on the
4354 status of each bookmark:
4354 status of each bookmark:
4355
4355
4356 :``added``: pull will create it
4356 :``added``: pull will create it
4357 :``advanced``: pull will update it
4357 :``advanced``: pull will update it
4358 :``diverged``: pull will create a divergent bookmark
4358 :``diverged``: pull will create a divergent bookmark
4359 :``changed``: result depends on remote changesets
4359 :``changed``: result depends on remote changesets
4360
4360
4361 From the point of view of pulling behavior, bookmark
4361 From the point of view of pulling behavior, bookmark
4362 existing only in the remote repository are treated as ``added``,
4362 existing only in the remote repository are treated as ``added``,
4363 even if it is in fact locally deleted.
4363 even if it is in fact locally deleted.
4364
4364
4365 .. container:: verbose
4365 .. container:: verbose
4366
4366
4367 For remote repository, using --bundle avoids downloading the
4367 For remote repository, using --bundle avoids downloading the
4368 changesets twice if the incoming is followed by a pull.
4368 changesets twice if the incoming is followed by a pull.
4369
4369
4370 Examples:
4370 Examples:
4371
4371
4372 - show incoming changes with patches and full description::
4372 - show incoming changes with patches and full description::
4373
4373
4374 hg incoming -vp
4374 hg incoming -vp
4375
4375
4376 - show incoming changes excluding merges, store a bundle::
4376 - show incoming changes excluding merges, store a bundle::
4377
4377
4378 hg in -vpM --bundle incoming.hg
4378 hg in -vpM --bundle incoming.hg
4379 hg pull incoming.hg
4379 hg pull incoming.hg
4380
4380
4381 - briefly list changes inside a bundle::
4381 - briefly list changes inside a bundle::
4382
4382
4383 hg in changes.hg -T "{desc|firstline}\\n"
4383 hg in changes.hg -T "{desc|firstline}\\n"
4384
4384
4385 Returns 0 if there are incoming changes, 1 otherwise.
4385 Returns 0 if there are incoming changes, 1 otherwise.
4386 """
4386 """
4387 opts = pycompat.byteskwargs(opts)
4387 opts = pycompat.byteskwargs(opts)
4388 if opts.get(b'graph'):
4388 if opts.get(b'graph'):
4389 logcmdutil.checkunsupportedgraphflags([], opts)
4389 logcmdutil.checkunsupportedgraphflags([], opts)
4390
4390
4391 def display(other, chlist, displayer):
4391 def display(other, chlist, displayer):
4392 revdag = logcmdutil.graphrevs(other, chlist, opts)
4392 revdag = logcmdutil.graphrevs(other, chlist, opts)
4393 logcmdutil.displaygraph(
4393 logcmdutil.displaygraph(
4394 ui, repo, revdag, displayer, graphmod.asciiedges
4394 ui, repo, revdag, displayer, graphmod.asciiedges
4395 )
4395 )
4396
4396
4397 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4397 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4398 return 0
4398 return 0
4399
4399
4400 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4400 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4401
4401
4402 if opts.get(b'bookmarks'):
4402 if opts.get(b'bookmarks'):
4403 srcs = urlutil.get_pull_paths(repo, ui, [source])
4403 srcs = urlutil.get_pull_paths(repo, ui, [source])
4404 for path in srcs:
4404 for path in srcs:
4405 # XXX the "branches" options are not used. Should it be used?
4405 # XXX the "branches" options are not used. Should it be used?
4406 other = hg.peer(repo, opts, path)
4406 other = hg.peer(repo, opts, path)
4407 try:
4407 try:
4408 if b'bookmarks' not in other.listkeys(b'namespaces'):
4408 if b'bookmarks' not in other.listkeys(b'namespaces'):
4409 ui.warn(_(b"remote doesn't support bookmarks\n"))
4409 ui.warn(_(b"remote doesn't support bookmarks\n"))
4410 return 0
4410 return 0
4411 ui.pager(b'incoming')
4411 ui.pager(b'incoming')
4412 ui.status(
4412 ui.status(
4413 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4413 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4414 )
4414 )
4415 return bookmarks.incoming(
4415 return bookmarks.incoming(
4416 ui, repo, other, mode=path.bookmarks_mode
4416 ui, repo, other, mode=path.bookmarks_mode
4417 )
4417 )
4418 finally:
4418 finally:
4419 other.close()
4419 other.close()
4420
4420
4421 return hg.incoming(ui, repo, source, opts)
4421 return hg.incoming(ui, repo, source, opts)
4422
4422
4423
4423
4424 @command(
4424 @command(
4425 b'init',
4425 b'init',
4426 remoteopts,
4426 remoteopts,
4427 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4427 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4428 helpcategory=command.CATEGORY_REPO_CREATION,
4428 helpcategory=command.CATEGORY_REPO_CREATION,
4429 helpbasic=True,
4429 helpbasic=True,
4430 norepo=True,
4430 norepo=True,
4431 )
4431 )
4432 def init(ui, dest=b".", **opts):
4432 def init(ui, dest=b".", **opts):
4433 """create a new repository in the given directory
4433 """create a new repository in the given directory
4434
4434
4435 Initialize a new repository in the given directory. If the given
4435 Initialize a new repository in the given directory. If the given
4436 directory does not exist, it will be created.
4436 directory does not exist, it will be created.
4437
4437
4438 If no directory is given, the current directory is used.
4438 If no directory is given, the current directory is used.
4439
4439
4440 It is possible to specify an ``ssh://`` URL as the destination.
4440 It is possible to specify an ``ssh://`` URL as the destination.
4441 See :hg:`help urls` for more information.
4441 See :hg:`help urls` for more information.
4442
4442
4443 Returns 0 on success.
4443 Returns 0 on success.
4444 """
4444 """
4445 opts = pycompat.byteskwargs(opts)
4445 opts = pycompat.byteskwargs(opts)
4446 path = urlutil.get_clone_path_obj(ui, dest)
4446 path = urlutil.get_clone_path_obj(ui, dest)
4447 peer = hg.peer(ui, opts, path, create=True)
4447 peer = hg.peer(ui, opts, path, create=True)
4448 peer.close()
4448 peer.close()
4449
4449
4450
4450
4451 @command(
4451 @command(
4452 b'locate',
4452 b'locate',
4453 [
4453 [
4454 (
4454 (
4455 b'r',
4455 b'r',
4456 b'rev',
4456 b'rev',
4457 b'',
4457 b'',
4458 _(b'search the repository as it is in REV'),
4458 _(b'search the repository as it is in REV'),
4459 _(b'REV'),
4459 _(b'REV'),
4460 ),
4460 ),
4461 (
4461 (
4462 b'0',
4462 b'0',
4463 b'print0',
4463 b'print0',
4464 None,
4464 None,
4465 _(b'end filenames with NUL, for use with xargs'),
4465 _(b'end filenames with NUL, for use with xargs'),
4466 ),
4466 ),
4467 (
4467 (
4468 b'f',
4468 b'f',
4469 b'fullpath',
4469 b'fullpath',
4470 None,
4470 None,
4471 _(b'print complete paths from the filesystem root'),
4471 _(b'print complete paths from the filesystem root'),
4472 ),
4472 ),
4473 ]
4473 ]
4474 + walkopts,
4474 + walkopts,
4475 _(b'[OPTION]... [PATTERN]...'),
4475 _(b'[OPTION]... [PATTERN]...'),
4476 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4476 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4477 )
4477 )
4478 def locate(ui, repo, *pats, **opts):
4478 def locate(ui, repo, *pats, **opts):
4479 """locate files matching specific patterns (DEPRECATED)
4479 """locate files matching specific patterns (DEPRECATED)
4480
4480
4481 Print files under Mercurial control in the working directory whose
4481 Print files under Mercurial control in the working directory whose
4482 names match the given patterns.
4482 names match the given patterns.
4483
4483
4484 By default, this command searches all directories in the working
4484 By default, this command searches all directories in the working
4485 directory. To search just the current directory and its
4485 directory. To search just the current directory and its
4486 subdirectories, use "--include .".
4486 subdirectories, use "--include .".
4487
4487
4488 If no patterns are given to match, this command prints the names
4488 If no patterns are given to match, this command prints the names
4489 of all files under Mercurial control in the working directory.
4489 of all files under Mercurial control in the working directory.
4490
4490
4491 If you want to feed the output of this command into the "xargs"
4491 If you want to feed the output of this command into the "xargs"
4492 command, use the -0 option to both this command and "xargs". This
4492 command, use the -0 option to both this command and "xargs". This
4493 will avoid the problem of "xargs" treating single filenames that
4493 will avoid the problem of "xargs" treating single filenames that
4494 contain whitespace as multiple filenames.
4494 contain whitespace as multiple filenames.
4495
4495
4496 See :hg:`help files` for a more versatile command.
4496 See :hg:`help files` for a more versatile command.
4497
4497
4498 Returns 0 if a match is found, 1 otherwise.
4498 Returns 0 if a match is found, 1 otherwise.
4499 """
4499 """
4500 if opts.get('print0'):
4500 if opts.get('print0'):
4501 end = b'\0'
4501 end = b'\0'
4502 else:
4502 else:
4503 end = b'\n'
4503 end = b'\n'
4504 ctx = logcmdutil.revsingle(repo, opts.get('rev'), None)
4504 ctx = logcmdutil.revsingle(repo, opts.get('rev'), None)
4505
4505
4506 ret = 1
4506 ret = 1
4507 m = scmutil.match(
4507 m = scmutil.match(
4508 ctx,
4508 ctx,
4509 pats,
4509 pats,
4510 pycompat.byteskwargs(opts),
4510 pycompat.byteskwargs(opts),
4511 default=b'relglob',
4511 default=b'relglob',
4512 badfn=lambda x, y: False,
4512 badfn=lambda x, y: False,
4513 )
4513 )
4514
4514
4515 ui.pager(b'locate')
4515 ui.pager(b'locate')
4516 if ctx.rev() is None:
4516 if ctx.rev() is None:
4517 # When run on the working copy, "locate" includes removed files, so
4517 # When run on the working copy, "locate" includes removed files, so
4518 # we get the list of files from the dirstate.
4518 # we get the list of files from the dirstate.
4519 filesgen = sorted(repo.dirstate.matches(m))
4519 filesgen = sorted(repo.dirstate.matches(m))
4520 else:
4520 else:
4521 filesgen = ctx.matches(m)
4521 filesgen = ctx.matches(m)
4522 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4522 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4523 for abs in filesgen:
4523 for abs in filesgen:
4524 if opts.get('fullpath'):
4524 if opts.get('fullpath'):
4525 ui.write(repo.wjoin(abs), end)
4525 ui.write(repo.wjoin(abs), end)
4526 else:
4526 else:
4527 ui.write(uipathfn(abs), end)
4527 ui.write(uipathfn(abs), end)
4528 ret = 0
4528 ret = 0
4529
4529
4530 return ret
4530 return ret
4531
4531
4532
4532
4533 @command(
4533 @command(
4534 b'log|history',
4534 b'log|history',
4535 [
4535 [
4536 (
4536 (
4537 b'f',
4537 b'f',
4538 b'follow',
4538 b'follow',
4539 None,
4539 None,
4540 _(
4540 _(
4541 b'follow changeset history, or file history across copies and renames'
4541 b'follow changeset history, or file history across copies and renames'
4542 ),
4542 ),
4543 ),
4543 ),
4544 (
4544 (
4545 b'',
4545 b'',
4546 b'follow-first',
4546 b'follow-first',
4547 None,
4547 None,
4548 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4548 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4549 ),
4549 ),
4550 (
4550 (
4551 b'd',
4551 b'd',
4552 b'date',
4552 b'date',
4553 b'',
4553 b'',
4554 _(b'show revisions matching date spec'),
4554 _(b'show revisions matching date spec'),
4555 _(b'DATE'),
4555 _(b'DATE'),
4556 ),
4556 ),
4557 (b'C', b'copies', None, _(b'show copied files')),
4557 (b'C', b'copies', None, _(b'show copied files')),
4558 (
4558 (
4559 b'k',
4559 b'k',
4560 b'keyword',
4560 b'keyword',
4561 [],
4561 [],
4562 _(b'do case-insensitive search for a given text'),
4562 _(b'do case-insensitive search for a given text'),
4563 _(b'TEXT'),
4563 _(b'TEXT'),
4564 ),
4564 ),
4565 (
4565 (
4566 b'r',
4566 b'r',
4567 b'rev',
4567 b'rev',
4568 [],
4568 [],
4569 _(b'revisions to select or follow from'),
4569 _(b'revisions to select or follow from'),
4570 _(b'REV'),
4570 _(b'REV'),
4571 ),
4571 ),
4572 (
4572 (
4573 b'L',
4573 b'L',
4574 b'line-range',
4574 b'line-range',
4575 [],
4575 [],
4576 _(b'follow line range of specified file (EXPERIMENTAL)'),
4576 _(b'follow line range of specified file (EXPERIMENTAL)'),
4577 _(b'FILE,RANGE'),
4577 _(b'FILE,RANGE'),
4578 ),
4578 ),
4579 (
4579 (
4580 b'',
4580 b'',
4581 b'removed',
4581 b'removed',
4582 None,
4582 None,
4583 _(b'include revisions where files were removed'),
4583 _(b'include revisions where files were removed'),
4584 ),
4584 ),
4585 (
4585 (
4586 b'm',
4586 b'm',
4587 b'only-merges',
4587 b'only-merges',
4588 None,
4588 None,
4589 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4589 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4590 ),
4590 ),
4591 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4591 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4592 (
4592 (
4593 b'',
4593 b'',
4594 b'only-branch',
4594 b'only-branch',
4595 [],
4595 [],
4596 _(
4596 _(
4597 b'show only changesets within the given named branch (DEPRECATED)'
4597 b'show only changesets within the given named branch (DEPRECATED)'
4598 ),
4598 ),
4599 _(b'BRANCH'),
4599 _(b'BRANCH'),
4600 ),
4600 ),
4601 (
4601 (
4602 b'b',
4602 b'b',
4603 b'branch',
4603 b'branch',
4604 [],
4604 [],
4605 _(b'show changesets within the given named branch'),
4605 _(b'show changesets within the given named branch'),
4606 _(b'BRANCH'),
4606 _(b'BRANCH'),
4607 ),
4607 ),
4608 (
4608 (
4609 b'B',
4609 b'B',
4610 b'bookmark',
4610 b'bookmark',
4611 [],
4611 [],
4612 _(b"show changesets within the given bookmark"),
4612 _(b"show changesets within the given bookmark"),
4613 _(b'BOOKMARK'),
4613 _(b'BOOKMARK'),
4614 ),
4614 ),
4615 (
4615 (
4616 b'P',
4616 b'P',
4617 b'prune',
4617 b'prune',
4618 [],
4618 [],
4619 _(b'do not display revision or any of its ancestors'),
4619 _(b'do not display revision or any of its ancestors'),
4620 _(b'REV'),
4620 _(b'REV'),
4621 ),
4621 ),
4622 ]
4622 ]
4623 + logopts
4623 + logopts
4624 + walkopts,
4624 + walkopts,
4625 _(b'[OPTION]... [FILE]'),
4625 _(b'[OPTION]... [FILE]'),
4626 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4626 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4627 helpbasic=True,
4627 helpbasic=True,
4628 inferrepo=True,
4628 inferrepo=True,
4629 intents={INTENT_READONLY},
4629 intents={INTENT_READONLY},
4630 )
4630 )
4631 def log(ui, repo, *pats, **opts):
4631 def log(ui, repo, *pats, **opts):
4632 """show revision history of entire repository or files
4632 """show revision history of entire repository or files
4633
4633
4634 Print the revision history of the specified files or the entire
4634 Print the revision history of the specified files or the entire
4635 project.
4635 project.
4636
4636
4637 If no revision range is specified, the default is ``tip:0`` unless
4637 If no revision range is specified, the default is ``tip:0`` unless
4638 --follow is set.
4638 --follow is set.
4639
4639
4640 File history is shown without following rename or copy history of
4640 File history is shown without following rename or copy history of
4641 files. Use -f/--follow with a filename to follow history across
4641 files. Use -f/--follow with a filename to follow history across
4642 renames and copies. --follow without a filename will only show
4642 renames and copies. --follow without a filename will only show
4643 ancestors of the starting revisions. The starting revisions can be
4643 ancestors of the starting revisions. The starting revisions can be
4644 specified by -r/--rev, which default to the working directory parent.
4644 specified by -r/--rev, which default to the working directory parent.
4645
4645
4646 By default this command prints revision number and changeset id,
4646 By default this command prints revision number and changeset id,
4647 tags, non-trivial parents, user, date and time, and a summary for
4647 tags, non-trivial parents, user, date and time, and a summary for
4648 each commit. When the -v/--verbose switch is used, the list of
4648 each commit. When the -v/--verbose switch is used, the list of
4649 changed files and full commit message are shown.
4649 changed files and full commit message are shown.
4650
4650
4651 With --graph the revisions are shown as an ASCII art DAG with the most
4651 With --graph the revisions are shown as an ASCII art DAG with the most
4652 recent changeset at the top.
4652 recent changeset at the top.
4653 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4653 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4654 involved in an unresolved merge conflict, '_' closes a branch,
4654 involved in an unresolved merge conflict, '_' closes a branch,
4655 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4655 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4656 changeset from the lines below is a parent of the 'o' merge on the same
4656 changeset from the lines below is a parent of the 'o' merge on the same
4657 line.
4657 line.
4658 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4658 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4659 of a '|' indicates one or more revisions in a path are omitted.
4659 of a '|' indicates one or more revisions in a path are omitted.
4660
4660
4661 .. container:: verbose
4661 .. container:: verbose
4662
4662
4663 Use -L/--line-range FILE,M:N options to follow the history of lines
4663 Use -L/--line-range FILE,M:N options to follow the history of lines
4664 from M to N in FILE. With -p/--patch only diff hunks affecting
4664 from M to N in FILE. With -p/--patch only diff hunks affecting
4665 specified line range will be shown. This option requires --follow;
4665 specified line range will be shown. This option requires --follow;
4666 it can be specified multiple times. Currently, this option is not
4666 it can be specified multiple times. Currently, this option is not
4667 compatible with --graph. This option is experimental.
4667 compatible with --graph. This option is experimental.
4668
4668
4669 .. note::
4669 .. note::
4670
4670
4671 :hg:`log --patch` may generate unexpected diff output for merge
4671 :hg:`log --patch` may generate unexpected diff output for merge
4672 changesets, as it will only compare the merge changeset against
4672 changesets, as it will only compare the merge changeset against
4673 its first parent. Also, only files different from BOTH parents
4673 its first parent. Also, only files different from BOTH parents
4674 will appear in files:.
4674 will appear in files:.
4675
4675
4676 .. note::
4676 .. note::
4677
4677
4678 For performance reasons, :hg:`log FILE` may omit duplicate changes
4678 For performance reasons, :hg:`log FILE` may omit duplicate changes
4679 made on branches and will not show removals or mode changes. To
4679 made on branches and will not show removals or mode changes. To
4680 see all such changes, use the --removed switch.
4680 see all such changes, use the --removed switch.
4681
4681
4682 .. container:: verbose
4682 .. container:: verbose
4683
4683
4684 .. note::
4684 .. note::
4685
4685
4686 The history resulting from -L/--line-range options depends on diff
4686 The history resulting from -L/--line-range options depends on diff
4687 options; for instance if white-spaces are ignored, respective changes
4687 options; for instance if white-spaces are ignored, respective changes
4688 with only white-spaces in specified line range will not be listed.
4688 with only white-spaces in specified line range will not be listed.
4689
4689
4690 .. container:: verbose
4690 .. container:: verbose
4691
4691
4692 Some examples:
4692 Some examples:
4693
4693
4694 - changesets with full descriptions and file lists::
4694 - changesets with full descriptions and file lists::
4695
4695
4696 hg log -v
4696 hg log -v
4697
4697
4698 - changesets ancestral to the working directory::
4698 - changesets ancestral to the working directory::
4699
4699
4700 hg log -f
4700 hg log -f
4701
4701
4702 - last 10 commits on the current branch::
4702 - last 10 commits on the current branch::
4703
4703
4704 hg log -l 10 -b .
4704 hg log -l 10 -b .
4705
4705
4706 - changesets showing all modifications of a file, including removals::
4706 - changesets showing all modifications of a file, including removals::
4707
4707
4708 hg log --removed file.c
4708 hg log --removed file.c
4709
4709
4710 - all changesets that touch a directory, with diffs, excluding merges::
4710 - all changesets that touch a directory, with diffs, excluding merges::
4711
4711
4712 hg log -Mp lib/
4712 hg log -Mp lib/
4713
4713
4714 - all revision numbers that match a keyword::
4714 - all revision numbers that match a keyword::
4715
4715
4716 hg log -k bug --template "{rev}\\n"
4716 hg log -k bug --template "{rev}\\n"
4717
4717
4718 - the full hash identifier of the working directory parent::
4718 - the full hash identifier of the working directory parent::
4719
4719
4720 hg log -r . --template "{node}\\n"
4720 hg log -r . --template "{node}\\n"
4721
4721
4722 - list available log templates::
4722 - list available log templates::
4723
4723
4724 hg log -T list
4724 hg log -T list
4725
4725
4726 - check if a given changeset is included in a tagged release::
4726 - check if a given changeset is included in a tagged release::
4727
4727
4728 hg log -r "a21ccf and ancestor(1.9)"
4728 hg log -r "a21ccf and ancestor(1.9)"
4729
4729
4730 - find all changesets by some user in a date range::
4730 - find all changesets by some user in a date range::
4731
4731
4732 hg log -k alice -d "may 2008 to jul 2008"
4732 hg log -k alice -d "may 2008 to jul 2008"
4733
4733
4734 - summary of all changesets after the last tag::
4734 - summary of all changesets after the last tag::
4735
4735
4736 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4736 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4737
4737
4738 - changesets touching lines 13 to 23 for file.c::
4738 - changesets touching lines 13 to 23 for file.c::
4739
4739
4740 hg log -L file.c,13:23
4740 hg log -L file.c,13:23
4741
4741
4742 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4742 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4743 main.c with patch::
4743 main.c with patch::
4744
4744
4745 hg log -L file.c,13:23 -L main.c,2:6 -p
4745 hg log -L file.c,13:23 -L main.c,2:6 -p
4746
4746
4747 See :hg:`help dates` for a list of formats valid for -d/--date.
4747 See :hg:`help dates` for a list of formats valid for -d/--date.
4748
4748
4749 See :hg:`help revisions` for more about specifying and ordering
4749 See :hg:`help revisions` for more about specifying and ordering
4750 revisions.
4750 revisions.
4751
4751
4752 See :hg:`help templates` for more about pre-packaged styles and
4752 See :hg:`help templates` for more about pre-packaged styles and
4753 specifying custom templates. The default template used by the log
4753 specifying custom templates. The default template used by the log
4754 command can be customized via the ``command-templates.log`` configuration
4754 command can be customized via the ``command-templates.log`` configuration
4755 setting.
4755 setting.
4756
4756
4757 Returns 0 on success.
4757 Returns 0 on success.
4758
4758
4759 """
4759 """
4760 opts = pycompat.byteskwargs(opts)
4760 opts = pycompat.byteskwargs(opts)
4761 linerange = opts.get(b'line_range')
4761 linerange = opts.get(b'line_range')
4762
4762
4763 if linerange and not opts.get(b'follow'):
4763 if linerange and not opts.get(b'follow'):
4764 raise error.InputError(_(b'--line-range requires --follow'))
4764 raise error.InputError(_(b'--line-range requires --follow'))
4765
4765
4766 if linerange and pats:
4766 if linerange and pats:
4767 # TODO: take pats as patterns with no line-range filter
4767 # TODO: take pats as patterns with no line-range filter
4768 raise error.InputError(
4768 raise error.InputError(
4769 _(b'FILE arguments are not compatible with --line-range option')
4769 _(b'FILE arguments are not compatible with --line-range option')
4770 )
4770 )
4771
4771
4772 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4772 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4773 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4773 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4774 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4774 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4775 if linerange:
4775 if linerange:
4776 # TODO: should follow file history from logcmdutil._initialrevs(),
4776 # TODO: should follow file history from logcmdutil._initialrevs(),
4777 # then filter the result by logcmdutil._makerevset() and --limit
4777 # then filter the result by logcmdutil._makerevset() and --limit
4778 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4778 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4779
4779
4780 getcopies = None
4780 getcopies = None
4781 if opts.get(b'copies'):
4781 if opts.get(b'copies'):
4782 endrev = None
4782 endrev = None
4783 if revs:
4783 if revs:
4784 endrev = revs.max() + 1
4784 endrev = revs.max() + 1
4785 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4785 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4786
4786
4787 ui.pager(b'log')
4787 ui.pager(b'log')
4788 displayer = logcmdutil.changesetdisplayer(
4788 displayer = logcmdutil.changesetdisplayer(
4789 ui, repo, opts, differ, buffered=True
4789 ui, repo, opts, differ, buffered=True
4790 )
4790 )
4791 if opts.get(b'graph'):
4791 if opts.get(b'graph'):
4792 displayfn = logcmdutil.displaygraphrevs
4792 displayfn = logcmdutil.displaygraphrevs
4793 else:
4793 else:
4794 displayfn = logcmdutil.displayrevs
4794 displayfn = logcmdutil.displayrevs
4795 displayfn(ui, repo, revs, displayer, getcopies)
4795 displayfn(ui, repo, revs, displayer, getcopies)
4796
4796
4797
4797
4798 @command(
4798 @command(
4799 b'manifest',
4799 b'manifest',
4800 [
4800 [
4801 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4801 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4802 (b'', b'all', False, _(b"list files from all revisions")),
4802 (b'', b'all', False, _(b"list files from all revisions")),
4803 ]
4803 ]
4804 + formatteropts,
4804 + formatteropts,
4805 _(b'[-r REV]'),
4805 _(b'[-r REV]'),
4806 helpcategory=command.CATEGORY_MAINTENANCE,
4806 helpcategory=command.CATEGORY_MAINTENANCE,
4807 intents={INTENT_READONLY},
4807 intents={INTENT_READONLY},
4808 )
4808 )
4809 def manifest(ui, repo, node=None, rev=None, **opts):
4809 def manifest(ui, repo, node=None, rev=None, **opts):
4810 """output the current or given revision of the project manifest
4810 """output the current or given revision of the project manifest
4811
4811
4812 Print a list of version controlled files for the given revision.
4812 Print a list of version controlled files for the given revision.
4813 If no revision is given, the first parent of the working directory
4813 If no revision is given, the first parent of the working directory
4814 is used, or the null revision if no revision is checked out.
4814 is used, or the null revision if no revision is checked out.
4815
4815
4816 With -v, print file permissions, symlink and executable bits.
4816 With -v, print file permissions, symlink and executable bits.
4817 With --debug, print file revision hashes.
4817 With --debug, print file revision hashes.
4818
4818
4819 If option --all is specified, the list of all files from all revisions
4819 If option --all is specified, the list of all files from all revisions
4820 is printed. This includes deleted and renamed files.
4820 is printed. This includes deleted and renamed files.
4821
4821
4822 Returns 0 on success.
4822 Returns 0 on success.
4823 """
4823 """
4824 fm = ui.formatter(b'manifest', pycompat.byteskwargs(opts))
4824 fm = ui.formatter(b'manifest', pycompat.byteskwargs(opts))
4825
4825
4826 if opts.get('all'):
4826 if opts.get('all'):
4827 if rev or node:
4827 if rev or node:
4828 raise error.InputError(_(b"can't specify a revision with --all"))
4828 raise error.InputError(_(b"can't specify a revision with --all"))
4829
4829
4830 res = set()
4830 res = set()
4831 for rev in repo:
4831 for rev in repo:
4832 ctx = repo[rev]
4832 ctx = repo[rev]
4833 res |= set(ctx.files())
4833 res |= set(ctx.files())
4834
4834
4835 ui.pager(b'manifest')
4835 ui.pager(b'manifest')
4836 for f in sorted(res):
4836 for f in sorted(res):
4837 fm.startitem()
4837 fm.startitem()
4838 fm.write(b"path", b'%s\n', f)
4838 fm.write(b"path", b'%s\n', f)
4839 fm.end()
4839 fm.end()
4840 return
4840 return
4841
4841
4842 if rev and node:
4842 if rev and node:
4843 raise error.InputError(_(b"please specify just one revision"))
4843 raise error.InputError(_(b"please specify just one revision"))
4844
4844
4845 if not node:
4845 if not node:
4846 node = rev
4846 node = rev
4847
4847
4848 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4848 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4849 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4849 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4850 if node:
4850 if node:
4851 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4851 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4852 ctx = logcmdutil.revsingle(repo, node)
4852 ctx = logcmdutil.revsingle(repo, node)
4853 mf = ctx.manifest()
4853 mf = ctx.manifest()
4854 ui.pager(b'manifest')
4854 ui.pager(b'manifest')
4855 for f in ctx:
4855 for f in ctx:
4856 fm.startitem()
4856 fm.startitem()
4857 fm.context(ctx=ctx)
4857 fm.context(ctx=ctx)
4858 fl = ctx[f].flags()
4858 fl = ctx[f].flags()
4859 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4859 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4860 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4860 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4861 fm.write(b'path', b'%s\n', f)
4861 fm.write(b'path', b'%s\n', f)
4862 fm.end()
4862 fm.end()
4863
4863
4864
4864
4865 @command(
4865 @command(
4866 b'merge',
4866 b'merge',
4867 [
4867 [
4868 (
4868 (
4869 b'f',
4869 b'f',
4870 b'force',
4870 b'force',
4871 None,
4871 None,
4872 _(b'force a merge including outstanding changes (DEPRECATED)'),
4872 _(b'force a merge including outstanding changes (DEPRECATED)'),
4873 ),
4873 ),
4874 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4874 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4875 (
4875 (
4876 b'P',
4876 b'P',
4877 b'preview',
4877 b'preview',
4878 None,
4878 None,
4879 _(b'review revisions to merge (no merge is performed)'),
4879 _(b'review revisions to merge (no merge is performed)'),
4880 ),
4880 ),
4881 (b'', b'abort', None, _(b'abort the ongoing merge')),
4881 (b'', b'abort', None, _(b'abort the ongoing merge')),
4882 ]
4882 ]
4883 + mergetoolopts,
4883 + mergetoolopts,
4884 _(b'[-P] [[-r] REV]'),
4884 _(b'[-P] [[-r] REV]'),
4885 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4885 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4886 helpbasic=True,
4886 helpbasic=True,
4887 )
4887 )
4888 def merge(ui, repo, node=None, **opts):
4888 def merge(ui, repo, node=None, **opts):
4889 """merge another revision into working directory
4889 """merge another revision into working directory
4890
4890
4891 The current working directory is updated with all changes made in
4891 The current working directory is updated with all changes made in
4892 the requested revision since the last common predecessor revision.
4892 the requested revision since the last common predecessor revision.
4893
4893
4894 Files that changed between either parent are marked as changed for
4894 Files that changed between either parent are marked as changed for
4895 the next commit and a commit must be performed before any further
4895 the next commit and a commit must be performed before any further
4896 updates to the repository are allowed. The next commit will have
4896 updates to the repository are allowed. The next commit will have
4897 two parents.
4897 two parents.
4898
4898
4899 ``--tool`` can be used to specify the merge tool used for file
4899 ``--tool`` can be used to specify the merge tool used for file
4900 merges. It overrides the HGMERGE environment variable and your
4900 merges. It overrides the HGMERGE environment variable and your
4901 configuration files. See :hg:`help merge-tools` for options.
4901 configuration files. See :hg:`help merge-tools` for options.
4902
4902
4903 If no revision is specified, the working directory's parent is a
4903 If no revision is specified, the working directory's parent is a
4904 head revision, and the current branch contains exactly one other
4904 head revision, and the current branch contains exactly one other
4905 head, the other head is merged with by default. Otherwise, an
4905 head, the other head is merged with by default. Otherwise, an
4906 explicit revision with which to merge must be provided.
4906 explicit revision with which to merge must be provided.
4907
4907
4908 See :hg:`help resolve` for information on handling file conflicts.
4908 See :hg:`help resolve` for information on handling file conflicts.
4909
4909
4910 To undo an uncommitted merge, use :hg:`merge --abort` which
4910 To undo an uncommitted merge, use :hg:`merge --abort` which
4911 will check out a clean copy of the original merge parent, losing
4911 will check out a clean copy of the original merge parent, losing
4912 all changes.
4912 all changes.
4913
4913
4914 Returns 0 on success, 1 if there are unresolved files.
4914 Returns 0 on success, 1 if there are unresolved files.
4915 """
4915 """
4916
4916
4917 abort = opts.get('abort')
4917 abort = opts.get('abort')
4918 if abort and repo.dirstate.p2() == repo.nullid:
4918 if abort and repo.dirstate.p2() == repo.nullid:
4919 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4919 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4920 cmdutil.check_incompatible_arguments(opts, 'abort', ['rev', 'preview'])
4920 cmdutil.check_incompatible_arguments(opts, 'abort', ['rev', 'preview'])
4921 if abort:
4921 if abort:
4922 state = cmdutil.getunfinishedstate(repo)
4922 state = cmdutil.getunfinishedstate(repo)
4923 if state and state._opname != b'merge':
4923 if state and state._opname != b'merge':
4924 raise error.StateError(
4924 raise error.StateError(
4925 _(b'cannot abort merge with %s in progress') % (state._opname),
4925 _(b'cannot abort merge with %s in progress') % (state._opname),
4926 hint=state.hint(),
4926 hint=state.hint(),
4927 )
4927 )
4928 if node:
4928 if node:
4929 raise error.InputError(_(b"cannot specify a node with --abort"))
4929 raise error.InputError(_(b"cannot specify a node with --abort"))
4930 return hg.abortmerge(repo.ui, repo)
4930 return hg.abortmerge(repo.ui, repo)
4931
4931
4932 if opts.get('rev') and node:
4932 if opts.get('rev') and node:
4933 raise error.InputError(_(b"please specify just one revision"))
4933 raise error.InputError(_(b"please specify just one revision"))
4934 if not node:
4934 if not node:
4935 node = opts.get('rev')
4935 node = opts.get('rev')
4936
4936
4937 if node:
4937 if node:
4938 ctx = logcmdutil.revsingle(repo, node)
4938 ctx = logcmdutil.revsingle(repo, node)
4939 else:
4939 else:
4940 if ui.configbool(b'commands', b'merge.require-rev'):
4940 if ui.configbool(b'commands', b'merge.require-rev'):
4941 raise error.InputError(
4941 raise error.InputError(
4942 _(
4942 _(
4943 b'configuration requires specifying revision to merge '
4943 b'configuration requires specifying revision to merge '
4944 b'with'
4944 b'with'
4945 )
4945 )
4946 )
4946 )
4947 ctx = repo[destutil.destmerge(repo)]
4947 ctx = repo[destutil.destmerge(repo)]
4948
4948
4949 if ctx.node() is None:
4949 if ctx.node() is None:
4950 raise error.InputError(
4950 raise error.InputError(
4951 _(b'merging with the working copy has no effect')
4951 _(b'merging with the working copy has no effect')
4952 )
4952 )
4953
4953
4954 if opts.get('preview'):
4954 if opts.get('preview'):
4955 # find nodes that are ancestors of p2 but not of p1
4955 # find nodes that are ancestors of p2 but not of p1
4956 p1 = repo[b'.'].node()
4956 p1 = repo[b'.'].node()
4957 p2 = ctx.node()
4957 p2 = ctx.node()
4958 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4958 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4959
4959
4960 displayer = logcmdutil.changesetdisplayer(
4960 displayer = logcmdutil.changesetdisplayer(
4961 ui, repo, pycompat.byteskwargs(opts)
4961 ui, repo, pycompat.byteskwargs(opts)
4962 )
4962 )
4963 for node in nodes:
4963 for node in nodes:
4964 displayer.show(repo[node])
4964 displayer.show(repo[node])
4965 displayer.close()
4965 displayer.close()
4966 return 0
4966 return 0
4967
4967
4968 # ui.forcemerge is an internal variable, do not document
4968 # ui.forcemerge is an internal variable, do not document
4969 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
4969 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
4970 with ui.configoverride(overrides, b'merge'):
4970 with ui.configoverride(overrides, b'merge'):
4971 force = opts.get('force')
4971 force = opts.get('force')
4972 labels = [b'working copy', b'merge rev', b'common ancestor']
4972 labels = [b'working copy', b'merge rev', b'common ancestor']
4973 return hg.merge(ctx, force=force, labels=labels)
4973 return hg.merge(ctx, force=force, labels=labels)
4974
4974
4975
4975
4976 statemod.addunfinished(
4976 statemod.addunfinished(
4977 b'merge',
4977 b'merge',
4978 fname=None,
4978 fname=None,
4979 clearable=True,
4979 clearable=True,
4980 allowcommit=True,
4980 allowcommit=True,
4981 cmdmsg=_(b'outstanding uncommitted merge'),
4981 cmdmsg=_(b'outstanding uncommitted merge'),
4982 abortfunc=hg.abortmerge,
4982 abortfunc=hg.abortmerge,
4983 statushint=_(
4983 statushint=_(
4984 b'To continue: hg commit\nTo abort: hg merge --abort'
4984 b'To continue: hg commit\nTo abort: hg merge --abort'
4985 ),
4985 ),
4986 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4986 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4987 )
4987 )
4988
4988
4989
4989
4990 @command(
4990 @command(
4991 b'outgoing|out',
4991 b'outgoing|out',
4992 [
4992 [
4993 (
4993 (
4994 b'f',
4994 b'f',
4995 b'force',
4995 b'force',
4996 None,
4996 None,
4997 _(b'run even when the destination is unrelated'),
4997 _(b'run even when the destination is unrelated'),
4998 ),
4998 ),
4999 (
4999 (
5000 b'r',
5000 b'r',
5001 b'rev',
5001 b'rev',
5002 [],
5002 [],
5003 _(b'a changeset intended to be included in the destination'),
5003 _(b'a changeset intended to be included in the destination'),
5004 _(b'REV'),
5004 _(b'REV'),
5005 ),
5005 ),
5006 (b'n', b'newest-first', None, _(b'show newest record first')),
5006 (b'n', b'newest-first', None, _(b'show newest record first')),
5007 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
5007 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
5008 (
5008 (
5009 b'b',
5009 b'b',
5010 b'branch',
5010 b'branch',
5011 [],
5011 [],
5012 _(b'a specific branch you would like to push'),
5012 _(b'a specific branch you would like to push'),
5013 _(b'BRANCH'),
5013 _(b'BRANCH'),
5014 ),
5014 ),
5015 ]
5015 ]
5016 + logopts
5016 + logopts
5017 + remoteopts
5017 + remoteopts
5018 + subrepoopts,
5018 + subrepoopts,
5019 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
5019 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
5020 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5020 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5021 )
5021 )
5022 def outgoing(ui, repo, *dests, **opts):
5022 def outgoing(ui, repo, *dests, **opts):
5023 """show changesets not found in the destination
5023 """show changesets not found in the destination
5024
5024
5025 Show changesets not found in the specified destination repository
5025 Show changesets not found in the specified destination repository
5026 or the default push location. These are the changesets that would
5026 or the default push location. These are the changesets that would
5027 be pushed if a push was requested.
5027 be pushed if a push was requested.
5028
5028
5029 See pull for details of valid destination formats.
5029 See pull for details of valid destination formats.
5030
5030
5031 .. container:: verbose
5031 .. container:: verbose
5032
5032
5033 With -B/--bookmarks, the result of bookmark comparison between
5033 With -B/--bookmarks, the result of bookmark comparison between
5034 local and remote repositories is displayed. With -v/--verbose,
5034 local and remote repositories is displayed. With -v/--verbose,
5035 status is also displayed for each bookmark like below::
5035 status is also displayed for each bookmark like below::
5036
5036
5037 BM1 01234567890a added
5037 BM1 01234567890a added
5038 BM2 deleted
5038 BM2 deleted
5039 BM3 234567890abc advanced
5039 BM3 234567890abc advanced
5040 BM4 34567890abcd diverged
5040 BM4 34567890abcd diverged
5041 BM5 4567890abcde changed
5041 BM5 4567890abcde changed
5042
5042
5043 The action taken when pushing depends on the
5043 The action taken when pushing depends on the
5044 status of each bookmark:
5044 status of each bookmark:
5045
5045
5046 :``added``: push with ``-B`` will create it
5046 :``added``: push with ``-B`` will create it
5047 :``deleted``: push with ``-B`` will delete it
5047 :``deleted``: push with ``-B`` will delete it
5048 :``advanced``: push will update it
5048 :``advanced``: push will update it
5049 :``diverged``: push with ``-B`` will update it
5049 :``diverged``: push with ``-B`` will update it
5050 :``changed``: push with ``-B`` will update it
5050 :``changed``: push with ``-B`` will update it
5051
5051
5052 From the point of view of pushing behavior, bookmarks
5052 From the point of view of pushing behavior, bookmarks
5053 existing only in the remote repository are treated as
5053 existing only in the remote repository are treated as
5054 ``deleted``, even if it is in fact added remotely.
5054 ``deleted``, even if it is in fact added remotely.
5055
5055
5056 Returns 0 if there are outgoing changes, 1 otherwise.
5056 Returns 0 if there are outgoing changes, 1 otherwise.
5057 """
5057 """
5058 opts = pycompat.byteskwargs(opts)
5058 opts = pycompat.byteskwargs(opts)
5059 if opts.get(b'bookmarks'):
5059 if opts.get(b'bookmarks'):
5060 for path in urlutil.get_push_paths(repo, ui, dests):
5060 for path in urlutil.get_push_paths(repo, ui, dests):
5061 other = hg.peer(repo, opts, path)
5061 other = hg.peer(repo, opts, path)
5062 try:
5062 try:
5063 if b'bookmarks' not in other.listkeys(b'namespaces'):
5063 if b'bookmarks' not in other.listkeys(b'namespaces'):
5064 ui.warn(_(b"remote doesn't support bookmarks\n"))
5064 ui.warn(_(b"remote doesn't support bookmarks\n"))
5065 return 0
5065 return 0
5066 ui.status(
5066 ui.status(
5067 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
5067 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
5068 )
5068 )
5069 ui.pager(b'outgoing')
5069 ui.pager(b'outgoing')
5070 return bookmarks.outgoing(ui, repo, other)
5070 return bookmarks.outgoing(ui, repo, other)
5071 finally:
5071 finally:
5072 other.close()
5072 other.close()
5073
5073
5074 return hg.outgoing(ui, repo, dests, opts)
5074 return hg.outgoing(ui, repo, dests, opts)
5075
5075
5076
5076
5077 @command(
5077 @command(
5078 b'parents',
5078 b'parents',
5079 [
5079 [
5080 (
5080 (
5081 b'r',
5081 b'r',
5082 b'rev',
5082 b'rev',
5083 b'',
5083 b'',
5084 _(b'show parents of the specified revision'),
5084 _(b'show parents of the specified revision'),
5085 _(b'REV'),
5085 _(b'REV'),
5086 ),
5086 ),
5087 ]
5087 ]
5088 + templateopts,
5088 + templateopts,
5089 _(b'[-r REV] [FILE]'),
5089 _(b'[-r REV] [FILE]'),
5090 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5090 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5091 inferrepo=True,
5091 inferrepo=True,
5092 )
5092 )
5093 def parents(ui, repo, file_=None, **opts):
5093 def parents(ui, repo, file_=None, **opts):
5094 """show the parents of the working directory or revision (DEPRECATED)
5094 """show the parents of the working directory or revision (DEPRECATED)
5095
5095
5096 Print the working directory's parent revisions. If a revision is
5096 Print the working directory's parent revisions. If a revision is
5097 given via -r/--rev, the parent of that revision will be printed.
5097 given via -r/--rev, the parent of that revision will be printed.
5098 If a file argument is given, the revision in which the file was
5098 If a file argument is given, the revision in which the file was
5099 last changed (before the working directory revision or the
5099 last changed (before the working directory revision or the
5100 argument to --rev if given) is printed.
5100 argument to --rev if given) is printed.
5101
5101
5102 This command is equivalent to::
5102 This command is equivalent to::
5103
5103
5104 hg log -r "p1()+p2()" or
5104 hg log -r "p1()+p2()" or
5105 hg log -r "p1(REV)+p2(REV)" or
5105 hg log -r "p1(REV)+p2(REV)" or
5106 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5106 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5107 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5107 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5108
5108
5109 See :hg:`summary` and :hg:`help revsets` for related information.
5109 See :hg:`summary` and :hg:`help revsets` for related information.
5110
5110
5111 Returns 0 on success.
5111 Returns 0 on success.
5112 """
5112 """
5113
5113
5114 opts = pycompat.byteskwargs(opts)
5114 opts = pycompat.byteskwargs(opts)
5115 rev = opts.get(b'rev')
5115 rev = opts.get(b'rev')
5116 if rev:
5116 if rev:
5117 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5117 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5118 ctx = logcmdutil.revsingle(repo, rev, None)
5118 ctx = logcmdutil.revsingle(repo, rev, None)
5119
5119
5120 if file_:
5120 if file_:
5121 m = scmutil.match(ctx, (file_,), opts)
5121 m = scmutil.match(ctx, (file_,), opts)
5122 if m.anypats() or len(m.files()) != 1:
5122 if m.anypats() or len(m.files()) != 1:
5123 raise error.InputError(_(b'can only specify an explicit filename'))
5123 raise error.InputError(_(b'can only specify an explicit filename'))
5124 file_ = m.files()[0]
5124 file_ = m.files()[0]
5125 filenodes = []
5125 filenodes = []
5126 for cp in ctx.parents():
5126 for cp in ctx.parents():
5127 if not cp:
5127 if not cp:
5128 continue
5128 continue
5129 try:
5129 try:
5130 filenodes.append(cp.filenode(file_))
5130 filenodes.append(cp.filenode(file_))
5131 except error.LookupError:
5131 except error.LookupError:
5132 pass
5132 pass
5133 if not filenodes:
5133 if not filenodes:
5134 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5134 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5135 p = []
5135 p = []
5136 for fn in filenodes:
5136 for fn in filenodes:
5137 fctx = repo.filectx(file_, fileid=fn)
5137 fctx = repo.filectx(file_, fileid=fn)
5138 p.append(fctx.node())
5138 p.append(fctx.node())
5139 else:
5139 else:
5140 p = [cp.node() for cp in ctx.parents()]
5140 p = [cp.node() for cp in ctx.parents()]
5141
5141
5142 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5142 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5143 for n in p:
5143 for n in p:
5144 if n != repo.nullid:
5144 if n != repo.nullid:
5145 displayer.show(repo[n])
5145 displayer.show(repo[n])
5146 displayer.close()
5146 displayer.close()
5147
5147
5148
5148
5149 @command(
5149 @command(
5150 b'paths',
5150 b'paths',
5151 formatteropts,
5151 formatteropts,
5152 _(b'[NAME]'),
5152 _(b'[NAME]'),
5153 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5153 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5154 optionalrepo=True,
5154 optionalrepo=True,
5155 intents={INTENT_READONLY},
5155 intents={INTENT_READONLY},
5156 )
5156 )
5157 def paths(ui, repo, search=None, **opts):
5157 def paths(ui, repo, search=None, **opts):
5158 """show aliases for remote repositories
5158 """show aliases for remote repositories
5159
5159
5160 Show definition of symbolic path name NAME. If no name is given,
5160 Show definition of symbolic path name NAME. If no name is given,
5161 show definition of all available names.
5161 show definition of all available names.
5162
5162
5163 Option -q/--quiet suppresses all output when searching for NAME
5163 Option -q/--quiet suppresses all output when searching for NAME
5164 and shows only the path names when listing all definitions.
5164 and shows only the path names when listing all definitions.
5165
5165
5166 Path names are defined in the [paths] section of your
5166 Path names are defined in the [paths] section of your
5167 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5167 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5168 repository, ``.hg/hgrc`` is used, too.
5168 repository, ``.hg/hgrc`` is used, too.
5169
5169
5170 The path names ``default`` and ``default-push`` have a special
5170 The path names ``default`` and ``default-push`` have a special
5171 meaning. When performing a push or pull operation, they are used
5171 meaning. When performing a push or pull operation, they are used
5172 as fallbacks if no location is specified on the command-line.
5172 as fallbacks if no location is specified on the command-line.
5173 When ``default-push`` is set, it will be used for push and
5173 When ``default-push`` is set, it will be used for push and
5174 ``default`` will be used for pull; otherwise ``default`` is used
5174 ``default`` will be used for pull; otherwise ``default`` is used
5175 as the fallback for both. When cloning a repository, the clone
5175 as the fallback for both. When cloning a repository, the clone
5176 source is written as ``default`` in ``.hg/hgrc``.
5176 source is written as ``default`` in ``.hg/hgrc``.
5177
5177
5178 .. note::
5178 .. note::
5179
5179
5180 ``default`` and ``default-push`` apply to all inbound (e.g.
5180 ``default`` and ``default-push`` apply to all inbound (e.g.
5181 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5181 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5182 and :hg:`bundle`) operations.
5182 and :hg:`bundle`) operations.
5183
5183
5184 See :hg:`help urls` for more information.
5184 See :hg:`help urls` for more information.
5185
5185
5186 .. container:: verbose
5186 .. container:: verbose
5187
5187
5188 Template:
5188 Template:
5189
5189
5190 The following keywords are supported. See also :hg:`help templates`.
5190 The following keywords are supported. See also :hg:`help templates`.
5191
5191
5192 :name: String. Symbolic name of the path alias.
5192 :name: String. Symbolic name of the path alias.
5193 :pushurl: String. URL for push operations.
5193 :pushurl: String. URL for push operations.
5194 :url: String. URL or directory path for the other operations.
5194 :url: String. URL or directory path for the other operations.
5195
5195
5196 Returns 0 on success.
5196 Returns 0 on success.
5197 """
5197 """
5198
5198
5199 pathitems = urlutil.list_paths(ui, search)
5199 pathitems = urlutil.list_paths(ui, search)
5200 ui.pager(b'paths')
5200 ui.pager(b'paths')
5201
5201
5202 fm = ui.formatter(b'paths', pycompat.byteskwargs(opts))
5202 fm = ui.formatter(b'paths', pycompat.byteskwargs(opts))
5203 if fm.isplain():
5203 if fm.isplain():
5204 hidepassword = urlutil.hidepassword
5204 hidepassword = urlutil.hidepassword
5205 else:
5205 else:
5206 hidepassword = bytes
5206 hidepassword = bytes
5207 if ui.quiet:
5207 if ui.quiet:
5208 namefmt = b'%s\n'
5208 namefmt = b'%s\n'
5209 else:
5209 else:
5210 namefmt = b'%s = '
5210 namefmt = b'%s = '
5211 showsubopts = not search and not ui.quiet
5211 showsubopts = not search and not ui.quiet
5212
5212
5213 for name, path in pathitems:
5213 for name, path in pathitems:
5214 fm.startitem()
5214 fm.startitem()
5215 fm.condwrite(not search, b'name', namefmt, name)
5215 fm.condwrite(not search, b'name', namefmt, name)
5216 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5216 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5217 for subopt, value in sorted(path.suboptions.items()):
5217 for subopt, value in sorted(path.suboptions.items()):
5218 assert subopt not in (b'name', b'url')
5218 assert subopt not in (b'name', b'url')
5219 if showsubopts:
5219 if showsubopts:
5220 fm.plain(b'%s:%s = ' % (name, subopt))
5220 fm.plain(b'%s:%s = ' % (name, subopt))
5221 display = urlutil.path_suboptions_display[subopt]
5221 display = urlutil.path_suboptions_display[subopt]
5222 value = display(value)
5222 value = display(value)
5223 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5223 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5224
5224
5225 fm.end()
5225 fm.end()
5226
5226
5227 if search and not pathitems:
5227 if search and not pathitems:
5228 if not ui.quiet:
5228 if not ui.quiet:
5229 ui.warn(_(b"not found!\n"))
5229 ui.warn(_(b"not found!\n"))
5230 return 1
5230 return 1
5231 else:
5231 else:
5232 return 0
5232 return 0
5233
5233
5234
5234
5235 @command(
5235 @command(
5236 b'phase',
5236 b'phase',
5237 [
5237 [
5238 (b'p', b'public', False, _(b'set changeset phase to public')),
5238 (b'p', b'public', False, _(b'set changeset phase to public')),
5239 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5239 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5240 (b's', b'secret', False, _(b'set changeset phase to secret')),
5240 (b's', b'secret', False, _(b'set changeset phase to secret')),
5241 (b'f', b'force', False, _(b'allow to move boundary backward')),
5241 (b'f', b'force', False, _(b'allow to move boundary backward')),
5242 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5242 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5243 ],
5243 ],
5244 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5244 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5245 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5245 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5246 )
5246 )
5247 def phase(ui, repo, *revs, **opts):
5247 def phase(ui, repo, *revs, **opts):
5248 """set or show the current phase name
5248 """set or show the current phase name
5249
5249
5250 With no argument, show the phase name of the current revision(s).
5250 With no argument, show the phase name of the current revision(s).
5251
5251
5252 With one of -p/--public, -d/--draft or -s/--secret, change the
5252 With one of -p/--public, -d/--draft or -s/--secret, change the
5253 phase value of the specified revisions.
5253 phase value of the specified revisions.
5254
5254
5255 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5255 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5256 lower phase to a higher phase. Phases are ordered as follows::
5256 lower phase to a higher phase. Phases are ordered as follows::
5257
5257
5258 public < draft < secret
5258 public < draft < secret
5259
5259
5260 Returns 0 on success, 1 if some phases could not be changed.
5260 Returns 0 on success, 1 if some phases could not be changed.
5261
5261
5262 (For more information about the phases concept, see :hg:`help phases`.)
5262 (For more information about the phases concept, see :hg:`help phases`.)
5263 """
5263 """
5264 opts = pycompat.byteskwargs(opts)
5264 opts = pycompat.byteskwargs(opts)
5265 # search for a unique phase argument
5265 # search for a unique phase argument
5266 targetphase = None
5266 targetphase = None
5267 for idx, name in enumerate(phases.cmdphasenames):
5267 for idx, name in enumerate(phases.cmdphasenames):
5268 if opts[name]:
5268 if opts[name]:
5269 if targetphase is not None:
5269 if targetphase is not None:
5270 raise error.InputError(_(b'only one phase can be specified'))
5270 raise error.InputError(_(b'only one phase can be specified'))
5271 targetphase = idx
5271 targetphase = idx
5272
5272
5273 # look for specified revision
5273 # look for specified revision
5274 revs = list(revs)
5274 revs = list(revs)
5275 revs.extend(opts[b'rev'])
5275 revs.extend(opts[b'rev'])
5276 if revs:
5276 if revs:
5277 revs = logcmdutil.revrange(repo, revs)
5277 revs = logcmdutil.revrange(repo, revs)
5278 else:
5278 else:
5279 # display both parents as the second parent phase can influence
5279 # display both parents as the second parent phase can influence
5280 # the phase of a merge commit
5280 # the phase of a merge commit
5281 revs = [c.rev() for c in repo[None].parents()]
5281 revs = [c.rev() for c in repo[None].parents()]
5282
5282
5283 ret = 0
5283 ret = 0
5284 if targetphase is None:
5284 if targetphase is None:
5285 # display
5285 # display
5286 for r in revs:
5286 for r in revs:
5287 ctx = repo[r]
5287 ctx = repo[r]
5288 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5288 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5289 else:
5289 else:
5290 with repo.lock(), repo.transaction(b"phase") as tr:
5290 with repo.lock(), repo.transaction(b"phase") as tr:
5291 # set phase
5291 # set phase
5292 if not revs:
5292 if not revs:
5293 raise error.InputError(_(b'empty revision set'))
5293 raise error.InputError(_(b'empty revision set'))
5294 nodes = [repo[r].node() for r in revs]
5294 nodes = [repo[r].node() for r in revs]
5295 # moving revision from public to draft may hide them
5295 # moving revision from public to draft may hide them
5296 # We have to check result on an unfiltered repository
5296 # We have to check result on an unfiltered repository
5297 unfi = repo.unfiltered()
5297 unfi = repo.unfiltered()
5298 getphase = unfi._phasecache.phase
5298 getphase = unfi._phasecache.phase
5299 olddata = [getphase(unfi, r) for r in unfi]
5299 olddata = [getphase(unfi, r) for r in unfi]
5300 phases.advanceboundary(repo, tr, targetphase, nodes)
5300 phases.advanceboundary(repo, tr, targetphase, nodes)
5301 if opts[b'force']:
5301 if opts[b'force']:
5302 phases.retractboundary(repo, tr, targetphase, nodes)
5302 phases.retractboundary(repo, tr, targetphase, nodes)
5303 getphase = unfi._phasecache.phase
5303 getphase = unfi._phasecache.phase
5304 newdata = [getphase(unfi, r) for r in unfi]
5304 newdata = [getphase(unfi, r) for r in unfi]
5305 changes = sum(newdata[r] != olddata[r] for r in unfi)
5305 changes = sum(newdata[r] != olddata[r] for r in unfi)
5306 cl = unfi.changelog
5306 cl = unfi.changelog
5307 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5307 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5308 if rejected:
5308 if rejected:
5309 ui.warn(
5309 ui.warn(
5310 _(
5310 _(
5311 b'cannot move %i changesets to a higher '
5311 b'cannot move %i changesets to a higher '
5312 b'phase, use --force\n'
5312 b'phase, use --force\n'
5313 )
5313 )
5314 % len(rejected)
5314 % len(rejected)
5315 )
5315 )
5316 ret = 1
5316 ret = 1
5317 if changes:
5317 if changes:
5318 msg = _(b'phase changed for %i changesets\n') % changes
5318 msg = _(b'phase changed for %i changesets\n') % changes
5319 if ret:
5319 if ret:
5320 ui.status(msg)
5320 ui.status(msg)
5321 else:
5321 else:
5322 ui.note(msg)
5322 ui.note(msg)
5323 else:
5323 else:
5324 ui.warn(_(b'no phases changed\n'))
5324 ui.warn(_(b'no phases changed\n'))
5325 return ret
5325 return ret
5326
5326
5327
5327
5328 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5328 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5329 """Run after a changegroup has been added via pull/unbundle
5329 """Run after a changegroup has been added via pull/unbundle
5330
5330
5331 This takes arguments below:
5331 This takes arguments below:
5332
5332
5333 :modheads: change of heads by pull/unbundle
5333 :modheads: change of heads by pull/unbundle
5334 :optupdate: updating working directory is needed or not
5334 :optupdate: updating working directory is needed or not
5335 :checkout: update destination revision (or None to default destination)
5335 :checkout: update destination revision (or None to default destination)
5336 :brev: a name, which might be a bookmark to be activated after updating
5336 :brev: a name, which might be a bookmark to be activated after updating
5337
5337
5338 return True if update raise any conflict, False otherwise.
5338 return True if update raise any conflict, False otherwise.
5339 """
5339 """
5340 if modheads == 0:
5340 if modheads == 0:
5341 return False
5341 return False
5342 if optupdate:
5342 if optupdate:
5343 try:
5343 try:
5344 return hg.updatetotally(ui, repo, checkout, brev)
5344 return hg.updatetotally(ui, repo, checkout, brev)
5345 except error.UpdateAbort as inst:
5345 except error.UpdateAbort as inst:
5346 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5346 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5347 hint = inst.hint
5347 hint = inst.hint
5348 raise error.UpdateAbort(msg, hint=hint)
5348 raise error.UpdateAbort(msg, hint=hint)
5349 if modheads is not None and modheads > 1:
5349 if modheads is not None and modheads > 1:
5350 currentbranchheads = len(repo.branchheads())
5350 currentbranchheads = len(repo.branchheads())
5351 if currentbranchheads == modheads:
5351 if currentbranchheads == modheads:
5352 ui.status(
5352 ui.status(
5353 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5353 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5354 )
5354 )
5355 elif currentbranchheads > 1:
5355 elif currentbranchheads > 1:
5356 ui.status(
5356 ui.status(
5357 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5357 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5358 )
5358 )
5359 else:
5359 else:
5360 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5360 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5361 elif not ui.configbool(b'commands', b'update.requiredest'):
5361 elif not ui.configbool(b'commands', b'update.requiredest'):
5362 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5362 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5363 return False
5363 return False
5364
5364
5365
5365
5366 @command(
5366 @command(
5367 b'pull',
5367 b'pull',
5368 [
5368 [
5369 (
5369 (
5370 b'u',
5370 b'u',
5371 b'update',
5371 b'update',
5372 None,
5372 None,
5373 _(b'update to new branch head if new descendants were pulled'),
5373 _(b'update to new branch head if new descendants were pulled'),
5374 ),
5374 ),
5375 (
5375 (
5376 b'f',
5376 b'f',
5377 b'force',
5377 b'force',
5378 None,
5378 None,
5379 _(b'run even when remote repository is unrelated'),
5379 _(b'run even when remote repository is unrelated'),
5380 ),
5380 ),
5381 (
5381 (
5382 b'',
5382 b'',
5383 b'confirm',
5383 b'confirm',
5384 None,
5384 None,
5385 _(b'confirm pull before applying changes'),
5385 _(b'confirm pull before applying changes'),
5386 ),
5386 ),
5387 (
5387 (
5388 b'r',
5388 b'r',
5389 b'rev',
5389 b'rev',
5390 [],
5390 [],
5391 _(b'a remote changeset intended to be added'),
5391 _(b'a remote changeset intended to be added'),
5392 _(b'REV'),
5392 _(b'REV'),
5393 ),
5393 ),
5394 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5394 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5395 (
5395 (
5396 b'b',
5396 b'b',
5397 b'branch',
5397 b'branch',
5398 [],
5398 [],
5399 _(b'a specific branch you would like to pull'),
5399 _(b'a specific branch you would like to pull'),
5400 _(b'BRANCH'),
5400 _(b'BRANCH'),
5401 ),
5401 ),
5402 (
5402 (
5403 b'',
5403 b'',
5404 b'remote-hidden',
5404 b'remote-hidden',
5405 False,
5405 False,
5406 _(b"include changesets hidden on the remote (EXPERIMENTAL)"),
5406 _(b"include changesets hidden on the remote (EXPERIMENTAL)"),
5407 ),
5407 ),
5408 ]
5408 ]
5409 + remoteopts,
5409 + remoteopts,
5410 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5410 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5411 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5411 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5412 helpbasic=True,
5412 helpbasic=True,
5413 )
5413 )
5414 def pull(ui, repo, *sources, **opts):
5414 def pull(ui, repo, *sources, **opts):
5415 """pull changes from the specified source
5415 """pull changes from the specified source
5416
5416
5417 Pull changes from a remote repository to a local one.
5417 Pull changes from a remote repository to a local one.
5418
5418
5419 This finds all changes from the repository at the specified path
5419 This finds all changes from the repository at the specified path
5420 or URL and adds them to a local repository (the current one unless
5420 or URL and adds them to a local repository (the current one unless
5421 -R is specified). By default, this does not update the copy of the
5421 -R is specified). By default, this does not update the copy of the
5422 project in the working directory.
5422 project in the working directory.
5423
5423
5424 When cloning from servers that support it, Mercurial may fetch
5424 When cloning from servers that support it, Mercurial may fetch
5425 pre-generated data. When this is done, hooks operating on incoming
5425 pre-generated data. When this is done, hooks operating on incoming
5426 changesets and changegroups may fire more than once, once for each
5426 changesets and changegroups may fire more than once, once for each
5427 pre-generated bundle and as well as for any additional remaining
5427 pre-generated bundle and as well as for any additional remaining
5428 data. See :hg:`help -e clonebundles` for more.
5428 data. See :hg:`help -e clonebundles` for more.
5429
5429
5430 Use :hg:`incoming` if you want to see what would have been added
5430 Use :hg:`incoming` if you want to see what would have been added
5431 by a pull at the time you issued this command. If you then decide
5431 by a pull at the time you issued this command. If you then decide
5432 to add those changes to the repository, you should use :hg:`pull
5432 to add those changes to the repository, you should use :hg:`pull
5433 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5433 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5434
5434
5435 If SOURCE is omitted, the 'default' path will be used.
5435 If SOURCE is omitted, the 'default' path will be used.
5436 See :hg:`help urls` for more information.
5436 See :hg:`help urls` for more information.
5437
5437
5438 If multiple sources are specified, they will be pulled sequentially as if
5438 If multiple sources are specified, they will be pulled sequentially as if
5439 the command was run multiple time. If --update is specify and the command
5439 the command was run multiple time. If --update is specify and the command
5440 will stop at the first failed --update.
5440 will stop at the first failed --update.
5441
5441
5442 Specifying bookmark as ``.`` is equivalent to specifying the active
5442 Specifying bookmark as ``.`` is equivalent to specifying the active
5443 bookmark's name.
5443 bookmark's name.
5444
5444
5445 .. container:: verbose
5445 .. container:: verbose
5446
5446
5447 One can use the `--remote-hidden` flag to pull changesets
5447 One can use the `--remote-hidden` flag to pull changesets
5448 hidden on the remote. This flag is "best effort", and will only
5448 hidden on the remote. This flag is "best effort", and will only
5449 work if the server supports the feature and is configured to
5449 work if the server supports the feature and is configured to
5450 allow the user to access hidden changesets. This option is
5450 allow the user to access hidden changesets. This option is
5451 experimental and backwards compatibility is not garanteed.
5451 experimental and backwards compatibility is not garanteed.
5452
5452
5453 Returns 0 on success, 1 if an update had unresolved files.
5453 Returns 0 on success, 1 if an update had unresolved files.
5454 """
5454 """
5455
5455
5456 if ui.configbool(b'commands', b'update.requiredest') and opts.get('update'):
5456 if ui.configbool(b'commands', b'update.requiredest') and opts.get('update'):
5457 msg = _(b'update destination required by configuration')
5457 msg = _(b'update destination required by configuration')
5458 hint = _(b'use hg pull followed by hg update DEST')
5458 hint = _(b'use hg pull followed by hg update DEST')
5459 raise error.InputError(msg, hint=hint)
5459 raise error.InputError(msg, hint=hint)
5460
5460
5461 update_conflict = None
5461 update_conflict = None
5462
5462
5463 for path in urlutil.get_pull_paths(repo, ui, sources):
5463 for path in urlutil.get_pull_paths(repo, ui, sources):
5464 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(path.loc))
5464 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(path.loc))
5465 ui.flush()
5465 ui.flush()
5466 other = hg.peer(
5466 other = hg.peer(
5467 repo,
5467 repo,
5468 pycompat.byteskwargs(opts),
5468 pycompat.byteskwargs(opts),
5469 path,
5469 path,
5470 remotehidden=opts['remote_hidden'],
5470 remotehidden=opts['remote_hidden'],
5471 )
5471 )
5472 update_conflict = None
5472 update_conflict = None
5473 try:
5473 try:
5474 branches = (path.branch, opts.get('branch', []))
5474 branches = (path.branch, opts.get('branch', []))
5475 revs, checkout = hg.addbranchrevs(
5475 revs, checkout = hg.addbranchrevs(
5476 repo,
5476 repo,
5477 other,
5477 other,
5478 branches,
5478 branches,
5479 opts.get('rev'),
5479 opts.get('rev'),
5480 remotehidden=opts['remote_hidden'],
5480 remotehidden=opts['remote_hidden'],
5481 )
5481 )
5482
5482
5483 pullopargs = {}
5483 pullopargs = {}
5484
5484
5485 nodes = None
5485 nodes = None
5486 if opts.get('bookmark') or revs:
5486 if opts.get('bookmark') or revs:
5487 # The list of bookmark used here is the same used to actually update
5487 # The list of bookmark used here is the same used to actually update
5488 # the bookmark names, to avoid the race from issue 4689 and we do
5488 # the bookmark names, to avoid the race from issue 4689 and we do
5489 # all lookup and bookmark queries in one go so they see the same
5489 # all lookup and bookmark queries in one go so they see the same
5490 # version of the server state (issue 4700).
5490 # version of the server state (issue 4700).
5491 nodes = []
5491 nodes = []
5492 fnodes = []
5492 fnodes = []
5493 revs = revs or []
5493 revs = revs or []
5494 if revs and not other.capable(b'lookup'):
5494 if revs and not other.capable(b'lookup'):
5495 err = _(
5495 err = _(
5496 b"other repository doesn't support revision lookup, "
5496 b"other repository doesn't support revision lookup, "
5497 b"so a rev cannot be specified."
5497 b"so a rev cannot be specified."
5498 )
5498 )
5499 raise error.Abort(err)
5499 raise error.Abort(err)
5500 with other.commandexecutor() as e:
5500 with other.commandexecutor() as e:
5501 fremotebookmarks = e.callcommand(
5501 fremotebookmarks = e.callcommand(
5502 b'listkeys', {b'namespace': b'bookmarks'}
5502 b'listkeys', {b'namespace': b'bookmarks'}
5503 )
5503 )
5504 for r in revs:
5504 for r in revs:
5505 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5505 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5506 remotebookmarks = fremotebookmarks.result()
5506 remotebookmarks = fremotebookmarks.result()
5507 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5507 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5508 pullopargs[b'remotebookmarks'] = remotebookmarks
5508 pullopargs[b'remotebookmarks'] = remotebookmarks
5509 for b in opts.get('bookmark', []):
5509 for b in opts.get('bookmark', []):
5510 b = repo._bookmarks.expandname(b)
5510 b = repo._bookmarks.expandname(b)
5511 if b not in remotebookmarks:
5511 if b not in remotebookmarks:
5512 raise error.InputError(
5512 raise error.InputError(
5513 _(b'remote bookmark %s not found!') % b
5513 _(b'remote bookmark %s not found!') % b
5514 )
5514 )
5515 nodes.append(remotebookmarks[b])
5515 nodes.append(remotebookmarks[b])
5516 for i, rev in enumerate(revs):
5516 for i, rev in enumerate(revs):
5517 node = fnodes[i].result()
5517 node = fnodes[i].result()
5518 nodes.append(node)
5518 nodes.append(node)
5519 if rev == checkout:
5519 if rev == checkout:
5520 checkout = node
5520 checkout = node
5521
5521
5522 wlock = util.nullcontextmanager()
5522 wlock = util.nullcontextmanager()
5523 if opts.get('update'):
5523 if opts.get('update'):
5524 wlock = repo.wlock()
5524 wlock = repo.wlock()
5525 with wlock:
5525 with wlock:
5526 pullopargs.update(opts.get('opargs', {}))
5526 pullopargs.update(opts.get('opargs', {}))
5527 modheads = exchange.pull(
5527 modheads = exchange.pull(
5528 repo,
5528 repo,
5529 other,
5529 other,
5530 path=path,
5530 path=path,
5531 heads=nodes,
5531 heads=nodes,
5532 force=opts.get('force'),
5532 force=opts.get('force'),
5533 bookmarks=opts.get('bookmark', ()),
5533 bookmarks=opts.get('bookmark', ()),
5534 opargs=pullopargs,
5534 opargs=pullopargs,
5535 confirm=opts.get('confirm'),
5535 confirm=opts.get('confirm'),
5536 ).cgresult
5536 ).cgresult
5537
5537
5538 # brev is a name, which might be a bookmark to be activated at
5538 # brev is a name, which might be a bookmark to be activated at
5539 # the end of the update. In other words, it is an explicit
5539 # the end of the update. In other words, it is an explicit
5540 # destination of the update
5540 # destination of the update
5541 brev = None
5541 brev = None
5542
5542
5543 if checkout:
5543 if checkout:
5544 checkout = repo.unfiltered().changelog.rev(checkout)
5544 checkout = repo.unfiltered().changelog.rev(checkout)
5545
5545
5546 # order below depends on implementation of
5546 # order below depends on implementation of
5547 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5547 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5548 # because 'checkout' is determined without it.
5548 # because 'checkout' is determined without it.
5549 if opts.get('rev'):
5549 if opts.get('rev'):
5550 brev = opts['rev'][0]
5550 brev = opts['rev'][0]
5551 elif opts.get('branch'):
5551 elif opts.get('branch'):
5552 brev = opts['branch'][0]
5552 brev = opts['branch'][0]
5553 else:
5553 else:
5554 brev = path.branch
5554 brev = path.branch
5555
5555
5556 # XXX path: we are losing the `path` object here. Keeping it
5556 # XXX path: we are losing the `path` object here. Keeping it
5557 # would be valuable. For example as a "variant" as we do
5557 # would be valuable. For example as a "variant" as we do
5558 # for pushes.
5558 # for pushes.
5559 repo._subtoppath = path.loc
5559 repo._subtoppath = path.loc
5560 try:
5560 try:
5561 update_conflict = postincoming(
5561 update_conflict = postincoming(
5562 ui, repo, modheads, opts.get('update'), checkout, brev
5562 ui, repo, modheads, opts.get('update'), checkout, brev
5563 )
5563 )
5564 except error.FilteredRepoLookupError as exc:
5564 except error.FilteredRepoLookupError as exc:
5565 msg = _(b'cannot update to target: %s') % exc.args[0]
5565 msg = _(b'cannot update to target: %s') % exc.args[0]
5566 exc.args = (msg,) + exc.args[1:]
5566 exc.args = (msg,) + exc.args[1:]
5567 raise
5567 raise
5568 finally:
5568 finally:
5569 del repo._subtoppath
5569 del repo._subtoppath
5570
5570
5571 finally:
5571 finally:
5572 other.close()
5572 other.close()
5573 # skip the remaining pull source if they are some conflict.
5573 # skip the remaining pull source if they are some conflict.
5574 if update_conflict:
5574 if update_conflict:
5575 break
5575 break
5576 if update_conflict:
5576 if update_conflict:
5577 return 1
5577 return 1
5578 else:
5578 else:
5579 return 0
5579 return 0
5580
5580
5581
5581
5582 @command(
5582 @command(
5583 b'purge|clean',
5583 b'purge|clean',
5584 [
5584 [
5585 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5585 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5586 (b'', b'all', None, _(b'purge ignored files too')),
5586 (b'', b'all', None, _(b'purge ignored files too')),
5587 (b'i', b'ignored', None, _(b'purge only ignored files')),
5587 (b'i', b'ignored', None, _(b'purge only ignored files')),
5588 (b'', b'dirs', None, _(b'purge empty directories')),
5588 (b'', b'dirs', None, _(b'purge empty directories')),
5589 (b'', b'files', None, _(b'purge files')),
5589 (b'', b'files', None, _(b'purge files')),
5590 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5590 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5591 (
5591 (
5592 b'0',
5592 b'0',
5593 b'print0',
5593 b'print0',
5594 None,
5594 None,
5595 _(
5595 _(
5596 b'end filenames with NUL, for use with xargs'
5596 b'end filenames with NUL, for use with xargs'
5597 b' (implies -p/--print)'
5597 b' (implies -p/--print)'
5598 ),
5598 ),
5599 ),
5599 ),
5600 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5600 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5601 ]
5601 ]
5602 + cmdutil.walkopts,
5602 + cmdutil.walkopts,
5603 _(b'hg purge [OPTION]... [DIR]...'),
5603 _(b'hg purge [OPTION]... [DIR]...'),
5604 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5604 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5605 )
5605 )
5606 def purge(ui, repo, *dirs, **opts):
5606 def purge(ui, repo, *dirs, **opts):
5607 """removes files not tracked by Mercurial
5607 """removes files not tracked by Mercurial
5608
5608
5609 Delete files not known to Mercurial. This is useful to test local
5609 Delete files not known to Mercurial. This is useful to test local
5610 and uncommitted changes in an otherwise-clean source tree.
5610 and uncommitted changes in an otherwise-clean source tree.
5611
5611
5612 This means that purge will delete the following by default:
5612 This means that purge will delete the following by default:
5613
5613
5614 - Unknown files: files marked with "?" by :hg:`status`
5614 - Unknown files: files marked with "?" by :hg:`status`
5615 - Empty directories: in fact Mercurial ignores directories unless
5615 - Empty directories: in fact Mercurial ignores directories unless
5616 they contain files under source control management
5616 they contain files under source control management
5617
5617
5618 But it will leave untouched:
5618 But it will leave untouched:
5619
5619
5620 - Modified and unmodified tracked files
5620 - Modified and unmodified tracked files
5621 - Ignored files (unless -i or --all is specified)
5621 - Ignored files (unless -i or --all is specified)
5622 - New files added to the repository (with :hg:`add`)
5622 - New files added to the repository (with :hg:`add`)
5623
5623
5624 The --files and --dirs options can be used to direct purge to delete
5624 The --files and --dirs options can be used to direct purge to delete
5625 only files, only directories, or both. If neither option is given,
5625 only files, only directories, or both. If neither option is given,
5626 both will be deleted.
5626 both will be deleted.
5627
5627
5628 If directories are given on the command line, only files in these
5628 If directories are given on the command line, only files in these
5629 directories are considered.
5629 directories are considered.
5630
5630
5631 Be careful with purge, as you could irreversibly delete some files
5631 Be careful with purge, as you could irreversibly delete some files
5632 you forgot to add to the repository. If you only want to print the
5632 you forgot to add to the repository. If you only want to print the
5633 list of files that this program would delete, use the --print
5633 list of files that this program would delete, use the --print
5634 option.
5634 option.
5635 """
5635 """
5636 cmdutil.check_at_most_one_arg(opts, 'all', 'ignored')
5636 cmdutil.check_at_most_one_arg(opts, 'all', 'ignored')
5637
5637
5638 act = not opts.get('print')
5638 act = not opts.get('print')
5639 eol = b'\n'
5639 eol = b'\n'
5640 if opts.get('print0'):
5640 if opts.get('print0'):
5641 eol = b'\0'
5641 eol = b'\0'
5642 act = False # --print0 implies --print
5642 act = False # --print0 implies --print
5643 if opts.get('all', False):
5643 if opts.get('all', False):
5644 ignored = True
5644 ignored = True
5645 unknown = True
5645 unknown = True
5646 else:
5646 else:
5647 ignored = opts.get('ignored', False)
5647 ignored = opts.get('ignored', False)
5648 unknown = not ignored
5648 unknown = not ignored
5649
5649
5650 removefiles = opts.get('files')
5650 removefiles = opts.get('files')
5651 removedirs = opts.get('dirs')
5651 removedirs = opts.get('dirs')
5652 confirm = opts.get('confirm')
5652 confirm = opts.get('confirm')
5653 if confirm is None:
5653 if confirm is None:
5654 try:
5654 try:
5655 extensions.find(b'purge')
5655 extensions.find(b'purge')
5656 confirm = False
5656 confirm = False
5657 except KeyError:
5657 except KeyError:
5658 confirm = True
5658 confirm = True
5659
5659
5660 if not removefiles and not removedirs:
5660 if not removefiles and not removedirs:
5661 removefiles = True
5661 removefiles = True
5662 removedirs = True
5662 removedirs = True
5663
5663
5664 match = scmutil.match(repo[None], dirs, pycompat.byteskwargs(opts))
5664 match = scmutil.match(repo[None], dirs, pycompat.byteskwargs(opts))
5665
5665
5666 paths = mergemod.purge(
5666 paths = mergemod.purge(
5667 repo,
5667 repo,
5668 match,
5668 match,
5669 unknown=unknown,
5669 unknown=unknown,
5670 ignored=ignored,
5670 ignored=ignored,
5671 removeemptydirs=removedirs,
5671 removeemptydirs=removedirs,
5672 removefiles=removefiles,
5672 removefiles=removefiles,
5673 abortonerror=opts.get('abort_on_err'),
5673 abortonerror=opts.get('abort_on_err'),
5674 noop=not act,
5674 noop=not act,
5675 confirm=confirm,
5675 confirm=confirm,
5676 )
5676 )
5677
5677
5678 for path in paths:
5678 for path in paths:
5679 if not act:
5679 if not act:
5680 ui.write(b'%s%s' % (path, eol))
5680 ui.write(b'%s%s' % (path, eol))
5681
5681
5682
5682
5683 @command(
5683 @command(
5684 b'push',
5684 b'push',
5685 [
5685 [
5686 (b'f', b'force', None, _(b'force push')),
5686 (b'f', b'force', None, _(b'force push')),
5687 (
5687 (
5688 b'r',
5688 b'r',
5689 b'rev',
5689 b'rev',
5690 [],
5690 [],
5691 _(b'a changeset intended to be included in the destination'),
5691 _(b'a changeset intended to be included in the destination'),
5692 _(b'REV'),
5692 _(b'REV'),
5693 ),
5693 ),
5694 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5694 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5695 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5695 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5696 (
5696 (
5697 b'b',
5697 b'b',
5698 b'branch',
5698 b'branch',
5699 [],
5699 [],
5700 _(b'a specific branch you would like to push'),
5700 _(b'a specific branch you would like to push'),
5701 _(b'BRANCH'),
5701 _(b'BRANCH'),
5702 ),
5702 ),
5703 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5703 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5704 (
5704 (
5705 b'',
5705 b'',
5706 b'pushvars',
5706 b'pushvars',
5707 [],
5707 [],
5708 _(b'variables that can be sent to server (ADVANCED)'),
5708 _(b'variables that can be sent to server (ADVANCED)'),
5709 ),
5709 ),
5710 (
5710 (
5711 b'',
5711 b'',
5712 b'publish',
5712 b'publish',
5713 False,
5713 False,
5714 _(b'push the changeset as public (EXPERIMENTAL)'),
5714 _(b'push the changeset as public (EXPERIMENTAL)'),
5715 ),
5715 ),
5716 ]
5716 ]
5717 + remoteopts,
5717 + remoteopts,
5718 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5718 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5719 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5719 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5720 helpbasic=True,
5720 helpbasic=True,
5721 )
5721 )
5722 def push(ui, repo, *dests, **opts):
5722 def push(ui, repo, *dests, **opts):
5723 """push changes to the specified destination
5723 """push changes to the specified destination
5724
5724
5725 Push changesets from the local repository to the specified
5725 Push changesets from the local repository to the specified
5726 destination.
5726 destination.
5727
5727
5728 This operation is symmetrical to pull: it is identical to a pull
5728 This operation is symmetrical to pull: it is identical to a pull
5729 in the destination repository from the current one.
5729 in the destination repository from the current one.
5730
5730
5731 By default, push will not allow creation of new heads at the
5731 By default, push will not allow creation of new heads at the
5732 destination, since multiple heads would make it unclear which head
5732 destination, since multiple heads would make it unclear which head
5733 to use. In this situation, it is recommended to pull and merge
5733 to use. In this situation, it is recommended to pull and merge
5734 before pushing.
5734 before pushing.
5735
5735
5736 Use --new-branch if you want to allow push to create a new named
5736 Use --new-branch if you want to allow push to create a new named
5737 branch that is not present at the destination. This allows you to
5737 branch that is not present at the destination. This allows you to
5738 only create a new branch without forcing other changes.
5738 only create a new branch without forcing other changes.
5739
5739
5740 .. note::
5740 .. note::
5741
5741
5742 Extra care should be taken with the -f/--force option,
5742 Extra care should be taken with the -f/--force option,
5743 which will push all new heads on all branches, an action which will
5743 which will push all new heads on all branches, an action which will
5744 almost always cause confusion for collaborators.
5744 almost always cause confusion for collaborators.
5745
5745
5746 If -r/--rev is used, the specified revision and all its ancestors
5746 If -r/--rev is used, the specified revision and all its ancestors
5747 will be pushed to the remote repository.
5747 will be pushed to the remote repository.
5748
5748
5749 If -B/--bookmark is used, the specified bookmarked revision, its
5749 If -B/--bookmark is used, the specified bookmarked revision, its
5750 ancestors, and the bookmark will be pushed to the remote
5750 ancestors, and the bookmark will be pushed to the remote
5751 repository. Specifying ``.`` is equivalent to specifying the active
5751 repository. Specifying ``.`` is equivalent to specifying the active
5752 bookmark's name. Use the --all-bookmarks option for pushing all
5752 bookmark's name. Use the --all-bookmarks option for pushing all
5753 current bookmarks.
5753 current bookmarks.
5754
5754
5755 Please see :hg:`help urls` for important details about ``ssh://``
5755 Please see :hg:`help urls` for important details about ``ssh://``
5756 URLs. If DESTINATION is omitted, a default path will be used.
5756 URLs. If DESTINATION is omitted, a default path will be used.
5757
5757
5758 When passed multiple destinations, push will process them one after the
5758 When passed multiple destinations, push will process them one after the
5759 other, but stop should an error occur.
5759 other, but stop should an error occur.
5760
5760
5761 .. container:: verbose
5761 .. container:: verbose
5762
5762
5763 The --pushvars option sends strings to the server that become
5763 The --pushvars option sends strings to the server that become
5764 environment variables prepended with ``HG_USERVAR_``. For example,
5764 environment variables prepended with ``HG_USERVAR_``. For example,
5765 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5765 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5766 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5766 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5767
5767
5768 pushvars can provide for user-overridable hooks as well as set debug
5768 pushvars can provide for user-overridable hooks as well as set debug
5769 levels. One example is having a hook that blocks commits containing
5769 levels. One example is having a hook that blocks commits containing
5770 conflict markers, but enables the user to override the hook if the file
5770 conflict markers, but enables the user to override the hook if the file
5771 is using conflict markers for testing purposes or the file format has
5771 is using conflict markers for testing purposes or the file format has
5772 strings that look like conflict markers.
5772 strings that look like conflict markers.
5773
5773
5774 By default, servers will ignore `--pushvars`. To enable it add the
5774 By default, servers will ignore `--pushvars`. To enable it add the
5775 following to your configuration file::
5775 following to your configuration file::
5776
5776
5777 [push]
5777 [push]
5778 pushvars.server = true
5778 pushvars.server = true
5779
5779
5780 Returns 0 if push was successful, 1 if nothing to push.
5780 Returns 0 if push was successful, 1 if nothing to push.
5781 """
5781 """
5782
5782
5783 opts = pycompat.byteskwargs(opts)
5783 opts = pycompat.byteskwargs(opts)
5784
5784
5785 if opts.get(b'all_bookmarks'):
5785 if opts.get(b'all_bookmarks'):
5786 cmdutil.check_incompatible_arguments(
5786 cmdutil.check_incompatible_arguments(
5787 opts,
5787 opts,
5788 b'all_bookmarks',
5788 b'all_bookmarks',
5789 [b'bookmark', b'rev'],
5789 [b'bookmark', b'rev'],
5790 )
5790 )
5791 opts[b'bookmark'] = list(repo._bookmarks)
5791 opts[b'bookmark'] = list(repo._bookmarks)
5792
5792
5793 if opts.get(b'bookmark'):
5793 if opts.get(b'bookmark'):
5794 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5794 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5795 for b in opts[b'bookmark']:
5795 for b in opts[b'bookmark']:
5796 # translate -B options to -r so changesets get pushed
5796 # translate -B options to -r so changesets get pushed
5797 b = repo._bookmarks.expandname(b)
5797 b = repo._bookmarks.expandname(b)
5798 if b in repo._bookmarks:
5798 if b in repo._bookmarks:
5799 opts.setdefault(b'rev', []).append(b)
5799 opts.setdefault(b'rev', []).append(b)
5800 else:
5800 else:
5801 # if we try to push a deleted bookmark, translate it to null
5801 # if we try to push a deleted bookmark, translate it to null
5802 # this lets simultaneous -r, -b options continue working
5802 # this lets simultaneous -r, -b options continue working
5803 opts.setdefault(b'rev', []).append(b"null")
5803 opts.setdefault(b'rev', []).append(b"null")
5804
5804
5805 some_pushed = False
5805 some_pushed = False
5806 result = 0
5806 result = 0
5807 for path in urlutil.get_push_paths(repo, ui, dests):
5807 for path in urlutil.get_push_paths(repo, ui, dests):
5808 dest = path.loc
5808 dest = path.loc
5809 branches = (path.branch, opts.get(b'branch') or [])
5809 branches = (path.branch, opts.get(b'branch') or [])
5810 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5810 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5811 revs, checkout = hg.addbranchrevs(
5811 revs, checkout = hg.addbranchrevs(
5812 repo, repo, branches, opts.get(b'rev')
5812 repo, repo, branches, opts.get(b'rev')
5813 )
5813 )
5814 other = hg.peer(repo, opts, dest)
5814 other = hg.peer(repo, opts, dest)
5815
5815
5816 try:
5816 try:
5817 if revs:
5817 if revs:
5818 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5818 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5819 if not revs:
5819 if not revs:
5820 raise error.InputError(
5820 raise error.InputError(
5821 _(b"specified revisions evaluate to an empty set"),
5821 _(b"specified revisions evaluate to an empty set"),
5822 hint=_(b"use different revision arguments"),
5822 hint=_(b"use different revision arguments"),
5823 )
5823 )
5824 elif path.pushrev:
5824 elif path.pushrev:
5825 # It doesn't make any sense to specify ancestor revisions. So limit
5825 # It doesn't make any sense to specify ancestor revisions. So limit
5826 # to DAG heads to make discovery simpler.
5826 # to DAG heads to make discovery simpler.
5827 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5827 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5828 revs = scmutil.revrange(repo, [expr])
5828 revs = scmutil.revrange(repo, [expr])
5829 revs = [repo[rev].node() for rev in revs]
5829 revs = [repo[rev].node() for rev in revs]
5830 if not revs:
5830 if not revs:
5831 raise error.InputError(
5831 raise error.InputError(
5832 _(
5832 _(
5833 b'default push revset for path evaluates to an empty set'
5833 b'default push revset for path evaluates to an empty set'
5834 )
5834 )
5835 )
5835 )
5836 elif ui.configbool(b'commands', b'push.require-revs'):
5836 elif ui.configbool(b'commands', b'push.require-revs'):
5837 raise error.InputError(
5837 raise error.InputError(
5838 _(b'no revisions specified to push'),
5838 _(b'no revisions specified to push'),
5839 hint=_(b'did you mean "hg push -r ."?'),
5839 hint=_(b'did you mean "hg push -r ."?'),
5840 )
5840 )
5841
5841
5842 repo._subtoppath = dest
5842 repo._subtoppath = dest
5843 try:
5843 try:
5844 # push subrepos depth-first for coherent ordering
5844 # push subrepos depth-first for coherent ordering
5845 c = repo[b'.']
5845 c = repo[b'.']
5846 subs = c.substate # only repos that are committed
5846 subs = c.substate # only repos that are committed
5847 for s in sorted(subs):
5847 for s in sorted(subs):
5848 sub_result = c.sub(s).push(opts)
5848 sub_result = c.sub(s).push(opts)
5849 if sub_result == 0:
5849 if sub_result == 0:
5850 return 1
5850 return 1
5851 finally:
5851 finally:
5852 del repo._subtoppath
5852 del repo._subtoppath
5853
5853
5854 opargs = dict(
5854 opargs = dict(
5855 opts.get(b'opargs', {})
5855 opts.get(b'opargs', {})
5856 ) # copy opargs since we may mutate it
5856 ) # copy opargs since we may mutate it
5857 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5857 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5858
5858
5859 pushop = exchange.push(
5859 pushop = exchange.push(
5860 repo,
5860 repo,
5861 other,
5861 other,
5862 opts.get(b'force'),
5862 opts.get(b'force'),
5863 revs=revs,
5863 revs=revs,
5864 newbranch=opts.get(b'new_branch'),
5864 newbranch=opts.get(b'new_branch'),
5865 bookmarks=opts.get(b'bookmark', ()),
5865 bookmarks=opts.get(b'bookmark', ()),
5866 publish=opts.get(b'publish'),
5866 publish=opts.get(b'publish'),
5867 opargs=opargs,
5867 opargs=opargs,
5868 )
5868 )
5869
5869
5870 if pushop.cgresult == 0:
5870 if pushop.cgresult == 0:
5871 result = 1
5871 result = 1
5872 elif pushop.cgresult is not None:
5872 elif pushop.cgresult is not None:
5873 some_pushed = True
5873 some_pushed = True
5874
5874
5875 if pushop.bkresult is not None:
5875 if pushop.bkresult is not None:
5876 if pushop.bkresult == 2:
5876 if pushop.bkresult == 2:
5877 result = 2
5877 result = 2
5878 elif not result and pushop.bkresult:
5878 elif not result and pushop.bkresult:
5879 result = 2
5879 result = 2
5880
5880
5881 if result:
5881 if result:
5882 break
5882 break
5883
5883
5884 finally:
5884 finally:
5885 other.close()
5885 other.close()
5886 if result == 0 and not some_pushed:
5886 if result == 0 and not some_pushed:
5887 result = 1
5887 result = 1
5888 return result
5888 return result
5889
5889
5890
5890
5891 @command(
5891 @command(
5892 b'recover',
5892 b'recover',
5893 [
5893 [
5894 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5894 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5895 ],
5895 ],
5896 helpcategory=command.CATEGORY_MAINTENANCE,
5896 helpcategory=command.CATEGORY_MAINTENANCE,
5897 )
5897 )
5898 def recover(ui, repo, **opts):
5898 def recover(ui, repo, **opts):
5899 """roll back an interrupted transaction
5899 """roll back an interrupted transaction
5900
5900
5901 Recover from an interrupted commit or pull.
5901 Recover from an interrupted commit or pull.
5902
5902
5903 This command tries to fix the repository status after an
5903 This command tries to fix the repository status after an
5904 interrupted operation. It should only be necessary when Mercurial
5904 interrupted operation. It should only be necessary when Mercurial
5905 suggests it.
5905 suggests it.
5906
5906
5907 Returns 0 if successful, 1 if nothing to recover or verify fails.
5907 Returns 0 if successful, 1 if nothing to recover or verify fails.
5908 """
5908 """
5909 ret = repo.recover()
5909 ret = repo.recover()
5910 if ret:
5910 if ret:
5911 if opts['verify']:
5911 if opts['verify']:
5912 return hg.verify(repo)
5912 return hg.verify(repo)
5913 else:
5913 else:
5914 msg = _(
5914 msg = _(
5915 b"(verify step skipped, run `hg verify` to check your "
5915 b"(verify step skipped, run `hg verify` to check your "
5916 b"repository content)\n"
5916 b"repository content)\n"
5917 )
5917 )
5918 ui.warn(msg)
5918 ui.warn(msg)
5919 return 0
5919 return 0
5920 return 1
5920 return 1
5921
5921
5922
5922
5923 @command(
5923 @command(
5924 b'remove|rm',
5924 b'remove|rm',
5925 [
5925 [
5926 (b'A', b'after', None, _(b'record delete for missing files')),
5926 (b'A', b'after', None, _(b'record delete for missing files')),
5927 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5927 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5928 ]
5928 ]
5929 + subrepoopts
5929 + subrepoopts
5930 + walkopts
5930 + walkopts
5931 + dryrunopts,
5931 + dryrunopts,
5932 _(b'[OPTION]... FILE...'),
5932 _(b'[OPTION]... FILE...'),
5933 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5933 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5934 helpbasic=True,
5934 helpbasic=True,
5935 inferrepo=True,
5935 inferrepo=True,
5936 )
5936 )
5937 def remove(ui, repo, *pats, **opts):
5937 def remove(ui, repo, *pats, **opts):
5938 """remove the specified files on the next commit
5938 """remove the specified files on the next commit
5939
5939
5940 Schedule the indicated files for removal from the current branch.
5940 Schedule the indicated files for removal from the current branch.
5941
5941
5942 This command schedules the files to be removed at the next commit.
5942 This command schedules the files to be removed at the next commit.
5943 To undo a remove before that, see :hg:`revert`. To undo added
5943 To undo a remove before that, see :hg:`revert`. To undo added
5944 files, see :hg:`forget`.
5944 files, see :hg:`forget`.
5945
5945
5946 .. container:: verbose
5946 .. container:: verbose
5947
5947
5948 -A/--after can be used to remove only files that have already
5948 -A/--after can be used to remove only files that have already
5949 been deleted, -f/--force can be used to force deletion, and -Af
5949 been deleted, -f/--force can be used to force deletion, and -Af
5950 can be used to remove files from the next revision without
5950 can be used to remove files from the next revision without
5951 deleting them from the working directory.
5951 deleting them from the working directory.
5952
5952
5953 The following table details the behavior of remove for different
5953 The following table details the behavior of remove for different
5954 file states (columns) and option combinations (rows). The file
5954 file states (columns) and option combinations (rows). The file
5955 states are Added [A], Clean [C], Modified [M] and Missing [!]
5955 states are Added [A], Clean [C], Modified [M] and Missing [!]
5956 (as reported by :hg:`status`). The actions are Warn, Remove
5956 (as reported by :hg:`status`). The actions are Warn, Remove
5957 (from branch) and Delete (from disk):
5957 (from branch) and Delete (from disk):
5958
5958
5959 ========= == == == ==
5959 ========= == == == ==
5960 opt/state A C M !
5960 opt/state A C M !
5961 ========= == == == ==
5961 ========= == == == ==
5962 none W RD W R
5962 none W RD W R
5963 -f R RD RD R
5963 -f R RD RD R
5964 -A W W W R
5964 -A W W W R
5965 -Af R R R R
5965 -Af R R R R
5966 ========= == == == ==
5966 ========= == == == ==
5967
5967
5968 .. note::
5968 .. note::
5969
5969
5970 :hg:`remove` never deletes files in Added [A] state from the
5970 :hg:`remove` never deletes files in Added [A] state from the
5971 working directory, not even if ``--force`` is specified.
5971 working directory, not even if ``--force`` is specified.
5972
5972
5973 Returns 0 on success, 1 if any warnings encountered.
5973 Returns 0 on success, 1 if any warnings encountered.
5974 """
5974 """
5975
5975
5976 after, force = opts.get('after'), opts.get('force')
5976 after, force = opts.get('after'), opts.get('force')
5977 dryrun = opts.get('dry_run')
5977 dryrun = opts.get('dry_run')
5978 if not pats and not after:
5978 if not pats and not after:
5979 raise error.InputError(_(b'no files specified'))
5979 raise error.InputError(_(b'no files specified'))
5980
5980
5981 with repo.wlock(), repo.dirstate.changing_files(repo):
5981 with repo.wlock(), repo.dirstate.changing_files(repo):
5982 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
5982 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
5983 subrepos = opts.get('subrepos')
5983 subrepos = opts.get('subrepos')
5984 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5984 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5985 return cmdutil.remove(
5985 return cmdutil.remove(
5986 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5986 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5987 )
5987 )
5988
5988
5989
5989
5990 @command(
5990 @command(
5991 b'rename|move|mv',
5991 b'rename|move|mv',
5992 [
5992 [
5993 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5993 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5994 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5994 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5995 (
5995 (
5996 b'',
5996 b'',
5997 b'at-rev',
5997 b'at-rev',
5998 b'',
5998 b'',
5999 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5999 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
6000 _(b'REV'),
6000 _(b'REV'),
6001 ),
6001 ),
6002 (
6002 (
6003 b'f',
6003 b'f',
6004 b'force',
6004 b'force',
6005 None,
6005 None,
6006 _(b'forcibly move over an existing managed file'),
6006 _(b'forcibly move over an existing managed file'),
6007 ),
6007 ),
6008 ]
6008 ]
6009 + walkopts
6009 + walkopts
6010 + dryrunopts,
6010 + dryrunopts,
6011 _(b'[OPTION]... SOURCE... DEST'),
6011 _(b'[OPTION]... SOURCE... DEST'),
6012 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6012 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6013 )
6013 )
6014 def rename(ui, repo, *pats, **opts):
6014 def rename(ui, repo, *pats, **opts):
6015 """rename files; equivalent of copy + remove
6015 """rename files; equivalent of copy + remove
6016
6016
6017 Mark dest as copies of sources; mark sources for deletion. If dest
6017 Mark dest as copies of sources; mark sources for deletion. If dest
6018 is a directory, copies are put in that directory. If dest is a
6018 is a directory, copies are put in that directory. If dest is a
6019 file, there can only be one source.
6019 file, there can only be one source.
6020
6020
6021 By default, this command copies the contents of files as they
6021 By default, this command copies the contents of files as they
6022 exist in the working directory. If invoked with -A/--after, the
6022 exist in the working directory. If invoked with -A/--after, the
6023 operation is recorded, but no copying is performed.
6023 operation is recorded, but no copying is performed.
6024
6024
6025 To undo marking a destination file as renamed, use --forget. With that
6025 To undo marking a destination file as renamed, use --forget. With that
6026 option, all given (positional) arguments are unmarked as renames. The
6026 option, all given (positional) arguments are unmarked as renames. The
6027 destination file(s) will be left in place (still tracked). The source
6027 destination file(s) will be left in place (still tracked). The source
6028 file(s) will not be restored. Note that :hg:`rename --forget` behaves
6028 file(s) will not be restored. Note that :hg:`rename --forget` behaves
6029 the same way as :hg:`copy --forget`.
6029 the same way as :hg:`copy --forget`.
6030
6030
6031 This command takes effect with the next commit by default.
6031 This command takes effect with the next commit by default.
6032
6032
6033 Returns 0 on success, 1 if errors are encountered.
6033 Returns 0 on success, 1 if errors are encountered.
6034 """
6034 """
6035 context = lambda repo: repo.dirstate.changing_files(repo)
6035 context = lambda repo: repo.dirstate.changing_files(repo)
6036 rev = opts.get('at_rev')
6036 rev = opts.get('at_rev')
6037
6037
6038 if rev:
6038 if rev:
6039 ctx = logcmdutil.revsingle(repo, rev)
6039 ctx = logcmdutil.revsingle(repo, rev)
6040 if ctx.rev() is not None:
6040 if ctx.rev() is not None:
6041
6041
6042 def context(repo):
6042 def context(repo):
6043 return util.nullcontextmanager()
6043 return util.nullcontextmanager()
6044
6044
6045 opts['at_rev'] = ctx.rev()
6045 opts['at_rev'] = ctx.rev()
6046 with repo.wlock(), context(repo):
6046 with repo.wlock(), context(repo):
6047 return cmdutil.copy(
6047 return cmdutil.copy(
6048 ui, repo, pats, pycompat.byteskwargs(opts), rename=True
6048 ui, repo, pats, pycompat.byteskwargs(opts), rename=True
6049 )
6049 )
6050
6050
6051
6051
6052 @command(
6052 @command(
6053 b'resolve',
6053 b'resolve',
6054 [
6054 [
6055 (b'a', b'all', None, _(b'select all unresolved files')),
6055 (b'a', b'all', None, _(b'select all unresolved files')),
6056 (b'l', b'list', None, _(b'list state of files needing merge')),
6056 (b'l', b'list', None, _(b'list state of files needing merge')),
6057 (b'm', b'mark', None, _(b'mark files as resolved')),
6057 (b'm', b'mark', None, _(b'mark files as resolved')),
6058 (b'u', b'unmark', None, _(b'mark files as unresolved')),
6058 (b'u', b'unmark', None, _(b'mark files as unresolved')),
6059 (b'n', b'no-status', None, _(b'hide status prefix')),
6059 (b'n', b'no-status', None, _(b'hide status prefix')),
6060 (b'', b're-merge', None, _(b're-merge files')),
6060 (b'', b're-merge', None, _(b're-merge files')),
6061 ]
6061 ]
6062 + mergetoolopts
6062 + mergetoolopts
6063 + walkopts
6063 + walkopts
6064 + formatteropts,
6064 + formatteropts,
6065 _(b'[OPTION]... [FILE]...'),
6065 _(b'[OPTION]... [FILE]...'),
6066 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6066 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6067 inferrepo=True,
6067 inferrepo=True,
6068 )
6068 )
6069 def resolve(ui, repo, *pats, **opts):
6069 def resolve(ui, repo, *pats, **opts):
6070 """redo merges or set/view the merge status of files
6070 """redo merges or set/view the merge status of files
6071
6071
6072 Merges with unresolved conflicts are often the result of
6072 Merges with unresolved conflicts are often the result of
6073 non-interactive merging using the ``internal:merge`` configuration
6073 non-interactive merging using the ``internal:merge`` configuration
6074 setting, or a command-line merge tool like ``diff3``. The resolve
6074 setting, or a command-line merge tool like ``diff3``. The resolve
6075 command is used to manage the files involved in a merge, after
6075 command is used to manage the files involved in a merge, after
6076 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6076 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6077 working directory must have two parents). See :hg:`help
6077 working directory must have two parents). See :hg:`help
6078 merge-tools` for information on configuring merge tools.
6078 merge-tools` for information on configuring merge tools.
6079
6079
6080 The resolve command can be used in the following ways:
6080 The resolve command can be used in the following ways:
6081
6081
6082 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6082 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
6083 the specified files, discarding any previous merge attempts. Re-merging
6083 the specified files, discarding any previous merge attempts. Re-merging
6084 is not performed for files already marked as resolved. Use ``--all/-a``
6084 is not performed for files already marked as resolved. Use ``--all/-a``
6085 to select all unresolved files. ``--tool`` can be used to specify
6085 to select all unresolved files. ``--tool`` can be used to specify
6086 the merge tool used for the given files. It overrides the HGMERGE
6086 the merge tool used for the given files. It overrides the HGMERGE
6087 environment variable and your configuration files. Previous file
6087 environment variable and your configuration files. Previous file
6088 contents are saved with a ``.orig`` suffix.
6088 contents are saved with a ``.orig`` suffix.
6089
6089
6090 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6090 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6091 (e.g. after having manually fixed-up the files). The default is
6091 (e.g. after having manually fixed-up the files). The default is
6092 to mark all unresolved files.
6092 to mark all unresolved files.
6093
6093
6094 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6094 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6095 default is to mark all resolved files.
6095 default is to mark all resolved files.
6096
6096
6097 - :hg:`resolve -l`: list files which had or still have conflicts.
6097 - :hg:`resolve -l`: list files which had or still have conflicts.
6098 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6098 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6099 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6099 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6100 the list. See :hg:`help filesets` for details.
6100 the list. See :hg:`help filesets` for details.
6101
6101
6102 .. note::
6102 .. note::
6103
6103
6104 Mercurial will not let you commit files with unresolved merge
6104 Mercurial will not let you commit files with unresolved merge
6105 conflicts. You must use :hg:`resolve -m ...` before you can
6105 conflicts. You must use :hg:`resolve -m ...` before you can
6106 commit after a conflicting merge.
6106 commit after a conflicting merge.
6107
6107
6108 .. container:: verbose
6108 .. container:: verbose
6109
6109
6110 Template:
6110 Template:
6111
6111
6112 The following keywords are supported in addition to the common template
6112 The following keywords are supported in addition to the common template
6113 keywords and functions. See also :hg:`help templates`.
6113 keywords and functions. See also :hg:`help templates`.
6114
6114
6115 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6115 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6116 :path: String. Repository-absolute path of the file.
6116 :path: String. Repository-absolute path of the file.
6117
6117
6118 Returns 0 on success, 1 if any files fail a resolve attempt.
6118 Returns 0 on success, 1 if any files fail a resolve attempt.
6119 """
6119 """
6120
6120
6121 opts = pycompat.byteskwargs(opts)
6121 opts = pycompat.byteskwargs(opts)
6122 confirm = ui.configbool(b'commands', b'resolve.confirm')
6122 confirm = ui.configbool(b'commands', b'resolve.confirm')
6123 flaglist = b'all mark unmark list no_status re_merge'.split()
6123 flaglist = b'all mark unmark list no_status re_merge'.split()
6124 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6124 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6125
6125
6126 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6126 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6127 if actioncount > 1:
6127 if actioncount > 1:
6128 raise error.InputError(_(b"too many actions specified"))
6128 raise error.InputError(_(b"too many actions specified"))
6129 elif actioncount == 0 and ui.configbool(
6129 elif actioncount == 0 and ui.configbool(
6130 b'commands', b'resolve.explicit-re-merge'
6130 b'commands', b'resolve.explicit-re-merge'
6131 ):
6131 ):
6132 hint = _(b'use --mark, --unmark, --list or --re-merge')
6132 hint = _(b'use --mark, --unmark, --list or --re-merge')
6133 raise error.InputError(_(b'no action specified'), hint=hint)
6133 raise error.InputError(_(b'no action specified'), hint=hint)
6134 if pats and all:
6134 if pats and all:
6135 raise error.InputError(_(b"can't specify --all and patterns"))
6135 raise error.InputError(_(b"can't specify --all and patterns"))
6136 if not (all or pats or show or mark or unmark):
6136 if not (all or pats or show or mark or unmark):
6137 raise error.InputError(
6137 raise error.InputError(
6138 _(b'no files or directories specified'),
6138 _(b'no files or directories specified'),
6139 hint=b'use --all to re-merge all unresolved files',
6139 hint=b'use --all to re-merge all unresolved files',
6140 )
6140 )
6141
6141
6142 if confirm:
6142 if confirm:
6143 if all:
6143 if all:
6144 if ui.promptchoice(
6144 if ui.promptchoice(
6145 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6145 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6146 ):
6146 ):
6147 raise error.CanceledError(_(b'user quit'))
6147 raise error.CanceledError(_(b'user quit'))
6148 if mark and not pats:
6148 if mark and not pats:
6149 if ui.promptchoice(
6149 if ui.promptchoice(
6150 _(
6150 _(
6151 b'mark all unresolved files as resolved (yn)?'
6151 b'mark all unresolved files as resolved (yn)?'
6152 b'$$ &Yes $$ &No'
6152 b'$$ &Yes $$ &No'
6153 )
6153 )
6154 ):
6154 ):
6155 raise error.CanceledError(_(b'user quit'))
6155 raise error.CanceledError(_(b'user quit'))
6156 if unmark and not pats:
6156 if unmark and not pats:
6157 if ui.promptchoice(
6157 if ui.promptchoice(
6158 _(
6158 _(
6159 b'mark all resolved files as unresolved (yn)?'
6159 b'mark all resolved files as unresolved (yn)?'
6160 b'$$ &Yes $$ &No'
6160 b'$$ &Yes $$ &No'
6161 )
6161 )
6162 ):
6162 ):
6163 raise error.CanceledError(_(b'user quit'))
6163 raise error.CanceledError(_(b'user quit'))
6164
6164
6165 uipathfn = scmutil.getuipathfn(repo)
6165 uipathfn = scmutil.getuipathfn(repo)
6166
6166
6167 if show:
6167 if show:
6168 ui.pager(b'resolve')
6168 ui.pager(b'resolve')
6169 fm = ui.formatter(b'resolve', opts)
6169 fm = ui.formatter(b'resolve', opts)
6170 ms = mergestatemod.mergestate.read(repo)
6170 ms = mergestatemod.mergestate.read(repo)
6171 wctx = repo[None]
6171 wctx = repo[None]
6172 m = scmutil.match(wctx, pats, opts)
6172 m = scmutil.match(wctx, pats, opts)
6173
6173
6174 # Labels and keys based on merge state. Unresolved path conflicts show
6174 # Labels and keys based on merge state. Unresolved path conflicts show
6175 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6175 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6176 # resolved conflicts.
6176 # resolved conflicts.
6177 mergestateinfo = {
6177 mergestateinfo = {
6178 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6178 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6179 b'resolve.unresolved',
6179 b'resolve.unresolved',
6180 b'U',
6180 b'U',
6181 ),
6181 ),
6182 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6182 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6183 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6183 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6184 b'resolve.unresolved',
6184 b'resolve.unresolved',
6185 b'P',
6185 b'P',
6186 ),
6186 ),
6187 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6187 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6188 b'resolve.resolved',
6188 b'resolve.resolved',
6189 b'R',
6189 b'R',
6190 ),
6190 ),
6191 }
6191 }
6192
6192
6193 for f in ms:
6193 for f in ms:
6194 if not m(f):
6194 if not m(f):
6195 continue
6195 continue
6196
6196
6197 label, key = mergestateinfo[ms[f]]
6197 label, key = mergestateinfo[ms[f]]
6198 fm.startitem()
6198 fm.startitem()
6199 fm.context(ctx=wctx)
6199 fm.context(ctx=wctx)
6200 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6200 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6201 fm.data(path=f)
6201 fm.data(path=f)
6202 fm.plain(b'%s\n' % uipathfn(f), label=label)
6202 fm.plain(b'%s\n' % uipathfn(f), label=label)
6203 fm.end()
6203 fm.end()
6204 return 0
6204 return 0
6205
6205
6206 with repo.wlock():
6206 with repo.wlock():
6207 ms = mergestatemod.mergestate.read(repo)
6207 ms = mergestatemod.mergestate.read(repo)
6208
6208
6209 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6209 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6210 raise error.StateError(
6210 raise error.StateError(
6211 _(b'resolve command not applicable when not merging')
6211 _(b'resolve command not applicable when not merging')
6212 )
6212 )
6213
6213
6214 wctx = repo[None]
6214 wctx = repo[None]
6215 m = scmutil.match(wctx, pats, opts)
6215 m = scmutil.match(wctx, pats, opts)
6216 ret = 0
6216 ret = 0
6217 didwork = False
6217 didwork = False
6218
6218
6219 hasconflictmarkers = []
6219 hasconflictmarkers = []
6220 if mark:
6220 if mark:
6221 markcheck = ui.config(b'commands', b'resolve.mark-check')
6221 markcheck = ui.config(b'commands', b'resolve.mark-check')
6222 if markcheck not in [b'warn', b'abort']:
6222 if markcheck not in [b'warn', b'abort']:
6223 # Treat all invalid / unrecognized values as 'none'.
6223 # Treat all invalid / unrecognized values as 'none'.
6224 markcheck = False
6224 markcheck = False
6225 for f in ms:
6225 for f in ms:
6226 if not m(f):
6226 if not m(f):
6227 continue
6227 continue
6228
6228
6229 didwork = True
6229 didwork = True
6230
6230
6231 # path conflicts must be resolved manually
6231 # path conflicts must be resolved manually
6232 if ms[f] in (
6232 if ms[f] in (
6233 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6233 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6234 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6234 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6235 ):
6235 ):
6236 if mark:
6236 if mark:
6237 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6237 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6238 elif unmark:
6238 elif unmark:
6239 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6239 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6240 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6240 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6241 ui.warn(
6241 ui.warn(
6242 _(b'%s: path conflict must be resolved manually\n')
6242 _(b'%s: path conflict must be resolved manually\n')
6243 % uipathfn(f)
6243 % uipathfn(f)
6244 )
6244 )
6245 continue
6245 continue
6246
6246
6247 if mark:
6247 if mark:
6248 if markcheck:
6248 if markcheck:
6249 fdata = repo.wvfs.tryread(f)
6249 fdata = repo.wvfs.tryread(f)
6250 if (
6250 if (
6251 filemerge.hasconflictmarkers(fdata)
6251 filemerge.hasconflictmarkers(fdata)
6252 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6252 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6253 ):
6253 ):
6254 hasconflictmarkers.append(f)
6254 hasconflictmarkers.append(f)
6255 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6255 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6256 elif unmark:
6256 elif unmark:
6257 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6257 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6258 else:
6258 else:
6259 # backup pre-resolve (merge uses .orig for its own purposes)
6259 # backup pre-resolve (merge uses .orig for its own purposes)
6260 a = repo.wjoin(f)
6260 a = repo.wjoin(f)
6261 try:
6261 try:
6262 util.copyfile(a, a + b".resolve")
6262 util.copyfile(a, a + b".resolve")
6263 except FileNotFoundError:
6263 except FileNotFoundError:
6264 pass
6264 pass
6265
6265
6266 try:
6266 try:
6267 # preresolve file
6267 # preresolve file
6268 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6268 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6269 with ui.configoverride(overrides, b'resolve'):
6269 with ui.configoverride(overrides, b'resolve'):
6270 r = ms.resolve(f, wctx)
6270 r = ms.resolve(f, wctx)
6271 if r:
6271 if r:
6272 ret = 1
6272 ret = 1
6273 finally:
6273 finally:
6274 ms.commit()
6274 ms.commit()
6275
6275
6276 # replace filemerge's .orig file with our resolve file
6276 # replace filemerge's .orig file with our resolve file
6277 try:
6277 try:
6278 util.rename(
6278 util.rename(
6279 a + b".resolve", scmutil.backuppath(ui, repo, f)
6279 a + b".resolve", scmutil.backuppath(ui, repo, f)
6280 )
6280 )
6281 except FileNotFoundError:
6281 except FileNotFoundError:
6282 pass
6282 pass
6283
6283
6284 if hasconflictmarkers:
6284 if hasconflictmarkers:
6285 ui.warn(
6285 ui.warn(
6286 _(
6286 _(
6287 b'warning: the following files still have conflict '
6287 b'warning: the following files still have conflict '
6288 b'markers:\n'
6288 b'markers:\n'
6289 )
6289 )
6290 + b''.join(
6290 + b''.join(
6291 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6291 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6292 )
6292 )
6293 )
6293 )
6294 if markcheck == b'abort' and not all and not pats:
6294 if markcheck == b'abort' and not all and not pats:
6295 raise error.StateError(
6295 raise error.StateError(
6296 _(b'conflict markers detected'),
6296 _(b'conflict markers detected'),
6297 hint=_(b'use --all to mark anyway'),
6297 hint=_(b'use --all to mark anyway'),
6298 )
6298 )
6299
6299
6300 ms.commit()
6300 ms.commit()
6301 branchmerge = repo.dirstate.p2() != repo.nullid
6301 branchmerge = repo.dirstate.p2() != repo.nullid
6302 # resolve is not doing a parent change here, however, `record updates`
6302 # resolve is not doing a parent change here, however, `record updates`
6303 # will call some dirstate API that at intended for parent changes call.
6303 # will call some dirstate API that at intended for parent changes call.
6304 # Ideally we would not need this and could implement a lighter version
6304 # Ideally we would not need this and could implement a lighter version
6305 # of the recordupdateslogic that will not have to deal with the part
6305 # of the recordupdateslogic that will not have to deal with the part
6306 # related to parent changes. However this would requires that:
6306 # related to parent changes. However this would requires that:
6307 # - we are sure we passed around enough information at update/merge
6307 # - we are sure we passed around enough information at update/merge
6308 # time to no longer needs it at `hg resolve time`
6308 # time to no longer needs it at `hg resolve time`
6309 # - we are sure we store that information well enough to be able to reuse it
6309 # - we are sure we store that information well enough to be able to reuse it
6310 # - we are the necessary logic to reuse it right.
6310 # - we are the necessary logic to reuse it right.
6311 #
6311 #
6312 # All this should eventually happens, but in the mean time, we use this
6312 # All this should eventually happens, but in the mean time, we use this
6313 # context manager slightly out of the context it should be.
6313 # context manager slightly out of the context it should be.
6314 with repo.dirstate.changing_parents(repo):
6314 with repo.dirstate.changing_parents(repo):
6315 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6315 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6316
6316
6317 if not didwork and pats:
6317 if not didwork and pats:
6318 hint = None
6318 hint = None
6319 if not any([p for p in pats if p.find(b':') >= 0]):
6319 if not any([p for p in pats if p.find(b':') >= 0]):
6320 pats = [b'path:%s' % p for p in pats]
6320 pats = [b'path:%s' % p for p in pats]
6321 m = scmutil.match(wctx, pats, opts)
6321 m = scmutil.match(wctx, pats, opts)
6322 for f in ms:
6322 for f in ms:
6323 if not m(f):
6323 if not m(f):
6324 continue
6324 continue
6325
6325
6326 def flag(o):
6326 def flag(o):
6327 if o == b're_merge':
6327 if o == b're_merge':
6328 return b'--re-merge '
6328 return b'--re-merge '
6329 return b'-%s ' % o[0:1]
6329 return b'-%s ' % o[0:1]
6330
6330
6331 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6331 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6332 hint = _(b"(try: hg resolve %s%s)\n") % (
6332 hint = _(b"(try: hg resolve %s%s)\n") % (
6333 flags,
6333 flags,
6334 b' '.join(pats),
6334 b' '.join(pats),
6335 )
6335 )
6336 break
6336 break
6337 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6337 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6338 if hint:
6338 if hint:
6339 ui.warn(hint)
6339 ui.warn(hint)
6340
6340
6341 unresolvedf = ms.unresolvedcount()
6341 unresolvedf = ms.unresolvedcount()
6342 if not unresolvedf:
6342 if not unresolvedf:
6343 ui.status(_(b'(no more unresolved files)\n'))
6343 ui.status(_(b'(no more unresolved files)\n'))
6344 cmdutil.checkafterresolved(repo)
6344 cmdutil.checkafterresolved(repo)
6345
6345
6346 return ret
6346 return ret
6347
6347
6348
6348
6349 @command(
6349 @command(
6350 b'revert',
6350 b'revert',
6351 [
6351 [
6352 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6352 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6353 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6353 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6354 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6354 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6355 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6355 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6356 (b'i', b'interactive', None, _(b'interactively select the changes')),
6356 (b'i', b'interactive', None, _(b'interactively select the changes')),
6357 ]
6357 ]
6358 + walkopts
6358 + walkopts
6359 + dryrunopts,
6359 + dryrunopts,
6360 _(b'[OPTION]... [-r REV] [NAME]...'),
6360 _(b'[OPTION]... [-r REV] [NAME]...'),
6361 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6361 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6362 )
6362 )
6363 def revert(ui, repo, *pats, **opts):
6363 def revert(ui, repo, *pats, **opts):
6364 """restore files to their checkout state
6364 """restore files to their checkout state
6365
6365
6366 .. note::
6366 .. note::
6367
6367
6368 To check out earlier revisions, you should use :hg:`update REV`.
6368 To check out earlier revisions, you should use :hg:`update REV`.
6369 To cancel an uncommitted merge (and lose your changes),
6369 To cancel an uncommitted merge (and lose your changes),
6370 use :hg:`merge --abort`.
6370 use :hg:`merge --abort`.
6371
6371
6372 With no revision specified, revert the specified files or directories
6372 With no revision specified, revert the specified files or directories
6373 to the contents they had in the parent of the working directory.
6373 to the contents they had in the parent of the working directory.
6374 This restores the contents of files to an unmodified
6374 This restores the contents of files to an unmodified
6375 state and unschedules adds, removes, copies, and renames. If the
6375 state and unschedules adds, removes, copies, and renames. If the
6376 working directory has two parents, you must explicitly specify a
6376 working directory has two parents, you must explicitly specify a
6377 revision.
6377 revision.
6378
6378
6379 Using the -r/--rev or -d/--date options, revert the given files or
6379 Using the -r/--rev or -d/--date options, revert the given files or
6380 directories to their states as of a specific revision. Because
6380 directories to their states as of a specific revision. Because
6381 revert does not change the working directory parents, this will
6381 revert does not change the working directory parents, this will
6382 cause these files to appear modified. This can be helpful to "back
6382 cause these files to appear modified. This can be helpful to "back
6383 out" some or all of an earlier change. See :hg:`backout` for a
6383 out" some or all of an earlier change. See :hg:`backout` for a
6384 related method.
6384 related method.
6385
6385
6386 Modified files are saved with a .orig suffix before reverting.
6386 Modified files are saved with a .orig suffix before reverting.
6387 To disable these backups, use --no-backup. It is possible to store
6387 To disable these backups, use --no-backup. It is possible to store
6388 the backup files in a custom directory relative to the root of the
6388 the backup files in a custom directory relative to the root of the
6389 repository by setting the ``ui.origbackuppath`` configuration
6389 repository by setting the ``ui.origbackuppath`` configuration
6390 option.
6390 option.
6391
6391
6392 See :hg:`help dates` for a list of formats valid for -d/--date.
6392 See :hg:`help dates` for a list of formats valid for -d/--date.
6393
6393
6394 See :hg:`help backout` for a way to reverse the effect of an
6394 See :hg:`help backout` for a way to reverse the effect of an
6395 earlier changeset.
6395 earlier changeset.
6396
6396
6397 Returns 0 on success.
6397 Returns 0 on success.
6398 """
6398 """
6399
6399
6400 if opts.get("date"):
6400 if opts.get("date"):
6401 cmdutil.check_incompatible_arguments(opts, 'date', ['rev'])
6401 cmdutil.check_incompatible_arguments(opts, 'date', ['rev'])
6402 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6402 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6403
6403
6404 parent, p2 = repo.dirstate.parents()
6404 parent, p2 = repo.dirstate.parents()
6405 if not opts.get('rev') and p2 != repo.nullid:
6405 if not opts.get('rev') and p2 != repo.nullid:
6406 # revert after merge is a trap for new users (issue2915)
6406 # revert after merge is a trap for new users (issue2915)
6407 raise error.InputError(
6407 raise error.InputError(
6408 _(b'uncommitted merge with no revision specified'),
6408 _(b'uncommitted merge with no revision specified'),
6409 hint=_(b"use 'hg update' or see 'hg help revert'"),
6409 hint=_(b"use 'hg update' or see 'hg help revert'"),
6410 )
6410 )
6411
6411
6412 rev = opts.get('rev')
6412 rev = opts.get('rev')
6413 if rev:
6413 if rev:
6414 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6414 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6415 ctx = logcmdutil.revsingle(repo, rev)
6415 ctx = logcmdutil.revsingle(repo, rev)
6416
6416
6417 if not (
6417 if not (
6418 pats
6418 pats
6419 or opts.get('include')
6419 or opts.get('include')
6420 or opts.get('exclude')
6420 or opts.get('exclude')
6421 or opts.get('all')
6421 or opts.get('all')
6422 or opts.get('interactive')
6422 or opts.get('interactive')
6423 ):
6423 ):
6424 msg = _(b"no files or directories specified")
6424 msg = _(b"no files or directories specified")
6425 if p2 != repo.nullid:
6425 if p2 != repo.nullid:
6426 hint = _(
6426 hint = _(
6427 b"uncommitted merge, use --all to discard all changes,"
6427 b"uncommitted merge, use --all to discard all changes,"
6428 b" or 'hg update -C .' to abort the merge"
6428 b" or 'hg update -C .' to abort the merge"
6429 )
6429 )
6430 raise error.InputError(msg, hint=hint)
6430 raise error.InputError(msg, hint=hint)
6431 dirty = any(repo.status())
6431 dirty = any(repo.status())
6432 node = ctx.node()
6432 node = ctx.node()
6433 if node != parent:
6433 if node != parent:
6434 if dirty:
6434 if dirty:
6435 hint = (
6435 hint = (
6436 _(
6436 _(
6437 b"uncommitted changes, use --all to discard all"
6437 b"uncommitted changes, use --all to discard all"
6438 b" changes, or 'hg update %d' to update"
6438 b" changes, or 'hg update %d' to update"
6439 )
6439 )
6440 % ctx.rev()
6440 % ctx.rev()
6441 )
6441 )
6442 else:
6442 else:
6443 hint = (
6443 hint = (
6444 _(
6444 _(
6445 b"use --all to revert all files,"
6445 b"use --all to revert all files,"
6446 b" or 'hg update %d' to update"
6446 b" or 'hg update %d' to update"
6447 )
6447 )
6448 % ctx.rev()
6448 % ctx.rev()
6449 )
6449 )
6450 elif dirty:
6450 elif dirty:
6451 hint = _(b"uncommitted changes, use --all to discard all changes")
6451 hint = _(b"uncommitted changes, use --all to discard all changes")
6452 else:
6452 else:
6453 hint = _(b"use --all to revert all files")
6453 hint = _(b"use --all to revert all files")
6454 raise error.InputError(msg, hint=hint)
6454 raise error.InputError(msg, hint=hint)
6455
6455
6456 return cmdutil.revert(ui, repo, ctx, *pats, **opts)
6456 return cmdutil.revert(ui, repo, ctx, *pats, **opts)
6457
6457
6458
6458
6459 @command(
6459 @command(
6460 b'rollback',
6460 b'rollback',
6461 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6461 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6462 helpcategory=command.CATEGORY_MAINTENANCE,
6462 helpcategory=command.CATEGORY_MAINTENANCE,
6463 )
6463 )
6464 def rollback(ui, repo, **opts):
6464 def rollback(ui, repo, **opts):
6465 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6465 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6466
6466
6467 Please use :hg:`commit --amend` instead of rollback to correct
6467 Please use :hg:`commit --amend` instead of rollback to correct
6468 mistakes in the last commit.
6468 mistakes in the last commit.
6469
6469
6470 This command should be used with care. There is only one level of
6470 This command should be used with care. There is only one level of
6471 rollback, and there is no way to undo a rollback. It will also
6471 rollback, and there is no way to undo a rollback. It will also
6472 restore the dirstate at the time of the last transaction, losing
6472 restore the dirstate at the time of the last transaction, losing
6473 any dirstate changes since that time. This command does not alter
6473 any dirstate changes since that time. This command does not alter
6474 the working directory.
6474 the working directory.
6475
6475
6476 Transactions are used to encapsulate the effects of all commands
6476 Transactions are used to encapsulate the effects of all commands
6477 that create new changesets or propagate existing changesets into a
6477 that create new changesets or propagate existing changesets into a
6478 repository.
6478 repository.
6479
6479
6480 .. container:: verbose
6480 .. container:: verbose
6481
6481
6482 For example, the following commands are transactional, and their
6482 For example, the following commands are transactional, and their
6483 effects can be rolled back:
6483 effects can be rolled back:
6484
6484
6485 - commit
6485 - commit
6486 - import
6486 - import
6487 - pull
6487 - pull
6488 - push (with this repository as the destination)
6488 - push (with this repository as the destination)
6489 - unbundle
6489 - unbundle
6490
6490
6491 To avoid permanent data loss, rollback will refuse to rollback a
6491 To avoid permanent data loss, rollback will refuse to rollback a
6492 commit transaction if it isn't checked out. Use --force to
6492 commit transaction if it isn't checked out. Use --force to
6493 override this protection.
6493 override this protection.
6494
6494
6495 The rollback command can be entirely disabled by setting the
6495 The rollback command can be entirely disabled by setting the
6496 ``ui.rollback`` configuration setting to false. If you're here
6496 ``ui.rollback`` configuration setting to false. If you're here
6497 because you want to use rollback and it's disabled, you can
6497 because you want to use rollback and it's disabled, you can
6498 re-enable the command by setting ``ui.rollback`` to true.
6498 re-enable the command by setting ``ui.rollback`` to true.
6499
6499
6500 This command is not intended for use on public repositories. Once
6500 This command is not intended for use on public repositories. Once
6501 changes are visible for pull by other users, rolling a transaction
6501 changes are visible for pull by other users, rolling a transaction
6502 back locally is ineffective (someone else may already have pulled
6502 back locally is ineffective (someone else may already have pulled
6503 the changes). Furthermore, a race is possible with readers of the
6503 the changes). Furthermore, a race is possible with readers of the
6504 repository; for example an in-progress pull from the repository
6504 repository; for example an in-progress pull from the repository
6505 may fail if a rollback is performed.
6505 may fail if a rollback is performed.
6506
6506
6507 Returns 0 on success, 1 if no rollback data is available.
6507 Returns 0 on success, 1 if no rollback data is available.
6508 """
6508 """
6509 if not ui.configbool(b'ui', b'rollback'):
6509 if not ui.configbool(b'ui', b'rollback'):
6510 raise error.Abort(
6510 raise error.Abort(
6511 _(b'rollback is disabled because it is unsafe'),
6511 _(b'rollback is disabled because it is unsafe'),
6512 hint=b'see `hg help -v rollback` for information',
6512 hint=b'see `hg help -v rollback` for information',
6513 )
6513 )
6514 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6514 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6515
6515
6516
6516
6517 @command(
6517 @command(
6518 b'root',
6518 b'root',
6519 [] + formatteropts,
6519 [] + formatteropts,
6520 intents={INTENT_READONLY},
6520 intents={INTENT_READONLY},
6521 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6521 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6522 )
6522 )
6523 def root(ui, repo, **opts):
6523 def root(ui, repo, **opts):
6524 """print the root (top) of the current working directory
6524 """print the root (top) of the current working directory
6525
6525
6526 Print the root directory of the current repository.
6526 Print the root directory of the current repository.
6527
6527
6528 .. container:: verbose
6528 .. container:: verbose
6529
6529
6530 Template:
6530 Template:
6531
6531
6532 The following keywords are supported in addition to the common template
6532 The following keywords are supported in addition to the common template
6533 keywords and functions. See also :hg:`help templates`.
6533 keywords and functions. See also :hg:`help templates`.
6534
6534
6535 :hgpath: String. Path to the .hg directory.
6535 :hgpath: String. Path to the .hg directory.
6536 :storepath: String. Path to the directory holding versioned data.
6536 :storepath: String. Path to the directory holding versioned data.
6537
6537
6538 Returns 0 on success.
6538 Returns 0 on success.
6539 """
6539 """
6540 opts = pycompat.byteskwargs(opts)
6540 opts = pycompat.byteskwargs(opts)
6541 with ui.formatter(b'root', opts) as fm:
6541 with ui.formatter(b'root', opts) as fm:
6542 fm.startitem()
6542 fm.startitem()
6543 fm.write(b'reporoot', b'%s\n', repo.root)
6543 fm.write(b'reporoot', b'%s\n', repo.root)
6544 fm.data(hgpath=repo.path, storepath=repo.spath)
6544 fm.data(hgpath=repo.path, storepath=repo.spath)
6545
6545
6546
6546
6547 @command(
6547 @command(
6548 b'serve',
6548 b'serve',
6549 [
6549 [
6550 (
6550 (
6551 b'A',
6551 b'A',
6552 b'accesslog',
6552 b'accesslog',
6553 b'',
6553 b'',
6554 _(b'name of access log file to write to'),
6554 _(b'name of access log file to write to'),
6555 _(b'FILE'),
6555 _(b'FILE'),
6556 ),
6556 ),
6557 (b'd', b'daemon', None, _(b'run server in background')),
6557 (b'd', b'daemon', None, _(b'run server in background')),
6558 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6558 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6559 (
6559 (
6560 b'E',
6560 b'E',
6561 b'errorlog',
6561 b'errorlog',
6562 b'',
6562 b'',
6563 _(b'name of error log file to write to'),
6563 _(b'name of error log file to write to'),
6564 _(b'FILE'),
6564 _(b'FILE'),
6565 ),
6565 ),
6566 # use string type, then we can check if something was passed
6566 # use string type, then we can check if something was passed
6567 (
6567 (
6568 b'p',
6568 b'p',
6569 b'port',
6569 b'port',
6570 b'',
6570 b'',
6571 _(b'port to listen on (default: 8000)'),
6571 _(b'port to listen on (default: 8000)'),
6572 _(b'PORT'),
6572 _(b'PORT'),
6573 ),
6573 ),
6574 (
6574 (
6575 b'a',
6575 b'a',
6576 b'address',
6576 b'address',
6577 b'',
6577 b'',
6578 _(b'address to listen on (default: all interfaces)'),
6578 _(b'address to listen on (default: all interfaces)'),
6579 _(b'ADDR'),
6579 _(b'ADDR'),
6580 ),
6580 ),
6581 (
6581 (
6582 b'',
6582 b'',
6583 b'prefix',
6583 b'prefix',
6584 b'',
6584 b'',
6585 _(b'prefix path to serve from (default: server root)'),
6585 _(b'prefix path to serve from (default: server root)'),
6586 _(b'PREFIX'),
6586 _(b'PREFIX'),
6587 ),
6587 ),
6588 (
6588 (
6589 b'n',
6589 b'n',
6590 b'name',
6590 b'name',
6591 b'',
6591 b'',
6592 _(b'name to show in web pages (default: working directory)'),
6592 _(b'name to show in web pages (default: working directory)'),
6593 _(b'NAME'),
6593 _(b'NAME'),
6594 ),
6594 ),
6595 (
6595 (
6596 b'',
6596 b'',
6597 b'web-conf',
6597 b'web-conf',
6598 b'',
6598 b'',
6599 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6599 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6600 _(b'FILE'),
6600 _(b'FILE'),
6601 ),
6601 ),
6602 (
6602 (
6603 b'',
6603 b'',
6604 b'webdir-conf',
6604 b'webdir-conf',
6605 b'',
6605 b'',
6606 _(b'name of the hgweb config file (DEPRECATED)'),
6606 _(b'name of the hgweb config file (DEPRECATED)'),
6607 _(b'FILE'),
6607 _(b'FILE'),
6608 ),
6608 ),
6609 (
6609 (
6610 b'',
6610 b'',
6611 b'pid-file',
6611 b'pid-file',
6612 b'',
6612 b'',
6613 _(b'name of file to write process ID to'),
6613 _(b'name of file to write process ID to'),
6614 _(b'FILE'),
6614 _(b'FILE'),
6615 ),
6615 ),
6616 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6616 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6617 (
6617 (
6618 b'',
6618 b'',
6619 b'cmdserver',
6619 b'cmdserver',
6620 b'',
6620 b'',
6621 _(b'for remote clients (ADVANCED)'),
6621 _(b'for remote clients (ADVANCED)'),
6622 _(b'MODE'),
6622 _(b'MODE'),
6623 ),
6623 ),
6624 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6624 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6625 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6625 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6626 (b'6', b'ipv6', None, _(b'use IPv6 instead of IPv4')),
6626 (b'6', b'ipv6', None, _(b'use IPv6 instead of IPv4')),
6627 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6627 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6628 (b'', b'print-url', None, _(b'start and print only the URL')),
6628 (b'', b'print-url', None, _(b'start and print only the URL')),
6629 ]
6629 ]
6630 + subrepoopts,
6630 + subrepoopts,
6631 _(b'[OPTION]...'),
6631 _(b'[OPTION]...'),
6632 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6632 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6633 helpbasic=True,
6633 helpbasic=True,
6634 optionalrepo=True,
6634 optionalrepo=True,
6635 )
6635 )
6636 def serve(ui, repo, **opts):
6636 def serve(ui, repo, **opts):
6637 """start stand-alone webserver
6637 """start stand-alone webserver
6638
6638
6639 Start a local HTTP repository browser and pull server. You can use
6639 Start a local HTTP repository browser and pull server. You can use
6640 this for ad-hoc sharing and browsing of repositories. It is
6640 this for ad-hoc sharing and browsing of repositories. It is
6641 recommended to use a real web server to serve a repository for
6641 recommended to use a real web server to serve a repository for
6642 longer periods of time.
6642 longer periods of time.
6643
6643
6644 Please note that the server does not implement access control.
6644 Please note that the server does not implement access control.
6645 This means that, by default, anybody can read from the server and
6645 This means that, by default, anybody can read from the server and
6646 nobody can write to it by default. Set the ``web.allow-push``
6646 nobody can write to it by default. Set the ``web.allow-push``
6647 option to ``*`` to allow everybody to push to the server. You
6647 option to ``*`` to allow everybody to push to the server. You
6648 should use a real web server if you need to authenticate users.
6648 should use a real web server if you need to authenticate users.
6649
6649
6650 By default, the server logs accesses to stdout and errors to
6650 By default, the server logs accesses to stdout and errors to
6651 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6651 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6652 files.
6652 files.
6653
6653
6654 To have the server choose a free port number to listen on, specify
6654 To have the server choose a free port number to listen on, specify
6655 a port number of 0; in this case, the server will print the port
6655 a port number of 0; in this case, the server will print the port
6656 number it uses.
6656 number it uses.
6657
6657
6658 Returns 0 on success.
6658 Returns 0 on success.
6659 """
6659 """
6660
6660
6661 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6661 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6662 opts = pycompat.byteskwargs(opts)
6662 opts = pycompat.byteskwargs(opts)
6663 if opts[b"print_url"] and ui.verbose:
6663 if opts[b"print_url"] and ui.verbose:
6664 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6664 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6665
6665
6666 if opts[b"stdio"]:
6666 if opts[b"stdio"]:
6667 if repo is None:
6667 if repo is None:
6668 raise error.RepoError(
6668 raise error.RepoError(
6669 _(b"there is no Mercurial repository here (.hg not found)")
6669 _(b"there is no Mercurial repository here (.hg not found)")
6670 )
6670 )
6671 accesshidden = False
6671 accesshidden = False
6672 if repo.filtername is None:
6672 if repo.filtername is None:
6673 allow = ui.configlist(
6673 allow = ui.configlist(
6674 b'experimental', b'server.allow-hidden-access'
6674 b'experimental', b'server.allow-hidden-access'
6675 )
6675 )
6676 user = procutil.getuser()
6676 user = procutil.getuser()
6677 if allow and scmutil.ismember(ui, user, allow):
6677 if allow and scmutil.ismember(ui, user, allow):
6678 accesshidden = True
6678 accesshidden = True
6679 else:
6679 else:
6680 msg = (
6680 msg = (
6681 _(
6681 _(
6682 b'ignoring request to access hidden changeset by '
6682 b'ignoring request to access hidden changeset by '
6683 b'unauthorized user: %s\n'
6683 b'unauthorized user: %s\n'
6684 )
6684 )
6685 % user
6685 % user
6686 )
6686 )
6687 ui.warn(msg)
6687 ui.warn(msg)
6688
6688
6689 s = wireprotoserver.sshserver(ui, repo, accesshidden=accesshidden)
6689 s = wireprotoserver.sshserver(ui, repo, accesshidden=accesshidden)
6690 s.serve_forever()
6690 s.serve_forever()
6691 return
6691 return
6692
6692
6693 service = server.createservice(ui, repo, opts)
6693 service = server.createservice(ui, repo, opts)
6694 return server.runservice(opts, initfn=service.init, runfn=service.run)
6694 return server.runservice(opts, initfn=service.init, runfn=service.run)
6695
6695
6696
6696
6697 @command(
6697 @command(
6698 b'shelve',
6698 b'shelve',
6699 [
6699 [
6700 (
6700 (
6701 b'A',
6701 b'A',
6702 b'addremove',
6702 b'addremove',
6703 None,
6703 None,
6704 _(b'mark new/missing files as added/removed before shelving'),
6704 _(b'mark new/missing files as added/removed before shelving'),
6705 ),
6705 ),
6706 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6706 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6707 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6707 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6708 (
6708 (
6709 b'',
6709 b'',
6710 b'date',
6710 b'date',
6711 b'',
6711 b'',
6712 _(b'shelve with the specified commit date'),
6712 _(b'shelve with the specified commit date'),
6713 _(b'DATE'),
6713 _(b'DATE'),
6714 ),
6714 ),
6715 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6715 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6716 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6716 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6717 (
6717 (
6718 b'k',
6718 b'k',
6719 b'keep',
6719 b'keep',
6720 False,
6720 False,
6721 _(b'shelve, but keep changes in the working directory'),
6721 _(b'shelve, but keep changes in the working directory'),
6722 ),
6722 ),
6723 (b'l', b'list', None, _(b'list current shelves')),
6723 (b'l', b'list', None, _(b'list current shelves')),
6724 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6724 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6725 (
6725 (
6726 b'n',
6726 b'n',
6727 b'name',
6727 b'name',
6728 b'',
6728 b'',
6729 _(b'use the given name for the shelved commit'),
6729 _(b'use the given name for the shelved commit'),
6730 _(b'NAME'),
6730 _(b'NAME'),
6731 ),
6731 ),
6732 (
6732 (
6733 b'p',
6733 b'p',
6734 b'patch',
6734 b'patch',
6735 None,
6735 None,
6736 _(
6736 _(
6737 b'output patches for changes (provide the names of the shelved '
6737 b'output patches for changes (provide the names of the shelved '
6738 b'changes as positional arguments)'
6738 b'changes as positional arguments)'
6739 ),
6739 ),
6740 ),
6740 ),
6741 (b'i', b'interactive', None, _(b'interactive mode')),
6741 (b'i', b'interactive', None, _(b'interactive mode')),
6742 (
6742 (
6743 b'',
6743 b'',
6744 b'stat',
6744 b'stat',
6745 None,
6745 None,
6746 _(
6746 _(
6747 b'output diffstat-style summary of changes (provide the names of '
6747 b'output diffstat-style summary of changes (provide the names of '
6748 b'the shelved changes as positional arguments)'
6748 b'the shelved changes as positional arguments)'
6749 ),
6749 ),
6750 ),
6750 ),
6751 ]
6751 ]
6752 + cmdutil.walkopts,
6752 + cmdutil.walkopts,
6753 _(b'hg shelve [OPTION]... [FILE]...'),
6753 _(b'hg shelve [OPTION]... [FILE]...'),
6754 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6754 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6755 )
6755 )
6756 def shelve(ui, repo, *pats, **opts):
6756 def shelve(ui, repo, *pats, **opts):
6757 """save and set aside changes from the working directory
6757 """save and set aside changes from the working directory
6758
6758
6759 Shelving takes files that "hg status" reports as not clean, saves
6759 Shelving takes files that "hg status" reports as not clean, saves
6760 the modifications to a bundle (a shelved change), and reverts the
6760 the modifications to a bundle (a shelved change), and reverts the
6761 files so that their state in the working directory becomes clean.
6761 files so that their state in the working directory becomes clean.
6762
6762
6763 To restore these changes to the working directory, using "hg
6763 To restore these changes to the working directory, using "hg
6764 unshelve"; this will work even if you switch to a different
6764 unshelve"; this will work even if you switch to a different
6765 commit.
6765 commit.
6766
6766
6767 When no files are specified, "hg shelve" saves all not-clean
6767 When no files are specified, "hg shelve" saves all not-clean
6768 files. If specific files or directories are named, only changes to
6768 files. If specific files or directories are named, only changes to
6769 those files are shelved.
6769 those files are shelved.
6770
6770
6771 In bare shelve (when no files are specified, without interactive,
6771 In bare shelve (when no files are specified, without interactive,
6772 include and exclude option), shelving remembers information if the
6772 include and exclude option), shelving remembers information if the
6773 working directory was on newly created branch, in other words working
6773 working directory was on newly created branch, in other words working
6774 directory was on different branch than its first parent. In this
6774 directory was on different branch than its first parent. In this
6775 situation unshelving restores branch information to the working directory.
6775 situation unshelving restores branch information to the working directory.
6776
6776
6777 Each shelved change has a name that makes it easier to find later.
6777 Each shelved change has a name that makes it easier to find later.
6778 The name of a shelved change defaults to being based on the active
6778 The name of a shelved change defaults to being based on the active
6779 bookmark, or if there is no active bookmark, the current named
6779 bookmark, or if there is no active bookmark, the current named
6780 branch. To specify a different name, use ``--name``.
6780 branch. To specify a different name, use ``--name``.
6781
6781
6782 To see a list of existing shelved changes, use the ``--list``
6782 To see a list of existing shelved changes, use the ``--list``
6783 option. For each shelved change, this will print its name, age,
6783 option. For each shelved change, this will print its name, age,
6784 and description; use ``--patch`` or ``--stat`` for more details.
6784 and description; use ``--patch`` or ``--stat`` for more details.
6785
6785
6786 To delete specific shelved changes, use ``--delete``. To delete
6786 To delete specific shelved changes, use ``--delete``. To delete
6787 all shelved changes, use ``--cleanup``.
6787 all shelved changes, use ``--cleanup``.
6788 """
6788 """
6789 opts = pycompat.byteskwargs(opts)
6789 opts = pycompat.byteskwargs(opts)
6790 allowables = [
6790 allowables = [
6791 (b'addremove', {b'create'}), # 'create' is pseudo action
6791 (b'addremove', {b'create'}), # 'create' is pseudo action
6792 (b'unknown', {b'create'}),
6792 (b'unknown', {b'create'}),
6793 (b'cleanup', {b'cleanup'}),
6793 (b'cleanup', {b'cleanup'}),
6794 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6794 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6795 (b'delete', {b'delete'}),
6795 (b'delete', {b'delete'}),
6796 (b'edit', {b'create'}),
6796 (b'edit', {b'create'}),
6797 (b'keep', {b'create'}),
6797 (b'keep', {b'create'}),
6798 (b'list', {b'list'}),
6798 (b'list', {b'list'}),
6799 (b'message', {b'create'}),
6799 (b'message', {b'create'}),
6800 (b'name', {b'create'}),
6800 (b'name', {b'create'}),
6801 (b'patch', {b'patch', b'list'}),
6801 (b'patch', {b'patch', b'list'}),
6802 (b'stat', {b'stat', b'list'}),
6802 (b'stat', {b'stat', b'list'}),
6803 ]
6803 ]
6804
6804
6805 def checkopt(opt):
6805 def checkopt(opt):
6806 if opts.get(opt):
6806 if opts.get(opt):
6807 for i, allowable in allowables:
6807 for i, allowable in allowables:
6808 if opts[i] and opt not in allowable:
6808 if opts[i] and opt not in allowable:
6809 raise error.InputError(
6809 raise error.InputError(
6810 _(
6810 _(
6811 b"options '--%s' and '--%s' may not be "
6811 b"options '--%s' and '--%s' may not be "
6812 b"used together"
6812 b"used together"
6813 )
6813 )
6814 % (opt, i)
6814 % (opt, i)
6815 )
6815 )
6816 return True
6816 return True
6817
6817
6818 if checkopt(b'cleanup'):
6818 if checkopt(b'cleanup'):
6819 if pats:
6819 if pats:
6820 raise error.InputError(
6820 raise error.InputError(
6821 _(b"cannot specify names when using '--cleanup'")
6821 _(b"cannot specify names when using '--cleanup'")
6822 )
6822 )
6823 return shelvemod.cleanupcmd(ui, repo)
6823 return shelvemod.cleanupcmd(ui, repo)
6824 elif checkopt(b'delete'):
6824 elif checkopt(b'delete'):
6825 return shelvemod.deletecmd(ui, repo, pats)
6825 return shelvemod.deletecmd(ui, repo, pats)
6826 elif checkopt(b'list'):
6826 elif checkopt(b'list'):
6827 return shelvemod.listcmd(ui, repo, pats, opts)
6827 return shelvemod.listcmd(ui, repo, pats, opts)
6828 elif checkopt(b'patch') or checkopt(b'stat'):
6828 elif checkopt(b'patch') or checkopt(b'stat'):
6829 return shelvemod.patchcmds(ui, repo, pats, opts)
6829 return shelvemod.patchcmds(ui, repo, pats, opts)
6830 else:
6830 else:
6831 return shelvemod.createcmd(ui, repo, pats, opts)
6831 return shelvemod.createcmd(ui, repo, pats, opts)
6832
6832
6833
6833
6834 _NOTTERSE = b'nothing'
6834 _NOTTERSE = b'nothing'
6835
6835
6836
6836
6837 @command(
6837 @command(
6838 b'status|st',
6838 b'status|st',
6839 [
6839 [
6840 (b'A', b'all', None, _(b'show status of all files')),
6840 (b'A', b'all', None, _(b'show status of all files')),
6841 (b'm', b'modified', None, _(b'show only modified files')),
6841 (b'm', b'modified', None, _(b'show only modified files')),
6842 (b'a', b'added', None, _(b'show only added files')),
6842 (b'a', b'added', None, _(b'show only added files')),
6843 (b'r', b'removed', None, _(b'show only removed files')),
6843 (b'r', b'removed', None, _(b'show only removed files')),
6844 (b'd', b'deleted', None, _(b'show only missing files')),
6844 (b'd', b'deleted', None, _(b'show only missing files')),
6845 (b'c', b'clean', None, _(b'show only files without changes')),
6845 (b'c', b'clean', None, _(b'show only files without changes')),
6846 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6846 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6847 (b'i', b'ignored', None, _(b'show only ignored files')),
6847 (b'i', b'ignored', None, _(b'show only ignored files')),
6848 (b'n', b'no-status', None, _(b'hide status prefix')),
6848 (b'n', b'no-status', None, _(b'hide status prefix')),
6849 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6849 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6850 (
6850 (
6851 b'C',
6851 b'C',
6852 b'copies',
6852 b'copies',
6853 None,
6853 None,
6854 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6854 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6855 ),
6855 ),
6856 (
6856 (
6857 b'0',
6857 b'0',
6858 b'print0',
6858 b'print0',
6859 None,
6859 None,
6860 _(b'end filenames with NUL, for use with xargs'),
6860 _(b'end filenames with NUL, for use with xargs'),
6861 ),
6861 ),
6862 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6862 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6863 (
6863 (
6864 b'',
6864 b'',
6865 b'change',
6865 b'change',
6866 b'',
6866 b'',
6867 _(b'list the changed files of a revision'),
6867 _(b'list the changed files of a revision'),
6868 _(b'REV'),
6868 _(b'REV'),
6869 ),
6869 ),
6870 ]
6870 ]
6871 + walkopts
6871 + walkopts
6872 + subrepoopts
6872 + subrepoopts
6873 + formatteropts,
6873 + formatteropts,
6874 _(b'[OPTION]... [FILE]...'),
6874 _(b'[OPTION]... [FILE]...'),
6875 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6875 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6876 helpbasic=True,
6876 helpbasic=True,
6877 inferrepo=True,
6877 inferrepo=True,
6878 intents={INTENT_READONLY},
6878 intents={INTENT_READONLY},
6879 )
6879 )
6880 def status(ui, repo, *pats, **opts):
6880 def status(ui, repo, *pats, **opts):
6881 """show changed files in the working directory
6881 """show changed files in the working directory
6882
6882
6883 Show status of files in the repository. If names are given, only
6883 Show status of files in the repository. If names are given, only
6884 files that match are shown. Files that are clean or ignored or
6884 files that match are shown. Files that are clean or ignored or
6885 the source of a copy/move operation, are not listed unless
6885 the source of a copy/move operation, are not listed unless
6886 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6886 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6887 Unless options described with "show only ..." are given, the
6887 Unless options described with "show only ..." are given, the
6888 options -mardu are used.
6888 options -mardu are used.
6889
6889
6890 Option -q/--quiet hides untracked (unknown and ignored) files
6890 Option -q/--quiet hides untracked (unknown and ignored) files
6891 unless explicitly requested with -u/--unknown or -i/--ignored.
6891 unless explicitly requested with -u/--unknown or -i/--ignored.
6892
6892
6893 .. note::
6893 .. note::
6894
6894
6895 :hg:`status` may appear to disagree with diff if permissions have
6895 :hg:`status` may appear to disagree with diff if permissions have
6896 changed or a merge has occurred. The standard diff format does
6896 changed or a merge has occurred. The standard diff format does
6897 not report permission changes and diff only reports changes
6897 not report permission changes and diff only reports changes
6898 relative to one merge parent.
6898 relative to one merge parent.
6899
6899
6900 If one revision is given, it is used as the base revision.
6900 If one revision is given, it is used as the base revision.
6901 If two revisions are given, the differences between them are
6901 If two revisions are given, the differences between them are
6902 shown. The --change option can also be used as a shortcut to list
6902 shown. The --change option can also be used as a shortcut to list
6903 the changed files of a revision from its first parent.
6903 the changed files of a revision from its first parent.
6904
6904
6905 The codes used to show the status of files are::
6905 The codes used to show the status of files are::
6906
6906
6907 M = modified
6907 M = modified
6908 A = added
6908 A = added
6909 R = removed
6909 R = removed
6910 C = clean
6910 C = clean
6911 ! = missing (deleted by non-hg command, but still tracked)
6911 ! = missing (deleted by non-hg command, but still tracked)
6912 ? = not tracked
6912 ? = not tracked
6913 I = ignored
6913 I = ignored
6914 = origin of the previous file (with --copies)
6914 = origin of the previous file (with --copies)
6915
6915
6916 .. container:: verbose
6916 .. container:: verbose
6917
6917
6918 The -t/--terse option abbreviates the output by showing only the directory
6918 The -t/--terse option abbreviates the output by showing only the directory
6919 name if all the files in it share the same status. The option takes an
6919 name if all the files in it share the same status. The option takes an
6920 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6920 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6921 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6921 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6922 for 'ignored' and 'c' for clean.
6922 for 'ignored' and 'c' for clean.
6923
6923
6924 It abbreviates only those statuses which are passed. Note that clean and
6924 It abbreviates only those statuses which are passed. Note that clean and
6925 ignored files are not displayed with '--terse ic' unless the -c/--clean
6925 ignored files are not displayed with '--terse ic' unless the -c/--clean
6926 and -i/--ignored options are also used.
6926 and -i/--ignored options are also used.
6927
6927
6928 The -v/--verbose option shows information when the repository is in an
6928 The -v/--verbose option shows information when the repository is in an
6929 unfinished merge, shelve, rebase state etc. You can have this behavior
6929 unfinished merge, shelve, rebase state etc. You can have this behavior
6930 turned on by default by enabling the ``commands.status.verbose`` option.
6930 turned on by default by enabling the ``commands.status.verbose`` option.
6931
6931
6932 You can skip displaying some of these states by setting
6932 You can skip displaying some of these states by setting
6933 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6933 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6934 'histedit', 'merge', 'rebase', or 'unshelve'.
6934 'histedit', 'merge', 'rebase', or 'unshelve'.
6935
6935
6936 Template:
6936 Template:
6937
6937
6938 The following keywords are supported in addition to the common template
6938 The following keywords are supported in addition to the common template
6939 keywords and functions. See also :hg:`help templates`.
6939 keywords and functions. See also :hg:`help templates`.
6940
6940
6941 :path: String. Repository-absolute path of the file.
6941 :path: String. Repository-absolute path of the file.
6942 :source: String. Repository-absolute path of the file originated from.
6942 :source: String. Repository-absolute path of the file originated from.
6943 Available if ``--copies`` is specified.
6943 Available if ``--copies`` is specified.
6944 :status: String. Character denoting file's status.
6944 :status: String. Character denoting file's status.
6945
6945
6946 Examples:
6946 Examples:
6947
6947
6948 - show changes in the working directory relative to a
6948 - show changes in the working directory relative to a
6949 changeset::
6949 changeset::
6950
6950
6951 hg status --rev 9353
6951 hg status --rev 9353
6952
6952
6953 - show changes in the working directory relative to the
6953 - show changes in the working directory relative to the
6954 current directory (see :hg:`help patterns` for more information)::
6954 current directory (see :hg:`help patterns` for more information)::
6955
6955
6956 hg status re:
6956 hg status re:
6957
6957
6958 - show all changes including copies in an existing changeset::
6958 - show all changes including copies in an existing changeset::
6959
6959
6960 hg status --copies --change 9353
6960 hg status --copies --change 9353
6961
6961
6962 - get a NUL separated list of added files, suitable for xargs::
6962 - get a NUL separated list of added files, suitable for xargs::
6963
6963
6964 hg status -an0
6964 hg status -an0
6965
6965
6966 - show more information about the repository status, abbreviating
6966 - show more information about the repository status, abbreviating
6967 added, removed, modified, deleted, and untracked paths::
6967 added, removed, modified, deleted, and untracked paths::
6968
6968
6969 hg status -v -t mardu
6969 hg status -v -t mardu
6970
6970
6971 Returns 0 on success.
6971 Returns 0 on success.
6972
6972
6973 """
6973 """
6974
6974
6975 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6975 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6976 opts = pycompat.byteskwargs(opts)
6976 opts = pycompat.byteskwargs(opts)
6977 revs = opts.get(b'rev', [])
6977 revs = opts.get(b'rev', [])
6978 change = opts.get(b'change', b'')
6978 change = opts.get(b'change', b'')
6979 terse = opts.get(b'terse', _NOTTERSE)
6979 terse = opts.get(b'terse', _NOTTERSE)
6980 if terse is _NOTTERSE:
6980 if terse is _NOTTERSE:
6981 if revs:
6981 if revs:
6982 terse = b''
6982 terse = b''
6983 else:
6983 else:
6984 terse = ui.config(b'commands', b'status.terse')
6984 terse = ui.config(b'commands', b'status.terse')
6985
6985
6986 if revs and terse:
6986 if revs and terse:
6987 msg = _(b'cannot use --terse with --rev')
6987 msg = _(b'cannot use --terse with --rev')
6988 raise error.InputError(msg)
6988 raise error.InputError(msg)
6989 elif change:
6989 elif change:
6990 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6990 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6991 ctx2 = logcmdutil.revsingle(repo, change, None)
6991 ctx2 = logcmdutil.revsingle(repo, change, None)
6992 ctx1 = ctx2.p1()
6992 ctx1 = ctx2.p1()
6993 else:
6993 else:
6994 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6994 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6995 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6995 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6996
6996
6997 forcerelativevalue = None
6997 forcerelativevalue = None
6998 if ui.hasconfig(b'commands', b'status.relative'):
6998 if ui.hasconfig(b'commands', b'status.relative'):
6999 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6999 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
7000 uipathfn = scmutil.getuipathfn(
7000 uipathfn = scmutil.getuipathfn(
7001 repo,
7001 repo,
7002 legacyrelativevalue=bool(pats),
7002 legacyrelativevalue=bool(pats),
7003 forcerelativevalue=forcerelativevalue,
7003 forcerelativevalue=forcerelativevalue,
7004 )
7004 )
7005
7005
7006 if opts.get(b'print0'):
7006 if opts.get(b'print0'):
7007 end = b'\0'
7007 end = b'\0'
7008 else:
7008 else:
7009 end = b'\n'
7009 end = b'\n'
7010 states = b'modified added removed deleted unknown ignored clean'.split()
7010 states = b'modified added removed deleted unknown ignored clean'.split()
7011 show = [k for k in states if opts.get(k)]
7011 show = [k for k in states if opts.get(k)]
7012 if opts.get(b'all'):
7012 if opts.get(b'all'):
7013 show += ui.quiet and (states[:4] + [b'clean']) or states
7013 show += ui.quiet and (states[:4] + [b'clean']) or states
7014
7014
7015 if not show:
7015 if not show:
7016 if ui.quiet:
7016 if ui.quiet:
7017 show = states[:4]
7017 show = states[:4]
7018 else:
7018 else:
7019 show = states[:5]
7019 show = states[:5]
7020
7020
7021 m = scmutil.match(ctx2, pats, opts)
7021 m = scmutil.match(ctx2, pats, opts)
7022 if terse:
7022 if terse:
7023 # we need to compute clean and unknown to terse
7023 # we need to compute clean and unknown to terse
7024 stat = repo.status(
7024 stat = repo.status(
7025 ctx1.node(),
7025 ctx1.node(),
7026 ctx2.node(),
7026 ctx2.node(),
7027 m,
7027 m,
7028 b'ignored' in show or b'i' in terse,
7028 b'ignored' in show or b'i' in terse,
7029 clean=True,
7029 clean=True,
7030 unknown=True,
7030 unknown=True,
7031 listsubrepos=opts.get(b'subrepos'),
7031 listsubrepos=opts.get(b'subrepos'),
7032 )
7032 )
7033
7033
7034 stat = cmdutil.tersedir(stat, terse)
7034 stat = cmdutil.tersedir(stat, terse)
7035 else:
7035 else:
7036 stat = repo.status(
7036 stat = repo.status(
7037 ctx1.node(),
7037 ctx1.node(),
7038 ctx2.node(),
7038 ctx2.node(),
7039 m,
7039 m,
7040 b'ignored' in show,
7040 b'ignored' in show,
7041 b'clean' in show,
7041 b'clean' in show,
7042 b'unknown' in show,
7042 b'unknown' in show,
7043 opts.get(b'subrepos'),
7043 opts.get(b'subrepos'),
7044 )
7044 )
7045
7045
7046 changestates = zip(
7046 changestates = zip(
7047 states,
7047 states,
7048 pycompat.iterbytestr(b'MAR!?IC'),
7048 pycompat.iterbytestr(b'MAR!?IC'),
7049 [getattr(stat, s.decode('utf8')) for s in states],
7049 [getattr(stat, s.decode('utf8')) for s in states],
7050 )
7050 )
7051
7051
7052 copy = {}
7052 copy = {}
7053 show_copies = ui.configbool(b'ui', b'statuscopies')
7053 show_copies = ui.configbool(b'ui', b'statuscopies')
7054 if opts.get(b'copies') is not None:
7054 if opts.get(b'copies') is not None:
7055 show_copies = opts.get(b'copies')
7055 show_copies = opts.get(b'copies')
7056 show_copies = (show_copies or opts.get(b'all')) and not opts.get(
7056 show_copies = (show_copies or opts.get(b'all')) and not opts.get(
7057 b'no_status'
7057 b'no_status'
7058 )
7058 )
7059 if show_copies:
7059 if show_copies:
7060 copy = copies.pathcopies(ctx1, ctx2, m)
7060 copy = copies.pathcopies(ctx1, ctx2, m)
7061
7061
7062 morestatus = None
7062 morestatus = None
7063 if (
7063 if (
7064 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
7064 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
7065 and not ui.plain()
7065 and not ui.plain()
7066 and not opts.get(b'print0')
7066 and not opts.get(b'print0')
7067 ):
7067 ):
7068 morestatus = cmdutil.readmorestatus(repo)
7068 morestatus = cmdutil.readmorestatus(repo)
7069
7069
7070 ui.pager(b'status')
7070 ui.pager(b'status')
7071 fm = ui.formatter(b'status', opts)
7071 fm = ui.formatter(b'status', opts)
7072 fmt = b'%s' + end
7072 fmt = b'%s' + end
7073 showchar = not opts.get(b'no_status')
7073 showchar = not opts.get(b'no_status')
7074
7074
7075 for state, char, files in changestates:
7075 for state, char, files in changestates:
7076 if state in show:
7076 if state in show:
7077 label = b'status.' + state
7077 label = b'status.' + state
7078 for f in files:
7078 for f in files:
7079 fm.startitem()
7079 fm.startitem()
7080 fm.context(ctx=ctx2)
7080 fm.context(ctx=ctx2)
7081 fm.data(itemtype=b'file', path=f)
7081 fm.data(itemtype=b'file', path=f)
7082 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7082 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7083 fm.plain(fmt % uipathfn(f), label=label)
7083 fm.plain(fmt % uipathfn(f), label=label)
7084 if f in copy:
7084 if f in copy:
7085 fm.data(source=copy[f])
7085 fm.data(source=copy[f])
7086 fm.plain(
7086 fm.plain(
7087 (b' %s' + end) % uipathfn(copy[f]),
7087 (b' %s' + end) % uipathfn(copy[f]),
7088 label=b'status.copied',
7088 label=b'status.copied',
7089 )
7089 )
7090 if morestatus:
7090 if morestatus:
7091 morestatus.formatfile(f, fm)
7091 morestatus.formatfile(f, fm)
7092
7092
7093 if morestatus:
7093 if morestatus:
7094 morestatus.formatfooter(fm)
7094 morestatus.formatfooter(fm)
7095 fm.end()
7095 fm.end()
7096
7096
7097
7097
7098 @command(
7098 @command(
7099 b'summary|sum',
7099 b'summary|sum',
7100 [(b'', b'remote', None, _(b'check for push and pull'))],
7100 [(b'', b'remote', None, _(b'check for push and pull'))],
7101 b'[--remote]',
7101 b'[--remote]',
7102 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7102 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7103 helpbasic=True,
7103 helpbasic=True,
7104 intents={INTENT_READONLY},
7104 intents={INTENT_READONLY},
7105 )
7105 )
7106 def summary(ui, repo, **opts):
7106 def summary(ui, repo, **opts):
7107 """summarize working directory state
7107 """summarize working directory state
7108
7108
7109 This generates a brief summary of the working directory state,
7109 This generates a brief summary of the working directory state,
7110 including parents, branch, commit status, phase and available updates.
7110 including parents, branch, commit status, phase and available updates.
7111
7111
7112 With the --remote option, this will check the default paths for
7112 With the --remote option, this will check the default paths for
7113 incoming and outgoing changes. This can be time-consuming.
7113 incoming and outgoing changes. This can be time-consuming.
7114
7114
7115 Returns 0 on success.
7115 Returns 0 on success.
7116 """
7116 """
7117
7117
7118 ui.pager(b'summary')
7118 ui.pager(b'summary')
7119 ctx = repo[None]
7119 ctx = repo[None]
7120 parents = ctx.parents()
7120 parents = ctx.parents()
7121 pnode = parents[0].node()
7121 pnode = parents[0].node()
7122 marks = []
7122 marks = []
7123
7123
7124 try:
7124 try:
7125 ms = mergestatemod.mergestate.read(repo)
7125 ms = mergestatemod.mergestate.read(repo)
7126 except error.UnsupportedMergeRecords as e:
7126 except error.UnsupportedMergeRecords as e:
7127 s = b' '.join(e.recordtypes)
7127 s = b' '.join(e.recordtypes)
7128 ui.warn(
7128 ui.warn(
7129 _(b'warning: merge state has unsupported record types: %s\n') % s
7129 _(b'warning: merge state has unsupported record types: %s\n') % s
7130 )
7130 )
7131 unresolved = []
7131 unresolved = []
7132 else:
7132 else:
7133 unresolved = list(ms.unresolved())
7133 unresolved = list(ms.unresolved())
7134
7134
7135 for p in parents:
7135 for p in parents:
7136 # label with log.changeset (instead of log.parent) since this
7136 # label with log.changeset (instead of log.parent) since this
7137 # shows a working directory parent *changeset*:
7137 # shows a working directory parent *changeset*:
7138 # i18n: column positioning for "hg summary"
7138 # i18n: column positioning for "hg summary"
7139 ui.write(
7139 ui.write(
7140 _(b'parent: %d:%s ') % (p.rev(), p),
7140 _(b'parent: %d:%s ') % (p.rev(), p),
7141 label=logcmdutil.changesetlabels(p),
7141 label=logcmdutil.changesetlabels(p),
7142 )
7142 )
7143 ui.write(b' '.join(p.tags()), label=b'log.tag')
7143 ui.write(b' '.join(p.tags()), label=b'log.tag')
7144 if p.bookmarks():
7144 if p.bookmarks():
7145 marks.extend(p.bookmarks())
7145 marks.extend(p.bookmarks())
7146 if p.rev() == -1:
7146 if p.rev() == -1:
7147 if not len(repo):
7147 if not len(repo):
7148 ui.write(_(b' (empty repository)'))
7148 ui.write(_(b' (empty repository)'))
7149 else:
7149 else:
7150 ui.write(_(b' (no revision checked out)'))
7150 ui.write(_(b' (no revision checked out)'))
7151 if p.obsolete():
7151 if p.obsolete():
7152 ui.write(_(b' (obsolete)'))
7152 ui.write(_(b' (obsolete)'))
7153 if p.isunstable():
7153 if p.isunstable():
7154 instabilities = (
7154 instabilities = (
7155 ui.label(instability, b'trouble.%s' % instability)
7155 ui.label(instability, b'trouble.%s' % instability)
7156 for instability in p.instabilities()
7156 for instability in p.instabilities()
7157 )
7157 )
7158 ui.write(b' (' + b', '.join(instabilities) + b')')
7158 ui.write(b' (' + b', '.join(instabilities) + b')')
7159 ui.write(b'\n')
7159 ui.write(b'\n')
7160 if p.description():
7160 if p.description():
7161 ui.status(
7161 ui.status(
7162 b' ' + p.description().splitlines()[0].strip() + b'\n',
7162 b' ' + p.description().splitlines()[0].strip() + b'\n',
7163 label=b'log.summary',
7163 label=b'log.summary',
7164 )
7164 )
7165
7165
7166 branch = ctx.branch()
7166 branch = ctx.branch()
7167 bheads = repo.branchheads(branch)
7167 bheads = repo.branchheads(branch)
7168 # i18n: column positioning for "hg summary"
7168 # i18n: column positioning for "hg summary"
7169 m = _(b'branch: %s\n') % branch
7169 m = _(b'branch: %s\n') % branch
7170 if branch != b'default':
7170 if branch != b'default':
7171 ui.write(m, label=b'log.branch')
7171 ui.write(m, label=b'log.branch')
7172 else:
7172 else:
7173 ui.status(m, label=b'log.branch')
7173 ui.status(m, label=b'log.branch')
7174
7174
7175 if marks:
7175 if marks:
7176 active = repo._activebookmark
7176 active = repo._activebookmark
7177 # i18n: column positioning for "hg summary"
7177 # i18n: column positioning for "hg summary"
7178 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7178 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7179 if active is not None:
7179 if active is not None:
7180 if active in marks:
7180 if active in marks:
7181 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7181 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7182 marks.remove(active)
7182 marks.remove(active)
7183 else:
7183 else:
7184 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7184 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7185 for m in marks:
7185 for m in marks:
7186 ui.write(b' ' + m, label=b'log.bookmark')
7186 ui.write(b' ' + m, label=b'log.bookmark')
7187 ui.write(b'\n', label=b'log.bookmark')
7187 ui.write(b'\n', label=b'log.bookmark')
7188
7188
7189 status = repo.status(unknown=True)
7189 status = repo.status(unknown=True)
7190
7190
7191 c = repo.dirstate.copies()
7191 c = repo.dirstate.copies()
7192 copied, renamed = [], []
7192 copied, renamed = [], []
7193 for d, s in c.items():
7193 for d, s in c.items():
7194 if s in status.removed:
7194 if s in status.removed:
7195 status.removed.remove(s)
7195 status.removed.remove(s)
7196 renamed.append(d)
7196 renamed.append(d)
7197 else:
7197 else:
7198 copied.append(d)
7198 copied.append(d)
7199 if d in status.added:
7199 if d in status.added:
7200 status.added.remove(d)
7200 status.added.remove(d)
7201
7201
7202 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7202 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7203
7203
7204 labels = [
7204 labels = [
7205 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7205 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7206 (ui.label(_(b'%d added'), b'status.added'), status.added),
7206 (ui.label(_(b'%d added'), b'status.added'), status.added),
7207 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7207 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7208 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7208 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7209 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7209 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7210 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7210 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7211 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7211 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7212 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7212 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7213 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7213 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7214 ]
7214 ]
7215 t = []
7215 t = []
7216 for l, s in labels:
7216 for l, s in labels:
7217 if s:
7217 if s:
7218 t.append(l % len(s))
7218 t.append(l % len(s))
7219
7219
7220 t = b', '.join(t)
7220 t = b', '.join(t)
7221 cleanworkdir = False
7221 cleanworkdir = False
7222
7222
7223 if repo.vfs.exists(b'graftstate'):
7223 if repo.vfs.exists(b'graftstate'):
7224 t += _(b' (graft in progress)')
7224 t += _(b' (graft in progress)')
7225 if repo.vfs.exists(b'updatestate'):
7225 if repo.vfs.exists(b'updatestate'):
7226 t += _(b' (interrupted update)')
7226 t += _(b' (interrupted update)')
7227 elif len(parents) > 1:
7227 elif len(parents) > 1:
7228 t += _(b' (merge)')
7228 t += _(b' (merge)')
7229 elif branch != parents[0].branch():
7229 elif branch != parents[0].branch():
7230 t += _(b' (new branch)')
7230 t += _(b' (new branch)')
7231 elif parents[0].closesbranch() and pnode in repo.branchheads(
7231 elif parents[0].closesbranch() and pnode in repo.branchheads(
7232 branch, closed=True
7232 branch, closed=True
7233 ):
7233 ):
7234 t += _(b' (head closed)')
7234 t += _(b' (head closed)')
7235 elif not (
7235 elif not (
7236 status.modified
7236 status.modified
7237 or status.added
7237 or status.added
7238 or status.removed
7238 or status.removed
7239 or renamed
7239 or renamed
7240 or copied
7240 or copied
7241 or subs
7241 or subs
7242 ):
7242 ):
7243 t += _(b' (clean)')
7243 t += _(b' (clean)')
7244 cleanworkdir = True
7244 cleanworkdir = True
7245 elif pnode not in bheads:
7245 elif pnode not in bheads:
7246 t += _(b' (new branch head)')
7246 t += _(b' (new branch head)')
7247
7247
7248 if parents:
7248 if parents:
7249 pendingphase = max(p.phase() for p in parents)
7249 pendingphase = max(p.phase() for p in parents)
7250 else:
7250 else:
7251 pendingphase = phases.public
7251 pendingphase = phases.public
7252
7252
7253 if pendingphase > phases.newcommitphase(ui):
7253 if pendingphase > phases.newcommitphase(ui):
7254 t += b' (%s)' % phases.phasenames[pendingphase]
7254 t += b' (%s)' % phases.phasenames[pendingphase]
7255
7255
7256 if cleanworkdir:
7256 if cleanworkdir:
7257 # i18n: column positioning for "hg summary"
7257 # i18n: column positioning for "hg summary"
7258 ui.status(_(b'commit: %s\n') % t.strip())
7258 ui.status(_(b'commit: %s\n') % t.strip())
7259 else:
7259 else:
7260 # i18n: column positioning for "hg summary"
7260 # i18n: column positioning for "hg summary"
7261 ui.write(_(b'commit: %s\n') % t.strip())
7261 ui.write(_(b'commit: %s\n') % t.strip())
7262
7262
7263 # all ancestors of branch heads - all ancestors of parent = new csets
7263 # all ancestors of branch heads - all ancestors of parent = new csets
7264 new = len(
7264 new = len(
7265 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7265 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7266 )
7266 )
7267
7267
7268 if new == 0:
7268 if new == 0:
7269 # i18n: column positioning for "hg summary"
7269 # i18n: column positioning for "hg summary"
7270 ui.status(_(b'update: (current)\n'))
7270 ui.status(_(b'update: (current)\n'))
7271 elif pnode not in bheads:
7271 elif pnode not in bheads:
7272 # i18n: column positioning for "hg summary"
7272 # i18n: column positioning for "hg summary"
7273 ui.write(_(b'update: %d new changesets (update)\n') % new)
7273 ui.write(_(b'update: %d new changesets (update)\n') % new)
7274 else:
7274 else:
7275 # i18n: column positioning for "hg summary"
7275 # i18n: column positioning for "hg summary"
7276 ui.write(
7276 ui.write(
7277 _(b'update: %d new changesets, %d branch heads (merge)\n')
7277 _(b'update: %d new changesets, %d branch heads (merge)\n')
7278 % (new, len(bheads))
7278 % (new, len(bheads))
7279 )
7279 )
7280
7280
7281 t = []
7281 t = []
7282 draft = len(repo.revs(b'draft()'))
7282 draft = len(repo.revs(b'draft()'))
7283 if draft:
7283 if draft:
7284 t.append(_(b'%d draft') % draft)
7284 t.append(_(b'%d draft') % draft)
7285 secret = len(repo.revs(b'secret()'))
7285 secret = len(repo.revs(b'secret()'))
7286 if secret:
7286 if secret:
7287 t.append(_(b'%d secret') % secret)
7287 t.append(_(b'%d secret') % secret)
7288
7288
7289 if draft or secret:
7289 if draft or secret:
7290 ui.status(_(b'phases: %s\n') % b', '.join(t))
7290 ui.status(_(b'phases: %s\n') % b', '.join(t))
7291
7291
7292 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7292 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7293 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7293 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7294 numtrouble = len(repo.revs(trouble + b"()"))
7294 numtrouble = len(repo.revs(trouble + b"()"))
7295 # We write all the possibilities to ease translation
7295 # We write all the possibilities to ease translation
7296 troublemsg = {
7296 troublemsg = {
7297 b"orphan": _(b"orphan: %d changesets"),
7297 b"orphan": _(b"orphan: %d changesets"),
7298 b"contentdivergent": _(b"content-divergent: %d changesets"),
7298 b"contentdivergent": _(b"content-divergent: %d changesets"),
7299 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7299 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7300 }
7300 }
7301 if numtrouble > 0:
7301 if numtrouble > 0:
7302 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7302 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7303
7303
7304 cmdutil.summaryhooks(ui, repo)
7304 cmdutil.summaryhooks(ui, repo)
7305
7305
7306 if opts.get('remote'):
7306 if opts.get('remote'):
7307 needsincoming, needsoutgoing = True, True
7307 needsincoming, needsoutgoing = True, True
7308 else:
7308 else:
7309 needsincoming, needsoutgoing = False, False
7309 needsincoming, needsoutgoing = False, False
7310 for i, o in cmdutil.summaryremotehooks(
7310 for i, o in cmdutil.summaryremotehooks(
7311 ui, repo, pycompat.byteskwargs(opts), None
7311 ui, repo, pycompat.byteskwargs(opts), None
7312 ):
7312 ):
7313 if i:
7313 if i:
7314 needsincoming = True
7314 needsincoming = True
7315 if o:
7315 if o:
7316 needsoutgoing = True
7316 needsoutgoing = True
7317 if not needsincoming and not needsoutgoing:
7317 if not needsincoming and not needsoutgoing:
7318 return
7318 return
7319
7319
7320 def getincoming():
7320 def getincoming():
7321 # XXX We should actually skip this if no default is specified, instead
7321 # XXX We should actually skip this if no default is specified, instead
7322 # of passing "default" which will resolve as "./default/" if no default
7322 # of passing "default" which will resolve as "./default/" if no default
7323 # path is defined.
7323 # path is defined.
7324 path = urlutil.get_unique_pull_path_obj(b'summary', ui, b'default')
7324 path = urlutil.get_unique_pull_path_obj(b'summary', ui, b'default')
7325 sbranch = path.branch
7325 sbranch = path.branch
7326 try:
7326 try:
7327 other = hg.peer(repo, {}, path)
7327 other = hg.peer(repo, {}, path)
7328 except error.RepoError:
7328 except error.RepoError:
7329 if opts.get('remote'):
7329 if opts.get('remote'):
7330 raise
7330 raise
7331 return path.loc, sbranch, None, None, None
7331 return path.loc, sbranch, None, None, None
7332 branches = (path.branch, [])
7332 branches = (path.branch, [])
7333 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7333 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7334 if revs:
7334 if revs:
7335 revs = [other.lookup(rev) for rev in revs]
7335 revs = [other.lookup(rev) for rev in revs]
7336 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(path.loc))
7336 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(path.loc))
7337 with repo.ui.silent():
7337 with repo.ui.silent():
7338 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7338 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7339 return path.loc, sbranch, other, commoninc, commoninc[1]
7339 return path.loc, sbranch, other, commoninc, commoninc[1]
7340
7340
7341 if needsincoming:
7341 if needsincoming:
7342 source, sbranch, sother, commoninc, incoming = getincoming()
7342 source, sbranch, sother, commoninc, incoming = getincoming()
7343 else:
7343 else:
7344 source = sbranch = sother = commoninc = incoming = None
7344 source = sbranch = sother = commoninc = incoming = None
7345
7345
7346 def getoutgoing():
7346 def getoutgoing():
7347 # XXX We should actually skip this if no default is specified, instead
7347 # XXX We should actually skip this if no default is specified, instead
7348 # of passing "default" which will resolve as "./default/" if no default
7348 # of passing "default" which will resolve as "./default/" if no default
7349 # path is defined.
7349 # path is defined.
7350 d = None
7350 d = None
7351 if b'default-push' in ui.paths:
7351 if b'default-push' in ui.paths:
7352 d = b'default-push'
7352 d = b'default-push'
7353 elif b'default' in ui.paths:
7353 elif b'default' in ui.paths:
7354 d = b'default'
7354 d = b'default'
7355 path = None
7355 path = None
7356 if d is not None:
7356 if d is not None:
7357 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7357 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7358 dest = path.loc
7358 dest = path.loc
7359 dbranch = path.branch
7359 dbranch = path.branch
7360 else:
7360 else:
7361 dest = b'default'
7361 dest = b'default'
7362 dbranch = None
7362 dbranch = None
7363 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7363 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7364 if source != dest:
7364 if source != dest:
7365 try:
7365 try:
7366 dother = hg.peer(repo, {}, path if path is not None else dest)
7366 dother = hg.peer(repo, {}, path if path is not None else dest)
7367 except error.RepoError:
7367 except error.RepoError:
7368 if opts.get('remote'):
7368 if opts.get('remote'):
7369 raise
7369 raise
7370 return dest, dbranch, None, None
7370 return dest, dbranch, None, None
7371 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7371 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7372 elif sother is None:
7372 elif sother is None:
7373 # there is no explicit destination peer, but source one is invalid
7373 # there is no explicit destination peer, but source one is invalid
7374 return dest, dbranch, None, None
7374 return dest, dbranch, None, None
7375 else:
7375 else:
7376 dother = sother
7376 dother = sother
7377 if source != dest or (sbranch is not None and sbranch != dbranch):
7377 if source != dest or (sbranch is not None and sbranch != dbranch):
7378 common = None
7378 common = None
7379 else:
7379 else:
7380 common = commoninc
7380 common = commoninc
7381 if revs:
7381 if revs:
7382 revs = [repo.lookup(rev) for rev in revs]
7382 revs = [repo.lookup(rev) for rev in revs]
7383 with repo.ui.silent():
7383 with repo.ui.silent():
7384 outgoing = discovery.findcommonoutgoing(
7384 outgoing = discovery.findcommonoutgoing(
7385 repo, dother, onlyheads=revs, commoninc=common
7385 repo, dother, onlyheads=revs, commoninc=common
7386 )
7386 )
7387 return dest, dbranch, dother, outgoing
7387 return dest, dbranch, dother, outgoing
7388
7388
7389 if needsoutgoing:
7389 if needsoutgoing:
7390 dest, dbranch, dother, outgoing = getoutgoing()
7390 dest, dbranch, dother, outgoing = getoutgoing()
7391 else:
7391 else:
7392 dest = dbranch = dother = outgoing = None
7392 dest = dbranch = dother = outgoing = None
7393
7393
7394 if opts.get('remote'):
7394 if opts.get('remote'):
7395 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7395 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7396 # The former always sets `sother` (or raises an exception if it can't);
7396 # The former always sets `sother` (or raises an exception if it can't);
7397 # the latter always sets `outgoing`.
7397 # the latter always sets `outgoing`.
7398 assert sother is not None
7398 assert sother is not None
7399 assert outgoing is not None
7399 assert outgoing is not None
7400
7400
7401 t = []
7401 t = []
7402 if incoming:
7402 if incoming:
7403 t.append(_(b'1 or more incoming'))
7403 t.append(_(b'1 or more incoming'))
7404 o = outgoing.missing
7404 o = outgoing.missing
7405 if o:
7405 if o:
7406 t.append(_(b'%d outgoing') % len(o))
7406 t.append(_(b'%d outgoing') % len(o))
7407 other = dother or sother
7407 other = dother or sother
7408 if b'bookmarks' in other.listkeys(b'namespaces'):
7408 if b'bookmarks' in other.listkeys(b'namespaces'):
7409 counts = bookmarks.summary(repo, other)
7409 counts = bookmarks.summary(repo, other)
7410 if counts[0] > 0:
7410 if counts[0] > 0:
7411 t.append(_(b'%d incoming bookmarks') % counts[0])
7411 t.append(_(b'%d incoming bookmarks') % counts[0])
7412 if counts[1] > 0:
7412 if counts[1] > 0:
7413 t.append(_(b'%d outgoing bookmarks') % counts[1])
7413 t.append(_(b'%d outgoing bookmarks') % counts[1])
7414
7414
7415 if t:
7415 if t:
7416 # i18n: column positioning for "hg summary"
7416 # i18n: column positioning for "hg summary"
7417 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7417 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7418 else:
7418 else:
7419 # i18n: column positioning for "hg summary"
7419 # i18n: column positioning for "hg summary"
7420 ui.status(_(b'remote: (synced)\n'))
7420 ui.status(_(b'remote: (synced)\n'))
7421
7421
7422 cmdutil.summaryremotehooks(
7422 cmdutil.summaryremotehooks(
7423 ui,
7423 ui,
7424 repo,
7424 repo,
7425 pycompat.byteskwargs(opts),
7425 pycompat.byteskwargs(opts),
7426 (
7426 (
7427 (source, sbranch, sother, commoninc),
7427 (source, sbranch, sother, commoninc),
7428 (dest, dbranch, dother, outgoing),
7428 (dest, dbranch, dother, outgoing),
7429 ),
7429 ),
7430 )
7430 )
7431
7431
7432
7432
7433 @command(
7433 @command(
7434 b'tag',
7434 b'tag',
7435 [
7435 [
7436 (b'f', b'force', None, _(b'force tag')),
7436 (b'f', b'force', None, _(b'force tag')),
7437 (b'l', b'local', None, _(b'make the tag local')),
7437 (b'l', b'local', None, _(b'make the tag local')),
7438 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7438 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7439 (b'', b'remove', None, _(b'remove a tag')),
7439 (b'', b'remove', None, _(b'remove a tag')),
7440 # -l/--local is already there, commitopts cannot be used
7440 # -l/--local is already there, commitopts cannot be used
7441 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7441 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7442 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7442 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7443 ]
7443 ]
7444 + commitopts2,
7444 + commitopts2,
7445 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7445 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7446 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7446 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7447 )
7447 )
7448 def tag(ui, repo, name1, *names, **opts):
7448 def tag(ui, repo, name1, *names, **opts):
7449 """add one or more tags for the current or given revision
7449 """add one or more tags for the current or given revision
7450
7450
7451 Name a particular revision using <name>.
7451 Name a particular revision using <name>.
7452
7452
7453 Tags are used to name particular revisions of the repository and are
7453 Tags are used to name particular revisions of the repository and are
7454 very useful to compare different revisions, to go back to significant
7454 very useful to compare different revisions, to go back to significant
7455 earlier versions or to mark branch points as releases, etc. Changing
7455 earlier versions or to mark branch points as releases, etc. Changing
7456 an existing tag is normally disallowed; use -f/--force to override.
7456 an existing tag is normally disallowed; use -f/--force to override.
7457
7457
7458 If no revision is given, the parent of the working directory is
7458 If no revision is given, the parent of the working directory is
7459 used.
7459 used.
7460
7460
7461 To facilitate version control, distribution, and merging of tags,
7461 To facilitate version control, distribution, and merging of tags,
7462 they are stored as a file named ".hgtags" which is managed similarly
7462 they are stored as a file named ".hgtags" which is managed similarly
7463 to other project files and can be hand-edited if necessary. This
7463 to other project files and can be hand-edited if necessary. This
7464 also means that tagging creates a new commit. The file
7464 also means that tagging creates a new commit. The file
7465 ".hg/localtags" is used for local tags (not shared among
7465 ".hg/localtags" is used for local tags (not shared among
7466 repositories).
7466 repositories).
7467
7467
7468 Tag commits are usually made at the head of a branch. If the parent
7468 Tag commits are usually made at the head of a branch. If the parent
7469 of the working directory is not a branch head, :hg:`tag` aborts; use
7469 of the working directory is not a branch head, :hg:`tag` aborts; use
7470 -f/--force to force the tag commit to be based on a non-head
7470 -f/--force to force the tag commit to be based on a non-head
7471 changeset.
7471 changeset.
7472
7472
7473 See :hg:`help dates` for a list of formats valid for -d/--date.
7473 See :hg:`help dates` for a list of formats valid for -d/--date.
7474
7474
7475 Since tag names have priority over branch names during revision
7475 Since tag names have priority over branch names during revision
7476 lookup, using an existing branch name as a tag name is discouraged.
7476 lookup, using an existing branch name as a tag name is discouraged.
7477
7477
7478 Returns 0 on success.
7478 Returns 0 on success.
7479 """
7479 """
7480 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7480 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7481 opts = pycompat.byteskwargs(opts)
7481
7482 with repo.wlock(), repo.lock():
7482 with repo.wlock(), repo.lock():
7483 rev_ = b"."
7483 rev_ = b"."
7484 names = [t.strip() for t in (name1,) + names]
7484 names = [t.strip() for t in (name1,) + names]
7485 if len(names) != len(set(names)):
7485 if len(names) != len(set(names)):
7486 raise error.InputError(_(b'tag names must be unique'))
7486 raise error.InputError(_(b'tag names must be unique'))
7487 for n in names:
7487 for n in names:
7488 scmutil.checknewlabel(repo, n, b'tag')
7488 scmutil.checknewlabel(repo, n, b'tag')
7489 if not n:
7489 if not n:
7490 raise error.InputError(
7490 raise error.InputError(
7491 _(b'tag names cannot consist entirely of whitespace')
7491 _(b'tag names cannot consist entirely of whitespace')
7492 )
7492 )
7493 if opts.get(b'rev'):
7493 if opts.get('rev'):
7494 rev_ = opts[b'rev']
7494 rev_ = opts['rev']
7495 message = opts.get(b'message')
7495 message = opts.get('message')
7496 if opts.get(b'remove'):
7496 if opts.get('remove'):
7497 if opts.get(b'local'):
7497 if opts.get('local'):
7498 expectedtype = b'local'
7498 expectedtype = b'local'
7499 else:
7499 else:
7500 expectedtype = b'global'
7500 expectedtype = b'global'
7501
7501
7502 for n in names:
7502 for n in names:
7503 if repo.tagtype(n) == b'global':
7503 if repo.tagtype(n) == b'global':
7504 alltags = tagsmod.findglobaltags(ui, repo)
7504 alltags = tagsmod.findglobaltags(ui, repo)
7505 if alltags[n][0] == repo.nullid:
7505 if alltags[n][0] == repo.nullid:
7506 raise error.InputError(
7506 raise error.InputError(
7507 _(b"tag '%s' is already removed") % n
7507 _(b"tag '%s' is already removed") % n
7508 )
7508 )
7509 if not repo.tagtype(n):
7509 if not repo.tagtype(n):
7510 raise error.InputError(_(b"tag '%s' does not exist") % n)
7510 raise error.InputError(_(b"tag '%s' does not exist") % n)
7511 if repo.tagtype(n) != expectedtype:
7511 if repo.tagtype(n) != expectedtype:
7512 if expectedtype == b'global':
7512 if expectedtype == b'global':
7513 raise error.InputError(
7513 raise error.InputError(
7514 _(b"tag '%s' is not a global tag") % n
7514 _(b"tag '%s' is not a global tag") % n
7515 )
7515 )
7516 else:
7516 else:
7517 raise error.InputError(
7517 raise error.InputError(
7518 _(b"tag '%s' is not a local tag") % n
7518 _(b"tag '%s' is not a local tag") % n
7519 )
7519 )
7520 rev_ = b'null'
7520 rev_ = b'null'
7521 if not message:
7521 if not message:
7522 # we don't translate commit messages
7522 # we don't translate commit messages
7523 message = b'Removed tag %s' % b', '.join(names)
7523 message = b'Removed tag %s' % b', '.join(names)
7524 elif not opts.get(b'force'):
7524 elif not opts.get('force'):
7525 for n in names:
7525 for n in names:
7526 if n in repo.tags():
7526 if n in repo.tags():
7527 raise error.InputError(
7527 raise error.InputError(
7528 _(b"tag '%s' already exists (use -f to force)") % n
7528 _(b"tag '%s' already exists (use -f to force)") % n
7529 )
7529 )
7530 if not opts.get(b'local'):
7530 if not opts.get('local'):
7531 p1, p2 = repo.dirstate.parents()
7531 p1, p2 = repo.dirstate.parents()
7532 if p2 != repo.nullid:
7532 if p2 != repo.nullid:
7533 raise error.StateError(_(b'uncommitted merge'))
7533 raise error.StateError(_(b'uncommitted merge'))
7534 bheads = repo.branchheads()
7534 bheads = repo.branchheads()
7535 if not opts.get(b'force') and bheads and p1 not in bheads:
7535 if not opts.get('force') and bheads and p1 not in bheads:
7536 raise error.InputError(
7536 raise error.InputError(
7537 _(
7537 _(
7538 b'working directory is not at a branch head '
7538 b'working directory is not at a branch head '
7539 b'(use -f to force)'
7539 b'(use -f to force)'
7540 )
7540 )
7541 )
7541 )
7542 node = logcmdutil.revsingle(repo, rev_).node()
7542 node = logcmdutil.revsingle(repo, rev_).node()
7543
7543
7544 # don't allow tagging the null rev or the working directory
7544 # don't allow tagging the null rev or the working directory
7545 if node is None:
7545 if node is None:
7546 raise error.InputError(_(b"cannot tag working directory"))
7546 raise error.InputError(_(b"cannot tag working directory"))
7547 elif not opts.get(b'remove') and node == nullid:
7547 elif not opts.get('remove') and node == nullid:
7548 raise error.InputError(_(b"cannot tag null revision"))
7548 raise error.InputError(_(b"cannot tag null revision"))
7549
7549
7550 if not message:
7550 if not message:
7551 # we don't translate commit messages
7551 # we don't translate commit messages
7552 message = b'Added tag %s for changeset %s' % (
7552 message = b'Added tag %s for changeset %s' % (
7553 b', '.join(names),
7553 b', '.join(names),
7554 short(node),
7554 short(node),
7555 )
7555 )
7556
7556
7557 date = opts.get(b'date')
7557 date = opts.get('date')
7558 if date:
7558 if date:
7559 date = dateutil.parsedate(date)
7559 date = dateutil.parsedate(date)
7560
7560
7561 if opts.get(b'remove'):
7561 if opts.get('remove'):
7562 editform = b'tag.remove'
7562 editform = b'tag.remove'
7563 else:
7563 else:
7564 editform = b'tag.add'
7564 editform = b'tag.add'
7565 editor = cmdutil.getcommiteditor(
7565 editor = cmdutil.getcommiteditor(editform=editform, **opts)
7566 editform=editform, **pycompat.strkwargs(opts)
7567 )
7568
7566
7569 tagsmod.tag(
7567 tagsmod.tag(
7570 repo,
7568 repo,
7571 names,
7569 names,
7572 node,
7570 node,
7573 message,
7571 message,
7574 opts.get(b'local'),
7572 opts.get('local'),
7575 opts.get(b'user'),
7573 opts.get('user'),
7576 date,
7574 date,
7577 editor=editor,
7575 editor=editor,
7578 )
7576 )
7579
7577
7580
7578
7581 @command(
7579 @command(
7582 b'tags',
7580 b'tags',
7583 formatteropts,
7581 formatteropts,
7584 b'',
7582 b'',
7585 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7583 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7586 intents={INTENT_READONLY},
7584 intents={INTENT_READONLY},
7587 )
7585 )
7588 def tags(ui, repo, **opts):
7586 def tags(ui, repo, **opts):
7589 """list repository tags
7587 """list repository tags
7590
7588
7591 This lists both regular and local tags. When the -v/--verbose
7589 This lists both regular and local tags. When the -v/--verbose
7592 switch is used, a third column "local" is printed for local tags.
7590 switch is used, a third column "local" is printed for local tags.
7593 When the -q/--quiet switch is used, only the tag name is printed.
7591 When the -q/--quiet switch is used, only the tag name is printed.
7594
7592
7595 .. container:: verbose
7593 .. container:: verbose
7596
7594
7597 Template:
7595 Template:
7598
7596
7599 The following keywords are supported in addition to the common template
7597 The following keywords are supported in addition to the common template
7600 keywords and functions such as ``{tag}``. See also
7598 keywords and functions such as ``{tag}``. See also
7601 :hg:`help templates`.
7599 :hg:`help templates`.
7602
7600
7603 :type: String. ``local`` for local tags.
7601 :type: String. ``local`` for local tags.
7604
7602
7605 Returns 0 on success.
7603 Returns 0 on success.
7606 """
7604 """
7607
7605
7608 opts = pycompat.byteskwargs(opts)
7606 opts = pycompat.byteskwargs(opts)
7609 ui.pager(b'tags')
7607 ui.pager(b'tags')
7610 fm = ui.formatter(b'tags', opts)
7608 fm = ui.formatter(b'tags', opts)
7611 hexfunc = fm.hexfunc
7609 hexfunc = fm.hexfunc
7612
7610
7613 for t, n in reversed(repo.tagslist()):
7611 for t, n in reversed(repo.tagslist()):
7614 hn = hexfunc(n)
7612 hn = hexfunc(n)
7615 label = b'tags.normal'
7613 label = b'tags.normal'
7616 tagtype = repo.tagtype(t)
7614 tagtype = repo.tagtype(t)
7617 if not tagtype or tagtype == b'global':
7615 if not tagtype or tagtype == b'global':
7618 tagtype = b''
7616 tagtype = b''
7619 else:
7617 else:
7620 label = b'tags.' + tagtype
7618 label = b'tags.' + tagtype
7621
7619
7622 fm.startitem()
7620 fm.startitem()
7623 fm.context(repo=repo)
7621 fm.context(repo=repo)
7624 fm.write(b'tag', b'%s', t, label=label)
7622 fm.write(b'tag', b'%s', t, label=label)
7625 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7623 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7626 fm.condwrite(
7624 fm.condwrite(
7627 not ui.quiet,
7625 not ui.quiet,
7628 b'rev node',
7626 b'rev node',
7629 fmt,
7627 fmt,
7630 repo.changelog.rev(n),
7628 repo.changelog.rev(n),
7631 hn,
7629 hn,
7632 label=label,
7630 label=label,
7633 )
7631 )
7634 fm.condwrite(
7632 fm.condwrite(
7635 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7633 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7636 )
7634 )
7637 fm.plain(b'\n')
7635 fm.plain(b'\n')
7638 fm.end()
7636 fm.end()
7639
7637
7640
7638
7641 @command(
7639 @command(
7642 b'tip',
7640 b'tip',
7643 [
7641 [
7644 (b'p', b'patch', None, _(b'show patch')),
7642 (b'p', b'patch', None, _(b'show patch')),
7645 (b'g', b'git', None, _(b'use git extended diff format')),
7643 (b'g', b'git', None, _(b'use git extended diff format')),
7646 ]
7644 ]
7647 + templateopts,
7645 + templateopts,
7648 _(b'[-p] [-g]'),
7646 _(b'[-p] [-g]'),
7649 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7647 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7650 )
7648 )
7651 def tip(ui, repo, **opts):
7649 def tip(ui, repo, **opts):
7652 """show the tip revision (DEPRECATED)
7650 """show the tip revision (DEPRECATED)
7653
7651
7654 The tip revision (usually just called the tip) is the changeset
7652 The tip revision (usually just called the tip) is the changeset
7655 most recently added to the repository (and therefore the most
7653 most recently added to the repository (and therefore the most
7656 recently changed head).
7654 recently changed head).
7657
7655
7658 If you have just made a commit, that commit will be the tip. If
7656 If you have just made a commit, that commit will be the tip. If
7659 you have just pulled changes from another repository, the tip of
7657 you have just pulled changes from another repository, the tip of
7660 that repository becomes the current tip. The "tip" tag is special
7658 that repository becomes the current tip. The "tip" tag is special
7661 and cannot be renamed or assigned to a different changeset.
7659 and cannot be renamed or assigned to a different changeset.
7662
7660
7663 This command is deprecated, please use :hg:`heads` instead.
7661 This command is deprecated, please use :hg:`heads` instead.
7664
7662
7665 Returns 0 on success.
7663 Returns 0 on success.
7666 """
7664 """
7667 opts = pycompat.byteskwargs(opts)
7665 opts = pycompat.byteskwargs(opts)
7668 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7666 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7669 displayer.show(repo[b'tip'])
7667 displayer.show(repo[b'tip'])
7670 displayer.close()
7668 displayer.close()
7671
7669
7672
7670
7673 @command(
7671 @command(
7674 b'unbundle',
7672 b'unbundle',
7675 [
7673 [
7676 (
7674 (
7677 b'u',
7675 b'u',
7678 b'update',
7676 b'update',
7679 None,
7677 None,
7680 _(b'update to new branch head if changesets were unbundled'),
7678 _(b'update to new branch head if changesets were unbundled'),
7681 )
7679 )
7682 ],
7680 ],
7683 _(b'[-u] FILE...'),
7681 _(b'[-u] FILE...'),
7684 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7682 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7685 )
7683 )
7686 def unbundle(ui, repo, fname1, *fnames, **opts):
7684 def unbundle(ui, repo, fname1, *fnames, **opts):
7687 """apply one or more bundle files
7685 """apply one or more bundle files
7688
7686
7689 Apply one or more bundle files generated by :hg:`bundle`.
7687 Apply one or more bundle files generated by :hg:`bundle`.
7690
7688
7691 Returns 0 on success, 1 if an update has unresolved files.
7689 Returns 0 on success, 1 if an update has unresolved files.
7692 """
7690 """
7693 fnames = (fname1,) + fnames
7691 fnames = (fname1,) + fnames
7694
7692
7695 with repo.lock():
7693 with repo.lock():
7696 for fname in fnames:
7694 for fname in fnames:
7697 f = hg.openpath(ui, fname)
7695 f = hg.openpath(ui, fname)
7698 gen = exchange.readbundle(ui, f, fname)
7696 gen = exchange.readbundle(ui, f, fname)
7699 if isinstance(gen, streamclone.streamcloneapplier):
7697 if isinstance(gen, streamclone.streamcloneapplier):
7700 raise error.InputError(
7698 raise error.InputError(
7701 _(
7699 _(
7702 b'packed bundles cannot be applied with '
7700 b'packed bundles cannot be applied with '
7703 b'"hg unbundle"'
7701 b'"hg unbundle"'
7704 ),
7702 ),
7705 hint=_(b'use "hg debugapplystreamclonebundle"'),
7703 hint=_(b'use "hg debugapplystreamclonebundle"'),
7706 )
7704 )
7707 url = b'bundle:' + fname
7705 url = b'bundle:' + fname
7708 try:
7706 try:
7709 txnname = b'unbundle'
7707 txnname = b'unbundle'
7710 if not isinstance(gen, bundle2.unbundle20):
7708 if not isinstance(gen, bundle2.unbundle20):
7711 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7709 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7712 with repo.transaction(txnname) as tr:
7710 with repo.transaction(txnname) as tr:
7713 op = bundle2.applybundle(
7711 op = bundle2.applybundle(
7714 repo, gen, tr, source=b'unbundle', url=url
7712 repo, gen, tr, source=b'unbundle', url=url
7715 )
7713 )
7716 except error.BundleUnknownFeatureError as exc:
7714 except error.BundleUnknownFeatureError as exc:
7717 raise error.Abort(
7715 raise error.Abort(
7718 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7716 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7719 hint=_(
7717 hint=_(
7720 b"see https://mercurial-scm.org/"
7718 b"see https://mercurial-scm.org/"
7721 b"wiki/BundleFeature for more "
7719 b"wiki/BundleFeature for more "
7722 b"information"
7720 b"information"
7723 ),
7721 ),
7724 )
7722 )
7725 modheads = bundle2.combinechangegroupresults(op)
7723 modheads = bundle2.combinechangegroupresults(op)
7726
7724
7727 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7725 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7728 return 1
7726 return 1
7729 else:
7727 else:
7730 return 0
7728 return 0
7731
7729
7732
7730
7733 @command(
7731 @command(
7734 b'unshelve',
7732 b'unshelve',
7735 [
7733 [
7736 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7734 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7737 (
7735 (
7738 b'c',
7736 b'c',
7739 b'continue',
7737 b'continue',
7740 None,
7738 None,
7741 _(b'continue an incomplete unshelve operation'),
7739 _(b'continue an incomplete unshelve operation'),
7742 ),
7740 ),
7743 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7741 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7744 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7742 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7745 (
7743 (
7746 b'n',
7744 b'n',
7747 b'name',
7745 b'name',
7748 b'',
7746 b'',
7749 _(b'restore shelved change with given name'),
7747 _(b'restore shelved change with given name'),
7750 _(b'NAME'),
7748 _(b'NAME'),
7751 ),
7749 ),
7752 (b't', b'tool', b'', _(b'specify merge tool')),
7750 (b't', b'tool', b'', _(b'specify merge tool')),
7753 (
7751 (
7754 b'',
7752 b'',
7755 b'date',
7753 b'date',
7756 b'',
7754 b'',
7757 _(b'set date for temporary commits (DEPRECATED)'),
7755 _(b'set date for temporary commits (DEPRECATED)'),
7758 _(b'DATE'),
7756 _(b'DATE'),
7759 ),
7757 ),
7760 ],
7758 ],
7761 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7759 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7762 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7763 )
7761 )
7764 def unshelve(ui, repo, *shelved, **opts):
7762 def unshelve(ui, repo, *shelved, **opts):
7765 """restore a shelved change to the working directory
7763 """restore a shelved change to the working directory
7766
7764
7767 This command accepts an optional name of a shelved change to
7765 This command accepts an optional name of a shelved change to
7768 restore. If none is given, the most recent shelved change is used.
7766 restore. If none is given, the most recent shelved change is used.
7769
7767
7770 If a shelved change is applied successfully, the bundle that
7768 If a shelved change is applied successfully, the bundle that
7771 contains the shelved changes is moved to a backup location
7769 contains the shelved changes is moved to a backup location
7772 (.hg/shelve-backup).
7770 (.hg/shelve-backup).
7773
7771
7774 Since you can restore a shelved change on top of an arbitrary
7772 Since you can restore a shelved change on top of an arbitrary
7775 commit, it is possible that unshelving will result in a conflict
7773 commit, it is possible that unshelving will result in a conflict
7776 between your changes and the commits you are unshelving onto. If
7774 between your changes and the commits you are unshelving onto. If
7777 this occurs, you must resolve the conflict, then use
7775 this occurs, you must resolve the conflict, then use
7778 ``--continue`` to complete the unshelve operation. (The bundle
7776 ``--continue`` to complete the unshelve operation. (The bundle
7779 will not be moved until you successfully complete the unshelve.)
7777 will not be moved until you successfully complete the unshelve.)
7780
7778
7781 (Alternatively, you can use ``--abort`` to abandon an unshelve
7779 (Alternatively, you can use ``--abort`` to abandon an unshelve
7782 that causes a conflict. This reverts the unshelved changes, and
7780 that causes a conflict. This reverts the unshelved changes, and
7783 leaves the bundle in place.)
7781 leaves the bundle in place.)
7784
7782
7785 If bare shelved change (without interactive, include and exclude
7783 If bare shelved change (without interactive, include and exclude
7786 option) was done on newly created branch it would restore branch
7784 option) was done on newly created branch it would restore branch
7787 information to the working directory.
7785 information to the working directory.
7788
7786
7789 After a successful unshelve, the shelved changes are stored in a
7787 After a successful unshelve, the shelved changes are stored in a
7790 backup directory. Only the N most recent backups are kept. N
7788 backup directory. Only the N most recent backups are kept. N
7791 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7789 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7792 configuration option.
7790 configuration option.
7793
7791
7794 .. container:: verbose
7792 .. container:: verbose
7795
7793
7796 Timestamp in seconds is used to decide order of backups. More
7794 Timestamp in seconds is used to decide order of backups. More
7797 than ``maxbackups`` backups are kept, if same timestamp
7795 than ``maxbackups`` backups are kept, if same timestamp
7798 prevents from deciding exact order of them, for safety.
7796 prevents from deciding exact order of them, for safety.
7799
7797
7800 Selected changes can be unshelved with ``--interactive`` flag.
7798 Selected changes can be unshelved with ``--interactive`` flag.
7801 The working directory is updated with the selected changes, and
7799 The working directory is updated with the selected changes, and
7802 only the unselected changes remain shelved.
7800 only the unselected changes remain shelved.
7803 Note: The whole shelve is applied to working directory first before
7801 Note: The whole shelve is applied to working directory first before
7804 running interactively. So, this will bring up all the conflicts between
7802 running interactively. So, this will bring up all the conflicts between
7805 working directory and the shelve, irrespective of which changes will be
7803 working directory and the shelve, irrespective of which changes will be
7806 unshelved.
7804 unshelved.
7807 """
7805 """
7808 with repo.wlock():
7806 with repo.wlock():
7809 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7807 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7810
7808
7811
7809
7812 statemod.addunfinished(
7810 statemod.addunfinished(
7813 b'unshelve',
7811 b'unshelve',
7814 fname=b'shelvedstate',
7812 fname=b'shelvedstate',
7815 continueflag=True,
7813 continueflag=True,
7816 abortfunc=shelvemod.hgabortunshelve,
7814 abortfunc=shelvemod.hgabortunshelve,
7817 continuefunc=shelvemod.hgcontinueunshelve,
7815 continuefunc=shelvemod.hgcontinueunshelve,
7818 cmdmsg=_(b'unshelve already in progress'),
7816 cmdmsg=_(b'unshelve already in progress'),
7819 )
7817 )
7820
7818
7821
7819
7822 @command(
7820 @command(
7823 b'update|up|checkout|co',
7821 b'update|up|checkout|co',
7824 [
7822 [
7825 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7823 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7826 (b'c', b'check', None, _(b'require clean working directory')),
7824 (b'c', b'check', None, _(b'require clean working directory')),
7827 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7825 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7828 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7826 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7829 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7827 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7830 ]
7828 ]
7831 + mergetoolopts,
7829 + mergetoolopts,
7832 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7830 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7833 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7831 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7834 helpbasic=True,
7832 helpbasic=True,
7835 )
7833 )
7836 def update(ui, repo, node=None, **opts):
7834 def update(ui, repo, node=None, **opts):
7837 """update working directory (or switch revisions)
7835 """update working directory (or switch revisions)
7838
7836
7839 Update the repository's working directory to the specified
7837 Update the repository's working directory to the specified
7840 changeset. If no changeset is specified, update to the tip of the
7838 changeset. If no changeset is specified, update to the tip of the
7841 current named branch and move the active bookmark (see :hg:`help
7839 current named branch and move the active bookmark (see :hg:`help
7842 bookmarks`).
7840 bookmarks`).
7843
7841
7844 Update sets the working directory's parent revision to the specified
7842 Update sets the working directory's parent revision to the specified
7845 changeset (see :hg:`help parents`).
7843 changeset (see :hg:`help parents`).
7846
7844
7847 If the changeset is not a descendant or ancestor of the working
7845 If the changeset is not a descendant or ancestor of the working
7848 directory's parent and there are uncommitted changes, the update is
7846 directory's parent and there are uncommitted changes, the update is
7849 aborted. With the -c/--check option, the working directory is checked
7847 aborted. With the -c/--check option, the working directory is checked
7850 for uncommitted changes; if none are found, the working directory is
7848 for uncommitted changes; if none are found, the working directory is
7851 updated to the specified changeset.
7849 updated to the specified changeset.
7852
7850
7853 .. container:: verbose
7851 .. container:: verbose
7854
7852
7855 The -C/--clean, -c/--check, and -m/--merge options control what
7853 The -C/--clean, -c/--check, and -m/--merge options control what
7856 happens if the working directory contains uncommitted changes.
7854 happens if the working directory contains uncommitted changes.
7857 At most of one of them can be specified.
7855 At most of one of them can be specified.
7858
7856
7859 1. If no option is specified, and if
7857 1. If no option is specified, and if
7860 the requested changeset is an ancestor or descendant of
7858 the requested changeset is an ancestor or descendant of
7861 the working directory's parent, the uncommitted changes
7859 the working directory's parent, the uncommitted changes
7862 are merged into the requested changeset and the merged
7860 are merged into the requested changeset and the merged
7863 result is left uncommitted. If the requested changeset is
7861 result is left uncommitted. If the requested changeset is
7864 not an ancestor or descendant (that is, it is on another
7862 not an ancestor or descendant (that is, it is on another
7865 branch), the update is aborted and the uncommitted changes
7863 branch), the update is aborted and the uncommitted changes
7866 are preserved.
7864 are preserved.
7867
7865
7868 2. With the -m/--merge option, the update is allowed even if the
7866 2. With the -m/--merge option, the update is allowed even if the
7869 requested changeset is not an ancestor or descendant of
7867 requested changeset is not an ancestor or descendant of
7870 the working directory's parent.
7868 the working directory's parent.
7871
7869
7872 3. With the -c/--check option, the update is aborted and the
7870 3. With the -c/--check option, the update is aborted and the
7873 uncommitted changes are preserved.
7871 uncommitted changes are preserved.
7874
7872
7875 4. With the -C/--clean option, uncommitted changes are discarded and
7873 4. With the -C/--clean option, uncommitted changes are discarded and
7876 the working directory is updated to the requested changeset.
7874 the working directory is updated to the requested changeset.
7877
7875
7878 To cancel an uncommitted merge (and lose your changes), use
7876 To cancel an uncommitted merge (and lose your changes), use
7879 :hg:`merge --abort`.
7877 :hg:`merge --abort`.
7880
7878
7881 Use null as the changeset to remove the working directory (like
7879 Use null as the changeset to remove the working directory (like
7882 :hg:`clone -U`).
7880 :hg:`clone -U`).
7883
7881
7884 If you want to revert just one file to an older revision, use
7882 If you want to revert just one file to an older revision, use
7885 :hg:`revert [-r REV] NAME`.
7883 :hg:`revert [-r REV] NAME`.
7886
7884
7887 See :hg:`help dates` for a list of formats valid for -d/--date.
7885 See :hg:`help dates` for a list of formats valid for -d/--date.
7888
7886
7889 Returns 0 on success, 1 if there are unresolved files.
7887 Returns 0 on success, 1 if there are unresolved files.
7890 """
7888 """
7891 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7889 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7892 rev = opts.get('rev')
7890 rev = opts.get('rev')
7893 date = opts.get('date')
7891 date = opts.get('date')
7894 clean = opts.get('clean')
7892 clean = opts.get('clean')
7895 check = opts.get('check')
7893 check = opts.get('check')
7896 merge = opts.get('merge')
7894 merge = opts.get('merge')
7897 if rev and node:
7895 if rev and node:
7898 raise error.InputError(_(b"please specify just one revision"))
7896 raise error.InputError(_(b"please specify just one revision"))
7899
7897
7900 if ui.configbool(b'commands', b'update.requiredest'):
7898 if ui.configbool(b'commands', b'update.requiredest'):
7901 if not node and not rev and not date:
7899 if not node and not rev and not date:
7902 raise error.InputError(
7900 raise error.InputError(
7903 _(b'you must specify a destination'),
7901 _(b'you must specify a destination'),
7904 hint=_(b'for example: hg update ".::"'),
7902 hint=_(b'for example: hg update ".::"'),
7905 )
7903 )
7906
7904
7907 if rev is None or rev == b'':
7905 if rev is None or rev == b'':
7908 rev = node
7906 rev = node
7909
7907
7910 if date and rev is not None:
7908 if date and rev is not None:
7911 raise error.InputError(_(b"you can't specify a revision and a date"))
7909 raise error.InputError(_(b"you can't specify a revision and a date"))
7912
7910
7913 updatecheck = None
7911 updatecheck = None
7914 if check or merge is not None and not merge:
7912 if check or merge is not None and not merge:
7915 updatecheck = b'abort'
7913 updatecheck = b'abort'
7916 elif merge or check is not None and not check:
7914 elif merge or check is not None and not check:
7917 updatecheck = b'none'
7915 updatecheck = b'none'
7918
7916
7919 with repo.wlock():
7917 with repo.wlock():
7920 cmdutil.clearunfinished(repo)
7918 cmdutil.clearunfinished(repo)
7921 if date:
7919 if date:
7922 rev = cmdutil.finddate(ui, repo, date)
7920 rev = cmdutil.finddate(ui, repo, date)
7923
7921
7924 # if we defined a bookmark, we have to remember the original name
7922 # if we defined a bookmark, we have to remember the original name
7925 brev = rev
7923 brev = rev
7926 if rev:
7924 if rev:
7927 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7925 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7928 ctx = logcmdutil.revsingle(repo, rev, default=None)
7926 ctx = logcmdutil.revsingle(repo, rev, default=None)
7929 rev = ctx.rev()
7927 rev = ctx.rev()
7930 hidden = ctx.hidden()
7928 hidden = ctx.hidden()
7931 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7929 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7932 with ui.configoverride(overrides, b'update'):
7930 with ui.configoverride(overrides, b'update'):
7933 ret = hg.updatetotally(
7931 ret = hg.updatetotally(
7934 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7932 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7935 )
7933 )
7936 if hidden:
7934 if hidden:
7937 ctxstr = ctx.hex()[:12]
7935 ctxstr = ctx.hex()[:12]
7938 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7936 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7939
7937
7940 if ctx.obsolete():
7938 if ctx.obsolete():
7941 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7939 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7942 ui.warn(b"(%s)\n" % obsfatemsg)
7940 ui.warn(b"(%s)\n" % obsfatemsg)
7943 return ret
7941 return ret
7944
7942
7945
7943
7946 @command(
7944 @command(
7947 b'verify',
7945 b'verify',
7948 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7946 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7949 helpcategory=command.CATEGORY_MAINTENANCE,
7947 helpcategory=command.CATEGORY_MAINTENANCE,
7950 )
7948 )
7951 def verify(ui, repo, **opts):
7949 def verify(ui, repo, **opts):
7952 """verify the integrity of the repository
7950 """verify the integrity of the repository
7953
7951
7954 Verify the integrity of the current repository.
7952 Verify the integrity of the current repository.
7955
7953
7956 This will perform an extensive check of the repository's
7954 This will perform an extensive check of the repository's
7957 integrity, validating the hashes and checksums of each entry in
7955 integrity, validating the hashes and checksums of each entry in
7958 the changelog, manifest, and tracked files, as well as the
7956 the changelog, manifest, and tracked files, as well as the
7959 integrity of their crosslinks and indices.
7957 integrity of their crosslinks and indices.
7960
7958
7961 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7959 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7962 for more information about recovery from corruption of the
7960 for more information about recovery from corruption of the
7963 repository.
7961 repository.
7964
7962
7965 Returns 0 on success, 1 if errors are encountered.
7963 Returns 0 on success, 1 if errors are encountered.
7966 """
7964 """
7967 opts = pycompat.byteskwargs(opts)
7965 opts = pycompat.byteskwargs(opts)
7968
7966
7969 level = None
7967 level = None
7970 if opts[b'full']:
7968 if opts[b'full']:
7971 level = verifymod.VERIFY_FULL
7969 level = verifymod.VERIFY_FULL
7972 return hg.verify(repo, level)
7970 return hg.verify(repo, level)
7973
7971
7974
7972
7975 @command(
7973 @command(
7976 b'version',
7974 b'version',
7977 [] + formatteropts,
7975 [] + formatteropts,
7978 helpcategory=command.CATEGORY_HELP,
7976 helpcategory=command.CATEGORY_HELP,
7979 norepo=True,
7977 norepo=True,
7980 intents={INTENT_READONLY},
7978 intents={INTENT_READONLY},
7981 )
7979 )
7982 def version_(ui, **opts):
7980 def version_(ui, **opts):
7983 """output version and copyright information
7981 """output version and copyright information
7984
7982
7985 .. container:: verbose
7983 .. container:: verbose
7986
7984
7987 Template:
7985 Template:
7988
7986
7989 The following keywords are supported. See also :hg:`help templates`.
7987 The following keywords are supported. See also :hg:`help templates`.
7990
7988
7991 :extensions: List of extensions.
7989 :extensions: List of extensions.
7992 :ver: String. Version number.
7990 :ver: String. Version number.
7993
7991
7994 And each entry of ``{extensions}`` provides the following sub-keywords
7992 And each entry of ``{extensions}`` provides the following sub-keywords
7995 in addition to ``{ver}``.
7993 in addition to ``{ver}``.
7996
7994
7997 :bundled: Boolean. True if included in the release.
7995 :bundled: Boolean. True if included in the release.
7998 :name: String. Extension name.
7996 :name: String. Extension name.
7999 """
7997 """
8000 opts = pycompat.byteskwargs(opts)
7998 opts = pycompat.byteskwargs(opts)
8001 if ui.verbose:
7999 if ui.verbose:
8002 ui.pager(b'version')
8000 ui.pager(b'version')
8003 fm = ui.formatter(b"version", opts)
8001 fm = ui.formatter(b"version", opts)
8004 fm.startitem()
8002 fm.startitem()
8005 fm.write(
8003 fm.write(
8006 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
8004 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
8007 )
8005 )
8008 license = _(
8006 license = _(
8009 b"(see https://mercurial-scm.org for more information)\n"
8007 b"(see https://mercurial-scm.org for more information)\n"
8010 b"\nCopyright (C) 2005-2023 Olivia Mackall and others\n"
8008 b"\nCopyright (C) 2005-2023 Olivia Mackall and others\n"
8011 b"This is free software; see the source for copying conditions. "
8009 b"This is free software; see the source for copying conditions. "
8012 b"There is NO\nwarranty; "
8010 b"There is NO\nwarranty; "
8013 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
8011 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
8014 )
8012 )
8015 if not ui.quiet:
8013 if not ui.quiet:
8016 fm.plain(license)
8014 fm.plain(license)
8017
8015
8018 if ui.verbose:
8016 if ui.verbose:
8019 fm.plain(_(b"\nEnabled extensions:\n\n"))
8017 fm.plain(_(b"\nEnabled extensions:\n\n"))
8020 # format names and versions into columns
8018 # format names and versions into columns
8021 names = []
8019 names = []
8022 vers = []
8020 vers = []
8023 isinternals = []
8021 isinternals = []
8024 for name, module in sorted(extensions.extensions()):
8022 for name, module in sorted(extensions.extensions()):
8025 names.append(name)
8023 names.append(name)
8026 vers.append(extensions.moduleversion(module) or None)
8024 vers.append(extensions.moduleversion(module) or None)
8027 isinternals.append(extensions.ismoduleinternal(module))
8025 isinternals.append(extensions.ismoduleinternal(module))
8028 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
8026 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
8029 if names:
8027 if names:
8030 namefmt = b" %%-%ds " % max(len(n) for n in names)
8028 namefmt = b" %%-%ds " % max(len(n) for n in names)
8031 places = [_(b"external"), _(b"internal")]
8029 places = [_(b"external"), _(b"internal")]
8032 for n, v, p in zip(names, vers, isinternals):
8030 for n, v, p in zip(names, vers, isinternals):
8033 fn.startitem()
8031 fn.startitem()
8034 fn.condwrite(ui.verbose, b"name", namefmt, n)
8032 fn.condwrite(ui.verbose, b"name", namefmt, n)
8035 if ui.verbose:
8033 if ui.verbose:
8036 fn.plain(b"%s " % places[p])
8034 fn.plain(b"%s " % places[p])
8037 fn.data(bundled=p)
8035 fn.data(bundled=p)
8038 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
8036 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
8039 if ui.verbose:
8037 if ui.verbose:
8040 fn.plain(b"\n")
8038 fn.plain(b"\n")
8041 fn.end()
8039 fn.end()
8042 fm.end()
8040 fm.end()
8043
8041
8044
8042
8045 def loadcmdtable(ui, name, cmdtable):
8043 def loadcmdtable(ui, name, cmdtable):
8046 """Load command functions from specified cmdtable"""
8044 """Load command functions from specified cmdtable"""
8047 overrides = [cmd for cmd in cmdtable if cmd in table]
8045 overrides = [cmd for cmd in cmdtable if cmd in table]
8048 if overrides:
8046 if overrides:
8049 ui.warn(
8047 ui.warn(
8050 _(b"extension '%s' overrides commands: %s\n")
8048 _(b"extension '%s' overrides commands: %s\n")
8051 % (name, b" ".join(overrides))
8049 % (name, b" ".join(overrides))
8052 )
8050 )
8053 table.update(cmdtable)
8051 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now