##// END OF EJS Templates
grep: warn on censored revisions instead of erroring out...
Jordi Gutiérrez Hermoso -
r43622:13b8097d default
parent child Browse files
Show More
@@ -1,7808 +1,7822 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from .pycompat import open
25 from .pycompat import open
26 from . import (
26 from . import (
27 archival,
27 archival,
28 bookmarks,
28 bookmarks,
29 bundle2,
29 bundle2,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 copies,
32 copies,
33 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
34 destutil,
34 destutil,
35 dirstateguard,
35 dirstateguard,
36 discovery,
36 discovery,
37 encoding,
37 encoding,
38 error,
38 error,
39 exchange,
39 exchange,
40 extensions,
40 extensions,
41 filemerge,
41 filemerge,
42 formatter,
42 formatter,
43 graphmod,
43 graphmod,
44 hbisect,
44 hbisect,
45 help,
45 help,
46 hg,
46 hg,
47 logcmdutil,
47 logcmdutil,
48 merge as mergemod,
48 merge as mergemod,
49 narrowspec,
49 narrowspec,
50 obsolete,
50 obsolete,
51 obsutil,
51 obsutil,
52 patch,
52 patch,
53 phases,
53 phases,
54 pycompat,
54 pycompat,
55 rcutil,
55 rcutil,
56 registrar,
56 registrar,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 shelve as shelvemod,
61 shelve as shelvemod,
62 state as statemod,
62 state as statemod,
63 streamclone,
63 streamclone,
64 tags as tagsmod,
64 tags as tagsmod,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 verify as verifymod,
67 verify as verifymod,
68 wireprotoserver,
68 wireprotoserver,
69 )
69 )
70 from .utils import (
70 from .utils import (
71 dateutil,
71 dateutil,
72 stringutil,
72 stringutil,
73 )
73 )
74
74
75 table = {}
75 table = {}
76 table.update(debugcommandsmod.command._table)
76 table.update(debugcommandsmod.command._table)
77
77
78 command = registrar.command(table)
78 command = registrar.command(table)
79 INTENT_READONLY = registrar.INTENT_READONLY
79 INTENT_READONLY = registrar.INTENT_READONLY
80
80
81 # common command options
81 # common command options
82
82
83 globalopts = [
83 globalopts = [
84 (
84 (
85 b'R',
85 b'R',
86 b'repository',
86 b'repository',
87 b'',
87 b'',
88 _(b'repository root directory or name of overlay bundle file'),
88 _(b'repository root directory or name of overlay bundle file'),
89 _(b'REPO'),
89 _(b'REPO'),
90 ),
90 ),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
91 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
92 (
92 (
93 b'y',
93 b'y',
94 b'noninteractive',
94 b'noninteractive',
95 None,
95 None,
96 _(
96 _(
97 b'do not prompt, automatically pick the first choice for all prompts'
97 b'do not prompt, automatically pick the first choice for all prompts'
98 ),
98 ),
99 ),
99 ),
100 (b'q', b'quiet', None, _(b'suppress output')),
100 (b'q', b'quiet', None, _(b'suppress output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
101 (b'v', b'verbose', None, _(b'enable additional output')),
102 (
102 (
103 b'',
103 b'',
104 b'color',
104 b'color',
105 b'',
105 b'',
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
106 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
107 # and should not be translated
107 # and should not be translated
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
108 _(b"when to colorize (boolean, always, auto, never, or debug)"),
109 _(b'TYPE'),
109 _(b'TYPE'),
110 ),
110 ),
111 (
111 (
112 b'',
112 b'',
113 b'config',
113 b'config',
114 [],
114 [],
115 _(b'set/override config option (use \'section.name=value\')'),
115 _(b'set/override config option (use \'section.name=value\')'),
116 _(b'CONFIG'),
116 _(b'CONFIG'),
117 ),
117 ),
118 (b'', b'debug', None, _(b'enable debugging output')),
118 (b'', b'debug', None, _(b'enable debugging output')),
119 (b'', b'debugger', None, _(b'start debugger')),
119 (b'', b'debugger', None, _(b'start debugger')),
120 (
120 (
121 b'',
121 b'',
122 b'encoding',
122 b'encoding',
123 encoding.encoding,
123 encoding.encoding,
124 _(b'set the charset encoding'),
124 _(b'set the charset encoding'),
125 _(b'ENCODE'),
125 _(b'ENCODE'),
126 ),
126 ),
127 (
127 (
128 b'',
128 b'',
129 b'encodingmode',
129 b'encodingmode',
130 encoding.encodingmode,
130 encoding.encodingmode,
131 _(b'set the charset encoding mode'),
131 _(b'set the charset encoding mode'),
132 _(b'MODE'),
132 _(b'MODE'),
133 ),
133 ),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
134 (b'', b'traceback', None, _(b'always print a traceback on exception')),
135 (b'', b'time', None, _(b'time how long the command takes')),
135 (b'', b'time', None, _(b'time how long the command takes')),
136 (b'', b'profile', None, _(b'print command execution profile')),
136 (b'', b'profile', None, _(b'print command execution profile')),
137 (b'', b'version', None, _(b'output version information and exit')),
137 (b'', b'version', None, _(b'output version information and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
138 (b'h', b'help', None, _(b'display help and exit')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
139 (b'', b'hidden', False, _(b'consider hidden changesets')),
140 (
140 (
141 b'',
141 b'',
142 b'pager',
142 b'pager',
143 b'auto',
143 b'auto',
144 _(b"when to paginate (boolean, always, auto, or never)"),
144 _(b"when to paginate (boolean, always, auto, or never)"),
145 _(b'TYPE'),
145 _(b'TYPE'),
146 ),
146 ),
147 ]
147 ]
148
148
149 dryrunopts = cmdutil.dryrunopts
149 dryrunopts = cmdutil.dryrunopts
150 remoteopts = cmdutil.remoteopts
150 remoteopts = cmdutil.remoteopts
151 walkopts = cmdutil.walkopts
151 walkopts = cmdutil.walkopts
152 commitopts = cmdutil.commitopts
152 commitopts = cmdutil.commitopts
153 commitopts2 = cmdutil.commitopts2
153 commitopts2 = cmdutil.commitopts2
154 commitopts3 = cmdutil.commitopts3
154 commitopts3 = cmdutil.commitopts3
155 formatteropts = cmdutil.formatteropts
155 formatteropts = cmdutil.formatteropts
156 templateopts = cmdutil.templateopts
156 templateopts = cmdutil.templateopts
157 logopts = cmdutil.logopts
157 logopts = cmdutil.logopts
158 diffopts = cmdutil.diffopts
158 diffopts = cmdutil.diffopts
159 diffwsopts = cmdutil.diffwsopts
159 diffwsopts = cmdutil.diffwsopts
160 diffopts2 = cmdutil.diffopts2
160 diffopts2 = cmdutil.diffopts2
161 mergetoolopts = cmdutil.mergetoolopts
161 mergetoolopts = cmdutil.mergetoolopts
162 similarityopts = cmdutil.similarityopts
162 similarityopts = cmdutil.similarityopts
163 subrepoopts = cmdutil.subrepoopts
163 subrepoopts = cmdutil.subrepoopts
164 debugrevlogopts = cmdutil.debugrevlogopts
164 debugrevlogopts = cmdutil.debugrevlogopts
165
165
166 # Commands start here, listed alphabetically
166 # Commands start here, listed alphabetically
167
167
168
168
169 @command(
169 @command(
170 b'abort',
170 b'abort',
171 dryrunopts,
171 dryrunopts,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
172 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
173 helpbasic=True,
173 helpbasic=True,
174 )
174 )
175 def abort(ui, repo, **opts):
175 def abort(ui, repo, **opts):
176 """abort an unfinished operation (EXPERIMENTAL)
176 """abort an unfinished operation (EXPERIMENTAL)
177
177
178 Aborts a multistep operation like graft, histedit, rebase, merge,
178 Aborts a multistep operation like graft, histedit, rebase, merge,
179 and unshelve if they are in an unfinished state.
179 and unshelve if they are in an unfinished state.
180
180
181 use --dry-run/-n to dry run the command.
181 use --dry-run/-n to dry run the command.
182 """
182 """
183 dryrun = opts.get(r'dry_run')
183 dryrun = opts.get(r'dry_run')
184 abortstate = cmdutil.getunfinishedstate(repo)
184 abortstate = cmdutil.getunfinishedstate(repo)
185 if not abortstate:
185 if not abortstate:
186 raise error.Abort(_(b'no operation in progress'))
186 raise error.Abort(_(b'no operation in progress'))
187 if not abortstate.abortfunc:
187 if not abortstate.abortfunc:
188 raise error.Abort(
188 raise error.Abort(
189 (
189 (
190 _(b"%s in progress but does not support 'hg abort'")
190 _(b"%s in progress but does not support 'hg abort'")
191 % (abortstate._opname)
191 % (abortstate._opname)
192 ),
192 ),
193 hint=abortstate.hint(),
193 hint=abortstate.hint(),
194 )
194 )
195 if dryrun:
195 if dryrun:
196 ui.status(
196 ui.status(
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
197 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
198 )
198 )
199 return
199 return
200 return abortstate.abortfunc(ui, repo)
200 return abortstate.abortfunc(ui, repo)
201
201
202
202
203 @command(
203 @command(
204 b'add',
204 b'add',
205 walkopts + subrepoopts + dryrunopts,
205 walkopts + subrepoopts + dryrunopts,
206 _(b'[OPTION]... [FILE]...'),
206 _(b'[OPTION]... [FILE]...'),
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
208 helpbasic=True,
208 helpbasic=True,
209 inferrepo=True,
209 inferrepo=True,
210 )
210 )
211 def add(ui, repo, *pats, **opts):
211 def add(ui, repo, *pats, **opts):
212 """add the specified files on the next commit
212 """add the specified files on the next commit
213
213
214 Schedule files to be version controlled and added to the
214 Schedule files to be version controlled and added to the
215 repository.
215 repository.
216
216
217 The files will be added to the repository at the next commit. To
217 The files will be added to the repository at the next commit. To
218 undo an add before that, see :hg:`forget`.
218 undo an add before that, see :hg:`forget`.
219
219
220 If no names are given, add all files to the repository (except
220 If no names are given, add all files to the repository (except
221 files matching ``.hgignore``).
221 files matching ``.hgignore``).
222
222
223 .. container:: verbose
223 .. container:: verbose
224
224
225 Examples:
225 Examples:
226
226
227 - New (unknown) files are added
227 - New (unknown) files are added
228 automatically by :hg:`add`::
228 automatically by :hg:`add`::
229
229
230 $ ls
230 $ ls
231 foo.c
231 foo.c
232 $ hg status
232 $ hg status
233 ? foo.c
233 ? foo.c
234 $ hg add
234 $ hg add
235 adding foo.c
235 adding foo.c
236 $ hg status
236 $ hg status
237 A foo.c
237 A foo.c
238
238
239 - Specific files to be added can be specified::
239 - Specific files to be added can be specified::
240
240
241 $ ls
241 $ ls
242 bar.c foo.c
242 bar.c foo.c
243 $ hg status
243 $ hg status
244 ? bar.c
244 ? bar.c
245 ? foo.c
245 ? foo.c
246 $ hg add bar.c
246 $ hg add bar.c
247 $ hg status
247 $ hg status
248 A bar.c
248 A bar.c
249 ? foo.c
249 ? foo.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253
253
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
254 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
255 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
256 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257 return rejected and 1 or 0
257 return rejected and 1 or 0
258
258
259
259
260 @command(
260 @command(
261 b'addremove',
261 b'addremove',
262 similarityopts + subrepoopts + walkopts + dryrunopts,
262 similarityopts + subrepoopts + walkopts + dryrunopts,
263 _(b'[OPTION]... [FILE]...'),
263 _(b'[OPTION]... [FILE]...'),
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
264 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
265 inferrepo=True,
265 inferrepo=True,
266 )
266 )
267 def addremove(ui, repo, *pats, **opts):
267 def addremove(ui, repo, *pats, **opts):
268 """add all new files, delete all missing files
268 """add all new files, delete all missing files
269
269
270 Add all new files and remove all missing files from the
270 Add all new files and remove all missing files from the
271 repository.
271 repository.
272
272
273 Unless names are given, new files are ignored if they match any of
273 Unless names are given, new files are ignored if they match any of
274 the patterns in ``.hgignore``. As with add, these changes take
274 the patterns in ``.hgignore``. As with add, these changes take
275 effect at the next commit.
275 effect at the next commit.
276
276
277 Use the -s/--similarity option to detect renamed files. This
277 Use the -s/--similarity option to detect renamed files. This
278 option takes a percentage between 0 (disabled) and 100 (files must
278 option takes a percentage between 0 (disabled) and 100 (files must
279 be identical) as its parameter. With a parameter greater than 0,
279 be identical) as its parameter. With a parameter greater than 0,
280 this compares every removed file with every added file and records
280 this compares every removed file with every added file and records
281 those similar enough as renames. Detecting renamed files this way
281 those similar enough as renames. Detecting renamed files this way
282 can be expensive. After using this option, :hg:`status -C` can be
282 can be expensive. After using this option, :hg:`status -C` can be
283 used to check which files were identified as moved or renamed. If
283 used to check which files were identified as moved or renamed. If
284 not specified, -s/--similarity defaults to 100 and only renames of
284 not specified, -s/--similarity defaults to 100 and only renames of
285 identical files are detected.
285 identical files are detected.
286
286
287 .. container:: verbose
287 .. container:: verbose
288
288
289 Examples:
289 Examples:
290
290
291 - A number of files (bar.c and foo.c) are new,
291 - A number of files (bar.c and foo.c) are new,
292 while foobar.c has been removed (without using :hg:`remove`)
292 while foobar.c has been removed (without using :hg:`remove`)
293 from the repository::
293 from the repository::
294
294
295 $ ls
295 $ ls
296 bar.c foo.c
296 bar.c foo.c
297 $ hg status
297 $ hg status
298 ! foobar.c
298 ! foobar.c
299 ? bar.c
299 ? bar.c
300 ? foo.c
300 ? foo.c
301 $ hg addremove
301 $ hg addremove
302 adding bar.c
302 adding bar.c
303 adding foo.c
303 adding foo.c
304 removing foobar.c
304 removing foobar.c
305 $ hg status
305 $ hg status
306 A bar.c
306 A bar.c
307 A foo.c
307 A foo.c
308 R foobar.c
308 R foobar.c
309
309
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
310 - A file foobar.c was moved to foo.c without using :hg:`rename`.
311 Afterwards, it was edited slightly::
311 Afterwards, it was edited slightly::
312
312
313 $ ls
313 $ ls
314 foo.c
314 foo.c
315 $ hg status
315 $ hg status
316 ! foobar.c
316 ! foobar.c
317 ? foo.c
317 ? foo.c
318 $ hg addremove --similarity 90
318 $ hg addremove --similarity 90
319 removing foobar.c
319 removing foobar.c
320 adding foo.c
320 adding foo.c
321 recording removal of foobar.c as rename to foo.c (94% similar)
321 recording removal of foobar.c as rename to foo.c (94% similar)
322 $ hg status -C
322 $ hg status -C
323 A foo.c
323 A foo.c
324 foobar.c
324 foobar.c
325 R foobar.c
325 R foobar.c
326
326
327 Returns 0 if all files are successfully added.
327 Returns 0 if all files are successfully added.
328 """
328 """
329 opts = pycompat.byteskwargs(opts)
329 opts = pycompat.byteskwargs(opts)
330 if not opts.get(b'similarity'):
330 if not opts.get(b'similarity'):
331 opts[b'similarity'] = b'100'
331 opts[b'similarity'] = b'100'
332 matcher = scmutil.match(repo[None], pats, opts)
332 matcher = scmutil.match(repo[None], pats, opts)
333 relative = scmutil.anypats(pats, opts)
333 relative = scmutil.anypats(pats, opts)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
334 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
335 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336
336
337
337
338 @command(
338 @command(
339 b'annotate|blame',
339 b'annotate|blame',
340 [
340 [
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
341 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342 (
342 (
343 b'',
343 b'',
344 b'follow',
344 b'follow',
345 None,
345 None,
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
346 _(b'follow copies/renames and list the filename (DEPRECATED)'),
347 ),
347 ),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
348 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
349 (b'a', b'text', None, _(b'treat all files as text')),
349 (b'a', b'text', None, _(b'treat all files as text')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
350 (b'u', b'user', None, _(b'list the author (long with -v)')),
351 (b'f', b'file', None, _(b'list the filename')),
351 (b'f', b'file', None, _(b'list the filename')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
352 (b'd', b'date', None, _(b'list the date (short with -q)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
353 (b'n', b'number', None, _(b'list the revision number (default)')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
354 (b'c', b'changeset', None, _(b'list the changeset')),
355 (
355 (
356 b'l',
356 b'l',
357 b'line-number',
357 b'line-number',
358 None,
358 None,
359 _(b'show line number at the first appearance'),
359 _(b'show line number at the first appearance'),
360 ),
360 ),
361 (
361 (
362 b'',
362 b'',
363 b'skip',
363 b'skip',
364 [],
364 [],
365 _(b'revision to not display (EXPERIMENTAL)'),
365 _(b'revision to not display (EXPERIMENTAL)'),
366 _(b'REV'),
366 _(b'REV'),
367 ),
367 ),
368 ]
368 ]
369 + diffwsopts
369 + diffwsopts
370 + walkopts
370 + walkopts
371 + formatteropts,
371 + formatteropts,
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
372 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
373 helpcategory=command.CATEGORY_FILE_CONTENTS,
374 helpbasic=True,
374 helpbasic=True,
375 inferrepo=True,
375 inferrepo=True,
376 )
376 )
377 def annotate(ui, repo, *pats, **opts):
377 def annotate(ui, repo, *pats, **opts):
378 """show changeset information by line for each file
378 """show changeset information by line for each file
379
379
380 List changes in files, showing the revision id responsible for
380 List changes in files, showing the revision id responsible for
381 each line.
381 each line.
382
382
383 This command is useful for discovering when a change was made and
383 This command is useful for discovering when a change was made and
384 by whom.
384 by whom.
385
385
386 If you include --file, --user, or --date, the revision number is
386 If you include --file, --user, or --date, the revision number is
387 suppressed unless you also include --number.
387 suppressed unless you also include --number.
388
388
389 Without the -a/--text option, annotate will avoid processing files
389 Without the -a/--text option, annotate will avoid processing files
390 it detects as binary. With -a, annotate will annotate the file
390 it detects as binary. With -a, annotate will annotate the file
391 anyway, although the results will probably be neither useful
391 anyway, although the results will probably be neither useful
392 nor desirable.
392 nor desirable.
393
393
394 .. container:: verbose
394 .. container:: verbose
395
395
396 Template:
396 Template:
397
397
398 The following keywords are supported in addition to the common template
398 The following keywords are supported in addition to the common template
399 keywords and functions. See also :hg:`help templates`.
399 keywords and functions. See also :hg:`help templates`.
400
400
401 :lines: List of lines with annotation data.
401 :lines: List of lines with annotation data.
402 :path: String. Repository-absolute path of the specified file.
402 :path: String. Repository-absolute path of the specified file.
403
403
404 And each entry of ``{lines}`` provides the following sub-keywords in
404 And each entry of ``{lines}`` provides the following sub-keywords in
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
405 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
406
406
407 :line: String. Line content.
407 :line: String. Line content.
408 :lineno: Integer. Line number at that revision.
408 :lineno: Integer. Line number at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
409 :path: String. Repository-absolute path of the file at that revision.
410
410
411 See :hg:`help templates.operators` for the list expansion syntax.
411 See :hg:`help templates.operators` for the list expansion syntax.
412
412
413 Returns 0 on success.
413 Returns 0 on success.
414 """
414 """
415 opts = pycompat.byteskwargs(opts)
415 opts = pycompat.byteskwargs(opts)
416 if not pats:
416 if not pats:
417 raise error.Abort(_(b'at least one filename or pattern is required'))
417 raise error.Abort(_(b'at least one filename or pattern is required'))
418
418
419 if opts.get(b'follow'):
419 if opts.get(b'follow'):
420 # --follow is deprecated and now just an alias for -f/--file
420 # --follow is deprecated and now just an alias for -f/--file
421 # to mimic the behavior of Mercurial before version 1.5
421 # to mimic the behavior of Mercurial before version 1.5
422 opts[b'file'] = True
422 opts[b'file'] = True
423
423
424 if (
424 if (
425 not opts.get(b'user')
425 not opts.get(b'user')
426 and not opts.get(b'changeset')
426 and not opts.get(b'changeset')
427 and not opts.get(b'date')
427 and not opts.get(b'date')
428 and not opts.get(b'file')
428 and not opts.get(b'file')
429 ):
429 ):
430 opts[b'number'] = True
430 opts[b'number'] = True
431
431
432 linenumber = opts.get(b'line_number') is not None
432 linenumber = opts.get(b'line_number') is not None
433 if (
433 if (
434 linenumber
434 linenumber
435 and (not opts.get(b'changeset'))
435 and (not opts.get(b'changeset'))
436 and (not opts.get(b'number'))
436 and (not opts.get(b'number'))
437 ):
437 ):
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
438 raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439
439
440 rev = opts.get(b'rev')
440 rev = opts.get(b'rev')
441 if rev:
441 if rev:
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
442 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443 ctx = scmutil.revsingle(repo, rev)
443 ctx = scmutil.revsingle(repo, rev)
444
444
445 ui.pager(b'annotate')
445 ui.pager(b'annotate')
446 rootfm = ui.formatter(b'annotate', opts)
446 rootfm = ui.formatter(b'annotate', opts)
447 if ui.debugflag:
447 if ui.debugflag:
448 shorthex = pycompat.identity
448 shorthex = pycompat.identity
449 else:
449 else:
450
450
451 def shorthex(h):
451 def shorthex(h):
452 return h[:12]
452 return h[:12]
453
453
454 if ui.quiet:
454 if ui.quiet:
455 datefunc = dateutil.shortdate
455 datefunc = dateutil.shortdate
456 else:
456 else:
457 datefunc = dateutil.datestr
457 datefunc = dateutil.datestr
458 if ctx.rev() is None:
458 if ctx.rev() is None:
459 if opts.get(b'changeset'):
459 if opts.get(b'changeset'):
460 # omit "+" suffix which is appended to node hex
460 # omit "+" suffix which is appended to node hex
461 def formatrev(rev):
461 def formatrev(rev):
462 if rev == wdirrev:
462 if rev == wdirrev:
463 return b'%d' % ctx.p1().rev()
463 return b'%d' % ctx.p1().rev()
464 else:
464 else:
465 return b'%d' % rev
465 return b'%d' % rev
466
466
467 else:
467 else:
468
468
469 def formatrev(rev):
469 def formatrev(rev):
470 if rev == wdirrev:
470 if rev == wdirrev:
471 return b'%d+' % ctx.p1().rev()
471 return b'%d+' % ctx.p1().rev()
472 else:
472 else:
473 return b'%d ' % rev
473 return b'%d ' % rev
474
474
475 def formathex(h):
475 def formathex(h):
476 if h == wdirhex:
476 if h == wdirhex:
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
477 return b'%s+' % shorthex(hex(ctx.p1().node()))
478 else:
478 else:
479 return b'%s ' % shorthex(h)
479 return b'%s ' % shorthex(h)
480
480
481 else:
481 else:
482 formatrev = b'%d'.__mod__
482 formatrev = b'%d'.__mod__
483 formathex = shorthex
483 formathex = shorthex
484
484
485 opmap = [
485 opmap = [
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
486 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
487 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
488 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
489 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
490 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
491 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 ]
492 ]
493 opnamemap = {
493 opnamemap = {
494 b'rev': b'number',
494 b'rev': b'number',
495 b'node': b'changeset',
495 b'node': b'changeset',
496 b'path': b'file',
496 b'path': b'file',
497 b'lineno': b'line_number',
497 b'lineno': b'line_number',
498 }
498 }
499
499
500 if rootfm.isplain():
500 if rootfm.isplain():
501
501
502 def makefunc(get, fmt):
502 def makefunc(get, fmt):
503 return lambda x: fmt(get(x))
503 return lambda x: fmt(get(x))
504
504
505 else:
505 else:
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return get
508 return get
509
509
510 datahint = rootfm.datahint()
510 datahint = rootfm.datahint()
511 funcmap = [
511 funcmap = [
512 (makefunc(get, fmt), sep)
512 (makefunc(get, fmt), sep)
513 for fn, sep, get, fmt in opmap
513 for fn, sep, get, fmt in opmap
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
514 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
515 ]
515 ]
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
516 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
517 fields = b' '.join(
517 fields = b' '.join(
518 fn
518 fn
519 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 )
521 )
522
522
523 def bad(x, y):
523 def bad(x, y):
524 raise error.Abort(b"%s: %s" % (x, y))
524 raise error.Abort(b"%s: %s" % (x, y))
525
525
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
526 m = scmutil.match(ctx, pats, opts, badfn=bad)
527
527
528 follow = not opts.get(b'no_follow')
528 follow = not opts.get(b'no_follow')
529 diffopts = patch.difffeatureopts(
529 diffopts = patch.difffeatureopts(
530 ui, opts, section=b'annotate', whitespace=True
530 ui, opts, section=b'annotate', whitespace=True
531 )
531 )
532 skiprevs = opts.get(b'skip')
532 skiprevs = opts.get(b'skip')
533 if skiprevs:
533 if skiprevs:
534 skiprevs = scmutil.revrange(repo, skiprevs)
534 skiprevs = scmutil.revrange(repo, skiprevs)
535
535
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
536 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
537 for abs in ctx.walk(m):
537 for abs in ctx.walk(m):
538 fctx = ctx[abs]
538 fctx = ctx[abs]
539 rootfm.startitem()
539 rootfm.startitem()
540 rootfm.data(path=abs)
540 rootfm.data(path=abs)
541 if not opts.get(b'text') and fctx.isbinary():
541 if not opts.get(b'text') and fctx.isbinary():
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
542 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 continue
543 continue
544
544
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
545 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 lines = fctx.annotate(
546 lines = fctx.annotate(
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
547 follow=follow, skiprevs=skiprevs, diffopts=diffopts
548 )
548 )
549 if not lines:
549 if not lines:
550 fm.end()
550 fm.end()
551 continue
551 continue
552 formats = []
552 formats = []
553 pieces = []
553 pieces = []
554
554
555 for f, sep in funcmap:
555 for f, sep in funcmap:
556 l = [f(n) for n in lines]
556 l = [f(n) for n in lines]
557 if fm.isplain():
557 if fm.isplain():
558 sizes = [encoding.colwidth(x) for x in l]
558 sizes = [encoding.colwidth(x) for x in l]
559 ml = max(sizes)
559 ml = max(sizes)
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
560 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561 else:
561 else:
562 formats.append([b'%s' for x in l])
562 formats.append([b'%s' for x in l])
563 pieces.append(l)
563 pieces.append(l)
564
564
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
565 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566 fm.startitem()
566 fm.startitem()
567 fm.context(fctx=n.fctx)
567 fm.context(fctx=n.fctx)
568 fm.write(fields, b"".join(f), *p)
568 fm.write(fields, b"".join(f), *p)
569 if n.skip:
569 if n.skip:
570 fmt = b"* %s"
570 fmt = b"* %s"
571 else:
571 else:
572 fmt = b": %s"
572 fmt = b": %s"
573 fm.write(b'line', fmt, n.text)
573 fm.write(b'line', fmt, n.text)
574
574
575 if not lines[-1].text.endswith(b'\n'):
575 if not lines[-1].text.endswith(b'\n'):
576 fm.plain(b'\n')
576 fm.plain(b'\n')
577 fm.end()
577 fm.end()
578
578
579 rootfm.end()
579 rootfm.end()
580
580
581
581
582 @command(
582 @command(
583 b'archive',
583 b'archive',
584 [
584 [
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
585 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586 (
586 (
587 b'p',
587 b'p',
588 b'prefix',
588 b'prefix',
589 b'',
589 b'',
590 _(b'directory prefix for files in archive'),
590 _(b'directory prefix for files in archive'),
591 _(b'PREFIX'),
591 _(b'PREFIX'),
592 ),
592 ),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
593 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
594 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 ]
595 ]
596 + subrepoopts
596 + subrepoopts
597 + walkopts,
597 + walkopts,
598 _(b'[OPTION]... DEST'),
598 _(b'[OPTION]... DEST'),
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
600 )
600 )
601 def archive(ui, repo, dest, **opts):
601 def archive(ui, repo, dest, **opts):
602 '''create an unversioned archive of a repository revision
602 '''create an unversioned archive of a repository revision
603
603
604 By default, the revision used is the parent of the working
604 By default, the revision used is the parent of the working
605 directory; use -r/--rev to specify a different revision.
605 directory; use -r/--rev to specify a different revision.
606
606
607 The archive type is automatically detected based on file
607 The archive type is automatically detected based on file
608 extension (to override, use -t/--type).
608 extension (to override, use -t/--type).
609
609
610 .. container:: verbose
610 .. container:: verbose
611
611
612 Examples:
612 Examples:
613
613
614 - create a zip file containing the 1.0 release::
614 - create a zip file containing the 1.0 release::
615
615
616 hg archive -r 1.0 project-1.0.zip
616 hg archive -r 1.0 project-1.0.zip
617
617
618 - create a tarball excluding .hg files::
618 - create a tarball excluding .hg files::
619
619
620 hg archive project.tar.gz -X ".hg*"
620 hg archive project.tar.gz -X ".hg*"
621
621
622 Valid types are:
622 Valid types are:
623
623
624 :``files``: a directory full of files (default)
624 :``files``: a directory full of files (default)
625 :``tar``: tar archive, uncompressed
625 :``tar``: tar archive, uncompressed
626 :``tbz2``: tar archive, compressed using bzip2
626 :``tbz2``: tar archive, compressed using bzip2
627 :``tgz``: tar archive, compressed using gzip
627 :``tgz``: tar archive, compressed using gzip
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
628 :``txz``: tar archive, compressed using lzma (only in Python 3)
629 :``uzip``: zip archive, uncompressed
629 :``uzip``: zip archive, uncompressed
630 :``zip``: zip archive, compressed using deflate
630 :``zip``: zip archive, compressed using deflate
631
631
632 The exact name of the destination archive or directory is given
632 The exact name of the destination archive or directory is given
633 using a format string; see :hg:`help export` for details.
633 using a format string; see :hg:`help export` for details.
634
634
635 Each member added to an archive file has a directory prefix
635 Each member added to an archive file has a directory prefix
636 prepended. Use -p/--prefix to specify a format string for the
636 prepended. Use -p/--prefix to specify a format string for the
637 prefix. The default is the basename of the archive, with suffixes
637 prefix. The default is the basename of the archive, with suffixes
638 removed.
638 removed.
639
639
640 Returns 0 on success.
640 Returns 0 on success.
641 '''
641 '''
642
642
643 opts = pycompat.byteskwargs(opts)
643 opts = pycompat.byteskwargs(opts)
644 rev = opts.get(b'rev')
644 rev = opts.get(b'rev')
645 if rev:
645 if rev:
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
646 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647 ctx = scmutil.revsingle(repo, rev)
647 ctx = scmutil.revsingle(repo, rev)
648 if not ctx:
648 if not ctx:
649 raise error.Abort(_(b'no working directory: please specify a revision'))
649 raise error.Abort(_(b'no working directory: please specify a revision'))
650 node = ctx.node()
650 node = ctx.node()
651 dest = cmdutil.makefilename(ctx, dest)
651 dest = cmdutil.makefilename(ctx, dest)
652 if os.path.realpath(dest) == repo.root:
652 if os.path.realpath(dest) == repo.root:
653 raise error.Abort(_(b'repository root cannot be destination'))
653 raise error.Abort(_(b'repository root cannot be destination'))
654
654
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
655 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
656 prefix = opts.get(b'prefix')
656 prefix = opts.get(b'prefix')
657
657
658 if dest == b'-':
658 if dest == b'-':
659 if kind == b'files':
659 if kind == b'files':
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
660 raise error.Abort(_(b'cannot archive plain files to stdout'))
661 dest = cmdutil.makefileobj(ctx, dest)
661 dest = cmdutil.makefileobj(ctx, dest)
662 if not prefix:
662 if not prefix:
663 prefix = os.path.basename(repo.root) + b'-%h'
663 prefix = os.path.basename(repo.root) + b'-%h'
664
664
665 prefix = cmdutil.makefilename(ctx, prefix)
665 prefix = cmdutil.makefilename(ctx, prefix)
666 match = scmutil.match(ctx, [], opts)
666 match = scmutil.match(ctx, [], opts)
667 archival.archive(
667 archival.archive(
668 repo,
668 repo,
669 dest,
669 dest,
670 node,
670 node,
671 kind,
671 kind,
672 not opts.get(b'no_decode'),
672 not opts.get(b'no_decode'),
673 match,
673 match,
674 prefix,
674 prefix,
675 subrepos=opts.get(b'subrepos'),
675 subrepos=opts.get(b'subrepos'),
676 )
676 )
677
677
678
678
679 @command(
679 @command(
680 b'backout',
680 b'backout',
681 [
681 [
682 (
682 (
683 b'',
683 b'',
684 b'merge',
684 b'merge',
685 None,
685 None,
686 _(b'merge with old dirstate parent after backout'),
686 _(b'merge with old dirstate parent after backout'),
687 ),
687 ),
688 (
688 (
689 b'',
689 b'',
690 b'commit',
690 b'commit',
691 None,
691 None,
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
692 _(b'commit if no conflicts were encountered (DEPRECATED)'),
693 ),
693 ),
694 (b'', b'no-commit', None, _(b'do not commit')),
694 (b'', b'no-commit', None, _(b'do not commit')),
695 (
695 (
696 b'',
696 b'',
697 b'parent',
697 b'parent',
698 b'',
698 b'',
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
699 _(b'parent to choose when backing out merge (DEPRECATED)'),
700 _(b'REV'),
700 _(b'REV'),
701 ),
701 ),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
702 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
703 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 ]
704 ]
705 + mergetoolopts
705 + mergetoolopts
706 + walkopts
706 + walkopts
707 + commitopts
707 + commitopts
708 + commitopts2,
708 + commitopts2,
709 _(b'[OPTION]... [-r] REV'),
709 _(b'[OPTION]... [-r] REV'),
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
710 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
711 )
711 )
712 def backout(ui, repo, node=None, rev=None, **opts):
712 def backout(ui, repo, node=None, rev=None, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Prepare a new changeset with the effect of REV undone in the
715 Prepare a new changeset with the effect of REV undone in the
716 current working directory. If no conflicts were encountered,
716 current working directory. If no conflicts were encountered,
717 it will be committed immediately.
717 it will be committed immediately.
718
718
719 If REV is the parent of the working directory, then this new changeset
719 If REV is the parent of the working directory, then this new changeset
720 is committed automatically (unless --no-commit is specified).
720 is committed automatically (unless --no-commit is specified).
721
721
722 .. note::
722 .. note::
723
723
724 :hg:`backout` cannot be used to fix either an unwanted or
724 :hg:`backout` cannot be used to fix either an unwanted or
725 incorrect merge.
725 incorrect merge.
726
726
727 .. container:: verbose
727 .. container:: verbose
728
728
729 Examples:
729 Examples:
730
730
731 - Reverse the effect of the parent of the working directory.
731 - Reverse the effect of the parent of the working directory.
732 This backout will be committed immediately::
732 This backout will be committed immediately::
733
733
734 hg backout -r .
734 hg backout -r .
735
735
736 - Reverse the effect of previous bad revision 23::
736 - Reverse the effect of previous bad revision 23::
737
737
738 hg backout -r 23
738 hg backout -r 23
739
739
740 - Reverse the effect of previous bad revision 23 and
740 - Reverse the effect of previous bad revision 23 and
741 leave changes uncommitted::
741 leave changes uncommitted::
742
742
743 hg backout -r 23 --no-commit
743 hg backout -r 23 --no-commit
744 hg commit -m "Backout revision 23"
744 hg commit -m "Backout revision 23"
745
745
746 By default, the pending changeset will have one parent,
746 By default, the pending changeset will have one parent,
747 maintaining a linear history. With --merge, the pending
747 maintaining a linear history. With --merge, the pending
748 changeset will instead have two parents: the old parent of the
748 changeset will instead have two parents: the old parent of the
749 working directory and a new child of REV that simply undoes REV.
749 working directory and a new child of REV that simply undoes REV.
750
750
751 Before version 1.7, the behavior without --merge was equivalent
751 Before version 1.7, the behavior without --merge was equivalent
752 to specifying --merge followed by :hg:`update --clean .` to
752 to specifying --merge followed by :hg:`update --clean .` to
753 cancel the merge and leave the child of REV as a head to be
753 cancel the merge and leave the child of REV as a head to be
754 merged separately.
754 merged separately.
755
755
756 See :hg:`help dates` for a list of formats valid for -d/--date.
756 See :hg:`help dates` for a list of formats valid for -d/--date.
757
757
758 See :hg:`help revert` for a way to restore files to the state
758 See :hg:`help revert` for a way to restore files to the state
759 of another revision.
759 of another revision.
760
760
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
761 Returns 0 on success, 1 if nothing to backout or there are unresolved
762 files.
762 files.
763 '''
763 '''
764 with repo.wlock(), repo.lock():
764 with repo.wlock(), repo.lock():
765 return _dobackout(ui, repo, node, rev, **opts)
765 return _dobackout(ui, repo, node, rev, **opts)
766
766
767
767
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
768 def _dobackout(ui, repo, node=None, rev=None, **opts):
769 opts = pycompat.byteskwargs(opts)
769 opts = pycompat.byteskwargs(opts)
770 if opts.get(b'commit') and opts.get(b'no_commit'):
770 if opts.get(b'commit') and opts.get(b'no_commit'):
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
771 raise error.Abort(_(b"cannot use --commit with --no-commit"))
772 if opts.get(b'merge') and opts.get(b'no_commit'):
772 if opts.get(b'merge') and opts.get(b'no_commit'):
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
773 raise error.Abort(_(b"cannot use --merge with --no-commit"))
774
774
775 if rev and node:
775 if rev and node:
776 raise error.Abort(_(b"please specify just one revision"))
776 raise error.Abort(_(b"please specify just one revision"))
777
777
778 if not rev:
778 if not rev:
779 rev = node
779 rev = node
780
780
781 if not rev:
781 if not rev:
782 raise error.Abort(_(b"please specify a revision to backout"))
782 raise error.Abort(_(b"please specify a revision to backout"))
783
783
784 date = opts.get(b'date')
784 date = opts.get(b'date')
785 if date:
785 if date:
786 opts[b'date'] = dateutil.parsedate(date)
786 opts[b'date'] = dateutil.parsedate(date)
787
787
788 cmdutil.checkunfinished(repo)
788 cmdutil.checkunfinished(repo)
789 cmdutil.bailifchanged(repo)
789 cmdutil.bailifchanged(repo)
790 node = scmutil.revsingle(repo, rev).node()
790 node = scmutil.revsingle(repo, rev).node()
791
791
792 op1, op2 = repo.dirstate.parents()
792 op1, op2 = repo.dirstate.parents()
793 if not repo.changelog.isancestor(node, op1):
793 if not repo.changelog.isancestor(node, op1):
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
794 raise error.Abort(_(b'cannot backout change that is not an ancestor'))
795
795
796 p1, p2 = repo.changelog.parents(node)
796 p1, p2 = repo.changelog.parents(node)
797 if p1 == nullid:
797 if p1 == nullid:
798 raise error.Abort(_(b'cannot backout a change with no parents'))
798 raise error.Abort(_(b'cannot backout a change with no parents'))
799 if p2 != nullid:
799 if p2 != nullid:
800 if not opts.get(b'parent'):
800 if not opts.get(b'parent'):
801 raise error.Abort(_(b'cannot backout a merge changeset'))
801 raise error.Abort(_(b'cannot backout a merge changeset'))
802 p = repo.lookup(opts[b'parent'])
802 p = repo.lookup(opts[b'parent'])
803 if p not in (p1, p2):
803 if p not in (p1, p2):
804 raise error.Abort(
804 raise error.Abort(
805 _(b'%s is not a parent of %s') % (short(p), short(node))
805 _(b'%s is not a parent of %s') % (short(p), short(node))
806 )
806 )
807 parent = p
807 parent = p
808 else:
808 else:
809 if opts.get(b'parent'):
809 if opts.get(b'parent'):
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
810 raise error.Abort(_(b'cannot use --parent on non-merge changeset'))
811 parent = p1
811 parent = p1
812
812
813 # the backout should appear on the same branch
813 # the backout should appear on the same branch
814 branch = repo.dirstate.branch()
814 branch = repo.dirstate.branch()
815 bheads = repo.branchheads(branch)
815 bheads = repo.branchheads(branch)
816 rctx = scmutil.revsingle(repo, hex(parent))
816 rctx = scmutil.revsingle(repo, hex(parent))
817 if not opts.get(b'merge') and op1 != node:
817 if not opts.get(b'merge') and op1 != node:
818 with dirstateguard.dirstateguard(repo, b'backout'):
818 with dirstateguard.dirstateguard(repo, b'backout'):
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
819 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
820 with ui.configoverride(overrides, b'backout'):
820 with ui.configoverride(overrides, b'backout'):
821 stats = mergemod.update(
821 stats = mergemod.update(
822 repo,
822 repo,
823 parent,
823 parent,
824 branchmerge=True,
824 branchmerge=True,
825 force=True,
825 force=True,
826 ancestor=node,
826 ancestor=node,
827 mergeancestor=False,
827 mergeancestor=False,
828 )
828 )
829 repo.setparents(op1, op2)
829 repo.setparents(op1, op2)
830 hg._showstats(repo, stats)
830 hg._showstats(repo, stats)
831 if stats.unresolvedcount:
831 if stats.unresolvedcount:
832 repo.ui.status(
832 repo.ui.status(
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
833 _(b"use 'hg resolve' to retry unresolved file merges\n")
834 )
834 )
835 return 1
835 return 1
836 else:
836 else:
837 hg.clean(repo, node, show_stats=False)
837 hg.clean(repo, node, show_stats=False)
838 repo.dirstate.setbranch(branch)
838 repo.dirstate.setbranch(branch)
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
839 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
840
840
841 if opts.get(b'no_commit'):
841 if opts.get(b'no_commit'):
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
842 msg = _(b"changeset %s backed out, don't forget to commit.\n")
843 ui.status(msg % short(node))
843 ui.status(msg % short(node))
844 return 0
844 return 0
845
845
846 def commitfunc(ui, repo, message, match, opts):
846 def commitfunc(ui, repo, message, match, opts):
847 editform = b'backout'
847 editform = b'backout'
848 e = cmdutil.getcommiteditor(
848 e = cmdutil.getcommiteditor(
849 editform=editform, **pycompat.strkwargs(opts)
849 editform=editform, **pycompat.strkwargs(opts)
850 )
850 )
851 if not message:
851 if not message:
852 # we don't translate commit messages
852 # we don't translate commit messages
853 message = b"Backed out changeset %s" % short(node)
853 message = b"Backed out changeset %s" % short(node)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
854 e = cmdutil.getcommiteditor(edit=True, editform=editform)
855 return repo.commit(
855 return repo.commit(
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
856 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
857 )
857 )
858
858
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
859 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
860 if not newnode:
860 if not newnode:
861 ui.status(_(b"nothing changed\n"))
861 ui.status(_(b"nothing changed\n"))
862 return 1
862 return 1
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
863 cmdutil.commitstatus(repo, newnode, branch, bheads)
864
864
865 def nice(node):
865 def nice(node):
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
866 return b'%d:%s' % (repo.changelog.rev(node), short(node))
867
867
868 ui.status(
868 ui.status(
869 _(b'changeset %s backs out changeset %s\n')
869 _(b'changeset %s backs out changeset %s\n')
870 % (nice(repo.changelog.tip()), nice(node))
870 % (nice(repo.changelog.tip()), nice(node))
871 )
871 )
872 if opts.get(b'merge') and op1 != node:
872 if opts.get(b'merge') and op1 != node:
873 hg.clean(repo, op1, show_stats=False)
873 hg.clean(repo, op1, show_stats=False)
874 ui.status(
874 ui.status(
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
875 _(b'merging with changeset %s\n') % nice(repo.changelog.tip())
876 )
876 )
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
877 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
878 with ui.configoverride(overrides, b'backout'):
878 with ui.configoverride(overrides, b'backout'):
879 return hg.merge(repo, hex(repo.changelog.tip()))
879 return hg.merge(repo, hex(repo.changelog.tip()))
880 return 0
880 return 0
881
881
882
882
883 @command(
883 @command(
884 b'bisect',
884 b'bisect',
885 [
885 [
886 (b'r', b'reset', False, _(b'reset bisect state')),
886 (b'r', b'reset', False, _(b'reset bisect state')),
887 (b'g', b'good', False, _(b'mark changeset good')),
887 (b'g', b'good', False, _(b'mark changeset good')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
888 (b'b', b'bad', False, _(b'mark changeset bad')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
889 (b's', b'skip', False, _(b'skip testing changeset')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
890 (b'e', b'extend', False, _(b'extend the bisect range')),
891 (
891 (
892 b'c',
892 b'c',
893 b'command',
893 b'command',
894 b'',
894 b'',
895 _(b'use command to check changeset state'),
895 _(b'use command to check changeset state'),
896 _(b'CMD'),
896 _(b'CMD'),
897 ),
897 ),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
898 (b'U', b'noupdate', False, _(b'do not update to target')),
899 ],
899 ],
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
900 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
901 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
902 )
902 )
903 def bisect(
903 def bisect(
904 ui,
904 ui,
905 repo,
905 repo,
906 rev=None,
906 rev=None,
907 extra=None,
907 extra=None,
908 command=None,
908 command=None,
909 reset=None,
909 reset=None,
910 good=None,
910 good=None,
911 bad=None,
911 bad=None,
912 skip=None,
912 skip=None,
913 extend=None,
913 extend=None,
914 noupdate=None,
914 noupdate=None,
915 ):
915 ):
916 """subdivision search of changesets
916 """subdivision search of changesets
917
917
918 This command helps to find changesets which introduce problems. To
918 This command helps to find changesets which introduce problems. To
919 use, mark the earliest changeset you know exhibits the problem as
919 use, mark the earliest changeset you know exhibits the problem as
920 bad, then mark the latest changeset which is free from the problem
920 bad, then mark the latest changeset which is free from the problem
921 as good. Bisect will update your working directory to a revision
921 as good. Bisect will update your working directory to a revision
922 for testing (unless the -U/--noupdate option is specified). Once
922 for testing (unless the -U/--noupdate option is specified). Once
923 you have performed tests, mark the working directory as good or
923 you have performed tests, mark the working directory as good or
924 bad, and bisect will either update to another candidate changeset
924 bad, and bisect will either update to another candidate changeset
925 or announce that it has found the bad revision.
925 or announce that it has found the bad revision.
926
926
927 As a shortcut, you can also use the revision argument to mark a
927 As a shortcut, you can also use the revision argument to mark a
928 revision as good or bad without checking it out first.
928 revision as good or bad without checking it out first.
929
929
930 If you supply a command, it will be used for automatic bisection.
930 If you supply a command, it will be used for automatic bisection.
931 The environment variable HG_NODE will contain the ID of the
931 The environment variable HG_NODE will contain the ID of the
932 changeset being tested. The exit status of the command will be
932 changeset being tested. The exit status of the command will be
933 used to mark revisions as good or bad: status 0 means good, 125
933 used to mark revisions as good or bad: status 0 means good, 125
934 means to skip the revision, 127 (command not found) will abort the
934 means to skip the revision, 127 (command not found) will abort the
935 bisection, and any other non-zero exit status means the revision
935 bisection, and any other non-zero exit status means the revision
936 is bad.
936 is bad.
937
937
938 .. container:: verbose
938 .. container:: verbose
939
939
940 Some examples:
940 Some examples:
941
941
942 - start a bisection with known bad revision 34, and good revision 12::
942 - start a bisection with known bad revision 34, and good revision 12::
943
943
944 hg bisect --bad 34
944 hg bisect --bad 34
945 hg bisect --good 12
945 hg bisect --good 12
946
946
947 - advance the current bisection by marking current revision as good or
947 - advance the current bisection by marking current revision as good or
948 bad::
948 bad::
949
949
950 hg bisect --good
950 hg bisect --good
951 hg bisect --bad
951 hg bisect --bad
952
952
953 - mark the current revision, or a known revision, to be skipped (e.g. if
953 - mark the current revision, or a known revision, to be skipped (e.g. if
954 that revision is not usable because of another issue)::
954 that revision is not usable because of another issue)::
955
955
956 hg bisect --skip
956 hg bisect --skip
957 hg bisect --skip 23
957 hg bisect --skip 23
958
958
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
959 - skip all revisions that do not touch directories ``foo`` or ``bar``::
960
960
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
961 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
962
962
963 - forget the current bisection::
963 - forget the current bisection::
964
964
965 hg bisect --reset
965 hg bisect --reset
966
966
967 - use 'make && make tests' to automatically find the first broken
967 - use 'make && make tests' to automatically find the first broken
968 revision::
968 revision::
969
969
970 hg bisect --reset
970 hg bisect --reset
971 hg bisect --bad 34
971 hg bisect --bad 34
972 hg bisect --good 12
972 hg bisect --good 12
973 hg bisect --command "make && make tests"
973 hg bisect --command "make && make tests"
974
974
975 - see all changesets whose states are already known in the current
975 - see all changesets whose states are already known in the current
976 bisection::
976 bisection::
977
977
978 hg log -r "bisect(pruned)"
978 hg log -r "bisect(pruned)"
979
979
980 - see the changeset currently being bisected (especially useful
980 - see the changeset currently being bisected (especially useful
981 if running with -U/--noupdate)::
981 if running with -U/--noupdate)::
982
982
983 hg log -r "bisect(current)"
983 hg log -r "bisect(current)"
984
984
985 - see all changesets that took part in the current bisection::
985 - see all changesets that took part in the current bisection::
986
986
987 hg log -r "bisect(range)"
987 hg log -r "bisect(range)"
988
988
989 - you can even get a nice graph::
989 - you can even get a nice graph::
990
990
991 hg log --graph -r "bisect(range)"
991 hg log --graph -r "bisect(range)"
992
992
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
993 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
994
994
995 Returns 0 on success.
995 Returns 0 on success.
996 """
996 """
997 # backward compatibility
997 # backward compatibility
998 if rev in b"good bad reset init".split():
998 if rev in b"good bad reset init".split():
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
999 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1000 cmd, rev, extra = rev, extra, None
1000 cmd, rev, extra = rev, extra, None
1001 if cmd == b"good":
1001 if cmd == b"good":
1002 good = True
1002 good = True
1003 elif cmd == b"bad":
1003 elif cmd == b"bad":
1004 bad = True
1004 bad = True
1005 else:
1005 else:
1006 reset = True
1006 reset = True
1007 elif extra:
1007 elif extra:
1008 raise error.Abort(_(b'incompatible arguments'))
1008 raise error.Abort(_(b'incompatible arguments'))
1009
1009
1010 incompatibles = {
1010 incompatibles = {
1011 b'--bad': bad,
1011 b'--bad': bad,
1012 b'--command': bool(command),
1012 b'--command': bool(command),
1013 b'--extend': extend,
1013 b'--extend': extend,
1014 b'--good': good,
1014 b'--good': good,
1015 b'--reset': reset,
1015 b'--reset': reset,
1016 b'--skip': skip,
1016 b'--skip': skip,
1017 }
1017 }
1018
1018
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1019 enabled = [x for x in incompatibles if incompatibles[x]]
1020
1020
1021 if len(enabled) > 1:
1021 if len(enabled) > 1:
1022 raise error.Abort(
1022 raise error.Abort(
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1023 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1024 )
1024 )
1025
1025
1026 if reset:
1026 if reset:
1027 hbisect.resetstate(repo)
1027 hbisect.resetstate(repo)
1028 return
1028 return
1029
1029
1030 state = hbisect.load_state(repo)
1030 state = hbisect.load_state(repo)
1031
1031
1032 # update state
1032 # update state
1033 if good or bad or skip:
1033 if good or bad or skip:
1034 if rev:
1034 if rev:
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1035 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1036 else:
1036 else:
1037 nodes = [repo.lookup(b'.')]
1037 nodes = [repo.lookup(b'.')]
1038 if good:
1038 if good:
1039 state[b'good'] += nodes
1039 state[b'good'] += nodes
1040 elif bad:
1040 elif bad:
1041 state[b'bad'] += nodes
1041 state[b'bad'] += nodes
1042 elif skip:
1042 elif skip:
1043 state[b'skip'] += nodes
1043 state[b'skip'] += nodes
1044 hbisect.save_state(repo, state)
1044 hbisect.save_state(repo, state)
1045 if not (state[b'good'] and state[b'bad']):
1045 if not (state[b'good'] and state[b'bad']):
1046 return
1046 return
1047
1047
1048 def mayupdate(repo, node, show_stats=True):
1048 def mayupdate(repo, node, show_stats=True):
1049 """common used update sequence"""
1049 """common used update sequence"""
1050 if noupdate:
1050 if noupdate:
1051 return
1051 return
1052 cmdutil.checkunfinished(repo)
1052 cmdutil.checkunfinished(repo)
1053 cmdutil.bailifchanged(repo)
1053 cmdutil.bailifchanged(repo)
1054 return hg.clean(repo, node, show_stats=show_stats)
1054 return hg.clean(repo, node, show_stats=show_stats)
1055
1055
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1056 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1057
1057
1058 if command:
1058 if command:
1059 changesets = 1
1059 changesets = 1
1060 if noupdate:
1060 if noupdate:
1061 try:
1061 try:
1062 node = state[b'current'][0]
1062 node = state[b'current'][0]
1063 except LookupError:
1063 except LookupError:
1064 raise error.Abort(
1064 raise error.Abort(
1065 _(
1065 _(
1066 b'current bisect revision is unknown - '
1066 b'current bisect revision is unknown - '
1067 b'start a new bisect to fix'
1067 b'start a new bisect to fix'
1068 )
1068 )
1069 )
1069 )
1070 else:
1070 else:
1071 node, p2 = repo.dirstate.parents()
1071 node, p2 = repo.dirstate.parents()
1072 if p2 != nullid:
1072 if p2 != nullid:
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1073 raise error.Abort(_(b'current bisect revision is a merge'))
1074 if rev:
1074 if rev:
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1075 node = repo[scmutil.revsingle(repo, rev, node)].node()
1076 try:
1076 try:
1077 while changesets:
1077 while changesets:
1078 # update state
1078 # update state
1079 state[b'current'] = [node]
1079 state[b'current'] = [node]
1080 hbisect.save_state(repo, state)
1080 hbisect.save_state(repo, state)
1081 status = ui.system(
1081 status = ui.system(
1082 command,
1082 command,
1083 environ={b'HG_NODE': hex(node)},
1083 environ={b'HG_NODE': hex(node)},
1084 blockedtag=b'bisect_check',
1084 blockedtag=b'bisect_check',
1085 )
1085 )
1086 if status == 125:
1086 if status == 125:
1087 transition = b"skip"
1087 transition = b"skip"
1088 elif status == 0:
1088 elif status == 0:
1089 transition = b"good"
1089 transition = b"good"
1090 # status < 0 means process was killed
1090 # status < 0 means process was killed
1091 elif status == 127:
1091 elif status == 127:
1092 raise error.Abort(_(b"failed to execute %s") % command)
1092 raise error.Abort(_(b"failed to execute %s") % command)
1093 elif status < 0:
1093 elif status < 0:
1094 raise error.Abort(_(b"%s killed") % command)
1094 raise error.Abort(_(b"%s killed") % command)
1095 else:
1095 else:
1096 transition = b"bad"
1096 transition = b"bad"
1097 state[transition].append(node)
1097 state[transition].append(node)
1098 ctx = repo[node]
1098 ctx = repo[node]
1099 ui.status(
1099 ui.status(
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1100 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1101 )
1101 )
1102 hbisect.checkstate(state)
1102 hbisect.checkstate(state)
1103 # bisect
1103 # bisect
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1104 nodes, changesets, bgood = hbisect.bisect(repo, state)
1105 # update to next check
1105 # update to next check
1106 node = nodes[0]
1106 node = nodes[0]
1107 mayupdate(repo, node, show_stats=False)
1107 mayupdate(repo, node, show_stats=False)
1108 finally:
1108 finally:
1109 state[b'current'] = [node]
1109 state[b'current'] = [node]
1110 hbisect.save_state(repo, state)
1110 hbisect.save_state(repo, state)
1111 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1111 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1112 return
1112 return
1113
1113
1114 hbisect.checkstate(state)
1114 hbisect.checkstate(state)
1115
1115
1116 # actually bisect
1116 # actually bisect
1117 nodes, changesets, good = hbisect.bisect(repo, state)
1117 nodes, changesets, good = hbisect.bisect(repo, state)
1118 if extend:
1118 if extend:
1119 if not changesets:
1119 if not changesets:
1120 extendnode = hbisect.extendrange(repo, state, nodes, good)
1120 extendnode = hbisect.extendrange(repo, state, nodes, good)
1121 if extendnode is not None:
1121 if extendnode is not None:
1122 ui.write(
1122 ui.write(
1123 _(b"Extending search to changeset %d:%s\n")
1123 _(b"Extending search to changeset %d:%s\n")
1124 % (extendnode.rev(), extendnode)
1124 % (extendnode.rev(), extendnode)
1125 )
1125 )
1126 state[b'current'] = [extendnode.node()]
1126 state[b'current'] = [extendnode.node()]
1127 hbisect.save_state(repo, state)
1127 hbisect.save_state(repo, state)
1128 return mayupdate(repo, extendnode.node())
1128 return mayupdate(repo, extendnode.node())
1129 raise error.Abort(_(b"nothing to extend"))
1129 raise error.Abort(_(b"nothing to extend"))
1130
1130
1131 if changesets == 0:
1131 if changesets == 0:
1132 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1132 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1133 else:
1133 else:
1134 assert len(nodes) == 1 # only a single node can be tested next
1134 assert len(nodes) == 1 # only a single node can be tested next
1135 node = nodes[0]
1135 node = nodes[0]
1136 # compute the approximate number of remaining tests
1136 # compute the approximate number of remaining tests
1137 tests, size = 0, 2
1137 tests, size = 0, 2
1138 while size <= changesets:
1138 while size <= changesets:
1139 tests, size = tests + 1, size * 2
1139 tests, size = tests + 1, size * 2
1140 rev = repo.changelog.rev(node)
1140 rev = repo.changelog.rev(node)
1141 ui.write(
1141 ui.write(
1142 _(
1142 _(
1143 b"Testing changeset %d:%s "
1143 b"Testing changeset %d:%s "
1144 b"(%d changesets remaining, ~%d tests)\n"
1144 b"(%d changesets remaining, ~%d tests)\n"
1145 )
1145 )
1146 % (rev, short(node), changesets, tests)
1146 % (rev, short(node), changesets, tests)
1147 )
1147 )
1148 state[b'current'] = [node]
1148 state[b'current'] = [node]
1149 hbisect.save_state(repo, state)
1149 hbisect.save_state(repo, state)
1150 return mayupdate(repo, node)
1150 return mayupdate(repo, node)
1151
1151
1152
1152
1153 @command(
1153 @command(
1154 b'bookmarks|bookmark',
1154 b'bookmarks|bookmark',
1155 [
1155 [
1156 (b'f', b'force', False, _(b'force')),
1156 (b'f', b'force', False, _(b'force')),
1157 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1157 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1158 (b'd', b'delete', False, _(b'delete a given bookmark')),
1158 (b'd', b'delete', False, _(b'delete a given bookmark')),
1159 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1159 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1160 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1160 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1161 (b'l', b'list', False, _(b'list existing bookmarks')),
1161 (b'l', b'list', False, _(b'list existing bookmarks')),
1162 ]
1162 ]
1163 + formatteropts,
1163 + formatteropts,
1164 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1164 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1166 )
1166 )
1167 def bookmark(ui, repo, *names, **opts):
1167 def bookmark(ui, repo, *names, **opts):
1168 '''create a new bookmark or list existing bookmarks
1168 '''create a new bookmark or list existing bookmarks
1169
1169
1170 Bookmarks are labels on changesets to help track lines of development.
1170 Bookmarks are labels on changesets to help track lines of development.
1171 Bookmarks are unversioned and can be moved, renamed and deleted.
1171 Bookmarks are unversioned and can be moved, renamed and deleted.
1172 Deleting or moving a bookmark has no effect on the associated changesets.
1172 Deleting or moving a bookmark has no effect on the associated changesets.
1173
1173
1174 Creating or updating to a bookmark causes it to be marked as 'active'.
1174 Creating or updating to a bookmark causes it to be marked as 'active'.
1175 The active bookmark is indicated with a '*'.
1175 The active bookmark is indicated with a '*'.
1176 When a commit is made, the active bookmark will advance to the new commit.
1176 When a commit is made, the active bookmark will advance to the new commit.
1177 A plain :hg:`update` will also advance an active bookmark, if possible.
1177 A plain :hg:`update` will also advance an active bookmark, if possible.
1178 Updating away from a bookmark will cause it to be deactivated.
1178 Updating away from a bookmark will cause it to be deactivated.
1179
1179
1180 Bookmarks can be pushed and pulled between repositories (see
1180 Bookmarks can be pushed and pulled between repositories (see
1181 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1181 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1182 diverged, a new 'divergent bookmark' of the form 'name@path' will
1182 diverged, a new 'divergent bookmark' of the form 'name@path' will
1183 be created. Using :hg:`merge` will resolve the divergence.
1183 be created. Using :hg:`merge` will resolve the divergence.
1184
1184
1185 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1185 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1186 the active bookmark's name.
1186 the active bookmark's name.
1187
1187
1188 A bookmark named '@' has the special property that :hg:`clone` will
1188 A bookmark named '@' has the special property that :hg:`clone` will
1189 check it out by default if it exists.
1189 check it out by default if it exists.
1190
1190
1191 .. container:: verbose
1191 .. container:: verbose
1192
1192
1193 Template:
1193 Template:
1194
1194
1195 The following keywords are supported in addition to the common template
1195 The following keywords are supported in addition to the common template
1196 keywords and functions such as ``{bookmark}``. See also
1196 keywords and functions such as ``{bookmark}``. See also
1197 :hg:`help templates`.
1197 :hg:`help templates`.
1198
1198
1199 :active: Boolean. True if the bookmark is active.
1199 :active: Boolean. True if the bookmark is active.
1200
1200
1201 Examples:
1201 Examples:
1202
1202
1203 - create an active bookmark for a new line of development::
1203 - create an active bookmark for a new line of development::
1204
1204
1205 hg book new-feature
1205 hg book new-feature
1206
1206
1207 - create an inactive bookmark as a place marker::
1207 - create an inactive bookmark as a place marker::
1208
1208
1209 hg book -i reviewed
1209 hg book -i reviewed
1210
1210
1211 - create an inactive bookmark on another changeset::
1211 - create an inactive bookmark on another changeset::
1212
1212
1213 hg book -r .^ tested
1213 hg book -r .^ tested
1214
1214
1215 - rename bookmark turkey to dinner::
1215 - rename bookmark turkey to dinner::
1216
1216
1217 hg book -m turkey dinner
1217 hg book -m turkey dinner
1218
1218
1219 - move the '@' bookmark from another branch::
1219 - move the '@' bookmark from another branch::
1220
1220
1221 hg book -f @
1221 hg book -f @
1222
1222
1223 - print only the active bookmark name::
1223 - print only the active bookmark name::
1224
1224
1225 hg book -ql .
1225 hg book -ql .
1226 '''
1226 '''
1227 opts = pycompat.byteskwargs(opts)
1227 opts = pycompat.byteskwargs(opts)
1228 force = opts.get(b'force')
1228 force = opts.get(b'force')
1229 rev = opts.get(b'rev')
1229 rev = opts.get(b'rev')
1230 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1230 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1231
1231
1232 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1232 selactions = [k for k in [b'delete', b'rename', b'list'] if opts.get(k)]
1233 if len(selactions) > 1:
1233 if len(selactions) > 1:
1234 raise error.Abort(
1234 raise error.Abort(
1235 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1235 _(b'--%s and --%s are incompatible') % tuple(selactions[:2])
1236 )
1236 )
1237 if selactions:
1237 if selactions:
1238 action = selactions[0]
1238 action = selactions[0]
1239 elif names or rev:
1239 elif names or rev:
1240 action = b'add'
1240 action = b'add'
1241 elif inactive:
1241 elif inactive:
1242 action = b'inactive' # meaning deactivate
1242 action = b'inactive' # meaning deactivate
1243 else:
1243 else:
1244 action = b'list'
1244 action = b'list'
1245
1245
1246 if rev and action in {b'delete', b'rename', b'list'}:
1246 if rev and action in {b'delete', b'rename', b'list'}:
1247 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1247 raise error.Abort(_(b"--rev is incompatible with --%s") % action)
1248 if inactive and action in {b'delete', b'list'}:
1248 if inactive and action in {b'delete', b'list'}:
1249 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1249 raise error.Abort(_(b"--inactive is incompatible with --%s") % action)
1250 if not names and action in {b'add', b'delete'}:
1250 if not names and action in {b'add', b'delete'}:
1251 raise error.Abort(_(b"bookmark name required"))
1251 raise error.Abort(_(b"bookmark name required"))
1252
1252
1253 if action in {b'add', b'delete', b'rename', b'inactive'}:
1253 if action in {b'add', b'delete', b'rename', b'inactive'}:
1254 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1254 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1255 if action == b'delete':
1255 if action == b'delete':
1256 names = pycompat.maplist(repo._bookmarks.expandname, names)
1256 names = pycompat.maplist(repo._bookmarks.expandname, names)
1257 bookmarks.delete(repo, tr, names)
1257 bookmarks.delete(repo, tr, names)
1258 elif action == b'rename':
1258 elif action == b'rename':
1259 if not names:
1259 if not names:
1260 raise error.Abort(_(b"new bookmark name required"))
1260 raise error.Abort(_(b"new bookmark name required"))
1261 elif len(names) > 1:
1261 elif len(names) > 1:
1262 raise error.Abort(_(b"only one new bookmark name allowed"))
1262 raise error.Abort(_(b"only one new bookmark name allowed"))
1263 oldname = repo._bookmarks.expandname(opts[b'rename'])
1263 oldname = repo._bookmarks.expandname(opts[b'rename'])
1264 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1264 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1265 elif action == b'add':
1265 elif action == b'add':
1266 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1266 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1267 elif action == b'inactive':
1267 elif action == b'inactive':
1268 if len(repo._bookmarks) == 0:
1268 if len(repo._bookmarks) == 0:
1269 ui.status(_(b"no bookmarks set\n"))
1269 ui.status(_(b"no bookmarks set\n"))
1270 elif not repo._activebookmark:
1270 elif not repo._activebookmark:
1271 ui.status(_(b"no active bookmark\n"))
1271 ui.status(_(b"no active bookmark\n"))
1272 else:
1272 else:
1273 bookmarks.deactivate(repo)
1273 bookmarks.deactivate(repo)
1274 elif action == b'list':
1274 elif action == b'list':
1275 names = pycompat.maplist(repo._bookmarks.expandname, names)
1275 names = pycompat.maplist(repo._bookmarks.expandname, names)
1276 with ui.formatter(b'bookmarks', opts) as fm:
1276 with ui.formatter(b'bookmarks', opts) as fm:
1277 bookmarks.printbookmarks(ui, repo, fm, names)
1277 bookmarks.printbookmarks(ui, repo, fm, names)
1278 else:
1278 else:
1279 raise error.ProgrammingError(b'invalid action: %s' % action)
1279 raise error.ProgrammingError(b'invalid action: %s' % action)
1280
1280
1281
1281
1282 @command(
1282 @command(
1283 b'branch',
1283 b'branch',
1284 [
1284 [
1285 (
1285 (
1286 b'f',
1286 b'f',
1287 b'force',
1287 b'force',
1288 None,
1288 None,
1289 _(b'set branch name even if it shadows an existing branch'),
1289 _(b'set branch name even if it shadows an existing branch'),
1290 ),
1290 ),
1291 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1291 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1292 (
1292 (
1293 b'r',
1293 b'r',
1294 b'rev',
1294 b'rev',
1295 [],
1295 [],
1296 _(b'change branches of the given revs (EXPERIMENTAL)'),
1296 _(b'change branches of the given revs (EXPERIMENTAL)'),
1297 ),
1297 ),
1298 ],
1298 ],
1299 _(b'[-fC] [NAME]'),
1299 _(b'[-fC] [NAME]'),
1300 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1300 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1301 )
1301 )
1302 def branch(ui, repo, label=None, **opts):
1302 def branch(ui, repo, label=None, **opts):
1303 """set or show the current branch name
1303 """set or show the current branch name
1304
1304
1305 .. note::
1305 .. note::
1306
1306
1307 Branch names are permanent and global. Use :hg:`bookmark` to create a
1307 Branch names are permanent and global. Use :hg:`bookmark` to create a
1308 light-weight bookmark instead. See :hg:`help glossary` for more
1308 light-weight bookmark instead. See :hg:`help glossary` for more
1309 information about named branches and bookmarks.
1309 information about named branches and bookmarks.
1310
1310
1311 With no argument, show the current branch name. With one argument,
1311 With no argument, show the current branch name. With one argument,
1312 set the working directory branch name (the branch will not exist
1312 set the working directory branch name (the branch will not exist
1313 in the repository until the next commit). Standard practice
1313 in the repository until the next commit). Standard practice
1314 recommends that primary development take place on the 'default'
1314 recommends that primary development take place on the 'default'
1315 branch.
1315 branch.
1316
1316
1317 Unless -f/--force is specified, branch will not let you set a
1317 Unless -f/--force is specified, branch will not let you set a
1318 branch name that already exists.
1318 branch name that already exists.
1319
1319
1320 Use -C/--clean to reset the working directory branch to that of
1320 Use -C/--clean to reset the working directory branch to that of
1321 the parent of the working directory, negating a previous branch
1321 the parent of the working directory, negating a previous branch
1322 change.
1322 change.
1323
1323
1324 Use the command :hg:`update` to switch to an existing branch. Use
1324 Use the command :hg:`update` to switch to an existing branch. Use
1325 :hg:`commit --close-branch` to mark this branch head as closed.
1325 :hg:`commit --close-branch` to mark this branch head as closed.
1326 When all heads of a branch are closed, the branch will be
1326 When all heads of a branch are closed, the branch will be
1327 considered closed.
1327 considered closed.
1328
1328
1329 Returns 0 on success.
1329 Returns 0 on success.
1330 """
1330 """
1331 opts = pycompat.byteskwargs(opts)
1331 opts = pycompat.byteskwargs(opts)
1332 revs = opts.get(b'rev')
1332 revs = opts.get(b'rev')
1333 if label:
1333 if label:
1334 label = label.strip()
1334 label = label.strip()
1335
1335
1336 if not opts.get(b'clean') and not label:
1336 if not opts.get(b'clean') and not label:
1337 if revs:
1337 if revs:
1338 raise error.Abort(_(b"no branch name specified for the revisions"))
1338 raise error.Abort(_(b"no branch name specified for the revisions"))
1339 ui.write(b"%s\n" % repo.dirstate.branch())
1339 ui.write(b"%s\n" % repo.dirstate.branch())
1340 return
1340 return
1341
1341
1342 with repo.wlock():
1342 with repo.wlock():
1343 if opts.get(b'clean'):
1343 if opts.get(b'clean'):
1344 label = repo[b'.'].branch()
1344 label = repo[b'.'].branch()
1345 repo.dirstate.setbranch(label)
1345 repo.dirstate.setbranch(label)
1346 ui.status(_(b'reset working directory to branch %s\n') % label)
1346 ui.status(_(b'reset working directory to branch %s\n') % label)
1347 elif label:
1347 elif label:
1348
1348
1349 scmutil.checknewlabel(repo, label, b'branch')
1349 scmutil.checknewlabel(repo, label, b'branch')
1350 if revs:
1350 if revs:
1351 return cmdutil.changebranch(ui, repo, revs, label)
1351 return cmdutil.changebranch(ui, repo, revs, label)
1352
1352
1353 if not opts.get(b'force') and label in repo.branchmap():
1353 if not opts.get(b'force') and label in repo.branchmap():
1354 if label not in [p.branch() for p in repo[None].parents()]:
1354 if label not in [p.branch() for p in repo[None].parents()]:
1355 raise error.Abort(
1355 raise error.Abort(
1356 _(b'a branch of the same name already exists'),
1356 _(b'a branch of the same name already exists'),
1357 # i18n: "it" refers to an existing branch
1357 # i18n: "it" refers to an existing branch
1358 hint=_(b"use 'hg update' to switch to it"),
1358 hint=_(b"use 'hg update' to switch to it"),
1359 )
1359 )
1360
1360
1361 repo.dirstate.setbranch(label)
1361 repo.dirstate.setbranch(label)
1362 ui.status(_(b'marked working directory as branch %s\n') % label)
1362 ui.status(_(b'marked working directory as branch %s\n') % label)
1363
1363
1364 # find any open named branches aside from default
1364 # find any open named branches aside from default
1365 for n, h, t, c in repo.branchmap().iterbranches():
1365 for n, h, t, c in repo.branchmap().iterbranches():
1366 if n != b"default" and not c:
1366 if n != b"default" and not c:
1367 return 0
1367 return 0
1368 ui.status(
1368 ui.status(
1369 _(
1369 _(
1370 b'(branches are permanent and global, '
1370 b'(branches are permanent and global, '
1371 b'did you want a bookmark?)\n'
1371 b'did you want a bookmark?)\n'
1372 )
1372 )
1373 )
1373 )
1374
1374
1375
1375
1376 @command(
1376 @command(
1377 b'branches',
1377 b'branches',
1378 [
1378 [
1379 (
1379 (
1380 b'a',
1380 b'a',
1381 b'active',
1381 b'active',
1382 False,
1382 False,
1383 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1383 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1384 ),
1384 ),
1385 (b'c', b'closed', False, _(b'show normal and closed branches')),
1385 (b'c', b'closed', False, _(b'show normal and closed branches')),
1386 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1386 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1387 ]
1387 ]
1388 + formatteropts,
1388 + formatteropts,
1389 _(b'[-c]'),
1389 _(b'[-c]'),
1390 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1390 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1391 intents={INTENT_READONLY},
1391 intents={INTENT_READONLY},
1392 )
1392 )
1393 def branches(ui, repo, active=False, closed=False, **opts):
1393 def branches(ui, repo, active=False, closed=False, **opts):
1394 """list repository named branches
1394 """list repository named branches
1395
1395
1396 List the repository's named branches, indicating which ones are
1396 List the repository's named branches, indicating which ones are
1397 inactive. If -c/--closed is specified, also list branches which have
1397 inactive. If -c/--closed is specified, also list branches which have
1398 been marked closed (see :hg:`commit --close-branch`).
1398 been marked closed (see :hg:`commit --close-branch`).
1399
1399
1400 Use the command :hg:`update` to switch to an existing branch.
1400 Use the command :hg:`update` to switch to an existing branch.
1401
1401
1402 .. container:: verbose
1402 .. container:: verbose
1403
1403
1404 Template:
1404 Template:
1405
1405
1406 The following keywords are supported in addition to the common template
1406 The following keywords are supported in addition to the common template
1407 keywords and functions such as ``{branch}``. See also
1407 keywords and functions such as ``{branch}``. See also
1408 :hg:`help templates`.
1408 :hg:`help templates`.
1409
1409
1410 :active: Boolean. True if the branch is active.
1410 :active: Boolean. True if the branch is active.
1411 :closed: Boolean. True if the branch is closed.
1411 :closed: Boolean. True if the branch is closed.
1412 :current: Boolean. True if it is the current branch.
1412 :current: Boolean. True if it is the current branch.
1413
1413
1414 Returns 0.
1414 Returns 0.
1415 """
1415 """
1416
1416
1417 opts = pycompat.byteskwargs(opts)
1417 opts = pycompat.byteskwargs(opts)
1418 revs = opts.get(b'rev')
1418 revs = opts.get(b'rev')
1419 selectedbranches = None
1419 selectedbranches = None
1420 if revs:
1420 if revs:
1421 revs = scmutil.revrange(repo, revs)
1421 revs = scmutil.revrange(repo, revs)
1422 getbi = repo.revbranchcache().branchinfo
1422 getbi = repo.revbranchcache().branchinfo
1423 selectedbranches = {getbi(r)[0] for r in revs}
1423 selectedbranches = {getbi(r)[0] for r in revs}
1424
1424
1425 ui.pager(b'branches')
1425 ui.pager(b'branches')
1426 fm = ui.formatter(b'branches', opts)
1426 fm = ui.formatter(b'branches', opts)
1427 hexfunc = fm.hexfunc
1427 hexfunc = fm.hexfunc
1428
1428
1429 allheads = set(repo.heads())
1429 allheads = set(repo.heads())
1430 branches = []
1430 branches = []
1431 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1431 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1432 if selectedbranches is not None and tag not in selectedbranches:
1432 if selectedbranches is not None and tag not in selectedbranches:
1433 continue
1433 continue
1434 isactive = False
1434 isactive = False
1435 if not isclosed:
1435 if not isclosed:
1436 openheads = set(repo.branchmap().iteropen(heads))
1436 openheads = set(repo.branchmap().iteropen(heads))
1437 isactive = bool(openheads & allheads)
1437 isactive = bool(openheads & allheads)
1438 branches.append((tag, repo[tip], isactive, not isclosed))
1438 branches.append((tag, repo[tip], isactive, not isclosed))
1439 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1439 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1440
1440
1441 for tag, ctx, isactive, isopen in branches:
1441 for tag, ctx, isactive, isopen in branches:
1442 if active and not isactive:
1442 if active and not isactive:
1443 continue
1443 continue
1444 if isactive:
1444 if isactive:
1445 label = b'branches.active'
1445 label = b'branches.active'
1446 notice = b''
1446 notice = b''
1447 elif not isopen:
1447 elif not isopen:
1448 if not closed:
1448 if not closed:
1449 continue
1449 continue
1450 label = b'branches.closed'
1450 label = b'branches.closed'
1451 notice = _(b' (closed)')
1451 notice = _(b' (closed)')
1452 else:
1452 else:
1453 label = b'branches.inactive'
1453 label = b'branches.inactive'
1454 notice = _(b' (inactive)')
1454 notice = _(b' (inactive)')
1455 current = tag == repo.dirstate.branch()
1455 current = tag == repo.dirstate.branch()
1456 if current:
1456 if current:
1457 label = b'branches.current'
1457 label = b'branches.current'
1458
1458
1459 fm.startitem()
1459 fm.startitem()
1460 fm.write(b'branch', b'%s', tag, label=label)
1460 fm.write(b'branch', b'%s', tag, label=label)
1461 rev = ctx.rev()
1461 rev = ctx.rev()
1462 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1462 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1463 fmt = b' ' * padsize + b' %d:%s'
1463 fmt = b' ' * padsize + b' %d:%s'
1464 fm.condwrite(
1464 fm.condwrite(
1465 not ui.quiet,
1465 not ui.quiet,
1466 b'rev node',
1466 b'rev node',
1467 fmt,
1467 fmt,
1468 rev,
1468 rev,
1469 hexfunc(ctx.node()),
1469 hexfunc(ctx.node()),
1470 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1470 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1471 )
1471 )
1472 fm.context(ctx=ctx)
1472 fm.context(ctx=ctx)
1473 fm.data(active=isactive, closed=not isopen, current=current)
1473 fm.data(active=isactive, closed=not isopen, current=current)
1474 if not ui.quiet:
1474 if not ui.quiet:
1475 fm.plain(notice)
1475 fm.plain(notice)
1476 fm.plain(b'\n')
1476 fm.plain(b'\n')
1477 fm.end()
1477 fm.end()
1478
1478
1479
1479
1480 @command(
1480 @command(
1481 b'bundle',
1481 b'bundle',
1482 [
1482 [
1483 (
1483 (
1484 b'f',
1484 b'f',
1485 b'force',
1485 b'force',
1486 None,
1486 None,
1487 _(b'run even when the destination is unrelated'),
1487 _(b'run even when the destination is unrelated'),
1488 ),
1488 ),
1489 (
1489 (
1490 b'r',
1490 b'r',
1491 b'rev',
1491 b'rev',
1492 [],
1492 [],
1493 _(b'a changeset intended to be added to the destination'),
1493 _(b'a changeset intended to be added to the destination'),
1494 _(b'REV'),
1494 _(b'REV'),
1495 ),
1495 ),
1496 (
1496 (
1497 b'b',
1497 b'b',
1498 b'branch',
1498 b'branch',
1499 [],
1499 [],
1500 _(b'a specific branch you would like to bundle'),
1500 _(b'a specific branch you would like to bundle'),
1501 _(b'BRANCH'),
1501 _(b'BRANCH'),
1502 ),
1502 ),
1503 (
1503 (
1504 b'',
1504 b'',
1505 b'base',
1505 b'base',
1506 [],
1506 [],
1507 _(b'a base changeset assumed to be available at the destination'),
1507 _(b'a base changeset assumed to be available at the destination'),
1508 _(b'REV'),
1508 _(b'REV'),
1509 ),
1509 ),
1510 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1510 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1511 (
1511 (
1512 b't',
1512 b't',
1513 b'type',
1513 b'type',
1514 b'bzip2',
1514 b'bzip2',
1515 _(b'bundle compression type to use'),
1515 _(b'bundle compression type to use'),
1516 _(b'TYPE'),
1516 _(b'TYPE'),
1517 ),
1517 ),
1518 ]
1518 ]
1519 + remoteopts,
1519 + remoteopts,
1520 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1520 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1521 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1521 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1522 )
1522 )
1523 def bundle(ui, repo, fname, dest=None, **opts):
1523 def bundle(ui, repo, fname, dest=None, **opts):
1524 """create a bundle file
1524 """create a bundle file
1525
1525
1526 Generate a bundle file containing data to be transferred to another
1526 Generate a bundle file containing data to be transferred to another
1527 repository.
1527 repository.
1528
1528
1529 To create a bundle containing all changesets, use -a/--all
1529 To create a bundle containing all changesets, use -a/--all
1530 (or --base null). Otherwise, hg assumes the destination will have
1530 (or --base null). Otherwise, hg assumes the destination will have
1531 all the nodes you specify with --base parameters. Otherwise, hg
1531 all the nodes you specify with --base parameters. Otherwise, hg
1532 will assume the repository has all the nodes in destination, or
1532 will assume the repository has all the nodes in destination, or
1533 default-push/default if no destination is specified, where destination
1533 default-push/default if no destination is specified, where destination
1534 is the repository you provide through DEST option.
1534 is the repository you provide through DEST option.
1535
1535
1536 You can change bundle format with the -t/--type option. See
1536 You can change bundle format with the -t/--type option. See
1537 :hg:`help bundlespec` for documentation on this format. By default,
1537 :hg:`help bundlespec` for documentation on this format. By default,
1538 the most appropriate format is used and compression defaults to
1538 the most appropriate format is used and compression defaults to
1539 bzip2.
1539 bzip2.
1540
1540
1541 The bundle file can then be transferred using conventional means
1541 The bundle file can then be transferred using conventional means
1542 and applied to another repository with the unbundle or pull
1542 and applied to another repository with the unbundle or pull
1543 command. This is useful when direct push and pull are not
1543 command. This is useful when direct push and pull are not
1544 available or when exporting an entire repository is undesirable.
1544 available or when exporting an entire repository is undesirable.
1545
1545
1546 Applying bundles preserves all changeset contents including
1546 Applying bundles preserves all changeset contents including
1547 permissions, copy/rename information, and revision history.
1547 permissions, copy/rename information, and revision history.
1548
1548
1549 Returns 0 on success, 1 if no changes found.
1549 Returns 0 on success, 1 if no changes found.
1550 """
1550 """
1551 opts = pycompat.byteskwargs(opts)
1551 opts = pycompat.byteskwargs(opts)
1552 revs = None
1552 revs = None
1553 if b'rev' in opts:
1553 if b'rev' in opts:
1554 revstrings = opts[b'rev']
1554 revstrings = opts[b'rev']
1555 revs = scmutil.revrange(repo, revstrings)
1555 revs = scmutil.revrange(repo, revstrings)
1556 if revstrings and not revs:
1556 if revstrings and not revs:
1557 raise error.Abort(_(b'no commits to bundle'))
1557 raise error.Abort(_(b'no commits to bundle'))
1558
1558
1559 bundletype = opts.get(b'type', b'bzip2').lower()
1559 bundletype = opts.get(b'type', b'bzip2').lower()
1560 try:
1560 try:
1561 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1561 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1562 except error.UnsupportedBundleSpecification as e:
1562 except error.UnsupportedBundleSpecification as e:
1563 raise error.Abort(
1563 raise error.Abort(
1564 pycompat.bytestr(e),
1564 pycompat.bytestr(e),
1565 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1565 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1566 )
1566 )
1567 cgversion = bundlespec.contentopts[b"cg.version"]
1567 cgversion = bundlespec.contentopts[b"cg.version"]
1568
1568
1569 # Packed bundles are a pseudo bundle format for now.
1569 # Packed bundles are a pseudo bundle format for now.
1570 if cgversion == b's1':
1570 if cgversion == b's1':
1571 raise error.Abort(
1571 raise error.Abort(
1572 _(b'packed bundles cannot be produced by "hg bundle"'),
1572 _(b'packed bundles cannot be produced by "hg bundle"'),
1573 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1573 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1574 )
1574 )
1575
1575
1576 if opts.get(b'all'):
1576 if opts.get(b'all'):
1577 if dest:
1577 if dest:
1578 raise error.Abort(
1578 raise error.Abort(
1579 _(b"--all is incompatible with specifying a destination")
1579 _(b"--all is incompatible with specifying a destination")
1580 )
1580 )
1581 if opts.get(b'base'):
1581 if opts.get(b'base'):
1582 ui.warn(_(b"ignoring --base because --all was specified\n"))
1582 ui.warn(_(b"ignoring --base because --all was specified\n"))
1583 base = [nullrev]
1583 base = [nullrev]
1584 else:
1584 else:
1585 base = scmutil.revrange(repo, opts.get(b'base'))
1585 base = scmutil.revrange(repo, opts.get(b'base'))
1586 if cgversion not in changegroup.supportedoutgoingversions(repo):
1586 if cgversion not in changegroup.supportedoutgoingversions(repo):
1587 raise error.Abort(
1587 raise error.Abort(
1588 _(b"repository does not support bundle version %s") % cgversion
1588 _(b"repository does not support bundle version %s") % cgversion
1589 )
1589 )
1590
1590
1591 if base:
1591 if base:
1592 if dest:
1592 if dest:
1593 raise error.Abort(
1593 raise error.Abort(
1594 _(b"--base is incompatible with specifying a destination")
1594 _(b"--base is incompatible with specifying a destination")
1595 )
1595 )
1596 common = [repo[rev].node() for rev in base]
1596 common = [repo[rev].node() for rev in base]
1597 heads = [repo[r].node() for r in revs] if revs else None
1597 heads = [repo[r].node() for r in revs] if revs else None
1598 outgoing = discovery.outgoing(repo, common, heads)
1598 outgoing = discovery.outgoing(repo, common, heads)
1599 else:
1599 else:
1600 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1600 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1601 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1601 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1602 other = hg.peer(repo, opts, dest)
1602 other = hg.peer(repo, opts, dest)
1603 revs = [repo[r].hex() for r in revs]
1603 revs = [repo[r].hex() for r in revs]
1604 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1604 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1605 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1605 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1606 outgoing = discovery.findcommonoutgoing(
1606 outgoing = discovery.findcommonoutgoing(
1607 repo,
1607 repo,
1608 other,
1608 other,
1609 onlyheads=heads,
1609 onlyheads=heads,
1610 force=opts.get(b'force'),
1610 force=opts.get(b'force'),
1611 portable=True,
1611 portable=True,
1612 )
1612 )
1613
1613
1614 if not outgoing.missing:
1614 if not outgoing.missing:
1615 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1615 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1616 return 1
1616 return 1
1617
1617
1618 if cgversion == b'01': # bundle1
1618 if cgversion == b'01': # bundle1
1619 bversion = b'HG10' + bundlespec.wirecompression
1619 bversion = b'HG10' + bundlespec.wirecompression
1620 bcompression = None
1620 bcompression = None
1621 elif cgversion in (b'02', b'03'):
1621 elif cgversion in (b'02', b'03'):
1622 bversion = b'HG20'
1622 bversion = b'HG20'
1623 bcompression = bundlespec.wirecompression
1623 bcompression = bundlespec.wirecompression
1624 else:
1624 else:
1625 raise error.ProgrammingError(
1625 raise error.ProgrammingError(
1626 b'bundle: unexpected changegroup version %s' % cgversion
1626 b'bundle: unexpected changegroup version %s' % cgversion
1627 )
1627 )
1628
1628
1629 # TODO compression options should be derived from bundlespec parsing.
1629 # TODO compression options should be derived from bundlespec parsing.
1630 # This is a temporary hack to allow adjusting bundle compression
1630 # This is a temporary hack to allow adjusting bundle compression
1631 # level without a) formalizing the bundlespec changes to declare it
1631 # level without a) formalizing the bundlespec changes to declare it
1632 # b) introducing a command flag.
1632 # b) introducing a command flag.
1633 compopts = {}
1633 compopts = {}
1634 complevel = ui.configint(
1634 complevel = ui.configint(
1635 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1635 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1636 )
1636 )
1637 if complevel is None:
1637 if complevel is None:
1638 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1638 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1639 if complevel is not None:
1639 if complevel is not None:
1640 compopts[b'level'] = complevel
1640 compopts[b'level'] = complevel
1641
1641
1642 # Allow overriding the bundling of obsmarker in phases through
1642 # Allow overriding the bundling of obsmarker in phases through
1643 # configuration while we don't have a bundle version that include them
1643 # configuration while we don't have a bundle version that include them
1644 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1644 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1645 bundlespec.contentopts[b'obsolescence'] = True
1645 bundlespec.contentopts[b'obsolescence'] = True
1646 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1646 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1647 bundlespec.contentopts[b'phases'] = True
1647 bundlespec.contentopts[b'phases'] = True
1648
1648
1649 bundle2.writenewbundle(
1649 bundle2.writenewbundle(
1650 ui,
1650 ui,
1651 repo,
1651 repo,
1652 b'bundle',
1652 b'bundle',
1653 fname,
1653 fname,
1654 bversion,
1654 bversion,
1655 outgoing,
1655 outgoing,
1656 bundlespec.contentopts,
1656 bundlespec.contentopts,
1657 compression=bcompression,
1657 compression=bcompression,
1658 compopts=compopts,
1658 compopts=compopts,
1659 )
1659 )
1660
1660
1661
1661
1662 @command(
1662 @command(
1663 b'cat',
1663 b'cat',
1664 [
1664 [
1665 (
1665 (
1666 b'o',
1666 b'o',
1667 b'output',
1667 b'output',
1668 b'',
1668 b'',
1669 _(b'print output to file with formatted name'),
1669 _(b'print output to file with formatted name'),
1670 _(b'FORMAT'),
1670 _(b'FORMAT'),
1671 ),
1671 ),
1672 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1672 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1673 (b'', b'decode', None, _(b'apply any matching decode filter')),
1673 (b'', b'decode', None, _(b'apply any matching decode filter')),
1674 ]
1674 ]
1675 + walkopts
1675 + walkopts
1676 + formatteropts,
1676 + formatteropts,
1677 _(b'[OPTION]... FILE...'),
1677 _(b'[OPTION]... FILE...'),
1678 helpcategory=command.CATEGORY_FILE_CONTENTS,
1678 helpcategory=command.CATEGORY_FILE_CONTENTS,
1679 inferrepo=True,
1679 inferrepo=True,
1680 intents={INTENT_READONLY},
1680 intents={INTENT_READONLY},
1681 )
1681 )
1682 def cat(ui, repo, file1, *pats, **opts):
1682 def cat(ui, repo, file1, *pats, **opts):
1683 """output the current or given revision of files
1683 """output the current or given revision of files
1684
1684
1685 Print the specified files as they were at the given revision. If
1685 Print the specified files as they were at the given revision. If
1686 no revision is given, the parent of the working directory is used.
1686 no revision is given, the parent of the working directory is used.
1687
1687
1688 Output may be to a file, in which case the name of the file is
1688 Output may be to a file, in which case the name of the file is
1689 given using a template string. See :hg:`help templates`. In addition
1689 given using a template string. See :hg:`help templates`. In addition
1690 to the common template keywords, the following formatting rules are
1690 to the common template keywords, the following formatting rules are
1691 supported:
1691 supported:
1692
1692
1693 :``%%``: literal "%" character
1693 :``%%``: literal "%" character
1694 :``%s``: basename of file being printed
1694 :``%s``: basename of file being printed
1695 :``%d``: dirname of file being printed, or '.' if in repository root
1695 :``%d``: dirname of file being printed, or '.' if in repository root
1696 :``%p``: root-relative path name of file being printed
1696 :``%p``: root-relative path name of file being printed
1697 :``%H``: changeset hash (40 hexadecimal digits)
1697 :``%H``: changeset hash (40 hexadecimal digits)
1698 :``%R``: changeset revision number
1698 :``%R``: changeset revision number
1699 :``%h``: short-form changeset hash (12 hexadecimal digits)
1699 :``%h``: short-form changeset hash (12 hexadecimal digits)
1700 :``%r``: zero-padded changeset revision number
1700 :``%r``: zero-padded changeset revision number
1701 :``%b``: basename of the exporting repository
1701 :``%b``: basename of the exporting repository
1702 :``\\``: literal "\\" character
1702 :``\\``: literal "\\" character
1703
1703
1704 .. container:: verbose
1704 .. container:: verbose
1705
1705
1706 Template:
1706 Template:
1707
1707
1708 The following keywords are supported in addition to the common template
1708 The following keywords are supported in addition to the common template
1709 keywords and functions. See also :hg:`help templates`.
1709 keywords and functions. See also :hg:`help templates`.
1710
1710
1711 :data: String. File content.
1711 :data: String. File content.
1712 :path: String. Repository-absolute path of the file.
1712 :path: String. Repository-absolute path of the file.
1713
1713
1714 Returns 0 on success.
1714 Returns 0 on success.
1715 """
1715 """
1716 opts = pycompat.byteskwargs(opts)
1716 opts = pycompat.byteskwargs(opts)
1717 rev = opts.get(b'rev')
1717 rev = opts.get(b'rev')
1718 if rev:
1718 if rev:
1719 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1719 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1720 ctx = scmutil.revsingle(repo, rev)
1720 ctx = scmutil.revsingle(repo, rev)
1721 m = scmutil.match(ctx, (file1,) + pats, opts)
1721 m = scmutil.match(ctx, (file1,) + pats, opts)
1722 fntemplate = opts.pop(b'output', b'')
1722 fntemplate = opts.pop(b'output', b'')
1723 if cmdutil.isstdiofilename(fntemplate):
1723 if cmdutil.isstdiofilename(fntemplate):
1724 fntemplate = b''
1724 fntemplate = b''
1725
1725
1726 if fntemplate:
1726 if fntemplate:
1727 fm = formatter.nullformatter(ui, b'cat', opts)
1727 fm = formatter.nullformatter(ui, b'cat', opts)
1728 else:
1728 else:
1729 ui.pager(b'cat')
1729 ui.pager(b'cat')
1730 fm = ui.formatter(b'cat', opts)
1730 fm = ui.formatter(b'cat', opts)
1731 with fm:
1731 with fm:
1732 return cmdutil.cat(
1732 return cmdutil.cat(
1733 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1733 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1734 )
1734 )
1735
1735
1736
1736
1737 @command(
1737 @command(
1738 b'clone',
1738 b'clone',
1739 [
1739 [
1740 (
1740 (
1741 b'U',
1741 b'U',
1742 b'noupdate',
1742 b'noupdate',
1743 None,
1743 None,
1744 _(
1744 _(
1745 b'the clone will include an empty working '
1745 b'the clone will include an empty working '
1746 b'directory (only a repository)'
1746 b'directory (only a repository)'
1747 ),
1747 ),
1748 ),
1748 ),
1749 (
1749 (
1750 b'u',
1750 b'u',
1751 b'updaterev',
1751 b'updaterev',
1752 b'',
1752 b'',
1753 _(b'revision, tag, or branch to check out'),
1753 _(b'revision, tag, or branch to check out'),
1754 _(b'REV'),
1754 _(b'REV'),
1755 ),
1755 ),
1756 (
1756 (
1757 b'r',
1757 b'r',
1758 b'rev',
1758 b'rev',
1759 [],
1759 [],
1760 _(
1760 _(
1761 b'do not clone everything, but include this changeset'
1761 b'do not clone everything, but include this changeset'
1762 b' and its ancestors'
1762 b' and its ancestors'
1763 ),
1763 ),
1764 _(b'REV'),
1764 _(b'REV'),
1765 ),
1765 ),
1766 (
1766 (
1767 b'b',
1767 b'b',
1768 b'branch',
1768 b'branch',
1769 [],
1769 [],
1770 _(
1770 _(
1771 b'do not clone everything, but include this branch\'s'
1771 b'do not clone everything, but include this branch\'s'
1772 b' changesets and their ancestors'
1772 b' changesets and their ancestors'
1773 ),
1773 ),
1774 _(b'BRANCH'),
1774 _(b'BRANCH'),
1775 ),
1775 ),
1776 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1776 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1777 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1777 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1778 (b'', b'stream', None, _(b'clone with minimal data processing')),
1778 (b'', b'stream', None, _(b'clone with minimal data processing')),
1779 ]
1779 ]
1780 + remoteopts,
1780 + remoteopts,
1781 _(b'[OPTION]... SOURCE [DEST]'),
1781 _(b'[OPTION]... SOURCE [DEST]'),
1782 helpcategory=command.CATEGORY_REPO_CREATION,
1782 helpcategory=command.CATEGORY_REPO_CREATION,
1783 helpbasic=True,
1783 helpbasic=True,
1784 norepo=True,
1784 norepo=True,
1785 )
1785 )
1786 def clone(ui, source, dest=None, **opts):
1786 def clone(ui, source, dest=None, **opts):
1787 """make a copy of an existing repository
1787 """make a copy of an existing repository
1788
1788
1789 Create a copy of an existing repository in a new directory.
1789 Create a copy of an existing repository in a new directory.
1790
1790
1791 If no destination directory name is specified, it defaults to the
1791 If no destination directory name is specified, it defaults to the
1792 basename of the source.
1792 basename of the source.
1793
1793
1794 The location of the source is added to the new repository's
1794 The location of the source is added to the new repository's
1795 ``.hg/hgrc`` file, as the default to be used for future pulls.
1795 ``.hg/hgrc`` file, as the default to be used for future pulls.
1796
1796
1797 Only local paths and ``ssh://`` URLs are supported as
1797 Only local paths and ``ssh://`` URLs are supported as
1798 destinations. For ``ssh://`` destinations, no working directory or
1798 destinations. For ``ssh://`` destinations, no working directory or
1799 ``.hg/hgrc`` will be created on the remote side.
1799 ``.hg/hgrc`` will be created on the remote side.
1800
1800
1801 If the source repository has a bookmark called '@' set, that
1801 If the source repository has a bookmark called '@' set, that
1802 revision will be checked out in the new repository by default.
1802 revision will be checked out in the new repository by default.
1803
1803
1804 To check out a particular version, use -u/--update, or
1804 To check out a particular version, use -u/--update, or
1805 -U/--noupdate to create a clone with no working directory.
1805 -U/--noupdate to create a clone with no working directory.
1806
1806
1807 To pull only a subset of changesets, specify one or more revisions
1807 To pull only a subset of changesets, specify one or more revisions
1808 identifiers with -r/--rev or branches with -b/--branch. The
1808 identifiers with -r/--rev or branches with -b/--branch. The
1809 resulting clone will contain only the specified changesets and
1809 resulting clone will contain only the specified changesets and
1810 their ancestors. These options (or 'clone src#rev dest') imply
1810 their ancestors. These options (or 'clone src#rev dest') imply
1811 --pull, even for local source repositories.
1811 --pull, even for local source repositories.
1812
1812
1813 In normal clone mode, the remote normalizes repository data into a common
1813 In normal clone mode, the remote normalizes repository data into a common
1814 exchange format and the receiving end translates this data into its local
1814 exchange format and the receiving end translates this data into its local
1815 storage format. --stream activates a different clone mode that essentially
1815 storage format. --stream activates a different clone mode that essentially
1816 copies repository files from the remote with minimal data processing. This
1816 copies repository files from the remote with minimal data processing. This
1817 significantly reduces the CPU cost of a clone both remotely and locally.
1817 significantly reduces the CPU cost of a clone both remotely and locally.
1818 However, it often increases the transferred data size by 30-40%. This can
1818 However, it often increases the transferred data size by 30-40%. This can
1819 result in substantially faster clones where I/O throughput is plentiful,
1819 result in substantially faster clones where I/O throughput is plentiful,
1820 especially for larger repositories. A side-effect of --stream clones is
1820 especially for larger repositories. A side-effect of --stream clones is
1821 that storage settings and requirements on the remote are applied locally:
1821 that storage settings and requirements on the remote are applied locally:
1822 a modern client may inherit legacy or inefficient storage used by the
1822 a modern client may inherit legacy or inefficient storage used by the
1823 remote or a legacy Mercurial client may not be able to clone from a
1823 remote or a legacy Mercurial client may not be able to clone from a
1824 modern Mercurial remote.
1824 modern Mercurial remote.
1825
1825
1826 .. note::
1826 .. note::
1827
1827
1828 Specifying a tag will include the tagged changeset but not the
1828 Specifying a tag will include the tagged changeset but not the
1829 changeset containing the tag.
1829 changeset containing the tag.
1830
1830
1831 .. container:: verbose
1831 .. container:: verbose
1832
1832
1833 For efficiency, hardlinks are used for cloning whenever the
1833 For efficiency, hardlinks are used for cloning whenever the
1834 source and destination are on the same filesystem (note this
1834 source and destination are on the same filesystem (note this
1835 applies only to the repository data, not to the working
1835 applies only to the repository data, not to the working
1836 directory). Some filesystems, such as AFS, implement hardlinking
1836 directory). Some filesystems, such as AFS, implement hardlinking
1837 incorrectly, but do not report errors. In these cases, use the
1837 incorrectly, but do not report errors. In these cases, use the
1838 --pull option to avoid hardlinking.
1838 --pull option to avoid hardlinking.
1839
1839
1840 Mercurial will update the working directory to the first applicable
1840 Mercurial will update the working directory to the first applicable
1841 revision from this list:
1841 revision from this list:
1842
1842
1843 a) null if -U or the source repository has no changesets
1843 a) null if -U or the source repository has no changesets
1844 b) if -u . and the source repository is local, the first parent of
1844 b) if -u . and the source repository is local, the first parent of
1845 the source repository's working directory
1845 the source repository's working directory
1846 c) the changeset specified with -u (if a branch name, this means the
1846 c) the changeset specified with -u (if a branch name, this means the
1847 latest head of that branch)
1847 latest head of that branch)
1848 d) the changeset specified with -r
1848 d) the changeset specified with -r
1849 e) the tipmost head specified with -b
1849 e) the tipmost head specified with -b
1850 f) the tipmost head specified with the url#branch source syntax
1850 f) the tipmost head specified with the url#branch source syntax
1851 g) the revision marked with the '@' bookmark, if present
1851 g) the revision marked with the '@' bookmark, if present
1852 h) the tipmost head of the default branch
1852 h) the tipmost head of the default branch
1853 i) tip
1853 i) tip
1854
1854
1855 When cloning from servers that support it, Mercurial may fetch
1855 When cloning from servers that support it, Mercurial may fetch
1856 pre-generated data from a server-advertised URL or inline from the
1856 pre-generated data from a server-advertised URL or inline from the
1857 same stream. When this is done, hooks operating on incoming changesets
1857 same stream. When this is done, hooks operating on incoming changesets
1858 and changegroups may fire more than once, once for each pre-generated
1858 and changegroups may fire more than once, once for each pre-generated
1859 bundle and as well as for any additional remaining data. In addition,
1859 bundle and as well as for any additional remaining data. In addition,
1860 if an error occurs, the repository may be rolled back to a partial
1860 if an error occurs, the repository may be rolled back to a partial
1861 clone. This behavior may change in future releases.
1861 clone. This behavior may change in future releases.
1862 See :hg:`help -e clonebundles` for more.
1862 See :hg:`help -e clonebundles` for more.
1863
1863
1864 Examples:
1864 Examples:
1865
1865
1866 - clone a remote repository to a new directory named hg/::
1866 - clone a remote repository to a new directory named hg/::
1867
1867
1868 hg clone https://www.mercurial-scm.org/repo/hg/
1868 hg clone https://www.mercurial-scm.org/repo/hg/
1869
1869
1870 - create a lightweight local clone::
1870 - create a lightweight local clone::
1871
1871
1872 hg clone project/ project-feature/
1872 hg clone project/ project-feature/
1873
1873
1874 - clone from an absolute path on an ssh server (note double-slash)::
1874 - clone from an absolute path on an ssh server (note double-slash)::
1875
1875
1876 hg clone ssh://user@server//home/projects/alpha/
1876 hg clone ssh://user@server//home/projects/alpha/
1877
1877
1878 - do a streaming clone while checking out a specified version::
1878 - do a streaming clone while checking out a specified version::
1879
1879
1880 hg clone --stream http://server/repo -u 1.5
1880 hg clone --stream http://server/repo -u 1.5
1881
1881
1882 - create a repository without changesets after a particular revision::
1882 - create a repository without changesets after a particular revision::
1883
1883
1884 hg clone -r 04e544 experimental/ good/
1884 hg clone -r 04e544 experimental/ good/
1885
1885
1886 - clone (and track) a particular named branch::
1886 - clone (and track) a particular named branch::
1887
1887
1888 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1888 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1889
1889
1890 See :hg:`help urls` for details on specifying URLs.
1890 See :hg:`help urls` for details on specifying URLs.
1891
1891
1892 Returns 0 on success.
1892 Returns 0 on success.
1893 """
1893 """
1894 opts = pycompat.byteskwargs(opts)
1894 opts = pycompat.byteskwargs(opts)
1895 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1895 if opts.get(b'noupdate') and opts.get(b'updaterev'):
1896 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1896 raise error.Abort(_(b"cannot specify both --noupdate and --updaterev"))
1897
1897
1898 # --include/--exclude can come from narrow or sparse.
1898 # --include/--exclude can come from narrow or sparse.
1899 includepats, excludepats = None, None
1899 includepats, excludepats = None, None
1900
1900
1901 # hg.clone() differentiates between None and an empty set. So make sure
1901 # hg.clone() differentiates between None and an empty set. So make sure
1902 # patterns are sets if narrow is requested without patterns.
1902 # patterns are sets if narrow is requested without patterns.
1903 if opts.get(b'narrow'):
1903 if opts.get(b'narrow'):
1904 includepats = set()
1904 includepats = set()
1905 excludepats = set()
1905 excludepats = set()
1906
1906
1907 if opts.get(b'include'):
1907 if opts.get(b'include'):
1908 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1908 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1909 if opts.get(b'exclude'):
1909 if opts.get(b'exclude'):
1910 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1910 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1911
1911
1912 r = hg.clone(
1912 r = hg.clone(
1913 ui,
1913 ui,
1914 opts,
1914 opts,
1915 source,
1915 source,
1916 dest,
1916 dest,
1917 pull=opts.get(b'pull'),
1917 pull=opts.get(b'pull'),
1918 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1918 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1919 revs=opts.get(b'rev'),
1919 revs=opts.get(b'rev'),
1920 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1920 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1921 branch=opts.get(b'branch'),
1921 branch=opts.get(b'branch'),
1922 shareopts=opts.get(b'shareopts'),
1922 shareopts=opts.get(b'shareopts'),
1923 storeincludepats=includepats,
1923 storeincludepats=includepats,
1924 storeexcludepats=excludepats,
1924 storeexcludepats=excludepats,
1925 depth=opts.get(b'depth') or None,
1925 depth=opts.get(b'depth') or None,
1926 )
1926 )
1927
1927
1928 return r is None
1928 return r is None
1929
1929
1930
1930
1931 @command(
1931 @command(
1932 b'commit|ci',
1932 b'commit|ci',
1933 [
1933 [
1934 (
1934 (
1935 b'A',
1935 b'A',
1936 b'addremove',
1936 b'addremove',
1937 None,
1937 None,
1938 _(b'mark new/missing files as added/removed before committing'),
1938 _(b'mark new/missing files as added/removed before committing'),
1939 ),
1939 ),
1940 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1940 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1941 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1941 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1942 (b's', b'secret', None, _(b'use the secret phase for committing')),
1942 (b's', b'secret', None, _(b'use the secret phase for committing')),
1943 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1943 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1944 (
1944 (
1945 b'',
1945 b'',
1946 b'force-close-branch',
1946 b'force-close-branch',
1947 None,
1947 None,
1948 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1948 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1949 ),
1949 ),
1950 (b'i', b'interactive', None, _(b'use interactive mode')),
1950 (b'i', b'interactive', None, _(b'use interactive mode')),
1951 ]
1951 ]
1952 + walkopts
1952 + walkopts
1953 + commitopts
1953 + commitopts
1954 + commitopts2
1954 + commitopts2
1955 + subrepoopts,
1955 + subrepoopts,
1956 _(b'[OPTION]... [FILE]...'),
1956 _(b'[OPTION]... [FILE]...'),
1957 helpcategory=command.CATEGORY_COMMITTING,
1957 helpcategory=command.CATEGORY_COMMITTING,
1958 helpbasic=True,
1958 helpbasic=True,
1959 inferrepo=True,
1959 inferrepo=True,
1960 )
1960 )
1961 def commit(ui, repo, *pats, **opts):
1961 def commit(ui, repo, *pats, **opts):
1962 """commit the specified files or all outstanding changes
1962 """commit the specified files or all outstanding changes
1963
1963
1964 Commit changes to the given files into the repository. Unlike a
1964 Commit changes to the given files into the repository. Unlike a
1965 centralized SCM, this operation is a local operation. See
1965 centralized SCM, this operation is a local operation. See
1966 :hg:`push` for a way to actively distribute your changes.
1966 :hg:`push` for a way to actively distribute your changes.
1967
1967
1968 If a list of files is omitted, all changes reported by :hg:`status`
1968 If a list of files is omitted, all changes reported by :hg:`status`
1969 will be committed.
1969 will be committed.
1970
1970
1971 If you are committing the result of a merge, do not provide any
1971 If you are committing the result of a merge, do not provide any
1972 filenames or -I/-X filters.
1972 filenames or -I/-X filters.
1973
1973
1974 If no commit message is specified, Mercurial starts your
1974 If no commit message is specified, Mercurial starts your
1975 configured editor where you can enter a message. In case your
1975 configured editor where you can enter a message. In case your
1976 commit fails, you will find a backup of your message in
1976 commit fails, you will find a backup of your message in
1977 ``.hg/last-message.txt``.
1977 ``.hg/last-message.txt``.
1978
1978
1979 The --close-branch flag can be used to mark the current branch
1979 The --close-branch flag can be used to mark the current branch
1980 head closed. When all heads of a branch are closed, the branch
1980 head closed. When all heads of a branch are closed, the branch
1981 will be considered closed and no longer listed.
1981 will be considered closed and no longer listed.
1982
1982
1983 The --amend flag can be used to amend the parent of the
1983 The --amend flag can be used to amend the parent of the
1984 working directory with a new commit that contains the changes
1984 working directory with a new commit that contains the changes
1985 in the parent in addition to those currently reported by :hg:`status`,
1985 in the parent in addition to those currently reported by :hg:`status`,
1986 if there are any. The old commit is stored in a backup bundle in
1986 if there are any. The old commit is stored in a backup bundle in
1987 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1987 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1988 on how to restore it).
1988 on how to restore it).
1989
1989
1990 Message, user and date are taken from the amended commit unless
1990 Message, user and date are taken from the amended commit unless
1991 specified. When a message isn't specified on the command line,
1991 specified. When a message isn't specified on the command line,
1992 the editor will open with the message of the amended commit.
1992 the editor will open with the message of the amended commit.
1993
1993
1994 It is not possible to amend public changesets (see :hg:`help phases`)
1994 It is not possible to amend public changesets (see :hg:`help phases`)
1995 or changesets that have children.
1995 or changesets that have children.
1996
1996
1997 See :hg:`help dates` for a list of formats valid for -d/--date.
1997 See :hg:`help dates` for a list of formats valid for -d/--date.
1998
1998
1999 Returns 0 on success, 1 if nothing changed.
1999 Returns 0 on success, 1 if nothing changed.
2000
2000
2001 .. container:: verbose
2001 .. container:: verbose
2002
2002
2003 Examples:
2003 Examples:
2004
2004
2005 - commit all files ending in .py::
2005 - commit all files ending in .py::
2006
2006
2007 hg commit --include "set:**.py"
2007 hg commit --include "set:**.py"
2008
2008
2009 - commit all non-binary files::
2009 - commit all non-binary files::
2010
2010
2011 hg commit --exclude "set:binary()"
2011 hg commit --exclude "set:binary()"
2012
2012
2013 - amend the current commit and set the date to now::
2013 - amend the current commit and set the date to now::
2014
2014
2015 hg commit --amend --date now
2015 hg commit --amend --date now
2016 """
2016 """
2017 with repo.wlock(), repo.lock():
2017 with repo.wlock(), repo.lock():
2018 return _docommit(ui, repo, *pats, **opts)
2018 return _docommit(ui, repo, *pats, **opts)
2019
2019
2020
2020
2021 def _docommit(ui, repo, *pats, **opts):
2021 def _docommit(ui, repo, *pats, **opts):
2022 if opts.get(r'interactive'):
2022 if opts.get(r'interactive'):
2023 opts.pop(r'interactive')
2023 opts.pop(r'interactive')
2024 ret = cmdutil.dorecord(
2024 ret = cmdutil.dorecord(
2025 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2025 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2026 )
2026 )
2027 # ret can be 0 (no changes to record) or the value returned by
2027 # ret can be 0 (no changes to record) or the value returned by
2028 # commit(), 1 if nothing changed or None on success.
2028 # commit(), 1 if nothing changed or None on success.
2029 return 1 if ret == 0 else ret
2029 return 1 if ret == 0 else ret
2030
2030
2031 opts = pycompat.byteskwargs(opts)
2031 opts = pycompat.byteskwargs(opts)
2032 if opts.get(b'subrepos'):
2032 if opts.get(b'subrepos'):
2033 if opts.get(b'amend'):
2033 if opts.get(b'amend'):
2034 raise error.Abort(_(b'cannot amend with --subrepos'))
2034 raise error.Abort(_(b'cannot amend with --subrepos'))
2035 # Let --subrepos on the command line override config setting.
2035 # Let --subrepos on the command line override config setting.
2036 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2036 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2037
2037
2038 cmdutil.checkunfinished(repo, commit=True)
2038 cmdutil.checkunfinished(repo, commit=True)
2039
2039
2040 branch = repo[None].branch()
2040 branch = repo[None].branch()
2041 bheads = repo.branchheads(branch)
2041 bheads = repo.branchheads(branch)
2042
2042
2043 extra = {}
2043 extra = {}
2044 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2044 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2045 extra[b'close'] = b'1'
2045 extra[b'close'] = b'1'
2046
2046
2047 if repo[b'.'].closesbranch():
2047 if repo[b'.'].closesbranch():
2048 raise error.Abort(
2048 raise error.Abort(
2049 _(b'current revision is already a branch closing head')
2049 _(b'current revision is already a branch closing head')
2050 )
2050 )
2051 elif not bheads:
2051 elif not bheads:
2052 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2052 raise error.Abort(_(b'branch "%s" has no heads to close') % branch)
2053 elif (
2053 elif (
2054 branch == repo[b'.'].branch()
2054 branch == repo[b'.'].branch()
2055 and repo[b'.'].node() not in bheads
2055 and repo[b'.'].node() not in bheads
2056 and not opts.get(b'force_close_branch')
2056 and not opts.get(b'force_close_branch')
2057 ):
2057 ):
2058 hint = _(
2058 hint = _(
2059 b'use --force-close-branch to close branch from a non-head'
2059 b'use --force-close-branch to close branch from a non-head'
2060 b' changeset'
2060 b' changeset'
2061 )
2061 )
2062 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2062 raise error.Abort(_(b'can only close branch heads'), hint=hint)
2063 elif opts.get(b'amend'):
2063 elif opts.get(b'amend'):
2064 if (
2064 if (
2065 repo[b'.'].p1().branch() != branch
2065 repo[b'.'].p1().branch() != branch
2066 and repo[b'.'].p2().branch() != branch
2066 and repo[b'.'].p2().branch() != branch
2067 ):
2067 ):
2068 raise error.Abort(_(b'can only close branch heads'))
2068 raise error.Abort(_(b'can only close branch heads'))
2069
2069
2070 if opts.get(b'amend'):
2070 if opts.get(b'amend'):
2071 if ui.configbool(b'ui', b'commitsubrepos'):
2071 if ui.configbool(b'ui', b'commitsubrepos'):
2072 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2072 raise error.Abort(_(b'cannot amend with ui.commitsubrepos enabled'))
2073
2073
2074 old = repo[b'.']
2074 old = repo[b'.']
2075 rewriteutil.precheck(repo, [old.rev()], b'amend')
2075 rewriteutil.precheck(repo, [old.rev()], b'amend')
2076
2076
2077 # Currently histedit gets confused if an amend happens while histedit
2077 # Currently histedit gets confused if an amend happens while histedit
2078 # is in progress. Since we have a checkunfinished command, we are
2078 # is in progress. Since we have a checkunfinished command, we are
2079 # temporarily honoring it.
2079 # temporarily honoring it.
2080 #
2080 #
2081 # Note: eventually this guard will be removed. Please do not expect
2081 # Note: eventually this guard will be removed. Please do not expect
2082 # this behavior to remain.
2082 # this behavior to remain.
2083 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2083 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2084 cmdutil.checkunfinished(repo)
2084 cmdutil.checkunfinished(repo)
2085
2085
2086 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2086 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2087 if node == old.node():
2087 if node == old.node():
2088 ui.status(_(b"nothing changed\n"))
2088 ui.status(_(b"nothing changed\n"))
2089 return 1
2089 return 1
2090 else:
2090 else:
2091
2091
2092 def commitfunc(ui, repo, message, match, opts):
2092 def commitfunc(ui, repo, message, match, opts):
2093 overrides = {}
2093 overrides = {}
2094 if opts.get(b'secret'):
2094 if opts.get(b'secret'):
2095 overrides[(b'phases', b'new-commit')] = b'secret'
2095 overrides[(b'phases', b'new-commit')] = b'secret'
2096
2096
2097 baseui = repo.baseui
2097 baseui = repo.baseui
2098 with baseui.configoverride(overrides, b'commit'):
2098 with baseui.configoverride(overrides, b'commit'):
2099 with ui.configoverride(overrides, b'commit'):
2099 with ui.configoverride(overrides, b'commit'):
2100 editform = cmdutil.mergeeditform(
2100 editform = cmdutil.mergeeditform(
2101 repo[None], b'commit.normal'
2101 repo[None], b'commit.normal'
2102 )
2102 )
2103 editor = cmdutil.getcommiteditor(
2103 editor = cmdutil.getcommiteditor(
2104 editform=editform, **pycompat.strkwargs(opts)
2104 editform=editform, **pycompat.strkwargs(opts)
2105 )
2105 )
2106 return repo.commit(
2106 return repo.commit(
2107 message,
2107 message,
2108 opts.get(b'user'),
2108 opts.get(b'user'),
2109 opts.get(b'date'),
2109 opts.get(b'date'),
2110 match,
2110 match,
2111 editor=editor,
2111 editor=editor,
2112 extra=extra,
2112 extra=extra,
2113 )
2113 )
2114
2114
2115 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2115 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2116
2116
2117 if not node:
2117 if not node:
2118 stat = cmdutil.postcommitstatus(repo, pats, opts)
2118 stat = cmdutil.postcommitstatus(repo, pats, opts)
2119 if stat[3]:
2119 if stat[3]:
2120 ui.status(
2120 ui.status(
2121 _(
2121 _(
2122 b"nothing changed (%d missing files, see "
2122 b"nothing changed (%d missing files, see "
2123 b"'hg status')\n"
2123 b"'hg status')\n"
2124 )
2124 )
2125 % len(stat[3])
2125 % len(stat[3])
2126 )
2126 )
2127 else:
2127 else:
2128 ui.status(_(b"nothing changed\n"))
2128 ui.status(_(b"nothing changed\n"))
2129 return 1
2129 return 1
2130
2130
2131 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2131 cmdutil.commitstatus(repo, node, branch, bheads, opts)
2132
2132
2133 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2133 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2134 status(
2134 status(
2135 ui,
2135 ui,
2136 repo,
2136 repo,
2137 modified=True,
2137 modified=True,
2138 added=True,
2138 added=True,
2139 removed=True,
2139 removed=True,
2140 deleted=True,
2140 deleted=True,
2141 unknown=True,
2141 unknown=True,
2142 subrepos=opts.get(b'subrepos'),
2142 subrepos=opts.get(b'subrepos'),
2143 )
2143 )
2144
2144
2145
2145
2146 @command(
2146 @command(
2147 b'config|showconfig|debugconfig',
2147 b'config|showconfig|debugconfig',
2148 [
2148 [
2149 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2149 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2150 (b'e', b'edit', None, _(b'edit user config')),
2150 (b'e', b'edit', None, _(b'edit user config')),
2151 (b'l', b'local', None, _(b'edit repository config')),
2151 (b'l', b'local', None, _(b'edit repository config')),
2152 (b'g', b'global', None, _(b'edit global config')),
2152 (b'g', b'global', None, _(b'edit global config')),
2153 ]
2153 ]
2154 + formatteropts,
2154 + formatteropts,
2155 _(b'[-u] [NAME]...'),
2155 _(b'[-u] [NAME]...'),
2156 helpcategory=command.CATEGORY_HELP,
2156 helpcategory=command.CATEGORY_HELP,
2157 optionalrepo=True,
2157 optionalrepo=True,
2158 intents={INTENT_READONLY},
2158 intents={INTENT_READONLY},
2159 )
2159 )
2160 def config(ui, repo, *values, **opts):
2160 def config(ui, repo, *values, **opts):
2161 """show combined config settings from all hgrc files
2161 """show combined config settings from all hgrc files
2162
2162
2163 With no arguments, print names and values of all config items.
2163 With no arguments, print names and values of all config items.
2164
2164
2165 With one argument of the form section.name, print just the value
2165 With one argument of the form section.name, print just the value
2166 of that config item.
2166 of that config item.
2167
2167
2168 With multiple arguments, print names and values of all config
2168 With multiple arguments, print names and values of all config
2169 items with matching section names or section.names.
2169 items with matching section names or section.names.
2170
2170
2171 With --edit, start an editor on the user-level config file. With
2171 With --edit, start an editor on the user-level config file. With
2172 --global, edit the system-wide config file. With --local, edit the
2172 --global, edit the system-wide config file. With --local, edit the
2173 repository-level config file.
2173 repository-level config file.
2174
2174
2175 With --debug, the source (filename and line number) is printed
2175 With --debug, the source (filename and line number) is printed
2176 for each config item.
2176 for each config item.
2177
2177
2178 See :hg:`help config` for more information about config files.
2178 See :hg:`help config` for more information about config files.
2179
2179
2180 .. container:: verbose
2180 .. container:: verbose
2181
2181
2182 Template:
2182 Template:
2183
2183
2184 The following keywords are supported. See also :hg:`help templates`.
2184 The following keywords are supported. See also :hg:`help templates`.
2185
2185
2186 :name: String. Config name.
2186 :name: String. Config name.
2187 :source: String. Filename and line number where the item is defined.
2187 :source: String. Filename and line number where the item is defined.
2188 :value: String. Config value.
2188 :value: String. Config value.
2189
2189
2190 Returns 0 on success, 1 if NAME does not exist.
2190 Returns 0 on success, 1 if NAME does not exist.
2191
2191
2192 """
2192 """
2193
2193
2194 opts = pycompat.byteskwargs(opts)
2194 opts = pycompat.byteskwargs(opts)
2195 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2195 if opts.get(b'edit') or opts.get(b'local') or opts.get(b'global'):
2196 if opts.get(b'local') and opts.get(b'global'):
2196 if opts.get(b'local') and opts.get(b'global'):
2197 raise error.Abort(_(b"can't use --local and --global together"))
2197 raise error.Abort(_(b"can't use --local and --global together"))
2198
2198
2199 if opts.get(b'local'):
2199 if opts.get(b'local'):
2200 if not repo:
2200 if not repo:
2201 raise error.Abort(_(b"can't use --local outside a repository"))
2201 raise error.Abort(_(b"can't use --local outside a repository"))
2202 paths = [repo.vfs.join(b'hgrc')]
2202 paths = [repo.vfs.join(b'hgrc')]
2203 elif opts.get(b'global'):
2203 elif opts.get(b'global'):
2204 paths = rcutil.systemrcpath()
2204 paths = rcutil.systemrcpath()
2205 else:
2205 else:
2206 paths = rcutil.userrcpath()
2206 paths = rcutil.userrcpath()
2207
2207
2208 for f in paths:
2208 for f in paths:
2209 if os.path.exists(f):
2209 if os.path.exists(f):
2210 break
2210 break
2211 else:
2211 else:
2212 if opts.get(b'global'):
2212 if opts.get(b'global'):
2213 samplehgrc = uimod.samplehgrcs[b'global']
2213 samplehgrc = uimod.samplehgrcs[b'global']
2214 elif opts.get(b'local'):
2214 elif opts.get(b'local'):
2215 samplehgrc = uimod.samplehgrcs[b'local']
2215 samplehgrc = uimod.samplehgrcs[b'local']
2216 else:
2216 else:
2217 samplehgrc = uimod.samplehgrcs[b'user']
2217 samplehgrc = uimod.samplehgrcs[b'user']
2218
2218
2219 f = paths[0]
2219 f = paths[0]
2220 fp = open(f, b"wb")
2220 fp = open(f, b"wb")
2221 fp.write(util.tonativeeol(samplehgrc))
2221 fp.write(util.tonativeeol(samplehgrc))
2222 fp.close()
2222 fp.close()
2223
2223
2224 editor = ui.geteditor()
2224 editor = ui.geteditor()
2225 ui.system(
2225 ui.system(
2226 b"%s \"%s\"" % (editor, f),
2226 b"%s \"%s\"" % (editor, f),
2227 onerr=error.Abort,
2227 onerr=error.Abort,
2228 errprefix=_(b"edit failed"),
2228 errprefix=_(b"edit failed"),
2229 blockedtag=b'config_edit',
2229 blockedtag=b'config_edit',
2230 )
2230 )
2231 return
2231 return
2232 ui.pager(b'config')
2232 ui.pager(b'config')
2233 fm = ui.formatter(b'config', opts)
2233 fm = ui.formatter(b'config', opts)
2234 for t, f in rcutil.rccomponents():
2234 for t, f in rcutil.rccomponents():
2235 if t == b'path':
2235 if t == b'path':
2236 ui.debug(b'read config from: %s\n' % f)
2236 ui.debug(b'read config from: %s\n' % f)
2237 elif t == b'items':
2237 elif t == b'items':
2238 for section, name, value, source in f:
2238 for section, name, value, source in f:
2239 ui.debug(b'set config by: %s\n' % source)
2239 ui.debug(b'set config by: %s\n' % source)
2240 else:
2240 else:
2241 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2241 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2242 untrusted = bool(opts.get(b'untrusted'))
2242 untrusted = bool(opts.get(b'untrusted'))
2243
2243
2244 selsections = selentries = []
2244 selsections = selentries = []
2245 if values:
2245 if values:
2246 selsections = [v for v in values if b'.' not in v]
2246 selsections = [v for v in values if b'.' not in v]
2247 selentries = [v for v in values if b'.' in v]
2247 selentries = [v for v in values if b'.' in v]
2248 uniquesel = len(selentries) == 1 and not selsections
2248 uniquesel = len(selentries) == 1 and not selsections
2249 selsections = set(selsections)
2249 selsections = set(selsections)
2250 selentries = set(selentries)
2250 selentries = set(selentries)
2251
2251
2252 matched = False
2252 matched = False
2253 for section, name, value in ui.walkconfig(untrusted=untrusted):
2253 for section, name, value in ui.walkconfig(untrusted=untrusted):
2254 source = ui.configsource(section, name, untrusted)
2254 source = ui.configsource(section, name, untrusted)
2255 value = pycompat.bytestr(value)
2255 value = pycompat.bytestr(value)
2256 defaultvalue = ui.configdefault(section, name)
2256 defaultvalue = ui.configdefault(section, name)
2257 if fm.isplain():
2257 if fm.isplain():
2258 source = source or b'none'
2258 source = source or b'none'
2259 value = value.replace(b'\n', b'\\n')
2259 value = value.replace(b'\n', b'\\n')
2260 entryname = section + b'.' + name
2260 entryname = section + b'.' + name
2261 if values and not (section in selsections or entryname in selentries):
2261 if values and not (section in selsections or entryname in selentries):
2262 continue
2262 continue
2263 fm.startitem()
2263 fm.startitem()
2264 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2264 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2265 if uniquesel:
2265 if uniquesel:
2266 fm.data(name=entryname)
2266 fm.data(name=entryname)
2267 fm.write(b'value', b'%s\n', value)
2267 fm.write(b'value', b'%s\n', value)
2268 else:
2268 else:
2269 fm.write(b'name value', b'%s=%s\n', entryname, value)
2269 fm.write(b'name value', b'%s=%s\n', entryname, value)
2270 fm.data(defaultvalue=defaultvalue)
2270 fm.data(defaultvalue=defaultvalue)
2271 matched = True
2271 matched = True
2272 fm.end()
2272 fm.end()
2273 if matched:
2273 if matched:
2274 return 0
2274 return 0
2275 return 1
2275 return 1
2276
2276
2277
2277
2278 @command(
2278 @command(
2279 b'continue',
2279 b'continue',
2280 dryrunopts,
2280 dryrunopts,
2281 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2281 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2282 helpbasic=True,
2282 helpbasic=True,
2283 )
2283 )
2284 def continuecmd(ui, repo, **opts):
2284 def continuecmd(ui, repo, **opts):
2285 """resumes an interrupted operation (EXPERIMENTAL)
2285 """resumes an interrupted operation (EXPERIMENTAL)
2286
2286
2287 Finishes a multistep operation like graft, histedit, rebase, merge,
2287 Finishes a multistep operation like graft, histedit, rebase, merge,
2288 and unshelve if they are in an interrupted state.
2288 and unshelve if they are in an interrupted state.
2289
2289
2290 use --dry-run/-n to dry run the command.
2290 use --dry-run/-n to dry run the command.
2291 """
2291 """
2292 dryrun = opts.get(r'dry_run')
2292 dryrun = opts.get(r'dry_run')
2293 contstate = cmdutil.getunfinishedstate(repo)
2293 contstate = cmdutil.getunfinishedstate(repo)
2294 if not contstate:
2294 if not contstate:
2295 raise error.Abort(_(b'no operation in progress'))
2295 raise error.Abort(_(b'no operation in progress'))
2296 if not contstate.continuefunc:
2296 if not contstate.continuefunc:
2297 raise error.Abort(
2297 raise error.Abort(
2298 (
2298 (
2299 _(b"%s in progress but does not support 'hg continue'")
2299 _(b"%s in progress but does not support 'hg continue'")
2300 % (contstate._opname)
2300 % (contstate._opname)
2301 ),
2301 ),
2302 hint=contstate.continuemsg(),
2302 hint=contstate.continuemsg(),
2303 )
2303 )
2304 if dryrun:
2304 if dryrun:
2305 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2305 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2306 return
2306 return
2307 return contstate.continuefunc(ui, repo)
2307 return contstate.continuefunc(ui, repo)
2308
2308
2309
2309
2310 @command(
2310 @command(
2311 b'copy|cp',
2311 b'copy|cp',
2312 [
2312 [
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2313 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2314 (
2314 (
2315 b'f',
2315 b'f',
2316 b'force',
2316 b'force',
2317 None,
2317 None,
2318 _(b'forcibly copy over an existing managed file'),
2318 _(b'forcibly copy over an existing managed file'),
2319 ),
2319 ),
2320 ]
2320 ]
2321 + walkopts
2321 + walkopts
2322 + dryrunopts,
2322 + dryrunopts,
2323 _(b'[OPTION]... SOURCE... DEST'),
2323 _(b'[OPTION]... SOURCE... DEST'),
2324 helpcategory=command.CATEGORY_FILE_CONTENTS,
2324 helpcategory=command.CATEGORY_FILE_CONTENTS,
2325 )
2325 )
2326 def copy(ui, repo, *pats, **opts):
2326 def copy(ui, repo, *pats, **opts):
2327 """mark files as copied for the next commit
2327 """mark files as copied for the next commit
2328
2328
2329 Mark dest as having copies of source files. If dest is a
2329 Mark dest as having copies of source files. If dest is a
2330 directory, copies are put in that directory. If dest is a file,
2330 directory, copies are put in that directory. If dest is a file,
2331 the source must be a single file.
2331 the source must be a single file.
2332
2332
2333 By default, this command copies the contents of files as they
2333 By default, this command copies the contents of files as they
2334 exist in the working directory. If invoked with -A/--after, the
2334 exist in the working directory. If invoked with -A/--after, the
2335 operation is recorded, but no copying is performed.
2335 operation is recorded, but no copying is performed.
2336
2336
2337 This command takes effect with the next commit. To undo a copy
2337 This command takes effect with the next commit. To undo a copy
2338 before that, see :hg:`revert`.
2338 before that, see :hg:`revert`.
2339
2339
2340 Returns 0 on success, 1 if errors are encountered.
2340 Returns 0 on success, 1 if errors are encountered.
2341 """
2341 """
2342 opts = pycompat.byteskwargs(opts)
2342 opts = pycompat.byteskwargs(opts)
2343 with repo.wlock(False):
2343 with repo.wlock(False):
2344 return cmdutil.copy(ui, repo, pats, opts)
2344 return cmdutil.copy(ui, repo, pats, opts)
2345
2345
2346
2346
2347 @command(
2347 @command(
2348 b'debugcommands',
2348 b'debugcommands',
2349 [],
2349 [],
2350 _(b'[COMMAND]'),
2350 _(b'[COMMAND]'),
2351 helpcategory=command.CATEGORY_HELP,
2351 helpcategory=command.CATEGORY_HELP,
2352 norepo=True,
2352 norepo=True,
2353 )
2353 )
2354 def debugcommands(ui, cmd=b'', *args):
2354 def debugcommands(ui, cmd=b'', *args):
2355 """list all available commands and options"""
2355 """list all available commands and options"""
2356 for cmd, vals in sorted(pycompat.iteritems(table)):
2356 for cmd, vals in sorted(pycompat.iteritems(table)):
2357 cmd = cmd.split(b'|')[0]
2357 cmd = cmd.split(b'|')[0]
2358 opts = b', '.join([i[1] for i in vals[1]])
2358 opts = b', '.join([i[1] for i in vals[1]])
2359 ui.write(b'%s: %s\n' % (cmd, opts))
2359 ui.write(b'%s: %s\n' % (cmd, opts))
2360
2360
2361
2361
2362 @command(
2362 @command(
2363 b'debugcomplete',
2363 b'debugcomplete',
2364 [(b'o', b'options', None, _(b'show the command options'))],
2364 [(b'o', b'options', None, _(b'show the command options'))],
2365 _(b'[-o] CMD'),
2365 _(b'[-o] CMD'),
2366 helpcategory=command.CATEGORY_HELP,
2366 helpcategory=command.CATEGORY_HELP,
2367 norepo=True,
2367 norepo=True,
2368 )
2368 )
2369 def debugcomplete(ui, cmd=b'', **opts):
2369 def debugcomplete(ui, cmd=b'', **opts):
2370 """returns the completion list associated with the given command"""
2370 """returns the completion list associated with the given command"""
2371
2371
2372 if opts.get(r'options'):
2372 if opts.get(r'options'):
2373 options = []
2373 options = []
2374 otables = [globalopts]
2374 otables = [globalopts]
2375 if cmd:
2375 if cmd:
2376 aliases, entry = cmdutil.findcmd(cmd, table, False)
2376 aliases, entry = cmdutil.findcmd(cmd, table, False)
2377 otables.append(entry[1])
2377 otables.append(entry[1])
2378 for t in otables:
2378 for t in otables:
2379 for o in t:
2379 for o in t:
2380 if b"(DEPRECATED)" in o[3]:
2380 if b"(DEPRECATED)" in o[3]:
2381 continue
2381 continue
2382 if o[0]:
2382 if o[0]:
2383 options.append(b'-%s' % o[0])
2383 options.append(b'-%s' % o[0])
2384 options.append(b'--%s' % o[1])
2384 options.append(b'--%s' % o[1])
2385 ui.write(b"%s\n" % b"\n".join(options))
2385 ui.write(b"%s\n" % b"\n".join(options))
2386 return
2386 return
2387
2387
2388 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2388 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2389 if ui.verbose:
2389 if ui.verbose:
2390 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2390 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2391 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2391 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2392
2392
2393
2393
2394 @command(
2394 @command(
2395 b'diff',
2395 b'diff',
2396 [
2396 [
2397 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2397 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2398 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2398 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2399 ]
2399 ]
2400 + diffopts
2400 + diffopts
2401 + diffopts2
2401 + diffopts2
2402 + walkopts
2402 + walkopts
2403 + subrepoopts,
2403 + subrepoopts,
2404 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2404 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2405 helpcategory=command.CATEGORY_FILE_CONTENTS,
2405 helpcategory=command.CATEGORY_FILE_CONTENTS,
2406 helpbasic=True,
2406 helpbasic=True,
2407 inferrepo=True,
2407 inferrepo=True,
2408 intents={INTENT_READONLY},
2408 intents={INTENT_READONLY},
2409 )
2409 )
2410 def diff(ui, repo, *pats, **opts):
2410 def diff(ui, repo, *pats, **opts):
2411 """diff repository (or selected files)
2411 """diff repository (or selected files)
2412
2412
2413 Show differences between revisions for the specified files.
2413 Show differences between revisions for the specified files.
2414
2414
2415 Differences between files are shown using the unified diff format.
2415 Differences between files are shown using the unified diff format.
2416
2416
2417 .. note::
2417 .. note::
2418
2418
2419 :hg:`diff` may generate unexpected results for merges, as it will
2419 :hg:`diff` may generate unexpected results for merges, as it will
2420 default to comparing against the working directory's first
2420 default to comparing against the working directory's first
2421 parent changeset if no revisions are specified.
2421 parent changeset if no revisions are specified.
2422
2422
2423 When two revision arguments are given, then changes are shown
2423 When two revision arguments are given, then changes are shown
2424 between those revisions. If only one revision is specified then
2424 between those revisions. If only one revision is specified then
2425 that revision is compared to the working directory, and, when no
2425 that revision is compared to the working directory, and, when no
2426 revisions are specified, the working directory files are compared
2426 revisions are specified, the working directory files are compared
2427 to its first parent.
2427 to its first parent.
2428
2428
2429 Alternatively you can specify -c/--change with a revision to see
2429 Alternatively you can specify -c/--change with a revision to see
2430 the changes in that changeset relative to its first parent.
2430 the changes in that changeset relative to its first parent.
2431
2431
2432 Without the -a/--text option, diff will avoid generating diffs of
2432 Without the -a/--text option, diff will avoid generating diffs of
2433 files it detects as binary. With -a, diff will generate a diff
2433 files it detects as binary. With -a, diff will generate a diff
2434 anyway, probably with undesirable results.
2434 anyway, probably with undesirable results.
2435
2435
2436 Use the -g/--git option to generate diffs in the git extended diff
2436 Use the -g/--git option to generate diffs in the git extended diff
2437 format. For more information, read :hg:`help diffs`.
2437 format. For more information, read :hg:`help diffs`.
2438
2438
2439 .. container:: verbose
2439 .. container:: verbose
2440
2440
2441 Examples:
2441 Examples:
2442
2442
2443 - compare a file in the current working directory to its parent::
2443 - compare a file in the current working directory to its parent::
2444
2444
2445 hg diff foo.c
2445 hg diff foo.c
2446
2446
2447 - compare two historical versions of a directory, with rename info::
2447 - compare two historical versions of a directory, with rename info::
2448
2448
2449 hg diff --git -r 1.0:1.2 lib/
2449 hg diff --git -r 1.0:1.2 lib/
2450
2450
2451 - get change stats relative to the last change on some date::
2451 - get change stats relative to the last change on some date::
2452
2452
2453 hg diff --stat -r "date('may 2')"
2453 hg diff --stat -r "date('may 2')"
2454
2454
2455 - diff all newly-added files that contain a keyword::
2455 - diff all newly-added files that contain a keyword::
2456
2456
2457 hg diff "set:added() and grep(GNU)"
2457 hg diff "set:added() and grep(GNU)"
2458
2458
2459 - compare a revision and its parents::
2459 - compare a revision and its parents::
2460
2460
2461 hg diff -c 9353 # compare against first parent
2461 hg diff -c 9353 # compare against first parent
2462 hg diff -r 9353^:9353 # same using revset syntax
2462 hg diff -r 9353^:9353 # same using revset syntax
2463 hg diff -r 9353^2:9353 # compare against the second parent
2463 hg diff -r 9353^2:9353 # compare against the second parent
2464
2464
2465 Returns 0 on success.
2465 Returns 0 on success.
2466 """
2466 """
2467
2467
2468 opts = pycompat.byteskwargs(opts)
2468 opts = pycompat.byteskwargs(opts)
2469 revs = opts.get(b'rev')
2469 revs = opts.get(b'rev')
2470 change = opts.get(b'change')
2470 change = opts.get(b'change')
2471 stat = opts.get(b'stat')
2471 stat = opts.get(b'stat')
2472 reverse = opts.get(b'reverse')
2472 reverse = opts.get(b'reverse')
2473
2473
2474 if revs and change:
2474 if revs and change:
2475 msg = _(b'cannot specify --rev and --change at the same time')
2475 msg = _(b'cannot specify --rev and --change at the same time')
2476 raise error.Abort(msg)
2476 raise error.Abort(msg)
2477 elif change:
2477 elif change:
2478 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2478 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2479 ctx2 = scmutil.revsingle(repo, change, None)
2479 ctx2 = scmutil.revsingle(repo, change, None)
2480 ctx1 = ctx2.p1()
2480 ctx1 = ctx2.p1()
2481 else:
2481 else:
2482 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2482 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2483 ctx1, ctx2 = scmutil.revpair(repo, revs)
2483 ctx1, ctx2 = scmutil.revpair(repo, revs)
2484 node1, node2 = ctx1.node(), ctx2.node()
2484 node1, node2 = ctx1.node(), ctx2.node()
2485
2485
2486 if reverse:
2486 if reverse:
2487 node1, node2 = node2, node1
2487 node1, node2 = node2, node1
2488
2488
2489 diffopts = patch.diffallopts(ui, opts)
2489 diffopts = patch.diffallopts(ui, opts)
2490 m = scmutil.match(ctx2, pats, opts)
2490 m = scmutil.match(ctx2, pats, opts)
2491 m = repo.narrowmatch(m)
2491 m = repo.narrowmatch(m)
2492 ui.pager(b'diff')
2492 ui.pager(b'diff')
2493 logcmdutil.diffordiffstat(
2493 logcmdutil.diffordiffstat(
2494 ui,
2494 ui,
2495 repo,
2495 repo,
2496 diffopts,
2496 diffopts,
2497 node1,
2497 node1,
2498 node2,
2498 node2,
2499 m,
2499 m,
2500 stat=stat,
2500 stat=stat,
2501 listsubrepos=opts.get(b'subrepos'),
2501 listsubrepos=opts.get(b'subrepos'),
2502 root=opts.get(b'root'),
2502 root=opts.get(b'root'),
2503 )
2503 )
2504
2504
2505
2505
2506 @command(
2506 @command(
2507 b'export',
2507 b'export',
2508 [
2508 [
2509 (
2509 (
2510 b'B',
2510 b'B',
2511 b'bookmark',
2511 b'bookmark',
2512 b'',
2512 b'',
2513 _(b'export changes only reachable by given bookmark'),
2513 _(b'export changes only reachable by given bookmark'),
2514 _(b'BOOKMARK'),
2514 _(b'BOOKMARK'),
2515 ),
2515 ),
2516 (
2516 (
2517 b'o',
2517 b'o',
2518 b'output',
2518 b'output',
2519 b'',
2519 b'',
2520 _(b'print output to file with formatted name'),
2520 _(b'print output to file with formatted name'),
2521 _(b'FORMAT'),
2521 _(b'FORMAT'),
2522 ),
2522 ),
2523 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2523 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2524 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2524 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2525 ]
2525 ]
2526 + diffopts
2526 + diffopts
2527 + formatteropts,
2527 + formatteropts,
2528 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2528 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2529 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2529 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2530 helpbasic=True,
2530 helpbasic=True,
2531 intents={INTENT_READONLY},
2531 intents={INTENT_READONLY},
2532 )
2532 )
2533 def export(ui, repo, *changesets, **opts):
2533 def export(ui, repo, *changesets, **opts):
2534 """dump the header and diffs for one or more changesets
2534 """dump the header and diffs for one or more changesets
2535
2535
2536 Print the changeset header and diffs for one or more revisions.
2536 Print the changeset header and diffs for one or more revisions.
2537 If no revision is given, the parent of the working directory is used.
2537 If no revision is given, the parent of the working directory is used.
2538
2538
2539 The information shown in the changeset header is: author, date,
2539 The information shown in the changeset header is: author, date,
2540 branch name (if non-default), changeset hash, parent(s) and commit
2540 branch name (if non-default), changeset hash, parent(s) and commit
2541 comment.
2541 comment.
2542
2542
2543 .. note::
2543 .. note::
2544
2544
2545 :hg:`export` may generate unexpected diff output for merge
2545 :hg:`export` may generate unexpected diff output for merge
2546 changesets, as it will compare the merge changeset against its
2546 changesets, as it will compare the merge changeset against its
2547 first parent only.
2547 first parent only.
2548
2548
2549 Output may be to a file, in which case the name of the file is
2549 Output may be to a file, in which case the name of the file is
2550 given using a template string. See :hg:`help templates`. In addition
2550 given using a template string. See :hg:`help templates`. In addition
2551 to the common template keywords, the following formatting rules are
2551 to the common template keywords, the following formatting rules are
2552 supported:
2552 supported:
2553
2553
2554 :``%%``: literal "%" character
2554 :``%%``: literal "%" character
2555 :``%H``: changeset hash (40 hexadecimal digits)
2555 :``%H``: changeset hash (40 hexadecimal digits)
2556 :``%N``: number of patches being generated
2556 :``%N``: number of patches being generated
2557 :``%R``: changeset revision number
2557 :``%R``: changeset revision number
2558 :``%b``: basename of the exporting repository
2558 :``%b``: basename of the exporting repository
2559 :``%h``: short-form changeset hash (12 hexadecimal digits)
2559 :``%h``: short-form changeset hash (12 hexadecimal digits)
2560 :``%m``: first line of the commit message (only alphanumeric characters)
2560 :``%m``: first line of the commit message (only alphanumeric characters)
2561 :``%n``: zero-padded sequence number, starting at 1
2561 :``%n``: zero-padded sequence number, starting at 1
2562 :``%r``: zero-padded changeset revision number
2562 :``%r``: zero-padded changeset revision number
2563 :``\\``: literal "\\" character
2563 :``\\``: literal "\\" character
2564
2564
2565 Without the -a/--text option, export will avoid generating diffs
2565 Without the -a/--text option, export will avoid generating diffs
2566 of files it detects as binary. With -a, export will generate a
2566 of files it detects as binary. With -a, export will generate a
2567 diff anyway, probably with undesirable results.
2567 diff anyway, probably with undesirable results.
2568
2568
2569 With -B/--bookmark changesets reachable by the given bookmark are
2569 With -B/--bookmark changesets reachable by the given bookmark are
2570 selected.
2570 selected.
2571
2571
2572 Use the -g/--git option to generate diffs in the git extended diff
2572 Use the -g/--git option to generate diffs in the git extended diff
2573 format. See :hg:`help diffs` for more information.
2573 format. See :hg:`help diffs` for more information.
2574
2574
2575 With the --switch-parent option, the diff will be against the
2575 With the --switch-parent option, the diff will be against the
2576 second parent. It can be useful to review a merge.
2576 second parent. It can be useful to review a merge.
2577
2577
2578 .. container:: verbose
2578 .. container:: verbose
2579
2579
2580 Template:
2580 Template:
2581
2581
2582 The following keywords are supported in addition to the common template
2582 The following keywords are supported in addition to the common template
2583 keywords and functions. See also :hg:`help templates`.
2583 keywords and functions. See also :hg:`help templates`.
2584
2584
2585 :diff: String. Diff content.
2585 :diff: String. Diff content.
2586 :parents: List of strings. Parent nodes of the changeset.
2586 :parents: List of strings. Parent nodes of the changeset.
2587
2587
2588 Examples:
2588 Examples:
2589
2589
2590 - use export and import to transplant a bugfix to the current
2590 - use export and import to transplant a bugfix to the current
2591 branch::
2591 branch::
2592
2592
2593 hg export -r 9353 | hg import -
2593 hg export -r 9353 | hg import -
2594
2594
2595 - export all the changesets between two revisions to a file with
2595 - export all the changesets between two revisions to a file with
2596 rename information::
2596 rename information::
2597
2597
2598 hg export --git -r 123:150 > changes.txt
2598 hg export --git -r 123:150 > changes.txt
2599
2599
2600 - split outgoing changes into a series of patches with
2600 - split outgoing changes into a series of patches with
2601 descriptive names::
2601 descriptive names::
2602
2602
2603 hg export -r "outgoing()" -o "%n-%m.patch"
2603 hg export -r "outgoing()" -o "%n-%m.patch"
2604
2604
2605 Returns 0 on success.
2605 Returns 0 on success.
2606 """
2606 """
2607 opts = pycompat.byteskwargs(opts)
2607 opts = pycompat.byteskwargs(opts)
2608 bookmark = opts.get(b'bookmark')
2608 bookmark = opts.get(b'bookmark')
2609 changesets += tuple(opts.get(b'rev', []))
2609 changesets += tuple(opts.get(b'rev', []))
2610
2610
2611 if bookmark and changesets:
2611 if bookmark and changesets:
2612 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2612 raise error.Abort(_(b"-r and -B are mutually exclusive"))
2613
2613
2614 if bookmark:
2614 if bookmark:
2615 if bookmark not in repo._bookmarks:
2615 if bookmark not in repo._bookmarks:
2616 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2616 raise error.Abort(_(b"bookmark '%s' not found") % bookmark)
2617
2617
2618 revs = scmutil.bookmarkrevs(repo, bookmark)
2618 revs = scmutil.bookmarkrevs(repo, bookmark)
2619 else:
2619 else:
2620 if not changesets:
2620 if not changesets:
2621 changesets = [b'.']
2621 changesets = [b'.']
2622
2622
2623 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2623 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2624 revs = scmutil.revrange(repo, changesets)
2624 revs = scmutil.revrange(repo, changesets)
2625
2625
2626 if not revs:
2626 if not revs:
2627 raise error.Abort(_(b"export requires at least one changeset"))
2627 raise error.Abort(_(b"export requires at least one changeset"))
2628 if len(revs) > 1:
2628 if len(revs) > 1:
2629 ui.note(_(b'exporting patches:\n'))
2629 ui.note(_(b'exporting patches:\n'))
2630 else:
2630 else:
2631 ui.note(_(b'exporting patch:\n'))
2631 ui.note(_(b'exporting patch:\n'))
2632
2632
2633 fntemplate = opts.get(b'output')
2633 fntemplate = opts.get(b'output')
2634 if cmdutil.isstdiofilename(fntemplate):
2634 if cmdutil.isstdiofilename(fntemplate):
2635 fntemplate = b''
2635 fntemplate = b''
2636
2636
2637 if fntemplate:
2637 if fntemplate:
2638 fm = formatter.nullformatter(ui, b'export', opts)
2638 fm = formatter.nullformatter(ui, b'export', opts)
2639 else:
2639 else:
2640 ui.pager(b'export')
2640 ui.pager(b'export')
2641 fm = ui.formatter(b'export', opts)
2641 fm = ui.formatter(b'export', opts)
2642 with fm:
2642 with fm:
2643 cmdutil.export(
2643 cmdutil.export(
2644 repo,
2644 repo,
2645 revs,
2645 revs,
2646 fm,
2646 fm,
2647 fntemplate=fntemplate,
2647 fntemplate=fntemplate,
2648 switch_parent=opts.get(b'switch_parent'),
2648 switch_parent=opts.get(b'switch_parent'),
2649 opts=patch.diffallopts(ui, opts),
2649 opts=patch.diffallopts(ui, opts),
2650 )
2650 )
2651
2651
2652
2652
2653 @command(
2653 @command(
2654 b'files',
2654 b'files',
2655 [
2655 [
2656 (
2656 (
2657 b'r',
2657 b'r',
2658 b'rev',
2658 b'rev',
2659 b'',
2659 b'',
2660 _(b'search the repository as it is in REV'),
2660 _(b'search the repository as it is in REV'),
2661 _(b'REV'),
2661 _(b'REV'),
2662 ),
2662 ),
2663 (
2663 (
2664 b'0',
2664 b'0',
2665 b'print0',
2665 b'print0',
2666 None,
2666 None,
2667 _(b'end filenames with NUL, for use with xargs'),
2667 _(b'end filenames with NUL, for use with xargs'),
2668 ),
2668 ),
2669 ]
2669 ]
2670 + walkopts
2670 + walkopts
2671 + formatteropts
2671 + formatteropts
2672 + subrepoopts,
2672 + subrepoopts,
2673 _(b'[OPTION]... [FILE]...'),
2673 _(b'[OPTION]... [FILE]...'),
2674 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2674 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2675 intents={INTENT_READONLY},
2675 intents={INTENT_READONLY},
2676 )
2676 )
2677 def files(ui, repo, *pats, **opts):
2677 def files(ui, repo, *pats, **opts):
2678 """list tracked files
2678 """list tracked files
2679
2679
2680 Print files under Mercurial control in the working directory or
2680 Print files under Mercurial control in the working directory or
2681 specified revision for given files (excluding removed files).
2681 specified revision for given files (excluding removed files).
2682 Files can be specified as filenames or filesets.
2682 Files can be specified as filenames or filesets.
2683
2683
2684 If no files are given to match, this command prints the names
2684 If no files are given to match, this command prints the names
2685 of all files under Mercurial control.
2685 of all files under Mercurial control.
2686
2686
2687 .. container:: verbose
2687 .. container:: verbose
2688
2688
2689 Template:
2689 Template:
2690
2690
2691 The following keywords are supported in addition to the common template
2691 The following keywords are supported in addition to the common template
2692 keywords and functions. See also :hg:`help templates`.
2692 keywords and functions. See also :hg:`help templates`.
2693
2693
2694 :flags: String. Character denoting file's symlink and executable bits.
2694 :flags: String. Character denoting file's symlink and executable bits.
2695 :path: String. Repository-absolute path of the file.
2695 :path: String. Repository-absolute path of the file.
2696 :size: Integer. Size of the file in bytes.
2696 :size: Integer. Size of the file in bytes.
2697
2697
2698 Examples:
2698 Examples:
2699
2699
2700 - list all files under the current directory::
2700 - list all files under the current directory::
2701
2701
2702 hg files .
2702 hg files .
2703
2703
2704 - shows sizes and flags for current revision::
2704 - shows sizes and flags for current revision::
2705
2705
2706 hg files -vr .
2706 hg files -vr .
2707
2707
2708 - list all files named README::
2708 - list all files named README::
2709
2709
2710 hg files -I "**/README"
2710 hg files -I "**/README"
2711
2711
2712 - list all binary files::
2712 - list all binary files::
2713
2713
2714 hg files "set:binary()"
2714 hg files "set:binary()"
2715
2715
2716 - find files containing a regular expression::
2716 - find files containing a regular expression::
2717
2717
2718 hg files "set:grep('bob')"
2718 hg files "set:grep('bob')"
2719
2719
2720 - search tracked file contents with xargs and grep::
2720 - search tracked file contents with xargs and grep::
2721
2721
2722 hg files -0 | xargs -0 grep foo
2722 hg files -0 | xargs -0 grep foo
2723
2723
2724 See :hg:`help patterns` and :hg:`help filesets` for more information
2724 See :hg:`help patterns` and :hg:`help filesets` for more information
2725 on specifying file patterns.
2725 on specifying file patterns.
2726
2726
2727 Returns 0 if a match is found, 1 otherwise.
2727 Returns 0 if a match is found, 1 otherwise.
2728
2728
2729 """
2729 """
2730
2730
2731 opts = pycompat.byteskwargs(opts)
2731 opts = pycompat.byteskwargs(opts)
2732 rev = opts.get(b'rev')
2732 rev = opts.get(b'rev')
2733 if rev:
2733 if rev:
2734 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2734 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2735 ctx = scmutil.revsingle(repo, rev, None)
2735 ctx = scmutil.revsingle(repo, rev, None)
2736
2736
2737 end = b'\n'
2737 end = b'\n'
2738 if opts.get(b'print0'):
2738 if opts.get(b'print0'):
2739 end = b'\0'
2739 end = b'\0'
2740 fmt = b'%s' + end
2740 fmt = b'%s' + end
2741
2741
2742 m = scmutil.match(ctx, pats, opts)
2742 m = scmutil.match(ctx, pats, opts)
2743 ui.pager(b'files')
2743 ui.pager(b'files')
2744 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2744 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2745 with ui.formatter(b'files', opts) as fm:
2745 with ui.formatter(b'files', opts) as fm:
2746 return cmdutil.files(
2746 return cmdutil.files(
2747 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2747 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2748 )
2748 )
2749
2749
2750
2750
2751 @command(
2751 @command(
2752 b'forget',
2752 b'forget',
2753 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2753 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2754 + walkopts
2754 + walkopts
2755 + dryrunopts,
2755 + dryrunopts,
2756 _(b'[OPTION]... FILE...'),
2756 _(b'[OPTION]... FILE...'),
2757 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2757 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2758 helpbasic=True,
2758 helpbasic=True,
2759 inferrepo=True,
2759 inferrepo=True,
2760 )
2760 )
2761 def forget(ui, repo, *pats, **opts):
2761 def forget(ui, repo, *pats, **opts):
2762 """forget the specified files on the next commit
2762 """forget the specified files on the next commit
2763
2763
2764 Mark the specified files so they will no longer be tracked
2764 Mark the specified files so they will no longer be tracked
2765 after the next commit.
2765 after the next commit.
2766
2766
2767 This only removes files from the current branch, not from the
2767 This only removes files from the current branch, not from the
2768 entire project history, and it does not delete them from the
2768 entire project history, and it does not delete them from the
2769 working directory.
2769 working directory.
2770
2770
2771 To delete the file from the working directory, see :hg:`remove`.
2771 To delete the file from the working directory, see :hg:`remove`.
2772
2772
2773 To undo a forget before the next commit, see :hg:`add`.
2773 To undo a forget before the next commit, see :hg:`add`.
2774
2774
2775 .. container:: verbose
2775 .. container:: verbose
2776
2776
2777 Examples:
2777 Examples:
2778
2778
2779 - forget newly-added binary files::
2779 - forget newly-added binary files::
2780
2780
2781 hg forget "set:added() and binary()"
2781 hg forget "set:added() and binary()"
2782
2782
2783 - forget files that would be excluded by .hgignore::
2783 - forget files that would be excluded by .hgignore::
2784
2784
2785 hg forget "set:hgignore()"
2785 hg forget "set:hgignore()"
2786
2786
2787 Returns 0 on success.
2787 Returns 0 on success.
2788 """
2788 """
2789
2789
2790 opts = pycompat.byteskwargs(opts)
2790 opts = pycompat.byteskwargs(opts)
2791 if not pats:
2791 if not pats:
2792 raise error.Abort(_(b'no files specified'))
2792 raise error.Abort(_(b'no files specified'))
2793
2793
2794 m = scmutil.match(repo[None], pats, opts)
2794 m = scmutil.match(repo[None], pats, opts)
2795 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2795 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2796 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2796 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2797 rejected = cmdutil.forget(
2797 rejected = cmdutil.forget(
2798 ui,
2798 ui,
2799 repo,
2799 repo,
2800 m,
2800 m,
2801 prefix=b"",
2801 prefix=b"",
2802 uipathfn=uipathfn,
2802 uipathfn=uipathfn,
2803 explicitonly=False,
2803 explicitonly=False,
2804 dryrun=dryrun,
2804 dryrun=dryrun,
2805 interactive=interactive,
2805 interactive=interactive,
2806 )[0]
2806 )[0]
2807 return rejected and 1 or 0
2807 return rejected and 1 or 0
2808
2808
2809
2809
2810 @command(
2810 @command(
2811 b'graft',
2811 b'graft',
2812 [
2812 [
2813 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2813 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2814 (
2814 (
2815 b'',
2815 b'',
2816 b'base',
2816 b'base',
2817 b'',
2817 b'',
2818 _(b'base revision when doing the graft merge (ADVANCED)'),
2818 _(b'base revision when doing the graft merge (ADVANCED)'),
2819 _(b'REV'),
2819 _(b'REV'),
2820 ),
2820 ),
2821 (b'c', b'continue', False, _(b'resume interrupted graft')),
2821 (b'c', b'continue', False, _(b'resume interrupted graft')),
2822 (b'', b'stop', False, _(b'stop interrupted graft')),
2822 (b'', b'stop', False, _(b'stop interrupted graft')),
2823 (b'', b'abort', False, _(b'abort interrupted graft')),
2823 (b'', b'abort', False, _(b'abort interrupted graft')),
2824 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2824 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2825 (b'', b'log', None, _(b'append graft info to log message')),
2825 (b'', b'log', None, _(b'append graft info to log message')),
2826 (
2826 (
2827 b'',
2827 b'',
2828 b'no-commit',
2828 b'no-commit',
2829 None,
2829 None,
2830 _(b"don't commit, just apply the changes in working directory"),
2830 _(b"don't commit, just apply the changes in working directory"),
2831 ),
2831 ),
2832 (b'f', b'force', False, _(b'force graft')),
2832 (b'f', b'force', False, _(b'force graft')),
2833 (
2833 (
2834 b'D',
2834 b'D',
2835 b'currentdate',
2835 b'currentdate',
2836 False,
2836 False,
2837 _(b'record the current date as commit date'),
2837 _(b'record the current date as commit date'),
2838 ),
2838 ),
2839 (
2839 (
2840 b'U',
2840 b'U',
2841 b'currentuser',
2841 b'currentuser',
2842 False,
2842 False,
2843 _(b'record the current user as committer'),
2843 _(b'record the current user as committer'),
2844 ),
2844 ),
2845 ]
2845 ]
2846 + commitopts2
2846 + commitopts2
2847 + mergetoolopts
2847 + mergetoolopts
2848 + dryrunopts,
2848 + dryrunopts,
2849 _(b'[OPTION]... [-r REV]... REV...'),
2849 _(b'[OPTION]... [-r REV]... REV...'),
2850 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2850 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2851 )
2851 )
2852 def graft(ui, repo, *revs, **opts):
2852 def graft(ui, repo, *revs, **opts):
2853 '''copy changes from other branches onto the current branch
2853 '''copy changes from other branches onto the current branch
2854
2854
2855 This command uses Mercurial's merge logic to copy individual
2855 This command uses Mercurial's merge logic to copy individual
2856 changes from other branches without merging branches in the
2856 changes from other branches without merging branches in the
2857 history graph. This is sometimes known as 'backporting' or
2857 history graph. This is sometimes known as 'backporting' or
2858 'cherry-picking'. By default, graft will copy user, date, and
2858 'cherry-picking'. By default, graft will copy user, date, and
2859 description from the source changesets.
2859 description from the source changesets.
2860
2860
2861 Changesets that are ancestors of the current revision, that have
2861 Changesets that are ancestors of the current revision, that have
2862 already been grafted, or that are merges will be skipped.
2862 already been grafted, or that are merges will be skipped.
2863
2863
2864 If --log is specified, log messages will have a comment appended
2864 If --log is specified, log messages will have a comment appended
2865 of the form::
2865 of the form::
2866
2866
2867 (grafted from CHANGESETHASH)
2867 (grafted from CHANGESETHASH)
2868
2868
2869 If --force is specified, revisions will be grafted even if they
2869 If --force is specified, revisions will be grafted even if they
2870 are already ancestors of, or have been grafted to, the destination.
2870 are already ancestors of, or have been grafted to, the destination.
2871 This is useful when the revisions have since been backed out.
2871 This is useful when the revisions have since been backed out.
2872
2872
2873 If a graft merge results in conflicts, the graft process is
2873 If a graft merge results in conflicts, the graft process is
2874 interrupted so that the current merge can be manually resolved.
2874 interrupted so that the current merge can be manually resolved.
2875 Once all conflicts are addressed, the graft process can be
2875 Once all conflicts are addressed, the graft process can be
2876 continued with the -c/--continue option.
2876 continued with the -c/--continue option.
2877
2877
2878 The -c/--continue option reapplies all the earlier options.
2878 The -c/--continue option reapplies all the earlier options.
2879
2879
2880 .. container:: verbose
2880 .. container:: verbose
2881
2881
2882 The --base option exposes more of how graft internally uses merge with a
2882 The --base option exposes more of how graft internally uses merge with a
2883 custom base revision. --base can be used to specify another ancestor than
2883 custom base revision. --base can be used to specify another ancestor than
2884 the first and only parent.
2884 the first and only parent.
2885
2885
2886 The command::
2886 The command::
2887
2887
2888 hg graft -r 345 --base 234
2888 hg graft -r 345 --base 234
2889
2889
2890 is thus pretty much the same as::
2890 is thus pretty much the same as::
2891
2891
2892 hg diff -r 234 -r 345 | hg import
2892 hg diff -r 234 -r 345 | hg import
2893
2893
2894 but using merge to resolve conflicts and track moved files.
2894 but using merge to resolve conflicts and track moved files.
2895
2895
2896 The result of a merge can thus be backported as a single commit by
2896 The result of a merge can thus be backported as a single commit by
2897 specifying one of the merge parents as base, and thus effectively
2897 specifying one of the merge parents as base, and thus effectively
2898 grafting the changes from the other side.
2898 grafting the changes from the other side.
2899
2899
2900 It is also possible to collapse multiple changesets and clean up history
2900 It is also possible to collapse multiple changesets and clean up history
2901 by specifying another ancestor as base, much like rebase --collapse
2901 by specifying another ancestor as base, much like rebase --collapse
2902 --keep.
2902 --keep.
2903
2903
2904 The commit message can be tweaked after the fact using commit --amend .
2904 The commit message can be tweaked after the fact using commit --amend .
2905
2905
2906 For using non-ancestors as the base to backout changes, see the backout
2906 For using non-ancestors as the base to backout changes, see the backout
2907 command and the hidden --parent option.
2907 command and the hidden --parent option.
2908
2908
2909 .. container:: verbose
2909 .. container:: verbose
2910
2910
2911 Examples:
2911 Examples:
2912
2912
2913 - copy a single change to the stable branch and edit its description::
2913 - copy a single change to the stable branch and edit its description::
2914
2914
2915 hg update stable
2915 hg update stable
2916 hg graft --edit 9393
2916 hg graft --edit 9393
2917
2917
2918 - graft a range of changesets with one exception, updating dates::
2918 - graft a range of changesets with one exception, updating dates::
2919
2919
2920 hg graft -D "2085::2093 and not 2091"
2920 hg graft -D "2085::2093 and not 2091"
2921
2921
2922 - continue a graft after resolving conflicts::
2922 - continue a graft after resolving conflicts::
2923
2923
2924 hg graft -c
2924 hg graft -c
2925
2925
2926 - show the source of a grafted changeset::
2926 - show the source of a grafted changeset::
2927
2927
2928 hg log --debug -r .
2928 hg log --debug -r .
2929
2929
2930 - show revisions sorted by date::
2930 - show revisions sorted by date::
2931
2931
2932 hg log -r "sort(all(), date)"
2932 hg log -r "sort(all(), date)"
2933
2933
2934 - backport the result of a merge as a single commit::
2934 - backport the result of a merge as a single commit::
2935
2935
2936 hg graft -r 123 --base 123^
2936 hg graft -r 123 --base 123^
2937
2937
2938 - land a feature branch as one changeset::
2938 - land a feature branch as one changeset::
2939
2939
2940 hg up -cr default
2940 hg up -cr default
2941 hg graft -r featureX --base "ancestor('featureX', 'default')"
2941 hg graft -r featureX --base "ancestor('featureX', 'default')"
2942
2942
2943 See :hg:`help revisions` for more about specifying revisions.
2943 See :hg:`help revisions` for more about specifying revisions.
2944
2944
2945 Returns 0 on successful completion.
2945 Returns 0 on successful completion.
2946 '''
2946 '''
2947 with repo.wlock():
2947 with repo.wlock():
2948 return _dograft(ui, repo, *revs, **opts)
2948 return _dograft(ui, repo, *revs, **opts)
2949
2949
2950
2950
2951 def _dograft(ui, repo, *revs, **opts):
2951 def _dograft(ui, repo, *revs, **opts):
2952 opts = pycompat.byteskwargs(opts)
2952 opts = pycompat.byteskwargs(opts)
2953 if revs and opts.get(b'rev'):
2953 if revs and opts.get(b'rev'):
2954 ui.warn(
2954 ui.warn(
2955 _(
2955 _(
2956 b'warning: inconsistent use of --rev might give unexpected '
2956 b'warning: inconsistent use of --rev might give unexpected '
2957 b'revision ordering!\n'
2957 b'revision ordering!\n'
2958 )
2958 )
2959 )
2959 )
2960
2960
2961 revs = list(revs)
2961 revs = list(revs)
2962 revs.extend(opts.get(b'rev'))
2962 revs.extend(opts.get(b'rev'))
2963 basectx = None
2963 basectx = None
2964 if opts.get(b'base'):
2964 if opts.get(b'base'):
2965 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2965 basectx = scmutil.revsingle(repo, opts[b'base'], None)
2966 # a dict of data to be stored in state file
2966 # a dict of data to be stored in state file
2967 statedata = {}
2967 statedata = {}
2968 # list of new nodes created by ongoing graft
2968 # list of new nodes created by ongoing graft
2969 statedata[b'newnodes'] = []
2969 statedata[b'newnodes'] = []
2970
2970
2971 if opts.get(b'user') and opts.get(b'currentuser'):
2971 if opts.get(b'user') and opts.get(b'currentuser'):
2972 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2972 raise error.Abort(_(b'--user and --currentuser are mutually exclusive'))
2973 if opts.get(b'date') and opts.get(b'currentdate'):
2973 if opts.get(b'date') and opts.get(b'currentdate'):
2974 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2974 raise error.Abort(_(b'--date and --currentdate are mutually exclusive'))
2975 if not opts.get(b'user') and opts.get(b'currentuser'):
2975 if not opts.get(b'user') and opts.get(b'currentuser'):
2976 opts[b'user'] = ui.username()
2976 opts[b'user'] = ui.username()
2977 if not opts.get(b'date') and opts.get(b'currentdate'):
2977 if not opts.get(b'date') and opts.get(b'currentdate'):
2978 opts[b'date'] = b"%d %d" % dateutil.makedate()
2978 opts[b'date'] = b"%d %d" % dateutil.makedate()
2979
2979
2980 editor = cmdutil.getcommiteditor(
2980 editor = cmdutil.getcommiteditor(
2981 editform=b'graft', **pycompat.strkwargs(opts)
2981 editform=b'graft', **pycompat.strkwargs(opts)
2982 )
2982 )
2983
2983
2984 cont = False
2984 cont = False
2985 if opts.get(b'no_commit'):
2985 if opts.get(b'no_commit'):
2986 if opts.get(b'edit'):
2986 if opts.get(b'edit'):
2987 raise error.Abort(
2987 raise error.Abort(
2988 _(b"cannot specify --no-commit and --edit together")
2988 _(b"cannot specify --no-commit and --edit together")
2989 )
2989 )
2990 if opts.get(b'currentuser'):
2990 if opts.get(b'currentuser'):
2991 raise error.Abort(
2991 raise error.Abort(
2992 _(b"cannot specify --no-commit and --currentuser together")
2992 _(b"cannot specify --no-commit and --currentuser together")
2993 )
2993 )
2994 if opts.get(b'currentdate'):
2994 if opts.get(b'currentdate'):
2995 raise error.Abort(
2995 raise error.Abort(
2996 _(b"cannot specify --no-commit and --currentdate together")
2996 _(b"cannot specify --no-commit and --currentdate together")
2997 )
2997 )
2998 if opts.get(b'log'):
2998 if opts.get(b'log'):
2999 raise error.Abort(
2999 raise error.Abort(
3000 _(b"cannot specify --no-commit and --log together")
3000 _(b"cannot specify --no-commit and --log together")
3001 )
3001 )
3002
3002
3003 graftstate = statemod.cmdstate(repo, b'graftstate')
3003 graftstate = statemod.cmdstate(repo, b'graftstate')
3004
3004
3005 if opts.get(b'stop'):
3005 if opts.get(b'stop'):
3006 if opts.get(b'continue'):
3006 if opts.get(b'continue'):
3007 raise error.Abort(
3007 raise error.Abort(
3008 _(b"cannot use '--continue' and '--stop' together")
3008 _(b"cannot use '--continue' and '--stop' together")
3009 )
3009 )
3010 if opts.get(b'abort'):
3010 if opts.get(b'abort'):
3011 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3011 raise error.Abort(_(b"cannot use '--abort' and '--stop' together"))
3012
3012
3013 if any(
3013 if any(
3014 (
3014 (
3015 opts.get(b'edit'),
3015 opts.get(b'edit'),
3016 opts.get(b'log'),
3016 opts.get(b'log'),
3017 opts.get(b'user'),
3017 opts.get(b'user'),
3018 opts.get(b'date'),
3018 opts.get(b'date'),
3019 opts.get(b'currentdate'),
3019 opts.get(b'currentdate'),
3020 opts.get(b'currentuser'),
3020 opts.get(b'currentuser'),
3021 opts.get(b'rev'),
3021 opts.get(b'rev'),
3022 )
3022 )
3023 ):
3023 ):
3024 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3024 raise error.Abort(_(b"cannot specify any other flag with '--stop'"))
3025 return _stopgraft(ui, repo, graftstate)
3025 return _stopgraft(ui, repo, graftstate)
3026 elif opts.get(b'abort'):
3026 elif opts.get(b'abort'):
3027 if opts.get(b'continue'):
3027 if opts.get(b'continue'):
3028 raise error.Abort(
3028 raise error.Abort(
3029 _(b"cannot use '--continue' and '--abort' together")
3029 _(b"cannot use '--continue' and '--abort' together")
3030 )
3030 )
3031 if any(
3031 if any(
3032 (
3032 (
3033 opts.get(b'edit'),
3033 opts.get(b'edit'),
3034 opts.get(b'log'),
3034 opts.get(b'log'),
3035 opts.get(b'user'),
3035 opts.get(b'user'),
3036 opts.get(b'date'),
3036 opts.get(b'date'),
3037 opts.get(b'currentdate'),
3037 opts.get(b'currentdate'),
3038 opts.get(b'currentuser'),
3038 opts.get(b'currentuser'),
3039 opts.get(b'rev'),
3039 opts.get(b'rev'),
3040 )
3040 )
3041 ):
3041 ):
3042 raise error.Abort(
3042 raise error.Abort(
3043 _(b"cannot specify any other flag with '--abort'")
3043 _(b"cannot specify any other flag with '--abort'")
3044 )
3044 )
3045
3045
3046 return cmdutil.abortgraft(ui, repo, graftstate)
3046 return cmdutil.abortgraft(ui, repo, graftstate)
3047 elif opts.get(b'continue'):
3047 elif opts.get(b'continue'):
3048 cont = True
3048 cont = True
3049 if revs:
3049 if revs:
3050 raise error.Abort(_(b"can't specify --continue and revisions"))
3050 raise error.Abort(_(b"can't specify --continue and revisions"))
3051 # read in unfinished revisions
3051 # read in unfinished revisions
3052 if graftstate.exists():
3052 if graftstate.exists():
3053 statedata = cmdutil.readgraftstate(repo, graftstate)
3053 statedata = cmdutil.readgraftstate(repo, graftstate)
3054 if statedata.get(b'date'):
3054 if statedata.get(b'date'):
3055 opts[b'date'] = statedata[b'date']
3055 opts[b'date'] = statedata[b'date']
3056 if statedata.get(b'user'):
3056 if statedata.get(b'user'):
3057 opts[b'user'] = statedata[b'user']
3057 opts[b'user'] = statedata[b'user']
3058 if statedata.get(b'log'):
3058 if statedata.get(b'log'):
3059 opts[b'log'] = True
3059 opts[b'log'] = True
3060 if statedata.get(b'no_commit'):
3060 if statedata.get(b'no_commit'):
3061 opts[b'no_commit'] = statedata.get(b'no_commit')
3061 opts[b'no_commit'] = statedata.get(b'no_commit')
3062 nodes = statedata[b'nodes']
3062 nodes = statedata[b'nodes']
3063 revs = [repo[node].rev() for node in nodes]
3063 revs = [repo[node].rev() for node in nodes]
3064 else:
3064 else:
3065 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3065 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3066 else:
3066 else:
3067 if not revs:
3067 if not revs:
3068 raise error.Abort(_(b'no revisions specified'))
3068 raise error.Abort(_(b'no revisions specified'))
3069 cmdutil.checkunfinished(repo)
3069 cmdutil.checkunfinished(repo)
3070 cmdutil.bailifchanged(repo)
3070 cmdutil.bailifchanged(repo)
3071 revs = scmutil.revrange(repo, revs)
3071 revs = scmutil.revrange(repo, revs)
3072
3072
3073 skipped = set()
3073 skipped = set()
3074 if basectx is None:
3074 if basectx is None:
3075 # check for merges
3075 # check for merges
3076 for rev in repo.revs(b'%ld and merge()', revs):
3076 for rev in repo.revs(b'%ld and merge()', revs):
3077 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3077 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3078 skipped.add(rev)
3078 skipped.add(rev)
3079 revs = [r for r in revs if r not in skipped]
3079 revs = [r for r in revs if r not in skipped]
3080 if not revs:
3080 if not revs:
3081 return -1
3081 return -1
3082 if basectx is not None and len(revs) != 1:
3082 if basectx is not None and len(revs) != 1:
3083 raise error.Abort(_(b'only one revision allowed with --base '))
3083 raise error.Abort(_(b'only one revision allowed with --base '))
3084
3084
3085 # Don't check in the --continue case, in effect retaining --force across
3085 # Don't check in the --continue case, in effect retaining --force across
3086 # --continues. That's because without --force, any revisions we decided to
3086 # --continues. That's because without --force, any revisions we decided to
3087 # skip would have been filtered out here, so they wouldn't have made their
3087 # skip would have been filtered out here, so they wouldn't have made their
3088 # way to the graftstate. With --force, any revisions we would have otherwise
3088 # way to the graftstate. With --force, any revisions we would have otherwise
3089 # skipped would not have been filtered out, and if they hadn't been applied
3089 # skipped would not have been filtered out, and if they hadn't been applied
3090 # already, they'd have been in the graftstate.
3090 # already, they'd have been in the graftstate.
3091 if not (cont or opts.get(b'force')) and basectx is None:
3091 if not (cont or opts.get(b'force')) and basectx is None:
3092 # check for ancestors of dest branch
3092 # check for ancestors of dest branch
3093 crev = repo[b'.'].rev()
3093 crev = repo[b'.'].rev()
3094 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3094 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3095 # XXX make this lazy in the future
3095 # XXX make this lazy in the future
3096 # don't mutate while iterating, create a copy
3096 # don't mutate while iterating, create a copy
3097 for rev in list(revs):
3097 for rev in list(revs):
3098 if rev in ancestors:
3098 if rev in ancestors:
3099 ui.warn(
3099 ui.warn(
3100 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3100 _(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev])
3101 )
3101 )
3102 # XXX remove on list is slow
3102 # XXX remove on list is slow
3103 revs.remove(rev)
3103 revs.remove(rev)
3104 if not revs:
3104 if not revs:
3105 return -1
3105 return -1
3106
3106
3107 # analyze revs for earlier grafts
3107 # analyze revs for earlier grafts
3108 ids = {}
3108 ids = {}
3109 for ctx in repo.set(b"%ld", revs):
3109 for ctx in repo.set(b"%ld", revs):
3110 ids[ctx.hex()] = ctx.rev()
3110 ids[ctx.hex()] = ctx.rev()
3111 n = ctx.extra().get(b'source')
3111 n = ctx.extra().get(b'source')
3112 if n:
3112 if n:
3113 ids[n] = ctx.rev()
3113 ids[n] = ctx.rev()
3114
3114
3115 # check ancestors for earlier grafts
3115 # check ancestors for earlier grafts
3116 ui.debug(b'scanning for duplicate grafts\n')
3116 ui.debug(b'scanning for duplicate grafts\n')
3117
3117
3118 # The only changesets we can be sure doesn't contain grafts of any
3118 # The only changesets we can be sure doesn't contain grafts of any
3119 # revs, are the ones that are common ancestors of *all* revs:
3119 # revs, are the ones that are common ancestors of *all* revs:
3120 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3120 for rev in repo.revs(b'only(%d,ancestor(%ld))', crev, revs):
3121 ctx = repo[rev]
3121 ctx = repo[rev]
3122 n = ctx.extra().get(b'source')
3122 n = ctx.extra().get(b'source')
3123 if n in ids:
3123 if n in ids:
3124 try:
3124 try:
3125 r = repo[n].rev()
3125 r = repo[n].rev()
3126 except error.RepoLookupError:
3126 except error.RepoLookupError:
3127 r = None
3127 r = None
3128 if r in revs:
3128 if r in revs:
3129 ui.warn(
3129 ui.warn(
3130 _(
3130 _(
3131 b'skipping revision %d:%s '
3131 b'skipping revision %d:%s '
3132 b'(already grafted to %d:%s)\n'
3132 b'(already grafted to %d:%s)\n'
3133 )
3133 )
3134 % (r, repo[r], rev, ctx)
3134 % (r, repo[r], rev, ctx)
3135 )
3135 )
3136 revs.remove(r)
3136 revs.remove(r)
3137 elif ids[n] in revs:
3137 elif ids[n] in revs:
3138 if r is None:
3138 if r is None:
3139 ui.warn(
3139 ui.warn(
3140 _(
3140 _(
3141 b'skipping already grafted revision %d:%s '
3141 b'skipping already grafted revision %d:%s '
3142 b'(%d:%s also has unknown origin %s)\n'
3142 b'(%d:%s also has unknown origin %s)\n'
3143 )
3143 )
3144 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3144 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3145 )
3145 )
3146 else:
3146 else:
3147 ui.warn(
3147 ui.warn(
3148 _(
3148 _(
3149 b'skipping already grafted revision %d:%s '
3149 b'skipping already grafted revision %d:%s '
3150 b'(%d:%s also has origin %d:%s)\n'
3150 b'(%d:%s also has origin %d:%s)\n'
3151 )
3151 )
3152 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3152 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3153 )
3153 )
3154 revs.remove(ids[n])
3154 revs.remove(ids[n])
3155 elif ctx.hex() in ids:
3155 elif ctx.hex() in ids:
3156 r = ids[ctx.hex()]
3156 r = ids[ctx.hex()]
3157 if r in revs:
3157 if r in revs:
3158 ui.warn(
3158 ui.warn(
3159 _(
3159 _(
3160 b'skipping already grafted revision %d:%s '
3160 b'skipping already grafted revision %d:%s '
3161 b'(was grafted from %d:%s)\n'
3161 b'(was grafted from %d:%s)\n'
3162 )
3162 )
3163 % (r, repo[r], rev, ctx)
3163 % (r, repo[r], rev, ctx)
3164 )
3164 )
3165 revs.remove(r)
3165 revs.remove(r)
3166 if not revs:
3166 if not revs:
3167 return -1
3167 return -1
3168
3168
3169 if opts.get(b'no_commit'):
3169 if opts.get(b'no_commit'):
3170 statedata[b'no_commit'] = True
3170 statedata[b'no_commit'] = True
3171 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3171 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3172 desc = b'%d:%s "%s"' % (
3172 desc = b'%d:%s "%s"' % (
3173 ctx.rev(),
3173 ctx.rev(),
3174 ctx,
3174 ctx,
3175 ctx.description().split(b'\n', 1)[0],
3175 ctx.description().split(b'\n', 1)[0],
3176 )
3176 )
3177 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3177 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3178 if names:
3178 if names:
3179 desc += b' (%s)' % b' '.join(names)
3179 desc += b' (%s)' % b' '.join(names)
3180 ui.status(_(b'grafting %s\n') % desc)
3180 ui.status(_(b'grafting %s\n') % desc)
3181 if opts.get(b'dry_run'):
3181 if opts.get(b'dry_run'):
3182 continue
3182 continue
3183
3183
3184 source = ctx.extra().get(b'source')
3184 source = ctx.extra().get(b'source')
3185 extra = {}
3185 extra = {}
3186 if source:
3186 if source:
3187 extra[b'source'] = source
3187 extra[b'source'] = source
3188 extra[b'intermediate-source'] = ctx.hex()
3188 extra[b'intermediate-source'] = ctx.hex()
3189 else:
3189 else:
3190 extra[b'source'] = ctx.hex()
3190 extra[b'source'] = ctx.hex()
3191 user = ctx.user()
3191 user = ctx.user()
3192 if opts.get(b'user'):
3192 if opts.get(b'user'):
3193 user = opts[b'user']
3193 user = opts[b'user']
3194 statedata[b'user'] = user
3194 statedata[b'user'] = user
3195 date = ctx.date()
3195 date = ctx.date()
3196 if opts.get(b'date'):
3196 if opts.get(b'date'):
3197 date = opts[b'date']
3197 date = opts[b'date']
3198 statedata[b'date'] = date
3198 statedata[b'date'] = date
3199 message = ctx.description()
3199 message = ctx.description()
3200 if opts.get(b'log'):
3200 if opts.get(b'log'):
3201 message += b'\n(grafted from %s)' % ctx.hex()
3201 message += b'\n(grafted from %s)' % ctx.hex()
3202 statedata[b'log'] = True
3202 statedata[b'log'] = True
3203
3203
3204 # we don't merge the first commit when continuing
3204 # we don't merge the first commit when continuing
3205 if not cont:
3205 if not cont:
3206 # perform the graft merge with p1(rev) as 'ancestor'
3206 # perform the graft merge with p1(rev) as 'ancestor'
3207 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3207 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3208 base = ctx.p1() if basectx is None else basectx
3208 base = ctx.p1() if basectx is None else basectx
3209 with ui.configoverride(overrides, b'graft'):
3209 with ui.configoverride(overrides, b'graft'):
3210 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3210 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3211 # report any conflicts
3211 # report any conflicts
3212 if stats.unresolvedcount > 0:
3212 if stats.unresolvedcount > 0:
3213 # write out state for --continue
3213 # write out state for --continue
3214 nodes = [repo[rev].hex() for rev in revs[pos:]]
3214 nodes = [repo[rev].hex() for rev in revs[pos:]]
3215 statedata[b'nodes'] = nodes
3215 statedata[b'nodes'] = nodes
3216 stateversion = 1
3216 stateversion = 1
3217 graftstate.save(stateversion, statedata)
3217 graftstate.save(stateversion, statedata)
3218 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3218 hint = _(b"use 'hg resolve' and 'hg graft --continue'")
3219 raise error.Abort(
3219 raise error.Abort(
3220 _(b"unresolved conflicts, can't continue"), hint=hint
3220 _(b"unresolved conflicts, can't continue"), hint=hint
3221 )
3221 )
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 hg.updaterepo(repo, pctx.node(), overwrite=True)
3251 hg.updaterepo(repo, pctx.node(), overwrite=True)
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'print all revisions that match (DEPRECATED) ')),
3273 (b'', b'all', None, _(b'print all revisions that match (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 = opts.get(b'all_files') and not opts.get(b'rev')
3378 plaingrep = opts.get(b'all_files') and not opts.get(b'rev')
3379 all_files = opts.get(b'all_files')
3379 all_files = opts.get(b'all_files')
3380 if plaingrep:
3380 if plaingrep:
3381 opts[b'rev'] = [b'wdir()']
3381 opts[b'rev'] = [b'wdir()']
3382
3382
3383 reflags = re.M
3383 reflags = re.M
3384 if opts.get(b'ignore_case'):
3384 if opts.get(b'ignore_case'):
3385 reflags |= re.I
3385 reflags |= re.I
3386 try:
3386 try:
3387 regexp = util.re.compile(pattern, reflags)
3387 regexp = util.re.compile(pattern, reflags)
3388 except re.error as inst:
3388 except re.error as inst:
3389 ui.warn(
3389 ui.warn(
3390 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3390 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3391 )
3391 )
3392 return 1
3392 return 1
3393 sep, eol = b':', b'\n'
3393 sep, eol = b':', b'\n'
3394 if opts.get(b'print0'):
3394 if opts.get(b'print0'):
3395 sep = eol = b'\0'
3395 sep = eol = b'\0'
3396
3396
3397 getfile = util.lrucachefunc(repo.file)
3397 getfile = util.lrucachefunc(repo.file)
3398
3398
3399 def matchlines(body):
3399 def matchlines(body):
3400 begin = 0
3400 begin = 0
3401 linenum = 0
3401 linenum = 0
3402 while begin < len(body):
3402 while begin < len(body):
3403 match = regexp.search(body, begin)
3403 match = regexp.search(body, begin)
3404 if not match:
3404 if not match:
3405 break
3405 break
3406 mstart, mend = match.span()
3406 mstart, mend = match.span()
3407 linenum += body.count(b'\n', begin, mstart) + 1
3407 linenum += body.count(b'\n', begin, mstart) + 1
3408 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3408 lstart = body.rfind(b'\n', begin, mstart) + 1 or begin
3409 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3409 begin = body.find(b'\n', mend) + 1 or len(body) + 1
3410 lend = begin - 1
3410 lend = begin - 1
3411 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3411 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3412
3412
3413 class linestate(object):
3413 class linestate(object):
3414 def __init__(self, line, linenum, colstart, colend):
3414 def __init__(self, line, linenum, colstart, colend):
3415 self.line = line
3415 self.line = line
3416 self.linenum = linenum
3416 self.linenum = linenum
3417 self.colstart = colstart
3417 self.colstart = colstart
3418 self.colend = colend
3418 self.colend = colend
3419
3419
3420 def __hash__(self):
3420 def __hash__(self):
3421 return hash((self.linenum, self.line))
3421 return hash((self.linenum, self.line))
3422
3422
3423 def __eq__(self, other):
3423 def __eq__(self, other):
3424 return self.line == other.line
3424 return self.line == other.line
3425
3425
3426 def findpos(self):
3426 def findpos(self):
3427 """Iterate all (start, end) indices of matches"""
3427 """Iterate all (start, end) indices of matches"""
3428 yield self.colstart, self.colend
3428 yield self.colstart, self.colend
3429 p = self.colend
3429 p = self.colend
3430 while p < len(self.line):
3430 while p < len(self.line):
3431 m = regexp.search(self.line, p)
3431 m = regexp.search(self.line, p)
3432 if not m:
3432 if not m:
3433 break
3433 break
3434 yield m.span()
3434 yield m.span()
3435 p = m.end()
3435 p = m.end()
3436
3436
3437 matches = {}
3437 matches = {}
3438 copies = {}
3438 copies = {}
3439
3439
3440 def grepbody(fn, rev, body):
3440 def grepbody(fn, rev, body):
3441 matches[rev].setdefault(fn, [])
3441 matches[rev].setdefault(fn, [])
3442 m = matches[rev][fn]
3442 m = matches[rev][fn]
3443 if body is None:
3444 return
3445
3443 for lnum, cstart, cend, line in matchlines(body):
3446 for lnum, cstart, cend, line in matchlines(body):
3444 s = linestate(line, lnum, cstart, cend)
3447 s = linestate(line, lnum, cstart, cend)
3445 m.append(s)
3448 m.append(s)
3446
3449
3447 def difflinestates(a, b):
3450 def difflinestates(a, b):
3448 sm = difflib.SequenceMatcher(None, a, b)
3451 sm = difflib.SequenceMatcher(None, a, b)
3449 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3452 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3450 if tag == r'insert':
3453 if tag == r'insert':
3451 for i in pycompat.xrange(blo, bhi):
3454 for i in pycompat.xrange(blo, bhi):
3452 yield (b'+', b[i])
3455 yield (b'+', b[i])
3453 elif tag == r'delete':
3456 elif tag == r'delete':
3454 for i in pycompat.xrange(alo, ahi):
3457 for i in pycompat.xrange(alo, ahi):
3455 yield (b'-', a[i])
3458 yield (b'-', a[i])
3456 elif tag == r'replace':
3459 elif tag == r'replace':
3457 for i in pycompat.xrange(alo, ahi):
3460 for i in pycompat.xrange(alo, ahi):
3458 yield (b'-', a[i])
3461 yield (b'-', a[i])
3459 for i in pycompat.xrange(blo, bhi):
3462 for i in pycompat.xrange(blo, bhi):
3460 yield (b'+', b[i])
3463 yield (b'+', b[i])
3461
3464
3462 uipathfn = scmutil.getuipathfn(repo)
3465 uipathfn = scmutil.getuipathfn(repo)
3463
3466
3464 def display(fm, fn, ctx, pstates, states):
3467 def display(fm, fn, ctx, pstates, states):
3465 rev = scmutil.intrev(ctx)
3468 rev = scmutil.intrev(ctx)
3466 if fm.isplain():
3469 if fm.isplain():
3467 formatuser = ui.shortuser
3470 formatuser = ui.shortuser
3468 else:
3471 else:
3469 formatuser = pycompat.bytestr
3472 formatuser = pycompat.bytestr
3470 if ui.quiet:
3473 if ui.quiet:
3471 datefmt = b'%Y-%m-%d'
3474 datefmt = b'%Y-%m-%d'
3472 else:
3475 else:
3473 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3476 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3474 found = False
3477 found = False
3475
3478
3476 @util.cachefunc
3479 @util.cachefunc
3477 def binary():
3480 def binary():
3478 flog = getfile(fn)
3481 flog = getfile(fn)
3479 try:
3482 try:
3480 return stringutil.binary(flog.read(ctx.filenode(fn)))
3483 return stringutil.binary(flog.read(ctx.filenode(fn)))
3481 except error.WdirUnsupported:
3484 except error.WdirUnsupported:
3482 return ctx[fn].isbinary()
3485 return ctx[fn].isbinary()
3483
3486
3484 fieldnamemap = {b'linenumber': b'lineno'}
3487 fieldnamemap = {b'linenumber': b'lineno'}
3485 if diff:
3488 if diff:
3486 iter = difflinestates(pstates, states)
3489 iter = difflinestates(pstates, states)
3487 else:
3490 else:
3488 iter = [(b'', l) for l in states]
3491 iter = [(b'', l) for l in states]
3489 for change, l in iter:
3492 for change, l in iter:
3490 fm.startitem()
3493 fm.startitem()
3491 fm.context(ctx=ctx)
3494 fm.context(ctx=ctx)
3492 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3495 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3493 fm.plain(uipathfn(fn), label=b'grep.filename')
3496 fm.plain(uipathfn(fn), label=b'grep.filename')
3494
3497
3495 cols = [
3498 cols = [
3496 (b'rev', b'%d', rev, not plaingrep, b''),
3499 (b'rev', b'%d', rev, not plaingrep, b''),
3497 (
3500 (
3498 b'linenumber',
3501 b'linenumber',
3499 b'%d',
3502 b'%d',
3500 l.linenum,
3503 l.linenum,
3501 opts.get(b'line_number'),
3504 opts.get(b'line_number'),
3502 b'',
3505 b'',
3503 ),
3506 ),
3504 ]
3507 ]
3505 if diff:
3508 if diff:
3506 cols.append(
3509 cols.append(
3507 (
3510 (
3508 b'change',
3511 b'change',
3509 b'%s',
3512 b'%s',
3510 change,
3513 change,
3511 True,
3514 True,
3512 b'grep.inserted '
3515 b'grep.inserted '
3513 if change == b'+'
3516 if change == b'+'
3514 else b'grep.deleted ',
3517 else b'grep.deleted ',
3515 )
3518 )
3516 )
3519 )
3517 cols.extend(
3520 cols.extend(
3518 [
3521 [
3519 (
3522 (
3520 b'user',
3523 b'user',
3521 b'%s',
3524 b'%s',
3522 formatuser(ctx.user()),
3525 formatuser(ctx.user()),
3523 opts.get(b'user'),
3526 opts.get(b'user'),
3524 b'',
3527 b'',
3525 ),
3528 ),
3526 (
3529 (
3527 b'date',
3530 b'date',
3528 b'%s',
3531 b'%s',
3529 fm.formatdate(ctx.date(), datefmt),
3532 fm.formatdate(ctx.date(), datefmt),
3530 opts.get(b'date'),
3533 opts.get(b'date'),
3531 b'',
3534 b'',
3532 ),
3535 ),
3533 ]
3536 ]
3534 )
3537 )
3535 for name, fmt, data, cond, extra_label in cols:
3538 for name, fmt, data, cond, extra_label in cols:
3536 if cond:
3539 if cond:
3537 fm.plain(sep, label=b'grep.sep')
3540 fm.plain(sep, label=b'grep.sep')
3538 field = fieldnamemap.get(name, name)
3541 field = fieldnamemap.get(name, name)
3539 label = extra_label + (b'grep.%s' % name)
3542 label = extra_label + (b'grep.%s' % name)
3540 fm.condwrite(cond, field, fmt, data, label=label)
3543 fm.condwrite(cond, field, fmt, data, label=label)
3541 if not opts.get(b'files_with_matches'):
3544 if not opts.get(b'files_with_matches'):
3542 fm.plain(sep, label=b'grep.sep')
3545 fm.plain(sep, label=b'grep.sep')
3543 if not opts.get(b'text') and binary():
3546 if not opts.get(b'text') and binary():
3544 fm.plain(_(b" Binary file matches"))
3547 fm.plain(_(b" Binary file matches"))
3545 else:
3548 else:
3546 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3549 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3547 fm.plain(eol)
3550 fm.plain(eol)
3548 found = True
3551 found = True
3549 if opts.get(b'files_with_matches'):
3552 if opts.get(b'files_with_matches'):
3550 break
3553 break
3551 return found
3554 return found
3552
3555
3553 def displaymatches(fm, l):
3556 def displaymatches(fm, l):
3554 p = 0
3557 p = 0
3555 for s, e in l.findpos():
3558 for s, e in l.findpos():
3556 if p < s:
3559 if p < s:
3557 fm.startitem()
3560 fm.startitem()
3558 fm.write(b'text', b'%s', l.line[p:s])
3561 fm.write(b'text', b'%s', l.line[p:s])
3559 fm.data(matched=False)
3562 fm.data(matched=False)
3560 fm.startitem()
3563 fm.startitem()
3561 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3564 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3562 fm.data(matched=True)
3565 fm.data(matched=True)
3563 p = e
3566 p = e
3564 if p < len(l.line):
3567 if p < len(l.line):
3565 fm.startitem()
3568 fm.startitem()
3566 fm.write(b'text', b'%s', l.line[p:])
3569 fm.write(b'text', b'%s', l.line[p:])
3567 fm.data(matched=False)
3570 fm.data(matched=False)
3568 fm.end()
3571 fm.end()
3569
3572
3570 skip = set()
3573 skip = set()
3571 revfiles = {}
3574 revfiles = {}
3572 match = scmutil.match(repo[None], pats, opts)
3575 match = scmutil.match(repo[None], pats, opts)
3573 found = False
3576 found = False
3574 follow = opts.get(b'follow')
3577 follow = opts.get(b'follow')
3575
3578
3576 getrenamed = scmutil.getrenamedfn(repo)
3579 getrenamed = scmutil.getrenamedfn(repo)
3577
3580
3581 def get_file_content(filename, filelog, filenode, context, revision):
3582 try:
3583 content = filelog.read(filenode)
3584 except error.WdirUnsupported:
3585 content = context[filename].data()
3586 except error.CensoredNodeError:
3587 content = None
3588 ui.warn(
3589 _(b'cannot search in censored file: %(filename)s:%(revnum)s\n')
3590 % {b'filename': filename, b'revnum': pycompat.bytestr(revision)}
3591 )
3592 return content
3593
3578 def prep(ctx, fns):
3594 def prep(ctx, fns):
3579 rev = ctx.rev()
3595 rev = ctx.rev()
3580 pctx = ctx.p1()
3596 pctx = ctx.p1()
3581 parent = pctx.rev()
3597 parent = pctx.rev()
3582 matches.setdefault(rev, {})
3598 matches.setdefault(rev, {})
3583 matches.setdefault(parent, {})
3599 matches.setdefault(parent, {})
3584 files = revfiles.setdefault(rev, [])
3600 files = revfiles.setdefault(rev, [])
3585 for fn in fns:
3601 for fn in fns:
3586 flog = getfile(fn)
3602 flog = getfile(fn)
3587 try:
3603 try:
3588 fnode = ctx.filenode(fn)
3604 fnode = ctx.filenode(fn)
3589 except error.LookupError:
3605 except error.LookupError:
3590 continue
3606 continue
3591
3607
3592 copy = None
3608 copy = None
3593 if follow:
3609 if follow:
3594 copy = getrenamed(fn, rev)
3610 copy = getrenamed(fn, rev)
3595 if copy:
3611 if copy:
3596 copies.setdefault(rev, {})[fn] = copy
3612 copies.setdefault(rev, {})[fn] = copy
3597 if fn in skip:
3613 if fn in skip:
3598 skip.add(copy)
3614 skip.add(copy)
3599 if fn in skip:
3615 if fn in skip:
3600 continue
3616 continue
3601 files.append(fn)
3617 files.append(fn)
3602
3618
3603 if fn not in matches[rev]:
3619 if fn not in matches[rev]:
3604 try:
3620 content = get_file_content(fn, flog, fnode, ctx, rev)
3605 content = flog.read(fnode)
3606 except error.WdirUnsupported:
3607 content = ctx[fn].data()
3608 grepbody(fn, rev, content)
3621 grepbody(fn, rev, content)
3609
3622
3610 pfn = copy or fn
3623 pfn = copy or fn
3611 if pfn not in matches[parent]:
3624 if pfn not in matches[parent]:
3612 try:
3625 try:
3613 fnode = pctx.filenode(pfn)
3626 pfnode = pctx.filenode(pfn)
3614 grepbody(pfn, parent, flog.read(fnode))
3627 pcontent = get_file_content(pfn, flog, pfnode, pctx, parent)
3628 grepbody(pfn, parent, pcontent)
3615 except error.LookupError:
3629 except error.LookupError:
3616 pass
3630 pass
3617
3631
3618 ui.pager(b'grep')
3632 ui.pager(b'grep')
3619 fm = ui.formatter(b'grep', opts)
3633 fm = ui.formatter(b'grep', opts)
3620 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3634 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
3621 rev = ctx.rev()
3635 rev = ctx.rev()
3622 parent = ctx.p1().rev()
3636 parent = ctx.p1().rev()
3623 for fn in sorted(revfiles.get(rev, [])):
3637 for fn in sorted(revfiles.get(rev, [])):
3624 states = matches[rev][fn]
3638 states = matches[rev][fn]
3625 copy = copies.get(rev, {}).get(fn)
3639 copy = copies.get(rev, {}).get(fn)
3626 if fn in skip:
3640 if fn in skip:
3627 if copy:
3641 if copy:
3628 skip.add(copy)
3642 skip.add(copy)
3629 continue
3643 continue
3630 pstates = matches.get(parent, {}).get(copy or fn, [])
3644 pstates = matches.get(parent, {}).get(copy or fn, [])
3631 if pstates or states:
3645 if pstates or states:
3632 r = display(fm, fn, ctx, pstates, states)
3646 r = display(fm, fn, ctx, pstates, states)
3633 found = found or r
3647 found = found or r
3634 if r and not diff and not all_files:
3648 if r and not diff and not all_files:
3635 skip.add(fn)
3649 skip.add(fn)
3636 if copy:
3650 if copy:
3637 skip.add(copy)
3651 skip.add(copy)
3638 del revfiles[rev]
3652 del revfiles[rev]
3639 # We will keep the matches dict for the duration of the window
3653 # We will keep the matches dict for the duration of the window
3640 # clear the matches dict once the window is over
3654 # clear the matches dict once the window is over
3641 if not revfiles:
3655 if not revfiles:
3642 matches.clear()
3656 matches.clear()
3643 fm.end()
3657 fm.end()
3644
3658
3645 return not found
3659 return not found
3646
3660
3647
3661
3648 @command(
3662 @command(
3649 b'heads',
3663 b'heads',
3650 [
3664 [
3651 (
3665 (
3652 b'r',
3666 b'r',
3653 b'rev',
3667 b'rev',
3654 b'',
3668 b'',
3655 _(b'show only heads which are descendants of STARTREV'),
3669 _(b'show only heads which are descendants of STARTREV'),
3656 _(b'STARTREV'),
3670 _(b'STARTREV'),
3657 ),
3671 ),
3658 (b't', b'topo', False, _(b'show topological heads only')),
3672 (b't', b'topo', False, _(b'show topological heads only')),
3659 (
3673 (
3660 b'a',
3674 b'a',
3661 b'active',
3675 b'active',
3662 False,
3676 False,
3663 _(b'show active branchheads only (DEPRECATED)'),
3677 _(b'show active branchheads only (DEPRECATED)'),
3664 ),
3678 ),
3665 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3679 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3666 ]
3680 ]
3667 + templateopts,
3681 + templateopts,
3668 _(b'[-ct] [-r STARTREV] [REV]...'),
3682 _(b'[-ct] [-r STARTREV] [REV]...'),
3669 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3683 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3670 intents={INTENT_READONLY},
3684 intents={INTENT_READONLY},
3671 )
3685 )
3672 def heads(ui, repo, *branchrevs, **opts):
3686 def heads(ui, repo, *branchrevs, **opts):
3673 """show branch heads
3687 """show branch heads
3674
3688
3675 With no arguments, show all open branch heads in the repository.
3689 With no arguments, show all open branch heads in the repository.
3676 Branch heads are changesets that have no descendants on the
3690 Branch heads are changesets that have no descendants on the
3677 same branch. They are where development generally takes place and
3691 same branch. They are where development generally takes place and
3678 are the usual targets for update and merge operations.
3692 are the usual targets for update and merge operations.
3679
3693
3680 If one or more REVs are given, only open branch heads on the
3694 If one or more REVs are given, only open branch heads on the
3681 branches associated with the specified changesets are shown. This
3695 branches associated with the specified changesets are shown. This
3682 means that you can use :hg:`heads .` to see the heads on the
3696 means that you can use :hg:`heads .` to see the heads on the
3683 currently checked-out branch.
3697 currently checked-out branch.
3684
3698
3685 If -c/--closed is specified, also show branch heads marked closed
3699 If -c/--closed is specified, also show branch heads marked closed
3686 (see :hg:`commit --close-branch`).
3700 (see :hg:`commit --close-branch`).
3687
3701
3688 If STARTREV is specified, only those heads that are descendants of
3702 If STARTREV is specified, only those heads that are descendants of
3689 STARTREV will be displayed.
3703 STARTREV will be displayed.
3690
3704
3691 If -t/--topo is specified, named branch mechanics will be ignored and only
3705 If -t/--topo is specified, named branch mechanics will be ignored and only
3692 topological heads (changesets with no children) will be shown.
3706 topological heads (changesets with no children) will be shown.
3693
3707
3694 Returns 0 if matching heads are found, 1 if not.
3708 Returns 0 if matching heads are found, 1 if not.
3695 """
3709 """
3696
3710
3697 opts = pycompat.byteskwargs(opts)
3711 opts = pycompat.byteskwargs(opts)
3698 start = None
3712 start = None
3699 rev = opts.get(b'rev')
3713 rev = opts.get(b'rev')
3700 if rev:
3714 if rev:
3701 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3715 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3702 start = scmutil.revsingle(repo, rev, None).node()
3716 start = scmutil.revsingle(repo, rev, None).node()
3703
3717
3704 if opts.get(b'topo'):
3718 if opts.get(b'topo'):
3705 heads = [repo[h] for h in repo.heads(start)]
3719 heads = [repo[h] for h in repo.heads(start)]
3706 else:
3720 else:
3707 heads = []
3721 heads = []
3708 for branch in repo.branchmap():
3722 for branch in repo.branchmap():
3709 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3723 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3710 heads = [repo[h] for h in heads]
3724 heads = [repo[h] for h in heads]
3711
3725
3712 if branchrevs:
3726 if branchrevs:
3713 branches = set(
3727 branches = set(
3714 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3728 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3715 )
3729 )
3716 heads = [h for h in heads if h.branch() in branches]
3730 heads = [h for h in heads if h.branch() in branches]
3717
3731
3718 if opts.get(b'active') and branchrevs:
3732 if opts.get(b'active') and branchrevs:
3719 dagheads = repo.heads(start)
3733 dagheads = repo.heads(start)
3720 heads = [h for h in heads if h.node() in dagheads]
3734 heads = [h for h in heads if h.node() in dagheads]
3721
3735
3722 if branchrevs:
3736 if branchrevs:
3723 haveheads = set(h.branch() for h in heads)
3737 haveheads = set(h.branch() for h in heads)
3724 if branches - haveheads:
3738 if branches - haveheads:
3725 headless = b', '.join(b for b in branches - haveheads)
3739 headless = b', '.join(b for b in branches - haveheads)
3726 msg = _(b'no open branch heads found on branches %s')
3740 msg = _(b'no open branch heads found on branches %s')
3727 if opts.get(b'rev'):
3741 if opts.get(b'rev'):
3728 msg += _(b' (started at %s)') % opts[b'rev']
3742 msg += _(b' (started at %s)') % opts[b'rev']
3729 ui.warn((msg + b'\n') % headless)
3743 ui.warn((msg + b'\n') % headless)
3730
3744
3731 if not heads:
3745 if not heads:
3732 return 1
3746 return 1
3733
3747
3734 ui.pager(b'heads')
3748 ui.pager(b'heads')
3735 heads = sorted(heads, key=lambda x: -(x.rev()))
3749 heads = sorted(heads, key=lambda x: -(x.rev()))
3736 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3750 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3737 for ctx in heads:
3751 for ctx in heads:
3738 displayer.show(ctx)
3752 displayer.show(ctx)
3739 displayer.close()
3753 displayer.close()
3740
3754
3741
3755
3742 @command(
3756 @command(
3743 b'help',
3757 b'help',
3744 [
3758 [
3745 (b'e', b'extension', None, _(b'show only help for extensions')),
3759 (b'e', b'extension', None, _(b'show only help for extensions')),
3746 (b'c', b'command', None, _(b'show only help for commands')),
3760 (b'c', b'command', None, _(b'show only help for commands')),
3747 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3761 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3748 (
3762 (
3749 b's',
3763 b's',
3750 b'system',
3764 b'system',
3751 [],
3765 [],
3752 _(b'show help for specific platform(s)'),
3766 _(b'show help for specific platform(s)'),
3753 _(b'PLATFORM'),
3767 _(b'PLATFORM'),
3754 ),
3768 ),
3755 ],
3769 ],
3756 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3770 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3757 helpcategory=command.CATEGORY_HELP,
3771 helpcategory=command.CATEGORY_HELP,
3758 norepo=True,
3772 norepo=True,
3759 intents={INTENT_READONLY},
3773 intents={INTENT_READONLY},
3760 )
3774 )
3761 def help_(ui, name=None, **opts):
3775 def help_(ui, name=None, **opts):
3762 """show help for a given topic or a help overview
3776 """show help for a given topic or a help overview
3763
3777
3764 With no arguments, print a list of commands with short help messages.
3778 With no arguments, print a list of commands with short help messages.
3765
3779
3766 Given a topic, extension, or command name, print help for that
3780 Given a topic, extension, or command name, print help for that
3767 topic.
3781 topic.
3768
3782
3769 Returns 0 if successful.
3783 Returns 0 if successful.
3770 """
3784 """
3771
3785
3772 keep = opts.get(r'system') or []
3786 keep = opts.get(r'system') or []
3773 if len(keep) == 0:
3787 if len(keep) == 0:
3774 if pycompat.sysplatform.startswith(b'win'):
3788 if pycompat.sysplatform.startswith(b'win'):
3775 keep.append(b'windows')
3789 keep.append(b'windows')
3776 elif pycompat.sysplatform == b'OpenVMS':
3790 elif pycompat.sysplatform == b'OpenVMS':
3777 keep.append(b'vms')
3791 keep.append(b'vms')
3778 elif pycompat.sysplatform == b'plan9':
3792 elif pycompat.sysplatform == b'plan9':
3779 keep.append(b'plan9')
3793 keep.append(b'plan9')
3780 else:
3794 else:
3781 keep.append(b'unix')
3795 keep.append(b'unix')
3782 keep.append(pycompat.sysplatform.lower())
3796 keep.append(pycompat.sysplatform.lower())
3783 if ui.verbose:
3797 if ui.verbose:
3784 keep.append(b'verbose')
3798 keep.append(b'verbose')
3785
3799
3786 commands = sys.modules[__name__]
3800 commands = sys.modules[__name__]
3787 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3801 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3788 ui.pager(b'help')
3802 ui.pager(b'help')
3789 ui.write(formatted)
3803 ui.write(formatted)
3790
3804
3791
3805
3792 @command(
3806 @command(
3793 b'identify|id',
3807 b'identify|id',
3794 [
3808 [
3795 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3809 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3796 (b'n', b'num', None, _(b'show local revision number')),
3810 (b'n', b'num', None, _(b'show local revision number')),
3797 (b'i', b'id', None, _(b'show global revision id')),
3811 (b'i', b'id', None, _(b'show global revision id')),
3798 (b'b', b'branch', None, _(b'show branch')),
3812 (b'b', b'branch', None, _(b'show branch')),
3799 (b't', b'tags', None, _(b'show tags')),
3813 (b't', b'tags', None, _(b'show tags')),
3800 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3814 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3801 ]
3815 ]
3802 + remoteopts
3816 + remoteopts
3803 + formatteropts,
3817 + formatteropts,
3804 _(b'[-nibtB] [-r REV] [SOURCE]'),
3818 _(b'[-nibtB] [-r REV] [SOURCE]'),
3805 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3819 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3806 optionalrepo=True,
3820 optionalrepo=True,
3807 intents={INTENT_READONLY},
3821 intents={INTENT_READONLY},
3808 )
3822 )
3809 def identify(
3823 def identify(
3810 ui,
3824 ui,
3811 repo,
3825 repo,
3812 source=None,
3826 source=None,
3813 rev=None,
3827 rev=None,
3814 num=None,
3828 num=None,
3815 id=None,
3829 id=None,
3816 branch=None,
3830 branch=None,
3817 tags=None,
3831 tags=None,
3818 bookmarks=None,
3832 bookmarks=None,
3819 **opts
3833 **opts
3820 ):
3834 ):
3821 """identify the working directory or specified revision
3835 """identify the working directory or specified revision
3822
3836
3823 Print a summary identifying the repository state at REV using one or
3837 Print a summary identifying the repository state at REV using one or
3824 two parent hash identifiers, followed by a "+" if the working
3838 two parent hash identifiers, followed by a "+" if the working
3825 directory has uncommitted changes, the branch name (if not default),
3839 directory has uncommitted changes, the branch name (if not default),
3826 a list of tags, and a list of bookmarks.
3840 a list of tags, and a list of bookmarks.
3827
3841
3828 When REV is not given, print a summary of the current state of the
3842 When REV is not given, print a summary of the current state of the
3829 repository including the working directory. Specify -r. to get information
3843 repository including the working directory. Specify -r. to get information
3830 of the working directory parent without scanning uncommitted changes.
3844 of the working directory parent without scanning uncommitted changes.
3831
3845
3832 Specifying a path to a repository root or Mercurial bundle will
3846 Specifying a path to a repository root or Mercurial bundle will
3833 cause lookup to operate on that repository/bundle.
3847 cause lookup to operate on that repository/bundle.
3834
3848
3835 .. container:: verbose
3849 .. container:: verbose
3836
3850
3837 Template:
3851 Template:
3838
3852
3839 The following keywords are supported in addition to the common template
3853 The following keywords are supported in addition to the common template
3840 keywords and functions. See also :hg:`help templates`.
3854 keywords and functions. See also :hg:`help templates`.
3841
3855
3842 :dirty: String. Character ``+`` denoting if the working directory has
3856 :dirty: String. Character ``+`` denoting if the working directory has
3843 uncommitted changes.
3857 uncommitted changes.
3844 :id: String. One or two nodes, optionally followed by ``+``.
3858 :id: String. One or two nodes, optionally followed by ``+``.
3845 :parents: List of strings. Parent nodes of the changeset.
3859 :parents: List of strings. Parent nodes of the changeset.
3846
3860
3847 Examples:
3861 Examples:
3848
3862
3849 - generate a build identifier for the working directory::
3863 - generate a build identifier for the working directory::
3850
3864
3851 hg id --id > build-id.dat
3865 hg id --id > build-id.dat
3852
3866
3853 - find the revision corresponding to a tag::
3867 - find the revision corresponding to a tag::
3854
3868
3855 hg id -n -r 1.3
3869 hg id -n -r 1.3
3856
3870
3857 - check the most recent revision of a remote repository::
3871 - check the most recent revision of a remote repository::
3858
3872
3859 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3873 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3860
3874
3861 See :hg:`log` for generating more information about specific revisions,
3875 See :hg:`log` for generating more information about specific revisions,
3862 including full hash identifiers.
3876 including full hash identifiers.
3863
3877
3864 Returns 0 if successful.
3878 Returns 0 if successful.
3865 """
3879 """
3866
3880
3867 opts = pycompat.byteskwargs(opts)
3881 opts = pycompat.byteskwargs(opts)
3868 if not repo and not source:
3882 if not repo and not source:
3869 raise error.Abort(
3883 raise error.Abort(
3870 _(b"there is no Mercurial repository here (.hg not found)")
3884 _(b"there is no Mercurial repository here (.hg not found)")
3871 )
3885 )
3872
3886
3873 default = not (num or id or branch or tags or bookmarks)
3887 default = not (num or id or branch or tags or bookmarks)
3874 output = []
3888 output = []
3875 revs = []
3889 revs = []
3876
3890
3877 if source:
3891 if source:
3878 source, branches = hg.parseurl(ui.expandpath(source))
3892 source, branches = hg.parseurl(ui.expandpath(source))
3879 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3893 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3880 repo = peer.local()
3894 repo = peer.local()
3881 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3895 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3882
3896
3883 fm = ui.formatter(b'identify', opts)
3897 fm = ui.formatter(b'identify', opts)
3884 fm.startitem()
3898 fm.startitem()
3885
3899
3886 if not repo:
3900 if not repo:
3887 if num or branch or tags:
3901 if num or branch or tags:
3888 raise error.Abort(
3902 raise error.Abort(
3889 _(b"can't query remote revision number, branch, or tags")
3903 _(b"can't query remote revision number, branch, or tags")
3890 )
3904 )
3891 if not rev and revs:
3905 if not rev and revs:
3892 rev = revs[0]
3906 rev = revs[0]
3893 if not rev:
3907 if not rev:
3894 rev = b"tip"
3908 rev = b"tip"
3895
3909
3896 remoterev = peer.lookup(rev)
3910 remoterev = peer.lookup(rev)
3897 hexrev = fm.hexfunc(remoterev)
3911 hexrev = fm.hexfunc(remoterev)
3898 if default or id:
3912 if default or id:
3899 output = [hexrev]
3913 output = [hexrev]
3900 fm.data(id=hexrev)
3914 fm.data(id=hexrev)
3901
3915
3902 @util.cachefunc
3916 @util.cachefunc
3903 def getbms():
3917 def getbms():
3904 bms = []
3918 bms = []
3905
3919
3906 if b'bookmarks' in peer.listkeys(b'namespaces'):
3920 if b'bookmarks' in peer.listkeys(b'namespaces'):
3907 hexremoterev = hex(remoterev)
3921 hexremoterev = hex(remoterev)
3908 bms = [
3922 bms = [
3909 bm
3923 bm
3910 for bm, bmr in pycompat.iteritems(
3924 for bm, bmr in pycompat.iteritems(
3911 peer.listkeys(b'bookmarks')
3925 peer.listkeys(b'bookmarks')
3912 )
3926 )
3913 if bmr == hexremoterev
3927 if bmr == hexremoterev
3914 ]
3928 ]
3915
3929
3916 return sorted(bms)
3930 return sorted(bms)
3917
3931
3918 if fm.isplain():
3932 if fm.isplain():
3919 if bookmarks:
3933 if bookmarks:
3920 output.extend(getbms())
3934 output.extend(getbms())
3921 elif default and not ui.quiet:
3935 elif default and not ui.quiet:
3922 # multiple bookmarks for a single parent separated by '/'
3936 # multiple bookmarks for a single parent separated by '/'
3923 bm = b'/'.join(getbms())
3937 bm = b'/'.join(getbms())
3924 if bm:
3938 if bm:
3925 output.append(bm)
3939 output.append(bm)
3926 else:
3940 else:
3927 fm.data(node=hex(remoterev))
3941 fm.data(node=hex(remoterev))
3928 if bookmarks or b'bookmarks' in fm.datahint():
3942 if bookmarks or b'bookmarks' in fm.datahint():
3929 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3943 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3930 else:
3944 else:
3931 if rev:
3945 if rev:
3932 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3946 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3933 ctx = scmutil.revsingle(repo, rev, None)
3947 ctx = scmutil.revsingle(repo, rev, None)
3934
3948
3935 if ctx.rev() is None:
3949 if ctx.rev() is None:
3936 ctx = repo[None]
3950 ctx = repo[None]
3937 parents = ctx.parents()
3951 parents = ctx.parents()
3938 taglist = []
3952 taglist = []
3939 for p in parents:
3953 for p in parents:
3940 taglist.extend(p.tags())
3954 taglist.extend(p.tags())
3941
3955
3942 dirty = b""
3956 dirty = b""
3943 if ctx.dirty(missing=True, merge=False, branch=False):
3957 if ctx.dirty(missing=True, merge=False, branch=False):
3944 dirty = b'+'
3958 dirty = b'+'
3945 fm.data(dirty=dirty)
3959 fm.data(dirty=dirty)
3946
3960
3947 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3961 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3948 if default or id:
3962 if default or id:
3949 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3963 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3950 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3964 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3951
3965
3952 if num:
3966 if num:
3953 numoutput = [b"%d" % p.rev() for p in parents]
3967 numoutput = [b"%d" % p.rev() for p in parents]
3954 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3968 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3955
3969
3956 fm.data(
3970 fm.data(
3957 parents=fm.formatlist(
3971 parents=fm.formatlist(
3958 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3972 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3959 )
3973 )
3960 )
3974 )
3961 else:
3975 else:
3962 hexoutput = fm.hexfunc(ctx.node())
3976 hexoutput = fm.hexfunc(ctx.node())
3963 if default or id:
3977 if default or id:
3964 output = [hexoutput]
3978 output = [hexoutput]
3965 fm.data(id=hexoutput)
3979 fm.data(id=hexoutput)
3966
3980
3967 if num:
3981 if num:
3968 output.append(pycompat.bytestr(ctx.rev()))
3982 output.append(pycompat.bytestr(ctx.rev()))
3969 taglist = ctx.tags()
3983 taglist = ctx.tags()
3970
3984
3971 if default and not ui.quiet:
3985 if default and not ui.quiet:
3972 b = ctx.branch()
3986 b = ctx.branch()
3973 if b != b'default':
3987 if b != b'default':
3974 output.append(b"(%s)" % b)
3988 output.append(b"(%s)" % b)
3975
3989
3976 # multiple tags for a single parent separated by '/'
3990 # multiple tags for a single parent separated by '/'
3977 t = b'/'.join(taglist)
3991 t = b'/'.join(taglist)
3978 if t:
3992 if t:
3979 output.append(t)
3993 output.append(t)
3980
3994
3981 # multiple bookmarks for a single parent separated by '/'
3995 # multiple bookmarks for a single parent separated by '/'
3982 bm = b'/'.join(ctx.bookmarks())
3996 bm = b'/'.join(ctx.bookmarks())
3983 if bm:
3997 if bm:
3984 output.append(bm)
3998 output.append(bm)
3985 else:
3999 else:
3986 if branch:
4000 if branch:
3987 output.append(ctx.branch())
4001 output.append(ctx.branch())
3988
4002
3989 if tags:
4003 if tags:
3990 output.extend(taglist)
4004 output.extend(taglist)
3991
4005
3992 if bookmarks:
4006 if bookmarks:
3993 output.extend(ctx.bookmarks())
4007 output.extend(ctx.bookmarks())
3994
4008
3995 fm.data(node=ctx.hex())
4009 fm.data(node=ctx.hex())
3996 fm.data(branch=ctx.branch())
4010 fm.data(branch=ctx.branch())
3997 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4011 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3998 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4012 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3999 fm.context(ctx=ctx)
4013 fm.context(ctx=ctx)
4000
4014
4001 fm.plain(b"%s\n" % b' '.join(output))
4015 fm.plain(b"%s\n" % b' '.join(output))
4002 fm.end()
4016 fm.end()
4003
4017
4004
4018
4005 @command(
4019 @command(
4006 b'import|patch',
4020 b'import|patch',
4007 [
4021 [
4008 (
4022 (
4009 b'p',
4023 b'p',
4010 b'strip',
4024 b'strip',
4011 1,
4025 1,
4012 _(
4026 _(
4013 b'directory strip option for patch. This has the same '
4027 b'directory strip option for patch. This has the same '
4014 b'meaning as the corresponding patch option'
4028 b'meaning as the corresponding patch option'
4015 ),
4029 ),
4016 _(b'NUM'),
4030 _(b'NUM'),
4017 ),
4031 ),
4018 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4032 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4019 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4033 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4020 (
4034 (
4021 b'f',
4035 b'f',
4022 b'force',
4036 b'force',
4023 None,
4037 None,
4024 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4038 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4025 ),
4039 ),
4026 (
4040 (
4027 b'',
4041 b'',
4028 b'no-commit',
4042 b'no-commit',
4029 None,
4043 None,
4030 _(b"don't commit, just update the working directory"),
4044 _(b"don't commit, just update the working directory"),
4031 ),
4045 ),
4032 (
4046 (
4033 b'',
4047 b'',
4034 b'bypass',
4048 b'bypass',
4035 None,
4049 None,
4036 _(b"apply patch without touching the working directory"),
4050 _(b"apply patch without touching the working directory"),
4037 ),
4051 ),
4038 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4052 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4039 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4053 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4040 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4054 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4041 (
4055 (
4042 b'',
4056 b'',
4043 b'import-branch',
4057 b'import-branch',
4044 None,
4058 None,
4045 _(b'use any branch information in patch (implied by --exact)'),
4059 _(b'use any branch information in patch (implied by --exact)'),
4046 ),
4060 ),
4047 ]
4061 ]
4048 + commitopts
4062 + commitopts
4049 + commitopts2
4063 + commitopts2
4050 + similarityopts,
4064 + similarityopts,
4051 _(b'[OPTION]... PATCH...'),
4065 _(b'[OPTION]... PATCH...'),
4052 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4066 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4053 )
4067 )
4054 def import_(ui, repo, patch1=None, *patches, **opts):
4068 def import_(ui, repo, patch1=None, *patches, **opts):
4055 """import an ordered set of patches
4069 """import an ordered set of patches
4056
4070
4057 Import a list of patches and commit them individually (unless
4071 Import a list of patches and commit them individually (unless
4058 --no-commit is specified).
4072 --no-commit is specified).
4059
4073
4060 To read a patch from standard input (stdin), use "-" as the patch
4074 To read a patch from standard input (stdin), use "-" as the patch
4061 name. If a URL is specified, the patch will be downloaded from
4075 name. If a URL is specified, the patch will be downloaded from
4062 there.
4076 there.
4063
4077
4064 Import first applies changes to the working directory (unless
4078 Import first applies changes to the working directory (unless
4065 --bypass is specified), import will abort if there are outstanding
4079 --bypass is specified), import will abort if there are outstanding
4066 changes.
4080 changes.
4067
4081
4068 Use --bypass to apply and commit patches directly to the
4082 Use --bypass to apply and commit patches directly to the
4069 repository, without affecting the working directory. Without
4083 repository, without affecting the working directory. Without
4070 --exact, patches will be applied on top of the working directory
4084 --exact, patches will be applied on top of the working directory
4071 parent revision.
4085 parent revision.
4072
4086
4073 You can import a patch straight from a mail message. Even patches
4087 You can import a patch straight from a mail message. Even patches
4074 as attachments work (to use the body part, it must have type
4088 as attachments work (to use the body part, it must have type
4075 text/plain or text/x-patch). From and Subject headers of email
4089 text/plain or text/x-patch). From and Subject headers of email
4076 message are used as default committer and commit message. All
4090 message are used as default committer and commit message. All
4077 text/plain body parts before first diff are added to the commit
4091 text/plain body parts before first diff are added to the commit
4078 message.
4092 message.
4079
4093
4080 If the imported patch was generated by :hg:`export`, user and
4094 If the imported patch was generated by :hg:`export`, user and
4081 description from patch override values from message headers and
4095 description from patch override values from message headers and
4082 body. Values given on command line with -m/--message and -u/--user
4096 body. Values given on command line with -m/--message and -u/--user
4083 override these.
4097 override these.
4084
4098
4085 If --exact is specified, import will set the working directory to
4099 If --exact is specified, import will set the working directory to
4086 the parent of each patch before applying it, and will abort if the
4100 the parent of each patch before applying it, and will abort if the
4087 resulting changeset has a different ID than the one recorded in
4101 resulting changeset has a different ID than the one recorded in
4088 the patch. This will guard against various ways that portable
4102 the patch. This will guard against various ways that portable
4089 patch formats and mail systems might fail to transfer Mercurial
4103 patch formats and mail systems might fail to transfer Mercurial
4090 data or metadata. See :hg:`bundle` for lossless transmission.
4104 data or metadata. See :hg:`bundle` for lossless transmission.
4091
4105
4092 Use --partial to ensure a changeset will be created from the patch
4106 Use --partial to ensure a changeset will be created from the patch
4093 even if some hunks fail to apply. Hunks that fail to apply will be
4107 even if some hunks fail to apply. Hunks that fail to apply will be
4094 written to a <target-file>.rej file. Conflicts can then be resolved
4108 written to a <target-file>.rej file. Conflicts can then be resolved
4095 by hand before :hg:`commit --amend` is run to update the created
4109 by hand before :hg:`commit --amend` is run to update the created
4096 changeset. This flag exists to let people import patches that
4110 changeset. This flag exists to let people import patches that
4097 partially apply without losing the associated metadata (author,
4111 partially apply without losing the associated metadata (author,
4098 date, description, ...).
4112 date, description, ...).
4099
4113
4100 .. note::
4114 .. note::
4101
4115
4102 When no hunks apply cleanly, :hg:`import --partial` will create
4116 When no hunks apply cleanly, :hg:`import --partial` will create
4103 an empty changeset, importing only the patch metadata.
4117 an empty changeset, importing only the patch metadata.
4104
4118
4105 With -s/--similarity, hg will attempt to discover renames and
4119 With -s/--similarity, hg will attempt to discover renames and
4106 copies in the patch in the same way as :hg:`addremove`.
4120 copies in the patch in the same way as :hg:`addremove`.
4107
4121
4108 It is possible to use external patch programs to perform the patch
4122 It is possible to use external patch programs to perform the patch
4109 by setting the ``ui.patch`` configuration option. For the default
4123 by setting the ``ui.patch`` configuration option. For the default
4110 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4124 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4111 See :hg:`help config` for more information about configuration
4125 See :hg:`help config` for more information about configuration
4112 files and how to use these options.
4126 files and how to use these options.
4113
4127
4114 See :hg:`help dates` for a list of formats valid for -d/--date.
4128 See :hg:`help dates` for a list of formats valid for -d/--date.
4115
4129
4116 .. container:: verbose
4130 .. container:: verbose
4117
4131
4118 Examples:
4132 Examples:
4119
4133
4120 - import a traditional patch from a website and detect renames::
4134 - import a traditional patch from a website and detect renames::
4121
4135
4122 hg import -s 80 http://example.com/bugfix.patch
4136 hg import -s 80 http://example.com/bugfix.patch
4123
4137
4124 - import a changeset from an hgweb server::
4138 - import a changeset from an hgweb server::
4125
4139
4126 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4140 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4127
4141
4128 - import all the patches in an Unix-style mbox::
4142 - import all the patches in an Unix-style mbox::
4129
4143
4130 hg import incoming-patches.mbox
4144 hg import incoming-patches.mbox
4131
4145
4132 - import patches from stdin::
4146 - import patches from stdin::
4133
4147
4134 hg import -
4148 hg import -
4135
4149
4136 - attempt to exactly restore an exported changeset (not always
4150 - attempt to exactly restore an exported changeset (not always
4137 possible)::
4151 possible)::
4138
4152
4139 hg import --exact proposed-fix.patch
4153 hg import --exact proposed-fix.patch
4140
4154
4141 - use an external tool to apply a patch which is too fuzzy for
4155 - use an external tool to apply a patch which is too fuzzy for
4142 the default internal tool.
4156 the default internal tool.
4143
4157
4144 hg import --config ui.patch="patch --merge" fuzzy.patch
4158 hg import --config ui.patch="patch --merge" fuzzy.patch
4145
4159
4146 - change the default fuzzing from 2 to a less strict 7
4160 - change the default fuzzing from 2 to a less strict 7
4147
4161
4148 hg import --config ui.fuzz=7 fuzz.patch
4162 hg import --config ui.fuzz=7 fuzz.patch
4149
4163
4150 Returns 0 on success, 1 on partial success (see --partial).
4164 Returns 0 on success, 1 on partial success (see --partial).
4151 """
4165 """
4152
4166
4153 opts = pycompat.byteskwargs(opts)
4167 opts = pycompat.byteskwargs(opts)
4154 if not patch1:
4168 if not patch1:
4155 raise error.Abort(_(b'need at least one patch to import'))
4169 raise error.Abort(_(b'need at least one patch to import'))
4156
4170
4157 patches = (patch1,) + patches
4171 patches = (patch1,) + patches
4158
4172
4159 date = opts.get(b'date')
4173 date = opts.get(b'date')
4160 if date:
4174 if date:
4161 opts[b'date'] = dateutil.parsedate(date)
4175 opts[b'date'] = dateutil.parsedate(date)
4162
4176
4163 exact = opts.get(b'exact')
4177 exact = opts.get(b'exact')
4164 update = not opts.get(b'bypass')
4178 update = not opts.get(b'bypass')
4165 if not update and opts.get(b'no_commit'):
4179 if not update and opts.get(b'no_commit'):
4166 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4180 raise error.Abort(_(b'cannot use --no-commit with --bypass'))
4167 try:
4181 try:
4168 sim = float(opts.get(b'similarity') or 0)
4182 sim = float(opts.get(b'similarity') or 0)
4169 except ValueError:
4183 except ValueError:
4170 raise error.Abort(_(b'similarity must be a number'))
4184 raise error.Abort(_(b'similarity must be a number'))
4171 if sim < 0 or sim > 100:
4185 if sim < 0 or sim > 100:
4172 raise error.Abort(_(b'similarity must be between 0 and 100'))
4186 raise error.Abort(_(b'similarity must be between 0 and 100'))
4173 if sim and not update:
4187 if sim and not update:
4174 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4188 raise error.Abort(_(b'cannot use --similarity with --bypass'))
4175 if exact:
4189 if exact:
4176 if opts.get(b'edit'):
4190 if opts.get(b'edit'):
4177 raise error.Abort(_(b'cannot use --exact with --edit'))
4191 raise error.Abort(_(b'cannot use --exact with --edit'))
4178 if opts.get(b'prefix'):
4192 if opts.get(b'prefix'):
4179 raise error.Abort(_(b'cannot use --exact with --prefix'))
4193 raise error.Abort(_(b'cannot use --exact with --prefix'))
4180
4194
4181 base = opts[b"base"]
4195 base = opts[b"base"]
4182 msgs = []
4196 msgs = []
4183 ret = 0
4197 ret = 0
4184
4198
4185 with repo.wlock():
4199 with repo.wlock():
4186 if update:
4200 if update:
4187 cmdutil.checkunfinished(repo)
4201 cmdutil.checkunfinished(repo)
4188 if exact or not opts.get(b'force'):
4202 if exact or not opts.get(b'force'):
4189 cmdutil.bailifchanged(repo)
4203 cmdutil.bailifchanged(repo)
4190
4204
4191 if not opts.get(b'no_commit'):
4205 if not opts.get(b'no_commit'):
4192 lock = repo.lock
4206 lock = repo.lock
4193 tr = lambda: repo.transaction(b'import')
4207 tr = lambda: repo.transaction(b'import')
4194 dsguard = util.nullcontextmanager
4208 dsguard = util.nullcontextmanager
4195 else:
4209 else:
4196 lock = util.nullcontextmanager
4210 lock = util.nullcontextmanager
4197 tr = util.nullcontextmanager
4211 tr = util.nullcontextmanager
4198 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4212 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4199 with lock(), tr(), dsguard():
4213 with lock(), tr(), dsguard():
4200 parents = repo[None].parents()
4214 parents = repo[None].parents()
4201 for patchurl in patches:
4215 for patchurl in patches:
4202 if patchurl == b'-':
4216 if patchurl == b'-':
4203 ui.status(_(b'applying patch from stdin\n'))
4217 ui.status(_(b'applying patch from stdin\n'))
4204 patchfile = ui.fin
4218 patchfile = ui.fin
4205 patchurl = b'stdin' # for error message
4219 patchurl = b'stdin' # for error message
4206 else:
4220 else:
4207 patchurl = os.path.join(base, patchurl)
4221 patchurl = os.path.join(base, patchurl)
4208 ui.status(_(b'applying %s\n') % patchurl)
4222 ui.status(_(b'applying %s\n') % patchurl)
4209 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4223 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4210
4224
4211 haspatch = False
4225 haspatch = False
4212 for hunk in patch.split(patchfile):
4226 for hunk in patch.split(patchfile):
4213 with patch.extract(ui, hunk) as patchdata:
4227 with patch.extract(ui, hunk) as patchdata:
4214 msg, node, rej = cmdutil.tryimportone(
4228 msg, node, rej = cmdutil.tryimportone(
4215 ui, repo, patchdata, parents, opts, msgs, hg.clean
4229 ui, repo, patchdata, parents, opts, msgs, hg.clean
4216 )
4230 )
4217 if msg:
4231 if msg:
4218 haspatch = True
4232 haspatch = True
4219 ui.note(msg + b'\n')
4233 ui.note(msg + b'\n')
4220 if update or exact:
4234 if update or exact:
4221 parents = repo[None].parents()
4235 parents = repo[None].parents()
4222 else:
4236 else:
4223 parents = [repo[node]]
4237 parents = [repo[node]]
4224 if rej:
4238 if rej:
4225 ui.write_err(_(b"patch applied partially\n"))
4239 ui.write_err(_(b"patch applied partially\n"))
4226 ui.write_err(
4240 ui.write_err(
4227 _(
4241 _(
4228 b"(fix the .rej files and run "
4242 b"(fix the .rej files and run "
4229 b"`hg commit --amend`)\n"
4243 b"`hg commit --amend`)\n"
4230 )
4244 )
4231 )
4245 )
4232 ret = 1
4246 ret = 1
4233 break
4247 break
4234
4248
4235 if not haspatch:
4249 if not haspatch:
4236 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4250 raise error.Abort(_(b'%s: no diffs found') % patchurl)
4237
4251
4238 if msgs:
4252 if msgs:
4239 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4253 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4240 return ret
4254 return ret
4241
4255
4242
4256
4243 @command(
4257 @command(
4244 b'incoming|in',
4258 b'incoming|in',
4245 [
4259 [
4246 (
4260 (
4247 b'f',
4261 b'f',
4248 b'force',
4262 b'force',
4249 None,
4263 None,
4250 _(b'run even if remote repository is unrelated'),
4264 _(b'run even if remote repository is unrelated'),
4251 ),
4265 ),
4252 (b'n', b'newest-first', None, _(b'show newest record first')),
4266 (b'n', b'newest-first', None, _(b'show newest record first')),
4253 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4267 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4254 (
4268 (
4255 b'r',
4269 b'r',
4256 b'rev',
4270 b'rev',
4257 [],
4271 [],
4258 _(b'a remote changeset intended to be added'),
4272 _(b'a remote changeset intended to be added'),
4259 _(b'REV'),
4273 _(b'REV'),
4260 ),
4274 ),
4261 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4275 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4262 (
4276 (
4263 b'b',
4277 b'b',
4264 b'branch',
4278 b'branch',
4265 [],
4279 [],
4266 _(b'a specific branch you would like to pull'),
4280 _(b'a specific branch you would like to pull'),
4267 _(b'BRANCH'),
4281 _(b'BRANCH'),
4268 ),
4282 ),
4269 ]
4283 ]
4270 + logopts
4284 + logopts
4271 + remoteopts
4285 + remoteopts
4272 + subrepoopts,
4286 + subrepoopts,
4273 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4287 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4274 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4288 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4275 )
4289 )
4276 def incoming(ui, repo, source=b"default", **opts):
4290 def incoming(ui, repo, source=b"default", **opts):
4277 """show new changesets found in source
4291 """show new changesets found in source
4278
4292
4279 Show new changesets found in the specified path/URL or the default
4293 Show new changesets found in the specified path/URL or the default
4280 pull location. These are the changesets that would have been pulled
4294 pull location. These are the changesets that would have been pulled
4281 by :hg:`pull` at the time you issued this command.
4295 by :hg:`pull` at the time you issued this command.
4282
4296
4283 See pull for valid source format details.
4297 See pull for valid source format details.
4284
4298
4285 .. container:: verbose
4299 .. container:: verbose
4286
4300
4287 With -B/--bookmarks, the result of bookmark comparison between
4301 With -B/--bookmarks, the result of bookmark comparison between
4288 local and remote repositories is displayed. With -v/--verbose,
4302 local and remote repositories is displayed. With -v/--verbose,
4289 status is also displayed for each bookmark like below::
4303 status is also displayed for each bookmark like below::
4290
4304
4291 BM1 01234567890a added
4305 BM1 01234567890a added
4292 BM2 1234567890ab advanced
4306 BM2 1234567890ab advanced
4293 BM3 234567890abc diverged
4307 BM3 234567890abc diverged
4294 BM4 34567890abcd changed
4308 BM4 34567890abcd changed
4295
4309
4296 The action taken locally when pulling depends on the
4310 The action taken locally when pulling depends on the
4297 status of each bookmark:
4311 status of each bookmark:
4298
4312
4299 :``added``: pull will create it
4313 :``added``: pull will create it
4300 :``advanced``: pull will update it
4314 :``advanced``: pull will update it
4301 :``diverged``: pull will create a divergent bookmark
4315 :``diverged``: pull will create a divergent bookmark
4302 :``changed``: result depends on remote changesets
4316 :``changed``: result depends on remote changesets
4303
4317
4304 From the point of view of pulling behavior, bookmark
4318 From the point of view of pulling behavior, bookmark
4305 existing only in the remote repository are treated as ``added``,
4319 existing only in the remote repository are treated as ``added``,
4306 even if it is in fact locally deleted.
4320 even if it is in fact locally deleted.
4307
4321
4308 .. container:: verbose
4322 .. container:: verbose
4309
4323
4310 For remote repository, using --bundle avoids downloading the
4324 For remote repository, using --bundle avoids downloading the
4311 changesets twice if the incoming is followed by a pull.
4325 changesets twice if the incoming is followed by a pull.
4312
4326
4313 Examples:
4327 Examples:
4314
4328
4315 - show incoming changes with patches and full description::
4329 - show incoming changes with patches and full description::
4316
4330
4317 hg incoming -vp
4331 hg incoming -vp
4318
4332
4319 - show incoming changes excluding merges, store a bundle::
4333 - show incoming changes excluding merges, store a bundle::
4320
4334
4321 hg in -vpM --bundle incoming.hg
4335 hg in -vpM --bundle incoming.hg
4322 hg pull incoming.hg
4336 hg pull incoming.hg
4323
4337
4324 - briefly list changes inside a bundle::
4338 - briefly list changes inside a bundle::
4325
4339
4326 hg in changes.hg -T "{desc|firstline}\\n"
4340 hg in changes.hg -T "{desc|firstline}\\n"
4327
4341
4328 Returns 0 if there are incoming changes, 1 otherwise.
4342 Returns 0 if there are incoming changes, 1 otherwise.
4329 """
4343 """
4330 opts = pycompat.byteskwargs(opts)
4344 opts = pycompat.byteskwargs(opts)
4331 if opts.get(b'graph'):
4345 if opts.get(b'graph'):
4332 logcmdutil.checkunsupportedgraphflags([], opts)
4346 logcmdutil.checkunsupportedgraphflags([], opts)
4333
4347
4334 def display(other, chlist, displayer):
4348 def display(other, chlist, displayer):
4335 revdag = logcmdutil.graphrevs(other, chlist, opts)
4349 revdag = logcmdutil.graphrevs(other, chlist, opts)
4336 logcmdutil.displaygraph(
4350 logcmdutil.displaygraph(
4337 ui, repo, revdag, displayer, graphmod.asciiedges
4351 ui, repo, revdag, displayer, graphmod.asciiedges
4338 )
4352 )
4339
4353
4340 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4354 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4341 return 0
4355 return 0
4342
4356
4343 if opts.get(b'bundle') and opts.get(b'subrepos'):
4357 if opts.get(b'bundle') and opts.get(b'subrepos'):
4344 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4358 raise error.Abort(_(b'cannot combine --bundle and --subrepos'))
4345
4359
4346 if opts.get(b'bookmarks'):
4360 if opts.get(b'bookmarks'):
4347 source, branches = hg.parseurl(
4361 source, branches = hg.parseurl(
4348 ui.expandpath(source), opts.get(b'branch')
4362 ui.expandpath(source), opts.get(b'branch')
4349 )
4363 )
4350 other = hg.peer(repo, opts, source)
4364 other = hg.peer(repo, opts, source)
4351 if b'bookmarks' not in other.listkeys(b'namespaces'):
4365 if b'bookmarks' not in other.listkeys(b'namespaces'):
4352 ui.warn(_(b"remote doesn't support bookmarks\n"))
4366 ui.warn(_(b"remote doesn't support bookmarks\n"))
4353 return 0
4367 return 0
4354 ui.pager(b'incoming')
4368 ui.pager(b'incoming')
4355 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4369 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4356 return bookmarks.incoming(ui, repo, other)
4370 return bookmarks.incoming(ui, repo, other)
4357
4371
4358 repo._subtoppath = ui.expandpath(source)
4372 repo._subtoppath = ui.expandpath(source)
4359 try:
4373 try:
4360 return hg.incoming(ui, repo, source, opts)
4374 return hg.incoming(ui, repo, source, opts)
4361 finally:
4375 finally:
4362 del repo._subtoppath
4376 del repo._subtoppath
4363
4377
4364
4378
4365 @command(
4379 @command(
4366 b'init',
4380 b'init',
4367 remoteopts,
4381 remoteopts,
4368 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4382 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4369 helpcategory=command.CATEGORY_REPO_CREATION,
4383 helpcategory=command.CATEGORY_REPO_CREATION,
4370 helpbasic=True,
4384 helpbasic=True,
4371 norepo=True,
4385 norepo=True,
4372 )
4386 )
4373 def init(ui, dest=b".", **opts):
4387 def init(ui, dest=b".", **opts):
4374 """create a new repository in the given directory
4388 """create a new repository in the given directory
4375
4389
4376 Initialize a new repository in the given directory. If the given
4390 Initialize a new repository in the given directory. If the given
4377 directory does not exist, it will be created.
4391 directory does not exist, it will be created.
4378
4392
4379 If no directory is given, the current directory is used.
4393 If no directory is given, the current directory is used.
4380
4394
4381 It is possible to specify an ``ssh://`` URL as the destination.
4395 It is possible to specify an ``ssh://`` URL as the destination.
4382 See :hg:`help urls` for more information.
4396 See :hg:`help urls` for more information.
4383
4397
4384 Returns 0 on success.
4398 Returns 0 on success.
4385 """
4399 """
4386 opts = pycompat.byteskwargs(opts)
4400 opts = pycompat.byteskwargs(opts)
4387 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4401 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4388
4402
4389
4403
4390 @command(
4404 @command(
4391 b'locate',
4405 b'locate',
4392 [
4406 [
4393 (
4407 (
4394 b'r',
4408 b'r',
4395 b'rev',
4409 b'rev',
4396 b'',
4410 b'',
4397 _(b'search the repository as it is in REV'),
4411 _(b'search the repository as it is in REV'),
4398 _(b'REV'),
4412 _(b'REV'),
4399 ),
4413 ),
4400 (
4414 (
4401 b'0',
4415 b'0',
4402 b'print0',
4416 b'print0',
4403 None,
4417 None,
4404 _(b'end filenames with NUL, for use with xargs'),
4418 _(b'end filenames with NUL, for use with xargs'),
4405 ),
4419 ),
4406 (
4420 (
4407 b'f',
4421 b'f',
4408 b'fullpath',
4422 b'fullpath',
4409 None,
4423 None,
4410 _(b'print complete paths from the filesystem root'),
4424 _(b'print complete paths from the filesystem root'),
4411 ),
4425 ),
4412 ]
4426 ]
4413 + walkopts,
4427 + walkopts,
4414 _(b'[OPTION]... [PATTERN]...'),
4428 _(b'[OPTION]... [PATTERN]...'),
4415 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4429 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4416 )
4430 )
4417 def locate(ui, repo, *pats, **opts):
4431 def locate(ui, repo, *pats, **opts):
4418 """locate files matching specific patterns (DEPRECATED)
4432 """locate files matching specific patterns (DEPRECATED)
4419
4433
4420 Print files under Mercurial control in the working directory whose
4434 Print files under Mercurial control in the working directory whose
4421 names match the given patterns.
4435 names match the given patterns.
4422
4436
4423 By default, this command searches all directories in the working
4437 By default, this command searches all directories in the working
4424 directory. To search just the current directory and its
4438 directory. To search just the current directory and its
4425 subdirectories, use "--include .".
4439 subdirectories, use "--include .".
4426
4440
4427 If no patterns are given to match, this command prints the names
4441 If no patterns are given to match, this command prints the names
4428 of all files under Mercurial control in the working directory.
4442 of all files under Mercurial control in the working directory.
4429
4443
4430 If you want to feed the output of this command into the "xargs"
4444 If you want to feed the output of this command into the "xargs"
4431 command, use the -0 option to both this command and "xargs". This
4445 command, use the -0 option to both this command and "xargs". This
4432 will avoid the problem of "xargs" treating single filenames that
4446 will avoid the problem of "xargs" treating single filenames that
4433 contain whitespace as multiple filenames.
4447 contain whitespace as multiple filenames.
4434
4448
4435 See :hg:`help files` for a more versatile command.
4449 See :hg:`help files` for a more versatile command.
4436
4450
4437 Returns 0 if a match is found, 1 otherwise.
4451 Returns 0 if a match is found, 1 otherwise.
4438 """
4452 """
4439 opts = pycompat.byteskwargs(opts)
4453 opts = pycompat.byteskwargs(opts)
4440 if opts.get(b'print0'):
4454 if opts.get(b'print0'):
4441 end = b'\0'
4455 end = b'\0'
4442 else:
4456 else:
4443 end = b'\n'
4457 end = b'\n'
4444 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4458 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4445
4459
4446 ret = 1
4460 ret = 1
4447 m = scmutil.match(
4461 m = scmutil.match(
4448 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4462 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4449 )
4463 )
4450
4464
4451 ui.pager(b'locate')
4465 ui.pager(b'locate')
4452 if ctx.rev() is None:
4466 if ctx.rev() is None:
4453 # When run on the working copy, "locate" includes removed files, so
4467 # When run on the working copy, "locate" includes removed files, so
4454 # we get the list of files from the dirstate.
4468 # we get the list of files from the dirstate.
4455 filesgen = sorted(repo.dirstate.matches(m))
4469 filesgen = sorted(repo.dirstate.matches(m))
4456 else:
4470 else:
4457 filesgen = ctx.matches(m)
4471 filesgen = ctx.matches(m)
4458 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4472 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4459 for abs in filesgen:
4473 for abs in filesgen:
4460 if opts.get(b'fullpath'):
4474 if opts.get(b'fullpath'):
4461 ui.write(repo.wjoin(abs), end)
4475 ui.write(repo.wjoin(abs), end)
4462 else:
4476 else:
4463 ui.write(uipathfn(abs), end)
4477 ui.write(uipathfn(abs), end)
4464 ret = 0
4478 ret = 0
4465
4479
4466 return ret
4480 return ret
4467
4481
4468
4482
4469 @command(
4483 @command(
4470 b'log|history',
4484 b'log|history',
4471 [
4485 [
4472 (
4486 (
4473 b'f',
4487 b'f',
4474 b'follow',
4488 b'follow',
4475 None,
4489 None,
4476 _(
4490 _(
4477 b'follow changeset history, or file history across copies and renames'
4491 b'follow changeset history, or file history across copies and renames'
4478 ),
4492 ),
4479 ),
4493 ),
4480 (
4494 (
4481 b'',
4495 b'',
4482 b'follow-first',
4496 b'follow-first',
4483 None,
4497 None,
4484 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4498 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4485 ),
4499 ),
4486 (
4500 (
4487 b'd',
4501 b'd',
4488 b'date',
4502 b'date',
4489 b'',
4503 b'',
4490 _(b'show revisions matching date spec'),
4504 _(b'show revisions matching date spec'),
4491 _(b'DATE'),
4505 _(b'DATE'),
4492 ),
4506 ),
4493 (b'C', b'copies', None, _(b'show copied files')),
4507 (b'C', b'copies', None, _(b'show copied files')),
4494 (
4508 (
4495 b'k',
4509 b'k',
4496 b'keyword',
4510 b'keyword',
4497 [],
4511 [],
4498 _(b'do case-insensitive search for a given text'),
4512 _(b'do case-insensitive search for a given text'),
4499 _(b'TEXT'),
4513 _(b'TEXT'),
4500 ),
4514 ),
4501 (
4515 (
4502 b'r',
4516 b'r',
4503 b'rev',
4517 b'rev',
4504 [],
4518 [],
4505 _(b'show the specified revision or revset'),
4519 _(b'show the specified revision or revset'),
4506 _(b'REV'),
4520 _(b'REV'),
4507 ),
4521 ),
4508 (
4522 (
4509 b'L',
4523 b'L',
4510 b'line-range',
4524 b'line-range',
4511 [],
4525 [],
4512 _(b'follow line range of specified file (EXPERIMENTAL)'),
4526 _(b'follow line range of specified file (EXPERIMENTAL)'),
4513 _(b'FILE,RANGE'),
4527 _(b'FILE,RANGE'),
4514 ),
4528 ),
4515 (
4529 (
4516 b'',
4530 b'',
4517 b'removed',
4531 b'removed',
4518 None,
4532 None,
4519 _(b'include revisions where files were removed'),
4533 _(b'include revisions where files were removed'),
4520 ),
4534 ),
4521 (
4535 (
4522 b'm',
4536 b'm',
4523 b'only-merges',
4537 b'only-merges',
4524 None,
4538 None,
4525 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4539 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4526 ),
4540 ),
4527 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4541 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4528 (
4542 (
4529 b'',
4543 b'',
4530 b'only-branch',
4544 b'only-branch',
4531 [],
4545 [],
4532 _(
4546 _(
4533 b'show only changesets within the given named branch (DEPRECATED)'
4547 b'show only changesets within the given named branch (DEPRECATED)'
4534 ),
4548 ),
4535 _(b'BRANCH'),
4549 _(b'BRANCH'),
4536 ),
4550 ),
4537 (
4551 (
4538 b'b',
4552 b'b',
4539 b'branch',
4553 b'branch',
4540 [],
4554 [],
4541 _(b'show changesets within the given named branch'),
4555 _(b'show changesets within the given named branch'),
4542 _(b'BRANCH'),
4556 _(b'BRANCH'),
4543 ),
4557 ),
4544 (
4558 (
4545 b'P',
4559 b'P',
4546 b'prune',
4560 b'prune',
4547 [],
4561 [],
4548 _(b'do not display revision or any of its ancestors'),
4562 _(b'do not display revision or any of its ancestors'),
4549 _(b'REV'),
4563 _(b'REV'),
4550 ),
4564 ),
4551 ]
4565 ]
4552 + logopts
4566 + logopts
4553 + walkopts,
4567 + walkopts,
4554 _(b'[OPTION]... [FILE]'),
4568 _(b'[OPTION]... [FILE]'),
4555 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4569 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4556 helpbasic=True,
4570 helpbasic=True,
4557 inferrepo=True,
4571 inferrepo=True,
4558 intents={INTENT_READONLY},
4572 intents={INTENT_READONLY},
4559 )
4573 )
4560 def log(ui, repo, *pats, **opts):
4574 def log(ui, repo, *pats, **opts):
4561 """show revision history of entire repository or files
4575 """show revision history of entire repository or files
4562
4576
4563 Print the revision history of the specified files or the entire
4577 Print the revision history of the specified files or the entire
4564 project.
4578 project.
4565
4579
4566 If no revision range is specified, the default is ``tip:0`` unless
4580 If no revision range is specified, the default is ``tip:0`` unless
4567 --follow is set, in which case the working directory parent is
4581 --follow is set, in which case the working directory parent is
4568 used as the starting revision.
4582 used as the starting revision.
4569
4583
4570 File history is shown without following rename or copy history of
4584 File history is shown without following rename or copy history of
4571 files. Use -f/--follow with a filename to follow history across
4585 files. Use -f/--follow with a filename to follow history across
4572 renames and copies. --follow without a filename will only show
4586 renames and copies. --follow without a filename will only show
4573 ancestors of the starting revision.
4587 ancestors of the starting revision.
4574
4588
4575 By default this command prints revision number and changeset id,
4589 By default this command prints revision number and changeset id,
4576 tags, non-trivial parents, user, date and time, and a summary for
4590 tags, non-trivial parents, user, date and time, and a summary for
4577 each commit. When the -v/--verbose switch is used, the list of
4591 each commit. When the -v/--verbose switch is used, the list of
4578 changed files and full commit message are shown.
4592 changed files and full commit message are shown.
4579
4593
4580 With --graph the revisions are shown as an ASCII art DAG with the most
4594 With --graph the revisions are shown as an ASCII art DAG with the most
4581 recent changeset at the top.
4595 recent changeset at the top.
4582 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4596 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
4583 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4597 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4584 changeset from the lines below is a parent of the 'o' merge on the same
4598 changeset from the lines below is a parent of the 'o' merge on the same
4585 line.
4599 line.
4586 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4600 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4587 of a '|' indicates one or more revisions in a path are omitted.
4601 of a '|' indicates one or more revisions in a path are omitted.
4588
4602
4589 .. container:: verbose
4603 .. container:: verbose
4590
4604
4591 Use -L/--line-range FILE,M:N options to follow the history of lines
4605 Use -L/--line-range FILE,M:N options to follow the history of lines
4592 from M to N in FILE. With -p/--patch only diff hunks affecting
4606 from M to N in FILE. With -p/--patch only diff hunks affecting
4593 specified line range will be shown. This option requires --follow;
4607 specified line range will be shown. This option requires --follow;
4594 it can be specified multiple times. Currently, this option is not
4608 it can be specified multiple times. Currently, this option is not
4595 compatible with --graph. This option is experimental.
4609 compatible with --graph. This option is experimental.
4596
4610
4597 .. note::
4611 .. note::
4598
4612
4599 :hg:`log --patch` may generate unexpected diff output for merge
4613 :hg:`log --patch` may generate unexpected diff output for merge
4600 changesets, as it will only compare the merge changeset against
4614 changesets, as it will only compare the merge changeset against
4601 its first parent. Also, only files different from BOTH parents
4615 its first parent. Also, only files different from BOTH parents
4602 will appear in files:.
4616 will appear in files:.
4603
4617
4604 .. note::
4618 .. note::
4605
4619
4606 For performance reasons, :hg:`log FILE` may omit duplicate changes
4620 For performance reasons, :hg:`log FILE` may omit duplicate changes
4607 made on branches and will not show removals or mode changes. To
4621 made on branches and will not show removals or mode changes. To
4608 see all such changes, use the --removed switch.
4622 see all such changes, use the --removed switch.
4609
4623
4610 .. container:: verbose
4624 .. container:: verbose
4611
4625
4612 .. note::
4626 .. note::
4613
4627
4614 The history resulting from -L/--line-range options depends on diff
4628 The history resulting from -L/--line-range options depends on diff
4615 options; for instance if white-spaces are ignored, respective changes
4629 options; for instance if white-spaces are ignored, respective changes
4616 with only white-spaces in specified line range will not be listed.
4630 with only white-spaces in specified line range will not be listed.
4617
4631
4618 .. container:: verbose
4632 .. container:: verbose
4619
4633
4620 Some examples:
4634 Some examples:
4621
4635
4622 - changesets with full descriptions and file lists::
4636 - changesets with full descriptions and file lists::
4623
4637
4624 hg log -v
4638 hg log -v
4625
4639
4626 - changesets ancestral to the working directory::
4640 - changesets ancestral to the working directory::
4627
4641
4628 hg log -f
4642 hg log -f
4629
4643
4630 - last 10 commits on the current branch::
4644 - last 10 commits on the current branch::
4631
4645
4632 hg log -l 10 -b .
4646 hg log -l 10 -b .
4633
4647
4634 - changesets showing all modifications of a file, including removals::
4648 - changesets showing all modifications of a file, including removals::
4635
4649
4636 hg log --removed file.c
4650 hg log --removed file.c
4637
4651
4638 - all changesets that touch a directory, with diffs, excluding merges::
4652 - all changesets that touch a directory, with diffs, excluding merges::
4639
4653
4640 hg log -Mp lib/
4654 hg log -Mp lib/
4641
4655
4642 - all revision numbers that match a keyword::
4656 - all revision numbers that match a keyword::
4643
4657
4644 hg log -k bug --template "{rev}\\n"
4658 hg log -k bug --template "{rev}\\n"
4645
4659
4646 - the full hash identifier of the working directory parent::
4660 - the full hash identifier of the working directory parent::
4647
4661
4648 hg log -r . --template "{node}\\n"
4662 hg log -r . --template "{node}\\n"
4649
4663
4650 - list available log templates::
4664 - list available log templates::
4651
4665
4652 hg log -T list
4666 hg log -T list
4653
4667
4654 - check if a given changeset is included in a tagged release::
4668 - check if a given changeset is included in a tagged release::
4655
4669
4656 hg log -r "a21ccf and ancestor(1.9)"
4670 hg log -r "a21ccf and ancestor(1.9)"
4657
4671
4658 - find all changesets by some user in a date range::
4672 - find all changesets by some user in a date range::
4659
4673
4660 hg log -k alice -d "may 2008 to jul 2008"
4674 hg log -k alice -d "may 2008 to jul 2008"
4661
4675
4662 - summary of all changesets after the last tag::
4676 - summary of all changesets after the last tag::
4663
4677
4664 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4678 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4665
4679
4666 - changesets touching lines 13 to 23 for file.c::
4680 - changesets touching lines 13 to 23 for file.c::
4667
4681
4668 hg log -L file.c,13:23
4682 hg log -L file.c,13:23
4669
4683
4670 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4684 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4671 main.c with patch::
4685 main.c with patch::
4672
4686
4673 hg log -L file.c,13:23 -L main.c,2:6 -p
4687 hg log -L file.c,13:23 -L main.c,2:6 -p
4674
4688
4675 See :hg:`help dates` for a list of formats valid for -d/--date.
4689 See :hg:`help dates` for a list of formats valid for -d/--date.
4676
4690
4677 See :hg:`help revisions` for more about specifying and ordering
4691 See :hg:`help revisions` for more about specifying and ordering
4678 revisions.
4692 revisions.
4679
4693
4680 See :hg:`help templates` for more about pre-packaged styles and
4694 See :hg:`help templates` for more about pre-packaged styles and
4681 specifying custom templates. The default template used by the log
4695 specifying custom templates. The default template used by the log
4682 command can be customized via the ``ui.logtemplate`` configuration
4696 command can be customized via the ``ui.logtemplate`` configuration
4683 setting.
4697 setting.
4684
4698
4685 Returns 0 on success.
4699 Returns 0 on success.
4686
4700
4687 """
4701 """
4688 opts = pycompat.byteskwargs(opts)
4702 opts = pycompat.byteskwargs(opts)
4689 linerange = opts.get(b'line_range')
4703 linerange = opts.get(b'line_range')
4690
4704
4691 if linerange and not opts.get(b'follow'):
4705 if linerange and not opts.get(b'follow'):
4692 raise error.Abort(_(b'--line-range requires --follow'))
4706 raise error.Abort(_(b'--line-range requires --follow'))
4693
4707
4694 if linerange and pats:
4708 if linerange and pats:
4695 # TODO: take pats as patterns with no line-range filter
4709 # TODO: take pats as patterns with no line-range filter
4696 raise error.Abort(
4710 raise error.Abort(
4697 _(b'FILE arguments are not compatible with --line-range option')
4711 _(b'FILE arguments are not compatible with --line-range option')
4698 )
4712 )
4699
4713
4700 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4714 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4701 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4715 revs, differ = logcmdutil.getrevs(repo, pats, opts)
4702 if linerange:
4716 if linerange:
4703 # TODO: should follow file history from logcmdutil._initialrevs(),
4717 # TODO: should follow file history from logcmdutil._initialrevs(),
4704 # then filter the result by logcmdutil._makerevset() and --limit
4718 # then filter the result by logcmdutil._makerevset() and --limit
4705 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4719 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4706
4720
4707 getcopies = None
4721 getcopies = None
4708 if opts.get(b'copies'):
4722 if opts.get(b'copies'):
4709 endrev = None
4723 endrev = None
4710 if revs:
4724 if revs:
4711 endrev = revs.max() + 1
4725 endrev = revs.max() + 1
4712 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4726 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4713
4727
4714 ui.pager(b'log')
4728 ui.pager(b'log')
4715 displayer = logcmdutil.changesetdisplayer(
4729 displayer = logcmdutil.changesetdisplayer(
4716 ui, repo, opts, differ, buffered=True
4730 ui, repo, opts, differ, buffered=True
4717 )
4731 )
4718 if opts.get(b'graph'):
4732 if opts.get(b'graph'):
4719 displayfn = logcmdutil.displaygraphrevs
4733 displayfn = logcmdutil.displaygraphrevs
4720 else:
4734 else:
4721 displayfn = logcmdutil.displayrevs
4735 displayfn = logcmdutil.displayrevs
4722 displayfn(ui, repo, revs, displayer, getcopies)
4736 displayfn(ui, repo, revs, displayer, getcopies)
4723
4737
4724
4738
4725 @command(
4739 @command(
4726 b'manifest',
4740 b'manifest',
4727 [
4741 [
4728 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4742 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4729 (b'', b'all', False, _(b"list files from all revisions")),
4743 (b'', b'all', False, _(b"list files from all revisions")),
4730 ]
4744 ]
4731 + formatteropts,
4745 + formatteropts,
4732 _(b'[-r REV]'),
4746 _(b'[-r REV]'),
4733 helpcategory=command.CATEGORY_MAINTENANCE,
4747 helpcategory=command.CATEGORY_MAINTENANCE,
4734 intents={INTENT_READONLY},
4748 intents={INTENT_READONLY},
4735 )
4749 )
4736 def manifest(ui, repo, node=None, rev=None, **opts):
4750 def manifest(ui, repo, node=None, rev=None, **opts):
4737 """output the current or given revision of the project manifest
4751 """output the current or given revision of the project manifest
4738
4752
4739 Print a list of version controlled files for the given revision.
4753 Print a list of version controlled files for the given revision.
4740 If no revision is given, the first parent of the working directory
4754 If no revision is given, the first parent of the working directory
4741 is used, or the null revision if no revision is checked out.
4755 is used, or the null revision if no revision is checked out.
4742
4756
4743 With -v, print file permissions, symlink and executable bits.
4757 With -v, print file permissions, symlink and executable bits.
4744 With --debug, print file revision hashes.
4758 With --debug, print file revision hashes.
4745
4759
4746 If option --all is specified, the list of all files from all revisions
4760 If option --all is specified, the list of all files from all revisions
4747 is printed. This includes deleted and renamed files.
4761 is printed. This includes deleted and renamed files.
4748
4762
4749 Returns 0 on success.
4763 Returns 0 on success.
4750 """
4764 """
4751 opts = pycompat.byteskwargs(opts)
4765 opts = pycompat.byteskwargs(opts)
4752 fm = ui.formatter(b'manifest', opts)
4766 fm = ui.formatter(b'manifest', opts)
4753
4767
4754 if opts.get(b'all'):
4768 if opts.get(b'all'):
4755 if rev or node:
4769 if rev or node:
4756 raise error.Abort(_(b"can't specify a revision with --all"))
4770 raise error.Abort(_(b"can't specify a revision with --all"))
4757
4771
4758 res = set()
4772 res = set()
4759 for rev in repo:
4773 for rev in repo:
4760 ctx = repo[rev]
4774 ctx = repo[rev]
4761 res |= set(ctx.files())
4775 res |= set(ctx.files())
4762
4776
4763 ui.pager(b'manifest')
4777 ui.pager(b'manifest')
4764 for f in sorted(res):
4778 for f in sorted(res):
4765 fm.startitem()
4779 fm.startitem()
4766 fm.write(b"path", b'%s\n', f)
4780 fm.write(b"path", b'%s\n', f)
4767 fm.end()
4781 fm.end()
4768 return
4782 return
4769
4783
4770 if rev and node:
4784 if rev and node:
4771 raise error.Abort(_(b"please specify just one revision"))
4785 raise error.Abort(_(b"please specify just one revision"))
4772
4786
4773 if not node:
4787 if not node:
4774 node = rev
4788 node = rev
4775
4789
4776 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4790 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4777 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4791 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4778 if node:
4792 if node:
4779 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4793 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4780 ctx = scmutil.revsingle(repo, node)
4794 ctx = scmutil.revsingle(repo, node)
4781 mf = ctx.manifest()
4795 mf = ctx.manifest()
4782 ui.pager(b'manifest')
4796 ui.pager(b'manifest')
4783 for f in ctx:
4797 for f in ctx:
4784 fm.startitem()
4798 fm.startitem()
4785 fm.context(ctx=ctx)
4799 fm.context(ctx=ctx)
4786 fl = ctx[f].flags()
4800 fl = ctx[f].flags()
4787 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4801 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4788 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4802 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4789 fm.write(b'path', b'%s\n', f)
4803 fm.write(b'path', b'%s\n', f)
4790 fm.end()
4804 fm.end()
4791
4805
4792
4806
4793 @command(
4807 @command(
4794 b'merge',
4808 b'merge',
4795 [
4809 [
4796 (
4810 (
4797 b'f',
4811 b'f',
4798 b'force',
4812 b'force',
4799 None,
4813 None,
4800 _(b'force a merge including outstanding changes (DEPRECATED)'),
4814 _(b'force a merge including outstanding changes (DEPRECATED)'),
4801 ),
4815 ),
4802 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4816 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4803 (
4817 (
4804 b'P',
4818 b'P',
4805 b'preview',
4819 b'preview',
4806 None,
4820 None,
4807 _(b'review revisions to merge (no merge is performed)'),
4821 _(b'review revisions to merge (no merge is performed)'),
4808 ),
4822 ),
4809 (b'', b'abort', None, _(b'abort the ongoing merge')),
4823 (b'', b'abort', None, _(b'abort the ongoing merge')),
4810 ]
4824 ]
4811 + mergetoolopts,
4825 + mergetoolopts,
4812 _(b'[-P] [[-r] REV]'),
4826 _(b'[-P] [[-r] REV]'),
4813 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4827 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4814 helpbasic=True,
4828 helpbasic=True,
4815 )
4829 )
4816 def merge(ui, repo, node=None, **opts):
4830 def merge(ui, repo, node=None, **opts):
4817 """merge another revision into working directory
4831 """merge another revision into working directory
4818
4832
4819 The current working directory is updated with all changes made in
4833 The current working directory is updated with all changes made in
4820 the requested revision since the last common predecessor revision.
4834 the requested revision since the last common predecessor revision.
4821
4835
4822 Files that changed between either parent are marked as changed for
4836 Files that changed between either parent are marked as changed for
4823 the next commit and a commit must be performed before any further
4837 the next commit and a commit must be performed before any further
4824 updates to the repository are allowed. The next commit will have
4838 updates to the repository are allowed. The next commit will have
4825 two parents.
4839 two parents.
4826
4840
4827 ``--tool`` can be used to specify the merge tool used for file
4841 ``--tool`` can be used to specify the merge tool used for file
4828 merges. It overrides the HGMERGE environment variable and your
4842 merges. It overrides the HGMERGE environment variable and your
4829 configuration files. See :hg:`help merge-tools` for options.
4843 configuration files. See :hg:`help merge-tools` for options.
4830
4844
4831 If no revision is specified, the working directory's parent is a
4845 If no revision is specified, the working directory's parent is a
4832 head revision, and the current branch contains exactly one other
4846 head revision, and the current branch contains exactly one other
4833 head, the other head is merged with by default. Otherwise, an
4847 head, the other head is merged with by default. Otherwise, an
4834 explicit revision with which to merge must be provided.
4848 explicit revision with which to merge must be provided.
4835
4849
4836 See :hg:`help resolve` for information on handling file conflicts.
4850 See :hg:`help resolve` for information on handling file conflicts.
4837
4851
4838 To undo an uncommitted merge, use :hg:`merge --abort` which
4852 To undo an uncommitted merge, use :hg:`merge --abort` which
4839 will check out a clean copy of the original merge parent, losing
4853 will check out a clean copy of the original merge parent, losing
4840 all changes.
4854 all changes.
4841
4855
4842 Returns 0 on success, 1 if there are unresolved files.
4856 Returns 0 on success, 1 if there are unresolved files.
4843 """
4857 """
4844
4858
4845 opts = pycompat.byteskwargs(opts)
4859 opts = pycompat.byteskwargs(opts)
4846 abort = opts.get(b'abort')
4860 abort = opts.get(b'abort')
4847 if abort and repo.dirstate.p2() == nullid:
4861 if abort and repo.dirstate.p2() == nullid:
4848 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4862 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4849 if abort:
4863 if abort:
4850 state = cmdutil.getunfinishedstate(repo)
4864 state = cmdutil.getunfinishedstate(repo)
4851 if state and state._opname != b'merge':
4865 if state and state._opname != b'merge':
4852 raise error.Abort(
4866 raise error.Abort(
4853 _(b'cannot abort merge with %s in progress') % (state._opname),
4867 _(b'cannot abort merge with %s in progress') % (state._opname),
4854 hint=state.hint(),
4868 hint=state.hint(),
4855 )
4869 )
4856 if node:
4870 if node:
4857 raise error.Abort(_(b"cannot specify a node with --abort"))
4871 raise error.Abort(_(b"cannot specify a node with --abort"))
4858 if opts.get(b'rev'):
4872 if opts.get(b'rev'):
4859 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4873 raise error.Abort(_(b"cannot specify both --rev and --abort"))
4860 if opts.get(b'preview'):
4874 if opts.get(b'preview'):
4861 raise error.Abort(_(b"cannot specify --preview with --abort"))
4875 raise error.Abort(_(b"cannot specify --preview with --abort"))
4862 if opts.get(b'rev') and node:
4876 if opts.get(b'rev') and node:
4863 raise error.Abort(_(b"please specify just one revision"))
4877 raise error.Abort(_(b"please specify just one revision"))
4864 if not node:
4878 if not node:
4865 node = opts.get(b'rev')
4879 node = opts.get(b'rev')
4866
4880
4867 if node:
4881 if node:
4868 node = scmutil.revsingle(repo, node).node()
4882 node = scmutil.revsingle(repo, node).node()
4869
4883
4870 if not node and not abort:
4884 if not node and not abort:
4871 node = repo[destutil.destmerge(repo)].node()
4885 node = repo[destutil.destmerge(repo)].node()
4872
4886
4873 if opts.get(b'preview'):
4887 if opts.get(b'preview'):
4874 # find nodes that are ancestors of p2 but not of p1
4888 # find nodes that are ancestors of p2 but not of p1
4875 p1 = repo.lookup(b'.')
4889 p1 = repo.lookup(b'.')
4876 p2 = node
4890 p2 = node
4877 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4891 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4878
4892
4879 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4893 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4880 for node in nodes:
4894 for node in nodes:
4881 displayer.show(repo[node])
4895 displayer.show(repo[node])
4882 displayer.close()
4896 displayer.close()
4883 return 0
4897 return 0
4884
4898
4885 # ui.forcemerge is an internal variable, do not document
4899 # ui.forcemerge is an internal variable, do not document
4886 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4900 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4887 with ui.configoverride(overrides, b'merge'):
4901 with ui.configoverride(overrides, b'merge'):
4888 force = opts.get(b'force')
4902 force = opts.get(b'force')
4889 labels = [b'working copy', b'merge rev']
4903 labels = [b'working copy', b'merge rev']
4890 return hg.merge(
4904 return hg.merge(
4891 repo,
4905 repo,
4892 node,
4906 node,
4893 force=force,
4907 force=force,
4894 mergeforce=force,
4908 mergeforce=force,
4895 labels=labels,
4909 labels=labels,
4896 abort=abort,
4910 abort=abort,
4897 )
4911 )
4898
4912
4899
4913
4900 statemod.addunfinished(
4914 statemod.addunfinished(
4901 b'merge',
4915 b'merge',
4902 fname=None,
4916 fname=None,
4903 clearable=True,
4917 clearable=True,
4904 allowcommit=True,
4918 allowcommit=True,
4905 cmdmsg=_(b'outstanding uncommitted merge'),
4919 cmdmsg=_(b'outstanding uncommitted merge'),
4906 abortfunc=hg.abortmerge,
4920 abortfunc=hg.abortmerge,
4907 statushint=_(
4921 statushint=_(
4908 b'To continue: hg commit\nTo abort: hg merge --abort'
4922 b'To continue: hg commit\nTo abort: hg merge --abort'
4909 ),
4923 ),
4910 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4924 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4911 )
4925 )
4912
4926
4913
4927
4914 @command(
4928 @command(
4915 b'outgoing|out',
4929 b'outgoing|out',
4916 [
4930 [
4917 (
4931 (
4918 b'f',
4932 b'f',
4919 b'force',
4933 b'force',
4920 None,
4934 None,
4921 _(b'run even when the destination is unrelated'),
4935 _(b'run even when the destination is unrelated'),
4922 ),
4936 ),
4923 (
4937 (
4924 b'r',
4938 b'r',
4925 b'rev',
4939 b'rev',
4926 [],
4940 [],
4927 _(b'a changeset intended to be included in the destination'),
4941 _(b'a changeset intended to be included in the destination'),
4928 _(b'REV'),
4942 _(b'REV'),
4929 ),
4943 ),
4930 (b'n', b'newest-first', None, _(b'show newest record first')),
4944 (b'n', b'newest-first', None, _(b'show newest record first')),
4931 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4945 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4932 (
4946 (
4933 b'b',
4947 b'b',
4934 b'branch',
4948 b'branch',
4935 [],
4949 [],
4936 _(b'a specific branch you would like to push'),
4950 _(b'a specific branch you would like to push'),
4937 _(b'BRANCH'),
4951 _(b'BRANCH'),
4938 ),
4952 ),
4939 ]
4953 ]
4940 + logopts
4954 + logopts
4941 + remoteopts
4955 + remoteopts
4942 + subrepoopts,
4956 + subrepoopts,
4943 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4957 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4944 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4958 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4945 )
4959 )
4946 def outgoing(ui, repo, dest=None, **opts):
4960 def outgoing(ui, repo, dest=None, **opts):
4947 """show changesets not found in the destination
4961 """show changesets not found in the destination
4948
4962
4949 Show changesets not found in the specified destination repository
4963 Show changesets not found in the specified destination repository
4950 or the default push location. These are the changesets that would
4964 or the default push location. These are the changesets that would
4951 be pushed if a push was requested.
4965 be pushed if a push was requested.
4952
4966
4953 See pull for details of valid destination formats.
4967 See pull for details of valid destination formats.
4954
4968
4955 .. container:: verbose
4969 .. container:: verbose
4956
4970
4957 With -B/--bookmarks, the result of bookmark comparison between
4971 With -B/--bookmarks, the result of bookmark comparison between
4958 local and remote repositories is displayed. With -v/--verbose,
4972 local and remote repositories is displayed. With -v/--verbose,
4959 status is also displayed for each bookmark like below::
4973 status is also displayed for each bookmark like below::
4960
4974
4961 BM1 01234567890a added
4975 BM1 01234567890a added
4962 BM2 deleted
4976 BM2 deleted
4963 BM3 234567890abc advanced
4977 BM3 234567890abc advanced
4964 BM4 34567890abcd diverged
4978 BM4 34567890abcd diverged
4965 BM5 4567890abcde changed
4979 BM5 4567890abcde changed
4966
4980
4967 The action taken when pushing depends on the
4981 The action taken when pushing depends on the
4968 status of each bookmark:
4982 status of each bookmark:
4969
4983
4970 :``added``: push with ``-B`` will create it
4984 :``added``: push with ``-B`` will create it
4971 :``deleted``: push with ``-B`` will delete it
4985 :``deleted``: push with ``-B`` will delete it
4972 :``advanced``: push will update it
4986 :``advanced``: push will update it
4973 :``diverged``: push with ``-B`` will update it
4987 :``diverged``: push with ``-B`` will update it
4974 :``changed``: push with ``-B`` will update it
4988 :``changed``: push with ``-B`` will update it
4975
4989
4976 From the point of view of pushing behavior, bookmarks
4990 From the point of view of pushing behavior, bookmarks
4977 existing only in the remote repository are treated as
4991 existing only in the remote repository are treated as
4978 ``deleted``, even if it is in fact added remotely.
4992 ``deleted``, even if it is in fact added remotely.
4979
4993
4980 Returns 0 if there are outgoing changes, 1 otherwise.
4994 Returns 0 if there are outgoing changes, 1 otherwise.
4981 """
4995 """
4982 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4996 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4983 # style URLs, so don't overwrite dest.
4997 # style URLs, so don't overwrite dest.
4984 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4998 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4985 if not path:
4999 if not path:
4986 raise error.Abort(
5000 raise error.Abort(
4987 _(b'default repository not configured!'),
5001 _(b'default repository not configured!'),
4988 hint=_(b"see 'hg help config.paths'"),
5002 hint=_(b"see 'hg help config.paths'"),
4989 )
5003 )
4990
5004
4991 opts = pycompat.byteskwargs(opts)
5005 opts = pycompat.byteskwargs(opts)
4992 if opts.get(b'graph'):
5006 if opts.get(b'graph'):
4993 logcmdutil.checkunsupportedgraphflags([], opts)
5007 logcmdutil.checkunsupportedgraphflags([], opts)
4994 o, other = hg._outgoing(ui, repo, dest, opts)
5008 o, other = hg._outgoing(ui, repo, dest, opts)
4995 if not o:
5009 if not o:
4996 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5010 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4997 return
5011 return
4998
5012
4999 revdag = logcmdutil.graphrevs(repo, o, opts)
5013 revdag = logcmdutil.graphrevs(repo, o, opts)
5000 ui.pager(b'outgoing')
5014 ui.pager(b'outgoing')
5001 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5015 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
5002 logcmdutil.displaygraph(
5016 logcmdutil.displaygraph(
5003 ui, repo, revdag, displayer, graphmod.asciiedges
5017 ui, repo, revdag, displayer, graphmod.asciiedges
5004 )
5018 )
5005 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5019 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5006 return 0
5020 return 0
5007
5021
5008 if opts.get(b'bookmarks'):
5022 if opts.get(b'bookmarks'):
5009 dest = path.pushloc or path.loc
5023 dest = path.pushloc or path.loc
5010 other = hg.peer(repo, opts, dest)
5024 other = hg.peer(repo, opts, dest)
5011 if b'bookmarks' not in other.listkeys(b'namespaces'):
5025 if b'bookmarks' not in other.listkeys(b'namespaces'):
5012 ui.warn(_(b"remote doesn't support bookmarks\n"))
5026 ui.warn(_(b"remote doesn't support bookmarks\n"))
5013 return 0
5027 return 0
5014 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5028 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
5015 ui.pager(b'outgoing')
5029 ui.pager(b'outgoing')
5016 return bookmarks.outgoing(ui, repo, other)
5030 return bookmarks.outgoing(ui, repo, other)
5017
5031
5018 repo._subtoppath = path.pushloc or path.loc
5032 repo._subtoppath = path.pushloc or path.loc
5019 try:
5033 try:
5020 return hg.outgoing(ui, repo, dest, opts)
5034 return hg.outgoing(ui, repo, dest, opts)
5021 finally:
5035 finally:
5022 del repo._subtoppath
5036 del repo._subtoppath
5023
5037
5024
5038
5025 @command(
5039 @command(
5026 b'parents',
5040 b'parents',
5027 [
5041 [
5028 (
5042 (
5029 b'r',
5043 b'r',
5030 b'rev',
5044 b'rev',
5031 b'',
5045 b'',
5032 _(b'show parents of the specified revision'),
5046 _(b'show parents of the specified revision'),
5033 _(b'REV'),
5047 _(b'REV'),
5034 ),
5048 ),
5035 ]
5049 ]
5036 + templateopts,
5050 + templateopts,
5037 _(b'[-r REV] [FILE]'),
5051 _(b'[-r REV] [FILE]'),
5038 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5052 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5039 inferrepo=True,
5053 inferrepo=True,
5040 )
5054 )
5041 def parents(ui, repo, file_=None, **opts):
5055 def parents(ui, repo, file_=None, **opts):
5042 """show the parents of the working directory or revision (DEPRECATED)
5056 """show the parents of the working directory or revision (DEPRECATED)
5043
5057
5044 Print the working directory's parent revisions. If a revision is
5058 Print the working directory's parent revisions. If a revision is
5045 given via -r/--rev, the parent of that revision will be printed.
5059 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
5060 If a file argument is given, the revision in which the file was
5047 last changed (before the working directory revision or the
5061 last changed (before the working directory revision or the
5048 argument to --rev if given) is printed.
5062 argument to --rev if given) is printed.
5049
5063
5050 This command is equivalent to::
5064 This command is equivalent to::
5051
5065
5052 hg log -r "p1()+p2()" or
5066 hg log -r "p1()+p2()" or
5053 hg log -r "p1(REV)+p2(REV)" or
5067 hg log -r "p1(REV)+p2(REV)" or
5054 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5068 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))"
5069 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5056
5070
5057 See :hg:`summary` and :hg:`help revsets` for related information.
5071 See :hg:`summary` and :hg:`help revsets` for related information.
5058
5072
5059 Returns 0 on success.
5073 Returns 0 on success.
5060 """
5074 """
5061
5075
5062 opts = pycompat.byteskwargs(opts)
5076 opts = pycompat.byteskwargs(opts)
5063 rev = opts.get(b'rev')
5077 rev = opts.get(b'rev')
5064 if rev:
5078 if rev:
5065 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5079 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5066 ctx = scmutil.revsingle(repo, rev, None)
5080 ctx = scmutil.revsingle(repo, rev, None)
5067
5081
5068 if file_:
5082 if file_:
5069 m = scmutil.match(ctx, (file_,), opts)
5083 m = scmutil.match(ctx, (file_,), opts)
5070 if m.anypats() or len(m.files()) != 1:
5084 if m.anypats() or len(m.files()) != 1:
5071 raise error.Abort(_(b'can only specify an explicit filename'))
5085 raise error.Abort(_(b'can only specify an explicit filename'))
5072 file_ = m.files()[0]
5086 file_ = m.files()[0]
5073 filenodes = []
5087 filenodes = []
5074 for cp in ctx.parents():
5088 for cp in ctx.parents():
5075 if not cp:
5089 if not cp:
5076 continue
5090 continue
5077 try:
5091 try:
5078 filenodes.append(cp.filenode(file_))
5092 filenodes.append(cp.filenode(file_))
5079 except error.LookupError:
5093 except error.LookupError:
5080 pass
5094 pass
5081 if not filenodes:
5095 if not filenodes:
5082 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5096 raise error.Abort(_(b"'%s' not found in manifest!") % file_)
5083 p = []
5097 p = []
5084 for fn in filenodes:
5098 for fn in filenodes:
5085 fctx = repo.filectx(file_, fileid=fn)
5099 fctx = repo.filectx(file_, fileid=fn)
5086 p.append(fctx.node())
5100 p.append(fctx.node())
5087 else:
5101 else:
5088 p = [cp.node() for cp in ctx.parents()]
5102 p = [cp.node() for cp in ctx.parents()]
5089
5103
5090 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5104 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5091 for n in p:
5105 for n in p:
5092 if n != nullid:
5106 if n != nullid:
5093 displayer.show(repo[n])
5107 displayer.show(repo[n])
5094 displayer.close()
5108 displayer.close()
5095
5109
5096
5110
5097 @command(
5111 @command(
5098 b'paths',
5112 b'paths',
5099 formatteropts,
5113 formatteropts,
5100 _(b'[NAME]'),
5114 _(b'[NAME]'),
5101 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5115 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5102 optionalrepo=True,
5116 optionalrepo=True,
5103 intents={INTENT_READONLY},
5117 intents={INTENT_READONLY},
5104 )
5118 )
5105 def paths(ui, repo, search=None, **opts):
5119 def paths(ui, repo, search=None, **opts):
5106 """show aliases for remote repositories
5120 """show aliases for remote repositories
5107
5121
5108 Show definition of symbolic path name NAME. If no name is given,
5122 Show definition of symbolic path name NAME. If no name is given,
5109 show definition of all available names.
5123 show definition of all available names.
5110
5124
5111 Option -q/--quiet suppresses all output when searching for NAME
5125 Option -q/--quiet suppresses all output when searching for NAME
5112 and shows only the path names when listing all definitions.
5126 and shows only the path names when listing all definitions.
5113
5127
5114 Path names are defined in the [paths] section of your
5128 Path names are defined in the [paths] section of your
5115 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5129 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5116 repository, ``.hg/hgrc`` is used, too.
5130 repository, ``.hg/hgrc`` is used, too.
5117
5131
5118 The path names ``default`` and ``default-push`` have a special
5132 The path names ``default`` and ``default-push`` have a special
5119 meaning. When performing a push or pull operation, they are used
5133 meaning. When performing a push or pull operation, they are used
5120 as fallbacks if no location is specified on the command-line.
5134 as fallbacks if no location is specified on the command-line.
5121 When ``default-push`` is set, it will be used for push and
5135 When ``default-push`` is set, it will be used for push and
5122 ``default`` will be used for pull; otherwise ``default`` is used
5136 ``default`` will be used for pull; otherwise ``default`` is used
5123 as the fallback for both. When cloning a repository, the clone
5137 as the fallback for both. When cloning a repository, the clone
5124 source is written as ``default`` in ``.hg/hgrc``.
5138 source is written as ``default`` in ``.hg/hgrc``.
5125
5139
5126 .. note::
5140 .. note::
5127
5141
5128 ``default`` and ``default-push`` apply to all inbound (e.g.
5142 ``default`` and ``default-push`` apply to all inbound (e.g.
5129 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5143 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5130 and :hg:`bundle`) operations.
5144 and :hg:`bundle`) operations.
5131
5145
5132 See :hg:`help urls` for more information.
5146 See :hg:`help urls` for more information.
5133
5147
5134 .. container:: verbose
5148 .. container:: verbose
5135
5149
5136 Template:
5150 Template:
5137
5151
5138 The following keywords are supported. See also :hg:`help templates`.
5152 The following keywords are supported. See also :hg:`help templates`.
5139
5153
5140 :name: String. Symbolic name of the path alias.
5154 :name: String. Symbolic name of the path alias.
5141 :pushurl: String. URL for push operations.
5155 :pushurl: String. URL for push operations.
5142 :url: String. URL or directory path for the other operations.
5156 :url: String. URL or directory path for the other operations.
5143
5157
5144 Returns 0 on success.
5158 Returns 0 on success.
5145 """
5159 """
5146
5160
5147 opts = pycompat.byteskwargs(opts)
5161 opts = pycompat.byteskwargs(opts)
5148 ui.pager(b'paths')
5162 ui.pager(b'paths')
5149 if search:
5163 if search:
5150 pathitems = [
5164 pathitems = [
5151 (name, path)
5165 (name, path)
5152 for name, path in pycompat.iteritems(ui.paths)
5166 for name, path in pycompat.iteritems(ui.paths)
5153 if name == search
5167 if name == search
5154 ]
5168 ]
5155 else:
5169 else:
5156 pathitems = sorted(pycompat.iteritems(ui.paths))
5170 pathitems = sorted(pycompat.iteritems(ui.paths))
5157
5171
5158 fm = ui.formatter(b'paths', opts)
5172 fm = ui.formatter(b'paths', opts)
5159 if fm.isplain():
5173 if fm.isplain():
5160 hidepassword = util.hidepassword
5174 hidepassword = util.hidepassword
5161 else:
5175 else:
5162 hidepassword = bytes
5176 hidepassword = bytes
5163 if ui.quiet:
5177 if ui.quiet:
5164 namefmt = b'%s\n'
5178 namefmt = b'%s\n'
5165 else:
5179 else:
5166 namefmt = b'%s = '
5180 namefmt = b'%s = '
5167 showsubopts = not search and not ui.quiet
5181 showsubopts = not search and not ui.quiet
5168
5182
5169 for name, path in pathitems:
5183 for name, path in pathitems:
5170 fm.startitem()
5184 fm.startitem()
5171 fm.condwrite(not search, b'name', namefmt, name)
5185 fm.condwrite(not search, b'name', namefmt, name)
5172 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5186 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5173 for subopt, value in sorted(path.suboptions.items()):
5187 for subopt, value in sorted(path.suboptions.items()):
5174 assert subopt not in (b'name', b'url')
5188 assert subopt not in (b'name', b'url')
5175 if showsubopts:
5189 if showsubopts:
5176 fm.plain(b'%s:%s = ' % (name, subopt))
5190 fm.plain(b'%s:%s = ' % (name, subopt))
5177 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5191 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5178
5192
5179 fm.end()
5193 fm.end()
5180
5194
5181 if search and not pathitems:
5195 if search and not pathitems:
5182 if not ui.quiet:
5196 if not ui.quiet:
5183 ui.warn(_(b"not found!\n"))
5197 ui.warn(_(b"not found!\n"))
5184 return 1
5198 return 1
5185 else:
5199 else:
5186 return 0
5200 return 0
5187
5201
5188
5202
5189 @command(
5203 @command(
5190 b'phase',
5204 b'phase',
5191 [
5205 [
5192 (b'p', b'public', False, _(b'set changeset phase to public')),
5206 (b'p', b'public', False, _(b'set changeset phase to public')),
5193 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5207 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5194 (b's', b'secret', False, _(b'set changeset phase to secret')),
5208 (b's', b'secret', False, _(b'set changeset phase to secret')),
5195 (b'f', b'force', False, _(b'allow to move boundary backward')),
5209 (b'f', b'force', False, _(b'allow to move boundary backward')),
5196 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5210 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5197 ],
5211 ],
5198 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5212 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5199 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5213 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5200 )
5214 )
5201 def phase(ui, repo, *revs, **opts):
5215 def phase(ui, repo, *revs, **opts):
5202 """set or show the current phase name
5216 """set or show the current phase name
5203
5217
5204 With no argument, show the phase name of the current revision(s).
5218 With no argument, show the phase name of the current revision(s).
5205
5219
5206 With one of -p/--public, -d/--draft or -s/--secret, change the
5220 With one of -p/--public, -d/--draft or -s/--secret, change the
5207 phase value of the specified revisions.
5221 phase value of the specified revisions.
5208
5222
5209 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5223 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::
5224 lower phase to a higher phase. Phases are ordered as follows::
5211
5225
5212 public < draft < secret
5226 public < draft < secret
5213
5227
5214 Returns 0 on success, 1 if some phases could not be changed.
5228 Returns 0 on success, 1 if some phases could not be changed.
5215
5229
5216 (For more information about the phases concept, see :hg:`help phases`.)
5230 (For more information about the phases concept, see :hg:`help phases`.)
5217 """
5231 """
5218 opts = pycompat.byteskwargs(opts)
5232 opts = pycompat.byteskwargs(opts)
5219 # search for a unique phase argument
5233 # search for a unique phase argument
5220 targetphase = None
5234 targetphase = None
5221 for idx, name in enumerate(phases.cmdphasenames):
5235 for idx, name in enumerate(phases.cmdphasenames):
5222 if opts[name]:
5236 if opts[name]:
5223 if targetphase is not None:
5237 if targetphase is not None:
5224 raise error.Abort(_(b'only one phase can be specified'))
5238 raise error.Abort(_(b'only one phase can be specified'))
5225 targetphase = idx
5239 targetphase = idx
5226
5240
5227 # look for specified revision
5241 # look for specified revision
5228 revs = list(revs)
5242 revs = list(revs)
5229 revs.extend(opts[b'rev'])
5243 revs.extend(opts[b'rev'])
5230 if not revs:
5244 if not revs:
5231 # display both parents as the second parent phase can influence
5245 # display both parents as the second parent phase can influence
5232 # the phase of a merge commit
5246 # the phase of a merge commit
5233 revs = [c.rev() for c in repo[None].parents()]
5247 revs = [c.rev() for c in repo[None].parents()]
5234
5248
5235 revs = scmutil.revrange(repo, revs)
5249 revs = scmutil.revrange(repo, revs)
5236
5250
5237 ret = 0
5251 ret = 0
5238 if targetphase is None:
5252 if targetphase is None:
5239 # display
5253 # display
5240 for r in revs:
5254 for r in revs:
5241 ctx = repo[r]
5255 ctx = repo[r]
5242 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5256 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5243 else:
5257 else:
5244 with repo.lock(), repo.transaction(b"phase") as tr:
5258 with repo.lock(), repo.transaction(b"phase") as tr:
5245 # set phase
5259 # set phase
5246 if not revs:
5260 if not revs:
5247 raise error.Abort(_(b'empty revision set'))
5261 raise error.Abort(_(b'empty revision set'))
5248 nodes = [repo[r].node() for r in revs]
5262 nodes = [repo[r].node() for r in revs]
5249 # moving revision from public to draft may hide them
5263 # moving revision from public to draft may hide them
5250 # We have to check result on an unfiltered repository
5264 # We have to check result on an unfiltered repository
5251 unfi = repo.unfiltered()
5265 unfi = repo.unfiltered()
5252 getphase = unfi._phasecache.phase
5266 getphase = unfi._phasecache.phase
5253 olddata = [getphase(unfi, r) for r in unfi]
5267 olddata = [getphase(unfi, r) for r in unfi]
5254 phases.advanceboundary(repo, tr, targetphase, nodes)
5268 phases.advanceboundary(repo, tr, targetphase, nodes)
5255 if opts[b'force']:
5269 if opts[b'force']:
5256 phases.retractboundary(repo, tr, targetphase, nodes)
5270 phases.retractboundary(repo, tr, targetphase, nodes)
5257 getphase = unfi._phasecache.phase
5271 getphase = unfi._phasecache.phase
5258 newdata = [getphase(unfi, r) for r in unfi]
5272 newdata = [getphase(unfi, r) for r in unfi]
5259 changes = sum(newdata[r] != olddata[r] for r in unfi)
5273 changes = sum(newdata[r] != olddata[r] for r in unfi)
5260 cl = unfi.changelog
5274 cl = unfi.changelog
5261 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5275 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5262 if rejected:
5276 if rejected:
5263 ui.warn(
5277 ui.warn(
5264 _(
5278 _(
5265 b'cannot move %i changesets to a higher '
5279 b'cannot move %i changesets to a higher '
5266 b'phase, use --force\n'
5280 b'phase, use --force\n'
5267 )
5281 )
5268 % len(rejected)
5282 % len(rejected)
5269 )
5283 )
5270 ret = 1
5284 ret = 1
5271 if changes:
5285 if changes:
5272 msg = _(b'phase changed for %i changesets\n') % changes
5286 msg = _(b'phase changed for %i changesets\n') % changes
5273 if ret:
5287 if ret:
5274 ui.status(msg)
5288 ui.status(msg)
5275 else:
5289 else:
5276 ui.note(msg)
5290 ui.note(msg)
5277 else:
5291 else:
5278 ui.warn(_(b'no phases changed\n'))
5292 ui.warn(_(b'no phases changed\n'))
5279 return ret
5293 return ret
5280
5294
5281
5295
5282 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5296 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5283 """Run after a changegroup has been added via pull/unbundle
5297 """Run after a changegroup has been added via pull/unbundle
5284
5298
5285 This takes arguments below:
5299 This takes arguments below:
5286
5300
5287 :modheads: change of heads by pull/unbundle
5301 :modheads: change of heads by pull/unbundle
5288 :optupdate: updating working directory is needed or not
5302 :optupdate: updating working directory is needed or not
5289 :checkout: update destination revision (or None to default destination)
5303 :checkout: update destination revision (or None to default destination)
5290 :brev: a name, which might be a bookmark to be activated after updating
5304 :brev: a name, which might be a bookmark to be activated after updating
5291 """
5305 """
5292 if modheads == 0:
5306 if modheads == 0:
5293 return
5307 return
5294 if optupdate:
5308 if optupdate:
5295 try:
5309 try:
5296 return hg.updatetotally(ui, repo, checkout, brev)
5310 return hg.updatetotally(ui, repo, checkout, brev)
5297 except error.UpdateAbort as inst:
5311 except error.UpdateAbort as inst:
5298 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5312 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5299 hint = inst.hint
5313 hint = inst.hint
5300 raise error.UpdateAbort(msg, hint=hint)
5314 raise error.UpdateAbort(msg, hint=hint)
5301 if modheads is not None and modheads > 1:
5315 if modheads is not None and modheads > 1:
5302 currentbranchheads = len(repo.branchheads())
5316 currentbranchheads = len(repo.branchheads())
5303 if currentbranchheads == modheads:
5317 if currentbranchheads == modheads:
5304 ui.status(
5318 ui.status(
5305 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5319 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5306 )
5320 )
5307 elif currentbranchheads > 1:
5321 elif currentbranchheads > 1:
5308 ui.status(
5322 ui.status(
5309 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5323 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5310 )
5324 )
5311 else:
5325 else:
5312 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5326 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5313 elif not ui.configbool(b'commands', b'update.requiredest'):
5327 elif not ui.configbool(b'commands', b'update.requiredest'):
5314 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5328 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5315
5329
5316
5330
5317 @command(
5331 @command(
5318 b'pull',
5332 b'pull',
5319 [
5333 [
5320 (
5334 (
5321 b'u',
5335 b'u',
5322 b'update',
5336 b'update',
5323 None,
5337 None,
5324 _(b'update to new branch head if new descendants were pulled'),
5338 _(b'update to new branch head if new descendants were pulled'),
5325 ),
5339 ),
5326 (
5340 (
5327 b'f',
5341 b'f',
5328 b'force',
5342 b'force',
5329 None,
5343 None,
5330 _(b'run even when remote repository is unrelated'),
5344 _(b'run even when remote repository is unrelated'),
5331 ),
5345 ),
5332 (
5346 (
5333 b'r',
5347 b'r',
5334 b'rev',
5348 b'rev',
5335 [],
5349 [],
5336 _(b'a remote changeset intended to be added'),
5350 _(b'a remote changeset intended to be added'),
5337 _(b'REV'),
5351 _(b'REV'),
5338 ),
5352 ),
5339 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5353 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5340 (
5354 (
5341 b'b',
5355 b'b',
5342 b'branch',
5356 b'branch',
5343 [],
5357 [],
5344 _(b'a specific branch you would like to pull'),
5358 _(b'a specific branch you would like to pull'),
5345 _(b'BRANCH'),
5359 _(b'BRANCH'),
5346 ),
5360 ),
5347 ]
5361 ]
5348 + remoteopts,
5362 + remoteopts,
5349 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5363 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5350 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5364 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5351 helpbasic=True,
5365 helpbasic=True,
5352 )
5366 )
5353 def pull(ui, repo, source=b"default", **opts):
5367 def pull(ui, repo, source=b"default", **opts):
5354 """pull changes from the specified source
5368 """pull changes from the specified source
5355
5369
5356 Pull changes from a remote repository to a local one.
5370 Pull changes from a remote repository to a local one.
5357
5371
5358 This finds all changes from the repository at the specified path
5372 This finds all changes from the repository at the specified path
5359 or URL and adds them to a local repository (the current one unless
5373 or URL and adds them to a local repository (the current one unless
5360 -R is specified). By default, this does not update the copy of the
5374 -R is specified). By default, this does not update the copy of the
5361 project in the working directory.
5375 project in the working directory.
5362
5376
5363 When cloning from servers that support it, Mercurial may fetch
5377 When cloning from servers that support it, Mercurial may fetch
5364 pre-generated data. When this is done, hooks operating on incoming
5378 pre-generated data. When this is done, hooks operating on incoming
5365 changesets and changegroups may fire more than once, once for each
5379 changesets and changegroups may fire more than once, once for each
5366 pre-generated bundle and as well as for any additional remaining
5380 pre-generated bundle and as well as for any additional remaining
5367 data. See :hg:`help -e clonebundles` for more.
5381 data. See :hg:`help -e clonebundles` for more.
5368
5382
5369 Use :hg:`incoming` if you want to see what would have been added
5383 Use :hg:`incoming` if you want to see what would have been added
5370 by a pull at the time you issued this command. If you then decide
5384 by a pull at the time you issued this command. If you then decide
5371 to add those changes to the repository, you should use :hg:`pull
5385 to add those changes to the repository, you should use :hg:`pull
5372 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5386 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5373
5387
5374 If SOURCE is omitted, the 'default' path will be used.
5388 If SOURCE is omitted, the 'default' path will be used.
5375 See :hg:`help urls` for more information.
5389 See :hg:`help urls` for more information.
5376
5390
5377 Specifying bookmark as ``.`` is equivalent to specifying the active
5391 Specifying bookmark as ``.`` is equivalent to specifying the active
5378 bookmark's name.
5392 bookmark's name.
5379
5393
5380 Returns 0 on success, 1 if an update had unresolved files.
5394 Returns 0 on success, 1 if an update had unresolved files.
5381 """
5395 """
5382
5396
5383 opts = pycompat.byteskwargs(opts)
5397 opts = pycompat.byteskwargs(opts)
5384 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5398 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5385 b'update'
5399 b'update'
5386 ):
5400 ):
5387 msg = _(b'update destination required by configuration')
5401 msg = _(b'update destination required by configuration')
5388 hint = _(b'use hg pull followed by hg update DEST')
5402 hint = _(b'use hg pull followed by hg update DEST')
5389 raise error.Abort(msg, hint=hint)
5403 raise error.Abort(msg, hint=hint)
5390
5404
5391 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5405 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5392 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5406 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5393 other = hg.peer(repo, opts, source)
5407 other = hg.peer(repo, opts, source)
5394 try:
5408 try:
5395 revs, checkout = hg.addbranchrevs(
5409 revs, checkout = hg.addbranchrevs(
5396 repo, other, branches, opts.get(b'rev')
5410 repo, other, branches, opts.get(b'rev')
5397 )
5411 )
5398
5412
5399 pullopargs = {}
5413 pullopargs = {}
5400
5414
5401 nodes = None
5415 nodes = None
5402 if opts.get(b'bookmark') or revs:
5416 if opts.get(b'bookmark') or revs:
5403 # The list of bookmark used here is the same used to actually update
5417 # The list of bookmark used here is the same used to actually update
5404 # the bookmark names, to avoid the race from issue 4689 and we do
5418 # the bookmark names, to avoid the race from issue 4689 and we do
5405 # all lookup and bookmark queries in one go so they see the same
5419 # all lookup and bookmark queries in one go so they see the same
5406 # version of the server state (issue 4700).
5420 # version of the server state (issue 4700).
5407 nodes = []
5421 nodes = []
5408 fnodes = []
5422 fnodes = []
5409 revs = revs or []
5423 revs = revs or []
5410 if revs and not other.capable(b'lookup'):
5424 if revs and not other.capable(b'lookup'):
5411 err = _(
5425 err = _(
5412 b"other repository doesn't support revision lookup, "
5426 b"other repository doesn't support revision lookup, "
5413 b"so a rev cannot be specified."
5427 b"so a rev cannot be specified."
5414 )
5428 )
5415 raise error.Abort(err)
5429 raise error.Abort(err)
5416 with other.commandexecutor() as e:
5430 with other.commandexecutor() as e:
5417 fremotebookmarks = e.callcommand(
5431 fremotebookmarks = e.callcommand(
5418 b'listkeys', {b'namespace': b'bookmarks'}
5432 b'listkeys', {b'namespace': b'bookmarks'}
5419 )
5433 )
5420 for r in revs:
5434 for r in revs:
5421 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5435 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5422 remotebookmarks = fremotebookmarks.result()
5436 remotebookmarks = fremotebookmarks.result()
5423 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5437 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5424 pullopargs[b'remotebookmarks'] = remotebookmarks
5438 pullopargs[b'remotebookmarks'] = remotebookmarks
5425 for b in opts.get(b'bookmark', []):
5439 for b in opts.get(b'bookmark', []):
5426 b = repo._bookmarks.expandname(b)
5440 b = repo._bookmarks.expandname(b)
5427 if b not in remotebookmarks:
5441 if b not in remotebookmarks:
5428 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5442 raise error.Abort(_(b'remote bookmark %s not found!') % b)
5429 nodes.append(remotebookmarks[b])
5443 nodes.append(remotebookmarks[b])
5430 for i, rev in enumerate(revs):
5444 for i, rev in enumerate(revs):
5431 node = fnodes[i].result()
5445 node = fnodes[i].result()
5432 nodes.append(node)
5446 nodes.append(node)
5433 if rev == checkout:
5447 if rev == checkout:
5434 checkout = node
5448 checkout = node
5435
5449
5436 wlock = util.nullcontextmanager()
5450 wlock = util.nullcontextmanager()
5437 if opts.get(b'update'):
5451 if opts.get(b'update'):
5438 wlock = repo.wlock()
5452 wlock = repo.wlock()
5439 with wlock:
5453 with wlock:
5440 pullopargs.update(opts.get(b'opargs', {}))
5454 pullopargs.update(opts.get(b'opargs', {}))
5441 modheads = exchange.pull(
5455 modheads = exchange.pull(
5442 repo,
5456 repo,
5443 other,
5457 other,
5444 heads=nodes,
5458 heads=nodes,
5445 force=opts.get(b'force'),
5459 force=opts.get(b'force'),
5446 bookmarks=opts.get(b'bookmark', ()),
5460 bookmarks=opts.get(b'bookmark', ()),
5447 opargs=pullopargs,
5461 opargs=pullopargs,
5448 ).cgresult
5462 ).cgresult
5449
5463
5450 # brev is a name, which might be a bookmark to be activated at
5464 # brev is a name, which might be a bookmark to be activated at
5451 # the end of the update. In other words, it is an explicit
5465 # the end of the update. In other words, it is an explicit
5452 # destination of the update
5466 # destination of the update
5453 brev = None
5467 brev = None
5454
5468
5455 if checkout:
5469 if checkout:
5456 checkout = repo.unfiltered().changelog.rev(checkout)
5470 checkout = repo.unfiltered().changelog.rev(checkout)
5457
5471
5458 # order below depends on implementation of
5472 # order below depends on implementation of
5459 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5473 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5460 # because 'checkout' is determined without it.
5474 # because 'checkout' is determined without it.
5461 if opts.get(b'rev'):
5475 if opts.get(b'rev'):
5462 brev = opts[b'rev'][0]
5476 brev = opts[b'rev'][0]
5463 elif opts.get(b'branch'):
5477 elif opts.get(b'branch'):
5464 brev = opts[b'branch'][0]
5478 brev = opts[b'branch'][0]
5465 else:
5479 else:
5466 brev = branches[0]
5480 brev = branches[0]
5467 repo._subtoppath = source
5481 repo._subtoppath = source
5468 try:
5482 try:
5469 ret = postincoming(
5483 ret = postincoming(
5470 ui, repo, modheads, opts.get(b'update'), checkout, brev
5484 ui, repo, modheads, opts.get(b'update'), checkout, brev
5471 )
5485 )
5472 except error.FilteredRepoLookupError as exc:
5486 except error.FilteredRepoLookupError as exc:
5473 msg = _(b'cannot update to target: %s') % exc.args[0]
5487 msg = _(b'cannot update to target: %s') % exc.args[0]
5474 exc.args = (msg,) + exc.args[1:]
5488 exc.args = (msg,) + exc.args[1:]
5475 raise
5489 raise
5476 finally:
5490 finally:
5477 del repo._subtoppath
5491 del repo._subtoppath
5478
5492
5479 finally:
5493 finally:
5480 other.close()
5494 other.close()
5481 return ret
5495 return ret
5482
5496
5483
5497
5484 @command(
5498 @command(
5485 b'push',
5499 b'push',
5486 [
5500 [
5487 (b'f', b'force', None, _(b'force push')),
5501 (b'f', b'force', None, _(b'force push')),
5488 (
5502 (
5489 b'r',
5503 b'r',
5490 b'rev',
5504 b'rev',
5491 [],
5505 [],
5492 _(b'a changeset intended to be included in the destination'),
5506 _(b'a changeset intended to be included in the destination'),
5493 _(b'REV'),
5507 _(b'REV'),
5494 ),
5508 ),
5495 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5509 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5496 (
5510 (
5497 b'b',
5511 b'b',
5498 b'branch',
5512 b'branch',
5499 [],
5513 [],
5500 _(b'a specific branch you would like to push'),
5514 _(b'a specific branch you would like to push'),
5501 _(b'BRANCH'),
5515 _(b'BRANCH'),
5502 ),
5516 ),
5503 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5517 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5504 (
5518 (
5505 b'',
5519 b'',
5506 b'pushvars',
5520 b'pushvars',
5507 [],
5521 [],
5508 _(b'variables that can be sent to server (ADVANCED)'),
5522 _(b'variables that can be sent to server (ADVANCED)'),
5509 ),
5523 ),
5510 (
5524 (
5511 b'',
5525 b'',
5512 b'publish',
5526 b'publish',
5513 False,
5527 False,
5514 _(b'push the changeset as public (EXPERIMENTAL)'),
5528 _(b'push the changeset as public (EXPERIMENTAL)'),
5515 ),
5529 ),
5516 ]
5530 ]
5517 + remoteopts,
5531 + remoteopts,
5518 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5532 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5519 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5533 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5520 helpbasic=True,
5534 helpbasic=True,
5521 )
5535 )
5522 def push(ui, repo, dest=None, **opts):
5536 def push(ui, repo, dest=None, **opts):
5523 """push changes to the specified destination
5537 """push changes to the specified destination
5524
5538
5525 Push changesets from the local repository to the specified
5539 Push changesets from the local repository to the specified
5526 destination.
5540 destination.
5527
5541
5528 This operation is symmetrical to pull: it is identical to a pull
5542 This operation is symmetrical to pull: it is identical to a pull
5529 in the destination repository from the current one.
5543 in the destination repository from the current one.
5530
5544
5531 By default, push will not allow creation of new heads at the
5545 By default, push will not allow creation of new heads at the
5532 destination, since multiple heads would make it unclear which head
5546 destination, since multiple heads would make it unclear which head
5533 to use. In this situation, it is recommended to pull and merge
5547 to use. In this situation, it is recommended to pull and merge
5534 before pushing.
5548 before pushing.
5535
5549
5536 Use --new-branch if you want to allow push to create a new named
5550 Use --new-branch if you want to allow push to create a new named
5537 branch that is not present at the destination. This allows you to
5551 branch that is not present at the destination. This allows you to
5538 only create a new branch without forcing other changes.
5552 only create a new branch without forcing other changes.
5539
5553
5540 .. note::
5554 .. note::
5541
5555
5542 Extra care should be taken with the -f/--force option,
5556 Extra care should be taken with the -f/--force option,
5543 which will push all new heads on all branches, an action which will
5557 which will push all new heads on all branches, an action which will
5544 almost always cause confusion for collaborators.
5558 almost always cause confusion for collaborators.
5545
5559
5546 If -r/--rev is used, the specified revision and all its ancestors
5560 If -r/--rev is used, the specified revision and all its ancestors
5547 will be pushed to the remote repository.
5561 will be pushed to the remote repository.
5548
5562
5549 If -B/--bookmark is used, the specified bookmarked revision, its
5563 If -B/--bookmark is used, the specified bookmarked revision, its
5550 ancestors, and the bookmark will be pushed to the remote
5564 ancestors, and the bookmark will be pushed to the remote
5551 repository. Specifying ``.`` is equivalent to specifying the active
5565 repository. Specifying ``.`` is equivalent to specifying the active
5552 bookmark's name.
5566 bookmark's name.
5553
5567
5554 Please see :hg:`help urls` for important details about ``ssh://``
5568 Please see :hg:`help urls` for important details about ``ssh://``
5555 URLs. If DESTINATION is omitted, a default path will be used.
5569 URLs. If DESTINATION is omitted, a default path will be used.
5556
5570
5557 .. container:: verbose
5571 .. container:: verbose
5558
5572
5559 The --pushvars option sends strings to the server that become
5573 The --pushvars option sends strings to the server that become
5560 environment variables prepended with ``HG_USERVAR_``. For example,
5574 environment variables prepended with ``HG_USERVAR_``. For example,
5561 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5575 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5562 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5576 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5563
5577
5564 pushvars can provide for user-overridable hooks as well as set debug
5578 pushvars can provide for user-overridable hooks as well as set debug
5565 levels. One example is having a hook that blocks commits containing
5579 levels. One example is having a hook that blocks commits containing
5566 conflict markers, but enables the user to override the hook if the file
5580 conflict markers, but enables the user to override the hook if the file
5567 is using conflict markers for testing purposes or the file format has
5581 is using conflict markers for testing purposes or the file format has
5568 strings that look like conflict markers.
5582 strings that look like conflict markers.
5569
5583
5570 By default, servers will ignore `--pushvars`. To enable it add the
5584 By default, servers will ignore `--pushvars`. To enable it add the
5571 following to your configuration file::
5585 following to your configuration file::
5572
5586
5573 [push]
5587 [push]
5574 pushvars.server = true
5588 pushvars.server = true
5575
5589
5576 Returns 0 if push was successful, 1 if nothing to push.
5590 Returns 0 if push was successful, 1 if nothing to push.
5577 """
5591 """
5578
5592
5579 opts = pycompat.byteskwargs(opts)
5593 opts = pycompat.byteskwargs(opts)
5580 if opts.get(b'bookmark'):
5594 if opts.get(b'bookmark'):
5581 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5595 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5582 for b in opts[b'bookmark']:
5596 for b in opts[b'bookmark']:
5583 # translate -B options to -r so changesets get pushed
5597 # translate -B options to -r so changesets get pushed
5584 b = repo._bookmarks.expandname(b)
5598 b = repo._bookmarks.expandname(b)
5585 if b in repo._bookmarks:
5599 if b in repo._bookmarks:
5586 opts.setdefault(b'rev', []).append(b)
5600 opts.setdefault(b'rev', []).append(b)
5587 else:
5601 else:
5588 # if we try to push a deleted bookmark, translate it to null
5602 # if we try to push a deleted bookmark, translate it to null
5589 # this lets simultaneous -r, -b options continue working
5603 # this lets simultaneous -r, -b options continue working
5590 opts.setdefault(b'rev', []).append(b"null")
5604 opts.setdefault(b'rev', []).append(b"null")
5591
5605
5592 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5606 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5593 if not path:
5607 if not path:
5594 raise error.Abort(
5608 raise error.Abort(
5595 _(b'default repository not configured!'),
5609 _(b'default repository not configured!'),
5596 hint=_(b"see 'hg help config.paths'"),
5610 hint=_(b"see 'hg help config.paths'"),
5597 )
5611 )
5598 dest = path.pushloc or path.loc
5612 dest = path.pushloc or path.loc
5599 branches = (path.branch, opts.get(b'branch') or [])
5613 branches = (path.branch, opts.get(b'branch') or [])
5600 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5614 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5601 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5615 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5602 other = hg.peer(repo, opts, dest)
5616 other = hg.peer(repo, opts, dest)
5603
5617
5604 if revs:
5618 if revs:
5605 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5619 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5606 if not revs:
5620 if not revs:
5607 raise error.Abort(
5621 raise error.Abort(
5608 _(b"specified revisions evaluate to an empty set"),
5622 _(b"specified revisions evaluate to an empty set"),
5609 hint=_(b"use different revision arguments"),
5623 hint=_(b"use different revision arguments"),
5610 )
5624 )
5611 elif path.pushrev:
5625 elif path.pushrev:
5612 # It doesn't make any sense to specify ancestor revisions. So limit
5626 # It doesn't make any sense to specify ancestor revisions. So limit
5613 # to DAG heads to make discovery simpler.
5627 # to DAG heads to make discovery simpler.
5614 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5628 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5615 revs = scmutil.revrange(repo, [expr])
5629 revs = scmutil.revrange(repo, [expr])
5616 revs = [repo[rev].node() for rev in revs]
5630 revs = [repo[rev].node() for rev in revs]
5617 if not revs:
5631 if not revs:
5618 raise error.Abort(
5632 raise error.Abort(
5619 _(b'default push revset for path evaluates to an empty set')
5633 _(b'default push revset for path evaluates to an empty set')
5620 )
5634 )
5621 elif ui.configbool(b'commands', b'push.require-revs'):
5635 elif ui.configbool(b'commands', b'push.require-revs'):
5622 raise error.Abort(
5636 raise error.Abort(
5623 _(b'no revisions specified to push'),
5637 _(b'no revisions specified to push'),
5624 hint=_(b'did you mean "hg push -r ."?'),
5638 hint=_(b'did you mean "hg push -r ."?'),
5625 )
5639 )
5626
5640
5627 repo._subtoppath = dest
5641 repo._subtoppath = dest
5628 try:
5642 try:
5629 # push subrepos depth-first for coherent ordering
5643 # push subrepos depth-first for coherent ordering
5630 c = repo[b'.']
5644 c = repo[b'.']
5631 subs = c.substate # only repos that are committed
5645 subs = c.substate # only repos that are committed
5632 for s in sorted(subs):
5646 for s in sorted(subs):
5633 result = c.sub(s).push(opts)
5647 result = c.sub(s).push(opts)
5634 if result == 0:
5648 if result == 0:
5635 return not result
5649 return not result
5636 finally:
5650 finally:
5637 del repo._subtoppath
5651 del repo._subtoppath
5638
5652
5639 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5653 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5640 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5654 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5641
5655
5642 pushop = exchange.push(
5656 pushop = exchange.push(
5643 repo,
5657 repo,
5644 other,
5658 other,
5645 opts.get(b'force'),
5659 opts.get(b'force'),
5646 revs=revs,
5660 revs=revs,
5647 newbranch=opts.get(b'new_branch'),
5661 newbranch=opts.get(b'new_branch'),
5648 bookmarks=opts.get(b'bookmark', ()),
5662 bookmarks=opts.get(b'bookmark', ()),
5649 publish=opts.get(b'publish'),
5663 publish=opts.get(b'publish'),
5650 opargs=opargs,
5664 opargs=opargs,
5651 )
5665 )
5652
5666
5653 result = not pushop.cgresult
5667 result = not pushop.cgresult
5654
5668
5655 if pushop.bkresult is not None:
5669 if pushop.bkresult is not None:
5656 if pushop.bkresult == 2:
5670 if pushop.bkresult == 2:
5657 result = 2
5671 result = 2
5658 elif not result and pushop.bkresult:
5672 elif not result and pushop.bkresult:
5659 result = 2
5673 result = 2
5660
5674
5661 return result
5675 return result
5662
5676
5663
5677
5664 @command(
5678 @command(
5665 b'recover',
5679 b'recover',
5666 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5680 [(b'', b'verify', True, b"run `hg verify` after succesful recover"),],
5667 helpcategory=command.CATEGORY_MAINTENANCE,
5681 helpcategory=command.CATEGORY_MAINTENANCE,
5668 )
5682 )
5669 def recover(ui, repo, **opts):
5683 def recover(ui, repo, **opts):
5670 """roll back an interrupted transaction
5684 """roll back an interrupted transaction
5671
5685
5672 Recover from an interrupted commit or pull.
5686 Recover from an interrupted commit or pull.
5673
5687
5674 This command tries to fix the repository status after an
5688 This command tries to fix the repository status after an
5675 interrupted operation. It should only be necessary when Mercurial
5689 interrupted operation. It should only be necessary when Mercurial
5676 suggests it.
5690 suggests it.
5677
5691
5678 Returns 0 if successful, 1 if nothing to recover or verify fails.
5692 Returns 0 if successful, 1 if nothing to recover or verify fails.
5679 """
5693 """
5680 ret = repo.recover()
5694 ret = repo.recover()
5681 if ret:
5695 if ret:
5682 if opts[r'verify']:
5696 if opts[r'verify']:
5683 return hg.verify(repo)
5697 return hg.verify(repo)
5684 else:
5698 else:
5685 msg = _(
5699 msg = _(
5686 b"(verify step skipped, run `hg verify` to check your "
5700 b"(verify step skipped, run `hg verify` to check your "
5687 b"repository content)\n"
5701 b"repository content)\n"
5688 )
5702 )
5689 ui.warn(msg)
5703 ui.warn(msg)
5690 return 0
5704 return 0
5691 return 1
5705 return 1
5692
5706
5693
5707
5694 @command(
5708 @command(
5695 b'remove|rm',
5709 b'remove|rm',
5696 [
5710 [
5697 (b'A', b'after', None, _(b'record delete for missing files')),
5711 (b'A', b'after', None, _(b'record delete for missing files')),
5698 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5712 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5699 ]
5713 ]
5700 + subrepoopts
5714 + subrepoopts
5701 + walkopts
5715 + walkopts
5702 + dryrunopts,
5716 + dryrunopts,
5703 _(b'[OPTION]... FILE...'),
5717 _(b'[OPTION]... FILE...'),
5704 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5718 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5705 helpbasic=True,
5719 helpbasic=True,
5706 inferrepo=True,
5720 inferrepo=True,
5707 )
5721 )
5708 def remove(ui, repo, *pats, **opts):
5722 def remove(ui, repo, *pats, **opts):
5709 """remove the specified files on the next commit
5723 """remove the specified files on the next commit
5710
5724
5711 Schedule the indicated files for removal from the current branch.
5725 Schedule the indicated files for removal from the current branch.
5712
5726
5713 This command schedules the files to be removed at the next commit.
5727 This command schedules the files to be removed at the next commit.
5714 To undo a remove before that, see :hg:`revert`. To undo added
5728 To undo a remove before that, see :hg:`revert`. To undo added
5715 files, see :hg:`forget`.
5729 files, see :hg:`forget`.
5716
5730
5717 .. container:: verbose
5731 .. container:: verbose
5718
5732
5719 -A/--after can be used to remove only files that have already
5733 -A/--after can be used to remove only files that have already
5720 been deleted, -f/--force can be used to force deletion, and -Af
5734 been deleted, -f/--force can be used to force deletion, and -Af
5721 can be used to remove files from the next revision without
5735 can be used to remove files from the next revision without
5722 deleting them from the working directory.
5736 deleting them from the working directory.
5723
5737
5724 The following table details the behavior of remove for different
5738 The following table details the behavior of remove for different
5725 file states (columns) and option combinations (rows). The file
5739 file states (columns) and option combinations (rows). The file
5726 states are Added [A], Clean [C], Modified [M] and Missing [!]
5740 states are Added [A], Clean [C], Modified [M] and Missing [!]
5727 (as reported by :hg:`status`). The actions are Warn, Remove
5741 (as reported by :hg:`status`). The actions are Warn, Remove
5728 (from branch) and Delete (from disk):
5742 (from branch) and Delete (from disk):
5729
5743
5730 ========= == == == ==
5744 ========= == == == ==
5731 opt/state A C M !
5745 opt/state A C M !
5732 ========= == == == ==
5746 ========= == == == ==
5733 none W RD W R
5747 none W RD W R
5734 -f R RD RD R
5748 -f R RD RD R
5735 -A W W W R
5749 -A W W W R
5736 -Af R R R R
5750 -Af R R R R
5737 ========= == == == ==
5751 ========= == == == ==
5738
5752
5739 .. note::
5753 .. note::
5740
5754
5741 :hg:`remove` never deletes files in Added [A] state from the
5755 :hg:`remove` never deletes files in Added [A] state from the
5742 working directory, not even if ``--force`` is specified.
5756 working directory, not even if ``--force`` is specified.
5743
5757
5744 Returns 0 on success, 1 if any warnings encountered.
5758 Returns 0 on success, 1 if any warnings encountered.
5745 """
5759 """
5746
5760
5747 opts = pycompat.byteskwargs(opts)
5761 opts = pycompat.byteskwargs(opts)
5748 after, force = opts.get(b'after'), opts.get(b'force')
5762 after, force = opts.get(b'after'), opts.get(b'force')
5749 dryrun = opts.get(b'dry_run')
5763 dryrun = opts.get(b'dry_run')
5750 if not pats and not after:
5764 if not pats and not after:
5751 raise error.Abort(_(b'no files specified'))
5765 raise error.Abort(_(b'no files specified'))
5752
5766
5753 m = scmutil.match(repo[None], pats, opts)
5767 m = scmutil.match(repo[None], pats, opts)
5754 subrepos = opts.get(b'subrepos')
5768 subrepos = opts.get(b'subrepos')
5755 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5769 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5756 return cmdutil.remove(
5770 return cmdutil.remove(
5757 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5771 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5758 )
5772 )
5759
5773
5760
5774
5761 @command(
5775 @command(
5762 b'rename|move|mv',
5776 b'rename|move|mv',
5763 [
5777 [
5764 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5778 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5765 (
5779 (
5766 b'f',
5780 b'f',
5767 b'force',
5781 b'force',
5768 None,
5782 None,
5769 _(b'forcibly move over an existing managed file'),
5783 _(b'forcibly move over an existing managed file'),
5770 ),
5784 ),
5771 ]
5785 ]
5772 + walkopts
5786 + walkopts
5773 + dryrunopts,
5787 + dryrunopts,
5774 _(b'[OPTION]... SOURCE... DEST'),
5788 _(b'[OPTION]... SOURCE... DEST'),
5775 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5789 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5776 )
5790 )
5777 def rename(ui, repo, *pats, **opts):
5791 def rename(ui, repo, *pats, **opts):
5778 """rename files; equivalent of copy + remove
5792 """rename files; equivalent of copy + remove
5779
5793
5780 Mark dest as copies of sources; mark sources for deletion. If dest
5794 Mark dest as copies of sources; mark sources for deletion. If dest
5781 is a directory, copies are put in that directory. If dest is a
5795 is a directory, copies are put in that directory. If dest is a
5782 file, there can only be one source.
5796 file, there can only be one source.
5783
5797
5784 By default, this command copies the contents of files as they
5798 By default, this command copies the contents of files as they
5785 exist in the working directory. If invoked with -A/--after, the
5799 exist in the working directory. If invoked with -A/--after, the
5786 operation is recorded, but no copying is performed.
5800 operation is recorded, but no copying is performed.
5787
5801
5788 This command takes effect at the next commit. To undo a rename
5802 This command takes effect at the next commit. To undo a rename
5789 before that, see :hg:`revert`.
5803 before that, see :hg:`revert`.
5790
5804
5791 Returns 0 on success, 1 if errors are encountered.
5805 Returns 0 on success, 1 if errors are encountered.
5792 """
5806 """
5793 opts = pycompat.byteskwargs(opts)
5807 opts = pycompat.byteskwargs(opts)
5794 with repo.wlock(False):
5808 with repo.wlock(False):
5795 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5809 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5796
5810
5797
5811
5798 @command(
5812 @command(
5799 b'resolve',
5813 b'resolve',
5800 [
5814 [
5801 (b'a', b'all', None, _(b'select all unresolved files')),
5815 (b'a', b'all', None, _(b'select all unresolved files')),
5802 (b'l', b'list', None, _(b'list state of files needing merge')),
5816 (b'l', b'list', None, _(b'list state of files needing merge')),
5803 (b'm', b'mark', None, _(b'mark files as resolved')),
5817 (b'm', b'mark', None, _(b'mark files as resolved')),
5804 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5818 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5805 (b'n', b'no-status', None, _(b'hide status prefix')),
5819 (b'n', b'no-status', None, _(b'hide status prefix')),
5806 (b'', b're-merge', None, _(b're-merge files')),
5820 (b'', b're-merge', None, _(b're-merge files')),
5807 ]
5821 ]
5808 + mergetoolopts
5822 + mergetoolopts
5809 + walkopts
5823 + walkopts
5810 + formatteropts,
5824 + formatteropts,
5811 _(b'[OPTION]... [FILE]...'),
5825 _(b'[OPTION]... [FILE]...'),
5812 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5826 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5813 inferrepo=True,
5827 inferrepo=True,
5814 )
5828 )
5815 def resolve(ui, repo, *pats, **opts):
5829 def resolve(ui, repo, *pats, **opts):
5816 """redo merges or set/view the merge status of files
5830 """redo merges or set/view the merge status of files
5817
5831
5818 Merges with unresolved conflicts are often the result of
5832 Merges with unresolved conflicts are often the result of
5819 non-interactive merging using the ``internal:merge`` configuration
5833 non-interactive merging using the ``internal:merge`` configuration
5820 setting, or a command-line merge tool like ``diff3``. The resolve
5834 setting, or a command-line merge tool like ``diff3``. The resolve
5821 command is used to manage the files involved in a merge, after
5835 command is used to manage the files involved in a merge, after
5822 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5836 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5823 working directory must have two parents). See :hg:`help
5837 working directory must have two parents). See :hg:`help
5824 merge-tools` for information on configuring merge tools.
5838 merge-tools` for information on configuring merge tools.
5825
5839
5826 The resolve command can be used in the following ways:
5840 The resolve command can be used in the following ways:
5827
5841
5828 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5842 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5829 the specified files, discarding any previous merge attempts. Re-merging
5843 the specified files, discarding any previous merge attempts. Re-merging
5830 is not performed for files already marked as resolved. Use ``--all/-a``
5844 is not performed for files already marked as resolved. Use ``--all/-a``
5831 to select all unresolved files. ``--tool`` can be used to specify
5845 to select all unresolved files. ``--tool`` can be used to specify
5832 the merge tool used for the given files. It overrides the HGMERGE
5846 the merge tool used for the given files. It overrides the HGMERGE
5833 environment variable and your configuration files. Previous file
5847 environment variable and your configuration files. Previous file
5834 contents are saved with a ``.orig`` suffix.
5848 contents are saved with a ``.orig`` suffix.
5835
5849
5836 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5850 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5837 (e.g. after having manually fixed-up the files). The default is
5851 (e.g. after having manually fixed-up the files). The default is
5838 to mark all unresolved files.
5852 to mark all unresolved files.
5839
5853
5840 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5854 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5841 default is to mark all resolved files.
5855 default is to mark all resolved files.
5842
5856
5843 - :hg:`resolve -l`: list files which had or still have conflicts.
5857 - :hg:`resolve -l`: list files which had or still have conflicts.
5844 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5858 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5845 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5859 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5846 the list. See :hg:`help filesets` for details.
5860 the list. See :hg:`help filesets` for details.
5847
5861
5848 .. note::
5862 .. note::
5849
5863
5850 Mercurial will not let you commit files with unresolved merge
5864 Mercurial will not let you commit files with unresolved merge
5851 conflicts. You must use :hg:`resolve -m ...` before you can
5865 conflicts. You must use :hg:`resolve -m ...` before you can
5852 commit after a conflicting merge.
5866 commit after a conflicting merge.
5853
5867
5854 .. container:: verbose
5868 .. container:: verbose
5855
5869
5856 Template:
5870 Template:
5857
5871
5858 The following keywords are supported in addition to the common template
5872 The following keywords are supported in addition to the common template
5859 keywords and functions. See also :hg:`help templates`.
5873 keywords and functions. See also :hg:`help templates`.
5860
5874
5861 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5875 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5862 :path: String. Repository-absolute path of the file.
5876 :path: String. Repository-absolute path of the file.
5863
5877
5864 Returns 0 on success, 1 if any files fail a resolve attempt.
5878 Returns 0 on success, 1 if any files fail a resolve attempt.
5865 """
5879 """
5866
5880
5867 opts = pycompat.byteskwargs(opts)
5881 opts = pycompat.byteskwargs(opts)
5868 confirm = ui.configbool(b'commands', b'resolve.confirm')
5882 confirm = ui.configbool(b'commands', b'resolve.confirm')
5869 flaglist = b'all mark unmark list no_status re_merge'.split()
5883 flaglist = b'all mark unmark list no_status re_merge'.split()
5870 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5884 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5871
5885
5872 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5886 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5873 if actioncount > 1:
5887 if actioncount > 1:
5874 raise error.Abort(_(b"too many actions specified"))
5888 raise error.Abort(_(b"too many actions specified"))
5875 elif actioncount == 0 and ui.configbool(
5889 elif actioncount == 0 and ui.configbool(
5876 b'commands', b'resolve.explicit-re-merge'
5890 b'commands', b'resolve.explicit-re-merge'
5877 ):
5891 ):
5878 hint = _(b'use --mark, --unmark, --list or --re-merge')
5892 hint = _(b'use --mark, --unmark, --list or --re-merge')
5879 raise error.Abort(_(b'no action specified'), hint=hint)
5893 raise error.Abort(_(b'no action specified'), hint=hint)
5880 if pats and all:
5894 if pats and all:
5881 raise error.Abort(_(b"can't specify --all and patterns"))
5895 raise error.Abort(_(b"can't specify --all and patterns"))
5882 if not (all or pats or show or mark or unmark):
5896 if not (all or pats or show or mark or unmark):
5883 raise error.Abort(
5897 raise error.Abort(
5884 _(b'no files or directories specified'),
5898 _(b'no files or directories specified'),
5885 hint=b'use --all to re-merge all unresolved files',
5899 hint=b'use --all to re-merge all unresolved files',
5886 )
5900 )
5887
5901
5888 if confirm:
5902 if confirm:
5889 if all:
5903 if all:
5890 if ui.promptchoice(
5904 if ui.promptchoice(
5891 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5905 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5892 ):
5906 ):
5893 raise error.Abort(_(b'user quit'))
5907 raise error.Abort(_(b'user quit'))
5894 if mark and not pats:
5908 if mark and not pats:
5895 if ui.promptchoice(
5909 if ui.promptchoice(
5896 _(
5910 _(
5897 b'mark all unresolved files as resolved (yn)?'
5911 b'mark all unresolved files as resolved (yn)?'
5898 b'$$ &Yes $$ &No'
5912 b'$$ &Yes $$ &No'
5899 )
5913 )
5900 ):
5914 ):
5901 raise error.Abort(_(b'user quit'))
5915 raise error.Abort(_(b'user quit'))
5902 if unmark and not pats:
5916 if unmark and not pats:
5903 if ui.promptchoice(
5917 if ui.promptchoice(
5904 _(
5918 _(
5905 b'mark all resolved files as unresolved (yn)?'
5919 b'mark all resolved files as unresolved (yn)?'
5906 b'$$ &Yes $$ &No'
5920 b'$$ &Yes $$ &No'
5907 )
5921 )
5908 ):
5922 ):
5909 raise error.Abort(_(b'user quit'))
5923 raise error.Abort(_(b'user quit'))
5910
5924
5911 uipathfn = scmutil.getuipathfn(repo)
5925 uipathfn = scmutil.getuipathfn(repo)
5912
5926
5913 if show:
5927 if show:
5914 ui.pager(b'resolve')
5928 ui.pager(b'resolve')
5915 fm = ui.formatter(b'resolve', opts)
5929 fm = ui.formatter(b'resolve', opts)
5916 ms = mergemod.mergestate.read(repo)
5930 ms = mergemod.mergestate.read(repo)
5917 wctx = repo[None]
5931 wctx = repo[None]
5918 m = scmutil.match(wctx, pats, opts)
5932 m = scmutil.match(wctx, pats, opts)
5919
5933
5920 # Labels and keys based on merge state. Unresolved path conflicts show
5934 # Labels and keys based on merge state. Unresolved path conflicts show
5921 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5935 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5922 # resolved conflicts.
5936 # resolved conflicts.
5923 mergestateinfo = {
5937 mergestateinfo = {
5924 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5938 mergemod.MERGE_RECORD_UNRESOLVED: (b'resolve.unresolved', b'U'),
5925 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5939 mergemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5926 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5940 mergemod.MERGE_RECORD_UNRESOLVED_PATH: (
5927 b'resolve.unresolved',
5941 b'resolve.unresolved',
5928 b'P',
5942 b'P',
5929 ),
5943 ),
5930 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5944 mergemod.MERGE_RECORD_RESOLVED_PATH: (b'resolve.resolved', b'R'),
5931 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5945 mergemod.MERGE_RECORD_DRIVER_RESOLVED: (
5932 b'resolve.driverresolved',
5946 b'resolve.driverresolved',
5933 b'D',
5947 b'D',
5934 ),
5948 ),
5935 }
5949 }
5936
5950
5937 for f in ms:
5951 for f in ms:
5938 if not m(f):
5952 if not m(f):
5939 continue
5953 continue
5940
5954
5941 label, key = mergestateinfo[ms[f]]
5955 label, key = mergestateinfo[ms[f]]
5942 fm.startitem()
5956 fm.startitem()
5943 fm.context(ctx=wctx)
5957 fm.context(ctx=wctx)
5944 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5958 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5945 fm.data(path=f)
5959 fm.data(path=f)
5946 fm.plain(b'%s\n' % uipathfn(f), label=label)
5960 fm.plain(b'%s\n' % uipathfn(f), label=label)
5947 fm.end()
5961 fm.end()
5948 return 0
5962 return 0
5949
5963
5950 with repo.wlock():
5964 with repo.wlock():
5951 ms = mergemod.mergestate.read(repo)
5965 ms = mergemod.mergestate.read(repo)
5952
5966
5953 if not (ms.active() or repo.dirstate.p2() != nullid):
5967 if not (ms.active() or repo.dirstate.p2() != nullid):
5954 raise error.Abort(
5968 raise error.Abort(
5955 _(b'resolve command not applicable when not merging')
5969 _(b'resolve command not applicable when not merging')
5956 )
5970 )
5957
5971
5958 wctx = repo[None]
5972 wctx = repo[None]
5959
5973
5960 if (
5974 if (
5961 ms.mergedriver
5975 ms.mergedriver
5962 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5976 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED
5963 ):
5977 ):
5964 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5978 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5965 ms.commit()
5979 ms.commit()
5966 # allow mark and unmark to go through
5980 # allow mark and unmark to go through
5967 if not mark and not unmark and not proceed:
5981 if not mark and not unmark and not proceed:
5968 return 1
5982 return 1
5969
5983
5970 m = scmutil.match(wctx, pats, opts)
5984 m = scmutil.match(wctx, pats, opts)
5971 ret = 0
5985 ret = 0
5972 didwork = False
5986 didwork = False
5973 runconclude = False
5987 runconclude = False
5974
5988
5975 tocomplete = []
5989 tocomplete = []
5976 hasconflictmarkers = []
5990 hasconflictmarkers = []
5977 if mark:
5991 if mark:
5978 markcheck = ui.config(b'commands', b'resolve.mark-check')
5992 markcheck = ui.config(b'commands', b'resolve.mark-check')
5979 if markcheck not in [b'warn', b'abort']:
5993 if markcheck not in [b'warn', b'abort']:
5980 # Treat all invalid / unrecognized values as 'none'.
5994 # Treat all invalid / unrecognized values as 'none'.
5981 markcheck = False
5995 markcheck = False
5982 for f in ms:
5996 for f in ms:
5983 if not m(f):
5997 if not m(f):
5984 continue
5998 continue
5985
5999
5986 didwork = True
6000 didwork = True
5987
6001
5988 # don't let driver-resolved files be marked, and run the conclude
6002 # don't let driver-resolved files be marked, and run the conclude
5989 # step if asked to resolve
6003 # step if asked to resolve
5990 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
6004 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
5991 exact = m.exact(f)
6005 exact = m.exact(f)
5992 if mark:
6006 if mark:
5993 if exact:
6007 if exact:
5994 ui.warn(
6008 ui.warn(
5995 _(b'not marking %s as it is driver-resolved\n')
6009 _(b'not marking %s as it is driver-resolved\n')
5996 % uipathfn(f)
6010 % uipathfn(f)
5997 )
6011 )
5998 elif unmark:
6012 elif unmark:
5999 if exact:
6013 if exact:
6000 ui.warn(
6014 ui.warn(
6001 _(b'not unmarking %s as it is driver-resolved\n')
6015 _(b'not unmarking %s as it is driver-resolved\n')
6002 % uipathfn(f)
6016 % uipathfn(f)
6003 )
6017 )
6004 else:
6018 else:
6005 runconclude = True
6019 runconclude = True
6006 continue
6020 continue
6007
6021
6008 # path conflicts must be resolved manually
6022 # path conflicts must be resolved manually
6009 if ms[f] in (
6023 if ms[f] in (
6010 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6024 mergemod.MERGE_RECORD_UNRESOLVED_PATH,
6011 mergemod.MERGE_RECORD_RESOLVED_PATH,
6025 mergemod.MERGE_RECORD_RESOLVED_PATH,
6012 ):
6026 ):
6013 if mark:
6027 if mark:
6014 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6028 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
6015 elif unmark:
6029 elif unmark:
6016 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6030 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
6017 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6031 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
6018 ui.warn(
6032 ui.warn(
6019 _(b'%s: path conflict must be resolved manually\n')
6033 _(b'%s: path conflict must be resolved manually\n')
6020 % uipathfn(f)
6034 % uipathfn(f)
6021 )
6035 )
6022 continue
6036 continue
6023
6037
6024 if mark:
6038 if mark:
6025 if markcheck:
6039 if markcheck:
6026 fdata = repo.wvfs.tryread(f)
6040 fdata = repo.wvfs.tryread(f)
6027 if (
6041 if (
6028 filemerge.hasconflictmarkers(fdata)
6042 filemerge.hasconflictmarkers(fdata)
6029 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6043 and ms[f] != mergemod.MERGE_RECORD_RESOLVED
6030 ):
6044 ):
6031 hasconflictmarkers.append(f)
6045 hasconflictmarkers.append(f)
6032 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6046 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
6033 elif unmark:
6047 elif unmark:
6034 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6048 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
6035 else:
6049 else:
6036 # backup pre-resolve (merge uses .orig for its own purposes)
6050 # backup pre-resolve (merge uses .orig for its own purposes)
6037 a = repo.wjoin(f)
6051 a = repo.wjoin(f)
6038 try:
6052 try:
6039 util.copyfile(a, a + b".resolve")
6053 util.copyfile(a, a + b".resolve")
6040 except (IOError, OSError) as inst:
6054 except (IOError, OSError) as inst:
6041 if inst.errno != errno.ENOENT:
6055 if inst.errno != errno.ENOENT:
6042 raise
6056 raise
6043
6057
6044 try:
6058 try:
6045 # preresolve file
6059 # preresolve file
6046 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6060 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6047 with ui.configoverride(overrides, b'resolve'):
6061 with ui.configoverride(overrides, b'resolve'):
6048 complete, r = ms.preresolve(f, wctx)
6062 complete, r = ms.preresolve(f, wctx)
6049 if not complete:
6063 if not complete:
6050 tocomplete.append(f)
6064 tocomplete.append(f)
6051 elif r:
6065 elif r:
6052 ret = 1
6066 ret = 1
6053 finally:
6067 finally:
6054 ms.commit()
6068 ms.commit()
6055
6069
6056 # replace filemerge's .orig file with our resolve file, but only
6070 # replace filemerge's .orig file with our resolve file, but only
6057 # for merges that are complete
6071 # for merges that are complete
6058 if complete:
6072 if complete:
6059 try:
6073 try:
6060 util.rename(
6074 util.rename(
6061 a + b".resolve", scmutil.backuppath(ui, repo, f)
6075 a + b".resolve", scmutil.backuppath(ui, repo, f)
6062 )
6076 )
6063 except OSError as inst:
6077 except OSError as inst:
6064 if inst.errno != errno.ENOENT:
6078 if inst.errno != errno.ENOENT:
6065 raise
6079 raise
6066
6080
6067 if hasconflictmarkers:
6081 if hasconflictmarkers:
6068 ui.warn(
6082 ui.warn(
6069 _(
6083 _(
6070 b'warning: the following files still have conflict '
6084 b'warning: the following files still have conflict '
6071 b'markers:\n'
6085 b'markers:\n'
6072 )
6086 )
6073 + b''.join(
6087 + b''.join(
6074 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6088 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6075 )
6089 )
6076 )
6090 )
6077 if markcheck == b'abort' and not all and not pats:
6091 if markcheck == b'abort' and not all and not pats:
6078 raise error.Abort(
6092 raise error.Abort(
6079 _(b'conflict markers detected'),
6093 _(b'conflict markers detected'),
6080 hint=_(b'use --all to mark anyway'),
6094 hint=_(b'use --all to mark anyway'),
6081 )
6095 )
6082
6096
6083 for f in tocomplete:
6097 for f in tocomplete:
6084 try:
6098 try:
6085 # resolve file
6099 # resolve file
6086 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6100 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6087 with ui.configoverride(overrides, b'resolve'):
6101 with ui.configoverride(overrides, b'resolve'):
6088 r = ms.resolve(f, wctx)
6102 r = ms.resolve(f, wctx)
6089 if r:
6103 if r:
6090 ret = 1
6104 ret = 1
6091 finally:
6105 finally:
6092 ms.commit()
6106 ms.commit()
6093
6107
6094 # replace filemerge's .orig file with our resolve file
6108 # replace filemerge's .orig file with our resolve file
6095 a = repo.wjoin(f)
6109 a = repo.wjoin(f)
6096 try:
6110 try:
6097 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6111 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6098 except OSError as inst:
6112 except OSError as inst:
6099 if inst.errno != errno.ENOENT:
6113 if inst.errno != errno.ENOENT:
6100 raise
6114 raise
6101
6115
6102 ms.commit()
6116 ms.commit()
6103 ms.recordactions()
6117 ms.recordactions()
6104
6118
6105 if not didwork and pats:
6119 if not didwork and pats:
6106 hint = None
6120 hint = None
6107 if not any([p for p in pats if p.find(b':') >= 0]):
6121 if not any([p for p in pats if p.find(b':') >= 0]):
6108 pats = [b'path:%s' % p for p in pats]
6122 pats = [b'path:%s' % p for p in pats]
6109 m = scmutil.match(wctx, pats, opts)
6123 m = scmutil.match(wctx, pats, opts)
6110 for f in ms:
6124 for f in ms:
6111 if not m(f):
6125 if not m(f):
6112 continue
6126 continue
6113
6127
6114 def flag(o):
6128 def flag(o):
6115 if o == b're_merge':
6129 if o == b're_merge':
6116 return b'--re-merge '
6130 return b'--re-merge '
6117 return b'-%s ' % o[0:1]
6131 return b'-%s ' % o[0:1]
6118
6132
6119 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6133 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6120 hint = _(b"(try: hg resolve %s%s)\n") % (
6134 hint = _(b"(try: hg resolve %s%s)\n") % (
6121 flags,
6135 flags,
6122 b' '.join(pats),
6136 b' '.join(pats),
6123 )
6137 )
6124 break
6138 break
6125 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6139 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6126 if hint:
6140 if hint:
6127 ui.warn(hint)
6141 ui.warn(hint)
6128 elif ms.mergedriver and ms.mdstate() != b's':
6142 elif ms.mergedriver and ms.mdstate() != b's':
6129 # run conclude step when either a driver-resolved file is requested
6143 # run conclude step when either a driver-resolved file is requested
6130 # or there are no driver-resolved files
6144 # or there are no driver-resolved files
6131 # we can't use 'ret' to determine whether any files are unresolved
6145 # we can't use 'ret' to determine whether any files are unresolved
6132 # because we might not have tried to resolve some
6146 # because we might not have tried to resolve some
6133 if (runconclude or not list(ms.driverresolved())) and not list(
6147 if (runconclude or not list(ms.driverresolved())) and not list(
6134 ms.unresolved()
6148 ms.unresolved()
6135 ):
6149 ):
6136 proceed = mergemod.driverconclude(repo, ms, wctx)
6150 proceed = mergemod.driverconclude(repo, ms, wctx)
6137 ms.commit()
6151 ms.commit()
6138 if not proceed:
6152 if not proceed:
6139 return 1
6153 return 1
6140
6154
6141 # Nudge users into finishing an unfinished operation
6155 # Nudge users into finishing an unfinished operation
6142 unresolvedf = list(ms.unresolved())
6156 unresolvedf = list(ms.unresolved())
6143 driverresolvedf = list(ms.driverresolved())
6157 driverresolvedf = list(ms.driverresolved())
6144 if not unresolvedf and not driverresolvedf:
6158 if not unresolvedf and not driverresolvedf:
6145 ui.status(_(b'(no more unresolved files)\n'))
6159 ui.status(_(b'(no more unresolved files)\n'))
6146 cmdutil.checkafterresolved(repo)
6160 cmdutil.checkafterresolved(repo)
6147 elif not unresolvedf:
6161 elif not unresolvedf:
6148 ui.status(
6162 ui.status(
6149 _(
6163 _(
6150 b'(no more unresolved files -- '
6164 b'(no more unresolved files -- '
6151 b'run "hg resolve --all" to conclude)\n'
6165 b'run "hg resolve --all" to conclude)\n'
6152 )
6166 )
6153 )
6167 )
6154
6168
6155 return ret
6169 return ret
6156
6170
6157
6171
6158 @command(
6172 @command(
6159 b'revert',
6173 b'revert',
6160 [
6174 [
6161 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6175 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6162 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6176 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6163 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6177 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6164 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6178 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6165 (b'i', b'interactive', None, _(b'interactively select the changes')),
6179 (b'i', b'interactive', None, _(b'interactively select the changes')),
6166 ]
6180 ]
6167 + walkopts
6181 + walkopts
6168 + dryrunopts,
6182 + dryrunopts,
6169 _(b'[OPTION]... [-r REV] [NAME]...'),
6183 _(b'[OPTION]... [-r REV] [NAME]...'),
6170 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6184 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6171 )
6185 )
6172 def revert(ui, repo, *pats, **opts):
6186 def revert(ui, repo, *pats, **opts):
6173 """restore files to their checkout state
6187 """restore files to their checkout state
6174
6188
6175 .. note::
6189 .. note::
6176
6190
6177 To check out earlier revisions, you should use :hg:`update REV`.
6191 To check out earlier revisions, you should use :hg:`update REV`.
6178 To cancel an uncommitted merge (and lose your changes),
6192 To cancel an uncommitted merge (and lose your changes),
6179 use :hg:`merge --abort`.
6193 use :hg:`merge --abort`.
6180
6194
6181 With no revision specified, revert the specified files or directories
6195 With no revision specified, revert the specified files or directories
6182 to the contents they had in the parent of the working directory.
6196 to the contents they had in the parent of the working directory.
6183 This restores the contents of files to an unmodified
6197 This restores the contents of files to an unmodified
6184 state and unschedules adds, removes, copies, and renames. If the
6198 state and unschedules adds, removes, copies, and renames. If the
6185 working directory has two parents, you must explicitly specify a
6199 working directory has two parents, you must explicitly specify a
6186 revision.
6200 revision.
6187
6201
6188 Using the -r/--rev or -d/--date options, revert the given files or
6202 Using the -r/--rev or -d/--date options, revert the given files or
6189 directories to their states as of a specific revision. Because
6203 directories to their states as of a specific revision. Because
6190 revert does not change the working directory parents, this will
6204 revert does not change the working directory parents, this will
6191 cause these files to appear modified. This can be helpful to "back
6205 cause these files to appear modified. This can be helpful to "back
6192 out" some or all of an earlier change. See :hg:`backout` for a
6206 out" some or all of an earlier change. See :hg:`backout` for a
6193 related method.
6207 related method.
6194
6208
6195 Modified files are saved with a .orig suffix before reverting.
6209 Modified files are saved with a .orig suffix before reverting.
6196 To disable these backups, use --no-backup. It is possible to store
6210 To disable these backups, use --no-backup. It is possible to store
6197 the backup files in a custom directory relative to the root of the
6211 the backup files in a custom directory relative to the root of the
6198 repository by setting the ``ui.origbackuppath`` configuration
6212 repository by setting the ``ui.origbackuppath`` configuration
6199 option.
6213 option.
6200
6214
6201 See :hg:`help dates` for a list of formats valid for -d/--date.
6215 See :hg:`help dates` for a list of formats valid for -d/--date.
6202
6216
6203 See :hg:`help backout` for a way to reverse the effect of an
6217 See :hg:`help backout` for a way to reverse the effect of an
6204 earlier changeset.
6218 earlier changeset.
6205
6219
6206 Returns 0 on success.
6220 Returns 0 on success.
6207 """
6221 """
6208
6222
6209 opts = pycompat.byteskwargs(opts)
6223 opts = pycompat.byteskwargs(opts)
6210 if opts.get(b"date"):
6224 if opts.get(b"date"):
6211 if opts.get(b"rev"):
6225 if opts.get(b"rev"):
6212 raise error.Abort(_(b"you can't specify a revision and a date"))
6226 raise error.Abort(_(b"you can't specify a revision and a date"))
6213 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6227 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6214
6228
6215 parent, p2 = repo.dirstate.parents()
6229 parent, p2 = repo.dirstate.parents()
6216 if not opts.get(b'rev') and p2 != nullid:
6230 if not opts.get(b'rev') and p2 != nullid:
6217 # revert after merge is a trap for new users (issue2915)
6231 # revert after merge is a trap for new users (issue2915)
6218 raise error.Abort(
6232 raise error.Abort(
6219 _(b'uncommitted merge with no revision specified'),
6233 _(b'uncommitted merge with no revision specified'),
6220 hint=_(b"use 'hg update' or see 'hg help revert'"),
6234 hint=_(b"use 'hg update' or see 'hg help revert'"),
6221 )
6235 )
6222
6236
6223 rev = opts.get(b'rev')
6237 rev = opts.get(b'rev')
6224 if rev:
6238 if rev:
6225 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6239 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6226 ctx = scmutil.revsingle(repo, rev)
6240 ctx = scmutil.revsingle(repo, rev)
6227
6241
6228 if not (
6242 if not (
6229 pats
6243 pats
6230 or opts.get(b'include')
6244 or opts.get(b'include')
6231 or opts.get(b'exclude')
6245 or opts.get(b'exclude')
6232 or opts.get(b'all')
6246 or opts.get(b'all')
6233 or opts.get(b'interactive')
6247 or opts.get(b'interactive')
6234 ):
6248 ):
6235 msg = _(b"no files or directories specified")
6249 msg = _(b"no files or directories specified")
6236 if p2 != nullid:
6250 if p2 != nullid:
6237 hint = _(
6251 hint = _(
6238 b"uncommitted merge, use --all to discard all changes,"
6252 b"uncommitted merge, use --all to discard all changes,"
6239 b" or 'hg update -C .' to abort the merge"
6253 b" or 'hg update -C .' to abort the merge"
6240 )
6254 )
6241 raise error.Abort(msg, hint=hint)
6255 raise error.Abort(msg, hint=hint)
6242 dirty = any(repo.status())
6256 dirty = any(repo.status())
6243 node = ctx.node()
6257 node = ctx.node()
6244 if node != parent:
6258 if node != parent:
6245 if dirty:
6259 if dirty:
6246 hint = (
6260 hint = (
6247 _(
6261 _(
6248 b"uncommitted changes, use --all to discard all"
6262 b"uncommitted changes, use --all to discard all"
6249 b" changes, or 'hg update %d' to update"
6263 b" changes, or 'hg update %d' to update"
6250 )
6264 )
6251 % ctx.rev()
6265 % ctx.rev()
6252 )
6266 )
6253 else:
6267 else:
6254 hint = (
6268 hint = (
6255 _(
6269 _(
6256 b"use --all to revert all files,"
6270 b"use --all to revert all files,"
6257 b" or 'hg update %d' to update"
6271 b" or 'hg update %d' to update"
6258 )
6272 )
6259 % ctx.rev()
6273 % ctx.rev()
6260 )
6274 )
6261 elif dirty:
6275 elif dirty:
6262 hint = _(b"uncommitted changes, use --all to discard all changes")
6276 hint = _(b"uncommitted changes, use --all to discard all changes")
6263 else:
6277 else:
6264 hint = _(b"use --all to revert all files")
6278 hint = _(b"use --all to revert all files")
6265 raise error.Abort(msg, hint=hint)
6279 raise error.Abort(msg, hint=hint)
6266
6280
6267 return cmdutil.revert(
6281 return cmdutil.revert(
6268 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6282 ui, repo, ctx, (parent, p2), *pats, **pycompat.strkwargs(opts)
6269 )
6283 )
6270
6284
6271
6285
6272 @command(
6286 @command(
6273 b'rollback',
6287 b'rollback',
6274 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6288 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6275 helpcategory=command.CATEGORY_MAINTENANCE,
6289 helpcategory=command.CATEGORY_MAINTENANCE,
6276 )
6290 )
6277 def rollback(ui, repo, **opts):
6291 def rollback(ui, repo, **opts):
6278 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6292 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6279
6293
6280 Please use :hg:`commit --amend` instead of rollback to correct
6294 Please use :hg:`commit --amend` instead of rollback to correct
6281 mistakes in the last commit.
6295 mistakes in the last commit.
6282
6296
6283 This command should be used with care. There is only one level of
6297 This command should be used with care. There is only one level of
6284 rollback, and there is no way to undo a rollback. It will also
6298 rollback, and there is no way to undo a rollback. It will also
6285 restore the dirstate at the time of the last transaction, losing
6299 restore the dirstate at the time of the last transaction, losing
6286 any dirstate changes since that time. This command does not alter
6300 any dirstate changes since that time. This command does not alter
6287 the working directory.
6301 the working directory.
6288
6302
6289 Transactions are used to encapsulate the effects of all commands
6303 Transactions are used to encapsulate the effects of all commands
6290 that create new changesets or propagate existing changesets into a
6304 that create new changesets or propagate existing changesets into a
6291 repository.
6305 repository.
6292
6306
6293 .. container:: verbose
6307 .. container:: verbose
6294
6308
6295 For example, the following commands are transactional, and their
6309 For example, the following commands are transactional, and their
6296 effects can be rolled back:
6310 effects can be rolled back:
6297
6311
6298 - commit
6312 - commit
6299 - import
6313 - import
6300 - pull
6314 - pull
6301 - push (with this repository as the destination)
6315 - push (with this repository as the destination)
6302 - unbundle
6316 - unbundle
6303
6317
6304 To avoid permanent data loss, rollback will refuse to rollback a
6318 To avoid permanent data loss, rollback will refuse to rollback a
6305 commit transaction if it isn't checked out. Use --force to
6319 commit transaction if it isn't checked out. Use --force to
6306 override this protection.
6320 override this protection.
6307
6321
6308 The rollback command can be entirely disabled by setting the
6322 The rollback command can be entirely disabled by setting the
6309 ``ui.rollback`` configuration setting to false. If you're here
6323 ``ui.rollback`` configuration setting to false. If you're here
6310 because you want to use rollback and it's disabled, you can
6324 because you want to use rollback and it's disabled, you can
6311 re-enable the command by setting ``ui.rollback`` to true.
6325 re-enable the command by setting ``ui.rollback`` to true.
6312
6326
6313 This command is not intended for use on public repositories. Once
6327 This command is not intended for use on public repositories. Once
6314 changes are visible for pull by other users, rolling a transaction
6328 changes are visible for pull by other users, rolling a transaction
6315 back locally is ineffective (someone else may already have pulled
6329 back locally is ineffective (someone else may already have pulled
6316 the changes). Furthermore, a race is possible with readers of the
6330 the changes). Furthermore, a race is possible with readers of the
6317 repository; for example an in-progress pull from the repository
6331 repository; for example an in-progress pull from the repository
6318 may fail if a rollback is performed.
6332 may fail if a rollback is performed.
6319
6333
6320 Returns 0 on success, 1 if no rollback data is available.
6334 Returns 0 on success, 1 if no rollback data is available.
6321 """
6335 """
6322 if not ui.configbool(b'ui', b'rollback'):
6336 if not ui.configbool(b'ui', b'rollback'):
6323 raise error.Abort(
6337 raise error.Abort(
6324 _(b'rollback is disabled because it is unsafe'),
6338 _(b'rollback is disabled because it is unsafe'),
6325 hint=b'see `hg help -v rollback` for information',
6339 hint=b'see `hg help -v rollback` for information',
6326 )
6340 )
6327 return repo.rollback(dryrun=opts.get(r'dry_run'), force=opts.get(r'force'))
6341 return repo.rollback(dryrun=opts.get(r'dry_run'), force=opts.get(r'force'))
6328
6342
6329
6343
6330 @command(
6344 @command(
6331 b'root',
6345 b'root',
6332 [] + formatteropts,
6346 [] + formatteropts,
6333 intents={INTENT_READONLY},
6347 intents={INTENT_READONLY},
6334 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6348 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6335 )
6349 )
6336 def root(ui, repo, **opts):
6350 def root(ui, repo, **opts):
6337 """print the root (top) of the current working directory
6351 """print the root (top) of the current working directory
6338
6352
6339 Print the root directory of the current repository.
6353 Print the root directory of the current repository.
6340
6354
6341 .. container:: verbose
6355 .. container:: verbose
6342
6356
6343 Template:
6357 Template:
6344
6358
6345 The following keywords are supported in addition to the common template
6359 The following keywords are supported in addition to the common template
6346 keywords and functions. See also :hg:`help templates`.
6360 keywords and functions. See also :hg:`help templates`.
6347
6361
6348 :hgpath: String. Path to the .hg directory.
6362 :hgpath: String. Path to the .hg directory.
6349 :storepath: String. Path to the directory holding versioned data.
6363 :storepath: String. Path to the directory holding versioned data.
6350
6364
6351 Returns 0 on success.
6365 Returns 0 on success.
6352 """
6366 """
6353 opts = pycompat.byteskwargs(opts)
6367 opts = pycompat.byteskwargs(opts)
6354 with ui.formatter(b'root', opts) as fm:
6368 with ui.formatter(b'root', opts) as fm:
6355 fm.startitem()
6369 fm.startitem()
6356 fm.write(b'reporoot', b'%s\n', repo.root)
6370 fm.write(b'reporoot', b'%s\n', repo.root)
6357 fm.data(hgpath=repo.path, storepath=repo.spath)
6371 fm.data(hgpath=repo.path, storepath=repo.spath)
6358
6372
6359
6373
6360 @command(
6374 @command(
6361 b'serve',
6375 b'serve',
6362 [
6376 [
6363 (
6377 (
6364 b'A',
6378 b'A',
6365 b'accesslog',
6379 b'accesslog',
6366 b'',
6380 b'',
6367 _(b'name of access log file to write to'),
6381 _(b'name of access log file to write to'),
6368 _(b'FILE'),
6382 _(b'FILE'),
6369 ),
6383 ),
6370 (b'd', b'daemon', None, _(b'run server in background')),
6384 (b'd', b'daemon', None, _(b'run server in background')),
6371 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6385 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6372 (
6386 (
6373 b'E',
6387 b'E',
6374 b'errorlog',
6388 b'errorlog',
6375 b'',
6389 b'',
6376 _(b'name of error log file to write to'),
6390 _(b'name of error log file to write to'),
6377 _(b'FILE'),
6391 _(b'FILE'),
6378 ),
6392 ),
6379 # use string type, then we can check if something was passed
6393 # use string type, then we can check if something was passed
6380 (
6394 (
6381 b'p',
6395 b'p',
6382 b'port',
6396 b'port',
6383 b'',
6397 b'',
6384 _(b'port to listen on (default: 8000)'),
6398 _(b'port to listen on (default: 8000)'),
6385 _(b'PORT'),
6399 _(b'PORT'),
6386 ),
6400 ),
6387 (
6401 (
6388 b'a',
6402 b'a',
6389 b'address',
6403 b'address',
6390 b'',
6404 b'',
6391 _(b'address to listen on (default: all interfaces)'),
6405 _(b'address to listen on (default: all interfaces)'),
6392 _(b'ADDR'),
6406 _(b'ADDR'),
6393 ),
6407 ),
6394 (
6408 (
6395 b'',
6409 b'',
6396 b'prefix',
6410 b'prefix',
6397 b'',
6411 b'',
6398 _(b'prefix path to serve from (default: server root)'),
6412 _(b'prefix path to serve from (default: server root)'),
6399 _(b'PREFIX'),
6413 _(b'PREFIX'),
6400 ),
6414 ),
6401 (
6415 (
6402 b'n',
6416 b'n',
6403 b'name',
6417 b'name',
6404 b'',
6418 b'',
6405 _(b'name to show in web pages (default: working directory)'),
6419 _(b'name to show in web pages (default: working directory)'),
6406 _(b'NAME'),
6420 _(b'NAME'),
6407 ),
6421 ),
6408 (
6422 (
6409 b'',
6423 b'',
6410 b'web-conf',
6424 b'web-conf',
6411 b'',
6425 b'',
6412 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6426 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6413 _(b'FILE'),
6427 _(b'FILE'),
6414 ),
6428 ),
6415 (
6429 (
6416 b'',
6430 b'',
6417 b'webdir-conf',
6431 b'webdir-conf',
6418 b'',
6432 b'',
6419 _(b'name of the hgweb config file (DEPRECATED)'),
6433 _(b'name of the hgweb config file (DEPRECATED)'),
6420 _(b'FILE'),
6434 _(b'FILE'),
6421 ),
6435 ),
6422 (
6436 (
6423 b'',
6437 b'',
6424 b'pid-file',
6438 b'pid-file',
6425 b'',
6439 b'',
6426 _(b'name of file to write process ID to'),
6440 _(b'name of file to write process ID to'),
6427 _(b'FILE'),
6441 _(b'FILE'),
6428 ),
6442 ),
6429 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6443 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6430 (
6444 (
6431 b'',
6445 b'',
6432 b'cmdserver',
6446 b'cmdserver',
6433 b'',
6447 b'',
6434 _(b'for remote clients (ADVANCED)'),
6448 _(b'for remote clients (ADVANCED)'),
6435 _(b'MODE'),
6449 _(b'MODE'),
6436 ),
6450 ),
6437 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6451 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6438 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6452 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6439 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6453 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6440 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6454 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6441 (b'', b'print-url', None, _(b'start and print only the URL')),
6455 (b'', b'print-url', None, _(b'start and print only the URL')),
6442 ]
6456 ]
6443 + subrepoopts,
6457 + subrepoopts,
6444 _(b'[OPTION]...'),
6458 _(b'[OPTION]...'),
6445 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6459 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6446 helpbasic=True,
6460 helpbasic=True,
6447 optionalrepo=True,
6461 optionalrepo=True,
6448 )
6462 )
6449 def serve(ui, repo, **opts):
6463 def serve(ui, repo, **opts):
6450 """start stand-alone webserver
6464 """start stand-alone webserver
6451
6465
6452 Start a local HTTP repository browser and pull server. You can use
6466 Start a local HTTP repository browser and pull server. You can use
6453 this for ad-hoc sharing and browsing of repositories. It is
6467 this for ad-hoc sharing and browsing of repositories. It is
6454 recommended to use a real web server to serve a repository for
6468 recommended to use a real web server to serve a repository for
6455 longer periods of time.
6469 longer periods of time.
6456
6470
6457 Please note that the server does not implement access control.
6471 Please note that the server does not implement access control.
6458 This means that, by default, anybody can read from the server and
6472 This means that, by default, anybody can read from the server and
6459 nobody can write to it by default. Set the ``web.allow-push``
6473 nobody can write to it by default. Set the ``web.allow-push``
6460 option to ``*`` to allow everybody to push to the server. You
6474 option to ``*`` to allow everybody to push to the server. You
6461 should use a real web server if you need to authenticate users.
6475 should use a real web server if you need to authenticate users.
6462
6476
6463 By default, the server logs accesses to stdout and errors to
6477 By default, the server logs accesses to stdout and errors to
6464 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6478 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6465 files.
6479 files.
6466
6480
6467 To have the server choose a free port number to listen on, specify
6481 To have the server choose a free port number to listen on, specify
6468 a port number of 0; in this case, the server will print the port
6482 a port number of 0; in this case, the server will print the port
6469 number it uses.
6483 number it uses.
6470
6484
6471 Returns 0 on success.
6485 Returns 0 on success.
6472 """
6486 """
6473
6487
6474 opts = pycompat.byteskwargs(opts)
6488 opts = pycompat.byteskwargs(opts)
6475 if opts[b"stdio"] and opts[b"cmdserver"]:
6489 if opts[b"stdio"] and opts[b"cmdserver"]:
6476 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6490 raise error.Abort(_(b"cannot use --stdio with --cmdserver"))
6477 if opts[b"print_url"] and ui.verbose:
6491 if opts[b"print_url"] and ui.verbose:
6478 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6492 raise error.Abort(_(b"cannot use --print-url with --verbose"))
6479
6493
6480 if opts[b"stdio"]:
6494 if opts[b"stdio"]:
6481 if repo is None:
6495 if repo is None:
6482 raise error.RepoError(
6496 raise error.RepoError(
6483 _(b"there is no Mercurial repository here (.hg not found)")
6497 _(b"there is no Mercurial repository here (.hg not found)")
6484 )
6498 )
6485 s = wireprotoserver.sshserver(ui, repo)
6499 s = wireprotoserver.sshserver(ui, repo)
6486 s.serve_forever()
6500 s.serve_forever()
6487
6501
6488 service = server.createservice(ui, repo, opts)
6502 service = server.createservice(ui, repo, opts)
6489 return server.runservice(opts, initfn=service.init, runfn=service.run)
6503 return server.runservice(opts, initfn=service.init, runfn=service.run)
6490
6504
6491
6505
6492 @command(
6506 @command(
6493 b'shelve',
6507 b'shelve',
6494 [
6508 [
6495 (
6509 (
6496 b'A',
6510 b'A',
6497 b'addremove',
6511 b'addremove',
6498 None,
6512 None,
6499 _(b'mark new/missing files as added/removed before shelving'),
6513 _(b'mark new/missing files as added/removed before shelving'),
6500 ),
6514 ),
6501 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6515 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6502 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6516 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6503 (
6517 (
6504 b'',
6518 b'',
6505 b'date',
6519 b'date',
6506 b'',
6520 b'',
6507 _(b'shelve with the specified commit date'),
6521 _(b'shelve with the specified commit date'),
6508 _(b'DATE'),
6522 _(b'DATE'),
6509 ),
6523 ),
6510 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6524 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6511 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6525 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6512 (
6526 (
6513 b'k',
6527 b'k',
6514 b'keep',
6528 b'keep',
6515 False,
6529 False,
6516 _(b'shelve, but keep changes in the working directory'),
6530 _(b'shelve, but keep changes in the working directory'),
6517 ),
6531 ),
6518 (b'l', b'list', None, _(b'list current shelves')),
6532 (b'l', b'list', None, _(b'list current shelves')),
6519 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6533 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6520 (
6534 (
6521 b'n',
6535 b'n',
6522 b'name',
6536 b'name',
6523 b'',
6537 b'',
6524 _(b'use the given name for the shelved commit'),
6538 _(b'use the given name for the shelved commit'),
6525 _(b'NAME'),
6539 _(b'NAME'),
6526 ),
6540 ),
6527 (
6541 (
6528 b'p',
6542 b'p',
6529 b'patch',
6543 b'patch',
6530 None,
6544 None,
6531 _(
6545 _(
6532 b'output patches for changes (provide the names of the shelved '
6546 b'output patches for changes (provide the names of the shelved '
6533 b'changes as positional arguments)'
6547 b'changes as positional arguments)'
6534 ),
6548 ),
6535 ),
6549 ),
6536 (b'i', b'interactive', None, _(b'interactive mode')),
6550 (b'i', b'interactive', None, _(b'interactive mode')),
6537 (
6551 (
6538 b'',
6552 b'',
6539 b'stat',
6553 b'stat',
6540 None,
6554 None,
6541 _(
6555 _(
6542 b'output diffstat-style summary of changes (provide the names of '
6556 b'output diffstat-style summary of changes (provide the names of '
6543 b'the shelved changes as positional arguments)'
6557 b'the shelved changes as positional arguments)'
6544 ),
6558 ),
6545 ),
6559 ),
6546 ]
6560 ]
6547 + cmdutil.walkopts,
6561 + cmdutil.walkopts,
6548 _(b'hg shelve [OPTION]... [FILE]...'),
6562 _(b'hg shelve [OPTION]... [FILE]...'),
6549 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6563 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6550 )
6564 )
6551 def shelve(ui, repo, *pats, **opts):
6565 def shelve(ui, repo, *pats, **opts):
6552 '''save and set aside changes from the working directory
6566 '''save and set aside changes from the working directory
6553
6567
6554 Shelving takes files that "hg status" reports as not clean, saves
6568 Shelving takes files that "hg status" reports as not clean, saves
6555 the modifications to a bundle (a shelved change), and reverts the
6569 the modifications to a bundle (a shelved change), and reverts the
6556 files so that their state in the working directory becomes clean.
6570 files so that their state in the working directory becomes clean.
6557
6571
6558 To restore these changes to the working directory, using "hg
6572 To restore these changes to the working directory, using "hg
6559 unshelve"; this will work even if you switch to a different
6573 unshelve"; this will work even if you switch to a different
6560 commit.
6574 commit.
6561
6575
6562 When no files are specified, "hg shelve" saves all not-clean
6576 When no files are specified, "hg shelve" saves all not-clean
6563 files. If specific files or directories are named, only changes to
6577 files. If specific files or directories are named, only changes to
6564 those files are shelved.
6578 those files are shelved.
6565
6579
6566 In bare shelve (when no files are specified, without interactive,
6580 In bare shelve (when no files are specified, without interactive,
6567 include and exclude option), shelving remembers information if the
6581 include and exclude option), shelving remembers information if the
6568 working directory was on newly created branch, in other words working
6582 working directory was on newly created branch, in other words working
6569 directory was on different branch than its first parent. In this
6583 directory was on different branch than its first parent. In this
6570 situation unshelving restores branch information to the working directory.
6584 situation unshelving restores branch information to the working directory.
6571
6585
6572 Each shelved change has a name that makes it easier to find later.
6586 Each shelved change has a name that makes it easier to find later.
6573 The name of a shelved change defaults to being based on the active
6587 The name of a shelved change defaults to being based on the active
6574 bookmark, or if there is no active bookmark, the current named
6588 bookmark, or if there is no active bookmark, the current named
6575 branch. To specify a different name, use ``--name``.
6589 branch. To specify a different name, use ``--name``.
6576
6590
6577 To see a list of existing shelved changes, use the ``--list``
6591 To see a list of existing shelved changes, use the ``--list``
6578 option. For each shelved change, this will print its name, age,
6592 option. For each shelved change, this will print its name, age,
6579 and description; use ``--patch`` or ``--stat`` for more details.
6593 and description; use ``--patch`` or ``--stat`` for more details.
6580
6594
6581 To delete specific shelved changes, use ``--delete``. To delete
6595 To delete specific shelved changes, use ``--delete``. To delete
6582 all shelved changes, use ``--cleanup``.
6596 all shelved changes, use ``--cleanup``.
6583 '''
6597 '''
6584 opts = pycompat.byteskwargs(opts)
6598 opts = pycompat.byteskwargs(opts)
6585 allowables = [
6599 allowables = [
6586 (b'addremove', {b'create'}), # 'create' is pseudo action
6600 (b'addremove', {b'create'}), # 'create' is pseudo action
6587 (b'unknown', {b'create'}),
6601 (b'unknown', {b'create'}),
6588 (b'cleanup', {b'cleanup'}),
6602 (b'cleanup', {b'cleanup'}),
6589 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6603 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6590 (b'delete', {b'delete'}),
6604 (b'delete', {b'delete'}),
6591 (b'edit', {b'create'}),
6605 (b'edit', {b'create'}),
6592 (b'keep', {b'create'}),
6606 (b'keep', {b'create'}),
6593 (b'list', {b'list'}),
6607 (b'list', {b'list'}),
6594 (b'message', {b'create'}),
6608 (b'message', {b'create'}),
6595 (b'name', {b'create'}),
6609 (b'name', {b'create'}),
6596 (b'patch', {b'patch', b'list'}),
6610 (b'patch', {b'patch', b'list'}),
6597 (b'stat', {b'stat', b'list'}),
6611 (b'stat', {b'stat', b'list'}),
6598 ]
6612 ]
6599
6613
6600 def checkopt(opt):
6614 def checkopt(opt):
6601 if opts.get(opt):
6615 if opts.get(opt):
6602 for i, allowable in allowables:
6616 for i, allowable in allowables:
6603 if opts[i] and opt not in allowable:
6617 if opts[i] and opt not in allowable:
6604 raise error.Abort(
6618 raise error.Abort(
6605 _(
6619 _(
6606 b"options '--%s' and '--%s' may not be "
6620 b"options '--%s' and '--%s' may not be "
6607 b"used together"
6621 b"used together"
6608 )
6622 )
6609 % (opt, i)
6623 % (opt, i)
6610 )
6624 )
6611 return True
6625 return True
6612
6626
6613 if checkopt(b'cleanup'):
6627 if checkopt(b'cleanup'):
6614 if pats:
6628 if pats:
6615 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6629 raise error.Abort(_(b"cannot specify names when using '--cleanup'"))
6616 return shelvemod.cleanupcmd(ui, repo)
6630 return shelvemod.cleanupcmd(ui, repo)
6617 elif checkopt(b'delete'):
6631 elif checkopt(b'delete'):
6618 return shelvemod.deletecmd(ui, repo, pats)
6632 return shelvemod.deletecmd(ui, repo, pats)
6619 elif checkopt(b'list'):
6633 elif checkopt(b'list'):
6620 return shelvemod.listcmd(ui, repo, pats, opts)
6634 return shelvemod.listcmd(ui, repo, pats, opts)
6621 elif checkopt(b'patch') or checkopt(b'stat'):
6635 elif checkopt(b'patch') or checkopt(b'stat'):
6622 return shelvemod.patchcmds(ui, repo, pats, opts)
6636 return shelvemod.patchcmds(ui, repo, pats, opts)
6623 else:
6637 else:
6624 return shelvemod.createcmd(ui, repo, pats, opts)
6638 return shelvemod.createcmd(ui, repo, pats, opts)
6625
6639
6626
6640
6627 _NOTTERSE = b'nothing'
6641 _NOTTERSE = b'nothing'
6628
6642
6629
6643
6630 @command(
6644 @command(
6631 b'status|st',
6645 b'status|st',
6632 [
6646 [
6633 (b'A', b'all', None, _(b'show status of all files')),
6647 (b'A', b'all', None, _(b'show status of all files')),
6634 (b'm', b'modified', None, _(b'show only modified files')),
6648 (b'm', b'modified', None, _(b'show only modified files')),
6635 (b'a', b'added', None, _(b'show only added files')),
6649 (b'a', b'added', None, _(b'show only added files')),
6636 (b'r', b'removed', None, _(b'show only removed files')),
6650 (b'r', b'removed', None, _(b'show only removed files')),
6637 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6651 (b'd', b'deleted', None, _(b'show only deleted (but tracked) files')),
6638 (b'c', b'clean', None, _(b'show only files without changes')),
6652 (b'c', b'clean', None, _(b'show only files without changes')),
6639 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6653 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6640 (b'i', b'ignored', None, _(b'show only ignored files')),
6654 (b'i', b'ignored', None, _(b'show only ignored files')),
6641 (b'n', b'no-status', None, _(b'hide status prefix')),
6655 (b'n', b'no-status', None, _(b'hide status prefix')),
6642 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6656 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6643 (b'C', b'copies', None, _(b'show source of copied files')),
6657 (b'C', b'copies', None, _(b'show source of copied files')),
6644 (
6658 (
6645 b'0',
6659 b'0',
6646 b'print0',
6660 b'print0',
6647 None,
6661 None,
6648 _(b'end filenames with NUL, for use with xargs'),
6662 _(b'end filenames with NUL, for use with xargs'),
6649 ),
6663 ),
6650 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6664 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6651 (
6665 (
6652 b'',
6666 b'',
6653 b'change',
6667 b'change',
6654 b'',
6668 b'',
6655 _(b'list the changed files of a revision'),
6669 _(b'list the changed files of a revision'),
6656 _(b'REV'),
6670 _(b'REV'),
6657 ),
6671 ),
6658 ]
6672 ]
6659 + walkopts
6673 + walkopts
6660 + subrepoopts
6674 + subrepoopts
6661 + formatteropts,
6675 + formatteropts,
6662 _(b'[OPTION]... [FILE]...'),
6676 _(b'[OPTION]... [FILE]...'),
6663 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6664 helpbasic=True,
6678 helpbasic=True,
6665 inferrepo=True,
6679 inferrepo=True,
6666 intents={INTENT_READONLY},
6680 intents={INTENT_READONLY},
6667 )
6681 )
6668 def status(ui, repo, *pats, **opts):
6682 def status(ui, repo, *pats, **opts):
6669 """show changed files in the working directory
6683 """show changed files in the working directory
6670
6684
6671 Show status of files in the repository. If names are given, only
6685 Show status of files in the repository. If names are given, only
6672 files that match are shown. Files that are clean or ignored or
6686 files that match are shown. Files that are clean or ignored or
6673 the source of a copy/move operation, are not listed unless
6687 the source of a copy/move operation, are not listed unless
6674 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6688 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6675 Unless options described with "show only ..." are given, the
6689 Unless options described with "show only ..." are given, the
6676 options -mardu are used.
6690 options -mardu are used.
6677
6691
6678 Option -q/--quiet hides untracked (unknown and ignored) files
6692 Option -q/--quiet hides untracked (unknown and ignored) files
6679 unless explicitly requested with -u/--unknown or -i/--ignored.
6693 unless explicitly requested with -u/--unknown or -i/--ignored.
6680
6694
6681 .. note::
6695 .. note::
6682
6696
6683 :hg:`status` may appear to disagree with diff if permissions have
6697 :hg:`status` may appear to disagree with diff if permissions have
6684 changed or a merge has occurred. The standard diff format does
6698 changed or a merge has occurred. The standard diff format does
6685 not report permission changes and diff only reports changes
6699 not report permission changes and diff only reports changes
6686 relative to one merge parent.
6700 relative to one merge parent.
6687
6701
6688 If one revision is given, it is used as the base revision.
6702 If one revision is given, it is used as the base revision.
6689 If two revisions are given, the differences between them are
6703 If two revisions are given, the differences between them are
6690 shown. The --change option can also be used as a shortcut to list
6704 shown. The --change option can also be used as a shortcut to list
6691 the changed files of a revision from its first parent.
6705 the changed files of a revision from its first parent.
6692
6706
6693 The codes used to show the status of files are::
6707 The codes used to show the status of files are::
6694
6708
6695 M = modified
6709 M = modified
6696 A = added
6710 A = added
6697 R = removed
6711 R = removed
6698 C = clean
6712 C = clean
6699 ! = missing (deleted by non-hg command, but still tracked)
6713 ! = missing (deleted by non-hg command, but still tracked)
6700 ? = not tracked
6714 ? = not tracked
6701 I = ignored
6715 I = ignored
6702 = origin of the previous file (with --copies)
6716 = origin of the previous file (with --copies)
6703
6717
6704 .. container:: verbose
6718 .. container:: verbose
6705
6719
6706 The -t/--terse option abbreviates the output by showing only the directory
6720 The -t/--terse option abbreviates the output by showing only the directory
6707 name if all the files in it share the same status. The option takes an
6721 name if all the files in it share the same status. The option takes an
6708 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6722 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6709 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6723 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6710 for 'ignored' and 'c' for clean.
6724 for 'ignored' and 'c' for clean.
6711
6725
6712 It abbreviates only those statuses which are passed. Note that clean and
6726 It abbreviates only those statuses which are passed. Note that clean and
6713 ignored files are not displayed with '--terse ic' unless the -c/--clean
6727 ignored files are not displayed with '--terse ic' unless the -c/--clean
6714 and -i/--ignored options are also used.
6728 and -i/--ignored options are also used.
6715
6729
6716 The -v/--verbose option shows information when the repository is in an
6730 The -v/--verbose option shows information when the repository is in an
6717 unfinished merge, shelve, rebase state etc. You can have this behavior
6731 unfinished merge, shelve, rebase state etc. You can have this behavior
6718 turned on by default by enabling the ``commands.status.verbose`` option.
6732 turned on by default by enabling the ``commands.status.verbose`` option.
6719
6733
6720 You can skip displaying some of these states by setting
6734 You can skip displaying some of these states by setting
6721 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6735 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6722 'histedit', 'merge', 'rebase', or 'unshelve'.
6736 'histedit', 'merge', 'rebase', or 'unshelve'.
6723
6737
6724 Template:
6738 Template:
6725
6739
6726 The following keywords are supported in addition to the common template
6740 The following keywords are supported in addition to the common template
6727 keywords and functions. See also :hg:`help templates`.
6741 keywords and functions. See also :hg:`help templates`.
6728
6742
6729 :path: String. Repository-absolute path of the file.
6743 :path: String. Repository-absolute path of the file.
6730 :source: String. Repository-absolute path of the file originated from.
6744 :source: String. Repository-absolute path of the file originated from.
6731 Available if ``--copies`` is specified.
6745 Available if ``--copies`` is specified.
6732 :status: String. Character denoting file's status.
6746 :status: String. Character denoting file's status.
6733
6747
6734 Examples:
6748 Examples:
6735
6749
6736 - show changes in the working directory relative to a
6750 - show changes in the working directory relative to a
6737 changeset::
6751 changeset::
6738
6752
6739 hg status --rev 9353
6753 hg status --rev 9353
6740
6754
6741 - show changes in the working directory relative to the
6755 - show changes in the working directory relative to the
6742 current directory (see :hg:`help patterns` for more information)::
6756 current directory (see :hg:`help patterns` for more information)::
6743
6757
6744 hg status re:
6758 hg status re:
6745
6759
6746 - show all changes including copies in an existing changeset::
6760 - show all changes including copies in an existing changeset::
6747
6761
6748 hg status --copies --change 9353
6762 hg status --copies --change 9353
6749
6763
6750 - get a NUL separated list of added files, suitable for xargs::
6764 - get a NUL separated list of added files, suitable for xargs::
6751
6765
6752 hg status -an0
6766 hg status -an0
6753
6767
6754 - show more information about the repository status, abbreviating
6768 - show more information about the repository status, abbreviating
6755 added, removed, modified, deleted, and untracked paths::
6769 added, removed, modified, deleted, and untracked paths::
6756
6770
6757 hg status -v -t mardu
6771 hg status -v -t mardu
6758
6772
6759 Returns 0 on success.
6773 Returns 0 on success.
6760
6774
6761 """
6775 """
6762
6776
6763 opts = pycompat.byteskwargs(opts)
6777 opts = pycompat.byteskwargs(opts)
6764 revs = opts.get(b'rev')
6778 revs = opts.get(b'rev')
6765 change = opts.get(b'change')
6779 change = opts.get(b'change')
6766 terse = opts.get(b'terse')
6780 terse = opts.get(b'terse')
6767 if terse is _NOTTERSE:
6781 if terse is _NOTTERSE:
6768 if revs:
6782 if revs:
6769 terse = b''
6783 terse = b''
6770 else:
6784 else:
6771 terse = ui.config(b'commands', b'status.terse')
6785 terse = ui.config(b'commands', b'status.terse')
6772
6786
6773 if revs and change:
6787 if revs and change:
6774 msg = _(b'cannot specify --rev and --change at the same time')
6788 msg = _(b'cannot specify --rev and --change at the same time')
6775 raise error.Abort(msg)
6789 raise error.Abort(msg)
6776 elif revs and terse:
6790 elif revs and terse:
6777 msg = _(b'cannot use --terse with --rev')
6791 msg = _(b'cannot use --terse with --rev')
6778 raise error.Abort(msg)
6792 raise error.Abort(msg)
6779 elif change:
6793 elif change:
6780 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6794 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6781 ctx2 = scmutil.revsingle(repo, change, None)
6795 ctx2 = scmutil.revsingle(repo, change, None)
6782 ctx1 = ctx2.p1()
6796 ctx1 = ctx2.p1()
6783 else:
6797 else:
6784 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6798 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6785 ctx1, ctx2 = scmutil.revpair(repo, revs)
6799 ctx1, ctx2 = scmutil.revpair(repo, revs)
6786
6800
6787 forcerelativevalue = None
6801 forcerelativevalue = None
6788 if ui.hasconfig(b'commands', b'status.relative'):
6802 if ui.hasconfig(b'commands', b'status.relative'):
6789 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6803 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6790 uipathfn = scmutil.getuipathfn(
6804 uipathfn = scmutil.getuipathfn(
6791 repo,
6805 repo,
6792 legacyrelativevalue=bool(pats),
6806 legacyrelativevalue=bool(pats),
6793 forcerelativevalue=forcerelativevalue,
6807 forcerelativevalue=forcerelativevalue,
6794 )
6808 )
6795
6809
6796 if opts.get(b'print0'):
6810 if opts.get(b'print0'):
6797 end = b'\0'
6811 end = b'\0'
6798 else:
6812 else:
6799 end = b'\n'
6813 end = b'\n'
6800 copy = {}
6814 copy = {}
6801 states = b'modified added removed deleted unknown ignored clean'.split()
6815 states = b'modified added removed deleted unknown ignored clean'.split()
6802 show = [k for k in states if opts.get(k)]
6816 show = [k for k in states if opts.get(k)]
6803 if opts.get(b'all'):
6817 if opts.get(b'all'):
6804 show += ui.quiet and (states[:4] + [b'clean']) or states
6818 show += ui.quiet and (states[:4] + [b'clean']) or states
6805
6819
6806 if not show:
6820 if not show:
6807 if ui.quiet:
6821 if ui.quiet:
6808 show = states[:4]
6822 show = states[:4]
6809 else:
6823 else:
6810 show = states[:5]
6824 show = states[:5]
6811
6825
6812 m = scmutil.match(ctx2, pats, opts)
6826 m = scmutil.match(ctx2, pats, opts)
6813 if terse:
6827 if terse:
6814 # we need to compute clean and unknown to terse
6828 # we need to compute clean and unknown to terse
6815 stat = repo.status(
6829 stat = repo.status(
6816 ctx1.node(),
6830 ctx1.node(),
6817 ctx2.node(),
6831 ctx2.node(),
6818 m,
6832 m,
6819 b'ignored' in show or b'i' in terse,
6833 b'ignored' in show or b'i' in terse,
6820 clean=True,
6834 clean=True,
6821 unknown=True,
6835 unknown=True,
6822 listsubrepos=opts.get(b'subrepos'),
6836 listsubrepos=opts.get(b'subrepos'),
6823 )
6837 )
6824
6838
6825 stat = cmdutil.tersedir(stat, terse)
6839 stat = cmdutil.tersedir(stat, terse)
6826 else:
6840 else:
6827 stat = repo.status(
6841 stat = repo.status(
6828 ctx1.node(),
6842 ctx1.node(),
6829 ctx2.node(),
6843 ctx2.node(),
6830 m,
6844 m,
6831 b'ignored' in show,
6845 b'ignored' in show,
6832 b'clean' in show,
6846 b'clean' in show,
6833 b'unknown' in show,
6847 b'unknown' in show,
6834 opts.get(b'subrepos'),
6848 opts.get(b'subrepos'),
6835 )
6849 )
6836
6850
6837 changestates = zip(states, pycompat.iterbytestr(b'MAR!?IC'), stat)
6851 changestates = zip(states, pycompat.iterbytestr(b'MAR!?IC'), stat)
6838
6852
6839 if (
6853 if (
6840 opts.get(b'all')
6854 opts.get(b'all')
6841 or opts.get(b'copies')
6855 or opts.get(b'copies')
6842 or ui.configbool(b'ui', b'statuscopies')
6856 or ui.configbool(b'ui', b'statuscopies')
6843 ) and not opts.get(b'no_status'):
6857 ) and not opts.get(b'no_status'):
6844 copy = copies.pathcopies(ctx1, ctx2, m)
6858 copy = copies.pathcopies(ctx1, ctx2, m)
6845
6859
6846 ui.pager(b'status')
6860 ui.pager(b'status')
6847 fm = ui.formatter(b'status', opts)
6861 fm = ui.formatter(b'status', opts)
6848 fmt = b'%s' + end
6862 fmt = b'%s' + end
6849 showchar = not opts.get(b'no_status')
6863 showchar = not opts.get(b'no_status')
6850
6864
6851 for state, char, files in changestates:
6865 for state, char, files in changestates:
6852 if state in show:
6866 if state in show:
6853 label = b'status.' + state
6867 label = b'status.' + state
6854 for f in files:
6868 for f in files:
6855 fm.startitem()
6869 fm.startitem()
6856 fm.context(ctx=ctx2)
6870 fm.context(ctx=ctx2)
6857 fm.data(path=f)
6871 fm.data(path=f)
6858 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6872 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6859 fm.plain(fmt % uipathfn(f), label=label)
6873 fm.plain(fmt % uipathfn(f), label=label)
6860 if f in copy:
6874 if f in copy:
6861 fm.data(source=copy[f])
6875 fm.data(source=copy[f])
6862 fm.plain(
6876 fm.plain(
6863 (b' %s' + end) % uipathfn(copy[f]),
6877 (b' %s' + end) % uipathfn(copy[f]),
6864 label=b'status.copied',
6878 label=b'status.copied',
6865 )
6879 )
6866
6880
6867 if (
6881 if (
6868 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6882 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6869 ) and not ui.plain():
6883 ) and not ui.plain():
6870 cmdutil.morestatus(repo, fm)
6884 cmdutil.morestatus(repo, fm)
6871 fm.end()
6885 fm.end()
6872
6886
6873
6887
6874 @command(
6888 @command(
6875 b'summary|sum',
6889 b'summary|sum',
6876 [(b'', b'remote', None, _(b'check for push and pull'))],
6890 [(b'', b'remote', None, _(b'check for push and pull'))],
6877 b'[--remote]',
6891 b'[--remote]',
6878 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6892 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6879 helpbasic=True,
6893 helpbasic=True,
6880 intents={INTENT_READONLY},
6894 intents={INTENT_READONLY},
6881 )
6895 )
6882 def summary(ui, repo, **opts):
6896 def summary(ui, repo, **opts):
6883 """summarize working directory state
6897 """summarize working directory state
6884
6898
6885 This generates a brief summary of the working directory state,
6899 This generates a brief summary of the working directory state,
6886 including parents, branch, commit status, phase and available updates.
6900 including parents, branch, commit status, phase and available updates.
6887
6901
6888 With the --remote option, this will check the default paths for
6902 With the --remote option, this will check the default paths for
6889 incoming and outgoing changes. This can be time-consuming.
6903 incoming and outgoing changes. This can be time-consuming.
6890
6904
6891 Returns 0 on success.
6905 Returns 0 on success.
6892 """
6906 """
6893
6907
6894 opts = pycompat.byteskwargs(opts)
6908 opts = pycompat.byteskwargs(opts)
6895 ui.pager(b'summary')
6909 ui.pager(b'summary')
6896 ctx = repo[None]
6910 ctx = repo[None]
6897 parents = ctx.parents()
6911 parents = ctx.parents()
6898 pnode = parents[0].node()
6912 pnode = parents[0].node()
6899 marks = []
6913 marks = []
6900
6914
6901 try:
6915 try:
6902 ms = mergemod.mergestate.read(repo)
6916 ms = mergemod.mergestate.read(repo)
6903 except error.UnsupportedMergeRecords as e:
6917 except error.UnsupportedMergeRecords as e:
6904 s = b' '.join(e.recordtypes)
6918 s = b' '.join(e.recordtypes)
6905 ui.warn(
6919 ui.warn(
6906 _(b'warning: merge state has unsupported record types: %s\n') % s
6920 _(b'warning: merge state has unsupported record types: %s\n') % s
6907 )
6921 )
6908 unresolved = []
6922 unresolved = []
6909 else:
6923 else:
6910 unresolved = list(ms.unresolved())
6924 unresolved = list(ms.unresolved())
6911
6925
6912 for p in parents:
6926 for p in parents:
6913 # label with log.changeset (instead of log.parent) since this
6927 # label with log.changeset (instead of log.parent) since this
6914 # shows a working directory parent *changeset*:
6928 # shows a working directory parent *changeset*:
6915 # i18n: column positioning for "hg summary"
6929 # i18n: column positioning for "hg summary"
6916 ui.write(
6930 ui.write(
6917 _(b'parent: %d:%s ') % (p.rev(), p),
6931 _(b'parent: %d:%s ') % (p.rev(), p),
6918 label=logcmdutil.changesetlabels(p),
6932 label=logcmdutil.changesetlabels(p),
6919 )
6933 )
6920 ui.write(b' '.join(p.tags()), label=b'log.tag')
6934 ui.write(b' '.join(p.tags()), label=b'log.tag')
6921 if p.bookmarks():
6935 if p.bookmarks():
6922 marks.extend(p.bookmarks())
6936 marks.extend(p.bookmarks())
6923 if p.rev() == -1:
6937 if p.rev() == -1:
6924 if not len(repo):
6938 if not len(repo):
6925 ui.write(_(b' (empty repository)'))
6939 ui.write(_(b' (empty repository)'))
6926 else:
6940 else:
6927 ui.write(_(b' (no revision checked out)'))
6941 ui.write(_(b' (no revision checked out)'))
6928 if p.obsolete():
6942 if p.obsolete():
6929 ui.write(_(b' (obsolete)'))
6943 ui.write(_(b' (obsolete)'))
6930 if p.isunstable():
6944 if p.isunstable():
6931 instabilities = (
6945 instabilities = (
6932 ui.label(instability, b'trouble.%s' % instability)
6946 ui.label(instability, b'trouble.%s' % instability)
6933 for instability in p.instabilities()
6947 for instability in p.instabilities()
6934 )
6948 )
6935 ui.write(b' (' + b', '.join(instabilities) + b')')
6949 ui.write(b' (' + b', '.join(instabilities) + b')')
6936 ui.write(b'\n')
6950 ui.write(b'\n')
6937 if p.description():
6951 if p.description():
6938 ui.status(
6952 ui.status(
6939 b' ' + p.description().splitlines()[0].strip() + b'\n',
6953 b' ' + p.description().splitlines()[0].strip() + b'\n',
6940 label=b'log.summary',
6954 label=b'log.summary',
6941 )
6955 )
6942
6956
6943 branch = ctx.branch()
6957 branch = ctx.branch()
6944 bheads = repo.branchheads(branch)
6958 bheads = repo.branchheads(branch)
6945 # i18n: column positioning for "hg summary"
6959 # i18n: column positioning for "hg summary"
6946 m = _(b'branch: %s\n') % branch
6960 m = _(b'branch: %s\n') % branch
6947 if branch != b'default':
6961 if branch != b'default':
6948 ui.write(m, label=b'log.branch')
6962 ui.write(m, label=b'log.branch')
6949 else:
6963 else:
6950 ui.status(m, label=b'log.branch')
6964 ui.status(m, label=b'log.branch')
6951
6965
6952 if marks:
6966 if marks:
6953 active = repo._activebookmark
6967 active = repo._activebookmark
6954 # i18n: column positioning for "hg summary"
6968 # i18n: column positioning for "hg summary"
6955 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6969 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6956 if active is not None:
6970 if active is not None:
6957 if active in marks:
6971 if active in marks:
6958 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6972 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6959 marks.remove(active)
6973 marks.remove(active)
6960 else:
6974 else:
6961 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6975 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6962 for m in marks:
6976 for m in marks:
6963 ui.write(b' ' + m, label=b'log.bookmark')
6977 ui.write(b' ' + m, label=b'log.bookmark')
6964 ui.write(b'\n', label=b'log.bookmark')
6978 ui.write(b'\n', label=b'log.bookmark')
6965
6979
6966 status = repo.status(unknown=True)
6980 status = repo.status(unknown=True)
6967
6981
6968 c = repo.dirstate.copies()
6982 c = repo.dirstate.copies()
6969 copied, renamed = [], []
6983 copied, renamed = [], []
6970 for d, s in pycompat.iteritems(c):
6984 for d, s in pycompat.iteritems(c):
6971 if s in status.removed:
6985 if s in status.removed:
6972 status.removed.remove(s)
6986 status.removed.remove(s)
6973 renamed.append(d)
6987 renamed.append(d)
6974 else:
6988 else:
6975 copied.append(d)
6989 copied.append(d)
6976 if d in status.added:
6990 if d in status.added:
6977 status.added.remove(d)
6991 status.added.remove(d)
6978
6992
6979 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6993 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6980
6994
6981 labels = [
6995 labels = [
6982 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6996 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6983 (ui.label(_(b'%d added'), b'status.added'), status.added),
6997 (ui.label(_(b'%d added'), b'status.added'), status.added),
6984 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6998 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6985 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6999 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6986 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7000 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6987 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7001 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6988 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7002 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6989 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7003 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6990 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7004 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6991 ]
7005 ]
6992 t = []
7006 t = []
6993 for l, s in labels:
7007 for l, s in labels:
6994 if s:
7008 if s:
6995 t.append(l % len(s))
7009 t.append(l % len(s))
6996
7010
6997 t = b', '.join(t)
7011 t = b', '.join(t)
6998 cleanworkdir = False
7012 cleanworkdir = False
6999
7013
7000 if repo.vfs.exists(b'graftstate'):
7014 if repo.vfs.exists(b'graftstate'):
7001 t += _(b' (graft in progress)')
7015 t += _(b' (graft in progress)')
7002 if repo.vfs.exists(b'updatestate'):
7016 if repo.vfs.exists(b'updatestate'):
7003 t += _(b' (interrupted update)')
7017 t += _(b' (interrupted update)')
7004 elif len(parents) > 1:
7018 elif len(parents) > 1:
7005 t += _(b' (merge)')
7019 t += _(b' (merge)')
7006 elif branch != parents[0].branch():
7020 elif branch != parents[0].branch():
7007 t += _(b' (new branch)')
7021 t += _(b' (new branch)')
7008 elif parents[0].closesbranch() and pnode in repo.branchheads(
7022 elif parents[0].closesbranch() and pnode in repo.branchheads(
7009 branch, closed=True
7023 branch, closed=True
7010 ):
7024 ):
7011 t += _(b' (head closed)')
7025 t += _(b' (head closed)')
7012 elif not (
7026 elif not (
7013 status.modified
7027 status.modified
7014 or status.added
7028 or status.added
7015 or status.removed
7029 or status.removed
7016 or renamed
7030 or renamed
7017 or copied
7031 or copied
7018 or subs
7032 or subs
7019 ):
7033 ):
7020 t += _(b' (clean)')
7034 t += _(b' (clean)')
7021 cleanworkdir = True
7035 cleanworkdir = True
7022 elif pnode not in bheads:
7036 elif pnode not in bheads:
7023 t += _(b' (new branch head)')
7037 t += _(b' (new branch head)')
7024
7038
7025 if parents:
7039 if parents:
7026 pendingphase = max(p.phase() for p in parents)
7040 pendingphase = max(p.phase() for p in parents)
7027 else:
7041 else:
7028 pendingphase = phases.public
7042 pendingphase = phases.public
7029
7043
7030 if pendingphase > phases.newcommitphase(ui):
7044 if pendingphase > phases.newcommitphase(ui):
7031 t += b' (%s)' % phases.phasenames[pendingphase]
7045 t += b' (%s)' % phases.phasenames[pendingphase]
7032
7046
7033 if cleanworkdir:
7047 if cleanworkdir:
7034 # i18n: column positioning for "hg summary"
7048 # i18n: column positioning for "hg summary"
7035 ui.status(_(b'commit: %s\n') % t.strip())
7049 ui.status(_(b'commit: %s\n') % t.strip())
7036 else:
7050 else:
7037 # i18n: column positioning for "hg summary"
7051 # i18n: column positioning for "hg summary"
7038 ui.write(_(b'commit: %s\n') % t.strip())
7052 ui.write(_(b'commit: %s\n') % t.strip())
7039
7053
7040 # all ancestors of branch heads - all ancestors of parent = new csets
7054 # all ancestors of branch heads - all ancestors of parent = new csets
7041 new = len(
7055 new = len(
7042 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7056 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7043 )
7057 )
7044
7058
7045 if new == 0:
7059 if new == 0:
7046 # i18n: column positioning for "hg summary"
7060 # i18n: column positioning for "hg summary"
7047 ui.status(_(b'update: (current)\n'))
7061 ui.status(_(b'update: (current)\n'))
7048 elif pnode not in bheads:
7062 elif pnode not in bheads:
7049 # i18n: column positioning for "hg summary"
7063 # i18n: column positioning for "hg summary"
7050 ui.write(_(b'update: %d new changesets (update)\n') % new)
7064 ui.write(_(b'update: %d new changesets (update)\n') % new)
7051 else:
7065 else:
7052 # i18n: column positioning for "hg summary"
7066 # i18n: column positioning for "hg summary"
7053 ui.write(
7067 ui.write(
7054 _(b'update: %d new changesets, %d branch heads (merge)\n')
7068 _(b'update: %d new changesets, %d branch heads (merge)\n')
7055 % (new, len(bheads))
7069 % (new, len(bheads))
7056 )
7070 )
7057
7071
7058 t = []
7072 t = []
7059 draft = len(repo.revs(b'draft()'))
7073 draft = len(repo.revs(b'draft()'))
7060 if draft:
7074 if draft:
7061 t.append(_(b'%d draft') % draft)
7075 t.append(_(b'%d draft') % draft)
7062 secret = len(repo.revs(b'secret()'))
7076 secret = len(repo.revs(b'secret()'))
7063 if secret:
7077 if secret:
7064 t.append(_(b'%d secret') % secret)
7078 t.append(_(b'%d secret') % secret)
7065
7079
7066 if draft or secret:
7080 if draft or secret:
7067 ui.status(_(b'phases: %s\n') % b', '.join(t))
7081 ui.status(_(b'phases: %s\n') % b', '.join(t))
7068
7082
7069 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7083 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7070 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7084 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7071 numtrouble = len(repo.revs(trouble + b"()"))
7085 numtrouble = len(repo.revs(trouble + b"()"))
7072 # We write all the possibilities to ease translation
7086 # We write all the possibilities to ease translation
7073 troublemsg = {
7087 troublemsg = {
7074 b"orphan": _(b"orphan: %d changesets"),
7088 b"orphan": _(b"orphan: %d changesets"),
7075 b"contentdivergent": _(b"content-divergent: %d changesets"),
7089 b"contentdivergent": _(b"content-divergent: %d changesets"),
7076 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7090 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7077 }
7091 }
7078 if numtrouble > 0:
7092 if numtrouble > 0:
7079 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7093 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7080
7094
7081 cmdutil.summaryhooks(ui, repo)
7095 cmdutil.summaryhooks(ui, repo)
7082
7096
7083 if opts.get(b'remote'):
7097 if opts.get(b'remote'):
7084 needsincoming, needsoutgoing = True, True
7098 needsincoming, needsoutgoing = True, True
7085 else:
7099 else:
7086 needsincoming, needsoutgoing = False, False
7100 needsincoming, needsoutgoing = False, False
7087 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7101 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7088 if i:
7102 if i:
7089 needsincoming = True
7103 needsincoming = True
7090 if o:
7104 if o:
7091 needsoutgoing = True
7105 needsoutgoing = True
7092 if not needsincoming and not needsoutgoing:
7106 if not needsincoming and not needsoutgoing:
7093 return
7107 return
7094
7108
7095 def getincoming():
7109 def getincoming():
7096 source, branches = hg.parseurl(ui.expandpath(b'default'))
7110 source, branches = hg.parseurl(ui.expandpath(b'default'))
7097 sbranch = branches[0]
7111 sbranch = branches[0]
7098 try:
7112 try:
7099 other = hg.peer(repo, {}, source)
7113 other = hg.peer(repo, {}, source)
7100 except error.RepoError:
7114 except error.RepoError:
7101 if opts.get(b'remote'):
7115 if opts.get(b'remote'):
7102 raise
7116 raise
7103 return source, sbranch, None, None, None
7117 return source, sbranch, None, None, None
7104 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7118 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7105 if revs:
7119 if revs:
7106 revs = [other.lookup(rev) for rev in revs]
7120 revs = [other.lookup(rev) for rev in revs]
7107 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7121 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7108 repo.ui.pushbuffer()
7122 repo.ui.pushbuffer()
7109 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7123 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7110 repo.ui.popbuffer()
7124 repo.ui.popbuffer()
7111 return source, sbranch, other, commoninc, commoninc[1]
7125 return source, sbranch, other, commoninc, commoninc[1]
7112
7126
7113 if needsincoming:
7127 if needsincoming:
7114 source, sbranch, sother, commoninc, incoming = getincoming()
7128 source, sbranch, sother, commoninc, incoming = getincoming()
7115 else:
7129 else:
7116 source = sbranch = sother = commoninc = incoming = None
7130 source = sbranch = sother = commoninc = incoming = None
7117
7131
7118 def getoutgoing():
7132 def getoutgoing():
7119 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7133 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7120 dbranch = branches[0]
7134 dbranch = branches[0]
7121 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7135 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7122 if source != dest:
7136 if source != dest:
7123 try:
7137 try:
7124 dother = hg.peer(repo, {}, dest)
7138 dother = hg.peer(repo, {}, dest)
7125 except error.RepoError:
7139 except error.RepoError:
7126 if opts.get(b'remote'):
7140 if opts.get(b'remote'):
7127 raise
7141 raise
7128 return dest, dbranch, None, None
7142 return dest, dbranch, None, None
7129 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7143 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7130 elif sother is None:
7144 elif sother is None:
7131 # there is no explicit destination peer, but source one is invalid
7145 # there is no explicit destination peer, but source one is invalid
7132 return dest, dbranch, None, None
7146 return dest, dbranch, None, None
7133 else:
7147 else:
7134 dother = sother
7148 dother = sother
7135 if source != dest or (sbranch is not None and sbranch != dbranch):
7149 if source != dest or (sbranch is not None and sbranch != dbranch):
7136 common = None
7150 common = None
7137 else:
7151 else:
7138 common = commoninc
7152 common = commoninc
7139 if revs:
7153 if revs:
7140 revs = [repo.lookup(rev) for rev in revs]
7154 revs = [repo.lookup(rev) for rev in revs]
7141 repo.ui.pushbuffer()
7155 repo.ui.pushbuffer()
7142 outgoing = discovery.findcommonoutgoing(
7156 outgoing = discovery.findcommonoutgoing(
7143 repo, dother, onlyheads=revs, commoninc=common
7157 repo, dother, onlyheads=revs, commoninc=common
7144 )
7158 )
7145 repo.ui.popbuffer()
7159 repo.ui.popbuffer()
7146 return dest, dbranch, dother, outgoing
7160 return dest, dbranch, dother, outgoing
7147
7161
7148 if needsoutgoing:
7162 if needsoutgoing:
7149 dest, dbranch, dother, outgoing = getoutgoing()
7163 dest, dbranch, dother, outgoing = getoutgoing()
7150 else:
7164 else:
7151 dest = dbranch = dother = outgoing = None
7165 dest = dbranch = dother = outgoing = None
7152
7166
7153 if opts.get(b'remote'):
7167 if opts.get(b'remote'):
7154 t = []
7168 t = []
7155 if incoming:
7169 if incoming:
7156 t.append(_(b'1 or more incoming'))
7170 t.append(_(b'1 or more incoming'))
7157 o = outgoing.missing
7171 o = outgoing.missing
7158 if o:
7172 if o:
7159 t.append(_(b'%d outgoing') % len(o))
7173 t.append(_(b'%d outgoing') % len(o))
7160 other = dother or sother
7174 other = dother or sother
7161 if b'bookmarks' in other.listkeys(b'namespaces'):
7175 if b'bookmarks' in other.listkeys(b'namespaces'):
7162 counts = bookmarks.summary(repo, other)
7176 counts = bookmarks.summary(repo, other)
7163 if counts[0] > 0:
7177 if counts[0] > 0:
7164 t.append(_(b'%d incoming bookmarks') % counts[0])
7178 t.append(_(b'%d incoming bookmarks') % counts[0])
7165 if counts[1] > 0:
7179 if counts[1] > 0:
7166 t.append(_(b'%d outgoing bookmarks') % counts[1])
7180 t.append(_(b'%d outgoing bookmarks') % counts[1])
7167
7181
7168 if t:
7182 if t:
7169 # i18n: column positioning for "hg summary"
7183 # i18n: column positioning for "hg summary"
7170 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7184 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7171 else:
7185 else:
7172 # i18n: column positioning for "hg summary"
7186 # i18n: column positioning for "hg summary"
7173 ui.status(_(b'remote: (synced)\n'))
7187 ui.status(_(b'remote: (synced)\n'))
7174
7188
7175 cmdutil.summaryremotehooks(
7189 cmdutil.summaryremotehooks(
7176 ui,
7190 ui,
7177 repo,
7191 repo,
7178 opts,
7192 opts,
7179 (
7193 (
7180 (source, sbranch, sother, commoninc),
7194 (source, sbranch, sother, commoninc),
7181 (dest, dbranch, dother, outgoing),
7195 (dest, dbranch, dother, outgoing),
7182 ),
7196 ),
7183 )
7197 )
7184
7198
7185
7199
7186 @command(
7200 @command(
7187 b'tag',
7201 b'tag',
7188 [
7202 [
7189 (b'f', b'force', None, _(b'force tag')),
7203 (b'f', b'force', None, _(b'force tag')),
7190 (b'l', b'local', None, _(b'make the tag local')),
7204 (b'l', b'local', None, _(b'make the tag local')),
7191 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7205 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7192 (b'', b'remove', None, _(b'remove a tag')),
7206 (b'', b'remove', None, _(b'remove a tag')),
7193 # -l/--local is already there, commitopts cannot be used
7207 # -l/--local is already there, commitopts cannot be used
7194 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7208 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7195 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7209 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7196 ]
7210 ]
7197 + commitopts2,
7211 + commitopts2,
7198 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7212 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7199 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7213 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7200 )
7214 )
7201 def tag(ui, repo, name1, *names, **opts):
7215 def tag(ui, repo, name1, *names, **opts):
7202 """add one or more tags for the current or given revision
7216 """add one or more tags for the current or given revision
7203
7217
7204 Name a particular revision using <name>.
7218 Name a particular revision using <name>.
7205
7219
7206 Tags are used to name particular revisions of the repository and are
7220 Tags are used to name particular revisions of the repository and are
7207 very useful to compare different revisions, to go back to significant
7221 very useful to compare different revisions, to go back to significant
7208 earlier versions or to mark branch points as releases, etc. Changing
7222 earlier versions or to mark branch points as releases, etc. Changing
7209 an existing tag is normally disallowed; use -f/--force to override.
7223 an existing tag is normally disallowed; use -f/--force to override.
7210
7224
7211 If no revision is given, the parent of the working directory is
7225 If no revision is given, the parent of the working directory is
7212 used.
7226 used.
7213
7227
7214 To facilitate version control, distribution, and merging of tags,
7228 To facilitate version control, distribution, and merging of tags,
7215 they are stored as a file named ".hgtags" which is managed similarly
7229 they are stored as a file named ".hgtags" which is managed similarly
7216 to other project files and can be hand-edited if necessary. This
7230 to other project files and can be hand-edited if necessary. This
7217 also means that tagging creates a new commit. The file
7231 also means that tagging creates a new commit. The file
7218 ".hg/localtags" is used for local tags (not shared among
7232 ".hg/localtags" is used for local tags (not shared among
7219 repositories).
7233 repositories).
7220
7234
7221 Tag commits are usually made at the head of a branch. If the parent
7235 Tag commits are usually made at the head of a branch. If the parent
7222 of the working directory is not a branch head, :hg:`tag` aborts; use
7236 of the working directory is not a branch head, :hg:`tag` aborts; use
7223 -f/--force to force the tag commit to be based on a non-head
7237 -f/--force to force the tag commit to be based on a non-head
7224 changeset.
7238 changeset.
7225
7239
7226 See :hg:`help dates` for a list of formats valid for -d/--date.
7240 See :hg:`help dates` for a list of formats valid for -d/--date.
7227
7241
7228 Since tag names have priority over branch names during revision
7242 Since tag names have priority over branch names during revision
7229 lookup, using an existing branch name as a tag name is discouraged.
7243 lookup, using an existing branch name as a tag name is discouraged.
7230
7244
7231 Returns 0 on success.
7245 Returns 0 on success.
7232 """
7246 """
7233 opts = pycompat.byteskwargs(opts)
7247 opts = pycompat.byteskwargs(opts)
7234 with repo.wlock(), repo.lock():
7248 with repo.wlock(), repo.lock():
7235 rev_ = b"."
7249 rev_ = b"."
7236 names = [t.strip() for t in (name1,) + names]
7250 names = [t.strip() for t in (name1,) + names]
7237 if len(names) != len(set(names)):
7251 if len(names) != len(set(names)):
7238 raise error.Abort(_(b'tag names must be unique'))
7252 raise error.Abort(_(b'tag names must be unique'))
7239 for n in names:
7253 for n in names:
7240 scmutil.checknewlabel(repo, n, b'tag')
7254 scmutil.checknewlabel(repo, n, b'tag')
7241 if not n:
7255 if not n:
7242 raise error.Abort(
7256 raise error.Abort(
7243 _(b'tag names cannot consist entirely of whitespace')
7257 _(b'tag names cannot consist entirely of whitespace')
7244 )
7258 )
7245 if opts.get(b'rev') and opts.get(b'remove'):
7259 if opts.get(b'rev') and opts.get(b'remove'):
7246 raise error.Abort(_(b"--rev and --remove are incompatible"))
7260 raise error.Abort(_(b"--rev and --remove are incompatible"))
7247 if opts.get(b'rev'):
7261 if opts.get(b'rev'):
7248 rev_ = opts[b'rev']
7262 rev_ = opts[b'rev']
7249 message = opts.get(b'message')
7263 message = opts.get(b'message')
7250 if opts.get(b'remove'):
7264 if opts.get(b'remove'):
7251 if opts.get(b'local'):
7265 if opts.get(b'local'):
7252 expectedtype = b'local'
7266 expectedtype = b'local'
7253 else:
7267 else:
7254 expectedtype = b'global'
7268 expectedtype = b'global'
7255
7269
7256 for n in names:
7270 for n in names:
7257 if repo.tagtype(n) == b'global':
7271 if repo.tagtype(n) == b'global':
7258 alltags = tagsmod.findglobaltags(ui, repo)
7272 alltags = tagsmod.findglobaltags(ui, repo)
7259 if alltags[n][0] == nullid:
7273 if alltags[n][0] == nullid:
7260 raise error.Abort(_(b"tag '%s' is already removed") % n)
7274 raise error.Abort(_(b"tag '%s' is already removed") % n)
7261 if not repo.tagtype(n):
7275 if not repo.tagtype(n):
7262 raise error.Abort(_(b"tag '%s' does not exist") % n)
7276 raise error.Abort(_(b"tag '%s' does not exist") % n)
7263 if repo.tagtype(n) != expectedtype:
7277 if repo.tagtype(n) != expectedtype:
7264 if expectedtype == b'global':
7278 if expectedtype == b'global':
7265 raise error.Abort(
7279 raise error.Abort(
7266 _(b"tag '%s' is not a global tag") % n
7280 _(b"tag '%s' is not a global tag") % n
7267 )
7281 )
7268 else:
7282 else:
7269 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7283 raise error.Abort(_(b"tag '%s' is not a local tag") % n)
7270 rev_ = b'null'
7284 rev_ = b'null'
7271 if not message:
7285 if not message:
7272 # we don't translate commit messages
7286 # we don't translate commit messages
7273 message = b'Removed tag %s' % b', '.join(names)
7287 message = b'Removed tag %s' % b', '.join(names)
7274 elif not opts.get(b'force'):
7288 elif not opts.get(b'force'):
7275 for n in names:
7289 for n in names:
7276 if n in repo.tags():
7290 if n in repo.tags():
7277 raise error.Abort(
7291 raise error.Abort(
7278 _(b"tag '%s' already exists (use -f to force)") % n
7292 _(b"tag '%s' already exists (use -f to force)") % n
7279 )
7293 )
7280 if not opts.get(b'local'):
7294 if not opts.get(b'local'):
7281 p1, p2 = repo.dirstate.parents()
7295 p1, p2 = repo.dirstate.parents()
7282 if p2 != nullid:
7296 if p2 != nullid:
7283 raise error.Abort(_(b'uncommitted merge'))
7297 raise error.Abort(_(b'uncommitted merge'))
7284 bheads = repo.branchheads()
7298 bheads = repo.branchheads()
7285 if not opts.get(b'force') and bheads and p1 not in bheads:
7299 if not opts.get(b'force') and bheads and p1 not in bheads:
7286 raise error.Abort(
7300 raise error.Abort(
7287 _(
7301 _(
7288 b'working directory is not at a branch head '
7302 b'working directory is not at a branch head '
7289 b'(use -f to force)'
7303 b'(use -f to force)'
7290 )
7304 )
7291 )
7305 )
7292 node = scmutil.revsingle(repo, rev_).node()
7306 node = scmutil.revsingle(repo, rev_).node()
7293
7307
7294 if not message:
7308 if not message:
7295 # we don't translate commit messages
7309 # we don't translate commit messages
7296 message = b'Added tag %s for changeset %s' % (
7310 message = b'Added tag %s for changeset %s' % (
7297 b', '.join(names),
7311 b', '.join(names),
7298 short(node),
7312 short(node),
7299 )
7313 )
7300
7314
7301 date = opts.get(b'date')
7315 date = opts.get(b'date')
7302 if date:
7316 if date:
7303 date = dateutil.parsedate(date)
7317 date = dateutil.parsedate(date)
7304
7318
7305 if opts.get(b'remove'):
7319 if opts.get(b'remove'):
7306 editform = b'tag.remove'
7320 editform = b'tag.remove'
7307 else:
7321 else:
7308 editform = b'tag.add'
7322 editform = b'tag.add'
7309 editor = cmdutil.getcommiteditor(
7323 editor = cmdutil.getcommiteditor(
7310 editform=editform, **pycompat.strkwargs(opts)
7324 editform=editform, **pycompat.strkwargs(opts)
7311 )
7325 )
7312
7326
7313 # don't allow tagging the null rev
7327 # don't allow tagging the null rev
7314 if (
7328 if (
7315 not opts.get(b'remove')
7329 not opts.get(b'remove')
7316 and scmutil.revsingle(repo, rev_).rev() == nullrev
7330 and scmutil.revsingle(repo, rev_).rev() == nullrev
7317 ):
7331 ):
7318 raise error.Abort(_(b"cannot tag null revision"))
7332 raise error.Abort(_(b"cannot tag null revision"))
7319
7333
7320 tagsmod.tag(
7334 tagsmod.tag(
7321 repo,
7335 repo,
7322 names,
7336 names,
7323 node,
7337 node,
7324 message,
7338 message,
7325 opts.get(b'local'),
7339 opts.get(b'local'),
7326 opts.get(b'user'),
7340 opts.get(b'user'),
7327 date,
7341 date,
7328 editor=editor,
7342 editor=editor,
7329 )
7343 )
7330
7344
7331
7345
7332 @command(
7346 @command(
7333 b'tags',
7347 b'tags',
7334 formatteropts,
7348 formatteropts,
7335 b'',
7349 b'',
7336 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7350 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7337 intents={INTENT_READONLY},
7351 intents={INTENT_READONLY},
7338 )
7352 )
7339 def tags(ui, repo, **opts):
7353 def tags(ui, repo, **opts):
7340 """list repository tags
7354 """list repository tags
7341
7355
7342 This lists both regular and local tags. When the -v/--verbose
7356 This lists both regular and local tags. When the -v/--verbose
7343 switch is used, a third column "local" is printed for local tags.
7357 switch is used, a third column "local" is printed for local tags.
7344 When the -q/--quiet switch is used, only the tag name is printed.
7358 When the -q/--quiet switch is used, only the tag name is printed.
7345
7359
7346 .. container:: verbose
7360 .. container:: verbose
7347
7361
7348 Template:
7362 Template:
7349
7363
7350 The following keywords are supported in addition to the common template
7364 The following keywords are supported in addition to the common template
7351 keywords and functions such as ``{tag}``. See also
7365 keywords and functions such as ``{tag}``. See also
7352 :hg:`help templates`.
7366 :hg:`help templates`.
7353
7367
7354 :type: String. ``local`` for local tags.
7368 :type: String. ``local`` for local tags.
7355
7369
7356 Returns 0 on success.
7370 Returns 0 on success.
7357 """
7371 """
7358
7372
7359 opts = pycompat.byteskwargs(opts)
7373 opts = pycompat.byteskwargs(opts)
7360 ui.pager(b'tags')
7374 ui.pager(b'tags')
7361 fm = ui.formatter(b'tags', opts)
7375 fm = ui.formatter(b'tags', opts)
7362 hexfunc = fm.hexfunc
7376 hexfunc = fm.hexfunc
7363
7377
7364 for t, n in reversed(repo.tagslist()):
7378 for t, n in reversed(repo.tagslist()):
7365 hn = hexfunc(n)
7379 hn = hexfunc(n)
7366 label = b'tags.normal'
7380 label = b'tags.normal'
7367 tagtype = b''
7381 tagtype = b''
7368 if repo.tagtype(t) == b'local':
7382 if repo.tagtype(t) == b'local':
7369 label = b'tags.local'
7383 label = b'tags.local'
7370 tagtype = b'local'
7384 tagtype = b'local'
7371
7385
7372 fm.startitem()
7386 fm.startitem()
7373 fm.context(repo=repo)
7387 fm.context(repo=repo)
7374 fm.write(b'tag', b'%s', t, label=label)
7388 fm.write(b'tag', b'%s', t, label=label)
7375 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7389 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7376 fm.condwrite(
7390 fm.condwrite(
7377 not ui.quiet,
7391 not ui.quiet,
7378 b'rev node',
7392 b'rev node',
7379 fmt,
7393 fmt,
7380 repo.changelog.rev(n),
7394 repo.changelog.rev(n),
7381 hn,
7395 hn,
7382 label=label,
7396 label=label,
7383 )
7397 )
7384 fm.condwrite(
7398 fm.condwrite(
7385 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7399 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7386 )
7400 )
7387 fm.plain(b'\n')
7401 fm.plain(b'\n')
7388 fm.end()
7402 fm.end()
7389
7403
7390
7404
7391 @command(
7405 @command(
7392 b'tip',
7406 b'tip',
7393 [
7407 [
7394 (b'p', b'patch', None, _(b'show patch')),
7408 (b'p', b'patch', None, _(b'show patch')),
7395 (b'g', b'git', None, _(b'use git extended diff format')),
7409 (b'g', b'git', None, _(b'use git extended diff format')),
7396 ]
7410 ]
7397 + templateopts,
7411 + templateopts,
7398 _(b'[-p] [-g]'),
7412 _(b'[-p] [-g]'),
7399 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7413 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7400 )
7414 )
7401 def tip(ui, repo, **opts):
7415 def tip(ui, repo, **opts):
7402 """show the tip revision (DEPRECATED)
7416 """show the tip revision (DEPRECATED)
7403
7417
7404 The tip revision (usually just called the tip) is the changeset
7418 The tip revision (usually just called the tip) is the changeset
7405 most recently added to the repository (and therefore the most
7419 most recently added to the repository (and therefore the most
7406 recently changed head).
7420 recently changed head).
7407
7421
7408 If you have just made a commit, that commit will be the tip. If
7422 If you have just made a commit, that commit will be the tip. If
7409 you have just pulled changes from another repository, the tip of
7423 you have just pulled changes from another repository, the tip of
7410 that repository becomes the current tip. The "tip" tag is special
7424 that repository becomes the current tip. The "tip" tag is special
7411 and cannot be renamed or assigned to a different changeset.
7425 and cannot be renamed or assigned to a different changeset.
7412
7426
7413 This command is deprecated, please use :hg:`heads` instead.
7427 This command is deprecated, please use :hg:`heads` instead.
7414
7428
7415 Returns 0 on success.
7429 Returns 0 on success.
7416 """
7430 """
7417 opts = pycompat.byteskwargs(opts)
7431 opts = pycompat.byteskwargs(opts)
7418 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7432 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7419 displayer.show(repo[b'tip'])
7433 displayer.show(repo[b'tip'])
7420 displayer.close()
7434 displayer.close()
7421
7435
7422
7436
7423 @command(
7437 @command(
7424 b'unbundle',
7438 b'unbundle',
7425 [
7439 [
7426 (
7440 (
7427 b'u',
7441 b'u',
7428 b'update',
7442 b'update',
7429 None,
7443 None,
7430 _(b'update to new branch head if changesets were unbundled'),
7444 _(b'update to new branch head if changesets were unbundled'),
7431 )
7445 )
7432 ],
7446 ],
7433 _(b'[-u] FILE...'),
7447 _(b'[-u] FILE...'),
7434 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7448 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7435 )
7449 )
7436 def unbundle(ui, repo, fname1, *fnames, **opts):
7450 def unbundle(ui, repo, fname1, *fnames, **opts):
7437 """apply one or more bundle files
7451 """apply one or more bundle files
7438
7452
7439 Apply one or more bundle files generated by :hg:`bundle`.
7453 Apply one or more bundle files generated by :hg:`bundle`.
7440
7454
7441 Returns 0 on success, 1 if an update has unresolved files.
7455 Returns 0 on success, 1 if an update has unresolved files.
7442 """
7456 """
7443 fnames = (fname1,) + fnames
7457 fnames = (fname1,) + fnames
7444
7458
7445 with repo.lock():
7459 with repo.lock():
7446 for fname in fnames:
7460 for fname in fnames:
7447 f = hg.openpath(ui, fname)
7461 f = hg.openpath(ui, fname)
7448 gen = exchange.readbundle(ui, f, fname)
7462 gen = exchange.readbundle(ui, f, fname)
7449 if isinstance(gen, streamclone.streamcloneapplier):
7463 if isinstance(gen, streamclone.streamcloneapplier):
7450 raise error.Abort(
7464 raise error.Abort(
7451 _(
7465 _(
7452 b'packed bundles cannot be applied with '
7466 b'packed bundles cannot be applied with '
7453 b'"hg unbundle"'
7467 b'"hg unbundle"'
7454 ),
7468 ),
7455 hint=_(b'use "hg debugapplystreamclonebundle"'),
7469 hint=_(b'use "hg debugapplystreamclonebundle"'),
7456 )
7470 )
7457 url = b'bundle:' + fname
7471 url = b'bundle:' + fname
7458 try:
7472 try:
7459 txnname = b'unbundle'
7473 txnname = b'unbundle'
7460 if not isinstance(gen, bundle2.unbundle20):
7474 if not isinstance(gen, bundle2.unbundle20):
7461 txnname = b'unbundle\n%s' % util.hidepassword(url)
7475 txnname = b'unbundle\n%s' % util.hidepassword(url)
7462 with repo.transaction(txnname) as tr:
7476 with repo.transaction(txnname) as tr:
7463 op = bundle2.applybundle(
7477 op = bundle2.applybundle(
7464 repo, gen, tr, source=b'unbundle', url=url
7478 repo, gen, tr, source=b'unbundle', url=url
7465 )
7479 )
7466 except error.BundleUnknownFeatureError as exc:
7480 except error.BundleUnknownFeatureError as exc:
7467 raise error.Abort(
7481 raise error.Abort(
7468 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7482 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7469 hint=_(
7483 hint=_(
7470 b"see https://mercurial-scm.org/"
7484 b"see https://mercurial-scm.org/"
7471 b"wiki/BundleFeature for more "
7485 b"wiki/BundleFeature for more "
7472 b"information"
7486 b"information"
7473 ),
7487 ),
7474 )
7488 )
7475 modheads = bundle2.combinechangegroupresults(op)
7489 modheads = bundle2.combinechangegroupresults(op)
7476
7490
7477 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
7491 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
7478
7492
7479
7493
7480 @command(
7494 @command(
7481 b'unshelve',
7495 b'unshelve',
7482 [
7496 [
7483 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7497 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7484 (
7498 (
7485 b'c',
7499 b'c',
7486 b'continue',
7500 b'continue',
7487 None,
7501 None,
7488 _(b'continue an incomplete unshelve operation'),
7502 _(b'continue an incomplete unshelve operation'),
7489 ),
7503 ),
7490 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7504 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7491 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7505 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7492 (
7506 (
7493 b'n',
7507 b'n',
7494 b'name',
7508 b'name',
7495 b'',
7509 b'',
7496 _(b'restore shelved change with given name'),
7510 _(b'restore shelved change with given name'),
7497 _(b'NAME'),
7511 _(b'NAME'),
7498 ),
7512 ),
7499 (b't', b'tool', b'', _(b'specify merge tool')),
7513 (b't', b'tool', b'', _(b'specify merge tool')),
7500 (
7514 (
7501 b'',
7515 b'',
7502 b'date',
7516 b'date',
7503 b'',
7517 b'',
7504 _(b'set date for temporary commits (DEPRECATED)'),
7518 _(b'set date for temporary commits (DEPRECATED)'),
7505 _(b'DATE'),
7519 _(b'DATE'),
7506 ),
7520 ),
7507 ],
7521 ],
7508 _(b'hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
7522 _(b'hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
7509 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7523 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7510 )
7524 )
7511 def unshelve(ui, repo, *shelved, **opts):
7525 def unshelve(ui, repo, *shelved, **opts):
7512 """restore a shelved change to the working directory
7526 """restore a shelved change to the working directory
7513
7527
7514 This command accepts an optional name of a shelved change to
7528 This command accepts an optional name of a shelved change to
7515 restore. If none is given, the most recent shelved change is used.
7529 restore. If none is given, the most recent shelved change is used.
7516
7530
7517 If a shelved change is applied successfully, the bundle that
7531 If a shelved change is applied successfully, the bundle that
7518 contains the shelved changes is moved to a backup location
7532 contains the shelved changes is moved to a backup location
7519 (.hg/shelve-backup).
7533 (.hg/shelve-backup).
7520
7534
7521 Since you can restore a shelved change on top of an arbitrary
7535 Since you can restore a shelved change on top of an arbitrary
7522 commit, it is possible that unshelving will result in a conflict
7536 commit, it is possible that unshelving will result in a conflict
7523 between your changes and the commits you are unshelving onto. If
7537 between your changes and the commits you are unshelving onto. If
7524 this occurs, you must resolve the conflict, then use
7538 this occurs, you must resolve the conflict, then use
7525 ``--continue`` to complete the unshelve operation. (The bundle
7539 ``--continue`` to complete the unshelve operation. (The bundle
7526 will not be moved until you successfully complete the unshelve.)
7540 will not be moved until you successfully complete the unshelve.)
7527
7541
7528 (Alternatively, you can use ``--abort`` to abandon an unshelve
7542 (Alternatively, you can use ``--abort`` to abandon an unshelve
7529 that causes a conflict. This reverts the unshelved changes, and
7543 that causes a conflict. This reverts the unshelved changes, and
7530 leaves the bundle in place.)
7544 leaves the bundle in place.)
7531
7545
7532 If bare shelved change (when no files are specified, without interactive,
7546 If bare shelved change (when no files are specified, without interactive,
7533 include and exclude option) was done on newly created branch it would
7547 include and exclude option) was done on newly created branch it would
7534 restore branch information to the working directory.
7548 restore branch information to the working directory.
7535
7549
7536 After a successful unshelve, the shelved changes are stored in a
7550 After a successful unshelve, the shelved changes are stored in a
7537 backup directory. Only the N most recent backups are kept. N
7551 backup directory. Only the N most recent backups are kept. N
7538 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7552 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7539 configuration option.
7553 configuration option.
7540
7554
7541 .. container:: verbose
7555 .. container:: verbose
7542
7556
7543 Timestamp in seconds is used to decide order of backups. More
7557 Timestamp in seconds is used to decide order of backups. More
7544 than ``maxbackups`` backups are kept, if same timestamp
7558 than ``maxbackups`` backups are kept, if same timestamp
7545 prevents from deciding exact order of them, for safety.
7559 prevents from deciding exact order of them, for safety.
7546
7560
7547 Selected changes can be unshelved with ``--interactive`` flag.
7561 Selected changes can be unshelved with ``--interactive`` flag.
7548 The working directory is updated with the selected changes, and
7562 The working directory is updated with the selected changes, and
7549 only the unselected changes remain shelved.
7563 only the unselected changes remain shelved.
7550 Note: The whole shelve is applied to working directory first before
7564 Note: The whole shelve is applied to working directory first before
7551 running interactively. So, this will bring up all the conflicts between
7565 running interactively. So, this will bring up all the conflicts between
7552 working directory and the shelve, irrespective of which changes will be
7566 working directory and the shelve, irrespective of which changes will be
7553 unshelved.
7567 unshelved.
7554 """
7568 """
7555 with repo.wlock():
7569 with repo.wlock():
7556 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7570 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
7557
7571
7558
7572
7559 statemod.addunfinished(
7573 statemod.addunfinished(
7560 b'unshelve',
7574 b'unshelve',
7561 fname=b'shelvedstate',
7575 fname=b'shelvedstate',
7562 continueflag=True,
7576 continueflag=True,
7563 abortfunc=shelvemod.hgabortunshelve,
7577 abortfunc=shelvemod.hgabortunshelve,
7564 continuefunc=shelvemod.hgcontinueunshelve,
7578 continuefunc=shelvemod.hgcontinueunshelve,
7565 cmdmsg=_(b'unshelve already in progress'),
7579 cmdmsg=_(b'unshelve already in progress'),
7566 )
7580 )
7567
7581
7568
7582
7569 @command(
7583 @command(
7570 b'update|up|checkout|co',
7584 b'update|up|checkout|co',
7571 [
7585 [
7572 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7586 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7573 (b'c', b'check', None, _(b'require clean working directory')),
7587 (b'c', b'check', None, _(b'require clean working directory')),
7574 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7588 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7575 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7589 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7576 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7590 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7577 ]
7591 ]
7578 + mergetoolopts,
7592 + mergetoolopts,
7579 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7593 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7580 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7594 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7581 helpbasic=True,
7595 helpbasic=True,
7582 )
7596 )
7583 def update(ui, repo, node=None, **opts):
7597 def update(ui, repo, node=None, **opts):
7584 """update working directory (or switch revisions)
7598 """update working directory (or switch revisions)
7585
7599
7586 Update the repository's working directory to the specified
7600 Update the repository's working directory to the specified
7587 changeset. If no changeset is specified, update to the tip of the
7601 changeset. If no changeset is specified, update to the tip of the
7588 current named branch and move the active bookmark (see :hg:`help
7602 current named branch and move the active bookmark (see :hg:`help
7589 bookmarks`).
7603 bookmarks`).
7590
7604
7591 Update sets the working directory's parent revision to the specified
7605 Update sets the working directory's parent revision to the specified
7592 changeset (see :hg:`help parents`).
7606 changeset (see :hg:`help parents`).
7593
7607
7594 If the changeset is not a descendant or ancestor of the working
7608 If the changeset is not a descendant or ancestor of the working
7595 directory's parent and there are uncommitted changes, the update is
7609 directory's parent and there are uncommitted changes, the update is
7596 aborted. With the -c/--check option, the working directory is checked
7610 aborted. With the -c/--check option, the working directory is checked
7597 for uncommitted changes; if none are found, the working directory is
7611 for uncommitted changes; if none are found, the working directory is
7598 updated to the specified changeset.
7612 updated to the specified changeset.
7599
7613
7600 .. container:: verbose
7614 .. container:: verbose
7601
7615
7602 The -C/--clean, -c/--check, and -m/--merge options control what
7616 The -C/--clean, -c/--check, and -m/--merge options control what
7603 happens if the working directory contains uncommitted changes.
7617 happens if the working directory contains uncommitted changes.
7604 At most of one of them can be specified.
7618 At most of one of them can be specified.
7605
7619
7606 1. If no option is specified, and if
7620 1. If no option is specified, and if
7607 the requested changeset is an ancestor or descendant of
7621 the requested changeset is an ancestor or descendant of
7608 the working directory's parent, the uncommitted changes
7622 the working directory's parent, the uncommitted changes
7609 are merged into the requested changeset and the merged
7623 are merged into the requested changeset and the merged
7610 result is left uncommitted. If the requested changeset is
7624 result is left uncommitted. If the requested changeset is
7611 not an ancestor or descendant (that is, it is on another
7625 not an ancestor or descendant (that is, it is on another
7612 branch), the update is aborted and the uncommitted changes
7626 branch), the update is aborted and the uncommitted changes
7613 are preserved.
7627 are preserved.
7614
7628
7615 2. With the -m/--merge option, the update is allowed even if the
7629 2. With the -m/--merge option, the update is allowed even if the
7616 requested changeset is not an ancestor or descendant of
7630 requested changeset is not an ancestor or descendant of
7617 the working directory's parent.
7631 the working directory's parent.
7618
7632
7619 3. With the -c/--check option, the update is aborted and the
7633 3. With the -c/--check option, the update is aborted and the
7620 uncommitted changes are preserved.
7634 uncommitted changes are preserved.
7621
7635
7622 4. With the -C/--clean option, uncommitted changes are discarded and
7636 4. With the -C/--clean option, uncommitted changes are discarded and
7623 the working directory is updated to the requested changeset.
7637 the working directory is updated to the requested changeset.
7624
7638
7625 To cancel an uncommitted merge (and lose your changes), use
7639 To cancel an uncommitted merge (and lose your changes), use
7626 :hg:`merge --abort`.
7640 :hg:`merge --abort`.
7627
7641
7628 Use null as the changeset to remove the working directory (like
7642 Use null as the changeset to remove the working directory (like
7629 :hg:`clone -U`).
7643 :hg:`clone -U`).
7630
7644
7631 If you want to revert just one file to an older revision, use
7645 If you want to revert just one file to an older revision, use
7632 :hg:`revert [-r REV] NAME`.
7646 :hg:`revert [-r REV] NAME`.
7633
7647
7634 See :hg:`help dates` for a list of formats valid for -d/--date.
7648 See :hg:`help dates` for a list of formats valid for -d/--date.
7635
7649
7636 Returns 0 on success, 1 if there are unresolved files.
7650 Returns 0 on success, 1 if there are unresolved files.
7637 """
7651 """
7638 rev = opts.get(r'rev')
7652 rev = opts.get(r'rev')
7639 date = opts.get(r'date')
7653 date = opts.get(r'date')
7640 clean = opts.get(r'clean')
7654 clean = opts.get(r'clean')
7641 check = opts.get(r'check')
7655 check = opts.get(r'check')
7642 merge = opts.get(r'merge')
7656 merge = opts.get(r'merge')
7643 if rev and node:
7657 if rev and node:
7644 raise error.Abort(_(b"please specify just one revision"))
7658 raise error.Abort(_(b"please specify just one revision"))
7645
7659
7646 if ui.configbool(b'commands', b'update.requiredest'):
7660 if ui.configbool(b'commands', b'update.requiredest'):
7647 if not node and not rev and not date:
7661 if not node and not rev and not date:
7648 raise error.Abort(
7662 raise error.Abort(
7649 _(b'you must specify a destination'),
7663 _(b'you must specify a destination'),
7650 hint=_(b'for example: hg update ".::"'),
7664 hint=_(b'for example: hg update ".::"'),
7651 )
7665 )
7652
7666
7653 if rev is None or rev == b'':
7667 if rev is None or rev == b'':
7654 rev = node
7668 rev = node
7655
7669
7656 if date and rev is not None:
7670 if date and rev is not None:
7657 raise error.Abort(_(b"you can't specify a revision and a date"))
7671 raise error.Abort(_(b"you can't specify a revision and a date"))
7658
7672
7659 if len([x for x in (clean, check, merge) if x]) > 1:
7673 if len([x for x in (clean, check, merge) if x]) > 1:
7660 raise error.Abort(
7674 raise error.Abort(
7661 _(
7675 _(
7662 b"can only specify one of -C/--clean, -c/--check, "
7676 b"can only specify one of -C/--clean, -c/--check, "
7663 b"or -m/--merge"
7677 b"or -m/--merge"
7664 )
7678 )
7665 )
7679 )
7666
7680
7667 updatecheck = None
7681 updatecheck = None
7668 if check:
7682 if check:
7669 updatecheck = b'abort'
7683 updatecheck = b'abort'
7670 elif merge:
7684 elif merge:
7671 updatecheck = b'none'
7685 updatecheck = b'none'
7672
7686
7673 with repo.wlock():
7687 with repo.wlock():
7674 cmdutil.clearunfinished(repo)
7688 cmdutil.clearunfinished(repo)
7675 if date:
7689 if date:
7676 rev = cmdutil.finddate(ui, repo, date)
7690 rev = cmdutil.finddate(ui, repo, date)
7677
7691
7678 # if we defined a bookmark, we have to remember the original name
7692 # if we defined a bookmark, we have to remember the original name
7679 brev = rev
7693 brev = rev
7680 if rev:
7694 if rev:
7681 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7695 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7682 ctx = scmutil.revsingle(repo, rev, default=None)
7696 ctx = scmutil.revsingle(repo, rev, default=None)
7683 rev = ctx.rev()
7697 rev = ctx.rev()
7684 hidden = ctx.hidden()
7698 hidden = ctx.hidden()
7685 overrides = {(b'ui', b'forcemerge'): opts.get(r'tool', b'')}
7699 overrides = {(b'ui', b'forcemerge'): opts.get(r'tool', b'')}
7686 with ui.configoverride(overrides, b'update'):
7700 with ui.configoverride(overrides, b'update'):
7687 ret = hg.updatetotally(
7701 ret = hg.updatetotally(
7688 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7702 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7689 )
7703 )
7690 if hidden:
7704 if hidden:
7691 ctxstr = ctx.hex()[:12]
7705 ctxstr = ctx.hex()[:12]
7692 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7706 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7693
7707
7694 if ctx.obsolete():
7708 if ctx.obsolete():
7695 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7709 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7696 ui.warn(b"(%s)\n" % obsfatemsg)
7710 ui.warn(b"(%s)\n" % obsfatemsg)
7697 return ret
7711 return ret
7698
7712
7699
7713
7700 @command(
7714 @command(
7701 b'verify',
7715 b'verify',
7702 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7716 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7703 helpcategory=command.CATEGORY_MAINTENANCE,
7717 helpcategory=command.CATEGORY_MAINTENANCE,
7704 )
7718 )
7705 def verify(ui, repo, **opts):
7719 def verify(ui, repo, **opts):
7706 """verify the integrity of the repository
7720 """verify the integrity of the repository
7707
7721
7708 Verify the integrity of the current repository.
7722 Verify the integrity of the current repository.
7709
7723
7710 This will perform an extensive check of the repository's
7724 This will perform an extensive check of the repository's
7711 integrity, validating the hashes and checksums of each entry in
7725 integrity, validating the hashes and checksums of each entry in
7712 the changelog, manifest, and tracked files, as well as the
7726 the changelog, manifest, and tracked files, as well as the
7713 integrity of their crosslinks and indices.
7727 integrity of their crosslinks and indices.
7714
7728
7715 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7729 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7716 for more information about recovery from corruption of the
7730 for more information about recovery from corruption of the
7717 repository.
7731 repository.
7718
7732
7719 Returns 0 on success, 1 if errors are encountered.
7733 Returns 0 on success, 1 if errors are encountered.
7720 """
7734 """
7721 opts = pycompat.byteskwargs(opts)
7735 opts = pycompat.byteskwargs(opts)
7722
7736
7723 level = None
7737 level = None
7724 if opts[b'full']:
7738 if opts[b'full']:
7725 level = verifymod.VERIFY_FULL
7739 level = verifymod.VERIFY_FULL
7726 return hg.verify(repo, level)
7740 return hg.verify(repo, level)
7727
7741
7728
7742
7729 @command(
7743 @command(
7730 b'version',
7744 b'version',
7731 [] + formatteropts,
7745 [] + formatteropts,
7732 helpcategory=command.CATEGORY_HELP,
7746 helpcategory=command.CATEGORY_HELP,
7733 norepo=True,
7747 norepo=True,
7734 intents={INTENT_READONLY},
7748 intents={INTENT_READONLY},
7735 )
7749 )
7736 def version_(ui, **opts):
7750 def version_(ui, **opts):
7737 """output version and copyright information
7751 """output version and copyright information
7738
7752
7739 .. container:: verbose
7753 .. container:: verbose
7740
7754
7741 Template:
7755 Template:
7742
7756
7743 The following keywords are supported. See also :hg:`help templates`.
7757 The following keywords are supported. See also :hg:`help templates`.
7744
7758
7745 :extensions: List of extensions.
7759 :extensions: List of extensions.
7746 :ver: String. Version number.
7760 :ver: String. Version number.
7747
7761
7748 And each entry of ``{extensions}`` provides the following sub-keywords
7762 And each entry of ``{extensions}`` provides the following sub-keywords
7749 in addition to ``{ver}``.
7763 in addition to ``{ver}``.
7750
7764
7751 :bundled: Boolean. True if included in the release.
7765 :bundled: Boolean. True if included in the release.
7752 :name: String. Extension name.
7766 :name: String. Extension name.
7753 """
7767 """
7754 opts = pycompat.byteskwargs(opts)
7768 opts = pycompat.byteskwargs(opts)
7755 if ui.verbose:
7769 if ui.verbose:
7756 ui.pager(b'version')
7770 ui.pager(b'version')
7757 fm = ui.formatter(b"version", opts)
7771 fm = ui.formatter(b"version", opts)
7758 fm.startitem()
7772 fm.startitem()
7759 fm.write(
7773 fm.write(
7760 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7774 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7761 )
7775 )
7762 license = _(
7776 license = _(
7763 b"(see https://mercurial-scm.org for more information)\n"
7777 b"(see https://mercurial-scm.org for more information)\n"
7764 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7778 b"\nCopyright (C) 2005-2019 Matt Mackall and others\n"
7765 b"This is free software; see the source for copying conditions. "
7779 b"This is free software; see the source for copying conditions. "
7766 b"There is NO\nwarranty; "
7780 b"There is NO\nwarranty; "
7767 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7781 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7768 )
7782 )
7769 if not ui.quiet:
7783 if not ui.quiet:
7770 fm.plain(license)
7784 fm.plain(license)
7771
7785
7772 if ui.verbose:
7786 if ui.verbose:
7773 fm.plain(_(b"\nEnabled extensions:\n\n"))
7787 fm.plain(_(b"\nEnabled extensions:\n\n"))
7774 # format names and versions into columns
7788 # format names and versions into columns
7775 names = []
7789 names = []
7776 vers = []
7790 vers = []
7777 isinternals = []
7791 isinternals = []
7778 for name, module in extensions.extensions():
7792 for name, module in extensions.extensions():
7779 names.append(name)
7793 names.append(name)
7780 vers.append(extensions.moduleversion(module) or None)
7794 vers.append(extensions.moduleversion(module) or None)
7781 isinternals.append(extensions.ismoduleinternal(module))
7795 isinternals.append(extensions.ismoduleinternal(module))
7782 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7796 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7783 if names:
7797 if names:
7784 namefmt = b" %%-%ds " % max(len(n) for n in names)
7798 namefmt = b" %%-%ds " % max(len(n) for n in names)
7785 places = [_(b"external"), _(b"internal")]
7799 places = [_(b"external"), _(b"internal")]
7786 for n, v, p in zip(names, vers, isinternals):
7800 for n, v, p in zip(names, vers, isinternals):
7787 fn.startitem()
7801 fn.startitem()
7788 fn.condwrite(ui.verbose, b"name", namefmt, n)
7802 fn.condwrite(ui.verbose, b"name", namefmt, n)
7789 if ui.verbose:
7803 if ui.verbose:
7790 fn.plain(b"%s " % places[p])
7804 fn.plain(b"%s " % places[p])
7791 fn.data(bundled=p)
7805 fn.data(bundled=p)
7792 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7806 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7793 if ui.verbose:
7807 if ui.verbose:
7794 fn.plain(b"\n")
7808 fn.plain(b"\n")
7795 fn.end()
7809 fn.end()
7796 fm.end()
7810 fm.end()
7797
7811
7798
7812
7799 def loadcmdtable(ui, name, cmdtable):
7813 def loadcmdtable(ui, name, cmdtable):
7800 """Load command functions from specified cmdtable
7814 """Load command functions from specified cmdtable
7801 """
7815 """
7802 overrides = [cmd for cmd in cmdtable if cmd in table]
7816 overrides = [cmd for cmd in cmdtable if cmd in table]
7803 if overrides:
7817 if overrides:
7804 ui.warn(
7818 ui.warn(
7805 _(b"extension '%s' overrides commands: %s\n")
7819 _(b"extension '%s' overrides commands: %s\n")
7806 % (name, b" ".join(overrides))
7820 % (name, b" ".join(overrides))
7807 )
7821 )
7808 table.update(cmdtable)
7822 table.update(cmdtable)
@@ -1,492 +1,519 b''
1 #require no-reposimplestore
1 #require no-reposimplestore
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > censor=
5 > censor=
6 > EOF
6 > EOF
7 $ cp $HGRCPATH $HGRCPATH.orig
7 $ cp $HGRCPATH $HGRCPATH.orig
8
8
9 Create repo with unimpeachable content
9 Create repo with unimpeachable content
10
10
11 $ hg init r
11 $ hg init r
12 $ cd r
12 $ cd r
13 $ echo 'Initially untainted file' > target
13 $ echo 'Initially untainted file' > target
14 $ echo 'Normal file here' > bystander
14 $ echo 'Normal file here' > bystander
15 $ hg add target bystander
15 $ hg add target bystander
16 $ hg ci -m init
16 $ hg ci -m init
17
17
18 Clone repo so we can test pull later
18 Clone repo so we can test pull later
19
19
20 $ cd ..
20 $ cd ..
21 $ hg clone r rpull
21 $ hg clone r rpull
22 updating to branch default
22 updating to branch default
23 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 $ cd r
24 $ cd r
25
25
26 Introduce content which will ultimately require censorship. Name the first
26 Introduce content which will ultimately require censorship. Name the first
27 censored node C1, second C2, and so on
27 censored node C1, second C2, and so on
28
28
29 $ echo 'Tainted file' > target
29 $ echo 'Tainted file' > target
30 $ echo 'Passwords: hunter2' >> target
30 $ echo 'Passwords: hunter2' >> target
31 $ hg ci -m taint target
31 $ hg ci -m taint target
32 $ C1=`hg id --debug -i`
32 $ C1=`hg id --debug -i`
33
33
34 $ echo 'hunter3' >> target
34 $ echo 'hunter3' >> target
35 $ echo 'Normal file v2' > bystander
35 $ echo 'Normal file v2' > bystander
36 $ hg ci -m moretaint target bystander
36 $ hg ci -m moretaint target bystander
37 $ C2=`hg id --debug -i`
37 $ C2=`hg id --debug -i`
38
38
39 Add a new sanitized versions to correct our mistake. Name the first head H1,
39 Add a new sanitized versions to correct our mistake. Name the first head H1,
40 the second head H2, and so on
40 the second head H2, and so on
41
41
42 $ echo 'Tainted file is now sanitized' > target
42 $ echo 'Tainted file is now sanitized' > target
43 $ hg ci -m sanitized target
43 $ hg ci -m sanitized target
44 $ H1=`hg id --debug -i`
44 $ H1=`hg id --debug -i`
45
45
46 $ hg update -r $C2
46 $ hg update -r $C2
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 $ echo 'Tainted file now super sanitized' > target
48 $ echo 'Tainted file now super sanitized' > target
49 $ hg ci -m 'super sanitized' target
49 $ hg ci -m 'super sanitized' target
50 created new head
50 created new head
51 $ H2=`hg id --debug -i`
51 $ H2=`hg id --debug -i`
52
52
53 Verify target contents before censorship at each revision
53 Verify target contents before censorship at each revision
54
54
55 $ hg cat -r $H1 target
55 $ hg cat -r $H1 target
56 Tainted file is now sanitized
56 Tainted file is now sanitized
57 $ hg cat -r $H2 target
57 $ hg cat -r $H2 target
58 Tainted file now super sanitized
58 Tainted file now super sanitized
59 $ hg cat -r $C2 target
59 $ hg cat -r $C2 target
60 Tainted file
60 Tainted file
61 Passwords: hunter2
61 Passwords: hunter2
62 hunter3
62 hunter3
63 $ hg cat -r $C1 target
63 $ hg cat -r $C1 target
64 Tainted file
64 Tainted file
65 Passwords: hunter2
65 Passwords: hunter2
66 $ hg cat -r 0 target
66 $ hg cat -r 0 target
67 Initially untainted file
67 Initially untainted file
68
68
69 Try to censor revision with too large of a tombstone message
69 Try to censor revision with too large of a tombstone message
70
70
71 $ hg censor -r $C1 -t 'blah blah blah blah blah blah blah blah bla' target
71 $ hg censor -r $C1 -t 'blah blah blah blah blah blah blah blah bla' target
72 abort: censor tombstone must be no longer than censored data
72 abort: censor tombstone must be no longer than censored data
73 [255]
73 [255]
74
74
75 Censor revision with 2 offenses
75 Censor revision with 2 offenses
76
76
77 (this also tests file pattern matching: path relative to cwd case)
77 (this also tests file pattern matching: path relative to cwd case)
78
78
79 $ mkdir -p foo/bar/baz
79 $ mkdir -p foo/bar/baz
80 $ hg --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
80 $ hg --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
81 $ hg cat -r $H1 target
81 $ hg cat -r $H1 target
82 Tainted file is now sanitized
82 Tainted file is now sanitized
83 $ hg cat -r $H2 target
83 $ hg cat -r $H2 target
84 Tainted file now super sanitized
84 Tainted file now super sanitized
85 $ hg cat -r $C2 target
85 $ hg cat -r $C2 target
86 abort: censored node: 1e0247a9a4b7
86 abort: censored node: 1e0247a9a4b7
87 (set censor.policy to ignore errors)
87 (set censor.policy to ignore errors)
88 [255]
88 [255]
89 $ hg cat -r $C1 target
89 $ hg cat -r $C1 target
90 Tainted file
90 Tainted file
91 Passwords: hunter2
91 Passwords: hunter2
92 $ hg cat -r 0 target
92 $ hg cat -r 0 target
93 Initially untainted file
93 Initially untainted file
94
94
95 Censor revision with 1 offense
95 Censor revision with 1 offense
96
96
97 (this also tests file pattern matching: with 'path:' scheme)
97 (this also tests file pattern matching: with 'path:' scheme)
98
98
99 $ hg --cwd foo/bar/baz censor -r $C1 path:target
99 $ hg --cwd foo/bar/baz censor -r $C1 path:target
100 $ hg cat -r $H1 target
100 $ hg cat -r $H1 target
101 Tainted file is now sanitized
101 Tainted file is now sanitized
102 $ hg cat -r $H2 target
102 $ hg cat -r $H2 target
103 Tainted file now super sanitized
103 Tainted file now super sanitized
104 $ hg cat -r $C2 target
104 $ hg cat -r $C2 target
105 abort: censored node: 1e0247a9a4b7
105 abort: censored node: 1e0247a9a4b7
106 (set censor.policy to ignore errors)
106 (set censor.policy to ignore errors)
107 [255]
107 [255]
108 $ hg cat -r $C1 target
108 $ hg cat -r $C1 target
109 abort: censored node: 613bc869fceb
109 abort: censored node: 613bc869fceb
110 (set censor.policy to ignore errors)
110 (set censor.policy to ignore errors)
111 [255]
111 [255]
112 $ hg cat -r 0 target
112 $ hg cat -r 0 target
113 Initially untainted file
113 Initially untainted file
114
114
115 Can only checkout target at uncensored revisions, -X is workaround for --all
115 Can only checkout target at uncensored revisions, -X is workaround for --all
116
116
117 $ hg revert -r $C2 target
117 $ hg revert -r $C2 target
118 abort: censored node: 1e0247a9a4b7
118 abort: censored node: 1e0247a9a4b7
119 (set censor.policy to ignore errors)
119 (set censor.policy to ignore errors)
120 [255]
120 [255]
121 $ hg revert -r $C1 target
121 $ hg revert -r $C1 target
122 abort: censored node: 613bc869fceb
122 abort: censored node: 613bc869fceb
123 (set censor.policy to ignore errors)
123 (set censor.policy to ignore errors)
124 [255]
124 [255]
125 $ hg revert -r $C1 --all
125 $ hg revert -r $C1 --all
126 reverting bystander
126 reverting bystander
127 reverting target
127 reverting target
128 abort: censored node: 613bc869fceb
128 abort: censored node: 613bc869fceb
129 (set censor.policy to ignore errors)
129 (set censor.policy to ignore errors)
130 [255]
130 [255]
131 $ hg revert -r $C1 --all -X target
131 $ hg revert -r $C1 --all -X target
132 $ cat target
132 $ cat target
133 Tainted file now super sanitized
133 Tainted file now super sanitized
134 $ hg revert -r 0 --all
134 $ hg revert -r 0 --all
135 reverting target
135 reverting target
136 $ cat target
136 $ cat target
137 Initially untainted file
137 Initially untainted file
138 $ hg revert -r $H2 --all
138 $ hg revert -r $H2 --all
139 reverting bystander
139 reverting bystander
140 reverting target
140 reverting target
141 $ cat target
141 $ cat target
142 Tainted file now super sanitized
142 Tainted file now super sanitized
143
143
144 Uncensored file can be viewed at any revision
144 Uncensored file can be viewed at any revision
145
145
146 $ hg cat -r $H1 bystander
146 $ hg cat -r $H1 bystander
147 Normal file v2
147 Normal file v2
148 $ hg cat -r $C2 bystander
148 $ hg cat -r $C2 bystander
149 Normal file v2
149 Normal file v2
150 $ hg cat -r $C1 bystander
150 $ hg cat -r $C1 bystander
151 Normal file here
151 Normal file here
152 $ hg cat -r 0 bystander
152 $ hg cat -r 0 bystander
153 Normal file here
153 Normal file here
154
154
155 Can update to children of censored revision
155 Can update to children of censored revision
156
156
157 $ hg update -r $H1
157 $ hg update -r $H1
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
159 $ cat target
159 $ cat target
160 Tainted file is now sanitized
160 Tainted file is now sanitized
161 $ hg update -r $H2
161 $ hg update -r $H2
162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 $ cat target
163 $ cat target
164 Tainted file now super sanitized
164 Tainted file now super sanitized
165
165
166 Set censor policy to abort in trusted $HGRC so hg verify fails
166 Set censor policy to abort in trusted $HGRC so hg verify fails
167
167
168 $ cp $HGRCPATH.orig $HGRCPATH
168 $ cp $HGRCPATH.orig $HGRCPATH
169 $ cat >> $HGRCPATH <<EOF
169 $ cat >> $HGRCPATH <<EOF
170 > [censor]
170 > [censor]
171 > policy = abort
171 > policy = abort
172 > EOF
172 > EOF
173
173
174 Repo fails verification due to censorship
174 Repo fails verification due to censorship
175
175
176 $ hg verify
176 $ hg verify
177 checking changesets
177 checking changesets
178 checking manifests
178 checking manifests
179 crosschecking files in changesets and manifests
179 crosschecking files in changesets and manifests
180 checking files
180 checking files
181 target@1: censored file data
181 target@1: censored file data
182 target@2: censored file data
182 target@2: censored file data
183 checked 5 changesets with 7 changes to 2 files
183 checked 5 changesets with 7 changes to 2 files
184 2 integrity errors encountered!
184 2 integrity errors encountered!
185 (first damaged changeset appears to be 1)
185 (first damaged changeset appears to be 1)
186 [1]
186 [1]
187
187
188 Cannot update to revision with censored data
188 Cannot update to revision with censored data
189
189
190 $ hg update -r $C2
190 $ hg update -r $C2
191 abort: censored node: 1e0247a9a4b7
191 abort: censored node: 1e0247a9a4b7
192 (set censor.policy to ignore errors)
192 (set censor.policy to ignore errors)
193 [255]
193 [255]
194 $ hg update -r $C1
194 $ hg update -r $C1
195 abort: censored node: 613bc869fceb
195 abort: censored node: 613bc869fceb
196 (set censor.policy to ignore errors)
196 (set censor.policy to ignore errors)
197 [255]
197 [255]
198 $ hg update -r 0
198 $ hg update -r 0
199 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
199 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 $ hg update -r $H2
200 $ hg update -r $H2
201 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
202
202
203 Set censor policy to ignore in trusted $HGRC so hg verify passes
203 Set censor policy to ignore in trusted $HGRC so hg verify passes
204
204
205 $ cp $HGRCPATH.orig $HGRCPATH
205 $ cp $HGRCPATH.orig $HGRCPATH
206 $ cat >> $HGRCPATH <<EOF
206 $ cat >> $HGRCPATH <<EOF
207 > [censor]
207 > [censor]
208 > policy = ignore
208 > policy = ignore
209 > EOF
209 > EOF
210
210
211 Repo passes verification with warnings with explicit config
211 Repo passes verification with warnings with explicit config
212
212
213 $ hg verify
213 $ hg verify
214 checking changesets
214 checking changesets
215 checking manifests
215 checking manifests
216 crosschecking files in changesets and manifests
216 crosschecking files in changesets and manifests
217 checking files
217 checking files
218 checked 5 changesets with 7 changes to 2 files
218 checked 5 changesets with 7 changes to 2 files
219
219
220 May update to revision with censored data with explicit config
220 May update to revision with censored data with explicit config
221
221
222 $ hg update -r $C2
222 $ hg update -r $C2
223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 $ cat target
224 $ cat target
225 $ hg update -r $C1
225 $ hg update -r $C1
226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 $ cat target
227 $ cat target
228 $ hg update -r 0
228 $ hg update -r 0
229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 $ cat target
230 $ cat target
231 Initially untainted file
231 Initially untainted file
232 $ hg update -r $H2
232 $ hg update -r $H2
233 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 $ cat target
234 $ cat target
235 Tainted file now super sanitized
235 Tainted file now super sanitized
236
236
237 Can merge in revision with censored data. Test requires one branch of history
237 Can merge in revision with censored data. Test requires one branch of history
238 with the file censored, but we can't censor at a head, so advance H1.
238 with the file censored, but we can't censor at a head, so advance H1.
239
239
240 $ hg update -r $H1
240 $ hg update -r $H1
241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 $ C3=$H1
242 $ C3=$H1
243 $ echo 'advanced head H1' > target
243 $ echo 'advanced head H1' > target
244 $ hg ci -m 'advance head H1' target
244 $ hg ci -m 'advance head H1' target
245 $ H1=`hg id --debug -i`
245 $ H1=`hg id --debug -i`
246 $ hg censor -r $C3 target
246 $ hg censor -r $C3 target
247 $ hg update -r $H2
247 $ hg update -r $H2
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 $ hg merge -r $C3
249 $ hg merge -r $C3
250 merging target
250 merging target
251 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
251 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
252 (branch merge, don't forget to commit)
252 (branch merge, don't forget to commit)
253
253
254 Revisions present in repository heads may not be censored
254 Revisions present in repository heads may not be censored
255
255
256 $ hg update -C -r $H2
256 $ hg update -C -r $H2
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
258 $ hg censor -r $H2 target
258 $ hg censor -r $H2 target
259 abort: cannot censor file in heads (78a8fc215e79)
259 abort: cannot censor file in heads (78a8fc215e79)
260 (clean/delete and commit first)
260 (clean/delete and commit first)
261 [255]
261 [255]
262 $ echo 'twiddling thumbs' > bystander
262 $ echo 'twiddling thumbs' > bystander
263 $ hg ci -m 'bystander commit'
263 $ hg ci -m 'bystander commit'
264 $ H2=`hg id --debug -i`
264 $ H2=`hg id --debug -i`
265 $ hg censor -r "$H2^" target
265 $ hg censor -r "$H2^" target
266 abort: cannot censor file in heads (efbe78065929)
266 abort: cannot censor file in heads (efbe78065929)
267 (clean/delete and commit first)
267 (clean/delete and commit first)
268 [255]
268 [255]
269
269
270 Cannot censor working directory
270 Cannot censor working directory
271
271
272 $ echo 'seriously no passwords' > target
272 $ echo 'seriously no passwords' > target
273 $ hg ci -m 'extend second head arbitrarily' target
273 $ hg ci -m 'extend second head arbitrarily' target
274 $ H2=`hg id --debug -i`
274 $ H2=`hg id --debug -i`
275 $ hg update -r "$H2^"
275 $ hg update -r "$H2^"
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 $ hg censor -r . target
277 $ hg censor -r . target
278 abort: cannot censor working directory
278 abort: cannot censor working directory
279 (clean/delete/update first)
279 (clean/delete/update first)
280 [255]
280 [255]
281 $ hg update -r $H2
281 $ hg update -r $H2
282 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
282 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
283
283
284 Can re-add file after being deleted + censored
284 Can re-add file after being deleted + censored
285
285
286 $ C4=$H2
286 $ C4=$H2
287 $ hg rm target
287 $ hg rm target
288 $ hg ci -m 'delete target so it may be censored'
288 $ hg ci -m 'delete target so it may be censored'
289 $ H2=`hg id --debug -i`
289 $ H2=`hg id --debug -i`
290 $ hg censor -r $C4 target
290 $ hg censor -r $C4 target
291 $ hg cat -r $C4 target
291 $ hg cat -r $C4 target
292 $ hg cat -r "$H2^^" target
292 $ hg cat -r "$H2^^" target
293 Tainted file now super sanitized
293 Tainted file now super sanitized
294 $ echo 'fresh start' > target
294 $ echo 'fresh start' > target
295 $ hg add target
295 $ hg add target
296 $ hg ci -m reincarnated target
296 $ hg ci -m reincarnated target
297 $ H2=`hg id --debug -i`
297 $ H2=`hg id --debug -i`
298 $ hg cat -r $H2 target
298 $ hg cat -r $H2 target
299 fresh start
299 fresh start
300 $ hg cat -r "$H2^" target
300 $ hg cat -r "$H2^" target
301 target: no such file in rev 452ec1762369
301 target: no such file in rev 452ec1762369
302 [1]
302 [1]
303 $ hg cat -r $C4 target
303 $ hg cat -r $C4 target
304 $ hg cat -r "$H2^^^" target
304 $ hg cat -r "$H2^^^" target
305 Tainted file now super sanitized
305 Tainted file now super sanitized
306
306
307 Can censor after revlog has expanded to no longer permit inline storage
307 Can censor after revlog has expanded to no longer permit inline storage
308
308
309 $ for x in `"$PYTHON" $TESTDIR/seq.py 0 50000`
309 $ for x in `"$PYTHON" $TESTDIR/seq.py 0 50000`
310 > do
310 > do
311 > echo "Password: hunter$x" >> target
311 > echo "Password: hunter$x" >> target
312 > done
312 > done
313 $ hg ci -m 'add 100k passwords'
313 $ hg ci -m 'add 100k passwords'
314 $ H2=`hg id --debug -i`
314 $ H2=`hg id --debug -i`
315 $ C5=$H2
315 $ C5=$H2
316 $ hg revert -r "$H2^" target
316 $ hg revert -r "$H2^" target
317 $ hg ci -m 'cleaned 100k passwords'
317 $ hg ci -m 'cleaned 100k passwords'
318 $ H2=`hg id --debug -i`
318 $ H2=`hg id --debug -i`
319 $ hg censor -r $C5 target
319 $ hg censor -r $C5 target
320 $ hg cat -r $C5 target
320 $ hg cat -r $C5 target
321 $ hg cat -r $H2 target
321 $ hg cat -r $H2 target
322 fresh start
322 fresh start
323
323
324 Repo with censored nodes can be cloned and cloned nodes are censored
324 Repo with censored nodes can be cloned and cloned nodes are censored
325
325
326 $ cd ..
326 $ cd ..
327 $ hg clone r rclone
327 $ hg clone r rclone
328 updating to branch default
328 updating to branch default
329 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 $ cd rclone
330 $ cd rclone
331 $ hg cat -r $H1 target
331 $ hg cat -r $H1 target
332 advanced head H1
332 advanced head H1
333 $ hg cat -r $H2~5 target
333 $ hg cat -r $H2~5 target
334 Tainted file now super sanitized
334 Tainted file now super sanitized
335 $ hg cat -r $C2 target
335 $ hg cat -r $C2 target
336 $ hg cat -r $C1 target
336 $ hg cat -r $C1 target
337 $ hg cat -r 0 target
337 $ hg cat -r 0 target
338 Initially untainted file
338 Initially untainted file
339 $ hg verify
339 $ hg verify
340 checking changesets
340 checking changesets
341 checking manifests
341 checking manifests
342 crosschecking files in changesets and manifests
342 crosschecking files in changesets and manifests
343 checking files
343 checking files
344 checked 12 changesets with 13 changes to 2 files
344 checked 12 changesets with 13 changes to 2 files
345
345
346 Repo cloned before tainted content introduced can pull censored nodes
346 Repo cloned before tainted content introduced can pull censored nodes
347
347
348 $ cd ../rpull
348 $ cd ../rpull
349 $ hg cat -r tip target
349 $ hg cat -r tip target
350 Initially untainted file
350 Initially untainted file
351 $ hg verify
351 $ hg verify
352 checking changesets
352 checking changesets
353 checking manifests
353 checking manifests
354 crosschecking files in changesets and manifests
354 crosschecking files in changesets and manifests
355 checking files
355 checking files
356 checked 1 changesets with 2 changes to 2 files
356 checked 1 changesets with 2 changes to 2 files
357 $ hg pull -r $H1 -r $H2
357 $ hg pull -r $H1 -r $H2
358 pulling from $TESTTMP/r
358 pulling from $TESTTMP/r
359 searching for changes
359 searching for changes
360 adding changesets
360 adding changesets
361 adding manifests
361 adding manifests
362 adding file changes
362 adding file changes
363 added 11 changesets with 11 changes to 2 files (+1 heads)
363 added 11 changesets with 11 changes to 2 files (+1 heads)
364 new changesets 186fb27560c3:683e4645fded
364 new changesets 186fb27560c3:683e4645fded
365 (run 'hg heads' to see heads, 'hg merge' to merge)
365 (run 'hg heads' to see heads, 'hg merge' to merge)
366 $ hg update 4
366 $ hg update 4
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ cat target
368 $ cat target
369 Tainted file now super sanitized
369 Tainted file now super sanitized
370 $ hg cat -r $H1 target
370 $ hg cat -r $H1 target
371 advanced head H1
371 advanced head H1
372 $ hg cat -r $H2~5 target
372 $ hg cat -r $H2~5 target
373 Tainted file now super sanitized
373 Tainted file now super sanitized
374 $ hg cat -r $C2 target
374 $ hg cat -r $C2 target
375 $ hg cat -r $C1 target
375 $ hg cat -r $C1 target
376 $ hg cat -r 0 target
376 $ hg cat -r 0 target
377 Initially untainted file
377 Initially untainted file
378 $ hg verify
378 $ hg verify
379 checking changesets
379 checking changesets
380 checking manifests
380 checking manifests
381 crosschecking files in changesets and manifests
381 crosschecking files in changesets and manifests
382 checking files
382 checking files
383 checked 12 changesets with 13 changes to 2 files
383 checked 12 changesets with 13 changes to 2 files
384
384
385 Censored nodes can be pushed if they censor previously unexchanged nodes
385 Censored nodes can be pushed if they censor previously unexchanged nodes
386
386
387 $ echo 'Passwords: hunter2hunter2' > target
387 $ echo 'Passwords: hunter2hunter2' > target
388 $ hg ci -m 're-add password from clone' target
388 $ hg ci -m 're-add password from clone' target
389 created new head
389 created new head
390 $ H3=`hg id --debug -i`
390 $ H3=`hg id --debug -i`
391 $ REV=$H3
391 $ REV=$H3
392 $ echo 'Re-sanitized; nothing to see here' > target
392 $ echo 'Re-sanitized; nothing to see here' > target
393 $ hg ci -m 're-sanitized' target
393 $ hg ci -m 're-sanitized' target
394 $ H2=`hg id --debug -i`
394 $ H2=`hg id --debug -i`
395 $ CLEANREV=$H2
395 $ CLEANREV=$H2
396 $ hg cat -r $REV target
396 $ hg cat -r $REV target
397 Passwords: hunter2hunter2
397 Passwords: hunter2hunter2
398 $ hg censor -r $REV target
398 $ hg censor -r $REV target
399 $ hg cat -r $REV target
399 $ hg cat -r $REV target
400 $ hg cat -r $CLEANREV target
400 $ hg cat -r $CLEANREV target
401 Re-sanitized; nothing to see here
401 Re-sanitized; nothing to see here
402 $ hg push -f -r $H2
402 $ hg push -f -r $H2
403 pushing to $TESTTMP/r
403 pushing to $TESTTMP/r
404 searching for changes
404 searching for changes
405 adding changesets
405 adding changesets
406 adding manifests
406 adding manifests
407 adding file changes
407 adding file changes
408 added 2 changesets with 2 changes to 1 files (+1 heads)
408 added 2 changesets with 2 changes to 1 files (+1 heads)
409
409
410 $ cd ../r
410 $ cd ../r
411 $ hg cat -r $REV target
411 $ hg cat -r $REV target
412 $ hg cat -r $CLEANREV target
412 $ hg cat -r $CLEANREV target
413 Re-sanitized; nothing to see here
413 Re-sanitized; nothing to see here
414 $ hg update $CLEANREV
414 $ hg update $CLEANREV
415 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
415 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 $ cat target
416 $ cat target
417 Re-sanitized; nothing to see here
417 Re-sanitized; nothing to see here
418
418
419 Censored nodes can be bundled up and unbundled in another repo
419 Censored nodes can be bundled up and unbundled in another repo
420
420
421 $ hg bundle --base 0 ../pwbundle
421 $ hg bundle --base 0 ../pwbundle
422 13 changesets found
422 13 changesets found
423 $ cd ../rclone
423 $ cd ../rclone
424 $ hg unbundle ../pwbundle
424 $ hg unbundle ../pwbundle
425 adding changesets
425 adding changesets
426 adding manifests
426 adding manifests
427 adding file changes
427 adding file changes
428 added 2 changesets with 2 changes to 2 files (+1 heads)
428 added 2 changesets with 2 changes to 2 files (+1 heads)
429 new changesets 075be80ac777:dcbaf17bf3a1 (2 drafts)
429 new changesets 075be80ac777:dcbaf17bf3a1 (2 drafts)
430 (run 'hg heads .' to see heads, 'hg merge' to merge)
430 (run 'hg heads .' to see heads, 'hg merge' to merge)
431 $ hg cat -r $REV target
431 $ hg cat -r $REV target
432 $ hg cat -r $CLEANREV target
432 $ hg cat -r $CLEANREV target
433 Re-sanitized; nothing to see here
433 Re-sanitized; nothing to see here
434 $ hg update $CLEANREV
434 $ hg update $CLEANREV
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 $ cat target
436 $ cat target
437 Re-sanitized; nothing to see here
437 Re-sanitized; nothing to see here
438 $ hg verify
438 $ hg verify
439 checking changesets
439 checking changesets
440 checking manifests
440 checking manifests
441 crosschecking files in changesets and manifests
441 crosschecking files in changesets and manifests
442 checking files
442 checking files
443 checked 14 changesets with 15 changes to 2 files
443 checked 14 changesets with 15 changes to 2 files
444
444
445 Grepping only warns, doesn't error out
446
447 $ cd ../rpull
448 $ hg grep 'Normal file'
449 bystander:Normal file v2
450 $ hg grep nothing
451 target:Re-sanitized; nothing to see here
452 $ hg grep --diff 'Normal file'
453 cannot search in censored file: target:7
454 cannot search in censored file: target:10
455 cannot search in censored file: target:12
456 bystander:6:-:Normal file v2
457 cannot search in censored file: target:1
458 cannot search in censored file: target:2
459 cannot search in censored file: target:3
460 bystander:2:-:Normal file here
461 bystander:2:+:Normal file v2
462 bystander:0:+:Normal file here
463 $ hg grep --diff nothing
464 cannot search in censored file: target:7
465 cannot search in censored file: target:10
466 cannot search in censored file: target:12
467 target:13:+:Re-sanitized; nothing to see here
468 cannot search in censored file: target:1
469 cannot search in censored file: target:2
470 cannot search in censored file: target:3
471
445 Censored nodes can be imported on top of censored nodes, consecutively
472 Censored nodes can be imported on top of censored nodes, consecutively
446
473
447 $ hg init ../rimport
474 $ hg init ../rimport
448 $ hg bundle --base 1 ../rimport/splitbundle
475 $ hg bundle --base 1 ../rimport/splitbundle
449 12 changesets found
476 12 changesets found
450 $ cd ../rimport
477 $ cd ../rimport
451 $ hg pull -r $H1 -r $H2 ../r
478 $ hg pull -r $H1 -r $H2 ../r
452 pulling from ../r
479 pulling from ../r
453 adding changesets
480 adding changesets
454 adding manifests
481 adding manifests
455 adding file changes
482 adding file changes
456 added 8 changesets with 10 changes to 2 files (+1 heads)
483 added 8 changesets with 10 changes to 2 files (+1 heads)
457 new changesets e97f55b2665a:dcbaf17bf3a1
484 new changesets e97f55b2665a:dcbaf17bf3a1
458 (run 'hg heads' to see heads, 'hg merge' to merge)
485 (run 'hg heads' to see heads, 'hg merge' to merge)
459 $ hg unbundle splitbundle
486 $ hg unbundle splitbundle
460 adding changesets
487 adding changesets
461 adding manifests
488 adding manifests
462 adding file changes
489 adding file changes
463 added 6 changesets with 5 changes to 2 files (+1 heads)
490 added 6 changesets with 5 changes to 2 files (+1 heads)
464 new changesets efbe78065929:683e4645fded (6 drafts)
491 new changesets efbe78065929:683e4645fded (6 drafts)
465 (run 'hg heads .' to see heads, 'hg merge' to merge)
492 (run 'hg heads .' to see heads, 'hg merge' to merge)
466 $ hg update $H2
493 $ hg update $H2
467 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
494 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 $ cat target
495 $ cat target
469 Re-sanitized; nothing to see here
496 Re-sanitized; nothing to see here
470 $ hg verify
497 $ hg verify
471 checking changesets
498 checking changesets
472 checking manifests
499 checking manifests
473 crosschecking files in changesets and manifests
500 crosschecking files in changesets and manifests
474 checking files
501 checking files
475 checked 14 changesets with 15 changes to 2 files
502 checked 14 changesets with 15 changes to 2 files
476 $ cd ../r
503 $ cd ../r
477
504
478 Can import bundle where first revision of a file is censored
505 Can import bundle where first revision of a file is censored
479
506
480 $ hg init ../rinit
507 $ hg init ../rinit
481 $ hg censor -r 0 target
508 $ hg censor -r 0 target
482 $ hg bundle -r 0 --base null ../rinit/initbundle
509 $ hg bundle -r 0 --base null ../rinit/initbundle
483 1 changesets found
510 1 changesets found
484 $ cd ../rinit
511 $ cd ../rinit
485 $ hg unbundle initbundle
512 $ hg unbundle initbundle
486 adding changesets
513 adding changesets
487 adding manifests
514 adding manifests
488 adding file changes
515 adding file changes
489 added 1 changesets with 2 changes to 2 files
516 added 1 changesets with 2 changes to 2 files
490 new changesets e97f55b2665a (1 drafts)
517 new changesets e97f55b2665a (1 drafts)
491 (run 'hg update' to get a working copy)
518 (run 'hg update' to get a working copy)
492 $ hg cat -r 0 target
519 $ hg cat -r 0 target
General Comments 0
You need to be logged in to leave comments. Login now