##// END OF EJS Templates
bisect: replace try:/finally: by a "restore_state" context manager...
Denis Laxalde -
r44064:f37da59a default
parent child Browse files
Show More
@@ -1,7832 +1,7829 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get('dry_run')
183 dryrun = opts.get('dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revision to not display (EXPERIMENTAL)'),
365 _(b'revision to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s' for x in l])
562 formats.append([b'%s' for x in l])
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo, hex(repo.changelog.tip()))
879 return hg.merge(repo, hex(repo.changelog.tip()))
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 try:
1076 with hbisect.restore_state(repo, state, node):
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 finally:
1109 state[b'current'] = [node]
1110 hbisect.save_state(repo, state)
1111 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1108 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1112 return
1109 return
1113
1110
1114 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1115
1112
1116 # actually bisect
1113 # actually bisect
1117 nodes, changesets, good = hbisect.bisect(repo, state)
1114 nodes, changesets, good = hbisect.bisect(repo, state)
1118 if extend:
1115 if extend:
1119 if not changesets:
1116 if not changesets:
1120 extendnode = hbisect.extendrange(repo, state, nodes, good)
1117 extendnode = hbisect.extendrange(repo, state, nodes, good)
1121 if extendnode is not None:
1118 if extendnode is not None:
1122 ui.write(
1119 ui.write(
1123 _(b"Extending search to changeset %d:%s\n")
1120 _(b"Extending search to changeset %d:%s\n")
1124 % (extendnode.rev(), extendnode)
1121 % (extendnode.rev(), extendnode)
1125 )
1122 )
1126 state[b'current'] = [extendnode.node()]
1123 state[b'current'] = [extendnode.node()]
1127 hbisect.save_state(repo, state)
1124 hbisect.save_state(repo, state)
1128 return mayupdate(repo, extendnode.node())
1125 return mayupdate(repo, extendnode.node())
1129 raise error.Abort(_(b"nothing to extend"))
1126 raise error.Abort(_(b"nothing to extend"))
1130
1127
1131 if changesets == 0:
1128 if changesets == 0:
1132 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1129 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1133 else:
1130 else:
1134 assert len(nodes) == 1 # only a single node can be tested next
1131 assert len(nodes) == 1 # only a single node can be tested next
1135 node = nodes[0]
1132 node = nodes[0]
1136 # compute the approximate number of remaining tests
1133 # compute the approximate number of remaining tests
1137 tests, size = 0, 2
1134 tests, size = 0, 2
1138 while size <= changesets:
1135 while size <= changesets:
1139 tests, size = tests + 1, size * 2
1136 tests, size = tests + 1, size * 2
1140 rev = repo.changelog.rev(node)
1137 rev = repo.changelog.rev(node)
1141 ui.write(
1138 ui.write(
1142 _(
1139 _(
1143 b"Testing changeset %d:%s "
1140 b"Testing changeset %d:%s "
1144 b"(%d changesets remaining, ~%d tests)\n"
1141 b"(%d changesets remaining, ~%d tests)\n"
1145 )
1142 )
1146 % (rev, short(node), changesets, tests)
1143 % (rev, short(node), changesets, tests)
1147 )
1144 )
1148 state[b'current'] = [node]
1145 state[b'current'] = [node]
1149 hbisect.save_state(repo, state)
1146 hbisect.save_state(repo, state)
1150 return mayupdate(repo, node)
1147 return mayupdate(repo, node)
1151
1148
1152
1149
1153 @command(
1150 @command(
1154 b'bookmarks|bookmark',
1151 b'bookmarks|bookmark',
1155 [
1152 [
1156 (b'f', b'force', False, _(b'force')),
1153 (b'f', b'force', False, _(b'force')),
1157 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1154 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1158 (b'd', b'delete', False, _(b'delete a given bookmark')),
1155 (b'd', b'delete', False, _(b'delete a given bookmark')),
1159 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1156 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1160 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1157 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1161 (b'l', b'list', False, _(b'list existing bookmarks')),
1158 (b'l', b'list', False, _(b'list existing bookmarks')),
1162 ]
1159 ]
1163 + formatteropts,
1160 + formatteropts,
1164 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1161 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1162 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1166 )
1163 )
1167 def bookmark(ui, repo, *names, **opts):
1164 def bookmark(ui, repo, *names, **opts):
1168 '''create a new bookmark or list existing bookmarks
1165 '''create a new bookmark or list existing bookmarks
1169
1166
1170 Bookmarks are labels on changesets to help track lines of development.
1167 Bookmarks are labels on changesets to help track lines of development.
1171 Bookmarks are unversioned and can be moved, renamed and deleted.
1168 Bookmarks are unversioned and can be moved, renamed and deleted.
1172 Deleting or moving a bookmark has no effect on the associated changesets.
1169 Deleting or moving a bookmark has no effect on the associated changesets.
1173
1170
1174 Creating or updating to a bookmark causes it to be marked as 'active'.
1171 Creating or updating to a bookmark causes it to be marked as 'active'.
1175 The active bookmark is indicated with a '*'.
1172 The active bookmark is indicated with a '*'.
1176 When a commit is made, the active bookmark will advance to the new commit.
1173 When a commit is made, the active bookmark will advance to the new commit.
1177 A plain :hg:`update` will also advance an active bookmark, if possible.
1174 A plain :hg:`update` will also advance an active bookmark, if possible.
1178 Updating away from a bookmark will cause it to be deactivated.
1175 Updating away from a bookmark will cause it to be deactivated.
1179
1176
1180 Bookmarks can be pushed and pulled between repositories (see
1177 Bookmarks can be pushed and pulled between repositories (see
1181 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1178 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1182 diverged, a new 'divergent bookmark' of the form 'name@path' will
1179 diverged, a new 'divergent bookmark' of the form 'name@path' will
1183 be created. Using :hg:`merge` will resolve the divergence.
1180 be created. Using :hg:`merge` will resolve the divergence.
1184
1181
1185 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1182 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1186 the active bookmark's name.
1183 the active bookmark's name.
1187
1184
1188 A bookmark named '@' has the special property that :hg:`clone` will
1185 A bookmark named '@' has the special property that :hg:`clone` will
1189 check it out by default if it exists.
1186 check it out by default if it exists.
1190
1187
1191 .. container:: verbose
1188 .. container:: verbose
1192
1189
1193 Template:
1190 Template:
1194
1191
1195 The following keywords are supported in addition to the common template
1192 The following keywords are supported in addition to the common template
1196 keywords and functions such as ``{bookmark}``. See also
1193 keywords and functions such as ``{bookmark}``. See also
1197 :hg:`help templates`.
1194 :hg:`help templates`.
1198
1195
1199 :active: Boolean. True if the bookmark is active.
1196 :active: Boolean. True if the bookmark is active.
1200
1197
1201 Examples:
1198 Examples:
1202
1199
1203 - create an active bookmark for a new line of development::
1200 - create an active bookmark for a new line of development::
1204
1201
1205 hg book new-feature
1202 hg book new-feature
1206
1203
1207 - create an inactive bookmark as a place marker::
1204 - create an inactive bookmark as a place marker::
1208
1205
1209 hg book -i reviewed
1206 hg book -i reviewed
1210
1207
1211 - create an inactive bookmark on another changeset::
1208 - create an inactive bookmark on another changeset::
1212
1209
1213 hg book -r .^ tested
1210 hg book -r .^ tested
1214
1211
1215 - rename bookmark turkey to dinner::
1212 - rename bookmark turkey to dinner::
1216
1213
1217 hg book -m turkey dinner
1214 hg book -m turkey dinner
1218
1215
1219 - move the '@' bookmark from another branch::
1216 - move the '@' bookmark from another branch::
1220
1217
1221 hg book -f @
1218 hg book -f @
1222
1219
1223 - print only the active bookmark name::
1220 - print only the active bookmark name::
1224
1221
1225 hg book -ql .
1222 hg book -ql .
1226 '''
1223 '''
1227 opts = pycompat.byteskwargs(opts)
1224 opts = pycompat.byteskwargs(opts)
1228 force = opts.get(b'force')
1225 force = opts.get(b'force')
1229 rev = opts.get(b'rev')
1226 rev = opts.get(b'rev')
1230 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1227 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1231
1228
1232 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1229 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1233 if len(selactions) > 1:
1230 if len(selactions) > 1:
1234 raise error.Abort(
1231 raise error.Abort(
1235 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1232 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1236 )
1233 )
1237 if selactions:
1234 if selactions:
1238 action = selactions[0]
1235 action = selactions[0]
1239 elif names or rev:
1236 elif names or rev:
1240 action = b'add'
1237 action = b'add'
1241 elif inactive:
1238 elif inactive:
1242 action = b'inactive' # meaning deactivate
1239 action = b'inactive' # meaning deactivate
1243 else:
1240 else:
1244 action = b'list'
1241 action = b'list'
1245
1242
1246 if rev and action in {b'delete', b'rename', b'list'}:
1243 if rev and action in {b'delete', b'rename', b'list'}:
1247 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1244 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1248 if inactive and action in {b'delete', b'list'}:
1245 if inactive and action in {b'delete', b'list'}:
1249 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1246 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1250 if not names and action in {b'add', b'delete'}:
1247 if not names and action in {b'add', b'delete'}:
1251 raise error.Abort(_(b"bookmark name required"))
1248 raise error.Abort(_(b"bookmark name required"))
1252
1249
1253 if action in {b'add', b'delete', b'rename', b'inactive'}:
1250 if action in {b'add', b'delete', b'rename', b'inactive'}:
1254 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1251 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1255 if action == b'delete':
1252 if action == b'delete':
1256 names = pycompat.maplist(repo._bookmarks.expandname, names)
1253 names = pycompat.maplist(repo._bookmarks.expandname, names)
1257 bookmarks.delete(repo, tr, names)
1254 bookmarks.delete(repo, tr, names)
1258 elif action == b'rename':
1255 elif action == b'rename':
1259 if not names:
1256 if not names:
1260 raise error.Abort(_(b"new bookmark name required"))
1257 raise error.Abort(_(b"new bookmark name required"))
1261 elif len(names) > 1:
1258 elif len(names) > 1:
1262 raise error.Abort(_(b"only one new bookmark name allowed"))
1259 raise error.Abort(_(b"only one new bookmark name allowed"))
1263 oldname = repo._bookmarks.expandname(opts[b'rename'])
1260 oldname = repo._bookmarks.expandname(opts[b'rename'])
1264 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1261 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1265 elif action == b'add':
1262 elif action == b'add':
1266 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1263 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1267 elif action == b'inactive':
1264 elif action == b'inactive':
1268 if len(repo._bookmarks) == 0:
1265 if len(repo._bookmarks) == 0:
1269 ui.status(_(b"no bookmarks set\n"))
1266 ui.status(_(b"no bookmarks set\n"))
1270 elif not repo._activebookmark:
1267 elif not repo._activebookmark:
1271 ui.status(_(b"no active bookmark\n"))
1268 ui.status(_(b"no active bookmark\n"))
1272 else:
1269 else:
1273 bookmarks.deactivate(repo)
1270 bookmarks.deactivate(repo)
1274 elif action == b'list':
1271 elif action == b'list':
1275 names = pycompat.maplist(repo._bookmarks.expandname, names)
1272 names = pycompat.maplist(repo._bookmarks.expandname, names)
1276 with ui.formatter(b'bookmarks', opts) as fm:
1273 with ui.formatter(b'bookmarks', opts) as fm:
1277 bookmarks.printbookmarks(ui, repo, fm, names)
1274 bookmarks.printbookmarks(ui, repo, fm, names)
1278 else:
1275 else:
1279 raise error.ProgrammingError(b'invalid action: %s' % action)
1276 raise error.ProgrammingError(b'invalid action: %s' % action)
1280
1277
1281
1278
1282 @command(
1279 @command(
1283 b'branch',
1280 b'branch',
1284 [
1281 [
1285 (
1282 (
1286 b'f',
1283 b'f',
1287 b'force',
1284 b'force',
1288 None,
1285 None,
1289 _(b'set branch name even if it shadows an existing branch'),
1286 _(b'set branch name even if it shadows an existing branch'),
1290 ),
1287 ),
1291 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1288 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1292 (
1289 (
1293 b'r',
1290 b'r',
1294 b'rev',
1291 b'rev',
1295 [],
1292 [],
1296 _(b'change branches of the given revs (EXPERIMENTAL)'),
1293 _(b'change branches of the given revs (EXPERIMENTAL)'),
1297 ),
1294 ),
1298 ],
1295 ],
1299 _(b'[-fC] [NAME]'),
1296 _(b'[-fC] [NAME]'),
1300 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1297 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1301 )
1298 )
1302 def branch(ui, repo, label=None, **opts):
1299 def branch(ui, repo, label=None, **opts):
1303 """set or show the current branch name
1300 """set or show the current branch name
1304
1301
1305 .. note::
1302 .. note::
1306
1303
1307 Branch names are permanent and global. Use :hg:`bookmark` to create a
1304 Branch names are permanent and global. Use :hg:`bookmark` to create a
1308 light-weight bookmark instead. See :hg:`help glossary` for more
1305 light-weight bookmark instead. See :hg:`help glossary` for more
1309 information about named branches and bookmarks.
1306 information about named branches and bookmarks.
1310
1307
1311 With no argument, show the current branch name. With one argument,
1308 With no argument, show the current branch name. With one argument,
1312 set the working directory branch name (the branch will not exist
1309 set the working directory branch name (the branch will not exist
1313 in the repository until the next commit). Standard practice
1310 in the repository until the next commit). Standard practice
1314 recommends that primary development take place on the 'default'
1311 recommends that primary development take place on the 'default'
1315 branch.
1312 branch.
1316
1313
1317 Unless -f/--force is specified, branch will not let you set a
1314 Unless -f/--force is specified, branch will not let you set a
1318 branch name that already exists.
1315 branch name that already exists.
1319
1316
1320 Use -C/--clean to reset the working directory branch to that of
1317 Use -C/--clean to reset the working directory branch to that of
1321 the parent of the working directory, negating a previous branch
1318 the parent of the working directory, negating a previous branch
1322 change.
1319 change.
1323
1320
1324 Use the command :hg:`update` to switch to an existing branch. Use
1321 Use the command :hg:`update` to switch to an existing branch. Use
1325 :hg:`commit --close-branch` to mark this branch head as closed.
1322 :hg:`commit --close-branch` to mark this branch head as closed.
1326 When all heads of a branch are closed, the branch will be
1323 When all heads of a branch are closed, the branch will be
1327 considered closed.
1324 considered closed.
1328
1325
1329 Returns 0 on success.
1326 Returns 0 on success.
1330 """
1327 """
1331 opts = pycompat.byteskwargs(opts)
1328 opts = pycompat.byteskwargs(opts)
1332 revs = opts.get(b'rev')
1329 revs = opts.get(b'rev')
1333 if label:
1330 if label:
1334 label = label.strip()
1331 label = label.strip()
1335
1332
1336 if not opts.get(b'clean') and not label:
1333 if not opts.get(b'clean') and not label:
1337 if revs:
1334 if revs:
1338 raise error.Abort(_(b"no branch name specified for the revisions"))
1335 raise error.Abort(_(b"no branch name specified for the revisions"))
1339 ui.write(b"%s\n" % repo.dirstate.branch())
1336 ui.write(b"%s\n" % repo.dirstate.branch())
1340 return
1337 return
1341
1338
1342 with repo.wlock():
1339 with repo.wlock():
1343 if opts.get(b'clean'):
1340 if opts.get(b'clean'):
1344 label = repo[b'.'].branch()
1341 label = repo[b'.'].branch()
1345 repo.dirstate.setbranch(label)
1342 repo.dirstate.setbranch(label)
1346 ui.status(_(b'reset working directory to branch %s\n') % label)
1343 ui.status(_(b'reset working directory to branch %s\n') % label)
1347 elif label:
1344 elif label:
1348
1345
1349 scmutil.checknewlabel(repo, label, b'branch')
1346 scmutil.checknewlabel(repo, label, b'branch')
1350 if revs:
1347 if revs:
1351 return cmdutil.changebranch(ui, repo, revs, label)
1348 return cmdutil.changebranch(ui, repo, revs, label)
1352
1349
1353 if not opts.get(b'force') and label in repo.branchmap():
1350 if not opts.get(b'force') and label in repo.branchmap():
1354 if label not in [p.branch() for p in repo[None].parents()]:
1351 if label not in [p.branch() for p in repo[None].parents()]:
1355 raise error.Abort(
1352 raise error.Abort(
1356 _(b'a branch of the same name already exists'),
1353 _(b'a branch of the same name already exists'),
1357 # i18n: "it" refers to an existing branch
1354 # i18n: "it" refers to an existing branch
1358 hint=_(b"use 'hg update' to switch to it"),
1355 hint=_(b"use 'hg update' to switch to it"),
1359 )
1356 )
1360
1357
1361 repo.dirstate.setbranch(label)
1358 repo.dirstate.setbranch(label)
1362 ui.status(_(b'marked working directory as branch %s\n') % label)
1359 ui.status(_(b'marked working directory as branch %s\n') % label)
1363
1360
1364 # find any open named branches aside from default
1361 # find any open named branches aside from default
1365 for n, h, t, c in repo.branchmap().iterbranches():
1362 for n, h, t, c in repo.branchmap().iterbranches():
1366 if n != b"default" and not c:
1363 if n != b"default" and not c:
1367 return 0
1364 return 0
1368 ui.status(
1365 ui.status(
1369 _(
1366 _(
1370 b'(branches are permanent and global, '
1367 b'(branches are permanent and global, '
1371 b'did you want a bookmark?)\n'
1368 b'did you want a bookmark?)\n'
1372 )
1369 )
1373 )
1370 )
1374
1371
1375
1372
1376 @command(
1373 @command(
1377 b'branches',
1374 b'branches',
1378 [
1375 [
1379 (
1376 (
1380 b'a',
1377 b'a',
1381 b'active',
1378 b'active',
1382 False,
1379 False,
1383 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1380 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1384 ),
1381 ),
1385 (b'c', b'closed', False, _(b'show normal and closed branches')),
1382 (b'c', b'closed', False, _(b'show normal and closed branches')),
1386 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1383 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1387 ]
1384 ]
1388 + formatteropts,
1385 + formatteropts,
1389 _(b'[-c]'),
1386 _(b'[-c]'),
1390 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1387 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1391 intents={INTENT_READONLY},
1388 intents={INTENT_READONLY},
1392 )
1389 )
1393 def branches(ui, repo, active=False, closed=False, **opts):
1390 def branches(ui, repo, active=False, closed=False, **opts):
1394 """list repository named branches
1391 """list repository named branches
1395
1392
1396 List the repository's named branches, indicating which ones are
1393 List the repository's named branches, indicating which ones are
1397 inactive. If -c/--closed is specified, also list branches which have
1394 inactive. If -c/--closed is specified, also list branches which have
1398 been marked closed (see :hg:`commit --close-branch`).
1395 been marked closed (see :hg:`commit --close-branch`).
1399
1396
1400 Use the command :hg:`update` to switch to an existing branch.
1397 Use the command :hg:`update` to switch to an existing branch.
1401
1398
1402 .. container:: verbose
1399 .. container:: verbose
1403
1400
1404 Template:
1401 Template:
1405
1402
1406 The following keywords are supported in addition to the common template
1403 The following keywords are supported in addition to the common template
1407 keywords and functions such as ``{branch}``. See also
1404 keywords and functions such as ``{branch}``. See also
1408 :hg:`help templates`.
1405 :hg:`help templates`.
1409
1406
1410 :active: Boolean. True if the branch is active.
1407 :active: Boolean. True if the branch is active.
1411 :closed: Boolean. True if the branch is closed.
1408 :closed: Boolean. True if the branch is closed.
1412 :current: Boolean. True if it is the current branch.
1409 :current: Boolean. True if it is the current branch.
1413
1410
1414 Returns 0.
1411 Returns 0.
1415 """
1412 """
1416
1413
1417 opts = pycompat.byteskwargs(opts)
1414 opts = pycompat.byteskwargs(opts)
1418 revs = opts.get(b'rev')
1415 revs = opts.get(b'rev')
1419 selectedbranches = None
1416 selectedbranches = None
1420 if revs:
1417 if revs:
1421 revs = scmutil.revrange(repo, revs)
1418 revs = scmutil.revrange(repo, revs)
1422 getbi = repo.revbranchcache().branchinfo
1419 getbi = repo.revbranchcache().branchinfo
1423 selectedbranches = {getbi(r)[0] for r in revs}
1420 selectedbranches = {getbi(r)[0] for r in revs}
1424
1421
1425 ui.pager(b'branches')
1422 ui.pager(b'branches')
1426 fm = ui.formatter(b'branches', opts)
1423 fm = ui.formatter(b'branches', opts)
1427 hexfunc = fm.hexfunc
1424 hexfunc = fm.hexfunc
1428
1425
1429 allheads = set(repo.heads())
1426 allheads = set(repo.heads())
1430 branches = []
1427 branches = []
1431 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1428 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1432 if selectedbranches is not None and tag not in selectedbranches:
1429 if selectedbranches is not None and tag not in selectedbranches:
1433 continue
1430 continue
1434 isactive = False
1431 isactive = False
1435 if not isclosed:
1432 if not isclosed:
1436 openheads = set(repo.branchmap().iteropen(heads))
1433 openheads = set(repo.branchmap().iteropen(heads))
1437 isactive = bool(openheads & allheads)
1434 isactive = bool(openheads & allheads)
1438 branches.append((tag, repo[tip], isactive, not isclosed))
1435 branches.append((tag, repo[tip], isactive, not isclosed))
1439 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1436 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1440
1437
1441 for tag, ctx, isactive, isopen in branches:
1438 for tag, ctx, isactive, isopen in branches:
1442 if active and not isactive:
1439 if active and not isactive:
1443 continue
1440 continue
1444 if isactive:
1441 if isactive:
1445 label = b'branches.active'
1442 label = b'branches.active'
1446 notice = b''
1443 notice = b''
1447 elif not isopen:
1444 elif not isopen:
1448 if not closed:
1445 if not closed:
1449 continue
1446 continue
1450 label = b'branches.closed'
1447 label = b'branches.closed'
1451 notice = _(b' (closed)')
1448 notice = _(b' (closed)')
1452 else:
1449 else:
1453 label = b'branches.inactive'
1450 label = b'branches.inactive'
1454 notice = _(b' (inactive)')
1451 notice = _(b' (inactive)')
1455 current = tag == repo.dirstate.branch()
1452 current = tag == repo.dirstate.branch()
1456 if current:
1453 if current:
1457 label = b'branches.current'
1454 label = b'branches.current'
1458
1455
1459 fm.startitem()
1456 fm.startitem()
1460 fm.write(b'branch', b'%s', tag, label=label)
1457 fm.write(b'branch', b'%s', tag, label=label)
1461 rev = ctx.rev()
1458 rev = ctx.rev()
1462 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1459 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1463 fmt = b' ' * padsize + b' %d:%s'
1460 fmt = b' ' * padsize + b' %d:%s'
1464 fm.condwrite(
1461 fm.condwrite(
1465 not ui.quiet,
1462 not ui.quiet,
1466 b'rev node',
1463 b'rev node',
1467 fmt,
1464 fmt,
1468 rev,
1465 rev,
1469 hexfunc(ctx.node()),
1466 hexfunc(ctx.node()),
1470 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1467 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1471 )
1468 )
1472 fm.context(ctx=ctx)
1469 fm.context(ctx=ctx)
1473 fm.data(active=isactive, closed=not isopen, current=current)
1470 fm.data(active=isactive, closed=not isopen, current=current)
1474 if not ui.quiet:
1471 if not ui.quiet:
1475 fm.plain(notice)
1472 fm.plain(notice)
1476 fm.plain(b'\n')
1473 fm.plain(b'\n')
1477 fm.end()
1474 fm.end()
1478
1475
1479
1476
1480 @command(
1477 @command(
1481 b'bundle',
1478 b'bundle',
1482 [
1479 [
1483 (
1480 (
1484 b'f',
1481 b'f',
1485 b'force',
1482 b'force',
1486 None,
1483 None,
1487 _(b'run even when the destination is unrelated'),
1484 _(b'run even when the destination is unrelated'),
1488 ),
1485 ),
1489 (
1486 (
1490 b'r',
1487 b'r',
1491 b'rev',
1488 b'rev',
1492 [],
1489 [],
1493 _(b'a changeset intended to be added to the destination'),
1490 _(b'a changeset intended to be added to the destination'),
1494 _(b'REV'),
1491 _(b'REV'),
1495 ),
1492 ),
1496 (
1493 (
1497 b'b',
1494 b'b',
1498 b'branch',
1495 b'branch',
1499 [],
1496 [],
1500 _(b'a specific branch you would like to bundle'),
1497 _(b'a specific branch you would like to bundle'),
1501 _(b'BRANCH'),
1498 _(b'BRANCH'),
1502 ),
1499 ),
1503 (
1500 (
1504 b'',
1501 b'',
1505 b'base',
1502 b'base',
1506 [],
1503 [],
1507 _(b'a base changeset assumed to be available at the destination'),
1504 _(b'a base changeset assumed to be available at the destination'),
1508 _(b'REV'),
1505 _(b'REV'),
1509 ),
1506 ),
1510 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1507 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1511 (
1508 (
1512 b't',
1509 b't',
1513 b'type',
1510 b'type',
1514 b'bzip2',
1511 b'bzip2',
1515 _(b'bundle compression type to use'),
1512 _(b'bundle compression type to use'),
1516 _(b'TYPE'),
1513 _(b'TYPE'),
1517 ),
1514 ),
1518 ]
1515 ]
1519 + remoteopts,
1516 + remoteopts,
1520 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1517 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1521 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1518 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1522 )
1519 )
1523 def bundle(ui, repo, fname, dest=None, **opts):
1520 def bundle(ui, repo, fname, dest=None, **opts):
1524 """create a bundle file
1521 """create a bundle file
1525
1522
1526 Generate a bundle file containing data to be transferred to another
1523 Generate a bundle file containing data to be transferred to another
1527 repository.
1524 repository.
1528
1525
1529 To create a bundle containing all changesets, use -a/--all
1526 To create a bundle containing all changesets, use -a/--all
1530 (or --base null). Otherwise, hg assumes the destination will have
1527 (or --base null). Otherwise, hg assumes the destination will have
1531 all the nodes you specify with --base parameters. Otherwise, hg
1528 all the nodes you specify with --base parameters. Otherwise, hg
1532 will assume the repository has all the nodes in destination, or
1529 will assume the repository has all the nodes in destination, or
1533 default-push/default if no destination is specified, where destination
1530 default-push/default if no destination is specified, where destination
1534 is the repository you provide through DEST option.
1531 is the repository you provide through DEST option.
1535
1532
1536 You can change bundle format with the -t/--type option. See
1533 You can change bundle format with the -t/--type option. See
1537 :hg:`help bundlespec` for documentation on this format. By default,
1534 :hg:`help bundlespec` for documentation on this format. By default,
1538 the most appropriate format is used and compression defaults to
1535 the most appropriate format is used and compression defaults to
1539 bzip2.
1536 bzip2.
1540
1537
1541 The bundle file can then be transferred using conventional means
1538 The bundle file can then be transferred using conventional means
1542 and applied to another repository with the unbundle or pull
1539 and applied to another repository with the unbundle or pull
1543 command. This is useful when direct push and pull are not
1540 command. This is useful when direct push and pull are not
1544 available or when exporting an entire repository is undesirable.
1541 available or when exporting an entire repository is undesirable.
1545
1542
1546 Applying bundles preserves all changeset contents including
1543 Applying bundles preserves all changeset contents including
1547 permissions, copy/rename information, and revision history.
1544 permissions, copy/rename information, and revision history.
1548
1545
1549 Returns 0 on success, 1 if no changes found.
1546 Returns 0 on success, 1 if no changes found.
1550 """
1547 """
1551 opts = pycompat.byteskwargs(opts)
1548 opts = pycompat.byteskwargs(opts)
1552 revs = None
1549 revs = None
1553 if b'rev' in opts:
1550 if b'rev' in opts:
1554 revstrings = opts[b'rev']
1551 revstrings = opts[b'rev']
1555 revs = scmutil.revrange(repo, revstrings)
1552 revs = scmutil.revrange(repo, revstrings)
1556 if revstrings and not revs:
1553 if revstrings and not revs:
1557 raise error.Abort(_(b'no commits to bundle'))
1554 raise error.Abort(_(b'no commits to bundle'))
1558
1555
1559 bundletype = opts.get(b'type', b'bzip2').lower()
1556 bundletype = opts.get(b'type', b'bzip2').lower()
1560 try:
1557 try:
1561 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1558 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1562 except error.UnsupportedBundleSpecification as e:
1559 except error.UnsupportedBundleSpecification as e:
1563 raise error.Abort(
1560 raise error.Abort(
1564 pycompat.bytestr(e),
1561 pycompat.bytestr(e),
1565 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1562 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1566 )
1563 )
1567 cgversion = bundlespec.contentopts[b"cg.version"]
1564 cgversion = bundlespec.contentopts[b"cg.version"]
1568
1565
1569 # Packed bundles are a pseudo bundle format for now.
1566 # Packed bundles are a pseudo bundle format for now.
1570 if cgversion == b's1':
1567 if cgversion == b's1':
1571 raise error.Abort(
1568 raise error.Abort(
1572 _(b'packed bundles cannot be produced by "hg bundle"'),
1569 _(b'packed bundles cannot be produced by "hg bundle"'),
1573 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1570 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1574 )
1571 )
1575
1572
1576 if opts.get(b'all'):
1573 if opts.get(b'all'):
1577 if dest:
1574 if dest:
1578 raise error.Abort(
1575 raise error.Abort(
1579 _(b"--all is incompatible with specifying a destination")
1576 _(b"--all is incompatible with specifying a destination")
1580 )
1577 )
1581 if opts.get(b'base'):
1578 if opts.get(b'base'):
1582 ui.warn(_(b"ignoring --base because --all was specified\n"))
1579 ui.warn(_(b"ignoring --base because --all was specified\n"))
1583 base = [nullrev]
1580 base = [nullrev]
1584 else:
1581 else:
1585 base = scmutil.revrange(repo, opts.get(b'base'))
1582 base = scmutil.revrange(repo, opts.get(b'base'))
1586 if cgversion not in changegroup.supportedoutgoingversions(repo):
1583 if cgversion not in changegroup.supportedoutgoingversions(repo):
1587 raise error.Abort(
1584 raise error.Abort(
1588 _(b"repository does not support bundle version %s") % cgversion
1585 _(b"repository does not support bundle version %s") % cgversion
1589 )
1586 )
1590
1587
1591 if base:
1588 if base:
1592 if dest:
1589 if dest:
1593 raise error.Abort(
1590 raise error.Abort(
1594 _(b"--base is incompatible with specifying a destination")
1591 _(b"--base is incompatible with specifying a destination")
1595 )
1592 )
1596 common = [repo[rev].node() for rev in base]
1593 common = [repo[rev].node() for rev in base]
1597 heads = [repo[r].node() for r in revs] if revs else None
1594 heads = [repo[r].node() for r in revs] if revs else None
1598 outgoing = discovery.outgoing(repo, common, heads)
1595 outgoing = discovery.outgoing(repo, common, heads)
1599 else:
1596 else:
1600 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1597 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1601 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1598 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1602 other = hg.peer(repo, opts, dest)
1599 other = hg.peer(repo, opts, dest)
1603 revs = [repo[r].hex() for r in revs]
1600 revs = [repo[r].hex() for r in revs]
1604 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1601 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1605 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1602 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1606 outgoing = discovery.findcommonoutgoing(
1603 outgoing = discovery.findcommonoutgoing(
1607 repo,
1604 repo,
1608 other,
1605 other,
1609 onlyheads=heads,
1606 onlyheads=heads,
1610 force=opts.get(b'force'),
1607 force=opts.get(b'force'),
1611 portable=True,
1608 portable=True,
1612 )
1609 )
1613
1610
1614 if not outgoing.missing:
1611 if not outgoing.missing:
1615 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1612 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1616 return 1
1613 return 1
1617
1614
1618 if cgversion == b'01': # bundle1
1615 if cgversion == b'01': # bundle1
1619 bversion = b'HG10' + bundlespec.wirecompression
1616 bversion = b'HG10' + bundlespec.wirecompression
1620 bcompression = None
1617 bcompression = None
1621 elif cgversion in (b'02', b'03'):
1618 elif cgversion in (b'02', b'03'):
1622 bversion = b'HG20'
1619 bversion = b'HG20'
1623 bcompression = bundlespec.wirecompression
1620 bcompression = bundlespec.wirecompression
1624 else:
1621 else:
1625 raise error.ProgrammingError(
1622 raise error.ProgrammingError(
1626 b'bundle: unexpected changegroup version %s' % cgversion
1623 b'bundle: unexpected changegroup version %s' % cgversion
1627 )
1624 )
1628
1625
1629 # TODO compression options should be derived from bundlespec parsing.
1626 # TODO compression options should be derived from bundlespec parsing.
1630 # This is a temporary hack to allow adjusting bundle compression
1627 # This is a temporary hack to allow adjusting bundle compression
1631 # level without a) formalizing the bundlespec changes to declare it
1628 # level without a) formalizing the bundlespec changes to declare it
1632 # b) introducing a command flag.
1629 # b) introducing a command flag.
1633 compopts = {}
1630 compopts = {}
1634 complevel = ui.configint(
1631 complevel = ui.configint(
1635 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1632 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1636 )
1633 )
1637 if complevel is None:
1634 if complevel is None:
1638 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1635 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1639 if complevel is not None:
1636 if complevel is not None:
1640 compopts[b'level'] = complevel
1637 compopts[b'level'] = complevel
1641
1638
1642 # Allow overriding the bundling of obsmarker in phases through
1639 # Allow overriding the bundling of obsmarker in phases through
1643 # configuration while we don't have a bundle version that include them
1640 # configuration while we don't have a bundle version that include them
1644 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1641 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1645 bundlespec.contentopts[b'obsolescence'] = True
1642 bundlespec.contentopts[b'obsolescence'] = True
1646 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1643 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1647 bundlespec.contentopts[b'phases'] = True
1644 bundlespec.contentopts[b'phases'] = True
1648
1645
1649 bundle2.writenewbundle(
1646 bundle2.writenewbundle(
1650 ui,
1647 ui,
1651 repo,
1648 repo,
1652 b'bundle',
1649 b'bundle',
1653 fname,
1650 fname,
1654 bversion,
1651 bversion,
1655 outgoing,
1652 outgoing,
1656 bundlespec.contentopts,
1653 bundlespec.contentopts,
1657 compression=bcompression,
1654 compression=bcompression,
1658 compopts=compopts,
1655 compopts=compopts,
1659 )
1656 )
1660
1657
1661
1658
1662 @command(
1659 @command(
1663 b'cat',
1660 b'cat',
1664 [
1661 [
1665 (
1662 (
1666 b'o',
1663 b'o',
1667 b'output',
1664 b'output',
1668 b'',
1665 b'',
1669 _(b'print output to file with formatted name'),
1666 _(b'print output to file with formatted name'),
1670 _(b'FORMAT'),
1667 _(b'FORMAT'),
1671 ),
1668 ),
1672 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1669 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1673 (b'', b'decode', None, _(b'apply any matching decode filter')),
1670 (b'', b'decode', None, _(b'apply any matching decode filter')),
1674 ]
1671 ]
1675 + walkopts
1672 + walkopts
1676 + formatteropts,
1673 + formatteropts,
1677 _(b'[OPTION]... FILE...'),
1674 _(b'[OPTION]... FILE...'),
1678 helpcategory=command.CATEGORY_FILE_CONTENTS,
1675 helpcategory=command.CATEGORY_FILE_CONTENTS,
1679 inferrepo=True,
1676 inferrepo=True,
1680 intents={INTENT_READONLY},
1677 intents={INTENT_READONLY},
1681 )
1678 )
1682 def cat(ui, repo, file1, *pats, **opts):
1679 def cat(ui, repo, file1, *pats, **opts):
1683 """output the current or given revision of files
1680 """output the current or given revision of files
1684
1681
1685 Print the specified files as they were at the given revision. If
1682 Print the specified files as they were at the given revision. If
1686 no revision is given, the parent of the working directory is used.
1683 no revision is given, the parent of the working directory is used.
1687
1684
1688 Output may be to a file, in which case the name of the file is
1685 Output may be to a file, in which case the name of the file is
1689 given using a template string. See :hg:`help templates`. In addition
1686 given using a template string. See :hg:`help templates`. In addition
1690 to the common template keywords, the following formatting rules are
1687 to the common template keywords, the following formatting rules are
1691 supported:
1688 supported:
1692
1689
1693 :``%%``: literal "%" character
1690 :``%%``: literal "%" character
1694 :``%s``: basename of file being printed
1691 :``%s``: basename of file being printed
1695 :``%d``: dirname of file being printed, or '.' if in repository root
1692 :``%d``: dirname of file being printed, or '.' if in repository root
1696 :``%p``: root-relative path name of file being printed
1693 :``%p``: root-relative path name of file being printed
1697 :``%H``: changeset hash (40 hexadecimal digits)
1694 :``%H``: changeset hash (40 hexadecimal digits)
1698 :``%R``: changeset revision number
1695 :``%R``: changeset revision number
1699 :``%h``: short-form changeset hash (12 hexadecimal digits)
1696 :``%h``: short-form changeset hash (12 hexadecimal digits)
1700 :``%r``: zero-padded changeset revision number
1697 :``%r``: zero-padded changeset revision number
1701 :``%b``: basename of the exporting repository
1698 :``%b``: basename of the exporting repository
1702 :``\\``: literal "\\" character
1699 :``\\``: literal "\\" character
1703
1700
1704 .. container:: verbose
1701 .. container:: verbose
1705
1702
1706 Template:
1703 Template:
1707
1704
1708 The following keywords are supported in addition to the common template
1705 The following keywords are supported in addition to the common template
1709 keywords and functions. See also :hg:`help templates`.
1706 keywords and functions. See also :hg:`help templates`.
1710
1707
1711 :data: String. File content.
1708 :data: String. File content.
1712 :path: String. Repository-absolute path of the file.
1709 :path: String. Repository-absolute path of the file.
1713
1710
1714 Returns 0 on success.
1711 Returns 0 on success.
1715 """
1712 """
1716 opts = pycompat.byteskwargs(opts)
1713 opts = pycompat.byteskwargs(opts)
1717 rev = opts.get(b'rev')
1714 rev = opts.get(b'rev')
1718 if rev:
1715 if rev:
1719 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1716 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1720 ctx = scmutil.revsingle(repo, rev)
1717 ctx = scmutil.revsingle(repo, rev)
1721 m = scmutil.match(ctx, (file1,) + pats, opts)
1718 m = scmutil.match(ctx, (file1,) + pats, opts)
1722 fntemplate = opts.pop(b'output', b'')
1719 fntemplate = opts.pop(b'output', b'')
1723 if cmdutil.isstdiofilename(fntemplate):
1720 if cmdutil.isstdiofilename(fntemplate):
1724 fntemplate = b''
1721 fntemplate = b''
1725
1722
1726 if fntemplate:
1723 if fntemplate:
1727 fm = formatter.nullformatter(ui, b'cat', opts)
1724 fm = formatter.nullformatter(ui, b'cat', opts)
1728 else:
1725 else:
1729 ui.pager(b'cat')
1726 ui.pager(b'cat')
1730 fm = ui.formatter(b'cat', opts)
1727 fm = ui.formatter(b'cat', opts)
1731 with fm:
1728 with fm:
1732 return cmdutil.cat(
1729 return cmdutil.cat(
1733 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1730 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1734 )
1731 )
1735
1732
1736
1733
1737 @command(
1734 @command(
1738 b'clone',
1735 b'clone',
1739 [
1736 [
1740 (
1737 (
1741 b'U',
1738 b'U',
1742 b'noupdate',
1739 b'noupdate',
1743 None,
1740 None,
1744 _(
1741 _(
1745 b'the clone will include an empty working '
1742 b'the clone will include an empty working '
1746 b'directory (only a repository)'
1743 b'directory (only a repository)'
1747 ),
1744 ),
1748 ),
1745 ),
1749 (
1746 (
1750 b'u',
1747 b'u',
1751 b'updaterev',
1748 b'updaterev',
1752 b'',
1749 b'',
1753 _(b'revision, tag, or branch to check out'),
1750 _(b'revision, tag, or branch to check out'),
1754 _(b'REV'),
1751 _(b'REV'),
1755 ),
1752 ),
1756 (
1753 (
1757 b'r',
1754 b'r',
1758 b'rev',
1755 b'rev',
1759 [],
1756 [],
1760 _(
1757 _(
1761 b'do not clone everything, but include this changeset'
1758 b'do not clone everything, but include this changeset'
1762 b' and its ancestors'
1759 b' and its ancestors'
1763 ),
1760 ),
1764 _(b'REV'),
1761 _(b'REV'),
1765 ),
1762 ),
1766 (
1763 (
1767 b'b',
1764 b'b',
1768 b'branch',
1765 b'branch',
1769 [],
1766 [],
1770 _(
1767 _(
1771 b'do not clone everything, but include this branch\'s'
1768 b'do not clone everything, but include this branch\'s'
1772 b' changesets and their ancestors'
1769 b' changesets and their ancestors'
1773 ),
1770 ),
1774 _(b'BRANCH'),
1771 _(b'BRANCH'),
1775 ),
1772 ),
1776 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1773 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1777 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1774 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1778 (b'', b'stream', None, _(b'clone with minimal data processing')),
1775 (b'', b'stream', None, _(b'clone with minimal data processing')),
1779 ]
1776 ]
1780 + remoteopts,
1777 + remoteopts,
1781 _(b'[OPTION]... SOURCE [DEST]'),
1778 _(b'[OPTION]... SOURCE [DEST]'),
1782 helpcategory=command.CATEGORY_REPO_CREATION,
1779 helpcategory=command.CATEGORY_REPO_CREATION,
1783 helpbasic=True,
1780 helpbasic=True,
1784 norepo=True,
1781 norepo=True,
1785 )
1782 )
1786 def clone(ui, source, dest=None, **opts):
1783 def clone(ui, source, dest=None, **opts):
1787 """make a copy of an existing repository
1784 """make a copy of an existing repository
1788
1785
1789 Create a copy of an existing repository in a new directory.
1786 Create a copy of an existing repository in a new directory.
1790
1787
1791 If no destination directory name is specified, it defaults to the
1788 If no destination directory name is specified, it defaults to the
1792 basename of the source.
1789 basename of the source.
1793
1790
1794 The location of the source is added to the new repository's
1791 The location of the source is added to the new repository's
1795 ``.hg/hgrc`` file, as the default to be used for future pulls.
1792 ``.hg/hgrc`` file, as the default to be used for future pulls.
1796
1793
1797 Only local paths and ``ssh://`` URLs are supported as
1794 Only local paths and ``ssh://`` URLs are supported as
1798 destinations. For ``ssh://`` destinations, no working directory or
1795 destinations. For ``ssh://`` destinations, no working directory or
1799 ``.hg/hgrc`` will be created on the remote side.
1796 ``.hg/hgrc`` will be created on the remote side.
1800
1797
1801 If the source repository has a bookmark called '@' set, that
1798 If the source repository has a bookmark called '@' set, that
1802 revision will be checked out in the new repository by default.
1799 revision will be checked out in the new repository by default.
1803
1800
1804 To check out a particular version, use -u/--update, or
1801 To check out a particular version, use -u/--update, or
1805 -U/--noupdate to create a clone with no working directory.
1802 -U/--noupdate to create a clone with no working directory.
1806
1803
1807 To pull only a subset of changesets, specify one or more revisions
1804 To pull only a subset of changesets, specify one or more revisions
1808 identifiers with -r/--rev or branches with -b/--branch. The
1805 identifiers with -r/--rev or branches with -b/--branch. The
1809 resulting clone will contain only the specified changesets and
1806 resulting clone will contain only the specified changesets and
1810 their ancestors. These options (or 'clone src#rev dest') imply
1807 their ancestors. These options (or 'clone src#rev dest') imply
1811 --pull, even for local source repositories.
1808 --pull, even for local source repositories.
1812
1809
1813 In normal clone mode, the remote normalizes repository data into a common
1810 In normal clone mode, the remote normalizes repository data into a common
1814 exchange format and the receiving end translates this data into its local
1811 exchange format and the receiving end translates this data into its local
1815 storage format. --stream activates a different clone mode that essentially
1812 storage format. --stream activates a different clone mode that essentially
1816 copies repository files from the remote with minimal data processing. This
1813 copies repository files from the remote with minimal data processing. This
1817 significantly reduces the CPU cost of a clone both remotely and locally.
1814 significantly reduces the CPU cost of a clone both remotely and locally.
1818 However, it often increases the transferred data size by 30-40%. This can
1815 However, it often increases the transferred data size by 30-40%. This can
1819 result in substantially faster clones where I/O throughput is plentiful,
1816 result in substantially faster clones where I/O throughput is plentiful,
1820 especially for larger repositories. A side-effect of --stream clones is
1817 especially for larger repositories. A side-effect of --stream clones is
1821 that storage settings and requirements on the remote are applied locally:
1818 that storage settings and requirements on the remote are applied locally:
1822 a modern client may inherit legacy or inefficient storage used by the
1819 a modern client may inherit legacy or inefficient storage used by the
1823 remote or a legacy Mercurial client may not be able to clone from a
1820 remote or a legacy Mercurial client may not be able to clone from a
1824 modern Mercurial remote.
1821 modern Mercurial remote.
1825
1822
1826 .. note::
1823 .. note::
1827
1824
1828 Specifying a tag will include the tagged changeset but not the
1825 Specifying a tag will include the tagged changeset but not the
1829 changeset containing the tag.
1826 changeset containing the tag.
1830
1827
1831 .. container:: verbose
1828 .. container:: verbose
1832
1829
1833 For efficiency, hardlinks are used for cloning whenever the
1830 For efficiency, hardlinks are used for cloning whenever the
1834 source and destination are on the same filesystem (note this
1831 source and destination are on the same filesystem (note this
1835 applies only to the repository data, not to the working
1832 applies only to the repository data, not to the working
1836 directory). Some filesystems, such as AFS, implement hardlinking
1833 directory). Some filesystems, such as AFS, implement hardlinking
1837 incorrectly, but do not report errors. In these cases, use the
1834 incorrectly, but do not report errors. In these cases, use the
1838 --pull option to avoid hardlinking.
1835 --pull option to avoid hardlinking.
1839
1836
1840 Mercurial will update the working directory to the first applicable
1837 Mercurial will update the working directory to the first applicable
1841 revision from this list:
1838 revision from this list:
1842
1839
1843 a) null if -U or the source repository has no changesets
1840 a) null if -U or the source repository has no changesets
1844 b) if -u . and the source repository is local, the first parent of
1841 b) if -u . and the source repository is local, the first parent of
1845 the source repository's working directory
1842 the source repository's working directory
1846 c) the changeset specified with -u (if a branch name, this means the
1843 c) the changeset specified with -u (if a branch name, this means the
1847 latest head of that branch)
1844 latest head of that branch)
1848 d) the changeset specified with -r
1845 d) the changeset specified with -r
1849 e) the tipmost head specified with -b
1846 e) the tipmost head specified with -b
1850 f) the tipmost head specified with the url#branch source syntax
1847 f) the tipmost head specified with the url#branch source syntax
1851 g) the revision marked with the '@' bookmark, if present
1848 g) the revision marked with the '@' bookmark, if present
1852 h) the tipmost head of the default branch
1849 h) the tipmost head of the default branch
1853 i) tip
1850 i) tip
1854
1851
1855 When cloning from servers that support it, Mercurial may fetch
1852 When cloning from servers that support it, Mercurial may fetch
1856 pre-generated data from a server-advertised URL or inline from the
1853 pre-generated data from a server-advertised URL or inline from the
1857 same stream. When this is done, hooks operating on incoming changesets
1854 same stream. When this is done, hooks operating on incoming changesets
1858 and changegroups may fire more than once, once for each pre-generated
1855 and changegroups may fire more than once, once for each pre-generated
1859 bundle and as well as for any additional remaining data. In addition,
1856 bundle and as well as for any additional remaining data. In addition,
1860 if an error occurs, the repository may be rolled back to a partial
1857 if an error occurs, the repository may be rolled back to a partial
1861 clone. This behavior may change in future releases.
1858 clone. This behavior may change in future releases.
1862 See :hg:`help -e clonebundles` for more.
1859 See :hg:`help -e clonebundles` for more.
1863
1860
1864 Examples:
1861 Examples:
1865
1862
1866 - clone a remote repository to a new directory named hg/::
1863 - clone a remote repository to a new directory named hg/::
1867
1864
1868 hg clone https://www.mercurial-scm.org/repo/hg/
1865 hg clone https://www.mercurial-scm.org/repo/hg/
1869
1866
1870 - create a lightweight local clone::
1867 - create a lightweight local clone::
1871
1868
1872 hg clone project/ project-feature/
1869 hg clone project/ project-feature/
1873
1870
1874 - clone from an absolute path on an ssh server (note double-slash)::
1871 - clone from an absolute path on an ssh server (note double-slash)::
1875
1872
1876 hg clone ssh://user@server//home/projects/alpha/
1873 hg clone ssh://user@server//home/projects/alpha/
1877
1874
1878 - do a streaming clone while checking out a specified version::
1875 - do a streaming clone while checking out a specified version::
1879
1876
1880 hg clone --stream http://server/repo -u 1.5
1877 hg clone --stream http://server/repo -u 1.5
1881
1878
1882 - create a repository without changesets after a particular revision::
1879 - create a repository without changesets after a particular revision::
1883
1880
1884 hg clone -r 04e544 experimental/ good/
1881 hg clone -r 04e544 experimental/ good/
1885
1882
1886 - clone (and track) a particular named branch::
1883 - clone (and track) a particular named branch::
1887
1884
1888 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1885 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1889
1886
1890 See :hg:`help urls` for details on specifying URLs.
1887 See :hg:`help urls` for details on specifying URLs.
1891
1888
1892 Returns 0 on success.
1889 Returns 0 on success.
1893 """
1890 """
1894 opts = pycompat.byteskwargs(opts)
1891 opts = pycompat.byteskwargs(opts)
1895 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1892 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1896 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1893 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1897
1894
1898 # --include/--exclude can come from narrow or sparse.
1895 # --include/--exclude can come from narrow or sparse.
1899 includepats, excludepats = None, None
1896 includepats, excludepats = None, None
1900
1897
1901 # hg.clone() differentiates between None and an empty set. So make sure
1898 # hg.clone() differentiates between None and an empty set. So make sure
1902 # patterns are sets if narrow is requested without patterns.
1899 # patterns are sets if narrow is requested without patterns.
1903 if opts.get(b'narrow'):
1900 if opts.get(b'narrow'):
1904 includepats = set()
1901 includepats = set()
1905 excludepats = set()
1902 excludepats = set()
1906
1903
1907 if opts.get(b'include'):
1904 if opts.get(b'include'):
1908 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1905 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1909 if opts.get(b'exclude'):
1906 if opts.get(b'exclude'):
1910 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1907 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1911
1908
1912 r = hg.clone(
1909 r = hg.clone(
1913 ui,
1910 ui,
1914 opts,
1911 opts,
1915 source,
1912 source,
1916 dest,
1913 dest,
1917 pull=opts.get(b'pull'),
1914 pull=opts.get(b'pull'),
1918 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1915 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1919 revs=opts.get(b'rev'),
1916 revs=opts.get(b'rev'),
1920 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1917 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1921 branch=opts.get(b'branch'),
1918 branch=opts.get(b'branch'),
1922 shareopts=opts.get(b'shareopts'),
1919 shareopts=opts.get(b'shareopts'),
1923 storeincludepats=includepats,
1920 storeincludepats=includepats,
1924 storeexcludepats=excludepats,
1921 storeexcludepats=excludepats,
1925 depth=opts.get(b'depth') or None,
1922 depth=opts.get(b'depth') or None,
1926 )
1923 )
1927
1924
1928 return r is None
1925 return r is None
1929
1926
1930
1927
1931 @command(
1928 @command(
1932 b'commit|ci',
1929 b'commit|ci',
1933 [
1930 [
1934 (
1931 (
1935 b'A',
1932 b'A',
1936 b'addremove',
1933 b'addremove',
1937 None,
1934 None,
1938 _(b'mark new/missing files as added/removed before committing'),
1935 _(b'mark new/missing files as added/removed before committing'),
1939 ),
1936 ),
1940 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1937 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1941 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1938 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1942 (b's', b'secret', None, _(b'use the secret phase for committing')),
1939 (b's', b'secret', None, _(b'use the secret phase for committing')),
1943 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1940 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1944 (
1941 (
1945 b'',
1942 b'',
1946 b'force-close-branch',
1943 b'force-close-branch',
1947 None,
1944 None,
1948 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1945 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1949 ),
1946 ),
1950 (b'i', b'interactive', None, _(b'use interactive mode')),
1947 (b'i', b'interactive', None, _(b'use interactive mode')),
1951 ]
1948 ]
1952 + walkopts
1949 + walkopts
1953 + commitopts
1950 + commitopts
1954 + commitopts2
1951 + commitopts2
1955 + subrepoopts,
1952 + subrepoopts,
1956 _(b'[OPTION]... [FILE]...'),
1953 _(b'[OPTION]... [FILE]...'),
1957 helpcategory=command.CATEGORY_COMMITTING,
1954 helpcategory=command.CATEGORY_COMMITTING,
1958 helpbasic=True,
1955 helpbasic=True,
1959 inferrepo=True,
1956 inferrepo=True,
1960 )
1957 )
1961 def commit(ui, repo, *pats, **opts):
1958 def commit(ui, repo, *pats, **opts):
1962 """commit the specified files or all outstanding changes
1959 """commit the specified files or all outstanding changes
1963
1960
1964 Commit changes to the given files into the repository. Unlike a
1961 Commit changes to the given files into the repository. Unlike a
1965 centralized SCM, this operation is a local operation. See
1962 centralized SCM, this operation is a local operation. See
1966 :hg:`push` for a way to actively distribute your changes.
1963 :hg:`push` for a way to actively distribute your changes.
1967
1964
1968 If a list of files is omitted, all changes reported by :hg:`status`
1965 If a list of files is omitted, all changes reported by :hg:`status`
1969 will be committed.
1966 will be committed.
1970
1967
1971 If you are committing the result of a merge, do not provide any
1968 If you are committing the result of a merge, do not provide any
1972 filenames or -I/-X filters.
1969 filenames or -I/-X filters.
1973
1970
1974 If no commit message is specified, Mercurial starts your
1971 If no commit message is specified, Mercurial starts your
1975 configured editor where you can enter a message. In case your
1972 configured editor where you can enter a message. In case your
1976 commit fails, you will find a backup of your message in
1973 commit fails, you will find a backup of your message in
1977 ``.hg/last-message.txt``.
1974 ``.hg/last-message.txt``.
1978
1975
1979 The --close-branch flag can be used to mark the current branch
1976 The --close-branch flag can be used to mark the current branch
1980 head closed. When all heads of a branch are closed, the branch
1977 head closed. When all heads of a branch are closed, the branch
1981 will be considered closed and no longer listed.
1978 will be considered closed and no longer listed.
1982
1979
1983 The --amend flag can be used to amend the parent of the
1980 The --amend flag can be used to amend the parent of the
1984 working directory with a new commit that contains the changes
1981 working directory with a new commit that contains the changes
1985 in the parent in addition to those currently reported by :hg:`status`,
1982 in the parent in addition to those currently reported by :hg:`status`,
1986 if there are any. The old commit is stored in a backup bundle in
1983 if there are any. The old commit is stored in a backup bundle in
1987 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1984 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1988 on how to restore it).
1985 on how to restore it).
1989
1986
1990 Message, user and date are taken from the amended commit unless
1987 Message, user and date are taken from the amended commit unless
1991 specified. When a message isn't specified on the command line,
1988 specified. When a message isn't specified on the command line,
1992 the editor will open with the message of the amended commit.
1989 the editor will open with the message of the amended commit.
1993
1990
1994 It is not possible to amend public changesets (see :hg:`help phases`)
1991 It is not possible to amend public changesets (see :hg:`help phases`)
1995 or changesets that have children.
1992 or changesets that have children.
1996
1993
1997 See :hg:`help dates` for a list of formats valid for -d/--date.
1994 See :hg:`help dates` for a list of formats valid for -d/--date.
1998
1995
1999 Returns 0 on success, 1 if nothing changed.
1996 Returns 0 on success, 1 if nothing changed.
2000
1997
2001 .. container:: verbose
1998 .. container:: verbose
2002
1999
2003 Examples:
2000 Examples:
2004
2001
2005 - commit all files ending in .py::
2002 - commit all files ending in .py::
2006
2003
2007 hg commit --include "set:**.py"
2004 hg commit --include "set:**.py"
2008
2005
2009 - commit all non-binary files::
2006 - commit all non-binary files::
2010
2007
2011 hg commit --exclude "set:binary()"
2008 hg commit --exclude "set:binary()"
2012
2009
2013 - amend the current commit and set the date to now::
2010 - amend the current commit and set the date to now::
2014
2011
2015 hg commit --amend --date now
2012 hg commit --amend --date now
2016 """
2013 """
2017 with repo.wlock(), repo.lock():
2014 with repo.wlock(), repo.lock():
2018 return _docommit(ui, repo, *pats, **opts)
2015 return _docommit(ui, repo, *pats, **opts)
2019
2016
2020
2017
2021 def _docommit(ui, repo, *pats, **opts):
2018 def _docommit(ui, repo, *pats, **opts):
2022 if opts.get('interactive'):
2019 if opts.get('interactive'):
2023 opts.pop('interactive')
2020 opts.pop('interactive')
2024 ret = cmdutil.dorecord(
2021 ret = cmdutil.dorecord(
2025 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2022 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2026 )
2023 )
2027 # ret can be 0 (no changes to record) or the value returned by
2024 # ret can be 0 (no changes to record) or the value returned by
2028 # commit(), 1 if nothing changed or None on success.
2025 # commit(), 1 if nothing changed or None on success.
2029 return 1 if ret == 0 else ret
2026 return 1 if ret == 0 else ret
2030
2027
2031 opts = pycompat.byteskwargs(opts)
2028 opts = pycompat.byteskwargs(opts)
2032 if opts.get(b'subrepos'):
2029 if opts.get(b'subrepos'):
2033 if opts.get(b'amend'):
2030 if opts.get(b'amend'):
2034 raise error.Abort(_(b'cannot amend with --subrepos'))
2031 raise error.Abort(_(b'cannot amend with --subrepos'))
2035 # Let --subrepos on the command line override config setting.
2032 # Let --subrepos on the command line override config setting.
2036 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2033 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2037
2034
2038 cmdutil.checkunfinished(repo, commit=True)
2035 cmdutil.checkunfinished(repo, commit=True)
2039
2036
2040 branch = repo[None].branch()
2037 branch = repo[None].branch()
2041 bheads = repo.branchheads(branch)
2038 bheads = repo.branchheads(branch)
2042
2039
2043 extra = {}
2040 extra = {}
2044 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2041 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2045 extra[b'close'] = b'1'
2042 extra[b'close'] = b'1'
2046
2043
2047 if repo[b'.'].closesbranch():
2044 if repo[b'.'].closesbranch():
2048 raise error.Abort(
2045 raise error.Abort(
2049 _(b'current revision is already a branch closing head')
2046 _(b'current revision is already a branch closing head')
2050 )
2047 )
2051 elif not bheads:
2048 elif not bheads:
2052 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2049 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2053 elif (
2050 elif (
2054 branch == repo[b'.'].branch()
2051 branch == repo[b'.'].branch()
2055 and repo[b'.'].node() not in bheads
2052 and repo[b'.'].node() not in bheads
2056 and not opts.get(b'force_close_branch')
2053 and not opts.get(b'force_close_branch')
2057 ):
2054 ):
2058 hint = _(
2055 hint = _(
2059 b'use --force-close-branch to close branch from a non-head'
2056 b'use --force-close-branch to close branch from a non-head'
2060 b' changeset'
2057 b' changeset'
2061 )
2058 )
2062 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2059 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2063 elif opts.get(b'amend'):
2060 elif opts.get(b'amend'):
2064 if (
2061 if (
2065 repo[b'.'].p1().branch() != branch
2062 repo[b'.'].p1().branch() != branch
2066 and repo[b'.'].p2().branch() != branch
2063 and repo[b'.'].p2().branch() != branch
2067 ):
2064 ):
2068 raise error.Abort(_(b'can only close branch heads'))
2065 raise error.Abort(_(b'can only close branch heads'))
2069
2066
2070 if opts.get(b'amend'):
2067 if opts.get(b'amend'):
2071 if ui.configbool(b'ui', b'commitsubrepos'):
2068 if ui.configbool(b'ui', b'commitsubrepos'):
2072 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2069 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2073
2070
2074 old = repo[b'.']
2071 old = repo[b'.']
2075 rewriteutil.precheck(repo, [old.rev()], b'amend')
2072 rewriteutil.precheck(repo, [old.rev()], b'amend')
2076
2073
2077 # Currently histedit gets confused if an amend happens while histedit
2074 # Currently histedit gets confused if an amend happens while histedit
2078 # is in progress. Since we have a checkunfinished command, we are
2075 # is in progress. Since we have a checkunfinished command, we are
2079 # temporarily honoring it.
2076 # temporarily honoring it.
2080 #
2077 #
2081 # Note: eventually this guard will be removed. Please do not expect
2078 # Note: eventually this guard will be removed. Please do not expect
2082 # this behavior to remain.
2079 # this behavior to remain.
2083 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2080 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2084 cmdutil.checkunfinished(repo)
2081 cmdutil.checkunfinished(repo)
2085
2082
2086 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2083 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2087 if node == old.node():
2084 if node == old.node():
2088 ui.status(_(b"nothing changed\n"))
2085 ui.status(_(b"nothing changed\n"))
2089 return 1
2086 return 1
2090 else:
2087 else:
2091
2088
2092 def commitfunc(ui, repo, message, match, opts):
2089 def commitfunc(ui, repo, message, match, opts):
2093 overrides = {}
2090 overrides = {}
2094 if opts.get(b'secret'):
2091 if opts.get(b'secret'):
2095 overrides[(b'phases', b'new-commit')] = b'secret'
2092 overrides[(b'phases', b'new-commit')] = b'secret'
2096
2093
2097 baseui = repo.baseui
2094 baseui = repo.baseui
2098 with baseui.configoverride(overrides, b'commit'):
2095 with baseui.configoverride(overrides, b'commit'):
2099 with ui.configoverride(overrides, b'commit'):
2096 with ui.configoverride(overrides, b'commit'):
2100 editform = cmdutil.mergeeditform(
2097 editform = cmdutil.mergeeditform(
2101 repo[None], b'commit.normal'
2098 repo[None], b'commit.normal'
2102 )
2099 )
2103 editor = cmdutil.getcommiteditor(
2100 editor = cmdutil.getcommiteditor(
2104 editform=editform, **pycompat.strkwargs(opts)
2101 editform=editform, **pycompat.strkwargs(opts)
2105 )
2102 )
2106 return repo.commit(
2103 return repo.commit(
2107 message,
2104 message,
2108 opts.get(b'user'),
2105 opts.get(b'user'),
2109 opts.get(b'date'),
2106 opts.get(b'date'),
2110 match,
2107 match,
2111 editor=editor,
2108 editor=editor,
2112 extra=extra,
2109 extra=extra,
2113 )
2110 )
2114
2111
2115 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2112 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2116
2113
2117 if not node:
2114 if not node:
2118 stat = cmdutil.postcommitstatus(repo, pats, opts)
2115 stat = cmdutil.postcommitstatus(repo, pats, opts)
2119 if stat.deleted:
2116 if stat.deleted:
2120 ui.status(
2117 ui.status(
2121 _(
2118 _(
2122 b"nothing changed (%d missing files, see "
2119 b"nothing changed (%d missing files, see "
2123 b"'hg status')\n"
2120 b"'hg status')\n"
2124 )
2121 )
2125 % len(stat.deleted)
2122 % len(stat.deleted)
2126 )
2123 )
2127 else:
2124 else:
2128 ui.status(_(b"nothing changed\n"))
2125 ui.status(_(b"nothing changed\n"))
2129 return 1
2126 return 1
2130
2127
2131 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2128 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2132
2129
2133 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2130 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2134 status(
2131 status(
2135 ui,
2132 ui,
2136 repo,
2133 repo,
2137 modified=True,
2134 modified=True,
2138 added=True,
2135 added=True,
2139 removed=True,
2136 removed=True,
2140 deleted=True,
2137 deleted=True,
2141 unknown=True,
2138 unknown=True,
2142 subrepos=opts.get(b'subrepos'),
2139 subrepos=opts.get(b'subrepos'),
2143 )
2140 )
2144
2141
2145
2142
2146 @command(
2143 @command(
2147 b'config|showconfig|debugconfig',
2144 b'config|showconfig|debugconfig',
2148 [
2145 [
2149 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2146 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2150 (b'e', b'edit', None, _(b'edit user config')),
2147 (b'e', b'edit', None, _(b'edit user config')),
2151 (b'l', b'local', None, _(b'edit repository config')),
2148 (b'l', b'local', None, _(b'edit repository config')),
2152 (b'g', b'global', None, _(b'edit global config')),
2149 (b'g', b'global', None, _(b'edit global config')),
2153 ]
2150 ]
2154 + formatteropts,
2151 + formatteropts,
2155 _(b'[-u] [NAME]...'),
2152 _(b'[-u] [NAME]...'),
2156 helpcategory=command.CATEGORY_HELP,
2153 helpcategory=command.CATEGORY_HELP,
2157 optionalrepo=True,
2154 optionalrepo=True,
2158 intents={INTENT_READONLY},
2155 intents={INTENT_READONLY},
2159 )
2156 )
2160 def config(ui, repo, *values, **opts):
2157 def config(ui, repo, *values, **opts):
2161 """show combined config settings from all hgrc files
2158 """show combined config settings from all hgrc files
2162
2159
2163 With no arguments, print names and values of all config items.
2160 With no arguments, print names and values of all config items.
2164
2161
2165 With one argument of the form section.name, print just the value
2162 With one argument of the form section.name, print just the value
2166 of that config item.
2163 of that config item.
2167
2164
2168 With multiple arguments, print names and values of all config
2165 With multiple arguments, print names and values of all config
2169 items with matching section names or section.names.
2166 items with matching section names or section.names.
2170
2167
2171 With --edit, start an editor on the user-level config file. With
2168 With --edit, start an editor on the user-level config file. With
2172 --global, edit the system-wide config file. With --local, edit the
2169 --global, edit the system-wide config file. With --local, edit the
2173 repository-level config file.
2170 repository-level config file.
2174
2171
2175 With --debug, the source (filename and line number) is printed
2172 With --debug, the source (filename and line number) is printed
2176 for each config item.
2173 for each config item.
2177
2174
2178 See :hg:`help config` for more information about config files.
2175 See :hg:`help config` for more information about config files.
2179
2176
2180 .. container:: verbose
2177 .. container:: verbose
2181
2178
2182 Template:
2179 Template:
2183
2180
2184 The following keywords are supported. See also :hg:`help templates`.
2181 The following keywords are supported. See also :hg:`help templates`.
2185
2182
2186 :name: String. Config name.
2183 :name: String. Config name.
2187 :source: String. Filename and line number where the item is defined.
2184 :source: String. Filename and line number where the item is defined.
2188 :value: String. Config value.
2185 :value: String. Config value.
2189
2186
2190 Returns 0 on success, 1 if NAME does not exist.
2187 Returns 0 on success, 1 if NAME does not exist.
2191
2188
2192 """
2189 """
2193
2190
2194 opts = pycompat.byteskwargs(opts)
2191 opts = pycompat.byteskwargs(opts)
2195 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2192 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2196 if opts.get(b'local') and opts.get(b'global'):
2193 if opts.get(b'local') and opts.get(b'global'):
2197 raise error.Abort(_(b"can't use --local and --global together"))
2194 raise error.Abort(_(b"can't use --local and --global together"))
2198
2195
2199 if opts.get(b'local'):
2196 if opts.get(b'local'):
2200 if not repo:
2197 if not repo:
2201 raise error.Abort(_(b"can't use --local outside a repository"))
2198 raise error.Abort(_(b"can't use --local outside a repository"))
2202 paths = [repo.vfs.join(b'hgrc')]
2199 paths = [repo.vfs.join(b'hgrc')]
2203 elif opts.get(b'global'):
2200 elif opts.get(b'global'):
2204 paths = rcutil.systemrcpath()
2201 paths = rcutil.systemrcpath()
2205 else:
2202 else:
2206 paths = rcutil.userrcpath()
2203 paths = rcutil.userrcpath()
2207
2204
2208 for f in paths:
2205 for f in paths:
2209 if os.path.exists(f):
2206 if os.path.exists(f):
2210 break
2207 break
2211 else:
2208 else:
2212 if opts.get(b'global'):
2209 if opts.get(b'global'):
2213 samplehgrc = uimod.samplehgrcs[b'global']
2210 samplehgrc = uimod.samplehgrcs[b'global']
2214 elif opts.get(b'local'):
2211 elif opts.get(b'local'):
2215 samplehgrc = uimod.samplehgrcs[b'local']
2212 samplehgrc = uimod.samplehgrcs[b'local']
2216 else:
2213 else:
2217 samplehgrc = uimod.samplehgrcs[b'user']
2214 samplehgrc = uimod.samplehgrcs[b'user']
2218
2215
2219 f = paths[0]
2216 f = paths[0]
2220 fp = open(f, b"wb")
2217 fp = open(f, b"wb")
2221 fp.write(util.tonativeeol(samplehgrc))
2218 fp.write(util.tonativeeol(samplehgrc))
2222 fp.close()
2219 fp.close()
2223
2220
2224 editor = ui.geteditor()
2221 editor = ui.geteditor()
2225 ui.system(
2222 ui.system(
2226 b"%s \"%s\"" % (editor, f),
2223 b"%s \"%s\"" % (editor, f),
2227 onerr=error.Abort,
2224 onerr=error.Abort,
2228 errprefix=_(b"edit failed"),
2225 errprefix=_(b"edit failed"),
2229 blockedtag=b'config_edit',
2226 blockedtag=b'config_edit',
2230 )
2227 )
2231 return
2228 return
2232 ui.pager(b'config')
2229 ui.pager(b'config')
2233 fm = ui.formatter(b'config', opts)
2230 fm = ui.formatter(b'config', opts)
2234 for t, f in rcutil.rccomponents():
2231 for t, f in rcutil.rccomponents():
2235 if t == b'path':
2232 if t == b'path':
2236 ui.debug(b'read config from: %s\n' % f)
2233 ui.debug(b'read config from: %s\n' % f)
2237 elif t == b'items':
2234 elif t == b'items':
2238 for section, name, value, source in f:
2235 for section, name, value, source in f:
2239 ui.debug(b'set config by: %s\n' % source)
2236 ui.debug(b'set config by: %s\n' % source)
2240 else:
2237 else:
2241 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2238 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2242 untrusted = bool(opts.get(b'untrusted'))
2239 untrusted = bool(opts.get(b'untrusted'))
2243
2240
2244 selsections = selentries = []
2241 selsections = selentries = []
2245 if values:
2242 if values:
2246 selsections = [v for v in values if b'.' not in v]
2243 selsections = [v for v in values if b'.' not in v]
2247 selentries = [v for v in values if b'.' in v]
2244 selentries = [v for v in values if b'.' in v]
2248 uniquesel = len(selentries) == 1 and not selsections
2245 uniquesel = len(selentries) == 1 and not selsections
2249 selsections = set(selsections)
2246 selsections = set(selsections)
2250 selentries = set(selentries)
2247 selentries = set(selentries)
2251
2248
2252 matched = False
2249 matched = False
2253 for section, name, value in ui.walkconfig(untrusted=untrusted):
2250 for section, name, value in ui.walkconfig(untrusted=untrusted):
2254 source = ui.configsource(section, name, untrusted)
2251 source = ui.configsource(section, name, untrusted)
2255 value = pycompat.bytestr(value)
2252 value = pycompat.bytestr(value)
2256 defaultvalue = ui.configdefault(section, name)
2253 defaultvalue = ui.configdefault(section, name)
2257 if fm.isplain():
2254 if fm.isplain():
2258 source = source or b'none'
2255 source = source or b'none'
2259 value = value.replace(b'\n', b'\\n')
2256 value = value.replace(b'\n', b'\\n')
2260 entryname = section + b'.' + name
2257 entryname = section + b'.' + name
2261 if values and not (section in selsections or entryname in selentries):
2258 if values and not (section in selsections or entryname in selentries):
2262 continue
2259 continue
2263 fm.startitem()
2260 fm.startitem()
2264 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2261 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2265 if uniquesel:
2262 if uniquesel:
2266 fm.data(name=entryname)
2263 fm.data(name=entryname)
2267 fm.write(b'value', b'%s\n', value)
2264 fm.write(b'value', b'%s\n', value)
2268 else:
2265 else:
2269 fm.write(b'name value', b'%s=%s\n', entryname, value)
2266 fm.write(b'name value', b'%s=%s\n', entryname, value)
2270 if formatter.isprintable(defaultvalue):
2267 if formatter.isprintable(defaultvalue):
2271 fm.data(defaultvalue=defaultvalue)
2268 fm.data(defaultvalue=defaultvalue)
2272 elif isinstance(defaultvalue, list) and all(
2269 elif isinstance(defaultvalue, list) and all(
2273 formatter.isprintable(e) for e in defaultvalue
2270 formatter.isprintable(e) for e in defaultvalue
2274 ):
2271 ):
2275 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2272 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2276 # TODO: no idea how to process unsupported defaultvalue types
2273 # TODO: no idea how to process unsupported defaultvalue types
2277 matched = True
2274 matched = True
2278 fm.end()
2275 fm.end()
2279 if matched:
2276 if matched:
2280 return 0
2277 return 0
2281 return 1
2278 return 1
2282
2279
2283
2280
2284 @command(
2281 @command(
2285 b'continue',
2282 b'continue',
2286 dryrunopts,
2283 dryrunopts,
2287 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2284 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2288 helpbasic=True,
2285 helpbasic=True,
2289 )
2286 )
2290 def continuecmd(ui, repo, **opts):
2287 def continuecmd(ui, repo, **opts):
2291 """resumes an interrupted operation (EXPERIMENTAL)
2288 """resumes an interrupted operation (EXPERIMENTAL)
2292
2289
2293 Finishes a multistep operation like graft, histedit, rebase, merge,
2290 Finishes a multistep operation like graft, histedit, rebase, merge,
2294 and unshelve if they are in an interrupted state.
2291 and unshelve if they are in an interrupted state.
2295
2292
2296 use --dry-run/-n to dry run the command.
2293 use --dry-run/-n to dry run the command.
2297 """
2294 """
2298 dryrun = opts.get('dry_run')
2295 dryrun = opts.get('dry_run')
2299 contstate = cmdutil.getunfinishedstate(repo)
2296 contstate = cmdutil.getunfinishedstate(repo)
2300 if not contstate:
2297 if not contstate:
2301 raise error.Abort(_(b'no operation in progress'))
2298 raise error.Abort(_(b'no operation in progress'))
2302 if not contstate.continuefunc:
2299 if not contstate.continuefunc:
2303 raise error.Abort(
2300 raise error.Abort(
2304 (
2301 (
2305 _(b"%s in progress but does not support 'hg continue'")
2302 _(b"%s in progress but does not support 'hg continue'")
2306 % (contstate._opname)
2303 % (contstate._opname)
2307 ),
2304 ),
2308 hint=contstate.continuemsg(),
2305 hint=contstate.continuemsg(),
2309 )
2306 )
2310 if dryrun:
2307 if dryrun:
2311 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2308 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2312 return
2309 return
2313 return contstate.continuefunc(ui, repo)
2310 return contstate.continuefunc(ui, repo)
2314
2311
2315
2312
2316 @command(
2313 @command(
2317 b'copy|cp',
2314 b'copy|cp',
2318 [
2315 [
2319 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2316 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2320 (
2317 (
2321 b'f',
2318 b'f',
2322 b'force',
2319 b'force',
2323 None,
2320 None,
2324 _(b'forcibly copy over an existing managed file'),
2321 _(b'forcibly copy over an existing managed file'),
2325 ),
2322 ),
2326 ]
2323 ]
2327 + walkopts
2324 + walkopts
2328 + dryrunopts,
2325 + dryrunopts,
2329 _(b'[OPTION]... SOURCE... DEST'),
2326 _(b'[OPTION]... SOURCE... DEST'),
2330 helpcategory=command.CATEGORY_FILE_CONTENTS,
2327 helpcategory=command.CATEGORY_FILE_CONTENTS,
2331 )
2328 )
2332 def copy(ui, repo, *pats, **opts):
2329 def copy(ui, repo, *pats, **opts):
2333 """mark files as copied for the next commit
2330 """mark files as copied for the next commit
2334
2331
2335 Mark dest as having copies of source files. If dest is a
2332 Mark dest as having copies of source files. If dest is a
2336 directory, copies are put in that directory. If dest is a file,
2333 directory, copies are put in that directory. If dest is a file,
2337 the source must be a single file.
2334 the source must be a single file.
2338
2335
2339 By default, this command copies the contents of files as they
2336 By default, this command copies the contents of files as they
2340 exist in the working directory. If invoked with -A/--after, the
2337 exist in the working directory. If invoked with -A/--after, the
2341 operation is recorded, but no copying is performed.
2338 operation is recorded, but no copying is performed.
2342
2339
2343 This command takes effect with the next commit. To undo a copy
2340 This command takes effect with the next commit. To undo a copy
2344 before that, see :hg:`revert`.
2341 before that, see :hg:`revert`.
2345
2342
2346 Returns 0 on success, 1 if errors are encountered.
2343 Returns 0 on success, 1 if errors are encountered.
2347 """
2344 """
2348 opts = pycompat.byteskwargs(opts)
2345 opts = pycompat.byteskwargs(opts)
2349 with repo.wlock(False):
2346 with repo.wlock(False):
2350 return cmdutil.copy(ui, repo, pats, opts)
2347 return cmdutil.copy(ui, repo, pats, opts)
2351
2348
2352
2349
2353 @command(
2350 @command(
2354 b'debugcommands',
2351 b'debugcommands',
2355 [],
2352 [],
2356 _(b'[COMMAND]'),
2353 _(b'[COMMAND]'),
2357 helpcategory=command.CATEGORY_HELP,
2354 helpcategory=command.CATEGORY_HELP,
2358 norepo=True,
2355 norepo=True,
2359 )
2356 )
2360 def debugcommands(ui, cmd=b'', *args):
2357 def debugcommands(ui, cmd=b'', *args):
2361 """list all available commands and options"""
2358 """list all available commands and options"""
2362 for cmd, vals in sorted(pycompat.iteritems(table)):
2359 for cmd, vals in sorted(pycompat.iteritems(table)):
2363 cmd = cmd.split(b'|')[0]
2360 cmd = cmd.split(b'|')[0]
2364 opts = b', '.join([i[1] for i in vals[1]])
2361 opts = b', '.join([i[1] for i in vals[1]])
2365 ui.write(b'%s: %s\n' % (cmd, opts))
2362 ui.write(b'%s: %s\n' % (cmd, opts))
2366
2363
2367
2364
2368 @command(
2365 @command(
2369 b'debugcomplete',
2366 b'debugcomplete',
2370 [(b'o', b'options', None, _(b'show the command options'))],
2367 [(b'o', b'options', None, _(b'show the command options'))],
2371 _(b'[-o] CMD'),
2368 _(b'[-o] CMD'),
2372 helpcategory=command.CATEGORY_HELP,
2369 helpcategory=command.CATEGORY_HELP,
2373 norepo=True,
2370 norepo=True,
2374 )
2371 )
2375 def debugcomplete(ui, cmd=b'', **opts):
2372 def debugcomplete(ui, cmd=b'', **opts):
2376 """returns the completion list associated with the given command"""
2373 """returns the completion list associated with the given command"""
2377
2374
2378 if opts.get('options'):
2375 if opts.get('options'):
2379 options = []
2376 options = []
2380 otables = [globalopts]
2377 otables = [globalopts]
2381 if cmd:
2378 if cmd:
2382 aliases, entry = cmdutil.findcmd(cmd, table, False)
2379 aliases, entry = cmdutil.findcmd(cmd, table, False)
2383 otables.append(entry[1])
2380 otables.append(entry[1])
2384 for t in otables:
2381 for t in otables:
2385 for o in t:
2382 for o in t:
2386 if b"(DEPRECATED)" in o[3]:
2383 if b"(DEPRECATED)" in o[3]:
2387 continue
2384 continue
2388 if o[0]:
2385 if o[0]:
2389 options.append(b'-%s' % o[0])
2386 options.append(b'-%s' % o[0])
2390 options.append(b'--%s' % o[1])
2387 options.append(b'--%s' % o[1])
2391 ui.write(b"%s\n" % b"\n".join(options))
2388 ui.write(b"%s\n" % b"\n".join(options))
2392 return
2389 return
2393
2390
2394 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2391 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2395 if ui.verbose:
2392 if ui.verbose:
2396 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2393 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2397 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2394 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2398
2395
2399
2396
2400 @command(
2397 @command(
2401 b'diff',
2398 b'diff',
2402 [
2399 [
2403 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2400 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2404 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2401 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2405 ]
2402 ]
2406 + diffopts
2403 + diffopts
2407 + diffopts2
2404 + diffopts2
2408 + walkopts
2405 + walkopts
2409 + subrepoopts,
2406 + subrepoopts,
2410 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2407 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2411 helpcategory=command.CATEGORY_FILE_CONTENTS,
2408 helpcategory=command.CATEGORY_FILE_CONTENTS,
2412 helpbasic=True,
2409 helpbasic=True,
2413 inferrepo=True,
2410 inferrepo=True,
2414 intents={INTENT_READONLY},
2411 intents={INTENT_READONLY},
2415 )
2412 )
2416 def diff(ui, repo, *pats, **opts):
2413 def diff(ui, repo, *pats, **opts):
2417 """diff repository (or selected files)
2414 """diff repository (or selected files)
2418
2415
2419 Show differences between revisions for the specified files.
2416 Show differences between revisions for the specified files.
2420
2417
2421 Differences between files are shown using the unified diff format.
2418 Differences between files are shown using the unified diff format.
2422
2419
2423 .. note::
2420 .. note::
2424
2421
2425 :hg:`diff` may generate unexpected results for merges, as it will
2422 :hg:`diff` may generate unexpected results for merges, as it will
2426 default to comparing against the working directory's first
2423 default to comparing against the working directory's first
2427 parent changeset if no revisions are specified.
2424 parent changeset if no revisions are specified.
2428
2425
2429 When two revision arguments are given, then changes are shown
2426 When two revision arguments are given, then changes are shown
2430 between those revisions. If only one revision is specified then
2427 between those revisions. If only one revision is specified then
2431 that revision is compared to the working directory, and, when no
2428 that revision is compared to the working directory, and, when no
2432 revisions are specified, the working directory files are compared
2429 revisions are specified, the working directory files are compared
2433 to its first parent.
2430 to its first parent.
2434
2431
2435 Alternatively you can specify -c/--change with a revision to see
2432 Alternatively you can specify -c/--change with a revision to see
2436 the changes in that changeset relative to its first parent.
2433 the changes in that changeset relative to its first parent.
2437
2434
2438 Without the -a/--text option, diff will avoid generating diffs of
2435 Without the -a/--text option, diff will avoid generating diffs of
2439 files it detects as binary. With -a, diff will generate a diff
2436 files it detects as binary. With -a, diff will generate a diff
2440 anyway, probably with undesirable results.
2437 anyway, probably with undesirable results.
2441
2438
2442 Use the -g/--git option to generate diffs in the git extended diff
2439 Use the -g/--git option to generate diffs in the git extended diff
2443 format. For more information, read :hg:`help diffs`.
2440 format. For more information, read :hg:`help diffs`.
2444
2441
2445 .. container:: verbose
2442 .. container:: verbose
2446
2443
2447 Examples:
2444 Examples:
2448
2445
2449 - compare a file in the current working directory to its parent::
2446 - compare a file in the current working directory to its parent::
2450
2447
2451 hg diff foo.c
2448 hg diff foo.c
2452
2449
2453 - compare two historical versions of a directory, with rename info::
2450 - compare two historical versions of a directory, with rename info::
2454
2451
2455 hg diff --git -r 1.0:1.2 lib/
2452 hg diff --git -r 1.0:1.2 lib/
2456
2453
2457 - get change stats relative to the last change on some date::
2454 - get change stats relative to the last change on some date::
2458
2455
2459 hg diff --stat -r "date('may 2')"
2456 hg diff --stat -r "date('may 2')"
2460
2457
2461 - diff all newly-added files that contain a keyword::
2458 - diff all newly-added files that contain a keyword::
2462
2459
2463 hg diff "set:added() and grep(GNU)"
2460 hg diff "set:added() and grep(GNU)"
2464
2461
2465 - compare a revision and its parents::
2462 - compare a revision and its parents::
2466
2463
2467 hg diff -c 9353 # compare against first parent
2464 hg diff -c 9353 # compare against first parent
2468 hg diff -r 9353^:9353 # same using revset syntax
2465 hg diff -r 9353^:9353 # same using revset syntax
2469 hg diff -r 9353^2:9353 # compare against the second parent
2466 hg diff -r 9353^2:9353 # compare against the second parent
2470
2467
2471 Returns 0 on success.
2468 Returns 0 on success.
2472 """
2469 """
2473
2470
2474 opts = pycompat.byteskwargs(opts)
2471 opts = pycompat.byteskwargs(opts)
2475 revs = opts.get(b'rev')
2472 revs = opts.get(b'rev')
2476 change = opts.get(b'change')
2473 change = opts.get(b'change')
2477 stat = opts.get(b'stat')
2474 stat = opts.get(b'stat')
2478 reverse = opts.get(b'reverse')
2475 reverse = opts.get(b'reverse')
2479
2476
2480 if revs and change:
2477 if revs and change:
2481 msg = _(b'cannot specify --rev and --change at the same time')
2478 msg = _(b'cannot specify --rev and --change at the same time')
2482 raise error.Abort(msg)
2479 raise error.Abort(msg)
2483 elif change:
2480 elif change:
2484 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2481 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2485 ctx2 = scmutil.revsingle(repo, change, None)
2482 ctx2 = scmutil.revsingle(repo, change, None)
2486 ctx1 = ctx2.p1()
2483 ctx1 = ctx2.p1()
2487 else:
2484 else:
2488 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2485 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2489 ctx1, ctx2 = scmutil.revpair(repo, revs)
2486 ctx1, ctx2 = scmutil.revpair(repo, revs)
2490 node1, node2 = ctx1.node(), ctx2.node()
2487 node1, node2 = ctx1.node(), ctx2.node()
2491
2488
2492 if reverse:
2489 if reverse:
2493 node1, node2 = node2, node1
2490 node1, node2 = node2, node1
2494
2491
2495 diffopts = patch.diffallopts(ui, opts)
2492 diffopts = patch.diffallopts(ui, opts)
2496 m = scmutil.match(ctx2, pats, opts)
2493 m = scmutil.match(ctx2, pats, opts)
2497 m = repo.narrowmatch(m)
2494 m = repo.narrowmatch(m)
2498 ui.pager(b'diff')
2495 ui.pager(b'diff')
2499 logcmdutil.diffordiffstat(
2496 logcmdutil.diffordiffstat(
2500 ui,
2497 ui,
2501 repo,
2498 repo,
2502 diffopts,
2499 diffopts,
2503 node1,
2500 node1,
2504 node2,
2501 node2,
2505 m,
2502 m,
2506 stat=stat,
2503 stat=stat,
2507 listsubrepos=opts.get(b'subrepos'),
2504 listsubrepos=opts.get(b'subrepos'),
2508 root=opts.get(b'root'),
2505 root=opts.get(b'root'),
2509 )
2506 )
2510
2507
2511
2508
2512 @command(
2509 @command(
2513 b'export',
2510 b'export',
2514 [
2511 [
2515 (
2512 (
2516 b'B',
2513 b'B',
2517 b'bookmark',
2514 b'bookmark',
2518 b'',
2515 b'',
2519 _(b'export changes only reachable by given bookmark'),
2516 _(b'export changes only reachable by given bookmark'),
2520 _(b'BOOKMARK'),
2517 _(b'BOOKMARK'),
2521 ),
2518 ),
2522 (
2519 (
2523 b'o',
2520 b'o',
2524 b'output',
2521 b'output',
2525 b'',
2522 b'',
2526 _(b'print output to file with formatted name'),
2523 _(b'print output to file with formatted name'),
2527 _(b'FORMAT'),
2524 _(b'FORMAT'),
2528 ),
2525 ),
2529 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2526 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2530 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2527 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2531 ]
2528 ]
2532 + diffopts
2529 + diffopts
2533 + formatteropts,
2530 + formatteropts,
2534 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2531 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2535 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2532 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2536 helpbasic=True,
2533 helpbasic=True,
2537 intents={INTENT_READONLY},
2534 intents={INTENT_READONLY},
2538 )
2535 )
2539 def export(ui, repo, *changesets, **opts):
2536 def export(ui, repo, *changesets, **opts):
2540 """dump the header and diffs for one or more changesets
2537 """dump the header and diffs for one or more changesets
2541
2538
2542 Print the changeset header and diffs for one or more revisions.
2539 Print the changeset header and diffs for one or more revisions.
2543 If no revision is given, the parent of the working directory is used.
2540 If no revision is given, the parent of the working directory is used.
2544
2541
2545 The information shown in the changeset header is: author, date,
2542 The information shown in the changeset header is: author, date,
2546 branch name (if non-default), changeset hash, parent(s) and commit
2543 branch name (if non-default), changeset hash, parent(s) and commit
2547 comment.
2544 comment.
2548
2545
2549 .. note::
2546 .. note::
2550
2547
2551 :hg:`export` may generate unexpected diff output for merge
2548 :hg:`export` may generate unexpected diff output for merge
2552 changesets, as it will compare the merge changeset against its
2549 changesets, as it will compare the merge changeset against its
2553 first parent only.
2550 first parent only.
2554
2551
2555 Output may be to a file, in which case the name of the file is
2552 Output may be to a file, in which case the name of the file is
2556 given using a template string. See :hg:`help templates`. In addition
2553 given using a template string. See :hg:`help templates`. In addition
2557 to the common template keywords, the following formatting rules are
2554 to the common template keywords, the following formatting rules are
2558 supported:
2555 supported:
2559
2556
2560 :``%%``: literal "%" character
2557 :``%%``: literal "%" character
2561 :``%H``: changeset hash (40 hexadecimal digits)
2558 :``%H``: changeset hash (40 hexadecimal digits)
2562 :``%N``: number of patches being generated
2559 :``%N``: number of patches being generated
2563 :``%R``: changeset revision number
2560 :``%R``: changeset revision number
2564 :``%b``: basename of the exporting repository
2561 :``%b``: basename of the exporting repository
2565 :``%h``: short-form changeset hash (12 hexadecimal digits)
2562 :``%h``: short-form changeset hash (12 hexadecimal digits)
2566 :``%m``: first line of the commit message (only alphanumeric characters)
2563 :``%m``: first line of the commit message (only alphanumeric characters)
2567 :``%n``: zero-padded sequence number, starting at 1
2564 :``%n``: zero-padded sequence number, starting at 1
2568 :``%r``: zero-padded changeset revision number
2565 :``%r``: zero-padded changeset revision number
2569 :``\\``: literal "\\" character
2566 :``\\``: literal "\\" character
2570
2567
2571 Without the -a/--text option, export will avoid generating diffs
2568 Without the -a/--text option, export will avoid generating diffs
2572 of files it detects as binary. With -a, export will generate a
2569 of files it detects as binary. With -a, export will generate a
2573 diff anyway, probably with undesirable results.
2570 diff anyway, probably with undesirable results.
2574
2571
2575 With -B/--bookmark changesets reachable by the given bookmark are
2572 With -B/--bookmark changesets reachable by the given bookmark are
2576 selected.
2573 selected.
2577
2574
2578 Use the -g/--git option to generate diffs in the git extended diff
2575 Use the -g/--git option to generate diffs in the git extended diff
2579 format. See :hg:`help diffs` for more information.
2576 format. See :hg:`help diffs` for more information.
2580
2577
2581 With the --switch-parent option, the diff will be against the
2578 With the --switch-parent option, the diff will be against the
2582 second parent. It can be useful to review a merge.
2579 second parent. It can be useful to review a merge.
2583
2580
2584 .. container:: verbose
2581 .. container:: verbose
2585
2582
2586 Template:
2583 Template:
2587
2584
2588 The following keywords are supported in addition to the common template
2585 The following keywords are supported in addition to the common template
2589 keywords and functions. See also :hg:`help templates`.
2586 keywords and functions. See also :hg:`help templates`.
2590
2587
2591 :diff: String. Diff content.
2588 :diff: String. Diff content.
2592 :parents: List of strings. Parent nodes of the changeset.
2589 :parents: List of strings. Parent nodes of the changeset.
2593
2590
2594 Examples:
2591 Examples:
2595
2592
2596 - use export and import to transplant a bugfix to the current
2593 - use export and import to transplant a bugfix to the current
2597 branch::
2594 branch::
2598
2595
2599 hg export -r 9353 | hg import -
2596 hg export -r 9353 | hg import -
2600
2597
2601 - export all the changesets between two revisions to a file with
2598 - export all the changesets between two revisions to a file with
2602 rename information::
2599 rename information::
2603
2600
2604 hg export --git -r 123:150 > changes.txt
2601 hg export --git -r 123:150 > changes.txt
2605
2602
2606 - split outgoing changes into a series of patches with
2603 - split outgoing changes into a series of patches with
2607 descriptive names::
2604 descriptive names::
2608
2605
2609 hg export -r "outgoing()" -o "%n-%m.patch"
2606 hg export -r "outgoing()" -o "%n-%m.patch"
2610
2607
2611 Returns 0 on success.
2608 Returns 0 on success.
2612 """
2609 """
2613 opts = pycompat.byteskwargs(opts)
2610 opts = pycompat.byteskwargs(opts)
2614 bookmark = opts.get(b'bookmark')
2611 bookmark = opts.get(b'bookmark')
2615 changesets += tuple(opts.get(b'rev', []))
2612 changesets += tuple(opts.get(b'rev', []))
2616
2613
2617 if bookmark and changesets:
2614 if bookmark and changesets:
2618 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2615 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2619
2616
2620 if bookmark:
2617 if bookmark:
2621 if bookmark not in repo._bookmarks:
2618 if bookmark not in repo._bookmarks:
2622 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2619 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2623
2620
2624 revs = scmutil.bookmarkrevs(repo, bookmark)
2621 revs = scmutil.bookmarkrevs(repo, bookmark)
2625 else:
2622 else:
2626 if not changesets:
2623 if not changesets:
2627 changesets = [b'.']
2624 changesets = [b'.']
2628
2625
2629 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2626 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2630 revs = scmutil.revrange(repo, changesets)
2627 revs = scmutil.revrange(repo, changesets)
2631
2628
2632 if not revs:
2629 if not revs:
2633 raise error.Abort(_(b"export requires at least one changeset"))
2630 raise error.Abort(_(b"export requires at least one changeset"))
2634 if len(revs) > 1:
2631 if len(revs) > 1:
2635 ui.note(_(b'exporting patches:\n'))
2632 ui.note(_(b'exporting patches:\n'))
2636 else:
2633 else:
2637 ui.note(_(b'exporting patch:\n'))
2634 ui.note(_(b'exporting patch:\n'))
2638
2635
2639 fntemplate = opts.get(b'output')
2636 fntemplate = opts.get(b'output')
2640 if cmdutil.isstdiofilename(fntemplate):
2637 if cmdutil.isstdiofilename(fntemplate):
2641 fntemplate = b''
2638 fntemplate = b''
2642
2639
2643 if fntemplate:
2640 if fntemplate:
2644 fm = formatter.nullformatter(ui, b'export', opts)
2641 fm = formatter.nullformatter(ui, b'export', opts)
2645 else:
2642 else:
2646 ui.pager(b'export')
2643 ui.pager(b'export')
2647 fm = ui.formatter(b'export', opts)
2644 fm = ui.formatter(b'export', opts)
2648 with fm:
2645 with fm:
2649 cmdutil.export(
2646 cmdutil.export(
2650 repo,
2647 repo,
2651 revs,
2648 revs,
2652 fm,
2649 fm,
2653 fntemplate=fntemplate,
2650 fntemplate=fntemplate,
2654 switch_parent=opts.get(b'switch_parent'),
2651 switch_parent=opts.get(b'switch_parent'),
2655 opts=patch.diffallopts(ui, opts),
2652 opts=patch.diffallopts(ui, opts),
2656 )
2653 )
2657
2654
2658
2655
2659 @command(
2656 @command(
2660 b'files',
2657 b'files',
2661 [
2658 [
2662 (
2659 (
2663 b'r',
2660 b'r',
2664 b'rev',
2661 b'rev',
2665 b'',
2662 b'',
2666 _(b'search the repository as it is in REV'),
2663 _(b'search the repository as it is in REV'),
2667 _(b'REV'),
2664 _(b'REV'),
2668 ),
2665 ),
2669 (
2666 (
2670 b'0',
2667 b'0',
2671 b'print0',
2668 b'print0',
2672 None,
2669 None,
2673 _(b'end filenames with NUL, for use with xargs'),
2670 _(b'end filenames with NUL, for use with xargs'),
2674 ),
2671 ),
2675 ]
2672 ]
2676 + walkopts
2673 + walkopts
2677 + formatteropts
2674 + formatteropts
2678 + subrepoopts,
2675 + subrepoopts,
2679 _(b'[OPTION]... [FILE]...'),
2676 _(b'[OPTION]... [FILE]...'),
2680 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2681 intents={INTENT_READONLY},
2678 intents={INTENT_READONLY},
2682 )
2679 )
2683 def files(ui, repo, *pats, **opts):
2680 def files(ui, repo, *pats, **opts):
2684 """list tracked files
2681 """list tracked files
2685
2682
2686 Print files under Mercurial control in the working directory or
2683 Print files under Mercurial control in the working directory or
2687 specified revision for given files (excluding removed files).
2684 specified revision for given files (excluding removed files).
2688 Files can be specified as filenames or filesets.
2685 Files can be specified as filenames or filesets.
2689
2686
2690 If no files are given to match, this command prints the names
2687 If no files are given to match, this command prints the names
2691 of all files under Mercurial control.
2688 of all files under Mercurial control.
2692
2689
2693 .. container:: verbose
2690 .. container:: verbose
2694
2691
2695 Template:
2692 Template:
2696
2693
2697 The following keywords are supported in addition to the common template
2694 The following keywords are supported in addition to the common template
2698 keywords and functions. See also :hg:`help templates`.
2695 keywords and functions. See also :hg:`help templates`.
2699
2696
2700 :flags: String. Character denoting file's symlink and executable bits.
2697 :flags: String. Character denoting file's symlink and executable bits.
2701 :path: String. Repository-absolute path of the file.
2698 :path: String. Repository-absolute path of the file.
2702 :size: Integer. Size of the file in bytes.
2699 :size: Integer. Size of the file in bytes.
2703
2700
2704 Examples:
2701 Examples:
2705
2702
2706 - list all files under the current directory::
2703 - list all files under the current directory::
2707
2704
2708 hg files .
2705 hg files .
2709
2706
2710 - shows sizes and flags for current revision::
2707 - shows sizes and flags for current revision::
2711
2708
2712 hg files -vr .
2709 hg files -vr .
2713
2710
2714 - list all files named README::
2711 - list all files named README::
2715
2712
2716 hg files -I "**/README"
2713 hg files -I "**/README"
2717
2714
2718 - list all binary files::
2715 - list all binary files::
2719
2716
2720 hg files "set:binary()"
2717 hg files "set:binary()"
2721
2718
2722 - find files containing a regular expression::
2719 - find files containing a regular expression::
2723
2720
2724 hg files "set:grep('bob')"
2721 hg files "set:grep('bob')"
2725
2722
2726 - search tracked file contents with xargs and grep::
2723 - search tracked file contents with xargs and grep::
2727
2724
2728 hg files -0 | xargs -0 grep foo
2725 hg files -0 | xargs -0 grep foo
2729
2726
2730 See :hg:`help patterns` and :hg:`help filesets` for more information
2727 See :hg:`help patterns` and :hg:`help filesets` for more information
2731 on specifying file patterns.
2728 on specifying file patterns.
2732
2729
2733 Returns 0 if a match is found, 1 otherwise.
2730 Returns 0 if a match is found, 1 otherwise.
2734
2731
2735 """
2732 """
2736
2733
2737 opts = pycompat.byteskwargs(opts)
2734 opts = pycompat.byteskwargs(opts)
2738 rev = opts.get(b'rev')
2735 rev = opts.get(b'rev')
2739 if rev:
2736 if rev:
2740 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2737 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2741 ctx = scmutil.revsingle(repo, rev, None)
2738 ctx = scmutil.revsingle(repo, rev, None)
2742
2739
2743 end = b'\n'
2740 end = b'\n'
2744 if opts.get(b'print0'):
2741 if opts.get(b'print0'):
2745 end = b'\0'
2742 end = b'\0'
2746 fmt = b'%s' + end
2743 fmt = b'%s' + end
2747
2744
2748 m = scmutil.match(ctx, pats, opts)
2745 m = scmutil.match(ctx, pats, opts)
2749 ui.pager(b'files')
2746 ui.pager(b'files')
2750 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2747 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2751 with ui.formatter(b'files', opts) as fm:
2748 with ui.formatter(b'files', opts) as fm:
2752 return cmdutil.files(
2749 return cmdutil.files(
2753 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2750 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2754 )
2751 )
2755
2752
2756
2753
2757 @command(
2754 @command(
2758 b'forget',
2755 b'forget',
2759 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2756 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2760 + walkopts
2757 + walkopts
2761 + dryrunopts,
2758 + dryrunopts,
2762 _(b'[OPTION]... FILE...'),
2759 _(b'[OPTION]... FILE...'),
2763 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2760 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2764 helpbasic=True,
2761 helpbasic=True,
2765 inferrepo=True,
2762 inferrepo=True,
2766 )
2763 )
2767 def forget(ui, repo, *pats, **opts):
2764 def forget(ui, repo, *pats, **opts):
2768 """forget the specified files on the next commit
2765 """forget the specified files on the next commit
2769
2766
2770 Mark the specified files so they will no longer be tracked
2767 Mark the specified files so they will no longer be tracked
2771 after the next commit.
2768 after the next commit.
2772
2769
2773 This only removes files from the current branch, not from the
2770 This only removes files from the current branch, not from the
2774 entire project history, and it does not delete them from the
2771 entire project history, and it does not delete them from the
2775 working directory.
2772 working directory.
2776
2773
2777 To delete the file from the working directory, see :hg:`remove`.
2774 To delete the file from the working directory, see :hg:`remove`.
2778
2775
2779 To undo a forget before the next commit, see :hg:`add`.
2776 To undo a forget before the next commit, see :hg:`add`.
2780
2777
2781 .. container:: verbose
2778 .. container:: verbose
2782
2779
2783 Examples:
2780 Examples:
2784
2781
2785 - forget newly-added binary files::
2782 - forget newly-added binary files::
2786
2783
2787 hg forget "set:added() and binary()"
2784 hg forget "set:added() and binary()"
2788
2785
2789 - forget files that would be excluded by .hgignore::
2786 - forget files that would be excluded by .hgignore::
2790
2787
2791 hg forget "set:hgignore()"
2788 hg forget "set:hgignore()"
2792
2789
2793 Returns 0 on success.
2790 Returns 0 on success.
2794 """
2791 """
2795
2792
2796 opts = pycompat.byteskwargs(opts)
2793 opts = pycompat.byteskwargs(opts)
2797 if not pats:
2794 if not pats:
2798 raise error.Abort(_(b'no files specified'))
2795 raise error.Abort(_(b'no files specified'))
2799
2796
2800 m = scmutil.match(repo[None], pats, opts)
2797 m = scmutil.match(repo[None], pats, opts)
2801 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2798 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2802 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2799 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2803 rejected = cmdutil.forget(
2800 rejected = cmdutil.forget(
2804 ui,
2801 ui,
2805 repo,
2802 repo,
2806 m,
2803 m,
2807 prefix=b"",
2804 prefix=b"",
2808 uipathfn=uipathfn,
2805 uipathfn=uipathfn,
2809 explicitonly=False,
2806 explicitonly=False,
2810 dryrun=dryrun,
2807 dryrun=dryrun,
2811 interactive=interactive,
2808 interactive=interactive,
2812 )[0]
2809 )[0]
2813 return rejected and 1 or 0
2810 return rejected and 1 or 0
2814
2811
2815
2812
2816 @command(
2813 @command(
2817 b'graft',
2814 b'graft',
2818 [
2815 [
2819 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2816 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2820 (
2817 (
2821 b'',
2818 b'',
2822 b'base',
2819 b'base',
2823 b'',
2820 b'',
2824 _(b'base revision when doing the graft merge (ADVANCED)'),
2821 _(b'base revision when doing the graft merge (ADVANCED)'),
2825 _(b'REV'),
2822 _(b'REV'),
2826 ),
2823 ),
2827 (b'c', b'continue', False, _(b'resume interrupted graft')),
2824 (b'c', b'continue', False, _(b'resume interrupted graft')),
2828 (b'', b'stop', False, _(b'stop interrupted graft')),
2825 (b'', b'stop', False, _(b'stop interrupted graft')),
2829 (b'', b'abort', False, _(b'abort interrupted graft')),
2826 (b'', b'abort', False, _(b'abort interrupted graft')),
2830 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2827 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2831 (b'', b'log', None, _(b'append graft info to log message')),
2828 (b'', b'log', None, _(b'append graft info to log message')),
2832 (
2829 (
2833 b'',
2830 b'',
2834 b'no-commit',
2831 b'no-commit',
2835 None,
2832 None,
2836 _(b"don't commit, just apply the changes in working directory"),
2833 _(b"don't commit, just apply the changes in working directory"),
2837 ),
2834 ),
2838 (b'f', b'force', False, _(b'force graft')),
2835 (b'f', b'force', False, _(b'force graft')),
2839 (
2836 (
2840 b'D',
2837 b'D',
2841 b'currentdate',
2838 b'currentdate',
2842 False,
2839 False,
2843 _(b'record the current date as commit date'),
2840 _(b'record the current date as commit date'),
2844 ),
2841 ),
2845 (
2842 (
2846 b'U',
2843 b'U',
2847 b'currentuser',
2844 b'currentuser',
2848 False,
2845 False,
2849 _(b'record the current user as committer'),
2846 _(b'record the current user as committer'),
2850 ),
2847 ),
2851 ]
2848 ]
2852 + commitopts2
2849 + commitopts2
2853 + mergetoolopts
2850 + mergetoolopts
2854 + dryrunopts,
2851 + dryrunopts,
2855 _(b'[OPTION]... [-r REV]... REV...'),
2852 _(b'[OPTION]... [-r REV]... REV...'),
2856 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2853 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2857 )
2854 )
2858 def graft(ui, repo, *revs, **opts):
2855 def graft(ui, repo, *revs, **opts):
2859 '''copy changes from other branches onto the current branch
2856 '''copy changes from other branches onto the current branch
2860
2857
2861 This command uses Mercurial's merge logic to copy individual
2858 This command uses Mercurial's merge logic to copy individual
2862 changes from other branches without merging branches in the
2859 changes from other branches without merging branches in the
2863 history graph. This is sometimes known as 'backporting' or
2860 history graph. This is sometimes known as 'backporting' or
2864 'cherry-picking'. By default, graft will copy user, date, and
2861 'cherry-picking'. By default, graft will copy user, date, and
2865 description from the source changesets.
2862 description from the source changesets.
2866
2863
2867 Changesets that are ancestors of the current revision, that have
2864 Changesets that are ancestors of the current revision, that have
2868 already been grafted, or that are merges will be skipped.
2865 already been grafted, or that are merges will be skipped.
2869
2866
2870 If --log is specified, log messages will have a comment appended
2867 If --log is specified, log messages will have a comment appended
2871 of the form::
2868 of the form::
2872
2869
2873 (grafted from CHANGESETHASH)
2870 (grafted from CHANGESETHASH)
2874
2871
2875 If --force is specified, revisions will be grafted even if they
2872 If --force is specified, revisions will be grafted even if they
2876 are already ancestors of, or have been grafted to, the destination.
2873 are already ancestors of, or have been grafted to, the destination.
2877 This is useful when the revisions have since been backed out.
2874 This is useful when the revisions have since been backed out.
2878
2875
2879 If a graft merge results in conflicts, the graft process is
2876 If a graft merge results in conflicts, the graft process is
2880 interrupted so that the current merge can be manually resolved.
2877 interrupted so that the current merge can be manually resolved.
2881 Once all conflicts are addressed, the graft process can be
2878 Once all conflicts are addressed, the graft process can be
2882 continued with the -c/--continue option.
2879 continued with the -c/--continue option.
2883
2880
2884 The -c/--continue option reapplies all the earlier options.
2881 The -c/--continue option reapplies all the earlier options.
2885
2882
2886 .. container:: verbose
2883 .. container:: verbose
2887
2884
2888 The --base option exposes more of how graft internally uses merge with a
2885 The --base option exposes more of how graft internally uses merge with a
2889 custom base revision. --base can be used to specify another ancestor than
2886 custom base revision. --base can be used to specify another ancestor than
2890 the first and only parent.
2887 the first and only parent.
2891
2888
2892 The command::
2889 The command::
2893
2890
2894 hg graft -r 345 --base 234
2891 hg graft -r 345 --base 234
2895
2892
2896 is thus pretty much the same as::
2893 is thus pretty much the same as::
2897
2894
2898 hg diff -r 234 -r 345 | hg import
2895 hg diff -r 234 -r 345 | hg import
2899
2896
2900 but using merge to resolve conflicts and track moved files.
2897 but using merge to resolve conflicts and track moved files.
2901
2898
2902 The result of a merge can thus be backported as a single commit by
2899 The result of a merge can thus be backported as a single commit by
2903 specifying one of the merge parents as base, and thus effectively
2900 specifying one of the merge parents as base, and thus effectively
2904 grafting the changes from the other side.
2901 grafting the changes from the other side.
2905
2902
2906 It is also possible to collapse multiple changesets and clean up history
2903 It is also possible to collapse multiple changesets and clean up history
2907 by specifying another ancestor as base, much like rebase --collapse
2904 by specifying another ancestor as base, much like rebase --collapse
2908 --keep.
2905 --keep.
2909
2906
2910 The commit message can be tweaked after the fact using commit --amend .
2907 The commit message can be tweaked after the fact using commit --amend .
2911
2908
2912 For using non-ancestors as the base to backout changes, see the backout
2909 For using non-ancestors as the base to backout changes, see the backout
2913 command and the hidden --parent option.
2910 command and the hidden --parent option.
2914
2911
2915 .. container:: verbose
2912 .. container:: verbose
2916
2913
2917 Examples:
2914 Examples:
2918
2915
2919 - copy a single change to the stable branch and edit its description::
2916 - copy a single change to the stable branch and edit its description::
2920
2917
2921 hg update stable
2918 hg update stable
2922 hg graft --edit 9393
2919 hg graft --edit 9393
2923
2920
2924 - graft a range of changesets with one exception, updating dates::
2921 - graft a range of changesets with one exception, updating dates::
2925
2922
2926 hg graft -D "2085::2093 and not 2091"
2923 hg graft -D "2085::2093 and not 2091"
2927
2924
2928 - continue a graft after resolving conflicts::
2925 - continue a graft after resolving conflicts::
2929
2926
2930 hg graft -c
2927 hg graft -c
2931
2928
2932 - show the source of a grafted changeset::
2929 - show the source of a grafted changeset::
2933
2930
2934 hg log --debug -r .
2931 hg log --debug -r .
2935
2932
2936 - show revisions sorted by date::
2933 - show revisions sorted by date::
2937
2934
2938 hg log -r "sort(all(), date)"
2935 hg log -r "sort(all(), date)"
2939
2936
2940 - backport the result of a merge as a single commit::
2937 - backport the result of a merge as a single commit::
2941
2938
2942 hg graft -r 123 --base 123^
2939 hg graft -r 123 --base 123^
2943
2940
2944 - land a feature branch as one changeset::
2941 - land a feature branch as one changeset::
2945
2942
2946 hg up -cr default
2943 hg up -cr default
2947 hg graft -r featureX --base "ancestor('featureX', 'default')"
2944 hg graft -r featureX --base "ancestor('featureX', 'default')"
2948
2945
2949 See :hg:`help revisions` for more about specifying revisions.
2946 See :hg:`help revisions` for more about specifying revisions.
2950
2947
2951 Returns 0 on successful completion.
2948 Returns 0 on successful completion.
2952 '''
2949 '''
2953 with repo.wlock():
2950 with repo.wlock():
2954 return _dograft(ui, repo, *revs, **opts)
2951 return _dograft(ui, repo, *revs, **opts)
2955
2952
2956
2953
2957 def _dograft(ui, repo, *revs, **opts):
2954 def _dograft(ui, repo, *revs, **opts):
2958 opts = pycompat.byteskwargs(opts)
2955 opts = pycompat.byteskwargs(opts)
2959 if revs and opts.get(b'rev'):
2956 if revs and opts.get(b'rev'):
2960 ui.warn(
2957 ui.warn(
2961 _(
2958 _(
2962 b'warning: inconsistent use of --rev might give unexpected '
2959 b'warning: inconsistent use of --rev might give unexpected '
2963 b'revision ordering!\n'
2960 b'revision ordering!\n'
2964 )
2961 )
2965 )
2962 )
2966
2963
2967 revs = list(revs)
2964 revs = list(revs)
2968 revs.extend(opts.get(b'rev'))
2965 revs.extend(opts.get(b'rev'))
2969 basectx = None
2966 basectx = None
2970 if opts.get(b'base'):
2967 if opts.get(b'base'):
2971 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2968 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2972 # a dict of data to be stored in state file
2969 # a dict of data to be stored in state file
2973 statedata = {}
2970 statedata = {}
2974 # list of new nodes created by ongoing graft
2971 # list of new nodes created by ongoing graft
2975 statedata[b'newnodes'] = []
2972 statedata[b'newnodes'] = []
2976
2973
2977 if opts.get(b'user') and opts.get(b'currentuser'):
2974 if opts.get(b'user') and opts.get(b'currentuser'):
2978 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2975 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2979 if opts.get(b'date') and opts.get(b'currentdate'):
2976 if opts.get(b'date') and opts.get(b'currentdate'):
2980 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2977 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2981 if not opts.get(b'user') and opts.get(b'currentuser'):
2978 if not opts.get(b'user') and opts.get(b'currentuser'):
2982 opts[b'user'] = ui.username()
2979 opts[b'user'] = ui.username()
2983 if not opts.get(b'date') and opts.get(b'currentdate'):
2980 if not opts.get(b'date') and opts.get(b'currentdate'):
2984 opts[b'date'] = b"%d %d" % dateutil.makedate()
2981 opts[b'date'] = b"%d %d" % dateutil.makedate()
2985
2982
2986 editor = cmdutil.getcommiteditor(
2983 editor = cmdutil.getcommiteditor(
2987 editform=b'graft', **pycompat.strkwargs(opts)
2984 editform=b'graft', **pycompat.strkwargs(opts)
2988 )
2985 )
2989
2986
2990 cont = False
2987 cont = False
2991 if opts.get(b'no_commit'):
2988 if opts.get(b'no_commit'):
2992 if opts.get(b'edit'):
2989 if opts.get(b'edit'):
2993 raise error.Abort(
2990 raise error.Abort(
2994 _(b"cannot specify --no-commit and --edit together")
2991 _(b"cannot specify --no-commit and --edit together")
2995 )
2992 )
2996 if opts.get(b'currentuser'):
2993 if opts.get(b'currentuser'):
2997 raise error.Abort(
2994 raise error.Abort(
2998 _(b"cannot specify --no-commit and --currentuser together")
2995 _(b"cannot specify --no-commit and --currentuser together")
2999 )
2996 )
3000 if opts.get(b'currentdate'):
2997 if opts.get(b'currentdate'):
3001 raise error.Abort(
2998 raise error.Abort(
3002 _(b"cannot specify --no-commit and --currentdate together")
2999 _(b"cannot specify --no-commit and --currentdate together")
3003 )
3000 )
3004 if opts.get(b'log'):
3001 if opts.get(b'log'):
3005 raise error.Abort(
3002 raise error.Abort(
3006 _(b"cannot specify --no-commit and --log together")
3003 _(b"cannot specify --no-commit and --log together")
3007 )
3004 )
3008
3005
3009 graftstate = statemod.cmdstate(repo, b'graftstate')
3006 graftstate = statemod.cmdstate(repo, b'graftstate')
3010
3007
3011 if opts.get(b'stop'):
3008 if opts.get(b'stop'):
3012 if opts.get(b'continue'):
3009 if opts.get(b'continue'):
3013 raise error.Abort(
3010 raise error.Abort(
3014 _(b"cannot use '--continue' and '--stop' together")
3011 _(b"cannot use '--continue' and '--stop' together")
3015 )
3012 )
3016 if opts.get(b'abort'):
3013 if opts.get(b'abort'):
3017 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3014 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3018
3015
3019 if any(
3016 if any(
3020 (
3017 (
3021 opts.get(b'edit'),
3018 opts.get(b'edit'),
3022 opts.get(b'log'),
3019 opts.get(b'log'),
3023 opts.get(b'user'),
3020 opts.get(b'user'),
3024 opts.get(b'date'),
3021 opts.get(b'date'),
3025 opts.get(b'currentdate'),
3022 opts.get(b'currentdate'),
3026 opts.get(b'currentuser'),
3023 opts.get(b'currentuser'),
3027 opts.get(b'rev'),
3024 opts.get(b'rev'),
3028 )
3025 )
3029 ):
3026 ):
3030 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3027 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3031 return _stopgraft(ui, repo, graftstate)
3028 return _stopgraft(ui, repo, graftstate)
3032 elif opts.get(b'abort'):
3029 elif opts.get(b'abort'):
3033 if opts.get(b'continue'):
3030 if opts.get(b'continue'):
3034 raise error.Abort(
3031 raise error.Abort(
3035 _(b"cannot use '--continue' and '--abort' together")
3032 _(b"cannot use '--continue' and '--abort' together")
3036 )
3033 )
3037 if any(
3034 if any(
3038 (
3035 (
3039 opts.get(b'edit'),
3036 opts.get(b'edit'),
3040 opts.get(b'log'),
3037 opts.get(b'log'),
3041 opts.get(b'user'),
3038 opts.get(b'user'),
3042 opts.get(b'date'),
3039 opts.get(b'date'),
3043 opts.get(b'currentdate'),
3040 opts.get(b'currentdate'),
3044 opts.get(b'currentuser'),
3041 opts.get(b'currentuser'),
3045 opts.get(b'rev'),
3042 opts.get(b'rev'),
3046 )
3043 )
3047 ):
3044 ):
3048 raise error.Abort(
3045 raise error.Abort(
3049 _(b"cannot specify any other flag with '--abort'")
3046 _(b"cannot specify any other flag with '--abort'")
3050 )
3047 )
3051
3048
3052 return cmdutil.abortgraft(ui, repo, graftstate)
3049 return cmdutil.abortgraft(ui, repo, graftstate)
3053 elif opts.get(b'continue'):
3050 elif opts.get(b'continue'):
3054 cont = True
3051 cont = True
3055 if revs:
3052 if revs:
3056 raise error.Abort(_(b"can't specify --continue and revisions"))
3053 raise error.Abort(_(b"can't specify --continue and revisions"))
3057 # read in unfinished revisions
3054 # read in unfinished revisions
3058 if graftstate.exists():
3055 if graftstate.exists():
3059 statedata = cmdutil.readgraftstate(repo, graftstate)
3056 statedata = cmdutil.readgraftstate(repo, graftstate)
3060 if statedata.get(b'date'):
3057 if statedata.get(b'date'):
3061 opts[b'date'] = statedata[b'date']
3058 opts[b'date'] = statedata[b'date']
3062 if statedata.get(b'user'):
3059 if statedata.get(b'user'):
3063 opts[b'user'] = statedata[b'user']
3060 opts[b'user'] = statedata[b'user']
3064 if statedata.get(b'log'):
3061 if statedata.get(b'log'):
3065 opts[b'log'] = True
3062 opts[b'log'] = True
3066 if statedata.get(b'no_commit'):
3063 if statedata.get(b'no_commit'):
3067 opts[b'no_commit'] = statedata.get(b'no_commit')
3064 opts[b'no_commit'] = statedata.get(b'no_commit')
3068 nodes = statedata[b'nodes']
3065 nodes = statedata[b'nodes']
3069 revs = [repo[node].rev() for node in nodes]
3066 revs = [repo[node].rev() for node in nodes]
3070 else:
3067 else:
3071 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3068 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3072 else:
3069 else:
3073 if not revs:
3070 if not revs:
3074 raise error.Abort(_(b'no revisions specified'))
3071 raise error.Abort(_(b'no revisions specified'))
3075 cmdutil.checkunfinished(repo)
3072 cmdutil.checkunfinished(repo)
3076 cmdutil.bailifchanged(repo)
3073 cmdutil.bailifchanged(repo)
3077 revs = scmutil.revrange(repo, revs)
3074 revs = scmutil.revrange(repo, revs)
3078
3075
3079 skipped = set()
3076 skipped = set()
3080 if basectx is None:
3077 if basectx is None:
3081 # check for merges
3078 # check for merges
3082 for rev in repo.revs(b'%ld and merge()', revs):
3079 for rev in repo.revs(b'%ld and merge()', revs):
3083 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3080 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3084 skipped.add(rev)
3081 skipped.add(rev)
3085 revs = [r for r in revs if r not in skipped]
3082 revs = [r for r in revs if r not in skipped]
3086 if not revs:
3083 if not revs:
3087 return -1
3084 return -1
3088 if basectx is not None and len(revs) != 1:
3085 if basectx is not None and len(revs) != 1:
3089 raise error.Abort(_(b'only one revision allowed with --base '))
3086 raise error.Abort(_(b'only one revision allowed with --base '))
3090
3087
3091 # Don't check in the --continue case, in effect retaining --force across
3088 # Don't check in the --continue case, in effect retaining --force across
3092 # --continues. That's because without --force, any revisions we decided to
3089 # --continues. That's because without --force, any revisions we decided to
3093 # skip would have been filtered out here, so they wouldn't have made their
3090 # skip would have been filtered out here, so they wouldn't have made their
3094 # way to the graftstate. With --force, any revisions we would have otherwise
3091 # way to the graftstate. With --force, any revisions we would have otherwise
3095 # skipped would not have been filtered out, and if they hadn't been applied
3092 # skipped would not have been filtered out, and if they hadn't been applied
3096 # already, they'd have been in the graftstate.
3093 # already, they'd have been in the graftstate.
3097 if not (cont or opts.get(b'force')) and basectx is None:
3094 if not (cont or opts.get(b'force')) and basectx is None:
3098 # check for ancestors of dest branch
3095 # check for ancestors of dest branch
3099 crev = repo[b'.'].rev()
3096 crev = repo[b'.'].rev()
3100 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3097 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3101 # XXX make this lazy in the future
3098 # XXX make this lazy in the future
3102 # don't mutate while iterating, create a copy
3099 # don't mutate while iterating, create a copy
3103 for rev in list(revs):
3100 for rev in list(revs):
3104 if rev in ancestors:
3101 if rev in ancestors:
3105 ui.warn(
3102 ui.warn(
3106 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3103 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3107 )
3104 )
3108 # XXX remove on list is slow
3105 # XXX remove on list is slow
3109 revs.remove(rev)
3106 revs.remove(rev)
3110 if not revs:
3107 if not revs:
3111 return -1
3108 return -1
3112
3109
3113 # analyze revs for earlier grafts
3110 # analyze revs for earlier grafts
3114 ids = {}
3111 ids = {}
3115 for ctx in repo.set(b"%ld", revs):
3112 for ctx in repo.set(b"%ld", revs):
3116 ids[ctx.hex()] = ctx.rev()
3113 ids[ctx.hex()] = ctx.rev()
3117 n = ctx.extra().get(b'source')
3114 n = ctx.extra().get(b'source')
3118 if n:
3115 if n:
3119 ids[n] = ctx.rev()
3116 ids[n] = ctx.rev()
3120
3117
3121 # check ancestors for earlier grafts
3118 # check ancestors for earlier grafts
3122 ui.debug(b'scanning for duplicate grafts\n')
3119 ui.debug(b'scanning for duplicate grafts\n')
3123
3120
3124 # The only changesets we can be sure doesn't contain grafts of any
3121 # The only changesets we can be sure doesn't contain grafts of any
3125 # revs, are the ones that are common ancestors of *all* revs:
3122 # revs, are the ones that are common ancestors of *all* revs:
3126 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3123 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3127 ctx = repo[rev]
3124 ctx = repo[rev]
3128 n = ctx.extra().get(b'source')
3125 n = ctx.extra().get(b'source')
3129 if n in ids:
3126 if n in ids:
3130 try:
3127 try:
3131 r = repo[n].rev()
3128 r = repo[n].rev()
3132 except error.RepoLookupError:
3129 except error.RepoLookupError:
3133 r = None
3130 r = None
3134 if r in revs:
3131 if r in revs:
3135 ui.warn(
3132 ui.warn(
3136 _(
3133 _(
3137 b'skipping revision %d:%s '
3134 b'skipping revision %d:%s '
3138 b'(already grafted to %d:%s)\n'
3135 b'(already grafted to %d:%s)\n'
3139 )
3136 )
3140 % (r, repo[r], rev, ctx)
3137 % (r, repo[r], rev, ctx)
3141 )
3138 )
3142 revs.remove(r)
3139 revs.remove(r)
3143 elif ids[n] in revs:
3140 elif ids[n] in revs:
3144 if r is None:
3141 if r is None:
3145 ui.warn(
3142 ui.warn(
3146 _(
3143 _(
3147 b'skipping already grafted revision %d:%s '
3144 b'skipping already grafted revision %d:%s '
3148 b'(%d:%s also has unknown origin %s)\n'
3145 b'(%d:%s also has unknown origin %s)\n'
3149 )
3146 )
3150 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3147 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3151 )
3148 )
3152 else:
3149 else:
3153 ui.warn(
3150 ui.warn(
3154 _(
3151 _(
3155 b'skipping already grafted revision %d:%s '
3152 b'skipping already grafted revision %d:%s '
3156 b'(%d:%s also has origin %d:%s)\n'
3153 b'(%d:%s also has origin %d:%s)\n'
3157 )
3154 )
3158 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3155 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3159 )
3156 )
3160 revs.remove(ids[n])
3157 revs.remove(ids[n])
3161 elif ctx.hex() in ids:
3158 elif ctx.hex() in ids:
3162 r = ids[ctx.hex()]
3159 r = ids[ctx.hex()]
3163 if r in revs:
3160 if r in revs:
3164 ui.warn(
3161 ui.warn(
3165 _(
3162 _(
3166 b'skipping already grafted revision %d:%s '
3163 b'skipping already grafted revision %d:%s '
3167 b'(was grafted from %d:%s)\n'
3164 b'(was grafted from %d:%s)\n'
3168 )
3165 )
3169 % (r, repo[r], rev, ctx)
3166 % (r, repo[r], rev, ctx)
3170 )
3167 )
3171 revs.remove(r)
3168 revs.remove(r)
3172 if not revs:
3169 if not revs:
3173 return -1
3170 return -1
3174
3171
3175 if opts.get(b'no_commit'):
3172 if opts.get(b'no_commit'):
3176 statedata[b'no_commit'] = True
3173 statedata[b'no_commit'] = True
3177 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3174 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3178 desc = b'%d:%s "%s"' % (
3175 desc = b'%d:%s "%s"' % (
3179 ctx.rev(),
3176 ctx.rev(),
3180 ctx,
3177 ctx,
3181 ctx.description().split(b'\n', 1)[0],
3178 ctx.description().split(b'\n', 1)[0],
3182 )
3179 )
3183 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3180 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3184 if names:
3181 if names:
3185 desc += b' (%s)' % b' '.join(names)
3182 desc += b' (%s)' % b' '.join(names)
3186 ui.status(_(b'grafting %s\n') % desc)
3183 ui.status(_(b'grafting %s\n') % desc)
3187 if opts.get(b'dry_run'):
3184 if opts.get(b'dry_run'):
3188 continue
3185 continue
3189
3186
3190 source = ctx.extra().get(b'source')
3187 source = ctx.extra().get(b'source')
3191 extra = {}
3188 extra = {}
3192 if source:
3189 if source:
3193 extra[b'source'] = source
3190 extra[b'source'] = source
3194 extra[b'intermediate-source'] = ctx.hex()
3191 extra[b'intermediate-source'] = ctx.hex()
3195 else:
3192 else:
3196 extra[b'source'] = ctx.hex()
3193 extra[b'source'] = ctx.hex()
3197 user = ctx.user()
3194 user = ctx.user()
3198 if opts.get(b'user'):
3195 if opts.get(b'user'):
3199 user = opts[b'user']
3196 user = opts[b'user']
3200 statedata[b'user'] = user
3197 statedata[b'user'] = user
3201 date = ctx.date()
3198 date = ctx.date()
3202 if opts.get(b'date'):
3199 if opts.get(b'date'):
3203 date = opts[b'date']
3200 date = opts[b'date']
3204 statedata[b'date'] = date
3201 statedata[b'date'] = date
3205 message = ctx.description()
3202 message = ctx.description()
3206 if opts.get(b'log'):
3203 if opts.get(b'log'):
3207 message += b'\n(grafted from %s)' % ctx.hex()
3204 message += b'\n(grafted from %s)' % ctx.hex()
3208 statedata[b'log'] = True
3205 statedata[b'log'] = True
3209
3206
3210 # we don't merge the first commit when continuing
3207 # we don't merge the first commit when continuing
3211 if not cont:
3208 if not cont:
3212 # perform the graft merge with p1(rev) as 'ancestor'
3209 # perform the graft merge with p1(rev) as 'ancestor'
3213 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3210 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3214 base = ctx.p1() if basectx is None else basectx
3211 base = ctx.p1() if basectx is None else basectx
3215 with ui.configoverride(overrides, b'graft'):
3212 with ui.configoverride(overrides, b'graft'):
3216 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3213 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3217 # report any conflicts
3214 # report any conflicts
3218 if stats.unresolvedcount > 0:
3215 if stats.unresolvedcount > 0:
3219 # write out state for --continue
3216 # write out state for --continue
3220 nodes = [repo[rev].hex() for rev in revs[pos:]]
3217 nodes = [repo[rev].hex() for rev in revs[pos:]]
3221 statedata[b'nodes'] = nodes
3218 statedata[b'nodes'] = nodes
3222 stateversion = 1
3219 stateversion = 1
3223 graftstate.save(stateversion, statedata)
3220 graftstate.save(stateversion, statedata)
3224 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3221 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3225 raise error.Abort(
3222 raise error.Abort(
3226 _(b"unresolved conflicts, can't continue"), hint=hint
3223 _(b"unresolved conflicts, can't continue"), hint=hint
3227 )
3224 )
3228 else:
3225 else:
3229 cont = False
3226 cont = False
3230
3227
3231 # commit if --no-commit is false
3228 # commit if --no-commit is false
3232 if not opts.get(b'no_commit'):
3229 if not opts.get(b'no_commit'):
3233 node = repo.commit(
3230 node = repo.commit(
3234 text=message, user=user, date=date, extra=extra, editor=editor
3231 text=message, user=user, date=date, extra=extra, editor=editor
3235 )
3232 )
3236 if node is None:
3233 if node is None:
3237 ui.warn(
3234 ui.warn(
3238 _(b'note: graft of %d:%s created no changes to commit\n')
3235 _(b'note: graft of %d:%s created no changes to commit\n')
3239 % (ctx.rev(), ctx)
3236 % (ctx.rev(), ctx)
3240 )
3237 )
3241 # checking that newnodes exist because old state files won't have it
3238 # checking that newnodes exist because old state files won't have it
3242 elif statedata.get(b'newnodes') is not None:
3239 elif statedata.get(b'newnodes') is not None:
3243 statedata[b'newnodes'].append(node)
3240 statedata[b'newnodes'].append(node)
3244
3241
3245 # remove state when we complete successfully
3242 # remove state when we complete successfully
3246 if not opts.get(b'dry_run'):
3243 if not opts.get(b'dry_run'):
3247 graftstate.delete()
3244 graftstate.delete()
3248
3245
3249 return 0
3246 return 0
3250
3247
3251
3248
3252 def _stopgraft(ui, repo, graftstate):
3249 def _stopgraft(ui, repo, graftstate):
3253 """stop the interrupted graft"""
3250 """stop the interrupted graft"""
3254 if not graftstate.exists():
3251 if not graftstate.exists():
3255 raise error.Abort(_(b"no interrupted graft found"))
3252 raise error.Abort(_(b"no interrupted graft found"))
3256 pctx = repo[b'.']
3253 pctx = repo[b'.']
3257 hg.updaterepo(repo, pctx.node(), overwrite=True)
3254 hg.updaterepo(repo, pctx.node(), overwrite=True)
3258 graftstate.delete()
3255 graftstate.delete()
3259 ui.status(_(b"stopped the interrupted graft\n"))
3256 ui.status(_(b"stopped the interrupted graft\n"))
3260 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3257 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3261 return 0
3258 return 0
3262
3259
3263
3260
3264 statemod.addunfinished(
3261 statemod.addunfinished(
3265 b'graft',
3262 b'graft',
3266 fname=b'graftstate',
3263 fname=b'graftstate',
3267 clearable=True,
3264 clearable=True,
3268 stopflag=True,
3265 stopflag=True,
3269 continueflag=True,
3266 continueflag=True,
3270 abortfunc=cmdutil.hgabortgraft,
3267 abortfunc=cmdutil.hgabortgraft,
3271 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3268 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3272 )
3269 )
3273
3270
3274
3271
3275 @command(
3272 @command(
3276 b'grep',
3273 b'grep',
3277 [
3274 [
3278 (b'0', b'print0', None, _(b'end fields with NUL')),
3275 (b'0', b'print0', None, _(b'end fields with NUL')),
3279 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3276 (b'', b'all', None, _(b'print all revisions that match (DEPRECATED) ')),
3280 (
3277 (
3281 b'',
3278 b'',
3282 b'diff',
3279 b'diff',
3283 None,
3280 None,
3284 _(
3281 _(
3285 b'search revision differences for when the pattern was added '
3282 b'search revision differences for when the pattern was added '
3286 b'or removed'
3283 b'or removed'
3287 ),
3284 ),
3288 ),
3285 ),
3289 (b'a', b'text', None, _(b'treat all files as text')),
3286 (b'a', b'text', None, _(b'treat all files as text')),
3290 (
3287 (
3291 b'f',
3288 b'f',
3292 b'follow',
3289 b'follow',
3293 None,
3290 None,
3294 _(
3291 _(
3295 b'follow changeset history,'
3292 b'follow changeset history,'
3296 b' or file history across copies and renames'
3293 b' or file history across copies and renames'
3297 ),
3294 ),
3298 ),
3295 ),
3299 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3296 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3300 (
3297 (
3301 b'l',
3298 b'l',
3302 b'files-with-matches',
3299 b'files-with-matches',
3303 None,
3300 None,
3304 _(b'print only filenames and revisions that match'),
3301 _(b'print only filenames and revisions that match'),
3305 ),
3302 ),
3306 (b'n', b'line-number', None, _(b'print matching line numbers')),
3303 (b'n', b'line-number', None, _(b'print matching line numbers')),
3307 (
3304 (
3308 b'r',
3305 b'r',
3309 b'rev',
3306 b'rev',
3310 [],
3307 [],
3311 _(b'search files changed within revision range'),
3308 _(b'search files changed within revision range'),
3312 _(b'REV'),
3309 _(b'REV'),
3313 ),
3310 ),
3314 (
3311 (
3315 b'',
3312 b'',
3316 b'all-files',
3313 b'all-files',
3317 None,
3314 None,
3318 _(
3315 _(
3319 b'include all files in the changeset while grepping (DEPRECATED)'
3316 b'include all files in the changeset while grepping (DEPRECATED)'
3320 ),
3317 ),
3321 ),
3318 ),
3322 (b'u', b'user', None, _(b'list the author (long with -v)')),
3319 (b'u', b'user', None, _(b'list the author (long with -v)')),
3323 (b'd', b'date', None, _(b'list the date (short with -q)')),
3320 (b'd', b'date', None, _(b'list the date (short with -q)')),
3324 ]
3321 ]
3325 + formatteropts
3322 + formatteropts
3326 + walkopts,
3323 + walkopts,
3327 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3324 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3328 helpcategory=command.CATEGORY_FILE_CONTENTS,
3325 helpcategory=command.CATEGORY_FILE_CONTENTS,
3329 inferrepo=True,
3326 inferrepo=True,
3330 intents={INTENT_READONLY},
3327 intents={INTENT_READONLY},
3331 )
3328 )
3332 def grep(ui, repo, pattern, *pats, **opts):
3329 def grep(ui, repo, pattern, *pats, **opts):
3333 """search for a pattern in specified files
3330 """search for a pattern in specified files
3334
3331
3335 Search the working directory or revision history for a regular
3332 Search the working directory or revision history for a regular
3336 expression in the specified files for the entire repository.
3333 expression in the specified files for the entire repository.
3337
3334
3338 By default, grep searches the repository files in the working
3335 By default, grep searches the repository files in the working
3339 directory and prints the files where it finds a match. To specify
3336 directory and prints the files where it finds a match. To specify
3340 historical revisions instead of the working directory, use the
3337 historical revisions instead of the working directory, use the
3341 --rev flag.
3338 --rev flag.
3342
3339
3343 To search instead historical revision differences that contains a
3340 To search instead historical revision differences that contains a
3344 change in match status ("-" for a match that becomes a non-match,
3341 change in match status ("-" for a match that becomes a non-match,
3345 or "+" for a non-match that becomes a match), use the --diff flag.
3342 or "+" for a non-match that becomes a match), use the --diff flag.
3346
3343
3347 PATTERN can be any Python (roughly Perl-compatible) regular
3344 PATTERN can be any Python (roughly Perl-compatible) regular
3348 expression.
3345 expression.
3349
3346
3350 If no FILEs are specified and the --rev flag isn't supplied, all
3347 If no FILEs are specified and the --rev flag isn't supplied, all
3351 files in the working directory are searched. When using the --rev
3348 files in the working directory are searched. When using the --rev
3352 flag and specifying FILEs, use the --follow argument to also
3349 flag and specifying FILEs, use the --follow argument to also
3353 follow the specified FILEs across renames and copies.
3350 follow the specified FILEs across renames and copies.
3354
3351
3355 .. container:: verbose
3352 .. container:: verbose
3356
3353
3357 Template:
3354 Template:
3358
3355
3359 The following keywords are supported in addition to the common template
3356 The following keywords are supported in addition to the common template
3360 keywords and functions. See also :hg:`help templates`.
3357 keywords and functions. See also :hg:`help templates`.
3361
3358
3362 :change: String. Character denoting insertion ``+`` or removal ``-``.
3359 :change: String. Character denoting insertion ``+`` or removal ``-``.
3363 Available if ``--diff`` is specified.
3360 Available if ``--diff`` is specified.
3364 :lineno: Integer. Line number of the match.
3361 :lineno: Integer. Line number of the match.
3365 :path: String. Repository-absolute path of the file.
3362 :path: String. Repository-absolute path of the file.
3366 :texts: List of text chunks.
3363 :texts: List of text chunks.
3367
3364
3368 And each entry of ``{texts}`` provides the following sub-keywords.
3365 And each entry of ``{texts}`` provides the following sub-keywords.
3369
3366
3370 :matched: Boolean. True if the chunk matches the specified pattern.
3367 :matched: Boolean. True if the chunk matches the specified pattern.
3371 :text: String. Chunk content.
3368 :text: String. Chunk content.
3372
3369
3373 See :hg:`help templates.operators` for the list expansion syntax.
3370 See :hg:`help templates.operators` for the list expansion syntax.
3374
3371
3375 Returns 0 if a match is found, 1 otherwise.
3372 Returns 0 if a match is found, 1 otherwise.
3376
3373
3377 """
3374 """
3378 opts = pycompat.byteskwargs(opts)
3375 opts = pycompat.byteskwargs(opts)
3379 diff = opts.get(b'all') or opts.get(b'diff')
3376 diff = opts.get(b'all') or opts.get(b'diff')
3380 if diff and opts.get(b'all_files'):
3377 if diff and opts.get(b'all_files'):
3381 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3378 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3382 if opts.get(b'all_files') is None and not diff:
3379 if opts.get(b'all_files') is None and not diff:
3383 opts[b'all_files'] = True
3380 opts[b'all_files'] = True
3384 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3381 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3385 all_files = opts.get(b'all_files')
3382 all_files = opts.get(b'all_files')
3386 if plaingrep:
3383 if plaingrep:
3387 opts[b'rev'] = [b'wdir()']
3384 opts[b'rev'] = [b'wdir()']
3388
3385
3389 reflags = re.M
3386 reflags = re.M
3390 if opts.get(b'ignore_case'):
3387 if opts.get(b'ignore_case'):
3391 reflags |= re.I
3388 reflags |= re.I
3392 try:
3389 try:
3393 regexp = util.re.compile(pattern, reflags)
3390 regexp = util.re.compile(pattern, reflags)
3394 except re.error as inst:
3391 except re.error as inst:
3395 ui.warn(
3392 ui.warn(
3396 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3393 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3397 )
3394 )
3398 return 1
3395 return 1
3399 sep, eol = b':', b'\n'
3396 sep, eol = b':', b'\n'
3400 if opts.get(b'print0'):
3397 if opts.get(b'print0'):
3401 sep = eol = b'\0'
3398 sep = eol = b'\0'
3402
3399
3403 getfile = util.lrucachefunc(repo.file)
3400 getfile = util.lrucachefunc(repo.file)
3404
3401
3405 def matchlines(body):
3402 def matchlines(body):
3406 begin = 0
3403 begin = 0
3407 linenum = 0
3404 linenum = 0
3408 while begin < len(body):
3405 while begin < len(body):
3409 match = regexp.search(body, begin)
3406 match = regexp.search(body, begin)
3410 if not match:
3407 if not match:
3411 break
3408 break
3412 mstart, mend = match.span()
3409 mstart, mend = match.span()
3413 linenum += body.count(b'\n', begin, mstart) + 1
3410 linenum += body.count(b'\n', begin, mstart) + 1
3414 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3411 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3415 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3412 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3416 lend = begin - 1
3413 lend = begin - 1
3417 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3414 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3418
3415
3419 class linestate(object):
3416 class linestate(object):
3420 def __init__(self, line, linenum, colstart, colend):
3417 def __init__(self, line, linenum, colstart, colend):
3421 self.line = line
3418 self.line = line
3422 self.linenum = linenum
3419 self.linenum = linenum
3423 self.colstart = colstart
3420 self.colstart = colstart
3424 self.colend = colend
3421 self.colend = colend
3425
3422
3426 def __hash__(self):
3423 def __hash__(self):
3427 return hash((self.linenum, self.line))
3424 return hash((self.linenum, self.line))
3428
3425
3429 def __eq__(self, other):
3426 def __eq__(self, other):
3430 return self.line == other.line
3427 return self.line == other.line
3431
3428
3432 def findpos(self):
3429 def findpos(self):
3433 """Iterate all (start, end) indices of matches"""
3430 """Iterate all (start, end) indices of matches"""
3434 yield self.colstart, self.colend
3431 yield self.colstart, self.colend
3435 p = self.colend
3432 p = self.colend
3436 while p < len(self.line):
3433 while p < len(self.line):
3437 m = regexp.search(self.line, p)
3434 m = regexp.search(self.line, p)
3438 if not m:
3435 if not m:
3439 break
3436 break
3440 yield m.span()
3437 yield m.span()
3441 p = m.end()
3438 p = m.end()
3442
3439
3443 matches = {}
3440 matches = {}
3444 copies = {}
3441 copies = {}
3445
3442
3446 def grepbody(fn, rev, body):
3443 def grepbody(fn, rev, body):
3447 matches[rev].setdefault(fn, [])
3444 matches[rev].setdefault(fn, [])
3448 m = matches[rev][fn]
3445 m = matches[rev][fn]
3449 if body is None:
3446 if body is None:
3450 return
3447 return
3451
3448
3452 for lnum, cstart, cend, line in matchlines(body):
3449 for lnum, cstart, cend, line in matchlines(body):
3453 s = linestate(line, lnum, cstart, cend)
3450 s = linestate(line, lnum, cstart, cend)
3454 m.append(s)
3451 m.append(s)
3455
3452
3456 def difflinestates(a, b):
3453 def difflinestates(a, b):
3457 sm = difflib.SequenceMatcher(None, a, b)
3454 sm = difflib.SequenceMatcher(None, a, b)
3458 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3455 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3459 if tag == 'insert':
3456 if tag == 'insert':
3460 for i in pycompat.xrange(blo, bhi):
3457 for i in pycompat.xrange(blo, bhi):
3461 yield (b'+', b[i])
3458 yield (b'+', b[i])
3462 elif tag == 'delete':
3459 elif tag == 'delete':
3463 for i in pycompat.xrange(alo, ahi):
3460 for i in pycompat.xrange(alo, ahi):
3464 yield (b'-', a[i])
3461 yield (b'-', a[i])
3465 elif tag == 'replace':
3462 elif tag == 'replace':
3466 for i in pycompat.xrange(alo, ahi):
3463 for i in pycompat.xrange(alo, ahi):
3467 yield (b'-', a[i])
3464 yield (b'-', a[i])
3468 for i in pycompat.xrange(blo, bhi):
3465 for i in pycompat.xrange(blo, bhi):
3469 yield (b'+', b[i])
3466 yield (b'+', b[i])
3470
3467
3471 uipathfn = scmutil.getuipathfn(repo)
3468 uipathfn = scmutil.getuipathfn(repo)
3472
3469
3473 def display(fm, fn, ctx, pstates, states):
3470 def display(fm, fn, ctx, pstates, states):
3474 rev = scmutil.intrev(ctx)
3471 rev = scmutil.intrev(ctx)
3475 if fm.isplain():
3472 if fm.isplain():
3476 formatuser = ui.shortuser
3473 formatuser = ui.shortuser
3477 else:
3474 else:
3478 formatuser = pycompat.bytestr
3475 formatuser = pycompat.bytestr
3479 if ui.quiet:
3476 if ui.quiet:
3480 datefmt = b'%Y-%m-%d'
3477 datefmt = b'%Y-%m-%d'
3481 else:
3478 else:
3482 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3479 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3483 found = False
3480 found = False
3484
3481
3485 @util.cachefunc
3482 @util.cachefunc
3486 def binary():
3483 def binary():
3487 flog = getfile(fn)
3484 flog = getfile(fn)
3488 try:
3485 try:
3489 return stringutil.binary(flog.read(ctx.filenode(fn)))
3486 return stringutil.binary(flog.read(ctx.filenode(fn)))
3490 except error.WdirUnsupported:
3487 except error.WdirUnsupported:
3491 return ctx[fn].isbinary()
3488 return ctx[fn].isbinary()
3492
3489
3493 fieldnamemap = {b'linenumber': b'lineno'}
3490 fieldnamemap = {b'linenumber': b'lineno'}
3494 if diff:
3491 if diff:
3495 iter = difflinestates(pstates, states)
3492 iter = difflinestates(pstates, states)
3496 else:
3493 else:
3497 iter = [(b'', l) for l in states]
3494 iter = [(b'', l) for l in states]
3498 for change, l in iter:
3495 for change, l in iter:
3499 fm.startitem()
3496 fm.startitem()
3500 fm.context(ctx=ctx)
3497 fm.context(ctx=ctx)
3501 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3498 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3502 fm.plain(uipathfn(fn), label=b'grep.filename')
3499 fm.plain(uipathfn(fn), label=b'grep.filename')
3503
3500
3504 cols = [
3501 cols = [
3505 (b'rev', b'%d', rev, not plaingrep, b''),
3502 (b'rev', b'%d', rev, not plaingrep, b''),
3506 (
3503 (
3507 b'linenumber',
3504 b'linenumber',
3508 b'%d',
3505 b'%d',
3509 l.linenum,
3506 l.linenum,
3510 opts.get(b'line_number'),
3507 opts.get(b'line_number'),
3511 b'',
3508 b'',
3512 ),
3509 ),
3513 ]
3510 ]
3514 if diff:
3511 if diff:
3515 cols.append(
3512 cols.append(
3516 (
3513 (
3517 b'change',
3514 b'change',
3518 b'%s',
3515 b'%s',
3519 change,
3516 change,
3520 True,
3517 True,
3521 b'grep.inserted '
3518 b'grep.inserted '
3522 if change == b'+'
3519 if change == b'+'
3523 else b'grep.deleted ',
3520 else b'grep.deleted ',
3524 )
3521 )
3525 )
3522 )
3526 cols.extend(
3523 cols.extend(
3527 [
3524 [
3528 (
3525 (
3529 b'user',
3526 b'user',
3530 b'%s',
3527 b'%s',
3531 formatuser(ctx.user()),
3528 formatuser(ctx.user()),
3532 opts.get(b'user'),
3529 opts.get(b'user'),
3533 b'',
3530 b'',
3534 ),
3531 ),
3535 (
3532 (
3536 b'date',
3533 b'date',
3537 b'%s',
3534 b'%s',
3538 fm.formatdate(ctx.date(), datefmt),
3535 fm.formatdate(ctx.date(), datefmt),
3539 opts.get(b'date'),
3536 opts.get(b'date'),
3540 b'',
3537 b'',
3541 ),
3538 ),
3542 ]
3539 ]
3543 )
3540 )
3544 for name, fmt, data, cond, extra_label in cols:
3541 for name, fmt, data, cond, extra_label in cols:
3545 if cond:
3542 if cond:
3546 fm.plain(sep, label=b'grep.sep')
3543 fm.plain(sep, label=b'grep.sep')
3547 field = fieldnamemap.get(name, name)
3544 field = fieldnamemap.get(name, name)
3548 label = extra_label + (b'grep.%s' % name)
3545 label = extra_label + (b'grep.%s' % name)
3549 fm.condwrite(cond, field, fmt, data, label=label)
3546 fm.condwrite(cond, field, fmt, data, label=label)
3550 if not opts.get(b'files_with_matches'):
3547 if not opts.get(b'files_with_matches'):
3551 fm.plain(sep, label=b'grep.sep')
3548 fm.plain(sep, label=b'grep.sep')
3552 if not opts.get(b'text') and binary():
3549 if not opts.get(b'text') and binary():
3553 fm.plain(_(b" Binary file matches"))
3550 fm.plain(_(b" Binary file matches"))
3554 else:
3551 else:
3555 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3552 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3556 fm.plain(eol)
3553 fm.plain(eol)
3557 found = True
3554 found = True
3558 if opts.get(b'files_with_matches'):
3555 if opts.get(b'files_with_matches'):
3559 break
3556 break
3560 return found
3557 return found
3561
3558
3562 def displaymatches(fm, l):
3559 def displaymatches(fm, l):
3563 p = 0
3560 p = 0
3564 for s, e in l.findpos():
3561 for s, e in l.findpos():
3565 if p < s:
3562 if p < s:
3566 fm.startitem()
3563 fm.startitem()
3567 fm.write(b'text', b'%s', l.line[p:s])
3564 fm.write(b'text', b'%s', l.line[p:s])
3568 fm.data(matched=False)
3565 fm.data(matched=False)
3569 fm.startitem()
3566 fm.startitem()
3570 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3567 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3571 fm.data(matched=True)
3568 fm.data(matched=True)
3572 p = e
3569 p = e
3573 if p < len(l.line):
3570 if p < len(l.line):
3574 fm.startitem()
3571 fm.startitem()
3575 fm.write(b'text', b'%s', l.line[p:])
3572 fm.write(b'text', b'%s', l.line[p:])
3576 fm.data(matched=False)
3573 fm.data(matched=False)
3577 fm.end()
3574 fm.end()
3578
3575
3579 skip = set()
3576 skip = set()
3580 revfiles = {}
3577 revfiles = {}
3581 match = scmutil.match(repo[None], pats, opts)
3578 match = scmutil.match(repo[None], pats, opts)
3582 found = False
3579 found = False
3583 follow = opts.get(b'follow')
3580 follow = opts.get(b'follow')
3584
3581
3585 getrenamed = scmutil.getrenamedfn(repo)
3582 getrenamed = scmutil.getrenamedfn(repo)
3586
3583
3587 def get_file_content(filename, filelog, filenode, context, revision):
3584 def get_file_content(filename, filelog, filenode, context, revision):
3588 try:
3585 try:
3589 content = filelog.read(filenode)
3586 content = filelog.read(filenode)
3590 except error.WdirUnsupported:
3587 except error.WdirUnsupported:
3591 content = context[filename].data()
3588 content = context[filename].data()
3592 except error.CensoredNodeError:
3589 except error.CensoredNodeError:
3593 content = None
3590 content = None
3594 ui.warn(
3591 ui.warn(
3595 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3592 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3596 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3593 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3597 )
3594 )
3598 return content
3595 return content
3599
3596
3600 def prep(ctx, fns):
3597 def prep(ctx, fns):
3601 rev = ctx.rev()
3598 rev = ctx.rev()
3602 pctx = ctx.p1()
3599 pctx = ctx.p1()
3603 parent = pctx.rev()
3600 parent = pctx.rev()
3604 matches.setdefault(rev, {})
3601 matches.setdefault(rev, {})
3605 matches.setdefault(parent, {})
3602 matches.setdefault(parent, {})
3606 files = revfiles.setdefault(rev, [])
3603 files = revfiles.setdefault(rev, [])
3607 for fn in fns:
3604 for fn in fns:
3608 flog = getfile(fn)
3605 flog = getfile(fn)
3609 try:
3606 try:
3610 fnode = ctx.filenode(fn)
3607 fnode = ctx.filenode(fn)
3611 except error.LookupError:
3608 except error.LookupError:
3612 continue
3609 continue
3613
3610
3614 copy = None
3611 copy = None
3615 if follow:
3612 if follow:
3616 copy = getrenamed(fn, rev)
3613 copy = getrenamed(fn, rev)
3617 if copy:
3614 if copy:
3618 copies.setdefault(rev, {})[fn] = copy
3615 copies.setdefault(rev, {})[fn] = copy
3619 if fn in skip:
3616 if fn in skip:
3620 skip.add(copy)
3617 skip.add(copy)
3621 if fn in skip:
3618 if fn in skip:
3622 continue
3619 continue
3623 files.append(fn)
3620 files.append(fn)
3624
3621
3625 if fn not in matches[rev]:
3622 if fn not in matches[rev]:
3626 content = get_file_content(fn, flog, fnode, ctx, rev)
3623 content = get_file_content(fn, flog, fnode, ctx, rev)
3627 grepbody(fn, rev, content)
3624 grepbody(fn, rev, content)
3628
3625
3629 pfn = copy or fn
3626 pfn = copy or fn
3630 if pfn not in matches[parent]:
3627 if pfn not in matches[parent]:
3631 try:
3628 try:
3632 pfnode = pctx.filenode(pfn)
3629 pfnode = pctx.filenode(pfn)
3633 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3630 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3634 grepbody(pfn, parent, pcontent)
3631 grepbody(pfn, parent, pcontent)
3635 except error.LookupError:
3632 except error.LookupError:
3636 pass
3633 pass
3637
3634
3638 ui.pager(b'grep')
3635 ui.pager(b'grep')
3639 fm = ui.formatter(b'grep', opts)
3636 fm = ui.formatter(b'grep', opts)
3640 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3637 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3641 rev = ctx.rev()
3638 rev = ctx.rev()
3642 parent = ctx.p1().rev()
3639 parent = ctx.p1().rev()
3643 for fn in sorted(revfiles.get(rev, [])):
3640 for fn in sorted(revfiles.get(rev, [])):
3644 states = matches[rev][fn]
3641 states = matches[rev][fn]
3645 copy = copies.get(rev, {}).get(fn)
3642 copy = copies.get(rev, {}).get(fn)
3646 if fn in skip:
3643 if fn in skip:
3647 if copy:
3644 if copy:
3648 skip.add(copy)
3645 skip.add(copy)
3649 continue
3646 continue
3650 pstates = matches.get(parent, {}).get(copy or fn, [])
3647 pstates = matches.get(parent, {}).get(copy or fn, [])
3651 if pstates or states:
3648 if pstates or states:
3652 r = display(fm, fn, ctx, pstates, states)
3649 r = display(fm, fn, ctx, pstates, states)
3653 found = found or r
3650 found = found or r
3654 if r and not diff and not all_files:
3651 if r and not diff and not all_files:
3655 skip.add(fn)
3652 skip.add(fn)
3656 if copy:
3653 if copy:
3657 skip.add(copy)
3654 skip.add(copy)
3658 del revfiles[rev]
3655 del revfiles[rev]
3659 # We will keep the matches dict for the duration of the window
3656 # We will keep the matches dict for the duration of the window
3660 # clear the matches dict once the window is over
3657 # clear the matches dict once the window is over
3661 if not revfiles:
3658 if not revfiles:
3662 matches.clear()
3659 matches.clear()
3663 fm.end()
3660 fm.end()
3664
3661
3665 return not found
3662 return not found
3666
3663
3667
3664
3668 @command(
3665 @command(
3669 b'heads',
3666 b'heads',
3670 [
3667 [
3671 (
3668 (
3672 b'r',
3669 b'r',
3673 b'rev',
3670 b'rev',
3674 b'',
3671 b'',
3675 _(b'show only heads which are descendants of STARTREV'),
3672 _(b'show only heads which are descendants of STARTREV'),
3676 _(b'STARTREV'),
3673 _(b'STARTREV'),
3677 ),
3674 ),
3678 (b't', b'topo', False, _(b'show topological heads only')),
3675 (b't', b'topo', False, _(b'show topological heads only')),
3679 (
3676 (
3680 b'a',
3677 b'a',
3681 b'active',
3678 b'active',
3682 False,
3679 False,
3683 _(b'show active branchheads only (DEPRECATED)'),
3680 _(b'show active branchheads only (DEPRECATED)'),
3684 ),
3681 ),
3685 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3682 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3686 ]
3683 ]
3687 + templateopts,
3684 + templateopts,
3688 _(b'[-ct] [-r STARTREV] [REV]...'),
3685 _(b'[-ct] [-r STARTREV] [REV]...'),
3689 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3686 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3690 intents={INTENT_READONLY},
3687 intents={INTENT_READONLY},
3691 )
3688 )
3692 def heads(ui, repo, *branchrevs, **opts):
3689 def heads(ui, repo, *branchrevs, **opts):
3693 """show branch heads
3690 """show branch heads
3694
3691
3695 With no arguments, show all open branch heads in the repository.
3692 With no arguments, show all open branch heads in the repository.
3696 Branch heads are changesets that have no descendants on the
3693 Branch heads are changesets that have no descendants on the
3697 same branch. They are where development generally takes place and
3694 same branch. They are where development generally takes place and
3698 are the usual targets for update and merge operations.
3695 are the usual targets for update and merge operations.
3699
3696
3700 If one or more REVs are given, only open branch heads on the
3697 If one or more REVs are given, only open branch heads on the
3701 branches associated with the specified changesets are shown. This
3698 branches associated with the specified changesets are shown. This
3702 means that you can use :hg:`heads .` to see the heads on the
3699 means that you can use :hg:`heads .` to see the heads on the
3703 currently checked-out branch.
3700 currently checked-out branch.
3704
3701
3705 If -c/--closed is specified, also show branch heads marked closed
3702 If -c/--closed is specified, also show branch heads marked closed
3706 (see :hg:`commit --close-branch`).
3703 (see :hg:`commit --close-branch`).
3707
3704
3708 If STARTREV is specified, only those heads that are descendants of
3705 If STARTREV is specified, only those heads that are descendants of
3709 STARTREV will be displayed.
3706 STARTREV will be displayed.
3710
3707
3711 If -t/--topo is specified, named branch mechanics will be ignored and only
3708 If -t/--topo is specified, named branch mechanics will be ignored and only
3712 topological heads (changesets with no children) will be shown.
3709 topological heads (changesets with no children) will be shown.
3713
3710
3714 Returns 0 if matching heads are found, 1 if not.
3711 Returns 0 if matching heads are found, 1 if not.
3715 """
3712 """
3716
3713
3717 opts = pycompat.byteskwargs(opts)
3714 opts = pycompat.byteskwargs(opts)
3718 start = None
3715 start = None
3719 rev = opts.get(b'rev')
3716 rev = opts.get(b'rev')
3720 if rev:
3717 if rev:
3721 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3718 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3722 start = scmutil.revsingle(repo, rev, None).node()
3719 start = scmutil.revsingle(repo, rev, None).node()
3723
3720
3724 if opts.get(b'topo'):
3721 if opts.get(b'topo'):
3725 heads = [repo[h] for h in repo.heads(start)]
3722 heads = [repo[h] for h in repo.heads(start)]
3726 else:
3723 else:
3727 heads = []
3724 heads = []
3728 for branch in repo.branchmap():
3725 for branch in repo.branchmap():
3729 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3726 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3730 heads = [repo[h] for h in heads]
3727 heads = [repo[h] for h in heads]
3731
3728
3732 if branchrevs:
3729 if branchrevs:
3733 branches = set(
3730 branches = set(
3734 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3731 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3735 )
3732 )
3736 heads = [h for h in heads if h.branch() in branches]
3733 heads = [h for h in heads if h.branch() in branches]
3737
3734
3738 if opts.get(b'active') and branchrevs:
3735 if opts.get(b'active') and branchrevs:
3739 dagheads = repo.heads(start)
3736 dagheads = repo.heads(start)
3740 heads = [h for h in heads if h.node() in dagheads]
3737 heads = [h for h in heads if h.node() in dagheads]
3741
3738
3742 if branchrevs:
3739 if branchrevs:
3743 haveheads = set(h.branch() for h in heads)
3740 haveheads = set(h.branch() for h in heads)
3744 if branches - haveheads:
3741 if branches - haveheads:
3745 headless = b', '.join(b for b in branches - haveheads)
3742 headless = b', '.join(b for b in branches - haveheads)
3746 msg = _(b'no open branch heads found on branches %s')
3743 msg = _(b'no open branch heads found on branches %s')
3747 if opts.get(b'rev'):
3744 if opts.get(b'rev'):
3748 msg += _(b' (started at %s)') % opts[b'rev']
3745 msg += _(b' (started at %s)') % opts[b'rev']
3749 ui.warn((msg + b'\n') % headless)
3746 ui.warn((msg + b'\n') % headless)
3750
3747
3751 if not heads:
3748 if not heads:
3752 return 1
3749 return 1
3753
3750
3754 ui.pager(b'heads')
3751 ui.pager(b'heads')
3755 heads = sorted(heads, key=lambda x: -(x.rev()))
3752 heads = sorted(heads, key=lambda x: -(x.rev()))
3756 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3753 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3757 for ctx in heads:
3754 for ctx in heads:
3758 displayer.show(ctx)
3755 displayer.show(ctx)
3759 displayer.close()
3756 displayer.close()
3760
3757
3761
3758
3762 @command(
3759 @command(
3763 b'help',
3760 b'help',
3764 [
3761 [
3765 (b'e', b'extension', None, _(b'show only help for extensions')),
3762 (b'e', b'extension', None, _(b'show only help for extensions')),
3766 (b'c', b'command', None, _(b'show only help for commands')),
3763 (b'c', b'command', None, _(b'show only help for commands')),
3767 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3764 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3768 (
3765 (
3769 b's',
3766 b's',
3770 b'system',
3767 b'system',
3771 [],
3768 [],
3772 _(b'show help for specific platform(s)'),
3769 _(b'show help for specific platform(s)'),
3773 _(b'PLATFORM'),
3770 _(b'PLATFORM'),
3774 ),
3771 ),
3775 ],
3772 ],
3776 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3773 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3777 helpcategory=command.CATEGORY_HELP,
3774 helpcategory=command.CATEGORY_HELP,
3778 norepo=True,
3775 norepo=True,
3779 intents={INTENT_READONLY},
3776 intents={INTENT_READONLY},
3780 )
3777 )
3781 def help_(ui, name=None, **opts):
3778 def help_(ui, name=None, **opts):
3782 """show help for a given topic or a help overview
3779 """show help for a given topic or a help overview
3783
3780
3784 With no arguments, print a list of commands with short help messages.
3781 With no arguments, print a list of commands with short help messages.
3785
3782
3786 Given a topic, extension, or command name, print help for that
3783 Given a topic, extension, or command name, print help for that
3787 topic.
3784 topic.
3788
3785
3789 Returns 0 if successful.
3786 Returns 0 if successful.
3790 """
3787 """
3791
3788
3792 keep = opts.get('system') or []
3789 keep = opts.get('system') or []
3793 if len(keep) == 0:
3790 if len(keep) == 0:
3794 if pycompat.sysplatform.startswith(b'win'):
3791 if pycompat.sysplatform.startswith(b'win'):
3795 keep.append(b'windows')
3792 keep.append(b'windows')
3796 elif pycompat.sysplatform == b'OpenVMS':
3793 elif pycompat.sysplatform == b'OpenVMS':
3797 keep.append(b'vms')
3794 keep.append(b'vms')
3798 elif pycompat.sysplatform == b'plan9':
3795 elif pycompat.sysplatform == b'plan9':
3799 keep.append(b'plan9')
3796 keep.append(b'plan9')
3800 else:
3797 else:
3801 keep.append(b'unix')
3798 keep.append(b'unix')
3802 keep.append(pycompat.sysplatform.lower())
3799 keep.append(pycompat.sysplatform.lower())
3803 if ui.verbose:
3800 if ui.verbose:
3804 keep.append(b'verbose')
3801 keep.append(b'verbose')
3805
3802
3806 commands = sys.modules[__name__]
3803 commands = sys.modules[__name__]
3807 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3804 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3808 ui.pager(b'help')
3805 ui.pager(b'help')
3809 ui.write(formatted)
3806 ui.write(formatted)
3810
3807
3811
3808
3812 @command(
3809 @command(
3813 b'identify|id',
3810 b'identify|id',
3814 [
3811 [
3815 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3812 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3816 (b'n', b'num', None, _(b'show local revision number')),
3813 (b'n', b'num', None, _(b'show local revision number')),
3817 (b'i', b'id', None, _(b'show global revision id')),
3814 (b'i', b'id', None, _(b'show global revision id')),
3818 (b'b', b'branch', None, _(b'show branch')),
3815 (b'b', b'branch', None, _(b'show branch')),
3819 (b't', b'tags', None, _(b'show tags')),
3816 (b't', b'tags', None, _(b'show tags')),
3820 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3817 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3821 ]
3818 ]
3822 + remoteopts
3819 + remoteopts
3823 + formatteropts,
3820 + formatteropts,
3824 _(b'[-nibtB] [-r REV] [SOURCE]'),
3821 _(b'[-nibtB] [-r REV] [SOURCE]'),
3825 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3822 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3826 optionalrepo=True,
3823 optionalrepo=True,
3827 intents={INTENT_READONLY},
3824 intents={INTENT_READONLY},
3828 )
3825 )
3829 def identify(
3826 def identify(
3830 ui,
3827 ui,
3831 repo,
3828 repo,
3832 source=None,
3829 source=None,
3833 rev=None,
3830 rev=None,
3834 num=None,
3831 num=None,
3835 id=None,
3832 id=None,
3836 branch=None,
3833 branch=None,
3837 tags=None,
3834 tags=None,
3838 bookmarks=None,
3835 bookmarks=None,
3839 **opts
3836 **opts
3840 ):
3837 ):
3841 """identify the working directory or specified revision
3838 """identify the working directory or specified revision
3842
3839
3843 Print a summary identifying the repository state at REV using one or
3840 Print a summary identifying the repository state at REV using one or
3844 two parent hash identifiers, followed by a "+" if the working
3841 two parent hash identifiers, followed by a "+" if the working
3845 directory has uncommitted changes, the branch name (if not default),
3842 directory has uncommitted changes, the branch name (if not default),
3846 a list of tags, and a list of bookmarks.
3843 a list of tags, and a list of bookmarks.
3847
3844
3848 When REV is not given, print a summary of the current state of the
3845 When REV is not given, print a summary of the current state of the
3849 repository including the working directory. Specify -r. to get information
3846 repository including the working directory. Specify -r. to get information
3850 of the working directory parent without scanning uncommitted changes.
3847 of the working directory parent without scanning uncommitted changes.
3851
3848
3852 Specifying a path to a repository root or Mercurial bundle will
3849 Specifying a path to a repository root or Mercurial bundle will
3853 cause lookup to operate on that repository/bundle.
3850 cause lookup to operate on that repository/bundle.
3854
3851
3855 .. container:: verbose
3852 .. container:: verbose
3856
3853
3857 Template:
3854 Template:
3858
3855
3859 The following keywords are supported in addition to the common template
3856 The following keywords are supported in addition to the common template
3860 keywords and functions. See also :hg:`help templates`.
3857 keywords and functions. See also :hg:`help templates`.
3861
3858
3862 :dirty: String. Character ``+`` denoting if the working directory has
3859 :dirty: String. Character ``+`` denoting if the working directory has
3863 uncommitted changes.
3860 uncommitted changes.
3864 :id: String. One or two nodes, optionally followed by ``+``.
3861 :id: String. One or two nodes, optionally followed by ``+``.
3865 :parents: List of strings. Parent nodes of the changeset.
3862 :parents: List of strings. Parent nodes of the changeset.
3866
3863
3867 Examples:
3864 Examples:
3868
3865
3869 - generate a build identifier for the working directory::
3866 - generate a build identifier for the working directory::
3870
3867
3871 hg id --id > build-id.dat
3868 hg id --id > build-id.dat
3872
3869
3873 - find the revision corresponding to a tag::
3870 - find the revision corresponding to a tag::
3874
3871
3875 hg id -n -r 1.3
3872 hg id -n -r 1.3
3876
3873
3877 - check the most recent revision of a remote repository::
3874 - check the most recent revision of a remote repository::
3878
3875
3879 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3876 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3880
3877
3881 See :hg:`log` for generating more information about specific revisions,
3878 See :hg:`log` for generating more information about specific revisions,
3882 including full hash identifiers.
3879 including full hash identifiers.
3883
3880
3884 Returns 0 if successful.
3881 Returns 0 if successful.
3885 """
3882 """
3886
3883
3887 opts = pycompat.byteskwargs(opts)
3884 opts = pycompat.byteskwargs(opts)
3888 if not repo and not source:
3885 if not repo and not source:
3889 raise error.Abort(
3886 raise error.Abort(
3890 _(b"there is no Mercurial repository here (.hg not found)")
3887 _(b"there is no Mercurial repository here (.hg not found)")
3891 )
3888 )
3892
3889
3893 default = not (num or id or branch or tags or bookmarks)
3890 default = not (num or id or branch or tags or bookmarks)
3894 output = []
3891 output = []
3895 revs = []
3892 revs = []
3896
3893
3897 if source:
3894 if source:
3898 source, branches = hg.parseurl(ui.expandpath(source))
3895 source, branches = hg.parseurl(ui.expandpath(source))
3899 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3896 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3900 repo = peer.local()
3897 repo = peer.local()
3901 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3898 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3902
3899
3903 fm = ui.formatter(b'identify', opts)
3900 fm = ui.formatter(b'identify', opts)
3904 fm.startitem()
3901 fm.startitem()
3905
3902
3906 if not repo:
3903 if not repo:
3907 if num or branch or tags:
3904 if num or branch or tags:
3908 raise error.Abort(
3905 raise error.Abort(
3909 _(b"can't query remote revision number, branch, or tags")
3906 _(b"can't query remote revision number, branch, or tags")
3910 )
3907 )
3911 if not rev and revs:
3908 if not rev and revs:
3912 rev = revs[0]
3909 rev = revs[0]
3913 if not rev:
3910 if not rev:
3914 rev = b"tip"
3911 rev = b"tip"
3915
3912
3916 remoterev = peer.lookup(rev)
3913 remoterev = peer.lookup(rev)
3917 hexrev = fm.hexfunc(remoterev)
3914 hexrev = fm.hexfunc(remoterev)
3918 if default or id:
3915 if default or id:
3919 output = [hexrev]
3916 output = [hexrev]
3920 fm.data(id=hexrev)
3917 fm.data(id=hexrev)
3921
3918
3922 @util.cachefunc
3919 @util.cachefunc
3923 def getbms():
3920 def getbms():
3924 bms = []
3921 bms = []
3925
3922
3926 if b'bookmarks' in peer.listkeys(b'namespaces'):
3923 if b'bookmarks' in peer.listkeys(b'namespaces'):
3927 hexremoterev = hex(remoterev)
3924 hexremoterev = hex(remoterev)
3928 bms = [
3925 bms = [
3929 bm
3926 bm
3930 for bm, bmr in pycompat.iteritems(
3927 for bm, bmr in pycompat.iteritems(
3931 peer.listkeys(b'bookmarks')
3928 peer.listkeys(b'bookmarks')
3932 )
3929 )
3933 if bmr == hexremoterev
3930 if bmr == hexremoterev
3934 ]
3931 ]
3935
3932
3936 return sorted(bms)
3933 return sorted(bms)
3937
3934
3938 if fm.isplain():
3935 if fm.isplain():
3939 if bookmarks:
3936 if bookmarks:
3940 output.extend(getbms())
3937 output.extend(getbms())
3941 elif default and not ui.quiet:
3938 elif default and not ui.quiet:
3942 # multiple bookmarks for a single parent separated by '/'
3939 # multiple bookmarks for a single parent separated by '/'
3943 bm = b'/'.join(getbms())
3940 bm = b'/'.join(getbms())
3944 if bm:
3941 if bm:
3945 output.append(bm)
3942 output.append(bm)
3946 else:
3943 else:
3947 fm.data(node=hex(remoterev))
3944 fm.data(node=hex(remoterev))
3948 if bookmarks or b'bookmarks' in fm.datahint():
3945 if bookmarks or b'bookmarks' in fm.datahint():
3949 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3946 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3950 else:
3947 else:
3951 if rev:
3948 if rev:
3952 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3949 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3953 ctx = scmutil.revsingle(repo, rev, None)
3950 ctx = scmutil.revsingle(repo, rev, None)
3954
3951
3955 if ctx.rev() is None:
3952 if ctx.rev() is None:
3956 ctx = repo[None]
3953 ctx = repo[None]
3957 parents = ctx.parents()
3954 parents = ctx.parents()
3958 taglist = []
3955 taglist = []
3959 for p in parents:
3956 for p in parents:
3960 taglist.extend(p.tags())
3957 taglist.extend(p.tags())
3961
3958
3962 dirty = b""
3959 dirty = b""
3963 if ctx.dirty(missing=True, merge=False, branch=False):
3960 if ctx.dirty(missing=True, merge=False, branch=False):
3964 dirty = b'+'
3961 dirty = b'+'
3965 fm.data(dirty=dirty)
3962 fm.data(dirty=dirty)
3966
3963
3967 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3964 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3968 if default or id:
3965 if default or id:
3969 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3966 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3970 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3967 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3971
3968
3972 if num:
3969 if num:
3973 numoutput = [b"%d" % p.rev() for p in parents]
3970 numoutput = [b"%d" % p.rev() for p in parents]
3974 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3971 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3975
3972
3976 fm.data(
3973 fm.data(
3977 parents=fm.formatlist(
3974 parents=fm.formatlist(
3978 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3975 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3979 )
3976 )
3980 )
3977 )
3981 else:
3978 else:
3982 hexoutput = fm.hexfunc(ctx.node())
3979 hexoutput = fm.hexfunc(ctx.node())
3983 if default or id:
3980 if default or id:
3984 output = [hexoutput]
3981 output = [hexoutput]
3985 fm.data(id=hexoutput)
3982 fm.data(id=hexoutput)
3986
3983
3987 if num:
3984 if num:
3988 output.append(pycompat.bytestr(ctx.rev()))
3985 output.append(pycompat.bytestr(ctx.rev()))
3989 taglist = ctx.tags()
3986 taglist = ctx.tags()
3990
3987
3991 if default and not ui.quiet:
3988 if default and not ui.quiet:
3992 b = ctx.branch()
3989 b = ctx.branch()
3993 if b != b'default':
3990 if b != b'default':
3994 output.append(b"(%s)" % b)
3991 output.append(b"(%s)" % b)
3995
3992
3996 # multiple tags for a single parent separated by '/'
3993 # multiple tags for a single parent separated by '/'
3997 t = b'/'.join(taglist)
3994 t = b'/'.join(taglist)
3998 if t:
3995 if t:
3999 output.append(t)
3996 output.append(t)
4000
3997
4001 # multiple bookmarks for a single parent separated by '/'
3998 # multiple bookmarks for a single parent separated by '/'
4002 bm = b'/'.join(ctx.bookmarks())
3999 bm = b'/'.join(ctx.bookmarks())
4003 if bm:
4000 if bm:
4004 output.append(bm)
4001 output.append(bm)
4005 else:
4002 else:
4006 if branch:
4003 if branch:
4007 output.append(ctx.branch())
4004 output.append(ctx.branch())
4008
4005
4009 if tags:
4006 if tags:
4010 output.extend(taglist)
4007 output.extend(taglist)
4011
4008
4012 if bookmarks:
4009 if bookmarks:
4013 output.extend(ctx.bookmarks())
4010 output.extend(ctx.bookmarks())
4014
4011
4015 fm.data(node=ctx.hex())
4012 fm.data(node=ctx.hex())
4016 fm.data(branch=ctx.branch())
4013 fm.data(branch=ctx.branch())
4017 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4014 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4018 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4015 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4019 fm.context(ctx=ctx)
4016 fm.context(ctx=ctx)
4020
4017
4021 fm.plain(b"%s\n" % b' '.join(output))
4018 fm.plain(b"%s\n" % b' '.join(output))
4022 fm.end()
4019 fm.end()
4023
4020
4024
4021
4025 @command(
4022 @command(
4026 b'import|patch',
4023 b'import|patch',
4027 [
4024 [
4028 (
4025 (
4029 b'p',
4026 b'p',
4030 b'strip',
4027 b'strip',
4031 1,
4028 1,
4032 _(
4029 _(
4033 b'directory strip option for patch. This has the same '
4030 b'directory strip option for patch. This has the same '
4034 b'meaning as the corresponding patch option'
4031 b'meaning as the corresponding patch option'
4035 ),
4032 ),
4036 _(b'NUM'),
4033 _(b'NUM'),
4037 ),
4034 ),
4038 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4035 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4039 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4036 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4040 (
4037 (
4041 b'f',
4038 b'f',
4042 b'force',
4039 b'force',
4043 None,
4040 None,
4044 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4041 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4045 ),
4042 ),
4046 (
4043 (
4047 b'',
4044 b'',
4048 b'no-commit',
4045 b'no-commit',
4049 None,
4046 None,
4050 _(b"don't commit, just update the working directory"),
4047 _(b"don't commit, just update the working directory"),
4051 ),
4048 ),
4052 (
4049 (
4053 b'',
4050 b'',
4054 b'bypass',
4051 b'bypass',
4055 None,
4052 None,
4056 _(b"apply patch without touching the working directory"),
4053 _(b"apply patch without touching the working directory"),
4057 ),
4054 ),
4058 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4055 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4059 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4056 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4060 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4057 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4061 (
4058 (
4062 b'',
4059 b'',
4063 b'import-branch',
4060 b'import-branch',
4064 None,
4061 None,
4065 _(b'use any branch information in patch (implied by --exact)'),
4062 _(b'use any branch information in patch (implied by --exact)'),
4066 ),
4063 ),
4067 ]
4064 ]
4068 + commitopts
4065 + commitopts
4069 + commitopts2
4066 + commitopts2
4070 + similarityopts,
4067 + similarityopts,
4071 _(b'[OPTION]... PATCH...'),
4068 _(b'[OPTION]... PATCH...'),
4072 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4069 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4073 )
4070 )
4074 def import_(ui, repo, patch1=None, *patches, **opts):
4071 def import_(ui, repo, patch1=None, *patches, **opts):
4075 """import an ordered set of patches
4072 """import an ordered set of patches
4076
4073
4077 Import a list of patches and commit them individually (unless
4074 Import a list of patches and commit them individually (unless
4078 --no-commit is specified).
4075 --no-commit is specified).
4079
4076
4080 To read a patch from standard input (stdin), use "-" as the patch
4077 To read a patch from standard input (stdin), use "-" as the patch
4081 name. If a URL is specified, the patch will be downloaded from
4078 name. If a URL is specified, the patch will be downloaded from
4082 there.
4079 there.
4083
4080
4084 Import first applies changes to the working directory (unless
4081 Import first applies changes to the working directory (unless
4085 --bypass is specified), import will abort if there are outstanding
4082 --bypass is specified), import will abort if there are outstanding
4086 changes.
4083 changes.
4087
4084
4088 Use --bypass to apply and commit patches directly to the
4085 Use --bypass to apply and commit patches directly to the
4089 repository, without affecting the working directory. Without
4086 repository, without affecting the working directory. Without
4090 --exact, patches will be applied on top of the working directory
4087 --exact, patches will be applied on top of the working directory
4091 parent revision.
4088 parent revision.
4092
4089
4093 You can import a patch straight from a mail message. Even patches
4090 You can import a patch straight from a mail message. Even patches
4094 as attachments work (to use the body part, it must have type
4091 as attachments work (to use the body part, it must have type
4095 text/plain or text/x-patch). From and Subject headers of email
4092 text/plain or text/x-patch). From and Subject headers of email
4096 message are used as default committer and commit message. All
4093 message are used as default committer and commit message. All
4097 text/plain body parts before first diff are added to the commit
4094 text/plain body parts before first diff are added to the commit
4098 message.
4095 message.
4099
4096
4100 If the imported patch was generated by :hg:`export`, user and
4097 If the imported patch was generated by :hg:`export`, user and
4101 description from patch override values from message headers and
4098 description from patch override values from message headers and
4102 body. Values given on command line with -m/--message and -u/--user
4099 body. Values given on command line with -m/--message and -u/--user
4103 override these.
4100 override these.
4104
4101
4105 If --exact is specified, import will set the working directory to
4102 If --exact is specified, import will set the working directory to
4106 the parent of each patch before applying it, and will abort if the
4103 the parent of each patch before applying it, and will abort if the
4107 resulting changeset has a different ID than the one recorded in
4104 resulting changeset has a different ID than the one recorded in
4108 the patch. This will guard against various ways that portable
4105 the patch. This will guard against various ways that portable
4109 patch formats and mail systems might fail to transfer Mercurial
4106 patch formats and mail systems might fail to transfer Mercurial
4110 data or metadata. See :hg:`bundle` for lossless transmission.
4107 data or metadata. See :hg:`bundle` for lossless transmission.
4111
4108
4112 Use --partial to ensure a changeset will be created from the patch
4109 Use --partial to ensure a changeset will be created from the patch
4113 even if some hunks fail to apply. Hunks that fail to apply will be
4110 even if some hunks fail to apply. Hunks that fail to apply will be
4114 written to a <target-file>.rej file. Conflicts can then be resolved
4111 written to a <target-file>.rej file. Conflicts can then be resolved
4115 by hand before :hg:`commit --amend` is run to update the created
4112 by hand before :hg:`commit --amend` is run to update the created
4116 changeset. This flag exists to let people import patches that
4113 changeset. This flag exists to let people import patches that
4117 partially apply without losing the associated metadata (author,
4114 partially apply without losing the associated metadata (author,
4118 date, description, ...).
4115 date, description, ...).
4119
4116
4120 .. note::
4117 .. note::
4121
4118
4122 When no hunks apply cleanly, :hg:`import --partial` will create
4119 When no hunks apply cleanly, :hg:`import --partial` will create
4123 an empty changeset, importing only the patch metadata.
4120 an empty changeset, importing only the patch metadata.
4124
4121
4125 With -s/--similarity, hg will attempt to discover renames and
4122 With -s/--similarity, hg will attempt to discover renames and
4126 copies in the patch in the same way as :hg:`addremove`.
4123 copies in the patch in the same way as :hg:`addremove`.
4127
4124
4128 It is possible to use external patch programs to perform the patch
4125 It is possible to use external patch programs to perform the patch
4129 by setting the ``ui.patch`` configuration option. For the default
4126 by setting the ``ui.patch`` configuration option. For the default
4130 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4127 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4131 See :hg:`help config` for more information about configuration
4128 See :hg:`help config` for more information about configuration
4132 files and how to use these options.
4129 files and how to use these options.
4133
4130
4134 See :hg:`help dates` for a list of formats valid for -d/--date.
4131 See :hg:`help dates` for a list of formats valid for -d/--date.
4135
4132
4136 .. container:: verbose
4133 .. container:: verbose
4137
4134
4138 Examples:
4135 Examples:
4139
4136
4140 - import a traditional patch from a website and detect renames::
4137 - import a traditional patch from a website and detect renames::
4141
4138
4142 hg import -s 80 http://example.com/bugfix.patch
4139 hg import -s 80 http://example.com/bugfix.patch
4143
4140
4144 - import a changeset from an hgweb server::
4141 - import a changeset from an hgweb server::
4145
4142
4146 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4143 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4147
4144
4148 - import all the patches in an Unix-style mbox::
4145 - import all the patches in an Unix-style mbox::
4149
4146
4150 hg import incoming-patches.mbox
4147 hg import incoming-patches.mbox
4151
4148
4152 - import patches from stdin::
4149 - import patches from stdin::
4153
4150
4154 hg import -
4151 hg import -
4155
4152
4156 - attempt to exactly restore an exported changeset (not always
4153 - attempt to exactly restore an exported changeset (not always
4157 possible)::
4154 possible)::
4158
4155
4159 hg import --exact proposed-fix.patch
4156 hg import --exact proposed-fix.patch
4160
4157
4161 - use an external tool to apply a patch which is too fuzzy for
4158 - use an external tool to apply a patch which is too fuzzy for
4162 the default internal tool.
4159 the default internal tool.
4163
4160
4164 hg import --config ui.patch="patch --merge" fuzzy.patch
4161 hg import --config ui.patch="patch --merge" fuzzy.patch
4165
4162
4166 - change the default fuzzing from 2 to a less strict 7
4163 - change the default fuzzing from 2 to a less strict 7
4167
4164
4168 hg import --config ui.fuzz=7 fuzz.patch
4165 hg import --config ui.fuzz=7 fuzz.patch
4169
4166
4170 Returns 0 on success, 1 on partial success (see --partial).
4167 Returns 0 on success, 1 on partial success (see --partial).
4171 """
4168 """
4172
4169
4173 opts = pycompat.byteskwargs(opts)
4170 opts = pycompat.byteskwargs(opts)
4174 if not patch1:
4171 if not patch1:
4175 raise error.Abort(_(b'need at least one patch to import'))
4172 raise error.Abort(_(b'need at least one patch to import'))
4176
4173
4177 patches = (patch1,) + patches
4174 patches = (patch1,) + patches
4178
4175
4179 date = opts.get(b'date')
4176 date = opts.get(b'date')
4180 if date:
4177 if date:
4181 opts[b'date'] = dateutil.parsedate(date)
4178 opts[b'date'] = dateutil.parsedate(date)
4182
4179
4183 exact = opts.get(b'exact')
4180 exact = opts.get(b'exact')
4184 update = not opts.get(b'bypass')
4181 update = not opts.get(b'bypass')
4185 if not update and opts.get(b'no_commit'):
4182 if not update and opts.get(b'no_commit'):
4186 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4183 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4187 try:
4184 try:
4188 sim = float(opts.get(b'similarity') or 0)
4185 sim = float(opts.get(b'similarity') or 0)
4189 except ValueError:
4186 except ValueError:
4190 raise error.Abort(_(b'similarity must be a number'))
4187 raise error.Abort(_(b'similarity must be a number'))
4191 if sim < 0 or sim > 100:
4188 if sim < 0 or sim > 100:
4192 raise error.Abort(_(b'similarity must be between 0 and 100'))
4189 raise error.Abort(_(b'similarity must be between 0 and 100'))
4193 if sim and not update:
4190 if sim and not update:
4194 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4191 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4195 if exact:
4192 if exact:
4196 if opts.get(b'edit'):
4193 if opts.get(b'edit'):
4197 raise error.Abort(_(b'cannot use --exact with --edit'))
4194 raise error.Abort(_(b'cannot use --exact with --edit'))
4198 if opts.get(b'prefix'):
4195 if opts.get(b'prefix'):
4199 raise error.Abort(_(b'cannot use --exact with --prefix'))
4196 raise error.Abort(_(b'cannot use --exact with --prefix'))
4200
4197
4201 base = opts[b"base"]
4198 base = opts[b"base"]
4202 msgs = []
4199 msgs = []
4203 ret = 0
4200 ret = 0
4204
4201
4205 with repo.wlock():
4202 with repo.wlock():
4206 if update:
4203 if update:
4207 cmdutil.checkunfinished(repo)
4204 cmdutil.checkunfinished(repo)
4208 if exact or not opts.get(b'force'):
4205 if exact or not opts.get(b'force'):
4209 cmdutil.bailifchanged(repo)
4206 cmdutil.bailifchanged(repo)
4210
4207
4211 if not opts.get(b'no_commit'):
4208 if not opts.get(b'no_commit'):
4212 lock = repo.lock
4209 lock = repo.lock
4213 tr = lambda: repo.transaction(b'import')
4210 tr = lambda: repo.transaction(b'import')
4214 dsguard = util.nullcontextmanager
4211 dsguard = util.nullcontextmanager
4215 else:
4212 else:
4216 lock = util.nullcontextmanager
4213 lock = util.nullcontextmanager
4217 tr = util.nullcontextmanager
4214 tr = util.nullcontextmanager
4218 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4215 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4219 with lock(), tr(), dsguard():
4216 with lock(), tr(), dsguard():
4220 parents = repo[None].parents()
4217 parents = repo[None].parents()
4221 for patchurl in patches:
4218 for patchurl in patches:
4222 if patchurl == b'-':
4219 if patchurl == b'-':
4223 ui.status(_(b'applying patch from stdin\n'))
4220 ui.status(_(b'applying patch from stdin\n'))
4224 patchfile = ui.fin
4221 patchfile = ui.fin
4225 patchurl = b'stdin' # for error message
4222 patchurl = b'stdin' # for error message
4226 else:
4223 else:
4227 patchurl = os.path.join(base, patchurl)
4224 patchurl = os.path.join(base, patchurl)
4228 ui.status(_(b'applying %s\n') % patchurl)
4225 ui.status(_(b'applying %s\n') % patchurl)
4229 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4226 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4230
4227
4231 haspatch = False
4228 haspatch = False
4232 for hunk in patch.split(patchfile):
4229 for hunk in patch.split(patchfile):
4233 with patch.extract(ui, hunk) as patchdata:
4230 with patch.extract(ui, hunk) as patchdata:
4234 msg, node, rej = cmdutil.tryimportone(
4231 msg, node, rej = cmdutil.tryimportone(
4235 ui, repo, patchdata, parents, opts, msgs, hg.clean
4232 ui, repo, patchdata, parents, opts, msgs, hg.clean
4236 )
4233 )
4237 if msg:
4234 if msg:
4238 haspatch = True
4235 haspatch = True
4239 ui.note(msg + b'\n')
4236 ui.note(msg + b'\n')
4240 if update or exact:
4237 if update or exact:
4241 parents = repo[None].parents()
4238 parents = repo[None].parents()
4242 else:
4239 else:
4243 parents = [repo[node]]
4240 parents = [repo[node]]
4244 if rej:
4241 if rej:
4245 ui.write_err(_(b"patch applied partially\n"))
4242 ui.write_err(_(b"patch applied partially\n"))
4246 ui.write_err(
4243 ui.write_err(
4247 _(
4244 _(
4248 b"(fix the .rej files and run "
4245 b"(fix the .rej files and run "
4249 b"`hg commit --amend`)\n"
4246 b"`hg commit --amend`)\n"
4250 )
4247 )
4251 )
4248 )
4252 ret = 1
4249 ret = 1
4253 break
4250 break
4254
4251
4255 if not haspatch:
4252 if not haspatch:
4256 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4253 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4257
4254
4258 if msgs:
4255 if msgs:
4259 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4256 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4260 return ret
4257 return ret
4261
4258
4262
4259
4263 @command(
4260 @command(
4264 b'incoming|in',
4261 b'incoming|in',
4265 [
4262 [
4266 (
4263 (
4267 b'f',
4264 b'f',
4268 b'force',
4265 b'force',
4269 None,
4266 None,
4270 _(b'run even if remote repository is unrelated'),
4267 _(b'run even if remote repository is unrelated'),
4271 ),
4268 ),
4272 (b'n', b'newest-first', None, _(b'show newest record first')),
4269 (b'n', b'newest-first', None, _(b'show newest record first')),
4273 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4270 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4274 (
4271 (
4275 b'r',
4272 b'r',
4276 b'rev',
4273 b'rev',
4277 [],
4274 [],
4278 _(b'a remote changeset intended to be added'),
4275 _(b'a remote changeset intended to be added'),
4279 _(b'REV'),
4276 _(b'REV'),
4280 ),
4277 ),
4281 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4278 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4282 (
4279 (
4283 b'b',
4280 b'b',
4284 b'branch',
4281 b'branch',
4285 [],
4282 [],
4286 _(b'a specific branch you would like to pull'),
4283 _(b'a specific branch you would like to pull'),
4287 _(b'BRANCH'),
4284 _(b'BRANCH'),
4288 ),
4285 ),
4289 ]
4286 ]
4290 + logopts
4287 + logopts
4291 + remoteopts
4288 + remoteopts
4292 + subrepoopts,
4289 + subrepoopts,
4293 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4290 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4294 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4291 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4295 )
4292 )
4296 def incoming(ui, repo, source=b"default", **opts):
4293 def incoming(ui, repo, source=b"default", **opts):
4297 """show new changesets found in source
4294 """show new changesets found in source
4298
4295
4299 Show new changesets found in the specified path/URL or the default
4296 Show new changesets found in the specified path/URL or the default
4300 pull location. These are the changesets that would have been pulled
4297 pull location. These are the changesets that would have been pulled
4301 by :hg:`pull` at the time you issued this command.
4298 by :hg:`pull` at the time you issued this command.
4302
4299
4303 See pull for valid source format details.
4300 See pull for valid source format details.
4304
4301
4305 .. container:: verbose
4302 .. container:: verbose
4306
4303
4307 With -B/--bookmarks, the result of bookmark comparison between
4304 With -B/--bookmarks, the result of bookmark comparison between
4308 local and remote repositories is displayed. With -v/--verbose,
4305 local and remote repositories is displayed. With -v/--verbose,
4309 status is also displayed for each bookmark like below::
4306 status is also displayed for each bookmark like below::
4310
4307
4311 BM1 01234567890a added
4308 BM1 01234567890a added
4312 BM2 1234567890ab advanced
4309 BM2 1234567890ab advanced
4313 BM3 234567890abc diverged
4310 BM3 234567890abc diverged
4314 BM4 34567890abcd changed
4311 BM4 34567890abcd changed
4315
4312
4316 The action taken locally when pulling depends on the
4313 The action taken locally when pulling depends on the
4317 status of each bookmark:
4314 status of each bookmark:
4318
4315
4319 :``added``: pull will create it
4316 :``added``: pull will create it
4320 :``advanced``: pull will update it
4317 :``advanced``: pull will update it
4321 :``diverged``: pull will create a divergent bookmark
4318 :``diverged``: pull will create a divergent bookmark
4322 :``changed``: result depends on remote changesets
4319 :``changed``: result depends on remote changesets
4323
4320
4324 From the point of view of pulling behavior, bookmark
4321 From the point of view of pulling behavior, bookmark
4325 existing only in the remote repository are treated as ``added``,
4322 existing only in the remote repository are treated as ``added``,
4326 even if it is in fact locally deleted.
4323 even if it is in fact locally deleted.
4327
4324
4328 .. container:: verbose
4325 .. container:: verbose
4329
4326
4330 For remote repository, using --bundle avoids downloading the
4327 For remote repository, using --bundle avoids downloading the
4331 changesets twice if the incoming is followed by a pull.
4328 changesets twice if the incoming is followed by a pull.
4332
4329
4333 Examples:
4330 Examples:
4334
4331
4335 - show incoming changes with patches and full description::
4332 - show incoming changes with patches and full description::
4336
4333
4337 hg incoming -vp
4334 hg incoming -vp
4338
4335
4339 - show incoming changes excluding merges, store a bundle::
4336 - show incoming changes excluding merges, store a bundle::
4340
4337
4341 hg in -vpM --bundle incoming.hg
4338 hg in -vpM --bundle incoming.hg
4342 hg pull incoming.hg
4339 hg pull incoming.hg
4343
4340
4344 - briefly list changes inside a bundle::
4341 - briefly list changes inside a bundle::
4345
4342
4346 hg in changes.hg -T "{desc|firstline}\\n"
4343 hg in changes.hg -T "{desc|firstline}\\n"
4347
4344
4348 Returns 0 if there are incoming changes, 1 otherwise.
4345 Returns 0 if there are incoming changes, 1 otherwise.
4349 """
4346 """
4350 opts = pycompat.byteskwargs(opts)
4347 opts = pycompat.byteskwargs(opts)
4351 if opts.get(b'graph'):
4348 if opts.get(b'graph'):
4352 logcmdutil.checkunsupportedgraphflags([], opts)
4349 logcmdutil.checkunsupportedgraphflags([], opts)
4353
4350
4354 def display(other, chlist, displayer):
4351 def display(other, chlist, displayer):
4355 revdag = logcmdutil.graphrevs(other, chlist, opts)
4352 revdag = logcmdutil.graphrevs(other, chlist, opts)
4356 logcmdutil.displaygraph(
4353 logcmdutil.displaygraph(
4357 ui, repo, revdag, displayer, graphmod.asciiedges
4354 ui, repo, revdag, displayer, graphmod.asciiedges
4358 )
4355 )
4359
4356
4360 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4357 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4361 return 0
4358 return 0
4362
4359
4363 if opts.get(b'bundle') and opts.get(b'subrepos'):
4360 if opts.get(b'bundle') and opts.get(b'subrepos'):
4364 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4361 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4365
4362
4366 if opts.get(b'bookmarks'):
4363 if opts.get(b'bookmarks'):
4367 source, branches = hg.parseurl(
4364 source, branches = hg.parseurl(
4368 ui.expandpath(source), opts.get(b'branch')
4365 ui.expandpath(source), opts.get(b'branch')
4369 )
4366 )
4370 other = hg.peer(repo, opts, source)
4367 other = hg.peer(repo, opts, source)
4371 if b'bookmarks' not in other.listkeys(b'namespaces'):
4368 if b'bookmarks' not in other.listkeys(b'namespaces'):
4372 ui.warn(_(b"remote doesn't support bookmarks\n"))
4369 ui.warn(_(b"remote doesn't support bookmarks\n"))
4373 return 0
4370 return 0
4374 ui.pager(b'incoming')
4371 ui.pager(b'incoming')
4375 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4372 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4376 return bookmarks.incoming(ui, repo, other)
4373 return bookmarks.incoming(ui, repo, other)
4377
4374
4378 repo._subtoppath = ui.expandpath(source)
4375 repo._subtoppath = ui.expandpath(source)
4379 try:
4376 try:
4380 return hg.incoming(ui, repo, source, opts)
4377 return hg.incoming(ui, repo, source, opts)
4381 finally:
4378 finally:
4382 del repo._subtoppath
4379 del repo._subtoppath
4383
4380
4384
4381
4385 @command(
4382 @command(
4386 b'init',
4383 b'init',
4387 remoteopts,
4384 remoteopts,
4388 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4385 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4389 helpcategory=command.CATEGORY_REPO_CREATION,
4386 helpcategory=command.CATEGORY_REPO_CREATION,
4390 helpbasic=True,
4387 helpbasic=True,
4391 norepo=True,
4388 norepo=True,
4392 )
4389 )
4393 def init(ui, dest=b".", **opts):
4390 def init(ui, dest=b".", **opts):
4394 """create a new repository in the given directory
4391 """create a new repository in the given directory
4395
4392
4396 Initialize a new repository in the given directory. If the given
4393 Initialize a new repository in the given directory. If the given
4397 directory does not exist, it will be created.
4394 directory does not exist, it will be created.
4398
4395
4399 If no directory is given, the current directory is used.
4396 If no directory is given, the current directory is used.
4400
4397
4401 It is possible to specify an ``ssh://`` URL as the destination.
4398 It is possible to specify an ``ssh://`` URL as the destination.
4402 See :hg:`help urls` for more information.
4399 See :hg:`help urls` for more information.
4403
4400
4404 Returns 0 on success.
4401 Returns 0 on success.
4405 """
4402 """
4406 opts = pycompat.byteskwargs(opts)
4403 opts = pycompat.byteskwargs(opts)
4407 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4404 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4408
4405
4409
4406
4410 @command(
4407 @command(
4411 b'locate',
4408 b'locate',
4412 [
4409 [
4413 (
4410 (
4414 b'r',
4411 b'r',
4415 b'rev',
4412 b'rev',
4416 b'',
4413 b'',
4417 _(b'search the repository as it is in REV'),
4414 _(b'search the repository as it is in REV'),
4418 _(b'REV'),
4415 _(b'REV'),
4419 ),
4416 ),
4420 (
4417 (
4421 b'0',
4418 b'0',
4422 b'print0',
4419 b'print0',
4423 None,
4420 None,
4424 _(b'end filenames with NUL, for use with xargs'),
4421 _(b'end filenames with NUL, for use with xargs'),
4425 ),
4422 ),
4426 (
4423 (
4427 b'f',
4424 b'f',
4428 b'fullpath',
4425 b'fullpath',
4429 None,
4426 None,
4430 _(b'print complete paths from the filesystem root'),
4427 _(b'print complete paths from the filesystem root'),
4431 ),
4428 ),
4432 ]
4429 ]
4433 + walkopts,
4430 + walkopts,
4434 _(b'[OPTION]... [PATTERN]...'),
4431 _(b'[OPTION]... [PATTERN]...'),
4435 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4432 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4436 )
4433 )
4437 def locate(ui, repo, *pats, **opts):
4434 def locate(ui, repo, *pats, **opts):
4438 """locate files matching specific patterns (DEPRECATED)
4435 """locate files matching specific patterns (DEPRECATED)
4439
4436
4440 Print files under Mercurial control in the working directory whose
4437 Print files under Mercurial control in the working directory whose
4441 names match the given patterns.
4438 names match the given patterns.
4442
4439
4443 By default, this command searches all directories in the working
4440 By default, this command searches all directories in the working
4444 directory. To search just the current directory and its
4441 directory. To search just the current directory and its
4445 subdirectories, use "--include .".
4442 subdirectories, use "--include .".
4446
4443
4447 If no patterns are given to match, this command prints the names
4444 If no patterns are given to match, this command prints the names
4448 of all files under Mercurial control in the working directory.
4445 of all files under Mercurial control in the working directory.
4449
4446
4450 If you want to feed the output of this command into the "xargs"
4447 If you want to feed the output of this command into the "xargs"
4451 command, use the -0 option to both this command and "xargs". This
4448 command, use the -0 option to both this command and "xargs". This
4452 will avoid the problem of "xargs" treating single filenames that
4449 will avoid the problem of "xargs" treating single filenames that
4453 contain whitespace as multiple filenames.
4450 contain whitespace as multiple filenames.
4454
4451
4455 See :hg:`help files` for a more versatile command.
4452 See :hg:`help files` for a more versatile command.
4456
4453
4457 Returns 0 if a match is found, 1 otherwise.
4454 Returns 0 if a match is found, 1 otherwise.
4458 """
4455 """
4459 opts = pycompat.byteskwargs(opts)
4456 opts = pycompat.byteskwargs(opts)
4460 if opts.get(b'print0'):
4457 if opts.get(b'print0'):
4461 end = b'\0'
4458 end = b'\0'
4462 else:
4459 else:
4463 end = b'\n'
4460 end = b'\n'
4464 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4461 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4465
4462
4466 ret = 1
4463 ret = 1
4467 m = scmutil.match(
4464 m = scmutil.match(
4468 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4465 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4469 )
4466 )
4470
4467
4471 ui.pager(b'locate')
4468 ui.pager(b'locate')
4472 if ctx.rev() is None:
4469 if ctx.rev() is None:
4473 # When run on the working copy, "locate" includes removed files, so
4470 # When run on the working copy, "locate" includes removed files, so
4474 # we get the list of files from the dirstate.
4471 # we get the list of files from the dirstate.
4475 filesgen = sorted(repo.dirstate.matches(m))
4472 filesgen = sorted(repo.dirstate.matches(m))
4476 else:
4473 else:
4477 filesgen = ctx.matches(m)
4474 filesgen = ctx.matches(m)
4478 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4475 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4479 for abs in filesgen:
4476 for abs in filesgen:
4480 if opts.get(b'fullpath'):
4477 if opts.get(b'fullpath'):
4481 ui.write(repo.wjoin(abs), end)
4478 ui.write(repo.wjoin(abs), end)
4482 else:
4479 else:
4483 ui.write(uipathfn(abs), end)
4480 ui.write(uipathfn(abs), end)
4484 ret = 0
4481 ret = 0
4485
4482
4486 return ret
4483 return ret
4487
4484
4488
4485
4489 @command(
4486 @command(
4490 b'log|history',
4487 b'log|history',
4491 [
4488 [
4492 (
4489 (
4493 b'f',
4490 b'f',
4494 b'follow',
4491 b'follow',
4495 None,
4492 None,
4496 _(
4493 _(
4497 b'follow changeset history, or file history across copies and renames'
4494 b'follow changeset history, or file history across copies and renames'
4498 ),
4495 ),
4499 ),
4496 ),
4500 (
4497 (
4501 b'',
4498 b'',
4502 b'follow-first',
4499 b'follow-first',
4503 None,
4500 None,
4504 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4501 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4505 ),
4502 ),
4506 (
4503 (
4507 b'd',
4504 b'd',
4508 b'date',
4505 b'date',
4509 b'',
4506 b'',
4510 _(b'show revisions matching date spec'),
4507 _(b'show revisions matching date spec'),
4511 _(b'DATE'),
4508 _(b'DATE'),
4512 ),
4509 ),
4513 (b'C', b'copies', None, _(b'show copied files')),
4510 (b'C', b'copies', None, _(b'show copied files')),
4514 (
4511 (
4515 b'k',
4512 b'k',
4516 b'keyword',
4513 b'keyword',
4517 [],
4514 [],
4518 _(b'do case-insensitive search for a given text'),
4515 _(b'do case-insensitive search for a given text'),
4519 _(b'TEXT'),
4516 _(b'TEXT'),
4520 ),
4517 ),
4521 (
4518 (
4522 b'r',
4519 b'r',
4523 b'rev',
4520 b'rev',
4524 [],
4521 [],
4525 _(b'show the specified revision or revset'),
4522 _(b'show the specified revision or revset'),
4526 _(b'REV'),
4523 _(b'REV'),
4527 ),
4524 ),
4528 (
4525 (
4529 b'L',
4526 b'L',
4530 b'line-range',
4527 b'line-range',
4531 [],
4528 [],
4532 _(b'follow line range of specified file (EXPERIMENTAL)'),
4529 _(b'follow line range of specified file (EXPERIMENTAL)'),
4533 _(b'FILE,RANGE'),
4530 _(b'FILE,RANGE'),
4534 ),
4531 ),
4535 (
4532 (
4536 b'',
4533 b'',
4537 b'removed',
4534 b'removed',
4538 None,
4535 None,
4539 _(b'include revisions where files were removed'),
4536 _(b'include revisions where files were removed'),
4540 ),
4537 ),
4541 (
4538 (
4542 b'm',
4539 b'm',
4543 b'only-merges',
4540 b'only-merges',
4544 None,
4541 None,
4545 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4542 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4546 ),
4543 ),
4547 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4544 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4548 (
4545 (
4549 b'',
4546 b'',
4550 b'only-branch',
4547 b'only-branch',
4551 [],
4548 [],
4552 _(
4549 _(
4553 b'show only changesets within the given named branch (DEPRECATED)'
4550 b'show only changesets within the given named branch (DEPRECATED)'
4554 ),
4551 ),
4555 _(b'BRANCH'),
4552 _(b'BRANCH'),
4556 ),
4553 ),
4557 (
4554 (
4558 b'b',
4555 b'b',
4559 b'branch',
4556 b'branch',
4560 [],
4557 [],
4561 _(b'show changesets within the given named branch'),
4558 _(b'show changesets within the given named branch'),
4562 _(b'BRANCH'),
4559 _(b'BRANCH'),
4563 ),
4560 ),
4564 (
4561 (
4565 b'P',
4562 b'P',
4566 b'prune',
4563 b'prune',
4567 [],
4564 [],
4568 _(b'do not display revision or any of its ancestors'),
4565 _(b'do not display revision or any of its ancestors'),
4569 _(b'REV'),
4566 _(b'REV'),
4570 ),
4567 ),
4571 ]
4568 ]
4572 + logopts
4569 + logopts
4573 + walkopts,
4570 + walkopts,
4574 _(b'[OPTION]... [FILE]'),
4571 _(b'[OPTION]... [FILE]'),
4575 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4572 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4576 helpbasic=True,
4573 helpbasic=True,
4577 inferrepo=True,
4574 inferrepo=True,
4578 intents={INTENT_READONLY},
4575 intents={INTENT_READONLY},
4579 )
4576 )
4580 def log(ui, repo, *pats, **opts):
4577 def log(ui, repo, *pats, **opts):
4581 """show revision history of entire repository or files
4578 """show revision history of entire repository or files
4582
4579
4583 Print the revision history of the specified files or the entire
4580 Print the revision history of the specified files or the entire
4584 project.
4581 project.
4585
4582
4586 If no revision range is specified, the default is ``tip:0`` unless
4583 If no revision range is specified, the default is ``tip:0`` unless
4587 --follow is set, in which case the working directory parent is
4584 --follow is set, in which case the working directory parent is
4588 used as the starting revision.
4585 used as the starting revision.
4589
4586
4590 File history is shown without following rename or copy history of
4587 File history is shown without following rename or copy history of
4591 files. Use -f/--follow with a filename to follow history across
4588 files. Use -f/--follow with a filename to follow history across
4592 renames and copies. --follow without a filename will only show
4589 renames and copies. --follow without a filename will only show
4593 ancestors of the starting revision.
4590 ancestors of the starting revision.
4594
4591
4595 By default this command prints revision number and changeset id,
4592 By default this command prints revision number and changeset id,
4596 tags, non-trivial parents, user, date and time, and a summary for
4593 tags, non-trivial parents, user, date and time, and a summary for
4597 each commit. When the -v/--verbose switch is used, the list of
4594 each commit. When the -v/--verbose switch is used, the list of
4598 changed files and full commit message are shown.
4595 changed files and full commit message are shown.
4599
4596
4600 With --graph the revisions are shown as an ASCII art DAG with the most
4597 With --graph the revisions are shown as an ASCII art DAG with the most
4601 recent changeset at the top.
4598 recent changeset at the top.
4602 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4599 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4603 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4600 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4604 changeset from the lines below is a parent of the 'o' merge on the same
4601 changeset from the lines below is a parent of the 'o' merge on the same
4605 line.
4602 line.
4606 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4603 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4607 of a '|' indicates one or more revisions in a path are omitted.
4604 of a '|' indicates one or more revisions in a path are omitted.
4608
4605
4609 .. container:: verbose
4606 .. container:: verbose
4610
4607
4611 Use -L/--line-range FILE,M:N options to follow the history of lines
4608 Use -L/--line-range FILE,M:N options to follow the history of lines
4612 from M to N in FILE. With -p/--patch only diff hunks affecting
4609 from M to N in FILE. With -p/--patch only diff hunks affecting
4613 specified line range will be shown. This option requires --follow;
4610 specified line range will be shown. This option requires --follow;
4614 it can be specified multiple times. Currently, this option is not
4611 it can be specified multiple times. Currently, this option is not
4615 compatible with --graph. This option is experimental.
4612 compatible with --graph. This option is experimental.
4616
4613
4617 .. note::
4614 .. note::
4618
4615
4619 :hg:`log --patch` may generate unexpected diff output for merge
4616 :hg:`log --patch` may generate unexpected diff output for merge
4620 changesets, as it will only compare the merge changeset against
4617 changesets, as it will only compare the merge changeset against
4621 its first parent. Also, only files different from BOTH parents
4618 its first parent. Also, only files different from BOTH parents
4622 will appear in files:.
4619 will appear in files:.
4623
4620
4624 .. note::
4621 .. note::
4625
4622
4626 For performance reasons, :hg:`log FILE` may omit duplicate changes
4623 For performance reasons, :hg:`log FILE` may omit duplicate changes
4627 made on branches and will not show removals or mode changes. To
4624 made on branches and will not show removals or mode changes. To
4628 see all such changes, use the --removed switch.
4625 see all such changes, use the --removed switch.
4629
4626
4630 .. container:: verbose
4627 .. container:: verbose
4631
4628
4632 .. note::
4629 .. note::
4633
4630
4634 The history resulting from -L/--line-range options depends on diff
4631 The history resulting from -L/--line-range options depends on diff
4635 options; for instance if white-spaces are ignored, respective changes
4632 options; for instance if white-spaces are ignored, respective changes
4636 with only white-spaces in specified line range will not be listed.
4633 with only white-spaces in specified line range will not be listed.
4637
4634
4638 .. container:: verbose
4635 .. container:: verbose
4639
4636
4640 Some examples:
4637 Some examples:
4641
4638
4642 - changesets with full descriptions and file lists::
4639 - changesets with full descriptions and file lists::
4643
4640
4644 hg log -v
4641 hg log -v
4645
4642
4646 - changesets ancestral to the working directory::
4643 - changesets ancestral to the working directory::
4647
4644
4648 hg log -f
4645 hg log -f
4649
4646
4650 - last 10 commits on the current branch::
4647 - last 10 commits on the current branch::
4651
4648
4652 hg log -l 10 -b .
4649 hg log -l 10 -b .
4653
4650
4654 - changesets showing all modifications of a file, including removals::
4651 - changesets showing all modifications of a file, including removals::
4655
4652
4656 hg log --removed file.c
4653 hg log --removed file.c
4657
4654
4658 - all changesets that touch a directory, with diffs, excluding merges::
4655 - all changesets that touch a directory, with diffs, excluding merges::
4659
4656
4660 hg log -Mp lib/
4657 hg log -Mp lib/
4661
4658
4662 - all revision numbers that match a keyword::
4659 - all revision numbers that match a keyword::
4663
4660
4664 hg log -k bug --template "{rev}\\n"
4661 hg log -k bug --template "{rev}\\n"
4665
4662
4666 - the full hash identifier of the working directory parent::
4663 - the full hash identifier of the working directory parent::
4667
4664
4668 hg log -r . --template "{node}\\n"
4665 hg log -r . --template "{node}\\n"
4669
4666
4670 - list available log templates::
4667 - list available log templates::
4671
4668
4672 hg log -T list
4669 hg log -T list
4673
4670
4674 - check if a given changeset is included in a tagged release::
4671 - check if a given changeset is included in a tagged release::
4675
4672
4676 hg log -r "a21ccf and ancestor(1.9)"
4673 hg log -r "a21ccf and ancestor(1.9)"
4677
4674
4678 - find all changesets by some user in a date range::
4675 - find all changesets by some user in a date range::
4679
4676
4680 hg log -k alice -d "may 2008 to jul 2008"
4677 hg log -k alice -d "may 2008 to jul 2008"
4681
4678
4682 - summary of all changesets after the last tag::
4679 - summary of all changesets after the last tag::
4683
4680
4684 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4681 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4685
4682
4686 - changesets touching lines 13 to 23 for file.c::
4683 - changesets touching lines 13 to 23 for file.c::
4687
4684
4688 hg log -L file.c,13:23
4685 hg log -L file.c,13:23
4689
4686
4690 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4687 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4691 main.c with patch::
4688 main.c with patch::
4692
4689
4693 hg log -L file.c,13:23 -L main.c,2:6 -p
4690 hg log -L file.c,13:23 -L main.c,2:6 -p
4694
4691
4695 See :hg:`help dates` for a list of formats valid for -d/--date.
4692 See :hg:`help dates` for a list of formats valid for -d/--date.
4696
4693
4697 See :hg:`help revisions` for more about specifying and ordering
4694 See :hg:`help revisions` for more about specifying and ordering
4698 revisions.
4695 revisions.
4699
4696
4700 See :hg:`help templates` for more about pre-packaged styles and
4697 See :hg:`help templates` for more about pre-packaged styles and
4701 specifying custom templates. The default template used by the log
4698 specifying custom templates. The default template used by the log
4702 command can be customized via the ``ui.logtemplate`` configuration
4699 command can be customized via the ``ui.logtemplate`` configuration
4703 setting.
4700 setting.
4704
4701
4705 Returns 0 on success.
4702 Returns 0 on success.
4706
4703
4707 """
4704 """
4708 opts = pycompat.byteskwargs(opts)
4705 opts = pycompat.byteskwargs(opts)
4709 linerange = opts.get(b'line_range')
4706 linerange = opts.get(b'line_range')
4710
4707
4711 if linerange and not opts.get(b'follow'):
4708 if linerange and not opts.get(b'follow'):
4712 raise error.Abort(_(b'--line-range requires --follow'))
4709 raise error.Abort(_(b'--line-range requires --follow'))
4713
4710
4714 if linerange and pats:
4711 if linerange and pats:
4715 # TODO: take pats as patterns with no line-range filter
4712 # TODO: take pats as patterns with no line-range filter
4716 raise error.Abort(
4713 raise error.Abort(
4717 _(b'FILE arguments are not compatible with --line-range option')
4714 _(b'FILE arguments are not compatible with --line-range option')
4718 )
4715 )
4719
4716
4720 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4717 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4721 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4718 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4722 if linerange:
4719 if linerange:
4723 # TODO: should follow file history from logcmdutil._initialrevs(),
4720 # TODO: should follow file history from logcmdutil._initialrevs(),
4724 # then filter the result by logcmdutil._makerevset() and --limit
4721 # then filter the result by logcmdutil._makerevset() and --limit
4725 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4722 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4726
4723
4727 getcopies = None
4724 getcopies = None
4728 if opts.get(b'copies'):
4725 if opts.get(b'copies'):
4729 endrev = None
4726 endrev = None
4730 if revs:
4727 if revs:
4731 endrev = revs.max() + 1
4728 endrev = revs.max() + 1
4732 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4729 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4733
4730
4734 ui.pager(b'log')
4731 ui.pager(b'log')
4735 displayer = logcmdutil.changesetdisplayer(
4732 displayer = logcmdutil.changesetdisplayer(
4736 ui, repo, opts, differ, buffered=True
4733 ui, repo, opts, differ, buffered=True
4737 )
4734 )
4738 if opts.get(b'graph'):
4735 if opts.get(b'graph'):
4739 displayfn = logcmdutil.displaygraphrevs
4736 displayfn = logcmdutil.displaygraphrevs
4740 else:
4737 else:
4741 displayfn = logcmdutil.displayrevs
4738 displayfn = logcmdutil.displayrevs
4742 displayfn(ui, repo, revs, displayer, getcopies)
4739 displayfn(ui, repo, revs, displayer, getcopies)
4743
4740
4744
4741
4745 @command(
4742 @command(
4746 b'manifest',
4743 b'manifest',
4747 [
4744 [
4748 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4745 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4749 (b'', b'all', False, _(b"list files from all revisions")),
4746 (b'', b'all', False, _(b"list files from all revisions")),
4750 ]
4747 ]
4751 + formatteropts,
4748 + formatteropts,
4752 _(b'[-r REV]'),
4749 _(b'[-r REV]'),
4753 helpcategory=command.CATEGORY_MAINTENANCE,
4750 helpcategory=command.CATEGORY_MAINTENANCE,
4754 intents={INTENT_READONLY},
4751 intents={INTENT_READONLY},
4755 )
4752 )
4756 def manifest(ui, repo, node=None, rev=None, **opts):
4753 def manifest(ui, repo, node=None, rev=None, **opts):
4757 """output the current or given revision of the project manifest
4754 """output the current or given revision of the project manifest
4758
4755
4759 Print a list of version controlled files for the given revision.
4756 Print a list of version controlled files for the given revision.
4760 If no revision is given, the first parent of the working directory
4757 If no revision is given, the first parent of the working directory
4761 is used, or the null revision if no revision is checked out.
4758 is used, or the null revision if no revision is checked out.
4762
4759
4763 With -v, print file permissions, symlink and executable bits.
4760 With -v, print file permissions, symlink and executable bits.
4764 With --debug, print file revision hashes.
4761 With --debug, print file revision hashes.
4765
4762
4766 If option --all is specified, the list of all files from all revisions
4763 If option --all is specified, the list of all files from all revisions
4767 is printed. This includes deleted and renamed files.
4764 is printed. This includes deleted and renamed files.
4768
4765
4769 Returns 0 on success.
4766 Returns 0 on success.
4770 """
4767 """
4771 opts = pycompat.byteskwargs(opts)
4768 opts = pycompat.byteskwargs(opts)
4772 fm = ui.formatter(b'manifest', opts)
4769 fm = ui.formatter(b'manifest', opts)
4773
4770
4774 if opts.get(b'all'):
4771 if opts.get(b'all'):
4775 if rev or node:
4772 if rev or node:
4776 raise error.Abort(_(b"can't specify a revision with --all"))
4773 raise error.Abort(_(b"can't specify a revision with --all"))
4777
4774
4778 res = set()
4775 res = set()
4779 for rev in repo:
4776 for rev in repo:
4780 ctx = repo[rev]
4777 ctx = repo[rev]
4781 res |= set(ctx.files())
4778 res |= set(ctx.files())
4782
4779
4783 ui.pager(b'manifest')
4780 ui.pager(b'manifest')
4784 for f in sorted(res):
4781 for f in sorted(res):
4785 fm.startitem()
4782 fm.startitem()
4786 fm.write(b"path", b'%s\n', f)
4783 fm.write(b"path", b'%s\n', f)
4787 fm.end()
4784 fm.end()
4788 return
4785 return
4789
4786
4790 if rev and node:
4787 if rev and node:
4791 raise error.Abort(_(b"please specify just one revision"))
4788 raise error.Abort(_(b"please specify just one revision"))
4792
4789
4793 if not node:
4790 if not node:
4794 node = rev
4791 node = rev
4795
4792
4796 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4793 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4797 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4794 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4798 if node:
4795 if node:
4799 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4796 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4800 ctx = scmutil.revsingle(repo, node)
4797 ctx = scmutil.revsingle(repo, node)
4801 mf = ctx.manifest()
4798 mf = ctx.manifest()
4802 ui.pager(b'manifest')
4799 ui.pager(b'manifest')
4803 for f in ctx:
4800 for f in ctx:
4804 fm.startitem()
4801 fm.startitem()
4805 fm.context(ctx=ctx)
4802 fm.context(ctx=ctx)
4806 fl = ctx[f].flags()
4803 fl = ctx[f].flags()
4807 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4804 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4808 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4805 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4809 fm.write(b'path', b'%s\n', f)
4806 fm.write(b'path', b'%s\n', f)
4810 fm.end()
4807 fm.end()
4811
4808
4812
4809
4813 @command(
4810 @command(
4814 b'merge',
4811 b'merge',
4815 [
4812 [
4816 (
4813 (
4817 b'f',
4814 b'f',
4818 b'force',
4815 b'force',
4819 None,
4816 None,
4820 _(b'force a merge including outstanding changes (DEPRECATED)'),
4817 _(b'force a merge including outstanding changes (DEPRECATED)'),
4821 ),
4818 ),
4822 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4819 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4823 (
4820 (
4824 b'P',
4821 b'P',
4825 b'preview',
4822 b'preview',
4826 None,
4823 None,
4827 _(b'review revisions to merge (no merge is performed)'),
4824 _(b'review revisions to merge (no merge is performed)'),
4828 ),
4825 ),
4829 (b'', b'abort', None, _(b'abort the ongoing merge')),
4826 (b'', b'abort', None, _(b'abort the ongoing merge')),
4830 ]
4827 ]
4831 + mergetoolopts,
4828 + mergetoolopts,
4832 _(b'[-P] [[-r] REV]'),
4829 _(b'[-P] [[-r] REV]'),
4833 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4830 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4834 helpbasic=True,
4831 helpbasic=True,
4835 )
4832 )
4836 def merge(ui, repo, node=None, **opts):
4833 def merge(ui, repo, node=None, **opts):
4837 """merge another revision into working directory
4834 """merge another revision into working directory
4838
4835
4839 The current working directory is updated with all changes made in
4836 The current working directory is updated with all changes made in
4840 the requested revision since the last common predecessor revision.
4837 the requested revision since the last common predecessor revision.
4841
4838
4842 Files that changed between either parent are marked as changed for
4839 Files that changed between either parent are marked as changed for
4843 the next commit and a commit must be performed before any further
4840 the next commit and a commit must be performed before any further
4844 updates to the repository are allowed. The next commit will have
4841 updates to the repository are allowed. The next commit will have
4845 two parents.
4842 two parents.
4846
4843
4847 ``--tool`` can be used to specify the merge tool used for file
4844 ``--tool`` can be used to specify the merge tool used for file
4848 merges. It overrides the HGMERGE environment variable and your
4845 merges. It overrides the HGMERGE environment variable and your
4849 configuration files. See :hg:`help merge-tools` for options.
4846 configuration files. See :hg:`help merge-tools` for options.
4850
4847
4851 If no revision is specified, the working directory's parent is a
4848 If no revision is specified, the working directory's parent is a
4852 head revision, and the current branch contains exactly one other
4849 head revision, and the current branch contains exactly one other
4853 head, the other head is merged with by default. Otherwise, an
4850 head, the other head is merged with by default. Otherwise, an
4854 explicit revision with which to merge must be provided.
4851 explicit revision with which to merge must be provided.
4855
4852
4856 See :hg:`help resolve` for information on handling file conflicts.
4853 See :hg:`help resolve` for information on handling file conflicts.
4857
4854
4858 To undo an uncommitted merge, use :hg:`merge --abort` which
4855 To undo an uncommitted merge, use :hg:`merge --abort` which
4859 will check out a clean copy of the original merge parent, losing
4856 will check out a clean copy of the original merge parent, losing
4860 all changes.
4857 all changes.
4861
4858
4862 Returns 0 on success, 1 if there are unresolved files.
4859 Returns 0 on success, 1 if there are unresolved files.
4863 """
4860 """
4864
4861
4865 opts = pycompat.byteskwargs(opts)
4862 opts = pycompat.byteskwargs(opts)
4866 abort = opts.get(b'abort')
4863 abort = opts.get(b'abort')
4867 if abort and repo.dirstate.p2() == nullid:
4864 if abort and repo.dirstate.p2() == nullid:
4868 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4865 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4869 if abort:
4866 if abort:
4870 state = cmdutil.getunfinishedstate(repo)
4867 state = cmdutil.getunfinishedstate(repo)
4871 if state and state._opname != b'merge':
4868 if state and state._opname != b'merge':
4872 raise error.Abort(
4869 raise error.Abort(
4873 _(b'cannot abort merge with %s in progress') % (state._opname),
4870 _(b'cannot abort merge with %s in progress') % (state._opname),
4874 hint=state.hint(),
4871 hint=state.hint(),
4875 )
4872 )
4876 if node:
4873 if node:
4877 raise error.Abort(_(b"cannot specify a node with --abort"))
4874 raise error.Abort(_(b"cannot specify a node with --abort"))
4878 if opts.get(b'rev'):
4875 if opts.get(b'rev'):
4879 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4876 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4880 if opts.get(b'preview'):
4877 if opts.get(b'preview'):
4881 raise error.Abort(_(b"cannot specify --preview with --abort"))
4878 raise error.Abort(_(b"cannot specify --preview with --abort"))
4882 if opts.get(b'rev') and node:
4879 if opts.get(b'rev') and node:
4883 raise error.Abort(_(b"please specify just one revision"))
4880 raise error.Abort(_(b"please specify just one revision"))
4884 if not node:
4881 if not node:
4885 node = opts.get(b'rev')
4882 node = opts.get(b'rev')
4886
4883
4887 if node:
4884 if node:
4888 node = scmutil.revsingle(repo, node).node()
4885 node = scmutil.revsingle(repo, node).node()
4889
4886
4890 if not node and not abort:
4887 if not node and not abort:
4891 node = repo[destutil.destmerge(repo)].node()
4888 node = repo[destutil.destmerge(repo)].node()
4892
4889
4893 if opts.get(b'preview'):
4890 if opts.get(b'preview'):
4894 # find nodes that are ancestors of p2 but not of p1
4891 # find nodes that are ancestors of p2 but not of p1
4895 p1 = repo.lookup(b'.')
4892 p1 = repo.lookup(b'.')
4896 p2 = node
4893 p2 = node
4897 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4894 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4898
4895
4899 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4896 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4900 for node in nodes:
4897 for node in nodes:
4901 displayer.show(repo[node])
4898 displayer.show(repo[node])
4902 displayer.close()
4899 displayer.close()
4903 return 0
4900 return 0
4904
4901
4905 # ui.forcemerge is an internal variable, do not document
4902 # ui.forcemerge is an internal variable, do not document
4906 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4903 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4907 with ui.configoverride(overrides, b'merge'):
4904 with ui.configoverride(overrides, b'merge'):
4908 force = opts.get(b'force')
4905 force = opts.get(b'force')
4909 labels = [b'working copy', b'merge rev']
4906 labels = [b'working copy', b'merge rev']
4910 return hg.merge(
4907 return hg.merge(
4911 repo,
4908 repo,
4912 node,
4909 node,
4913 force=force,
4910 force=force,
4914 mergeforce=force,
4911 mergeforce=force,
4915 labels=labels,
4912 labels=labels,
4916 abort=abort,
4913 abort=abort,
4917 )
4914 )
4918
4915
4919
4916
4920 statemod.addunfinished(
4917 statemod.addunfinished(
4921 b'merge',
4918 b'merge',
4922 fname=None,
4919 fname=None,
4923 clearable=True,
4920 clearable=True,
4924 allowcommit=True,
4921 allowcommit=True,
4925 cmdmsg=_(b'outstanding uncommitted merge'),
4922 cmdmsg=_(b'outstanding uncommitted merge'),
4926 abortfunc=hg.abortmerge,
4923 abortfunc=hg.abortmerge,
4927 statushint=_(
4924 statushint=_(
4928 b'To continue: hg commit\nTo abort: hg merge --abort'
4925 b'To continue: hg commit\nTo abort: hg merge --abort'
4929 ),
4926 ),
4930 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4927 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4931 )
4928 )
4932
4929
4933
4930
4934 @command(
4931 @command(
4935 b'outgoing|out',
4932 b'outgoing|out',
4936 [
4933 [
4937 (
4934 (
4938 b'f',
4935 b'f',
4939 b'force',
4936 b'force',
4940 None,
4937 None,
4941 _(b'run even when the destination is unrelated'),
4938 _(b'run even when the destination is unrelated'),
4942 ),
4939 ),
4943 (
4940 (
4944 b'r',
4941 b'r',
4945 b'rev',
4942 b'rev',
4946 [],
4943 [],
4947 _(b'a changeset intended to be included in the destination'),
4944 _(b'a changeset intended to be included in the destination'),
4948 _(b'REV'),
4945 _(b'REV'),
4949 ),
4946 ),
4950 (b'n', b'newest-first', None, _(b'show newest record first')),
4947 (b'n', b'newest-first', None, _(b'show newest record first')),
4951 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4948 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4952 (
4949 (
4953 b'b',
4950 b'b',
4954 b'branch',
4951 b'branch',
4955 [],
4952 [],
4956 _(b'a specific branch you would like to push'),
4953 _(b'a specific branch you would like to push'),
4957 _(b'BRANCH'),
4954 _(b'BRANCH'),
4958 ),
4955 ),
4959 ]
4956 ]
4960 + logopts
4957 + logopts
4961 + remoteopts
4958 + remoteopts
4962 + subrepoopts,
4959 + subrepoopts,
4963 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4960 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4964 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4961 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4965 )
4962 )
4966 def outgoing(ui, repo, dest=None, **opts):
4963 def outgoing(ui, repo, dest=None, **opts):
4967 """show changesets not found in the destination
4964 """show changesets not found in the destination
4968
4965
4969 Show changesets not found in the specified destination repository
4966 Show changesets not found in the specified destination repository
4970 or the default push location. These are the changesets that would
4967 or the default push location. These are the changesets that would
4971 be pushed if a push was requested.
4968 be pushed if a push was requested.
4972
4969
4973 See pull for details of valid destination formats.
4970 See pull for details of valid destination formats.
4974
4971
4975 .. container:: verbose
4972 .. container:: verbose
4976
4973
4977 With -B/--bookmarks, the result of bookmark comparison between
4974 With -B/--bookmarks, the result of bookmark comparison between
4978 local and remote repositories is displayed. With -v/--verbose,
4975 local and remote repositories is displayed. With -v/--verbose,
4979 status is also displayed for each bookmark like below::
4976 status is also displayed for each bookmark like below::
4980
4977
4981 BM1 01234567890a added
4978 BM1 01234567890a added
4982 BM2 deleted
4979 BM2 deleted
4983 BM3 234567890abc advanced
4980 BM3 234567890abc advanced
4984 BM4 34567890abcd diverged
4981 BM4 34567890abcd diverged
4985 BM5 4567890abcde changed
4982 BM5 4567890abcde changed
4986
4983
4987 The action taken when pushing depends on the
4984 The action taken when pushing depends on the
4988 status of each bookmark:
4985 status of each bookmark:
4989
4986
4990 :``added``: push with ``-B`` will create it
4987 :``added``: push with ``-B`` will create it
4991 :``deleted``: push with ``-B`` will delete it
4988 :``deleted``: push with ``-B`` will delete it
4992 :``advanced``: push will update it
4989 :``advanced``: push will update it
4993 :``diverged``: push with ``-B`` will update it
4990 :``diverged``: push with ``-B`` will update it
4994 :``changed``: push with ``-B`` will update it
4991 :``changed``: push with ``-B`` will update it
4995
4992
4996 From the point of view of pushing behavior, bookmarks
4993 From the point of view of pushing behavior, bookmarks
4997 existing only in the remote repository are treated as
4994 existing only in the remote repository are treated as
4998 ``deleted``, even if it is in fact added remotely.
4995 ``deleted``, even if it is in fact added remotely.
4999
4996
5000 Returns 0 if there are outgoing changes, 1 otherwise.
4997 Returns 0 if there are outgoing changes, 1 otherwise.
5001 """
4998 """
5002 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4999 # hg._outgoing() needs to re-resolve the path in order to handle #branch
5003 # style URLs, so don't overwrite dest.
5000 # style URLs, so don't overwrite dest.
5004 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5001 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5005 if not path:
5002 if not path:
5006 raise error.Abort(
5003 raise error.Abort(
5007 _(b'default repository not configured!'),
5004 _(b'default repository not configured!'),
5008 hint=_(b"see 'hg help config.paths'"),
5005 hint=_(b"see 'hg help config.paths'"),
5009 )
5006 )
5010
5007
5011 opts = pycompat.byteskwargs(opts)
5008 opts = pycompat.byteskwargs(opts)
5012 if opts.get(b'graph'):
5009 if opts.get(b'graph'):
5013 logcmdutil.checkunsupportedgraphflags([], opts)
5010 logcmdutil.checkunsupportedgraphflags([], opts)
5014 o, other = hg._outgoing(ui, repo, dest, opts)
5011 o, other = hg._outgoing(ui, repo, dest, opts)
5015 if not o:
5012 if not o:
5016 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5013 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5017 return
5014 return
5018
5015
5019 revdag = logcmdutil.graphrevs(repo, o, opts)
5016 revdag = logcmdutil.graphrevs(repo, o, opts)
5020 ui.pager(b'outgoing')
5017 ui.pager(b'outgoing')
5021 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5018 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5022 logcmdutil.displaygraph(
5019 logcmdutil.displaygraph(
5023 ui, repo, revdag, displayer, graphmod.asciiedges
5020 ui, repo, revdag, displayer, graphmod.asciiedges
5024 )
5021 )
5025 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5022 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5026 return 0
5023 return 0
5027
5024
5028 if opts.get(b'bookmarks'):
5025 if opts.get(b'bookmarks'):
5029 dest = path.pushloc or path.loc
5026 dest = path.pushloc or path.loc
5030 other = hg.peer(repo, opts, dest)
5027 other = hg.peer(repo, opts, dest)
5031 if b'bookmarks' not in other.listkeys(b'namespaces'):
5028 if b'bookmarks' not in other.listkeys(b'namespaces'):
5032 ui.warn(_(b"remote doesn't support bookmarks\n"))
5029 ui.warn(_(b"remote doesn't support bookmarks\n"))
5033 return 0
5030 return 0
5034 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5031 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5035 ui.pager(b'outgoing')
5032 ui.pager(b'outgoing')
5036 return bookmarks.outgoing(ui, repo, other)
5033 return bookmarks.outgoing(ui, repo, other)
5037
5034
5038 repo._subtoppath = path.pushloc or path.loc
5035 repo._subtoppath = path.pushloc or path.loc
5039 try:
5036 try:
5040 return hg.outgoing(ui, repo, dest, opts)
5037 return hg.outgoing(ui, repo, dest, opts)
5041 finally:
5038 finally:
5042 del repo._subtoppath
5039 del repo._subtoppath
5043
5040
5044
5041
5045 @command(
5042 @command(
5046 b'parents',
5043 b'parents',
5047 [
5044 [
5048 (
5045 (
5049 b'r',
5046 b'r',
5050 b'rev',
5047 b'rev',
5051 b'',
5048 b'',
5052 _(b'show parents of the specified revision'),
5049 _(b'show parents of the specified revision'),
5053 _(b'REV'),
5050 _(b'REV'),
5054 ),
5051 ),
5055 ]
5052 ]
5056 + templateopts,
5053 + templateopts,
5057 _(b'[-r REV] [FILE]'),
5054 _(b'[-r REV] [FILE]'),
5058 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5055 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5059 inferrepo=True,
5056 inferrepo=True,
5060 )
5057 )
5061 def parents(ui, repo, file_=None, **opts):
5058 def parents(ui, repo, file_=None, **opts):
5062 """show the parents of the working directory or revision (DEPRECATED)
5059 """show the parents of the working directory or revision (DEPRECATED)
5063
5060
5064 Print the working directory's parent revisions. If a revision is
5061 Print the working directory's parent revisions. If a revision is
5065 given via -r/--rev, the parent of that revision will be printed.
5062 given via -r/--rev, the parent of that revision will be printed.
5066 If a file argument is given, the revision in which the file was
5063 If a file argument is given, the revision in which the file was
5067 last changed (before the working directory revision or the
5064 last changed (before the working directory revision or the
5068 argument to --rev if given) is printed.
5065 argument to --rev if given) is printed.
5069
5066
5070 This command is equivalent to::
5067 This command is equivalent to::
5071
5068
5072 hg log -r "p1()+p2()" or
5069 hg log -r "p1()+p2()" or
5073 hg log -r "p1(REV)+p2(REV)" or
5070 hg log -r "p1(REV)+p2(REV)" or
5074 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5071 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5075 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5072 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5076
5073
5077 See :hg:`summary` and :hg:`help revsets` for related information.
5074 See :hg:`summary` and :hg:`help revsets` for related information.
5078
5075
5079 Returns 0 on success.
5076 Returns 0 on success.
5080 """
5077 """
5081
5078
5082 opts = pycompat.byteskwargs(opts)
5079 opts = pycompat.byteskwargs(opts)
5083 rev = opts.get(b'rev')
5080 rev = opts.get(b'rev')
5084 if rev:
5081 if rev:
5085 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5082 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5086 ctx = scmutil.revsingle(repo, rev, None)
5083 ctx = scmutil.revsingle(repo, rev, None)
5087
5084
5088 if file_:
5085 if file_:
5089 m = scmutil.match(ctx, (file_,), opts)
5086 m = scmutil.match(ctx, (file_,), opts)
5090 if m.anypats() or len(m.files()) != 1:
5087 if m.anypats() or len(m.files()) != 1:
5091 raise error.Abort(_(b'can only specify an explicit filename'))
5088 raise error.Abort(_(b'can only specify an explicit filename'))
5092 file_ = m.files()[0]
5089 file_ = m.files()[0]
5093 filenodes = []
5090 filenodes = []
5094 for cp in ctx.parents():
5091 for cp in ctx.parents():
5095 if not cp:
5092 if not cp:
5096 continue
5093 continue
5097 try:
5094 try:
5098 filenodes.append(cp.filenode(file_))
5095 filenodes.append(cp.filenode(file_))
5099 except error.LookupError:
5096 except error.LookupError:
5100 pass
5097 pass
5101 if not filenodes:
5098 if not filenodes:
5102 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5099 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5103 p = []
5100 p = []
5104 for fn in filenodes:
5101 for fn in filenodes:
5105 fctx = repo.filectx(file_, fileid=fn)
5102 fctx = repo.filectx(file_, fileid=fn)
5106 p.append(fctx.node())
5103 p.append(fctx.node())
5107 else:
5104 else:
5108 p = [cp.node() for cp in ctx.parents()]
5105 p = [cp.node() for cp in ctx.parents()]
5109
5106
5110 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5107 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5111 for n in p:
5108 for n in p:
5112 if n != nullid:
5109 if n != nullid:
5113 displayer.show(repo[n])
5110 displayer.show(repo[n])
5114 displayer.close()
5111 displayer.close()
5115
5112
5116
5113
5117 @command(
5114 @command(
5118 b'paths',
5115 b'paths',
5119 formatteropts,
5116 formatteropts,
5120 _(b'[NAME]'),
5117 _(b'[NAME]'),
5121 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5118 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5122 optionalrepo=True,
5119 optionalrepo=True,
5123 intents={INTENT_READONLY},
5120 intents={INTENT_READONLY},
5124 )
5121 )
5125 def paths(ui, repo, search=None, **opts):
5122 def paths(ui, repo, search=None, **opts):
5126 """show aliases for remote repositories
5123 """show aliases for remote repositories
5127
5124
5128 Show definition of symbolic path name NAME. If no name is given,
5125 Show definition of symbolic path name NAME. If no name is given,
5129 show definition of all available names.
5126 show definition of all available names.
5130
5127
5131 Option -q/--quiet suppresses all output when searching for NAME
5128 Option -q/--quiet suppresses all output when searching for NAME
5132 and shows only the path names when listing all definitions.
5129 and shows only the path names when listing all definitions.
5133
5130
5134 Path names are defined in the [paths] section of your
5131 Path names are defined in the [paths] section of your
5135 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5132 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5136 repository, ``.hg/hgrc`` is used, too.
5133 repository, ``.hg/hgrc`` is used, too.
5137
5134
5138 The path names ``default`` and ``default-push`` have a special
5135 The path names ``default`` and ``default-push`` have a special
5139 meaning. When performing a push or pull operation, they are used
5136 meaning. When performing a push or pull operation, they are used
5140 as fallbacks if no location is specified on the command-line.
5137 as fallbacks if no location is specified on the command-line.
5141 When ``default-push`` is set, it will be used for push and
5138 When ``default-push`` is set, it will be used for push and
5142 ``default`` will be used for pull; otherwise ``default`` is used
5139 ``default`` will be used for pull; otherwise ``default`` is used
5143 as the fallback for both. When cloning a repository, the clone
5140 as the fallback for both. When cloning a repository, the clone
5144 source is written as ``default`` in ``.hg/hgrc``.
5141 source is written as ``default`` in ``.hg/hgrc``.
5145
5142
5146 .. note::
5143 .. note::
5147
5144
5148 ``default`` and ``default-push`` apply to all inbound (e.g.
5145 ``default`` and ``default-push`` apply to all inbound (e.g.
5149 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5146 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5150 and :hg:`bundle`) operations.
5147 and :hg:`bundle`) operations.
5151
5148
5152 See :hg:`help urls` for more information.
5149 See :hg:`help urls` for more information.
5153
5150
5154 .. container:: verbose
5151 .. container:: verbose
5155
5152
5156 Template:
5153 Template:
5157
5154
5158 The following keywords are supported. See also :hg:`help templates`.
5155 The following keywords are supported. See also :hg:`help templates`.
5159
5156
5160 :name: String. Symbolic name of the path alias.
5157 :name: String. Symbolic name of the path alias.
5161 :pushurl: String. URL for push operations.
5158 :pushurl: String. URL for push operations.
5162 :url: String. URL or directory path for the other operations.
5159 :url: String. URL or directory path for the other operations.
5163
5160
5164 Returns 0 on success.
5161 Returns 0 on success.
5165 """
5162 """
5166
5163
5167 opts = pycompat.byteskwargs(opts)
5164 opts = pycompat.byteskwargs(opts)
5168 ui.pager(b'paths')
5165 ui.pager(b'paths')
5169 if search:
5166 if search:
5170 pathitems = [
5167 pathitems = [
5171 (name, path)
5168 (name, path)
5172 for name, path in pycompat.iteritems(ui.paths)
5169 for name, path in pycompat.iteritems(ui.paths)
5173 if name == search
5170 if name == search
5174 ]
5171 ]
5175 else:
5172 else:
5176 pathitems = sorted(pycompat.iteritems(ui.paths))
5173 pathitems = sorted(pycompat.iteritems(ui.paths))
5177
5174
5178 fm = ui.formatter(b'paths', opts)
5175 fm = ui.formatter(b'paths', opts)
5179 if fm.isplain():
5176 if fm.isplain():
5180 hidepassword = util.hidepassword
5177 hidepassword = util.hidepassword
5181 else:
5178 else:
5182 hidepassword = bytes
5179 hidepassword = bytes
5183 if ui.quiet:
5180 if ui.quiet:
5184 namefmt = b'%s\n'
5181 namefmt = b'%s\n'
5185 else:
5182 else:
5186 namefmt = b'%s = '
5183 namefmt = b'%s = '
5187 showsubopts = not search and not ui.quiet
5184 showsubopts = not search and not ui.quiet
5188
5185
5189 for name, path in pathitems:
5186 for name, path in pathitems:
5190 fm.startitem()
5187 fm.startitem()
5191 fm.condwrite(not search, b'name', namefmt, name)
5188 fm.condwrite(not search, b'name', namefmt, name)
5192 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5189 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5193 for subopt, value in sorted(path.suboptions.items()):
5190 for subopt, value in sorted(path.suboptions.items()):
5194 assert subopt not in (b'name', b'url')
5191 assert subopt not in (b'name', b'url')
5195 if showsubopts:
5192 if showsubopts:
5196 fm.plain(b'%s:%s = ' % (name, subopt))
5193 fm.plain(b'%s:%s = ' % (name, subopt))
5197 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5194 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5198
5195
5199 fm.end()
5196 fm.end()
5200
5197
5201 if search and not pathitems:
5198 if search and not pathitems:
5202 if not ui.quiet:
5199 if not ui.quiet:
5203 ui.warn(_(b"not found!\n"))
5200 ui.warn(_(b"not found!\n"))
5204 return 1
5201 return 1
5205 else:
5202 else:
5206 return 0
5203 return 0
5207
5204
5208
5205
5209 @command(
5206 @command(
5210 b'phase',
5207 b'phase',
5211 [
5208 [
5212 (b'p', b'public', False, _(b'set changeset phase to public')),
5209 (b'p', b'public', False, _(b'set changeset phase to public')),
5213 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5210 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5214 (b's', b'secret', False, _(b'set changeset phase to secret')),
5211 (b's', b'secret', False, _(b'set changeset phase to secret')),
5215 (b'f', b'force', False, _(b'allow to move boundary backward')),
5212 (b'f', b'force', False, _(b'allow to move boundary backward')),
5216 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5213 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5217 ],
5214 ],
5218 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5215 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5219 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5216 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5220 )
5217 )
5221 def phase(ui, repo, *revs, **opts):
5218 def phase(ui, repo, *revs, **opts):
5222 """set or show the current phase name
5219 """set or show the current phase name
5223
5220
5224 With no argument, show the phase name of the current revision(s).
5221 With no argument, show the phase name of the current revision(s).
5225
5222
5226 With one of -p/--public, -d/--draft or -s/--secret, change the
5223 With one of -p/--public, -d/--draft or -s/--secret, change the
5227 phase value of the specified revisions.
5224 phase value of the specified revisions.
5228
5225
5229 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5226 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5230 lower phase to a higher phase. Phases are ordered as follows::
5227 lower phase to a higher phase. Phases are ordered as follows::
5231
5228
5232 public < draft < secret
5229 public < draft < secret
5233
5230
5234 Returns 0 on success, 1 if some phases could not be changed.
5231 Returns 0 on success, 1 if some phases could not be changed.
5235
5232
5236 (For more information about the phases concept, see :hg:`help phases`.)
5233 (For more information about the phases concept, see :hg:`help phases`.)
5237 """
5234 """
5238 opts = pycompat.byteskwargs(opts)
5235 opts = pycompat.byteskwargs(opts)
5239 # search for a unique phase argument
5236 # search for a unique phase argument
5240 targetphase = None
5237 targetphase = None
5241 for idx, name in enumerate(phases.cmdphasenames):
5238 for idx, name in enumerate(phases.cmdphasenames):
5242 if opts[name]:
5239 if opts[name]:
5243 if targetphase is not None:
5240 if targetphase is not None:
5244 raise error.Abort(_(b'only one phase can be specified'))
5241 raise error.Abort(_(b'only one phase can be specified'))
5245 targetphase = idx
5242 targetphase = idx
5246
5243
5247 # look for specified revision
5244 # look for specified revision
5248 revs = list(revs)
5245 revs = list(revs)
5249 revs.extend(opts[b'rev'])
5246 revs.extend(opts[b'rev'])
5250 if not revs:
5247 if not revs:
5251 # display both parents as the second parent phase can influence
5248 # display both parents as the second parent phase can influence
5252 # the phase of a merge commit
5249 # the phase of a merge commit
5253 revs = [c.rev() for c in repo[None].parents()]
5250 revs = [c.rev() for c in repo[None].parents()]
5254
5251
5255 revs = scmutil.revrange(repo, revs)
5252 revs = scmutil.revrange(repo, revs)
5256
5253
5257 ret = 0
5254 ret = 0
5258 if targetphase is None:
5255 if targetphase is None:
5259 # display
5256 # display
5260 for r in revs:
5257 for r in revs:
5261 ctx = repo[r]
5258 ctx = repo[r]
5262 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5259 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5263 else:
5260 else:
5264 with repo.lock(), repo.transaction(b"phase") as tr:
5261 with repo.lock(), repo.transaction(b"phase") as tr:
5265 # set phase
5262 # set phase
5266 if not revs:
5263 if not revs:
5267 raise error.Abort(_(b'empty revision set'))
5264 raise error.Abort(_(b'empty revision set'))
5268 nodes = [repo[r].node() for r in revs]
5265 nodes = [repo[r].node() for r in revs]
5269 # moving revision from public to draft may hide them
5266 # moving revision from public to draft may hide them
5270 # We have to check result on an unfiltered repository
5267 # We have to check result on an unfiltered repository
5271 unfi = repo.unfiltered()
5268 unfi = repo.unfiltered()
5272 getphase = unfi._phasecache.phase
5269 getphase = unfi._phasecache.phase
5273 olddata = [getphase(unfi, r) for r in unfi]
5270 olddata = [getphase(unfi, r) for r in unfi]
5274 phases.advanceboundary(repo, tr, targetphase, nodes)
5271 phases.advanceboundary(repo, tr, targetphase, nodes)
5275 if opts[b'force']:
5272 if opts[b'force']:
5276 phases.retractboundary(repo, tr, targetphase, nodes)
5273 phases.retractboundary(repo, tr, targetphase, nodes)
5277 getphase = unfi._phasecache.phase
5274 getphase = unfi._phasecache.phase
5278 newdata = [getphase(unfi, r) for r in unfi]
5275 newdata = [getphase(unfi, r) for r in unfi]
5279 changes = sum(newdata[r] != olddata[r] for r in unfi)
5276 changes = sum(newdata[r] != olddata[r] for r in unfi)
5280 cl = unfi.changelog
5277 cl = unfi.changelog
5281 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5278 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5282 if rejected:
5279 if rejected:
5283 ui.warn(
5280 ui.warn(
5284 _(
5281 _(
5285 b'cannot move %i changesets to a higher '
5282 b'cannot move %i changesets to a higher '
5286 b'phase, use --force\n'
5283 b'phase, use --force\n'
5287 )
5284 )
5288 % len(rejected)
5285 % len(rejected)
5289 )
5286 )
5290 ret = 1
5287 ret = 1
5291 if changes:
5288 if changes:
5292 msg = _(b'phase changed for %i changesets\n') % changes
5289 msg = _(b'phase changed for %i changesets\n') % changes
5293 if ret:
5290 if ret:
5294 ui.status(msg)
5291 ui.status(msg)
5295 else:
5292 else:
5296 ui.note(msg)
5293 ui.note(msg)
5297 else:
5294 else:
5298 ui.warn(_(b'no phases changed\n'))
5295 ui.warn(_(b'no phases changed\n'))
5299 return ret
5296 return ret
5300
5297
5301
5298
5302 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5299 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5303 """Run after a changegroup has been added via pull/unbundle
5300 """Run after a changegroup has been added via pull/unbundle
5304
5301
5305 This takes arguments below:
5302 This takes arguments below:
5306
5303
5307 :modheads: change of heads by pull/unbundle
5304 :modheads: change of heads by pull/unbundle
5308 :optupdate: updating working directory is needed or not
5305 :optupdate: updating working directory is needed or not
5309 :checkout: update destination revision (or None to default destination)
5306 :checkout: update destination revision (or None to default destination)
5310 :brev: a name, which might be a bookmark to be activated after updating
5307 :brev: a name, which might be a bookmark to be activated after updating
5311 """
5308 """
5312 if modheads == 0:
5309 if modheads == 0:
5313 return
5310 return
5314 if optupdate:
5311 if optupdate:
5315 try:
5312 try:
5316 return hg.updatetotally(ui, repo, checkout, brev)
5313 return hg.updatetotally(ui, repo, checkout, brev)
5317 except error.UpdateAbort as inst:
5314 except error.UpdateAbort as inst:
5318 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5315 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5319 hint = inst.hint
5316 hint = inst.hint
5320 raise error.UpdateAbort(msg, hint=hint)
5317 raise error.UpdateAbort(msg, hint=hint)
5321 if modheads is not None and modheads > 1:
5318 if modheads is not None and modheads > 1:
5322 currentbranchheads = len(repo.branchheads())
5319 currentbranchheads = len(repo.branchheads())
5323 if currentbranchheads == modheads:
5320 if currentbranchheads == modheads:
5324 ui.status(
5321 ui.status(
5325 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5322 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5326 )
5323 )
5327 elif currentbranchheads > 1:
5324 elif currentbranchheads > 1:
5328 ui.status(
5325 ui.status(
5329 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5326 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5330 )
5327 )
5331 else:
5328 else:
5332 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5329 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5333 elif not ui.configbool(b'commands', b'update.requiredest'):
5330 elif not ui.configbool(b'commands', b'update.requiredest'):
5334 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5331 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5335
5332
5336
5333
5337 @command(
5334 @command(
5338 b'pull',
5335 b'pull',
5339 [
5336 [
5340 (
5337 (
5341 b'u',
5338 b'u',
5342 b'update',
5339 b'update',
5343 None,
5340 None,
5344 _(b'update to new branch head if new descendants were pulled'),
5341 _(b'update to new branch head if new descendants were pulled'),
5345 ),
5342 ),
5346 (
5343 (
5347 b'f',
5344 b'f',
5348 b'force',
5345 b'force',
5349 None,
5346 None,
5350 _(b'run even when remote repository is unrelated'),
5347 _(b'run even when remote repository is unrelated'),
5351 ),
5348 ),
5352 (
5349 (
5353 b'r',
5350 b'r',
5354 b'rev',
5351 b'rev',
5355 [],
5352 [],
5356 _(b'a remote changeset intended to be added'),
5353 _(b'a remote changeset intended to be added'),
5357 _(b'REV'),
5354 _(b'REV'),
5358 ),
5355 ),
5359 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5356 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5360 (
5357 (
5361 b'b',
5358 b'b',
5362 b'branch',
5359 b'branch',
5363 [],
5360 [],
5364 _(b'a specific branch you would like to pull'),
5361 _(b'a specific branch you would like to pull'),
5365 _(b'BRANCH'),
5362 _(b'BRANCH'),
5366 ),
5363 ),
5367 ]
5364 ]
5368 + remoteopts,
5365 + remoteopts,
5369 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5366 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5370 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5367 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5371 helpbasic=True,
5368 helpbasic=True,
5372 )
5369 )
5373 def pull(ui, repo, source=b"default", **opts):
5370 def pull(ui, repo, source=b"default", **opts):
5374 """pull changes from the specified source
5371 """pull changes from the specified source
5375
5372
5376 Pull changes from a remote repository to a local one.
5373 Pull changes from a remote repository to a local one.
5377
5374
5378 This finds all changes from the repository at the specified path
5375 This finds all changes from the repository at the specified path
5379 or URL and adds them to a local repository (the current one unless
5376 or URL and adds them to a local repository (the current one unless
5380 -R is specified). By default, this does not update the copy of the
5377 -R is specified). By default, this does not update the copy of the
5381 project in the working directory.
5378 project in the working directory.
5382
5379
5383 When cloning from servers that support it, Mercurial may fetch
5380 When cloning from servers that support it, Mercurial may fetch
5384 pre-generated data. When this is done, hooks operating on incoming
5381 pre-generated data. When this is done, hooks operating on incoming
5385 changesets and changegroups may fire more than once, once for each
5382 changesets and changegroups may fire more than once, once for each
5386 pre-generated bundle and as well as for any additional remaining
5383 pre-generated bundle and as well as for any additional remaining
5387 data. See :hg:`help -e clonebundles` for more.
5384 data. See :hg:`help -e clonebundles` for more.
5388
5385
5389 Use :hg:`incoming` if you want to see what would have been added
5386 Use :hg:`incoming` if you want to see what would have been added
5390 by a pull at the time you issued this command. If you then decide
5387 by a pull at the time you issued this command. If you then decide
5391 to add those changes to the repository, you should use :hg:`pull
5388 to add those changes to the repository, you should use :hg:`pull
5392 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5389 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5393
5390
5394 If SOURCE is omitted, the 'default' path will be used.
5391 If SOURCE is omitted, the 'default' path will be used.
5395 See :hg:`help urls` for more information.
5392 See :hg:`help urls` for more information.
5396
5393
5397 Specifying bookmark as ``.`` is equivalent to specifying the active
5394 Specifying bookmark as ``.`` is equivalent to specifying the active
5398 bookmark's name.
5395 bookmark's name.
5399
5396
5400 Returns 0 on success, 1 if an update had unresolved files.
5397 Returns 0 on success, 1 if an update had unresolved files.
5401 """
5398 """
5402
5399
5403 opts = pycompat.byteskwargs(opts)
5400 opts = pycompat.byteskwargs(opts)
5404 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5401 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5405 b'update'
5402 b'update'
5406 ):
5403 ):
5407 msg = _(b'update destination required by configuration')
5404 msg = _(b'update destination required by configuration')
5408 hint = _(b'use hg pull followed by hg update DEST')
5405 hint = _(b'use hg pull followed by hg update DEST')
5409 raise error.Abort(msg, hint=hint)
5406 raise error.Abort(msg, hint=hint)
5410
5407
5411 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5408 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5412 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5409 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5413 other = hg.peer(repo, opts, source)
5410 other = hg.peer(repo, opts, source)
5414 try:
5411 try:
5415 revs, checkout = hg.addbranchrevs(
5412 revs, checkout = hg.addbranchrevs(
5416 repo, other, branches, opts.get(b'rev')
5413 repo, other, branches, opts.get(b'rev')
5417 )
5414 )
5418
5415
5419 pullopargs = {}
5416 pullopargs = {}
5420
5417
5421 nodes = None
5418 nodes = None
5422 if opts.get(b'bookmark') or revs:
5419 if opts.get(b'bookmark') or revs:
5423 # The list of bookmark used here is the same used to actually update
5420 # The list of bookmark used here is the same used to actually update
5424 # the bookmark names, to avoid the race from issue 4689 and we do
5421 # the bookmark names, to avoid the race from issue 4689 and we do
5425 # all lookup and bookmark queries in one go so they see the same
5422 # all lookup and bookmark queries in one go so they see the same
5426 # version of the server state (issue 4700).
5423 # version of the server state (issue 4700).
5427 nodes = []
5424 nodes = []
5428 fnodes = []
5425 fnodes = []
5429 revs = revs or []
5426 revs = revs or []
5430 if revs and not other.capable(b'lookup'):
5427 if revs and not other.capable(b'lookup'):
5431 err = _(
5428 err = _(
5432 b"other repository doesn't support revision lookup, "
5429 b"other repository doesn't support revision lookup, "
5433 b"so a rev cannot be specified."
5430 b"so a rev cannot be specified."
5434 )
5431 )
5435 raise error.Abort(err)
5432 raise error.Abort(err)
5436 with other.commandexecutor() as e:
5433 with other.commandexecutor() as e:
5437 fremotebookmarks = e.callcommand(
5434 fremotebookmarks = e.callcommand(
5438 b'listkeys', {b'namespace': b'bookmarks'}
5435 b'listkeys', {b'namespace': b'bookmarks'}
5439 )
5436 )
5440 for r in revs:
5437 for r in revs:
5441 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5438 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5442 remotebookmarks = fremotebookmarks.result()
5439 remotebookmarks = fremotebookmarks.result()
5443 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5440 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5444 pullopargs[b'remotebookmarks'] = remotebookmarks
5441 pullopargs[b'remotebookmarks'] = remotebookmarks
5445 for b in opts.get(b'bookmark', []):
5442 for b in opts.get(b'bookmark', []):
5446 b = repo._bookmarks.expandname(b)
5443 b = repo._bookmarks.expandname(b)
5447 if b not in remotebookmarks:
5444 if b not in remotebookmarks:
5448 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5445 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5449 nodes.append(remotebookmarks[b])
5446 nodes.append(remotebookmarks[b])
5450 for i, rev in enumerate(revs):
5447 for i, rev in enumerate(revs):
5451 node = fnodes[i].result()
5448 node = fnodes[i].result()
5452 nodes.append(node)
5449 nodes.append(node)
5453 if rev == checkout:
5450 if rev == checkout:
5454 checkout = node
5451 checkout = node
5455
5452
5456 wlock = util.nullcontextmanager()
5453 wlock = util.nullcontextmanager()
5457 if opts.get(b'update'):
5454 if opts.get(b'update'):
5458 wlock = repo.wlock()
5455 wlock = repo.wlock()
5459 with wlock:
5456 with wlock:
5460 pullopargs.update(opts.get(b'opargs', {}))
5457 pullopargs.update(opts.get(b'opargs', {}))
5461 modheads = exchange.pull(
5458 modheads = exchange.pull(
5462 repo,
5459 repo,
5463 other,
5460 other,
5464 heads=nodes,
5461 heads=nodes,
5465 force=opts.get(b'force'),
5462 force=opts.get(b'force'),
5466 bookmarks=opts.get(b'bookmark', ()),
5463 bookmarks=opts.get(b'bookmark', ()),
5467 opargs=pullopargs,
5464 opargs=pullopargs,
5468 ).cgresult
5465 ).cgresult
5469
5466
5470 # brev is a name, which might be a bookmark to be activated at
5467 # brev is a name, which might be a bookmark to be activated at
5471 # the end of the update. In other words, it is an explicit
5468 # the end of the update. In other words, it is an explicit
5472 # destination of the update
5469 # destination of the update
5473 brev = None
5470 brev = None
5474
5471
5475 if checkout:
5472 if checkout:
5476 checkout = repo.unfiltered().changelog.rev(checkout)
5473 checkout = repo.unfiltered().changelog.rev(checkout)
5477
5474
5478 # order below depends on implementation of
5475 # order below depends on implementation of
5479 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5476 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5480 # because 'checkout' is determined without it.
5477 # because 'checkout' is determined without it.
5481 if opts.get(b'rev'):
5478 if opts.get(b'rev'):
5482 brev = opts[b'rev'][0]
5479 brev = opts[b'rev'][0]
5483 elif opts.get(b'branch'):
5480 elif opts.get(b'branch'):
5484 brev = opts[b'branch'][0]
5481 brev = opts[b'branch'][0]
5485 else:
5482 else:
5486 brev = branches[0]
5483 brev = branches[0]
5487 repo._subtoppath = source
5484 repo._subtoppath = source
5488 try:
5485 try:
5489 ret = postincoming(
5486 ret = postincoming(
5490 ui, repo, modheads, opts.get(b'update'), checkout, brev
5487 ui, repo, modheads, opts.get(b'update'), checkout, brev
5491 )
5488 )
5492 except error.FilteredRepoLookupError as exc:
5489 except error.FilteredRepoLookupError as exc:
5493 msg = _(b'cannot update to target: %s') % exc.args[0]
5490 msg = _(b'cannot update to target: %s') % exc.args[0]
5494 exc.args = (msg,) + exc.args[1:]
5491 exc.args = (msg,) + exc.args[1:]
5495 raise
5492 raise
5496 finally:
5493 finally:
5497 del repo._subtoppath
5494 del repo._subtoppath
5498
5495
5499 finally:
5496 finally:
5500 other.close()
5497 other.close()
5501 return ret
5498 return ret
5502
5499
5503
5500
5504 @command(
5501 @command(
5505 b'push',
5502 b'push',
5506 [
5503 [
5507 (b'f', b'force', None, _(b'force push')),
5504 (b'f', b'force', None, _(b'force push')),
5508 (
5505 (
5509 b'r',
5506 b'r',
5510 b'rev',
5507 b'rev',
5511 [],
5508 [],
5512 _(b'a changeset intended to be included in the destination'),
5509 _(b'a changeset intended to be included in the destination'),
5513 _(b'REV'),
5510 _(b'REV'),
5514 ),
5511 ),
5515 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5512 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5516 (
5513 (
5517 b'b',
5514 b'b',
5518 b'branch',
5515 b'branch',
5519 [],
5516 [],
5520 _(b'a specific branch you would like to push'),
5517 _(b'a specific branch you would like to push'),
5521 _(b'BRANCH'),
5518 _(b'BRANCH'),
5522 ),
5519 ),
5523 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5520 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5524 (
5521 (
5525 b'',
5522 b'',
5526 b'pushvars',
5523 b'pushvars',
5527 [],
5524 [],
5528 _(b'variables that can be sent to server (ADVANCED)'),
5525 _(b'variables that can be sent to server (ADVANCED)'),
5529 ),
5526 ),
5530 (
5527 (
5531 b'',
5528 b'',
5532 b'publish',
5529 b'publish',
5533 False,
5530 False,
5534 _(b'push the changeset as public (EXPERIMENTAL)'),
5531 _(b'push the changeset as public (EXPERIMENTAL)'),
5535 ),
5532 ),
5536 ]
5533 ]
5537 + remoteopts,
5534 + remoteopts,
5538 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5535 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5539 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5536 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5540 helpbasic=True,
5537 helpbasic=True,
5541 )
5538 )
5542 def push(ui, repo, dest=None, **opts):
5539 def push(ui, repo, dest=None, **opts):
5543 """push changes to the specified destination
5540 """push changes to the specified destination
5544
5541
5545 Push changesets from the local repository to the specified
5542 Push changesets from the local repository to the specified
5546 destination.
5543 destination.
5547
5544
5548 This operation is symmetrical to pull: it is identical to a pull
5545 This operation is symmetrical to pull: it is identical to a pull
5549 in the destination repository from the current one.
5546 in the destination repository from the current one.
5550
5547
5551 By default, push will not allow creation of new heads at the
5548 By default, push will not allow creation of new heads at the
5552 destination, since multiple heads would make it unclear which head
5549 destination, since multiple heads would make it unclear which head
5553 to use. In this situation, it is recommended to pull and merge
5550 to use. In this situation, it is recommended to pull and merge
5554 before pushing.
5551 before pushing.
5555
5552
5556 Use --new-branch if you want to allow push to create a new named
5553 Use --new-branch if you want to allow push to create a new named
5557 branch that is not present at the destination. This allows you to
5554 branch that is not present at the destination. This allows you to
5558 only create a new branch without forcing other changes.
5555 only create a new branch without forcing other changes.
5559
5556
5560 .. note::
5557 .. note::
5561
5558
5562 Extra care should be taken with the -f/--force option,
5559 Extra care should be taken with the -f/--force option,
5563 which will push all new heads on all branches, an action which will
5560 which will push all new heads on all branches, an action which will
5564 almost always cause confusion for collaborators.
5561 almost always cause confusion for collaborators.
5565
5562
5566 If -r/--rev is used, the specified revision and all its ancestors
5563 If -r/--rev is used, the specified revision and all its ancestors
5567 will be pushed to the remote repository.
5564 will be pushed to the remote repository.
5568
5565
5569 If -B/--bookmark is used, the specified bookmarked revision, its
5566 If -B/--bookmark is used, the specified bookmarked revision, its
5570 ancestors, and the bookmark will be pushed to the remote
5567 ancestors, and the bookmark will be pushed to the remote
5571 repository. Specifying ``.`` is equivalent to specifying the active
5568 repository. Specifying ``.`` is equivalent to specifying the active
5572 bookmark's name.
5569 bookmark's name.
5573
5570
5574 Please see :hg:`help urls` for important details about ``ssh://``
5571 Please see :hg:`help urls` for important details about ``ssh://``
5575 URLs. If DESTINATION is omitted, a default path will be used.
5572 URLs. If DESTINATION is omitted, a default path will be used.
5576
5573
5577 .. container:: verbose
5574 .. container:: verbose
5578
5575
5579 The --pushvars option sends strings to the server that become
5576 The --pushvars option sends strings to the server that become
5580 environment variables prepended with ``HG_USERVAR_``. For example,
5577 environment variables prepended with ``HG_USERVAR_``. For example,
5581 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5578 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5582 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5579 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5583
5580
5584 pushvars can provide for user-overridable hooks as well as set debug
5581 pushvars can provide for user-overridable hooks as well as set debug
5585 levels. One example is having a hook that blocks commits containing
5582 levels. One example is having a hook that blocks commits containing
5586 conflict markers, but enables the user to override the hook if the file
5583 conflict markers, but enables the user to override the hook if the file
5587 is using conflict markers for testing purposes or the file format has
5584 is using conflict markers for testing purposes or the file format has
5588 strings that look like conflict markers.
5585 strings that look like conflict markers.
5589
5586
5590 By default, servers will ignore `--pushvars`. To enable it add the
5587 By default, servers will ignore `--pushvars`. To enable it add the
5591 following to your configuration file::
5588 following to your configuration file::
5592
5589
5593 [push]
5590 [push]
5594 pushvars.server = true
5591 pushvars.server = true
5595
5592
5596 Returns 0 if push was successful, 1 if nothing to push.
5593 Returns 0 if push was successful, 1 if nothing to push.
5597 """
5594 """
5598
5595
5599 opts = pycompat.byteskwargs(opts)
5596 opts = pycompat.byteskwargs(opts)
5600 if opts.get(b'bookmark'):
5597 if opts.get(b'bookmark'):
5601 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5598 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5602 for b in opts[b'bookmark']:
5599 for b in opts[b'bookmark']:
5603 # translate -B options to -r so changesets get pushed
5600 # translate -B options to -r so changesets get pushed
5604 b = repo._bookmarks.expandname(b)
5601 b = repo._bookmarks.expandname(b)
5605 if b in repo._bookmarks:
5602 if b in repo._bookmarks:
5606 opts.setdefault(b'rev', []).append(b)
5603 opts.setdefault(b'rev', []).append(b)
5607 else:
5604 else:
5608 # if we try to push a deleted bookmark, translate it to null
5605 # if we try to push a deleted bookmark, translate it to null
5609 # this lets simultaneous -r, -b options continue working
5606 # this lets simultaneous -r, -b options continue working
5610 opts.setdefault(b'rev', []).append(b"null")
5607 opts.setdefault(b'rev', []).append(b"null")
5611
5608
5612 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5609 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5613 if not path:
5610 if not path:
5614 raise error.Abort(
5611 raise error.Abort(
5615 _(b'default repository not configured!'),
5612 _(b'default repository not configured!'),
5616 hint=_(b"see 'hg help config.paths'"),
5613 hint=_(b"see 'hg help config.paths'"),
5617 )
5614 )
5618 dest = path.pushloc or path.loc
5615 dest = path.pushloc or path.loc
5619 branches = (path.branch, opts.get(b'branch') or [])
5616 branches = (path.branch, opts.get(b'branch') or [])
5620 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5617 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5621 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5618 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5622 other = hg.peer(repo, opts, dest)
5619 other = hg.peer(repo, opts, dest)
5623
5620
5624 if revs:
5621 if revs:
5625 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5622 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5626 if not revs:
5623 if not revs:
5627 raise error.Abort(
5624 raise error.Abort(
5628 _(b"specified revisions evaluate to an empty set"),
5625 _(b"specified revisions evaluate to an empty set"),
5629 hint=_(b"use different revision arguments"),
5626 hint=_(b"use different revision arguments"),
5630 )
5627 )
5631 elif path.pushrev:
5628 elif path.pushrev:
5632 # It doesn't make any sense to specify ancestor revisions. So limit
5629 # It doesn't make any sense to specify ancestor revisions. So limit
5633 # to DAG heads to make discovery simpler.
5630 # to DAG heads to make discovery simpler.
5634 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5631 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5635 revs = scmutil.revrange(repo, [expr])
5632 revs = scmutil.revrange(repo, [expr])
5636 revs = [repo[rev].node() for rev in revs]
5633 revs = [repo[rev].node() for rev in revs]
5637 if not revs:
5634 if not revs:
5638 raise error.Abort(
5635 raise error.Abort(
5639 _(b'default push revset for path evaluates to an empty set')
5636 _(b'default push revset for path evaluates to an empty set')
5640 )
5637 )
5641 elif ui.configbool(b'commands', b'push.require-revs'):
5638 elif ui.configbool(b'commands', b'push.require-revs'):
5642 raise error.Abort(
5639 raise error.Abort(
5643 _(b'no revisions specified to push'),
5640 _(b'no revisions specified to push'),
5644 hint=_(b'did you mean "hg push -r ."?'),
5641 hint=_(b'did you mean "hg push -r ."?'),
5645 )
5642 )
5646
5643
5647 repo._subtoppath = dest
5644 repo._subtoppath = dest
5648 try:
5645 try:
5649 # push subrepos depth-first for coherent ordering
5646 # push subrepos depth-first for coherent ordering
5650 c = repo[b'.']
5647 c = repo[b'.']
5651 subs = c.substate # only repos that are committed
5648 subs = c.substate # only repos that are committed
5652 for s in sorted(subs):
5649 for s in sorted(subs):
5653 result = c.sub(s).push(opts)
5650 result = c.sub(s).push(opts)
5654 if result == 0:
5651 if result == 0:
5655 return not result
5652 return not result
5656 finally:
5653 finally:
5657 del repo._subtoppath
5654 del repo._subtoppath
5658
5655
5659 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5656 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5660 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5657 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5661
5658
5662 pushop = exchange.push(
5659 pushop = exchange.push(
5663 repo,
5660 repo,
5664 other,
5661 other,
5665 opts.get(b'force'),
5662 opts.get(b'force'),
5666 revs=revs,
5663 revs=revs,
5667 newbranch=opts.get(b'new_branch'),
5664 newbranch=opts.get(b'new_branch'),
5668 bookmarks=opts.get(b'bookmark', ()),
5665 bookmarks=opts.get(b'bookmark', ()),
5669 publish=opts.get(b'publish'),
5666 publish=opts.get(b'publish'),
5670 opargs=opargs,
5667 opargs=opargs,
5671 )
5668 )
5672
5669
5673 result = not pushop.cgresult
5670 result = not pushop.cgresult
5674
5671
5675 if pushop.bkresult is not None:
5672 if pushop.bkresult is not None:
5676 if pushop.bkresult == 2:
5673 if pushop.bkresult == 2:
5677 result = 2
5674 result = 2
5678 elif not result and pushop.bkresult:
5675 elif not result and pushop.bkresult:
5679 result = 2
5676 result = 2
5680
5677
5681 return result
5678 return result
5682
5679
5683
5680
5684 @command(
5681 @command(
5685 b'recover',
5682 b'recover',
5686 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5683 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5687 helpcategory=command.CATEGORY_MAINTENANCE,
5684 helpcategory=command.CATEGORY_MAINTENANCE,
5688 )
5685 )
5689 def recover(ui, repo, **opts):
5686 def recover(ui, repo, **opts):
5690 """roll back an interrupted transaction
5687 """roll back an interrupted transaction
5691
5688
5692 Recover from an interrupted commit or pull.
5689 Recover from an interrupted commit or pull.
5693
5690
5694 This command tries to fix the repository status after an
5691 This command tries to fix the repository status after an
5695 interrupted operation. It should only be necessary when Mercurial
5692 interrupted operation. It should only be necessary when Mercurial
5696 suggests it.
5693 suggests it.
5697
5694
5698 Returns 0 if successful, 1 if nothing to recover or verify fails.
5695 Returns 0 if successful, 1 if nothing to recover or verify fails.
5699 """
5696 """
5700 ret = repo.recover()
5697 ret = repo.recover()
5701 if ret:
5698 if ret:
5702 if opts['verify']:
5699 if opts['verify']:
5703 return hg.verify(repo)
5700 return hg.verify(repo)
5704 else:
5701 else:
5705 msg = _(
5702 msg = _(
5706 b"(verify step skipped, run `hg verify` to check your "
5703 b"(verify step skipped, run `hg verify` to check your "
5707 b"repository content)\n"
5704 b"repository content)\n"
5708 )
5705 )
5709 ui.warn(msg)
5706 ui.warn(msg)
5710 return 0
5707 return 0
5711 return 1
5708 return 1
5712
5709
5713
5710
5714 @command(
5711 @command(
5715 b'remove|rm',
5712 b'remove|rm',
5716 [
5713 [
5717 (b'A', b'after', None, _(b'record delete for missing files')),
5714 (b'A', b'after', None, _(b'record delete for missing files')),
5718 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5715 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5719 ]
5716 ]
5720 + subrepoopts
5717 + subrepoopts
5721 + walkopts
5718 + walkopts
5722 + dryrunopts,
5719 + dryrunopts,
5723 _(b'[OPTION]... FILE...'),
5720 _(b'[OPTION]... FILE...'),
5724 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5721 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5725 helpbasic=True,
5722 helpbasic=True,
5726 inferrepo=True,
5723 inferrepo=True,
5727 )
5724 )
5728 def remove(ui, repo, *pats, **opts):
5725 def remove(ui, repo, *pats, **opts):
5729 """remove the specified files on the next commit
5726 """remove the specified files on the next commit
5730
5727
5731 Schedule the indicated files for removal from the current branch.
5728 Schedule the indicated files for removal from the current branch.
5732
5729
5733 This command schedules the files to be removed at the next commit.
5730 This command schedules the files to be removed at the next commit.
5734 To undo a remove before that, see :hg:`revert`. To undo added
5731 To undo a remove before that, see :hg:`revert`. To undo added
5735 files, see :hg:`forget`.
5732 files, see :hg:`forget`.
5736
5733
5737 .. container:: verbose
5734 .. container:: verbose
5738
5735
5739 -A/--after can be used to remove only files that have already
5736 -A/--after can be used to remove only files that have already
5740 been deleted, -f/--force can be used to force deletion, and -Af
5737 been deleted, -f/--force can be used to force deletion, and -Af
5741 can be used to remove files from the next revision without
5738 can be used to remove files from the next revision without
5742 deleting them from the working directory.
5739 deleting them from the working directory.
5743
5740
5744 The following table details the behavior of remove for different
5741 The following table details the behavior of remove for different
5745 file states (columns) and option combinations (rows). The file
5742 file states (columns) and option combinations (rows). The file
5746 states are Added [A], Clean [C], Modified [M] and Missing [!]
5743 states are Added [A], Clean [C], Modified [M] and Missing [!]
5747 (as reported by :hg:`status`). The actions are Warn, Remove
5744 (as reported by :hg:`status`). The actions are Warn, Remove
5748 (from branch) and Delete (from disk):
5745 (from branch) and Delete (from disk):
5749
5746
5750 ========= == == == ==
5747 ========= == == == ==
5751 opt/state A C M !
5748 opt/state A C M !
5752 ========= == == == ==
5749 ========= == == == ==
5753 none W RD W R
5750 none W RD W R
5754 -f R RD RD R
5751 -f R RD RD R
5755 -A W W W R
5752 -A W W W R
5756 -Af R R R R
5753 -Af R R R R
5757 ========= == == == ==
5754 ========= == == == ==
5758
5755
5759 .. note::
5756 .. note::
5760
5757
5761 :hg:`remove` never deletes files in Added [A] state from the
5758 :hg:`remove` never deletes files in Added [A] state from the
5762 working directory, not even if ``--force`` is specified.
5759 working directory, not even if ``--force`` is specified.
5763
5760
5764 Returns 0 on success, 1 if any warnings encountered.
5761 Returns 0 on success, 1 if any warnings encountered.
5765 """
5762 """
5766
5763
5767 opts = pycompat.byteskwargs(opts)
5764 opts = pycompat.byteskwargs(opts)
5768 after, force = opts.get(b'after'), opts.get(b'force')
5765 after, force = opts.get(b'after'), opts.get(b'force')
5769 dryrun = opts.get(b'dry_run')
5766 dryrun = opts.get(b'dry_run')
5770 if not pats and not after:
5767 if not pats and not after:
5771 raise error.Abort(_(b'no files specified'))
5768 raise error.Abort(_(b'no files specified'))
5772
5769
5773 m = scmutil.match(repo[None], pats, opts)
5770 m = scmutil.match(repo[None], pats, opts)
5774 subrepos = opts.get(b'subrepos')
5771 subrepos = opts.get(b'subrepos')
5775 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5772 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5776 return cmdutil.remove(
5773 return cmdutil.remove(
5777 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5774 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5778 )
5775 )
5779
5776
5780
5777
5781 @command(
5778 @command(
5782 b'rename|move|mv',
5779 b'rename|move|mv',
5783 [
5780 [
5784 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5781 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5785 (
5782 (
5786 b'f',
5783 b'f',
5787 b'force',
5784 b'force',
5788 None,
5785 None,
5789 _(b'forcibly move over an existing managed file'),
5786 _(b'forcibly move over an existing managed file'),
5790 ),
5787 ),
5791 ]
5788 ]
5792 + walkopts
5789 + walkopts
5793 + dryrunopts,
5790 + dryrunopts,
5794 _(b'[OPTION]... SOURCE... DEST'),
5791 _(b'[OPTION]... SOURCE... DEST'),
5795 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5796 )
5793 )
5797 def rename(ui, repo, *pats, **opts):
5794 def rename(ui, repo, *pats, **opts):
5798 """rename files; equivalent of copy + remove
5795 """rename files; equivalent of copy + remove
5799
5796
5800 Mark dest as copies of sources; mark sources for deletion. If dest
5797 Mark dest as copies of sources; mark sources for deletion. If dest
5801 is a directory, copies are put in that directory. If dest is a
5798 is a directory, copies are put in that directory. If dest is a
5802 file, there can only be one source.
5799 file, there can only be one source.
5803
5800
5804 By default, this command copies the contents of files as they
5801 By default, this command copies the contents of files as they
5805 exist in the working directory. If invoked with -A/--after, the
5802 exist in the working directory. If invoked with -A/--after, the
5806 operation is recorded, but no copying is performed.
5803 operation is recorded, but no copying is performed.
5807
5804
5808 This command takes effect at the next commit. To undo a rename
5805 This command takes effect at the next commit. To undo a rename
5809 before that, see :hg:`revert`.
5806 before that, see :hg:`revert`.
5810
5807
5811 Returns 0 on success, 1 if errors are encountered.
5808 Returns 0 on success, 1 if errors are encountered.
5812 """
5809 """
5813 opts = pycompat.byteskwargs(opts)
5810 opts = pycompat.byteskwargs(opts)
5814 with repo.wlock(False):
5811 with repo.wlock(False):
5815 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5812 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5816
5813
5817
5814
5818 @command(
5815 @command(
5819 b'resolve',
5816 b'resolve',
5820 [
5817 [
5821 (b'a', b'all', None, _(b'select all unresolved files')),
5818 (b'a', b'all', None, _(b'select all unresolved files')),
5822 (b'l', b'list', None, _(b'list state of files needing merge')),
5819 (b'l', b'list', None, _(b'list state of files needing merge')),
5823 (b'm', b'mark', None, _(b'mark files as resolved')),
5820 (b'm', b'mark', None, _(b'mark files as resolved')),
5824 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5821 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5825 (b'n', b'no-status', None, _(b'hide status prefix')),
5822 (b'n', b'no-status', None, _(b'hide status prefix')),
5826 (b'', b're-merge', None, _(b're-merge files')),
5823 (b'', b're-merge', None, _(b're-merge files')),
5827 ]
5824 ]
5828 + mergetoolopts
5825 + mergetoolopts
5829 + walkopts
5826 + walkopts
5830 + formatteropts,
5827 + formatteropts,
5831 _(b'[OPTION]... [FILE]...'),
5828 _(b'[OPTION]... [FILE]...'),
5832 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5829 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5833 inferrepo=True,
5830 inferrepo=True,
5834 )
5831 )
5835 def resolve(ui, repo, *pats, **opts):
5832 def resolve(ui, repo, *pats, **opts):
5836 """redo merges or set/view the merge status of files
5833 """redo merges or set/view the merge status of files
5837
5834
5838 Merges with unresolved conflicts are often the result of
5835 Merges with unresolved conflicts are often the result of
5839 non-interactive merging using the ``internal:merge`` configuration
5836 non-interactive merging using the ``internal:merge`` configuration
5840 setting, or a command-line merge tool like ``diff3``. The resolve
5837 setting, or a command-line merge tool like ``diff3``. The resolve
5841 command is used to manage the files involved in a merge, after
5838 command is used to manage the files involved in a merge, after
5842 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5839 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5843 working directory must have two parents). See :hg:`help
5840 working directory must have two parents). See :hg:`help
5844 merge-tools` for information on configuring merge tools.
5841 merge-tools` for information on configuring merge tools.
5845
5842
5846 The resolve command can be used in the following ways:
5843 The resolve command can be used in the following ways:
5847
5844
5848 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5845 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5849 the specified files, discarding any previous merge attempts. Re-merging
5846 the specified files, discarding any previous merge attempts. Re-merging
5850 is not performed for files already marked as resolved. Use ``--all/-a``
5847 is not performed for files already marked as resolved. Use ``--all/-a``
5851 to select all unresolved files. ``--tool`` can be used to specify
5848 to select all unresolved files. ``--tool`` can be used to specify
5852 the merge tool used for the given files. It overrides the HGMERGE
5849 the merge tool used for the given files. It overrides the HGMERGE
5853 environment variable and your configuration files. Previous file
5850 environment variable and your configuration files. Previous file
5854 contents are saved with a ``.orig`` suffix.
5851 contents are saved with a ``.orig`` suffix.
5855
5852
5856 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5853 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5857 (e.g. after having manually fixed-up the files). The default is
5854 (e.g. after having manually fixed-up the files). The default is
5858 to mark all unresolved files.
5855 to mark all unresolved files.
5859
5856
5860 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5857 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5861 default is to mark all resolved files.
5858 default is to mark all resolved files.
5862
5859
5863 - :hg:`resolve -l`: list files which had or still have conflicts.
5860 - :hg:`resolve -l`: list files which had or still have conflicts.
5864 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5861 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5865 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5862 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5866 the list. See :hg:`help filesets` for details.
5863 the list. See :hg:`help filesets` for details.
5867
5864
5868 .. note::
5865 .. note::
5869
5866
5870 Mercurial will not let you commit files with unresolved merge
5867 Mercurial will not let you commit files with unresolved merge
5871 conflicts. You must use :hg:`resolve -m ...` before you can
5868 conflicts. You must use :hg:`resolve -m ...` before you can
5872 commit after a conflicting merge.
5869 commit after a conflicting merge.
5873
5870
5874 .. container:: verbose
5871 .. container:: verbose
5875
5872
5876 Template:
5873 Template:
5877
5874
5878 The following keywords are supported in addition to the common template
5875 The following keywords are supported in addition to the common template
5879 keywords and functions. See also :hg:`help templates`.
5876 keywords and functions. See also :hg:`help templates`.
5880
5877
5881 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5878 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5882 :path: String. Repository-absolute path of the file.
5879 :path: String. Repository-absolute path of the file.
5883
5880
5884 Returns 0 on success, 1 if any files fail a resolve attempt.
5881 Returns 0 on success, 1 if any files fail a resolve attempt.
5885 """
5882 """
5886
5883
5887 opts = pycompat.byteskwargs(opts)
5884 opts = pycompat.byteskwargs(opts)
5888 confirm = ui.configbool(b'commands', b'resolve.confirm')
5885 confirm = ui.configbool(b'commands', b'resolve.confirm')
5889 flaglist = b'all mark unmark list no_status re_merge'.split()
5886 flaglist = b'all mark unmark list no_status re_merge'.split()
5890 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5887 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5891
5888
5892 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5889 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5893 if actioncount > 1:
5890 if actioncount > 1:
5894 raise error.Abort(_(b"too many actions specified"))
5891 raise error.Abort(_(b"too many actions specified"))
5895 elif actioncount == 0 and ui.configbool(
5892 elif actioncount == 0 and ui.configbool(
5896 b'commands', b'resolve.explicit-re-merge'
5893 b'commands', b'resolve.explicit-re-merge'
5897 ):
5894 ):
5898 hint = _(b'use --mark, --unmark, --list or --re-merge')
5895 hint = _(b'use --mark, --unmark, --list or --re-merge')
5899 raise error.Abort(_(b'no action specified'), hint=hint)
5896 raise error.Abort(_(b'no action specified'), hint=hint)
5900 if pats and all:
5897 if pats and all:
5901 raise error.Abort(_(b"can't specify --all and patterns"))
5898 raise error.Abort(_(b"can't specify --all and patterns"))
5902 if not (all or pats or show or mark or unmark):
5899 if not (all or pats or show or mark or unmark):
5903 raise error.Abort(
5900 raise error.Abort(
5904 _(b'no files or directories specified'),
5901 _(b'no files or directories specified'),
5905 hint=b'use --all to re-merge all unresolved files',
5902 hint=b'use --all to re-merge all unresolved files',
5906 )
5903 )
5907
5904
5908 if confirm:
5905 if confirm:
5909 if all:
5906 if all:
5910 if ui.promptchoice(
5907 if ui.promptchoice(
5911 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5908 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5912 ):
5909 ):
5913 raise error.Abort(_(b'user quit'))
5910 raise error.Abort(_(b'user quit'))
5914 if mark and not pats:
5911 if mark and not pats:
5915 if ui.promptchoice(
5912 if ui.promptchoice(
5916 _(
5913 _(
5917 b'mark all unresolved files as resolved (yn)?'
5914 b'mark all unresolved files as resolved (yn)?'
5918 b'$$ &Yes $$ &No'
5915 b'$$ &Yes $$ &No'
5919 )
5916 )
5920 ):
5917 ):
5921 raise error.Abort(_(b'user quit'))
5918 raise error.Abort(_(b'user quit'))
5922 if unmark and not pats:
5919 if unmark and not pats:
5923 if ui.promptchoice(
5920 if ui.promptchoice(
5924 _(
5921 _(
5925 b'mark all resolved files as unresolved (yn)?'
5922 b'mark all resolved files as unresolved (yn)?'
5926 b'$$ &Yes $$ &No'
5923 b'$$ &Yes $$ &No'
5927 )
5924 )
5928 ):
5925 ):
5929 raise error.Abort(_(b'user quit'))
5926 raise error.Abort(_(b'user quit'))
5930
5927
5931 uipathfn = scmutil.getuipathfn(repo)
5928 uipathfn = scmutil.getuipathfn(repo)
5932
5929
5933 if show:
5930 if show:
5934 ui.pager(b'resolve')
5931 ui.pager(b'resolve')
5935 fm = ui.formatter(b'resolve', opts)
5932 fm = ui.formatter(b'resolve', opts)
5936 ms = mergemod.mergestate.read(repo)
5933 ms = mergemod.mergestate.read(repo)
5937 wctx = repo[None]
5934 wctx = repo[None]
5938 m = scmutil.match(wctx, pats, opts)
5935 m = scmutil.match(wctx, pats, opts)
5939
5936
5940 # Labels and keys based on merge state. Unresolved path conflicts show
5937 # Labels and keys based on merge state. Unresolved path conflicts show
5941 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5938 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5942 # resolved conflicts.
5939 # resolved conflicts.
5943 mergestateinfo = {
5940 mergestateinfo = {
5944 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5941 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5945 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5942 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5946 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5943 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5947 b'resolve.unresolved',
5944 b'resolve.unresolved',
5948 b'P',
5945 b'P',
5949 ),
5946 ),
5950 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5947 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5951 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5948 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5952 b'resolve.driverresolved',
5949 b'resolve.driverresolved',
5953 b'D',
5950 b'D',
5954 ),
5951 ),
5955 }
5952 }
5956
5953
5957 for f in ms:
5954 for f in ms:
5958 if not m(f):
5955 if not m(f):
5959 continue
5956 continue
5960
5957
5961 label, key = mergestateinfo[ms[f]]
5958 label, key = mergestateinfo[ms[f]]
5962 fm.startitem()
5959 fm.startitem()
5963 fm.context(ctx=wctx)
5960 fm.context(ctx=wctx)
5964 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5961 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5965 fm.data(path=f)
5962 fm.data(path=f)
5966 fm.plain(b'%s\n' % uipathfn(f), label=label)
5963 fm.plain(b'%s\n' % uipathfn(f), label=label)
5967 fm.end()
5964 fm.end()
5968 return 0
5965 return 0
5969
5966
5970 with repo.wlock():
5967 with repo.wlock():
5971 ms = mergemod.mergestate.read(repo)
5968 ms = mergemod.mergestate.read(repo)
5972
5969
5973 if not (ms.active() or repo.dirstate.p2() != nullid):
5970 if not (ms.active() or repo.dirstate.p2() != nullid):
5974 raise error.Abort(
5971 raise error.Abort(
5975 _(b'resolve command not applicable when not merging')
5972 _(b'resolve command not applicable when not merging')
5976 )
5973 )
5977
5974
5978 wctx = repo[None]
5975 wctx = repo[None]
5979
5976
5980 if (
5977 if (
5981 ms.mergedriver
5978 ms.mergedriver
5982 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5979 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5983 ):
5980 ):
5984 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5981 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5985 ms.commit()
5982 ms.commit()
5986 # allow mark and unmark to go through
5983 # allow mark and unmark to go through
5987 if not mark and not unmark and not proceed:
5984 if not mark and not unmark and not proceed:
5988 return 1
5985 return 1
5989
5986
5990 m = scmutil.match(wctx, pats, opts)
5987 m = scmutil.match(wctx, pats, opts)
5991 ret = 0
5988 ret = 0
5992 didwork = False
5989 didwork = False
5993 runconclude = False
5990 runconclude = False
5994
5991
5995 tocomplete = []
5992 tocomplete = []
5996 hasconflictmarkers = []
5993 hasconflictmarkers = []
5997 if mark:
5994 if mark:
5998 markcheck = ui.config(b'commands', b'resolve.mark-check')
5995 markcheck = ui.config(b'commands', b'resolve.mark-check')
5999 if markcheck not in [b'warn', b'abort']:
5996 if markcheck not in [b'warn', b'abort']:
6000 # Treat all invalid / unrecognized values as 'none'.
5997 # Treat all invalid / unrecognized values as 'none'.
6001 markcheck = False
5998 markcheck = False
6002 for f in ms:
5999 for f in ms:
6003 if not m(f):
6000 if not m(f):
6004 continue
6001 continue
6005
6002
6006 didwork = True
6003 didwork = True
6007
6004
6008 # don't let driver-resolved files be marked, and run the conclude
6005 # don't let driver-resolved files be marked, and run the conclude
6009 # step if asked to resolve
6006 # step if asked to resolve
6010 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6007 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6011 exact = m.exact(f)
6008 exact = m.exact(f)
6012 if mark:
6009 if mark:
6013 if exact:
6010 if exact:
6014 ui.warn(
6011 ui.warn(
6015 _(b'not marking %s as it is driver-resolved\n')
6012 _(b'not marking %s as it is driver-resolved\n')
6016 % uipathfn(f)
6013 % uipathfn(f)
6017 )
6014 )
6018 elif unmark:
6015 elif unmark:
6019 if exact:
6016 if exact:
6020 ui.warn(
6017 ui.warn(
6021 _(b'not unmarking %s as it is driver-resolved\n')
6018 _(b'not unmarking %s as it is driver-resolved\n')
6022 % uipathfn(f)
6019 % uipathfn(f)
6023 )
6020 )
6024 else:
6021 else:
6025 runconclude = True
6022 runconclude = True
6026 continue
6023 continue
6027
6024
6028 # path conflicts must be resolved manually
6025 # path conflicts must be resolved manually
6029 if ms[f] in (
6026 if ms[f] in (
6030 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6027 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6031 mergemod.MERGE_RECORD_RESOLVED_PATH,
6028 mergemod.MERGE_RECORD_RESOLVED_PATH,
6032 ):
6029 ):
6033 if mark:
6030 if mark:
6034 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6031 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6035 elif unmark:
6032 elif unmark:
6036 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6033 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6037 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6034 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6038 ui.warn(
6035 ui.warn(
6039 _(b'%s: path conflict must be resolved manually\n')
6036 _(b'%s: path conflict must be resolved manually\n')
6040 % uipathfn(f)
6037 % uipathfn(f)
6041 )
6038 )
6042 continue
6039 continue
6043
6040
6044 if mark:
6041 if mark:
6045 if markcheck:
6042 if markcheck:
6046 fdata = repo.wvfs.tryread(f)
6043 fdata = repo.wvfs.tryread(f)
6047 if (
6044 if (
6048 filemerge.hasconflictmarkers(fdata)
6045 filemerge.hasconflictmarkers(fdata)
6049 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6046 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6050 ):
6047 ):
6051 hasconflictmarkers.append(f)
6048 hasconflictmarkers.append(f)
6052 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6049 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6053 elif unmark:
6050 elif unmark:
6054 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6051 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6055 else:
6052 else:
6056 # backup pre-resolve (merge uses .orig for its own purposes)
6053 # backup pre-resolve (merge uses .orig for its own purposes)
6057 a = repo.wjoin(f)
6054 a = repo.wjoin(f)
6058 try:
6055 try:
6059 util.copyfile(a, a + b".resolve")
6056 util.copyfile(a, a + b".resolve")
6060 except (IOError, OSError) as inst:
6057 except (IOError, OSError) as inst:
6061 if inst.errno != errno.ENOENT:
6058 if inst.errno != errno.ENOENT:
6062 raise
6059 raise
6063
6060
6064 try:
6061 try:
6065 # preresolve file
6062 # preresolve file
6066 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6063 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6067 with ui.configoverride(overrides, b'resolve'):
6064 with ui.configoverride(overrides, b'resolve'):
6068 complete, r = ms.preresolve(f, wctx)
6065 complete, r = ms.preresolve(f, wctx)
6069 if not complete:
6066 if not complete:
6070 tocomplete.append(f)
6067 tocomplete.append(f)
6071 elif r:
6068 elif r:
6072 ret = 1
6069 ret = 1
6073 finally:
6070 finally:
6074 ms.commit()
6071 ms.commit()
6075
6072
6076 # replace filemerge's .orig file with our resolve file, but only
6073 # replace filemerge's .orig file with our resolve file, but only
6077 # for merges that are complete
6074 # for merges that are complete
6078 if complete:
6075 if complete:
6079 try:
6076 try:
6080 util.rename(
6077 util.rename(
6081 a + b".resolve", scmutil.backuppath(ui, repo, f)
6078 a + b".resolve", scmutil.backuppath(ui, repo, f)
6082 )
6079 )
6083 except OSError as inst:
6080 except OSError as inst:
6084 if inst.errno != errno.ENOENT:
6081 if inst.errno != errno.ENOENT:
6085 raise
6082 raise
6086
6083
6087 if hasconflictmarkers:
6084 if hasconflictmarkers:
6088 ui.warn(
6085 ui.warn(
6089 _(
6086 _(
6090 b'warning: the following files still have conflict '
6087 b'warning: the following files still have conflict '
6091 b'markers:\n'
6088 b'markers:\n'
6092 )
6089 )
6093 + b''.join(
6090 + b''.join(
6094 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6091 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6095 )
6092 )
6096 )
6093 )
6097 if markcheck == b'abort' and not all and not pats:
6094 if markcheck == b'abort' and not all and not pats:
6098 raise error.Abort(
6095 raise error.Abort(
6099 _(b'conflict markers detected'),
6096 _(b'conflict markers detected'),
6100 hint=_(b'use --all to mark anyway'),
6097 hint=_(b'use --all to mark anyway'),
6101 )
6098 )
6102
6099
6103 for f in tocomplete:
6100 for f in tocomplete:
6104 try:
6101 try:
6105 # resolve file
6102 # resolve file
6106 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6103 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6107 with ui.configoverride(overrides, b'resolve'):
6104 with ui.configoverride(overrides, b'resolve'):
6108 r = ms.resolve(f, wctx)
6105 r = ms.resolve(f, wctx)
6109 if r:
6106 if r:
6110 ret = 1
6107 ret = 1
6111 finally:
6108 finally:
6112 ms.commit()
6109 ms.commit()
6113
6110
6114 # replace filemerge's .orig file with our resolve file
6111 # replace filemerge's .orig file with our resolve file
6115 a = repo.wjoin(f)
6112 a = repo.wjoin(f)
6116 try:
6113 try:
6117 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6114 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6118 except OSError as inst:
6115 except OSError as inst:
6119 if inst.errno != errno.ENOENT:
6116 if inst.errno != errno.ENOENT:
6120 raise
6117 raise
6121
6118
6122 ms.commit()
6119 ms.commit()
6123 ms.recordactions()
6120 ms.recordactions()
6124
6121
6125 if not didwork and pats:
6122 if not didwork and pats:
6126 hint = None
6123 hint = None
6127 if not any([p for p in pats if p.find(b':') >= 0]):
6124 if not any([p for p in pats if p.find(b':') >= 0]):
6128 pats = [b'path:%s' % p for p in pats]
6125 pats = [b'path:%s' % p for p in pats]
6129 m = scmutil.match(wctx, pats, opts)
6126 m = scmutil.match(wctx, pats, opts)
6130 for f in ms:
6127 for f in ms:
6131 if not m(f):
6128 if not m(f):
6132 continue
6129 continue
6133
6130
6134 def flag(o):
6131 def flag(o):
6135 if o == b're_merge':
6132 if o == b're_merge':
6136 return b'--re-merge '
6133 return b'--re-merge '
6137 return b'-%s ' % o[0:1]
6134 return b'-%s ' % o[0:1]
6138
6135
6139 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6136 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6140 hint = _(b"(try: hg resolve %s%s)\n") % (
6137 hint = _(b"(try: hg resolve %s%s)\n") % (
6141 flags,
6138 flags,
6142 b' '.join(pats),
6139 b' '.join(pats),
6143 )
6140 )
6144 break
6141 break
6145 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6142 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6146 if hint:
6143 if hint:
6147 ui.warn(hint)
6144 ui.warn(hint)
6148 elif ms.mergedriver and ms.mdstate() != b's':
6145 elif ms.mergedriver and ms.mdstate() != b's':
6149 # run conclude step when either a driver-resolved file is requested
6146 # run conclude step when either a driver-resolved file is requested
6150 # or there are no driver-resolved files
6147 # or there are no driver-resolved files
6151 # we can't use 'ret' to determine whether any files are unresolved
6148 # we can't use 'ret' to determine whether any files are unresolved
6152 # because we might not have tried to resolve some
6149 # because we might not have tried to resolve some
6153 if (runconclude or not list(ms.driverresolved())) and not list(
6150 if (runconclude or not list(ms.driverresolved())) and not list(
6154 ms.unresolved()
6151 ms.unresolved()
6155 ):
6152 ):
6156 proceed = mergemod.driverconclude(repo, ms, wctx)
6153 proceed = mergemod.driverconclude(repo, ms, wctx)
6157 ms.commit()
6154 ms.commit()
6158 if not proceed:
6155 if not proceed:
6159 return 1
6156 return 1
6160
6157
6161 # Nudge users into finishing an unfinished operation
6158 # Nudge users into finishing an unfinished operation
6162 unresolvedf = list(ms.unresolved())
6159 unresolvedf = list(ms.unresolved())
6163 driverresolvedf = list(ms.driverresolved())
6160 driverresolvedf = list(ms.driverresolved())
6164 if not unresolvedf and not driverresolvedf:
6161 if not unresolvedf and not driverresolvedf:
6165 ui.status(_(b'(no more unresolved files)\n'))
6162 ui.status(_(b'(no more unresolved files)\n'))
6166 cmdutil.checkafterresolved(repo)
6163 cmdutil.checkafterresolved(repo)
6167 elif not unresolvedf:
6164 elif not unresolvedf:
6168 ui.status(
6165 ui.status(
6169 _(
6166 _(
6170 b'(no more unresolved files -- '
6167 b'(no more unresolved files -- '
6171 b'run "hg resolve --all" to conclude)\n'
6168 b'run "hg resolve --all" to conclude)\n'
6172 )
6169 )
6173 )
6170 )
6174
6171
6175 return ret
6172 return ret
6176
6173
6177
6174
6178 @command(
6175 @command(
6179 b'revert',
6176 b'revert',
6180 [
6177 [
6181 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6178 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6182 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6179 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6183 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6180 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6184 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6181 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6185 (b'i', b'interactive', None, _(b'interactively select the changes')),
6182 (b'i', b'interactive', None, _(b'interactively select the changes')),
6186 ]
6183 ]
6187 + walkopts
6184 + walkopts
6188 + dryrunopts,
6185 + dryrunopts,
6189 _(b'[OPTION]... [-r REV] [NAME]...'),
6186 _(b'[OPTION]... [-r REV] [NAME]...'),
6190 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6187 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6191 )
6188 )
6192 def revert(ui, repo, *pats, **opts):
6189 def revert(ui, repo, *pats, **opts):
6193 """restore files to their checkout state
6190 """restore files to their checkout state
6194
6191
6195 .. note::
6192 .. note::
6196
6193
6197 To check out earlier revisions, you should use :hg:`update REV`.
6194 To check out earlier revisions, you should use :hg:`update REV`.
6198 To cancel an uncommitted merge (and lose your changes),
6195 To cancel an uncommitted merge (and lose your changes),
6199 use :hg:`merge --abort`.
6196 use :hg:`merge --abort`.
6200
6197
6201 With no revision specified, revert the specified files or directories
6198 With no revision specified, revert the specified files or directories
6202 to the contents they had in the parent of the working directory.
6199 to the contents they had in the parent of the working directory.
6203 This restores the contents of files to an unmodified
6200 This restores the contents of files to an unmodified
6204 state and unschedules adds, removes, copies, and renames. If the
6201 state and unschedules adds, removes, copies, and renames. If the
6205 working directory has two parents, you must explicitly specify a
6202 working directory has two parents, you must explicitly specify a
6206 revision.
6203 revision.
6207
6204
6208 Using the -r/--rev or -d/--date options, revert the given files or
6205 Using the -r/--rev or -d/--date options, revert the given files or
6209 directories to their states as of a specific revision. Because
6206 directories to their states as of a specific revision. Because
6210 revert does not change the working directory parents, this will
6207 revert does not change the working directory parents, this will
6211 cause these files to appear modified. This can be helpful to "back
6208 cause these files to appear modified. This can be helpful to "back
6212 out" some or all of an earlier change. See :hg:`backout` for a
6209 out" some or all of an earlier change. See :hg:`backout` for a
6213 related method.
6210 related method.
6214
6211
6215 Modified files are saved with a .orig suffix before reverting.
6212 Modified files are saved with a .orig suffix before reverting.
6216 To disable these backups, use --no-backup. It is possible to store
6213 To disable these backups, use --no-backup. It is possible to store
6217 the backup files in a custom directory relative to the root of the
6214 the backup files in a custom directory relative to the root of the
6218 repository by setting the ``ui.origbackuppath`` configuration
6215 repository by setting the ``ui.origbackuppath`` configuration
6219 option.
6216 option.
6220
6217
6221 See :hg:`help dates` for a list of formats valid for -d/--date.
6218 See :hg:`help dates` for a list of formats valid for -d/--date.
6222
6219
6223 See :hg:`help backout` for a way to reverse the effect of an
6220 See :hg:`help backout` for a way to reverse the effect of an
6224 earlier changeset.
6221 earlier changeset.
6225
6222
6226 Returns 0 on success.
6223 Returns 0 on success.
6227 """
6224 """
6228
6225
6229 opts = pycompat.byteskwargs(opts)
6226 opts = pycompat.byteskwargs(opts)
6230 if opts.get(b"date"):
6227 if opts.get(b"date"):
6231 if opts.get(b"rev"):
6228 if opts.get(b"rev"):
6232 raise error.Abort(_(b"you can't specify a revision and a date"))
6229 raise error.Abort(_(b"you can't specify a revision and a date"))
6233 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6230 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6234
6231
6235 parent, p2 = repo.dirstate.parents()
6232 parent, p2 = repo.dirstate.parents()
6236 if not opts.get(b'rev') and p2 != nullid:
6233 if not opts.get(b'rev') and p2 != nullid:
6237 # revert after merge is a trap for new users (issue2915)
6234 # revert after merge is a trap for new users (issue2915)
6238 raise error.Abort(
6235 raise error.Abort(
6239 _(b'uncommitted merge with no revision specified'),
6236 _(b'uncommitted merge with no revision specified'),
6240 hint=_(b"use 'hg update' or see 'hg help revert'"),
6237 hint=_(b"use 'hg update' or see 'hg help revert'"),
6241 )
6238 )
6242
6239
6243 rev = opts.get(b'rev')
6240 rev = opts.get(b'rev')
6244 if rev:
6241 if rev:
6245 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6242 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6246 ctx = scmutil.revsingle(repo, rev)
6243 ctx = scmutil.revsingle(repo, rev)
6247
6244
6248 if not (
6245 if not (
6249 pats
6246 pats
6250 or opts.get(b'include')
6247 or opts.get(b'include')
6251 or opts.get(b'exclude')
6248 or opts.get(b'exclude')
6252 or opts.get(b'all')
6249 or opts.get(b'all')
6253 or opts.get(b'interactive')
6250 or opts.get(b'interactive')
6254 ):
6251 ):
6255 msg = _(b"no files or directories specified")
6252 msg = _(b"no files or directories specified")
6256 if p2 != nullid:
6253 if p2 != nullid:
6257 hint = _(
6254 hint = _(
6258 b"uncommitted merge, use --all to discard all changes,"
6255 b"uncommitted merge, use --all to discard all changes,"
6259 b" or 'hg update -C .' to abort the merge"
6256 b" or 'hg update -C .' to abort the merge"
6260 )
6257 )
6261 raise error.Abort(msg, hint=hint)
6258 raise error.Abort(msg, hint=hint)
6262 dirty = any(repo.status())
6259 dirty = any(repo.status())
6263 node = ctx.node()
6260 node = ctx.node()
6264 if node != parent:
6261 if node != parent:
6265 if dirty:
6262 if dirty:
6266 hint = (
6263 hint = (
6267 _(
6264 _(
6268 b"uncommitted changes, use --all to discard all"
6265 b"uncommitted changes, use --all to discard all"
6269 b" changes, or 'hg update %d' to update"
6266 b" changes, or 'hg update %d' to update"
6270 )
6267 )
6271 % ctx.rev()
6268 % ctx.rev()
6272 )
6269 )
6273 else:
6270 else:
6274 hint = (
6271 hint = (
6275 _(
6272 _(
6276 b"use --all to revert all files,"
6273 b"use --all to revert all files,"
6277 b" or 'hg update %d' to update"
6274 b" or 'hg update %d' to update"
6278 )
6275 )
6279 % ctx.rev()
6276 % ctx.rev()
6280 )
6277 )
6281 elif dirty:
6278 elif dirty:
6282 hint = _(b"uncommitted changes, use --all to discard all changes")
6279 hint = _(b"uncommitted changes, use --all to discard all changes")
6283 else:
6280 else:
6284 hint = _(b"use --all to revert all files")
6281 hint = _(b"use --all to revert all files")
6285 raise error.Abort(msg, hint=hint)
6282 raise error.Abort(msg, hint=hint)
6286
6283
6287 return cmdutil.revert(
6284 return cmdutil.revert(
6288 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6285 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6289 )
6286 )
6290
6287
6291
6288
6292 @command(
6289 @command(
6293 b'rollback',
6290 b'rollback',
6294 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6291 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6295 helpcategory=command.CATEGORY_MAINTENANCE,
6292 helpcategory=command.CATEGORY_MAINTENANCE,
6296 )
6293 )
6297 def rollback(ui, repo, **opts):
6294 def rollback(ui, repo, **opts):
6298 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6295 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6299
6296
6300 Please use :hg:`commit --amend` instead of rollback to correct
6297 Please use :hg:`commit --amend` instead of rollback to correct
6301 mistakes in the last commit.
6298 mistakes in the last commit.
6302
6299
6303 This command should be used with care. There is only one level of
6300 This command should be used with care. There is only one level of
6304 rollback, and there is no way to undo a rollback. It will also
6301 rollback, and there is no way to undo a rollback. It will also
6305 restore the dirstate at the time of the last transaction, losing
6302 restore the dirstate at the time of the last transaction, losing
6306 any dirstate changes since that time. This command does not alter
6303 any dirstate changes since that time. This command does not alter
6307 the working directory.
6304 the working directory.
6308
6305
6309 Transactions are used to encapsulate the effects of all commands
6306 Transactions are used to encapsulate the effects of all commands
6310 that create new changesets or propagate existing changesets into a
6307 that create new changesets or propagate existing changesets into a
6311 repository.
6308 repository.
6312
6309
6313 .. container:: verbose
6310 .. container:: verbose
6314
6311
6315 For example, the following commands are transactional, and their
6312 For example, the following commands are transactional, and their
6316 effects can be rolled back:
6313 effects can be rolled back:
6317
6314
6318 - commit
6315 - commit
6319 - import
6316 - import
6320 - pull
6317 - pull
6321 - push (with this repository as the destination)
6318 - push (with this repository as the destination)
6322 - unbundle
6319 - unbundle
6323
6320
6324 To avoid permanent data loss, rollback will refuse to rollback a
6321 To avoid permanent data loss, rollback will refuse to rollback a
6325 commit transaction if it isn't checked out. Use --force to
6322 commit transaction if it isn't checked out. Use --force to
6326 override this protection.
6323 override this protection.
6327
6324
6328 The rollback command can be entirely disabled by setting the
6325 The rollback command can be entirely disabled by setting the
6329 ``ui.rollback`` configuration setting to false. If you're here
6326 ``ui.rollback`` configuration setting to false. If you're here
6330 because you want to use rollback and it's disabled, you can
6327 because you want to use rollback and it's disabled, you can
6331 re-enable the command by setting ``ui.rollback`` to true.
6328 re-enable the command by setting ``ui.rollback`` to true.
6332
6329
6333 This command is not intended for use on public repositories. Once
6330 This command is not intended for use on public repositories. Once
6334 changes are visible for pull by other users, rolling a transaction
6331 changes are visible for pull by other users, rolling a transaction
6335 back locally is ineffective (someone else may already have pulled
6332 back locally is ineffective (someone else may already have pulled
6336 the changes). Furthermore, a race is possible with readers of the
6333 the changes). Furthermore, a race is possible with readers of the
6337 repository; for example an in-progress pull from the repository
6334 repository; for example an in-progress pull from the repository
6338 may fail if a rollback is performed.
6335 may fail if a rollback is performed.
6339
6336
6340 Returns 0 on success, 1 if no rollback data is available.
6337 Returns 0 on success, 1 if no rollback data is available.
6341 """
6338 """
6342 if not ui.configbool(b'ui', b'rollback'):
6339 if not ui.configbool(b'ui', b'rollback'):
6343 raise error.Abort(
6340 raise error.Abort(
6344 _(b'rollback is disabled because it is unsafe'),
6341 _(b'rollback is disabled because it is unsafe'),
6345 hint=b'see `hg help -v rollback` for information',
6342 hint=b'see `hg help -v rollback` for information',
6346 )
6343 )
6347 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6344 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6348
6345
6349
6346
6350 @command(
6347 @command(
6351 b'root',
6348 b'root',
6352 [] + formatteropts,
6349 [] + formatteropts,
6353 intents={INTENT_READONLY},
6350 intents={INTENT_READONLY},
6354 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6351 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6355 )
6352 )
6356 def root(ui, repo, **opts):
6353 def root(ui, repo, **opts):
6357 """print the root (top) of the current working directory
6354 """print the root (top) of the current working directory
6358
6355
6359 Print the root directory of the current repository.
6356 Print the root directory of the current repository.
6360
6357
6361 .. container:: verbose
6358 .. container:: verbose
6362
6359
6363 Template:
6360 Template:
6364
6361
6365 The following keywords are supported in addition to the common template
6362 The following keywords are supported in addition to the common template
6366 keywords and functions. See also :hg:`help templates`.
6363 keywords and functions. See also :hg:`help templates`.
6367
6364
6368 :hgpath: String. Path to the .hg directory.
6365 :hgpath: String. Path to the .hg directory.
6369 :storepath: String. Path to the directory holding versioned data.
6366 :storepath: String. Path to the directory holding versioned data.
6370
6367
6371 Returns 0 on success.
6368 Returns 0 on success.
6372 """
6369 """
6373 opts = pycompat.byteskwargs(opts)
6370 opts = pycompat.byteskwargs(opts)
6374 with ui.formatter(b'root', opts) as fm:
6371 with ui.formatter(b'root', opts) as fm:
6375 fm.startitem()
6372 fm.startitem()
6376 fm.write(b'reporoot', b'%s\n', repo.root)
6373 fm.write(b'reporoot', b'%s\n', repo.root)
6377 fm.data(hgpath=repo.path, storepath=repo.spath)
6374 fm.data(hgpath=repo.path, storepath=repo.spath)
6378
6375
6379
6376
6380 @command(
6377 @command(
6381 b'serve',
6378 b'serve',
6382 [
6379 [
6383 (
6380 (
6384 b'A',
6381 b'A',
6385 b'accesslog',
6382 b'accesslog',
6386 b'',
6383 b'',
6387 _(b'name of access log file to write to'),
6384 _(b'name of access log file to write to'),
6388 _(b'FILE'),
6385 _(b'FILE'),
6389 ),
6386 ),
6390 (b'd', b'daemon', None, _(b'run server in background')),
6387 (b'd', b'daemon', None, _(b'run server in background')),
6391 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6388 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6392 (
6389 (
6393 b'E',
6390 b'E',
6394 b'errorlog',
6391 b'errorlog',
6395 b'',
6392 b'',
6396 _(b'name of error log file to write to'),
6393 _(b'name of error log file to write to'),
6397 _(b'FILE'),
6394 _(b'FILE'),
6398 ),
6395 ),
6399 # use string type, then we can check if something was passed
6396 # use string type, then we can check if something was passed
6400 (
6397 (
6401 b'p',
6398 b'p',
6402 b'port',
6399 b'port',
6403 b'',
6400 b'',
6404 _(b'port to listen on (default: 8000)'),
6401 _(b'port to listen on (default: 8000)'),
6405 _(b'PORT'),
6402 _(b'PORT'),
6406 ),
6403 ),
6407 (
6404 (
6408 b'a',
6405 b'a',
6409 b'address',
6406 b'address',
6410 b'',
6407 b'',
6411 _(b'address to listen on (default: all interfaces)'),
6408 _(b'address to listen on (default: all interfaces)'),
6412 _(b'ADDR'),
6409 _(b'ADDR'),
6413 ),
6410 ),
6414 (
6411 (
6415 b'',
6412 b'',
6416 b'prefix',
6413 b'prefix',
6417 b'',
6414 b'',
6418 _(b'prefix path to serve from (default: server root)'),
6415 _(b'prefix path to serve from (default: server root)'),
6419 _(b'PREFIX'),
6416 _(b'PREFIX'),
6420 ),
6417 ),
6421 (
6418 (
6422 b'n',
6419 b'n',
6423 b'name',
6420 b'name',
6424 b'',
6421 b'',
6425 _(b'name to show in web pages (default: working directory)'),
6422 _(b'name to show in web pages (default: working directory)'),
6426 _(b'NAME'),
6423 _(b'NAME'),
6427 ),
6424 ),
6428 (
6425 (
6429 b'',
6426 b'',
6430 b'web-conf',
6427 b'web-conf',
6431 b'',
6428 b'',
6432 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6429 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6433 _(b'FILE'),
6430 _(b'FILE'),
6434 ),
6431 ),
6435 (
6432 (
6436 b'',
6433 b'',
6437 b'webdir-conf',
6434 b'webdir-conf',
6438 b'',
6435 b'',
6439 _(b'name of the hgweb config file (DEPRECATED)'),
6436 _(b'name of the hgweb config file (DEPRECATED)'),
6440 _(b'FILE'),
6437 _(b'FILE'),
6441 ),
6438 ),
6442 (
6439 (
6443 b'',
6440 b'',
6444 b'pid-file',
6441 b'pid-file',
6445 b'',
6442 b'',
6446 _(b'name of file to write process ID to'),
6443 _(b'name of file to write process ID to'),
6447 _(b'FILE'),
6444 _(b'FILE'),
6448 ),
6445 ),
6449 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6446 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6450 (
6447 (
6451 b'',
6448 b'',
6452 b'cmdserver',
6449 b'cmdserver',
6453 b'',
6450 b'',
6454 _(b'for remote clients (ADVANCED)'),
6451 _(b'for remote clients (ADVANCED)'),
6455 _(b'MODE'),
6452 _(b'MODE'),
6456 ),
6453 ),
6457 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6454 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6458 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6455 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6459 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6456 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6460 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6457 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6461 (b'', b'print-url', None, _(b'start and print only the URL')),
6458 (b'', b'print-url', None, _(b'start and print only the URL')),
6462 ]
6459 ]
6463 + subrepoopts,
6460 + subrepoopts,
6464 _(b'[OPTION]...'),
6461 _(b'[OPTION]...'),
6465 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6462 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6466 helpbasic=True,
6463 helpbasic=True,
6467 optionalrepo=True,
6464 optionalrepo=True,
6468 )
6465 )
6469 def serve(ui, repo, **opts):
6466 def serve(ui, repo, **opts):
6470 """start stand-alone webserver
6467 """start stand-alone webserver
6471
6468
6472 Start a local HTTP repository browser and pull server. You can use
6469 Start a local HTTP repository browser and pull server. You can use
6473 this for ad-hoc sharing and browsing of repositories. It is
6470 this for ad-hoc sharing and browsing of repositories. It is
6474 recommended to use a real web server to serve a repository for
6471 recommended to use a real web server to serve a repository for
6475 longer periods of time.
6472 longer periods of time.
6476
6473
6477 Please note that the server does not implement access control.
6474 Please note that the server does not implement access control.
6478 This means that, by default, anybody can read from the server and
6475 This means that, by default, anybody can read from the server and
6479 nobody can write to it by default. Set the ``web.allow-push``
6476 nobody can write to it by default. Set the ``web.allow-push``
6480 option to ``*`` to allow everybody to push to the server. You
6477 option to ``*`` to allow everybody to push to the server. You
6481 should use a real web server if you need to authenticate users.
6478 should use a real web server if you need to authenticate users.
6482
6479
6483 By default, the server logs accesses to stdout and errors to
6480 By default, the server logs accesses to stdout and errors to
6484 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6481 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6485 files.
6482 files.
6486
6483
6487 To have the server choose a free port number to listen on, specify
6484 To have the server choose a free port number to listen on, specify
6488 a port number of 0; in this case, the server will print the port
6485 a port number of 0; in this case, the server will print the port
6489 number it uses.
6486 number it uses.
6490
6487
6491 Returns 0 on success.
6488 Returns 0 on success.
6492 """
6489 """
6493
6490
6494 opts = pycompat.byteskwargs(opts)
6491 opts = pycompat.byteskwargs(opts)
6495 if opts[b"stdio"] and opts[b"cmdserver"]:
6492 if opts[b"stdio"] and opts[b"cmdserver"]:
6496 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6493 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6497 if opts[b"print_url"] and ui.verbose:
6494 if opts[b"print_url"] and ui.verbose:
6498 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6495 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6499
6496
6500 if opts[b"stdio"]:
6497 if opts[b"stdio"]:
6501 if repo is None:
6498 if repo is None:
6502 raise error.RepoError(
6499 raise error.RepoError(
6503 _(b"there is no Mercurial repository here (.hg not found)")
6500 _(b"there is no Mercurial repository here (.hg not found)")
6504 )
6501 )
6505 s = wireprotoserver.sshserver(ui, repo)
6502 s = wireprotoserver.sshserver(ui, repo)
6506 s.serve_forever()
6503 s.serve_forever()
6507
6504
6508 service = server.createservice(ui, repo, opts)
6505 service = server.createservice(ui, repo, opts)
6509 return server.runservice(opts, initfn=service.init, runfn=service.run)
6506 return server.runservice(opts, initfn=service.init, runfn=service.run)
6510
6507
6511
6508
6512 @command(
6509 @command(
6513 b'shelve',
6510 b'shelve',
6514 [
6511 [
6515 (
6512 (
6516 b'A',
6513 b'A',
6517 b'addremove',
6514 b'addremove',
6518 None,
6515 None,
6519 _(b'mark new/missing files as added/removed before shelving'),
6516 _(b'mark new/missing files as added/removed before shelving'),
6520 ),
6517 ),
6521 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6518 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6522 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6519 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6523 (
6520 (
6524 b'',
6521 b'',
6525 b'date',
6522 b'date',
6526 b'',
6523 b'',
6527 _(b'shelve with the specified commit date'),
6524 _(b'shelve with the specified commit date'),
6528 _(b'DATE'),
6525 _(b'DATE'),
6529 ),
6526 ),
6530 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6527 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6531 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6528 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6532 (
6529 (
6533 b'k',
6530 b'k',
6534 b'keep',
6531 b'keep',
6535 False,
6532 False,
6536 _(b'shelve, but keep changes in the working directory'),
6533 _(b'shelve, but keep changes in the working directory'),
6537 ),
6534 ),
6538 (b'l', b'list', None, _(b'list current shelves')),
6535 (b'l', b'list', None, _(b'list current shelves')),
6539 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6536 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6540 (
6537 (
6541 b'n',
6538 b'n',
6542 b'name',
6539 b'name',
6543 b'',
6540 b'',
6544 _(b'use the given name for the shelved commit'),
6541 _(b'use the given name for the shelved commit'),
6545 _(b'NAME'),
6542 _(b'NAME'),
6546 ),
6543 ),
6547 (
6544 (
6548 b'p',
6545 b'p',
6549 b'patch',
6546 b'patch',
6550 None,
6547 None,
6551 _(
6548 _(
6552 b'output patches for changes (provide the names of the shelved '
6549 b'output patches for changes (provide the names of the shelved '
6553 b'changes as positional arguments)'
6550 b'changes as positional arguments)'
6554 ),
6551 ),
6555 ),
6552 ),
6556 (b'i', b'interactive', None, _(b'interactive mode')),
6553 (b'i', b'interactive', None, _(b'interactive mode')),
6557 (
6554 (
6558 b'',
6555 b'',
6559 b'stat',
6556 b'stat',
6560 None,
6557 None,
6561 _(
6558 _(
6562 b'output diffstat-style summary of changes (provide the names of '
6559 b'output diffstat-style summary of changes (provide the names of '
6563 b'the shelved changes as positional arguments)'
6560 b'the shelved changes as positional arguments)'
6564 ),
6561 ),
6565 ),
6562 ),
6566 ]
6563 ]
6567 + cmdutil.walkopts,
6564 + cmdutil.walkopts,
6568 _(b'hg shelve [OPTION]... [FILE]...'),
6565 _(b'hg shelve [OPTION]... [FILE]...'),
6569 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6566 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6570 )
6567 )
6571 def shelve(ui, repo, *pats, **opts):
6568 def shelve(ui, repo, *pats, **opts):
6572 '''save and set aside changes from the working directory
6569 '''save and set aside changes from the working directory
6573
6570
6574 Shelving takes files that "hg status" reports as not clean, saves
6571 Shelving takes files that "hg status" reports as not clean, saves
6575 the modifications to a bundle (a shelved change), and reverts the
6572 the modifications to a bundle (a shelved change), and reverts the
6576 files so that their state in the working directory becomes clean.
6573 files so that their state in the working directory becomes clean.
6577
6574
6578 To restore these changes to the working directory, using "hg
6575 To restore these changes to the working directory, using "hg
6579 unshelve"; this will work even if you switch to a different
6576 unshelve"; this will work even if you switch to a different
6580 commit.
6577 commit.
6581
6578
6582 When no files are specified, "hg shelve" saves all not-clean
6579 When no files are specified, "hg shelve" saves all not-clean
6583 files. If specific files or directories are named, only changes to
6580 files. If specific files or directories are named, only changes to
6584 those files are shelved.
6581 those files are shelved.
6585
6582
6586 In bare shelve (when no files are specified, without interactive,
6583 In bare shelve (when no files are specified, without interactive,
6587 include and exclude option), shelving remembers information if the
6584 include and exclude option), shelving remembers information if the
6588 working directory was on newly created branch, in other words working
6585 working directory was on newly created branch, in other words working
6589 directory was on different branch than its first parent. In this
6586 directory was on different branch than its first parent. In this
6590 situation unshelving restores branch information to the working directory.
6587 situation unshelving restores branch information to the working directory.
6591
6588
6592 Each shelved change has a name that makes it easier to find later.
6589 Each shelved change has a name that makes it easier to find later.
6593 The name of a shelved change defaults to being based on the active
6590 The name of a shelved change defaults to being based on the active
6594 bookmark, or if there is no active bookmark, the current named
6591 bookmark, or if there is no active bookmark, the current named
6595 branch. To specify a different name, use ``--name``.
6592 branch. To specify a different name, use ``--name``.
6596
6593
6597 To see a list of existing shelved changes, use the ``--list``
6594 To see a list of existing shelved changes, use the ``--list``
6598 option. For each shelved change, this will print its name, age,
6595 option. For each shelved change, this will print its name, age,
6599 and description; use ``--patch`` or ``--stat`` for more details.
6596 and description; use ``--patch`` or ``--stat`` for more details.
6600
6597
6601 To delete specific shelved changes, use ``--delete``. To delete
6598 To delete specific shelved changes, use ``--delete``. To delete
6602 all shelved changes, use ``--cleanup``.
6599 all shelved changes, use ``--cleanup``.
6603 '''
6600 '''
6604 opts = pycompat.byteskwargs(opts)
6601 opts = pycompat.byteskwargs(opts)
6605 allowables = [
6602 allowables = [
6606 (b'addremove', {b'create'}), # 'create' is pseudo action
6603 (b'addremove', {b'create'}), # 'create' is pseudo action
6607 (b'unknown', {b'create'}),
6604 (b'unknown', {b'create'}),
6608 (b'cleanup', {b'cleanup'}),
6605 (b'cleanup', {b'cleanup'}),
6609 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6606 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6610 (b'delete', {b'delete'}),
6607 (b'delete', {b'delete'}),
6611 (b'edit', {b'create'}),
6608 (b'edit', {b'create'}),
6612 (b'keep', {b'create'}),
6609 (b'keep', {b'create'}),
6613 (b'list', {b'list'}),
6610 (b'list', {b'list'}),
6614 (b'message', {b'create'}),
6611 (b'message', {b'create'}),
6615 (b'name', {b'create'}),
6612 (b'name', {b'create'}),
6616 (b'patch', {b'patch', b'list'}),
6613 (b'patch', {b'patch', b'list'}),
6617 (b'stat', {b'stat', b'list'}),
6614 (b'stat', {b'stat', b'list'}),
6618 ]
6615 ]
6619
6616
6620 def checkopt(opt):
6617 def checkopt(opt):
6621 if opts.get(opt):
6618 if opts.get(opt):
6622 for i, allowable in allowables:
6619 for i, allowable in allowables:
6623 if opts[i] and opt not in allowable:
6620 if opts[i] and opt not in allowable:
6624 raise error.Abort(
6621 raise error.Abort(
6625 _(
6622 _(
6626 b"options '--%s' and '--%s' may not be "
6623 b"options '--%s' and '--%s' may not be "
6627 b"used together"
6624 b"used together"
6628 )
6625 )
6629 % (opt, i)
6626 % (opt, i)
6630 )
6627 )
6631 return True
6628 return True
6632
6629
6633 if checkopt(b'cleanup'):
6630 if checkopt(b'cleanup'):
6634 if pats:
6631 if pats:
6635 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6632 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6636 return shelvemod.cleanupcmd(ui, repo)
6633 return shelvemod.cleanupcmd(ui, repo)
6637 elif checkopt(b'delete'):
6634 elif checkopt(b'delete'):
6638 return shelvemod.deletecmd(ui, repo, pats)
6635 return shelvemod.deletecmd(ui, repo, pats)
6639 elif checkopt(b'list'):
6636 elif checkopt(b'list'):
6640 return shelvemod.listcmd(ui, repo, pats, opts)
6637 return shelvemod.listcmd(ui, repo, pats, opts)
6641 elif checkopt(b'patch') or checkopt(b'stat'):
6638 elif checkopt(b'patch') or checkopt(b'stat'):
6642 return shelvemod.patchcmds(ui, repo, pats, opts)
6639 return shelvemod.patchcmds(ui, repo, pats, opts)
6643 else:
6640 else:
6644 return shelvemod.createcmd(ui, repo, pats, opts)
6641 return shelvemod.createcmd(ui, repo, pats, opts)
6645
6642
6646
6643
6647 _NOTTERSE = b'nothing'
6644 _NOTTERSE = b'nothing'
6648
6645
6649
6646
6650 @command(
6647 @command(
6651 b'status|st',
6648 b'status|st',
6652 [
6649 [
6653 (b'A', b'all', None, _(b'show status of all files')),
6650 (b'A', b'all', None, _(b'show status of all files')),
6654 (b'm', b'modified', None, _(b'show only modified files')),
6651 (b'm', b'modified', None, _(b'show only modified files')),
6655 (b'a', b'added', None, _(b'show only added files')),
6652 (b'a', b'added', None, _(b'show only added files')),
6656 (b'r', b'removed', None, _(b'show only removed files')),
6653 (b'r', b'removed', None, _(b'show only removed files')),
6657 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6654 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6658 (b'c', b'clean', None, _(b'show only files without changes')),
6655 (b'c', b'clean', None, _(b'show only files without changes')),
6659 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6656 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6660 (b'i', b'ignored', None, _(b'show only ignored files')),
6657 (b'i', b'ignored', None, _(b'show only ignored files')),
6661 (b'n', b'no-status', None, _(b'hide status prefix')),
6658 (b'n', b'no-status', None, _(b'hide status prefix')),
6662 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6659 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6663 (b'C', b'copies', None, _(b'show source of copied files')),
6660 (b'C', b'copies', None, _(b'show source of copied files')),
6664 (
6661 (
6665 b'0',
6662 b'0',
6666 b'print0',
6663 b'print0',
6667 None,
6664 None,
6668 _(b'end filenames with NUL, for use with xargs'),
6665 _(b'end filenames with NUL, for use with xargs'),
6669 ),
6666 ),
6670 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6667 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6671 (
6668 (
6672 b'',
6669 b'',
6673 b'change',
6670 b'change',
6674 b'',
6671 b'',
6675 _(b'list the changed files of a revision'),
6672 _(b'list the changed files of a revision'),
6676 _(b'REV'),
6673 _(b'REV'),
6677 ),
6674 ),
6678 ]
6675 ]
6679 + walkopts
6676 + walkopts
6680 + subrepoopts
6677 + subrepoopts
6681 + formatteropts,
6678 + formatteropts,
6682 _(b'[OPTION]... [FILE]...'),
6679 _(b'[OPTION]... [FILE]...'),
6683 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6680 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6684 helpbasic=True,
6681 helpbasic=True,
6685 inferrepo=True,
6682 inferrepo=True,
6686 intents={INTENT_READONLY},
6683 intents={INTENT_READONLY},
6687 )
6684 )
6688 def status(ui, repo, *pats, **opts):
6685 def status(ui, repo, *pats, **opts):
6689 """show changed files in the working directory
6686 """show changed files in the working directory
6690
6687
6691 Show status of files in the repository. If names are given, only
6688 Show status of files in the repository. If names are given, only
6692 files that match are shown. Files that are clean or ignored or
6689 files that match are shown. Files that are clean or ignored or
6693 the source of a copy/move operation, are not listed unless
6690 the source of a copy/move operation, are not listed unless
6694 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6691 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6695 Unless options described with "show only ..." are given, the
6692 Unless options described with "show only ..." are given, the
6696 options -mardu are used.
6693 options -mardu are used.
6697
6694
6698 Option -q/--quiet hides untracked (unknown and ignored) files
6695 Option -q/--quiet hides untracked (unknown and ignored) files
6699 unless explicitly requested with -u/--unknown or -i/--ignored.
6696 unless explicitly requested with -u/--unknown or -i/--ignored.
6700
6697
6701 .. note::
6698 .. note::
6702
6699
6703 :hg:`status` may appear to disagree with diff if permissions have
6700 :hg:`status` may appear to disagree with diff if permissions have
6704 changed or a merge has occurred. The standard diff format does
6701 changed or a merge has occurred. The standard diff format does
6705 not report permission changes and diff only reports changes
6702 not report permission changes and diff only reports changes
6706 relative to one merge parent.
6703 relative to one merge parent.
6707
6704
6708 If one revision is given, it is used as the base revision.
6705 If one revision is given, it is used as the base revision.
6709 If two revisions are given, the differences between them are
6706 If two revisions are given, the differences between them are
6710 shown. The --change option can also be used as a shortcut to list
6707 shown. The --change option can also be used as a shortcut to list
6711 the changed files of a revision from its first parent.
6708 the changed files of a revision from its first parent.
6712
6709
6713 The codes used to show the status of files are::
6710 The codes used to show the status of files are::
6714
6711
6715 M = modified
6712 M = modified
6716 A = added
6713 A = added
6717 R = removed
6714 R = removed
6718 C = clean
6715 C = clean
6719 ! = missing (deleted by non-hg command, but still tracked)
6716 ! = missing (deleted by non-hg command, but still tracked)
6720 ? = not tracked
6717 ? = not tracked
6721 I = ignored
6718 I = ignored
6722 = origin of the previous file (with --copies)
6719 = origin of the previous file (with --copies)
6723
6720
6724 .. container:: verbose
6721 .. container:: verbose
6725
6722
6726 The -t/--terse option abbreviates the output by showing only the directory
6723 The -t/--terse option abbreviates the output by showing only the directory
6727 name if all the files in it share the same status. The option takes an
6724 name if all the files in it share the same status. The option takes an
6728 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6725 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6729 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6726 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6730 for 'ignored' and 'c' for clean.
6727 for 'ignored' and 'c' for clean.
6731
6728
6732 It abbreviates only those statuses which are passed. Note that clean and
6729 It abbreviates only those statuses which are passed. Note that clean and
6733 ignored files are not displayed with '--terse ic' unless the -c/--clean
6730 ignored files are not displayed with '--terse ic' unless the -c/--clean
6734 and -i/--ignored options are also used.
6731 and -i/--ignored options are also used.
6735
6732
6736 The -v/--verbose option shows information when the repository is in an
6733 The -v/--verbose option shows information when the repository is in an
6737 unfinished merge, shelve, rebase state etc. You can have this behavior
6734 unfinished merge, shelve, rebase state etc. You can have this behavior
6738 turned on by default by enabling the ``commands.status.verbose`` option.
6735 turned on by default by enabling the ``commands.status.verbose`` option.
6739
6736
6740 You can skip displaying some of these states by setting
6737 You can skip displaying some of these states by setting
6741 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6738 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6742 'histedit', 'merge', 'rebase', or 'unshelve'.
6739 'histedit', 'merge', 'rebase', or 'unshelve'.
6743
6740
6744 Template:
6741 Template:
6745
6742
6746 The following keywords are supported in addition to the common template
6743 The following keywords are supported in addition to the common template
6747 keywords and functions. See also :hg:`help templates`.
6744 keywords and functions. See also :hg:`help templates`.
6748
6745
6749 :path: String. Repository-absolute path of the file.
6746 :path: String. Repository-absolute path of the file.
6750 :source: String. Repository-absolute path of the file originated from.
6747 :source: String. Repository-absolute path of the file originated from.
6751 Available if ``--copies`` is specified.
6748 Available if ``--copies`` is specified.
6752 :status: String. Character denoting file's status.
6749 :status: String. Character denoting file's status.
6753
6750
6754 Examples:
6751 Examples:
6755
6752
6756 - show changes in the working directory relative to a
6753 - show changes in the working directory relative to a
6757 changeset::
6754 changeset::
6758
6755
6759 hg status --rev 9353
6756 hg status --rev 9353
6760
6757
6761 - show changes in the working directory relative to the
6758 - show changes in the working directory relative to the
6762 current directory (see :hg:`help patterns` for more information)::
6759 current directory (see :hg:`help patterns` for more information)::
6763
6760
6764 hg status re:
6761 hg status re:
6765
6762
6766 - show all changes including copies in an existing changeset::
6763 - show all changes including copies in an existing changeset::
6767
6764
6768 hg status --copies --change 9353
6765 hg status --copies --change 9353
6769
6766
6770 - get a NUL separated list of added files, suitable for xargs::
6767 - get a NUL separated list of added files, suitable for xargs::
6771
6768
6772 hg status -an0
6769 hg status -an0
6773
6770
6774 - show more information about the repository status, abbreviating
6771 - show more information about the repository status, abbreviating
6775 added, removed, modified, deleted, and untracked paths::
6772 added, removed, modified, deleted, and untracked paths::
6776
6773
6777 hg status -v -t mardu
6774 hg status -v -t mardu
6778
6775
6779 Returns 0 on success.
6776 Returns 0 on success.
6780
6777
6781 """
6778 """
6782
6779
6783 opts = pycompat.byteskwargs(opts)
6780 opts = pycompat.byteskwargs(opts)
6784 revs = opts.get(b'rev')
6781 revs = opts.get(b'rev')
6785 change = opts.get(b'change')
6782 change = opts.get(b'change')
6786 terse = opts.get(b'terse')
6783 terse = opts.get(b'terse')
6787 if terse is _NOTTERSE:
6784 if terse is _NOTTERSE:
6788 if revs:
6785 if revs:
6789 terse = b''
6786 terse = b''
6790 else:
6787 else:
6791 terse = ui.config(b'commands', b'status.terse')
6788 terse = ui.config(b'commands', b'status.terse')
6792
6789
6793 if revs and change:
6790 if revs and change:
6794 msg = _(b'cannot specify --rev and --change at the same time')
6791 msg = _(b'cannot specify --rev and --change at the same time')
6795 raise error.Abort(msg)
6792 raise error.Abort(msg)
6796 elif revs and terse:
6793 elif revs and terse:
6797 msg = _(b'cannot use --terse with --rev')
6794 msg = _(b'cannot use --terse with --rev')
6798 raise error.Abort(msg)
6795 raise error.Abort(msg)
6799 elif change:
6796 elif change:
6800 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6797 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6801 ctx2 = scmutil.revsingle(repo, change, None)
6798 ctx2 = scmutil.revsingle(repo, change, None)
6802 ctx1 = ctx2.p1()
6799 ctx1 = ctx2.p1()
6803 else:
6800 else:
6804 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6801 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6805 ctx1, ctx2 = scmutil.revpair(repo, revs)
6802 ctx1, ctx2 = scmutil.revpair(repo, revs)
6806
6803
6807 forcerelativevalue = None
6804 forcerelativevalue = None
6808 if ui.hasconfig(b'commands', b'status.relative'):
6805 if ui.hasconfig(b'commands', b'status.relative'):
6809 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6806 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6810 uipathfn = scmutil.getuipathfn(
6807 uipathfn = scmutil.getuipathfn(
6811 repo,
6808 repo,
6812 legacyrelativevalue=bool(pats),
6809 legacyrelativevalue=bool(pats),
6813 forcerelativevalue=forcerelativevalue,
6810 forcerelativevalue=forcerelativevalue,
6814 )
6811 )
6815
6812
6816 if opts.get(b'print0'):
6813 if opts.get(b'print0'):
6817 end = b'\0'
6814 end = b'\0'
6818 else:
6815 else:
6819 end = b'\n'
6816 end = b'\n'
6820 states = b'modified added removed deleted unknown ignored clean'.split()
6817 states = b'modified added removed deleted unknown ignored clean'.split()
6821 show = [k for k in states if opts.get(k)]
6818 show = [k for k in states if opts.get(k)]
6822 if opts.get(b'all'):
6819 if opts.get(b'all'):
6823 show += ui.quiet and (states[:4] + [b'clean']) or states
6820 show += ui.quiet and (states[:4] + [b'clean']) or states
6824
6821
6825 if not show:
6822 if not show:
6826 if ui.quiet:
6823 if ui.quiet:
6827 show = states[:4]
6824 show = states[:4]
6828 else:
6825 else:
6829 show = states[:5]
6826 show = states[:5]
6830
6827
6831 m = scmutil.match(ctx2, pats, opts)
6828 m = scmutil.match(ctx2, pats, opts)
6832 if terse:
6829 if terse:
6833 # we need to compute clean and unknown to terse
6830 # we need to compute clean and unknown to terse
6834 stat = repo.status(
6831 stat = repo.status(
6835 ctx1.node(),
6832 ctx1.node(),
6836 ctx2.node(),
6833 ctx2.node(),
6837 m,
6834 m,
6838 b'ignored' in show or b'i' in terse,
6835 b'ignored' in show or b'i' in terse,
6839 clean=True,
6836 clean=True,
6840 unknown=True,
6837 unknown=True,
6841 listsubrepos=opts.get(b'subrepos'),
6838 listsubrepos=opts.get(b'subrepos'),
6842 )
6839 )
6843
6840
6844 stat = cmdutil.tersedir(stat, terse)
6841 stat = cmdutil.tersedir(stat, terse)
6845 else:
6842 else:
6846 stat = repo.status(
6843 stat = repo.status(
6847 ctx1.node(),
6844 ctx1.node(),
6848 ctx2.node(),
6845 ctx2.node(),
6849 m,
6846 m,
6850 b'ignored' in show,
6847 b'ignored' in show,
6851 b'clean' in show,
6848 b'clean' in show,
6852 b'unknown' in show,
6849 b'unknown' in show,
6853 opts.get(b'subrepos'),
6850 opts.get(b'subrepos'),
6854 )
6851 )
6855
6852
6856 changestates = zip(
6853 changestates = zip(
6857 states,
6854 states,
6858 pycompat.iterbytestr(b'MAR!?IC'),
6855 pycompat.iterbytestr(b'MAR!?IC'),
6859 [getattr(stat, s.decode('utf8')) for s in states],
6856 [getattr(stat, s.decode('utf8')) for s in states],
6860 )
6857 )
6861
6858
6862 copy = {}
6859 copy = {}
6863 if (
6860 if (
6864 opts.get(b'all')
6861 opts.get(b'all')
6865 or opts.get(b'copies')
6862 or opts.get(b'copies')
6866 or ui.configbool(b'ui', b'statuscopies')
6863 or ui.configbool(b'ui', b'statuscopies')
6867 ) and not opts.get(b'no_status'):
6864 ) and not opts.get(b'no_status'):
6868 copy = copies.pathcopies(ctx1, ctx2, m)
6865 copy = copies.pathcopies(ctx1, ctx2, m)
6869
6866
6870 ui.pager(b'status')
6867 ui.pager(b'status')
6871 fm = ui.formatter(b'status', opts)
6868 fm = ui.formatter(b'status', opts)
6872 fmt = b'%s' + end
6869 fmt = b'%s' + end
6873 showchar = not opts.get(b'no_status')
6870 showchar = not opts.get(b'no_status')
6874
6871
6875 for state, char, files in changestates:
6872 for state, char, files in changestates:
6876 if state in show:
6873 if state in show:
6877 label = b'status.' + state
6874 label = b'status.' + state
6878 for f in files:
6875 for f in files:
6879 fm.startitem()
6876 fm.startitem()
6880 fm.context(ctx=ctx2)
6877 fm.context(ctx=ctx2)
6881 fm.data(path=f)
6878 fm.data(path=f)
6882 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6879 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6883 fm.plain(fmt % uipathfn(f), label=label)
6880 fm.plain(fmt % uipathfn(f), label=label)
6884 if f in copy:
6881 if f in copy:
6885 fm.data(source=copy[f])
6882 fm.data(source=copy[f])
6886 fm.plain(
6883 fm.plain(
6887 (b' %s' + end) % uipathfn(copy[f]),
6884 (b' %s' + end) % uipathfn(copy[f]),
6888 label=b'status.copied',
6885 label=b'status.copied',
6889 )
6886 )
6890
6887
6891 if (
6888 if (
6892 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6889 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6893 ) and not ui.plain():
6890 ) and not ui.plain():
6894 cmdutil.morestatus(repo, fm)
6891 cmdutil.morestatus(repo, fm)
6895 fm.end()
6892 fm.end()
6896
6893
6897
6894
6898 @command(
6895 @command(
6899 b'summary|sum',
6896 b'summary|sum',
6900 [(b'', b'remote', None, _(b'check for push and pull'))],
6897 [(b'', b'remote', None, _(b'check for push and pull'))],
6901 b'[--remote]',
6898 b'[--remote]',
6902 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6899 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6903 helpbasic=True,
6900 helpbasic=True,
6904 intents={INTENT_READONLY},
6901 intents={INTENT_READONLY},
6905 )
6902 )
6906 def summary(ui, repo, **opts):
6903 def summary(ui, repo, **opts):
6907 """summarize working directory state
6904 """summarize working directory state
6908
6905
6909 This generates a brief summary of the working directory state,
6906 This generates a brief summary of the working directory state,
6910 including parents, branch, commit status, phase and available updates.
6907 including parents, branch, commit status, phase and available updates.
6911
6908
6912 With the --remote option, this will check the default paths for
6909 With the --remote option, this will check the default paths for
6913 incoming and outgoing changes. This can be time-consuming.
6910 incoming and outgoing changes. This can be time-consuming.
6914
6911
6915 Returns 0 on success.
6912 Returns 0 on success.
6916 """
6913 """
6917
6914
6918 opts = pycompat.byteskwargs(opts)
6915 opts = pycompat.byteskwargs(opts)
6919 ui.pager(b'summary')
6916 ui.pager(b'summary')
6920 ctx = repo[None]
6917 ctx = repo[None]
6921 parents = ctx.parents()
6918 parents = ctx.parents()
6922 pnode = parents[0].node()
6919 pnode = parents[0].node()
6923 marks = []
6920 marks = []
6924
6921
6925 try:
6922 try:
6926 ms = mergemod.mergestate.read(repo)
6923 ms = mergemod.mergestate.read(repo)
6927 except error.UnsupportedMergeRecords as e:
6924 except error.UnsupportedMergeRecords as e:
6928 s = b' '.join(e.recordtypes)
6925 s = b' '.join(e.recordtypes)
6929 ui.warn(
6926 ui.warn(
6930 _(b'warning: merge state has unsupported record types: %s\n') % s
6927 _(b'warning: merge state has unsupported record types: %s\n') % s
6931 )
6928 )
6932 unresolved = []
6929 unresolved = []
6933 else:
6930 else:
6934 unresolved = list(ms.unresolved())
6931 unresolved = list(ms.unresolved())
6935
6932
6936 for p in parents:
6933 for p in parents:
6937 # label with log.changeset (instead of log.parent) since this
6934 # label with log.changeset (instead of log.parent) since this
6938 # shows a working directory parent *changeset*:
6935 # shows a working directory parent *changeset*:
6939 # i18n: column positioning for "hg summary"
6936 # i18n: column positioning for "hg summary"
6940 ui.write(
6937 ui.write(
6941 _(b'parent: %d:%s ') % (p.rev(), p),
6938 _(b'parent: %d:%s ') % (p.rev(), p),
6942 label=logcmdutil.changesetlabels(p),
6939 label=logcmdutil.changesetlabels(p),
6943 )
6940 )
6944 ui.write(b' '.join(p.tags()), label=b'log.tag')
6941 ui.write(b' '.join(p.tags()), label=b'log.tag')
6945 if p.bookmarks():
6942 if p.bookmarks():
6946 marks.extend(p.bookmarks())
6943 marks.extend(p.bookmarks())
6947 if p.rev() == -1:
6944 if p.rev() == -1:
6948 if not len(repo):
6945 if not len(repo):
6949 ui.write(_(b' (empty repository)'))
6946 ui.write(_(b' (empty repository)'))
6950 else:
6947 else:
6951 ui.write(_(b' (no revision checked out)'))
6948 ui.write(_(b' (no revision checked out)'))
6952 if p.obsolete():
6949 if p.obsolete():
6953 ui.write(_(b' (obsolete)'))
6950 ui.write(_(b' (obsolete)'))
6954 if p.isunstable():
6951 if p.isunstable():
6955 instabilities = (
6952 instabilities = (
6956 ui.label(instability, b'trouble.%s' % instability)
6953 ui.label(instability, b'trouble.%s' % instability)
6957 for instability in p.instabilities()
6954 for instability in p.instabilities()
6958 )
6955 )
6959 ui.write(b' (' + b', '.join(instabilities) + b')')
6956 ui.write(b' (' + b', '.join(instabilities) + b')')
6960 ui.write(b'\n')
6957 ui.write(b'\n')
6961 if p.description():
6958 if p.description():
6962 ui.status(
6959 ui.status(
6963 b' ' + p.description().splitlines()[0].strip() + b'\n',
6960 b' ' + p.description().splitlines()[0].strip() + b'\n',
6964 label=b'log.summary',
6961 label=b'log.summary',
6965 )
6962 )
6966
6963
6967 branch = ctx.branch()
6964 branch = ctx.branch()
6968 bheads = repo.branchheads(branch)
6965 bheads = repo.branchheads(branch)
6969 # i18n: column positioning for "hg summary"
6966 # i18n: column positioning for "hg summary"
6970 m = _(b'branch: %s\n') % branch
6967 m = _(b'branch: %s\n') % branch
6971 if branch != b'default':
6968 if branch != b'default':
6972 ui.write(m, label=b'log.branch')
6969 ui.write(m, label=b'log.branch')
6973 else:
6970 else:
6974 ui.status(m, label=b'log.branch')
6971 ui.status(m, label=b'log.branch')
6975
6972
6976 if marks:
6973 if marks:
6977 active = repo._activebookmark
6974 active = repo._activebookmark
6978 # i18n: column positioning for "hg summary"
6975 # i18n: column positioning for "hg summary"
6979 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6976 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6980 if active is not None:
6977 if active is not None:
6981 if active in marks:
6978 if active in marks:
6982 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6979 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6983 marks.remove(active)
6980 marks.remove(active)
6984 else:
6981 else:
6985 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6982 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6986 for m in marks:
6983 for m in marks:
6987 ui.write(b' ' + m, label=b'log.bookmark')
6984 ui.write(b' ' + m, label=b'log.bookmark')
6988 ui.write(b'\n', label=b'log.bookmark')
6985 ui.write(b'\n', label=b'log.bookmark')
6989
6986
6990 status = repo.status(unknown=True)
6987 status = repo.status(unknown=True)
6991
6988
6992 c = repo.dirstate.copies()
6989 c = repo.dirstate.copies()
6993 copied, renamed = [], []
6990 copied, renamed = [], []
6994 for d, s in pycompat.iteritems(c):
6991 for d, s in pycompat.iteritems(c):
6995 if s in status.removed:
6992 if s in status.removed:
6996 status.removed.remove(s)
6993 status.removed.remove(s)
6997 renamed.append(d)
6994 renamed.append(d)
6998 else:
6995 else:
6999 copied.append(d)
6996 copied.append(d)
7000 if d in status.added:
6997 if d in status.added:
7001 status.added.remove(d)
6998 status.added.remove(d)
7002
6999
7003 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7000 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7004
7001
7005 labels = [
7002 labels = [
7006 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7003 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7007 (ui.label(_(b'%d added'), b'status.added'), status.added),
7004 (ui.label(_(b'%d added'), b'status.added'), status.added),
7008 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7005 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7009 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7006 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7010 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7007 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7011 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7008 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7012 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7009 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7013 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7010 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7014 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7011 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7015 ]
7012 ]
7016 t = []
7013 t = []
7017 for l, s in labels:
7014 for l, s in labels:
7018 if s:
7015 if s:
7019 t.append(l % len(s))
7016 t.append(l % len(s))
7020
7017
7021 t = b', '.join(t)
7018 t = b', '.join(t)
7022 cleanworkdir = False
7019 cleanworkdir = False
7023
7020
7024 if repo.vfs.exists(b'graftstate'):
7021 if repo.vfs.exists(b'graftstate'):
7025 t += _(b' (graft in progress)')
7022 t += _(b' (graft in progress)')
7026 if repo.vfs.exists(b'updatestate'):
7023 if repo.vfs.exists(b'updatestate'):
7027 t += _(b' (interrupted update)')
7024 t += _(b' (interrupted update)')
7028 elif len(parents) > 1:
7025 elif len(parents) > 1:
7029 t += _(b' (merge)')
7026 t += _(b' (merge)')
7030 elif branch != parents[0].branch():
7027 elif branch != parents[0].branch():
7031 t += _(b' (new branch)')
7028 t += _(b' (new branch)')
7032 elif parents[0].closesbranch() and pnode in repo.branchheads(
7029 elif parents[0].closesbranch() and pnode in repo.branchheads(
7033 branch, closed=True
7030 branch, closed=True
7034 ):
7031 ):
7035 t += _(b' (head closed)')
7032 t += _(b' (head closed)')
7036 elif not (
7033 elif not (
7037 status.modified
7034 status.modified
7038 or status.added
7035 or status.added
7039 or status.removed
7036 or status.removed
7040 or renamed
7037 or renamed
7041 or copied
7038 or copied
7042 or subs
7039 or subs
7043 ):
7040 ):
7044 t += _(b' (clean)')
7041 t += _(b' (clean)')
7045 cleanworkdir = True
7042 cleanworkdir = True
7046 elif pnode not in bheads:
7043 elif pnode not in bheads:
7047 t += _(b' (new branch head)')
7044 t += _(b' (new branch head)')
7048
7045
7049 if parents:
7046 if parents:
7050 pendingphase = max(p.phase() for p in parents)
7047 pendingphase = max(p.phase() for p in parents)
7051 else:
7048 else:
7052 pendingphase = phases.public
7049 pendingphase = phases.public
7053
7050
7054 if pendingphase > phases.newcommitphase(ui):
7051 if pendingphase > phases.newcommitphase(ui):
7055 t += b' (%s)' % phases.phasenames[pendingphase]
7052 t += b' (%s)' % phases.phasenames[pendingphase]
7056
7053
7057 if cleanworkdir:
7054 if cleanworkdir:
7058 # i18n: column positioning for "hg summary"
7055 # i18n: column positioning for "hg summary"
7059 ui.status(_(b'commit: %s\n') % t.strip())
7056 ui.status(_(b'commit: %s\n') % t.strip())
7060 else:
7057 else:
7061 # i18n: column positioning for "hg summary"
7058 # i18n: column positioning for "hg summary"
7062 ui.write(_(b'commit: %s\n') % t.strip())
7059 ui.write(_(b'commit: %s\n') % t.strip())
7063
7060
7064 # all ancestors of branch heads - all ancestors of parent = new csets
7061 # all ancestors of branch heads - all ancestors of parent = new csets
7065 new = len(
7062 new = len(
7066 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7063 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7067 )
7064 )
7068
7065
7069 if new == 0:
7066 if new == 0:
7070 # i18n: column positioning for "hg summary"
7067 # i18n: column positioning for "hg summary"
7071 ui.status(_(b'update: (current)\n'))
7068 ui.status(_(b'update: (current)\n'))
7072 elif pnode not in bheads:
7069 elif pnode not in bheads:
7073 # i18n: column positioning for "hg summary"
7070 # i18n: column positioning for "hg summary"
7074 ui.write(_(b'update: %d new changesets (update)\n') % new)
7071 ui.write(_(b'update: %d new changesets (update)\n') % new)
7075 else:
7072 else:
7076 # i18n: column positioning for "hg summary"
7073 # i18n: column positioning for "hg summary"
7077 ui.write(
7074 ui.write(
7078 _(b'update: %d new changesets, %d branch heads (merge)\n')
7075 _(b'update: %d new changesets, %d branch heads (merge)\n')
7079 % (new, len(bheads))
7076 % (new, len(bheads))
7080 )
7077 )
7081
7078
7082 t = []
7079 t = []
7083 draft = len(repo.revs(b'draft()'))
7080 draft = len(repo.revs(b'draft()'))
7084 if draft:
7081 if draft:
7085 t.append(_(b'%d draft') % draft)
7082 t.append(_(b'%d draft') % draft)
7086 secret = len(repo.revs(b'secret()'))
7083 secret = len(repo.revs(b'secret()'))
7087 if secret:
7084 if secret:
7088 t.append(_(b'%d secret') % secret)
7085 t.append(_(b'%d secret') % secret)
7089
7086
7090 if draft or secret:
7087 if draft or secret:
7091 ui.status(_(b'phases: %s\n') % b', '.join(t))
7088 ui.status(_(b'phases: %s\n') % b', '.join(t))
7092
7089
7093 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7090 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7094 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7091 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7095 numtrouble = len(repo.revs(trouble + b"()"))
7092 numtrouble = len(repo.revs(trouble + b"()"))
7096 # We write all the possibilities to ease translation
7093 # We write all the possibilities to ease translation
7097 troublemsg = {
7094 troublemsg = {
7098 b"orphan": _(b"orphan: %d changesets"),
7095 b"orphan": _(b"orphan: %d changesets"),
7099 b"contentdivergent": _(b"content-divergent: %d changesets"),
7096 b"contentdivergent": _(b"content-divergent: %d changesets"),
7100 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7097 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7101 }
7098 }
7102 if numtrouble > 0:
7099 if numtrouble > 0:
7103 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7100 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7104
7101
7105 cmdutil.summaryhooks(ui, repo)
7102 cmdutil.summaryhooks(ui, repo)
7106
7103
7107 if opts.get(b'remote'):
7104 if opts.get(b'remote'):
7108 needsincoming, needsoutgoing = True, True
7105 needsincoming, needsoutgoing = True, True
7109 else:
7106 else:
7110 needsincoming, needsoutgoing = False, False
7107 needsincoming, needsoutgoing = False, False
7111 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7108 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7112 if i:
7109 if i:
7113 needsincoming = True
7110 needsincoming = True
7114 if o:
7111 if o:
7115 needsoutgoing = True
7112 needsoutgoing = True
7116 if not needsincoming and not needsoutgoing:
7113 if not needsincoming and not needsoutgoing:
7117 return
7114 return
7118
7115
7119 def getincoming():
7116 def getincoming():
7120 source, branches = hg.parseurl(ui.expandpath(b'default'))
7117 source, branches = hg.parseurl(ui.expandpath(b'default'))
7121 sbranch = branches[0]
7118 sbranch = branches[0]
7122 try:
7119 try:
7123 other = hg.peer(repo, {}, source)
7120 other = hg.peer(repo, {}, source)
7124 except error.RepoError:
7121 except error.RepoError:
7125 if opts.get(b'remote'):
7122 if opts.get(b'remote'):
7126 raise
7123 raise
7127 return source, sbranch, None, None, None
7124 return source, sbranch, None, None, None
7128 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7125 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7129 if revs:
7126 if revs:
7130 revs = [other.lookup(rev) for rev in revs]
7127 revs = [other.lookup(rev) for rev in revs]
7131 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7128 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7132 repo.ui.pushbuffer()
7129 repo.ui.pushbuffer()
7133 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7130 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7134 repo.ui.popbuffer()
7131 repo.ui.popbuffer()
7135 return source, sbranch, other, commoninc, commoninc[1]
7132 return source, sbranch, other, commoninc, commoninc[1]
7136
7133
7137 if needsincoming:
7134 if needsincoming:
7138 source, sbranch, sother, commoninc, incoming = getincoming()
7135 source, sbranch, sother, commoninc, incoming = getincoming()
7139 else:
7136 else:
7140 source = sbranch = sother = commoninc = incoming = None
7137 source = sbranch = sother = commoninc = incoming = None
7141
7138
7142 def getoutgoing():
7139 def getoutgoing():
7143 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7140 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7144 dbranch = branches[0]
7141 dbranch = branches[0]
7145 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7142 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7146 if source != dest:
7143 if source != dest:
7147 try:
7144 try:
7148 dother = hg.peer(repo, {}, dest)
7145 dother = hg.peer(repo, {}, dest)
7149 except error.RepoError:
7146 except error.RepoError:
7150 if opts.get(b'remote'):
7147 if opts.get(b'remote'):
7151 raise
7148 raise
7152 return dest, dbranch, None, None
7149 return dest, dbranch, None, None
7153 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7150 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7154 elif sother is None:
7151 elif sother is None:
7155 # there is no explicit destination peer, but source one is invalid
7152 # there is no explicit destination peer, but source one is invalid
7156 return dest, dbranch, None, None
7153 return dest, dbranch, None, None
7157 else:
7154 else:
7158 dother = sother
7155 dother = sother
7159 if source != dest or (sbranch is not None and sbranch != dbranch):
7156 if source != dest or (sbranch is not None and sbranch != dbranch):
7160 common = None
7157 common = None
7161 else:
7158 else:
7162 common = commoninc
7159 common = commoninc
7163 if revs:
7160 if revs:
7164 revs = [repo.lookup(rev) for rev in revs]
7161 revs = [repo.lookup(rev) for rev in revs]
7165 repo.ui.pushbuffer()
7162 repo.ui.pushbuffer()
7166 outgoing = discovery.findcommonoutgoing(
7163 outgoing = discovery.findcommonoutgoing(
7167 repo, dother, onlyheads=revs, commoninc=common
7164 repo, dother, onlyheads=revs, commoninc=common
7168 )
7165 )
7169 repo.ui.popbuffer()
7166 repo.ui.popbuffer()
7170 return dest, dbranch, dother, outgoing
7167 return dest, dbranch, dother, outgoing
7171
7168
7172 if needsoutgoing:
7169 if needsoutgoing:
7173 dest, dbranch, dother, outgoing = getoutgoing()
7170 dest, dbranch, dother, outgoing = getoutgoing()
7174 else:
7171 else:
7175 dest = dbranch = dother = outgoing = None
7172 dest = dbranch = dother = outgoing = None
7176
7173
7177 if opts.get(b'remote'):
7174 if opts.get(b'remote'):
7178 t = []
7175 t = []
7179 if incoming:
7176 if incoming:
7180 t.append(_(b'1 or more incoming'))
7177 t.append(_(b'1 or more incoming'))
7181 o = outgoing.missing
7178 o = outgoing.missing
7182 if o:
7179 if o:
7183 t.append(_(b'%d outgoing') % len(o))
7180 t.append(_(b'%d outgoing') % len(o))
7184 other = dother or sother
7181 other = dother or sother
7185 if b'bookmarks' in other.listkeys(b'namespaces'):
7182 if b'bookmarks' in other.listkeys(b'namespaces'):
7186 counts = bookmarks.summary(repo, other)
7183 counts = bookmarks.summary(repo, other)
7187 if counts[0] > 0:
7184 if counts[0] > 0:
7188 t.append(_(b'%d incoming bookmarks') % counts[0])
7185 t.append(_(b'%d incoming bookmarks') % counts[0])
7189 if counts[1] > 0:
7186 if counts[1] > 0:
7190 t.append(_(b'%d outgoing bookmarks') % counts[1])
7187 t.append(_(b'%d outgoing bookmarks') % counts[1])
7191
7188
7192 if t:
7189 if t:
7193 # i18n: column positioning for "hg summary"
7190 # i18n: column positioning for "hg summary"
7194 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7191 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7195 else:
7192 else:
7196 # i18n: column positioning for "hg summary"
7193 # i18n: column positioning for "hg summary"
7197 ui.status(_(b'remote: (synced)\n'))
7194 ui.status(_(b'remote: (synced)\n'))
7198
7195
7199 cmdutil.summaryremotehooks(
7196 cmdutil.summaryremotehooks(
7200 ui,
7197 ui,
7201 repo,
7198 repo,
7202 opts,
7199 opts,
7203 (
7200 (
7204 (source, sbranch, sother, commoninc),
7201 (source, sbranch, sother, commoninc),
7205 (dest, dbranch, dother, outgoing),
7202 (dest, dbranch, dother, outgoing),
7206 ),
7203 ),
7207 )
7204 )
7208
7205
7209
7206
7210 @command(
7207 @command(
7211 b'tag',
7208 b'tag',
7212 [
7209 [
7213 (b'f', b'force', None, _(b'force tag')),
7210 (b'f', b'force', None, _(b'force tag')),
7214 (b'l', b'local', None, _(b'make the tag local')),
7211 (b'l', b'local', None, _(b'make the tag local')),
7215 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7212 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7216 (b'', b'remove', None, _(b'remove a tag')),
7213 (b'', b'remove', None, _(b'remove a tag')),
7217 # -l/--local is already there, commitopts cannot be used
7214 # -l/--local is already there, commitopts cannot be used
7218 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7215 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7219 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7216 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7220 ]
7217 ]
7221 + commitopts2,
7218 + commitopts2,
7222 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7219 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7223 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7220 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7224 )
7221 )
7225 def tag(ui, repo, name1, *names, **opts):
7222 def tag(ui, repo, name1, *names, **opts):
7226 """add one or more tags for the current or given revision
7223 """add one or more tags for the current or given revision
7227
7224
7228 Name a particular revision using <name>.
7225 Name a particular revision using <name>.
7229
7226
7230 Tags are used to name particular revisions of the repository and are
7227 Tags are used to name particular revisions of the repository and are
7231 very useful to compare different revisions, to go back to significant
7228 very useful to compare different revisions, to go back to significant
7232 earlier versions or to mark branch points as releases, etc. Changing
7229 earlier versions or to mark branch points as releases, etc. Changing
7233 an existing tag is normally disallowed; use -f/--force to override.
7230 an existing tag is normally disallowed; use -f/--force to override.
7234
7231
7235 If no revision is given, the parent of the working directory is
7232 If no revision is given, the parent of the working directory is
7236 used.
7233 used.
7237
7234
7238 To facilitate version control, distribution, and merging of tags,
7235 To facilitate version control, distribution, and merging of tags,
7239 they are stored as a file named ".hgtags" which is managed similarly
7236 they are stored as a file named ".hgtags" which is managed similarly
7240 to other project files and can be hand-edited if necessary. This
7237 to other project files and can be hand-edited if necessary. This
7241 also means that tagging creates a new commit. The file
7238 also means that tagging creates a new commit. The file
7242 ".hg/localtags" is used for local tags (not shared among
7239 ".hg/localtags" is used for local tags (not shared among
7243 repositories).
7240 repositories).
7244
7241
7245 Tag commits are usually made at the head of a branch. If the parent
7242 Tag commits are usually made at the head of a branch. If the parent
7246 of the working directory is not a branch head, :hg:`tag` aborts; use
7243 of the working directory is not a branch head, :hg:`tag` aborts; use
7247 -f/--force to force the tag commit to be based on a non-head
7244 -f/--force to force the tag commit to be based on a non-head
7248 changeset.
7245 changeset.
7249
7246
7250 See :hg:`help dates` for a list of formats valid for -d/--date.
7247 See :hg:`help dates` for a list of formats valid for -d/--date.
7251
7248
7252 Since tag names have priority over branch names during revision
7249 Since tag names have priority over branch names during revision
7253 lookup, using an existing branch name as a tag name is discouraged.
7250 lookup, using an existing branch name as a tag name is discouraged.
7254
7251
7255 Returns 0 on success.
7252 Returns 0 on success.
7256 """
7253 """
7257 opts = pycompat.byteskwargs(opts)
7254 opts = pycompat.byteskwargs(opts)
7258 with repo.wlock(), repo.lock():
7255 with repo.wlock(), repo.lock():
7259 rev_ = b"."
7256 rev_ = b"."
7260 names = [t.strip() for t in (name1,) + names]
7257 names = [t.strip() for t in (name1,) + names]
7261 if len(names) != len(set(names)):
7258 if len(names) != len(set(names)):
7262 raise error.Abort(_(b'tag names must be unique'))
7259 raise error.Abort(_(b'tag names must be unique'))
7263 for n in names:
7260 for n in names:
7264 scmutil.checknewlabel(repo, n, b'tag')
7261 scmutil.checknewlabel(repo, n, b'tag')
7265 if not n:
7262 if not n:
7266 raise error.Abort(
7263 raise error.Abort(
7267 _(b'tag names cannot consist entirely of whitespace')
7264 _(b'tag names cannot consist entirely of whitespace')
7268 )
7265 )
7269 if opts.get(b'rev') and opts.get(b'remove'):
7266 if opts.get(b'rev') and opts.get(b'remove'):
7270 raise error.Abort(_(b"--rev and --remove are incompatible"))
7267 raise error.Abort(_(b"--rev and --remove are incompatible"))
7271 if opts.get(b'rev'):
7268 if opts.get(b'rev'):
7272 rev_ = opts[b'rev']
7269 rev_ = opts[b'rev']
7273 message = opts.get(b'message')
7270 message = opts.get(b'message')
7274 if opts.get(b'remove'):
7271 if opts.get(b'remove'):
7275 if opts.get(b'local'):
7272 if opts.get(b'local'):
7276 expectedtype = b'local'
7273 expectedtype = b'local'
7277 else:
7274 else:
7278 expectedtype = b'global'
7275 expectedtype = b'global'
7279
7276
7280 for n in names:
7277 for n in names:
7281 if repo.tagtype(n) == b'global':
7278 if repo.tagtype(n) == b'global':
7282 alltags = tagsmod.findglobaltags(ui, repo)
7279 alltags = tagsmod.findglobaltags(ui, repo)
7283 if alltags[n][0] == nullid:
7280 if alltags[n][0] == nullid:
7284 raise error.Abort(_(b"tag '%s' is already removed") % n)
7281 raise error.Abort(_(b"tag '%s' is already removed") % n)
7285 if not repo.tagtype(n):
7282 if not repo.tagtype(n):
7286 raise error.Abort(_(b"tag '%s' does not exist") % n)
7283 raise error.Abort(_(b"tag '%s' does not exist") % n)
7287 if repo.tagtype(n) != expectedtype:
7284 if repo.tagtype(n) != expectedtype:
7288 if expectedtype == b'global':
7285 if expectedtype == b'global':
7289 raise error.Abort(
7286 raise error.Abort(
7290 _(b"tag '%s' is not a global tag") % n
7287 _(b"tag '%s' is not a global tag") % n
7291 )
7288 )
7292 else:
7289 else:
7293 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7290 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7294 rev_ = b'null'
7291 rev_ = b'null'
7295 if not message:
7292 if not message:
7296 # we don't translate commit messages
7293 # we don't translate commit messages
7297 message = b'Removed tag %s' % b', '.join(names)
7294 message = b'Removed tag %s' % b', '.join(names)
7298 elif not opts.get(b'force'):
7295 elif not opts.get(b'force'):
7299 for n in names:
7296 for n in names:
7300 if n in repo.tags():
7297 if n in repo.tags():
7301 raise error.Abort(
7298 raise error.Abort(
7302 _(b"tag '%s' already exists (use -f to force)") % n
7299 _(b"tag '%s' already exists (use -f to force)") % n
7303 )
7300 )
7304 if not opts.get(b'local'):
7301 if not opts.get(b'local'):
7305 p1, p2 = repo.dirstate.parents()
7302 p1, p2 = repo.dirstate.parents()
7306 if p2 != nullid:
7303 if p2 != nullid:
7307 raise error.Abort(_(b'uncommitted merge'))
7304 raise error.Abort(_(b'uncommitted merge'))
7308 bheads = repo.branchheads()
7305 bheads = repo.branchheads()
7309 if not opts.get(b'force') and bheads and p1 not in bheads:
7306 if not opts.get(b'force') and bheads and p1 not in bheads:
7310 raise error.Abort(
7307 raise error.Abort(
7311 _(
7308 _(
7312 b'working directory is not at a branch head '
7309 b'working directory is not at a branch head '
7313 b'(use -f to force)'
7310 b'(use -f to force)'
7314 )
7311 )
7315 )
7312 )
7316 node = scmutil.revsingle(repo, rev_).node()
7313 node = scmutil.revsingle(repo, rev_).node()
7317
7314
7318 if not message:
7315 if not message:
7319 # we don't translate commit messages
7316 # we don't translate commit messages
7320 message = b'Added tag %s for changeset %s' % (
7317 message = b'Added tag %s for changeset %s' % (
7321 b', '.join(names),
7318 b', '.join(names),
7322 short(node),
7319 short(node),
7323 )
7320 )
7324
7321
7325 date = opts.get(b'date')
7322 date = opts.get(b'date')
7326 if date:
7323 if date:
7327 date = dateutil.parsedate(date)
7324 date = dateutil.parsedate(date)
7328
7325
7329 if opts.get(b'remove'):
7326 if opts.get(b'remove'):
7330 editform = b'tag.remove'
7327 editform = b'tag.remove'
7331 else:
7328 else:
7332 editform = b'tag.add'
7329 editform = b'tag.add'
7333 editor = cmdutil.getcommiteditor(
7330 editor = cmdutil.getcommiteditor(
7334 editform=editform, **pycompat.strkwargs(opts)
7331 editform=editform, **pycompat.strkwargs(opts)
7335 )
7332 )
7336
7333
7337 # don't allow tagging the null rev
7334 # don't allow tagging the null rev
7338 if (
7335 if (
7339 not opts.get(b'remove')
7336 not opts.get(b'remove')
7340 and scmutil.revsingle(repo, rev_).rev() == nullrev
7337 and scmutil.revsingle(repo, rev_).rev() == nullrev
7341 ):
7338 ):
7342 raise error.Abort(_(b"cannot tag null revision"))
7339 raise error.Abort(_(b"cannot tag null revision"))
7343
7340
7344 tagsmod.tag(
7341 tagsmod.tag(
7345 repo,
7342 repo,
7346 names,
7343 names,
7347 node,
7344 node,
7348 message,
7345 message,
7349 opts.get(b'local'),
7346 opts.get(b'local'),
7350 opts.get(b'user'),
7347 opts.get(b'user'),
7351 date,
7348 date,
7352 editor=editor,
7349 editor=editor,
7353 )
7350 )
7354
7351
7355
7352
7356 @command(
7353 @command(
7357 b'tags',
7354 b'tags',
7358 formatteropts,
7355 formatteropts,
7359 b'',
7356 b'',
7360 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7357 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7361 intents={INTENT_READONLY},
7358 intents={INTENT_READONLY},
7362 )
7359 )
7363 def tags(ui, repo, **opts):
7360 def tags(ui, repo, **opts):
7364 """list repository tags
7361 """list repository tags
7365
7362
7366 This lists both regular and local tags. When the -v/--verbose
7363 This lists both regular and local tags. When the -v/--verbose
7367 switch is used, a third column "local" is printed for local tags.
7364 switch is used, a third column "local" is printed for local tags.
7368 When the -q/--quiet switch is used, only the tag name is printed.
7365 When the -q/--quiet switch is used, only the tag name is printed.
7369
7366
7370 .. container:: verbose
7367 .. container:: verbose
7371
7368
7372 Template:
7369 Template:
7373
7370
7374 The following keywords are supported in addition to the common template
7371 The following keywords are supported in addition to the common template
7375 keywords and functions such as ``{tag}``. See also
7372 keywords and functions such as ``{tag}``. See also
7376 :hg:`help templates`.
7373 :hg:`help templates`.
7377
7374
7378 :type: String. ``local`` for local tags.
7375 :type: String. ``local`` for local tags.
7379
7376
7380 Returns 0 on success.
7377 Returns 0 on success.
7381 """
7378 """
7382
7379
7383 opts = pycompat.byteskwargs(opts)
7380 opts = pycompat.byteskwargs(opts)
7384 ui.pager(b'tags')
7381 ui.pager(b'tags')
7385 fm = ui.formatter(b'tags', opts)
7382 fm = ui.formatter(b'tags', opts)
7386 hexfunc = fm.hexfunc
7383 hexfunc = fm.hexfunc
7387
7384
7388 for t, n in reversed(repo.tagslist()):
7385 for t, n in reversed(repo.tagslist()):
7389 hn = hexfunc(n)
7386 hn = hexfunc(n)
7390 label = b'tags.normal'
7387 label = b'tags.normal'
7391 tagtype = b''
7388 tagtype = b''
7392 if repo.tagtype(t) == b'local':
7389 if repo.tagtype(t) == b'local':
7393 label = b'tags.local'
7390 label = b'tags.local'
7394 tagtype = b'local'
7391 tagtype = b'local'
7395
7392
7396 fm.startitem()
7393 fm.startitem()
7397 fm.context(repo=repo)
7394 fm.context(repo=repo)
7398 fm.write(b'tag', b'%s', t, label=label)
7395 fm.write(b'tag', b'%s', t, label=label)
7399 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7396 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7400 fm.condwrite(
7397 fm.condwrite(
7401 not ui.quiet,
7398 not ui.quiet,
7402 b'rev node',
7399 b'rev node',
7403 fmt,
7400 fmt,
7404 repo.changelog.rev(n),
7401 repo.changelog.rev(n),
7405 hn,
7402 hn,
7406 label=label,
7403 label=label,
7407 )
7404 )
7408 fm.condwrite(
7405 fm.condwrite(
7409 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7406 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7410 )
7407 )
7411 fm.plain(b'\n')
7408 fm.plain(b'\n')
7412 fm.end()
7409 fm.end()
7413
7410
7414
7411
7415 @command(
7412 @command(
7416 b'tip',
7413 b'tip',
7417 [
7414 [
7418 (b'p', b'patch', None, _(b'show patch')),
7415 (b'p', b'patch', None, _(b'show patch')),
7419 (b'g', b'git', None, _(b'use git extended diff format')),
7416 (b'g', b'git', None, _(b'use git extended diff format')),
7420 ]
7417 ]
7421 + templateopts,
7418 + templateopts,
7422 _(b'[-p] [-g]'),
7419 _(b'[-p] [-g]'),
7423 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7420 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7424 )
7421 )
7425 def tip(ui, repo, **opts):
7422 def tip(ui, repo, **opts):
7426 """show the tip revision (DEPRECATED)
7423 """show the tip revision (DEPRECATED)
7427
7424
7428 The tip revision (usually just called the tip) is the changeset
7425 The tip revision (usually just called the tip) is the changeset
7429 most recently added to the repository (and therefore the most
7426 most recently added to the repository (and therefore the most
7430 recently changed head).
7427 recently changed head).
7431
7428
7432 If you have just made a commit, that commit will be the tip. If
7429 If you have just made a commit, that commit will be the tip. If
7433 you have just pulled changes from another repository, the tip of
7430 you have just pulled changes from another repository, the tip of
7434 that repository becomes the current tip. The "tip" tag is special
7431 that repository becomes the current tip. The "tip" tag is special
7435 and cannot be renamed or assigned to a different changeset.
7432 and cannot be renamed or assigned to a different changeset.
7436
7433
7437 This command is deprecated, please use :hg:`heads` instead.
7434 This command is deprecated, please use :hg:`heads` instead.
7438
7435
7439 Returns 0 on success.
7436 Returns 0 on success.
7440 """
7437 """
7441 opts = pycompat.byteskwargs(opts)
7438 opts = pycompat.byteskwargs(opts)
7442 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7439 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7443 displayer.show(repo[b'tip'])
7440 displayer.show(repo[b'tip'])
7444 displayer.close()
7441 displayer.close()
7445
7442
7446
7443
7447 @command(
7444 @command(
7448 b'unbundle',
7445 b'unbundle',
7449 [
7446 [
7450 (
7447 (
7451 b'u',
7448 b'u',
7452 b'update',
7449 b'update',
7453 None,
7450 None,
7454 _(b'update to new branch head if changesets were unbundled'),
7451 _(b'update to new branch head if changesets were unbundled'),
7455 )
7452 )
7456 ],
7453 ],
7457 _(b'[-u] FILE...'),
7454 _(b'[-u] FILE...'),
7458 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7455 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7459 )
7456 )
7460 def unbundle(ui, repo, fname1, *fnames, **opts):
7457 def unbundle(ui, repo, fname1, *fnames, **opts):
7461 """apply one or more bundle files
7458 """apply one or more bundle files
7462
7459
7463 Apply one or more bundle files generated by :hg:`bundle`.
7460 Apply one or more bundle files generated by :hg:`bundle`.
7464
7461
7465 Returns 0 on success, 1 if an update has unresolved files.
7462 Returns 0 on success, 1 if an update has unresolved files.
7466 """
7463 """
7467 fnames = (fname1,) + fnames
7464 fnames = (fname1,) + fnames
7468
7465
7469 with repo.lock():
7466 with repo.lock():
7470 for fname in fnames:
7467 for fname in fnames:
7471 f = hg.openpath(ui, fname)
7468 f = hg.openpath(ui, fname)
7472 gen = exchange.readbundle(ui, f, fname)
7469 gen = exchange.readbundle(ui, f, fname)
7473 if isinstance(gen, streamclone.streamcloneapplier):
7470 if isinstance(gen, streamclone.streamcloneapplier):
7474 raise error.Abort(
7471 raise error.Abort(
7475 _(
7472 _(
7476 b'packed bundles cannot be applied with '
7473 b'packed bundles cannot be applied with '
7477 b'"hg unbundle"'
7474 b'"hg unbundle"'
7478 ),
7475 ),
7479 hint=_(b'use "hg debugapplystreamclonebundle"'),
7476 hint=_(b'use "hg debugapplystreamclonebundle"'),
7480 )
7477 )
7481 url = b'bundle:' + fname
7478 url = b'bundle:' + fname
7482 try:
7479 try:
7483 txnname = b'unbundle'
7480 txnname = b'unbundle'
7484 if not isinstance(gen, bundle2.unbundle20):
7481 if not isinstance(gen, bundle2.unbundle20):
7485 txnname = b'unbundle\n%s' % util.hidepassword(url)
7482 txnname = b'unbundle\n%s' % util.hidepassword(url)
7486 with repo.transaction(txnname) as tr:
7483 with repo.transaction(txnname) as tr:
7487 op = bundle2.applybundle(
7484 op = bundle2.applybundle(
7488 repo, gen, tr, source=b'unbundle', url=url
7485 repo, gen, tr, source=b'unbundle', url=url
7489 )
7486 )
7490 except error.BundleUnknownFeatureError as exc:
7487 except error.BundleUnknownFeatureError as exc:
7491 raise error.Abort(
7488 raise error.Abort(
7492 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7489 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7493 hint=_(
7490 hint=_(
7494 b"see https://mercurial-scm.org/"
7491 b"see https://mercurial-scm.org/"
7495 b"wiki/BundleFeature for more "
7492 b"wiki/BundleFeature for more "
7496 b"information"
7493 b"information"
7497 ),
7494 ),
7498 )
7495 )
7499 modheads = bundle2.combinechangegroupresults(op)
7496 modheads = bundle2.combinechangegroupresults(op)
7500
7497
7501 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7498 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7502
7499
7503
7500
7504 @command(
7501 @command(
7505 b'unshelve',
7502 b'unshelve',
7506 [
7503 [
7507 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7504 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7508 (
7505 (
7509 b'c',
7506 b'c',
7510 b'continue',
7507 b'continue',
7511 None,
7508 None,
7512 _(b'continue an incomplete unshelve operation'),
7509 _(b'continue an incomplete unshelve operation'),
7513 ),
7510 ),
7514 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7511 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7515 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7512 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7516 (
7513 (
7517 b'n',
7514 b'n',
7518 b'name',
7515 b'name',
7519 b'',
7516 b'',
7520 _(b'restore shelved change with given name'),
7517 _(b'restore shelved change with given name'),
7521 _(b'NAME'),
7518 _(b'NAME'),
7522 ),
7519 ),
7523 (b't', b'tool', b'', _(b'specify merge tool')),
7520 (b't', b'tool', b'', _(b'specify merge tool')),
7524 (
7521 (
7525 b'',
7522 b'',
7526 b'date',
7523 b'date',
7527 b'',
7524 b'',
7528 _(b'set date for temporary commits (DEPRECATED)'),
7525 _(b'set date for temporary commits (DEPRECATED)'),
7529 _(b'DATE'),
7526 _(b'DATE'),
7530 ),
7527 ),
7531 ],
7528 ],
7532 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7529 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7533 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7530 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7534 )
7531 )
7535 def unshelve(ui, repo, *shelved, **opts):
7532 def unshelve(ui, repo, *shelved, **opts):
7536 """restore a shelved change to the working directory
7533 """restore a shelved change to the working directory
7537
7534
7538 This command accepts an optional name of a shelved change to
7535 This command accepts an optional name of a shelved change to
7539 restore. If none is given, the most recent shelved change is used.
7536 restore. If none is given, the most recent shelved change is used.
7540
7537
7541 If a shelved change is applied successfully, the bundle that
7538 If a shelved change is applied successfully, the bundle that
7542 contains the shelved changes is moved to a backup location
7539 contains the shelved changes is moved to a backup location
7543 (.hg/shelve-backup).
7540 (.hg/shelve-backup).
7544
7541
7545 Since you can restore a shelved change on top of an arbitrary
7542 Since you can restore a shelved change on top of an arbitrary
7546 commit, it is possible that unshelving will result in a conflict
7543 commit, it is possible that unshelving will result in a conflict
7547 between your changes and the commits you are unshelving onto. If
7544 between your changes and the commits you are unshelving onto. If
7548 this occurs, you must resolve the conflict, then use
7545 this occurs, you must resolve the conflict, then use
7549 ``--continue`` to complete the unshelve operation. (The bundle
7546 ``--continue`` to complete the unshelve operation. (The bundle
7550 will not be moved until you successfully complete the unshelve.)
7547 will not be moved until you successfully complete the unshelve.)
7551
7548
7552 (Alternatively, you can use ``--abort`` to abandon an unshelve
7549 (Alternatively, you can use ``--abort`` to abandon an unshelve
7553 that causes a conflict. This reverts the unshelved changes, and
7550 that causes a conflict. This reverts the unshelved changes, and
7554 leaves the bundle in place.)
7551 leaves the bundle in place.)
7555
7552
7556 If bare shelved change (without interactive, include and exclude
7553 If bare shelved change (without interactive, include and exclude
7557 option) was done on newly created branch it would restore branch
7554 option) was done on newly created branch it would restore branch
7558 information to the working directory.
7555 information to the working directory.
7559
7556
7560 After a successful unshelve, the shelved changes are stored in a
7557 After a successful unshelve, the shelved changes are stored in a
7561 backup directory. Only the N most recent backups are kept. N
7558 backup directory. Only the N most recent backups are kept. N
7562 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7559 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7563 configuration option.
7560 configuration option.
7564
7561
7565 .. container:: verbose
7562 .. container:: verbose
7566
7563
7567 Timestamp in seconds is used to decide order of backups. More
7564 Timestamp in seconds is used to decide order of backups. More
7568 than ``maxbackups`` backups are kept, if same timestamp
7565 than ``maxbackups`` backups are kept, if same timestamp
7569 prevents from deciding exact order of them, for safety.
7566 prevents from deciding exact order of them, for safety.
7570
7567
7571 Selected changes can be unshelved with ``--interactive`` flag.
7568 Selected changes can be unshelved with ``--interactive`` flag.
7572 The working directory is updated with the selected changes, and
7569 The working directory is updated with the selected changes, and
7573 only the unselected changes remain shelved.
7570 only the unselected changes remain shelved.
7574 Note: The whole shelve is applied to working directory first before
7571 Note: The whole shelve is applied to working directory first before
7575 running interactively. So, this will bring up all the conflicts between
7572 running interactively. So, this will bring up all the conflicts between
7576 working directory and the shelve, irrespective of which changes will be
7573 working directory and the shelve, irrespective of which changes will be
7577 unshelved.
7574 unshelved.
7578 """
7575 """
7579 with repo.wlock():
7576 with repo.wlock():
7580 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7577 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7581
7578
7582
7579
7583 statemod.addunfinished(
7580 statemod.addunfinished(
7584 b'unshelve',
7581 b'unshelve',
7585 fname=b'shelvedstate',
7582 fname=b'shelvedstate',
7586 continueflag=True,
7583 continueflag=True,
7587 abortfunc=shelvemod.hgabortunshelve,
7584 abortfunc=shelvemod.hgabortunshelve,
7588 continuefunc=shelvemod.hgcontinueunshelve,
7585 continuefunc=shelvemod.hgcontinueunshelve,
7589 cmdmsg=_(b'unshelve already in progress'),
7586 cmdmsg=_(b'unshelve already in progress'),
7590 )
7587 )
7591
7588
7592
7589
7593 @command(
7590 @command(
7594 b'update|up|checkout|co',
7591 b'update|up|checkout|co',
7595 [
7592 [
7596 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7593 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7597 (b'c', b'check', None, _(b'require clean working directory')),
7594 (b'c', b'check', None, _(b'require clean working directory')),
7598 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7595 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7599 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7596 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7600 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7597 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7601 ]
7598 ]
7602 + mergetoolopts,
7599 + mergetoolopts,
7603 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7600 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7604 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7601 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7605 helpbasic=True,
7602 helpbasic=True,
7606 )
7603 )
7607 def update(ui, repo, node=None, **opts):
7604 def update(ui, repo, node=None, **opts):
7608 """update working directory (or switch revisions)
7605 """update working directory (or switch revisions)
7609
7606
7610 Update the repository's working directory to the specified
7607 Update the repository's working directory to the specified
7611 changeset. If no changeset is specified, update to the tip of the
7608 changeset. If no changeset is specified, update to the tip of the
7612 current named branch and move the active bookmark (see :hg:`help
7609 current named branch and move the active bookmark (see :hg:`help
7613 bookmarks`).
7610 bookmarks`).
7614
7611
7615 Update sets the working directory's parent revision to the specified
7612 Update sets the working directory's parent revision to the specified
7616 changeset (see :hg:`help parents`).
7613 changeset (see :hg:`help parents`).
7617
7614
7618 If the changeset is not a descendant or ancestor of the working
7615 If the changeset is not a descendant or ancestor of the working
7619 directory's parent and there are uncommitted changes, the update is
7616 directory's parent and there are uncommitted changes, the update is
7620 aborted. With the -c/--check option, the working directory is checked
7617 aborted. With the -c/--check option, the working directory is checked
7621 for uncommitted changes; if none are found, the working directory is
7618 for uncommitted changes; if none are found, the working directory is
7622 updated to the specified changeset.
7619 updated to the specified changeset.
7623
7620
7624 .. container:: verbose
7621 .. container:: verbose
7625
7622
7626 The -C/--clean, -c/--check, and -m/--merge options control what
7623 The -C/--clean, -c/--check, and -m/--merge options control what
7627 happens if the working directory contains uncommitted changes.
7624 happens if the working directory contains uncommitted changes.
7628 At most of one of them can be specified.
7625 At most of one of them can be specified.
7629
7626
7630 1. If no option is specified, and if
7627 1. If no option is specified, and if
7631 the requested changeset is an ancestor or descendant of
7628 the requested changeset is an ancestor or descendant of
7632 the working directory's parent, the uncommitted changes
7629 the working directory's parent, the uncommitted changes
7633 are merged into the requested changeset and the merged
7630 are merged into the requested changeset and the merged
7634 result is left uncommitted. If the requested changeset is
7631 result is left uncommitted. If the requested changeset is
7635 not an ancestor or descendant (that is, it is on another
7632 not an ancestor or descendant (that is, it is on another
7636 branch), the update is aborted and the uncommitted changes
7633 branch), the update is aborted and the uncommitted changes
7637 are preserved.
7634 are preserved.
7638
7635
7639 2. With the -m/--merge option, the update is allowed even if the
7636 2. With the -m/--merge option, the update is allowed even if the
7640 requested changeset is not an ancestor or descendant of
7637 requested changeset is not an ancestor or descendant of
7641 the working directory's parent.
7638 the working directory's parent.
7642
7639
7643 3. With the -c/--check option, the update is aborted and the
7640 3. With the -c/--check option, the update is aborted and the
7644 uncommitted changes are preserved.
7641 uncommitted changes are preserved.
7645
7642
7646 4. With the -C/--clean option, uncommitted changes are discarded and
7643 4. With the -C/--clean option, uncommitted changes are discarded and
7647 the working directory is updated to the requested changeset.
7644 the working directory is updated to the requested changeset.
7648
7645
7649 To cancel an uncommitted merge (and lose your changes), use
7646 To cancel an uncommitted merge (and lose your changes), use
7650 :hg:`merge --abort`.
7647 :hg:`merge --abort`.
7651
7648
7652 Use null as the changeset to remove the working directory (like
7649 Use null as the changeset to remove the working directory (like
7653 :hg:`clone -U`).
7650 :hg:`clone -U`).
7654
7651
7655 If you want to revert just one file to an older revision, use
7652 If you want to revert just one file to an older revision, use
7656 :hg:`revert [-r REV] NAME`.
7653 :hg:`revert [-r REV] NAME`.
7657
7654
7658 See :hg:`help dates` for a list of formats valid for -d/--date.
7655 See :hg:`help dates` for a list of formats valid for -d/--date.
7659
7656
7660 Returns 0 on success, 1 if there are unresolved files.
7657 Returns 0 on success, 1 if there are unresolved files.
7661 """
7658 """
7662 rev = opts.get('rev')
7659 rev = opts.get('rev')
7663 date = opts.get('date')
7660 date = opts.get('date')
7664 clean = opts.get('clean')
7661 clean = opts.get('clean')
7665 check = opts.get('check')
7662 check = opts.get('check')
7666 merge = opts.get('merge')
7663 merge = opts.get('merge')
7667 if rev and node:
7664 if rev and node:
7668 raise error.Abort(_(b"please specify just one revision"))
7665 raise error.Abort(_(b"please specify just one revision"))
7669
7666
7670 if ui.configbool(b'commands', b'update.requiredest'):
7667 if ui.configbool(b'commands', b'update.requiredest'):
7671 if not node and not rev and not date:
7668 if not node and not rev and not date:
7672 raise error.Abort(
7669 raise error.Abort(
7673 _(b'you must specify a destination'),
7670 _(b'you must specify a destination'),
7674 hint=_(b'for example: hg update ".::"'),
7671 hint=_(b'for example: hg update ".::"'),
7675 )
7672 )
7676
7673
7677 if rev is None or rev == b'':
7674 if rev is None or rev == b'':
7678 rev = node
7675 rev = node
7679
7676
7680 if date and rev is not None:
7677 if date and rev is not None:
7681 raise error.Abort(_(b"you can't specify a revision and a date"))
7678 raise error.Abort(_(b"you can't specify a revision and a date"))
7682
7679
7683 if len([x for x in (clean, check, merge) if x]) > 1:
7680 if len([x for x in (clean, check, merge) if x]) > 1:
7684 raise error.Abort(
7681 raise error.Abort(
7685 _(
7682 _(
7686 b"can only specify one of -C/--clean, -c/--check, "
7683 b"can only specify one of -C/--clean, -c/--check, "
7687 b"or -m/--merge"
7684 b"or -m/--merge"
7688 )
7685 )
7689 )
7686 )
7690
7687
7691 updatecheck = None
7688 updatecheck = None
7692 if check:
7689 if check:
7693 updatecheck = b'abort'
7690 updatecheck = b'abort'
7694 elif merge:
7691 elif merge:
7695 updatecheck = b'none'
7692 updatecheck = b'none'
7696
7693
7697 with repo.wlock():
7694 with repo.wlock():
7698 cmdutil.clearunfinished(repo)
7695 cmdutil.clearunfinished(repo)
7699 if date:
7696 if date:
7700 rev = cmdutil.finddate(ui, repo, date)
7697 rev = cmdutil.finddate(ui, repo, date)
7701
7698
7702 # if we defined a bookmark, we have to remember the original name
7699 # if we defined a bookmark, we have to remember the original name
7703 brev = rev
7700 brev = rev
7704 if rev:
7701 if rev:
7705 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7702 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7706 ctx = scmutil.revsingle(repo, rev, default=None)
7703 ctx = scmutil.revsingle(repo, rev, default=None)
7707 rev = ctx.rev()
7704 rev = ctx.rev()
7708 hidden = ctx.hidden()
7705 hidden = ctx.hidden()
7709 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7706 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7710 with ui.configoverride(overrides, b'update'):
7707 with ui.configoverride(overrides, b'update'):
7711 ret = hg.updatetotally(
7708 ret = hg.updatetotally(
7712 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7709 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7713 )
7710 )
7714 if hidden:
7711 if hidden:
7715 ctxstr = ctx.hex()[:12]
7712 ctxstr = ctx.hex()[:12]
7716 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7713 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7717
7714
7718 if ctx.obsolete():
7715 if ctx.obsolete():
7719 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7716 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7720 ui.warn(b"(%s)\n" % obsfatemsg)
7717 ui.warn(b"(%s)\n" % obsfatemsg)
7721 return ret
7718 return ret
7722
7719
7723
7720
7724 @command(
7721 @command(
7725 b'verify',
7722 b'verify',
7726 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7723 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7727 helpcategory=command.CATEGORY_MAINTENANCE,
7724 helpcategory=command.CATEGORY_MAINTENANCE,
7728 )
7725 )
7729 def verify(ui, repo, **opts):
7726 def verify(ui, repo, **opts):
7730 """verify the integrity of the repository
7727 """verify the integrity of the repository
7731
7728
7732 Verify the integrity of the current repository.
7729 Verify the integrity of the current repository.
7733
7730
7734 This will perform an extensive check of the repository's
7731 This will perform an extensive check of the repository's
7735 integrity, validating the hashes and checksums of each entry in
7732 integrity, validating the hashes and checksums of each entry in
7736 the changelog, manifest, and tracked files, as well as the
7733 the changelog, manifest, and tracked files, as well as the
7737 integrity of their crosslinks and indices.
7734 integrity of their crosslinks and indices.
7738
7735
7739 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7736 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7740 for more information about recovery from corruption of the
7737 for more information about recovery from corruption of the
7741 repository.
7738 repository.
7742
7739
7743 Returns 0 on success, 1 if errors are encountered.
7740 Returns 0 on success, 1 if errors are encountered.
7744 """
7741 """
7745 opts = pycompat.byteskwargs(opts)
7742 opts = pycompat.byteskwargs(opts)
7746
7743
7747 level = None
7744 level = None
7748 if opts[b'full']:
7745 if opts[b'full']:
7749 level = verifymod.VERIFY_FULL
7746 level = verifymod.VERIFY_FULL
7750 return hg.verify(repo, level)
7747 return hg.verify(repo, level)
7751
7748
7752
7749
7753 @command(
7750 @command(
7754 b'version',
7751 b'version',
7755 [] + formatteropts,
7752 [] + formatteropts,
7756 helpcategory=command.CATEGORY_HELP,
7753 helpcategory=command.CATEGORY_HELP,
7757 norepo=True,
7754 norepo=True,
7758 intents={INTENT_READONLY},
7755 intents={INTENT_READONLY},
7759 )
7756 )
7760 def version_(ui, **opts):
7757 def version_(ui, **opts):
7761 """output version and copyright information
7758 """output version and copyright information
7762
7759
7763 .. container:: verbose
7760 .. container:: verbose
7764
7761
7765 Template:
7762 Template:
7766
7763
7767 The following keywords are supported. See also :hg:`help templates`.
7764 The following keywords are supported. See also :hg:`help templates`.
7768
7765
7769 :extensions: List of extensions.
7766 :extensions: List of extensions.
7770 :ver: String. Version number.
7767 :ver: String. Version number.
7771
7768
7772 And each entry of ``{extensions}`` provides the following sub-keywords
7769 And each entry of ``{extensions}`` provides the following sub-keywords
7773 in addition to ``{ver}``.
7770 in addition to ``{ver}``.
7774
7771
7775 :bundled: Boolean. True if included in the release.
7772 :bundled: Boolean. True if included in the release.
7776 :name: String. Extension name.
7773 :name: String. Extension name.
7777 """
7774 """
7778 opts = pycompat.byteskwargs(opts)
7775 opts = pycompat.byteskwargs(opts)
7779 if ui.verbose:
7776 if ui.verbose:
7780 ui.pager(b'version')
7777 ui.pager(b'version')
7781 fm = ui.formatter(b"version", opts)
7778 fm = ui.formatter(b"version", opts)
7782 fm.startitem()
7779 fm.startitem()
7783 fm.write(
7780 fm.write(
7784 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7781 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7785 )
7782 )
7786 license = _(
7783 license = _(
7787 b"(see https://mercurial-scm.org for more information)\n"
7784 b"(see https://mercurial-scm.org for more information)\n"
7788 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7785 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7789 b"This is free software; see the source for copying conditions. "
7786 b"This is free software; see the source for copying conditions. "
7790 b"There is NO\nwarranty; "
7787 b"There is NO\nwarranty; "
7791 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7788 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7792 )
7789 )
7793 if not ui.quiet:
7790 if not ui.quiet:
7794 fm.plain(license)
7791 fm.plain(license)
7795
7792
7796 if ui.verbose:
7793 if ui.verbose:
7797 fm.plain(_(b"\nEnabled extensions:\n\n"))
7794 fm.plain(_(b"\nEnabled extensions:\n\n"))
7798 # format names and versions into columns
7795 # format names and versions into columns
7799 names = []
7796 names = []
7800 vers = []
7797 vers = []
7801 isinternals = []
7798 isinternals = []
7802 for name, module in extensions.extensions():
7799 for name, module in extensions.extensions():
7803 names.append(name)
7800 names.append(name)
7804 vers.append(extensions.moduleversion(module) or None)
7801 vers.append(extensions.moduleversion(module) or None)
7805 isinternals.append(extensions.ismoduleinternal(module))
7802 isinternals.append(extensions.ismoduleinternal(module))
7806 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7803 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7807 if names:
7804 if names:
7808 namefmt = b" %%-%ds " % max(len(n) for n in names)
7805 namefmt = b" %%-%ds " % max(len(n) for n in names)
7809 places = [_(b"external"), _(b"internal")]
7806 places = [_(b"external"), _(b"internal")]
7810 for n, v, p in zip(names, vers, isinternals):
7807 for n, v, p in zip(names, vers, isinternals):
7811 fn.startitem()
7808 fn.startitem()
7812 fn.condwrite(ui.verbose, b"name", namefmt, n)
7809 fn.condwrite(ui.verbose, b"name", namefmt, n)
7813 if ui.verbose:
7810 if ui.verbose:
7814 fn.plain(b"%s " % places[p])
7811 fn.plain(b"%s " % places[p])
7815 fn.data(bundled=p)
7812 fn.data(bundled=p)
7816 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7813 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7817 if ui.verbose:
7814 if ui.verbose:
7818 fn.plain(b"\n")
7815 fn.plain(b"\n")
7819 fn.end()
7816 fn.end()
7820 fm.end()
7817 fm.end()
7821
7818
7822
7819
7823 def loadcmdtable(ui, name, cmdtable):
7820 def loadcmdtable(ui, name, cmdtable):
7824 """Load command functions from specified cmdtable
7821 """Load command functions from specified cmdtable
7825 """
7822 """
7826 overrides = [cmd for cmd in cmdtable if cmd in table]
7823 overrides = [cmd for cmd in cmdtable if cmd in table]
7827 if overrides:
7824 if overrides:
7828 ui.warn(
7825 ui.warn(
7829 _(b"extension '%s' overrides commands: %s\n")
7826 _(b"extension '%s' overrides commands: %s\n")
7830 % (name, b" ".join(overrides))
7827 % (name, b" ".join(overrides))
7831 )
7828 )
7832 table.update(cmdtable)
7829 table.update(cmdtable)
@@ -1,319 +1,329 b''
1 # changelog bisection for mercurial
1 # changelog bisection for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall
3 # Copyright 2007 Matt Mackall
4 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
4 # Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
5 #
5 #
6 # Inspired by git bisect, extension skeleton taken from mq.py.
6 # Inspired by git bisect, extension skeleton taken from mq.py.
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import collections
13 import collections
14 import contextlib
14
15
15 from .i18n import _
16 from .i18n import _
16 from .node import (
17 from .node import (
17 hex,
18 hex,
18 short,
19 short,
19 )
20 )
20 from . import error
21 from . import error
21
22
22
23
23 def bisect(repo, state):
24 def bisect(repo, state):
24 """find the next node (if any) for testing during a bisect search.
25 """find the next node (if any) for testing during a bisect search.
25 returns a (nodes, number, good) tuple.
26 returns a (nodes, number, good) tuple.
26
27
27 'nodes' is the final result of the bisect if 'number' is 0.
28 'nodes' is the final result of the bisect if 'number' is 0.
28 Otherwise 'number' indicates the remaining possible candidates for
29 Otherwise 'number' indicates the remaining possible candidates for
29 the search and 'nodes' contains the next bisect target.
30 the search and 'nodes' contains the next bisect target.
30 'good' is True if bisect is searching for a first good changeset, False
31 'good' is True if bisect is searching for a first good changeset, False
31 if searching for a first bad one.
32 if searching for a first bad one.
32 """
33 """
33
34
34 repo = repo.unfiltered()
35 repo = repo.unfiltered()
35 changelog = repo.changelog
36 changelog = repo.changelog
36 clparents = changelog.parentrevs
37 clparents = changelog.parentrevs
37 skip = {changelog.rev(n) for n in state[b'skip']}
38 skip = {changelog.rev(n) for n in state[b'skip']}
38
39
39 def buildancestors(bad, good):
40 def buildancestors(bad, good):
40 badrev = min([changelog.rev(n) for n in bad])
41 badrev = min([changelog.rev(n) for n in bad])
41 ancestors = collections.defaultdict(lambda: None)
42 ancestors = collections.defaultdict(lambda: None)
42 for rev in repo.revs(b"descendants(%ln) - ancestors(%ln)", good, good):
43 for rev in repo.revs(b"descendants(%ln) - ancestors(%ln)", good, good):
43 ancestors[rev] = []
44 ancestors[rev] = []
44 if ancestors[badrev] is None:
45 if ancestors[badrev] is None:
45 return badrev, None
46 return badrev, None
46 return badrev, ancestors
47 return badrev, ancestors
47
48
48 good = False
49 good = False
49 badrev, ancestors = buildancestors(state[b'bad'], state[b'good'])
50 badrev, ancestors = buildancestors(state[b'bad'], state[b'good'])
50 if not ancestors: # looking for bad to good transition?
51 if not ancestors: # looking for bad to good transition?
51 good = True
52 good = True
52 badrev, ancestors = buildancestors(state[b'good'], state[b'bad'])
53 badrev, ancestors = buildancestors(state[b'good'], state[b'bad'])
53 bad = changelog.node(badrev)
54 bad = changelog.node(badrev)
54 if not ancestors: # now we're confused
55 if not ancestors: # now we're confused
55 if (
56 if (
56 len(state[b'bad']) == 1
57 len(state[b'bad']) == 1
57 and len(state[b'good']) == 1
58 and len(state[b'good']) == 1
58 and state[b'bad'] != state[b'good']
59 and state[b'bad'] != state[b'good']
59 ):
60 ):
60 raise error.Abort(_(b"starting revisions are not directly related"))
61 raise error.Abort(_(b"starting revisions are not directly related"))
61 raise error.Abort(
62 raise error.Abort(
62 _(b"inconsistent state, %d:%s is good and bad")
63 _(b"inconsistent state, %d:%s is good and bad")
63 % (badrev, short(bad))
64 % (badrev, short(bad))
64 )
65 )
65
66
66 # build children dict
67 # build children dict
67 children = {}
68 children = {}
68 visit = collections.deque([badrev])
69 visit = collections.deque([badrev])
69 candidates = []
70 candidates = []
70 while visit:
71 while visit:
71 rev = visit.popleft()
72 rev = visit.popleft()
72 if ancestors[rev] == []:
73 if ancestors[rev] == []:
73 candidates.append(rev)
74 candidates.append(rev)
74 for prev in clparents(rev):
75 for prev in clparents(rev):
75 if prev != -1:
76 if prev != -1:
76 if prev in children:
77 if prev in children:
77 children[prev].append(rev)
78 children[prev].append(rev)
78 else:
79 else:
79 children[prev] = [rev]
80 children[prev] = [rev]
80 visit.append(prev)
81 visit.append(prev)
81
82
82 candidates.sort()
83 candidates.sort()
83 # have we narrowed it down to one entry?
84 # have we narrowed it down to one entry?
84 # or have all other possible candidates besides 'bad' have been skipped?
85 # or have all other possible candidates besides 'bad' have been skipped?
85 tot = len(candidates)
86 tot = len(candidates)
86 unskipped = [c for c in candidates if (c not in skip) and (c != badrev)]
87 unskipped = [c for c in candidates if (c not in skip) and (c != badrev)]
87 if tot == 1 or not unskipped:
88 if tot == 1 or not unskipped:
88 return ([changelog.node(c) for c in candidates], 0, good)
89 return ([changelog.node(c) for c in candidates], 0, good)
89 perfect = tot // 2
90 perfect = tot // 2
90
91
91 # find the best node to test
92 # find the best node to test
92 best_rev = None
93 best_rev = None
93 best_len = -1
94 best_len = -1
94 poison = set()
95 poison = set()
95 for rev in candidates:
96 for rev in candidates:
96 if rev in poison:
97 if rev in poison:
97 # poison children
98 # poison children
98 poison.update(children.get(rev, []))
99 poison.update(children.get(rev, []))
99 continue
100 continue
100
101
101 a = ancestors[rev] or [rev]
102 a = ancestors[rev] or [rev]
102 ancestors[rev] = None
103 ancestors[rev] = None
103
104
104 x = len(a) # number of ancestors
105 x = len(a) # number of ancestors
105 y = tot - x # number of non-ancestors
106 y = tot - x # number of non-ancestors
106 value = min(x, y) # how good is this test?
107 value = min(x, y) # how good is this test?
107 if value > best_len and rev not in skip:
108 if value > best_len and rev not in skip:
108 best_len = value
109 best_len = value
109 best_rev = rev
110 best_rev = rev
110 if value == perfect: # found a perfect candidate? quit early
111 if value == perfect: # found a perfect candidate? quit early
111 break
112 break
112
113
113 if y < perfect and rev not in skip: # all downhill from here?
114 if y < perfect and rev not in skip: # all downhill from here?
114 # poison children
115 # poison children
115 poison.update(children.get(rev, []))
116 poison.update(children.get(rev, []))
116 continue
117 continue
117
118
118 for c in children.get(rev, []):
119 for c in children.get(rev, []):
119 if ancestors[c]:
120 if ancestors[c]:
120 ancestors[c] = list(set(ancestors[c] + a))
121 ancestors[c] = list(set(ancestors[c] + a))
121 else:
122 else:
122 ancestors[c] = a + [c]
123 ancestors[c] = a + [c]
123
124
124 assert best_rev is not None
125 assert best_rev is not None
125 best_node = changelog.node(best_rev)
126 best_node = changelog.node(best_rev)
126
127
127 return ([best_node], tot, good)
128 return ([best_node], tot, good)
128
129
129
130
130 def extendrange(repo, state, nodes, good):
131 def extendrange(repo, state, nodes, good):
131 # bisect is incomplete when it ends on a merge node and
132 # bisect is incomplete when it ends on a merge node and
132 # one of the parent was not checked.
133 # one of the parent was not checked.
133 parents = repo[nodes[0]].parents()
134 parents = repo[nodes[0]].parents()
134 if len(parents) > 1:
135 if len(parents) > 1:
135 if good:
136 if good:
136 side = state[b'bad']
137 side = state[b'bad']
137 else:
138 else:
138 side = state[b'good']
139 side = state[b'good']
139 num = len(set(i.node() for i in parents) & set(side))
140 num = len(set(i.node() for i in parents) & set(side))
140 if num == 1:
141 if num == 1:
141 return parents[0].ancestor(parents[1])
142 return parents[0].ancestor(parents[1])
142 return None
143 return None
143
144
144
145
145 def load_state(repo):
146 def load_state(repo):
146 state = {b'current': [], b'good': [], b'bad': [], b'skip': []}
147 state = {b'current': [], b'good': [], b'bad': [], b'skip': []}
147 for l in repo.vfs.tryreadlines(b"bisect.state"):
148 for l in repo.vfs.tryreadlines(b"bisect.state"):
148 kind, node = l[:-1].split()
149 kind, node = l[:-1].split()
149 node = repo.unfiltered().lookup(node)
150 node = repo.unfiltered().lookup(node)
150 if kind not in state:
151 if kind not in state:
151 raise error.Abort(_(b"unknown bisect kind %s") % kind)
152 raise error.Abort(_(b"unknown bisect kind %s") % kind)
152 state[kind].append(node)
153 state[kind].append(node)
153 return state
154 return state
154
155
155
156
156 def save_state(repo, state):
157 def save_state(repo, state):
157 f = repo.vfs(b"bisect.state", b"w", atomictemp=True)
158 f = repo.vfs(b"bisect.state", b"w", atomictemp=True)
158 with repo.wlock():
159 with repo.wlock():
159 for kind in sorted(state):
160 for kind in sorted(state):
160 for node in state[kind]:
161 for node in state[kind]:
161 f.write(b"%s %s\n" % (kind, hex(node)))
162 f.write(b"%s %s\n" % (kind, hex(node)))
162 f.close()
163 f.close()
163
164
164
165
165 def resetstate(repo):
166 def resetstate(repo):
166 """remove any bisect state from the repository"""
167 """remove any bisect state from the repository"""
167 if repo.vfs.exists(b"bisect.state"):
168 if repo.vfs.exists(b"bisect.state"):
168 repo.vfs.unlink(b"bisect.state")
169 repo.vfs.unlink(b"bisect.state")
169
170
170
171
171 def checkstate(state):
172 def checkstate(state):
172 """check we have both 'good' and 'bad' to define a range
173 """check we have both 'good' and 'bad' to define a range
173
174
174 Raise Abort exception otherwise."""
175 Raise Abort exception otherwise."""
175 if state[b'good'] and state[b'bad']:
176 if state[b'good'] and state[b'bad']:
176 return True
177 return True
177 if not state[b'good']:
178 if not state[b'good']:
178 raise error.Abort(_(b'cannot bisect (no known good revisions)'))
179 raise error.Abort(_(b'cannot bisect (no known good revisions)'))
179 else:
180 else:
180 raise error.Abort(_(b'cannot bisect (no known bad revisions)'))
181 raise error.Abort(_(b'cannot bisect (no known bad revisions)'))
181
182
182
183
184 @contextlib.contextmanager
185 def restore_state(repo, state, node):
186 try:
187 yield
188 finally:
189 state[b'current'] = [node]
190 save_state(repo, state)
191
192
183 def get(repo, status):
193 def get(repo, status):
184 """
194 """
185 Return a list of revision(s) that match the given status:
195 Return a list of revision(s) that match the given status:
186
196
187 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
197 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
188 - ``goods``, ``bads`` : csets topologically good/bad
198 - ``goods``, ``bads`` : csets topologically good/bad
189 - ``range`` : csets taking part in the bisection
199 - ``range`` : csets taking part in the bisection
190 - ``pruned`` : csets that are goods, bads or skipped
200 - ``pruned`` : csets that are goods, bads or skipped
191 - ``untested`` : csets whose fate is yet unknown
201 - ``untested`` : csets whose fate is yet unknown
192 - ``ignored`` : csets ignored due to DAG topology
202 - ``ignored`` : csets ignored due to DAG topology
193 - ``current`` : the cset currently being bisected
203 - ``current`` : the cset currently being bisected
194 """
204 """
195 state = load_state(repo)
205 state = load_state(repo)
196 if status in (b'good', b'bad', b'skip', b'current'):
206 if status in (b'good', b'bad', b'skip', b'current'):
197 return map(repo.unfiltered().changelog.rev, state[status])
207 return map(repo.unfiltered().changelog.rev, state[status])
198 else:
208 else:
199 # In the following sets, we do *not* call 'bisect()' with more
209 # In the following sets, we do *not* call 'bisect()' with more
200 # than one level of recursion, because that can be very, very
210 # than one level of recursion, because that can be very, very
201 # time consuming. Instead, we always develop the expression as
211 # time consuming. Instead, we always develop the expression as
202 # much as possible.
212 # much as possible.
203
213
204 # 'range' is all csets that make the bisection:
214 # 'range' is all csets that make the bisection:
205 # - have a good ancestor and a bad descendant, or conversely
215 # - have a good ancestor and a bad descendant, or conversely
206 # that's because the bisection can go either way
216 # that's because the bisection can go either way
207 range = b'( bisect(bad)::bisect(good) | bisect(good)::bisect(bad) )'
217 range = b'( bisect(bad)::bisect(good) | bisect(good)::bisect(bad) )'
208
218
209 _t = repo.revs(b'bisect(good)::bisect(bad)')
219 _t = repo.revs(b'bisect(good)::bisect(bad)')
210 # The sets of topologically good or bad csets
220 # The sets of topologically good or bad csets
211 if len(_t) == 0:
221 if len(_t) == 0:
212 # Goods are topologically after bads
222 # Goods are topologically after bads
213 goods = b'bisect(good)::' # Pruned good csets
223 goods = b'bisect(good)::' # Pruned good csets
214 bads = b'::bisect(bad)' # Pruned bad csets
224 bads = b'::bisect(bad)' # Pruned bad csets
215 else:
225 else:
216 # Goods are topologically before bads
226 # Goods are topologically before bads
217 goods = b'::bisect(good)' # Pruned good csets
227 goods = b'::bisect(good)' # Pruned good csets
218 bads = b'bisect(bad)::' # Pruned bad csets
228 bads = b'bisect(bad)::' # Pruned bad csets
219
229
220 # 'pruned' is all csets whose fate is already known: good, bad, skip
230 # 'pruned' is all csets whose fate is already known: good, bad, skip
221 skips = b'bisect(skip)' # Pruned skipped csets
231 skips = b'bisect(skip)' # Pruned skipped csets
222 pruned = b'( (%s) | (%s) | (%s) )' % (goods, bads, skips)
232 pruned = b'( (%s) | (%s) | (%s) )' % (goods, bads, skips)
223
233
224 # 'untested' is all cset that are- in 'range', but not in 'pruned'
234 # 'untested' is all cset that are- in 'range', but not in 'pruned'
225 untested = b'( (%s) - (%s) )' % (range, pruned)
235 untested = b'( (%s) - (%s) )' % (range, pruned)
226
236
227 # 'ignored' is all csets that were not used during the bisection
237 # 'ignored' is all csets that were not used during the bisection
228 # due to DAG topology, but may however have had an impact.
238 # due to DAG topology, but may however have had an impact.
229 # E.g., a branch merged between bads and goods, but whose branch-
239 # E.g., a branch merged between bads and goods, but whose branch-
230 # point is out-side of the range.
240 # point is out-side of the range.
231 iba = b'::bisect(bad) - ::bisect(good)' # Ignored bads' ancestors
241 iba = b'::bisect(bad) - ::bisect(good)' # Ignored bads' ancestors
232 iga = b'::bisect(good) - ::bisect(bad)' # Ignored goods' ancestors
242 iga = b'::bisect(good) - ::bisect(bad)' # Ignored goods' ancestors
233 ignored = b'( ( (%s) | (%s) ) - (%s) )' % (iba, iga, range)
243 ignored = b'( ( (%s) | (%s) ) - (%s) )' % (iba, iga, range)
234
244
235 if status == b'range':
245 if status == b'range':
236 return repo.revs(range)
246 return repo.revs(range)
237 elif status == b'pruned':
247 elif status == b'pruned':
238 return repo.revs(pruned)
248 return repo.revs(pruned)
239 elif status == b'untested':
249 elif status == b'untested':
240 return repo.revs(untested)
250 return repo.revs(untested)
241 elif status == b'ignored':
251 elif status == b'ignored':
242 return repo.revs(ignored)
252 return repo.revs(ignored)
243 elif status == b"goods":
253 elif status == b"goods":
244 return repo.revs(goods)
254 return repo.revs(goods)
245 elif status == b"bads":
255 elif status == b"bads":
246 return repo.revs(bads)
256 return repo.revs(bads)
247 else:
257 else:
248 raise error.ParseError(_(b'invalid bisect state'))
258 raise error.ParseError(_(b'invalid bisect state'))
249
259
250
260
251 def label(repo, node):
261 def label(repo, node):
252 rev = repo.changelog.rev(node)
262 rev = repo.changelog.rev(node)
253
263
254 # Try explicit sets
264 # Try explicit sets
255 if rev in get(repo, b'good'):
265 if rev in get(repo, b'good'):
256 # i18n: bisect changeset status
266 # i18n: bisect changeset status
257 return _(b'good')
267 return _(b'good')
258 if rev in get(repo, b'bad'):
268 if rev in get(repo, b'bad'):
259 # i18n: bisect changeset status
269 # i18n: bisect changeset status
260 return _(b'bad')
270 return _(b'bad')
261 if rev in get(repo, b'skip'):
271 if rev in get(repo, b'skip'):
262 # i18n: bisect changeset status
272 # i18n: bisect changeset status
263 return _(b'skipped')
273 return _(b'skipped')
264 if rev in get(repo, b'untested') or rev in get(repo, b'current'):
274 if rev in get(repo, b'untested') or rev in get(repo, b'current'):
265 # i18n: bisect changeset status
275 # i18n: bisect changeset status
266 return _(b'untested')
276 return _(b'untested')
267 if rev in get(repo, b'ignored'):
277 if rev in get(repo, b'ignored'):
268 # i18n: bisect changeset status
278 # i18n: bisect changeset status
269 return _(b'ignored')
279 return _(b'ignored')
270
280
271 # Try implicit sets
281 # Try implicit sets
272 if rev in get(repo, b'goods'):
282 if rev in get(repo, b'goods'):
273 # i18n: bisect changeset status
283 # i18n: bisect changeset status
274 return _(b'good (implicit)')
284 return _(b'good (implicit)')
275 if rev in get(repo, b'bads'):
285 if rev in get(repo, b'bads'):
276 # i18n: bisect changeset status
286 # i18n: bisect changeset status
277 return _(b'bad (implicit)')
287 return _(b'bad (implicit)')
278
288
279 return None
289 return None
280
290
281
291
282 def printresult(ui, repo, state, displayer, nodes, good):
292 def printresult(ui, repo, state, displayer, nodes, good):
283 repo = repo.unfiltered()
293 repo = repo.unfiltered()
284 if len(nodes) == 1:
294 if len(nodes) == 1:
285 # narrowed it down to a single revision
295 # narrowed it down to a single revision
286 if good:
296 if good:
287 ui.write(_(b"The first good revision is:\n"))
297 ui.write(_(b"The first good revision is:\n"))
288 else:
298 else:
289 ui.write(_(b"The first bad revision is:\n"))
299 ui.write(_(b"The first bad revision is:\n"))
290 displayer.show(repo[nodes[0]])
300 displayer.show(repo[nodes[0]])
291 extendnode = extendrange(repo, state, nodes, good)
301 extendnode = extendrange(repo, state, nodes, good)
292 if extendnode is not None:
302 if extendnode is not None:
293 ui.write(
303 ui.write(
294 _(
304 _(
295 b'Not all ancestors of this changeset have been'
305 b'Not all ancestors of this changeset have been'
296 b' checked.\nUse bisect --extend to continue the '
306 b' checked.\nUse bisect --extend to continue the '
297 b'bisection from\nthe common ancestor, %s.\n'
307 b'bisection from\nthe common ancestor, %s.\n'
298 )
308 )
299 % extendnode
309 % extendnode
300 )
310 )
301 else:
311 else:
302 # multiple possible revisions
312 # multiple possible revisions
303 if good:
313 if good:
304 ui.write(
314 ui.write(
305 _(
315 _(
306 b"Due to skipped revisions, the first "
316 b"Due to skipped revisions, the first "
307 b"good revision could be any of:\n"
317 b"good revision could be any of:\n"
308 )
318 )
309 )
319 )
310 else:
320 else:
311 ui.write(
321 ui.write(
312 _(
322 _(
313 b"Due to skipped revisions, the first "
323 b"Due to skipped revisions, the first "
314 b"bad revision could be any of:\n"
324 b"bad revision could be any of:\n"
315 )
325 )
316 )
326 )
317 for n in nodes:
327 for n in nodes:
318 displayer.show(repo[n])
328 displayer.show(repo[n])
319 displayer.close()
329 displayer.close()
General Comments 0
You need to be logged in to leave comments. Login now