##// END OF EJS Templates
grep: add stub class that maintains cache and states of grep operation...
Yuya Nishihara -
r46289:494642ed default
parent child Browse files
Show More
@@ -1,7771 +1,7773 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 errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 wdirhex,
21 wdirhex,
22 wdirrev,
22 wdirrev,
23 )
23 )
24 from .pycompat import open
24 from .pycompat import open
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 copies,
31 copies,
32 debugcommands as debugcommandsmod,
32 debugcommands as debugcommandsmod,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 discovery,
35 discovery,
36 encoding,
36 encoding,
37 error,
37 error,
38 exchange,
38 exchange,
39 extensions,
39 extensions,
40 filemerge,
40 filemerge,
41 formatter,
41 formatter,
42 graphmod,
42 graphmod,
43 grep as grepmod,
43 grep as grepmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 match as matchmod,
48 match as matchmod,
49 merge as mergemod,
49 merge as mergemod,
50 mergestate as mergestatemod,
50 mergestate as mergestatemod,
51 narrowspec,
51 narrowspec,
52 obsolete,
52 obsolete,
53 obsutil,
53 obsutil,
54 patch,
54 patch,
55 phases,
55 phases,
56 pycompat,
56 pycompat,
57 rcutil,
57 rcutil,
58 registrar,
58 registrar,
59 requirements,
59 requirements,
60 revsetlang,
60 revsetlang,
61 rewriteutil,
61 rewriteutil,
62 scmutil,
62 scmutil,
63 server,
63 server,
64 shelve as shelvemod,
64 shelve as shelvemod,
65 state as statemod,
65 state as statemod,
66 streamclone,
66 streamclone,
67 tags as tagsmod,
67 tags as tagsmod,
68 ui as uimod,
68 ui as uimod,
69 util,
69 util,
70 verify as verifymod,
70 verify as verifymod,
71 vfs as vfsmod,
71 vfs as vfsmod,
72 wireprotoserver,
72 wireprotoserver,
73 )
73 )
74 from .utils import (
74 from .utils import (
75 dateutil,
75 dateutil,
76 stringutil,
76 stringutil,
77 )
77 )
78
78
79 table = {}
79 table = {}
80 table.update(debugcommandsmod.command._table)
80 table.update(debugcommandsmod.command._table)
81
81
82 command = registrar.command(table)
82 command = registrar.command(table)
83 INTENT_READONLY = registrar.INTENT_READONLY
83 INTENT_READONLY = registrar.INTENT_READONLY
84
84
85 # common command options
85 # common command options
86
86
87 globalopts = [
87 globalopts = [
88 (
88 (
89 b'R',
89 b'R',
90 b'repository',
90 b'repository',
91 b'',
91 b'',
92 _(b'repository root directory or name of overlay bundle file'),
92 _(b'repository root directory or name of overlay bundle file'),
93 _(b'REPO'),
93 _(b'REPO'),
94 ),
94 ),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
96 (
96 (
97 b'y',
97 b'y',
98 b'noninteractive',
98 b'noninteractive',
99 None,
99 None,
100 _(
100 _(
101 b'do not prompt, automatically pick the first choice for all prompts'
101 b'do not prompt, automatically pick the first choice for all prompts'
102 ),
102 ),
103 ),
103 ),
104 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'q', b'quiet', None, _(b'suppress output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
106 (
106 (
107 b'',
107 b'',
108 b'color',
108 b'color',
109 b'',
109 b'',
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
111 # and should not be translated
111 # and should not be translated
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
113 _(b'TYPE'),
113 _(b'TYPE'),
114 ),
114 ),
115 (
115 (
116 b'',
116 b'',
117 b'config',
117 b'config',
118 [],
118 [],
119 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'set/override config option (use \'section.name=value\')'),
120 _(b'CONFIG'),
120 _(b'CONFIG'),
121 ),
121 ),
122 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debug', None, _(b'enable debugging output')),
123 (b'', b'debugger', None, _(b'start debugger')),
123 (b'', b'debugger', None, _(b'start debugger')),
124 (
124 (
125 b'',
125 b'',
126 b'encoding',
126 b'encoding',
127 encoding.encoding,
127 encoding.encoding,
128 _(b'set the charset encoding'),
128 _(b'set the charset encoding'),
129 _(b'ENCODE'),
129 _(b'ENCODE'),
130 ),
130 ),
131 (
131 (
132 b'',
132 b'',
133 b'encodingmode',
133 b'encodingmode',
134 encoding.encodingmode,
134 encoding.encodingmode,
135 _(b'set the charset encoding mode'),
135 _(b'set the charset encoding mode'),
136 _(b'MODE'),
136 _(b'MODE'),
137 ),
137 ),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
139 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'time', None, _(b'time how long the command takes')),
140 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'profile', None, _(b'print command execution profile')),
141 (b'', b'version', None, _(b'output version information and exit')),
141 (b'', b'version', None, _(b'output version information and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
144 (
144 (
145 b'',
145 b'',
146 b'pager',
146 b'pager',
147 b'auto',
147 b'auto',
148 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b"when to paginate (boolean, always, auto, or never)"),
149 _(b'TYPE'),
149 _(b'TYPE'),
150 ),
150 ),
151 ]
151 ]
152
152
153 dryrunopts = cmdutil.dryrunopts
153 dryrunopts = cmdutil.dryrunopts
154 remoteopts = cmdutil.remoteopts
154 remoteopts = cmdutil.remoteopts
155 walkopts = cmdutil.walkopts
155 walkopts = cmdutil.walkopts
156 commitopts = cmdutil.commitopts
156 commitopts = cmdutil.commitopts
157 commitopts2 = cmdutil.commitopts2
157 commitopts2 = cmdutil.commitopts2
158 commitopts3 = cmdutil.commitopts3
158 commitopts3 = cmdutil.commitopts3
159 formatteropts = cmdutil.formatteropts
159 formatteropts = cmdutil.formatteropts
160 templateopts = cmdutil.templateopts
160 templateopts = cmdutil.templateopts
161 logopts = cmdutil.logopts
161 logopts = cmdutil.logopts
162 diffopts = cmdutil.diffopts
162 diffopts = cmdutil.diffopts
163 diffwsopts = cmdutil.diffwsopts
163 diffwsopts = cmdutil.diffwsopts
164 diffopts2 = cmdutil.diffopts2
164 diffopts2 = cmdutil.diffopts2
165 mergetoolopts = cmdutil.mergetoolopts
165 mergetoolopts = cmdutil.mergetoolopts
166 similarityopts = cmdutil.similarityopts
166 similarityopts = cmdutil.similarityopts
167 subrepoopts = cmdutil.subrepoopts
167 subrepoopts = cmdutil.subrepoopts
168 debugrevlogopts = cmdutil.debugrevlogopts
168 debugrevlogopts = cmdutil.debugrevlogopts
169
169
170 # Commands start here, listed alphabetically
170 # Commands start here, listed alphabetically
171
171
172
172
173 @command(
173 @command(
174 b'abort',
174 b'abort',
175 dryrunopts,
175 dryrunopts,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
177 helpbasic=True,
177 helpbasic=True,
178 )
178 )
179 def abort(ui, repo, **opts):
179 def abort(ui, repo, **opts):
180 """abort an unfinished operation (EXPERIMENTAL)
180 """abort an unfinished operation (EXPERIMENTAL)
181
181
182 Aborts a multistep operation like graft, histedit, rebase, merge,
182 Aborts a multistep operation like graft, histedit, rebase, merge,
183 and unshelve if they are in an unfinished state.
183 and unshelve if they are in an unfinished state.
184
184
185 use --dry-run/-n to dry run the command.
185 use --dry-run/-n to dry run the command.
186 """
186 """
187 dryrun = opts.get('dry_run')
187 dryrun = opts.get('dry_run')
188 abortstate = cmdutil.getunfinishedstate(repo)
188 abortstate = cmdutil.getunfinishedstate(repo)
189 if not abortstate:
189 if not abortstate:
190 raise error.Abort(_(b'no operation in progress'))
190 raise error.Abort(_(b'no operation in progress'))
191 if not abortstate.abortfunc:
191 if not abortstate.abortfunc:
192 raise error.Abort(
192 raise error.Abort(
193 (
193 (
194 _(b"%s in progress but does not support 'hg abort'")
194 _(b"%s in progress but does not support 'hg abort'")
195 % (abortstate._opname)
195 % (abortstate._opname)
196 ),
196 ),
197 hint=abortstate.hint(),
197 hint=abortstate.hint(),
198 )
198 )
199 if dryrun:
199 if dryrun:
200 ui.status(
200 ui.status(
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
202 )
202 )
203 return
203 return
204 return abortstate.abortfunc(ui, repo)
204 return abortstate.abortfunc(ui, repo)
205
205
206
206
207 @command(
207 @command(
208 b'add',
208 b'add',
209 walkopts + subrepoopts + dryrunopts,
209 walkopts + subrepoopts + dryrunopts,
210 _(b'[OPTION]... [FILE]...'),
210 _(b'[OPTION]... [FILE]...'),
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
212 helpbasic=True,
212 helpbasic=True,
213 inferrepo=True,
213 inferrepo=True,
214 )
214 )
215 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
216 """add the specified files on the next commit
216 """add the specified files on the next commit
217
217
218 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
219 repository.
219 repository.
220
220
221 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
222 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
223
223
224 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
225 files matching ``.hgignore``).
225 files matching ``.hgignore``).
226
226
227 .. container:: verbose
227 .. container:: verbose
228
228
229 Examples:
229 Examples:
230
230
231 - New (unknown) files are added
231 - New (unknown) files are added
232 automatically by :hg:`add`::
232 automatically by :hg:`add`::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ? foo.c
237 ? foo.c
238 $ hg add
238 $ hg add
239 adding foo.c
239 adding foo.c
240 $ hg status
240 $ hg status
241 A foo.c
241 A foo.c
242
242
243 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
244
244
245 $ ls
245 $ ls
246 bar.c foo.c
246 bar.c foo.c
247 $ hg status
247 $ hg status
248 ? bar.c
248 ? bar.c
249 ? foo.c
249 ? foo.c
250 $ hg add bar.c
250 $ hg add bar.c
251 $ hg status
251 $ hg status
252 A bar.c
252 A bar.c
253 ? foo.c
253 ? foo.c
254
254
255 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
256 """
256 """
257
257
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
261 return rejected and 1 or 0
261 return rejected and 1 or 0
262
262
263
263
264 @command(
264 @command(
265 b'addremove',
265 b'addremove',
266 similarityopts + subrepoopts + walkopts + dryrunopts,
266 similarityopts + subrepoopts + walkopts + dryrunopts,
267 _(b'[OPTION]... [FILE]...'),
267 _(b'[OPTION]... [FILE]...'),
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
269 inferrepo=True,
269 inferrepo=True,
270 )
270 )
271 def addremove(ui, repo, *pats, **opts):
271 def addremove(ui, repo, *pats, **opts):
272 """add all new files, delete all missing files
272 """add all new files, delete all missing files
273
273
274 Add all new files and remove all missing files from the
274 Add all new files and remove all missing files from the
275 repository.
275 repository.
276
276
277 Unless names are given, new files are ignored if they match any of
277 Unless names are given, new files are ignored if they match any of
278 the patterns in ``.hgignore``. As with add, these changes take
278 the patterns in ``.hgignore``. As with add, these changes take
279 effect at the next commit.
279 effect at the next commit.
280
280
281 Use the -s/--similarity option to detect renamed files. This
281 Use the -s/--similarity option to detect renamed files. This
282 option takes a percentage between 0 (disabled) and 100 (files must
282 option takes a percentage between 0 (disabled) and 100 (files must
283 be identical) as its parameter. With a parameter greater than 0,
283 be identical) as its parameter. With a parameter greater than 0,
284 this compares every removed file with every added file and records
284 this compares every removed file with every added file and records
285 those similar enough as renames. Detecting renamed files this way
285 those similar enough as renames. Detecting renamed files this way
286 can be expensive. After using this option, :hg:`status -C` can be
286 can be expensive. After using this option, :hg:`status -C` can be
287 used to check which files were identified as moved or renamed. If
287 used to check which files were identified as moved or renamed. If
288 not specified, -s/--similarity defaults to 100 and only renames of
288 not specified, -s/--similarity defaults to 100 and only renames of
289 identical files are detected.
289 identical files are detected.
290
290
291 .. container:: verbose
291 .. container:: verbose
292
292
293 Examples:
293 Examples:
294
294
295 - A number of files (bar.c and foo.c) are new,
295 - A number of files (bar.c and foo.c) are new,
296 while foobar.c has been removed (without using :hg:`remove`)
296 while foobar.c has been removed (without using :hg:`remove`)
297 from the repository::
297 from the repository::
298
298
299 $ ls
299 $ ls
300 bar.c foo.c
300 bar.c foo.c
301 $ hg status
301 $ hg status
302 ! foobar.c
302 ! foobar.c
303 ? bar.c
303 ? bar.c
304 ? foo.c
304 ? foo.c
305 $ hg addremove
305 $ hg addremove
306 adding bar.c
306 adding bar.c
307 adding foo.c
307 adding foo.c
308 removing foobar.c
308 removing foobar.c
309 $ hg status
309 $ hg status
310 A bar.c
310 A bar.c
311 A foo.c
311 A foo.c
312 R foobar.c
312 R foobar.c
313
313
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
315 Afterwards, it was edited slightly::
315 Afterwards, it was edited slightly::
316
316
317 $ ls
317 $ ls
318 foo.c
318 foo.c
319 $ hg status
319 $ hg status
320 ! foobar.c
320 ! foobar.c
321 ? foo.c
321 ? foo.c
322 $ hg addremove --similarity 90
322 $ hg addremove --similarity 90
323 removing foobar.c
323 removing foobar.c
324 adding foo.c
324 adding foo.c
325 recording removal of foobar.c as rename to foo.c (94% similar)
325 recording removal of foobar.c as rename to foo.c (94% similar)
326 $ hg status -C
326 $ hg status -C
327 A foo.c
327 A foo.c
328 foobar.c
328 foobar.c
329 R foobar.c
329 R foobar.c
330
330
331 Returns 0 if all files are successfully added.
331 Returns 0 if all files are successfully added.
332 """
332 """
333 opts = pycompat.byteskwargs(opts)
333 opts = pycompat.byteskwargs(opts)
334 if not opts.get(b'similarity'):
334 if not opts.get(b'similarity'):
335 opts[b'similarity'] = b'100'
335 opts[b'similarity'] = b'100'
336 matcher = scmutil.match(repo[None], pats, opts)
336 matcher = scmutil.match(repo[None], pats, opts)
337 relative = scmutil.anypats(pats, opts)
337 relative = scmutil.anypats(pats, opts)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
340
340
341
341
342 @command(
342 @command(
343 b'annotate|blame',
343 b'annotate|blame',
344 [
344 [
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
346 (
346 (
347 b'',
347 b'',
348 b'follow',
348 b'follow',
349 None,
349 None,
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
351 ),
351 ),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
353 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'a', b'text', None, _(b'treat all files as text')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
355 (b'f', b'file', None, _(b'list the filename')),
355 (b'f', b'file', None, _(b'list the filename')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
359 (
359 (
360 b'l',
360 b'l',
361 b'line-number',
361 b'line-number',
362 None,
362 None,
363 _(b'show line number at the first appearance'),
363 _(b'show line number at the first appearance'),
364 ),
364 ),
365 (
365 (
366 b'',
366 b'',
367 b'skip',
367 b'skip',
368 [],
368 [],
369 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'revset to not display (EXPERIMENTAL)'),
370 _(b'REV'),
370 _(b'REV'),
371 ),
371 ),
372 ]
372 ]
373 + diffwsopts
373 + diffwsopts
374 + walkopts
374 + walkopts
375 + formatteropts,
375 + formatteropts,
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
378 helpbasic=True,
378 helpbasic=True,
379 inferrepo=True,
379 inferrepo=True,
380 )
380 )
381 def annotate(ui, repo, *pats, **opts):
381 def annotate(ui, repo, *pats, **opts):
382 """show changeset information by line for each file
382 """show changeset information by line for each file
383
383
384 List changes in files, showing the revision id responsible for
384 List changes in files, showing the revision id responsible for
385 each line.
385 each line.
386
386
387 This command is useful for discovering when a change was made and
387 This command is useful for discovering when a change was made and
388 by whom.
388 by whom.
389
389
390 If you include --file, --user, or --date, the revision number is
390 If you include --file, --user, or --date, the revision number is
391 suppressed unless you also include --number.
391 suppressed unless you also include --number.
392
392
393 Without the -a/--text option, annotate will avoid processing files
393 Without the -a/--text option, annotate will avoid processing files
394 it detects as binary. With -a, annotate will annotate the file
394 it detects as binary. With -a, annotate will annotate the file
395 anyway, although the results will probably be neither useful
395 anyway, although the results will probably be neither useful
396 nor desirable.
396 nor desirable.
397
397
398 .. container:: verbose
398 .. container:: verbose
399
399
400 Template:
400 Template:
401
401
402 The following keywords are supported in addition to the common template
402 The following keywords are supported in addition to the common template
403 keywords and functions. See also :hg:`help templates`.
403 keywords and functions. See also :hg:`help templates`.
404
404
405 :lines: List of lines with annotation data.
405 :lines: List of lines with annotation data.
406 :path: String. Repository-absolute path of the specified file.
406 :path: String. Repository-absolute path of the specified file.
407
407
408 And each entry of ``{lines}`` provides the following sub-keywords in
408 And each entry of ``{lines}`` provides the following sub-keywords in
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
410
410
411 :line: String. Line content.
411 :line: String. Line content.
412 :lineno: Integer. Line number at that revision.
412 :lineno: Integer. Line number at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
414
414
415 See :hg:`help templates.operators` for the list expansion syntax.
415 See :hg:`help templates.operators` for the list expansion syntax.
416
416
417 Returns 0 on success.
417 Returns 0 on success.
418 """
418 """
419 opts = pycompat.byteskwargs(opts)
419 opts = pycompat.byteskwargs(opts)
420 if not pats:
420 if not pats:
421 raise error.Abort(_(b'at least one filename or pattern is required'))
421 raise error.Abort(_(b'at least one filename or pattern is required'))
422
422
423 if opts.get(b'follow'):
423 if opts.get(b'follow'):
424 # --follow is deprecated and now just an alias for -f/--file
424 # --follow is deprecated and now just an alias for -f/--file
425 # to mimic the behavior of Mercurial before version 1.5
425 # to mimic the behavior of Mercurial before version 1.5
426 opts[b'file'] = True
426 opts[b'file'] = True
427
427
428 if (
428 if (
429 not opts.get(b'user')
429 not opts.get(b'user')
430 and not opts.get(b'changeset')
430 and not opts.get(b'changeset')
431 and not opts.get(b'date')
431 and not opts.get(b'date')
432 and not opts.get(b'file')
432 and not opts.get(b'file')
433 ):
433 ):
434 opts[b'number'] = True
434 opts[b'number'] = True
435
435
436 linenumber = opts.get(b'line_number') is not None
436 linenumber = opts.get(b'line_number') is not None
437 if (
437 if (
438 linenumber
438 linenumber
439 and (not opts.get(b'changeset'))
439 and (not opts.get(b'changeset'))
440 and (not opts.get(b'number'))
440 and (not opts.get(b'number'))
441 ):
441 ):
442 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
442 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
443
443
444 rev = opts.get(b'rev')
444 rev = opts.get(b'rev')
445 if rev:
445 if rev:
446 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
446 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
447 ctx = scmutil.revsingle(repo, rev)
447 ctx = scmutil.revsingle(repo, rev)
448
448
449 ui.pager(b'annotate')
449 ui.pager(b'annotate')
450 rootfm = ui.formatter(b'annotate', opts)
450 rootfm = ui.formatter(b'annotate', opts)
451 if ui.debugflag:
451 if ui.debugflag:
452 shorthex = pycompat.identity
452 shorthex = pycompat.identity
453 else:
453 else:
454
454
455 def shorthex(h):
455 def shorthex(h):
456 return h[:12]
456 return h[:12]
457
457
458 if ui.quiet:
458 if ui.quiet:
459 datefunc = dateutil.shortdate
459 datefunc = dateutil.shortdate
460 else:
460 else:
461 datefunc = dateutil.datestr
461 datefunc = dateutil.datestr
462 if ctx.rev() is None:
462 if ctx.rev() is None:
463 if opts.get(b'changeset'):
463 if opts.get(b'changeset'):
464 # omit "+" suffix which is appended to node hex
464 # omit "+" suffix which is appended to node hex
465 def formatrev(rev):
465 def formatrev(rev):
466 if rev == wdirrev:
466 if rev == wdirrev:
467 return b'%d' % ctx.p1().rev()
467 return b'%d' % ctx.p1().rev()
468 else:
468 else:
469 return b'%d' % rev
469 return b'%d' % rev
470
470
471 else:
471 else:
472
472
473 def formatrev(rev):
473 def formatrev(rev):
474 if rev == wdirrev:
474 if rev == wdirrev:
475 return b'%d+' % ctx.p1().rev()
475 return b'%d+' % ctx.p1().rev()
476 else:
476 else:
477 return b'%d ' % rev
477 return b'%d ' % rev
478
478
479 def formathex(h):
479 def formathex(h):
480 if h == wdirhex:
480 if h == wdirhex:
481 return b'%s+' % shorthex(hex(ctx.p1().node()))
481 return b'%s+' % shorthex(hex(ctx.p1().node()))
482 else:
482 else:
483 return b'%s ' % shorthex(h)
483 return b'%s ' % shorthex(h)
484
484
485 else:
485 else:
486 formatrev = b'%d'.__mod__
486 formatrev = b'%d'.__mod__
487 formathex = shorthex
487 formathex = shorthex
488
488
489 opmap = [
489 opmap = [
490 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
490 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
491 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
491 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
492 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
492 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
493 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
493 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
494 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
494 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
495 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
495 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
496 ]
496 ]
497 opnamemap = {
497 opnamemap = {
498 b'rev': b'number',
498 b'rev': b'number',
499 b'node': b'changeset',
499 b'node': b'changeset',
500 b'path': b'file',
500 b'path': b'file',
501 b'lineno': b'line_number',
501 b'lineno': b'line_number',
502 }
502 }
503
503
504 if rootfm.isplain():
504 if rootfm.isplain():
505
505
506 def makefunc(get, fmt):
506 def makefunc(get, fmt):
507 return lambda x: fmt(get(x))
507 return lambda x: fmt(get(x))
508
508
509 else:
509 else:
510
510
511 def makefunc(get, fmt):
511 def makefunc(get, fmt):
512 return get
512 return get
513
513
514 datahint = rootfm.datahint()
514 datahint = rootfm.datahint()
515 funcmap = [
515 funcmap = [
516 (makefunc(get, fmt), sep)
516 (makefunc(get, fmt), sep)
517 for fn, sep, get, fmt in opmap
517 for fn, sep, get, fmt in opmap
518 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
518 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
519 ]
519 ]
520 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
520 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
521 fields = b' '.join(
521 fields = b' '.join(
522 fn
522 fn
523 for fn, sep, get, fmt in opmap
523 for fn, sep, get, fmt in opmap
524 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
524 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
525 )
525 )
526
526
527 def bad(x, y):
527 def bad(x, y):
528 raise error.Abort(b"%s: %s" % (x, y))
528 raise error.Abort(b"%s: %s" % (x, y))
529
529
530 m = scmutil.match(ctx, pats, opts, badfn=bad)
530 m = scmutil.match(ctx, pats, opts, badfn=bad)
531
531
532 follow = not opts.get(b'no_follow')
532 follow = not opts.get(b'no_follow')
533 diffopts = patch.difffeatureopts(
533 diffopts = patch.difffeatureopts(
534 ui, opts, section=b'annotate', whitespace=True
534 ui, opts, section=b'annotate', whitespace=True
535 )
535 )
536 skiprevs = opts.get(b'skip')
536 skiprevs = opts.get(b'skip')
537 if skiprevs:
537 if skiprevs:
538 skiprevs = scmutil.revrange(repo, skiprevs)
538 skiprevs = scmutil.revrange(repo, skiprevs)
539
539
540 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
540 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
541 for abs in ctx.walk(m):
541 for abs in ctx.walk(m):
542 fctx = ctx[abs]
542 fctx = ctx[abs]
543 rootfm.startitem()
543 rootfm.startitem()
544 rootfm.data(path=abs)
544 rootfm.data(path=abs)
545 if not opts.get(b'text') and fctx.isbinary():
545 if not opts.get(b'text') and fctx.isbinary():
546 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
546 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
547 continue
547 continue
548
548
549 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
549 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
550 lines = fctx.annotate(
550 lines = fctx.annotate(
551 follow=follow, skiprevs=skiprevs, diffopts=diffopts
551 follow=follow, skiprevs=skiprevs, diffopts=diffopts
552 )
552 )
553 if not lines:
553 if not lines:
554 fm.end()
554 fm.end()
555 continue
555 continue
556 formats = []
556 formats = []
557 pieces = []
557 pieces = []
558
558
559 for f, sep in funcmap:
559 for f, sep in funcmap:
560 l = [f(n) for n in lines]
560 l = [f(n) for n in lines]
561 if fm.isplain():
561 if fm.isplain():
562 sizes = [encoding.colwidth(x) for x in l]
562 sizes = [encoding.colwidth(x) for x in l]
563 ml = max(sizes)
563 ml = max(sizes)
564 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
564 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
565 else:
565 else:
566 formats.append([b'%s'] * len(l))
566 formats.append([b'%s'] * len(l))
567 pieces.append(l)
567 pieces.append(l)
568
568
569 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
569 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
570 fm.startitem()
570 fm.startitem()
571 fm.context(fctx=n.fctx)
571 fm.context(fctx=n.fctx)
572 fm.write(fields, b"".join(f), *p)
572 fm.write(fields, b"".join(f), *p)
573 if n.skip:
573 if n.skip:
574 fmt = b"* %s"
574 fmt = b"* %s"
575 else:
575 else:
576 fmt = b": %s"
576 fmt = b": %s"
577 fm.write(b'line', fmt, n.text)
577 fm.write(b'line', fmt, n.text)
578
578
579 if not lines[-1].text.endswith(b'\n'):
579 if not lines[-1].text.endswith(b'\n'):
580 fm.plain(b'\n')
580 fm.plain(b'\n')
581 fm.end()
581 fm.end()
582
582
583 rootfm.end()
583 rootfm.end()
584
584
585
585
586 @command(
586 @command(
587 b'archive',
587 b'archive',
588 [
588 [
589 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
589 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
590 (
590 (
591 b'p',
591 b'p',
592 b'prefix',
592 b'prefix',
593 b'',
593 b'',
594 _(b'directory prefix for files in archive'),
594 _(b'directory prefix for files in archive'),
595 _(b'PREFIX'),
595 _(b'PREFIX'),
596 ),
596 ),
597 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
597 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
598 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
598 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
599 ]
599 ]
600 + subrepoopts
600 + subrepoopts
601 + walkopts,
601 + walkopts,
602 _(b'[OPTION]... DEST'),
602 _(b'[OPTION]... DEST'),
603 helpcategory=command.CATEGORY_IMPORT_EXPORT,
603 helpcategory=command.CATEGORY_IMPORT_EXPORT,
604 )
604 )
605 def archive(ui, repo, dest, **opts):
605 def archive(ui, repo, dest, **opts):
606 '''create an unversioned archive of a repository revision
606 '''create an unversioned archive of a repository revision
607
607
608 By default, the revision used is the parent of the working
608 By default, the revision used is the parent of the working
609 directory; use -r/--rev to specify a different revision.
609 directory; use -r/--rev to specify a different revision.
610
610
611 The archive type is automatically detected based on file
611 The archive type is automatically detected based on file
612 extension (to override, use -t/--type).
612 extension (to override, use -t/--type).
613
613
614 .. container:: verbose
614 .. container:: verbose
615
615
616 Examples:
616 Examples:
617
617
618 - create a zip file containing the 1.0 release::
618 - create a zip file containing the 1.0 release::
619
619
620 hg archive -r 1.0 project-1.0.zip
620 hg archive -r 1.0 project-1.0.zip
621
621
622 - create a tarball excluding .hg files::
622 - create a tarball excluding .hg files::
623
623
624 hg archive project.tar.gz -X ".hg*"
624 hg archive project.tar.gz -X ".hg*"
625
625
626 Valid types are:
626 Valid types are:
627
627
628 :``files``: a directory full of files (default)
628 :``files``: a directory full of files (default)
629 :``tar``: tar archive, uncompressed
629 :``tar``: tar archive, uncompressed
630 :``tbz2``: tar archive, compressed using bzip2
630 :``tbz2``: tar archive, compressed using bzip2
631 :``tgz``: tar archive, compressed using gzip
631 :``tgz``: tar archive, compressed using gzip
632 :``txz``: tar archive, compressed using lzma (only in Python 3)
632 :``txz``: tar archive, compressed using lzma (only in Python 3)
633 :``uzip``: zip archive, uncompressed
633 :``uzip``: zip archive, uncompressed
634 :``zip``: zip archive, compressed using deflate
634 :``zip``: zip archive, compressed using deflate
635
635
636 The exact name of the destination archive or directory is given
636 The exact name of the destination archive or directory is given
637 using a format string; see :hg:`help export` for details.
637 using a format string; see :hg:`help export` for details.
638
638
639 Each member added to an archive file has a directory prefix
639 Each member added to an archive file has a directory prefix
640 prepended. Use -p/--prefix to specify a format string for the
640 prepended. Use -p/--prefix to specify a format string for the
641 prefix. The default is the basename of the archive, with suffixes
641 prefix. The default is the basename of the archive, with suffixes
642 removed.
642 removed.
643
643
644 Returns 0 on success.
644 Returns 0 on success.
645 '''
645 '''
646
646
647 opts = pycompat.byteskwargs(opts)
647 opts = pycompat.byteskwargs(opts)
648 rev = opts.get(b'rev')
648 rev = opts.get(b'rev')
649 if rev:
649 if rev:
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
650 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
651 ctx = scmutil.revsingle(repo, rev)
651 ctx = scmutil.revsingle(repo, rev)
652 if not ctx:
652 if not ctx:
653 raise error.Abort(_(b'no working directory: please specify a revision'))
653 raise error.Abort(_(b'no working directory: please specify a revision'))
654 node = ctx.node()
654 node = ctx.node()
655 dest = cmdutil.makefilename(ctx, dest)
655 dest = cmdutil.makefilename(ctx, dest)
656 if os.path.realpath(dest) == repo.root:
656 if os.path.realpath(dest) == repo.root:
657 raise error.Abort(_(b'repository root cannot be destination'))
657 raise error.Abort(_(b'repository root cannot be destination'))
658
658
659 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
659 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
660 prefix = opts.get(b'prefix')
660 prefix = opts.get(b'prefix')
661
661
662 if dest == b'-':
662 if dest == b'-':
663 if kind == b'files':
663 if kind == b'files':
664 raise error.Abort(_(b'cannot archive plain files to stdout'))
664 raise error.Abort(_(b'cannot archive plain files to stdout'))
665 dest = cmdutil.makefileobj(ctx, dest)
665 dest = cmdutil.makefileobj(ctx, dest)
666 if not prefix:
666 if not prefix:
667 prefix = os.path.basename(repo.root) + b'-%h'
667 prefix = os.path.basename(repo.root) + b'-%h'
668
668
669 prefix = cmdutil.makefilename(ctx, prefix)
669 prefix = cmdutil.makefilename(ctx, prefix)
670 match = scmutil.match(ctx, [], opts)
670 match = scmutil.match(ctx, [], opts)
671 archival.archive(
671 archival.archive(
672 repo,
672 repo,
673 dest,
673 dest,
674 node,
674 node,
675 kind,
675 kind,
676 not opts.get(b'no_decode'),
676 not opts.get(b'no_decode'),
677 match,
677 match,
678 prefix,
678 prefix,
679 subrepos=opts.get(b'subrepos'),
679 subrepos=opts.get(b'subrepos'),
680 )
680 )
681
681
682
682
683 @command(
683 @command(
684 b'backout',
684 b'backout',
685 [
685 [
686 (
686 (
687 b'',
687 b'',
688 b'merge',
688 b'merge',
689 None,
689 None,
690 _(b'merge with old dirstate parent after backout'),
690 _(b'merge with old dirstate parent after backout'),
691 ),
691 ),
692 (
692 (
693 b'',
693 b'',
694 b'commit',
694 b'commit',
695 None,
695 None,
696 _(b'commit if no conflicts were encountered (DEPRECATED)'),
696 _(b'commit if no conflicts were encountered (DEPRECATED)'),
697 ),
697 ),
698 (b'', b'no-commit', None, _(b'do not commit')),
698 (b'', b'no-commit', None, _(b'do not commit')),
699 (
699 (
700 b'',
700 b'',
701 b'parent',
701 b'parent',
702 b'',
702 b'',
703 _(b'parent to choose when backing out merge (DEPRECATED)'),
703 _(b'parent to choose when backing out merge (DEPRECATED)'),
704 _(b'REV'),
704 _(b'REV'),
705 ),
705 ),
706 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
706 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
707 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
707 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
708 ]
708 ]
709 + mergetoolopts
709 + mergetoolopts
710 + walkopts
710 + walkopts
711 + commitopts
711 + commitopts
712 + commitopts2,
712 + commitopts2,
713 _(b'[OPTION]... [-r] REV'),
713 _(b'[OPTION]... [-r] REV'),
714 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
714 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
715 )
715 )
716 def backout(ui, repo, node=None, rev=None, **opts):
716 def backout(ui, repo, node=None, rev=None, **opts):
717 '''reverse effect of earlier changeset
717 '''reverse effect of earlier changeset
718
718
719 Prepare a new changeset with the effect of REV undone in the
719 Prepare a new changeset with the effect of REV undone in the
720 current working directory. If no conflicts were encountered,
720 current working directory. If no conflicts were encountered,
721 it will be committed immediately.
721 it will be committed immediately.
722
722
723 If REV is the parent of the working directory, then this new changeset
723 If REV is the parent of the working directory, then this new changeset
724 is committed automatically (unless --no-commit is specified).
724 is committed automatically (unless --no-commit is specified).
725
725
726 .. note::
726 .. note::
727
727
728 :hg:`backout` cannot be used to fix either an unwanted or
728 :hg:`backout` cannot be used to fix either an unwanted or
729 incorrect merge.
729 incorrect merge.
730
730
731 .. container:: verbose
731 .. container:: verbose
732
732
733 Examples:
733 Examples:
734
734
735 - Reverse the effect of the parent of the working directory.
735 - Reverse the effect of the parent of the working directory.
736 This backout will be committed immediately::
736 This backout will be committed immediately::
737
737
738 hg backout -r .
738 hg backout -r .
739
739
740 - Reverse the effect of previous bad revision 23::
740 - Reverse the effect of previous bad revision 23::
741
741
742 hg backout -r 23
742 hg backout -r 23
743
743
744 - Reverse the effect of previous bad revision 23 and
744 - Reverse the effect of previous bad revision 23 and
745 leave changes uncommitted::
745 leave changes uncommitted::
746
746
747 hg backout -r 23 --no-commit
747 hg backout -r 23 --no-commit
748 hg commit -m "Backout revision 23"
748 hg commit -m "Backout revision 23"
749
749
750 By default, the pending changeset will have one parent,
750 By default, the pending changeset will have one parent,
751 maintaining a linear history. With --merge, the pending
751 maintaining a linear history. With --merge, the pending
752 changeset will instead have two parents: the old parent of the
752 changeset will instead have two parents: the old parent of the
753 working directory and a new child of REV that simply undoes REV.
753 working directory and a new child of REV that simply undoes REV.
754
754
755 Before version 1.7, the behavior without --merge was equivalent
755 Before version 1.7, the behavior without --merge was equivalent
756 to specifying --merge followed by :hg:`update --clean .` to
756 to specifying --merge followed by :hg:`update --clean .` to
757 cancel the merge and leave the child of REV as a head to be
757 cancel the merge and leave the child of REV as a head to be
758 merged separately.
758 merged separately.
759
759
760 See :hg:`help dates` for a list of formats valid for -d/--date.
760 See :hg:`help dates` for a list of formats valid for -d/--date.
761
761
762 See :hg:`help revert` for a way to restore files to the state
762 See :hg:`help revert` for a way to restore files to the state
763 of another revision.
763 of another revision.
764
764
765 Returns 0 on success, 1 if nothing to backout or there are unresolved
765 Returns 0 on success, 1 if nothing to backout or there are unresolved
766 files.
766 files.
767 '''
767 '''
768 with repo.wlock(), repo.lock():
768 with repo.wlock(), repo.lock():
769 return _dobackout(ui, repo, node, rev, **opts)
769 return _dobackout(ui, repo, node, rev, **opts)
770
770
771
771
772 def _dobackout(ui, repo, node=None, rev=None, **opts):
772 def _dobackout(ui, repo, node=None, rev=None, **opts):
773 opts = pycompat.byteskwargs(opts)
773 opts = pycompat.byteskwargs(opts)
774 if opts.get(b'commit') and opts.get(b'no_commit'):
774 if opts.get(b'commit') and opts.get(b'no_commit'):
775 raise error.Abort(_(b"cannot use --commit with --no-commit"))
775 raise error.Abort(_(b"cannot use --commit with --no-commit"))
776 if opts.get(b'merge') and opts.get(b'no_commit'):
776 if opts.get(b'merge') and opts.get(b'no_commit'):
777 raise error.Abort(_(b"cannot use --merge with --no-commit"))
777 raise error.Abort(_(b"cannot use --merge with --no-commit"))
778
778
779 if rev and node:
779 if rev and node:
780 raise error.Abort(_(b"please specify just one revision"))
780 raise error.Abort(_(b"please specify just one revision"))
781
781
782 if not rev:
782 if not rev:
783 rev = node
783 rev = node
784
784
785 if not rev:
785 if not rev:
786 raise error.Abort(_(b"please specify a revision to backout"))
786 raise error.Abort(_(b"please specify a revision to backout"))
787
787
788 date = opts.get(b'date')
788 date = opts.get(b'date')
789 if date:
789 if date:
790 opts[b'date'] = dateutil.parsedate(date)
790 opts[b'date'] = dateutil.parsedate(date)
791
791
792 cmdutil.checkunfinished(repo)
792 cmdutil.checkunfinished(repo)
793 cmdutil.bailifchanged(repo)
793 cmdutil.bailifchanged(repo)
794 ctx = scmutil.revsingle(repo, rev)
794 ctx = scmutil.revsingle(repo, rev)
795 node = ctx.node()
795 node = ctx.node()
796
796
797 op1, op2 = repo.dirstate.parents()
797 op1, op2 = repo.dirstate.parents()
798 if not repo.changelog.isancestor(node, op1):
798 if not repo.changelog.isancestor(node, op1):
799 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
799 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
800
800
801 p1, p2 = repo.changelog.parents(node)
801 p1, p2 = repo.changelog.parents(node)
802 if p1 == nullid:
802 if p1 == nullid:
803 raise error.Abort(_(b'cannot backout a change with no parents'))
803 raise error.Abort(_(b'cannot backout a change with no parents'))
804 if p2 != nullid:
804 if p2 != nullid:
805 if not opts.get(b'parent'):
805 if not opts.get(b'parent'):
806 raise error.Abort(_(b'cannot backout a merge changeset'))
806 raise error.Abort(_(b'cannot backout a merge changeset'))
807 p = repo.lookup(opts[b'parent'])
807 p = repo.lookup(opts[b'parent'])
808 if p not in (p1, p2):
808 if p not in (p1, p2):
809 raise error.Abort(
809 raise error.Abort(
810 _(b'%s is not a parent of %s') % (short(p), short(node))
810 _(b'%s is not a parent of %s') % (short(p), short(node))
811 )
811 )
812 parent = p
812 parent = p
813 else:
813 else:
814 if opts.get(b'parent'):
814 if opts.get(b'parent'):
815 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
815 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
816 parent = p1
816 parent = p1
817
817
818 # the backout should appear on the same branch
818 # the backout should appear on the same branch
819 branch = repo.dirstate.branch()
819 branch = repo.dirstate.branch()
820 bheads = repo.branchheads(branch)
820 bheads = repo.branchheads(branch)
821 rctx = scmutil.revsingle(repo, hex(parent))
821 rctx = scmutil.revsingle(repo, hex(parent))
822 if not opts.get(b'merge') and op1 != node:
822 if not opts.get(b'merge') and op1 != node:
823 with dirstateguard.dirstateguard(repo, b'backout'):
823 with dirstateguard.dirstateguard(repo, b'backout'):
824 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
824 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
825 with ui.configoverride(overrides, b'backout'):
825 with ui.configoverride(overrides, b'backout'):
826 stats = mergemod.back_out(ctx, parent=repo[parent])
826 stats = mergemod.back_out(ctx, parent=repo[parent])
827 repo.setparents(op1, op2)
827 repo.setparents(op1, op2)
828 hg._showstats(repo, stats)
828 hg._showstats(repo, stats)
829 if stats.unresolvedcount:
829 if stats.unresolvedcount:
830 repo.ui.status(
830 repo.ui.status(
831 _(b"use 'hg resolve' to retry unresolved file merges\n")
831 _(b"use 'hg resolve' to retry unresolved file merges\n")
832 )
832 )
833 return 1
833 return 1
834 else:
834 else:
835 hg.clean(repo, node, show_stats=False)
835 hg.clean(repo, node, show_stats=False)
836 repo.dirstate.setbranch(branch)
836 repo.dirstate.setbranch(branch)
837 cmdutil.revert(ui, repo, rctx)
837 cmdutil.revert(ui, repo, rctx)
838
838
839 if opts.get(b'no_commit'):
839 if opts.get(b'no_commit'):
840 msg = _(b"changeset %s backed out, don't forget to commit.\n")
840 msg = _(b"changeset %s backed out, don't forget to commit.\n")
841 ui.status(msg % short(node))
841 ui.status(msg % short(node))
842 return 0
842 return 0
843
843
844 def commitfunc(ui, repo, message, match, opts):
844 def commitfunc(ui, repo, message, match, opts):
845 editform = b'backout'
845 editform = b'backout'
846 e = cmdutil.getcommiteditor(
846 e = cmdutil.getcommiteditor(
847 editform=editform, **pycompat.strkwargs(opts)
847 editform=editform, **pycompat.strkwargs(opts)
848 )
848 )
849 if not message:
849 if not message:
850 # we don't translate commit messages
850 # we don't translate commit messages
851 message = b"Backed out changeset %s" % short(node)
851 message = b"Backed out changeset %s" % short(node)
852 e = cmdutil.getcommiteditor(edit=True, editform=editform)
852 e = cmdutil.getcommiteditor(edit=True, editform=editform)
853 return repo.commit(
853 return repo.commit(
854 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
854 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
855 )
855 )
856
856
857 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
857 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
858 if not newnode:
858 if not newnode:
859 ui.status(_(b"nothing changed\n"))
859 ui.status(_(b"nothing changed\n"))
860 return 1
860 return 1
861 cmdutil.commitstatus(repo, newnode, branch, bheads)
861 cmdutil.commitstatus(repo, newnode, branch, bheads)
862
862
863 def nice(node):
863 def nice(node):
864 return b'%d:%s' % (repo.changelog.rev(node), short(node))
864 return b'%d:%s' % (repo.changelog.rev(node), short(node))
865
865
866 ui.status(
866 ui.status(
867 _(b'changeset %s backs out changeset %s\n')
867 _(b'changeset %s backs out changeset %s\n')
868 % (nice(repo.changelog.tip()), nice(node))
868 % (nice(repo.changelog.tip()), nice(node))
869 )
869 )
870 if opts.get(b'merge') and op1 != node:
870 if opts.get(b'merge') and op1 != node:
871 hg.clean(repo, op1, show_stats=False)
871 hg.clean(repo, op1, show_stats=False)
872 ui.status(
872 ui.status(
873 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
873 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
874 )
874 )
875 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
875 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
876 with ui.configoverride(overrides, b'backout'):
876 with ui.configoverride(overrides, b'backout'):
877 return hg.merge(repo[b'tip'])
877 return hg.merge(repo[b'tip'])
878 return 0
878 return 0
879
879
880
880
881 @command(
881 @command(
882 b'bisect',
882 b'bisect',
883 [
883 [
884 (b'r', b'reset', False, _(b'reset bisect state')),
884 (b'r', b'reset', False, _(b'reset bisect state')),
885 (b'g', b'good', False, _(b'mark changeset good')),
885 (b'g', b'good', False, _(b'mark changeset good')),
886 (b'b', b'bad', False, _(b'mark changeset bad')),
886 (b'b', b'bad', False, _(b'mark changeset bad')),
887 (b's', b'skip', False, _(b'skip testing changeset')),
887 (b's', b'skip', False, _(b'skip testing changeset')),
888 (b'e', b'extend', False, _(b'extend the bisect range')),
888 (b'e', b'extend', False, _(b'extend the bisect range')),
889 (
889 (
890 b'c',
890 b'c',
891 b'command',
891 b'command',
892 b'',
892 b'',
893 _(b'use command to check changeset state'),
893 _(b'use command to check changeset state'),
894 _(b'CMD'),
894 _(b'CMD'),
895 ),
895 ),
896 (b'U', b'noupdate', False, _(b'do not update to target')),
896 (b'U', b'noupdate', False, _(b'do not update to target')),
897 ],
897 ],
898 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
898 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
899 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
899 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
900 )
900 )
901 def bisect(
901 def bisect(
902 ui,
902 ui,
903 repo,
903 repo,
904 rev=None,
904 rev=None,
905 extra=None,
905 extra=None,
906 command=None,
906 command=None,
907 reset=None,
907 reset=None,
908 good=None,
908 good=None,
909 bad=None,
909 bad=None,
910 skip=None,
910 skip=None,
911 extend=None,
911 extend=None,
912 noupdate=None,
912 noupdate=None,
913 ):
913 ):
914 """subdivision search of changesets
914 """subdivision search of changesets
915
915
916 This command helps to find changesets which introduce problems. To
916 This command helps to find changesets which introduce problems. To
917 use, mark the earliest changeset you know exhibits the problem as
917 use, mark the earliest changeset you know exhibits the problem as
918 bad, then mark the latest changeset which is free from the problem
918 bad, then mark the latest changeset which is free from the problem
919 as good. Bisect will update your working directory to a revision
919 as good. Bisect will update your working directory to a revision
920 for testing (unless the -U/--noupdate option is specified). Once
920 for testing (unless the -U/--noupdate option is specified). Once
921 you have performed tests, mark the working directory as good or
921 you have performed tests, mark the working directory as good or
922 bad, and bisect will either update to another candidate changeset
922 bad, and bisect will either update to another candidate changeset
923 or announce that it has found the bad revision.
923 or announce that it has found the bad revision.
924
924
925 As a shortcut, you can also use the revision argument to mark a
925 As a shortcut, you can also use the revision argument to mark a
926 revision as good or bad without checking it out first.
926 revision as good or bad without checking it out first.
927
927
928 If you supply a command, it will be used for automatic bisection.
928 If you supply a command, it will be used for automatic bisection.
929 The environment variable HG_NODE will contain the ID of the
929 The environment variable HG_NODE will contain the ID of the
930 changeset being tested. The exit status of the command will be
930 changeset being tested. The exit status of the command will be
931 used to mark revisions as good or bad: status 0 means good, 125
931 used to mark revisions as good or bad: status 0 means good, 125
932 means to skip the revision, 127 (command not found) will abort the
932 means to skip the revision, 127 (command not found) will abort the
933 bisection, and any other non-zero exit status means the revision
933 bisection, and any other non-zero exit status means the revision
934 is bad.
934 is bad.
935
935
936 .. container:: verbose
936 .. container:: verbose
937
937
938 Some examples:
938 Some examples:
939
939
940 - start a bisection with known bad revision 34, and good revision 12::
940 - start a bisection with known bad revision 34, and good revision 12::
941
941
942 hg bisect --bad 34
942 hg bisect --bad 34
943 hg bisect --good 12
943 hg bisect --good 12
944
944
945 - advance the current bisection by marking current revision as good or
945 - advance the current bisection by marking current revision as good or
946 bad::
946 bad::
947
947
948 hg bisect --good
948 hg bisect --good
949 hg bisect --bad
949 hg bisect --bad
950
950
951 - mark the current revision, or a known revision, to be skipped (e.g. if
951 - mark the current revision, or a known revision, to be skipped (e.g. if
952 that revision is not usable because of another issue)::
952 that revision is not usable because of another issue)::
953
953
954 hg bisect --skip
954 hg bisect --skip
955 hg bisect --skip 23
955 hg bisect --skip 23
956
956
957 - skip all revisions that do not touch directories ``foo`` or ``bar``::
957 - skip all revisions that do not touch directories ``foo`` or ``bar``::
958
958
959 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
959 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
960
960
961 - forget the current bisection::
961 - forget the current bisection::
962
962
963 hg bisect --reset
963 hg bisect --reset
964
964
965 - use 'make && make tests' to automatically find the first broken
965 - use 'make && make tests' to automatically find the first broken
966 revision::
966 revision::
967
967
968 hg bisect --reset
968 hg bisect --reset
969 hg bisect --bad 34
969 hg bisect --bad 34
970 hg bisect --good 12
970 hg bisect --good 12
971 hg bisect --command "make && make tests"
971 hg bisect --command "make && make tests"
972
972
973 - see all changesets whose states are already known in the current
973 - see all changesets whose states are already known in the current
974 bisection::
974 bisection::
975
975
976 hg log -r "bisect(pruned)"
976 hg log -r "bisect(pruned)"
977
977
978 - see the changeset currently being bisected (especially useful
978 - see the changeset currently being bisected (especially useful
979 if running with -U/--noupdate)::
979 if running with -U/--noupdate)::
980
980
981 hg log -r "bisect(current)"
981 hg log -r "bisect(current)"
982
982
983 - see all changesets that took part in the current bisection::
983 - see all changesets that took part in the current bisection::
984
984
985 hg log -r "bisect(range)"
985 hg log -r "bisect(range)"
986
986
987 - you can even get a nice graph::
987 - you can even get a nice graph::
988
988
989 hg log --graph -r "bisect(range)"
989 hg log --graph -r "bisect(range)"
990
990
991 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
991 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
992
992
993 Returns 0 on success.
993 Returns 0 on success.
994 """
994 """
995 # backward compatibility
995 # backward compatibility
996 if rev in b"good bad reset init".split():
996 if rev in b"good bad reset init".split():
997 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
997 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
998 cmd, rev, extra = rev, extra, None
998 cmd, rev, extra = rev, extra, None
999 if cmd == b"good":
999 if cmd == b"good":
1000 good = True
1000 good = True
1001 elif cmd == b"bad":
1001 elif cmd == b"bad":
1002 bad = True
1002 bad = True
1003 else:
1003 else:
1004 reset = True
1004 reset = True
1005 elif extra:
1005 elif extra:
1006 raise error.Abort(_(b'incompatible arguments'))
1006 raise error.Abort(_(b'incompatible arguments'))
1007
1007
1008 incompatibles = {
1008 incompatibles = {
1009 b'--bad': bad,
1009 b'--bad': bad,
1010 b'--command': bool(command),
1010 b'--command': bool(command),
1011 b'--extend': extend,
1011 b'--extend': extend,
1012 b'--good': good,
1012 b'--good': good,
1013 b'--reset': reset,
1013 b'--reset': reset,
1014 b'--skip': skip,
1014 b'--skip': skip,
1015 }
1015 }
1016
1016
1017 enabled = [x for x in incompatibles if incompatibles[x]]
1017 enabled = [x for x in incompatibles if incompatibles[x]]
1018
1018
1019 if len(enabled) > 1:
1019 if len(enabled) > 1:
1020 raise error.Abort(
1020 raise error.Abort(
1021 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1021 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1022 )
1022 )
1023
1023
1024 if reset:
1024 if reset:
1025 hbisect.resetstate(repo)
1025 hbisect.resetstate(repo)
1026 return
1026 return
1027
1027
1028 state = hbisect.load_state(repo)
1028 state = hbisect.load_state(repo)
1029
1029
1030 # update state
1030 # update state
1031 if good or bad or skip:
1031 if good or bad or skip:
1032 if rev:
1032 if rev:
1033 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1033 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1034 else:
1034 else:
1035 nodes = [repo.lookup(b'.')]
1035 nodes = [repo.lookup(b'.')]
1036 if good:
1036 if good:
1037 state[b'good'] += nodes
1037 state[b'good'] += nodes
1038 elif bad:
1038 elif bad:
1039 state[b'bad'] += nodes
1039 state[b'bad'] += nodes
1040 elif skip:
1040 elif skip:
1041 state[b'skip'] += nodes
1041 state[b'skip'] += nodes
1042 hbisect.save_state(repo, state)
1042 hbisect.save_state(repo, state)
1043 if not (state[b'good'] and state[b'bad']):
1043 if not (state[b'good'] and state[b'bad']):
1044 return
1044 return
1045
1045
1046 def mayupdate(repo, node, show_stats=True):
1046 def mayupdate(repo, node, show_stats=True):
1047 """common used update sequence"""
1047 """common used update sequence"""
1048 if noupdate:
1048 if noupdate:
1049 return
1049 return
1050 cmdutil.checkunfinished(repo)
1050 cmdutil.checkunfinished(repo)
1051 cmdutil.bailifchanged(repo)
1051 cmdutil.bailifchanged(repo)
1052 return hg.clean(repo, node, show_stats=show_stats)
1052 return hg.clean(repo, node, show_stats=show_stats)
1053
1053
1054 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1054 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1055
1055
1056 if command:
1056 if command:
1057 changesets = 1
1057 changesets = 1
1058 if noupdate:
1058 if noupdate:
1059 try:
1059 try:
1060 node = state[b'current'][0]
1060 node = state[b'current'][0]
1061 except LookupError:
1061 except LookupError:
1062 raise error.Abort(
1062 raise error.Abort(
1063 _(
1063 _(
1064 b'current bisect revision is unknown - '
1064 b'current bisect revision is unknown - '
1065 b'start a new bisect to fix'
1065 b'start a new bisect to fix'
1066 )
1066 )
1067 )
1067 )
1068 else:
1068 else:
1069 node, p2 = repo.dirstate.parents()
1069 node, p2 = repo.dirstate.parents()
1070 if p2 != nullid:
1070 if p2 != nullid:
1071 raise error.Abort(_(b'current bisect revision is a merge'))
1071 raise error.Abort(_(b'current bisect revision is a merge'))
1072 if rev:
1072 if rev:
1073 node = repo[scmutil.revsingle(repo, rev, node)].node()
1073 node = repo[scmutil.revsingle(repo, rev, node)].node()
1074 with hbisect.restore_state(repo, state, node):
1074 with hbisect.restore_state(repo, state, node):
1075 while changesets:
1075 while changesets:
1076 # update state
1076 # update state
1077 state[b'current'] = [node]
1077 state[b'current'] = [node]
1078 hbisect.save_state(repo, state)
1078 hbisect.save_state(repo, state)
1079 status = ui.system(
1079 status = ui.system(
1080 command,
1080 command,
1081 environ={b'HG_NODE': hex(node)},
1081 environ={b'HG_NODE': hex(node)},
1082 blockedtag=b'bisect_check',
1082 blockedtag=b'bisect_check',
1083 )
1083 )
1084 if status == 125:
1084 if status == 125:
1085 transition = b"skip"
1085 transition = b"skip"
1086 elif status == 0:
1086 elif status == 0:
1087 transition = b"good"
1087 transition = b"good"
1088 # status < 0 means process was killed
1088 # status < 0 means process was killed
1089 elif status == 127:
1089 elif status == 127:
1090 raise error.Abort(_(b"failed to execute %s") % command)
1090 raise error.Abort(_(b"failed to execute %s") % command)
1091 elif status < 0:
1091 elif status < 0:
1092 raise error.Abort(_(b"%s killed") % command)
1092 raise error.Abort(_(b"%s killed") % command)
1093 else:
1093 else:
1094 transition = b"bad"
1094 transition = b"bad"
1095 state[transition].append(node)
1095 state[transition].append(node)
1096 ctx = repo[node]
1096 ctx = repo[node]
1097 ui.status(
1097 ui.status(
1098 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1098 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1099 )
1099 )
1100 hbisect.checkstate(state)
1100 hbisect.checkstate(state)
1101 # bisect
1101 # bisect
1102 nodes, changesets, bgood = hbisect.bisect(repo, state)
1102 nodes, changesets, bgood = hbisect.bisect(repo, state)
1103 # update to next check
1103 # update to next check
1104 node = nodes[0]
1104 node = nodes[0]
1105 mayupdate(repo, node, show_stats=False)
1105 mayupdate(repo, node, show_stats=False)
1106 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1106 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1107 return
1107 return
1108
1108
1109 hbisect.checkstate(state)
1109 hbisect.checkstate(state)
1110
1110
1111 # actually bisect
1111 # actually bisect
1112 nodes, changesets, good = hbisect.bisect(repo, state)
1112 nodes, changesets, good = hbisect.bisect(repo, state)
1113 if extend:
1113 if extend:
1114 if not changesets:
1114 if not changesets:
1115 extendnode = hbisect.extendrange(repo, state, nodes, good)
1115 extendnode = hbisect.extendrange(repo, state, nodes, good)
1116 if extendnode is not None:
1116 if extendnode is not None:
1117 ui.write(
1117 ui.write(
1118 _(b"Extending search to changeset %d:%s\n")
1118 _(b"Extending search to changeset %d:%s\n")
1119 % (extendnode.rev(), extendnode)
1119 % (extendnode.rev(), extendnode)
1120 )
1120 )
1121 state[b'current'] = [extendnode.node()]
1121 state[b'current'] = [extendnode.node()]
1122 hbisect.save_state(repo, state)
1122 hbisect.save_state(repo, state)
1123 return mayupdate(repo, extendnode.node())
1123 return mayupdate(repo, extendnode.node())
1124 raise error.Abort(_(b"nothing to extend"))
1124 raise error.Abort(_(b"nothing to extend"))
1125
1125
1126 if changesets == 0:
1126 if changesets == 0:
1127 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1127 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1128 else:
1128 else:
1129 assert len(nodes) == 1 # only a single node can be tested next
1129 assert len(nodes) == 1 # only a single node can be tested next
1130 node = nodes[0]
1130 node = nodes[0]
1131 # compute the approximate number of remaining tests
1131 # compute the approximate number of remaining tests
1132 tests, size = 0, 2
1132 tests, size = 0, 2
1133 while size <= changesets:
1133 while size <= changesets:
1134 tests, size = tests + 1, size * 2
1134 tests, size = tests + 1, size * 2
1135 rev = repo.changelog.rev(node)
1135 rev = repo.changelog.rev(node)
1136 ui.write(
1136 ui.write(
1137 _(
1137 _(
1138 b"Testing changeset %d:%s "
1138 b"Testing changeset %d:%s "
1139 b"(%d changesets remaining, ~%d tests)\n"
1139 b"(%d changesets remaining, ~%d tests)\n"
1140 )
1140 )
1141 % (rev, short(node), changesets, tests)
1141 % (rev, short(node), changesets, tests)
1142 )
1142 )
1143 state[b'current'] = [node]
1143 state[b'current'] = [node]
1144 hbisect.save_state(repo, state)
1144 hbisect.save_state(repo, state)
1145 return mayupdate(repo, node)
1145 return mayupdate(repo, node)
1146
1146
1147
1147
1148 @command(
1148 @command(
1149 b'bookmarks|bookmark',
1149 b'bookmarks|bookmark',
1150 [
1150 [
1151 (b'f', b'force', False, _(b'force')),
1151 (b'f', b'force', False, _(b'force')),
1152 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1152 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1153 (b'd', b'delete', False, _(b'delete a given bookmark')),
1153 (b'd', b'delete', False, _(b'delete a given bookmark')),
1154 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1154 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1155 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1155 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1156 (b'l', b'list', False, _(b'list existing bookmarks')),
1156 (b'l', b'list', False, _(b'list existing bookmarks')),
1157 ]
1157 ]
1158 + formatteropts,
1158 + formatteropts,
1159 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1159 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1160 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1160 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1161 )
1161 )
1162 def bookmark(ui, repo, *names, **opts):
1162 def bookmark(ui, repo, *names, **opts):
1163 '''create a new bookmark or list existing bookmarks
1163 '''create a new bookmark or list existing bookmarks
1164
1164
1165 Bookmarks are labels on changesets to help track lines of development.
1165 Bookmarks are labels on changesets to help track lines of development.
1166 Bookmarks are unversioned and can be moved, renamed and deleted.
1166 Bookmarks are unversioned and can be moved, renamed and deleted.
1167 Deleting or moving a bookmark has no effect on the associated changesets.
1167 Deleting or moving a bookmark has no effect on the associated changesets.
1168
1168
1169 Creating or updating to a bookmark causes it to be marked as 'active'.
1169 Creating or updating to a bookmark causes it to be marked as 'active'.
1170 The active bookmark is indicated with a '*'.
1170 The active bookmark is indicated with a '*'.
1171 When a commit is made, the active bookmark will advance to the new commit.
1171 When a commit is made, the active bookmark will advance to the new commit.
1172 A plain :hg:`update` will also advance an active bookmark, if possible.
1172 A plain :hg:`update` will also advance an active bookmark, if possible.
1173 Updating away from a bookmark will cause it to be deactivated.
1173 Updating away from a bookmark will cause it to be deactivated.
1174
1174
1175 Bookmarks can be pushed and pulled between repositories (see
1175 Bookmarks can be pushed and pulled between repositories (see
1176 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1176 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1177 diverged, a new 'divergent bookmark' of the form 'name@path' will
1177 diverged, a new 'divergent bookmark' of the form 'name@path' will
1178 be created. Using :hg:`merge` will resolve the divergence.
1178 be created. Using :hg:`merge` will resolve the divergence.
1179
1179
1180 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1180 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1181 the active bookmark's name.
1181 the active bookmark's name.
1182
1182
1183 A bookmark named '@' has the special property that :hg:`clone` will
1183 A bookmark named '@' has the special property that :hg:`clone` will
1184 check it out by default if it exists.
1184 check it out by default if it exists.
1185
1185
1186 .. container:: verbose
1186 .. container:: verbose
1187
1187
1188 Template:
1188 Template:
1189
1189
1190 The following keywords are supported in addition to the common template
1190 The following keywords are supported in addition to the common template
1191 keywords and functions such as ``{bookmark}``. See also
1191 keywords and functions such as ``{bookmark}``. See also
1192 :hg:`help templates`.
1192 :hg:`help templates`.
1193
1193
1194 :active: Boolean. True if the bookmark is active.
1194 :active: Boolean. True if the bookmark is active.
1195
1195
1196 Examples:
1196 Examples:
1197
1197
1198 - create an active bookmark for a new line of development::
1198 - create an active bookmark for a new line of development::
1199
1199
1200 hg book new-feature
1200 hg book new-feature
1201
1201
1202 - create an inactive bookmark as a place marker::
1202 - create an inactive bookmark as a place marker::
1203
1203
1204 hg book -i reviewed
1204 hg book -i reviewed
1205
1205
1206 - create an inactive bookmark on another changeset::
1206 - create an inactive bookmark on another changeset::
1207
1207
1208 hg book -r .^ tested
1208 hg book -r .^ tested
1209
1209
1210 - rename bookmark turkey to dinner::
1210 - rename bookmark turkey to dinner::
1211
1211
1212 hg book -m turkey dinner
1212 hg book -m turkey dinner
1213
1213
1214 - move the '@' bookmark from another branch::
1214 - move the '@' bookmark from another branch::
1215
1215
1216 hg book -f @
1216 hg book -f @
1217
1217
1218 - print only the active bookmark name::
1218 - print only the active bookmark name::
1219
1219
1220 hg book -ql .
1220 hg book -ql .
1221 '''
1221 '''
1222 opts = pycompat.byteskwargs(opts)
1222 opts = pycompat.byteskwargs(opts)
1223 force = opts.get(b'force')
1223 force = opts.get(b'force')
1224 rev = opts.get(b'rev')
1224 rev = opts.get(b'rev')
1225 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1225 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1226
1226
1227 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1227 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1228 if action:
1228 if action:
1229 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1229 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1230 elif names or rev:
1230 elif names or rev:
1231 action = b'add'
1231 action = b'add'
1232 elif inactive:
1232 elif inactive:
1233 action = b'inactive' # meaning deactivate
1233 action = b'inactive' # meaning deactivate
1234 else:
1234 else:
1235 action = b'list'
1235 action = b'list'
1236
1236
1237 cmdutil.check_incompatible_arguments(
1237 cmdutil.check_incompatible_arguments(
1238 opts, b'inactive', [b'delete', b'list']
1238 opts, b'inactive', [b'delete', b'list']
1239 )
1239 )
1240 if not names and action in {b'add', b'delete'}:
1240 if not names and action in {b'add', b'delete'}:
1241 raise error.Abort(_(b"bookmark name required"))
1241 raise error.Abort(_(b"bookmark name required"))
1242
1242
1243 if action in {b'add', b'delete', b'rename', b'inactive'}:
1243 if action in {b'add', b'delete', b'rename', b'inactive'}:
1244 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1244 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1245 if action == b'delete':
1245 if action == b'delete':
1246 names = pycompat.maplist(repo._bookmarks.expandname, names)
1246 names = pycompat.maplist(repo._bookmarks.expandname, names)
1247 bookmarks.delete(repo, tr, names)
1247 bookmarks.delete(repo, tr, names)
1248 elif action == b'rename':
1248 elif action == b'rename':
1249 if not names:
1249 if not names:
1250 raise error.Abort(_(b"new bookmark name required"))
1250 raise error.Abort(_(b"new bookmark name required"))
1251 elif len(names) > 1:
1251 elif len(names) > 1:
1252 raise error.Abort(_(b"only one new bookmark name allowed"))
1252 raise error.Abort(_(b"only one new bookmark name allowed"))
1253 oldname = repo._bookmarks.expandname(opts[b'rename'])
1253 oldname = repo._bookmarks.expandname(opts[b'rename'])
1254 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1254 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1255 elif action == b'add':
1255 elif action == b'add':
1256 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1256 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1257 elif action == b'inactive':
1257 elif action == b'inactive':
1258 if len(repo._bookmarks) == 0:
1258 if len(repo._bookmarks) == 0:
1259 ui.status(_(b"no bookmarks set\n"))
1259 ui.status(_(b"no bookmarks set\n"))
1260 elif not repo._activebookmark:
1260 elif not repo._activebookmark:
1261 ui.status(_(b"no active bookmark\n"))
1261 ui.status(_(b"no active bookmark\n"))
1262 else:
1262 else:
1263 bookmarks.deactivate(repo)
1263 bookmarks.deactivate(repo)
1264 elif action == b'list':
1264 elif action == b'list':
1265 names = pycompat.maplist(repo._bookmarks.expandname, names)
1265 names = pycompat.maplist(repo._bookmarks.expandname, names)
1266 with ui.formatter(b'bookmarks', opts) as fm:
1266 with ui.formatter(b'bookmarks', opts) as fm:
1267 bookmarks.printbookmarks(ui, repo, fm, names)
1267 bookmarks.printbookmarks(ui, repo, fm, names)
1268 else:
1268 else:
1269 raise error.ProgrammingError(b'invalid action: %s' % action)
1269 raise error.ProgrammingError(b'invalid action: %s' % action)
1270
1270
1271
1271
1272 @command(
1272 @command(
1273 b'branch',
1273 b'branch',
1274 [
1274 [
1275 (
1275 (
1276 b'f',
1276 b'f',
1277 b'force',
1277 b'force',
1278 None,
1278 None,
1279 _(b'set branch name even if it shadows an existing branch'),
1279 _(b'set branch name even if it shadows an existing branch'),
1280 ),
1280 ),
1281 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1281 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1282 (
1282 (
1283 b'r',
1283 b'r',
1284 b'rev',
1284 b'rev',
1285 [],
1285 [],
1286 _(b'change branches of the given revs (EXPERIMENTAL)'),
1286 _(b'change branches of the given revs (EXPERIMENTAL)'),
1287 ),
1287 ),
1288 ],
1288 ],
1289 _(b'[-fC] [NAME]'),
1289 _(b'[-fC] [NAME]'),
1290 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1290 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1291 )
1291 )
1292 def branch(ui, repo, label=None, **opts):
1292 def branch(ui, repo, label=None, **opts):
1293 """set or show the current branch name
1293 """set or show the current branch name
1294
1294
1295 .. note::
1295 .. note::
1296
1296
1297 Branch names are permanent and global. Use :hg:`bookmark` to create a
1297 Branch names are permanent and global. Use :hg:`bookmark` to create a
1298 light-weight bookmark instead. See :hg:`help glossary` for more
1298 light-weight bookmark instead. See :hg:`help glossary` for more
1299 information about named branches and bookmarks.
1299 information about named branches and bookmarks.
1300
1300
1301 With no argument, show the current branch name. With one argument,
1301 With no argument, show the current branch name. With one argument,
1302 set the working directory branch name (the branch will not exist
1302 set the working directory branch name (the branch will not exist
1303 in the repository until the next commit). Standard practice
1303 in the repository until the next commit). Standard practice
1304 recommends that primary development take place on the 'default'
1304 recommends that primary development take place on the 'default'
1305 branch.
1305 branch.
1306
1306
1307 Unless -f/--force is specified, branch will not let you set a
1307 Unless -f/--force is specified, branch will not let you set a
1308 branch name that already exists.
1308 branch name that already exists.
1309
1309
1310 Use -C/--clean to reset the working directory branch to that of
1310 Use -C/--clean to reset the working directory branch to that of
1311 the parent of the working directory, negating a previous branch
1311 the parent of the working directory, negating a previous branch
1312 change.
1312 change.
1313
1313
1314 Use the command :hg:`update` to switch to an existing branch. Use
1314 Use the command :hg:`update` to switch to an existing branch. Use
1315 :hg:`commit --close-branch` to mark this branch head as closed.
1315 :hg:`commit --close-branch` to mark this branch head as closed.
1316 When all heads of a branch are closed, the branch will be
1316 When all heads of a branch are closed, the branch will be
1317 considered closed.
1317 considered closed.
1318
1318
1319 Returns 0 on success.
1319 Returns 0 on success.
1320 """
1320 """
1321 opts = pycompat.byteskwargs(opts)
1321 opts = pycompat.byteskwargs(opts)
1322 revs = opts.get(b'rev')
1322 revs = opts.get(b'rev')
1323 if label:
1323 if label:
1324 label = label.strip()
1324 label = label.strip()
1325
1325
1326 if not opts.get(b'clean') and not label:
1326 if not opts.get(b'clean') and not label:
1327 if revs:
1327 if revs:
1328 raise error.Abort(_(b"no branch name specified for the revisions"))
1328 raise error.Abort(_(b"no branch name specified for the revisions"))
1329 ui.write(b"%s\n" % repo.dirstate.branch())
1329 ui.write(b"%s\n" % repo.dirstate.branch())
1330 return
1330 return
1331
1331
1332 with repo.wlock():
1332 with repo.wlock():
1333 if opts.get(b'clean'):
1333 if opts.get(b'clean'):
1334 label = repo[b'.'].branch()
1334 label = repo[b'.'].branch()
1335 repo.dirstate.setbranch(label)
1335 repo.dirstate.setbranch(label)
1336 ui.status(_(b'reset working directory to branch %s\n') % label)
1336 ui.status(_(b'reset working directory to branch %s\n') % label)
1337 elif label:
1337 elif label:
1338
1338
1339 scmutil.checknewlabel(repo, label, b'branch')
1339 scmutil.checknewlabel(repo, label, b'branch')
1340 if revs:
1340 if revs:
1341 return cmdutil.changebranch(ui, repo, revs, label, opts)
1341 return cmdutil.changebranch(ui, repo, revs, label, opts)
1342
1342
1343 if not opts.get(b'force') and label in repo.branchmap():
1343 if not opts.get(b'force') and label in repo.branchmap():
1344 if label not in [p.branch() for p in repo[None].parents()]:
1344 if label not in [p.branch() for p in repo[None].parents()]:
1345 raise error.Abort(
1345 raise error.Abort(
1346 _(b'a branch of the same name already exists'),
1346 _(b'a branch of the same name already exists'),
1347 # i18n: "it" refers to an existing branch
1347 # i18n: "it" refers to an existing branch
1348 hint=_(b"use 'hg update' to switch to it"),
1348 hint=_(b"use 'hg update' to switch to it"),
1349 )
1349 )
1350
1350
1351 repo.dirstate.setbranch(label)
1351 repo.dirstate.setbranch(label)
1352 ui.status(_(b'marked working directory as branch %s\n') % label)
1352 ui.status(_(b'marked working directory as branch %s\n') % label)
1353
1353
1354 # find any open named branches aside from default
1354 # find any open named branches aside from default
1355 for n, h, t, c in repo.branchmap().iterbranches():
1355 for n, h, t, c in repo.branchmap().iterbranches():
1356 if n != b"default" and not c:
1356 if n != b"default" and not c:
1357 return 0
1357 return 0
1358 ui.status(
1358 ui.status(
1359 _(
1359 _(
1360 b'(branches are permanent and global, '
1360 b'(branches are permanent and global, '
1361 b'did you want a bookmark?)\n'
1361 b'did you want a bookmark?)\n'
1362 )
1362 )
1363 )
1363 )
1364
1364
1365
1365
1366 @command(
1366 @command(
1367 b'branches',
1367 b'branches',
1368 [
1368 [
1369 (
1369 (
1370 b'a',
1370 b'a',
1371 b'active',
1371 b'active',
1372 False,
1372 False,
1373 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1373 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1374 ),
1374 ),
1375 (b'c', b'closed', False, _(b'show normal and closed branches')),
1375 (b'c', b'closed', False, _(b'show normal and closed branches')),
1376 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1376 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1377 ]
1377 ]
1378 + formatteropts,
1378 + formatteropts,
1379 _(b'[-c]'),
1379 _(b'[-c]'),
1380 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1380 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1381 intents={INTENT_READONLY},
1381 intents={INTENT_READONLY},
1382 )
1382 )
1383 def branches(ui, repo, active=False, closed=False, **opts):
1383 def branches(ui, repo, active=False, closed=False, **opts):
1384 """list repository named branches
1384 """list repository named branches
1385
1385
1386 List the repository's named branches, indicating which ones are
1386 List the repository's named branches, indicating which ones are
1387 inactive. If -c/--closed is specified, also list branches which have
1387 inactive. If -c/--closed is specified, also list branches which have
1388 been marked closed (see :hg:`commit --close-branch`).
1388 been marked closed (see :hg:`commit --close-branch`).
1389
1389
1390 Use the command :hg:`update` to switch to an existing branch.
1390 Use the command :hg:`update` to switch to an existing branch.
1391
1391
1392 .. container:: verbose
1392 .. container:: verbose
1393
1393
1394 Template:
1394 Template:
1395
1395
1396 The following keywords are supported in addition to the common template
1396 The following keywords are supported in addition to the common template
1397 keywords and functions such as ``{branch}``. See also
1397 keywords and functions such as ``{branch}``. See also
1398 :hg:`help templates`.
1398 :hg:`help templates`.
1399
1399
1400 :active: Boolean. True if the branch is active.
1400 :active: Boolean. True if the branch is active.
1401 :closed: Boolean. True if the branch is closed.
1401 :closed: Boolean. True if the branch is closed.
1402 :current: Boolean. True if it is the current branch.
1402 :current: Boolean. True if it is the current branch.
1403
1403
1404 Returns 0.
1404 Returns 0.
1405 """
1405 """
1406
1406
1407 opts = pycompat.byteskwargs(opts)
1407 opts = pycompat.byteskwargs(opts)
1408 revs = opts.get(b'rev')
1408 revs = opts.get(b'rev')
1409 selectedbranches = None
1409 selectedbranches = None
1410 if revs:
1410 if revs:
1411 revs = scmutil.revrange(repo, revs)
1411 revs = scmutil.revrange(repo, revs)
1412 getbi = repo.revbranchcache().branchinfo
1412 getbi = repo.revbranchcache().branchinfo
1413 selectedbranches = {getbi(r)[0] for r in revs}
1413 selectedbranches = {getbi(r)[0] for r in revs}
1414
1414
1415 ui.pager(b'branches')
1415 ui.pager(b'branches')
1416 fm = ui.formatter(b'branches', opts)
1416 fm = ui.formatter(b'branches', opts)
1417 hexfunc = fm.hexfunc
1417 hexfunc = fm.hexfunc
1418
1418
1419 allheads = set(repo.heads())
1419 allheads = set(repo.heads())
1420 branches = []
1420 branches = []
1421 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1421 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1422 if selectedbranches is not None and tag not in selectedbranches:
1422 if selectedbranches is not None and tag not in selectedbranches:
1423 continue
1423 continue
1424 isactive = False
1424 isactive = False
1425 if not isclosed:
1425 if not isclosed:
1426 openheads = set(repo.branchmap().iteropen(heads))
1426 openheads = set(repo.branchmap().iteropen(heads))
1427 isactive = bool(openheads & allheads)
1427 isactive = bool(openheads & allheads)
1428 branches.append((tag, repo[tip], isactive, not isclosed))
1428 branches.append((tag, repo[tip], isactive, not isclosed))
1429 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1429 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1430
1430
1431 for tag, ctx, isactive, isopen in branches:
1431 for tag, ctx, isactive, isopen in branches:
1432 if active and not isactive:
1432 if active and not isactive:
1433 continue
1433 continue
1434 if isactive:
1434 if isactive:
1435 label = b'branches.active'
1435 label = b'branches.active'
1436 notice = b''
1436 notice = b''
1437 elif not isopen:
1437 elif not isopen:
1438 if not closed:
1438 if not closed:
1439 continue
1439 continue
1440 label = b'branches.closed'
1440 label = b'branches.closed'
1441 notice = _(b' (closed)')
1441 notice = _(b' (closed)')
1442 else:
1442 else:
1443 label = b'branches.inactive'
1443 label = b'branches.inactive'
1444 notice = _(b' (inactive)')
1444 notice = _(b' (inactive)')
1445 current = tag == repo.dirstate.branch()
1445 current = tag == repo.dirstate.branch()
1446 if current:
1446 if current:
1447 label = b'branches.current'
1447 label = b'branches.current'
1448
1448
1449 fm.startitem()
1449 fm.startitem()
1450 fm.write(b'branch', b'%s', tag, label=label)
1450 fm.write(b'branch', b'%s', tag, label=label)
1451 rev = ctx.rev()
1451 rev = ctx.rev()
1452 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1452 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1453 fmt = b' ' * padsize + b' %d:%s'
1453 fmt = b' ' * padsize + b' %d:%s'
1454 fm.condwrite(
1454 fm.condwrite(
1455 not ui.quiet,
1455 not ui.quiet,
1456 b'rev node',
1456 b'rev node',
1457 fmt,
1457 fmt,
1458 rev,
1458 rev,
1459 hexfunc(ctx.node()),
1459 hexfunc(ctx.node()),
1460 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1460 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1461 )
1461 )
1462 fm.context(ctx=ctx)
1462 fm.context(ctx=ctx)
1463 fm.data(active=isactive, closed=not isopen, current=current)
1463 fm.data(active=isactive, closed=not isopen, current=current)
1464 if not ui.quiet:
1464 if not ui.quiet:
1465 fm.plain(notice)
1465 fm.plain(notice)
1466 fm.plain(b'\n')
1466 fm.plain(b'\n')
1467 fm.end()
1467 fm.end()
1468
1468
1469
1469
1470 @command(
1470 @command(
1471 b'bundle',
1471 b'bundle',
1472 [
1472 [
1473 (
1473 (
1474 b'f',
1474 b'f',
1475 b'force',
1475 b'force',
1476 None,
1476 None,
1477 _(b'run even when the destination is unrelated'),
1477 _(b'run even when the destination is unrelated'),
1478 ),
1478 ),
1479 (
1479 (
1480 b'r',
1480 b'r',
1481 b'rev',
1481 b'rev',
1482 [],
1482 [],
1483 _(b'a changeset intended to be added to the destination'),
1483 _(b'a changeset intended to be added to the destination'),
1484 _(b'REV'),
1484 _(b'REV'),
1485 ),
1485 ),
1486 (
1486 (
1487 b'b',
1487 b'b',
1488 b'branch',
1488 b'branch',
1489 [],
1489 [],
1490 _(b'a specific branch you would like to bundle'),
1490 _(b'a specific branch you would like to bundle'),
1491 _(b'BRANCH'),
1491 _(b'BRANCH'),
1492 ),
1492 ),
1493 (
1493 (
1494 b'',
1494 b'',
1495 b'base',
1495 b'base',
1496 [],
1496 [],
1497 _(b'a base changeset assumed to be available at the destination'),
1497 _(b'a base changeset assumed to be available at the destination'),
1498 _(b'REV'),
1498 _(b'REV'),
1499 ),
1499 ),
1500 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1500 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1501 (
1501 (
1502 b't',
1502 b't',
1503 b'type',
1503 b'type',
1504 b'bzip2',
1504 b'bzip2',
1505 _(b'bundle compression type to use'),
1505 _(b'bundle compression type to use'),
1506 _(b'TYPE'),
1506 _(b'TYPE'),
1507 ),
1507 ),
1508 ]
1508 ]
1509 + remoteopts,
1509 + remoteopts,
1510 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1510 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1511 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1511 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1512 )
1512 )
1513 def bundle(ui, repo, fname, dest=None, **opts):
1513 def bundle(ui, repo, fname, dest=None, **opts):
1514 """create a bundle file
1514 """create a bundle file
1515
1515
1516 Generate a bundle file containing data to be transferred to another
1516 Generate a bundle file containing data to be transferred to another
1517 repository.
1517 repository.
1518
1518
1519 To create a bundle containing all changesets, use -a/--all
1519 To create a bundle containing all changesets, use -a/--all
1520 (or --base null). Otherwise, hg assumes the destination will have
1520 (or --base null). Otherwise, hg assumes the destination will have
1521 all the nodes you specify with --base parameters. Otherwise, hg
1521 all the nodes you specify with --base parameters. Otherwise, hg
1522 will assume the repository has all the nodes in destination, or
1522 will assume the repository has all the nodes in destination, or
1523 default-push/default if no destination is specified, where destination
1523 default-push/default if no destination is specified, where destination
1524 is the repository you provide through DEST option.
1524 is the repository you provide through DEST option.
1525
1525
1526 You can change bundle format with the -t/--type option. See
1526 You can change bundle format with the -t/--type option. See
1527 :hg:`help bundlespec` for documentation on this format. By default,
1527 :hg:`help bundlespec` for documentation on this format. By default,
1528 the most appropriate format is used and compression defaults to
1528 the most appropriate format is used and compression defaults to
1529 bzip2.
1529 bzip2.
1530
1530
1531 The bundle file can then be transferred using conventional means
1531 The bundle file can then be transferred using conventional means
1532 and applied to another repository with the unbundle or pull
1532 and applied to another repository with the unbundle or pull
1533 command. This is useful when direct push and pull are not
1533 command. This is useful when direct push and pull are not
1534 available or when exporting an entire repository is undesirable.
1534 available or when exporting an entire repository is undesirable.
1535
1535
1536 Applying bundles preserves all changeset contents including
1536 Applying bundles preserves all changeset contents including
1537 permissions, copy/rename information, and revision history.
1537 permissions, copy/rename information, and revision history.
1538
1538
1539 Returns 0 on success, 1 if no changes found.
1539 Returns 0 on success, 1 if no changes found.
1540 """
1540 """
1541 opts = pycompat.byteskwargs(opts)
1541 opts = pycompat.byteskwargs(opts)
1542 revs = None
1542 revs = None
1543 if b'rev' in opts:
1543 if b'rev' in opts:
1544 revstrings = opts[b'rev']
1544 revstrings = opts[b'rev']
1545 revs = scmutil.revrange(repo, revstrings)
1545 revs = scmutil.revrange(repo, revstrings)
1546 if revstrings and not revs:
1546 if revstrings and not revs:
1547 raise error.Abort(_(b'no commits to bundle'))
1547 raise error.Abort(_(b'no commits to bundle'))
1548
1548
1549 bundletype = opts.get(b'type', b'bzip2').lower()
1549 bundletype = opts.get(b'type', b'bzip2').lower()
1550 try:
1550 try:
1551 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1551 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1552 except error.UnsupportedBundleSpecification as e:
1552 except error.UnsupportedBundleSpecification as e:
1553 raise error.Abort(
1553 raise error.Abort(
1554 pycompat.bytestr(e),
1554 pycompat.bytestr(e),
1555 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1555 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1556 )
1556 )
1557 cgversion = bundlespec.contentopts[b"cg.version"]
1557 cgversion = bundlespec.contentopts[b"cg.version"]
1558
1558
1559 # Packed bundles are a pseudo bundle format for now.
1559 # Packed bundles are a pseudo bundle format for now.
1560 if cgversion == b's1':
1560 if cgversion == b's1':
1561 raise error.Abort(
1561 raise error.Abort(
1562 _(b'packed bundles cannot be produced by "hg bundle"'),
1562 _(b'packed bundles cannot be produced by "hg bundle"'),
1563 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1563 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1564 )
1564 )
1565
1565
1566 if opts.get(b'all'):
1566 if opts.get(b'all'):
1567 if dest:
1567 if dest:
1568 raise error.Abort(
1568 raise error.Abort(
1569 _(b"--all is incompatible with specifying a destination")
1569 _(b"--all is incompatible with specifying a destination")
1570 )
1570 )
1571 if opts.get(b'base'):
1571 if opts.get(b'base'):
1572 ui.warn(_(b"ignoring --base because --all was specified\n"))
1572 ui.warn(_(b"ignoring --base because --all was specified\n"))
1573 base = [nullrev]
1573 base = [nullrev]
1574 else:
1574 else:
1575 base = scmutil.revrange(repo, opts.get(b'base'))
1575 base = scmutil.revrange(repo, opts.get(b'base'))
1576 if cgversion not in changegroup.supportedoutgoingversions(repo):
1576 if cgversion not in changegroup.supportedoutgoingversions(repo):
1577 raise error.Abort(
1577 raise error.Abort(
1578 _(b"repository does not support bundle version %s") % cgversion
1578 _(b"repository does not support bundle version %s") % cgversion
1579 )
1579 )
1580
1580
1581 if base:
1581 if base:
1582 if dest:
1582 if dest:
1583 raise error.Abort(
1583 raise error.Abort(
1584 _(b"--base is incompatible with specifying a destination")
1584 _(b"--base is incompatible with specifying a destination")
1585 )
1585 )
1586 common = [repo[rev].node() for rev in base]
1586 common = [repo[rev].node() for rev in base]
1587 heads = [repo[r].node() for r in revs] if revs else None
1587 heads = [repo[r].node() for r in revs] if revs else None
1588 outgoing = discovery.outgoing(repo, common, heads)
1588 outgoing = discovery.outgoing(repo, common, heads)
1589 else:
1589 else:
1590 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1590 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1591 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1591 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1592 other = hg.peer(repo, opts, dest)
1592 other = hg.peer(repo, opts, dest)
1593 revs = [repo[r].hex() for r in revs]
1593 revs = [repo[r].hex() for r in revs]
1594 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1594 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1595 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1595 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1596 outgoing = discovery.findcommonoutgoing(
1596 outgoing = discovery.findcommonoutgoing(
1597 repo,
1597 repo,
1598 other,
1598 other,
1599 onlyheads=heads,
1599 onlyheads=heads,
1600 force=opts.get(b'force'),
1600 force=opts.get(b'force'),
1601 portable=True,
1601 portable=True,
1602 )
1602 )
1603
1603
1604 if not outgoing.missing:
1604 if not outgoing.missing:
1605 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1605 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1606 return 1
1606 return 1
1607
1607
1608 if cgversion == b'01': # bundle1
1608 if cgversion == b'01': # bundle1
1609 bversion = b'HG10' + bundlespec.wirecompression
1609 bversion = b'HG10' + bundlespec.wirecompression
1610 bcompression = None
1610 bcompression = None
1611 elif cgversion in (b'02', b'03'):
1611 elif cgversion in (b'02', b'03'):
1612 bversion = b'HG20'
1612 bversion = b'HG20'
1613 bcompression = bundlespec.wirecompression
1613 bcompression = bundlespec.wirecompression
1614 else:
1614 else:
1615 raise error.ProgrammingError(
1615 raise error.ProgrammingError(
1616 b'bundle: unexpected changegroup version %s' % cgversion
1616 b'bundle: unexpected changegroup version %s' % cgversion
1617 )
1617 )
1618
1618
1619 # TODO compression options should be derived from bundlespec parsing.
1619 # TODO compression options should be derived from bundlespec parsing.
1620 # This is a temporary hack to allow adjusting bundle compression
1620 # This is a temporary hack to allow adjusting bundle compression
1621 # level without a) formalizing the bundlespec changes to declare it
1621 # level without a) formalizing the bundlespec changes to declare it
1622 # b) introducing a command flag.
1622 # b) introducing a command flag.
1623 compopts = {}
1623 compopts = {}
1624 complevel = ui.configint(
1624 complevel = ui.configint(
1625 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1625 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1626 )
1626 )
1627 if complevel is None:
1627 if complevel is None:
1628 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1628 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1629 if complevel is not None:
1629 if complevel is not None:
1630 compopts[b'level'] = complevel
1630 compopts[b'level'] = complevel
1631
1631
1632 # Allow overriding the bundling of obsmarker in phases through
1632 # Allow overriding the bundling of obsmarker in phases through
1633 # configuration while we don't have a bundle version that include them
1633 # configuration while we don't have a bundle version that include them
1634 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1634 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1635 bundlespec.contentopts[b'obsolescence'] = True
1635 bundlespec.contentopts[b'obsolescence'] = True
1636 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1636 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1637 bundlespec.contentopts[b'phases'] = True
1637 bundlespec.contentopts[b'phases'] = True
1638
1638
1639 bundle2.writenewbundle(
1639 bundle2.writenewbundle(
1640 ui,
1640 ui,
1641 repo,
1641 repo,
1642 b'bundle',
1642 b'bundle',
1643 fname,
1643 fname,
1644 bversion,
1644 bversion,
1645 outgoing,
1645 outgoing,
1646 bundlespec.contentopts,
1646 bundlespec.contentopts,
1647 compression=bcompression,
1647 compression=bcompression,
1648 compopts=compopts,
1648 compopts=compopts,
1649 )
1649 )
1650
1650
1651
1651
1652 @command(
1652 @command(
1653 b'cat',
1653 b'cat',
1654 [
1654 [
1655 (
1655 (
1656 b'o',
1656 b'o',
1657 b'output',
1657 b'output',
1658 b'',
1658 b'',
1659 _(b'print output to file with formatted name'),
1659 _(b'print output to file with formatted name'),
1660 _(b'FORMAT'),
1660 _(b'FORMAT'),
1661 ),
1661 ),
1662 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1662 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1663 (b'', b'decode', None, _(b'apply any matching decode filter')),
1663 (b'', b'decode', None, _(b'apply any matching decode filter')),
1664 ]
1664 ]
1665 + walkopts
1665 + walkopts
1666 + formatteropts,
1666 + formatteropts,
1667 _(b'[OPTION]... FILE...'),
1667 _(b'[OPTION]... FILE...'),
1668 helpcategory=command.CATEGORY_FILE_CONTENTS,
1668 helpcategory=command.CATEGORY_FILE_CONTENTS,
1669 inferrepo=True,
1669 inferrepo=True,
1670 intents={INTENT_READONLY},
1670 intents={INTENT_READONLY},
1671 )
1671 )
1672 def cat(ui, repo, file1, *pats, **opts):
1672 def cat(ui, repo, file1, *pats, **opts):
1673 """output the current or given revision of files
1673 """output the current or given revision of files
1674
1674
1675 Print the specified files as they were at the given revision. If
1675 Print the specified files as they were at the given revision. If
1676 no revision is given, the parent of the working directory is used.
1676 no revision is given, the parent of the working directory is used.
1677
1677
1678 Output may be to a file, in which case the name of the file is
1678 Output may be to a file, in which case the name of the file is
1679 given using a template string. See :hg:`help templates`. In addition
1679 given using a template string. See :hg:`help templates`. In addition
1680 to the common template keywords, the following formatting rules are
1680 to the common template keywords, the following formatting rules are
1681 supported:
1681 supported:
1682
1682
1683 :``%%``: literal "%" character
1683 :``%%``: literal "%" character
1684 :``%s``: basename of file being printed
1684 :``%s``: basename of file being printed
1685 :``%d``: dirname of file being printed, or '.' if in repository root
1685 :``%d``: dirname of file being printed, or '.' if in repository root
1686 :``%p``: root-relative path name of file being printed
1686 :``%p``: root-relative path name of file being printed
1687 :``%H``: changeset hash (40 hexadecimal digits)
1687 :``%H``: changeset hash (40 hexadecimal digits)
1688 :``%R``: changeset revision number
1688 :``%R``: changeset revision number
1689 :``%h``: short-form changeset hash (12 hexadecimal digits)
1689 :``%h``: short-form changeset hash (12 hexadecimal digits)
1690 :``%r``: zero-padded changeset revision number
1690 :``%r``: zero-padded changeset revision number
1691 :``%b``: basename of the exporting repository
1691 :``%b``: basename of the exporting repository
1692 :``\\``: literal "\\" character
1692 :``\\``: literal "\\" character
1693
1693
1694 .. container:: verbose
1694 .. container:: verbose
1695
1695
1696 Template:
1696 Template:
1697
1697
1698 The following keywords are supported in addition to the common template
1698 The following keywords are supported in addition to the common template
1699 keywords and functions. See also :hg:`help templates`.
1699 keywords and functions. See also :hg:`help templates`.
1700
1700
1701 :data: String. File content.
1701 :data: String. File content.
1702 :path: String. Repository-absolute path of the file.
1702 :path: String. Repository-absolute path of the file.
1703
1703
1704 Returns 0 on success.
1704 Returns 0 on success.
1705 """
1705 """
1706 opts = pycompat.byteskwargs(opts)
1706 opts = pycompat.byteskwargs(opts)
1707 rev = opts.get(b'rev')
1707 rev = opts.get(b'rev')
1708 if rev:
1708 if rev:
1709 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1709 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1710 ctx = scmutil.revsingle(repo, rev)
1710 ctx = scmutil.revsingle(repo, rev)
1711 m = scmutil.match(ctx, (file1,) + pats, opts)
1711 m = scmutil.match(ctx, (file1,) + pats, opts)
1712 fntemplate = opts.pop(b'output', b'')
1712 fntemplate = opts.pop(b'output', b'')
1713 if cmdutil.isstdiofilename(fntemplate):
1713 if cmdutil.isstdiofilename(fntemplate):
1714 fntemplate = b''
1714 fntemplate = b''
1715
1715
1716 if fntemplate:
1716 if fntemplate:
1717 fm = formatter.nullformatter(ui, b'cat', opts)
1717 fm = formatter.nullformatter(ui, b'cat', opts)
1718 else:
1718 else:
1719 ui.pager(b'cat')
1719 ui.pager(b'cat')
1720 fm = ui.formatter(b'cat', opts)
1720 fm = ui.formatter(b'cat', opts)
1721 with fm:
1721 with fm:
1722 return cmdutil.cat(
1722 return cmdutil.cat(
1723 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1723 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1724 )
1724 )
1725
1725
1726
1726
1727 @command(
1727 @command(
1728 b'clone',
1728 b'clone',
1729 [
1729 [
1730 (
1730 (
1731 b'U',
1731 b'U',
1732 b'noupdate',
1732 b'noupdate',
1733 None,
1733 None,
1734 _(
1734 _(
1735 b'the clone will include an empty working '
1735 b'the clone will include an empty working '
1736 b'directory (only a repository)'
1736 b'directory (only a repository)'
1737 ),
1737 ),
1738 ),
1738 ),
1739 (
1739 (
1740 b'u',
1740 b'u',
1741 b'updaterev',
1741 b'updaterev',
1742 b'',
1742 b'',
1743 _(b'revision, tag, or branch to check out'),
1743 _(b'revision, tag, or branch to check out'),
1744 _(b'REV'),
1744 _(b'REV'),
1745 ),
1745 ),
1746 (
1746 (
1747 b'r',
1747 b'r',
1748 b'rev',
1748 b'rev',
1749 [],
1749 [],
1750 _(
1750 _(
1751 b'do not clone everything, but include this changeset'
1751 b'do not clone everything, but include this changeset'
1752 b' and its ancestors'
1752 b' and its ancestors'
1753 ),
1753 ),
1754 _(b'REV'),
1754 _(b'REV'),
1755 ),
1755 ),
1756 (
1756 (
1757 b'b',
1757 b'b',
1758 b'branch',
1758 b'branch',
1759 [],
1759 [],
1760 _(
1760 _(
1761 b'do not clone everything, but include this branch\'s'
1761 b'do not clone everything, but include this branch\'s'
1762 b' changesets and their ancestors'
1762 b' changesets and their ancestors'
1763 ),
1763 ),
1764 _(b'BRANCH'),
1764 _(b'BRANCH'),
1765 ),
1765 ),
1766 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1766 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1767 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1767 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1768 (b'', b'stream', None, _(b'clone with minimal data processing')),
1768 (b'', b'stream', None, _(b'clone with minimal data processing')),
1769 ]
1769 ]
1770 + remoteopts,
1770 + remoteopts,
1771 _(b'[OPTION]... SOURCE [DEST]'),
1771 _(b'[OPTION]... SOURCE [DEST]'),
1772 helpcategory=command.CATEGORY_REPO_CREATION,
1772 helpcategory=command.CATEGORY_REPO_CREATION,
1773 helpbasic=True,
1773 helpbasic=True,
1774 norepo=True,
1774 norepo=True,
1775 )
1775 )
1776 def clone(ui, source, dest=None, **opts):
1776 def clone(ui, source, dest=None, **opts):
1777 """make a copy of an existing repository
1777 """make a copy of an existing repository
1778
1778
1779 Create a copy of an existing repository in a new directory.
1779 Create a copy of an existing repository in a new directory.
1780
1780
1781 If no destination directory name is specified, it defaults to the
1781 If no destination directory name is specified, it defaults to the
1782 basename of the source.
1782 basename of the source.
1783
1783
1784 The location of the source is added to the new repository's
1784 The location of the source is added to the new repository's
1785 ``.hg/hgrc`` file, as the default to be used for future pulls.
1785 ``.hg/hgrc`` file, as the default to be used for future pulls.
1786
1786
1787 Only local paths and ``ssh://`` URLs are supported as
1787 Only local paths and ``ssh://`` URLs are supported as
1788 destinations. For ``ssh://`` destinations, no working directory or
1788 destinations. For ``ssh://`` destinations, no working directory or
1789 ``.hg/hgrc`` will be created on the remote side.
1789 ``.hg/hgrc`` will be created on the remote side.
1790
1790
1791 If the source repository has a bookmark called '@' set, that
1791 If the source repository has a bookmark called '@' set, that
1792 revision will be checked out in the new repository by default.
1792 revision will be checked out in the new repository by default.
1793
1793
1794 To check out a particular version, use -u/--update, or
1794 To check out a particular version, use -u/--update, or
1795 -U/--noupdate to create a clone with no working directory.
1795 -U/--noupdate to create a clone with no working directory.
1796
1796
1797 To pull only a subset of changesets, specify one or more revisions
1797 To pull only a subset of changesets, specify one or more revisions
1798 identifiers with -r/--rev or branches with -b/--branch. The
1798 identifiers with -r/--rev or branches with -b/--branch. The
1799 resulting clone will contain only the specified changesets and
1799 resulting clone will contain only the specified changesets and
1800 their ancestors. These options (or 'clone src#rev dest') imply
1800 their ancestors. These options (or 'clone src#rev dest') imply
1801 --pull, even for local source repositories.
1801 --pull, even for local source repositories.
1802
1802
1803 In normal clone mode, the remote normalizes repository data into a common
1803 In normal clone mode, the remote normalizes repository data into a common
1804 exchange format and the receiving end translates this data into its local
1804 exchange format and the receiving end translates this data into its local
1805 storage format. --stream activates a different clone mode that essentially
1805 storage format. --stream activates a different clone mode that essentially
1806 copies repository files from the remote with minimal data processing. This
1806 copies repository files from the remote with minimal data processing. This
1807 significantly reduces the CPU cost of a clone both remotely and locally.
1807 significantly reduces the CPU cost of a clone both remotely and locally.
1808 However, it often increases the transferred data size by 30-40%. This can
1808 However, it often increases the transferred data size by 30-40%. This can
1809 result in substantially faster clones where I/O throughput is plentiful,
1809 result in substantially faster clones where I/O throughput is plentiful,
1810 especially for larger repositories. A side-effect of --stream clones is
1810 especially for larger repositories. A side-effect of --stream clones is
1811 that storage settings and requirements on the remote are applied locally:
1811 that storage settings and requirements on the remote are applied locally:
1812 a modern client may inherit legacy or inefficient storage used by the
1812 a modern client may inherit legacy or inefficient storage used by the
1813 remote or a legacy Mercurial client may not be able to clone from a
1813 remote or a legacy Mercurial client may not be able to clone from a
1814 modern Mercurial remote.
1814 modern Mercurial remote.
1815
1815
1816 .. note::
1816 .. note::
1817
1817
1818 Specifying a tag will include the tagged changeset but not the
1818 Specifying a tag will include the tagged changeset but not the
1819 changeset containing the tag.
1819 changeset containing the tag.
1820
1820
1821 .. container:: verbose
1821 .. container:: verbose
1822
1822
1823 For efficiency, hardlinks are used for cloning whenever the
1823 For efficiency, hardlinks are used for cloning whenever the
1824 source and destination are on the same filesystem (note this
1824 source and destination are on the same filesystem (note this
1825 applies only to the repository data, not to the working
1825 applies only to the repository data, not to the working
1826 directory). Some filesystems, such as AFS, implement hardlinking
1826 directory). Some filesystems, such as AFS, implement hardlinking
1827 incorrectly, but do not report errors. In these cases, use the
1827 incorrectly, but do not report errors. In these cases, use the
1828 --pull option to avoid hardlinking.
1828 --pull option to avoid hardlinking.
1829
1829
1830 Mercurial will update the working directory to the first applicable
1830 Mercurial will update the working directory to the first applicable
1831 revision from this list:
1831 revision from this list:
1832
1832
1833 a) null if -U or the source repository has no changesets
1833 a) null if -U or the source repository has no changesets
1834 b) if -u . and the source repository is local, the first parent of
1834 b) if -u . and the source repository is local, the first parent of
1835 the source repository's working directory
1835 the source repository's working directory
1836 c) the changeset specified with -u (if a branch name, this means the
1836 c) the changeset specified with -u (if a branch name, this means the
1837 latest head of that branch)
1837 latest head of that branch)
1838 d) the changeset specified with -r
1838 d) the changeset specified with -r
1839 e) the tipmost head specified with -b
1839 e) the tipmost head specified with -b
1840 f) the tipmost head specified with the url#branch source syntax
1840 f) the tipmost head specified with the url#branch source syntax
1841 g) the revision marked with the '@' bookmark, if present
1841 g) the revision marked with the '@' bookmark, if present
1842 h) the tipmost head of the default branch
1842 h) the tipmost head of the default branch
1843 i) tip
1843 i) tip
1844
1844
1845 When cloning from servers that support it, Mercurial may fetch
1845 When cloning from servers that support it, Mercurial may fetch
1846 pre-generated data from a server-advertised URL or inline from the
1846 pre-generated data from a server-advertised URL or inline from the
1847 same stream. When this is done, hooks operating on incoming changesets
1847 same stream. When this is done, hooks operating on incoming changesets
1848 and changegroups may fire more than once, once for each pre-generated
1848 and changegroups may fire more than once, once for each pre-generated
1849 bundle and as well as for any additional remaining data. In addition,
1849 bundle and as well as for any additional remaining data. In addition,
1850 if an error occurs, the repository may be rolled back to a partial
1850 if an error occurs, the repository may be rolled back to a partial
1851 clone. This behavior may change in future releases.
1851 clone. This behavior may change in future releases.
1852 See :hg:`help -e clonebundles` for more.
1852 See :hg:`help -e clonebundles` for more.
1853
1853
1854 Examples:
1854 Examples:
1855
1855
1856 - clone a remote repository to a new directory named hg/::
1856 - clone a remote repository to a new directory named hg/::
1857
1857
1858 hg clone https://www.mercurial-scm.org/repo/hg/
1858 hg clone https://www.mercurial-scm.org/repo/hg/
1859
1859
1860 - create a lightweight local clone::
1860 - create a lightweight local clone::
1861
1861
1862 hg clone project/ project-feature/
1862 hg clone project/ project-feature/
1863
1863
1864 - clone from an absolute path on an ssh server (note double-slash)::
1864 - clone from an absolute path on an ssh server (note double-slash)::
1865
1865
1866 hg clone ssh://user@server//home/projects/alpha/
1866 hg clone ssh://user@server//home/projects/alpha/
1867
1867
1868 - do a streaming clone while checking out a specified version::
1868 - do a streaming clone while checking out a specified version::
1869
1869
1870 hg clone --stream http://server/repo -u 1.5
1870 hg clone --stream http://server/repo -u 1.5
1871
1871
1872 - create a repository without changesets after a particular revision::
1872 - create a repository without changesets after a particular revision::
1873
1873
1874 hg clone -r 04e544 experimental/ good/
1874 hg clone -r 04e544 experimental/ good/
1875
1875
1876 - clone (and track) a particular named branch::
1876 - clone (and track) a particular named branch::
1877
1877
1878 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1878 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1879
1879
1880 See :hg:`help urls` for details on specifying URLs.
1880 See :hg:`help urls` for details on specifying URLs.
1881
1881
1882 Returns 0 on success.
1882 Returns 0 on success.
1883 """
1883 """
1884 opts = pycompat.byteskwargs(opts)
1884 opts = pycompat.byteskwargs(opts)
1885 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1885 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1886
1886
1887 # --include/--exclude can come from narrow or sparse.
1887 # --include/--exclude can come from narrow or sparse.
1888 includepats, excludepats = None, None
1888 includepats, excludepats = None, None
1889
1889
1890 # hg.clone() differentiates between None and an empty set. So make sure
1890 # hg.clone() differentiates between None and an empty set. So make sure
1891 # patterns are sets if narrow is requested without patterns.
1891 # patterns are sets if narrow is requested without patterns.
1892 if opts.get(b'narrow'):
1892 if opts.get(b'narrow'):
1893 includepats = set()
1893 includepats = set()
1894 excludepats = set()
1894 excludepats = set()
1895
1895
1896 if opts.get(b'include'):
1896 if opts.get(b'include'):
1897 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1897 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1898 if opts.get(b'exclude'):
1898 if opts.get(b'exclude'):
1899 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1899 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1900
1900
1901 r = hg.clone(
1901 r = hg.clone(
1902 ui,
1902 ui,
1903 opts,
1903 opts,
1904 source,
1904 source,
1905 dest,
1905 dest,
1906 pull=opts.get(b'pull'),
1906 pull=opts.get(b'pull'),
1907 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1907 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1908 revs=opts.get(b'rev'),
1908 revs=opts.get(b'rev'),
1909 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1909 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1910 branch=opts.get(b'branch'),
1910 branch=opts.get(b'branch'),
1911 shareopts=opts.get(b'shareopts'),
1911 shareopts=opts.get(b'shareopts'),
1912 storeincludepats=includepats,
1912 storeincludepats=includepats,
1913 storeexcludepats=excludepats,
1913 storeexcludepats=excludepats,
1914 depth=opts.get(b'depth') or None,
1914 depth=opts.get(b'depth') or None,
1915 )
1915 )
1916
1916
1917 return r is None
1917 return r is None
1918
1918
1919
1919
1920 @command(
1920 @command(
1921 b'commit|ci',
1921 b'commit|ci',
1922 [
1922 [
1923 (
1923 (
1924 b'A',
1924 b'A',
1925 b'addremove',
1925 b'addremove',
1926 None,
1926 None,
1927 _(b'mark new/missing files as added/removed before committing'),
1927 _(b'mark new/missing files as added/removed before committing'),
1928 ),
1928 ),
1929 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1929 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1930 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1930 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1931 (b's', b'secret', None, _(b'use the secret phase for committing')),
1931 (b's', b'secret', None, _(b'use the secret phase for committing')),
1932 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1932 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1933 (
1933 (
1934 b'',
1934 b'',
1935 b'force-close-branch',
1935 b'force-close-branch',
1936 None,
1936 None,
1937 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1937 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1938 ),
1938 ),
1939 (b'i', b'interactive', None, _(b'use interactive mode')),
1939 (b'i', b'interactive', None, _(b'use interactive mode')),
1940 ]
1940 ]
1941 + walkopts
1941 + walkopts
1942 + commitopts
1942 + commitopts
1943 + commitopts2
1943 + commitopts2
1944 + subrepoopts,
1944 + subrepoopts,
1945 _(b'[OPTION]... [FILE]...'),
1945 _(b'[OPTION]... [FILE]...'),
1946 helpcategory=command.CATEGORY_COMMITTING,
1946 helpcategory=command.CATEGORY_COMMITTING,
1947 helpbasic=True,
1947 helpbasic=True,
1948 inferrepo=True,
1948 inferrepo=True,
1949 )
1949 )
1950 def commit(ui, repo, *pats, **opts):
1950 def commit(ui, repo, *pats, **opts):
1951 """commit the specified files or all outstanding changes
1951 """commit the specified files or all outstanding changes
1952
1952
1953 Commit changes to the given files into the repository. Unlike a
1953 Commit changes to the given files into the repository. Unlike a
1954 centralized SCM, this operation is a local operation. See
1954 centralized SCM, this operation is a local operation. See
1955 :hg:`push` for a way to actively distribute your changes.
1955 :hg:`push` for a way to actively distribute your changes.
1956
1956
1957 If a list of files is omitted, all changes reported by :hg:`status`
1957 If a list of files is omitted, all changes reported by :hg:`status`
1958 will be committed.
1958 will be committed.
1959
1959
1960 If you are committing the result of a merge, do not provide any
1960 If you are committing the result of a merge, do not provide any
1961 filenames or -I/-X filters.
1961 filenames or -I/-X filters.
1962
1962
1963 If no commit message is specified, Mercurial starts your
1963 If no commit message is specified, Mercurial starts your
1964 configured editor where you can enter a message. In case your
1964 configured editor where you can enter a message. In case your
1965 commit fails, you will find a backup of your message in
1965 commit fails, you will find a backup of your message in
1966 ``.hg/last-message.txt``.
1966 ``.hg/last-message.txt``.
1967
1967
1968 The --close-branch flag can be used to mark the current branch
1968 The --close-branch flag can be used to mark the current branch
1969 head closed. When all heads of a branch are closed, the branch
1969 head closed. When all heads of a branch are closed, the branch
1970 will be considered closed and no longer listed.
1970 will be considered closed and no longer listed.
1971
1971
1972 The --amend flag can be used to amend the parent of the
1972 The --amend flag can be used to amend the parent of the
1973 working directory with a new commit that contains the changes
1973 working directory with a new commit that contains the changes
1974 in the parent in addition to those currently reported by :hg:`status`,
1974 in the parent in addition to those currently reported by :hg:`status`,
1975 if there are any. The old commit is stored in a backup bundle in
1975 if there are any. The old commit is stored in a backup bundle in
1976 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1976 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1977 on how to restore it).
1977 on how to restore it).
1978
1978
1979 Message, user and date are taken from the amended commit unless
1979 Message, user and date are taken from the amended commit unless
1980 specified. When a message isn't specified on the command line,
1980 specified. When a message isn't specified on the command line,
1981 the editor will open with the message of the amended commit.
1981 the editor will open with the message of the amended commit.
1982
1982
1983 It is not possible to amend public changesets (see :hg:`help phases`)
1983 It is not possible to amend public changesets (see :hg:`help phases`)
1984 or changesets that have children.
1984 or changesets that have children.
1985
1985
1986 See :hg:`help dates` for a list of formats valid for -d/--date.
1986 See :hg:`help dates` for a list of formats valid for -d/--date.
1987
1987
1988 Returns 0 on success, 1 if nothing changed.
1988 Returns 0 on success, 1 if nothing changed.
1989
1989
1990 .. container:: verbose
1990 .. container:: verbose
1991
1991
1992 Examples:
1992 Examples:
1993
1993
1994 - commit all files ending in .py::
1994 - commit all files ending in .py::
1995
1995
1996 hg commit --include "set:**.py"
1996 hg commit --include "set:**.py"
1997
1997
1998 - commit all non-binary files::
1998 - commit all non-binary files::
1999
1999
2000 hg commit --exclude "set:binary()"
2000 hg commit --exclude "set:binary()"
2001
2001
2002 - amend the current commit and set the date to now::
2002 - amend the current commit and set the date to now::
2003
2003
2004 hg commit --amend --date now
2004 hg commit --amend --date now
2005 """
2005 """
2006 with repo.wlock(), repo.lock():
2006 with repo.wlock(), repo.lock():
2007 return _docommit(ui, repo, *pats, **opts)
2007 return _docommit(ui, repo, *pats, **opts)
2008
2008
2009
2009
2010 def _docommit(ui, repo, *pats, **opts):
2010 def _docommit(ui, repo, *pats, **opts):
2011 if opts.get('interactive'):
2011 if opts.get('interactive'):
2012 opts.pop('interactive')
2012 opts.pop('interactive')
2013 ret = cmdutil.dorecord(
2013 ret = cmdutil.dorecord(
2014 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2014 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2015 )
2015 )
2016 # ret can be 0 (no changes to record) or the value returned by
2016 # ret can be 0 (no changes to record) or the value returned by
2017 # commit(), 1 if nothing changed or None on success.
2017 # commit(), 1 if nothing changed or None on success.
2018 return 1 if ret == 0 else ret
2018 return 1 if ret == 0 else ret
2019
2019
2020 opts = pycompat.byteskwargs(opts)
2020 opts = pycompat.byteskwargs(opts)
2021 if opts.get(b'subrepos'):
2021 if opts.get(b'subrepos'):
2022 if opts.get(b'amend'):
2022 if opts.get(b'amend'):
2023 raise error.Abort(_(b'cannot amend with --subrepos'))
2023 raise error.Abort(_(b'cannot amend with --subrepos'))
2024 # Let --subrepos on the command line override config setting.
2024 # Let --subrepos on the command line override config setting.
2025 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2025 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2026
2026
2027 cmdutil.checkunfinished(repo, commit=True)
2027 cmdutil.checkunfinished(repo, commit=True)
2028
2028
2029 branch = repo[None].branch()
2029 branch = repo[None].branch()
2030 bheads = repo.branchheads(branch)
2030 bheads = repo.branchheads(branch)
2031
2031
2032 extra = {}
2032 extra = {}
2033 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2033 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2034 extra[b'close'] = b'1'
2034 extra[b'close'] = b'1'
2035
2035
2036 if repo[b'.'].closesbranch():
2036 if repo[b'.'].closesbranch():
2037 raise error.Abort(
2037 raise error.Abort(
2038 _(b'current revision is already a branch closing head')
2038 _(b'current revision is already a branch closing head')
2039 )
2039 )
2040 elif not bheads:
2040 elif not bheads:
2041 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2041 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2042 elif (
2042 elif (
2043 branch == repo[b'.'].branch()
2043 branch == repo[b'.'].branch()
2044 and repo[b'.'].node() not in bheads
2044 and repo[b'.'].node() not in bheads
2045 and not opts.get(b'force_close_branch')
2045 and not opts.get(b'force_close_branch')
2046 ):
2046 ):
2047 hint = _(
2047 hint = _(
2048 b'use --force-close-branch to close branch from a non-head'
2048 b'use --force-close-branch to close branch from a non-head'
2049 b' changeset'
2049 b' changeset'
2050 )
2050 )
2051 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2051 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2052 elif opts.get(b'amend'):
2052 elif opts.get(b'amend'):
2053 if (
2053 if (
2054 repo[b'.'].p1().branch() != branch
2054 repo[b'.'].p1().branch() != branch
2055 and repo[b'.'].p2().branch() != branch
2055 and repo[b'.'].p2().branch() != branch
2056 ):
2056 ):
2057 raise error.Abort(_(b'can only close branch heads'))
2057 raise error.Abort(_(b'can only close branch heads'))
2058
2058
2059 if opts.get(b'amend'):
2059 if opts.get(b'amend'):
2060 if ui.configbool(b'ui', b'commitsubrepos'):
2060 if ui.configbool(b'ui', b'commitsubrepos'):
2061 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2061 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2062
2062
2063 old = repo[b'.']
2063 old = repo[b'.']
2064 rewriteutil.precheck(repo, [old.rev()], b'amend')
2064 rewriteutil.precheck(repo, [old.rev()], b'amend')
2065
2065
2066 # Currently histedit gets confused if an amend happens while histedit
2066 # Currently histedit gets confused if an amend happens while histedit
2067 # is in progress. Since we have a checkunfinished command, we are
2067 # is in progress. Since we have a checkunfinished command, we are
2068 # temporarily honoring it.
2068 # temporarily honoring it.
2069 #
2069 #
2070 # Note: eventually this guard will be removed. Please do not expect
2070 # Note: eventually this guard will be removed. Please do not expect
2071 # this behavior to remain.
2071 # this behavior to remain.
2072 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2072 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2073 cmdutil.checkunfinished(repo)
2073 cmdutil.checkunfinished(repo)
2074
2074
2075 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2075 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2076 if node == old.node():
2076 if node == old.node():
2077 ui.status(_(b"nothing changed\n"))
2077 ui.status(_(b"nothing changed\n"))
2078 return 1
2078 return 1
2079 else:
2079 else:
2080
2080
2081 def commitfunc(ui, repo, message, match, opts):
2081 def commitfunc(ui, repo, message, match, opts):
2082 overrides = {}
2082 overrides = {}
2083 if opts.get(b'secret'):
2083 if opts.get(b'secret'):
2084 overrides[(b'phases', b'new-commit')] = b'secret'
2084 overrides[(b'phases', b'new-commit')] = b'secret'
2085
2085
2086 baseui = repo.baseui
2086 baseui = repo.baseui
2087 with baseui.configoverride(overrides, b'commit'):
2087 with baseui.configoverride(overrides, b'commit'):
2088 with ui.configoverride(overrides, b'commit'):
2088 with ui.configoverride(overrides, b'commit'):
2089 editform = cmdutil.mergeeditform(
2089 editform = cmdutil.mergeeditform(
2090 repo[None], b'commit.normal'
2090 repo[None], b'commit.normal'
2091 )
2091 )
2092 editor = cmdutil.getcommiteditor(
2092 editor = cmdutil.getcommiteditor(
2093 editform=editform, **pycompat.strkwargs(opts)
2093 editform=editform, **pycompat.strkwargs(opts)
2094 )
2094 )
2095 return repo.commit(
2095 return repo.commit(
2096 message,
2096 message,
2097 opts.get(b'user'),
2097 opts.get(b'user'),
2098 opts.get(b'date'),
2098 opts.get(b'date'),
2099 match,
2099 match,
2100 editor=editor,
2100 editor=editor,
2101 extra=extra,
2101 extra=extra,
2102 )
2102 )
2103
2103
2104 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2104 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2105
2105
2106 if not node:
2106 if not node:
2107 stat = cmdutil.postcommitstatus(repo, pats, opts)
2107 stat = cmdutil.postcommitstatus(repo, pats, opts)
2108 if stat.deleted:
2108 if stat.deleted:
2109 ui.status(
2109 ui.status(
2110 _(
2110 _(
2111 b"nothing changed (%d missing files, see "
2111 b"nothing changed (%d missing files, see "
2112 b"'hg status')\n"
2112 b"'hg status')\n"
2113 )
2113 )
2114 % len(stat.deleted)
2114 % len(stat.deleted)
2115 )
2115 )
2116 else:
2116 else:
2117 ui.status(_(b"nothing changed\n"))
2117 ui.status(_(b"nothing changed\n"))
2118 return 1
2118 return 1
2119
2119
2120 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2120 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2121
2121
2122 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2122 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2123 status(
2123 status(
2124 ui,
2124 ui,
2125 repo,
2125 repo,
2126 modified=True,
2126 modified=True,
2127 added=True,
2127 added=True,
2128 removed=True,
2128 removed=True,
2129 deleted=True,
2129 deleted=True,
2130 unknown=True,
2130 unknown=True,
2131 subrepos=opts.get(b'subrepos'),
2131 subrepos=opts.get(b'subrepos'),
2132 )
2132 )
2133
2133
2134
2134
2135 @command(
2135 @command(
2136 b'config|showconfig|debugconfig',
2136 b'config|showconfig|debugconfig',
2137 [
2137 [
2138 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2138 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2139 (b'e', b'edit', None, _(b'edit user config')),
2139 (b'e', b'edit', None, _(b'edit user config')),
2140 (b'l', b'local', None, _(b'edit repository config')),
2140 (b'l', b'local', None, _(b'edit repository config')),
2141 (
2141 (
2142 b'',
2142 b'',
2143 b'shared',
2143 b'shared',
2144 None,
2144 None,
2145 _(b'edit shared source repository config (EXPERIMENTAL)'),
2145 _(b'edit shared source repository config (EXPERIMENTAL)'),
2146 ),
2146 ),
2147 (b'g', b'global', None, _(b'edit global config')),
2147 (b'g', b'global', None, _(b'edit global config')),
2148 ]
2148 ]
2149 + formatteropts,
2149 + formatteropts,
2150 _(b'[-u] [NAME]...'),
2150 _(b'[-u] [NAME]...'),
2151 helpcategory=command.CATEGORY_HELP,
2151 helpcategory=command.CATEGORY_HELP,
2152 optionalrepo=True,
2152 optionalrepo=True,
2153 intents={INTENT_READONLY},
2153 intents={INTENT_READONLY},
2154 )
2154 )
2155 def config(ui, repo, *values, **opts):
2155 def config(ui, repo, *values, **opts):
2156 """show combined config settings from all hgrc files
2156 """show combined config settings from all hgrc files
2157
2157
2158 With no arguments, print names and values of all config items.
2158 With no arguments, print names and values of all config items.
2159
2159
2160 With one argument of the form section.name, print just the value
2160 With one argument of the form section.name, print just the value
2161 of that config item.
2161 of that config item.
2162
2162
2163 With multiple arguments, print names and values of all config
2163 With multiple arguments, print names and values of all config
2164 items with matching section names or section.names.
2164 items with matching section names or section.names.
2165
2165
2166 With --edit, start an editor on the user-level config file. With
2166 With --edit, start an editor on the user-level config file. With
2167 --global, edit the system-wide config file. With --local, edit the
2167 --global, edit the system-wide config file. With --local, edit the
2168 repository-level config file.
2168 repository-level config file.
2169
2169
2170 With --debug, the source (filename and line number) is printed
2170 With --debug, the source (filename and line number) is printed
2171 for each config item.
2171 for each config item.
2172
2172
2173 See :hg:`help config` for more information about config files.
2173 See :hg:`help config` for more information about config files.
2174
2174
2175 .. container:: verbose
2175 .. container:: verbose
2176
2176
2177 Template:
2177 Template:
2178
2178
2179 The following keywords are supported. See also :hg:`help templates`.
2179 The following keywords are supported. See also :hg:`help templates`.
2180
2180
2181 :name: String. Config name.
2181 :name: String. Config name.
2182 :source: String. Filename and line number where the item is defined.
2182 :source: String. Filename and line number where the item is defined.
2183 :value: String. Config value.
2183 :value: String. Config value.
2184
2184
2185 The --shared flag can be used to edit the config file of shared source
2185 The --shared flag can be used to edit the config file of shared source
2186 repository. It only works when you have shared using the experimental
2186 repository. It only works when you have shared using the experimental
2187 share safe feature.
2187 share safe feature.
2188
2188
2189 Returns 0 on success, 1 if NAME does not exist.
2189 Returns 0 on success, 1 if NAME does not exist.
2190
2190
2191 """
2191 """
2192
2192
2193 opts = pycompat.byteskwargs(opts)
2193 opts = pycompat.byteskwargs(opts)
2194 editopts = (b'edit', b'local', b'global', b'shared')
2194 editopts = (b'edit', b'local', b'global', b'shared')
2195 if any(opts.get(o) for o in editopts):
2195 if any(opts.get(o) for o in editopts):
2196 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2196 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2197 if opts.get(b'local'):
2197 if opts.get(b'local'):
2198 if not repo:
2198 if not repo:
2199 raise error.Abort(_(b"can't use --local outside a repository"))
2199 raise error.Abort(_(b"can't use --local outside a repository"))
2200 paths = [repo.vfs.join(b'hgrc')]
2200 paths = [repo.vfs.join(b'hgrc')]
2201 elif opts.get(b'global'):
2201 elif opts.get(b'global'):
2202 paths = rcutil.systemrcpath()
2202 paths = rcutil.systemrcpath()
2203 elif opts.get(b'shared'):
2203 elif opts.get(b'shared'):
2204 if not repo.shared():
2204 if not repo.shared():
2205 raise error.Abort(
2205 raise error.Abort(
2206 _(b"repository is not shared; can't use --shared")
2206 _(b"repository is not shared; can't use --shared")
2207 )
2207 )
2208 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2208 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2209 raise error.Abort(
2209 raise error.Abort(
2210 _(
2210 _(
2211 b"share safe feature not unabled; "
2211 b"share safe feature not unabled; "
2212 b"unable to edit shared source repository config"
2212 b"unable to edit shared source repository config"
2213 )
2213 )
2214 )
2214 )
2215 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2215 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2216 else:
2216 else:
2217 paths = rcutil.userrcpath()
2217 paths = rcutil.userrcpath()
2218
2218
2219 for f in paths:
2219 for f in paths:
2220 if os.path.exists(f):
2220 if os.path.exists(f):
2221 break
2221 break
2222 else:
2222 else:
2223 if opts.get(b'global'):
2223 if opts.get(b'global'):
2224 samplehgrc = uimod.samplehgrcs[b'global']
2224 samplehgrc = uimod.samplehgrcs[b'global']
2225 elif opts.get(b'local'):
2225 elif opts.get(b'local'):
2226 samplehgrc = uimod.samplehgrcs[b'local']
2226 samplehgrc = uimod.samplehgrcs[b'local']
2227 else:
2227 else:
2228 samplehgrc = uimod.samplehgrcs[b'user']
2228 samplehgrc = uimod.samplehgrcs[b'user']
2229
2229
2230 f = paths[0]
2230 f = paths[0]
2231 fp = open(f, b"wb")
2231 fp = open(f, b"wb")
2232 fp.write(util.tonativeeol(samplehgrc))
2232 fp.write(util.tonativeeol(samplehgrc))
2233 fp.close()
2233 fp.close()
2234
2234
2235 editor = ui.geteditor()
2235 editor = ui.geteditor()
2236 ui.system(
2236 ui.system(
2237 b"%s \"%s\"" % (editor, f),
2237 b"%s \"%s\"" % (editor, f),
2238 onerr=error.Abort,
2238 onerr=error.Abort,
2239 errprefix=_(b"edit failed"),
2239 errprefix=_(b"edit failed"),
2240 blockedtag=b'config_edit',
2240 blockedtag=b'config_edit',
2241 )
2241 )
2242 return
2242 return
2243 ui.pager(b'config')
2243 ui.pager(b'config')
2244 fm = ui.formatter(b'config', opts)
2244 fm = ui.formatter(b'config', opts)
2245 for t, f in rcutil.rccomponents():
2245 for t, f in rcutil.rccomponents():
2246 if t == b'path':
2246 if t == b'path':
2247 ui.debug(b'read config from: %s\n' % f)
2247 ui.debug(b'read config from: %s\n' % f)
2248 elif t == b'resource':
2248 elif t == b'resource':
2249 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2249 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2250 elif t == b'items':
2250 elif t == b'items':
2251 # Don't print anything for 'items'.
2251 # Don't print anything for 'items'.
2252 pass
2252 pass
2253 else:
2253 else:
2254 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2254 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2255 untrusted = bool(opts.get(b'untrusted'))
2255 untrusted = bool(opts.get(b'untrusted'))
2256
2256
2257 selsections = selentries = []
2257 selsections = selentries = []
2258 if values:
2258 if values:
2259 selsections = [v for v in values if b'.' not in v]
2259 selsections = [v for v in values if b'.' not in v]
2260 selentries = [v for v in values if b'.' in v]
2260 selentries = [v for v in values if b'.' in v]
2261 uniquesel = len(selentries) == 1 and not selsections
2261 uniquesel = len(selentries) == 1 and not selsections
2262 selsections = set(selsections)
2262 selsections = set(selsections)
2263 selentries = set(selentries)
2263 selentries = set(selentries)
2264
2264
2265 matched = False
2265 matched = False
2266 for section, name, value in ui.walkconfig(untrusted=untrusted):
2266 for section, name, value in ui.walkconfig(untrusted=untrusted):
2267 source = ui.configsource(section, name, untrusted)
2267 source = ui.configsource(section, name, untrusted)
2268 value = pycompat.bytestr(value)
2268 value = pycompat.bytestr(value)
2269 defaultvalue = ui.configdefault(section, name)
2269 defaultvalue = ui.configdefault(section, name)
2270 if fm.isplain():
2270 if fm.isplain():
2271 source = source or b'none'
2271 source = source or b'none'
2272 value = value.replace(b'\n', b'\\n')
2272 value = value.replace(b'\n', b'\\n')
2273 entryname = section + b'.' + name
2273 entryname = section + b'.' + name
2274 if values and not (section in selsections or entryname in selentries):
2274 if values and not (section in selsections or entryname in selentries):
2275 continue
2275 continue
2276 fm.startitem()
2276 fm.startitem()
2277 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2277 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2278 if uniquesel:
2278 if uniquesel:
2279 fm.data(name=entryname)
2279 fm.data(name=entryname)
2280 fm.write(b'value', b'%s\n', value)
2280 fm.write(b'value', b'%s\n', value)
2281 else:
2281 else:
2282 fm.write(b'name value', b'%s=%s\n', entryname, value)
2282 fm.write(b'name value', b'%s=%s\n', entryname, value)
2283 if formatter.isprintable(defaultvalue):
2283 if formatter.isprintable(defaultvalue):
2284 fm.data(defaultvalue=defaultvalue)
2284 fm.data(defaultvalue=defaultvalue)
2285 elif isinstance(defaultvalue, list) and all(
2285 elif isinstance(defaultvalue, list) and all(
2286 formatter.isprintable(e) for e in defaultvalue
2286 formatter.isprintable(e) for e in defaultvalue
2287 ):
2287 ):
2288 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2288 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2289 # TODO: no idea how to process unsupported defaultvalue types
2289 # TODO: no idea how to process unsupported defaultvalue types
2290 matched = True
2290 matched = True
2291 fm.end()
2291 fm.end()
2292 if matched:
2292 if matched:
2293 return 0
2293 return 0
2294 return 1
2294 return 1
2295
2295
2296
2296
2297 @command(
2297 @command(
2298 b'continue',
2298 b'continue',
2299 dryrunopts,
2299 dryrunopts,
2300 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2300 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2301 helpbasic=True,
2301 helpbasic=True,
2302 )
2302 )
2303 def continuecmd(ui, repo, **opts):
2303 def continuecmd(ui, repo, **opts):
2304 """resumes an interrupted operation (EXPERIMENTAL)
2304 """resumes an interrupted operation (EXPERIMENTAL)
2305
2305
2306 Finishes a multistep operation like graft, histedit, rebase, merge,
2306 Finishes a multistep operation like graft, histedit, rebase, merge,
2307 and unshelve if they are in an interrupted state.
2307 and unshelve if they are in an interrupted state.
2308
2308
2309 use --dry-run/-n to dry run the command.
2309 use --dry-run/-n to dry run the command.
2310 """
2310 """
2311 dryrun = opts.get('dry_run')
2311 dryrun = opts.get('dry_run')
2312 contstate = cmdutil.getunfinishedstate(repo)
2312 contstate = cmdutil.getunfinishedstate(repo)
2313 if not contstate:
2313 if not contstate:
2314 raise error.Abort(_(b'no operation in progress'))
2314 raise error.Abort(_(b'no operation in progress'))
2315 if not contstate.continuefunc:
2315 if not contstate.continuefunc:
2316 raise error.Abort(
2316 raise error.Abort(
2317 (
2317 (
2318 _(b"%s in progress but does not support 'hg continue'")
2318 _(b"%s in progress but does not support 'hg continue'")
2319 % (contstate._opname)
2319 % (contstate._opname)
2320 ),
2320 ),
2321 hint=contstate.continuemsg(),
2321 hint=contstate.continuemsg(),
2322 )
2322 )
2323 if dryrun:
2323 if dryrun:
2324 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2324 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2325 return
2325 return
2326 return contstate.continuefunc(ui, repo)
2326 return contstate.continuefunc(ui, repo)
2327
2327
2328
2328
2329 @command(
2329 @command(
2330 b'copy|cp',
2330 b'copy|cp',
2331 [
2331 [
2332 (b'', b'forget', None, _(b'unmark a file as copied')),
2332 (b'', b'forget', None, _(b'unmark a file as copied')),
2333 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2333 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2334 (
2334 (
2335 b'',
2335 b'',
2336 b'at-rev',
2336 b'at-rev',
2337 b'',
2337 b'',
2338 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2338 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2339 _(b'REV'),
2339 _(b'REV'),
2340 ),
2340 ),
2341 (
2341 (
2342 b'f',
2342 b'f',
2343 b'force',
2343 b'force',
2344 None,
2344 None,
2345 _(b'forcibly copy over an existing managed file'),
2345 _(b'forcibly copy over an existing managed file'),
2346 ),
2346 ),
2347 ]
2347 ]
2348 + walkopts
2348 + walkopts
2349 + dryrunopts,
2349 + dryrunopts,
2350 _(b'[OPTION]... SOURCE... DEST'),
2350 _(b'[OPTION]... SOURCE... DEST'),
2351 helpcategory=command.CATEGORY_FILE_CONTENTS,
2351 helpcategory=command.CATEGORY_FILE_CONTENTS,
2352 )
2352 )
2353 def copy(ui, repo, *pats, **opts):
2353 def copy(ui, repo, *pats, **opts):
2354 """mark files as copied for the next commit
2354 """mark files as copied for the next commit
2355
2355
2356 Mark dest as having copies of source files. If dest is a
2356 Mark dest as having copies of source files. If dest is a
2357 directory, copies are put in that directory. If dest is a file,
2357 directory, copies are put in that directory. If dest is a file,
2358 the source must be a single file.
2358 the source must be a single file.
2359
2359
2360 By default, this command copies the contents of files as they
2360 By default, this command copies the contents of files as they
2361 exist in the working directory. If invoked with -A/--after, the
2361 exist in the working directory. If invoked with -A/--after, the
2362 operation is recorded, but no copying is performed.
2362 operation is recorded, but no copying is performed.
2363
2363
2364 To undo marking a file as copied, use --forget. With that option,
2364 To undo marking a file as copied, use --forget. With that option,
2365 all given (positional) arguments are unmarked as copies. The destination
2365 all given (positional) arguments are unmarked as copies. The destination
2366 file(s) will be left in place (still tracked).
2366 file(s) will be left in place (still tracked).
2367
2367
2368 This command takes effect with the next commit by default.
2368 This command takes effect with the next commit by default.
2369
2369
2370 Returns 0 on success, 1 if errors are encountered.
2370 Returns 0 on success, 1 if errors are encountered.
2371 """
2371 """
2372 opts = pycompat.byteskwargs(opts)
2372 opts = pycompat.byteskwargs(opts)
2373 with repo.wlock():
2373 with repo.wlock():
2374 return cmdutil.copy(ui, repo, pats, opts)
2374 return cmdutil.copy(ui, repo, pats, opts)
2375
2375
2376
2376
2377 @command(
2377 @command(
2378 b'debugcommands',
2378 b'debugcommands',
2379 [],
2379 [],
2380 _(b'[COMMAND]'),
2380 _(b'[COMMAND]'),
2381 helpcategory=command.CATEGORY_HELP,
2381 helpcategory=command.CATEGORY_HELP,
2382 norepo=True,
2382 norepo=True,
2383 )
2383 )
2384 def debugcommands(ui, cmd=b'', *args):
2384 def debugcommands(ui, cmd=b'', *args):
2385 """list all available commands and options"""
2385 """list all available commands and options"""
2386 for cmd, vals in sorted(pycompat.iteritems(table)):
2386 for cmd, vals in sorted(pycompat.iteritems(table)):
2387 cmd = cmd.split(b'|')[0]
2387 cmd = cmd.split(b'|')[0]
2388 opts = b', '.join([i[1] for i in vals[1]])
2388 opts = b', '.join([i[1] for i in vals[1]])
2389 ui.write(b'%s: %s\n' % (cmd, opts))
2389 ui.write(b'%s: %s\n' % (cmd, opts))
2390
2390
2391
2391
2392 @command(
2392 @command(
2393 b'debugcomplete',
2393 b'debugcomplete',
2394 [(b'o', b'options', None, _(b'show the command options'))],
2394 [(b'o', b'options', None, _(b'show the command options'))],
2395 _(b'[-o] CMD'),
2395 _(b'[-o] CMD'),
2396 helpcategory=command.CATEGORY_HELP,
2396 helpcategory=command.CATEGORY_HELP,
2397 norepo=True,
2397 norepo=True,
2398 )
2398 )
2399 def debugcomplete(ui, cmd=b'', **opts):
2399 def debugcomplete(ui, cmd=b'', **opts):
2400 """returns the completion list associated with the given command"""
2400 """returns the completion list associated with the given command"""
2401
2401
2402 if opts.get('options'):
2402 if opts.get('options'):
2403 options = []
2403 options = []
2404 otables = [globalopts]
2404 otables = [globalopts]
2405 if cmd:
2405 if cmd:
2406 aliases, entry = cmdutil.findcmd(cmd, table, False)
2406 aliases, entry = cmdutil.findcmd(cmd, table, False)
2407 otables.append(entry[1])
2407 otables.append(entry[1])
2408 for t in otables:
2408 for t in otables:
2409 for o in t:
2409 for o in t:
2410 if b"(DEPRECATED)" in o[3]:
2410 if b"(DEPRECATED)" in o[3]:
2411 continue
2411 continue
2412 if o[0]:
2412 if o[0]:
2413 options.append(b'-%s' % o[0])
2413 options.append(b'-%s' % o[0])
2414 options.append(b'--%s' % o[1])
2414 options.append(b'--%s' % o[1])
2415 ui.write(b"%s\n" % b"\n".join(options))
2415 ui.write(b"%s\n" % b"\n".join(options))
2416 return
2416 return
2417
2417
2418 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2418 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2419 if ui.verbose:
2419 if ui.verbose:
2420 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2420 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2421 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2421 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2422
2422
2423
2423
2424 @command(
2424 @command(
2425 b'diff',
2425 b'diff',
2426 [
2426 [
2427 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2427 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2428 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2428 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2429 ]
2429 ]
2430 + diffopts
2430 + diffopts
2431 + diffopts2
2431 + diffopts2
2432 + walkopts
2432 + walkopts
2433 + subrepoopts,
2433 + subrepoopts,
2434 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2434 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2435 helpcategory=command.CATEGORY_FILE_CONTENTS,
2435 helpcategory=command.CATEGORY_FILE_CONTENTS,
2436 helpbasic=True,
2436 helpbasic=True,
2437 inferrepo=True,
2437 inferrepo=True,
2438 intents={INTENT_READONLY},
2438 intents={INTENT_READONLY},
2439 )
2439 )
2440 def diff(ui, repo, *pats, **opts):
2440 def diff(ui, repo, *pats, **opts):
2441 """diff repository (or selected files)
2441 """diff repository (or selected files)
2442
2442
2443 Show differences between revisions for the specified files.
2443 Show differences between revisions for the specified files.
2444
2444
2445 Differences between files are shown using the unified diff format.
2445 Differences between files are shown using the unified diff format.
2446
2446
2447 .. note::
2447 .. note::
2448
2448
2449 :hg:`diff` may generate unexpected results for merges, as it will
2449 :hg:`diff` may generate unexpected results for merges, as it will
2450 default to comparing against the working directory's first
2450 default to comparing against the working directory's first
2451 parent changeset if no revisions are specified.
2451 parent changeset if no revisions are specified.
2452
2452
2453 When two revision arguments are given, then changes are shown
2453 When two revision arguments are given, then changes are shown
2454 between those revisions. If only one revision is specified then
2454 between those revisions. If only one revision is specified then
2455 that revision is compared to the working directory, and, when no
2455 that revision is compared to the working directory, and, when no
2456 revisions are specified, the working directory files are compared
2456 revisions are specified, the working directory files are compared
2457 to its first parent.
2457 to its first parent.
2458
2458
2459 Alternatively you can specify -c/--change with a revision to see
2459 Alternatively you can specify -c/--change with a revision to see
2460 the changes in that changeset relative to its first parent.
2460 the changes in that changeset relative to its first parent.
2461
2461
2462 Without the -a/--text option, diff will avoid generating diffs of
2462 Without the -a/--text option, diff will avoid generating diffs of
2463 files it detects as binary. With -a, diff will generate a diff
2463 files it detects as binary. With -a, diff will generate a diff
2464 anyway, probably with undesirable results.
2464 anyway, probably with undesirable results.
2465
2465
2466 Use the -g/--git option to generate diffs in the git extended diff
2466 Use the -g/--git option to generate diffs in the git extended diff
2467 format. For more information, read :hg:`help diffs`.
2467 format. For more information, read :hg:`help diffs`.
2468
2468
2469 .. container:: verbose
2469 .. container:: verbose
2470
2470
2471 Examples:
2471 Examples:
2472
2472
2473 - compare a file in the current working directory to its parent::
2473 - compare a file in the current working directory to its parent::
2474
2474
2475 hg diff foo.c
2475 hg diff foo.c
2476
2476
2477 - compare two historical versions of a directory, with rename info::
2477 - compare two historical versions of a directory, with rename info::
2478
2478
2479 hg diff --git -r 1.0:1.2 lib/
2479 hg diff --git -r 1.0:1.2 lib/
2480
2480
2481 - get change stats relative to the last change on some date::
2481 - get change stats relative to the last change on some date::
2482
2482
2483 hg diff --stat -r "date('may 2')"
2483 hg diff --stat -r "date('may 2')"
2484
2484
2485 - diff all newly-added files that contain a keyword::
2485 - diff all newly-added files that contain a keyword::
2486
2486
2487 hg diff "set:added() and grep(GNU)"
2487 hg diff "set:added() and grep(GNU)"
2488
2488
2489 - compare a revision and its parents::
2489 - compare a revision and its parents::
2490
2490
2491 hg diff -c 9353 # compare against first parent
2491 hg diff -c 9353 # compare against first parent
2492 hg diff -r 9353^:9353 # same using revset syntax
2492 hg diff -r 9353^:9353 # same using revset syntax
2493 hg diff -r 9353^2:9353 # compare against the second parent
2493 hg diff -r 9353^2:9353 # compare against the second parent
2494
2494
2495 Returns 0 on success.
2495 Returns 0 on success.
2496 """
2496 """
2497
2497
2498 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2498 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2499 opts = pycompat.byteskwargs(opts)
2499 opts = pycompat.byteskwargs(opts)
2500 revs = opts.get(b'rev')
2500 revs = opts.get(b'rev')
2501 change = opts.get(b'change')
2501 change = opts.get(b'change')
2502 stat = opts.get(b'stat')
2502 stat = opts.get(b'stat')
2503 reverse = opts.get(b'reverse')
2503 reverse = opts.get(b'reverse')
2504
2504
2505 if change:
2505 if change:
2506 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2506 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2507 ctx2 = scmutil.revsingle(repo, change, None)
2507 ctx2 = scmutil.revsingle(repo, change, None)
2508 ctx1 = ctx2.p1()
2508 ctx1 = ctx2.p1()
2509 else:
2509 else:
2510 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2510 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2511 ctx1, ctx2 = scmutil.revpair(repo, revs)
2511 ctx1, ctx2 = scmutil.revpair(repo, revs)
2512
2512
2513 if reverse:
2513 if reverse:
2514 ctxleft = ctx2
2514 ctxleft = ctx2
2515 ctxright = ctx1
2515 ctxright = ctx1
2516 else:
2516 else:
2517 ctxleft = ctx1
2517 ctxleft = ctx1
2518 ctxright = ctx2
2518 ctxright = ctx2
2519
2519
2520 diffopts = patch.diffallopts(ui, opts)
2520 diffopts = patch.diffallopts(ui, opts)
2521 m = scmutil.match(ctx2, pats, opts)
2521 m = scmutil.match(ctx2, pats, opts)
2522 m = repo.narrowmatch(m)
2522 m = repo.narrowmatch(m)
2523 ui.pager(b'diff')
2523 ui.pager(b'diff')
2524 logcmdutil.diffordiffstat(
2524 logcmdutil.diffordiffstat(
2525 ui,
2525 ui,
2526 repo,
2526 repo,
2527 diffopts,
2527 diffopts,
2528 ctxleft,
2528 ctxleft,
2529 ctxright,
2529 ctxright,
2530 m,
2530 m,
2531 stat=stat,
2531 stat=stat,
2532 listsubrepos=opts.get(b'subrepos'),
2532 listsubrepos=opts.get(b'subrepos'),
2533 root=opts.get(b'root'),
2533 root=opts.get(b'root'),
2534 )
2534 )
2535
2535
2536
2536
2537 @command(
2537 @command(
2538 b'export',
2538 b'export',
2539 [
2539 [
2540 (
2540 (
2541 b'B',
2541 b'B',
2542 b'bookmark',
2542 b'bookmark',
2543 b'',
2543 b'',
2544 _(b'export changes only reachable by given bookmark'),
2544 _(b'export changes only reachable by given bookmark'),
2545 _(b'BOOKMARK'),
2545 _(b'BOOKMARK'),
2546 ),
2546 ),
2547 (
2547 (
2548 b'o',
2548 b'o',
2549 b'output',
2549 b'output',
2550 b'',
2550 b'',
2551 _(b'print output to file with formatted name'),
2551 _(b'print output to file with formatted name'),
2552 _(b'FORMAT'),
2552 _(b'FORMAT'),
2553 ),
2553 ),
2554 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2554 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2555 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2555 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2556 ]
2556 ]
2557 + diffopts
2557 + diffopts
2558 + formatteropts,
2558 + formatteropts,
2559 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2559 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2560 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2560 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2561 helpbasic=True,
2561 helpbasic=True,
2562 intents={INTENT_READONLY},
2562 intents={INTENT_READONLY},
2563 )
2563 )
2564 def export(ui, repo, *changesets, **opts):
2564 def export(ui, repo, *changesets, **opts):
2565 """dump the header and diffs for one or more changesets
2565 """dump the header and diffs for one or more changesets
2566
2566
2567 Print the changeset header and diffs for one or more revisions.
2567 Print the changeset header and diffs for one or more revisions.
2568 If no revision is given, the parent of the working directory is used.
2568 If no revision is given, the parent of the working directory is used.
2569
2569
2570 The information shown in the changeset header is: author, date,
2570 The information shown in the changeset header is: author, date,
2571 branch name (if non-default), changeset hash, parent(s) and commit
2571 branch name (if non-default), changeset hash, parent(s) and commit
2572 comment.
2572 comment.
2573
2573
2574 .. note::
2574 .. note::
2575
2575
2576 :hg:`export` may generate unexpected diff output for merge
2576 :hg:`export` may generate unexpected diff output for merge
2577 changesets, as it will compare the merge changeset against its
2577 changesets, as it will compare the merge changeset against its
2578 first parent only.
2578 first parent only.
2579
2579
2580 Output may be to a file, in which case the name of the file is
2580 Output may be to a file, in which case the name of the file is
2581 given using a template string. See :hg:`help templates`. In addition
2581 given using a template string. See :hg:`help templates`. In addition
2582 to the common template keywords, the following formatting rules are
2582 to the common template keywords, the following formatting rules are
2583 supported:
2583 supported:
2584
2584
2585 :``%%``: literal "%" character
2585 :``%%``: literal "%" character
2586 :``%H``: changeset hash (40 hexadecimal digits)
2586 :``%H``: changeset hash (40 hexadecimal digits)
2587 :``%N``: number of patches being generated
2587 :``%N``: number of patches being generated
2588 :``%R``: changeset revision number
2588 :``%R``: changeset revision number
2589 :``%b``: basename of the exporting repository
2589 :``%b``: basename of the exporting repository
2590 :``%h``: short-form changeset hash (12 hexadecimal digits)
2590 :``%h``: short-form changeset hash (12 hexadecimal digits)
2591 :``%m``: first line of the commit message (only alphanumeric characters)
2591 :``%m``: first line of the commit message (only alphanumeric characters)
2592 :``%n``: zero-padded sequence number, starting at 1
2592 :``%n``: zero-padded sequence number, starting at 1
2593 :``%r``: zero-padded changeset revision number
2593 :``%r``: zero-padded changeset revision number
2594 :``\\``: literal "\\" character
2594 :``\\``: literal "\\" character
2595
2595
2596 Without the -a/--text option, export will avoid generating diffs
2596 Without the -a/--text option, export will avoid generating diffs
2597 of files it detects as binary. With -a, export will generate a
2597 of files it detects as binary. With -a, export will generate a
2598 diff anyway, probably with undesirable results.
2598 diff anyway, probably with undesirable results.
2599
2599
2600 With -B/--bookmark changesets reachable by the given bookmark are
2600 With -B/--bookmark changesets reachable by the given bookmark are
2601 selected.
2601 selected.
2602
2602
2603 Use the -g/--git option to generate diffs in the git extended diff
2603 Use the -g/--git option to generate diffs in the git extended diff
2604 format. See :hg:`help diffs` for more information.
2604 format. See :hg:`help diffs` for more information.
2605
2605
2606 With the --switch-parent option, the diff will be against the
2606 With the --switch-parent option, the diff will be against the
2607 second parent. It can be useful to review a merge.
2607 second parent. It can be useful to review a merge.
2608
2608
2609 .. container:: verbose
2609 .. container:: verbose
2610
2610
2611 Template:
2611 Template:
2612
2612
2613 The following keywords are supported in addition to the common template
2613 The following keywords are supported in addition to the common template
2614 keywords and functions. See also :hg:`help templates`.
2614 keywords and functions. See also :hg:`help templates`.
2615
2615
2616 :diff: String. Diff content.
2616 :diff: String. Diff content.
2617 :parents: List of strings. Parent nodes of the changeset.
2617 :parents: List of strings. Parent nodes of the changeset.
2618
2618
2619 Examples:
2619 Examples:
2620
2620
2621 - use export and import to transplant a bugfix to the current
2621 - use export and import to transplant a bugfix to the current
2622 branch::
2622 branch::
2623
2623
2624 hg export -r 9353 | hg import -
2624 hg export -r 9353 | hg import -
2625
2625
2626 - export all the changesets between two revisions to a file with
2626 - export all the changesets between two revisions to a file with
2627 rename information::
2627 rename information::
2628
2628
2629 hg export --git -r 123:150 > changes.txt
2629 hg export --git -r 123:150 > changes.txt
2630
2630
2631 - split outgoing changes into a series of patches with
2631 - split outgoing changes into a series of patches with
2632 descriptive names::
2632 descriptive names::
2633
2633
2634 hg export -r "outgoing()" -o "%n-%m.patch"
2634 hg export -r "outgoing()" -o "%n-%m.patch"
2635
2635
2636 Returns 0 on success.
2636 Returns 0 on success.
2637 """
2637 """
2638 opts = pycompat.byteskwargs(opts)
2638 opts = pycompat.byteskwargs(opts)
2639 bookmark = opts.get(b'bookmark')
2639 bookmark = opts.get(b'bookmark')
2640 changesets += tuple(opts.get(b'rev', []))
2640 changesets += tuple(opts.get(b'rev', []))
2641
2641
2642 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2642 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2643
2643
2644 if bookmark:
2644 if bookmark:
2645 if bookmark not in repo._bookmarks:
2645 if bookmark not in repo._bookmarks:
2646 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2646 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2647
2647
2648 revs = scmutil.bookmarkrevs(repo, bookmark)
2648 revs = scmutil.bookmarkrevs(repo, bookmark)
2649 else:
2649 else:
2650 if not changesets:
2650 if not changesets:
2651 changesets = [b'.']
2651 changesets = [b'.']
2652
2652
2653 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2653 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2654 revs = scmutil.revrange(repo, changesets)
2654 revs = scmutil.revrange(repo, changesets)
2655
2655
2656 if not revs:
2656 if not revs:
2657 raise error.Abort(_(b"export requires at least one changeset"))
2657 raise error.Abort(_(b"export requires at least one changeset"))
2658 if len(revs) > 1:
2658 if len(revs) > 1:
2659 ui.note(_(b'exporting patches:\n'))
2659 ui.note(_(b'exporting patches:\n'))
2660 else:
2660 else:
2661 ui.note(_(b'exporting patch:\n'))
2661 ui.note(_(b'exporting patch:\n'))
2662
2662
2663 fntemplate = opts.get(b'output')
2663 fntemplate = opts.get(b'output')
2664 if cmdutil.isstdiofilename(fntemplate):
2664 if cmdutil.isstdiofilename(fntemplate):
2665 fntemplate = b''
2665 fntemplate = b''
2666
2666
2667 if fntemplate:
2667 if fntemplate:
2668 fm = formatter.nullformatter(ui, b'export', opts)
2668 fm = formatter.nullformatter(ui, b'export', opts)
2669 else:
2669 else:
2670 ui.pager(b'export')
2670 ui.pager(b'export')
2671 fm = ui.formatter(b'export', opts)
2671 fm = ui.formatter(b'export', opts)
2672 with fm:
2672 with fm:
2673 cmdutil.export(
2673 cmdutil.export(
2674 repo,
2674 repo,
2675 revs,
2675 revs,
2676 fm,
2676 fm,
2677 fntemplate=fntemplate,
2677 fntemplate=fntemplate,
2678 switch_parent=opts.get(b'switch_parent'),
2678 switch_parent=opts.get(b'switch_parent'),
2679 opts=patch.diffallopts(ui, opts),
2679 opts=patch.diffallopts(ui, opts),
2680 )
2680 )
2681
2681
2682
2682
2683 @command(
2683 @command(
2684 b'files',
2684 b'files',
2685 [
2685 [
2686 (
2686 (
2687 b'r',
2687 b'r',
2688 b'rev',
2688 b'rev',
2689 b'',
2689 b'',
2690 _(b'search the repository as it is in REV'),
2690 _(b'search the repository as it is in REV'),
2691 _(b'REV'),
2691 _(b'REV'),
2692 ),
2692 ),
2693 (
2693 (
2694 b'0',
2694 b'0',
2695 b'print0',
2695 b'print0',
2696 None,
2696 None,
2697 _(b'end filenames with NUL, for use with xargs'),
2697 _(b'end filenames with NUL, for use with xargs'),
2698 ),
2698 ),
2699 ]
2699 ]
2700 + walkopts
2700 + walkopts
2701 + formatteropts
2701 + formatteropts
2702 + subrepoopts,
2702 + subrepoopts,
2703 _(b'[OPTION]... [FILE]...'),
2703 _(b'[OPTION]... [FILE]...'),
2704 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2704 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2705 intents={INTENT_READONLY},
2705 intents={INTENT_READONLY},
2706 )
2706 )
2707 def files(ui, repo, *pats, **opts):
2707 def files(ui, repo, *pats, **opts):
2708 """list tracked files
2708 """list tracked files
2709
2709
2710 Print files under Mercurial control in the working directory or
2710 Print files under Mercurial control in the working directory or
2711 specified revision for given files (excluding removed files).
2711 specified revision for given files (excluding removed files).
2712 Files can be specified as filenames or filesets.
2712 Files can be specified as filenames or filesets.
2713
2713
2714 If no files are given to match, this command prints the names
2714 If no files are given to match, this command prints the names
2715 of all files under Mercurial control.
2715 of all files under Mercurial control.
2716
2716
2717 .. container:: verbose
2717 .. container:: verbose
2718
2718
2719 Template:
2719 Template:
2720
2720
2721 The following keywords are supported in addition to the common template
2721 The following keywords are supported in addition to the common template
2722 keywords and functions. See also :hg:`help templates`.
2722 keywords and functions. See also :hg:`help templates`.
2723
2723
2724 :flags: String. Character denoting file's symlink and executable bits.
2724 :flags: String. Character denoting file's symlink and executable bits.
2725 :path: String. Repository-absolute path of the file.
2725 :path: String. Repository-absolute path of the file.
2726 :size: Integer. Size of the file in bytes.
2726 :size: Integer. Size of the file in bytes.
2727
2727
2728 Examples:
2728 Examples:
2729
2729
2730 - list all files under the current directory::
2730 - list all files under the current directory::
2731
2731
2732 hg files .
2732 hg files .
2733
2733
2734 - shows sizes and flags for current revision::
2734 - shows sizes and flags for current revision::
2735
2735
2736 hg files -vr .
2736 hg files -vr .
2737
2737
2738 - list all files named README::
2738 - list all files named README::
2739
2739
2740 hg files -I "**/README"
2740 hg files -I "**/README"
2741
2741
2742 - list all binary files::
2742 - list all binary files::
2743
2743
2744 hg files "set:binary()"
2744 hg files "set:binary()"
2745
2745
2746 - find files containing a regular expression::
2746 - find files containing a regular expression::
2747
2747
2748 hg files "set:grep('bob')"
2748 hg files "set:grep('bob')"
2749
2749
2750 - search tracked file contents with xargs and grep::
2750 - search tracked file contents with xargs and grep::
2751
2751
2752 hg files -0 | xargs -0 grep foo
2752 hg files -0 | xargs -0 grep foo
2753
2753
2754 See :hg:`help patterns` and :hg:`help filesets` for more information
2754 See :hg:`help patterns` and :hg:`help filesets` for more information
2755 on specifying file patterns.
2755 on specifying file patterns.
2756
2756
2757 Returns 0 if a match is found, 1 otherwise.
2757 Returns 0 if a match is found, 1 otherwise.
2758
2758
2759 """
2759 """
2760
2760
2761 opts = pycompat.byteskwargs(opts)
2761 opts = pycompat.byteskwargs(opts)
2762 rev = opts.get(b'rev')
2762 rev = opts.get(b'rev')
2763 if rev:
2763 if rev:
2764 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2764 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2765 ctx = scmutil.revsingle(repo, rev, None)
2765 ctx = scmutil.revsingle(repo, rev, None)
2766
2766
2767 end = b'\n'
2767 end = b'\n'
2768 if opts.get(b'print0'):
2768 if opts.get(b'print0'):
2769 end = b'\0'
2769 end = b'\0'
2770 fmt = b'%s' + end
2770 fmt = b'%s' + end
2771
2771
2772 m = scmutil.match(ctx, pats, opts)
2772 m = scmutil.match(ctx, pats, opts)
2773 ui.pager(b'files')
2773 ui.pager(b'files')
2774 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2774 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2775 with ui.formatter(b'files', opts) as fm:
2775 with ui.formatter(b'files', opts) as fm:
2776 return cmdutil.files(
2776 return cmdutil.files(
2777 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2777 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2778 )
2778 )
2779
2779
2780
2780
2781 @command(
2781 @command(
2782 b'forget',
2782 b'forget',
2783 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2783 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2784 + walkopts
2784 + walkopts
2785 + dryrunopts,
2785 + dryrunopts,
2786 _(b'[OPTION]... FILE...'),
2786 _(b'[OPTION]... FILE...'),
2787 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2787 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2788 helpbasic=True,
2788 helpbasic=True,
2789 inferrepo=True,
2789 inferrepo=True,
2790 )
2790 )
2791 def forget(ui, repo, *pats, **opts):
2791 def forget(ui, repo, *pats, **opts):
2792 """forget the specified files on the next commit
2792 """forget the specified files on the next commit
2793
2793
2794 Mark the specified files so they will no longer be tracked
2794 Mark the specified files so they will no longer be tracked
2795 after the next commit.
2795 after the next commit.
2796
2796
2797 This only removes files from the current branch, not from the
2797 This only removes files from the current branch, not from the
2798 entire project history, and it does not delete them from the
2798 entire project history, and it does not delete them from the
2799 working directory.
2799 working directory.
2800
2800
2801 To delete the file from the working directory, see :hg:`remove`.
2801 To delete the file from the working directory, see :hg:`remove`.
2802
2802
2803 To undo a forget before the next commit, see :hg:`add`.
2803 To undo a forget before the next commit, see :hg:`add`.
2804
2804
2805 .. container:: verbose
2805 .. container:: verbose
2806
2806
2807 Examples:
2807 Examples:
2808
2808
2809 - forget newly-added binary files::
2809 - forget newly-added binary files::
2810
2810
2811 hg forget "set:added() and binary()"
2811 hg forget "set:added() and binary()"
2812
2812
2813 - forget files that would be excluded by .hgignore::
2813 - forget files that would be excluded by .hgignore::
2814
2814
2815 hg forget "set:hgignore()"
2815 hg forget "set:hgignore()"
2816
2816
2817 Returns 0 on success.
2817 Returns 0 on success.
2818 """
2818 """
2819
2819
2820 opts = pycompat.byteskwargs(opts)
2820 opts = pycompat.byteskwargs(opts)
2821 if not pats:
2821 if not pats:
2822 raise error.Abort(_(b'no files specified'))
2822 raise error.Abort(_(b'no files specified'))
2823
2823
2824 m = scmutil.match(repo[None], pats, opts)
2824 m = scmutil.match(repo[None], pats, opts)
2825 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2825 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2826 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2826 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2827 rejected = cmdutil.forget(
2827 rejected = cmdutil.forget(
2828 ui,
2828 ui,
2829 repo,
2829 repo,
2830 m,
2830 m,
2831 prefix=b"",
2831 prefix=b"",
2832 uipathfn=uipathfn,
2832 uipathfn=uipathfn,
2833 explicitonly=False,
2833 explicitonly=False,
2834 dryrun=dryrun,
2834 dryrun=dryrun,
2835 interactive=interactive,
2835 interactive=interactive,
2836 )[0]
2836 )[0]
2837 return rejected and 1 or 0
2837 return rejected and 1 or 0
2838
2838
2839
2839
2840 @command(
2840 @command(
2841 b'graft',
2841 b'graft',
2842 [
2842 [
2843 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2843 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2844 (
2844 (
2845 b'',
2845 b'',
2846 b'base',
2846 b'base',
2847 b'',
2847 b'',
2848 _(b'base revision when doing the graft merge (ADVANCED)'),
2848 _(b'base revision when doing the graft merge (ADVANCED)'),
2849 _(b'REV'),
2849 _(b'REV'),
2850 ),
2850 ),
2851 (b'c', b'continue', False, _(b'resume interrupted graft')),
2851 (b'c', b'continue', False, _(b'resume interrupted graft')),
2852 (b'', b'stop', False, _(b'stop interrupted graft')),
2852 (b'', b'stop', False, _(b'stop interrupted graft')),
2853 (b'', b'abort', False, _(b'abort interrupted graft')),
2853 (b'', b'abort', False, _(b'abort interrupted graft')),
2854 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2854 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2855 (b'', b'log', None, _(b'append graft info to log message')),
2855 (b'', b'log', None, _(b'append graft info to log message')),
2856 (
2856 (
2857 b'',
2857 b'',
2858 b'no-commit',
2858 b'no-commit',
2859 None,
2859 None,
2860 _(b"don't commit, just apply the changes in working directory"),
2860 _(b"don't commit, just apply the changes in working directory"),
2861 ),
2861 ),
2862 (b'f', b'force', False, _(b'force graft')),
2862 (b'f', b'force', False, _(b'force graft')),
2863 (
2863 (
2864 b'D',
2864 b'D',
2865 b'currentdate',
2865 b'currentdate',
2866 False,
2866 False,
2867 _(b'record the current date as commit date'),
2867 _(b'record the current date as commit date'),
2868 ),
2868 ),
2869 (
2869 (
2870 b'U',
2870 b'U',
2871 b'currentuser',
2871 b'currentuser',
2872 False,
2872 False,
2873 _(b'record the current user as committer'),
2873 _(b'record the current user as committer'),
2874 ),
2874 ),
2875 ]
2875 ]
2876 + commitopts2
2876 + commitopts2
2877 + mergetoolopts
2877 + mergetoolopts
2878 + dryrunopts,
2878 + dryrunopts,
2879 _(b'[OPTION]... [-r REV]... REV...'),
2879 _(b'[OPTION]... [-r REV]... REV...'),
2880 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2880 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2881 )
2881 )
2882 def graft(ui, repo, *revs, **opts):
2882 def graft(ui, repo, *revs, **opts):
2883 '''copy changes from other branches onto the current branch
2883 '''copy changes from other branches onto the current branch
2884
2884
2885 This command uses Mercurial's merge logic to copy individual
2885 This command uses Mercurial's merge logic to copy individual
2886 changes from other branches without merging branches in the
2886 changes from other branches without merging branches in the
2887 history graph. This is sometimes known as 'backporting' or
2887 history graph. This is sometimes known as 'backporting' or
2888 'cherry-picking'. By default, graft will copy user, date, and
2888 'cherry-picking'. By default, graft will copy user, date, and
2889 description from the source changesets.
2889 description from the source changesets.
2890
2890
2891 Changesets that are ancestors of the current revision, that have
2891 Changesets that are ancestors of the current revision, that have
2892 already been grafted, or that are merges will be skipped.
2892 already been grafted, or that are merges will be skipped.
2893
2893
2894 If --log is specified, log messages will have a comment appended
2894 If --log is specified, log messages will have a comment appended
2895 of the form::
2895 of the form::
2896
2896
2897 (grafted from CHANGESETHASH)
2897 (grafted from CHANGESETHASH)
2898
2898
2899 If --force is specified, revisions will be grafted even if they
2899 If --force is specified, revisions will be grafted even if they
2900 are already ancestors of, or have been grafted to, the destination.
2900 are already ancestors of, or have been grafted to, the destination.
2901 This is useful when the revisions have since been backed out.
2901 This is useful when the revisions have since been backed out.
2902
2902
2903 If a graft merge results in conflicts, the graft process is
2903 If a graft merge results in conflicts, the graft process is
2904 interrupted so that the current merge can be manually resolved.
2904 interrupted so that the current merge can be manually resolved.
2905 Once all conflicts are addressed, the graft process can be
2905 Once all conflicts are addressed, the graft process can be
2906 continued with the -c/--continue option.
2906 continued with the -c/--continue option.
2907
2907
2908 The -c/--continue option reapplies all the earlier options.
2908 The -c/--continue option reapplies all the earlier options.
2909
2909
2910 .. container:: verbose
2910 .. container:: verbose
2911
2911
2912 The --base option exposes more of how graft internally uses merge with a
2912 The --base option exposes more of how graft internally uses merge with a
2913 custom base revision. --base can be used to specify another ancestor than
2913 custom base revision. --base can be used to specify another ancestor than
2914 the first and only parent.
2914 the first and only parent.
2915
2915
2916 The command::
2916 The command::
2917
2917
2918 hg graft -r 345 --base 234
2918 hg graft -r 345 --base 234
2919
2919
2920 is thus pretty much the same as::
2920 is thus pretty much the same as::
2921
2921
2922 hg diff -r 234 -r 345 | hg import
2922 hg diff -r 234 -r 345 | hg import
2923
2923
2924 but using merge to resolve conflicts and track moved files.
2924 but using merge to resolve conflicts and track moved files.
2925
2925
2926 The result of a merge can thus be backported as a single commit by
2926 The result of a merge can thus be backported as a single commit by
2927 specifying one of the merge parents as base, and thus effectively
2927 specifying one of the merge parents as base, and thus effectively
2928 grafting the changes from the other side.
2928 grafting the changes from the other side.
2929
2929
2930 It is also possible to collapse multiple changesets and clean up history
2930 It is also possible to collapse multiple changesets and clean up history
2931 by specifying another ancestor as base, much like rebase --collapse
2931 by specifying another ancestor as base, much like rebase --collapse
2932 --keep.
2932 --keep.
2933
2933
2934 The commit message can be tweaked after the fact using commit --amend .
2934 The commit message can be tweaked after the fact using commit --amend .
2935
2935
2936 For using non-ancestors as the base to backout changes, see the backout
2936 For using non-ancestors as the base to backout changes, see the backout
2937 command and the hidden --parent option.
2937 command and the hidden --parent option.
2938
2938
2939 .. container:: verbose
2939 .. container:: verbose
2940
2940
2941 Examples:
2941 Examples:
2942
2942
2943 - copy a single change to the stable branch and edit its description::
2943 - copy a single change to the stable branch and edit its description::
2944
2944
2945 hg update stable
2945 hg update stable
2946 hg graft --edit 9393
2946 hg graft --edit 9393
2947
2947
2948 - graft a range of changesets with one exception, updating dates::
2948 - graft a range of changesets with one exception, updating dates::
2949
2949
2950 hg graft -D "2085::2093 and not 2091"
2950 hg graft -D "2085::2093 and not 2091"
2951
2951
2952 - continue a graft after resolving conflicts::
2952 - continue a graft after resolving conflicts::
2953
2953
2954 hg graft -c
2954 hg graft -c
2955
2955
2956 - show the source of a grafted changeset::
2956 - show the source of a grafted changeset::
2957
2957
2958 hg log --debug -r .
2958 hg log --debug -r .
2959
2959
2960 - show revisions sorted by date::
2960 - show revisions sorted by date::
2961
2961
2962 hg log -r "sort(all(), date)"
2962 hg log -r "sort(all(), date)"
2963
2963
2964 - backport the result of a merge as a single commit::
2964 - backport the result of a merge as a single commit::
2965
2965
2966 hg graft -r 123 --base 123^
2966 hg graft -r 123 --base 123^
2967
2967
2968 - land a feature branch as one changeset::
2968 - land a feature branch as one changeset::
2969
2969
2970 hg up -cr default
2970 hg up -cr default
2971 hg graft -r featureX --base "ancestor('featureX', 'default')"
2971 hg graft -r featureX --base "ancestor('featureX', 'default')"
2972
2972
2973 See :hg:`help revisions` for more about specifying revisions.
2973 See :hg:`help revisions` for more about specifying revisions.
2974
2974
2975 Returns 0 on successful completion, 1 if there are unresolved files.
2975 Returns 0 on successful completion, 1 if there are unresolved files.
2976 '''
2976 '''
2977 with repo.wlock():
2977 with repo.wlock():
2978 return _dograft(ui, repo, *revs, **opts)
2978 return _dograft(ui, repo, *revs, **opts)
2979
2979
2980
2980
2981 def _dograft(ui, repo, *revs, **opts):
2981 def _dograft(ui, repo, *revs, **opts):
2982 opts = pycompat.byteskwargs(opts)
2982 opts = pycompat.byteskwargs(opts)
2983 if revs and opts.get(b'rev'):
2983 if revs and opts.get(b'rev'):
2984 ui.warn(
2984 ui.warn(
2985 _(
2985 _(
2986 b'warning: inconsistent use of --rev might give unexpected '
2986 b'warning: inconsistent use of --rev might give unexpected '
2987 b'revision ordering!\n'
2987 b'revision ordering!\n'
2988 )
2988 )
2989 )
2989 )
2990
2990
2991 revs = list(revs)
2991 revs = list(revs)
2992 revs.extend(opts.get(b'rev'))
2992 revs.extend(opts.get(b'rev'))
2993 # a dict of data to be stored in state file
2993 # a dict of data to be stored in state file
2994 statedata = {}
2994 statedata = {}
2995 # list of new nodes created by ongoing graft
2995 # list of new nodes created by ongoing graft
2996 statedata[b'newnodes'] = []
2996 statedata[b'newnodes'] = []
2997
2997
2998 cmdutil.resolvecommitoptions(ui, opts)
2998 cmdutil.resolvecommitoptions(ui, opts)
2999
2999
3000 editor = cmdutil.getcommiteditor(
3000 editor = cmdutil.getcommiteditor(
3001 editform=b'graft', **pycompat.strkwargs(opts)
3001 editform=b'graft', **pycompat.strkwargs(opts)
3002 )
3002 )
3003
3003
3004 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3004 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3005
3005
3006 cont = False
3006 cont = False
3007 if opts.get(b'no_commit'):
3007 if opts.get(b'no_commit'):
3008 cmdutil.check_incompatible_arguments(
3008 cmdutil.check_incompatible_arguments(
3009 opts,
3009 opts,
3010 b'no_commit',
3010 b'no_commit',
3011 [b'edit', b'currentuser', b'currentdate', b'log'],
3011 [b'edit', b'currentuser', b'currentdate', b'log'],
3012 )
3012 )
3013
3013
3014 graftstate = statemod.cmdstate(repo, b'graftstate')
3014 graftstate = statemod.cmdstate(repo, b'graftstate')
3015
3015
3016 if opts.get(b'stop'):
3016 if opts.get(b'stop'):
3017 cmdutil.check_incompatible_arguments(
3017 cmdutil.check_incompatible_arguments(
3018 opts,
3018 opts,
3019 b'stop',
3019 b'stop',
3020 [
3020 [
3021 b'edit',
3021 b'edit',
3022 b'log',
3022 b'log',
3023 b'user',
3023 b'user',
3024 b'date',
3024 b'date',
3025 b'currentdate',
3025 b'currentdate',
3026 b'currentuser',
3026 b'currentuser',
3027 b'rev',
3027 b'rev',
3028 ],
3028 ],
3029 )
3029 )
3030 return _stopgraft(ui, repo, graftstate)
3030 return _stopgraft(ui, repo, graftstate)
3031 elif opts.get(b'abort'):
3031 elif opts.get(b'abort'):
3032 cmdutil.check_incompatible_arguments(
3032 cmdutil.check_incompatible_arguments(
3033 opts,
3033 opts,
3034 b'abort',
3034 b'abort',
3035 [
3035 [
3036 b'edit',
3036 b'edit',
3037 b'log',
3037 b'log',
3038 b'user',
3038 b'user',
3039 b'date',
3039 b'date',
3040 b'currentdate',
3040 b'currentdate',
3041 b'currentuser',
3041 b'currentuser',
3042 b'rev',
3042 b'rev',
3043 ],
3043 ],
3044 )
3044 )
3045 return cmdutil.abortgraft(ui, repo, graftstate)
3045 return cmdutil.abortgraft(ui, repo, graftstate)
3046 elif opts.get(b'continue'):
3046 elif opts.get(b'continue'):
3047 cont = True
3047 cont = True
3048 if revs:
3048 if revs:
3049 raise error.Abort(_(b"can't specify --continue and revisions"))
3049 raise error.Abort(_(b"can't specify --continue and revisions"))
3050 # read in unfinished revisions
3050 # read in unfinished revisions
3051 if graftstate.exists():
3051 if graftstate.exists():
3052 statedata = cmdutil.readgraftstate(repo, graftstate)
3052 statedata = cmdutil.readgraftstate(repo, graftstate)
3053 if statedata.get(b'date'):
3053 if statedata.get(b'date'):
3054 opts[b'date'] = statedata[b'date']
3054 opts[b'date'] = statedata[b'date']
3055 if statedata.get(b'user'):
3055 if statedata.get(b'user'):
3056 opts[b'user'] = statedata[b'user']
3056 opts[b'user'] = statedata[b'user']
3057 if statedata.get(b'log'):
3057 if statedata.get(b'log'):
3058 opts[b'log'] = True
3058 opts[b'log'] = True
3059 if statedata.get(b'no_commit'):
3059 if statedata.get(b'no_commit'):
3060 opts[b'no_commit'] = statedata.get(b'no_commit')
3060 opts[b'no_commit'] = statedata.get(b'no_commit')
3061 if statedata.get(b'base'):
3061 if statedata.get(b'base'):
3062 opts[b'base'] = statedata.get(b'base')
3062 opts[b'base'] = statedata.get(b'base')
3063 nodes = statedata[b'nodes']
3063 nodes = statedata[b'nodes']
3064 revs = [repo[node].rev() for node in nodes]
3064 revs = [repo[node].rev() for node in nodes]
3065 else:
3065 else:
3066 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3066 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3067 else:
3067 else:
3068 if not revs:
3068 if not revs:
3069 raise error.Abort(_(b'no revisions specified'))
3069 raise error.Abort(_(b'no revisions specified'))
3070 cmdutil.checkunfinished(repo)
3070 cmdutil.checkunfinished(repo)
3071 cmdutil.bailifchanged(repo)
3071 cmdutil.bailifchanged(repo)
3072 revs = scmutil.revrange(repo, revs)
3072 revs = scmutil.revrange(repo, revs)
3073
3073
3074 skipped = set()
3074 skipped = set()
3075 basectx = None
3075 basectx = None
3076 if opts.get(b'base'):
3076 if opts.get(b'base'):
3077 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3077 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3078 if basectx is None:
3078 if basectx is None:
3079 # check for merges
3079 # check for merges
3080 for rev in repo.revs(b'%ld and merge()', revs):
3080 for rev in repo.revs(b'%ld and merge()', revs):
3081 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3081 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3082 skipped.add(rev)
3082 skipped.add(rev)
3083 revs = [r for r in revs if r not in skipped]
3083 revs = [r for r in revs if r not in skipped]
3084 if not revs:
3084 if not revs:
3085 return -1
3085 return -1
3086 if basectx is not None and len(revs) != 1:
3086 if basectx is not None and len(revs) != 1:
3087 raise error.Abort(_(b'only one revision allowed with --base '))
3087 raise error.Abort(_(b'only one revision allowed with --base '))
3088
3088
3089 # Don't check in the --continue case, in effect retaining --force across
3089 # Don't check in the --continue case, in effect retaining --force across
3090 # --continues. That's because without --force, any revisions we decided to
3090 # --continues. That's because without --force, any revisions we decided to
3091 # skip would have been filtered out here, so they wouldn't have made their
3091 # skip would have been filtered out here, so they wouldn't have made their
3092 # way to the graftstate. With --force, any revisions we would have otherwise
3092 # way to the graftstate. With --force, any revisions we would have otherwise
3093 # skipped would not have been filtered out, and if they hadn't been applied
3093 # skipped would not have been filtered out, and if they hadn't been applied
3094 # already, they'd have been in the graftstate.
3094 # already, they'd have been in the graftstate.
3095 if not (cont or opts.get(b'force')) and basectx is None:
3095 if not (cont or opts.get(b'force')) and basectx is None:
3096 # check for ancestors of dest branch
3096 # check for ancestors of dest branch
3097 ancestors = repo.revs(b'%ld & (::.)', revs)
3097 ancestors = repo.revs(b'%ld & (::.)', revs)
3098 for rev in ancestors:
3098 for rev in ancestors:
3099 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3099 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3100
3100
3101 revs = [r for r in revs if r not in ancestors]
3101 revs = [r for r in revs if r not in ancestors]
3102
3102
3103 if not revs:
3103 if not revs:
3104 return -1
3104 return -1
3105
3105
3106 # analyze revs for earlier grafts
3106 # analyze revs for earlier grafts
3107 ids = {}
3107 ids = {}
3108 for ctx in repo.set(b"%ld", revs):
3108 for ctx in repo.set(b"%ld", revs):
3109 ids[ctx.hex()] = ctx.rev()
3109 ids[ctx.hex()] = ctx.rev()
3110 n = ctx.extra().get(b'source')
3110 n = ctx.extra().get(b'source')
3111 if n:
3111 if n:
3112 ids[n] = ctx.rev()
3112 ids[n] = ctx.rev()
3113
3113
3114 # check ancestors for earlier grafts
3114 # check ancestors for earlier grafts
3115 ui.debug(b'scanning for duplicate grafts\n')
3115 ui.debug(b'scanning for duplicate grafts\n')
3116
3116
3117 # The only changesets we can be sure doesn't contain grafts of any
3117 # The only changesets we can be sure doesn't contain grafts of any
3118 # revs, are the ones that are common ancestors of *all* revs:
3118 # revs, are the ones that are common ancestors of *all* revs:
3119 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3119 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3120 ctx = repo[rev]
3120 ctx = repo[rev]
3121 n = ctx.extra().get(b'source')
3121 n = ctx.extra().get(b'source')
3122 if n in ids:
3122 if n in ids:
3123 try:
3123 try:
3124 r = repo[n].rev()
3124 r = repo[n].rev()
3125 except error.RepoLookupError:
3125 except error.RepoLookupError:
3126 r = None
3126 r = None
3127 if r in revs:
3127 if r in revs:
3128 ui.warn(
3128 ui.warn(
3129 _(
3129 _(
3130 b'skipping revision %d:%s '
3130 b'skipping revision %d:%s '
3131 b'(already grafted to %d:%s)\n'
3131 b'(already grafted to %d:%s)\n'
3132 )
3132 )
3133 % (r, repo[r], rev, ctx)
3133 % (r, repo[r], rev, ctx)
3134 )
3134 )
3135 revs.remove(r)
3135 revs.remove(r)
3136 elif ids[n] in revs:
3136 elif ids[n] in revs:
3137 if r is None:
3137 if r is None:
3138 ui.warn(
3138 ui.warn(
3139 _(
3139 _(
3140 b'skipping already grafted revision %d:%s '
3140 b'skipping already grafted revision %d:%s '
3141 b'(%d:%s also has unknown origin %s)\n'
3141 b'(%d:%s also has unknown origin %s)\n'
3142 )
3142 )
3143 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3143 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3144 )
3144 )
3145 else:
3145 else:
3146 ui.warn(
3146 ui.warn(
3147 _(
3147 _(
3148 b'skipping already grafted revision %d:%s '
3148 b'skipping already grafted revision %d:%s '
3149 b'(%d:%s also has origin %d:%s)\n'
3149 b'(%d:%s also has origin %d:%s)\n'
3150 )
3150 )
3151 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3151 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3152 )
3152 )
3153 revs.remove(ids[n])
3153 revs.remove(ids[n])
3154 elif ctx.hex() in ids:
3154 elif ctx.hex() in ids:
3155 r = ids[ctx.hex()]
3155 r = ids[ctx.hex()]
3156 if r in revs:
3156 if r in revs:
3157 ui.warn(
3157 ui.warn(
3158 _(
3158 _(
3159 b'skipping already grafted revision %d:%s '
3159 b'skipping already grafted revision %d:%s '
3160 b'(was grafted from %d:%s)\n'
3160 b'(was grafted from %d:%s)\n'
3161 )
3161 )
3162 % (r, repo[r], rev, ctx)
3162 % (r, repo[r], rev, ctx)
3163 )
3163 )
3164 revs.remove(r)
3164 revs.remove(r)
3165 if not revs:
3165 if not revs:
3166 return -1
3166 return -1
3167
3167
3168 if opts.get(b'no_commit'):
3168 if opts.get(b'no_commit'):
3169 statedata[b'no_commit'] = True
3169 statedata[b'no_commit'] = True
3170 if opts.get(b'base'):
3170 if opts.get(b'base'):
3171 statedata[b'base'] = opts[b'base']
3171 statedata[b'base'] = opts[b'base']
3172 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3172 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3173 desc = b'%d:%s "%s"' % (
3173 desc = b'%d:%s "%s"' % (
3174 ctx.rev(),
3174 ctx.rev(),
3175 ctx,
3175 ctx,
3176 ctx.description().split(b'\n', 1)[0],
3176 ctx.description().split(b'\n', 1)[0],
3177 )
3177 )
3178 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3178 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3179 if names:
3179 if names:
3180 desc += b' (%s)' % b' '.join(names)
3180 desc += b' (%s)' % b' '.join(names)
3181 ui.status(_(b'grafting %s\n') % desc)
3181 ui.status(_(b'grafting %s\n') % desc)
3182 if opts.get(b'dry_run'):
3182 if opts.get(b'dry_run'):
3183 continue
3183 continue
3184
3184
3185 source = ctx.extra().get(b'source')
3185 source = ctx.extra().get(b'source')
3186 extra = {}
3186 extra = {}
3187 if source:
3187 if source:
3188 extra[b'source'] = source
3188 extra[b'source'] = source
3189 extra[b'intermediate-source'] = ctx.hex()
3189 extra[b'intermediate-source'] = ctx.hex()
3190 else:
3190 else:
3191 extra[b'source'] = ctx.hex()
3191 extra[b'source'] = ctx.hex()
3192 user = ctx.user()
3192 user = ctx.user()
3193 if opts.get(b'user'):
3193 if opts.get(b'user'):
3194 user = opts[b'user']
3194 user = opts[b'user']
3195 statedata[b'user'] = user
3195 statedata[b'user'] = user
3196 date = ctx.date()
3196 date = ctx.date()
3197 if opts.get(b'date'):
3197 if opts.get(b'date'):
3198 date = opts[b'date']
3198 date = opts[b'date']
3199 statedata[b'date'] = date
3199 statedata[b'date'] = date
3200 message = ctx.description()
3200 message = ctx.description()
3201 if opts.get(b'log'):
3201 if opts.get(b'log'):
3202 message += b'\n(grafted from %s)' % ctx.hex()
3202 message += b'\n(grafted from %s)' % ctx.hex()
3203 statedata[b'log'] = True
3203 statedata[b'log'] = True
3204
3204
3205 # we don't merge the first commit when continuing
3205 # we don't merge the first commit when continuing
3206 if not cont:
3206 if not cont:
3207 # perform the graft merge with p1(rev) as 'ancestor'
3207 # perform the graft merge with p1(rev) as 'ancestor'
3208 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3208 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3209 base = ctx.p1() if basectx is None else basectx
3209 base = ctx.p1() if basectx is None else basectx
3210 with ui.configoverride(overrides, b'graft'):
3210 with ui.configoverride(overrides, b'graft'):
3211 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3211 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3212 # report any conflicts
3212 # report any conflicts
3213 if stats.unresolvedcount > 0:
3213 if stats.unresolvedcount > 0:
3214 # write out state for --continue
3214 # write out state for --continue
3215 nodes = [repo[rev].hex() for rev in revs[pos:]]
3215 nodes = [repo[rev].hex() for rev in revs[pos:]]
3216 statedata[b'nodes'] = nodes
3216 statedata[b'nodes'] = nodes
3217 stateversion = 1
3217 stateversion = 1
3218 graftstate.save(stateversion, statedata)
3218 graftstate.save(stateversion, statedata)
3219 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3219 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3220 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3220 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3221 return 1
3221 return 1
3222 else:
3222 else:
3223 cont = False
3223 cont = False
3224
3224
3225 # commit if --no-commit is false
3225 # commit if --no-commit is false
3226 if not opts.get(b'no_commit'):
3226 if not opts.get(b'no_commit'):
3227 node = repo.commit(
3227 node = repo.commit(
3228 text=message, user=user, date=date, extra=extra, editor=editor
3228 text=message, user=user, date=date, extra=extra, editor=editor
3229 )
3229 )
3230 if node is None:
3230 if node is None:
3231 ui.warn(
3231 ui.warn(
3232 _(b'note: graft of %d:%s created no changes to commit\n')
3232 _(b'note: graft of %d:%s created no changes to commit\n')
3233 % (ctx.rev(), ctx)
3233 % (ctx.rev(), ctx)
3234 )
3234 )
3235 # checking that newnodes exist because old state files won't have it
3235 # checking that newnodes exist because old state files won't have it
3236 elif statedata.get(b'newnodes') is not None:
3236 elif statedata.get(b'newnodes') is not None:
3237 statedata[b'newnodes'].append(node)
3237 statedata[b'newnodes'].append(node)
3238
3238
3239 # remove state when we complete successfully
3239 # remove state when we complete successfully
3240 if not opts.get(b'dry_run'):
3240 if not opts.get(b'dry_run'):
3241 graftstate.delete()
3241 graftstate.delete()
3242
3242
3243 return 0
3243 return 0
3244
3244
3245
3245
3246 def _stopgraft(ui, repo, graftstate):
3246 def _stopgraft(ui, repo, graftstate):
3247 """stop the interrupted graft"""
3247 """stop the interrupted graft"""
3248 if not graftstate.exists():
3248 if not graftstate.exists():
3249 raise error.Abort(_(b"no interrupted graft found"))
3249 raise error.Abort(_(b"no interrupted graft found"))
3250 pctx = repo[b'.']
3250 pctx = repo[b'.']
3251 mergemod.clean_update(pctx)
3251 mergemod.clean_update(pctx)
3252 graftstate.delete()
3252 graftstate.delete()
3253 ui.status(_(b"stopped the interrupted graft\n"))
3253 ui.status(_(b"stopped the interrupted graft\n"))
3254 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3254 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3255 return 0
3255 return 0
3256
3256
3257
3257
3258 statemod.addunfinished(
3258 statemod.addunfinished(
3259 b'graft',
3259 b'graft',
3260 fname=b'graftstate',
3260 fname=b'graftstate',
3261 clearable=True,
3261 clearable=True,
3262 stopflag=True,
3262 stopflag=True,
3263 continueflag=True,
3263 continueflag=True,
3264 abortfunc=cmdutil.hgabortgraft,
3264 abortfunc=cmdutil.hgabortgraft,
3265 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3265 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3266 )
3266 )
3267
3267
3268
3268
3269 @command(
3269 @command(
3270 b'grep',
3270 b'grep',
3271 [
3271 [
3272 (b'0', b'print0', None, _(b'end fields with NUL')),
3272 (b'0', b'print0', None, _(b'end fields with NUL')),
3273 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3273 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3274 (
3274 (
3275 b'',
3275 b'',
3276 b'diff',
3276 b'diff',
3277 None,
3277 None,
3278 _(
3278 _(
3279 b'search revision differences for when the pattern was added '
3279 b'search revision differences for when the pattern was added '
3280 b'or removed'
3280 b'or removed'
3281 ),
3281 ),
3282 ),
3282 ),
3283 (b'a', b'text', None, _(b'treat all files as text')),
3283 (b'a', b'text', None, _(b'treat all files as text')),
3284 (
3284 (
3285 b'f',
3285 b'f',
3286 b'follow',
3286 b'follow',
3287 None,
3287 None,
3288 _(
3288 _(
3289 b'follow changeset history,'
3289 b'follow changeset history,'
3290 b' or file history across copies and renames'
3290 b' or file history across copies and renames'
3291 ),
3291 ),
3292 ),
3292 ),
3293 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3293 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3294 (
3294 (
3295 b'l',
3295 b'l',
3296 b'files-with-matches',
3296 b'files-with-matches',
3297 None,
3297 None,
3298 _(b'print only filenames and revisions that match'),
3298 _(b'print only filenames and revisions that match'),
3299 ),
3299 ),
3300 (b'n', b'line-number', None, _(b'print matching line numbers')),
3300 (b'n', b'line-number', None, _(b'print matching line numbers')),
3301 (
3301 (
3302 b'r',
3302 b'r',
3303 b'rev',
3303 b'rev',
3304 [],
3304 [],
3305 _(b'search files changed within revision range'),
3305 _(b'search files changed within revision range'),
3306 _(b'REV'),
3306 _(b'REV'),
3307 ),
3307 ),
3308 (
3308 (
3309 b'',
3309 b'',
3310 b'all-files',
3310 b'all-files',
3311 None,
3311 None,
3312 _(
3312 _(
3313 b'include all files in the changeset while grepping (DEPRECATED)'
3313 b'include all files in the changeset while grepping (DEPRECATED)'
3314 ),
3314 ),
3315 ),
3315 ),
3316 (b'u', b'user', None, _(b'list the author (long with -v)')),
3316 (b'u', b'user', None, _(b'list the author (long with -v)')),
3317 (b'd', b'date', None, _(b'list the date (short with -q)')),
3317 (b'd', b'date', None, _(b'list the date (short with -q)')),
3318 ]
3318 ]
3319 + formatteropts
3319 + formatteropts
3320 + walkopts,
3320 + walkopts,
3321 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3321 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3322 helpcategory=command.CATEGORY_FILE_CONTENTS,
3322 helpcategory=command.CATEGORY_FILE_CONTENTS,
3323 inferrepo=True,
3323 inferrepo=True,
3324 intents={INTENT_READONLY},
3324 intents={INTENT_READONLY},
3325 )
3325 )
3326 def grep(ui, repo, pattern, *pats, **opts):
3326 def grep(ui, repo, pattern, *pats, **opts):
3327 """search for a pattern in specified files
3327 """search for a pattern in specified files
3328
3328
3329 Search the working directory or revision history for a regular
3329 Search the working directory or revision history for a regular
3330 expression in the specified files for the entire repository.
3330 expression in the specified files for the entire repository.
3331
3331
3332 By default, grep searches the repository files in the working
3332 By default, grep searches the repository files in the working
3333 directory and prints the files where it finds a match. To specify
3333 directory and prints the files where it finds a match. To specify
3334 historical revisions instead of the working directory, use the
3334 historical revisions instead of the working directory, use the
3335 --rev flag.
3335 --rev flag.
3336
3336
3337 To search instead historical revision differences that contains a
3337 To search instead historical revision differences that contains a
3338 change in match status ("-" for a match that becomes a non-match,
3338 change in match status ("-" for a match that becomes a non-match,
3339 or "+" for a non-match that becomes a match), use the --diff flag.
3339 or "+" for a non-match that becomes a match), use the --diff flag.
3340
3340
3341 PATTERN can be any Python (roughly Perl-compatible) regular
3341 PATTERN can be any Python (roughly Perl-compatible) regular
3342 expression.
3342 expression.
3343
3343
3344 If no FILEs are specified and the --rev flag isn't supplied, all
3344 If no FILEs are specified and the --rev flag isn't supplied, all
3345 files in the working directory are searched. When using the --rev
3345 files in the working directory are searched. When using the --rev
3346 flag and specifying FILEs, use the --follow argument to also
3346 flag and specifying FILEs, use the --follow argument to also
3347 follow the specified FILEs across renames and copies.
3347 follow the specified FILEs across renames and copies.
3348
3348
3349 .. container:: verbose
3349 .. container:: verbose
3350
3350
3351 Template:
3351 Template:
3352
3352
3353 The following keywords are supported in addition to the common template
3353 The following keywords are supported in addition to the common template
3354 keywords and functions. See also :hg:`help templates`.
3354 keywords and functions. See also :hg:`help templates`.
3355
3355
3356 :change: String. Character denoting insertion ``+`` or removal ``-``.
3356 :change: String. Character denoting insertion ``+`` or removal ``-``.
3357 Available if ``--diff`` is specified.
3357 Available if ``--diff`` is specified.
3358 :lineno: Integer. Line number of the match.
3358 :lineno: Integer. Line number of the match.
3359 :path: String. Repository-absolute path of the file.
3359 :path: String. Repository-absolute path of the file.
3360 :texts: List of text chunks.
3360 :texts: List of text chunks.
3361
3361
3362 And each entry of ``{texts}`` provides the following sub-keywords.
3362 And each entry of ``{texts}`` provides the following sub-keywords.
3363
3363
3364 :matched: Boolean. True if the chunk matches the specified pattern.
3364 :matched: Boolean. True if the chunk matches the specified pattern.
3365 :text: String. Chunk content.
3365 :text: String. Chunk content.
3366
3366
3367 See :hg:`help templates.operators` for the list expansion syntax.
3367 See :hg:`help templates.operators` for the list expansion syntax.
3368
3368
3369 Returns 0 if a match is found, 1 otherwise.
3369 Returns 0 if a match is found, 1 otherwise.
3370
3370
3371 """
3371 """
3372 opts = pycompat.byteskwargs(opts)
3372 opts = pycompat.byteskwargs(opts)
3373 diff = opts.get(b'all') or opts.get(b'diff')
3373 diff = opts.get(b'all') or opts.get(b'diff')
3374 if diff and opts.get(b'all_files'):
3374 if diff and opts.get(b'all_files'):
3375 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3375 raise error.Abort(_(b'--diff and --all-files are mutually exclusive'))
3376 if opts.get(b'all_files') is None and not diff:
3376 if opts.get(b'all_files') is None and not diff:
3377 opts[b'all_files'] = True
3377 opts[b'all_files'] = True
3378 plaingrep = (
3378 plaingrep = (
3379 opts.get(b'all_files')
3379 opts.get(b'all_files')
3380 and not opts.get(b'rev')
3380 and not opts.get(b'rev')
3381 and not opts.get(b'follow')
3381 and not opts.get(b'follow')
3382 )
3382 )
3383 all_files = opts.get(b'all_files')
3383 all_files = opts.get(b'all_files')
3384 if plaingrep:
3384 if plaingrep:
3385 opts[b'rev'] = [b'wdir()']
3385 opts[b'rev'] = [b'wdir()']
3386
3386
3387 reflags = re.M
3387 reflags = re.M
3388 if opts.get(b'ignore_case'):
3388 if opts.get(b'ignore_case'):
3389 reflags |= re.I
3389 reflags |= re.I
3390 try:
3390 try:
3391 regexp = util.re.compile(pattern, reflags)
3391 regexp = util.re.compile(pattern, reflags)
3392 except re.error as inst:
3392 except re.error as inst:
3393 ui.warn(
3393 ui.warn(
3394 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3394 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3395 )
3395 )
3396 return 1
3396 return 1
3397 sep, eol = b':', b'\n'
3397 sep, eol = b':', b'\n'
3398 if opts.get(b'print0'):
3398 if opts.get(b'print0'):
3399 sep = eol = b'\0'
3399 sep = eol = b'\0'
3400
3400
3401 getfile = util.lrucachefunc(repo.file)
3401 searcher = grepmod.grepsearcher(ui, repo, regexp)
3402 matches = {}
3402
3403 copies = {}
3403 getfile = searcher._getfile
3404 matches = searcher._matches
3405 copies = searcher._copies
3404
3406
3405 def grepbody(fn, rev, body):
3407 def grepbody(fn, rev, body):
3406 matches[rev].setdefault(fn, [])
3408 matches[rev].setdefault(fn, [])
3407 m = matches[rev][fn]
3409 m = matches[rev][fn]
3408 if body is None:
3410 if body is None:
3409 return
3411 return
3410
3412
3411 for lnum, cstart, cend, line in grepmod.matchlines(body, regexp):
3413 for lnum, cstart, cend, line in grepmod.matchlines(body, regexp):
3412 s = grepmod.linestate(line, lnum, cstart, cend)
3414 s = grepmod.linestate(line, lnum, cstart, cend)
3413 m.append(s)
3415 m.append(s)
3414
3416
3415 uipathfn = scmutil.getuipathfn(repo)
3417 uipathfn = scmutil.getuipathfn(repo)
3416
3418
3417 def display(fm, fn, ctx, pstates, states):
3419 def display(fm, fn, ctx, pstates, states):
3418 rev = scmutil.intrev(ctx)
3420 rev = scmutil.intrev(ctx)
3419 if fm.isplain():
3421 if fm.isplain():
3420 formatuser = ui.shortuser
3422 formatuser = ui.shortuser
3421 else:
3423 else:
3422 formatuser = pycompat.bytestr
3424 formatuser = pycompat.bytestr
3423 if ui.quiet:
3425 if ui.quiet:
3424 datefmt = b'%Y-%m-%d'
3426 datefmt = b'%Y-%m-%d'
3425 else:
3427 else:
3426 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3428 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3427 found = False
3429 found = False
3428
3430
3429 @util.cachefunc
3431 @util.cachefunc
3430 def binary():
3432 def binary():
3431 flog = getfile(fn)
3433 flog = getfile(fn)
3432 try:
3434 try:
3433 return stringutil.binary(flog.read(ctx.filenode(fn)))
3435 return stringutil.binary(flog.read(ctx.filenode(fn)))
3434 except error.WdirUnsupported:
3436 except error.WdirUnsupported:
3435 return ctx[fn].isbinary()
3437 return ctx[fn].isbinary()
3436
3438
3437 fieldnamemap = {b'linenumber': b'lineno'}
3439 fieldnamemap = {b'linenumber': b'lineno'}
3438 if diff:
3440 if diff:
3439 iter = grepmod.difflinestates(pstates, states)
3441 iter = grepmod.difflinestates(pstates, states)
3440 else:
3442 else:
3441 iter = [(b'', l) for l in states]
3443 iter = [(b'', l) for l in states]
3442 for change, l in iter:
3444 for change, l in iter:
3443 fm.startitem()
3445 fm.startitem()
3444 fm.context(ctx=ctx)
3446 fm.context(ctx=ctx)
3445 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3447 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3446 fm.plain(uipathfn(fn), label=b'grep.filename')
3448 fm.plain(uipathfn(fn), label=b'grep.filename')
3447
3449
3448 cols = [
3450 cols = [
3449 (b'rev', b'%d', rev, not plaingrep, b''),
3451 (b'rev', b'%d', rev, not plaingrep, b''),
3450 (
3452 (
3451 b'linenumber',
3453 b'linenumber',
3452 b'%d',
3454 b'%d',
3453 l.linenum,
3455 l.linenum,
3454 opts.get(b'line_number'),
3456 opts.get(b'line_number'),
3455 b'',
3457 b'',
3456 ),
3458 ),
3457 ]
3459 ]
3458 if diff:
3460 if diff:
3459 cols.append(
3461 cols.append(
3460 (
3462 (
3461 b'change',
3463 b'change',
3462 b'%s',
3464 b'%s',
3463 change,
3465 change,
3464 True,
3466 True,
3465 b'grep.inserted '
3467 b'grep.inserted '
3466 if change == b'+'
3468 if change == b'+'
3467 else b'grep.deleted ',
3469 else b'grep.deleted ',
3468 )
3470 )
3469 )
3471 )
3470 cols.extend(
3472 cols.extend(
3471 [
3473 [
3472 (
3474 (
3473 b'user',
3475 b'user',
3474 b'%s',
3476 b'%s',
3475 formatuser(ctx.user()),
3477 formatuser(ctx.user()),
3476 opts.get(b'user'),
3478 opts.get(b'user'),
3477 b'',
3479 b'',
3478 ),
3480 ),
3479 (
3481 (
3480 b'date',
3482 b'date',
3481 b'%s',
3483 b'%s',
3482 fm.formatdate(ctx.date(), datefmt),
3484 fm.formatdate(ctx.date(), datefmt),
3483 opts.get(b'date'),
3485 opts.get(b'date'),
3484 b'',
3486 b'',
3485 ),
3487 ),
3486 ]
3488 ]
3487 )
3489 )
3488 for name, fmt, data, cond, extra_label in cols:
3490 for name, fmt, data, cond, extra_label in cols:
3489 if cond:
3491 if cond:
3490 fm.plain(sep, label=b'grep.sep')
3492 fm.plain(sep, label=b'grep.sep')
3491 field = fieldnamemap.get(name, name)
3493 field = fieldnamemap.get(name, name)
3492 label = extra_label + (b'grep.%s' % name)
3494 label = extra_label + (b'grep.%s' % name)
3493 fm.condwrite(cond, field, fmt, data, label=label)
3495 fm.condwrite(cond, field, fmt, data, label=label)
3494 if not opts.get(b'files_with_matches'):
3496 if not opts.get(b'files_with_matches'):
3495 fm.plain(sep, label=b'grep.sep')
3497 fm.plain(sep, label=b'grep.sep')
3496 if not opts.get(b'text') and binary():
3498 if not opts.get(b'text') and binary():
3497 fm.plain(_(b" Binary file matches"))
3499 fm.plain(_(b" Binary file matches"))
3498 else:
3500 else:
3499 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3501 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3500 fm.plain(eol)
3502 fm.plain(eol)
3501 found = True
3503 found = True
3502 if opts.get(b'files_with_matches'):
3504 if opts.get(b'files_with_matches'):
3503 break
3505 break
3504 return found
3506 return found
3505
3507
3506 def displaymatches(fm, l):
3508 def displaymatches(fm, l):
3507 p = 0
3509 p = 0
3508 for s, e in l.findpos(regexp):
3510 for s, e in l.findpos(regexp):
3509 if p < s:
3511 if p < s:
3510 fm.startitem()
3512 fm.startitem()
3511 fm.write(b'text', b'%s', l.line[p:s])
3513 fm.write(b'text', b'%s', l.line[p:s])
3512 fm.data(matched=False)
3514 fm.data(matched=False)
3513 fm.startitem()
3515 fm.startitem()
3514 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3516 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3515 fm.data(matched=True)
3517 fm.data(matched=True)
3516 p = e
3518 p = e
3517 if p < len(l.line):
3519 if p < len(l.line):
3518 fm.startitem()
3520 fm.startitem()
3519 fm.write(b'text', b'%s', l.line[p:])
3521 fm.write(b'text', b'%s', l.line[p:])
3520 fm.data(matched=False)
3522 fm.data(matched=False)
3521 fm.end()
3523 fm.end()
3522
3524
3523 skip = set()
3525 skip = searcher._skip
3524 revfiles = {}
3526 revfiles = searcher._revfiles
3525 found = False
3527 found = False
3526 follow = opts.get(b'follow')
3528 follow = opts.get(b'follow')
3527
3529
3528 getrenamed = scmutil.getrenamedfn(repo)
3530 getrenamed = searcher._getrenamed
3529
3531
3530 def readfile(ctx, fn):
3532 def readfile(ctx, fn):
3531 rev = ctx.rev()
3533 rev = ctx.rev()
3532 if rev is None:
3534 if rev is None:
3533 fctx = ctx[fn]
3535 fctx = ctx[fn]
3534 try:
3536 try:
3535 return fctx.data()
3537 return fctx.data()
3536 except IOError as e:
3538 except IOError as e:
3537 if e.errno != errno.ENOENT:
3539 if e.errno != errno.ENOENT:
3538 raise
3540 raise
3539 else:
3541 else:
3540 flog = getfile(fn)
3542 flog = getfile(fn)
3541 fnode = ctx.filenode(fn)
3543 fnode = ctx.filenode(fn)
3542 try:
3544 try:
3543 return flog.read(fnode)
3545 return flog.read(fnode)
3544 except error.CensoredNodeError:
3546 except error.CensoredNodeError:
3545 ui.warn(
3547 ui.warn(
3546 _(
3548 _(
3547 b'cannot search in censored file: %(filename)s:%(revnum)s\n'
3549 b'cannot search in censored file: %(filename)s:%(revnum)s\n'
3548 )
3550 )
3549 % {b'filename': fn, b'revnum': pycompat.bytestr(rev),}
3551 % {b'filename': fn, b'revnum': pycompat.bytestr(rev),}
3550 )
3552 )
3551
3553
3552 def prep(ctx, fmatch):
3554 def prep(ctx, fmatch):
3553 rev = ctx.rev()
3555 rev = ctx.rev()
3554 pctx = ctx.p1()
3556 pctx = ctx.p1()
3555 matches.setdefault(rev, {})
3557 matches.setdefault(rev, {})
3556 if diff:
3558 if diff:
3557 parent = pctx.rev()
3559 parent = pctx.rev()
3558 matches.setdefault(parent, {})
3560 matches.setdefault(parent, {})
3559 files = revfiles.setdefault(rev, [])
3561 files = revfiles.setdefault(rev, [])
3560 if rev is None:
3562 if rev is None:
3561 # in `hg grep pattern`, 2/3 of the time is spent is spent in
3563 # in `hg grep pattern`, 2/3 of the time is spent is spent in
3562 # pathauditor checks without this in mozilla-central
3564 # pathauditor checks without this in mozilla-central
3563 contextmanager = repo.wvfs.audit.cached
3565 contextmanager = repo.wvfs.audit.cached
3564 else:
3566 else:
3565 contextmanager = util.nullcontextmanager
3567 contextmanager = util.nullcontextmanager
3566 with contextmanager():
3568 with contextmanager():
3567 # TODO: maybe better to warn missing files?
3569 # TODO: maybe better to warn missing files?
3568 if all_files:
3570 if all_files:
3569 fmatch = matchmod.badmatch(fmatch, lambda f, msg: None)
3571 fmatch = matchmod.badmatch(fmatch, lambda f, msg: None)
3570 filenames = ctx.matches(fmatch)
3572 filenames = ctx.matches(fmatch)
3571 else:
3573 else:
3572 filenames = (f for f in ctx.files() if fmatch(f))
3574 filenames = (f for f in ctx.files() if fmatch(f))
3573 for fn in filenames:
3575 for fn in filenames:
3574 # fn might not exist in the revision (could be a file removed by
3576 # fn might not exist in the revision (could be a file removed by
3575 # the revision). We could check `fn not in ctx` even when rev is
3577 # the revision). We could check `fn not in ctx` even when rev is
3576 # None, but it's less racy to protect againt that in readfile.
3578 # None, but it's less racy to protect againt that in readfile.
3577 if rev is not None and fn not in ctx:
3579 if rev is not None and fn not in ctx:
3578 continue
3580 continue
3579
3581
3580 copy = None
3582 copy = None
3581 if follow:
3583 if follow:
3582 copy = getrenamed(fn, rev)
3584 copy = getrenamed(fn, rev)
3583 if copy:
3585 if copy:
3584 copies.setdefault(rev, {})[fn] = copy
3586 copies.setdefault(rev, {})[fn] = copy
3585 if fn in skip:
3587 if fn in skip:
3586 skip.add(copy)
3588 skip.add(copy)
3587 if fn in skip:
3589 if fn in skip:
3588 continue
3590 continue
3589 files.append(fn)
3591 files.append(fn)
3590
3592
3591 if fn not in matches[rev]:
3593 if fn not in matches[rev]:
3592 grepbody(fn, rev, readfile(ctx, fn))
3594 grepbody(fn, rev, readfile(ctx, fn))
3593
3595
3594 if diff:
3596 if diff:
3595 pfn = copy or fn
3597 pfn = copy or fn
3596 if pfn not in matches[parent] and pfn in pctx:
3598 if pfn not in matches[parent] and pfn in pctx:
3597 grepbody(pfn, parent, readfile(pctx, pfn))
3599 grepbody(pfn, parent, readfile(pctx, pfn))
3598
3600
3599 wopts = logcmdutil.walkopts(
3601 wopts = logcmdutil.walkopts(
3600 pats=pats,
3602 pats=pats,
3601 opts=opts,
3603 opts=opts,
3602 revspec=opts[b'rev'],
3604 revspec=opts[b'rev'],
3603 include_pats=opts[b'include'],
3605 include_pats=opts[b'include'],
3604 exclude_pats=opts[b'exclude'],
3606 exclude_pats=opts[b'exclude'],
3605 follow=follow,
3607 follow=follow,
3606 force_changelog_traversal=all_files,
3608 force_changelog_traversal=all_files,
3607 filter_revisions_by_pats=not all_files,
3609 filter_revisions_by_pats=not all_files,
3608 )
3610 )
3609 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3611 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3610
3612
3611 ui.pager(b'grep')
3613 ui.pager(b'grep')
3612 fm = ui.formatter(b'grep', opts)
3614 fm = ui.formatter(b'grep', opts)
3613 for ctx in cmdutil.walkchangerevs(repo, revs, makefilematcher, prep):
3615 for ctx in cmdutil.walkchangerevs(repo, revs, makefilematcher, prep):
3614 rev = ctx.rev()
3616 rev = ctx.rev()
3615 parent = ctx.p1().rev()
3617 parent = ctx.p1().rev()
3616 for fn in sorted(revfiles.get(rev, [])):
3618 for fn in sorted(revfiles.get(rev, [])):
3617 states = matches[rev][fn]
3619 states = matches[rev][fn]
3618 copy = copies.get(rev, {}).get(fn)
3620 copy = copies.get(rev, {}).get(fn)
3619 if fn in skip:
3621 if fn in skip:
3620 if copy:
3622 if copy:
3621 skip.add(copy)
3623 skip.add(copy)
3622 continue
3624 continue
3623 pstates = matches.get(parent, {}).get(copy or fn, [])
3625 pstates = matches.get(parent, {}).get(copy or fn, [])
3624 if pstates or states:
3626 if pstates or states:
3625 r = display(fm, fn, ctx, pstates, states)
3627 r = display(fm, fn, ctx, pstates, states)
3626 found = found or r
3628 found = found or r
3627 if r and not diff and not all_files:
3629 if r and not diff and not all_files:
3628 skip.add(fn)
3630 skip.add(fn)
3629 if copy:
3631 if copy:
3630 skip.add(copy)
3632 skip.add(copy)
3631 del revfiles[rev]
3633 del revfiles[rev]
3632 # We will keep the matches dict for the duration of the window
3634 # We will keep the matches dict for the duration of the window
3633 # clear the matches dict once the window is over
3635 # clear the matches dict once the window is over
3634 if not revfiles:
3636 if not revfiles:
3635 matches.clear()
3637 matches.clear()
3636 fm.end()
3638 fm.end()
3637
3639
3638 return not found
3640 return not found
3639
3641
3640
3642
3641 @command(
3643 @command(
3642 b'heads',
3644 b'heads',
3643 [
3645 [
3644 (
3646 (
3645 b'r',
3647 b'r',
3646 b'rev',
3648 b'rev',
3647 b'',
3649 b'',
3648 _(b'show only heads which are descendants of STARTREV'),
3650 _(b'show only heads which are descendants of STARTREV'),
3649 _(b'STARTREV'),
3651 _(b'STARTREV'),
3650 ),
3652 ),
3651 (b't', b'topo', False, _(b'show topological heads only')),
3653 (b't', b'topo', False, _(b'show topological heads only')),
3652 (
3654 (
3653 b'a',
3655 b'a',
3654 b'active',
3656 b'active',
3655 False,
3657 False,
3656 _(b'show active branchheads only (DEPRECATED)'),
3658 _(b'show active branchheads only (DEPRECATED)'),
3657 ),
3659 ),
3658 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3660 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3659 ]
3661 ]
3660 + templateopts,
3662 + templateopts,
3661 _(b'[-ct] [-r STARTREV] [REV]...'),
3663 _(b'[-ct] [-r STARTREV] [REV]...'),
3662 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3664 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3663 intents={INTENT_READONLY},
3665 intents={INTENT_READONLY},
3664 )
3666 )
3665 def heads(ui, repo, *branchrevs, **opts):
3667 def heads(ui, repo, *branchrevs, **opts):
3666 """show branch heads
3668 """show branch heads
3667
3669
3668 With no arguments, show all open branch heads in the repository.
3670 With no arguments, show all open branch heads in the repository.
3669 Branch heads are changesets that have no descendants on the
3671 Branch heads are changesets that have no descendants on the
3670 same branch. They are where development generally takes place and
3672 same branch. They are where development generally takes place and
3671 are the usual targets for update and merge operations.
3673 are the usual targets for update and merge operations.
3672
3674
3673 If one or more REVs are given, only open branch heads on the
3675 If one or more REVs are given, only open branch heads on the
3674 branches associated with the specified changesets are shown. This
3676 branches associated with the specified changesets are shown. This
3675 means that you can use :hg:`heads .` to see the heads on the
3677 means that you can use :hg:`heads .` to see the heads on the
3676 currently checked-out branch.
3678 currently checked-out branch.
3677
3679
3678 If -c/--closed is specified, also show branch heads marked closed
3680 If -c/--closed is specified, also show branch heads marked closed
3679 (see :hg:`commit --close-branch`).
3681 (see :hg:`commit --close-branch`).
3680
3682
3681 If STARTREV is specified, only those heads that are descendants of
3683 If STARTREV is specified, only those heads that are descendants of
3682 STARTREV will be displayed.
3684 STARTREV will be displayed.
3683
3685
3684 If -t/--topo is specified, named branch mechanics will be ignored and only
3686 If -t/--topo is specified, named branch mechanics will be ignored and only
3685 topological heads (changesets with no children) will be shown.
3687 topological heads (changesets with no children) will be shown.
3686
3688
3687 Returns 0 if matching heads are found, 1 if not.
3689 Returns 0 if matching heads are found, 1 if not.
3688 """
3690 """
3689
3691
3690 opts = pycompat.byteskwargs(opts)
3692 opts = pycompat.byteskwargs(opts)
3691 start = None
3693 start = None
3692 rev = opts.get(b'rev')
3694 rev = opts.get(b'rev')
3693 if rev:
3695 if rev:
3694 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3696 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3695 start = scmutil.revsingle(repo, rev, None).node()
3697 start = scmutil.revsingle(repo, rev, None).node()
3696
3698
3697 if opts.get(b'topo'):
3699 if opts.get(b'topo'):
3698 heads = [repo[h] for h in repo.heads(start)]
3700 heads = [repo[h] for h in repo.heads(start)]
3699 else:
3701 else:
3700 heads = []
3702 heads = []
3701 for branch in repo.branchmap():
3703 for branch in repo.branchmap():
3702 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3704 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3703 heads = [repo[h] for h in heads]
3705 heads = [repo[h] for h in heads]
3704
3706
3705 if branchrevs:
3707 if branchrevs:
3706 branches = {
3708 branches = {
3707 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3709 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3708 }
3710 }
3709 heads = [h for h in heads if h.branch() in branches]
3711 heads = [h for h in heads if h.branch() in branches]
3710
3712
3711 if opts.get(b'active') and branchrevs:
3713 if opts.get(b'active') and branchrevs:
3712 dagheads = repo.heads(start)
3714 dagheads = repo.heads(start)
3713 heads = [h for h in heads if h.node() in dagheads]
3715 heads = [h for h in heads if h.node() in dagheads]
3714
3716
3715 if branchrevs:
3717 if branchrevs:
3716 haveheads = {h.branch() for h in heads}
3718 haveheads = {h.branch() for h in heads}
3717 if branches - haveheads:
3719 if branches - haveheads:
3718 headless = b', '.join(b for b in branches - haveheads)
3720 headless = b', '.join(b for b in branches - haveheads)
3719 msg = _(b'no open branch heads found on branches %s')
3721 msg = _(b'no open branch heads found on branches %s')
3720 if opts.get(b'rev'):
3722 if opts.get(b'rev'):
3721 msg += _(b' (started at %s)') % opts[b'rev']
3723 msg += _(b' (started at %s)') % opts[b'rev']
3722 ui.warn((msg + b'\n') % headless)
3724 ui.warn((msg + b'\n') % headless)
3723
3725
3724 if not heads:
3726 if not heads:
3725 return 1
3727 return 1
3726
3728
3727 ui.pager(b'heads')
3729 ui.pager(b'heads')
3728 heads = sorted(heads, key=lambda x: -(x.rev()))
3730 heads = sorted(heads, key=lambda x: -(x.rev()))
3729 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3731 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3730 for ctx in heads:
3732 for ctx in heads:
3731 displayer.show(ctx)
3733 displayer.show(ctx)
3732 displayer.close()
3734 displayer.close()
3733
3735
3734
3736
3735 @command(
3737 @command(
3736 b'help',
3738 b'help',
3737 [
3739 [
3738 (b'e', b'extension', None, _(b'show only help for extensions')),
3740 (b'e', b'extension', None, _(b'show only help for extensions')),
3739 (b'c', b'command', None, _(b'show only help for commands')),
3741 (b'c', b'command', None, _(b'show only help for commands')),
3740 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3742 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3741 (
3743 (
3742 b's',
3744 b's',
3743 b'system',
3745 b'system',
3744 [],
3746 [],
3745 _(b'show help for specific platform(s)'),
3747 _(b'show help for specific platform(s)'),
3746 _(b'PLATFORM'),
3748 _(b'PLATFORM'),
3747 ),
3749 ),
3748 ],
3750 ],
3749 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3751 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3750 helpcategory=command.CATEGORY_HELP,
3752 helpcategory=command.CATEGORY_HELP,
3751 norepo=True,
3753 norepo=True,
3752 intents={INTENT_READONLY},
3754 intents={INTENT_READONLY},
3753 )
3755 )
3754 def help_(ui, name=None, **opts):
3756 def help_(ui, name=None, **opts):
3755 """show help for a given topic or a help overview
3757 """show help for a given topic or a help overview
3756
3758
3757 With no arguments, print a list of commands with short help messages.
3759 With no arguments, print a list of commands with short help messages.
3758
3760
3759 Given a topic, extension, or command name, print help for that
3761 Given a topic, extension, or command name, print help for that
3760 topic.
3762 topic.
3761
3763
3762 Returns 0 if successful.
3764 Returns 0 if successful.
3763 """
3765 """
3764
3766
3765 keep = opts.get('system') or []
3767 keep = opts.get('system') or []
3766 if len(keep) == 0:
3768 if len(keep) == 0:
3767 if pycompat.sysplatform.startswith(b'win'):
3769 if pycompat.sysplatform.startswith(b'win'):
3768 keep.append(b'windows')
3770 keep.append(b'windows')
3769 elif pycompat.sysplatform == b'OpenVMS':
3771 elif pycompat.sysplatform == b'OpenVMS':
3770 keep.append(b'vms')
3772 keep.append(b'vms')
3771 elif pycompat.sysplatform == b'plan9':
3773 elif pycompat.sysplatform == b'plan9':
3772 keep.append(b'plan9')
3774 keep.append(b'plan9')
3773 else:
3775 else:
3774 keep.append(b'unix')
3776 keep.append(b'unix')
3775 keep.append(pycompat.sysplatform.lower())
3777 keep.append(pycompat.sysplatform.lower())
3776 if ui.verbose:
3778 if ui.verbose:
3777 keep.append(b'verbose')
3779 keep.append(b'verbose')
3778
3780
3779 commands = sys.modules[__name__]
3781 commands = sys.modules[__name__]
3780 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3782 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3781 ui.pager(b'help')
3783 ui.pager(b'help')
3782 ui.write(formatted)
3784 ui.write(formatted)
3783
3785
3784
3786
3785 @command(
3787 @command(
3786 b'identify|id',
3788 b'identify|id',
3787 [
3789 [
3788 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3790 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3789 (b'n', b'num', None, _(b'show local revision number')),
3791 (b'n', b'num', None, _(b'show local revision number')),
3790 (b'i', b'id', None, _(b'show global revision id')),
3792 (b'i', b'id', None, _(b'show global revision id')),
3791 (b'b', b'branch', None, _(b'show branch')),
3793 (b'b', b'branch', None, _(b'show branch')),
3792 (b't', b'tags', None, _(b'show tags')),
3794 (b't', b'tags', None, _(b'show tags')),
3793 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3795 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3794 ]
3796 ]
3795 + remoteopts
3797 + remoteopts
3796 + formatteropts,
3798 + formatteropts,
3797 _(b'[-nibtB] [-r REV] [SOURCE]'),
3799 _(b'[-nibtB] [-r REV] [SOURCE]'),
3798 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3800 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3799 optionalrepo=True,
3801 optionalrepo=True,
3800 intents={INTENT_READONLY},
3802 intents={INTENT_READONLY},
3801 )
3803 )
3802 def identify(
3804 def identify(
3803 ui,
3805 ui,
3804 repo,
3806 repo,
3805 source=None,
3807 source=None,
3806 rev=None,
3808 rev=None,
3807 num=None,
3809 num=None,
3808 id=None,
3810 id=None,
3809 branch=None,
3811 branch=None,
3810 tags=None,
3812 tags=None,
3811 bookmarks=None,
3813 bookmarks=None,
3812 **opts
3814 **opts
3813 ):
3815 ):
3814 """identify the working directory or specified revision
3816 """identify the working directory or specified revision
3815
3817
3816 Print a summary identifying the repository state at REV using one or
3818 Print a summary identifying the repository state at REV using one or
3817 two parent hash identifiers, followed by a "+" if the working
3819 two parent hash identifiers, followed by a "+" if the working
3818 directory has uncommitted changes, the branch name (if not default),
3820 directory has uncommitted changes, the branch name (if not default),
3819 a list of tags, and a list of bookmarks.
3821 a list of tags, and a list of bookmarks.
3820
3822
3821 When REV is not given, print a summary of the current state of the
3823 When REV is not given, print a summary of the current state of the
3822 repository including the working directory. Specify -r. to get information
3824 repository including the working directory. Specify -r. to get information
3823 of the working directory parent without scanning uncommitted changes.
3825 of the working directory parent without scanning uncommitted changes.
3824
3826
3825 Specifying a path to a repository root or Mercurial bundle will
3827 Specifying a path to a repository root or Mercurial bundle will
3826 cause lookup to operate on that repository/bundle.
3828 cause lookup to operate on that repository/bundle.
3827
3829
3828 .. container:: verbose
3830 .. container:: verbose
3829
3831
3830 Template:
3832 Template:
3831
3833
3832 The following keywords are supported in addition to the common template
3834 The following keywords are supported in addition to the common template
3833 keywords and functions. See also :hg:`help templates`.
3835 keywords and functions. See also :hg:`help templates`.
3834
3836
3835 :dirty: String. Character ``+`` denoting if the working directory has
3837 :dirty: String. Character ``+`` denoting if the working directory has
3836 uncommitted changes.
3838 uncommitted changes.
3837 :id: String. One or two nodes, optionally followed by ``+``.
3839 :id: String. One or two nodes, optionally followed by ``+``.
3838 :parents: List of strings. Parent nodes of the changeset.
3840 :parents: List of strings. Parent nodes of the changeset.
3839
3841
3840 Examples:
3842 Examples:
3841
3843
3842 - generate a build identifier for the working directory::
3844 - generate a build identifier for the working directory::
3843
3845
3844 hg id --id > build-id.dat
3846 hg id --id > build-id.dat
3845
3847
3846 - find the revision corresponding to a tag::
3848 - find the revision corresponding to a tag::
3847
3849
3848 hg id -n -r 1.3
3850 hg id -n -r 1.3
3849
3851
3850 - check the most recent revision of a remote repository::
3852 - check the most recent revision of a remote repository::
3851
3853
3852 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3854 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3853
3855
3854 See :hg:`log` for generating more information about specific revisions,
3856 See :hg:`log` for generating more information about specific revisions,
3855 including full hash identifiers.
3857 including full hash identifiers.
3856
3858
3857 Returns 0 if successful.
3859 Returns 0 if successful.
3858 """
3860 """
3859
3861
3860 opts = pycompat.byteskwargs(opts)
3862 opts = pycompat.byteskwargs(opts)
3861 if not repo and not source:
3863 if not repo and not source:
3862 raise error.Abort(
3864 raise error.Abort(
3863 _(b"there is no Mercurial repository here (.hg not found)")
3865 _(b"there is no Mercurial repository here (.hg not found)")
3864 )
3866 )
3865
3867
3866 default = not (num or id or branch or tags or bookmarks)
3868 default = not (num or id or branch or tags or bookmarks)
3867 output = []
3869 output = []
3868 revs = []
3870 revs = []
3869
3871
3870 if source:
3872 if source:
3871 source, branches = hg.parseurl(ui.expandpath(source))
3873 source, branches = hg.parseurl(ui.expandpath(source))
3872 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3874 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3873 repo = peer.local()
3875 repo = peer.local()
3874 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3876 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3875
3877
3876 fm = ui.formatter(b'identify', opts)
3878 fm = ui.formatter(b'identify', opts)
3877 fm.startitem()
3879 fm.startitem()
3878
3880
3879 if not repo:
3881 if not repo:
3880 if num or branch or tags:
3882 if num or branch or tags:
3881 raise error.Abort(
3883 raise error.Abort(
3882 _(b"can't query remote revision number, branch, or tags")
3884 _(b"can't query remote revision number, branch, or tags")
3883 )
3885 )
3884 if not rev and revs:
3886 if not rev and revs:
3885 rev = revs[0]
3887 rev = revs[0]
3886 if not rev:
3888 if not rev:
3887 rev = b"tip"
3889 rev = b"tip"
3888
3890
3889 remoterev = peer.lookup(rev)
3891 remoterev = peer.lookup(rev)
3890 hexrev = fm.hexfunc(remoterev)
3892 hexrev = fm.hexfunc(remoterev)
3891 if default or id:
3893 if default or id:
3892 output = [hexrev]
3894 output = [hexrev]
3893 fm.data(id=hexrev)
3895 fm.data(id=hexrev)
3894
3896
3895 @util.cachefunc
3897 @util.cachefunc
3896 def getbms():
3898 def getbms():
3897 bms = []
3899 bms = []
3898
3900
3899 if b'bookmarks' in peer.listkeys(b'namespaces'):
3901 if b'bookmarks' in peer.listkeys(b'namespaces'):
3900 hexremoterev = hex(remoterev)
3902 hexremoterev = hex(remoterev)
3901 bms = [
3903 bms = [
3902 bm
3904 bm
3903 for bm, bmr in pycompat.iteritems(
3905 for bm, bmr in pycompat.iteritems(
3904 peer.listkeys(b'bookmarks')
3906 peer.listkeys(b'bookmarks')
3905 )
3907 )
3906 if bmr == hexremoterev
3908 if bmr == hexremoterev
3907 ]
3909 ]
3908
3910
3909 return sorted(bms)
3911 return sorted(bms)
3910
3912
3911 if fm.isplain():
3913 if fm.isplain():
3912 if bookmarks:
3914 if bookmarks:
3913 output.extend(getbms())
3915 output.extend(getbms())
3914 elif default and not ui.quiet:
3916 elif default and not ui.quiet:
3915 # multiple bookmarks for a single parent separated by '/'
3917 # multiple bookmarks for a single parent separated by '/'
3916 bm = b'/'.join(getbms())
3918 bm = b'/'.join(getbms())
3917 if bm:
3919 if bm:
3918 output.append(bm)
3920 output.append(bm)
3919 else:
3921 else:
3920 fm.data(node=hex(remoterev))
3922 fm.data(node=hex(remoterev))
3921 if bookmarks or b'bookmarks' in fm.datahint():
3923 if bookmarks or b'bookmarks' in fm.datahint():
3922 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3924 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3923 else:
3925 else:
3924 if rev:
3926 if rev:
3925 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3927 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3926 ctx = scmutil.revsingle(repo, rev, None)
3928 ctx = scmutil.revsingle(repo, rev, None)
3927
3929
3928 if ctx.rev() is None:
3930 if ctx.rev() is None:
3929 ctx = repo[None]
3931 ctx = repo[None]
3930 parents = ctx.parents()
3932 parents = ctx.parents()
3931 taglist = []
3933 taglist = []
3932 for p in parents:
3934 for p in parents:
3933 taglist.extend(p.tags())
3935 taglist.extend(p.tags())
3934
3936
3935 dirty = b""
3937 dirty = b""
3936 if ctx.dirty(missing=True, merge=False, branch=False):
3938 if ctx.dirty(missing=True, merge=False, branch=False):
3937 dirty = b'+'
3939 dirty = b'+'
3938 fm.data(dirty=dirty)
3940 fm.data(dirty=dirty)
3939
3941
3940 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3942 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3941 if default or id:
3943 if default or id:
3942 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3944 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3943 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3945 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3944
3946
3945 if num:
3947 if num:
3946 numoutput = [b"%d" % p.rev() for p in parents]
3948 numoutput = [b"%d" % p.rev() for p in parents]
3947 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3949 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3948
3950
3949 fm.data(
3951 fm.data(
3950 parents=fm.formatlist(
3952 parents=fm.formatlist(
3951 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3953 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3952 )
3954 )
3953 )
3955 )
3954 else:
3956 else:
3955 hexoutput = fm.hexfunc(ctx.node())
3957 hexoutput = fm.hexfunc(ctx.node())
3956 if default or id:
3958 if default or id:
3957 output = [hexoutput]
3959 output = [hexoutput]
3958 fm.data(id=hexoutput)
3960 fm.data(id=hexoutput)
3959
3961
3960 if num:
3962 if num:
3961 output.append(pycompat.bytestr(ctx.rev()))
3963 output.append(pycompat.bytestr(ctx.rev()))
3962 taglist = ctx.tags()
3964 taglist = ctx.tags()
3963
3965
3964 if default and not ui.quiet:
3966 if default and not ui.quiet:
3965 b = ctx.branch()
3967 b = ctx.branch()
3966 if b != b'default':
3968 if b != b'default':
3967 output.append(b"(%s)" % b)
3969 output.append(b"(%s)" % b)
3968
3970
3969 # multiple tags for a single parent separated by '/'
3971 # multiple tags for a single parent separated by '/'
3970 t = b'/'.join(taglist)
3972 t = b'/'.join(taglist)
3971 if t:
3973 if t:
3972 output.append(t)
3974 output.append(t)
3973
3975
3974 # multiple bookmarks for a single parent separated by '/'
3976 # multiple bookmarks for a single parent separated by '/'
3975 bm = b'/'.join(ctx.bookmarks())
3977 bm = b'/'.join(ctx.bookmarks())
3976 if bm:
3978 if bm:
3977 output.append(bm)
3979 output.append(bm)
3978 else:
3980 else:
3979 if branch:
3981 if branch:
3980 output.append(ctx.branch())
3982 output.append(ctx.branch())
3981
3983
3982 if tags:
3984 if tags:
3983 output.extend(taglist)
3985 output.extend(taglist)
3984
3986
3985 if bookmarks:
3987 if bookmarks:
3986 output.extend(ctx.bookmarks())
3988 output.extend(ctx.bookmarks())
3987
3989
3988 fm.data(node=ctx.hex())
3990 fm.data(node=ctx.hex())
3989 fm.data(branch=ctx.branch())
3991 fm.data(branch=ctx.branch())
3990 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3992 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3991 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3993 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3992 fm.context(ctx=ctx)
3994 fm.context(ctx=ctx)
3993
3995
3994 fm.plain(b"%s\n" % b' '.join(output))
3996 fm.plain(b"%s\n" % b' '.join(output))
3995 fm.end()
3997 fm.end()
3996
3998
3997
3999
3998 @command(
4000 @command(
3999 b'import|patch',
4001 b'import|patch',
4000 [
4002 [
4001 (
4003 (
4002 b'p',
4004 b'p',
4003 b'strip',
4005 b'strip',
4004 1,
4006 1,
4005 _(
4007 _(
4006 b'directory strip option for patch. This has the same '
4008 b'directory strip option for patch. This has the same '
4007 b'meaning as the corresponding patch option'
4009 b'meaning as the corresponding patch option'
4008 ),
4010 ),
4009 _(b'NUM'),
4011 _(b'NUM'),
4010 ),
4012 ),
4011 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4013 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4012 (b'', b'secret', None, _(b'use the secret phase for committing')),
4014 (b'', b'secret', None, _(b'use the secret phase for committing')),
4013 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4015 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4014 (
4016 (
4015 b'f',
4017 b'f',
4016 b'force',
4018 b'force',
4017 None,
4019 None,
4018 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4020 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4019 ),
4021 ),
4020 (
4022 (
4021 b'',
4023 b'',
4022 b'no-commit',
4024 b'no-commit',
4023 None,
4025 None,
4024 _(b"don't commit, just update the working directory"),
4026 _(b"don't commit, just update the working directory"),
4025 ),
4027 ),
4026 (
4028 (
4027 b'',
4029 b'',
4028 b'bypass',
4030 b'bypass',
4029 None,
4031 None,
4030 _(b"apply patch without touching the working directory"),
4032 _(b"apply patch without touching the working directory"),
4031 ),
4033 ),
4032 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4034 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4033 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4035 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4034 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4036 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4035 (
4037 (
4036 b'',
4038 b'',
4037 b'import-branch',
4039 b'import-branch',
4038 None,
4040 None,
4039 _(b'use any branch information in patch (implied by --exact)'),
4041 _(b'use any branch information in patch (implied by --exact)'),
4040 ),
4042 ),
4041 ]
4043 ]
4042 + commitopts
4044 + commitopts
4043 + commitopts2
4045 + commitopts2
4044 + similarityopts,
4046 + similarityopts,
4045 _(b'[OPTION]... PATCH...'),
4047 _(b'[OPTION]... PATCH...'),
4046 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4048 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4047 )
4049 )
4048 def import_(ui, repo, patch1=None, *patches, **opts):
4050 def import_(ui, repo, patch1=None, *patches, **opts):
4049 """import an ordered set of patches
4051 """import an ordered set of patches
4050
4052
4051 Import a list of patches and commit them individually (unless
4053 Import a list of patches and commit them individually (unless
4052 --no-commit is specified).
4054 --no-commit is specified).
4053
4055
4054 To read a patch from standard input (stdin), use "-" as the patch
4056 To read a patch from standard input (stdin), use "-" as the patch
4055 name. If a URL is specified, the patch will be downloaded from
4057 name. If a URL is specified, the patch will be downloaded from
4056 there.
4058 there.
4057
4059
4058 Import first applies changes to the working directory (unless
4060 Import first applies changes to the working directory (unless
4059 --bypass is specified), import will abort if there are outstanding
4061 --bypass is specified), import will abort if there are outstanding
4060 changes.
4062 changes.
4061
4063
4062 Use --bypass to apply and commit patches directly to the
4064 Use --bypass to apply and commit patches directly to the
4063 repository, without affecting the working directory. Without
4065 repository, without affecting the working directory. Without
4064 --exact, patches will be applied on top of the working directory
4066 --exact, patches will be applied on top of the working directory
4065 parent revision.
4067 parent revision.
4066
4068
4067 You can import a patch straight from a mail message. Even patches
4069 You can import a patch straight from a mail message. Even patches
4068 as attachments work (to use the body part, it must have type
4070 as attachments work (to use the body part, it must have type
4069 text/plain or text/x-patch). From and Subject headers of email
4071 text/plain or text/x-patch). From and Subject headers of email
4070 message are used as default committer and commit message. All
4072 message are used as default committer and commit message. All
4071 text/plain body parts before first diff are added to the commit
4073 text/plain body parts before first diff are added to the commit
4072 message.
4074 message.
4073
4075
4074 If the imported patch was generated by :hg:`export`, user and
4076 If the imported patch was generated by :hg:`export`, user and
4075 description from patch override values from message headers and
4077 description from patch override values from message headers and
4076 body. Values given on command line with -m/--message and -u/--user
4078 body. Values given on command line with -m/--message and -u/--user
4077 override these.
4079 override these.
4078
4080
4079 If --exact is specified, import will set the working directory to
4081 If --exact is specified, import will set the working directory to
4080 the parent of each patch before applying it, and will abort if the
4082 the parent of each patch before applying it, and will abort if the
4081 resulting changeset has a different ID than the one recorded in
4083 resulting changeset has a different ID than the one recorded in
4082 the patch. This will guard against various ways that portable
4084 the patch. This will guard against various ways that portable
4083 patch formats and mail systems might fail to transfer Mercurial
4085 patch formats and mail systems might fail to transfer Mercurial
4084 data or metadata. See :hg:`bundle` for lossless transmission.
4086 data or metadata. See :hg:`bundle` for lossless transmission.
4085
4087
4086 Use --partial to ensure a changeset will be created from the patch
4088 Use --partial to ensure a changeset will be created from the patch
4087 even if some hunks fail to apply. Hunks that fail to apply will be
4089 even if some hunks fail to apply. Hunks that fail to apply will be
4088 written to a <target-file>.rej file. Conflicts can then be resolved
4090 written to a <target-file>.rej file. Conflicts can then be resolved
4089 by hand before :hg:`commit --amend` is run to update the created
4091 by hand before :hg:`commit --amend` is run to update the created
4090 changeset. This flag exists to let people import patches that
4092 changeset. This flag exists to let people import patches that
4091 partially apply without losing the associated metadata (author,
4093 partially apply without losing the associated metadata (author,
4092 date, description, ...).
4094 date, description, ...).
4093
4095
4094 .. note::
4096 .. note::
4095
4097
4096 When no hunks apply cleanly, :hg:`import --partial` will create
4098 When no hunks apply cleanly, :hg:`import --partial` will create
4097 an empty changeset, importing only the patch metadata.
4099 an empty changeset, importing only the patch metadata.
4098
4100
4099 With -s/--similarity, hg will attempt to discover renames and
4101 With -s/--similarity, hg will attempt to discover renames and
4100 copies in the patch in the same way as :hg:`addremove`.
4102 copies in the patch in the same way as :hg:`addremove`.
4101
4103
4102 It is possible to use external patch programs to perform the patch
4104 It is possible to use external patch programs to perform the patch
4103 by setting the ``ui.patch`` configuration option. For the default
4105 by setting the ``ui.patch`` configuration option. For the default
4104 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4106 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4105 See :hg:`help config` for more information about configuration
4107 See :hg:`help config` for more information about configuration
4106 files and how to use these options.
4108 files and how to use these options.
4107
4109
4108 See :hg:`help dates` for a list of formats valid for -d/--date.
4110 See :hg:`help dates` for a list of formats valid for -d/--date.
4109
4111
4110 .. container:: verbose
4112 .. container:: verbose
4111
4113
4112 Examples:
4114 Examples:
4113
4115
4114 - import a traditional patch from a website and detect renames::
4116 - import a traditional patch from a website and detect renames::
4115
4117
4116 hg import -s 80 http://example.com/bugfix.patch
4118 hg import -s 80 http://example.com/bugfix.patch
4117
4119
4118 - import a changeset from an hgweb server::
4120 - import a changeset from an hgweb server::
4119
4121
4120 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4122 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4121
4123
4122 - import all the patches in an Unix-style mbox::
4124 - import all the patches in an Unix-style mbox::
4123
4125
4124 hg import incoming-patches.mbox
4126 hg import incoming-patches.mbox
4125
4127
4126 - import patches from stdin::
4128 - import patches from stdin::
4127
4129
4128 hg import -
4130 hg import -
4129
4131
4130 - attempt to exactly restore an exported changeset (not always
4132 - attempt to exactly restore an exported changeset (not always
4131 possible)::
4133 possible)::
4132
4134
4133 hg import --exact proposed-fix.patch
4135 hg import --exact proposed-fix.patch
4134
4136
4135 - use an external tool to apply a patch which is too fuzzy for
4137 - use an external tool to apply a patch which is too fuzzy for
4136 the default internal tool.
4138 the default internal tool.
4137
4139
4138 hg import --config ui.patch="patch --merge" fuzzy.patch
4140 hg import --config ui.patch="patch --merge" fuzzy.patch
4139
4141
4140 - change the default fuzzing from 2 to a less strict 7
4142 - change the default fuzzing from 2 to a less strict 7
4141
4143
4142 hg import --config ui.fuzz=7 fuzz.patch
4144 hg import --config ui.fuzz=7 fuzz.patch
4143
4145
4144 Returns 0 on success, 1 on partial success (see --partial).
4146 Returns 0 on success, 1 on partial success (see --partial).
4145 """
4147 """
4146
4148
4147 opts = pycompat.byteskwargs(opts)
4149 opts = pycompat.byteskwargs(opts)
4148 if not patch1:
4150 if not patch1:
4149 raise error.Abort(_(b'need at least one patch to import'))
4151 raise error.Abort(_(b'need at least one patch to import'))
4150
4152
4151 patches = (patch1,) + patches
4153 patches = (patch1,) + patches
4152
4154
4153 date = opts.get(b'date')
4155 date = opts.get(b'date')
4154 if date:
4156 if date:
4155 opts[b'date'] = dateutil.parsedate(date)
4157 opts[b'date'] = dateutil.parsedate(date)
4156
4158
4157 exact = opts.get(b'exact')
4159 exact = opts.get(b'exact')
4158 update = not opts.get(b'bypass')
4160 update = not opts.get(b'bypass')
4159 if not update and opts.get(b'no_commit'):
4161 if not update and opts.get(b'no_commit'):
4160 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4162 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4161 if opts.get(b'secret') and opts.get(b'no_commit'):
4163 if opts.get(b'secret') and opts.get(b'no_commit'):
4162 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4164 raise error.Abort(_(b'cannot use --no-commit with --secret'))
4163 try:
4165 try:
4164 sim = float(opts.get(b'similarity') or 0)
4166 sim = float(opts.get(b'similarity') or 0)
4165 except ValueError:
4167 except ValueError:
4166 raise error.Abort(_(b'similarity must be a number'))
4168 raise error.Abort(_(b'similarity must be a number'))
4167 if sim < 0 or sim > 100:
4169 if sim < 0 or sim > 100:
4168 raise error.Abort(_(b'similarity must be between 0 and 100'))
4170 raise error.Abort(_(b'similarity must be between 0 and 100'))
4169 if sim and not update:
4171 if sim and not update:
4170 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4172 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4171 if exact:
4173 if exact:
4172 if opts.get(b'edit'):
4174 if opts.get(b'edit'):
4173 raise error.Abort(_(b'cannot use --exact with --edit'))
4175 raise error.Abort(_(b'cannot use --exact with --edit'))
4174 if opts.get(b'prefix'):
4176 if opts.get(b'prefix'):
4175 raise error.Abort(_(b'cannot use --exact with --prefix'))
4177 raise error.Abort(_(b'cannot use --exact with --prefix'))
4176
4178
4177 base = opts[b"base"]
4179 base = opts[b"base"]
4178 msgs = []
4180 msgs = []
4179 ret = 0
4181 ret = 0
4180
4182
4181 with repo.wlock():
4183 with repo.wlock():
4182 if update:
4184 if update:
4183 cmdutil.checkunfinished(repo)
4185 cmdutil.checkunfinished(repo)
4184 if exact or not opts.get(b'force'):
4186 if exact or not opts.get(b'force'):
4185 cmdutil.bailifchanged(repo)
4187 cmdutil.bailifchanged(repo)
4186
4188
4187 if not opts.get(b'no_commit'):
4189 if not opts.get(b'no_commit'):
4188 lock = repo.lock
4190 lock = repo.lock
4189 tr = lambda: repo.transaction(b'import')
4191 tr = lambda: repo.transaction(b'import')
4190 dsguard = util.nullcontextmanager
4192 dsguard = util.nullcontextmanager
4191 else:
4193 else:
4192 lock = util.nullcontextmanager
4194 lock = util.nullcontextmanager
4193 tr = util.nullcontextmanager
4195 tr = util.nullcontextmanager
4194 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4196 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4195 with lock(), tr(), dsguard():
4197 with lock(), tr(), dsguard():
4196 parents = repo[None].parents()
4198 parents = repo[None].parents()
4197 for patchurl in patches:
4199 for patchurl in patches:
4198 if patchurl == b'-':
4200 if patchurl == b'-':
4199 ui.status(_(b'applying patch from stdin\n'))
4201 ui.status(_(b'applying patch from stdin\n'))
4200 patchfile = ui.fin
4202 patchfile = ui.fin
4201 patchurl = b'stdin' # for error message
4203 patchurl = b'stdin' # for error message
4202 else:
4204 else:
4203 patchurl = os.path.join(base, patchurl)
4205 patchurl = os.path.join(base, patchurl)
4204 ui.status(_(b'applying %s\n') % patchurl)
4206 ui.status(_(b'applying %s\n') % patchurl)
4205 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4207 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4206
4208
4207 haspatch = False
4209 haspatch = False
4208 for hunk in patch.split(patchfile):
4210 for hunk in patch.split(patchfile):
4209 with patch.extract(ui, hunk) as patchdata:
4211 with patch.extract(ui, hunk) as patchdata:
4210 msg, node, rej = cmdutil.tryimportone(
4212 msg, node, rej = cmdutil.tryimportone(
4211 ui, repo, patchdata, parents, opts, msgs, hg.clean
4213 ui, repo, patchdata, parents, opts, msgs, hg.clean
4212 )
4214 )
4213 if msg:
4215 if msg:
4214 haspatch = True
4216 haspatch = True
4215 ui.note(msg + b'\n')
4217 ui.note(msg + b'\n')
4216 if update or exact:
4218 if update or exact:
4217 parents = repo[None].parents()
4219 parents = repo[None].parents()
4218 else:
4220 else:
4219 parents = [repo[node]]
4221 parents = [repo[node]]
4220 if rej:
4222 if rej:
4221 ui.write_err(_(b"patch applied partially\n"))
4223 ui.write_err(_(b"patch applied partially\n"))
4222 ui.write_err(
4224 ui.write_err(
4223 _(
4225 _(
4224 b"(fix the .rej files and run "
4226 b"(fix the .rej files and run "
4225 b"`hg commit --amend`)\n"
4227 b"`hg commit --amend`)\n"
4226 )
4228 )
4227 )
4229 )
4228 ret = 1
4230 ret = 1
4229 break
4231 break
4230
4232
4231 if not haspatch:
4233 if not haspatch:
4232 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4234 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4233
4235
4234 if msgs:
4236 if msgs:
4235 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4237 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4236 return ret
4238 return ret
4237
4239
4238
4240
4239 @command(
4241 @command(
4240 b'incoming|in',
4242 b'incoming|in',
4241 [
4243 [
4242 (
4244 (
4243 b'f',
4245 b'f',
4244 b'force',
4246 b'force',
4245 None,
4247 None,
4246 _(b'run even if remote repository is unrelated'),
4248 _(b'run even if remote repository is unrelated'),
4247 ),
4249 ),
4248 (b'n', b'newest-first', None, _(b'show newest record first')),
4250 (b'n', b'newest-first', None, _(b'show newest record first')),
4249 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4251 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4250 (
4252 (
4251 b'r',
4253 b'r',
4252 b'rev',
4254 b'rev',
4253 [],
4255 [],
4254 _(b'a remote changeset intended to be added'),
4256 _(b'a remote changeset intended to be added'),
4255 _(b'REV'),
4257 _(b'REV'),
4256 ),
4258 ),
4257 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4259 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4258 (
4260 (
4259 b'b',
4261 b'b',
4260 b'branch',
4262 b'branch',
4261 [],
4263 [],
4262 _(b'a specific branch you would like to pull'),
4264 _(b'a specific branch you would like to pull'),
4263 _(b'BRANCH'),
4265 _(b'BRANCH'),
4264 ),
4266 ),
4265 ]
4267 ]
4266 + logopts
4268 + logopts
4267 + remoteopts
4269 + remoteopts
4268 + subrepoopts,
4270 + subrepoopts,
4269 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4271 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4270 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4272 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4271 )
4273 )
4272 def incoming(ui, repo, source=b"default", **opts):
4274 def incoming(ui, repo, source=b"default", **opts):
4273 """show new changesets found in source
4275 """show new changesets found in source
4274
4276
4275 Show new changesets found in the specified path/URL or the default
4277 Show new changesets found in the specified path/URL or the default
4276 pull location. These are the changesets that would have been pulled
4278 pull location. These are the changesets that would have been pulled
4277 by :hg:`pull` at the time you issued this command.
4279 by :hg:`pull` at the time you issued this command.
4278
4280
4279 See pull for valid source format details.
4281 See pull for valid source format details.
4280
4282
4281 .. container:: verbose
4283 .. container:: verbose
4282
4284
4283 With -B/--bookmarks, the result of bookmark comparison between
4285 With -B/--bookmarks, the result of bookmark comparison between
4284 local and remote repositories is displayed. With -v/--verbose,
4286 local and remote repositories is displayed. With -v/--verbose,
4285 status is also displayed for each bookmark like below::
4287 status is also displayed for each bookmark like below::
4286
4288
4287 BM1 01234567890a added
4289 BM1 01234567890a added
4288 BM2 1234567890ab advanced
4290 BM2 1234567890ab advanced
4289 BM3 234567890abc diverged
4291 BM3 234567890abc diverged
4290 BM4 34567890abcd changed
4292 BM4 34567890abcd changed
4291
4293
4292 The action taken locally when pulling depends on the
4294 The action taken locally when pulling depends on the
4293 status of each bookmark:
4295 status of each bookmark:
4294
4296
4295 :``added``: pull will create it
4297 :``added``: pull will create it
4296 :``advanced``: pull will update it
4298 :``advanced``: pull will update it
4297 :``diverged``: pull will create a divergent bookmark
4299 :``diverged``: pull will create a divergent bookmark
4298 :``changed``: result depends on remote changesets
4300 :``changed``: result depends on remote changesets
4299
4301
4300 From the point of view of pulling behavior, bookmark
4302 From the point of view of pulling behavior, bookmark
4301 existing only in the remote repository are treated as ``added``,
4303 existing only in the remote repository are treated as ``added``,
4302 even if it is in fact locally deleted.
4304 even if it is in fact locally deleted.
4303
4305
4304 .. container:: verbose
4306 .. container:: verbose
4305
4307
4306 For remote repository, using --bundle avoids downloading the
4308 For remote repository, using --bundle avoids downloading the
4307 changesets twice if the incoming is followed by a pull.
4309 changesets twice if the incoming is followed by a pull.
4308
4310
4309 Examples:
4311 Examples:
4310
4312
4311 - show incoming changes with patches and full description::
4313 - show incoming changes with patches and full description::
4312
4314
4313 hg incoming -vp
4315 hg incoming -vp
4314
4316
4315 - show incoming changes excluding merges, store a bundle::
4317 - show incoming changes excluding merges, store a bundle::
4316
4318
4317 hg in -vpM --bundle incoming.hg
4319 hg in -vpM --bundle incoming.hg
4318 hg pull incoming.hg
4320 hg pull incoming.hg
4319
4321
4320 - briefly list changes inside a bundle::
4322 - briefly list changes inside a bundle::
4321
4323
4322 hg in changes.hg -T "{desc|firstline}\\n"
4324 hg in changes.hg -T "{desc|firstline}\\n"
4323
4325
4324 Returns 0 if there are incoming changes, 1 otherwise.
4326 Returns 0 if there are incoming changes, 1 otherwise.
4325 """
4327 """
4326 opts = pycompat.byteskwargs(opts)
4328 opts = pycompat.byteskwargs(opts)
4327 if opts.get(b'graph'):
4329 if opts.get(b'graph'):
4328 logcmdutil.checkunsupportedgraphflags([], opts)
4330 logcmdutil.checkunsupportedgraphflags([], opts)
4329
4331
4330 def display(other, chlist, displayer):
4332 def display(other, chlist, displayer):
4331 revdag = logcmdutil.graphrevs(other, chlist, opts)
4333 revdag = logcmdutil.graphrevs(other, chlist, opts)
4332 logcmdutil.displaygraph(
4334 logcmdutil.displaygraph(
4333 ui, repo, revdag, displayer, graphmod.asciiedges
4335 ui, repo, revdag, displayer, graphmod.asciiedges
4334 )
4336 )
4335
4337
4336 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4338 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4337 return 0
4339 return 0
4338
4340
4339 if opts.get(b'bundle') and opts.get(b'subrepos'):
4341 if opts.get(b'bundle') and opts.get(b'subrepos'):
4340 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4342 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4341
4343
4342 if opts.get(b'bookmarks'):
4344 if opts.get(b'bookmarks'):
4343 source, branches = hg.parseurl(
4345 source, branches = hg.parseurl(
4344 ui.expandpath(source), opts.get(b'branch')
4346 ui.expandpath(source), opts.get(b'branch')
4345 )
4347 )
4346 other = hg.peer(repo, opts, source)
4348 other = hg.peer(repo, opts, source)
4347 if b'bookmarks' not in other.listkeys(b'namespaces'):
4349 if b'bookmarks' not in other.listkeys(b'namespaces'):
4348 ui.warn(_(b"remote doesn't support bookmarks\n"))
4350 ui.warn(_(b"remote doesn't support bookmarks\n"))
4349 return 0
4351 return 0
4350 ui.pager(b'incoming')
4352 ui.pager(b'incoming')
4351 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4353 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4352 return bookmarks.incoming(ui, repo, other)
4354 return bookmarks.incoming(ui, repo, other)
4353
4355
4354 repo._subtoppath = ui.expandpath(source)
4356 repo._subtoppath = ui.expandpath(source)
4355 try:
4357 try:
4356 return hg.incoming(ui, repo, source, opts)
4358 return hg.incoming(ui, repo, source, opts)
4357 finally:
4359 finally:
4358 del repo._subtoppath
4360 del repo._subtoppath
4359
4361
4360
4362
4361 @command(
4363 @command(
4362 b'init',
4364 b'init',
4363 remoteopts,
4365 remoteopts,
4364 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4366 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4365 helpcategory=command.CATEGORY_REPO_CREATION,
4367 helpcategory=command.CATEGORY_REPO_CREATION,
4366 helpbasic=True,
4368 helpbasic=True,
4367 norepo=True,
4369 norepo=True,
4368 )
4370 )
4369 def init(ui, dest=b".", **opts):
4371 def init(ui, dest=b".", **opts):
4370 """create a new repository in the given directory
4372 """create a new repository in the given directory
4371
4373
4372 Initialize a new repository in the given directory. If the given
4374 Initialize a new repository in the given directory. If the given
4373 directory does not exist, it will be created.
4375 directory does not exist, it will be created.
4374
4376
4375 If no directory is given, the current directory is used.
4377 If no directory is given, the current directory is used.
4376
4378
4377 It is possible to specify an ``ssh://`` URL as the destination.
4379 It is possible to specify an ``ssh://`` URL as the destination.
4378 See :hg:`help urls` for more information.
4380 See :hg:`help urls` for more information.
4379
4381
4380 Returns 0 on success.
4382 Returns 0 on success.
4381 """
4383 """
4382 opts = pycompat.byteskwargs(opts)
4384 opts = pycompat.byteskwargs(opts)
4383 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4385 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4384
4386
4385
4387
4386 @command(
4388 @command(
4387 b'locate',
4389 b'locate',
4388 [
4390 [
4389 (
4391 (
4390 b'r',
4392 b'r',
4391 b'rev',
4393 b'rev',
4392 b'',
4394 b'',
4393 _(b'search the repository as it is in REV'),
4395 _(b'search the repository as it is in REV'),
4394 _(b'REV'),
4396 _(b'REV'),
4395 ),
4397 ),
4396 (
4398 (
4397 b'0',
4399 b'0',
4398 b'print0',
4400 b'print0',
4399 None,
4401 None,
4400 _(b'end filenames with NUL, for use with xargs'),
4402 _(b'end filenames with NUL, for use with xargs'),
4401 ),
4403 ),
4402 (
4404 (
4403 b'f',
4405 b'f',
4404 b'fullpath',
4406 b'fullpath',
4405 None,
4407 None,
4406 _(b'print complete paths from the filesystem root'),
4408 _(b'print complete paths from the filesystem root'),
4407 ),
4409 ),
4408 ]
4410 ]
4409 + walkopts,
4411 + walkopts,
4410 _(b'[OPTION]... [PATTERN]...'),
4412 _(b'[OPTION]... [PATTERN]...'),
4411 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4413 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4412 )
4414 )
4413 def locate(ui, repo, *pats, **opts):
4415 def locate(ui, repo, *pats, **opts):
4414 """locate files matching specific patterns (DEPRECATED)
4416 """locate files matching specific patterns (DEPRECATED)
4415
4417
4416 Print files under Mercurial control in the working directory whose
4418 Print files under Mercurial control in the working directory whose
4417 names match the given patterns.
4419 names match the given patterns.
4418
4420
4419 By default, this command searches all directories in the working
4421 By default, this command searches all directories in the working
4420 directory. To search just the current directory and its
4422 directory. To search just the current directory and its
4421 subdirectories, use "--include .".
4423 subdirectories, use "--include .".
4422
4424
4423 If no patterns are given to match, this command prints the names
4425 If no patterns are given to match, this command prints the names
4424 of all files under Mercurial control in the working directory.
4426 of all files under Mercurial control in the working directory.
4425
4427
4426 If you want to feed the output of this command into the "xargs"
4428 If you want to feed the output of this command into the "xargs"
4427 command, use the -0 option to both this command and "xargs". This
4429 command, use the -0 option to both this command and "xargs". This
4428 will avoid the problem of "xargs" treating single filenames that
4430 will avoid the problem of "xargs" treating single filenames that
4429 contain whitespace as multiple filenames.
4431 contain whitespace as multiple filenames.
4430
4432
4431 See :hg:`help files` for a more versatile command.
4433 See :hg:`help files` for a more versatile command.
4432
4434
4433 Returns 0 if a match is found, 1 otherwise.
4435 Returns 0 if a match is found, 1 otherwise.
4434 """
4436 """
4435 opts = pycompat.byteskwargs(opts)
4437 opts = pycompat.byteskwargs(opts)
4436 if opts.get(b'print0'):
4438 if opts.get(b'print0'):
4437 end = b'\0'
4439 end = b'\0'
4438 else:
4440 else:
4439 end = b'\n'
4441 end = b'\n'
4440 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4442 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4441
4443
4442 ret = 1
4444 ret = 1
4443 m = scmutil.match(
4445 m = scmutil.match(
4444 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4446 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4445 )
4447 )
4446
4448
4447 ui.pager(b'locate')
4449 ui.pager(b'locate')
4448 if ctx.rev() is None:
4450 if ctx.rev() is None:
4449 # When run on the working copy, "locate" includes removed files, so
4451 # When run on the working copy, "locate" includes removed files, so
4450 # we get the list of files from the dirstate.
4452 # we get the list of files from the dirstate.
4451 filesgen = sorted(repo.dirstate.matches(m))
4453 filesgen = sorted(repo.dirstate.matches(m))
4452 else:
4454 else:
4453 filesgen = ctx.matches(m)
4455 filesgen = ctx.matches(m)
4454 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4456 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4455 for abs in filesgen:
4457 for abs in filesgen:
4456 if opts.get(b'fullpath'):
4458 if opts.get(b'fullpath'):
4457 ui.write(repo.wjoin(abs), end)
4459 ui.write(repo.wjoin(abs), end)
4458 else:
4460 else:
4459 ui.write(uipathfn(abs), end)
4461 ui.write(uipathfn(abs), end)
4460 ret = 0
4462 ret = 0
4461
4463
4462 return ret
4464 return ret
4463
4465
4464
4466
4465 @command(
4467 @command(
4466 b'log|history',
4468 b'log|history',
4467 [
4469 [
4468 (
4470 (
4469 b'f',
4471 b'f',
4470 b'follow',
4472 b'follow',
4471 None,
4473 None,
4472 _(
4474 _(
4473 b'follow changeset history, or file history across copies and renames'
4475 b'follow changeset history, or file history across copies and renames'
4474 ),
4476 ),
4475 ),
4477 ),
4476 (
4478 (
4477 b'',
4479 b'',
4478 b'follow-first',
4480 b'follow-first',
4479 None,
4481 None,
4480 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4482 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4481 ),
4483 ),
4482 (
4484 (
4483 b'd',
4485 b'd',
4484 b'date',
4486 b'date',
4485 b'',
4487 b'',
4486 _(b'show revisions matching date spec'),
4488 _(b'show revisions matching date spec'),
4487 _(b'DATE'),
4489 _(b'DATE'),
4488 ),
4490 ),
4489 (b'C', b'copies', None, _(b'show copied files')),
4491 (b'C', b'copies', None, _(b'show copied files')),
4490 (
4492 (
4491 b'k',
4493 b'k',
4492 b'keyword',
4494 b'keyword',
4493 [],
4495 [],
4494 _(b'do case-insensitive search for a given text'),
4496 _(b'do case-insensitive search for a given text'),
4495 _(b'TEXT'),
4497 _(b'TEXT'),
4496 ),
4498 ),
4497 (
4499 (
4498 b'r',
4500 b'r',
4499 b'rev',
4501 b'rev',
4500 [],
4502 [],
4501 _(b'show the specified revision or revset'),
4503 _(b'show the specified revision or revset'),
4502 _(b'REV'),
4504 _(b'REV'),
4503 ),
4505 ),
4504 (
4506 (
4505 b'L',
4507 b'L',
4506 b'line-range',
4508 b'line-range',
4507 [],
4509 [],
4508 _(b'follow line range of specified file (EXPERIMENTAL)'),
4510 _(b'follow line range of specified file (EXPERIMENTAL)'),
4509 _(b'FILE,RANGE'),
4511 _(b'FILE,RANGE'),
4510 ),
4512 ),
4511 (
4513 (
4512 b'',
4514 b'',
4513 b'removed',
4515 b'removed',
4514 None,
4516 None,
4515 _(b'include revisions where files were removed'),
4517 _(b'include revisions where files were removed'),
4516 ),
4518 ),
4517 (
4519 (
4518 b'm',
4520 b'm',
4519 b'only-merges',
4521 b'only-merges',
4520 None,
4522 None,
4521 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4523 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4522 ),
4524 ),
4523 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4525 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4524 (
4526 (
4525 b'',
4527 b'',
4526 b'only-branch',
4528 b'only-branch',
4527 [],
4529 [],
4528 _(
4530 _(
4529 b'show only changesets within the given named branch (DEPRECATED)'
4531 b'show only changesets within the given named branch (DEPRECATED)'
4530 ),
4532 ),
4531 _(b'BRANCH'),
4533 _(b'BRANCH'),
4532 ),
4534 ),
4533 (
4535 (
4534 b'b',
4536 b'b',
4535 b'branch',
4537 b'branch',
4536 [],
4538 [],
4537 _(b'show changesets within the given named branch'),
4539 _(b'show changesets within the given named branch'),
4538 _(b'BRANCH'),
4540 _(b'BRANCH'),
4539 ),
4541 ),
4540 (
4542 (
4541 b'P',
4543 b'P',
4542 b'prune',
4544 b'prune',
4543 [],
4545 [],
4544 _(b'do not display revision or any of its ancestors'),
4546 _(b'do not display revision or any of its ancestors'),
4545 _(b'REV'),
4547 _(b'REV'),
4546 ),
4548 ),
4547 ]
4549 ]
4548 + logopts
4550 + logopts
4549 + walkopts,
4551 + walkopts,
4550 _(b'[OPTION]... [FILE]'),
4552 _(b'[OPTION]... [FILE]'),
4551 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4553 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4552 helpbasic=True,
4554 helpbasic=True,
4553 inferrepo=True,
4555 inferrepo=True,
4554 intents={INTENT_READONLY},
4556 intents={INTENT_READONLY},
4555 )
4557 )
4556 def log(ui, repo, *pats, **opts):
4558 def log(ui, repo, *pats, **opts):
4557 """show revision history of entire repository or files
4559 """show revision history of entire repository or files
4558
4560
4559 Print the revision history of the specified files or the entire
4561 Print the revision history of the specified files or the entire
4560 project.
4562 project.
4561
4563
4562 If no revision range is specified, the default is ``tip:0`` unless
4564 If no revision range is specified, the default is ``tip:0`` unless
4563 --follow is set, in which case the working directory parent is
4565 --follow is set, in which case the working directory parent is
4564 used as the starting revision.
4566 used as the starting revision.
4565
4567
4566 File history is shown without following rename or copy history of
4568 File history is shown without following rename or copy history of
4567 files. Use -f/--follow with a filename to follow history across
4569 files. Use -f/--follow with a filename to follow history across
4568 renames and copies. --follow without a filename will only show
4570 renames and copies. --follow without a filename will only show
4569 ancestors of the starting revision.
4571 ancestors of the starting revision.
4570
4572
4571 By default this command prints revision number and changeset id,
4573 By default this command prints revision number and changeset id,
4572 tags, non-trivial parents, user, date and time, and a summary for
4574 tags, non-trivial parents, user, date and time, and a summary for
4573 each commit. When the -v/--verbose switch is used, the list of
4575 each commit. When the -v/--verbose switch is used, the list of
4574 changed files and full commit message are shown.
4576 changed files and full commit message are shown.
4575
4577
4576 With --graph the revisions are shown as an ASCII art DAG with the most
4578 With --graph the revisions are shown as an ASCII art DAG with the most
4577 recent changeset at the top.
4579 recent changeset at the top.
4578 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4580 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4579 involved in an unresolved merge conflict, '_' closes a branch,
4581 involved in an unresolved merge conflict, '_' closes a branch,
4580 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4582 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4581 changeset from the lines below is a parent of the 'o' merge on the same
4583 changeset from the lines below is a parent of the 'o' merge on the same
4582 line.
4584 line.
4583 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4585 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4584 of a '|' indicates one or more revisions in a path are omitted.
4586 of a '|' indicates one or more revisions in a path are omitted.
4585
4587
4586 .. container:: verbose
4588 .. container:: verbose
4587
4589
4588 Use -L/--line-range FILE,M:N options to follow the history of lines
4590 Use -L/--line-range FILE,M:N options to follow the history of lines
4589 from M to N in FILE. With -p/--patch only diff hunks affecting
4591 from M to N in FILE. With -p/--patch only diff hunks affecting
4590 specified line range will be shown. This option requires --follow;
4592 specified line range will be shown. This option requires --follow;
4591 it can be specified multiple times. Currently, this option is not
4593 it can be specified multiple times. Currently, this option is not
4592 compatible with --graph. This option is experimental.
4594 compatible with --graph. This option is experimental.
4593
4595
4594 .. note::
4596 .. note::
4595
4597
4596 :hg:`log --patch` may generate unexpected diff output for merge
4598 :hg:`log --patch` may generate unexpected diff output for merge
4597 changesets, as it will only compare the merge changeset against
4599 changesets, as it will only compare the merge changeset against
4598 its first parent. Also, only files different from BOTH parents
4600 its first parent. Also, only files different from BOTH parents
4599 will appear in files:.
4601 will appear in files:.
4600
4602
4601 .. note::
4603 .. note::
4602
4604
4603 For performance reasons, :hg:`log FILE` may omit duplicate changes
4605 For performance reasons, :hg:`log FILE` may omit duplicate changes
4604 made on branches and will not show removals or mode changes. To
4606 made on branches and will not show removals or mode changes. To
4605 see all such changes, use the --removed switch.
4607 see all such changes, use the --removed switch.
4606
4608
4607 .. container:: verbose
4609 .. container:: verbose
4608
4610
4609 .. note::
4611 .. note::
4610
4612
4611 The history resulting from -L/--line-range options depends on diff
4613 The history resulting from -L/--line-range options depends on diff
4612 options; for instance if white-spaces are ignored, respective changes
4614 options; for instance if white-spaces are ignored, respective changes
4613 with only white-spaces in specified line range will not be listed.
4615 with only white-spaces in specified line range will not be listed.
4614
4616
4615 .. container:: verbose
4617 .. container:: verbose
4616
4618
4617 Some examples:
4619 Some examples:
4618
4620
4619 - changesets with full descriptions and file lists::
4621 - changesets with full descriptions and file lists::
4620
4622
4621 hg log -v
4623 hg log -v
4622
4624
4623 - changesets ancestral to the working directory::
4625 - changesets ancestral to the working directory::
4624
4626
4625 hg log -f
4627 hg log -f
4626
4628
4627 - last 10 commits on the current branch::
4629 - last 10 commits on the current branch::
4628
4630
4629 hg log -l 10 -b .
4631 hg log -l 10 -b .
4630
4632
4631 - changesets showing all modifications of a file, including removals::
4633 - changesets showing all modifications of a file, including removals::
4632
4634
4633 hg log --removed file.c
4635 hg log --removed file.c
4634
4636
4635 - all changesets that touch a directory, with diffs, excluding merges::
4637 - all changesets that touch a directory, with diffs, excluding merges::
4636
4638
4637 hg log -Mp lib/
4639 hg log -Mp lib/
4638
4640
4639 - all revision numbers that match a keyword::
4641 - all revision numbers that match a keyword::
4640
4642
4641 hg log -k bug --template "{rev}\\n"
4643 hg log -k bug --template "{rev}\\n"
4642
4644
4643 - the full hash identifier of the working directory parent::
4645 - the full hash identifier of the working directory parent::
4644
4646
4645 hg log -r . --template "{node}\\n"
4647 hg log -r . --template "{node}\\n"
4646
4648
4647 - list available log templates::
4649 - list available log templates::
4648
4650
4649 hg log -T list
4651 hg log -T list
4650
4652
4651 - check if a given changeset is included in a tagged release::
4653 - check if a given changeset is included in a tagged release::
4652
4654
4653 hg log -r "a21ccf and ancestor(1.9)"
4655 hg log -r "a21ccf and ancestor(1.9)"
4654
4656
4655 - find all changesets by some user in a date range::
4657 - find all changesets by some user in a date range::
4656
4658
4657 hg log -k alice -d "may 2008 to jul 2008"
4659 hg log -k alice -d "may 2008 to jul 2008"
4658
4660
4659 - summary of all changesets after the last tag::
4661 - summary of all changesets after the last tag::
4660
4662
4661 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4663 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4662
4664
4663 - changesets touching lines 13 to 23 for file.c::
4665 - changesets touching lines 13 to 23 for file.c::
4664
4666
4665 hg log -L file.c,13:23
4667 hg log -L file.c,13:23
4666
4668
4667 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4669 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4668 main.c with patch::
4670 main.c with patch::
4669
4671
4670 hg log -L file.c,13:23 -L main.c,2:6 -p
4672 hg log -L file.c,13:23 -L main.c,2:6 -p
4671
4673
4672 See :hg:`help dates` for a list of formats valid for -d/--date.
4674 See :hg:`help dates` for a list of formats valid for -d/--date.
4673
4675
4674 See :hg:`help revisions` for more about specifying and ordering
4676 See :hg:`help revisions` for more about specifying and ordering
4675 revisions.
4677 revisions.
4676
4678
4677 See :hg:`help templates` for more about pre-packaged styles and
4679 See :hg:`help templates` for more about pre-packaged styles and
4678 specifying custom templates. The default template used by the log
4680 specifying custom templates. The default template used by the log
4679 command can be customized via the ``ui.logtemplate`` configuration
4681 command can be customized via the ``ui.logtemplate`` configuration
4680 setting.
4682 setting.
4681
4683
4682 Returns 0 on success.
4684 Returns 0 on success.
4683
4685
4684 """
4686 """
4685 opts = pycompat.byteskwargs(opts)
4687 opts = pycompat.byteskwargs(opts)
4686 linerange = opts.get(b'line_range')
4688 linerange = opts.get(b'line_range')
4687
4689
4688 if linerange and not opts.get(b'follow'):
4690 if linerange and not opts.get(b'follow'):
4689 raise error.Abort(_(b'--line-range requires --follow'))
4691 raise error.Abort(_(b'--line-range requires --follow'))
4690
4692
4691 if linerange and pats:
4693 if linerange and pats:
4692 # TODO: take pats as patterns with no line-range filter
4694 # TODO: take pats as patterns with no line-range filter
4693 raise error.Abort(
4695 raise error.Abort(
4694 _(b'FILE arguments are not compatible with --line-range option')
4696 _(b'FILE arguments are not compatible with --line-range option')
4695 )
4697 )
4696
4698
4697 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4699 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4698 revs, differ = logcmdutil.getrevs(
4700 revs, differ = logcmdutil.getrevs(
4699 repo, logcmdutil.parseopts(ui, pats, opts)
4701 repo, logcmdutil.parseopts(ui, pats, opts)
4700 )
4702 )
4701 if linerange:
4703 if linerange:
4702 # TODO: should follow file history from logcmdutil._initialrevs(),
4704 # TODO: should follow file history from logcmdutil._initialrevs(),
4703 # then filter the result by logcmdutil._makerevset() and --limit
4705 # then filter the result by logcmdutil._makerevset() and --limit
4704 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4706 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4705
4707
4706 getcopies = None
4708 getcopies = None
4707 if opts.get(b'copies'):
4709 if opts.get(b'copies'):
4708 endrev = None
4710 endrev = None
4709 if revs:
4711 if revs:
4710 endrev = revs.max() + 1
4712 endrev = revs.max() + 1
4711 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4713 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4712
4714
4713 ui.pager(b'log')
4715 ui.pager(b'log')
4714 displayer = logcmdutil.changesetdisplayer(
4716 displayer = logcmdutil.changesetdisplayer(
4715 ui, repo, opts, differ, buffered=True
4717 ui, repo, opts, differ, buffered=True
4716 )
4718 )
4717 if opts.get(b'graph'):
4719 if opts.get(b'graph'):
4718 displayfn = logcmdutil.displaygraphrevs
4720 displayfn = logcmdutil.displaygraphrevs
4719 else:
4721 else:
4720 displayfn = logcmdutil.displayrevs
4722 displayfn = logcmdutil.displayrevs
4721 displayfn(ui, repo, revs, displayer, getcopies)
4723 displayfn(ui, repo, revs, displayer, getcopies)
4722
4724
4723
4725
4724 @command(
4726 @command(
4725 b'manifest',
4727 b'manifest',
4726 [
4728 [
4727 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4729 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4728 (b'', b'all', False, _(b"list files from all revisions")),
4730 (b'', b'all', False, _(b"list files from all revisions")),
4729 ]
4731 ]
4730 + formatteropts,
4732 + formatteropts,
4731 _(b'[-r REV]'),
4733 _(b'[-r REV]'),
4732 helpcategory=command.CATEGORY_MAINTENANCE,
4734 helpcategory=command.CATEGORY_MAINTENANCE,
4733 intents={INTENT_READONLY},
4735 intents={INTENT_READONLY},
4734 )
4736 )
4735 def manifest(ui, repo, node=None, rev=None, **opts):
4737 def manifest(ui, repo, node=None, rev=None, **opts):
4736 """output the current or given revision of the project manifest
4738 """output the current or given revision of the project manifest
4737
4739
4738 Print a list of version controlled files for the given revision.
4740 Print a list of version controlled files for the given revision.
4739 If no revision is given, the first parent of the working directory
4741 If no revision is given, the first parent of the working directory
4740 is used, or the null revision if no revision is checked out.
4742 is used, or the null revision if no revision is checked out.
4741
4743
4742 With -v, print file permissions, symlink and executable bits.
4744 With -v, print file permissions, symlink and executable bits.
4743 With --debug, print file revision hashes.
4745 With --debug, print file revision hashes.
4744
4746
4745 If option --all is specified, the list of all files from all revisions
4747 If option --all is specified, the list of all files from all revisions
4746 is printed. This includes deleted and renamed files.
4748 is printed. This includes deleted and renamed files.
4747
4749
4748 Returns 0 on success.
4750 Returns 0 on success.
4749 """
4751 """
4750 opts = pycompat.byteskwargs(opts)
4752 opts = pycompat.byteskwargs(opts)
4751 fm = ui.formatter(b'manifest', opts)
4753 fm = ui.formatter(b'manifest', opts)
4752
4754
4753 if opts.get(b'all'):
4755 if opts.get(b'all'):
4754 if rev or node:
4756 if rev or node:
4755 raise error.Abort(_(b"can't specify a revision with --all"))
4757 raise error.Abort(_(b"can't specify a revision with --all"))
4756
4758
4757 res = set()
4759 res = set()
4758 for rev in repo:
4760 for rev in repo:
4759 ctx = repo[rev]
4761 ctx = repo[rev]
4760 res |= set(ctx.files())
4762 res |= set(ctx.files())
4761
4763
4762 ui.pager(b'manifest')
4764 ui.pager(b'manifest')
4763 for f in sorted(res):
4765 for f in sorted(res):
4764 fm.startitem()
4766 fm.startitem()
4765 fm.write(b"path", b'%s\n', f)
4767 fm.write(b"path", b'%s\n', f)
4766 fm.end()
4768 fm.end()
4767 return
4769 return
4768
4770
4769 if rev and node:
4771 if rev and node:
4770 raise error.Abort(_(b"please specify just one revision"))
4772 raise error.Abort(_(b"please specify just one revision"))
4771
4773
4772 if not node:
4774 if not node:
4773 node = rev
4775 node = rev
4774
4776
4775 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4777 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4776 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4778 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4777 if node:
4779 if node:
4778 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4780 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4779 ctx = scmutil.revsingle(repo, node)
4781 ctx = scmutil.revsingle(repo, node)
4780 mf = ctx.manifest()
4782 mf = ctx.manifest()
4781 ui.pager(b'manifest')
4783 ui.pager(b'manifest')
4782 for f in ctx:
4784 for f in ctx:
4783 fm.startitem()
4785 fm.startitem()
4784 fm.context(ctx=ctx)
4786 fm.context(ctx=ctx)
4785 fl = ctx[f].flags()
4787 fl = ctx[f].flags()
4786 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4788 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4787 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4789 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4788 fm.write(b'path', b'%s\n', f)
4790 fm.write(b'path', b'%s\n', f)
4789 fm.end()
4791 fm.end()
4790
4792
4791
4793
4792 @command(
4794 @command(
4793 b'merge',
4795 b'merge',
4794 [
4796 [
4795 (
4797 (
4796 b'f',
4798 b'f',
4797 b'force',
4799 b'force',
4798 None,
4800 None,
4799 _(b'force a merge including outstanding changes (DEPRECATED)'),
4801 _(b'force a merge including outstanding changes (DEPRECATED)'),
4800 ),
4802 ),
4801 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4803 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4802 (
4804 (
4803 b'P',
4805 b'P',
4804 b'preview',
4806 b'preview',
4805 None,
4807 None,
4806 _(b'review revisions to merge (no merge is performed)'),
4808 _(b'review revisions to merge (no merge is performed)'),
4807 ),
4809 ),
4808 (b'', b'abort', None, _(b'abort the ongoing merge')),
4810 (b'', b'abort', None, _(b'abort the ongoing merge')),
4809 ]
4811 ]
4810 + mergetoolopts,
4812 + mergetoolopts,
4811 _(b'[-P] [[-r] REV]'),
4813 _(b'[-P] [[-r] REV]'),
4812 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4814 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4813 helpbasic=True,
4815 helpbasic=True,
4814 )
4816 )
4815 def merge(ui, repo, node=None, **opts):
4817 def merge(ui, repo, node=None, **opts):
4816 """merge another revision into working directory
4818 """merge another revision into working directory
4817
4819
4818 The current working directory is updated with all changes made in
4820 The current working directory is updated with all changes made in
4819 the requested revision since the last common predecessor revision.
4821 the requested revision since the last common predecessor revision.
4820
4822
4821 Files that changed between either parent are marked as changed for
4823 Files that changed between either parent are marked as changed for
4822 the next commit and a commit must be performed before any further
4824 the next commit and a commit must be performed before any further
4823 updates to the repository are allowed. The next commit will have
4825 updates to the repository are allowed. The next commit will have
4824 two parents.
4826 two parents.
4825
4827
4826 ``--tool`` can be used to specify the merge tool used for file
4828 ``--tool`` can be used to specify the merge tool used for file
4827 merges. It overrides the HGMERGE environment variable and your
4829 merges. It overrides the HGMERGE environment variable and your
4828 configuration files. See :hg:`help merge-tools` for options.
4830 configuration files. See :hg:`help merge-tools` for options.
4829
4831
4830 If no revision is specified, the working directory's parent is a
4832 If no revision is specified, the working directory's parent is a
4831 head revision, and the current branch contains exactly one other
4833 head revision, and the current branch contains exactly one other
4832 head, the other head is merged with by default. Otherwise, an
4834 head, the other head is merged with by default. Otherwise, an
4833 explicit revision with which to merge must be provided.
4835 explicit revision with which to merge must be provided.
4834
4836
4835 See :hg:`help resolve` for information on handling file conflicts.
4837 See :hg:`help resolve` for information on handling file conflicts.
4836
4838
4837 To undo an uncommitted merge, use :hg:`merge --abort` which
4839 To undo an uncommitted merge, use :hg:`merge --abort` which
4838 will check out a clean copy of the original merge parent, losing
4840 will check out a clean copy of the original merge parent, losing
4839 all changes.
4841 all changes.
4840
4842
4841 Returns 0 on success, 1 if there are unresolved files.
4843 Returns 0 on success, 1 if there are unresolved files.
4842 """
4844 """
4843
4845
4844 opts = pycompat.byteskwargs(opts)
4846 opts = pycompat.byteskwargs(opts)
4845 abort = opts.get(b'abort')
4847 abort = opts.get(b'abort')
4846 if abort and repo.dirstate.p2() == nullid:
4848 if abort and repo.dirstate.p2() == nullid:
4847 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4849 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4848 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4850 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4849 if abort:
4851 if abort:
4850 state = cmdutil.getunfinishedstate(repo)
4852 state = cmdutil.getunfinishedstate(repo)
4851 if state and state._opname != b'merge':
4853 if state and state._opname != b'merge':
4852 raise error.Abort(
4854 raise error.Abort(
4853 _(b'cannot abort merge with %s in progress') % (state._opname),
4855 _(b'cannot abort merge with %s in progress') % (state._opname),
4854 hint=state.hint(),
4856 hint=state.hint(),
4855 )
4857 )
4856 if node:
4858 if node:
4857 raise error.Abort(_(b"cannot specify a node with --abort"))
4859 raise error.Abort(_(b"cannot specify a node with --abort"))
4858 return hg.abortmerge(repo.ui, repo)
4860 return hg.abortmerge(repo.ui, repo)
4859
4861
4860 if opts.get(b'rev') and node:
4862 if opts.get(b'rev') and node:
4861 raise error.Abort(_(b"please specify just one revision"))
4863 raise error.Abort(_(b"please specify just one revision"))
4862 if not node:
4864 if not node:
4863 node = opts.get(b'rev')
4865 node = opts.get(b'rev')
4864
4866
4865 if node:
4867 if node:
4866 ctx = scmutil.revsingle(repo, node)
4868 ctx = scmutil.revsingle(repo, node)
4867 else:
4869 else:
4868 if ui.configbool(b'commands', b'merge.require-rev'):
4870 if ui.configbool(b'commands', b'merge.require-rev'):
4869 raise error.Abort(
4871 raise error.Abort(
4870 _(
4872 _(
4871 b'configuration requires specifying revision to merge '
4873 b'configuration requires specifying revision to merge '
4872 b'with'
4874 b'with'
4873 )
4875 )
4874 )
4876 )
4875 ctx = repo[destutil.destmerge(repo)]
4877 ctx = repo[destutil.destmerge(repo)]
4876
4878
4877 if ctx.node() is None:
4879 if ctx.node() is None:
4878 raise error.Abort(_(b'merging with the working copy has no effect'))
4880 raise error.Abort(_(b'merging with the working copy has no effect'))
4879
4881
4880 if opts.get(b'preview'):
4882 if opts.get(b'preview'):
4881 # find nodes that are ancestors of p2 but not of p1
4883 # find nodes that are ancestors of p2 but not of p1
4882 p1 = repo[b'.'].node()
4884 p1 = repo[b'.'].node()
4883 p2 = ctx.node()
4885 p2 = ctx.node()
4884 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4886 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4885
4887
4886 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4888 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4887 for node in nodes:
4889 for node in nodes:
4888 displayer.show(repo[node])
4890 displayer.show(repo[node])
4889 displayer.close()
4891 displayer.close()
4890 return 0
4892 return 0
4891
4893
4892 # ui.forcemerge is an internal variable, do not document
4894 # ui.forcemerge is an internal variable, do not document
4893 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4895 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4894 with ui.configoverride(overrides, b'merge'):
4896 with ui.configoverride(overrides, b'merge'):
4895 force = opts.get(b'force')
4897 force = opts.get(b'force')
4896 labels = [b'working copy', b'merge rev']
4898 labels = [b'working copy', b'merge rev']
4897 return hg.merge(ctx, force=force, labels=labels)
4899 return hg.merge(ctx, force=force, labels=labels)
4898
4900
4899
4901
4900 statemod.addunfinished(
4902 statemod.addunfinished(
4901 b'merge',
4903 b'merge',
4902 fname=None,
4904 fname=None,
4903 clearable=True,
4905 clearable=True,
4904 allowcommit=True,
4906 allowcommit=True,
4905 cmdmsg=_(b'outstanding uncommitted merge'),
4907 cmdmsg=_(b'outstanding uncommitted merge'),
4906 abortfunc=hg.abortmerge,
4908 abortfunc=hg.abortmerge,
4907 statushint=_(
4909 statushint=_(
4908 b'To continue: hg commit\nTo abort: hg merge --abort'
4910 b'To continue: hg commit\nTo abort: hg merge --abort'
4909 ),
4911 ),
4910 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4912 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4911 )
4913 )
4912
4914
4913
4915
4914 @command(
4916 @command(
4915 b'outgoing|out',
4917 b'outgoing|out',
4916 [
4918 [
4917 (
4919 (
4918 b'f',
4920 b'f',
4919 b'force',
4921 b'force',
4920 None,
4922 None,
4921 _(b'run even when the destination is unrelated'),
4923 _(b'run even when the destination is unrelated'),
4922 ),
4924 ),
4923 (
4925 (
4924 b'r',
4926 b'r',
4925 b'rev',
4927 b'rev',
4926 [],
4928 [],
4927 _(b'a changeset intended to be included in the destination'),
4929 _(b'a changeset intended to be included in the destination'),
4928 _(b'REV'),
4930 _(b'REV'),
4929 ),
4931 ),
4930 (b'n', b'newest-first', None, _(b'show newest record first')),
4932 (b'n', b'newest-first', None, _(b'show newest record first')),
4931 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4933 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4932 (
4934 (
4933 b'b',
4935 b'b',
4934 b'branch',
4936 b'branch',
4935 [],
4937 [],
4936 _(b'a specific branch you would like to push'),
4938 _(b'a specific branch you would like to push'),
4937 _(b'BRANCH'),
4939 _(b'BRANCH'),
4938 ),
4940 ),
4939 ]
4941 ]
4940 + logopts
4942 + logopts
4941 + remoteopts
4943 + remoteopts
4942 + subrepoopts,
4944 + subrepoopts,
4943 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4945 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4944 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4946 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4945 )
4947 )
4946 def outgoing(ui, repo, dest=None, **opts):
4948 def outgoing(ui, repo, dest=None, **opts):
4947 """show changesets not found in the destination
4949 """show changesets not found in the destination
4948
4950
4949 Show changesets not found in the specified destination repository
4951 Show changesets not found in the specified destination repository
4950 or the default push location. These are the changesets that would
4952 or the default push location. These are the changesets that would
4951 be pushed if a push was requested.
4953 be pushed if a push was requested.
4952
4954
4953 See pull for details of valid destination formats.
4955 See pull for details of valid destination formats.
4954
4956
4955 .. container:: verbose
4957 .. container:: verbose
4956
4958
4957 With -B/--bookmarks, the result of bookmark comparison between
4959 With -B/--bookmarks, the result of bookmark comparison between
4958 local and remote repositories is displayed. With -v/--verbose,
4960 local and remote repositories is displayed. With -v/--verbose,
4959 status is also displayed for each bookmark like below::
4961 status is also displayed for each bookmark like below::
4960
4962
4961 BM1 01234567890a added
4963 BM1 01234567890a added
4962 BM2 deleted
4964 BM2 deleted
4963 BM3 234567890abc advanced
4965 BM3 234567890abc advanced
4964 BM4 34567890abcd diverged
4966 BM4 34567890abcd diverged
4965 BM5 4567890abcde changed
4967 BM5 4567890abcde changed
4966
4968
4967 The action taken when pushing depends on the
4969 The action taken when pushing depends on the
4968 status of each bookmark:
4970 status of each bookmark:
4969
4971
4970 :``added``: push with ``-B`` will create it
4972 :``added``: push with ``-B`` will create it
4971 :``deleted``: push with ``-B`` will delete it
4973 :``deleted``: push with ``-B`` will delete it
4972 :``advanced``: push will update it
4974 :``advanced``: push will update it
4973 :``diverged``: push with ``-B`` will update it
4975 :``diverged``: push with ``-B`` will update it
4974 :``changed``: push with ``-B`` will update it
4976 :``changed``: push with ``-B`` will update it
4975
4977
4976 From the point of view of pushing behavior, bookmarks
4978 From the point of view of pushing behavior, bookmarks
4977 existing only in the remote repository are treated as
4979 existing only in the remote repository are treated as
4978 ``deleted``, even if it is in fact added remotely.
4980 ``deleted``, even if it is in fact added remotely.
4979
4981
4980 Returns 0 if there are outgoing changes, 1 otherwise.
4982 Returns 0 if there are outgoing changes, 1 otherwise.
4981 """
4983 """
4982 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4984 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4983 # style URLs, so don't overwrite dest.
4985 # style URLs, so don't overwrite dest.
4984 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4986 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4985 if not path:
4987 if not path:
4986 raise error.Abort(
4988 raise error.Abort(
4987 _(b'default repository not configured!'),
4989 _(b'default repository not configured!'),
4988 hint=_(b"see 'hg help config.paths'"),
4990 hint=_(b"see 'hg help config.paths'"),
4989 )
4991 )
4990
4992
4991 opts = pycompat.byteskwargs(opts)
4993 opts = pycompat.byteskwargs(opts)
4992 if opts.get(b'graph'):
4994 if opts.get(b'graph'):
4993 logcmdutil.checkunsupportedgraphflags([], opts)
4995 logcmdutil.checkunsupportedgraphflags([], opts)
4994 o, other = hg._outgoing(ui, repo, dest, opts)
4996 o, other = hg._outgoing(ui, repo, dest, opts)
4995 if not o:
4997 if not o:
4996 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4998 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4997 return
4999 return
4998
5000
4999 revdag = logcmdutil.graphrevs(repo, o, opts)
5001 revdag = logcmdutil.graphrevs(repo, o, opts)
5000 ui.pager(b'outgoing')
5002 ui.pager(b'outgoing')
5001 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5003 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5002 logcmdutil.displaygraph(
5004 logcmdutil.displaygraph(
5003 ui, repo, revdag, displayer, graphmod.asciiedges
5005 ui, repo, revdag, displayer, graphmod.asciiedges
5004 )
5006 )
5005 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5007 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5006 return 0
5008 return 0
5007
5009
5008 if opts.get(b'bookmarks'):
5010 if opts.get(b'bookmarks'):
5009 dest = path.pushloc or path.loc
5011 dest = path.pushloc or path.loc
5010 other = hg.peer(repo, opts, dest)
5012 other = hg.peer(repo, opts, dest)
5011 if b'bookmarks' not in other.listkeys(b'namespaces'):
5013 if b'bookmarks' not in other.listkeys(b'namespaces'):
5012 ui.warn(_(b"remote doesn't support bookmarks\n"))
5014 ui.warn(_(b"remote doesn't support bookmarks\n"))
5013 return 0
5015 return 0
5014 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5016 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5015 ui.pager(b'outgoing')
5017 ui.pager(b'outgoing')
5016 return bookmarks.outgoing(ui, repo, other)
5018 return bookmarks.outgoing(ui, repo, other)
5017
5019
5018 repo._subtoppath = path.pushloc or path.loc
5020 repo._subtoppath = path.pushloc or path.loc
5019 try:
5021 try:
5020 return hg.outgoing(ui, repo, dest, opts)
5022 return hg.outgoing(ui, repo, dest, opts)
5021 finally:
5023 finally:
5022 del repo._subtoppath
5024 del repo._subtoppath
5023
5025
5024
5026
5025 @command(
5027 @command(
5026 b'parents',
5028 b'parents',
5027 [
5029 [
5028 (
5030 (
5029 b'r',
5031 b'r',
5030 b'rev',
5032 b'rev',
5031 b'',
5033 b'',
5032 _(b'show parents of the specified revision'),
5034 _(b'show parents of the specified revision'),
5033 _(b'REV'),
5035 _(b'REV'),
5034 ),
5036 ),
5035 ]
5037 ]
5036 + templateopts,
5038 + templateopts,
5037 _(b'[-r REV] [FILE]'),
5039 _(b'[-r REV] [FILE]'),
5038 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5040 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5039 inferrepo=True,
5041 inferrepo=True,
5040 )
5042 )
5041 def parents(ui, repo, file_=None, **opts):
5043 def parents(ui, repo, file_=None, **opts):
5042 """show the parents of the working directory or revision (DEPRECATED)
5044 """show the parents of the working directory or revision (DEPRECATED)
5043
5045
5044 Print the working directory's parent revisions. If a revision is
5046 Print the working directory's parent revisions. If a revision is
5045 given via -r/--rev, the parent of that revision will be printed.
5047 given via -r/--rev, the parent of that revision will be printed.
5046 If a file argument is given, the revision in which the file was
5048 If a file argument is given, the revision in which the file was
5047 last changed (before the working directory revision or the
5049 last changed (before the working directory revision or the
5048 argument to --rev if given) is printed.
5050 argument to --rev if given) is printed.
5049
5051
5050 This command is equivalent to::
5052 This command is equivalent to::
5051
5053
5052 hg log -r "p1()+p2()" or
5054 hg log -r "p1()+p2()" or
5053 hg log -r "p1(REV)+p2(REV)" or
5055 hg log -r "p1(REV)+p2(REV)" or
5054 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5056 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5055 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5057 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5056
5058
5057 See :hg:`summary` and :hg:`help revsets` for related information.
5059 See :hg:`summary` and :hg:`help revsets` for related information.
5058
5060
5059 Returns 0 on success.
5061 Returns 0 on success.
5060 """
5062 """
5061
5063
5062 opts = pycompat.byteskwargs(opts)
5064 opts = pycompat.byteskwargs(opts)
5063 rev = opts.get(b'rev')
5065 rev = opts.get(b'rev')
5064 if rev:
5066 if rev:
5065 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5067 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5066 ctx = scmutil.revsingle(repo, rev, None)
5068 ctx = scmutil.revsingle(repo, rev, None)
5067
5069
5068 if file_:
5070 if file_:
5069 m = scmutil.match(ctx, (file_,), opts)
5071 m = scmutil.match(ctx, (file_,), opts)
5070 if m.anypats() or len(m.files()) != 1:
5072 if m.anypats() or len(m.files()) != 1:
5071 raise error.Abort(_(b'can only specify an explicit filename'))
5073 raise error.Abort(_(b'can only specify an explicit filename'))
5072 file_ = m.files()[0]
5074 file_ = m.files()[0]
5073 filenodes = []
5075 filenodes = []
5074 for cp in ctx.parents():
5076 for cp in ctx.parents():
5075 if not cp:
5077 if not cp:
5076 continue
5078 continue
5077 try:
5079 try:
5078 filenodes.append(cp.filenode(file_))
5080 filenodes.append(cp.filenode(file_))
5079 except error.LookupError:
5081 except error.LookupError:
5080 pass
5082 pass
5081 if not filenodes:
5083 if not filenodes:
5082 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5084 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5083 p = []
5085 p = []
5084 for fn in filenodes:
5086 for fn in filenodes:
5085 fctx = repo.filectx(file_, fileid=fn)
5087 fctx = repo.filectx(file_, fileid=fn)
5086 p.append(fctx.node())
5088 p.append(fctx.node())
5087 else:
5089 else:
5088 p = [cp.node() for cp in ctx.parents()]
5090 p = [cp.node() for cp in ctx.parents()]
5089
5091
5090 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5092 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5091 for n in p:
5093 for n in p:
5092 if n != nullid:
5094 if n != nullid:
5093 displayer.show(repo[n])
5095 displayer.show(repo[n])
5094 displayer.close()
5096 displayer.close()
5095
5097
5096
5098
5097 @command(
5099 @command(
5098 b'paths',
5100 b'paths',
5099 formatteropts,
5101 formatteropts,
5100 _(b'[NAME]'),
5102 _(b'[NAME]'),
5101 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5103 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5102 optionalrepo=True,
5104 optionalrepo=True,
5103 intents={INTENT_READONLY},
5105 intents={INTENT_READONLY},
5104 )
5106 )
5105 def paths(ui, repo, search=None, **opts):
5107 def paths(ui, repo, search=None, **opts):
5106 """show aliases for remote repositories
5108 """show aliases for remote repositories
5107
5109
5108 Show definition of symbolic path name NAME. If no name is given,
5110 Show definition of symbolic path name NAME. If no name is given,
5109 show definition of all available names.
5111 show definition of all available names.
5110
5112
5111 Option -q/--quiet suppresses all output when searching for NAME
5113 Option -q/--quiet suppresses all output when searching for NAME
5112 and shows only the path names when listing all definitions.
5114 and shows only the path names when listing all definitions.
5113
5115
5114 Path names are defined in the [paths] section of your
5116 Path names are defined in the [paths] section of your
5115 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5117 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5116 repository, ``.hg/hgrc`` is used, too.
5118 repository, ``.hg/hgrc`` is used, too.
5117
5119
5118 The path names ``default`` and ``default-push`` have a special
5120 The path names ``default`` and ``default-push`` have a special
5119 meaning. When performing a push or pull operation, they are used
5121 meaning. When performing a push or pull operation, they are used
5120 as fallbacks if no location is specified on the command-line.
5122 as fallbacks if no location is specified on the command-line.
5121 When ``default-push`` is set, it will be used for push and
5123 When ``default-push`` is set, it will be used for push and
5122 ``default`` will be used for pull; otherwise ``default`` is used
5124 ``default`` will be used for pull; otherwise ``default`` is used
5123 as the fallback for both. When cloning a repository, the clone
5125 as the fallback for both. When cloning a repository, the clone
5124 source is written as ``default`` in ``.hg/hgrc``.
5126 source is written as ``default`` in ``.hg/hgrc``.
5125
5127
5126 .. note::
5128 .. note::
5127
5129
5128 ``default`` and ``default-push`` apply to all inbound (e.g.
5130 ``default`` and ``default-push`` apply to all inbound (e.g.
5129 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5131 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5130 and :hg:`bundle`) operations.
5132 and :hg:`bundle`) operations.
5131
5133
5132 See :hg:`help urls` for more information.
5134 See :hg:`help urls` for more information.
5133
5135
5134 .. container:: verbose
5136 .. container:: verbose
5135
5137
5136 Template:
5138 Template:
5137
5139
5138 The following keywords are supported. See also :hg:`help templates`.
5140 The following keywords are supported. See also :hg:`help templates`.
5139
5141
5140 :name: String. Symbolic name of the path alias.
5142 :name: String. Symbolic name of the path alias.
5141 :pushurl: String. URL for push operations.
5143 :pushurl: String. URL for push operations.
5142 :url: String. URL or directory path for the other operations.
5144 :url: String. URL or directory path for the other operations.
5143
5145
5144 Returns 0 on success.
5146 Returns 0 on success.
5145 """
5147 """
5146
5148
5147 opts = pycompat.byteskwargs(opts)
5149 opts = pycompat.byteskwargs(opts)
5148 ui.pager(b'paths')
5150 ui.pager(b'paths')
5149 if search:
5151 if search:
5150 pathitems = [
5152 pathitems = [
5151 (name, path)
5153 (name, path)
5152 for name, path in pycompat.iteritems(ui.paths)
5154 for name, path in pycompat.iteritems(ui.paths)
5153 if name == search
5155 if name == search
5154 ]
5156 ]
5155 else:
5157 else:
5156 pathitems = sorted(pycompat.iteritems(ui.paths))
5158 pathitems = sorted(pycompat.iteritems(ui.paths))
5157
5159
5158 fm = ui.formatter(b'paths', opts)
5160 fm = ui.formatter(b'paths', opts)
5159 if fm.isplain():
5161 if fm.isplain():
5160 hidepassword = util.hidepassword
5162 hidepassword = util.hidepassword
5161 else:
5163 else:
5162 hidepassword = bytes
5164 hidepassword = bytes
5163 if ui.quiet:
5165 if ui.quiet:
5164 namefmt = b'%s\n'
5166 namefmt = b'%s\n'
5165 else:
5167 else:
5166 namefmt = b'%s = '
5168 namefmt = b'%s = '
5167 showsubopts = not search and not ui.quiet
5169 showsubopts = not search and not ui.quiet
5168
5170
5169 for name, path in pathitems:
5171 for name, path in pathitems:
5170 fm.startitem()
5172 fm.startitem()
5171 fm.condwrite(not search, b'name', namefmt, name)
5173 fm.condwrite(not search, b'name', namefmt, name)
5172 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5174 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5173 for subopt, value in sorted(path.suboptions.items()):
5175 for subopt, value in sorted(path.suboptions.items()):
5174 assert subopt not in (b'name', b'url')
5176 assert subopt not in (b'name', b'url')
5175 if showsubopts:
5177 if showsubopts:
5176 fm.plain(b'%s:%s = ' % (name, subopt))
5178 fm.plain(b'%s:%s = ' % (name, subopt))
5177 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5179 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5178
5180
5179 fm.end()
5181 fm.end()
5180
5182
5181 if search and not pathitems:
5183 if search and not pathitems:
5182 if not ui.quiet:
5184 if not ui.quiet:
5183 ui.warn(_(b"not found!\n"))
5185 ui.warn(_(b"not found!\n"))
5184 return 1
5186 return 1
5185 else:
5187 else:
5186 return 0
5188 return 0
5187
5189
5188
5190
5189 @command(
5191 @command(
5190 b'phase',
5192 b'phase',
5191 [
5193 [
5192 (b'p', b'public', False, _(b'set changeset phase to public')),
5194 (b'p', b'public', False, _(b'set changeset phase to public')),
5193 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5195 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5194 (b's', b'secret', False, _(b'set changeset phase to secret')),
5196 (b's', b'secret', False, _(b'set changeset phase to secret')),
5195 (b'f', b'force', False, _(b'allow to move boundary backward')),
5197 (b'f', b'force', False, _(b'allow to move boundary backward')),
5196 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5198 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5197 ],
5199 ],
5198 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5200 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5199 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5201 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5200 )
5202 )
5201 def phase(ui, repo, *revs, **opts):
5203 def phase(ui, repo, *revs, **opts):
5202 """set or show the current phase name
5204 """set or show the current phase name
5203
5205
5204 With no argument, show the phase name of the current revision(s).
5206 With no argument, show the phase name of the current revision(s).
5205
5207
5206 With one of -p/--public, -d/--draft or -s/--secret, change the
5208 With one of -p/--public, -d/--draft or -s/--secret, change the
5207 phase value of the specified revisions.
5209 phase value of the specified revisions.
5208
5210
5209 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5211 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5210 lower phase to a higher phase. Phases are ordered as follows::
5212 lower phase to a higher phase. Phases are ordered as follows::
5211
5213
5212 public < draft < secret
5214 public < draft < secret
5213
5215
5214 Returns 0 on success, 1 if some phases could not be changed.
5216 Returns 0 on success, 1 if some phases could not be changed.
5215
5217
5216 (For more information about the phases concept, see :hg:`help phases`.)
5218 (For more information about the phases concept, see :hg:`help phases`.)
5217 """
5219 """
5218 opts = pycompat.byteskwargs(opts)
5220 opts = pycompat.byteskwargs(opts)
5219 # search for a unique phase argument
5221 # search for a unique phase argument
5220 targetphase = None
5222 targetphase = None
5221 for idx, name in enumerate(phases.cmdphasenames):
5223 for idx, name in enumerate(phases.cmdphasenames):
5222 if opts[name]:
5224 if opts[name]:
5223 if targetphase is not None:
5225 if targetphase is not None:
5224 raise error.Abort(_(b'only one phase can be specified'))
5226 raise error.Abort(_(b'only one phase can be specified'))
5225 targetphase = idx
5227 targetphase = idx
5226
5228
5227 # look for specified revision
5229 # look for specified revision
5228 revs = list(revs)
5230 revs = list(revs)
5229 revs.extend(opts[b'rev'])
5231 revs.extend(opts[b'rev'])
5230 if not revs:
5232 if not revs:
5231 # display both parents as the second parent phase can influence
5233 # display both parents as the second parent phase can influence
5232 # the phase of a merge commit
5234 # the phase of a merge commit
5233 revs = [c.rev() for c in repo[None].parents()]
5235 revs = [c.rev() for c in repo[None].parents()]
5234
5236
5235 revs = scmutil.revrange(repo, revs)
5237 revs = scmutil.revrange(repo, revs)
5236
5238
5237 ret = 0
5239 ret = 0
5238 if targetphase is None:
5240 if targetphase is None:
5239 # display
5241 # display
5240 for r in revs:
5242 for r in revs:
5241 ctx = repo[r]
5243 ctx = repo[r]
5242 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5244 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5243 else:
5245 else:
5244 with repo.lock(), repo.transaction(b"phase") as tr:
5246 with repo.lock(), repo.transaction(b"phase") as tr:
5245 # set phase
5247 # set phase
5246 if not revs:
5248 if not revs:
5247 raise error.Abort(_(b'empty revision set'))
5249 raise error.Abort(_(b'empty revision set'))
5248 nodes = [repo[r].node() for r in revs]
5250 nodes = [repo[r].node() for r in revs]
5249 # moving revision from public to draft may hide them
5251 # moving revision from public to draft may hide them
5250 # We have to check result on an unfiltered repository
5252 # We have to check result on an unfiltered repository
5251 unfi = repo.unfiltered()
5253 unfi = repo.unfiltered()
5252 getphase = unfi._phasecache.phase
5254 getphase = unfi._phasecache.phase
5253 olddata = [getphase(unfi, r) for r in unfi]
5255 olddata = [getphase(unfi, r) for r in unfi]
5254 phases.advanceboundary(repo, tr, targetphase, nodes)
5256 phases.advanceboundary(repo, tr, targetphase, nodes)
5255 if opts[b'force']:
5257 if opts[b'force']:
5256 phases.retractboundary(repo, tr, targetphase, nodes)
5258 phases.retractboundary(repo, tr, targetphase, nodes)
5257 getphase = unfi._phasecache.phase
5259 getphase = unfi._phasecache.phase
5258 newdata = [getphase(unfi, r) for r in unfi]
5260 newdata = [getphase(unfi, r) for r in unfi]
5259 changes = sum(newdata[r] != olddata[r] for r in unfi)
5261 changes = sum(newdata[r] != olddata[r] for r in unfi)
5260 cl = unfi.changelog
5262 cl = unfi.changelog
5261 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5263 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5262 if rejected:
5264 if rejected:
5263 ui.warn(
5265 ui.warn(
5264 _(
5266 _(
5265 b'cannot move %i changesets to a higher '
5267 b'cannot move %i changesets to a higher '
5266 b'phase, use --force\n'
5268 b'phase, use --force\n'
5267 )
5269 )
5268 % len(rejected)
5270 % len(rejected)
5269 )
5271 )
5270 ret = 1
5272 ret = 1
5271 if changes:
5273 if changes:
5272 msg = _(b'phase changed for %i changesets\n') % changes
5274 msg = _(b'phase changed for %i changesets\n') % changes
5273 if ret:
5275 if ret:
5274 ui.status(msg)
5276 ui.status(msg)
5275 else:
5277 else:
5276 ui.note(msg)
5278 ui.note(msg)
5277 else:
5279 else:
5278 ui.warn(_(b'no phases changed\n'))
5280 ui.warn(_(b'no phases changed\n'))
5279 return ret
5281 return ret
5280
5282
5281
5283
5282 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5284 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5283 """Run after a changegroup has been added via pull/unbundle
5285 """Run after a changegroup has been added via pull/unbundle
5284
5286
5285 This takes arguments below:
5287 This takes arguments below:
5286
5288
5287 :modheads: change of heads by pull/unbundle
5289 :modheads: change of heads by pull/unbundle
5288 :optupdate: updating working directory is needed or not
5290 :optupdate: updating working directory is needed or not
5289 :checkout: update destination revision (or None to default destination)
5291 :checkout: update destination revision (or None to default destination)
5290 :brev: a name, which might be a bookmark to be activated after updating
5292 :brev: a name, which might be a bookmark to be activated after updating
5291 """
5293 """
5292 if modheads == 0:
5294 if modheads == 0:
5293 return
5295 return
5294 if optupdate:
5296 if optupdate:
5295 try:
5297 try:
5296 return hg.updatetotally(ui, repo, checkout, brev)
5298 return hg.updatetotally(ui, repo, checkout, brev)
5297 except error.UpdateAbort as inst:
5299 except error.UpdateAbort as inst:
5298 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5300 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5299 hint = inst.hint
5301 hint = inst.hint
5300 raise error.UpdateAbort(msg, hint=hint)
5302 raise error.UpdateAbort(msg, hint=hint)
5301 if modheads is not None and modheads > 1:
5303 if modheads is not None and modheads > 1:
5302 currentbranchheads = len(repo.branchheads())
5304 currentbranchheads = len(repo.branchheads())
5303 if currentbranchheads == modheads:
5305 if currentbranchheads == modheads:
5304 ui.status(
5306 ui.status(
5305 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5307 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5306 )
5308 )
5307 elif currentbranchheads > 1:
5309 elif currentbranchheads > 1:
5308 ui.status(
5310 ui.status(
5309 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5311 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5310 )
5312 )
5311 else:
5313 else:
5312 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5314 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5313 elif not ui.configbool(b'commands', b'update.requiredest'):
5315 elif not ui.configbool(b'commands', b'update.requiredest'):
5314 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5316 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5315
5317
5316
5318
5317 @command(
5319 @command(
5318 b'pull',
5320 b'pull',
5319 [
5321 [
5320 (
5322 (
5321 b'u',
5323 b'u',
5322 b'update',
5324 b'update',
5323 None,
5325 None,
5324 _(b'update to new branch head if new descendants were pulled'),
5326 _(b'update to new branch head if new descendants were pulled'),
5325 ),
5327 ),
5326 (
5328 (
5327 b'f',
5329 b'f',
5328 b'force',
5330 b'force',
5329 None,
5331 None,
5330 _(b'run even when remote repository is unrelated'),
5332 _(b'run even when remote repository is unrelated'),
5331 ),
5333 ),
5332 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5334 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5333 (
5335 (
5334 b'r',
5336 b'r',
5335 b'rev',
5337 b'rev',
5336 [],
5338 [],
5337 _(b'a remote changeset intended to be added'),
5339 _(b'a remote changeset intended to be added'),
5338 _(b'REV'),
5340 _(b'REV'),
5339 ),
5341 ),
5340 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5342 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5341 (
5343 (
5342 b'b',
5344 b'b',
5343 b'branch',
5345 b'branch',
5344 [],
5346 [],
5345 _(b'a specific branch you would like to pull'),
5347 _(b'a specific branch you would like to pull'),
5346 _(b'BRANCH'),
5348 _(b'BRANCH'),
5347 ),
5349 ),
5348 ]
5350 ]
5349 + remoteopts,
5351 + remoteopts,
5350 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5352 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5351 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5353 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5352 helpbasic=True,
5354 helpbasic=True,
5353 )
5355 )
5354 def pull(ui, repo, source=b"default", **opts):
5356 def pull(ui, repo, source=b"default", **opts):
5355 """pull changes from the specified source
5357 """pull changes from the specified source
5356
5358
5357 Pull changes from a remote repository to a local one.
5359 Pull changes from a remote repository to a local one.
5358
5360
5359 This finds all changes from the repository at the specified path
5361 This finds all changes from the repository at the specified path
5360 or URL and adds them to a local repository (the current one unless
5362 or URL and adds them to a local repository (the current one unless
5361 -R is specified). By default, this does not update the copy of the
5363 -R is specified). By default, this does not update the copy of the
5362 project in the working directory.
5364 project in the working directory.
5363
5365
5364 When cloning from servers that support it, Mercurial may fetch
5366 When cloning from servers that support it, Mercurial may fetch
5365 pre-generated data. When this is done, hooks operating on incoming
5367 pre-generated data. When this is done, hooks operating on incoming
5366 changesets and changegroups may fire more than once, once for each
5368 changesets and changegroups may fire more than once, once for each
5367 pre-generated bundle and as well as for any additional remaining
5369 pre-generated bundle and as well as for any additional remaining
5368 data. See :hg:`help -e clonebundles` for more.
5370 data. See :hg:`help -e clonebundles` for more.
5369
5371
5370 Use :hg:`incoming` if you want to see what would have been added
5372 Use :hg:`incoming` if you want to see what would have been added
5371 by a pull at the time you issued this command. If you then decide
5373 by a pull at the time you issued this command. If you then decide
5372 to add those changes to the repository, you should use :hg:`pull
5374 to add those changes to the repository, you should use :hg:`pull
5373 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5375 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5374
5376
5375 If SOURCE is omitted, the 'default' path will be used.
5377 If SOURCE is omitted, the 'default' path will be used.
5376 See :hg:`help urls` for more information.
5378 See :hg:`help urls` for more information.
5377
5379
5378 Specifying bookmark as ``.`` is equivalent to specifying the active
5380 Specifying bookmark as ``.`` is equivalent to specifying the active
5379 bookmark's name.
5381 bookmark's name.
5380
5382
5381 Returns 0 on success, 1 if an update had unresolved files.
5383 Returns 0 on success, 1 if an update had unresolved files.
5382 """
5384 """
5383
5385
5384 opts = pycompat.byteskwargs(opts)
5386 opts = pycompat.byteskwargs(opts)
5385 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5387 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5386 b'update'
5388 b'update'
5387 ):
5389 ):
5388 msg = _(b'update destination required by configuration')
5390 msg = _(b'update destination required by configuration')
5389 hint = _(b'use hg pull followed by hg update DEST')
5391 hint = _(b'use hg pull followed by hg update DEST')
5390 raise error.Abort(msg, hint=hint)
5392 raise error.Abort(msg, hint=hint)
5391
5393
5392 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5394 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5393 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5395 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5394 other = hg.peer(repo, opts, source)
5396 other = hg.peer(repo, opts, source)
5395 try:
5397 try:
5396 revs, checkout = hg.addbranchrevs(
5398 revs, checkout = hg.addbranchrevs(
5397 repo, other, branches, opts.get(b'rev')
5399 repo, other, branches, opts.get(b'rev')
5398 )
5400 )
5399
5401
5400 pullopargs = {}
5402 pullopargs = {}
5401
5403
5402 nodes = None
5404 nodes = None
5403 if opts.get(b'bookmark') or revs:
5405 if opts.get(b'bookmark') or revs:
5404 # The list of bookmark used here is the same used to actually update
5406 # The list of bookmark used here is the same used to actually update
5405 # the bookmark names, to avoid the race from issue 4689 and we do
5407 # the bookmark names, to avoid the race from issue 4689 and we do
5406 # all lookup and bookmark queries in one go so they see the same
5408 # all lookup and bookmark queries in one go so they see the same
5407 # version of the server state (issue 4700).
5409 # version of the server state (issue 4700).
5408 nodes = []
5410 nodes = []
5409 fnodes = []
5411 fnodes = []
5410 revs = revs or []
5412 revs = revs or []
5411 if revs and not other.capable(b'lookup'):
5413 if revs and not other.capable(b'lookup'):
5412 err = _(
5414 err = _(
5413 b"other repository doesn't support revision lookup, "
5415 b"other repository doesn't support revision lookup, "
5414 b"so a rev cannot be specified."
5416 b"so a rev cannot be specified."
5415 )
5417 )
5416 raise error.Abort(err)
5418 raise error.Abort(err)
5417 with other.commandexecutor() as e:
5419 with other.commandexecutor() as e:
5418 fremotebookmarks = e.callcommand(
5420 fremotebookmarks = e.callcommand(
5419 b'listkeys', {b'namespace': b'bookmarks'}
5421 b'listkeys', {b'namespace': b'bookmarks'}
5420 )
5422 )
5421 for r in revs:
5423 for r in revs:
5422 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5424 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5423 remotebookmarks = fremotebookmarks.result()
5425 remotebookmarks = fremotebookmarks.result()
5424 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5426 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5425 pullopargs[b'remotebookmarks'] = remotebookmarks
5427 pullopargs[b'remotebookmarks'] = remotebookmarks
5426 for b in opts.get(b'bookmark', []):
5428 for b in opts.get(b'bookmark', []):
5427 b = repo._bookmarks.expandname(b)
5429 b = repo._bookmarks.expandname(b)
5428 if b not in remotebookmarks:
5430 if b not in remotebookmarks:
5429 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5431 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5430 nodes.append(remotebookmarks[b])
5432 nodes.append(remotebookmarks[b])
5431 for i, rev in enumerate(revs):
5433 for i, rev in enumerate(revs):
5432 node = fnodes[i].result()
5434 node = fnodes[i].result()
5433 nodes.append(node)
5435 nodes.append(node)
5434 if rev == checkout:
5436 if rev == checkout:
5435 checkout = node
5437 checkout = node
5436
5438
5437 wlock = util.nullcontextmanager()
5439 wlock = util.nullcontextmanager()
5438 if opts.get(b'update'):
5440 if opts.get(b'update'):
5439 wlock = repo.wlock()
5441 wlock = repo.wlock()
5440 with wlock:
5442 with wlock:
5441 pullopargs.update(opts.get(b'opargs', {}))
5443 pullopargs.update(opts.get(b'opargs', {}))
5442 modheads = exchange.pull(
5444 modheads = exchange.pull(
5443 repo,
5445 repo,
5444 other,
5446 other,
5445 heads=nodes,
5447 heads=nodes,
5446 force=opts.get(b'force'),
5448 force=opts.get(b'force'),
5447 bookmarks=opts.get(b'bookmark', ()),
5449 bookmarks=opts.get(b'bookmark', ()),
5448 opargs=pullopargs,
5450 opargs=pullopargs,
5449 confirm=opts.get(b'confirm'),
5451 confirm=opts.get(b'confirm'),
5450 ).cgresult
5452 ).cgresult
5451
5453
5452 # brev is a name, which might be a bookmark to be activated at
5454 # brev is a name, which might be a bookmark to be activated at
5453 # the end of the update. In other words, it is an explicit
5455 # the end of the update. In other words, it is an explicit
5454 # destination of the update
5456 # destination of the update
5455 brev = None
5457 brev = None
5456
5458
5457 if checkout:
5459 if checkout:
5458 checkout = repo.unfiltered().changelog.rev(checkout)
5460 checkout = repo.unfiltered().changelog.rev(checkout)
5459
5461
5460 # order below depends on implementation of
5462 # order below depends on implementation of
5461 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5463 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5462 # because 'checkout' is determined without it.
5464 # because 'checkout' is determined without it.
5463 if opts.get(b'rev'):
5465 if opts.get(b'rev'):
5464 brev = opts[b'rev'][0]
5466 brev = opts[b'rev'][0]
5465 elif opts.get(b'branch'):
5467 elif opts.get(b'branch'):
5466 brev = opts[b'branch'][0]
5468 brev = opts[b'branch'][0]
5467 else:
5469 else:
5468 brev = branches[0]
5470 brev = branches[0]
5469 repo._subtoppath = source
5471 repo._subtoppath = source
5470 try:
5472 try:
5471 ret = postincoming(
5473 ret = postincoming(
5472 ui, repo, modheads, opts.get(b'update'), checkout, brev
5474 ui, repo, modheads, opts.get(b'update'), checkout, brev
5473 )
5475 )
5474 except error.FilteredRepoLookupError as exc:
5476 except error.FilteredRepoLookupError as exc:
5475 msg = _(b'cannot update to target: %s') % exc.args[0]
5477 msg = _(b'cannot update to target: %s') % exc.args[0]
5476 exc.args = (msg,) + exc.args[1:]
5478 exc.args = (msg,) + exc.args[1:]
5477 raise
5479 raise
5478 finally:
5480 finally:
5479 del repo._subtoppath
5481 del repo._subtoppath
5480
5482
5481 finally:
5483 finally:
5482 other.close()
5484 other.close()
5483 return ret
5485 return ret
5484
5486
5485
5487
5486 @command(
5488 @command(
5487 b'push',
5489 b'push',
5488 [
5490 [
5489 (b'f', b'force', None, _(b'force push')),
5491 (b'f', b'force', None, _(b'force push')),
5490 (
5492 (
5491 b'r',
5493 b'r',
5492 b'rev',
5494 b'rev',
5493 [],
5495 [],
5494 _(b'a changeset intended to be included in the destination'),
5496 _(b'a changeset intended to be included in the destination'),
5495 _(b'REV'),
5497 _(b'REV'),
5496 ),
5498 ),
5497 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5499 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5498 (
5500 (
5499 b'b',
5501 b'b',
5500 b'branch',
5502 b'branch',
5501 [],
5503 [],
5502 _(b'a specific branch you would like to push'),
5504 _(b'a specific branch you would like to push'),
5503 _(b'BRANCH'),
5505 _(b'BRANCH'),
5504 ),
5506 ),
5505 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5507 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5506 (
5508 (
5507 b'',
5509 b'',
5508 b'pushvars',
5510 b'pushvars',
5509 [],
5511 [],
5510 _(b'variables that can be sent to server (ADVANCED)'),
5512 _(b'variables that can be sent to server (ADVANCED)'),
5511 ),
5513 ),
5512 (
5514 (
5513 b'',
5515 b'',
5514 b'publish',
5516 b'publish',
5515 False,
5517 False,
5516 _(b'push the changeset as public (EXPERIMENTAL)'),
5518 _(b'push the changeset as public (EXPERIMENTAL)'),
5517 ),
5519 ),
5518 ]
5520 ]
5519 + remoteopts,
5521 + remoteopts,
5520 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5522 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5521 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5523 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5522 helpbasic=True,
5524 helpbasic=True,
5523 )
5525 )
5524 def push(ui, repo, dest=None, **opts):
5526 def push(ui, repo, dest=None, **opts):
5525 """push changes to the specified destination
5527 """push changes to the specified destination
5526
5528
5527 Push changesets from the local repository to the specified
5529 Push changesets from the local repository to the specified
5528 destination.
5530 destination.
5529
5531
5530 This operation is symmetrical to pull: it is identical to a pull
5532 This operation is symmetrical to pull: it is identical to a pull
5531 in the destination repository from the current one.
5533 in the destination repository from the current one.
5532
5534
5533 By default, push will not allow creation of new heads at the
5535 By default, push will not allow creation of new heads at the
5534 destination, since multiple heads would make it unclear which head
5536 destination, since multiple heads would make it unclear which head
5535 to use. In this situation, it is recommended to pull and merge
5537 to use. In this situation, it is recommended to pull and merge
5536 before pushing.
5538 before pushing.
5537
5539
5538 Use --new-branch if you want to allow push to create a new named
5540 Use --new-branch if you want to allow push to create a new named
5539 branch that is not present at the destination. This allows you to
5541 branch that is not present at the destination. This allows you to
5540 only create a new branch without forcing other changes.
5542 only create a new branch without forcing other changes.
5541
5543
5542 .. note::
5544 .. note::
5543
5545
5544 Extra care should be taken with the -f/--force option,
5546 Extra care should be taken with the -f/--force option,
5545 which will push all new heads on all branches, an action which will
5547 which will push all new heads on all branches, an action which will
5546 almost always cause confusion for collaborators.
5548 almost always cause confusion for collaborators.
5547
5549
5548 If -r/--rev is used, the specified revision and all its ancestors
5550 If -r/--rev is used, the specified revision and all its ancestors
5549 will be pushed to the remote repository.
5551 will be pushed to the remote repository.
5550
5552
5551 If -B/--bookmark is used, the specified bookmarked revision, its
5553 If -B/--bookmark is used, the specified bookmarked revision, its
5552 ancestors, and the bookmark will be pushed to the remote
5554 ancestors, and the bookmark will be pushed to the remote
5553 repository. Specifying ``.`` is equivalent to specifying the active
5555 repository. Specifying ``.`` is equivalent to specifying the active
5554 bookmark's name.
5556 bookmark's name.
5555
5557
5556 Please see :hg:`help urls` for important details about ``ssh://``
5558 Please see :hg:`help urls` for important details about ``ssh://``
5557 URLs. If DESTINATION is omitted, a default path will be used.
5559 URLs. If DESTINATION is omitted, a default path will be used.
5558
5560
5559 .. container:: verbose
5561 .. container:: verbose
5560
5562
5561 The --pushvars option sends strings to the server that become
5563 The --pushvars option sends strings to the server that become
5562 environment variables prepended with ``HG_USERVAR_``. For example,
5564 environment variables prepended with ``HG_USERVAR_``. For example,
5563 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5565 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5564 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5566 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5565
5567
5566 pushvars can provide for user-overridable hooks as well as set debug
5568 pushvars can provide for user-overridable hooks as well as set debug
5567 levels. One example is having a hook that blocks commits containing
5569 levels. One example is having a hook that blocks commits containing
5568 conflict markers, but enables the user to override the hook if the file
5570 conflict markers, but enables the user to override the hook if the file
5569 is using conflict markers for testing purposes or the file format has
5571 is using conflict markers for testing purposes or the file format has
5570 strings that look like conflict markers.
5572 strings that look like conflict markers.
5571
5573
5572 By default, servers will ignore `--pushvars`. To enable it add the
5574 By default, servers will ignore `--pushvars`. To enable it add the
5573 following to your configuration file::
5575 following to your configuration file::
5574
5576
5575 [push]
5577 [push]
5576 pushvars.server = true
5578 pushvars.server = true
5577
5579
5578 Returns 0 if push was successful, 1 if nothing to push.
5580 Returns 0 if push was successful, 1 if nothing to push.
5579 """
5581 """
5580
5582
5581 opts = pycompat.byteskwargs(opts)
5583 opts = pycompat.byteskwargs(opts)
5582 if opts.get(b'bookmark'):
5584 if opts.get(b'bookmark'):
5583 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5585 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5584 for b in opts[b'bookmark']:
5586 for b in opts[b'bookmark']:
5585 # translate -B options to -r so changesets get pushed
5587 # translate -B options to -r so changesets get pushed
5586 b = repo._bookmarks.expandname(b)
5588 b = repo._bookmarks.expandname(b)
5587 if b in repo._bookmarks:
5589 if b in repo._bookmarks:
5588 opts.setdefault(b'rev', []).append(b)
5590 opts.setdefault(b'rev', []).append(b)
5589 else:
5591 else:
5590 # if we try to push a deleted bookmark, translate it to null
5592 # if we try to push a deleted bookmark, translate it to null
5591 # this lets simultaneous -r, -b options continue working
5593 # this lets simultaneous -r, -b options continue working
5592 opts.setdefault(b'rev', []).append(b"null")
5594 opts.setdefault(b'rev', []).append(b"null")
5593
5595
5594 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5596 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5595 if not path:
5597 if not path:
5596 raise error.Abort(
5598 raise error.Abort(
5597 _(b'default repository not configured!'),
5599 _(b'default repository not configured!'),
5598 hint=_(b"see 'hg help config.paths'"),
5600 hint=_(b"see 'hg help config.paths'"),
5599 )
5601 )
5600 dest = path.pushloc or path.loc
5602 dest = path.pushloc or path.loc
5601 branches = (path.branch, opts.get(b'branch') or [])
5603 branches = (path.branch, opts.get(b'branch') or [])
5602 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5604 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5603 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5605 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5604 other = hg.peer(repo, opts, dest)
5606 other = hg.peer(repo, opts, dest)
5605
5607
5606 if revs:
5608 if revs:
5607 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5609 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5608 if not revs:
5610 if not revs:
5609 raise error.Abort(
5611 raise error.Abort(
5610 _(b"specified revisions evaluate to an empty set"),
5612 _(b"specified revisions evaluate to an empty set"),
5611 hint=_(b"use different revision arguments"),
5613 hint=_(b"use different revision arguments"),
5612 )
5614 )
5613 elif path.pushrev:
5615 elif path.pushrev:
5614 # It doesn't make any sense to specify ancestor revisions. So limit
5616 # It doesn't make any sense to specify ancestor revisions. So limit
5615 # to DAG heads to make discovery simpler.
5617 # to DAG heads to make discovery simpler.
5616 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5618 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5617 revs = scmutil.revrange(repo, [expr])
5619 revs = scmutil.revrange(repo, [expr])
5618 revs = [repo[rev].node() for rev in revs]
5620 revs = [repo[rev].node() for rev in revs]
5619 if not revs:
5621 if not revs:
5620 raise error.Abort(
5622 raise error.Abort(
5621 _(b'default push revset for path evaluates to an empty set')
5623 _(b'default push revset for path evaluates to an empty set')
5622 )
5624 )
5623 elif ui.configbool(b'commands', b'push.require-revs'):
5625 elif ui.configbool(b'commands', b'push.require-revs'):
5624 raise error.Abort(
5626 raise error.Abort(
5625 _(b'no revisions specified to push'),
5627 _(b'no revisions specified to push'),
5626 hint=_(b'did you mean "hg push -r ."?'),
5628 hint=_(b'did you mean "hg push -r ."?'),
5627 )
5629 )
5628
5630
5629 repo._subtoppath = dest
5631 repo._subtoppath = dest
5630 try:
5632 try:
5631 # push subrepos depth-first for coherent ordering
5633 # push subrepos depth-first for coherent ordering
5632 c = repo[b'.']
5634 c = repo[b'.']
5633 subs = c.substate # only repos that are committed
5635 subs = c.substate # only repos that are committed
5634 for s in sorted(subs):
5636 for s in sorted(subs):
5635 result = c.sub(s).push(opts)
5637 result = c.sub(s).push(opts)
5636 if result == 0:
5638 if result == 0:
5637 return not result
5639 return not result
5638 finally:
5640 finally:
5639 del repo._subtoppath
5641 del repo._subtoppath
5640
5642
5641 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5643 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5642 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5644 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5643
5645
5644 pushop = exchange.push(
5646 pushop = exchange.push(
5645 repo,
5647 repo,
5646 other,
5648 other,
5647 opts.get(b'force'),
5649 opts.get(b'force'),
5648 revs=revs,
5650 revs=revs,
5649 newbranch=opts.get(b'new_branch'),
5651 newbranch=opts.get(b'new_branch'),
5650 bookmarks=opts.get(b'bookmark', ()),
5652 bookmarks=opts.get(b'bookmark', ()),
5651 publish=opts.get(b'publish'),
5653 publish=opts.get(b'publish'),
5652 opargs=opargs,
5654 opargs=opargs,
5653 )
5655 )
5654
5656
5655 result = not pushop.cgresult
5657 result = not pushop.cgresult
5656
5658
5657 if pushop.bkresult is not None:
5659 if pushop.bkresult is not None:
5658 if pushop.bkresult == 2:
5660 if pushop.bkresult == 2:
5659 result = 2
5661 result = 2
5660 elif not result and pushop.bkresult:
5662 elif not result and pushop.bkresult:
5661 result = 2
5663 result = 2
5662
5664
5663 return result
5665 return result
5664
5666
5665
5667
5666 @command(
5668 @command(
5667 b'recover',
5669 b'recover',
5668 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5670 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5669 helpcategory=command.CATEGORY_MAINTENANCE,
5671 helpcategory=command.CATEGORY_MAINTENANCE,
5670 )
5672 )
5671 def recover(ui, repo, **opts):
5673 def recover(ui, repo, **opts):
5672 """roll back an interrupted transaction
5674 """roll back an interrupted transaction
5673
5675
5674 Recover from an interrupted commit or pull.
5676 Recover from an interrupted commit or pull.
5675
5677
5676 This command tries to fix the repository status after an
5678 This command tries to fix the repository status after an
5677 interrupted operation. It should only be necessary when Mercurial
5679 interrupted operation. It should only be necessary when Mercurial
5678 suggests it.
5680 suggests it.
5679
5681
5680 Returns 0 if successful, 1 if nothing to recover or verify fails.
5682 Returns 0 if successful, 1 if nothing to recover or verify fails.
5681 """
5683 """
5682 ret = repo.recover()
5684 ret = repo.recover()
5683 if ret:
5685 if ret:
5684 if opts['verify']:
5686 if opts['verify']:
5685 return hg.verify(repo)
5687 return hg.verify(repo)
5686 else:
5688 else:
5687 msg = _(
5689 msg = _(
5688 b"(verify step skipped, run `hg verify` to check your "
5690 b"(verify step skipped, run `hg verify` to check your "
5689 b"repository content)\n"
5691 b"repository content)\n"
5690 )
5692 )
5691 ui.warn(msg)
5693 ui.warn(msg)
5692 return 0
5694 return 0
5693 return 1
5695 return 1
5694
5696
5695
5697
5696 @command(
5698 @command(
5697 b'remove|rm',
5699 b'remove|rm',
5698 [
5700 [
5699 (b'A', b'after', None, _(b'record delete for missing files')),
5701 (b'A', b'after', None, _(b'record delete for missing files')),
5700 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5702 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5701 ]
5703 ]
5702 + subrepoopts
5704 + subrepoopts
5703 + walkopts
5705 + walkopts
5704 + dryrunopts,
5706 + dryrunopts,
5705 _(b'[OPTION]... FILE...'),
5707 _(b'[OPTION]... FILE...'),
5706 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5708 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5707 helpbasic=True,
5709 helpbasic=True,
5708 inferrepo=True,
5710 inferrepo=True,
5709 )
5711 )
5710 def remove(ui, repo, *pats, **opts):
5712 def remove(ui, repo, *pats, **opts):
5711 """remove the specified files on the next commit
5713 """remove the specified files on the next commit
5712
5714
5713 Schedule the indicated files for removal from the current branch.
5715 Schedule the indicated files for removal from the current branch.
5714
5716
5715 This command schedules the files to be removed at the next commit.
5717 This command schedules the files to be removed at the next commit.
5716 To undo a remove before that, see :hg:`revert`. To undo added
5718 To undo a remove before that, see :hg:`revert`. To undo added
5717 files, see :hg:`forget`.
5719 files, see :hg:`forget`.
5718
5720
5719 .. container:: verbose
5721 .. container:: verbose
5720
5722
5721 -A/--after can be used to remove only files that have already
5723 -A/--after can be used to remove only files that have already
5722 been deleted, -f/--force can be used to force deletion, and -Af
5724 been deleted, -f/--force can be used to force deletion, and -Af
5723 can be used to remove files from the next revision without
5725 can be used to remove files from the next revision without
5724 deleting them from the working directory.
5726 deleting them from the working directory.
5725
5727
5726 The following table details the behavior of remove for different
5728 The following table details the behavior of remove for different
5727 file states (columns) and option combinations (rows). The file
5729 file states (columns) and option combinations (rows). The file
5728 states are Added [A], Clean [C], Modified [M] and Missing [!]
5730 states are Added [A], Clean [C], Modified [M] and Missing [!]
5729 (as reported by :hg:`status`). The actions are Warn, Remove
5731 (as reported by :hg:`status`). The actions are Warn, Remove
5730 (from branch) and Delete (from disk):
5732 (from branch) and Delete (from disk):
5731
5733
5732 ========= == == == ==
5734 ========= == == == ==
5733 opt/state A C M !
5735 opt/state A C M !
5734 ========= == == == ==
5736 ========= == == == ==
5735 none W RD W R
5737 none W RD W R
5736 -f R RD RD R
5738 -f R RD RD R
5737 -A W W W R
5739 -A W W W R
5738 -Af R R R R
5740 -Af R R R R
5739 ========= == == == ==
5741 ========= == == == ==
5740
5742
5741 .. note::
5743 .. note::
5742
5744
5743 :hg:`remove` never deletes files in Added [A] state from the
5745 :hg:`remove` never deletes files in Added [A] state from the
5744 working directory, not even if ``--force`` is specified.
5746 working directory, not even if ``--force`` is specified.
5745
5747
5746 Returns 0 on success, 1 if any warnings encountered.
5748 Returns 0 on success, 1 if any warnings encountered.
5747 """
5749 """
5748
5750
5749 opts = pycompat.byteskwargs(opts)
5751 opts = pycompat.byteskwargs(opts)
5750 after, force = opts.get(b'after'), opts.get(b'force')
5752 after, force = opts.get(b'after'), opts.get(b'force')
5751 dryrun = opts.get(b'dry_run')
5753 dryrun = opts.get(b'dry_run')
5752 if not pats and not after:
5754 if not pats and not after:
5753 raise error.Abort(_(b'no files specified'))
5755 raise error.Abort(_(b'no files specified'))
5754
5756
5755 m = scmutil.match(repo[None], pats, opts)
5757 m = scmutil.match(repo[None], pats, opts)
5756 subrepos = opts.get(b'subrepos')
5758 subrepos = opts.get(b'subrepos')
5757 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5759 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5758 return cmdutil.remove(
5760 return cmdutil.remove(
5759 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5761 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5760 )
5762 )
5761
5763
5762
5764
5763 @command(
5765 @command(
5764 b'rename|move|mv',
5766 b'rename|move|mv',
5765 [
5767 [
5766 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5768 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5767 (
5769 (
5768 b'',
5770 b'',
5769 b'at-rev',
5771 b'at-rev',
5770 b'',
5772 b'',
5771 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5773 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5772 _(b'REV'),
5774 _(b'REV'),
5773 ),
5775 ),
5774 (
5776 (
5775 b'f',
5777 b'f',
5776 b'force',
5778 b'force',
5777 None,
5779 None,
5778 _(b'forcibly move over an existing managed file'),
5780 _(b'forcibly move over an existing managed file'),
5779 ),
5781 ),
5780 ]
5782 ]
5781 + walkopts
5783 + walkopts
5782 + dryrunopts,
5784 + dryrunopts,
5783 _(b'[OPTION]... SOURCE... DEST'),
5785 _(b'[OPTION]... SOURCE... DEST'),
5784 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5786 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5785 )
5787 )
5786 def rename(ui, repo, *pats, **opts):
5788 def rename(ui, repo, *pats, **opts):
5787 """rename files; equivalent of copy + remove
5789 """rename files; equivalent of copy + remove
5788
5790
5789 Mark dest as copies of sources; mark sources for deletion. If dest
5791 Mark dest as copies of sources; mark sources for deletion. If dest
5790 is a directory, copies are put in that directory. If dest is a
5792 is a directory, copies are put in that directory. If dest is a
5791 file, there can only be one source.
5793 file, there can only be one source.
5792
5794
5793 By default, this command copies the contents of files as they
5795 By default, this command copies the contents of files as they
5794 exist in the working directory. If invoked with -A/--after, the
5796 exist in the working directory. If invoked with -A/--after, the
5795 operation is recorded, but no copying is performed.
5797 operation is recorded, but no copying is performed.
5796
5798
5797 This command takes effect at the next commit. To undo a rename
5799 This command takes effect at the next commit. To undo a rename
5798 before that, see :hg:`revert`.
5800 before that, see :hg:`revert`.
5799
5801
5800 Returns 0 on success, 1 if errors are encountered.
5802 Returns 0 on success, 1 if errors are encountered.
5801 """
5803 """
5802 opts = pycompat.byteskwargs(opts)
5804 opts = pycompat.byteskwargs(opts)
5803 with repo.wlock():
5805 with repo.wlock():
5804 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5806 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5805
5807
5806
5808
5807 @command(
5809 @command(
5808 b'resolve',
5810 b'resolve',
5809 [
5811 [
5810 (b'a', b'all', None, _(b'select all unresolved files')),
5812 (b'a', b'all', None, _(b'select all unresolved files')),
5811 (b'l', b'list', None, _(b'list state of files needing merge')),
5813 (b'l', b'list', None, _(b'list state of files needing merge')),
5812 (b'm', b'mark', None, _(b'mark files as resolved')),
5814 (b'm', b'mark', None, _(b'mark files as resolved')),
5813 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5815 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5814 (b'n', b'no-status', None, _(b'hide status prefix')),
5816 (b'n', b'no-status', None, _(b'hide status prefix')),
5815 (b'', b're-merge', None, _(b're-merge files')),
5817 (b'', b're-merge', None, _(b're-merge files')),
5816 ]
5818 ]
5817 + mergetoolopts
5819 + mergetoolopts
5818 + walkopts
5820 + walkopts
5819 + formatteropts,
5821 + formatteropts,
5820 _(b'[OPTION]... [FILE]...'),
5822 _(b'[OPTION]... [FILE]...'),
5821 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5823 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5822 inferrepo=True,
5824 inferrepo=True,
5823 )
5825 )
5824 def resolve(ui, repo, *pats, **opts):
5826 def resolve(ui, repo, *pats, **opts):
5825 """redo merges or set/view the merge status of files
5827 """redo merges or set/view the merge status of files
5826
5828
5827 Merges with unresolved conflicts are often the result of
5829 Merges with unresolved conflicts are often the result of
5828 non-interactive merging using the ``internal:merge`` configuration
5830 non-interactive merging using the ``internal:merge`` configuration
5829 setting, or a command-line merge tool like ``diff3``. The resolve
5831 setting, or a command-line merge tool like ``diff3``. The resolve
5830 command is used to manage the files involved in a merge, after
5832 command is used to manage the files involved in a merge, after
5831 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5833 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5832 working directory must have two parents). See :hg:`help
5834 working directory must have two parents). See :hg:`help
5833 merge-tools` for information on configuring merge tools.
5835 merge-tools` for information on configuring merge tools.
5834
5836
5835 The resolve command can be used in the following ways:
5837 The resolve command can be used in the following ways:
5836
5838
5837 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5839 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5838 the specified files, discarding any previous merge attempts. Re-merging
5840 the specified files, discarding any previous merge attempts. Re-merging
5839 is not performed for files already marked as resolved. Use ``--all/-a``
5841 is not performed for files already marked as resolved. Use ``--all/-a``
5840 to select all unresolved files. ``--tool`` can be used to specify
5842 to select all unresolved files. ``--tool`` can be used to specify
5841 the merge tool used for the given files. It overrides the HGMERGE
5843 the merge tool used for the given files. It overrides the HGMERGE
5842 environment variable and your configuration files. Previous file
5844 environment variable and your configuration files. Previous file
5843 contents are saved with a ``.orig`` suffix.
5845 contents are saved with a ``.orig`` suffix.
5844
5846
5845 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5847 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5846 (e.g. after having manually fixed-up the files). The default is
5848 (e.g. after having manually fixed-up the files). The default is
5847 to mark all unresolved files.
5849 to mark all unresolved files.
5848
5850
5849 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5851 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5850 default is to mark all resolved files.
5852 default is to mark all resolved files.
5851
5853
5852 - :hg:`resolve -l`: list files which had or still have conflicts.
5854 - :hg:`resolve -l`: list files which had or still have conflicts.
5853 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5855 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5854 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5856 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5855 the list. See :hg:`help filesets` for details.
5857 the list. See :hg:`help filesets` for details.
5856
5858
5857 .. note::
5859 .. note::
5858
5860
5859 Mercurial will not let you commit files with unresolved merge
5861 Mercurial will not let you commit files with unresolved merge
5860 conflicts. You must use :hg:`resolve -m ...` before you can
5862 conflicts. You must use :hg:`resolve -m ...` before you can
5861 commit after a conflicting merge.
5863 commit after a conflicting merge.
5862
5864
5863 .. container:: verbose
5865 .. container:: verbose
5864
5866
5865 Template:
5867 Template:
5866
5868
5867 The following keywords are supported in addition to the common template
5869 The following keywords are supported in addition to the common template
5868 keywords and functions. See also :hg:`help templates`.
5870 keywords and functions. See also :hg:`help templates`.
5869
5871
5870 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5872 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5871 :path: String. Repository-absolute path of the file.
5873 :path: String. Repository-absolute path of the file.
5872
5874
5873 Returns 0 on success, 1 if any files fail a resolve attempt.
5875 Returns 0 on success, 1 if any files fail a resolve attempt.
5874 """
5876 """
5875
5877
5876 opts = pycompat.byteskwargs(opts)
5878 opts = pycompat.byteskwargs(opts)
5877 confirm = ui.configbool(b'commands', b'resolve.confirm')
5879 confirm = ui.configbool(b'commands', b'resolve.confirm')
5878 flaglist = b'all mark unmark list no_status re_merge'.split()
5880 flaglist = b'all mark unmark list no_status re_merge'.split()
5879 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5881 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5880
5882
5881 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5883 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5882 if actioncount > 1:
5884 if actioncount > 1:
5883 raise error.Abort(_(b"too many actions specified"))
5885 raise error.Abort(_(b"too many actions specified"))
5884 elif actioncount == 0 and ui.configbool(
5886 elif actioncount == 0 and ui.configbool(
5885 b'commands', b'resolve.explicit-re-merge'
5887 b'commands', b'resolve.explicit-re-merge'
5886 ):
5888 ):
5887 hint = _(b'use --mark, --unmark, --list or --re-merge')
5889 hint = _(b'use --mark, --unmark, --list or --re-merge')
5888 raise error.Abort(_(b'no action specified'), hint=hint)
5890 raise error.Abort(_(b'no action specified'), hint=hint)
5889 if pats and all:
5891 if pats and all:
5890 raise error.Abort(_(b"can't specify --all and patterns"))
5892 raise error.Abort(_(b"can't specify --all and patterns"))
5891 if not (all or pats or show or mark or unmark):
5893 if not (all or pats or show or mark or unmark):
5892 raise error.Abort(
5894 raise error.Abort(
5893 _(b'no files or directories specified'),
5895 _(b'no files or directories specified'),
5894 hint=b'use --all to re-merge all unresolved files',
5896 hint=b'use --all to re-merge all unresolved files',
5895 )
5897 )
5896
5898
5897 if confirm:
5899 if confirm:
5898 if all:
5900 if all:
5899 if ui.promptchoice(
5901 if ui.promptchoice(
5900 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5902 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5901 ):
5903 ):
5902 raise error.Abort(_(b'user quit'))
5904 raise error.Abort(_(b'user quit'))
5903 if mark and not pats:
5905 if mark and not pats:
5904 if ui.promptchoice(
5906 if ui.promptchoice(
5905 _(
5907 _(
5906 b'mark all unresolved files as resolved (yn)?'
5908 b'mark all unresolved files as resolved (yn)?'
5907 b'$$ &Yes $$ &No'
5909 b'$$ &Yes $$ &No'
5908 )
5910 )
5909 ):
5911 ):
5910 raise error.Abort(_(b'user quit'))
5912 raise error.Abort(_(b'user quit'))
5911 if unmark and not pats:
5913 if unmark and not pats:
5912 if ui.promptchoice(
5914 if ui.promptchoice(
5913 _(
5915 _(
5914 b'mark all resolved files as unresolved (yn)?'
5916 b'mark all resolved files as unresolved (yn)?'
5915 b'$$ &Yes $$ &No'
5917 b'$$ &Yes $$ &No'
5916 )
5918 )
5917 ):
5919 ):
5918 raise error.Abort(_(b'user quit'))
5920 raise error.Abort(_(b'user quit'))
5919
5921
5920 uipathfn = scmutil.getuipathfn(repo)
5922 uipathfn = scmutil.getuipathfn(repo)
5921
5923
5922 if show:
5924 if show:
5923 ui.pager(b'resolve')
5925 ui.pager(b'resolve')
5924 fm = ui.formatter(b'resolve', opts)
5926 fm = ui.formatter(b'resolve', opts)
5925 ms = mergestatemod.mergestate.read(repo)
5927 ms = mergestatemod.mergestate.read(repo)
5926 wctx = repo[None]
5928 wctx = repo[None]
5927 m = scmutil.match(wctx, pats, opts)
5929 m = scmutil.match(wctx, pats, opts)
5928
5930
5929 # Labels and keys based on merge state. Unresolved path conflicts show
5931 # Labels and keys based on merge state. Unresolved path conflicts show
5930 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5932 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5931 # resolved conflicts.
5933 # resolved conflicts.
5932 mergestateinfo = {
5934 mergestateinfo = {
5933 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5935 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5934 b'resolve.unresolved',
5936 b'resolve.unresolved',
5935 b'U',
5937 b'U',
5936 ),
5938 ),
5937 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5939 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5938 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5940 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5939 b'resolve.unresolved',
5941 b'resolve.unresolved',
5940 b'P',
5942 b'P',
5941 ),
5943 ),
5942 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5944 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5943 b'resolve.resolved',
5945 b'resolve.resolved',
5944 b'R',
5946 b'R',
5945 ),
5947 ),
5946 }
5948 }
5947
5949
5948 for f in ms:
5950 for f in ms:
5949 if not m(f):
5951 if not m(f):
5950 continue
5952 continue
5951
5953
5952 label, key = mergestateinfo[ms[f]]
5954 label, key = mergestateinfo[ms[f]]
5953 fm.startitem()
5955 fm.startitem()
5954 fm.context(ctx=wctx)
5956 fm.context(ctx=wctx)
5955 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5957 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5956 fm.data(path=f)
5958 fm.data(path=f)
5957 fm.plain(b'%s\n' % uipathfn(f), label=label)
5959 fm.plain(b'%s\n' % uipathfn(f), label=label)
5958 fm.end()
5960 fm.end()
5959 return 0
5961 return 0
5960
5962
5961 with repo.wlock():
5963 with repo.wlock():
5962 ms = mergestatemod.mergestate.read(repo)
5964 ms = mergestatemod.mergestate.read(repo)
5963
5965
5964 if not (ms.active() or repo.dirstate.p2() != nullid):
5966 if not (ms.active() or repo.dirstate.p2() != nullid):
5965 raise error.Abort(
5967 raise error.Abort(
5966 _(b'resolve command not applicable when not merging')
5968 _(b'resolve command not applicable when not merging')
5967 )
5969 )
5968
5970
5969 wctx = repo[None]
5971 wctx = repo[None]
5970 m = scmutil.match(wctx, pats, opts)
5972 m = scmutil.match(wctx, pats, opts)
5971 ret = 0
5973 ret = 0
5972 didwork = False
5974 didwork = False
5973
5975
5974 tocomplete = []
5976 tocomplete = []
5975 hasconflictmarkers = []
5977 hasconflictmarkers = []
5976 if mark:
5978 if mark:
5977 markcheck = ui.config(b'commands', b'resolve.mark-check')
5979 markcheck = ui.config(b'commands', b'resolve.mark-check')
5978 if markcheck not in [b'warn', b'abort']:
5980 if markcheck not in [b'warn', b'abort']:
5979 # Treat all invalid / unrecognized values as 'none'.
5981 # Treat all invalid / unrecognized values as 'none'.
5980 markcheck = False
5982 markcheck = False
5981 for f in ms:
5983 for f in ms:
5982 if not m(f):
5984 if not m(f):
5983 continue
5985 continue
5984
5986
5985 didwork = True
5987 didwork = True
5986
5988
5987 # path conflicts must be resolved manually
5989 # path conflicts must be resolved manually
5988 if ms[f] in (
5990 if ms[f] in (
5989 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5991 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5990 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5992 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5991 ):
5993 ):
5992 if mark:
5994 if mark:
5993 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5995 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5994 elif unmark:
5996 elif unmark:
5995 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5997 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5996 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5998 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5997 ui.warn(
5999 ui.warn(
5998 _(b'%s: path conflict must be resolved manually\n')
6000 _(b'%s: path conflict must be resolved manually\n')
5999 % uipathfn(f)
6001 % uipathfn(f)
6000 )
6002 )
6001 continue
6003 continue
6002
6004
6003 if mark:
6005 if mark:
6004 if markcheck:
6006 if markcheck:
6005 fdata = repo.wvfs.tryread(f)
6007 fdata = repo.wvfs.tryread(f)
6006 if (
6008 if (
6007 filemerge.hasconflictmarkers(fdata)
6009 filemerge.hasconflictmarkers(fdata)
6008 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6010 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6009 ):
6011 ):
6010 hasconflictmarkers.append(f)
6012 hasconflictmarkers.append(f)
6011 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6013 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6012 elif unmark:
6014 elif unmark:
6013 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6015 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6014 else:
6016 else:
6015 # backup pre-resolve (merge uses .orig for its own purposes)
6017 # backup pre-resolve (merge uses .orig for its own purposes)
6016 a = repo.wjoin(f)
6018 a = repo.wjoin(f)
6017 try:
6019 try:
6018 util.copyfile(a, a + b".resolve")
6020 util.copyfile(a, a + b".resolve")
6019 except (IOError, OSError) as inst:
6021 except (IOError, OSError) as inst:
6020 if inst.errno != errno.ENOENT:
6022 if inst.errno != errno.ENOENT:
6021 raise
6023 raise
6022
6024
6023 try:
6025 try:
6024 # preresolve file
6026 # preresolve file
6025 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6027 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6026 with ui.configoverride(overrides, b'resolve'):
6028 with ui.configoverride(overrides, b'resolve'):
6027 complete, r = ms.preresolve(f, wctx)
6029 complete, r = ms.preresolve(f, wctx)
6028 if not complete:
6030 if not complete:
6029 tocomplete.append(f)
6031 tocomplete.append(f)
6030 elif r:
6032 elif r:
6031 ret = 1
6033 ret = 1
6032 finally:
6034 finally:
6033 ms.commit()
6035 ms.commit()
6034
6036
6035 # replace filemerge's .orig file with our resolve file, but only
6037 # replace filemerge's .orig file with our resolve file, but only
6036 # for merges that are complete
6038 # for merges that are complete
6037 if complete:
6039 if complete:
6038 try:
6040 try:
6039 util.rename(
6041 util.rename(
6040 a + b".resolve", scmutil.backuppath(ui, repo, f)
6042 a + b".resolve", scmutil.backuppath(ui, repo, f)
6041 )
6043 )
6042 except OSError as inst:
6044 except OSError as inst:
6043 if inst.errno != errno.ENOENT:
6045 if inst.errno != errno.ENOENT:
6044 raise
6046 raise
6045
6047
6046 if hasconflictmarkers:
6048 if hasconflictmarkers:
6047 ui.warn(
6049 ui.warn(
6048 _(
6050 _(
6049 b'warning: the following files still have conflict '
6051 b'warning: the following files still have conflict '
6050 b'markers:\n'
6052 b'markers:\n'
6051 )
6053 )
6052 + b''.join(
6054 + b''.join(
6053 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6055 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6054 )
6056 )
6055 )
6057 )
6056 if markcheck == b'abort' and not all and not pats:
6058 if markcheck == b'abort' and not all and not pats:
6057 raise error.Abort(
6059 raise error.Abort(
6058 _(b'conflict markers detected'),
6060 _(b'conflict markers detected'),
6059 hint=_(b'use --all to mark anyway'),
6061 hint=_(b'use --all to mark anyway'),
6060 )
6062 )
6061
6063
6062 for f in tocomplete:
6064 for f in tocomplete:
6063 try:
6065 try:
6064 # resolve file
6066 # resolve file
6065 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6067 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6066 with ui.configoverride(overrides, b'resolve'):
6068 with ui.configoverride(overrides, b'resolve'):
6067 r = ms.resolve(f, wctx)
6069 r = ms.resolve(f, wctx)
6068 if r:
6070 if r:
6069 ret = 1
6071 ret = 1
6070 finally:
6072 finally:
6071 ms.commit()
6073 ms.commit()
6072
6074
6073 # replace filemerge's .orig file with our resolve file
6075 # replace filemerge's .orig file with our resolve file
6074 a = repo.wjoin(f)
6076 a = repo.wjoin(f)
6075 try:
6077 try:
6076 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6078 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6077 except OSError as inst:
6079 except OSError as inst:
6078 if inst.errno != errno.ENOENT:
6080 if inst.errno != errno.ENOENT:
6079 raise
6081 raise
6080
6082
6081 ms.commit()
6083 ms.commit()
6082 branchmerge = repo.dirstate.p2() != nullid
6084 branchmerge = repo.dirstate.p2() != nullid
6083 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6085 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6084
6086
6085 if not didwork and pats:
6087 if not didwork and pats:
6086 hint = None
6088 hint = None
6087 if not any([p for p in pats if p.find(b':') >= 0]):
6089 if not any([p for p in pats if p.find(b':') >= 0]):
6088 pats = [b'path:%s' % p for p in pats]
6090 pats = [b'path:%s' % p for p in pats]
6089 m = scmutil.match(wctx, pats, opts)
6091 m = scmutil.match(wctx, pats, opts)
6090 for f in ms:
6092 for f in ms:
6091 if not m(f):
6093 if not m(f):
6092 continue
6094 continue
6093
6095
6094 def flag(o):
6096 def flag(o):
6095 if o == b're_merge':
6097 if o == b're_merge':
6096 return b'--re-merge '
6098 return b'--re-merge '
6097 return b'-%s ' % o[0:1]
6099 return b'-%s ' % o[0:1]
6098
6100
6099 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6101 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6100 hint = _(b"(try: hg resolve %s%s)\n") % (
6102 hint = _(b"(try: hg resolve %s%s)\n") % (
6101 flags,
6103 flags,
6102 b' '.join(pats),
6104 b' '.join(pats),
6103 )
6105 )
6104 break
6106 break
6105 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6107 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6106 if hint:
6108 if hint:
6107 ui.warn(hint)
6109 ui.warn(hint)
6108
6110
6109 unresolvedf = list(ms.unresolved())
6111 unresolvedf = list(ms.unresolved())
6110 if not unresolvedf:
6112 if not unresolvedf:
6111 ui.status(_(b'(no more unresolved files)\n'))
6113 ui.status(_(b'(no more unresolved files)\n'))
6112 cmdutil.checkafterresolved(repo)
6114 cmdutil.checkafterresolved(repo)
6113
6115
6114 return ret
6116 return ret
6115
6117
6116
6118
6117 @command(
6119 @command(
6118 b'revert',
6120 b'revert',
6119 [
6121 [
6120 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6122 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6121 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6123 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6122 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6124 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6123 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6125 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6124 (b'i', b'interactive', None, _(b'interactively select the changes')),
6126 (b'i', b'interactive', None, _(b'interactively select the changes')),
6125 ]
6127 ]
6126 + walkopts
6128 + walkopts
6127 + dryrunopts,
6129 + dryrunopts,
6128 _(b'[OPTION]... [-r REV] [NAME]...'),
6130 _(b'[OPTION]... [-r REV] [NAME]...'),
6129 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6131 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6130 )
6132 )
6131 def revert(ui, repo, *pats, **opts):
6133 def revert(ui, repo, *pats, **opts):
6132 """restore files to their checkout state
6134 """restore files to their checkout state
6133
6135
6134 .. note::
6136 .. note::
6135
6137
6136 To check out earlier revisions, you should use :hg:`update REV`.
6138 To check out earlier revisions, you should use :hg:`update REV`.
6137 To cancel an uncommitted merge (and lose your changes),
6139 To cancel an uncommitted merge (and lose your changes),
6138 use :hg:`merge --abort`.
6140 use :hg:`merge --abort`.
6139
6141
6140 With no revision specified, revert the specified files or directories
6142 With no revision specified, revert the specified files or directories
6141 to the contents they had in the parent of the working directory.
6143 to the contents they had in the parent of the working directory.
6142 This restores the contents of files to an unmodified
6144 This restores the contents of files to an unmodified
6143 state and unschedules adds, removes, copies, and renames. If the
6145 state and unschedules adds, removes, copies, and renames. If the
6144 working directory has two parents, you must explicitly specify a
6146 working directory has two parents, you must explicitly specify a
6145 revision.
6147 revision.
6146
6148
6147 Using the -r/--rev or -d/--date options, revert the given files or
6149 Using the -r/--rev or -d/--date options, revert the given files or
6148 directories to their states as of a specific revision. Because
6150 directories to their states as of a specific revision. Because
6149 revert does not change the working directory parents, this will
6151 revert does not change the working directory parents, this will
6150 cause these files to appear modified. This can be helpful to "back
6152 cause these files to appear modified. This can be helpful to "back
6151 out" some or all of an earlier change. See :hg:`backout` for a
6153 out" some or all of an earlier change. See :hg:`backout` for a
6152 related method.
6154 related method.
6153
6155
6154 Modified files are saved with a .orig suffix before reverting.
6156 Modified files are saved with a .orig suffix before reverting.
6155 To disable these backups, use --no-backup. It is possible to store
6157 To disable these backups, use --no-backup. It is possible to store
6156 the backup files in a custom directory relative to the root of the
6158 the backup files in a custom directory relative to the root of the
6157 repository by setting the ``ui.origbackuppath`` configuration
6159 repository by setting the ``ui.origbackuppath`` configuration
6158 option.
6160 option.
6159
6161
6160 See :hg:`help dates` for a list of formats valid for -d/--date.
6162 See :hg:`help dates` for a list of formats valid for -d/--date.
6161
6163
6162 See :hg:`help backout` for a way to reverse the effect of an
6164 See :hg:`help backout` for a way to reverse the effect of an
6163 earlier changeset.
6165 earlier changeset.
6164
6166
6165 Returns 0 on success.
6167 Returns 0 on success.
6166 """
6168 """
6167
6169
6168 opts = pycompat.byteskwargs(opts)
6170 opts = pycompat.byteskwargs(opts)
6169 if opts.get(b"date"):
6171 if opts.get(b"date"):
6170 if opts.get(b"rev"):
6172 if opts.get(b"rev"):
6171 raise error.Abort(_(b"you can't specify a revision and a date"))
6173 raise error.Abort(_(b"you can't specify a revision and a date"))
6172 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6174 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6173
6175
6174 parent, p2 = repo.dirstate.parents()
6176 parent, p2 = repo.dirstate.parents()
6175 if not opts.get(b'rev') and p2 != nullid:
6177 if not opts.get(b'rev') and p2 != nullid:
6176 # revert after merge is a trap for new users (issue2915)
6178 # revert after merge is a trap for new users (issue2915)
6177 raise error.Abort(
6179 raise error.Abort(
6178 _(b'uncommitted merge with no revision specified'),
6180 _(b'uncommitted merge with no revision specified'),
6179 hint=_(b"use 'hg update' or see 'hg help revert'"),
6181 hint=_(b"use 'hg update' or see 'hg help revert'"),
6180 )
6182 )
6181
6183
6182 rev = opts.get(b'rev')
6184 rev = opts.get(b'rev')
6183 if rev:
6185 if rev:
6184 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6186 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6185 ctx = scmutil.revsingle(repo, rev)
6187 ctx = scmutil.revsingle(repo, rev)
6186
6188
6187 if not (
6189 if not (
6188 pats
6190 pats
6189 or opts.get(b'include')
6191 or opts.get(b'include')
6190 or opts.get(b'exclude')
6192 or opts.get(b'exclude')
6191 or opts.get(b'all')
6193 or opts.get(b'all')
6192 or opts.get(b'interactive')
6194 or opts.get(b'interactive')
6193 ):
6195 ):
6194 msg = _(b"no files or directories specified")
6196 msg = _(b"no files or directories specified")
6195 if p2 != nullid:
6197 if p2 != nullid:
6196 hint = _(
6198 hint = _(
6197 b"uncommitted merge, use --all to discard all changes,"
6199 b"uncommitted merge, use --all to discard all changes,"
6198 b" or 'hg update -C .' to abort the merge"
6200 b" or 'hg update -C .' to abort the merge"
6199 )
6201 )
6200 raise error.Abort(msg, hint=hint)
6202 raise error.Abort(msg, hint=hint)
6201 dirty = any(repo.status())
6203 dirty = any(repo.status())
6202 node = ctx.node()
6204 node = ctx.node()
6203 if node != parent:
6205 if node != parent:
6204 if dirty:
6206 if dirty:
6205 hint = (
6207 hint = (
6206 _(
6208 _(
6207 b"uncommitted changes, use --all to discard all"
6209 b"uncommitted changes, use --all to discard all"
6208 b" changes, or 'hg update %d' to update"
6210 b" changes, or 'hg update %d' to update"
6209 )
6211 )
6210 % ctx.rev()
6212 % ctx.rev()
6211 )
6213 )
6212 else:
6214 else:
6213 hint = (
6215 hint = (
6214 _(
6216 _(
6215 b"use --all to revert all files,"
6217 b"use --all to revert all files,"
6216 b" or 'hg update %d' to update"
6218 b" or 'hg update %d' to update"
6217 )
6219 )
6218 % ctx.rev()
6220 % ctx.rev()
6219 )
6221 )
6220 elif dirty:
6222 elif dirty:
6221 hint = _(b"uncommitted changes, use --all to discard all changes")
6223 hint = _(b"uncommitted changes, use --all to discard all changes")
6222 else:
6224 else:
6223 hint = _(b"use --all to revert all files")
6225 hint = _(b"use --all to revert all files")
6224 raise error.Abort(msg, hint=hint)
6226 raise error.Abort(msg, hint=hint)
6225
6227
6226 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6228 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6227
6229
6228
6230
6229 @command(
6231 @command(
6230 b'rollback',
6232 b'rollback',
6231 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6233 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6232 helpcategory=command.CATEGORY_MAINTENANCE,
6234 helpcategory=command.CATEGORY_MAINTENANCE,
6233 )
6235 )
6234 def rollback(ui, repo, **opts):
6236 def rollback(ui, repo, **opts):
6235 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6237 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6236
6238
6237 Please use :hg:`commit --amend` instead of rollback to correct
6239 Please use :hg:`commit --amend` instead of rollback to correct
6238 mistakes in the last commit.
6240 mistakes in the last commit.
6239
6241
6240 This command should be used with care. There is only one level of
6242 This command should be used with care. There is only one level of
6241 rollback, and there is no way to undo a rollback. It will also
6243 rollback, and there is no way to undo a rollback. It will also
6242 restore the dirstate at the time of the last transaction, losing
6244 restore the dirstate at the time of the last transaction, losing
6243 any dirstate changes since that time. This command does not alter
6245 any dirstate changes since that time. This command does not alter
6244 the working directory.
6246 the working directory.
6245
6247
6246 Transactions are used to encapsulate the effects of all commands
6248 Transactions are used to encapsulate the effects of all commands
6247 that create new changesets or propagate existing changesets into a
6249 that create new changesets or propagate existing changesets into a
6248 repository.
6250 repository.
6249
6251
6250 .. container:: verbose
6252 .. container:: verbose
6251
6253
6252 For example, the following commands are transactional, and their
6254 For example, the following commands are transactional, and their
6253 effects can be rolled back:
6255 effects can be rolled back:
6254
6256
6255 - commit
6257 - commit
6256 - import
6258 - import
6257 - pull
6259 - pull
6258 - push (with this repository as the destination)
6260 - push (with this repository as the destination)
6259 - unbundle
6261 - unbundle
6260
6262
6261 To avoid permanent data loss, rollback will refuse to rollback a
6263 To avoid permanent data loss, rollback will refuse to rollback a
6262 commit transaction if it isn't checked out. Use --force to
6264 commit transaction if it isn't checked out. Use --force to
6263 override this protection.
6265 override this protection.
6264
6266
6265 The rollback command can be entirely disabled by setting the
6267 The rollback command can be entirely disabled by setting the
6266 ``ui.rollback`` configuration setting to false. If you're here
6268 ``ui.rollback`` configuration setting to false. If you're here
6267 because you want to use rollback and it's disabled, you can
6269 because you want to use rollback and it's disabled, you can
6268 re-enable the command by setting ``ui.rollback`` to true.
6270 re-enable the command by setting ``ui.rollback`` to true.
6269
6271
6270 This command is not intended for use on public repositories. Once
6272 This command is not intended for use on public repositories. Once
6271 changes are visible for pull by other users, rolling a transaction
6273 changes are visible for pull by other users, rolling a transaction
6272 back locally is ineffective (someone else may already have pulled
6274 back locally is ineffective (someone else may already have pulled
6273 the changes). Furthermore, a race is possible with readers of the
6275 the changes). Furthermore, a race is possible with readers of the
6274 repository; for example an in-progress pull from the repository
6276 repository; for example an in-progress pull from the repository
6275 may fail if a rollback is performed.
6277 may fail if a rollback is performed.
6276
6278
6277 Returns 0 on success, 1 if no rollback data is available.
6279 Returns 0 on success, 1 if no rollback data is available.
6278 """
6280 """
6279 if not ui.configbool(b'ui', b'rollback'):
6281 if not ui.configbool(b'ui', b'rollback'):
6280 raise error.Abort(
6282 raise error.Abort(
6281 _(b'rollback is disabled because it is unsafe'),
6283 _(b'rollback is disabled because it is unsafe'),
6282 hint=b'see `hg help -v rollback` for information',
6284 hint=b'see `hg help -v rollback` for information',
6283 )
6285 )
6284 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6286 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6285
6287
6286
6288
6287 @command(
6289 @command(
6288 b'root',
6290 b'root',
6289 [] + formatteropts,
6291 [] + formatteropts,
6290 intents={INTENT_READONLY},
6292 intents={INTENT_READONLY},
6291 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6293 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6292 )
6294 )
6293 def root(ui, repo, **opts):
6295 def root(ui, repo, **opts):
6294 """print the root (top) of the current working directory
6296 """print the root (top) of the current working directory
6295
6297
6296 Print the root directory of the current repository.
6298 Print the root directory of the current repository.
6297
6299
6298 .. container:: verbose
6300 .. container:: verbose
6299
6301
6300 Template:
6302 Template:
6301
6303
6302 The following keywords are supported in addition to the common template
6304 The following keywords are supported in addition to the common template
6303 keywords and functions. See also :hg:`help templates`.
6305 keywords and functions. See also :hg:`help templates`.
6304
6306
6305 :hgpath: String. Path to the .hg directory.
6307 :hgpath: String. Path to the .hg directory.
6306 :storepath: String. Path to the directory holding versioned data.
6308 :storepath: String. Path to the directory holding versioned data.
6307
6309
6308 Returns 0 on success.
6310 Returns 0 on success.
6309 """
6311 """
6310 opts = pycompat.byteskwargs(opts)
6312 opts = pycompat.byteskwargs(opts)
6311 with ui.formatter(b'root', opts) as fm:
6313 with ui.formatter(b'root', opts) as fm:
6312 fm.startitem()
6314 fm.startitem()
6313 fm.write(b'reporoot', b'%s\n', repo.root)
6315 fm.write(b'reporoot', b'%s\n', repo.root)
6314 fm.data(hgpath=repo.path, storepath=repo.spath)
6316 fm.data(hgpath=repo.path, storepath=repo.spath)
6315
6317
6316
6318
6317 @command(
6319 @command(
6318 b'serve',
6320 b'serve',
6319 [
6321 [
6320 (
6322 (
6321 b'A',
6323 b'A',
6322 b'accesslog',
6324 b'accesslog',
6323 b'',
6325 b'',
6324 _(b'name of access log file to write to'),
6326 _(b'name of access log file to write to'),
6325 _(b'FILE'),
6327 _(b'FILE'),
6326 ),
6328 ),
6327 (b'd', b'daemon', None, _(b'run server in background')),
6329 (b'd', b'daemon', None, _(b'run server in background')),
6328 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6330 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6329 (
6331 (
6330 b'E',
6332 b'E',
6331 b'errorlog',
6333 b'errorlog',
6332 b'',
6334 b'',
6333 _(b'name of error log file to write to'),
6335 _(b'name of error log file to write to'),
6334 _(b'FILE'),
6336 _(b'FILE'),
6335 ),
6337 ),
6336 # use string type, then we can check if something was passed
6338 # use string type, then we can check if something was passed
6337 (
6339 (
6338 b'p',
6340 b'p',
6339 b'port',
6341 b'port',
6340 b'',
6342 b'',
6341 _(b'port to listen on (default: 8000)'),
6343 _(b'port to listen on (default: 8000)'),
6342 _(b'PORT'),
6344 _(b'PORT'),
6343 ),
6345 ),
6344 (
6346 (
6345 b'a',
6347 b'a',
6346 b'address',
6348 b'address',
6347 b'',
6349 b'',
6348 _(b'address to listen on (default: all interfaces)'),
6350 _(b'address to listen on (default: all interfaces)'),
6349 _(b'ADDR'),
6351 _(b'ADDR'),
6350 ),
6352 ),
6351 (
6353 (
6352 b'',
6354 b'',
6353 b'prefix',
6355 b'prefix',
6354 b'',
6356 b'',
6355 _(b'prefix path to serve from (default: server root)'),
6357 _(b'prefix path to serve from (default: server root)'),
6356 _(b'PREFIX'),
6358 _(b'PREFIX'),
6357 ),
6359 ),
6358 (
6360 (
6359 b'n',
6361 b'n',
6360 b'name',
6362 b'name',
6361 b'',
6363 b'',
6362 _(b'name to show in web pages (default: working directory)'),
6364 _(b'name to show in web pages (default: working directory)'),
6363 _(b'NAME'),
6365 _(b'NAME'),
6364 ),
6366 ),
6365 (
6367 (
6366 b'',
6368 b'',
6367 b'web-conf',
6369 b'web-conf',
6368 b'',
6370 b'',
6369 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6371 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6370 _(b'FILE'),
6372 _(b'FILE'),
6371 ),
6373 ),
6372 (
6374 (
6373 b'',
6375 b'',
6374 b'webdir-conf',
6376 b'webdir-conf',
6375 b'',
6377 b'',
6376 _(b'name of the hgweb config file (DEPRECATED)'),
6378 _(b'name of the hgweb config file (DEPRECATED)'),
6377 _(b'FILE'),
6379 _(b'FILE'),
6378 ),
6380 ),
6379 (
6381 (
6380 b'',
6382 b'',
6381 b'pid-file',
6383 b'pid-file',
6382 b'',
6384 b'',
6383 _(b'name of file to write process ID to'),
6385 _(b'name of file to write process ID to'),
6384 _(b'FILE'),
6386 _(b'FILE'),
6385 ),
6387 ),
6386 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6388 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6387 (
6389 (
6388 b'',
6390 b'',
6389 b'cmdserver',
6391 b'cmdserver',
6390 b'',
6392 b'',
6391 _(b'for remote clients (ADVANCED)'),
6393 _(b'for remote clients (ADVANCED)'),
6392 _(b'MODE'),
6394 _(b'MODE'),
6393 ),
6395 ),
6394 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6396 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6395 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6397 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6396 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6398 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6397 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6399 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6398 (b'', b'print-url', None, _(b'start and print only the URL')),
6400 (b'', b'print-url', None, _(b'start and print only the URL')),
6399 ]
6401 ]
6400 + subrepoopts,
6402 + subrepoopts,
6401 _(b'[OPTION]...'),
6403 _(b'[OPTION]...'),
6402 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6404 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6403 helpbasic=True,
6405 helpbasic=True,
6404 optionalrepo=True,
6406 optionalrepo=True,
6405 )
6407 )
6406 def serve(ui, repo, **opts):
6408 def serve(ui, repo, **opts):
6407 """start stand-alone webserver
6409 """start stand-alone webserver
6408
6410
6409 Start a local HTTP repository browser and pull server. You can use
6411 Start a local HTTP repository browser and pull server. You can use
6410 this for ad-hoc sharing and browsing of repositories. It is
6412 this for ad-hoc sharing and browsing of repositories. It is
6411 recommended to use a real web server to serve a repository for
6413 recommended to use a real web server to serve a repository for
6412 longer periods of time.
6414 longer periods of time.
6413
6415
6414 Please note that the server does not implement access control.
6416 Please note that the server does not implement access control.
6415 This means that, by default, anybody can read from the server and
6417 This means that, by default, anybody can read from the server and
6416 nobody can write to it by default. Set the ``web.allow-push``
6418 nobody can write to it by default. Set the ``web.allow-push``
6417 option to ``*`` to allow everybody to push to the server. You
6419 option to ``*`` to allow everybody to push to the server. You
6418 should use a real web server if you need to authenticate users.
6420 should use a real web server if you need to authenticate users.
6419
6421
6420 By default, the server logs accesses to stdout and errors to
6422 By default, the server logs accesses to stdout and errors to
6421 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6423 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6422 files.
6424 files.
6423
6425
6424 To have the server choose a free port number to listen on, specify
6426 To have the server choose a free port number to listen on, specify
6425 a port number of 0; in this case, the server will print the port
6427 a port number of 0; in this case, the server will print the port
6426 number it uses.
6428 number it uses.
6427
6429
6428 Returns 0 on success.
6430 Returns 0 on success.
6429 """
6431 """
6430
6432
6431 opts = pycompat.byteskwargs(opts)
6433 opts = pycompat.byteskwargs(opts)
6432 if opts[b"stdio"] and opts[b"cmdserver"]:
6434 if opts[b"stdio"] and opts[b"cmdserver"]:
6433 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6435 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6434 if opts[b"print_url"] and ui.verbose:
6436 if opts[b"print_url"] and ui.verbose:
6435 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6437 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6436
6438
6437 if opts[b"stdio"]:
6439 if opts[b"stdio"]:
6438 if repo is None:
6440 if repo is None:
6439 raise error.RepoError(
6441 raise error.RepoError(
6440 _(b"there is no Mercurial repository here (.hg not found)")
6442 _(b"there is no Mercurial repository here (.hg not found)")
6441 )
6443 )
6442 s = wireprotoserver.sshserver(ui, repo)
6444 s = wireprotoserver.sshserver(ui, repo)
6443 s.serve_forever()
6445 s.serve_forever()
6444
6446
6445 service = server.createservice(ui, repo, opts)
6447 service = server.createservice(ui, repo, opts)
6446 return server.runservice(opts, initfn=service.init, runfn=service.run)
6448 return server.runservice(opts, initfn=service.init, runfn=service.run)
6447
6449
6448
6450
6449 @command(
6451 @command(
6450 b'shelve',
6452 b'shelve',
6451 [
6453 [
6452 (
6454 (
6453 b'A',
6455 b'A',
6454 b'addremove',
6456 b'addremove',
6455 None,
6457 None,
6456 _(b'mark new/missing files as added/removed before shelving'),
6458 _(b'mark new/missing files as added/removed before shelving'),
6457 ),
6459 ),
6458 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6460 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6459 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6461 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6460 (
6462 (
6461 b'',
6463 b'',
6462 b'date',
6464 b'date',
6463 b'',
6465 b'',
6464 _(b'shelve with the specified commit date'),
6466 _(b'shelve with the specified commit date'),
6465 _(b'DATE'),
6467 _(b'DATE'),
6466 ),
6468 ),
6467 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6469 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6468 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6470 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6469 (
6471 (
6470 b'k',
6472 b'k',
6471 b'keep',
6473 b'keep',
6472 False,
6474 False,
6473 _(b'shelve, but keep changes in the working directory'),
6475 _(b'shelve, but keep changes in the working directory'),
6474 ),
6476 ),
6475 (b'l', b'list', None, _(b'list current shelves')),
6477 (b'l', b'list', None, _(b'list current shelves')),
6476 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6478 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6477 (
6479 (
6478 b'n',
6480 b'n',
6479 b'name',
6481 b'name',
6480 b'',
6482 b'',
6481 _(b'use the given name for the shelved commit'),
6483 _(b'use the given name for the shelved commit'),
6482 _(b'NAME'),
6484 _(b'NAME'),
6483 ),
6485 ),
6484 (
6486 (
6485 b'p',
6487 b'p',
6486 b'patch',
6488 b'patch',
6487 None,
6489 None,
6488 _(
6490 _(
6489 b'output patches for changes (provide the names of the shelved '
6491 b'output patches for changes (provide the names of the shelved '
6490 b'changes as positional arguments)'
6492 b'changes as positional arguments)'
6491 ),
6493 ),
6492 ),
6494 ),
6493 (b'i', b'interactive', None, _(b'interactive mode')),
6495 (b'i', b'interactive', None, _(b'interactive mode')),
6494 (
6496 (
6495 b'',
6497 b'',
6496 b'stat',
6498 b'stat',
6497 None,
6499 None,
6498 _(
6500 _(
6499 b'output diffstat-style summary of changes (provide the names of '
6501 b'output diffstat-style summary of changes (provide the names of '
6500 b'the shelved changes as positional arguments)'
6502 b'the shelved changes as positional arguments)'
6501 ),
6503 ),
6502 ),
6504 ),
6503 ]
6505 ]
6504 + cmdutil.walkopts,
6506 + cmdutil.walkopts,
6505 _(b'hg shelve [OPTION]... [FILE]...'),
6507 _(b'hg shelve [OPTION]... [FILE]...'),
6506 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6508 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6507 )
6509 )
6508 def shelve(ui, repo, *pats, **opts):
6510 def shelve(ui, repo, *pats, **opts):
6509 '''save and set aside changes from the working directory
6511 '''save and set aside changes from the working directory
6510
6512
6511 Shelving takes files that "hg status" reports as not clean, saves
6513 Shelving takes files that "hg status" reports as not clean, saves
6512 the modifications to a bundle (a shelved change), and reverts the
6514 the modifications to a bundle (a shelved change), and reverts the
6513 files so that their state in the working directory becomes clean.
6515 files so that their state in the working directory becomes clean.
6514
6516
6515 To restore these changes to the working directory, using "hg
6517 To restore these changes to the working directory, using "hg
6516 unshelve"; this will work even if you switch to a different
6518 unshelve"; this will work even if you switch to a different
6517 commit.
6519 commit.
6518
6520
6519 When no files are specified, "hg shelve" saves all not-clean
6521 When no files are specified, "hg shelve" saves all not-clean
6520 files. If specific files or directories are named, only changes to
6522 files. If specific files or directories are named, only changes to
6521 those files are shelved.
6523 those files are shelved.
6522
6524
6523 In bare shelve (when no files are specified, without interactive,
6525 In bare shelve (when no files are specified, without interactive,
6524 include and exclude option), shelving remembers information if the
6526 include and exclude option), shelving remembers information if the
6525 working directory was on newly created branch, in other words working
6527 working directory was on newly created branch, in other words working
6526 directory was on different branch than its first parent. In this
6528 directory was on different branch than its first parent. In this
6527 situation unshelving restores branch information to the working directory.
6529 situation unshelving restores branch information to the working directory.
6528
6530
6529 Each shelved change has a name that makes it easier to find later.
6531 Each shelved change has a name that makes it easier to find later.
6530 The name of a shelved change defaults to being based on the active
6532 The name of a shelved change defaults to being based on the active
6531 bookmark, or if there is no active bookmark, the current named
6533 bookmark, or if there is no active bookmark, the current named
6532 branch. To specify a different name, use ``--name``.
6534 branch. To specify a different name, use ``--name``.
6533
6535
6534 To see a list of existing shelved changes, use the ``--list``
6536 To see a list of existing shelved changes, use the ``--list``
6535 option. For each shelved change, this will print its name, age,
6537 option. For each shelved change, this will print its name, age,
6536 and description; use ``--patch`` or ``--stat`` for more details.
6538 and description; use ``--patch`` or ``--stat`` for more details.
6537
6539
6538 To delete specific shelved changes, use ``--delete``. To delete
6540 To delete specific shelved changes, use ``--delete``. To delete
6539 all shelved changes, use ``--cleanup``.
6541 all shelved changes, use ``--cleanup``.
6540 '''
6542 '''
6541 opts = pycompat.byteskwargs(opts)
6543 opts = pycompat.byteskwargs(opts)
6542 allowables = [
6544 allowables = [
6543 (b'addremove', {b'create'}), # 'create' is pseudo action
6545 (b'addremove', {b'create'}), # 'create' is pseudo action
6544 (b'unknown', {b'create'}),
6546 (b'unknown', {b'create'}),
6545 (b'cleanup', {b'cleanup'}),
6547 (b'cleanup', {b'cleanup'}),
6546 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6548 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6547 (b'delete', {b'delete'}),
6549 (b'delete', {b'delete'}),
6548 (b'edit', {b'create'}),
6550 (b'edit', {b'create'}),
6549 (b'keep', {b'create'}),
6551 (b'keep', {b'create'}),
6550 (b'list', {b'list'}),
6552 (b'list', {b'list'}),
6551 (b'message', {b'create'}),
6553 (b'message', {b'create'}),
6552 (b'name', {b'create'}),
6554 (b'name', {b'create'}),
6553 (b'patch', {b'patch', b'list'}),
6555 (b'patch', {b'patch', b'list'}),
6554 (b'stat', {b'stat', b'list'}),
6556 (b'stat', {b'stat', b'list'}),
6555 ]
6557 ]
6556
6558
6557 def checkopt(opt):
6559 def checkopt(opt):
6558 if opts.get(opt):
6560 if opts.get(opt):
6559 for i, allowable in allowables:
6561 for i, allowable in allowables:
6560 if opts[i] and opt not in allowable:
6562 if opts[i] and opt not in allowable:
6561 raise error.Abort(
6563 raise error.Abort(
6562 _(
6564 _(
6563 b"options '--%s' and '--%s' may not be "
6565 b"options '--%s' and '--%s' may not be "
6564 b"used together"
6566 b"used together"
6565 )
6567 )
6566 % (opt, i)
6568 % (opt, i)
6567 )
6569 )
6568 return True
6570 return True
6569
6571
6570 if checkopt(b'cleanup'):
6572 if checkopt(b'cleanup'):
6571 if pats:
6573 if pats:
6572 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6574 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6573 return shelvemod.cleanupcmd(ui, repo)
6575 return shelvemod.cleanupcmd(ui, repo)
6574 elif checkopt(b'delete'):
6576 elif checkopt(b'delete'):
6575 return shelvemod.deletecmd(ui, repo, pats)
6577 return shelvemod.deletecmd(ui, repo, pats)
6576 elif checkopt(b'list'):
6578 elif checkopt(b'list'):
6577 return shelvemod.listcmd(ui, repo, pats, opts)
6579 return shelvemod.listcmd(ui, repo, pats, opts)
6578 elif checkopt(b'patch') or checkopt(b'stat'):
6580 elif checkopt(b'patch') or checkopt(b'stat'):
6579 return shelvemod.patchcmds(ui, repo, pats, opts)
6581 return shelvemod.patchcmds(ui, repo, pats, opts)
6580 else:
6582 else:
6581 return shelvemod.createcmd(ui, repo, pats, opts)
6583 return shelvemod.createcmd(ui, repo, pats, opts)
6582
6584
6583
6585
6584 _NOTTERSE = b'nothing'
6586 _NOTTERSE = b'nothing'
6585
6587
6586
6588
6587 @command(
6589 @command(
6588 b'status|st',
6590 b'status|st',
6589 [
6591 [
6590 (b'A', b'all', None, _(b'show status of all files')),
6592 (b'A', b'all', None, _(b'show status of all files')),
6591 (b'm', b'modified', None, _(b'show only modified files')),
6593 (b'm', b'modified', None, _(b'show only modified files')),
6592 (b'a', b'added', None, _(b'show only added files')),
6594 (b'a', b'added', None, _(b'show only added files')),
6593 (b'r', b'removed', None, _(b'show only removed files')),
6595 (b'r', b'removed', None, _(b'show only removed files')),
6594 (b'd', b'deleted', None, _(b'show only missing files')),
6596 (b'd', b'deleted', None, _(b'show only missing files')),
6595 (b'c', b'clean', None, _(b'show only files without changes')),
6597 (b'c', b'clean', None, _(b'show only files without changes')),
6596 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6598 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6597 (b'i', b'ignored', None, _(b'show only ignored files')),
6599 (b'i', b'ignored', None, _(b'show only ignored files')),
6598 (b'n', b'no-status', None, _(b'hide status prefix')),
6600 (b'n', b'no-status', None, _(b'hide status prefix')),
6599 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6601 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6600 (
6602 (
6601 b'C',
6603 b'C',
6602 b'copies',
6604 b'copies',
6603 None,
6605 None,
6604 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6606 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6605 ),
6607 ),
6606 (
6608 (
6607 b'0',
6609 b'0',
6608 b'print0',
6610 b'print0',
6609 None,
6611 None,
6610 _(b'end filenames with NUL, for use with xargs'),
6612 _(b'end filenames with NUL, for use with xargs'),
6611 ),
6613 ),
6612 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6614 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6613 (
6615 (
6614 b'',
6616 b'',
6615 b'change',
6617 b'change',
6616 b'',
6618 b'',
6617 _(b'list the changed files of a revision'),
6619 _(b'list the changed files of a revision'),
6618 _(b'REV'),
6620 _(b'REV'),
6619 ),
6621 ),
6620 ]
6622 ]
6621 + walkopts
6623 + walkopts
6622 + subrepoopts
6624 + subrepoopts
6623 + formatteropts,
6625 + formatteropts,
6624 _(b'[OPTION]... [FILE]...'),
6626 _(b'[OPTION]... [FILE]...'),
6625 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6627 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6626 helpbasic=True,
6628 helpbasic=True,
6627 inferrepo=True,
6629 inferrepo=True,
6628 intents={INTENT_READONLY},
6630 intents={INTENT_READONLY},
6629 )
6631 )
6630 def status(ui, repo, *pats, **opts):
6632 def status(ui, repo, *pats, **opts):
6631 """show changed files in the working directory
6633 """show changed files in the working directory
6632
6634
6633 Show status of files in the repository. If names are given, only
6635 Show status of files in the repository. If names are given, only
6634 files that match are shown. Files that are clean or ignored or
6636 files that match are shown. Files that are clean or ignored or
6635 the source of a copy/move operation, are not listed unless
6637 the source of a copy/move operation, are not listed unless
6636 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6638 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6637 Unless options described with "show only ..." are given, the
6639 Unless options described with "show only ..." are given, the
6638 options -mardu are used.
6640 options -mardu are used.
6639
6641
6640 Option -q/--quiet hides untracked (unknown and ignored) files
6642 Option -q/--quiet hides untracked (unknown and ignored) files
6641 unless explicitly requested with -u/--unknown or -i/--ignored.
6643 unless explicitly requested with -u/--unknown or -i/--ignored.
6642
6644
6643 .. note::
6645 .. note::
6644
6646
6645 :hg:`status` may appear to disagree with diff if permissions have
6647 :hg:`status` may appear to disagree with diff if permissions have
6646 changed or a merge has occurred. The standard diff format does
6648 changed or a merge has occurred. The standard diff format does
6647 not report permission changes and diff only reports changes
6649 not report permission changes and diff only reports changes
6648 relative to one merge parent.
6650 relative to one merge parent.
6649
6651
6650 If one revision is given, it is used as the base revision.
6652 If one revision is given, it is used as the base revision.
6651 If two revisions are given, the differences between them are
6653 If two revisions are given, the differences between them are
6652 shown. The --change option can also be used as a shortcut to list
6654 shown. The --change option can also be used as a shortcut to list
6653 the changed files of a revision from its first parent.
6655 the changed files of a revision from its first parent.
6654
6656
6655 The codes used to show the status of files are::
6657 The codes used to show the status of files are::
6656
6658
6657 M = modified
6659 M = modified
6658 A = added
6660 A = added
6659 R = removed
6661 R = removed
6660 C = clean
6662 C = clean
6661 ! = missing (deleted by non-hg command, but still tracked)
6663 ! = missing (deleted by non-hg command, but still tracked)
6662 ? = not tracked
6664 ? = not tracked
6663 I = ignored
6665 I = ignored
6664 = origin of the previous file (with --copies)
6666 = origin of the previous file (with --copies)
6665
6667
6666 .. container:: verbose
6668 .. container:: verbose
6667
6669
6668 The -t/--terse option abbreviates the output by showing only the directory
6670 The -t/--terse option abbreviates the output by showing only the directory
6669 name if all the files in it share the same status. The option takes an
6671 name if all the files in it share the same status. The option takes an
6670 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6672 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6671 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6673 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6672 for 'ignored' and 'c' for clean.
6674 for 'ignored' and 'c' for clean.
6673
6675
6674 It abbreviates only those statuses which are passed. Note that clean and
6676 It abbreviates only those statuses which are passed. Note that clean and
6675 ignored files are not displayed with '--terse ic' unless the -c/--clean
6677 ignored files are not displayed with '--terse ic' unless the -c/--clean
6676 and -i/--ignored options are also used.
6678 and -i/--ignored options are also used.
6677
6679
6678 The -v/--verbose option shows information when the repository is in an
6680 The -v/--verbose option shows information when the repository is in an
6679 unfinished merge, shelve, rebase state etc. You can have this behavior
6681 unfinished merge, shelve, rebase state etc. You can have this behavior
6680 turned on by default by enabling the ``commands.status.verbose`` option.
6682 turned on by default by enabling the ``commands.status.verbose`` option.
6681
6683
6682 You can skip displaying some of these states by setting
6684 You can skip displaying some of these states by setting
6683 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6685 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6684 'histedit', 'merge', 'rebase', or 'unshelve'.
6686 'histedit', 'merge', 'rebase', or 'unshelve'.
6685
6687
6686 Template:
6688 Template:
6687
6689
6688 The following keywords are supported in addition to the common template
6690 The following keywords are supported in addition to the common template
6689 keywords and functions. See also :hg:`help templates`.
6691 keywords and functions. See also :hg:`help templates`.
6690
6692
6691 :path: String. Repository-absolute path of the file.
6693 :path: String. Repository-absolute path of the file.
6692 :source: String. Repository-absolute path of the file originated from.
6694 :source: String. Repository-absolute path of the file originated from.
6693 Available if ``--copies`` is specified.
6695 Available if ``--copies`` is specified.
6694 :status: String. Character denoting file's status.
6696 :status: String. Character denoting file's status.
6695
6697
6696 Examples:
6698 Examples:
6697
6699
6698 - show changes in the working directory relative to a
6700 - show changes in the working directory relative to a
6699 changeset::
6701 changeset::
6700
6702
6701 hg status --rev 9353
6703 hg status --rev 9353
6702
6704
6703 - show changes in the working directory relative to the
6705 - show changes in the working directory relative to the
6704 current directory (see :hg:`help patterns` for more information)::
6706 current directory (see :hg:`help patterns` for more information)::
6705
6707
6706 hg status re:
6708 hg status re:
6707
6709
6708 - show all changes including copies in an existing changeset::
6710 - show all changes including copies in an existing changeset::
6709
6711
6710 hg status --copies --change 9353
6712 hg status --copies --change 9353
6711
6713
6712 - get a NUL separated list of added files, suitable for xargs::
6714 - get a NUL separated list of added files, suitable for xargs::
6713
6715
6714 hg status -an0
6716 hg status -an0
6715
6717
6716 - show more information about the repository status, abbreviating
6718 - show more information about the repository status, abbreviating
6717 added, removed, modified, deleted, and untracked paths::
6719 added, removed, modified, deleted, and untracked paths::
6718
6720
6719 hg status -v -t mardu
6721 hg status -v -t mardu
6720
6722
6721 Returns 0 on success.
6723 Returns 0 on success.
6722
6724
6723 """
6725 """
6724
6726
6725 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6727 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6726 opts = pycompat.byteskwargs(opts)
6728 opts = pycompat.byteskwargs(opts)
6727 revs = opts.get(b'rev')
6729 revs = opts.get(b'rev')
6728 change = opts.get(b'change')
6730 change = opts.get(b'change')
6729 terse = opts.get(b'terse')
6731 terse = opts.get(b'terse')
6730 if terse is _NOTTERSE:
6732 if terse is _NOTTERSE:
6731 if revs:
6733 if revs:
6732 terse = b''
6734 terse = b''
6733 else:
6735 else:
6734 terse = ui.config(b'commands', b'status.terse')
6736 terse = ui.config(b'commands', b'status.terse')
6735
6737
6736 if revs and terse:
6738 if revs and terse:
6737 msg = _(b'cannot use --terse with --rev')
6739 msg = _(b'cannot use --terse with --rev')
6738 raise error.Abort(msg)
6740 raise error.Abort(msg)
6739 elif change:
6741 elif change:
6740 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6742 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6741 ctx2 = scmutil.revsingle(repo, change, None)
6743 ctx2 = scmutil.revsingle(repo, change, None)
6742 ctx1 = ctx2.p1()
6744 ctx1 = ctx2.p1()
6743 else:
6745 else:
6744 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6746 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6745 ctx1, ctx2 = scmutil.revpair(repo, revs)
6747 ctx1, ctx2 = scmutil.revpair(repo, revs)
6746
6748
6747 forcerelativevalue = None
6749 forcerelativevalue = None
6748 if ui.hasconfig(b'commands', b'status.relative'):
6750 if ui.hasconfig(b'commands', b'status.relative'):
6749 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6751 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6750 uipathfn = scmutil.getuipathfn(
6752 uipathfn = scmutil.getuipathfn(
6751 repo,
6753 repo,
6752 legacyrelativevalue=bool(pats),
6754 legacyrelativevalue=bool(pats),
6753 forcerelativevalue=forcerelativevalue,
6755 forcerelativevalue=forcerelativevalue,
6754 )
6756 )
6755
6757
6756 if opts.get(b'print0'):
6758 if opts.get(b'print0'):
6757 end = b'\0'
6759 end = b'\0'
6758 else:
6760 else:
6759 end = b'\n'
6761 end = b'\n'
6760 states = b'modified added removed deleted unknown ignored clean'.split()
6762 states = b'modified added removed deleted unknown ignored clean'.split()
6761 show = [k for k in states if opts.get(k)]
6763 show = [k for k in states if opts.get(k)]
6762 if opts.get(b'all'):
6764 if opts.get(b'all'):
6763 show += ui.quiet and (states[:4] + [b'clean']) or states
6765 show += ui.quiet and (states[:4] + [b'clean']) or states
6764
6766
6765 if not show:
6767 if not show:
6766 if ui.quiet:
6768 if ui.quiet:
6767 show = states[:4]
6769 show = states[:4]
6768 else:
6770 else:
6769 show = states[:5]
6771 show = states[:5]
6770
6772
6771 m = scmutil.match(ctx2, pats, opts)
6773 m = scmutil.match(ctx2, pats, opts)
6772 if terse:
6774 if terse:
6773 # we need to compute clean and unknown to terse
6775 # we need to compute clean and unknown to terse
6774 stat = repo.status(
6776 stat = repo.status(
6775 ctx1.node(),
6777 ctx1.node(),
6776 ctx2.node(),
6778 ctx2.node(),
6777 m,
6779 m,
6778 b'ignored' in show or b'i' in terse,
6780 b'ignored' in show or b'i' in terse,
6779 clean=True,
6781 clean=True,
6780 unknown=True,
6782 unknown=True,
6781 listsubrepos=opts.get(b'subrepos'),
6783 listsubrepos=opts.get(b'subrepos'),
6782 )
6784 )
6783
6785
6784 stat = cmdutil.tersedir(stat, terse)
6786 stat = cmdutil.tersedir(stat, terse)
6785 else:
6787 else:
6786 stat = repo.status(
6788 stat = repo.status(
6787 ctx1.node(),
6789 ctx1.node(),
6788 ctx2.node(),
6790 ctx2.node(),
6789 m,
6791 m,
6790 b'ignored' in show,
6792 b'ignored' in show,
6791 b'clean' in show,
6793 b'clean' in show,
6792 b'unknown' in show,
6794 b'unknown' in show,
6793 opts.get(b'subrepos'),
6795 opts.get(b'subrepos'),
6794 )
6796 )
6795
6797
6796 changestates = zip(
6798 changestates = zip(
6797 states,
6799 states,
6798 pycompat.iterbytestr(b'MAR!?IC'),
6800 pycompat.iterbytestr(b'MAR!?IC'),
6799 [getattr(stat, s.decode('utf8')) for s in states],
6801 [getattr(stat, s.decode('utf8')) for s in states],
6800 )
6802 )
6801
6803
6802 copy = {}
6804 copy = {}
6803 if (
6805 if (
6804 opts.get(b'all')
6806 opts.get(b'all')
6805 or opts.get(b'copies')
6807 or opts.get(b'copies')
6806 or ui.configbool(b'ui', b'statuscopies')
6808 or ui.configbool(b'ui', b'statuscopies')
6807 ) and not opts.get(b'no_status'):
6809 ) and not opts.get(b'no_status'):
6808 copy = copies.pathcopies(ctx1, ctx2, m)
6810 copy = copies.pathcopies(ctx1, ctx2, m)
6809
6811
6810 morestatus = None
6812 morestatus = None
6811 if (
6813 if (
6812 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6814 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6813 ) and not ui.plain():
6815 ) and not ui.plain():
6814 morestatus = cmdutil.readmorestatus(repo)
6816 morestatus = cmdutil.readmorestatus(repo)
6815
6817
6816 ui.pager(b'status')
6818 ui.pager(b'status')
6817 fm = ui.formatter(b'status', opts)
6819 fm = ui.formatter(b'status', opts)
6818 fmt = b'%s' + end
6820 fmt = b'%s' + end
6819 showchar = not opts.get(b'no_status')
6821 showchar = not opts.get(b'no_status')
6820
6822
6821 for state, char, files in changestates:
6823 for state, char, files in changestates:
6822 if state in show:
6824 if state in show:
6823 label = b'status.' + state
6825 label = b'status.' + state
6824 for f in files:
6826 for f in files:
6825 fm.startitem()
6827 fm.startitem()
6826 fm.context(ctx=ctx2)
6828 fm.context(ctx=ctx2)
6827 fm.data(itemtype=b'file', path=f)
6829 fm.data(itemtype=b'file', path=f)
6828 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6830 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6829 fm.plain(fmt % uipathfn(f), label=label)
6831 fm.plain(fmt % uipathfn(f), label=label)
6830 if f in copy:
6832 if f in copy:
6831 fm.data(source=copy[f])
6833 fm.data(source=copy[f])
6832 fm.plain(
6834 fm.plain(
6833 (b' %s' + end) % uipathfn(copy[f]),
6835 (b' %s' + end) % uipathfn(copy[f]),
6834 label=b'status.copied',
6836 label=b'status.copied',
6835 )
6837 )
6836 if morestatus:
6838 if morestatus:
6837 morestatus.formatfile(f, fm)
6839 morestatus.formatfile(f, fm)
6838
6840
6839 if morestatus:
6841 if morestatus:
6840 morestatus.formatfooter(fm)
6842 morestatus.formatfooter(fm)
6841 fm.end()
6843 fm.end()
6842
6844
6843
6845
6844 @command(
6846 @command(
6845 b'summary|sum',
6847 b'summary|sum',
6846 [(b'', b'remote', None, _(b'check for push and pull'))],
6848 [(b'', b'remote', None, _(b'check for push and pull'))],
6847 b'[--remote]',
6849 b'[--remote]',
6848 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6850 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6849 helpbasic=True,
6851 helpbasic=True,
6850 intents={INTENT_READONLY},
6852 intents={INTENT_READONLY},
6851 )
6853 )
6852 def summary(ui, repo, **opts):
6854 def summary(ui, repo, **opts):
6853 """summarize working directory state
6855 """summarize working directory state
6854
6856
6855 This generates a brief summary of the working directory state,
6857 This generates a brief summary of the working directory state,
6856 including parents, branch, commit status, phase and available updates.
6858 including parents, branch, commit status, phase and available updates.
6857
6859
6858 With the --remote option, this will check the default paths for
6860 With the --remote option, this will check the default paths for
6859 incoming and outgoing changes. This can be time-consuming.
6861 incoming and outgoing changes. This can be time-consuming.
6860
6862
6861 Returns 0 on success.
6863 Returns 0 on success.
6862 """
6864 """
6863
6865
6864 opts = pycompat.byteskwargs(opts)
6866 opts = pycompat.byteskwargs(opts)
6865 ui.pager(b'summary')
6867 ui.pager(b'summary')
6866 ctx = repo[None]
6868 ctx = repo[None]
6867 parents = ctx.parents()
6869 parents = ctx.parents()
6868 pnode = parents[0].node()
6870 pnode = parents[0].node()
6869 marks = []
6871 marks = []
6870
6872
6871 try:
6873 try:
6872 ms = mergestatemod.mergestate.read(repo)
6874 ms = mergestatemod.mergestate.read(repo)
6873 except error.UnsupportedMergeRecords as e:
6875 except error.UnsupportedMergeRecords as e:
6874 s = b' '.join(e.recordtypes)
6876 s = b' '.join(e.recordtypes)
6875 ui.warn(
6877 ui.warn(
6876 _(b'warning: merge state has unsupported record types: %s\n') % s
6878 _(b'warning: merge state has unsupported record types: %s\n') % s
6877 )
6879 )
6878 unresolved = []
6880 unresolved = []
6879 else:
6881 else:
6880 unresolved = list(ms.unresolved())
6882 unresolved = list(ms.unresolved())
6881
6883
6882 for p in parents:
6884 for p in parents:
6883 # label with log.changeset (instead of log.parent) since this
6885 # label with log.changeset (instead of log.parent) since this
6884 # shows a working directory parent *changeset*:
6886 # shows a working directory parent *changeset*:
6885 # i18n: column positioning for "hg summary"
6887 # i18n: column positioning for "hg summary"
6886 ui.write(
6888 ui.write(
6887 _(b'parent: %d:%s ') % (p.rev(), p),
6889 _(b'parent: %d:%s ') % (p.rev(), p),
6888 label=logcmdutil.changesetlabels(p),
6890 label=logcmdutil.changesetlabels(p),
6889 )
6891 )
6890 ui.write(b' '.join(p.tags()), label=b'log.tag')
6892 ui.write(b' '.join(p.tags()), label=b'log.tag')
6891 if p.bookmarks():
6893 if p.bookmarks():
6892 marks.extend(p.bookmarks())
6894 marks.extend(p.bookmarks())
6893 if p.rev() == -1:
6895 if p.rev() == -1:
6894 if not len(repo):
6896 if not len(repo):
6895 ui.write(_(b' (empty repository)'))
6897 ui.write(_(b' (empty repository)'))
6896 else:
6898 else:
6897 ui.write(_(b' (no revision checked out)'))
6899 ui.write(_(b' (no revision checked out)'))
6898 if p.obsolete():
6900 if p.obsolete():
6899 ui.write(_(b' (obsolete)'))
6901 ui.write(_(b' (obsolete)'))
6900 if p.isunstable():
6902 if p.isunstable():
6901 instabilities = (
6903 instabilities = (
6902 ui.label(instability, b'trouble.%s' % instability)
6904 ui.label(instability, b'trouble.%s' % instability)
6903 for instability in p.instabilities()
6905 for instability in p.instabilities()
6904 )
6906 )
6905 ui.write(b' (' + b', '.join(instabilities) + b')')
6907 ui.write(b' (' + b', '.join(instabilities) + b')')
6906 ui.write(b'\n')
6908 ui.write(b'\n')
6907 if p.description():
6909 if p.description():
6908 ui.status(
6910 ui.status(
6909 b' ' + p.description().splitlines()[0].strip() + b'\n',
6911 b' ' + p.description().splitlines()[0].strip() + b'\n',
6910 label=b'log.summary',
6912 label=b'log.summary',
6911 )
6913 )
6912
6914
6913 branch = ctx.branch()
6915 branch = ctx.branch()
6914 bheads = repo.branchheads(branch)
6916 bheads = repo.branchheads(branch)
6915 # i18n: column positioning for "hg summary"
6917 # i18n: column positioning for "hg summary"
6916 m = _(b'branch: %s\n') % branch
6918 m = _(b'branch: %s\n') % branch
6917 if branch != b'default':
6919 if branch != b'default':
6918 ui.write(m, label=b'log.branch')
6920 ui.write(m, label=b'log.branch')
6919 else:
6921 else:
6920 ui.status(m, label=b'log.branch')
6922 ui.status(m, label=b'log.branch')
6921
6923
6922 if marks:
6924 if marks:
6923 active = repo._activebookmark
6925 active = repo._activebookmark
6924 # i18n: column positioning for "hg summary"
6926 # i18n: column positioning for "hg summary"
6925 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6927 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6926 if active is not None:
6928 if active is not None:
6927 if active in marks:
6929 if active in marks:
6928 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6930 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6929 marks.remove(active)
6931 marks.remove(active)
6930 else:
6932 else:
6931 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6933 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6932 for m in marks:
6934 for m in marks:
6933 ui.write(b' ' + m, label=b'log.bookmark')
6935 ui.write(b' ' + m, label=b'log.bookmark')
6934 ui.write(b'\n', label=b'log.bookmark')
6936 ui.write(b'\n', label=b'log.bookmark')
6935
6937
6936 status = repo.status(unknown=True)
6938 status = repo.status(unknown=True)
6937
6939
6938 c = repo.dirstate.copies()
6940 c = repo.dirstate.copies()
6939 copied, renamed = [], []
6941 copied, renamed = [], []
6940 for d, s in pycompat.iteritems(c):
6942 for d, s in pycompat.iteritems(c):
6941 if s in status.removed:
6943 if s in status.removed:
6942 status.removed.remove(s)
6944 status.removed.remove(s)
6943 renamed.append(d)
6945 renamed.append(d)
6944 else:
6946 else:
6945 copied.append(d)
6947 copied.append(d)
6946 if d in status.added:
6948 if d in status.added:
6947 status.added.remove(d)
6949 status.added.remove(d)
6948
6950
6949 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6951 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6950
6952
6951 labels = [
6953 labels = [
6952 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6954 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6953 (ui.label(_(b'%d added'), b'status.added'), status.added),
6955 (ui.label(_(b'%d added'), b'status.added'), status.added),
6954 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6956 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6955 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6957 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6956 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6958 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6957 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6959 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6958 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6960 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6959 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6961 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6960 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6962 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6961 ]
6963 ]
6962 t = []
6964 t = []
6963 for l, s in labels:
6965 for l, s in labels:
6964 if s:
6966 if s:
6965 t.append(l % len(s))
6967 t.append(l % len(s))
6966
6968
6967 t = b', '.join(t)
6969 t = b', '.join(t)
6968 cleanworkdir = False
6970 cleanworkdir = False
6969
6971
6970 if repo.vfs.exists(b'graftstate'):
6972 if repo.vfs.exists(b'graftstate'):
6971 t += _(b' (graft in progress)')
6973 t += _(b' (graft in progress)')
6972 if repo.vfs.exists(b'updatestate'):
6974 if repo.vfs.exists(b'updatestate'):
6973 t += _(b' (interrupted update)')
6975 t += _(b' (interrupted update)')
6974 elif len(parents) > 1:
6976 elif len(parents) > 1:
6975 t += _(b' (merge)')
6977 t += _(b' (merge)')
6976 elif branch != parents[0].branch():
6978 elif branch != parents[0].branch():
6977 t += _(b' (new branch)')
6979 t += _(b' (new branch)')
6978 elif parents[0].closesbranch() and pnode in repo.branchheads(
6980 elif parents[0].closesbranch() and pnode in repo.branchheads(
6979 branch, closed=True
6981 branch, closed=True
6980 ):
6982 ):
6981 t += _(b' (head closed)')
6983 t += _(b' (head closed)')
6982 elif not (
6984 elif not (
6983 status.modified
6985 status.modified
6984 or status.added
6986 or status.added
6985 or status.removed
6987 or status.removed
6986 or renamed
6988 or renamed
6987 or copied
6989 or copied
6988 or subs
6990 or subs
6989 ):
6991 ):
6990 t += _(b' (clean)')
6992 t += _(b' (clean)')
6991 cleanworkdir = True
6993 cleanworkdir = True
6992 elif pnode not in bheads:
6994 elif pnode not in bheads:
6993 t += _(b' (new branch head)')
6995 t += _(b' (new branch head)')
6994
6996
6995 if parents:
6997 if parents:
6996 pendingphase = max(p.phase() for p in parents)
6998 pendingphase = max(p.phase() for p in parents)
6997 else:
6999 else:
6998 pendingphase = phases.public
7000 pendingphase = phases.public
6999
7001
7000 if pendingphase > phases.newcommitphase(ui):
7002 if pendingphase > phases.newcommitphase(ui):
7001 t += b' (%s)' % phases.phasenames[pendingphase]
7003 t += b' (%s)' % phases.phasenames[pendingphase]
7002
7004
7003 if cleanworkdir:
7005 if cleanworkdir:
7004 # i18n: column positioning for "hg summary"
7006 # i18n: column positioning for "hg summary"
7005 ui.status(_(b'commit: %s\n') % t.strip())
7007 ui.status(_(b'commit: %s\n') % t.strip())
7006 else:
7008 else:
7007 # i18n: column positioning for "hg summary"
7009 # i18n: column positioning for "hg summary"
7008 ui.write(_(b'commit: %s\n') % t.strip())
7010 ui.write(_(b'commit: %s\n') % t.strip())
7009
7011
7010 # all ancestors of branch heads - all ancestors of parent = new csets
7012 # all ancestors of branch heads - all ancestors of parent = new csets
7011 new = len(
7013 new = len(
7012 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7014 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7013 )
7015 )
7014
7016
7015 if new == 0:
7017 if new == 0:
7016 # i18n: column positioning for "hg summary"
7018 # i18n: column positioning for "hg summary"
7017 ui.status(_(b'update: (current)\n'))
7019 ui.status(_(b'update: (current)\n'))
7018 elif pnode not in bheads:
7020 elif pnode not in bheads:
7019 # i18n: column positioning for "hg summary"
7021 # i18n: column positioning for "hg summary"
7020 ui.write(_(b'update: %d new changesets (update)\n') % new)
7022 ui.write(_(b'update: %d new changesets (update)\n') % new)
7021 else:
7023 else:
7022 # i18n: column positioning for "hg summary"
7024 # i18n: column positioning for "hg summary"
7023 ui.write(
7025 ui.write(
7024 _(b'update: %d new changesets, %d branch heads (merge)\n')
7026 _(b'update: %d new changesets, %d branch heads (merge)\n')
7025 % (new, len(bheads))
7027 % (new, len(bheads))
7026 )
7028 )
7027
7029
7028 t = []
7030 t = []
7029 draft = len(repo.revs(b'draft()'))
7031 draft = len(repo.revs(b'draft()'))
7030 if draft:
7032 if draft:
7031 t.append(_(b'%d draft') % draft)
7033 t.append(_(b'%d draft') % draft)
7032 secret = len(repo.revs(b'secret()'))
7034 secret = len(repo.revs(b'secret()'))
7033 if secret:
7035 if secret:
7034 t.append(_(b'%d secret') % secret)
7036 t.append(_(b'%d secret') % secret)
7035
7037
7036 if draft or secret:
7038 if draft or secret:
7037 ui.status(_(b'phases: %s\n') % b', '.join(t))
7039 ui.status(_(b'phases: %s\n') % b', '.join(t))
7038
7040
7039 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7041 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7040 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7042 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7041 numtrouble = len(repo.revs(trouble + b"()"))
7043 numtrouble = len(repo.revs(trouble + b"()"))
7042 # We write all the possibilities to ease translation
7044 # We write all the possibilities to ease translation
7043 troublemsg = {
7045 troublemsg = {
7044 b"orphan": _(b"orphan: %d changesets"),
7046 b"orphan": _(b"orphan: %d changesets"),
7045 b"contentdivergent": _(b"content-divergent: %d changesets"),
7047 b"contentdivergent": _(b"content-divergent: %d changesets"),
7046 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7048 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7047 }
7049 }
7048 if numtrouble > 0:
7050 if numtrouble > 0:
7049 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7051 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7050
7052
7051 cmdutil.summaryhooks(ui, repo)
7053 cmdutil.summaryhooks(ui, repo)
7052
7054
7053 if opts.get(b'remote'):
7055 if opts.get(b'remote'):
7054 needsincoming, needsoutgoing = True, True
7056 needsincoming, needsoutgoing = True, True
7055 else:
7057 else:
7056 needsincoming, needsoutgoing = False, False
7058 needsincoming, needsoutgoing = False, False
7057 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7059 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7058 if i:
7060 if i:
7059 needsincoming = True
7061 needsincoming = True
7060 if o:
7062 if o:
7061 needsoutgoing = True
7063 needsoutgoing = True
7062 if not needsincoming and not needsoutgoing:
7064 if not needsincoming and not needsoutgoing:
7063 return
7065 return
7064
7066
7065 def getincoming():
7067 def getincoming():
7066 source, branches = hg.parseurl(ui.expandpath(b'default'))
7068 source, branches = hg.parseurl(ui.expandpath(b'default'))
7067 sbranch = branches[0]
7069 sbranch = branches[0]
7068 try:
7070 try:
7069 other = hg.peer(repo, {}, source)
7071 other = hg.peer(repo, {}, source)
7070 except error.RepoError:
7072 except error.RepoError:
7071 if opts.get(b'remote'):
7073 if opts.get(b'remote'):
7072 raise
7074 raise
7073 return source, sbranch, None, None, None
7075 return source, sbranch, None, None, None
7074 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7076 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7075 if revs:
7077 if revs:
7076 revs = [other.lookup(rev) for rev in revs]
7078 revs = [other.lookup(rev) for rev in revs]
7077 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7079 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7078 repo.ui.pushbuffer()
7080 repo.ui.pushbuffer()
7079 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7081 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7080 repo.ui.popbuffer()
7082 repo.ui.popbuffer()
7081 return source, sbranch, other, commoninc, commoninc[1]
7083 return source, sbranch, other, commoninc, commoninc[1]
7082
7084
7083 if needsincoming:
7085 if needsincoming:
7084 source, sbranch, sother, commoninc, incoming = getincoming()
7086 source, sbranch, sother, commoninc, incoming = getincoming()
7085 else:
7087 else:
7086 source = sbranch = sother = commoninc = incoming = None
7088 source = sbranch = sother = commoninc = incoming = None
7087
7089
7088 def getoutgoing():
7090 def getoutgoing():
7089 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7091 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7090 dbranch = branches[0]
7092 dbranch = branches[0]
7091 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7093 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7092 if source != dest:
7094 if source != dest:
7093 try:
7095 try:
7094 dother = hg.peer(repo, {}, dest)
7096 dother = hg.peer(repo, {}, dest)
7095 except error.RepoError:
7097 except error.RepoError:
7096 if opts.get(b'remote'):
7098 if opts.get(b'remote'):
7097 raise
7099 raise
7098 return dest, dbranch, None, None
7100 return dest, dbranch, None, None
7099 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7101 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7100 elif sother is None:
7102 elif sother is None:
7101 # there is no explicit destination peer, but source one is invalid
7103 # there is no explicit destination peer, but source one is invalid
7102 return dest, dbranch, None, None
7104 return dest, dbranch, None, None
7103 else:
7105 else:
7104 dother = sother
7106 dother = sother
7105 if source != dest or (sbranch is not None and sbranch != dbranch):
7107 if source != dest or (sbranch is not None and sbranch != dbranch):
7106 common = None
7108 common = None
7107 else:
7109 else:
7108 common = commoninc
7110 common = commoninc
7109 if revs:
7111 if revs:
7110 revs = [repo.lookup(rev) for rev in revs]
7112 revs = [repo.lookup(rev) for rev in revs]
7111 repo.ui.pushbuffer()
7113 repo.ui.pushbuffer()
7112 outgoing = discovery.findcommonoutgoing(
7114 outgoing = discovery.findcommonoutgoing(
7113 repo, dother, onlyheads=revs, commoninc=common
7115 repo, dother, onlyheads=revs, commoninc=common
7114 )
7116 )
7115 repo.ui.popbuffer()
7117 repo.ui.popbuffer()
7116 return dest, dbranch, dother, outgoing
7118 return dest, dbranch, dother, outgoing
7117
7119
7118 if needsoutgoing:
7120 if needsoutgoing:
7119 dest, dbranch, dother, outgoing = getoutgoing()
7121 dest, dbranch, dother, outgoing = getoutgoing()
7120 else:
7122 else:
7121 dest = dbranch = dother = outgoing = None
7123 dest = dbranch = dother = outgoing = None
7122
7124
7123 if opts.get(b'remote'):
7125 if opts.get(b'remote'):
7124 t = []
7126 t = []
7125 if incoming:
7127 if incoming:
7126 t.append(_(b'1 or more incoming'))
7128 t.append(_(b'1 or more incoming'))
7127 o = outgoing.missing
7129 o = outgoing.missing
7128 if o:
7130 if o:
7129 t.append(_(b'%d outgoing') % len(o))
7131 t.append(_(b'%d outgoing') % len(o))
7130 other = dother or sother
7132 other = dother or sother
7131 if b'bookmarks' in other.listkeys(b'namespaces'):
7133 if b'bookmarks' in other.listkeys(b'namespaces'):
7132 counts = bookmarks.summary(repo, other)
7134 counts = bookmarks.summary(repo, other)
7133 if counts[0] > 0:
7135 if counts[0] > 0:
7134 t.append(_(b'%d incoming bookmarks') % counts[0])
7136 t.append(_(b'%d incoming bookmarks') % counts[0])
7135 if counts[1] > 0:
7137 if counts[1] > 0:
7136 t.append(_(b'%d outgoing bookmarks') % counts[1])
7138 t.append(_(b'%d outgoing bookmarks') % counts[1])
7137
7139
7138 if t:
7140 if t:
7139 # i18n: column positioning for "hg summary"
7141 # i18n: column positioning for "hg summary"
7140 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7142 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7141 else:
7143 else:
7142 # i18n: column positioning for "hg summary"
7144 # i18n: column positioning for "hg summary"
7143 ui.status(_(b'remote: (synced)\n'))
7145 ui.status(_(b'remote: (synced)\n'))
7144
7146
7145 cmdutil.summaryremotehooks(
7147 cmdutil.summaryremotehooks(
7146 ui,
7148 ui,
7147 repo,
7149 repo,
7148 opts,
7150 opts,
7149 (
7151 (
7150 (source, sbranch, sother, commoninc),
7152 (source, sbranch, sother, commoninc),
7151 (dest, dbranch, dother, outgoing),
7153 (dest, dbranch, dother, outgoing),
7152 ),
7154 ),
7153 )
7155 )
7154
7156
7155
7157
7156 @command(
7158 @command(
7157 b'tag',
7159 b'tag',
7158 [
7160 [
7159 (b'f', b'force', None, _(b'force tag')),
7161 (b'f', b'force', None, _(b'force tag')),
7160 (b'l', b'local', None, _(b'make the tag local')),
7162 (b'l', b'local', None, _(b'make the tag local')),
7161 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7163 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7162 (b'', b'remove', None, _(b'remove a tag')),
7164 (b'', b'remove', None, _(b'remove a tag')),
7163 # -l/--local is already there, commitopts cannot be used
7165 # -l/--local is already there, commitopts cannot be used
7164 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7166 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7165 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7167 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7166 ]
7168 ]
7167 + commitopts2,
7169 + commitopts2,
7168 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7170 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7169 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7171 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7170 )
7172 )
7171 def tag(ui, repo, name1, *names, **opts):
7173 def tag(ui, repo, name1, *names, **opts):
7172 """add one or more tags for the current or given revision
7174 """add one or more tags for the current or given revision
7173
7175
7174 Name a particular revision using <name>.
7176 Name a particular revision using <name>.
7175
7177
7176 Tags are used to name particular revisions of the repository and are
7178 Tags are used to name particular revisions of the repository and are
7177 very useful to compare different revisions, to go back to significant
7179 very useful to compare different revisions, to go back to significant
7178 earlier versions or to mark branch points as releases, etc. Changing
7180 earlier versions or to mark branch points as releases, etc. Changing
7179 an existing tag is normally disallowed; use -f/--force to override.
7181 an existing tag is normally disallowed; use -f/--force to override.
7180
7182
7181 If no revision is given, the parent of the working directory is
7183 If no revision is given, the parent of the working directory is
7182 used.
7184 used.
7183
7185
7184 To facilitate version control, distribution, and merging of tags,
7186 To facilitate version control, distribution, and merging of tags,
7185 they are stored as a file named ".hgtags" which is managed similarly
7187 they are stored as a file named ".hgtags" which is managed similarly
7186 to other project files and can be hand-edited if necessary. This
7188 to other project files and can be hand-edited if necessary. This
7187 also means that tagging creates a new commit. The file
7189 also means that tagging creates a new commit. The file
7188 ".hg/localtags" is used for local tags (not shared among
7190 ".hg/localtags" is used for local tags (not shared among
7189 repositories).
7191 repositories).
7190
7192
7191 Tag commits are usually made at the head of a branch. If the parent
7193 Tag commits are usually made at the head of a branch. If the parent
7192 of the working directory is not a branch head, :hg:`tag` aborts; use
7194 of the working directory is not a branch head, :hg:`tag` aborts; use
7193 -f/--force to force the tag commit to be based on a non-head
7195 -f/--force to force the tag commit to be based on a non-head
7194 changeset.
7196 changeset.
7195
7197
7196 See :hg:`help dates` for a list of formats valid for -d/--date.
7198 See :hg:`help dates` for a list of formats valid for -d/--date.
7197
7199
7198 Since tag names have priority over branch names during revision
7200 Since tag names have priority over branch names during revision
7199 lookup, using an existing branch name as a tag name is discouraged.
7201 lookup, using an existing branch name as a tag name is discouraged.
7200
7202
7201 Returns 0 on success.
7203 Returns 0 on success.
7202 """
7204 """
7203 opts = pycompat.byteskwargs(opts)
7205 opts = pycompat.byteskwargs(opts)
7204 with repo.wlock(), repo.lock():
7206 with repo.wlock(), repo.lock():
7205 rev_ = b"."
7207 rev_ = b"."
7206 names = [t.strip() for t in (name1,) + names]
7208 names = [t.strip() for t in (name1,) + names]
7207 if len(names) != len(set(names)):
7209 if len(names) != len(set(names)):
7208 raise error.Abort(_(b'tag names must be unique'))
7210 raise error.Abort(_(b'tag names must be unique'))
7209 for n in names:
7211 for n in names:
7210 scmutil.checknewlabel(repo, n, b'tag')
7212 scmutil.checknewlabel(repo, n, b'tag')
7211 if not n:
7213 if not n:
7212 raise error.Abort(
7214 raise error.Abort(
7213 _(b'tag names cannot consist entirely of whitespace')
7215 _(b'tag names cannot consist entirely of whitespace')
7214 )
7216 )
7215 if opts.get(b'rev') and opts.get(b'remove'):
7217 if opts.get(b'rev') and opts.get(b'remove'):
7216 raise error.Abort(_(b"--rev and --remove are incompatible"))
7218 raise error.Abort(_(b"--rev and --remove are incompatible"))
7217 if opts.get(b'rev'):
7219 if opts.get(b'rev'):
7218 rev_ = opts[b'rev']
7220 rev_ = opts[b'rev']
7219 message = opts.get(b'message')
7221 message = opts.get(b'message')
7220 if opts.get(b'remove'):
7222 if opts.get(b'remove'):
7221 if opts.get(b'local'):
7223 if opts.get(b'local'):
7222 expectedtype = b'local'
7224 expectedtype = b'local'
7223 else:
7225 else:
7224 expectedtype = b'global'
7226 expectedtype = b'global'
7225
7227
7226 for n in names:
7228 for n in names:
7227 if repo.tagtype(n) == b'global':
7229 if repo.tagtype(n) == b'global':
7228 alltags = tagsmod.findglobaltags(ui, repo)
7230 alltags = tagsmod.findglobaltags(ui, repo)
7229 if alltags[n][0] == nullid:
7231 if alltags[n][0] == nullid:
7230 raise error.Abort(_(b"tag '%s' is already removed") % n)
7232 raise error.Abort(_(b"tag '%s' is already removed") % n)
7231 if not repo.tagtype(n):
7233 if not repo.tagtype(n):
7232 raise error.Abort(_(b"tag '%s' does not exist") % n)
7234 raise error.Abort(_(b"tag '%s' does not exist") % n)
7233 if repo.tagtype(n) != expectedtype:
7235 if repo.tagtype(n) != expectedtype:
7234 if expectedtype == b'global':
7236 if expectedtype == b'global':
7235 raise error.Abort(
7237 raise error.Abort(
7236 _(b"tag '%s' is not a global tag") % n
7238 _(b"tag '%s' is not a global tag") % n
7237 )
7239 )
7238 else:
7240 else:
7239 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7241 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7240 rev_ = b'null'
7242 rev_ = b'null'
7241 if not message:
7243 if not message:
7242 # we don't translate commit messages
7244 # we don't translate commit messages
7243 message = b'Removed tag %s' % b', '.join(names)
7245 message = b'Removed tag %s' % b', '.join(names)
7244 elif not opts.get(b'force'):
7246 elif not opts.get(b'force'):
7245 for n in names:
7247 for n in names:
7246 if n in repo.tags():
7248 if n in repo.tags():
7247 raise error.Abort(
7249 raise error.Abort(
7248 _(b"tag '%s' already exists (use -f to force)") % n
7250 _(b"tag '%s' already exists (use -f to force)") % n
7249 )
7251 )
7250 if not opts.get(b'local'):
7252 if not opts.get(b'local'):
7251 p1, p2 = repo.dirstate.parents()
7253 p1, p2 = repo.dirstate.parents()
7252 if p2 != nullid:
7254 if p2 != nullid:
7253 raise error.Abort(_(b'uncommitted merge'))
7255 raise error.Abort(_(b'uncommitted merge'))
7254 bheads = repo.branchheads()
7256 bheads = repo.branchheads()
7255 if not opts.get(b'force') and bheads and p1 not in bheads:
7257 if not opts.get(b'force') and bheads and p1 not in bheads:
7256 raise error.Abort(
7258 raise error.Abort(
7257 _(
7259 _(
7258 b'working directory is not at a branch head '
7260 b'working directory is not at a branch head '
7259 b'(use -f to force)'
7261 b'(use -f to force)'
7260 )
7262 )
7261 )
7263 )
7262 node = scmutil.revsingle(repo, rev_).node()
7264 node = scmutil.revsingle(repo, rev_).node()
7263
7265
7264 if not message:
7266 if not message:
7265 # we don't translate commit messages
7267 # we don't translate commit messages
7266 message = b'Added tag %s for changeset %s' % (
7268 message = b'Added tag %s for changeset %s' % (
7267 b', '.join(names),
7269 b', '.join(names),
7268 short(node),
7270 short(node),
7269 )
7271 )
7270
7272
7271 date = opts.get(b'date')
7273 date = opts.get(b'date')
7272 if date:
7274 if date:
7273 date = dateutil.parsedate(date)
7275 date = dateutil.parsedate(date)
7274
7276
7275 if opts.get(b'remove'):
7277 if opts.get(b'remove'):
7276 editform = b'tag.remove'
7278 editform = b'tag.remove'
7277 else:
7279 else:
7278 editform = b'tag.add'
7280 editform = b'tag.add'
7279 editor = cmdutil.getcommiteditor(
7281 editor = cmdutil.getcommiteditor(
7280 editform=editform, **pycompat.strkwargs(opts)
7282 editform=editform, **pycompat.strkwargs(opts)
7281 )
7283 )
7282
7284
7283 # don't allow tagging the null rev
7285 # don't allow tagging the null rev
7284 if (
7286 if (
7285 not opts.get(b'remove')
7287 not opts.get(b'remove')
7286 and scmutil.revsingle(repo, rev_).rev() == nullrev
7288 and scmutil.revsingle(repo, rev_).rev() == nullrev
7287 ):
7289 ):
7288 raise error.Abort(_(b"cannot tag null revision"))
7290 raise error.Abort(_(b"cannot tag null revision"))
7289
7291
7290 tagsmod.tag(
7292 tagsmod.tag(
7291 repo,
7293 repo,
7292 names,
7294 names,
7293 node,
7295 node,
7294 message,
7296 message,
7295 opts.get(b'local'),
7297 opts.get(b'local'),
7296 opts.get(b'user'),
7298 opts.get(b'user'),
7297 date,
7299 date,
7298 editor=editor,
7300 editor=editor,
7299 )
7301 )
7300
7302
7301
7303
7302 @command(
7304 @command(
7303 b'tags',
7305 b'tags',
7304 formatteropts,
7306 formatteropts,
7305 b'',
7307 b'',
7306 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7308 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7307 intents={INTENT_READONLY},
7309 intents={INTENT_READONLY},
7308 )
7310 )
7309 def tags(ui, repo, **opts):
7311 def tags(ui, repo, **opts):
7310 """list repository tags
7312 """list repository tags
7311
7313
7312 This lists both regular and local tags. When the -v/--verbose
7314 This lists both regular and local tags. When the -v/--verbose
7313 switch is used, a third column "local" is printed for local tags.
7315 switch is used, a third column "local" is printed for local tags.
7314 When the -q/--quiet switch is used, only the tag name is printed.
7316 When the -q/--quiet switch is used, only the tag name is printed.
7315
7317
7316 .. container:: verbose
7318 .. container:: verbose
7317
7319
7318 Template:
7320 Template:
7319
7321
7320 The following keywords are supported in addition to the common template
7322 The following keywords are supported in addition to the common template
7321 keywords and functions such as ``{tag}``. See also
7323 keywords and functions such as ``{tag}``. See also
7322 :hg:`help templates`.
7324 :hg:`help templates`.
7323
7325
7324 :type: String. ``local`` for local tags.
7326 :type: String. ``local`` for local tags.
7325
7327
7326 Returns 0 on success.
7328 Returns 0 on success.
7327 """
7329 """
7328
7330
7329 opts = pycompat.byteskwargs(opts)
7331 opts = pycompat.byteskwargs(opts)
7330 ui.pager(b'tags')
7332 ui.pager(b'tags')
7331 fm = ui.formatter(b'tags', opts)
7333 fm = ui.formatter(b'tags', opts)
7332 hexfunc = fm.hexfunc
7334 hexfunc = fm.hexfunc
7333
7335
7334 for t, n in reversed(repo.tagslist()):
7336 for t, n in reversed(repo.tagslist()):
7335 hn = hexfunc(n)
7337 hn = hexfunc(n)
7336 label = b'tags.normal'
7338 label = b'tags.normal'
7337 tagtype = b''
7339 tagtype = b''
7338 if repo.tagtype(t) == b'local':
7340 if repo.tagtype(t) == b'local':
7339 label = b'tags.local'
7341 label = b'tags.local'
7340 tagtype = b'local'
7342 tagtype = b'local'
7341
7343
7342 fm.startitem()
7344 fm.startitem()
7343 fm.context(repo=repo)
7345 fm.context(repo=repo)
7344 fm.write(b'tag', b'%s', t, label=label)
7346 fm.write(b'tag', b'%s', t, label=label)
7345 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7347 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7346 fm.condwrite(
7348 fm.condwrite(
7347 not ui.quiet,
7349 not ui.quiet,
7348 b'rev node',
7350 b'rev node',
7349 fmt,
7351 fmt,
7350 repo.changelog.rev(n),
7352 repo.changelog.rev(n),
7351 hn,
7353 hn,
7352 label=label,
7354 label=label,
7353 )
7355 )
7354 fm.condwrite(
7356 fm.condwrite(
7355 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7357 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7356 )
7358 )
7357 fm.plain(b'\n')
7359 fm.plain(b'\n')
7358 fm.end()
7360 fm.end()
7359
7361
7360
7362
7361 @command(
7363 @command(
7362 b'tip',
7364 b'tip',
7363 [
7365 [
7364 (b'p', b'patch', None, _(b'show patch')),
7366 (b'p', b'patch', None, _(b'show patch')),
7365 (b'g', b'git', None, _(b'use git extended diff format')),
7367 (b'g', b'git', None, _(b'use git extended diff format')),
7366 ]
7368 ]
7367 + templateopts,
7369 + templateopts,
7368 _(b'[-p] [-g]'),
7370 _(b'[-p] [-g]'),
7369 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7371 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7370 )
7372 )
7371 def tip(ui, repo, **opts):
7373 def tip(ui, repo, **opts):
7372 """show the tip revision (DEPRECATED)
7374 """show the tip revision (DEPRECATED)
7373
7375
7374 The tip revision (usually just called the tip) is the changeset
7376 The tip revision (usually just called the tip) is the changeset
7375 most recently added to the repository (and therefore the most
7377 most recently added to the repository (and therefore the most
7376 recently changed head).
7378 recently changed head).
7377
7379
7378 If you have just made a commit, that commit will be the tip. If
7380 If you have just made a commit, that commit will be the tip. If
7379 you have just pulled changes from another repository, the tip of
7381 you have just pulled changes from another repository, the tip of
7380 that repository becomes the current tip. The "tip" tag is special
7382 that repository becomes the current tip. The "tip" tag is special
7381 and cannot be renamed or assigned to a different changeset.
7383 and cannot be renamed or assigned to a different changeset.
7382
7384
7383 This command is deprecated, please use :hg:`heads` instead.
7385 This command is deprecated, please use :hg:`heads` instead.
7384
7386
7385 Returns 0 on success.
7387 Returns 0 on success.
7386 """
7388 """
7387 opts = pycompat.byteskwargs(opts)
7389 opts = pycompat.byteskwargs(opts)
7388 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7390 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7389 displayer.show(repo[b'tip'])
7391 displayer.show(repo[b'tip'])
7390 displayer.close()
7392 displayer.close()
7391
7393
7392
7394
7393 @command(
7395 @command(
7394 b'unbundle',
7396 b'unbundle',
7395 [
7397 [
7396 (
7398 (
7397 b'u',
7399 b'u',
7398 b'update',
7400 b'update',
7399 None,
7401 None,
7400 _(b'update to new branch head if changesets were unbundled'),
7402 _(b'update to new branch head if changesets were unbundled'),
7401 )
7403 )
7402 ],
7404 ],
7403 _(b'[-u] FILE...'),
7405 _(b'[-u] FILE...'),
7404 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7406 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7405 )
7407 )
7406 def unbundle(ui, repo, fname1, *fnames, **opts):
7408 def unbundle(ui, repo, fname1, *fnames, **opts):
7407 """apply one or more bundle files
7409 """apply one or more bundle files
7408
7410
7409 Apply one or more bundle files generated by :hg:`bundle`.
7411 Apply one or more bundle files generated by :hg:`bundle`.
7410
7412
7411 Returns 0 on success, 1 if an update has unresolved files.
7413 Returns 0 on success, 1 if an update has unresolved files.
7412 """
7414 """
7413 fnames = (fname1,) + fnames
7415 fnames = (fname1,) + fnames
7414
7416
7415 with repo.lock():
7417 with repo.lock():
7416 for fname in fnames:
7418 for fname in fnames:
7417 f = hg.openpath(ui, fname)
7419 f = hg.openpath(ui, fname)
7418 gen = exchange.readbundle(ui, f, fname)
7420 gen = exchange.readbundle(ui, f, fname)
7419 if isinstance(gen, streamclone.streamcloneapplier):
7421 if isinstance(gen, streamclone.streamcloneapplier):
7420 raise error.Abort(
7422 raise error.Abort(
7421 _(
7423 _(
7422 b'packed bundles cannot be applied with '
7424 b'packed bundles cannot be applied with '
7423 b'"hg unbundle"'
7425 b'"hg unbundle"'
7424 ),
7426 ),
7425 hint=_(b'use "hg debugapplystreamclonebundle"'),
7427 hint=_(b'use "hg debugapplystreamclonebundle"'),
7426 )
7428 )
7427 url = b'bundle:' + fname
7429 url = b'bundle:' + fname
7428 try:
7430 try:
7429 txnname = b'unbundle'
7431 txnname = b'unbundle'
7430 if not isinstance(gen, bundle2.unbundle20):
7432 if not isinstance(gen, bundle2.unbundle20):
7431 txnname = b'unbundle\n%s' % util.hidepassword(url)
7433 txnname = b'unbundle\n%s' % util.hidepassword(url)
7432 with repo.transaction(txnname) as tr:
7434 with repo.transaction(txnname) as tr:
7433 op = bundle2.applybundle(
7435 op = bundle2.applybundle(
7434 repo, gen, tr, source=b'unbundle', url=url
7436 repo, gen, tr, source=b'unbundle', url=url
7435 )
7437 )
7436 except error.BundleUnknownFeatureError as exc:
7438 except error.BundleUnknownFeatureError as exc:
7437 raise error.Abort(
7439 raise error.Abort(
7438 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7440 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7439 hint=_(
7441 hint=_(
7440 b"see https://mercurial-scm.org/"
7442 b"see https://mercurial-scm.org/"
7441 b"wiki/BundleFeature for more "
7443 b"wiki/BundleFeature for more "
7442 b"information"
7444 b"information"
7443 ),
7445 ),
7444 )
7446 )
7445 modheads = bundle2.combinechangegroupresults(op)
7447 modheads = bundle2.combinechangegroupresults(op)
7446
7448
7447 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7449 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7448
7450
7449
7451
7450 @command(
7452 @command(
7451 b'unshelve',
7453 b'unshelve',
7452 [
7454 [
7453 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7455 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7454 (
7456 (
7455 b'c',
7457 b'c',
7456 b'continue',
7458 b'continue',
7457 None,
7459 None,
7458 _(b'continue an incomplete unshelve operation'),
7460 _(b'continue an incomplete unshelve operation'),
7459 ),
7461 ),
7460 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7462 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7461 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7463 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7462 (
7464 (
7463 b'n',
7465 b'n',
7464 b'name',
7466 b'name',
7465 b'',
7467 b'',
7466 _(b'restore shelved change with given name'),
7468 _(b'restore shelved change with given name'),
7467 _(b'NAME'),
7469 _(b'NAME'),
7468 ),
7470 ),
7469 (b't', b'tool', b'', _(b'specify merge tool')),
7471 (b't', b'tool', b'', _(b'specify merge tool')),
7470 (
7472 (
7471 b'',
7473 b'',
7472 b'date',
7474 b'date',
7473 b'',
7475 b'',
7474 _(b'set date for temporary commits (DEPRECATED)'),
7476 _(b'set date for temporary commits (DEPRECATED)'),
7475 _(b'DATE'),
7477 _(b'DATE'),
7476 ),
7478 ),
7477 ],
7479 ],
7478 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7480 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7479 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7481 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7480 )
7482 )
7481 def unshelve(ui, repo, *shelved, **opts):
7483 def unshelve(ui, repo, *shelved, **opts):
7482 """restore a shelved change to the working directory
7484 """restore a shelved change to the working directory
7483
7485
7484 This command accepts an optional name of a shelved change to
7486 This command accepts an optional name of a shelved change to
7485 restore. If none is given, the most recent shelved change is used.
7487 restore. If none is given, the most recent shelved change is used.
7486
7488
7487 If a shelved change is applied successfully, the bundle that
7489 If a shelved change is applied successfully, the bundle that
7488 contains the shelved changes is moved to a backup location
7490 contains the shelved changes is moved to a backup location
7489 (.hg/shelve-backup).
7491 (.hg/shelve-backup).
7490
7492
7491 Since you can restore a shelved change on top of an arbitrary
7493 Since you can restore a shelved change on top of an arbitrary
7492 commit, it is possible that unshelving will result in a conflict
7494 commit, it is possible that unshelving will result in a conflict
7493 between your changes and the commits you are unshelving onto. If
7495 between your changes and the commits you are unshelving onto. If
7494 this occurs, you must resolve the conflict, then use
7496 this occurs, you must resolve the conflict, then use
7495 ``--continue`` to complete the unshelve operation. (The bundle
7497 ``--continue`` to complete the unshelve operation. (The bundle
7496 will not be moved until you successfully complete the unshelve.)
7498 will not be moved until you successfully complete the unshelve.)
7497
7499
7498 (Alternatively, you can use ``--abort`` to abandon an unshelve
7500 (Alternatively, you can use ``--abort`` to abandon an unshelve
7499 that causes a conflict. This reverts the unshelved changes, and
7501 that causes a conflict. This reverts the unshelved changes, and
7500 leaves the bundle in place.)
7502 leaves the bundle in place.)
7501
7503
7502 If bare shelved change (without interactive, include and exclude
7504 If bare shelved change (without interactive, include and exclude
7503 option) was done on newly created branch it would restore branch
7505 option) was done on newly created branch it would restore branch
7504 information to the working directory.
7506 information to the working directory.
7505
7507
7506 After a successful unshelve, the shelved changes are stored in a
7508 After a successful unshelve, the shelved changes are stored in a
7507 backup directory. Only the N most recent backups are kept. N
7509 backup directory. Only the N most recent backups are kept. N
7508 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7510 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7509 configuration option.
7511 configuration option.
7510
7512
7511 .. container:: verbose
7513 .. container:: verbose
7512
7514
7513 Timestamp in seconds is used to decide order of backups. More
7515 Timestamp in seconds is used to decide order of backups. More
7514 than ``maxbackups`` backups are kept, if same timestamp
7516 than ``maxbackups`` backups are kept, if same timestamp
7515 prevents from deciding exact order of them, for safety.
7517 prevents from deciding exact order of them, for safety.
7516
7518
7517 Selected changes can be unshelved with ``--interactive`` flag.
7519 Selected changes can be unshelved with ``--interactive`` flag.
7518 The working directory is updated with the selected changes, and
7520 The working directory is updated with the selected changes, and
7519 only the unselected changes remain shelved.
7521 only the unselected changes remain shelved.
7520 Note: The whole shelve is applied to working directory first before
7522 Note: The whole shelve is applied to working directory first before
7521 running interactively. So, this will bring up all the conflicts between
7523 running interactively. So, this will bring up all the conflicts between
7522 working directory and the shelve, irrespective of which changes will be
7524 working directory and the shelve, irrespective of which changes will be
7523 unshelved.
7525 unshelved.
7524 """
7526 """
7525 with repo.wlock():
7527 with repo.wlock():
7526 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7528 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7527
7529
7528
7530
7529 statemod.addunfinished(
7531 statemod.addunfinished(
7530 b'unshelve',
7532 b'unshelve',
7531 fname=b'shelvedstate',
7533 fname=b'shelvedstate',
7532 continueflag=True,
7534 continueflag=True,
7533 abortfunc=shelvemod.hgabortunshelve,
7535 abortfunc=shelvemod.hgabortunshelve,
7534 continuefunc=shelvemod.hgcontinueunshelve,
7536 continuefunc=shelvemod.hgcontinueunshelve,
7535 cmdmsg=_(b'unshelve already in progress'),
7537 cmdmsg=_(b'unshelve already in progress'),
7536 )
7538 )
7537
7539
7538
7540
7539 @command(
7541 @command(
7540 b'update|up|checkout|co',
7542 b'update|up|checkout|co',
7541 [
7543 [
7542 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7544 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7543 (b'c', b'check', None, _(b'require clean working directory')),
7545 (b'c', b'check', None, _(b'require clean working directory')),
7544 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7546 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7545 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7547 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7546 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7548 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7547 ]
7549 ]
7548 + mergetoolopts,
7550 + mergetoolopts,
7549 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7551 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7550 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7552 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7551 helpbasic=True,
7553 helpbasic=True,
7552 )
7554 )
7553 def update(ui, repo, node=None, **opts):
7555 def update(ui, repo, node=None, **opts):
7554 """update working directory (or switch revisions)
7556 """update working directory (or switch revisions)
7555
7557
7556 Update the repository's working directory to the specified
7558 Update the repository's working directory to the specified
7557 changeset. If no changeset is specified, update to the tip of the
7559 changeset. If no changeset is specified, update to the tip of the
7558 current named branch and move the active bookmark (see :hg:`help
7560 current named branch and move the active bookmark (see :hg:`help
7559 bookmarks`).
7561 bookmarks`).
7560
7562
7561 Update sets the working directory's parent revision to the specified
7563 Update sets the working directory's parent revision to the specified
7562 changeset (see :hg:`help parents`).
7564 changeset (see :hg:`help parents`).
7563
7565
7564 If the changeset is not a descendant or ancestor of the working
7566 If the changeset is not a descendant or ancestor of the working
7565 directory's parent and there are uncommitted changes, the update is
7567 directory's parent and there are uncommitted changes, the update is
7566 aborted. With the -c/--check option, the working directory is checked
7568 aborted. With the -c/--check option, the working directory is checked
7567 for uncommitted changes; if none are found, the working directory is
7569 for uncommitted changes; if none are found, the working directory is
7568 updated to the specified changeset.
7570 updated to the specified changeset.
7569
7571
7570 .. container:: verbose
7572 .. container:: verbose
7571
7573
7572 The -C/--clean, -c/--check, and -m/--merge options control what
7574 The -C/--clean, -c/--check, and -m/--merge options control what
7573 happens if the working directory contains uncommitted changes.
7575 happens if the working directory contains uncommitted changes.
7574 At most of one of them can be specified.
7576 At most of one of them can be specified.
7575
7577
7576 1. If no option is specified, and if
7578 1. If no option is specified, and if
7577 the requested changeset is an ancestor or descendant of
7579 the requested changeset is an ancestor or descendant of
7578 the working directory's parent, the uncommitted changes
7580 the working directory's parent, the uncommitted changes
7579 are merged into the requested changeset and the merged
7581 are merged into the requested changeset and the merged
7580 result is left uncommitted. If the requested changeset is
7582 result is left uncommitted. If the requested changeset is
7581 not an ancestor or descendant (that is, it is on another
7583 not an ancestor or descendant (that is, it is on another
7582 branch), the update is aborted and the uncommitted changes
7584 branch), the update is aborted and the uncommitted changes
7583 are preserved.
7585 are preserved.
7584
7586
7585 2. With the -m/--merge option, the update is allowed even if the
7587 2. With the -m/--merge option, the update is allowed even if the
7586 requested changeset is not an ancestor or descendant of
7588 requested changeset is not an ancestor or descendant of
7587 the working directory's parent.
7589 the working directory's parent.
7588
7590
7589 3. With the -c/--check option, the update is aborted and the
7591 3. With the -c/--check option, the update is aborted and the
7590 uncommitted changes are preserved.
7592 uncommitted changes are preserved.
7591
7593
7592 4. With the -C/--clean option, uncommitted changes are discarded and
7594 4. With the -C/--clean option, uncommitted changes are discarded and
7593 the working directory is updated to the requested changeset.
7595 the working directory is updated to the requested changeset.
7594
7596
7595 To cancel an uncommitted merge (and lose your changes), use
7597 To cancel an uncommitted merge (and lose your changes), use
7596 :hg:`merge --abort`.
7598 :hg:`merge --abort`.
7597
7599
7598 Use null as the changeset to remove the working directory (like
7600 Use null as the changeset to remove the working directory (like
7599 :hg:`clone -U`).
7601 :hg:`clone -U`).
7600
7602
7601 If you want to revert just one file to an older revision, use
7603 If you want to revert just one file to an older revision, use
7602 :hg:`revert [-r REV] NAME`.
7604 :hg:`revert [-r REV] NAME`.
7603
7605
7604 See :hg:`help dates` for a list of formats valid for -d/--date.
7606 See :hg:`help dates` for a list of formats valid for -d/--date.
7605
7607
7606 Returns 0 on success, 1 if there are unresolved files.
7608 Returns 0 on success, 1 if there are unresolved files.
7607 """
7609 """
7608 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7610 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7609 rev = opts.get('rev')
7611 rev = opts.get('rev')
7610 date = opts.get('date')
7612 date = opts.get('date')
7611 clean = opts.get('clean')
7613 clean = opts.get('clean')
7612 check = opts.get('check')
7614 check = opts.get('check')
7613 merge = opts.get('merge')
7615 merge = opts.get('merge')
7614 if rev and node:
7616 if rev and node:
7615 raise error.Abort(_(b"please specify just one revision"))
7617 raise error.Abort(_(b"please specify just one revision"))
7616
7618
7617 if ui.configbool(b'commands', b'update.requiredest'):
7619 if ui.configbool(b'commands', b'update.requiredest'):
7618 if not node and not rev and not date:
7620 if not node and not rev and not date:
7619 raise error.Abort(
7621 raise error.Abort(
7620 _(b'you must specify a destination'),
7622 _(b'you must specify a destination'),
7621 hint=_(b'for example: hg update ".::"'),
7623 hint=_(b'for example: hg update ".::"'),
7622 )
7624 )
7623
7625
7624 if rev is None or rev == b'':
7626 if rev is None or rev == b'':
7625 rev = node
7627 rev = node
7626
7628
7627 if date and rev is not None:
7629 if date and rev is not None:
7628 raise error.Abort(_(b"you can't specify a revision and a date"))
7630 raise error.Abort(_(b"you can't specify a revision and a date"))
7629
7631
7630 updatecheck = None
7632 updatecheck = None
7631 if check:
7633 if check:
7632 updatecheck = b'abort'
7634 updatecheck = b'abort'
7633 elif merge:
7635 elif merge:
7634 updatecheck = b'none'
7636 updatecheck = b'none'
7635
7637
7636 with repo.wlock():
7638 with repo.wlock():
7637 cmdutil.clearunfinished(repo)
7639 cmdutil.clearunfinished(repo)
7638 if date:
7640 if date:
7639 rev = cmdutil.finddate(ui, repo, date)
7641 rev = cmdutil.finddate(ui, repo, date)
7640
7642
7641 # if we defined a bookmark, we have to remember the original name
7643 # if we defined a bookmark, we have to remember the original name
7642 brev = rev
7644 brev = rev
7643 if rev:
7645 if rev:
7644 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7645 ctx = scmutil.revsingle(repo, rev, default=None)
7647 ctx = scmutil.revsingle(repo, rev, default=None)
7646 rev = ctx.rev()
7648 rev = ctx.rev()
7647 hidden = ctx.hidden()
7649 hidden = ctx.hidden()
7648 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7650 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7649 with ui.configoverride(overrides, b'update'):
7651 with ui.configoverride(overrides, b'update'):
7650 ret = hg.updatetotally(
7652 ret = hg.updatetotally(
7651 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7653 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7652 )
7654 )
7653 if hidden:
7655 if hidden:
7654 ctxstr = ctx.hex()[:12]
7656 ctxstr = ctx.hex()[:12]
7655 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7657 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7656
7658
7657 if ctx.obsolete():
7659 if ctx.obsolete():
7658 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7660 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7659 ui.warn(b"(%s)\n" % obsfatemsg)
7661 ui.warn(b"(%s)\n" % obsfatemsg)
7660 return ret
7662 return ret
7661
7663
7662
7664
7663 @command(
7665 @command(
7664 b'verify',
7666 b'verify',
7665 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7667 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7666 helpcategory=command.CATEGORY_MAINTENANCE,
7668 helpcategory=command.CATEGORY_MAINTENANCE,
7667 )
7669 )
7668 def verify(ui, repo, **opts):
7670 def verify(ui, repo, **opts):
7669 """verify the integrity of the repository
7671 """verify the integrity of the repository
7670
7672
7671 Verify the integrity of the current repository.
7673 Verify the integrity of the current repository.
7672
7674
7673 This will perform an extensive check of the repository's
7675 This will perform an extensive check of the repository's
7674 integrity, validating the hashes and checksums of each entry in
7676 integrity, validating the hashes and checksums of each entry in
7675 the changelog, manifest, and tracked files, as well as the
7677 the changelog, manifest, and tracked files, as well as the
7676 integrity of their crosslinks and indices.
7678 integrity of their crosslinks and indices.
7677
7679
7678 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7680 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7679 for more information about recovery from corruption of the
7681 for more information about recovery from corruption of the
7680 repository.
7682 repository.
7681
7683
7682 Returns 0 on success, 1 if errors are encountered.
7684 Returns 0 on success, 1 if errors are encountered.
7683 """
7685 """
7684 opts = pycompat.byteskwargs(opts)
7686 opts = pycompat.byteskwargs(opts)
7685
7687
7686 level = None
7688 level = None
7687 if opts[b'full']:
7689 if opts[b'full']:
7688 level = verifymod.VERIFY_FULL
7690 level = verifymod.VERIFY_FULL
7689 return hg.verify(repo, level)
7691 return hg.verify(repo, level)
7690
7692
7691
7693
7692 @command(
7694 @command(
7693 b'version',
7695 b'version',
7694 [] + formatteropts,
7696 [] + formatteropts,
7695 helpcategory=command.CATEGORY_HELP,
7697 helpcategory=command.CATEGORY_HELP,
7696 norepo=True,
7698 norepo=True,
7697 intents={INTENT_READONLY},
7699 intents={INTENT_READONLY},
7698 )
7700 )
7699 def version_(ui, **opts):
7701 def version_(ui, **opts):
7700 """output version and copyright information
7702 """output version and copyright information
7701
7703
7702 .. container:: verbose
7704 .. container:: verbose
7703
7705
7704 Template:
7706 Template:
7705
7707
7706 The following keywords are supported. See also :hg:`help templates`.
7708 The following keywords are supported. See also :hg:`help templates`.
7707
7709
7708 :extensions: List of extensions.
7710 :extensions: List of extensions.
7709 :ver: String. Version number.
7711 :ver: String. Version number.
7710
7712
7711 And each entry of ``{extensions}`` provides the following sub-keywords
7713 And each entry of ``{extensions}`` provides the following sub-keywords
7712 in addition to ``{ver}``.
7714 in addition to ``{ver}``.
7713
7715
7714 :bundled: Boolean. True if included in the release.
7716 :bundled: Boolean. True if included in the release.
7715 :name: String. Extension name.
7717 :name: String. Extension name.
7716 """
7718 """
7717 opts = pycompat.byteskwargs(opts)
7719 opts = pycompat.byteskwargs(opts)
7718 if ui.verbose:
7720 if ui.verbose:
7719 ui.pager(b'version')
7721 ui.pager(b'version')
7720 fm = ui.formatter(b"version", opts)
7722 fm = ui.formatter(b"version", opts)
7721 fm.startitem()
7723 fm.startitem()
7722 fm.write(
7724 fm.write(
7723 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7725 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7724 )
7726 )
7725 license = _(
7727 license = _(
7726 b"(see https://mercurial-scm.org for more information)\n"
7728 b"(see https://mercurial-scm.org for more information)\n"
7727 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7729 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7728 b"This is free software; see the source for copying conditions. "
7730 b"This is free software; see the source for copying conditions. "
7729 b"There is NO\nwarranty; "
7731 b"There is NO\nwarranty; "
7730 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7732 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7731 )
7733 )
7732 if not ui.quiet:
7734 if not ui.quiet:
7733 fm.plain(license)
7735 fm.plain(license)
7734
7736
7735 if ui.verbose:
7737 if ui.verbose:
7736 fm.plain(_(b"\nEnabled extensions:\n\n"))
7738 fm.plain(_(b"\nEnabled extensions:\n\n"))
7737 # format names and versions into columns
7739 # format names and versions into columns
7738 names = []
7740 names = []
7739 vers = []
7741 vers = []
7740 isinternals = []
7742 isinternals = []
7741 for name, module in sorted(extensions.extensions()):
7743 for name, module in sorted(extensions.extensions()):
7742 names.append(name)
7744 names.append(name)
7743 vers.append(extensions.moduleversion(module) or None)
7745 vers.append(extensions.moduleversion(module) or None)
7744 isinternals.append(extensions.ismoduleinternal(module))
7746 isinternals.append(extensions.ismoduleinternal(module))
7745 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7747 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7746 if names:
7748 if names:
7747 namefmt = b" %%-%ds " % max(len(n) for n in names)
7749 namefmt = b" %%-%ds " % max(len(n) for n in names)
7748 places = [_(b"external"), _(b"internal")]
7750 places = [_(b"external"), _(b"internal")]
7749 for n, v, p in zip(names, vers, isinternals):
7751 for n, v, p in zip(names, vers, isinternals):
7750 fn.startitem()
7752 fn.startitem()
7751 fn.condwrite(ui.verbose, b"name", namefmt, n)
7753 fn.condwrite(ui.verbose, b"name", namefmt, n)
7752 if ui.verbose:
7754 if ui.verbose:
7753 fn.plain(b"%s " % places[p])
7755 fn.plain(b"%s " % places[p])
7754 fn.data(bundled=p)
7756 fn.data(bundled=p)
7755 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7757 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7756 if ui.verbose:
7758 if ui.verbose:
7757 fn.plain(b"\n")
7759 fn.plain(b"\n")
7758 fn.end()
7760 fn.end()
7759 fm.end()
7761 fm.end()
7760
7762
7761
7763
7762 def loadcmdtable(ui, name, cmdtable):
7764 def loadcmdtable(ui, name, cmdtable):
7763 """Load command functions from specified cmdtable
7765 """Load command functions from specified cmdtable
7764 """
7766 """
7765 overrides = [cmd for cmd in cmdtable if cmd in table]
7767 overrides = [cmd for cmd in cmdtable if cmd in table]
7766 if overrides:
7768 if overrides:
7767 ui.warn(
7769 ui.warn(
7768 _(b"extension '%s' overrides commands: %s\n")
7770 _(b"extension '%s' overrides commands: %s\n")
7769 % (name, b" ".join(overrides))
7771 % (name, b" ".join(overrides))
7770 )
7772 )
7771 table.update(cmdtable)
7773 table.update(cmdtable)
@@ -1,71 +1,92 b''
1 # grep.py - logic for history walk and grep
1 # grep.py - logic for history walk and grep
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
11
12 from . import pycompat
12 from . import (
13 pycompat,
14 scmutil,
15 util,
16 )
13
17
14
18
15 def matchlines(body, regexp):
19 def matchlines(body, regexp):
16 begin = 0
20 begin = 0
17 linenum = 0
21 linenum = 0
18 while begin < len(body):
22 while begin < len(body):
19 match = regexp.search(body, begin)
23 match = regexp.search(body, begin)
20 if not match:
24 if not match:
21 break
25 break
22 mstart, mend = match.span()
26 mstart, mend = match.span()
23 linenum += body.count(b'\n', begin, mstart) + 1
27 linenum += body.count(b'\n', begin, mstart) + 1
24 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
28 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
25 begin = body.find(b'\n', mend) + 1 or len(body) + 1
29 begin = body.find(b'\n', mend) + 1 or len(body) + 1
26 lend = begin - 1
30 lend = begin - 1
27 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
31 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
28
32
29
33
30 class linestate(object):
34 class linestate(object):
31 def __init__(self, line, linenum, colstart, colend):
35 def __init__(self, line, linenum, colstart, colend):
32 self.line = line
36 self.line = line
33 self.linenum = linenum
37 self.linenum = linenum
34 self.colstart = colstart
38 self.colstart = colstart
35 self.colend = colend
39 self.colend = colend
36
40
37 def __hash__(self):
41 def __hash__(self):
38 return hash(self.line)
42 return hash(self.line)
39
43
40 def __eq__(self, other):
44 def __eq__(self, other):
41 return self.line == other.line
45 return self.line == other.line
42
46
43 def findpos(self, regexp):
47 def findpos(self, regexp):
44 """Iterate all (start, end) indices of matches"""
48 """Iterate all (start, end) indices of matches"""
45 yield self.colstart, self.colend
49 yield self.colstart, self.colend
46 p = self.colend
50 p = self.colend
47 while p < len(self.line):
51 while p < len(self.line):
48 m = regexp.search(self.line, p)
52 m = regexp.search(self.line, p)
49 if not m:
53 if not m:
50 break
54 break
51 if m.end() == p:
55 if m.end() == p:
52 p += 1
56 p += 1
53 else:
57 else:
54 yield m.span()
58 yield m.span()
55 p = m.end()
59 p = m.end()
56
60
57
61
58 def difflinestates(a, b):
62 def difflinestates(a, b):
59 sm = difflib.SequenceMatcher(None, a, b)
63 sm = difflib.SequenceMatcher(None, a, b)
60 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
64 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
61 if tag == 'insert':
65 if tag == 'insert':
62 for i in pycompat.xrange(blo, bhi):
66 for i in pycompat.xrange(blo, bhi):
63 yield (b'+', b[i])
67 yield (b'+', b[i])
64 elif tag == 'delete':
68 elif tag == 'delete':
65 for i in pycompat.xrange(alo, ahi):
69 for i in pycompat.xrange(alo, ahi):
66 yield (b'-', a[i])
70 yield (b'-', a[i])
67 elif tag == 'replace':
71 elif tag == 'replace':
68 for i in pycompat.xrange(alo, ahi):
72 for i in pycompat.xrange(alo, ahi):
69 yield (b'-', a[i])
73 yield (b'-', a[i])
70 for i in pycompat.xrange(blo, bhi):
74 for i in pycompat.xrange(blo, bhi):
71 yield (b'+', b[i])
75 yield (b'+', b[i])
76
77
78 class grepsearcher(object):
79 """Search files and revisions for lines matching the given pattern"""
80
81 def __init__(self, ui, repo, regexp):
82 self._ui = ui
83 self._repo = repo
84 self._regexp = regexp
85
86 self._getfile = util.lrucachefunc(repo.file)
87 self._getrenamed = scmutil.getrenamedfn(repo)
88
89 self._matches = {}
90 self._copies = {}
91 self._skip = set()
92 self._revfiles = {}
General Comments 0
You need to be logged in to leave comments. Login now