##// END OF EJS Templates
errors: use InputError for some invalid revsets and such...
Martin von Zweigbergk -
r48844:7d908ee1 default
parent child Browse files
Show More
@@ -1,7968 +1,7968 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullrev,
18 nullrev,
19 short,
19 short,
20 wdirrev,
20 wdirrev,
21 )
21 )
22 from .pycompat import open
22 from .pycompat import open
23 from . import (
23 from . import (
24 archival,
24 archival,
25 bookmarks,
25 bookmarks,
26 bundle2,
26 bundle2,
27 bundlecaches,
27 bundlecaches,
28 changegroup,
28 changegroup,
29 cmdutil,
29 cmdutil,
30 copies,
30 copies,
31 debugcommands as debugcommandsmod,
31 debugcommands as debugcommandsmod,
32 destutil,
32 destutil,
33 dirstateguard,
33 dirstateguard,
34 discovery,
34 discovery,
35 encoding,
35 encoding,
36 error,
36 error,
37 exchange,
37 exchange,
38 extensions,
38 extensions,
39 filemerge,
39 filemerge,
40 formatter,
40 formatter,
41 graphmod,
41 graphmod,
42 grep as grepmod,
42 grep as grepmod,
43 hbisect,
43 hbisect,
44 help,
44 help,
45 hg,
45 hg,
46 logcmdutil,
46 logcmdutil,
47 merge as mergemod,
47 merge as mergemod,
48 mergestate as mergestatemod,
48 mergestate as mergestatemod,
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 requirements,
57 requirements,
58 revsetlang,
58 revsetlang,
59 rewriteutil,
59 rewriteutil,
60 scmutil,
60 scmutil,
61 server,
61 server,
62 shelve as shelvemod,
62 shelve as shelvemod,
63 state as statemod,
63 state as statemod,
64 streamclone,
64 streamclone,
65 tags as tagsmod,
65 tags as tagsmod,
66 ui as uimod,
66 ui as uimod,
67 util,
67 util,
68 verify as verifymod,
68 verify as verifymod,
69 vfs as vfsmod,
69 vfs as vfsmod,
70 wireprotoserver,
70 wireprotoserver,
71 )
71 )
72 from .utils import (
72 from .utils import (
73 dateutil,
73 dateutil,
74 stringutil,
74 stringutil,
75 urlutil,
75 urlutil,
76 )
76 )
77
77
78 table = {}
78 table = {}
79 table.update(debugcommandsmod.command._table)
79 table.update(debugcommandsmod.command._table)
80
80
81 command = registrar.command(table)
81 command = registrar.command(table)
82 INTENT_READONLY = registrar.INTENT_READONLY
82 INTENT_READONLY = registrar.INTENT_READONLY
83
83
84 # common command options
84 # common command options
85
85
86 globalopts = [
86 globalopts = [
87 (
87 (
88 b'R',
88 b'R',
89 b'repository',
89 b'repository',
90 b'',
90 b'',
91 _(b'repository root directory or name of overlay bundle file'),
91 _(b'repository root directory or name of overlay bundle file'),
92 _(b'REPO'),
92 _(b'REPO'),
93 ),
93 ),
94 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
94 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (
95 (
96 b'y',
96 b'y',
97 b'noninteractive',
97 b'noninteractive',
98 None,
98 None,
99 _(
99 _(
100 b'do not prompt, automatically pick the first choice for all prompts'
100 b'do not prompt, automatically pick the first choice for all prompts'
101 ),
101 ),
102 ),
102 ),
103 (b'q', b'quiet', None, _(b'suppress output')),
103 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'v', b'verbose', None, _(b'enable additional output')),
104 (b'v', b'verbose', None, _(b'enable additional output')),
105 (
105 (
106 b'',
106 b'',
107 b'color',
107 b'color',
108 b'',
108 b'',
109 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
109 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # and should not be translated
110 # and should not be translated
111 _(b"when to colorize (boolean, always, auto, never, or debug)"),
111 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b'TYPE'),
112 _(b'TYPE'),
113 ),
113 ),
114 (
114 (
115 b'',
115 b'',
116 b'config',
116 b'config',
117 [],
117 [],
118 _(b'set/override config option (use \'section.name=value\')'),
118 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'CONFIG'),
119 _(b'CONFIG'),
120 ),
120 ),
121 (b'', b'debug', None, _(b'enable debugging output')),
121 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debugger', None, _(b'start debugger')),
122 (b'', b'debugger', None, _(b'start debugger')),
123 (
123 (
124 b'',
124 b'',
125 b'encoding',
125 b'encoding',
126 encoding.encoding,
126 encoding.encoding,
127 _(b'set the charset encoding'),
127 _(b'set the charset encoding'),
128 _(b'ENCODE'),
128 _(b'ENCODE'),
129 ),
129 ),
130 (
130 (
131 b'',
131 b'',
132 b'encodingmode',
132 b'encodingmode',
133 encoding.encodingmode,
133 encoding.encodingmode,
134 _(b'set the charset encoding mode'),
134 _(b'set the charset encoding mode'),
135 _(b'MODE'),
135 _(b'MODE'),
136 ),
136 ),
137 (b'', b'traceback', None, _(b'always print a traceback on exception')),
137 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'time', None, _(b'time how long the command takes')),
138 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'profile', None, _(b'print command execution profile')),
139 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'version', None, _(b'output version information and exit')),
140 (b'', b'version', None, _(b'output version information and exit')),
141 (b'h', b'help', None, _(b'display help and exit')),
141 (b'h', b'help', None, _(b'display help and exit')),
142 (b'', b'hidden', False, _(b'consider hidden changesets')),
142 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (
143 (
144 b'',
144 b'',
145 b'pager',
145 b'pager',
146 b'auto',
146 b'auto',
147 _(b"when to paginate (boolean, always, auto, or never)"),
147 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b'TYPE'),
148 _(b'TYPE'),
149 ),
149 ),
150 ]
150 ]
151
151
152 dryrunopts = cmdutil.dryrunopts
152 dryrunopts = cmdutil.dryrunopts
153 remoteopts = cmdutil.remoteopts
153 remoteopts = cmdutil.remoteopts
154 walkopts = cmdutil.walkopts
154 walkopts = cmdutil.walkopts
155 commitopts = cmdutil.commitopts
155 commitopts = cmdutil.commitopts
156 commitopts2 = cmdutil.commitopts2
156 commitopts2 = cmdutil.commitopts2
157 commitopts3 = cmdutil.commitopts3
157 commitopts3 = cmdutil.commitopts3
158 formatteropts = cmdutil.formatteropts
158 formatteropts = cmdutil.formatteropts
159 templateopts = cmdutil.templateopts
159 templateopts = cmdutil.templateopts
160 logopts = cmdutil.logopts
160 logopts = cmdutil.logopts
161 diffopts = cmdutil.diffopts
161 diffopts = cmdutil.diffopts
162 diffwsopts = cmdutil.diffwsopts
162 diffwsopts = cmdutil.diffwsopts
163 diffopts2 = cmdutil.diffopts2
163 diffopts2 = cmdutil.diffopts2
164 mergetoolopts = cmdutil.mergetoolopts
164 mergetoolopts = cmdutil.mergetoolopts
165 similarityopts = cmdutil.similarityopts
165 similarityopts = cmdutil.similarityopts
166 subrepoopts = cmdutil.subrepoopts
166 subrepoopts = cmdutil.subrepoopts
167 debugrevlogopts = cmdutil.debugrevlogopts
167 debugrevlogopts = cmdutil.debugrevlogopts
168
168
169 # Commands start here, listed alphabetically
169 # Commands start here, listed alphabetically
170
170
171
171
172 @command(
172 @command(
173 b'abort',
173 b'abort',
174 dryrunopts,
174 dryrunopts,
175 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
175 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpbasic=True,
176 helpbasic=True,
177 )
177 )
178 def abort(ui, repo, **opts):
178 def abort(ui, repo, **opts):
179 """abort an unfinished operation (EXPERIMENTAL)
179 """abort an unfinished operation (EXPERIMENTAL)
180
180
181 Aborts a multistep operation like graft, histedit, rebase, merge,
181 Aborts a multistep operation like graft, histedit, rebase, merge,
182 and unshelve if they are in an unfinished state.
182 and unshelve if they are in an unfinished state.
183
183
184 use --dry-run/-n to dry run the command.
184 use --dry-run/-n to dry run the command.
185 """
185 """
186 dryrun = opts.get('dry_run')
186 dryrun = opts.get('dry_run')
187 abortstate = cmdutil.getunfinishedstate(repo)
187 abortstate = cmdutil.getunfinishedstate(repo)
188 if not abortstate:
188 if not abortstate:
189 raise error.StateError(_(b'no operation in progress'))
189 raise error.StateError(_(b'no operation in progress'))
190 if not abortstate.abortfunc:
190 if not abortstate.abortfunc:
191 raise error.InputError(
191 raise error.InputError(
192 (
192 (
193 _(b"%s in progress but does not support 'hg abort'")
193 _(b"%s in progress but does not support 'hg abort'")
194 % (abortstate._opname)
194 % (abortstate._opname)
195 ),
195 ),
196 hint=abortstate.hint(),
196 hint=abortstate.hint(),
197 )
197 )
198 if dryrun:
198 if dryrun:
199 ui.status(
199 ui.status(
200 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
200 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 )
201 )
202 return
202 return
203 return abortstate.abortfunc(ui, repo)
203 return abortstate.abortfunc(ui, repo)
204
204
205
205
206 @command(
206 @command(
207 b'add',
207 b'add',
208 walkopts + subrepoopts + dryrunopts,
208 walkopts + subrepoopts + dryrunopts,
209 _(b'[OPTION]... [FILE]...'),
209 _(b'[OPTION]... [FILE]...'),
210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpbasic=True,
211 helpbasic=True,
212 inferrepo=True,
212 inferrepo=True,
213 )
213 )
214 def add(ui, repo, *pats, **opts):
214 def add(ui, repo, *pats, **opts):
215 """add the specified files on the next commit
215 """add the specified files on the next commit
216
216
217 Schedule files to be version controlled and added to the
217 Schedule files to be version controlled and added to the
218 repository.
218 repository.
219
219
220 The files will be added to the repository at the next commit. To
220 The files will be added to the repository at the next commit. To
221 undo an add before that, see :hg:`forget`.
221 undo an add before that, see :hg:`forget`.
222
222
223 If no names are given, add all files to the repository (except
223 If no names are given, add all files to the repository (except
224 files matching ``.hgignore``).
224 files matching ``.hgignore``).
225
225
226 .. container:: verbose
226 .. container:: verbose
227
227
228 Examples:
228 Examples:
229
229
230 - New (unknown) files are added
230 - New (unknown) files are added
231 automatically by :hg:`add`::
231 automatically by :hg:`add`::
232
232
233 $ ls
233 $ ls
234 foo.c
234 foo.c
235 $ hg status
235 $ hg status
236 ? foo.c
236 ? foo.c
237 $ hg add
237 $ hg add
238 adding foo.c
238 adding foo.c
239 $ hg status
239 $ hg status
240 A foo.c
240 A foo.c
241
241
242 - Specific files to be added can be specified::
242 - Specific files to be added can be specified::
243
243
244 $ ls
244 $ ls
245 bar.c foo.c
245 bar.c foo.c
246 $ hg status
246 $ hg status
247 ? bar.c
247 ? bar.c
248 ? foo.c
248 ? foo.c
249 $ hg add bar.c
249 $ hg add bar.c
250 $ hg status
250 $ hg status
251 A bar.c
251 A bar.c
252 ? foo.c
252 ? foo.c
253
253
254 Returns 0 if all files are successfully added.
254 Returns 0 if all files are successfully added.
255 """
255 """
256
256
257 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
257 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
258 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
259 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 return rejected and 1 or 0
260 return rejected and 1 or 0
261
261
262
262
263 @command(
263 @command(
264 b'addremove',
264 b'addremove',
265 similarityopts + subrepoopts + walkopts + dryrunopts,
265 similarityopts + subrepoopts + walkopts + dryrunopts,
266 _(b'[OPTION]... [FILE]...'),
266 _(b'[OPTION]... [FILE]...'),
267 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
267 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 inferrepo=True,
268 inferrepo=True,
269 )
269 )
270 def addremove(ui, repo, *pats, **opts):
270 def addremove(ui, repo, *pats, **opts):
271 """add all new files, delete all missing files
271 """add all new files, delete all missing files
272
272
273 Add all new files and remove all missing files from the
273 Add all new files and remove all missing files from the
274 repository.
274 repository.
275
275
276 Unless names are given, new files are ignored if they match any of
276 Unless names are given, new files are ignored if they match any of
277 the patterns in ``.hgignore``. As with add, these changes take
277 the patterns in ``.hgignore``. As with add, these changes take
278 effect at the next commit.
278 effect at the next commit.
279
279
280 Use the -s/--similarity option to detect renamed files. This
280 Use the -s/--similarity option to detect renamed files. This
281 option takes a percentage between 0 (disabled) and 100 (files must
281 option takes a percentage between 0 (disabled) and 100 (files must
282 be identical) as its parameter. With a parameter greater than 0,
282 be identical) as its parameter. With a parameter greater than 0,
283 this compares every removed file with every added file and records
283 this compares every removed file with every added file and records
284 those similar enough as renames. Detecting renamed files this way
284 those similar enough as renames. Detecting renamed files this way
285 can be expensive. After using this option, :hg:`status -C` can be
285 can be expensive. After using this option, :hg:`status -C` can be
286 used to check which files were identified as moved or renamed. If
286 used to check which files were identified as moved or renamed. If
287 not specified, -s/--similarity defaults to 100 and only renames of
287 not specified, -s/--similarity defaults to 100 and only renames of
288 identical files are detected.
288 identical files are detected.
289
289
290 .. container:: verbose
290 .. container:: verbose
291
291
292 Examples:
292 Examples:
293
293
294 - A number of files (bar.c and foo.c) are new,
294 - A number of files (bar.c and foo.c) are new,
295 while foobar.c has been removed (without using :hg:`remove`)
295 while foobar.c has been removed (without using :hg:`remove`)
296 from the repository::
296 from the repository::
297
297
298 $ ls
298 $ ls
299 bar.c foo.c
299 bar.c foo.c
300 $ hg status
300 $ hg status
301 ! foobar.c
301 ! foobar.c
302 ? bar.c
302 ? bar.c
303 ? foo.c
303 ? foo.c
304 $ hg addremove
304 $ hg addremove
305 adding bar.c
305 adding bar.c
306 adding foo.c
306 adding foo.c
307 removing foobar.c
307 removing foobar.c
308 $ hg status
308 $ hg status
309 A bar.c
309 A bar.c
310 A foo.c
310 A foo.c
311 R foobar.c
311 R foobar.c
312
312
313 - A file foobar.c was moved to foo.c without using :hg:`rename`.
313 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 Afterwards, it was edited slightly::
314 Afterwards, it was edited slightly::
315
315
316 $ ls
316 $ ls
317 foo.c
317 foo.c
318 $ hg status
318 $ hg status
319 ! foobar.c
319 ! foobar.c
320 ? foo.c
320 ? foo.c
321 $ hg addremove --similarity 90
321 $ hg addremove --similarity 90
322 removing foobar.c
322 removing foobar.c
323 adding foo.c
323 adding foo.c
324 recording removal of foobar.c as rename to foo.c (94% similar)
324 recording removal of foobar.c as rename to foo.c (94% similar)
325 $ hg status -C
325 $ hg status -C
326 A foo.c
326 A foo.c
327 foobar.c
327 foobar.c
328 R foobar.c
328 R foobar.c
329
329
330 Returns 0 if all files are successfully added.
330 Returns 0 if all files are successfully added.
331 """
331 """
332 opts = pycompat.byteskwargs(opts)
332 opts = pycompat.byteskwargs(opts)
333 if not opts.get(b'similarity'):
333 if not opts.get(b'similarity'):
334 opts[b'similarity'] = b'100'
334 opts[b'similarity'] = b'100'
335 matcher = scmutil.match(repo[None], pats, opts)
335 matcher = scmutil.match(repo[None], pats, opts)
336 relative = scmutil.anypats(pats, opts)
336 relative = scmutil.anypats(pats, opts)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
337 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
338 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339
339
340
340
341 @command(
341 @command(
342 b'annotate|blame',
342 b'annotate|blame',
343 [
343 [
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
344 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (
345 (
346 b'',
346 b'',
347 b'follow',
347 b'follow',
348 None,
348 None,
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
349 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 ),
350 ),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
351 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'a', b'text', None, _(b'treat all files as text')),
352 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
353 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'f', b'file', None, _(b'list the filename')),
354 (b'f', b'file', None, _(b'list the filename')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
355 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
356 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
357 (b'c', b'changeset', None, _(b'list the changeset')),
358 (
358 (
359 b'l',
359 b'l',
360 b'line-number',
360 b'line-number',
361 None,
361 None,
362 _(b'show line number at the first appearance'),
362 _(b'show line number at the first appearance'),
363 ),
363 ),
364 (
364 (
365 b'',
365 b'',
366 b'skip',
366 b'skip',
367 [],
367 [],
368 _(b'revset to not display (EXPERIMENTAL)'),
368 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'REV'),
369 _(b'REV'),
370 ),
370 ),
371 ]
371 ]
372 + diffwsopts
372 + diffwsopts
373 + walkopts
373 + walkopts
374 + formatteropts,
374 + formatteropts,
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
375 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
376 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpbasic=True,
377 helpbasic=True,
378 inferrepo=True,
378 inferrepo=True,
379 )
379 )
380 def annotate(ui, repo, *pats, **opts):
380 def annotate(ui, repo, *pats, **opts):
381 """show changeset information by line for each file
381 """show changeset information by line for each file
382
382
383 List changes in files, showing the revision id responsible for
383 List changes in files, showing the revision id responsible for
384 each line.
384 each line.
385
385
386 This command is useful for discovering when a change was made and
386 This command is useful for discovering when a change was made and
387 by whom.
387 by whom.
388
388
389 If you include --file, --user, or --date, the revision number is
389 If you include --file, --user, or --date, the revision number is
390 suppressed unless you also include --number.
390 suppressed unless you also include --number.
391
391
392 Without the -a/--text option, annotate will avoid processing files
392 Without the -a/--text option, annotate will avoid processing files
393 it detects as binary. With -a, annotate will annotate the file
393 it detects as binary. With -a, annotate will annotate the file
394 anyway, although the results will probably be neither useful
394 anyway, although the results will probably be neither useful
395 nor desirable.
395 nor desirable.
396
396
397 .. container:: verbose
397 .. container:: verbose
398
398
399 Template:
399 Template:
400
400
401 The following keywords are supported in addition to the common template
401 The following keywords are supported in addition to the common template
402 keywords and functions. See also :hg:`help templates`.
402 keywords and functions. See also :hg:`help templates`.
403
403
404 :lines: List of lines with annotation data.
404 :lines: List of lines with annotation data.
405 :path: String. Repository-absolute path of the specified file.
405 :path: String. Repository-absolute path of the specified file.
406
406
407 And each entry of ``{lines}`` provides the following sub-keywords in
407 And each entry of ``{lines}`` provides the following sub-keywords in
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
408 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409
409
410 :line: String. Line content.
410 :line: String. Line content.
411 :lineno: Integer. Line number at that revision.
411 :lineno: Integer. Line number at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
412 :path: String. Repository-absolute path of the file at that revision.
413
413
414 See :hg:`help templates.operators` for the list expansion syntax.
414 See :hg:`help templates.operators` for the list expansion syntax.
415
415
416 Returns 0 on success.
416 Returns 0 on success.
417 """
417 """
418 opts = pycompat.byteskwargs(opts)
418 opts = pycompat.byteskwargs(opts)
419 if not pats:
419 if not pats:
420 raise error.InputError(
420 raise error.InputError(
421 _(b'at least one filename or pattern is required')
421 _(b'at least one filename or pattern is required')
422 )
422 )
423
423
424 if opts.get(b'follow'):
424 if opts.get(b'follow'):
425 # --follow is deprecated and now just an alias for -f/--file
425 # --follow is deprecated and now just an alias for -f/--file
426 # to mimic the behavior of Mercurial before version 1.5
426 # to mimic the behavior of Mercurial before version 1.5
427 opts[b'file'] = True
427 opts[b'file'] = True
428
428
429 if (
429 if (
430 not opts.get(b'user')
430 not opts.get(b'user')
431 and not opts.get(b'changeset')
431 and not opts.get(b'changeset')
432 and not opts.get(b'date')
432 and not opts.get(b'date')
433 and not opts.get(b'file')
433 and not opts.get(b'file')
434 ):
434 ):
435 opts[b'number'] = True
435 opts[b'number'] = True
436
436
437 linenumber = opts.get(b'line_number') is not None
437 linenumber = opts.get(b'line_number') is not None
438 if (
438 if (
439 linenumber
439 linenumber
440 and (not opts.get(b'changeset'))
440 and (not opts.get(b'changeset'))
441 and (not opts.get(b'number'))
441 and (not opts.get(b'number'))
442 ):
442 ):
443 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
443 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
444
444
445 rev = opts.get(b'rev')
445 rev = opts.get(b'rev')
446 if rev:
446 if rev:
447 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
447 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
448 ctx = scmutil.revsingle(repo, rev)
448 ctx = scmutil.revsingle(repo, rev)
449
449
450 ui.pager(b'annotate')
450 ui.pager(b'annotate')
451 rootfm = ui.formatter(b'annotate', opts)
451 rootfm = ui.formatter(b'annotate', opts)
452 if ui.debugflag:
452 if ui.debugflag:
453 shorthex = pycompat.identity
453 shorthex = pycompat.identity
454 else:
454 else:
455
455
456 def shorthex(h):
456 def shorthex(h):
457 return h[:12]
457 return h[:12]
458
458
459 if ui.quiet:
459 if ui.quiet:
460 datefunc = dateutil.shortdate
460 datefunc = dateutil.shortdate
461 else:
461 else:
462 datefunc = dateutil.datestr
462 datefunc = dateutil.datestr
463 if ctx.rev() is None:
463 if ctx.rev() is None:
464 if opts.get(b'changeset'):
464 if opts.get(b'changeset'):
465 # omit "+" suffix which is appended to node hex
465 # omit "+" suffix which is appended to node hex
466 def formatrev(rev):
466 def formatrev(rev):
467 if rev == wdirrev:
467 if rev == wdirrev:
468 return b'%d' % ctx.p1().rev()
468 return b'%d' % ctx.p1().rev()
469 else:
469 else:
470 return b'%d' % rev
470 return b'%d' % rev
471
471
472 else:
472 else:
473
473
474 def formatrev(rev):
474 def formatrev(rev):
475 if rev == wdirrev:
475 if rev == wdirrev:
476 return b'%d+' % ctx.p1().rev()
476 return b'%d+' % ctx.p1().rev()
477 else:
477 else:
478 return b'%d ' % rev
478 return b'%d ' % rev
479
479
480 def formathex(h):
480 def formathex(h):
481 if h == repo.nodeconstants.wdirhex:
481 if h == repo.nodeconstants.wdirhex:
482 return b'%s+' % shorthex(hex(ctx.p1().node()))
482 return b'%s+' % shorthex(hex(ctx.p1().node()))
483 else:
483 else:
484 return b'%s ' % shorthex(h)
484 return b'%s ' % shorthex(h)
485
485
486 else:
486 else:
487 formatrev = b'%d'.__mod__
487 formatrev = b'%d'.__mod__
488 formathex = shorthex
488 formathex = shorthex
489
489
490 opmap = [
490 opmap = [
491 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
491 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
492 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
492 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
493 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
493 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
494 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
494 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
495 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
495 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
496 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
496 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
497 ]
497 ]
498 opnamemap = {
498 opnamemap = {
499 b'rev': b'number',
499 b'rev': b'number',
500 b'node': b'changeset',
500 b'node': b'changeset',
501 b'path': b'file',
501 b'path': b'file',
502 b'lineno': b'line_number',
502 b'lineno': b'line_number',
503 }
503 }
504
504
505 if rootfm.isplain():
505 if rootfm.isplain():
506
506
507 def makefunc(get, fmt):
507 def makefunc(get, fmt):
508 return lambda x: fmt(get(x))
508 return lambda x: fmt(get(x))
509
509
510 else:
510 else:
511
511
512 def makefunc(get, fmt):
512 def makefunc(get, fmt):
513 return get
513 return get
514
514
515 datahint = rootfm.datahint()
515 datahint = rootfm.datahint()
516 funcmap = [
516 funcmap = [
517 (makefunc(get, fmt), sep)
517 (makefunc(get, fmt), sep)
518 for fn, sep, get, fmt in opmap
518 for fn, sep, get, fmt in opmap
519 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
519 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 ]
520 ]
521 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
521 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
522 fields = b' '.join(
522 fields = b' '.join(
523 fn
523 fn
524 for fn, sep, get, fmt in opmap
524 for fn, sep, get, fmt in opmap
525 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
525 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526 )
526 )
527
527
528 def bad(x, y):
528 def bad(x, y):
529 raise error.Abort(b"%s: %s" % (x, y))
529 raise error.Abort(b"%s: %s" % (x, y))
530
530
531 m = scmutil.match(ctx, pats, opts, badfn=bad)
531 m = scmutil.match(ctx, pats, opts, badfn=bad)
532
532
533 follow = not opts.get(b'no_follow')
533 follow = not opts.get(b'no_follow')
534 diffopts = patch.difffeatureopts(
534 diffopts = patch.difffeatureopts(
535 ui, opts, section=b'annotate', whitespace=True
535 ui, opts, section=b'annotate', whitespace=True
536 )
536 )
537 skiprevs = opts.get(b'skip')
537 skiprevs = opts.get(b'skip')
538 if skiprevs:
538 if skiprevs:
539 skiprevs = scmutil.revrange(repo, skiprevs)
539 skiprevs = scmutil.revrange(repo, skiprevs)
540
540
541 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
541 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
542 for abs in ctx.walk(m):
542 for abs in ctx.walk(m):
543 fctx = ctx[abs]
543 fctx = ctx[abs]
544 rootfm.startitem()
544 rootfm.startitem()
545 rootfm.data(path=abs)
545 rootfm.data(path=abs)
546 if not opts.get(b'text') and fctx.isbinary():
546 if not opts.get(b'text') and fctx.isbinary():
547 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
547 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
548 continue
548 continue
549
549
550 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
550 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
551 lines = fctx.annotate(
551 lines = fctx.annotate(
552 follow=follow, skiprevs=skiprevs, diffopts=diffopts
552 follow=follow, skiprevs=skiprevs, diffopts=diffopts
553 )
553 )
554 if not lines:
554 if not lines:
555 fm.end()
555 fm.end()
556 continue
556 continue
557 formats = []
557 formats = []
558 pieces = []
558 pieces = []
559
559
560 for f, sep in funcmap:
560 for f, sep in funcmap:
561 l = [f(n) for n in lines]
561 l = [f(n) for n in lines]
562 if fm.isplain():
562 if fm.isplain():
563 sizes = [encoding.colwidth(x) for x in l]
563 sizes = [encoding.colwidth(x) for x in l]
564 ml = max(sizes)
564 ml = max(sizes)
565 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
565 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
566 else:
566 else:
567 formats.append([b'%s'] * len(l))
567 formats.append([b'%s'] * len(l))
568 pieces.append(l)
568 pieces.append(l)
569
569
570 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
570 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
571 fm.startitem()
571 fm.startitem()
572 fm.context(fctx=n.fctx)
572 fm.context(fctx=n.fctx)
573 fm.write(fields, b"".join(f), *p)
573 fm.write(fields, b"".join(f), *p)
574 if n.skip:
574 if n.skip:
575 fmt = b"* %s"
575 fmt = b"* %s"
576 else:
576 else:
577 fmt = b": %s"
577 fmt = b": %s"
578 fm.write(b'line', fmt, n.text)
578 fm.write(b'line', fmt, n.text)
579
579
580 if not lines[-1].text.endswith(b'\n'):
580 if not lines[-1].text.endswith(b'\n'):
581 fm.plain(b'\n')
581 fm.plain(b'\n')
582 fm.end()
582 fm.end()
583
583
584 rootfm.end()
584 rootfm.end()
585
585
586
586
587 @command(
587 @command(
588 b'archive',
588 b'archive',
589 [
589 [
590 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
590 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
591 (
591 (
592 b'p',
592 b'p',
593 b'prefix',
593 b'prefix',
594 b'',
594 b'',
595 _(b'directory prefix for files in archive'),
595 _(b'directory prefix for files in archive'),
596 _(b'PREFIX'),
596 _(b'PREFIX'),
597 ),
597 ),
598 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
598 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
599 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
599 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
600 ]
600 ]
601 + subrepoopts
601 + subrepoopts
602 + walkopts,
602 + walkopts,
603 _(b'[OPTION]... DEST'),
603 _(b'[OPTION]... DEST'),
604 helpcategory=command.CATEGORY_IMPORT_EXPORT,
604 helpcategory=command.CATEGORY_IMPORT_EXPORT,
605 )
605 )
606 def archive(ui, repo, dest, **opts):
606 def archive(ui, repo, dest, **opts):
607 """create an unversioned archive of a repository revision
607 """create an unversioned archive of a repository revision
608
608
609 By default, the revision used is the parent of the working
609 By default, the revision used is the parent of the working
610 directory; use -r/--rev to specify a different revision.
610 directory; use -r/--rev to specify a different revision.
611
611
612 The archive type is automatically detected based on file
612 The archive type is automatically detected based on file
613 extension (to override, use -t/--type).
613 extension (to override, use -t/--type).
614
614
615 .. container:: verbose
615 .. container:: verbose
616
616
617 Examples:
617 Examples:
618
618
619 - create a zip file containing the 1.0 release::
619 - create a zip file containing the 1.0 release::
620
620
621 hg archive -r 1.0 project-1.0.zip
621 hg archive -r 1.0 project-1.0.zip
622
622
623 - create a tarball excluding .hg files::
623 - create a tarball excluding .hg files::
624
624
625 hg archive project.tar.gz -X ".hg*"
625 hg archive project.tar.gz -X ".hg*"
626
626
627 Valid types are:
627 Valid types are:
628
628
629 :``files``: a directory full of files (default)
629 :``files``: a directory full of files (default)
630 :``tar``: tar archive, uncompressed
630 :``tar``: tar archive, uncompressed
631 :``tbz2``: tar archive, compressed using bzip2
631 :``tbz2``: tar archive, compressed using bzip2
632 :``tgz``: tar archive, compressed using gzip
632 :``tgz``: tar archive, compressed using gzip
633 :``txz``: tar archive, compressed using lzma (only in Python 3)
633 :``txz``: tar archive, compressed using lzma (only in Python 3)
634 :``uzip``: zip archive, uncompressed
634 :``uzip``: zip archive, uncompressed
635 :``zip``: zip archive, compressed using deflate
635 :``zip``: zip archive, compressed using deflate
636
636
637 The exact name of the destination archive or directory is given
637 The exact name of the destination archive or directory is given
638 using a format string; see :hg:`help export` for details.
638 using a format string; see :hg:`help export` for details.
639
639
640 Each member added to an archive file has a directory prefix
640 Each member added to an archive file has a directory prefix
641 prepended. Use -p/--prefix to specify a format string for the
641 prepended. Use -p/--prefix to specify a format string for the
642 prefix. The default is the basename of the archive, with suffixes
642 prefix. The default is the basename of the archive, with suffixes
643 removed.
643 removed.
644
644
645 Returns 0 on success.
645 Returns 0 on success.
646 """
646 """
647
647
648 opts = pycompat.byteskwargs(opts)
648 opts = pycompat.byteskwargs(opts)
649 rev = opts.get(b'rev')
649 rev = opts.get(b'rev')
650 if rev:
650 if rev:
651 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
651 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
652 ctx = scmutil.revsingle(repo, rev)
652 ctx = scmutil.revsingle(repo, rev)
653 if not ctx:
653 if not ctx:
654 raise error.InputError(
654 raise error.InputError(
655 _(b'no working directory: please specify a revision')
655 _(b'no working directory: please specify a revision')
656 )
656 )
657 node = ctx.node()
657 node = ctx.node()
658 dest = cmdutil.makefilename(ctx, dest)
658 dest = cmdutil.makefilename(ctx, dest)
659 if os.path.realpath(dest) == repo.root:
659 if os.path.realpath(dest) == repo.root:
660 raise error.InputError(_(b'repository root cannot be destination'))
660 raise error.InputError(_(b'repository root cannot be destination'))
661
661
662 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
662 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
663 prefix = opts.get(b'prefix')
663 prefix = opts.get(b'prefix')
664
664
665 if dest == b'-':
665 if dest == b'-':
666 if kind == b'files':
666 if kind == b'files':
667 raise error.InputError(_(b'cannot archive plain files to stdout'))
667 raise error.InputError(_(b'cannot archive plain files to stdout'))
668 dest = cmdutil.makefileobj(ctx, dest)
668 dest = cmdutil.makefileobj(ctx, dest)
669 if not prefix:
669 if not prefix:
670 prefix = os.path.basename(repo.root) + b'-%h'
670 prefix = os.path.basename(repo.root) + b'-%h'
671
671
672 prefix = cmdutil.makefilename(ctx, prefix)
672 prefix = cmdutil.makefilename(ctx, prefix)
673 match = scmutil.match(ctx, [], opts)
673 match = scmutil.match(ctx, [], opts)
674 archival.archive(
674 archival.archive(
675 repo,
675 repo,
676 dest,
676 dest,
677 node,
677 node,
678 kind,
678 kind,
679 not opts.get(b'no_decode'),
679 not opts.get(b'no_decode'),
680 match,
680 match,
681 prefix,
681 prefix,
682 subrepos=opts.get(b'subrepos'),
682 subrepos=opts.get(b'subrepos'),
683 )
683 )
684
684
685
685
686 @command(
686 @command(
687 b'backout',
687 b'backout',
688 [
688 [
689 (
689 (
690 b'',
690 b'',
691 b'merge',
691 b'merge',
692 None,
692 None,
693 _(b'merge with old dirstate parent after backout'),
693 _(b'merge with old dirstate parent after backout'),
694 ),
694 ),
695 (
695 (
696 b'',
696 b'',
697 b'commit',
697 b'commit',
698 None,
698 None,
699 _(b'commit if no conflicts were encountered (DEPRECATED)'),
699 _(b'commit if no conflicts were encountered (DEPRECATED)'),
700 ),
700 ),
701 (b'', b'no-commit', None, _(b'do not commit')),
701 (b'', b'no-commit', None, _(b'do not commit')),
702 (
702 (
703 b'',
703 b'',
704 b'parent',
704 b'parent',
705 b'',
705 b'',
706 _(b'parent to choose when backing out merge (DEPRECATED)'),
706 _(b'parent to choose when backing out merge (DEPRECATED)'),
707 _(b'REV'),
707 _(b'REV'),
708 ),
708 ),
709 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
709 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
710 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
710 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
711 ]
711 ]
712 + mergetoolopts
712 + mergetoolopts
713 + walkopts
713 + walkopts
714 + commitopts
714 + commitopts
715 + commitopts2,
715 + commitopts2,
716 _(b'[OPTION]... [-r] REV'),
716 _(b'[OPTION]... [-r] REV'),
717 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
717 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
718 )
718 )
719 def backout(ui, repo, node=None, rev=None, **opts):
719 def backout(ui, repo, node=None, rev=None, **opts):
720 """reverse effect of earlier changeset
720 """reverse effect of earlier changeset
721
721
722 Prepare a new changeset with the effect of REV undone in the
722 Prepare a new changeset with the effect of REV undone in the
723 current working directory. If no conflicts were encountered,
723 current working directory. If no conflicts were encountered,
724 it will be committed immediately.
724 it will be committed immediately.
725
725
726 If REV is the parent of the working directory, then this new changeset
726 If REV is the parent of the working directory, then this new changeset
727 is committed automatically (unless --no-commit is specified).
727 is committed automatically (unless --no-commit is specified).
728
728
729 .. note::
729 .. note::
730
730
731 :hg:`backout` cannot be used to fix either an unwanted or
731 :hg:`backout` cannot be used to fix either an unwanted or
732 incorrect merge.
732 incorrect merge.
733
733
734 .. container:: verbose
734 .. container:: verbose
735
735
736 Examples:
736 Examples:
737
737
738 - Reverse the effect of the parent of the working directory.
738 - Reverse the effect of the parent of the working directory.
739 This backout will be committed immediately::
739 This backout will be committed immediately::
740
740
741 hg backout -r .
741 hg backout -r .
742
742
743 - Reverse the effect of previous bad revision 23::
743 - Reverse the effect of previous bad revision 23::
744
744
745 hg backout -r 23
745 hg backout -r 23
746
746
747 - Reverse the effect of previous bad revision 23 and
747 - Reverse the effect of previous bad revision 23 and
748 leave changes uncommitted::
748 leave changes uncommitted::
749
749
750 hg backout -r 23 --no-commit
750 hg backout -r 23 --no-commit
751 hg commit -m "Backout revision 23"
751 hg commit -m "Backout revision 23"
752
752
753 By default, the pending changeset will have one parent,
753 By default, the pending changeset will have one parent,
754 maintaining a linear history. With --merge, the pending
754 maintaining a linear history. With --merge, the pending
755 changeset will instead have two parents: the old parent of the
755 changeset will instead have two parents: the old parent of the
756 working directory and a new child of REV that simply undoes REV.
756 working directory and a new child of REV that simply undoes REV.
757
757
758 Before version 1.7, the behavior without --merge was equivalent
758 Before version 1.7, the behavior without --merge was equivalent
759 to specifying --merge followed by :hg:`update --clean .` to
759 to specifying --merge followed by :hg:`update --clean .` to
760 cancel the merge and leave the child of REV as a head to be
760 cancel the merge and leave the child of REV as a head to be
761 merged separately.
761 merged separately.
762
762
763 See :hg:`help dates` for a list of formats valid for -d/--date.
763 See :hg:`help dates` for a list of formats valid for -d/--date.
764
764
765 See :hg:`help revert` for a way to restore files to the state
765 See :hg:`help revert` for a way to restore files to the state
766 of another revision.
766 of another revision.
767
767
768 Returns 0 on success, 1 if nothing to backout or there are unresolved
768 Returns 0 on success, 1 if nothing to backout or there are unresolved
769 files.
769 files.
770 """
770 """
771 with repo.wlock(), repo.lock():
771 with repo.wlock(), repo.lock():
772 return _dobackout(ui, repo, node, rev, **opts)
772 return _dobackout(ui, repo, node, rev, **opts)
773
773
774
774
775 def _dobackout(ui, repo, node=None, rev=None, **opts):
775 def _dobackout(ui, repo, node=None, rev=None, **opts):
776 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
776 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
777 opts = pycompat.byteskwargs(opts)
777 opts = pycompat.byteskwargs(opts)
778
778
779 if rev and node:
779 if rev and node:
780 raise error.InputError(_(b"please specify just one revision"))
780 raise error.InputError(_(b"please specify just one revision"))
781
781
782 if not rev:
782 if not rev:
783 rev = node
783 rev = node
784
784
785 if not rev:
785 if not rev:
786 raise error.InputError(_(b"please specify a revision to backout"))
786 raise error.InputError(_(b"please specify a revision to backout"))
787
787
788 date = opts.get(b'date')
788 date = opts.get(b'date')
789 if date:
789 if date:
790 opts[b'date'] = dateutil.parsedate(date)
790 opts[b'date'] = dateutil.parsedate(date)
791
791
792 cmdutil.checkunfinished(repo)
792 cmdutil.checkunfinished(repo)
793 cmdutil.bailifchanged(repo)
793 cmdutil.bailifchanged(repo)
794 ctx = scmutil.revsingle(repo, rev)
794 ctx = scmutil.revsingle(repo, rev)
795 node = ctx.node()
795 node = ctx.node()
796
796
797 op1, op2 = repo.dirstate.parents()
797 op1, op2 = repo.dirstate.parents()
798 if not repo.changelog.isancestor(node, op1):
798 if not repo.changelog.isancestor(node, op1):
799 raise error.InputError(
799 raise error.InputError(
800 _(b'cannot backout change that is not an ancestor')
800 _(b'cannot backout change that is not an ancestor')
801 )
801 )
802
802
803 p1, p2 = repo.changelog.parents(node)
803 p1, p2 = repo.changelog.parents(node)
804 if p1 == repo.nullid:
804 if p1 == repo.nullid:
805 raise error.InputError(_(b'cannot backout a change with no parents'))
805 raise error.InputError(_(b'cannot backout a change with no parents'))
806 if p2 != repo.nullid:
806 if p2 != repo.nullid:
807 if not opts.get(b'parent'):
807 if not opts.get(b'parent'):
808 raise error.InputError(_(b'cannot backout a merge changeset'))
808 raise error.InputError(_(b'cannot backout a merge changeset'))
809 p = repo.lookup(opts[b'parent'])
809 p = repo.lookup(opts[b'parent'])
810 if p not in (p1, p2):
810 if p not in (p1, p2):
811 raise error.InputError(
811 raise error.InputError(
812 _(b'%s is not a parent of %s') % (short(p), short(node))
812 _(b'%s is not a parent of %s') % (short(p), short(node))
813 )
813 )
814 parent = p
814 parent = p
815 else:
815 else:
816 if opts.get(b'parent'):
816 if opts.get(b'parent'):
817 raise error.InputError(
817 raise error.InputError(
818 _(b'cannot use --parent on non-merge changeset')
818 _(b'cannot use --parent on non-merge changeset')
819 )
819 )
820 parent = p1
820 parent = p1
821
821
822 # the backout should appear on the same branch
822 # the backout should appear on the same branch
823 branch = repo.dirstate.branch()
823 branch = repo.dirstate.branch()
824 bheads = repo.branchheads(branch)
824 bheads = repo.branchheads(branch)
825 rctx = scmutil.revsingle(repo, hex(parent))
825 rctx = scmutil.revsingle(repo, hex(parent))
826 if not opts.get(b'merge') and op1 != node:
826 if not opts.get(b'merge') and op1 != node:
827 with dirstateguard.dirstateguard(repo, b'backout'):
827 with dirstateguard.dirstateguard(repo, b'backout'):
828 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
828 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
829 with ui.configoverride(overrides, b'backout'):
829 with ui.configoverride(overrides, b'backout'):
830 stats = mergemod.back_out(ctx, parent=repo[parent])
830 stats = mergemod.back_out(ctx, parent=repo[parent])
831 repo.setparents(op1, op2)
831 repo.setparents(op1, op2)
832 hg._showstats(repo, stats)
832 hg._showstats(repo, stats)
833 if stats.unresolvedcount:
833 if stats.unresolvedcount:
834 repo.ui.status(
834 repo.ui.status(
835 _(b"use 'hg resolve' to retry unresolved file merges\n")
835 _(b"use 'hg resolve' to retry unresolved file merges\n")
836 )
836 )
837 return 1
837 return 1
838 else:
838 else:
839 hg.clean(repo, node, show_stats=False)
839 hg.clean(repo, node, show_stats=False)
840 repo.dirstate.setbranch(branch)
840 repo.dirstate.setbranch(branch)
841 cmdutil.revert(ui, repo, rctx)
841 cmdutil.revert(ui, repo, rctx)
842
842
843 if opts.get(b'no_commit'):
843 if opts.get(b'no_commit'):
844 msg = _(b"changeset %s backed out, don't forget to commit.\n")
844 msg = _(b"changeset %s backed out, don't forget to commit.\n")
845 ui.status(msg % short(node))
845 ui.status(msg % short(node))
846 return 0
846 return 0
847
847
848 def commitfunc(ui, repo, message, match, opts):
848 def commitfunc(ui, repo, message, match, opts):
849 editform = b'backout'
849 editform = b'backout'
850 e = cmdutil.getcommiteditor(
850 e = cmdutil.getcommiteditor(
851 editform=editform, **pycompat.strkwargs(opts)
851 editform=editform, **pycompat.strkwargs(opts)
852 )
852 )
853 if not message:
853 if not message:
854 # we don't translate commit messages
854 # we don't translate commit messages
855 message = b"Backed out changeset %s" % short(node)
855 message = b"Backed out changeset %s" % short(node)
856 e = cmdutil.getcommiteditor(edit=True, editform=editform)
856 e = cmdutil.getcommiteditor(edit=True, editform=editform)
857 return repo.commit(
857 return repo.commit(
858 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
858 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
859 )
859 )
860
860
861 # save to detect changes
861 # save to detect changes
862 tip = repo.changelog.tip()
862 tip = repo.changelog.tip()
863
863
864 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
864 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
865 if not newnode:
865 if not newnode:
866 ui.status(_(b"nothing changed\n"))
866 ui.status(_(b"nothing changed\n"))
867 return 1
867 return 1
868 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
868 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
869
869
870 def nice(node):
870 def nice(node):
871 return b'%d:%s' % (repo.changelog.rev(node), short(node))
871 return b'%d:%s' % (repo.changelog.rev(node), short(node))
872
872
873 ui.status(
873 ui.status(
874 _(b'changeset %s backs out changeset %s\n')
874 _(b'changeset %s backs out changeset %s\n')
875 % (nice(newnode), nice(node))
875 % (nice(newnode), nice(node))
876 )
876 )
877 if opts.get(b'merge') and op1 != node:
877 if opts.get(b'merge') and op1 != node:
878 hg.clean(repo, op1, show_stats=False)
878 hg.clean(repo, op1, show_stats=False)
879 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
879 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
880 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
880 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
881 with ui.configoverride(overrides, b'backout'):
881 with ui.configoverride(overrides, b'backout'):
882 return hg.merge(repo[b'tip'])
882 return hg.merge(repo[b'tip'])
883 return 0
883 return 0
884
884
885
885
886 @command(
886 @command(
887 b'bisect',
887 b'bisect',
888 [
888 [
889 (b'r', b'reset', False, _(b'reset bisect state')),
889 (b'r', b'reset', False, _(b'reset bisect state')),
890 (b'g', b'good', False, _(b'mark changeset good')),
890 (b'g', b'good', False, _(b'mark changeset good')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
891 (b'b', b'bad', False, _(b'mark changeset bad')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
892 (b's', b'skip', False, _(b'skip testing changeset')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
893 (b'e', b'extend', False, _(b'extend the bisect range')),
894 (
894 (
895 b'c',
895 b'c',
896 b'command',
896 b'command',
897 b'',
897 b'',
898 _(b'use command to check changeset state'),
898 _(b'use command to check changeset state'),
899 _(b'CMD'),
899 _(b'CMD'),
900 ),
900 ),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
901 (b'U', b'noupdate', False, _(b'do not update to target')),
902 ],
902 ],
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
903 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
904 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905 )
905 )
906 def bisect(
906 def bisect(
907 ui,
907 ui,
908 repo,
908 repo,
909 positional_1=None,
909 positional_1=None,
910 positional_2=None,
910 positional_2=None,
911 command=None,
911 command=None,
912 reset=None,
912 reset=None,
913 good=None,
913 good=None,
914 bad=None,
914 bad=None,
915 skip=None,
915 skip=None,
916 extend=None,
916 extend=None,
917 noupdate=None,
917 noupdate=None,
918 ):
918 ):
919 """subdivision search of changesets
919 """subdivision search of changesets
920
920
921 This command helps to find changesets which introduce problems. To
921 This command helps to find changesets which introduce problems. To
922 use, mark the earliest changeset you know exhibits the problem as
922 use, mark the earliest changeset you know exhibits the problem as
923 bad, then mark the latest changeset which is free from the problem
923 bad, then mark the latest changeset which is free from the problem
924 as good. Bisect will update your working directory to a revision
924 as good. Bisect will update your working directory to a revision
925 for testing (unless the -U/--noupdate option is specified). Once
925 for testing (unless the -U/--noupdate option is specified). Once
926 you have performed tests, mark the working directory as good or
926 you have performed tests, mark the working directory as good or
927 bad, and bisect will either update to another candidate changeset
927 bad, and bisect will either update to another candidate changeset
928 or announce that it has found the bad revision.
928 or announce that it has found the bad revision.
929
929
930 As a shortcut, you can also use the revision argument to mark a
930 As a shortcut, you can also use the revision argument to mark a
931 revision as good or bad without checking it out first.
931 revision as good or bad without checking it out first.
932
932
933 If you supply a command, it will be used for automatic bisection.
933 If you supply a command, it will be used for automatic bisection.
934 The environment variable HG_NODE will contain the ID of the
934 The environment variable HG_NODE will contain the ID of the
935 changeset being tested. The exit status of the command will be
935 changeset being tested. The exit status of the command will be
936 used to mark revisions as good or bad: status 0 means good, 125
936 used to mark revisions as good or bad: status 0 means good, 125
937 means to skip the revision, 127 (command not found) will abort the
937 means to skip the revision, 127 (command not found) will abort the
938 bisection, and any other non-zero exit status means the revision
938 bisection, and any other non-zero exit status means the revision
939 is bad.
939 is bad.
940
940
941 .. container:: verbose
941 .. container:: verbose
942
942
943 Some examples:
943 Some examples:
944
944
945 - start a bisection with known bad revision 34, and good revision 12::
945 - start a bisection with known bad revision 34, and good revision 12::
946
946
947 hg bisect --bad 34
947 hg bisect --bad 34
948 hg bisect --good 12
948 hg bisect --good 12
949
949
950 - advance the current bisection by marking current revision as good or
950 - advance the current bisection by marking current revision as good or
951 bad::
951 bad::
952
952
953 hg bisect --good
953 hg bisect --good
954 hg bisect --bad
954 hg bisect --bad
955
955
956 - mark the current revision, or a known revision, to be skipped (e.g. if
956 - mark the current revision, or a known revision, to be skipped (e.g. if
957 that revision is not usable because of another issue)::
957 that revision is not usable because of another issue)::
958
958
959 hg bisect --skip
959 hg bisect --skip
960 hg bisect --skip 23
960 hg bisect --skip 23
961
961
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
962 - skip all revisions that do not touch directories ``foo`` or ``bar``::
963
963
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
964 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965
965
966 - forget the current bisection::
966 - forget the current bisection::
967
967
968 hg bisect --reset
968 hg bisect --reset
969
969
970 - use 'make && make tests' to automatically find the first broken
970 - use 'make && make tests' to automatically find the first broken
971 revision::
971 revision::
972
972
973 hg bisect --reset
973 hg bisect --reset
974 hg bisect --bad 34
974 hg bisect --bad 34
975 hg bisect --good 12
975 hg bisect --good 12
976 hg bisect --command "make && make tests"
976 hg bisect --command "make && make tests"
977
977
978 - see all changesets whose states are already known in the current
978 - see all changesets whose states are already known in the current
979 bisection::
979 bisection::
980
980
981 hg log -r "bisect(pruned)"
981 hg log -r "bisect(pruned)"
982
982
983 - see the changeset currently being bisected (especially useful
983 - see the changeset currently being bisected (especially useful
984 if running with -U/--noupdate)::
984 if running with -U/--noupdate)::
985
985
986 hg log -r "bisect(current)"
986 hg log -r "bisect(current)"
987
987
988 - see all changesets that took part in the current bisection::
988 - see all changesets that took part in the current bisection::
989
989
990 hg log -r "bisect(range)"
990 hg log -r "bisect(range)"
991
991
992 - you can even get a nice graph::
992 - you can even get a nice graph::
993
993
994 hg log --graph -r "bisect(range)"
994 hg log --graph -r "bisect(range)"
995
995
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
996 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997
997
998 Returns 0 on success.
998 Returns 0 on success.
999 """
999 """
1000 rev = []
1000 rev = []
1001 # backward compatibility
1001 # backward compatibility
1002 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1002 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1003 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1003 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004 cmd = positional_1
1004 cmd = positional_1
1005 rev.append(positional_2)
1005 rev.append(positional_2)
1006 if cmd == b"good":
1006 if cmd == b"good":
1007 good = True
1007 good = True
1008 elif cmd == b"bad":
1008 elif cmd == b"bad":
1009 bad = True
1009 bad = True
1010 else:
1010 else:
1011 reset = True
1011 reset = True
1012 elif positional_2:
1012 elif positional_2:
1013 raise error.InputError(_(b'incompatible arguments'))
1013 raise error.InputError(_(b'incompatible arguments'))
1014 elif positional_1 is not None:
1014 elif positional_1 is not None:
1015 rev.append(positional_1)
1015 rev.append(positional_1)
1016
1016
1017 incompatibles = {
1017 incompatibles = {
1018 b'--bad': bad,
1018 b'--bad': bad,
1019 b'--command': bool(command),
1019 b'--command': bool(command),
1020 b'--extend': extend,
1020 b'--extend': extend,
1021 b'--good': good,
1021 b'--good': good,
1022 b'--reset': reset,
1022 b'--reset': reset,
1023 b'--skip': skip,
1023 b'--skip': skip,
1024 }
1024 }
1025
1025
1026 enabled = [x for x in incompatibles if incompatibles[x]]
1026 enabled = [x for x in incompatibles if incompatibles[x]]
1027
1027
1028 if len(enabled) > 1:
1028 if len(enabled) > 1:
1029 raise error.InputError(
1029 raise error.InputError(
1030 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1030 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1031 )
1031 )
1032
1032
1033 if reset:
1033 if reset:
1034 hbisect.resetstate(repo)
1034 hbisect.resetstate(repo)
1035 return
1035 return
1036
1036
1037 state = hbisect.load_state(repo)
1037 state = hbisect.load_state(repo)
1038
1038
1039 if rev:
1039 if rev:
1040 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1040 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1041 else:
1041 else:
1042 nodes = [repo.lookup(b'.')]
1042 nodes = [repo.lookup(b'.')]
1043
1043
1044 # update state
1044 # update state
1045 if good or bad or skip:
1045 if good or bad or skip:
1046 if good:
1046 if good:
1047 state[b'good'] += nodes
1047 state[b'good'] += nodes
1048 elif bad:
1048 elif bad:
1049 state[b'bad'] += nodes
1049 state[b'bad'] += nodes
1050 elif skip:
1050 elif skip:
1051 state[b'skip'] += nodes
1051 state[b'skip'] += nodes
1052 hbisect.save_state(repo, state)
1052 hbisect.save_state(repo, state)
1053 if not (state[b'good'] and state[b'bad']):
1053 if not (state[b'good'] and state[b'bad']):
1054 return
1054 return
1055
1055
1056 def mayupdate(repo, node, show_stats=True):
1056 def mayupdate(repo, node, show_stats=True):
1057 """common used update sequence"""
1057 """common used update sequence"""
1058 if noupdate:
1058 if noupdate:
1059 return
1059 return
1060 cmdutil.checkunfinished(repo)
1060 cmdutil.checkunfinished(repo)
1061 cmdutil.bailifchanged(repo)
1061 cmdutil.bailifchanged(repo)
1062 return hg.clean(repo, node, show_stats=show_stats)
1062 return hg.clean(repo, node, show_stats=show_stats)
1063
1063
1064 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1064 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1065
1065
1066 if command:
1066 if command:
1067 changesets = 1
1067 changesets = 1
1068 if noupdate:
1068 if noupdate:
1069 try:
1069 try:
1070 node = state[b'current'][0]
1070 node = state[b'current'][0]
1071 except LookupError:
1071 except LookupError:
1072 raise error.StateError(
1072 raise error.StateError(
1073 _(
1073 _(
1074 b'current bisect revision is unknown - '
1074 b'current bisect revision is unknown - '
1075 b'start a new bisect to fix'
1075 b'start a new bisect to fix'
1076 )
1076 )
1077 )
1077 )
1078 else:
1078 else:
1079 node, p2 = repo.dirstate.parents()
1079 node, p2 = repo.dirstate.parents()
1080 if p2 != repo.nullid:
1080 if p2 != repo.nullid:
1081 raise error.StateError(_(b'current bisect revision is a merge'))
1081 raise error.StateError(_(b'current bisect revision is a merge'))
1082 if rev:
1082 if rev:
1083 if not nodes:
1083 if not nodes:
1084 raise error.Abort(_(b'empty revision set'))
1084 raise error.InputError(_(b'empty revision set'))
1085 node = repo[nodes[-1]].node()
1085 node = repo[nodes[-1]].node()
1086 with hbisect.restore_state(repo, state, node):
1086 with hbisect.restore_state(repo, state, node):
1087 while changesets:
1087 while changesets:
1088 # update state
1088 # update state
1089 state[b'current'] = [node]
1089 state[b'current'] = [node]
1090 hbisect.save_state(repo, state)
1090 hbisect.save_state(repo, state)
1091 status = ui.system(
1091 status = ui.system(
1092 command,
1092 command,
1093 environ={b'HG_NODE': hex(node)},
1093 environ={b'HG_NODE': hex(node)},
1094 blockedtag=b'bisect_check',
1094 blockedtag=b'bisect_check',
1095 )
1095 )
1096 if status == 125:
1096 if status == 125:
1097 transition = b"skip"
1097 transition = b"skip"
1098 elif status == 0:
1098 elif status == 0:
1099 transition = b"good"
1099 transition = b"good"
1100 # status < 0 means process was killed
1100 # status < 0 means process was killed
1101 elif status == 127:
1101 elif status == 127:
1102 raise error.Abort(_(b"failed to execute %s") % command)
1102 raise error.Abort(_(b"failed to execute %s") % command)
1103 elif status < 0:
1103 elif status < 0:
1104 raise error.Abort(_(b"%s killed") % command)
1104 raise error.Abort(_(b"%s killed") % command)
1105 else:
1105 else:
1106 transition = b"bad"
1106 transition = b"bad"
1107 state[transition].append(node)
1107 state[transition].append(node)
1108 ctx = repo[node]
1108 ctx = repo[node]
1109 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1109 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1110 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1110 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1111 hbisect.checkstate(state)
1111 hbisect.checkstate(state)
1112 # bisect
1112 # bisect
1113 nodes, changesets, bgood = hbisect.bisect(repo, state)
1113 nodes, changesets, bgood = hbisect.bisect(repo, state)
1114 # update to next check
1114 # update to next check
1115 node = nodes[0]
1115 node = nodes[0]
1116 mayupdate(repo, node, show_stats=False)
1116 mayupdate(repo, node, show_stats=False)
1117 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1117 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1118 return
1118 return
1119
1119
1120 hbisect.checkstate(state)
1120 hbisect.checkstate(state)
1121
1121
1122 # actually bisect
1122 # actually bisect
1123 nodes, changesets, good = hbisect.bisect(repo, state)
1123 nodes, changesets, good = hbisect.bisect(repo, state)
1124 if extend:
1124 if extend:
1125 if not changesets:
1125 if not changesets:
1126 extendctx = hbisect.extendrange(repo, state, nodes, good)
1126 extendctx = hbisect.extendrange(repo, state, nodes, good)
1127 if extendctx is not None:
1127 if extendctx is not None:
1128 ui.write(
1128 ui.write(
1129 _(b"Extending search to changeset %s\n")
1129 _(b"Extending search to changeset %s\n")
1130 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1130 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1131 )
1131 )
1132 state[b'current'] = [extendctx.node()]
1132 state[b'current'] = [extendctx.node()]
1133 hbisect.save_state(repo, state)
1133 hbisect.save_state(repo, state)
1134 return mayupdate(repo, extendctx.node())
1134 return mayupdate(repo, extendctx.node())
1135 raise error.StateError(_(b"nothing to extend"))
1135 raise error.StateError(_(b"nothing to extend"))
1136
1136
1137 if changesets == 0:
1137 if changesets == 0:
1138 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1138 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1139 else:
1139 else:
1140 assert len(nodes) == 1 # only a single node can be tested next
1140 assert len(nodes) == 1 # only a single node can be tested next
1141 node = nodes[0]
1141 node = nodes[0]
1142 # compute the approximate number of remaining tests
1142 # compute the approximate number of remaining tests
1143 tests, size = 0, 2
1143 tests, size = 0, 2
1144 while size <= changesets:
1144 while size <= changesets:
1145 tests, size = tests + 1, size * 2
1145 tests, size = tests + 1, size * 2
1146 rev = repo.changelog.rev(node)
1146 rev = repo.changelog.rev(node)
1147 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1147 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1148 ui.write(
1148 ui.write(
1149 _(
1149 _(
1150 b"Testing changeset %s "
1150 b"Testing changeset %s "
1151 b"(%d changesets remaining, ~%d tests)\n"
1151 b"(%d changesets remaining, ~%d tests)\n"
1152 )
1152 )
1153 % (summary, changesets, tests)
1153 % (summary, changesets, tests)
1154 )
1154 )
1155 state[b'current'] = [node]
1155 state[b'current'] = [node]
1156 hbisect.save_state(repo, state)
1156 hbisect.save_state(repo, state)
1157 return mayupdate(repo, node)
1157 return mayupdate(repo, node)
1158
1158
1159
1159
1160 @command(
1160 @command(
1161 b'bookmarks|bookmark',
1161 b'bookmarks|bookmark',
1162 [
1162 [
1163 (b'f', b'force', False, _(b'force')),
1163 (b'f', b'force', False, _(b'force')),
1164 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1164 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1165 (b'd', b'delete', False, _(b'delete a given bookmark')),
1165 (b'd', b'delete', False, _(b'delete a given bookmark')),
1166 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1166 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1167 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1167 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1168 (b'l', b'list', False, _(b'list existing bookmarks')),
1168 (b'l', b'list', False, _(b'list existing bookmarks')),
1169 ]
1169 ]
1170 + formatteropts,
1170 + formatteropts,
1171 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1171 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1172 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1172 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1173 )
1173 )
1174 def bookmark(ui, repo, *names, **opts):
1174 def bookmark(ui, repo, *names, **opts):
1175 """create a new bookmark or list existing bookmarks
1175 """create a new bookmark or list existing bookmarks
1176
1176
1177 Bookmarks are labels on changesets to help track lines of development.
1177 Bookmarks are labels on changesets to help track lines of development.
1178 Bookmarks are unversioned and can be moved, renamed and deleted.
1178 Bookmarks are unversioned and can be moved, renamed and deleted.
1179 Deleting or moving a bookmark has no effect on the associated changesets.
1179 Deleting or moving a bookmark has no effect on the associated changesets.
1180
1180
1181 Creating or updating to a bookmark causes it to be marked as 'active'.
1181 Creating or updating to a bookmark causes it to be marked as 'active'.
1182 The active bookmark is indicated with a '*'.
1182 The active bookmark is indicated with a '*'.
1183 When a commit is made, the active bookmark will advance to the new commit.
1183 When a commit is made, the active bookmark will advance to the new commit.
1184 A plain :hg:`update` will also advance an active bookmark, if possible.
1184 A plain :hg:`update` will also advance an active bookmark, if possible.
1185 Updating away from a bookmark will cause it to be deactivated.
1185 Updating away from a bookmark will cause it to be deactivated.
1186
1186
1187 Bookmarks can be pushed and pulled between repositories (see
1187 Bookmarks can be pushed and pulled between repositories (see
1188 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1188 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1189 diverged, a new 'divergent bookmark' of the form 'name@path' will
1189 diverged, a new 'divergent bookmark' of the form 'name@path' will
1190 be created. Using :hg:`merge` will resolve the divergence.
1190 be created. Using :hg:`merge` will resolve the divergence.
1191
1191
1192 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1192 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1193 the active bookmark's name.
1193 the active bookmark's name.
1194
1194
1195 A bookmark named '@' has the special property that :hg:`clone` will
1195 A bookmark named '@' has the special property that :hg:`clone` will
1196 check it out by default if it exists.
1196 check it out by default if it exists.
1197
1197
1198 .. container:: verbose
1198 .. container:: verbose
1199
1199
1200 Template:
1200 Template:
1201
1201
1202 The following keywords are supported in addition to the common template
1202 The following keywords are supported in addition to the common template
1203 keywords and functions such as ``{bookmark}``. See also
1203 keywords and functions such as ``{bookmark}``. See also
1204 :hg:`help templates`.
1204 :hg:`help templates`.
1205
1205
1206 :active: Boolean. True if the bookmark is active.
1206 :active: Boolean. True if the bookmark is active.
1207
1207
1208 Examples:
1208 Examples:
1209
1209
1210 - create an active bookmark for a new line of development::
1210 - create an active bookmark for a new line of development::
1211
1211
1212 hg book new-feature
1212 hg book new-feature
1213
1213
1214 - create an inactive bookmark as a place marker::
1214 - create an inactive bookmark as a place marker::
1215
1215
1216 hg book -i reviewed
1216 hg book -i reviewed
1217
1217
1218 - create an inactive bookmark on another changeset::
1218 - create an inactive bookmark on another changeset::
1219
1219
1220 hg book -r .^ tested
1220 hg book -r .^ tested
1221
1221
1222 - rename bookmark turkey to dinner::
1222 - rename bookmark turkey to dinner::
1223
1223
1224 hg book -m turkey dinner
1224 hg book -m turkey dinner
1225
1225
1226 - move the '@' bookmark from another branch::
1226 - move the '@' bookmark from another branch::
1227
1227
1228 hg book -f @
1228 hg book -f @
1229
1229
1230 - print only the active bookmark name::
1230 - print only the active bookmark name::
1231
1231
1232 hg book -ql .
1232 hg book -ql .
1233 """
1233 """
1234 opts = pycompat.byteskwargs(opts)
1234 opts = pycompat.byteskwargs(opts)
1235 force = opts.get(b'force')
1235 force = opts.get(b'force')
1236 rev = opts.get(b'rev')
1236 rev = opts.get(b'rev')
1237 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1237 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1238
1238
1239 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1239 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1240 if action:
1240 if action:
1241 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1241 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1242 elif names or rev:
1242 elif names or rev:
1243 action = b'add'
1243 action = b'add'
1244 elif inactive:
1244 elif inactive:
1245 action = b'inactive' # meaning deactivate
1245 action = b'inactive' # meaning deactivate
1246 else:
1246 else:
1247 action = b'list'
1247 action = b'list'
1248
1248
1249 cmdutil.check_incompatible_arguments(
1249 cmdutil.check_incompatible_arguments(
1250 opts, b'inactive', [b'delete', b'list']
1250 opts, b'inactive', [b'delete', b'list']
1251 )
1251 )
1252 if not names and action in {b'add', b'delete'}:
1252 if not names and action in {b'add', b'delete'}:
1253 raise error.InputError(_(b"bookmark name required"))
1253 raise error.InputError(_(b"bookmark name required"))
1254
1254
1255 if action in {b'add', b'delete', b'rename', b'inactive'}:
1255 if action in {b'add', b'delete', b'rename', b'inactive'}:
1256 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1256 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1257 if action == b'delete':
1257 if action == b'delete':
1258 names = pycompat.maplist(repo._bookmarks.expandname, names)
1258 names = pycompat.maplist(repo._bookmarks.expandname, names)
1259 bookmarks.delete(repo, tr, names)
1259 bookmarks.delete(repo, tr, names)
1260 elif action == b'rename':
1260 elif action == b'rename':
1261 if not names:
1261 if not names:
1262 raise error.InputError(_(b"new bookmark name required"))
1262 raise error.InputError(_(b"new bookmark name required"))
1263 elif len(names) > 1:
1263 elif len(names) > 1:
1264 raise error.InputError(
1264 raise error.InputError(
1265 _(b"only one new bookmark name allowed")
1265 _(b"only one new bookmark name allowed")
1266 )
1266 )
1267 oldname = repo._bookmarks.expandname(opts[b'rename'])
1267 oldname = repo._bookmarks.expandname(opts[b'rename'])
1268 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1268 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1269 elif action == b'add':
1269 elif action == b'add':
1270 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1270 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1271 elif action == b'inactive':
1271 elif action == b'inactive':
1272 if len(repo._bookmarks) == 0:
1272 if len(repo._bookmarks) == 0:
1273 ui.status(_(b"no bookmarks set\n"))
1273 ui.status(_(b"no bookmarks set\n"))
1274 elif not repo._activebookmark:
1274 elif not repo._activebookmark:
1275 ui.status(_(b"no active bookmark\n"))
1275 ui.status(_(b"no active bookmark\n"))
1276 else:
1276 else:
1277 bookmarks.deactivate(repo)
1277 bookmarks.deactivate(repo)
1278 elif action == b'list':
1278 elif action == b'list':
1279 names = pycompat.maplist(repo._bookmarks.expandname, names)
1279 names = pycompat.maplist(repo._bookmarks.expandname, names)
1280 with ui.formatter(b'bookmarks', opts) as fm:
1280 with ui.formatter(b'bookmarks', opts) as fm:
1281 bookmarks.printbookmarks(ui, repo, fm, names)
1281 bookmarks.printbookmarks(ui, repo, fm, names)
1282 else:
1282 else:
1283 raise error.ProgrammingError(b'invalid action: %s' % action)
1283 raise error.ProgrammingError(b'invalid action: %s' % action)
1284
1284
1285
1285
1286 @command(
1286 @command(
1287 b'branch',
1287 b'branch',
1288 [
1288 [
1289 (
1289 (
1290 b'f',
1290 b'f',
1291 b'force',
1291 b'force',
1292 None,
1292 None,
1293 _(b'set branch name even if it shadows an existing branch'),
1293 _(b'set branch name even if it shadows an existing branch'),
1294 ),
1294 ),
1295 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1295 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1296 (
1296 (
1297 b'r',
1297 b'r',
1298 b'rev',
1298 b'rev',
1299 [],
1299 [],
1300 _(b'change branches of the given revs (EXPERIMENTAL)'),
1300 _(b'change branches of the given revs (EXPERIMENTAL)'),
1301 ),
1301 ),
1302 ],
1302 ],
1303 _(b'[-fC] [NAME]'),
1303 _(b'[-fC] [NAME]'),
1304 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1304 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1305 )
1305 )
1306 def branch(ui, repo, label=None, **opts):
1306 def branch(ui, repo, label=None, **opts):
1307 """set or show the current branch name
1307 """set or show the current branch name
1308
1308
1309 .. note::
1309 .. note::
1310
1310
1311 Branch names are permanent and global. Use :hg:`bookmark` to create a
1311 Branch names are permanent and global. Use :hg:`bookmark` to create a
1312 light-weight bookmark instead. See :hg:`help glossary` for more
1312 light-weight bookmark instead. See :hg:`help glossary` for more
1313 information about named branches and bookmarks.
1313 information about named branches and bookmarks.
1314
1314
1315 With no argument, show the current branch name. With one argument,
1315 With no argument, show the current branch name. With one argument,
1316 set the working directory branch name (the branch will not exist
1316 set the working directory branch name (the branch will not exist
1317 in the repository until the next commit). Standard practice
1317 in the repository until the next commit). Standard practice
1318 recommends that primary development take place on the 'default'
1318 recommends that primary development take place on the 'default'
1319 branch.
1319 branch.
1320
1320
1321 Unless -f/--force is specified, branch will not let you set a
1321 Unless -f/--force is specified, branch will not let you set a
1322 branch name that already exists.
1322 branch name that already exists.
1323
1323
1324 Use -C/--clean to reset the working directory branch to that of
1324 Use -C/--clean to reset the working directory branch to that of
1325 the parent of the working directory, negating a previous branch
1325 the parent of the working directory, negating a previous branch
1326 change.
1326 change.
1327
1327
1328 Use the command :hg:`update` to switch to an existing branch. Use
1328 Use the command :hg:`update` to switch to an existing branch. Use
1329 :hg:`commit --close-branch` to mark this branch head as closed.
1329 :hg:`commit --close-branch` to mark this branch head as closed.
1330 When all heads of a branch are closed, the branch will be
1330 When all heads of a branch are closed, the branch will be
1331 considered closed.
1331 considered closed.
1332
1332
1333 Returns 0 on success.
1333 Returns 0 on success.
1334 """
1334 """
1335 opts = pycompat.byteskwargs(opts)
1335 opts = pycompat.byteskwargs(opts)
1336 revs = opts.get(b'rev')
1336 revs = opts.get(b'rev')
1337 if label:
1337 if label:
1338 label = label.strip()
1338 label = label.strip()
1339
1339
1340 if not opts.get(b'clean') and not label:
1340 if not opts.get(b'clean') and not label:
1341 if revs:
1341 if revs:
1342 raise error.InputError(
1342 raise error.InputError(
1343 _(b"no branch name specified for the revisions")
1343 _(b"no branch name specified for the revisions")
1344 )
1344 )
1345 ui.write(b"%s\n" % repo.dirstate.branch())
1345 ui.write(b"%s\n" % repo.dirstate.branch())
1346 return
1346 return
1347
1347
1348 with repo.wlock():
1348 with repo.wlock():
1349 if opts.get(b'clean'):
1349 if opts.get(b'clean'):
1350 label = repo[b'.'].branch()
1350 label = repo[b'.'].branch()
1351 repo.dirstate.setbranch(label)
1351 repo.dirstate.setbranch(label)
1352 ui.status(_(b'reset working directory to branch %s\n') % label)
1352 ui.status(_(b'reset working directory to branch %s\n') % label)
1353 elif label:
1353 elif label:
1354
1354
1355 scmutil.checknewlabel(repo, label, b'branch')
1355 scmutil.checknewlabel(repo, label, b'branch')
1356 if revs:
1356 if revs:
1357 return cmdutil.changebranch(ui, repo, revs, label, opts)
1357 return cmdutil.changebranch(ui, repo, revs, label, opts)
1358
1358
1359 if not opts.get(b'force') and label in repo.branchmap():
1359 if not opts.get(b'force') and label in repo.branchmap():
1360 if label not in [p.branch() for p in repo[None].parents()]:
1360 if label not in [p.branch() for p in repo[None].parents()]:
1361 raise error.InputError(
1361 raise error.InputError(
1362 _(b'a branch of the same name already exists'),
1362 _(b'a branch of the same name already exists'),
1363 # i18n: "it" refers to an existing branch
1363 # i18n: "it" refers to an existing branch
1364 hint=_(b"use 'hg update' to switch to it"),
1364 hint=_(b"use 'hg update' to switch to it"),
1365 )
1365 )
1366
1366
1367 repo.dirstate.setbranch(label)
1367 repo.dirstate.setbranch(label)
1368 ui.status(_(b'marked working directory as branch %s\n') % label)
1368 ui.status(_(b'marked working directory as branch %s\n') % label)
1369
1369
1370 # find any open named branches aside from default
1370 # find any open named branches aside from default
1371 for n, h, t, c in repo.branchmap().iterbranches():
1371 for n, h, t, c in repo.branchmap().iterbranches():
1372 if n != b"default" and not c:
1372 if n != b"default" and not c:
1373 return 0
1373 return 0
1374 ui.status(
1374 ui.status(
1375 _(
1375 _(
1376 b'(branches are permanent and global, '
1376 b'(branches are permanent and global, '
1377 b'did you want a bookmark?)\n'
1377 b'did you want a bookmark?)\n'
1378 )
1378 )
1379 )
1379 )
1380
1380
1381
1381
1382 @command(
1382 @command(
1383 b'branches',
1383 b'branches',
1384 [
1384 [
1385 (
1385 (
1386 b'a',
1386 b'a',
1387 b'active',
1387 b'active',
1388 False,
1388 False,
1389 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1389 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1390 ),
1390 ),
1391 (b'c', b'closed', False, _(b'show normal and closed branches')),
1391 (b'c', b'closed', False, _(b'show normal and closed branches')),
1392 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1392 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1393 ]
1393 ]
1394 + formatteropts,
1394 + formatteropts,
1395 _(b'[-c]'),
1395 _(b'[-c]'),
1396 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1396 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1397 intents={INTENT_READONLY},
1397 intents={INTENT_READONLY},
1398 )
1398 )
1399 def branches(ui, repo, active=False, closed=False, **opts):
1399 def branches(ui, repo, active=False, closed=False, **opts):
1400 """list repository named branches
1400 """list repository named branches
1401
1401
1402 List the repository's named branches, indicating which ones are
1402 List the repository's named branches, indicating which ones are
1403 inactive. If -c/--closed is specified, also list branches which have
1403 inactive. If -c/--closed is specified, also list branches which have
1404 been marked closed (see :hg:`commit --close-branch`).
1404 been marked closed (see :hg:`commit --close-branch`).
1405
1405
1406 Use the command :hg:`update` to switch to an existing branch.
1406 Use the command :hg:`update` to switch to an existing branch.
1407
1407
1408 .. container:: verbose
1408 .. container:: verbose
1409
1409
1410 Template:
1410 Template:
1411
1411
1412 The following keywords are supported in addition to the common template
1412 The following keywords are supported in addition to the common template
1413 keywords and functions such as ``{branch}``. See also
1413 keywords and functions such as ``{branch}``. See also
1414 :hg:`help templates`.
1414 :hg:`help templates`.
1415
1415
1416 :active: Boolean. True if the branch is active.
1416 :active: Boolean. True if the branch is active.
1417 :closed: Boolean. True if the branch is closed.
1417 :closed: Boolean. True if the branch is closed.
1418 :current: Boolean. True if it is the current branch.
1418 :current: Boolean. True if it is the current branch.
1419
1419
1420 Returns 0.
1420 Returns 0.
1421 """
1421 """
1422
1422
1423 opts = pycompat.byteskwargs(opts)
1423 opts = pycompat.byteskwargs(opts)
1424 revs = opts.get(b'rev')
1424 revs = opts.get(b'rev')
1425 selectedbranches = None
1425 selectedbranches = None
1426 if revs:
1426 if revs:
1427 revs = scmutil.revrange(repo, revs)
1427 revs = scmutil.revrange(repo, revs)
1428 getbi = repo.revbranchcache().branchinfo
1428 getbi = repo.revbranchcache().branchinfo
1429 selectedbranches = {getbi(r)[0] for r in revs}
1429 selectedbranches = {getbi(r)[0] for r in revs}
1430
1430
1431 ui.pager(b'branches')
1431 ui.pager(b'branches')
1432 fm = ui.formatter(b'branches', opts)
1432 fm = ui.formatter(b'branches', opts)
1433 hexfunc = fm.hexfunc
1433 hexfunc = fm.hexfunc
1434
1434
1435 allheads = set(repo.heads())
1435 allheads = set(repo.heads())
1436 branches = []
1436 branches = []
1437 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1437 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1438 if selectedbranches is not None and tag not in selectedbranches:
1438 if selectedbranches is not None and tag not in selectedbranches:
1439 continue
1439 continue
1440 isactive = False
1440 isactive = False
1441 if not isclosed:
1441 if not isclosed:
1442 openheads = set(repo.branchmap().iteropen(heads))
1442 openheads = set(repo.branchmap().iteropen(heads))
1443 isactive = bool(openheads & allheads)
1443 isactive = bool(openheads & allheads)
1444 branches.append((tag, repo[tip], isactive, not isclosed))
1444 branches.append((tag, repo[tip], isactive, not isclosed))
1445 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1445 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1446
1446
1447 for tag, ctx, isactive, isopen in branches:
1447 for tag, ctx, isactive, isopen in branches:
1448 if active and not isactive:
1448 if active and not isactive:
1449 continue
1449 continue
1450 if isactive:
1450 if isactive:
1451 label = b'branches.active'
1451 label = b'branches.active'
1452 notice = b''
1452 notice = b''
1453 elif not isopen:
1453 elif not isopen:
1454 if not closed:
1454 if not closed:
1455 continue
1455 continue
1456 label = b'branches.closed'
1456 label = b'branches.closed'
1457 notice = _(b' (closed)')
1457 notice = _(b' (closed)')
1458 else:
1458 else:
1459 label = b'branches.inactive'
1459 label = b'branches.inactive'
1460 notice = _(b' (inactive)')
1460 notice = _(b' (inactive)')
1461 current = tag == repo.dirstate.branch()
1461 current = tag == repo.dirstate.branch()
1462 if current:
1462 if current:
1463 label = b'branches.current'
1463 label = b'branches.current'
1464
1464
1465 fm.startitem()
1465 fm.startitem()
1466 fm.write(b'branch', b'%s', tag, label=label)
1466 fm.write(b'branch', b'%s', tag, label=label)
1467 rev = ctx.rev()
1467 rev = ctx.rev()
1468 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1468 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1469 fmt = b' ' * padsize + b' %d:%s'
1469 fmt = b' ' * padsize + b' %d:%s'
1470 fm.condwrite(
1470 fm.condwrite(
1471 not ui.quiet,
1471 not ui.quiet,
1472 b'rev node',
1472 b'rev node',
1473 fmt,
1473 fmt,
1474 rev,
1474 rev,
1475 hexfunc(ctx.node()),
1475 hexfunc(ctx.node()),
1476 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1476 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1477 )
1477 )
1478 fm.context(ctx=ctx)
1478 fm.context(ctx=ctx)
1479 fm.data(active=isactive, closed=not isopen, current=current)
1479 fm.data(active=isactive, closed=not isopen, current=current)
1480 if not ui.quiet:
1480 if not ui.quiet:
1481 fm.plain(notice)
1481 fm.plain(notice)
1482 fm.plain(b'\n')
1482 fm.plain(b'\n')
1483 fm.end()
1483 fm.end()
1484
1484
1485
1485
1486 @command(
1486 @command(
1487 b'bundle',
1487 b'bundle',
1488 [
1488 [
1489 (
1489 (
1490 b'f',
1490 b'f',
1491 b'force',
1491 b'force',
1492 None,
1492 None,
1493 _(b'run even when the destination is unrelated'),
1493 _(b'run even when the destination is unrelated'),
1494 ),
1494 ),
1495 (
1495 (
1496 b'r',
1496 b'r',
1497 b'rev',
1497 b'rev',
1498 [],
1498 [],
1499 _(b'a changeset intended to be added to the destination'),
1499 _(b'a changeset intended to be added to the destination'),
1500 _(b'REV'),
1500 _(b'REV'),
1501 ),
1501 ),
1502 (
1502 (
1503 b'b',
1503 b'b',
1504 b'branch',
1504 b'branch',
1505 [],
1505 [],
1506 _(b'a specific branch you would like to bundle'),
1506 _(b'a specific branch you would like to bundle'),
1507 _(b'BRANCH'),
1507 _(b'BRANCH'),
1508 ),
1508 ),
1509 (
1509 (
1510 b'',
1510 b'',
1511 b'base',
1511 b'base',
1512 [],
1512 [],
1513 _(b'a base changeset assumed to be available at the destination'),
1513 _(b'a base changeset assumed to be available at the destination'),
1514 _(b'REV'),
1514 _(b'REV'),
1515 ),
1515 ),
1516 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1516 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1517 (
1517 (
1518 b't',
1518 b't',
1519 b'type',
1519 b'type',
1520 b'bzip2',
1520 b'bzip2',
1521 _(b'bundle compression type to use'),
1521 _(b'bundle compression type to use'),
1522 _(b'TYPE'),
1522 _(b'TYPE'),
1523 ),
1523 ),
1524 ]
1524 ]
1525 + remoteopts,
1525 + remoteopts,
1526 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1526 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1527 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1527 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1528 )
1528 )
1529 def bundle(ui, repo, fname, *dests, **opts):
1529 def bundle(ui, repo, fname, *dests, **opts):
1530 """create a bundle file
1530 """create a bundle file
1531
1531
1532 Generate a bundle file containing data to be transferred to another
1532 Generate a bundle file containing data to be transferred to another
1533 repository.
1533 repository.
1534
1534
1535 To create a bundle containing all changesets, use -a/--all
1535 To create a bundle containing all changesets, use -a/--all
1536 (or --base null). Otherwise, hg assumes the destination will have
1536 (or --base null). Otherwise, hg assumes the destination will have
1537 all the nodes you specify with --base parameters. Otherwise, hg
1537 all the nodes you specify with --base parameters. Otherwise, hg
1538 will assume the repository has all the nodes in destination, or
1538 will assume the repository has all the nodes in destination, or
1539 default-push/default if no destination is specified, where destination
1539 default-push/default if no destination is specified, where destination
1540 is the repositories you provide through DEST option.
1540 is the repositories you provide through DEST option.
1541
1541
1542 You can change bundle format with the -t/--type option. See
1542 You can change bundle format with the -t/--type option. See
1543 :hg:`help bundlespec` for documentation on this format. By default,
1543 :hg:`help bundlespec` for documentation on this format. By default,
1544 the most appropriate format is used and compression defaults to
1544 the most appropriate format is used and compression defaults to
1545 bzip2.
1545 bzip2.
1546
1546
1547 The bundle file can then be transferred using conventional means
1547 The bundle file can then be transferred using conventional means
1548 and applied to another repository with the unbundle or pull
1548 and applied to another repository with the unbundle or pull
1549 command. This is useful when direct push and pull are not
1549 command. This is useful when direct push and pull are not
1550 available or when exporting an entire repository is undesirable.
1550 available or when exporting an entire repository is undesirable.
1551
1551
1552 Applying bundles preserves all changeset contents including
1552 Applying bundles preserves all changeset contents including
1553 permissions, copy/rename information, and revision history.
1553 permissions, copy/rename information, and revision history.
1554
1554
1555 Returns 0 on success, 1 if no changes found.
1555 Returns 0 on success, 1 if no changes found.
1556 """
1556 """
1557 opts = pycompat.byteskwargs(opts)
1557 opts = pycompat.byteskwargs(opts)
1558 revs = None
1558 revs = None
1559 if b'rev' in opts:
1559 if b'rev' in opts:
1560 revstrings = opts[b'rev']
1560 revstrings = opts[b'rev']
1561 revs = scmutil.revrange(repo, revstrings)
1561 revs = scmutil.revrange(repo, revstrings)
1562 if revstrings and not revs:
1562 if revstrings and not revs:
1563 raise error.InputError(_(b'no commits to bundle'))
1563 raise error.InputError(_(b'no commits to bundle'))
1564
1564
1565 bundletype = opts.get(b'type', b'bzip2').lower()
1565 bundletype = opts.get(b'type', b'bzip2').lower()
1566 try:
1566 try:
1567 bundlespec = bundlecaches.parsebundlespec(
1567 bundlespec = bundlecaches.parsebundlespec(
1568 repo, bundletype, strict=False
1568 repo, bundletype, strict=False
1569 )
1569 )
1570 except error.UnsupportedBundleSpecification as e:
1570 except error.UnsupportedBundleSpecification as e:
1571 raise error.InputError(
1571 raise error.InputError(
1572 pycompat.bytestr(e),
1572 pycompat.bytestr(e),
1573 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1573 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1574 )
1574 )
1575 cgversion = bundlespec.contentopts[b"cg.version"]
1575 cgversion = bundlespec.contentopts[b"cg.version"]
1576
1576
1577 # Packed bundles are a pseudo bundle format for now.
1577 # Packed bundles are a pseudo bundle format for now.
1578 if cgversion == b's1':
1578 if cgversion == b's1':
1579 raise error.InputError(
1579 raise error.InputError(
1580 _(b'packed bundles cannot be produced by "hg bundle"'),
1580 _(b'packed bundles cannot be produced by "hg bundle"'),
1581 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1581 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1582 )
1582 )
1583
1583
1584 if opts.get(b'all'):
1584 if opts.get(b'all'):
1585 if dests:
1585 if dests:
1586 raise error.InputError(
1586 raise error.InputError(
1587 _(b"--all is incompatible with specifying destinations")
1587 _(b"--all is incompatible with specifying destinations")
1588 )
1588 )
1589 if opts.get(b'base'):
1589 if opts.get(b'base'):
1590 ui.warn(_(b"ignoring --base because --all was specified\n"))
1590 ui.warn(_(b"ignoring --base because --all was specified\n"))
1591 base = [nullrev]
1591 base = [nullrev]
1592 else:
1592 else:
1593 base = scmutil.revrange(repo, opts.get(b'base'))
1593 base = scmutil.revrange(repo, opts.get(b'base'))
1594 if cgversion not in changegroup.supportedoutgoingversions(repo):
1594 if cgversion not in changegroup.supportedoutgoingversions(repo):
1595 raise error.Abort(
1595 raise error.Abort(
1596 _(b"repository does not support bundle version %s") % cgversion
1596 _(b"repository does not support bundle version %s") % cgversion
1597 )
1597 )
1598
1598
1599 if base:
1599 if base:
1600 if dests:
1600 if dests:
1601 raise error.InputError(
1601 raise error.InputError(
1602 _(b"--base is incompatible with specifying destinations")
1602 _(b"--base is incompatible with specifying destinations")
1603 )
1603 )
1604 common = [repo[rev].node() for rev in base]
1604 common = [repo[rev].node() for rev in base]
1605 heads = [repo[r].node() for r in revs] if revs else None
1605 heads = [repo[r].node() for r in revs] if revs else None
1606 outgoing = discovery.outgoing(repo, common, heads)
1606 outgoing = discovery.outgoing(repo, common, heads)
1607 missing = outgoing.missing
1607 missing = outgoing.missing
1608 excluded = outgoing.excluded
1608 excluded = outgoing.excluded
1609 else:
1609 else:
1610 missing = set()
1610 missing = set()
1611 excluded = set()
1611 excluded = set()
1612 for path in urlutil.get_push_paths(repo, ui, dests):
1612 for path in urlutil.get_push_paths(repo, ui, dests):
1613 other = hg.peer(repo, opts, path.rawloc)
1613 other = hg.peer(repo, opts, path.rawloc)
1614 if revs is not None:
1614 if revs is not None:
1615 hex_revs = [repo[r].hex() for r in revs]
1615 hex_revs = [repo[r].hex() for r in revs]
1616 else:
1616 else:
1617 hex_revs = None
1617 hex_revs = None
1618 branches = (path.branch, [])
1618 branches = (path.branch, [])
1619 head_revs, checkout = hg.addbranchrevs(
1619 head_revs, checkout = hg.addbranchrevs(
1620 repo, repo, branches, hex_revs
1620 repo, repo, branches, hex_revs
1621 )
1621 )
1622 heads = (
1622 heads = (
1623 head_revs
1623 head_revs
1624 and pycompat.maplist(repo.lookup, head_revs)
1624 and pycompat.maplist(repo.lookup, head_revs)
1625 or head_revs
1625 or head_revs
1626 )
1626 )
1627 outgoing = discovery.findcommonoutgoing(
1627 outgoing = discovery.findcommonoutgoing(
1628 repo,
1628 repo,
1629 other,
1629 other,
1630 onlyheads=heads,
1630 onlyheads=heads,
1631 force=opts.get(b'force'),
1631 force=opts.get(b'force'),
1632 portable=True,
1632 portable=True,
1633 )
1633 )
1634 missing.update(outgoing.missing)
1634 missing.update(outgoing.missing)
1635 excluded.update(outgoing.excluded)
1635 excluded.update(outgoing.excluded)
1636
1636
1637 if not missing:
1637 if not missing:
1638 scmutil.nochangesfound(ui, repo, not base and excluded)
1638 scmutil.nochangesfound(ui, repo, not base and excluded)
1639 return 1
1639 return 1
1640
1640
1641 if heads:
1641 if heads:
1642 outgoing = discovery.outgoing(
1642 outgoing = discovery.outgoing(
1643 repo, missingroots=missing, ancestorsof=heads
1643 repo, missingroots=missing, ancestorsof=heads
1644 )
1644 )
1645 else:
1645 else:
1646 outgoing = discovery.outgoing(repo, missingroots=missing)
1646 outgoing = discovery.outgoing(repo, missingroots=missing)
1647 outgoing.excluded = sorted(excluded)
1647 outgoing.excluded = sorted(excluded)
1648
1648
1649 if cgversion == b'01': # bundle1
1649 if cgversion == b'01': # bundle1
1650 bversion = b'HG10' + bundlespec.wirecompression
1650 bversion = b'HG10' + bundlespec.wirecompression
1651 bcompression = None
1651 bcompression = None
1652 elif cgversion in (b'02', b'03'):
1652 elif cgversion in (b'02', b'03'):
1653 bversion = b'HG20'
1653 bversion = b'HG20'
1654 bcompression = bundlespec.wirecompression
1654 bcompression = bundlespec.wirecompression
1655 else:
1655 else:
1656 raise error.ProgrammingError(
1656 raise error.ProgrammingError(
1657 b'bundle: unexpected changegroup version %s' % cgversion
1657 b'bundle: unexpected changegroup version %s' % cgversion
1658 )
1658 )
1659
1659
1660 # TODO compression options should be derived from bundlespec parsing.
1660 # TODO compression options should be derived from bundlespec parsing.
1661 # This is a temporary hack to allow adjusting bundle compression
1661 # This is a temporary hack to allow adjusting bundle compression
1662 # level without a) formalizing the bundlespec changes to declare it
1662 # level without a) formalizing the bundlespec changes to declare it
1663 # b) introducing a command flag.
1663 # b) introducing a command flag.
1664 compopts = {}
1664 compopts = {}
1665 complevel = ui.configint(
1665 complevel = ui.configint(
1666 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1666 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1667 )
1667 )
1668 if complevel is None:
1668 if complevel is None:
1669 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1669 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1670 if complevel is not None:
1670 if complevel is not None:
1671 compopts[b'level'] = complevel
1671 compopts[b'level'] = complevel
1672
1672
1673 compthreads = ui.configint(
1673 compthreads = ui.configint(
1674 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1674 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1675 )
1675 )
1676 if compthreads is None:
1676 if compthreads is None:
1677 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1677 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1678 if compthreads is not None:
1678 if compthreads is not None:
1679 compopts[b'threads'] = compthreads
1679 compopts[b'threads'] = compthreads
1680
1680
1681 # Bundling of obsmarker and phases is optional as not all clients
1681 # Bundling of obsmarker and phases is optional as not all clients
1682 # support the necessary features.
1682 # support the necessary features.
1683 cfg = ui.configbool
1683 cfg = ui.configbool
1684 contentopts = {
1684 contentopts = {
1685 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1685 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1686 b'obsolescence-mandatory': cfg(
1686 b'obsolescence-mandatory': cfg(
1687 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1687 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1688 ),
1688 ),
1689 b'phases': cfg(b'experimental', b'bundle-phases'),
1689 b'phases': cfg(b'experimental', b'bundle-phases'),
1690 }
1690 }
1691 bundlespec.contentopts.update(contentopts)
1691 bundlespec.contentopts.update(contentopts)
1692
1692
1693 bundle2.writenewbundle(
1693 bundle2.writenewbundle(
1694 ui,
1694 ui,
1695 repo,
1695 repo,
1696 b'bundle',
1696 b'bundle',
1697 fname,
1697 fname,
1698 bversion,
1698 bversion,
1699 outgoing,
1699 outgoing,
1700 bundlespec.contentopts,
1700 bundlespec.contentopts,
1701 compression=bcompression,
1701 compression=bcompression,
1702 compopts=compopts,
1702 compopts=compopts,
1703 )
1703 )
1704
1704
1705
1705
1706 @command(
1706 @command(
1707 b'cat',
1707 b'cat',
1708 [
1708 [
1709 (
1709 (
1710 b'o',
1710 b'o',
1711 b'output',
1711 b'output',
1712 b'',
1712 b'',
1713 _(b'print output to file with formatted name'),
1713 _(b'print output to file with formatted name'),
1714 _(b'FORMAT'),
1714 _(b'FORMAT'),
1715 ),
1715 ),
1716 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1716 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1717 (b'', b'decode', None, _(b'apply any matching decode filter')),
1717 (b'', b'decode', None, _(b'apply any matching decode filter')),
1718 ]
1718 ]
1719 + walkopts
1719 + walkopts
1720 + formatteropts,
1720 + formatteropts,
1721 _(b'[OPTION]... FILE...'),
1721 _(b'[OPTION]... FILE...'),
1722 helpcategory=command.CATEGORY_FILE_CONTENTS,
1722 helpcategory=command.CATEGORY_FILE_CONTENTS,
1723 inferrepo=True,
1723 inferrepo=True,
1724 intents={INTENT_READONLY},
1724 intents={INTENT_READONLY},
1725 )
1725 )
1726 def cat(ui, repo, file1, *pats, **opts):
1726 def cat(ui, repo, file1, *pats, **opts):
1727 """output the current or given revision of files
1727 """output the current or given revision of files
1728
1728
1729 Print the specified files as they were at the given revision. If
1729 Print the specified files as they were at the given revision. If
1730 no revision is given, the parent of the working directory is used.
1730 no revision is given, the parent of the working directory is used.
1731
1731
1732 Output may be to a file, in which case the name of the file is
1732 Output may be to a file, in which case the name of the file is
1733 given using a template string. See :hg:`help templates`. In addition
1733 given using a template string. See :hg:`help templates`. In addition
1734 to the common template keywords, the following formatting rules are
1734 to the common template keywords, the following formatting rules are
1735 supported:
1735 supported:
1736
1736
1737 :``%%``: literal "%" character
1737 :``%%``: literal "%" character
1738 :``%s``: basename of file being printed
1738 :``%s``: basename of file being printed
1739 :``%d``: dirname of file being printed, or '.' if in repository root
1739 :``%d``: dirname of file being printed, or '.' if in repository root
1740 :``%p``: root-relative path name of file being printed
1740 :``%p``: root-relative path name of file being printed
1741 :``%H``: changeset hash (40 hexadecimal digits)
1741 :``%H``: changeset hash (40 hexadecimal digits)
1742 :``%R``: changeset revision number
1742 :``%R``: changeset revision number
1743 :``%h``: short-form changeset hash (12 hexadecimal digits)
1743 :``%h``: short-form changeset hash (12 hexadecimal digits)
1744 :``%r``: zero-padded changeset revision number
1744 :``%r``: zero-padded changeset revision number
1745 :``%b``: basename of the exporting repository
1745 :``%b``: basename of the exporting repository
1746 :``\\``: literal "\\" character
1746 :``\\``: literal "\\" character
1747
1747
1748 .. container:: verbose
1748 .. container:: verbose
1749
1749
1750 Template:
1750 Template:
1751
1751
1752 The following keywords are supported in addition to the common template
1752 The following keywords are supported in addition to the common template
1753 keywords and functions. See also :hg:`help templates`.
1753 keywords and functions. See also :hg:`help templates`.
1754
1754
1755 :data: String. File content.
1755 :data: String. File content.
1756 :path: String. Repository-absolute path of the file.
1756 :path: String. Repository-absolute path of the file.
1757
1757
1758 Returns 0 on success.
1758 Returns 0 on success.
1759 """
1759 """
1760 opts = pycompat.byteskwargs(opts)
1760 opts = pycompat.byteskwargs(opts)
1761 rev = opts.get(b'rev')
1761 rev = opts.get(b'rev')
1762 if rev:
1762 if rev:
1763 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1763 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1764 ctx = scmutil.revsingle(repo, rev)
1764 ctx = scmutil.revsingle(repo, rev)
1765 m = scmutil.match(ctx, (file1,) + pats, opts)
1765 m = scmutil.match(ctx, (file1,) + pats, opts)
1766 fntemplate = opts.pop(b'output', b'')
1766 fntemplate = opts.pop(b'output', b'')
1767 if cmdutil.isstdiofilename(fntemplate):
1767 if cmdutil.isstdiofilename(fntemplate):
1768 fntemplate = b''
1768 fntemplate = b''
1769
1769
1770 if fntemplate:
1770 if fntemplate:
1771 fm = formatter.nullformatter(ui, b'cat', opts)
1771 fm = formatter.nullformatter(ui, b'cat', opts)
1772 else:
1772 else:
1773 ui.pager(b'cat')
1773 ui.pager(b'cat')
1774 fm = ui.formatter(b'cat', opts)
1774 fm = ui.formatter(b'cat', opts)
1775 with fm:
1775 with fm:
1776 return cmdutil.cat(
1776 return cmdutil.cat(
1777 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1777 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1778 )
1778 )
1779
1779
1780
1780
1781 @command(
1781 @command(
1782 b'clone',
1782 b'clone',
1783 [
1783 [
1784 (
1784 (
1785 b'U',
1785 b'U',
1786 b'noupdate',
1786 b'noupdate',
1787 None,
1787 None,
1788 _(
1788 _(
1789 b'the clone will include an empty working '
1789 b'the clone will include an empty working '
1790 b'directory (only a repository)'
1790 b'directory (only a repository)'
1791 ),
1791 ),
1792 ),
1792 ),
1793 (
1793 (
1794 b'u',
1794 b'u',
1795 b'updaterev',
1795 b'updaterev',
1796 b'',
1796 b'',
1797 _(b'revision, tag, or branch to check out'),
1797 _(b'revision, tag, or branch to check out'),
1798 _(b'REV'),
1798 _(b'REV'),
1799 ),
1799 ),
1800 (
1800 (
1801 b'r',
1801 b'r',
1802 b'rev',
1802 b'rev',
1803 [],
1803 [],
1804 _(
1804 _(
1805 b'do not clone everything, but include this changeset'
1805 b'do not clone everything, but include this changeset'
1806 b' and its ancestors'
1806 b' and its ancestors'
1807 ),
1807 ),
1808 _(b'REV'),
1808 _(b'REV'),
1809 ),
1809 ),
1810 (
1810 (
1811 b'b',
1811 b'b',
1812 b'branch',
1812 b'branch',
1813 [],
1813 [],
1814 _(
1814 _(
1815 b'do not clone everything, but include this branch\'s'
1815 b'do not clone everything, but include this branch\'s'
1816 b' changesets and their ancestors'
1816 b' changesets and their ancestors'
1817 ),
1817 ),
1818 _(b'BRANCH'),
1818 _(b'BRANCH'),
1819 ),
1819 ),
1820 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1820 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1821 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1821 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1822 (b'', b'stream', None, _(b'clone with minimal data processing')),
1822 (b'', b'stream', None, _(b'clone with minimal data processing')),
1823 ]
1823 ]
1824 + remoteopts,
1824 + remoteopts,
1825 _(b'[OPTION]... SOURCE [DEST]'),
1825 _(b'[OPTION]... SOURCE [DEST]'),
1826 helpcategory=command.CATEGORY_REPO_CREATION,
1826 helpcategory=command.CATEGORY_REPO_CREATION,
1827 helpbasic=True,
1827 helpbasic=True,
1828 norepo=True,
1828 norepo=True,
1829 )
1829 )
1830 def clone(ui, source, dest=None, **opts):
1830 def clone(ui, source, dest=None, **opts):
1831 """make a copy of an existing repository
1831 """make a copy of an existing repository
1832
1832
1833 Create a copy of an existing repository in a new directory.
1833 Create a copy of an existing repository in a new directory.
1834
1834
1835 If no destination directory name is specified, it defaults to the
1835 If no destination directory name is specified, it defaults to the
1836 basename of the source.
1836 basename of the source.
1837
1837
1838 The location of the source is added to the new repository's
1838 The location of the source is added to the new repository's
1839 ``.hg/hgrc`` file, as the default to be used for future pulls.
1839 ``.hg/hgrc`` file, as the default to be used for future pulls.
1840
1840
1841 Only local paths and ``ssh://`` URLs are supported as
1841 Only local paths and ``ssh://`` URLs are supported as
1842 destinations. For ``ssh://`` destinations, no working directory or
1842 destinations. For ``ssh://`` destinations, no working directory or
1843 ``.hg/hgrc`` will be created on the remote side.
1843 ``.hg/hgrc`` will be created on the remote side.
1844
1844
1845 If the source repository has a bookmark called '@' set, that
1845 If the source repository has a bookmark called '@' set, that
1846 revision will be checked out in the new repository by default.
1846 revision will be checked out in the new repository by default.
1847
1847
1848 To check out a particular version, use -u/--update, or
1848 To check out a particular version, use -u/--update, or
1849 -U/--noupdate to create a clone with no working directory.
1849 -U/--noupdate to create a clone with no working directory.
1850
1850
1851 To pull only a subset of changesets, specify one or more revisions
1851 To pull only a subset of changesets, specify one or more revisions
1852 identifiers with -r/--rev or branches with -b/--branch. The
1852 identifiers with -r/--rev or branches with -b/--branch. The
1853 resulting clone will contain only the specified changesets and
1853 resulting clone will contain only the specified changesets and
1854 their ancestors. These options (or 'clone src#rev dest') imply
1854 their ancestors. These options (or 'clone src#rev dest') imply
1855 --pull, even for local source repositories.
1855 --pull, even for local source repositories.
1856
1856
1857 In normal clone mode, the remote normalizes repository data into a common
1857 In normal clone mode, the remote normalizes repository data into a common
1858 exchange format and the receiving end translates this data into its local
1858 exchange format and the receiving end translates this data into its local
1859 storage format. --stream activates a different clone mode that essentially
1859 storage format. --stream activates a different clone mode that essentially
1860 copies repository files from the remote with minimal data processing. This
1860 copies repository files from the remote with minimal data processing. This
1861 significantly reduces the CPU cost of a clone both remotely and locally.
1861 significantly reduces the CPU cost of a clone both remotely and locally.
1862 However, it often increases the transferred data size by 30-40%. This can
1862 However, it often increases the transferred data size by 30-40%. This can
1863 result in substantially faster clones where I/O throughput is plentiful,
1863 result in substantially faster clones where I/O throughput is plentiful,
1864 especially for larger repositories. A side-effect of --stream clones is
1864 especially for larger repositories. A side-effect of --stream clones is
1865 that storage settings and requirements on the remote are applied locally:
1865 that storage settings and requirements on the remote are applied locally:
1866 a modern client may inherit legacy or inefficient storage used by the
1866 a modern client may inherit legacy or inefficient storage used by the
1867 remote or a legacy Mercurial client may not be able to clone from a
1867 remote or a legacy Mercurial client may not be able to clone from a
1868 modern Mercurial remote.
1868 modern Mercurial remote.
1869
1869
1870 .. note::
1870 .. note::
1871
1871
1872 Specifying a tag will include the tagged changeset but not the
1872 Specifying a tag will include the tagged changeset but not the
1873 changeset containing the tag.
1873 changeset containing the tag.
1874
1874
1875 .. container:: verbose
1875 .. container:: verbose
1876
1876
1877 For efficiency, hardlinks are used for cloning whenever the
1877 For efficiency, hardlinks are used for cloning whenever the
1878 source and destination are on the same filesystem (note this
1878 source and destination are on the same filesystem (note this
1879 applies only to the repository data, not to the working
1879 applies only to the repository data, not to the working
1880 directory). Some filesystems, such as AFS, implement hardlinking
1880 directory). Some filesystems, such as AFS, implement hardlinking
1881 incorrectly, but do not report errors. In these cases, use the
1881 incorrectly, but do not report errors. In these cases, use the
1882 --pull option to avoid hardlinking.
1882 --pull option to avoid hardlinking.
1883
1883
1884 Mercurial will update the working directory to the first applicable
1884 Mercurial will update the working directory to the first applicable
1885 revision from this list:
1885 revision from this list:
1886
1886
1887 a) null if -U or the source repository has no changesets
1887 a) null if -U or the source repository has no changesets
1888 b) if -u . and the source repository is local, the first parent of
1888 b) if -u . and the source repository is local, the first parent of
1889 the source repository's working directory
1889 the source repository's working directory
1890 c) the changeset specified with -u (if a branch name, this means the
1890 c) the changeset specified with -u (if a branch name, this means the
1891 latest head of that branch)
1891 latest head of that branch)
1892 d) the changeset specified with -r
1892 d) the changeset specified with -r
1893 e) the tipmost head specified with -b
1893 e) the tipmost head specified with -b
1894 f) the tipmost head specified with the url#branch source syntax
1894 f) the tipmost head specified with the url#branch source syntax
1895 g) the revision marked with the '@' bookmark, if present
1895 g) the revision marked with the '@' bookmark, if present
1896 h) the tipmost head of the default branch
1896 h) the tipmost head of the default branch
1897 i) tip
1897 i) tip
1898
1898
1899 When cloning from servers that support it, Mercurial may fetch
1899 When cloning from servers that support it, Mercurial may fetch
1900 pre-generated data from a server-advertised URL or inline from the
1900 pre-generated data from a server-advertised URL or inline from the
1901 same stream. When this is done, hooks operating on incoming changesets
1901 same stream. When this is done, hooks operating on incoming changesets
1902 and changegroups may fire more than once, once for each pre-generated
1902 and changegroups may fire more than once, once for each pre-generated
1903 bundle and as well as for any additional remaining data. In addition,
1903 bundle and as well as for any additional remaining data. In addition,
1904 if an error occurs, the repository may be rolled back to a partial
1904 if an error occurs, the repository may be rolled back to a partial
1905 clone. This behavior may change in future releases.
1905 clone. This behavior may change in future releases.
1906 See :hg:`help -e clonebundles` for more.
1906 See :hg:`help -e clonebundles` for more.
1907
1907
1908 Examples:
1908 Examples:
1909
1909
1910 - clone a remote repository to a new directory named hg/::
1910 - clone a remote repository to a new directory named hg/::
1911
1911
1912 hg clone https://www.mercurial-scm.org/repo/hg/
1912 hg clone https://www.mercurial-scm.org/repo/hg/
1913
1913
1914 - create a lightweight local clone::
1914 - create a lightweight local clone::
1915
1915
1916 hg clone project/ project-feature/
1916 hg clone project/ project-feature/
1917
1917
1918 - clone from an absolute path on an ssh server (note double-slash)::
1918 - clone from an absolute path on an ssh server (note double-slash)::
1919
1919
1920 hg clone ssh://user@server//home/projects/alpha/
1920 hg clone ssh://user@server//home/projects/alpha/
1921
1921
1922 - do a streaming clone while checking out a specified version::
1922 - do a streaming clone while checking out a specified version::
1923
1923
1924 hg clone --stream http://server/repo -u 1.5
1924 hg clone --stream http://server/repo -u 1.5
1925
1925
1926 - create a repository without changesets after a particular revision::
1926 - create a repository without changesets after a particular revision::
1927
1927
1928 hg clone -r 04e544 experimental/ good/
1928 hg clone -r 04e544 experimental/ good/
1929
1929
1930 - clone (and track) a particular named branch::
1930 - clone (and track) a particular named branch::
1931
1931
1932 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1932 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1933
1933
1934 See :hg:`help urls` for details on specifying URLs.
1934 See :hg:`help urls` for details on specifying URLs.
1935
1935
1936 Returns 0 on success.
1936 Returns 0 on success.
1937 """
1937 """
1938 opts = pycompat.byteskwargs(opts)
1938 opts = pycompat.byteskwargs(opts)
1939 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1939 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1940
1940
1941 # --include/--exclude can come from narrow or sparse.
1941 # --include/--exclude can come from narrow or sparse.
1942 includepats, excludepats = None, None
1942 includepats, excludepats = None, None
1943
1943
1944 # hg.clone() differentiates between None and an empty set. So make sure
1944 # hg.clone() differentiates between None and an empty set. So make sure
1945 # patterns are sets if narrow is requested without patterns.
1945 # patterns are sets if narrow is requested without patterns.
1946 if opts.get(b'narrow'):
1946 if opts.get(b'narrow'):
1947 includepats = set()
1947 includepats = set()
1948 excludepats = set()
1948 excludepats = set()
1949
1949
1950 if opts.get(b'include'):
1950 if opts.get(b'include'):
1951 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1951 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1952 if opts.get(b'exclude'):
1952 if opts.get(b'exclude'):
1953 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1953 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1954
1954
1955 r = hg.clone(
1955 r = hg.clone(
1956 ui,
1956 ui,
1957 opts,
1957 opts,
1958 source,
1958 source,
1959 dest,
1959 dest,
1960 pull=opts.get(b'pull'),
1960 pull=opts.get(b'pull'),
1961 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1961 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1962 revs=opts.get(b'rev'),
1962 revs=opts.get(b'rev'),
1963 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1963 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1964 branch=opts.get(b'branch'),
1964 branch=opts.get(b'branch'),
1965 shareopts=opts.get(b'shareopts'),
1965 shareopts=opts.get(b'shareopts'),
1966 storeincludepats=includepats,
1966 storeincludepats=includepats,
1967 storeexcludepats=excludepats,
1967 storeexcludepats=excludepats,
1968 depth=opts.get(b'depth') or None,
1968 depth=opts.get(b'depth') or None,
1969 )
1969 )
1970
1970
1971 return r is None
1971 return r is None
1972
1972
1973
1973
1974 @command(
1974 @command(
1975 b'commit|ci',
1975 b'commit|ci',
1976 [
1976 [
1977 (
1977 (
1978 b'A',
1978 b'A',
1979 b'addremove',
1979 b'addremove',
1980 None,
1980 None,
1981 _(b'mark new/missing files as added/removed before committing'),
1981 _(b'mark new/missing files as added/removed before committing'),
1982 ),
1982 ),
1983 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1983 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1984 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1984 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1985 (b's', b'secret', None, _(b'use the secret phase for committing')),
1985 (b's', b'secret', None, _(b'use the secret phase for committing')),
1986 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1986 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1987 (
1987 (
1988 b'',
1988 b'',
1989 b'force-close-branch',
1989 b'force-close-branch',
1990 None,
1990 None,
1991 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1991 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1992 ),
1992 ),
1993 (b'i', b'interactive', None, _(b'use interactive mode')),
1993 (b'i', b'interactive', None, _(b'use interactive mode')),
1994 ]
1994 ]
1995 + walkopts
1995 + walkopts
1996 + commitopts
1996 + commitopts
1997 + commitopts2
1997 + commitopts2
1998 + subrepoopts,
1998 + subrepoopts,
1999 _(b'[OPTION]... [FILE]...'),
1999 _(b'[OPTION]... [FILE]...'),
2000 helpcategory=command.CATEGORY_COMMITTING,
2000 helpcategory=command.CATEGORY_COMMITTING,
2001 helpbasic=True,
2001 helpbasic=True,
2002 inferrepo=True,
2002 inferrepo=True,
2003 )
2003 )
2004 def commit(ui, repo, *pats, **opts):
2004 def commit(ui, repo, *pats, **opts):
2005 """commit the specified files or all outstanding changes
2005 """commit the specified files or all outstanding changes
2006
2006
2007 Commit changes to the given files into the repository. Unlike a
2007 Commit changes to the given files into the repository. Unlike a
2008 centralized SCM, this operation is a local operation. See
2008 centralized SCM, this operation is a local operation. See
2009 :hg:`push` for a way to actively distribute your changes.
2009 :hg:`push` for a way to actively distribute your changes.
2010
2010
2011 If a list of files is omitted, all changes reported by :hg:`status`
2011 If a list of files is omitted, all changes reported by :hg:`status`
2012 will be committed.
2012 will be committed.
2013
2013
2014 If you are committing the result of a merge, do not provide any
2014 If you are committing the result of a merge, do not provide any
2015 filenames or -I/-X filters.
2015 filenames or -I/-X filters.
2016
2016
2017 If no commit message is specified, Mercurial starts your
2017 If no commit message is specified, Mercurial starts your
2018 configured editor where you can enter a message. In case your
2018 configured editor where you can enter a message. In case your
2019 commit fails, you will find a backup of your message in
2019 commit fails, you will find a backup of your message in
2020 ``.hg/last-message.txt``.
2020 ``.hg/last-message.txt``.
2021
2021
2022 The --close-branch flag can be used to mark the current branch
2022 The --close-branch flag can be used to mark the current branch
2023 head closed. When all heads of a branch are closed, the branch
2023 head closed. When all heads of a branch are closed, the branch
2024 will be considered closed and no longer listed.
2024 will be considered closed and no longer listed.
2025
2025
2026 The --amend flag can be used to amend the parent of the
2026 The --amend flag can be used to amend the parent of the
2027 working directory with a new commit that contains the changes
2027 working directory with a new commit that contains the changes
2028 in the parent in addition to those currently reported by :hg:`status`,
2028 in the parent in addition to those currently reported by :hg:`status`,
2029 if there are any. The old commit is stored in a backup bundle in
2029 if there are any. The old commit is stored in a backup bundle in
2030 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2030 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2031 on how to restore it).
2031 on how to restore it).
2032
2032
2033 Message, user and date are taken from the amended commit unless
2033 Message, user and date are taken from the amended commit unless
2034 specified. When a message isn't specified on the command line,
2034 specified. When a message isn't specified on the command line,
2035 the editor will open with the message of the amended commit.
2035 the editor will open with the message of the amended commit.
2036
2036
2037 It is not possible to amend public changesets (see :hg:`help phases`)
2037 It is not possible to amend public changesets (see :hg:`help phases`)
2038 or changesets that have children.
2038 or changesets that have children.
2039
2039
2040 See :hg:`help dates` for a list of formats valid for -d/--date.
2040 See :hg:`help dates` for a list of formats valid for -d/--date.
2041
2041
2042 Returns 0 on success, 1 if nothing changed.
2042 Returns 0 on success, 1 if nothing changed.
2043
2043
2044 .. container:: verbose
2044 .. container:: verbose
2045
2045
2046 Examples:
2046 Examples:
2047
2047
2048 - commit all files ending in .py::
2048 - commit all files ending in .py::
2049
2049
2050 hg commit --include "set:**.py"
2050 hg commit --include "set:**.py"
2051
2051
2052 - commit all non-binary files::
2052 - commit all non-binary files::
2053
2053
2054 hg commit --exclude "set:binary()"
2054 hg commit --exclude "set:binary()"
2055
2055
2056 - amend the current commit and set the date to now::
2056 - amend the current commit and set the date to now::
2057
2057
2058 hg commit --amend --date now
2058 hg commit --amend --date now
2059 """
2059 """
2060 with repo.wlock(), repo.lock():
2060 with repo.wlock(), repo.lock():
2061 return _docommit(ui, repo, *pats, **opts)
2061 return _docommit(ui, repo, *pats, **opts)
2062
2062
2063
2063
2064 def _docommit(ui, repo, *pats, **opts):
2064 def _docommit(ui, repo, *pats, **opts):
2065 if opts.get('interactive'):
2065 if opts.get('interactive'):
2066 opts.pop('interactive')
2066 opts.pop('interactive')
2067 ret = cmdutil.dorecord(
2067 ret = cmdutil.dorecord(
2068 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2068 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2069 )
2069 )
2070 # ret can be 0 (no changes to record) or the value returned by
2070 # ret can be 0 (no changes to record) or the value returned by
2071 # commit(), 1 if nothing changed or None on success.
2071 # commit(), 1 if nothing changed or None on success.
2072 return 1 if ret == 0 else ret
2072 return 1 if ret == 0 else ret
2073
2073
2074 if opts.get('subrepos'):
2074 if opts.get('subrepos'):
2075 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2075 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2076 # Let --subrepos on the command line override config setting.
2076 # Let --subrepos on the command line override config setting.
2077 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2077 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2078
2078
2079 cmdutil.checkunfinished(repo, commit=True)
2079 cmdutil.checkunfinished(repo, commit=True)
2080
2080
2081 branch = repo[None].branch()
2081 branch = repo[None].branch()
2082 bheads = repo.branchheads(branch)
2082 bheads = repo.branchheads(branch)
2083 tip = repo.changelog.tip()
2083 tip = repo.changelog.tip()
2084
2084
2085 extra = {}
2085 extra = {}
2086 if opts.get('close_branch') or opts.get('force_close_branch'):
2086 if opts.get('close_branch') or opts.get('force_close_branch'):
2087 extra[b'close'] = b'1'
2087 extra[b'close'] = b'1'
2088
2088
2089 if repo[b'.'].closesbranch():
2089 if repo[b'.'].closesbranch():
2090 raise error.InputError(
2090 raise error.InputError(
2091 _(b'current revision is already a branch closing head')
2091 _(b'current revision is already a branch closing head')
2092 )
2092 )
2093 elif not bheads:
2093 elif not bheads:
2094 raise error.InputError(
2094 raise error.InputError(
2095 _(b'branch "%s" has no heads to close') % branch
2095 _(b'branch "%s" has no heads to close') % branch
2096 )
2096 )
2097 elif (
2097 elif (
2098 branch == repo[b'.'].branch()
2098 branch == repo[b'.'].branch()
2099 and repo[b'.'].node() not in bheads
2099 and repo[b'.'].node() not in bheads
2100 and not opts.get('force_close_branch')
2100 and not opts.get('force_close_branch')
2101 ):
2101 ):
2102 hint = _(
2102 hint = _(
2103 b'use --force-close-branch to close branch from a non-head'
2103 b'use --force-close-branch to close branch from a non-head'
2104 b' changeset'
2104 b' changeset'
2105 )
2105 )
2106 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2106 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2107 elif opts.get('amend'):
2107 elif opts.get('amend'):
2108 if (
2108 if (
2109 repo[b'.'].p1().branch() != branch
2109 repo[b'.'].p1().branch() != branch
2110 and repo[b'.'].p2().branch() != branch
2110 and repo[b'.'].p2().branch() != branch
2111 ):
2111 ):
2112 raise error.InputError(_(b'can only close branch heads'))
2112 raise error.InputError(_(b'can only close branch heads'))
2113
2113
2114 if opts.get('amend'):
2114 if opts.get('amend'):
2115 if ui.configbool(b'ui', b'commitsubrepos'):
2115 if ui.configbool(b'ui', b'commitsubrepos'):
2116 raise error.InputError(
2116 raise error.InputError(
2117 _(b'cannot amend with ui.commitsubrepos enabled')
2117 _(b'cannot amend with ui.commitsubrepos enabled')
2118 )
2118 )
2119
2119
2120 old = repo[b'.']
2120 old = repo[b'.']
2121 rewriteutil.precheck(repo, [old.rev()], b'amend')
2121 rewriteutil.precheck(repo, [old.rev()], b'amend')
2122
2122
2123 # Currently histedit gets confused if an amend happens while histedit
2123 # Currently histedit gets confused if an amend happens while histedit
2124 # is in progress. Since we have a checkunfinished command, we are
2124 # is in progress. Since we have a checkunfinished command, we are
2125 # temporarily honoring it.
2125 # temporarily honoring it.
2126 #
2126 #
2127 # Note: eventually this guard will be removed. Please do not expect
2127 # Note: eventually this guard will be removed. Please do not expect
2128 # this behavior to remain.
2128 # this behavior to remain.
2129 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2129 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2130 cmdutil.checkunfinished(repo)
2130 cmdutil.checkunfinished(repo)
2131
2131
2132 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2132 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2133 opts = pycompat.byteskwargs(opts)
2133 opts = pycompat.byteskwargs(opts)
2134 if node == old.node():
2134 if node == old.node():
2135 ui.status(_(b"nothing changed\n"))
2135 ui.status(_(b"nothing changed\n"))
2136 return 1
2136 return 1
2137 else:
2137 else:
2138
2138
2139 def commitfunc(ui, repo, message, match, opts):
2139 def commitfunc(ui, repo, message, match, opts):
2140 overrides = {}
2140 overrides = {}
2141 if opts.get(b'secret'):
2141 if opts.get(b'secret'):
2142 overrides[(b'phases', b'new-commit')] = b'secret'
2142 overrides[(b'phases', b'new-commit')] = b'secret'
2143
2143
2144 baseui = repo.baseui
2144 baseui = repo.baseui
2145 with baseui.configoverride(overrides, b'commit'):
2145 with baseui.configoverride(overrides, b'commit'):
2146 with ui.configoverride(overrides, b'commit'):
2146 with ui.configoverride(overrides, b'commit'):
2147 editform = cmdutil.mergeeditform(
2147 editform = cmdutil.mergeeditform(
2148 repo[None], b'commit.normal'
2148 repo[None], b'commit.normal'
2149 )
2149 )
2150 editor = cmdutil.getcommiteditor(
2150 editor = cmdutil.getcommiteditor(
2151 editform=editform, **pycompat.strkwargs(opts)
2151 editform=editform, **pycompat.strkwargs(opts)
2152 )
2152 )
2153 return repo.commit(
2153 return repo.commit(
2154 message,
2154 message,
2155 opts.get(b'user'),
2155 opts.get(b'user'),
2156 opts.get(b'date'),
2156 opts.get(b'date'),
2157 match,
2157 match,
2158 editor=editor,
2158 editor=editor,
2159 extra=extra,
2159 extra=extra,
2160 )
2160 )
2161
2161
2162 opts = pycompat.byteskwargs(opts)
2162 opts = pycompat.byteskwargs(opts)
2163 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2163 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2164
2164
2165 if not node:
2165 if not node:
2166 stat = cmdutil.postcommitstatus(repo, pats, opts)
2166 stat = cmdutil.postcommitstatus(repo, pats, opts)
2167 if stat.deleted:
2167 if stat.deleted:
2168 ui.status(
2168 ui.status(
2169 _(
2169 _(
2170 b"nothing changed (%d missing files, see "
2170 b"nothing changed (%d missing files, see "
2171 b"'hg status')\n"
2171 b"'hg status')\n"
2172 )
2172 )
2173 % len(stat.deleted)
2173 % len(stat.deleted)
2174 )
2174 )
2175 else:
2175 else:
2176 ui.status(_(b"nothing changed\n"))
2176 ui.status(_(b"nothing changed\n"))
2177 return 1
2177 return 1
2178
2178
2179 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2179 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2180
2180
2181 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2181 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2182 status(
2182 status(
2183 ui,
2183 ui,
2184 repo,
2184 repo,
2185 modified=True,
2185 modified=True,
2186 added=True,
2186 added=True,
2187 removed=True,
2187 removed=True,
2188 deleted=True,
2188 deleted=True,
2189 unknown=True,
2189 unknown=True,
2190 subrepos=opts.get(b'subrepos'),
2190 subrepos=opts.get(b'subrepos'),
2191 )
2191 )
2192
2192
2193
2193
2194 @command(
2194 @command(
2195 b'config|showconfig|debugconfig',
2195 b'config|showconfig|debugconfig',
2196 [
2196 [
2197 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2197 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2198 # This is experimental because we need
2198 # This is experimental because we need
2199 # * reasonable behavior around aliases,
2199 # * reasonable behavior around aliases,
2200 # * decide if we display [debug] [experimental] and [devel] section par
2200 # * decide if we display [debug] [experimental] and [devel] section par
2201 # default
2201 # default
2202 # * some way to display "generic" config entry (the one matching
2202 # * some way to display "generic" config entry (the one matching
2203 # regexp,
2203 # regexp,
2204 # * proper display of the different value type
2204 # * proper display of the different value type
2205 # * a better way to handle <DYNAMIC> values (and variable types),
2205 # * a better way to handle <DYNAMIC> values (and variable types),
2206 # * maybe some type information ?
2206 # * maybe some type information ?
2207 (
2207 (
2208 b'',
2208 b'',
2209 b'exp-all-known',
2209 b'exp-all-known',
2210 None,
2210 None,
2211 _(b'show all known config option (EXPERIMENTAL)'),
2211 _(b'show all known config option (EXPERIMENTAL)'),
2212 ),
2212 ),
2213 (b'e', b'edit', None, _(b'edit user config')),
2213 (b'e', b'edit', None, _(b'edit user config')),
2214 (b'l', b'local', None, _(b'edit repository config')),
2214 (b'l', b'local', None, _(b'edit repository config')),
2215 (b'', b'source', None, _(b'show source of configuration value')),
2215 (b'', b'source', None, _(b'show source of configuration value')),
2216 (
2216 (
2217 b'',
2217 b'',
2218 b'shared',
2218 b'shared',
2219 None,
2219 None,
2220 _(b'edit shared source repository config (EXPERIMENTAL)'),
2220 _(b'edit shared source repository config (EXPERIMENTAL)'),
2221 ),
2221 ),
2222 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2222 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2223 (b'g', b'global', None, _(b'edit global config')),
2223 (b'g', b'global', None, _(b'edit global config')),
2224 ]
2224 ]
2225 + formatteropts,
2225 + formatteropts,
2226 _(b'[-u] [NAME]...'),
2226 _(b'[-u] [NAME]...'),
2227 helpcategory=command.CATEGORY_HELP,
2227 helpcategory=command.CATEGORY_HELP,
2228 optionalrepo=True,
2228 optionalrepo=True,
2229 intents={INTENT_READONLY},
2229 intents={INTENT_READONLY},
2230 )
2230 )
2231 def config(ui, repo, *values, **opts):
2231 def config(ui, repo, *values, **opts):
2232 """show combined config settings from all hgrc files
2232 """show combined config settings from all hgrc files
2233
2233
2234 With no arguments, print names and values of all config items.
2234 With no arguments, print names and values of all config items.
2235
2235
2236 With one argument of the form section.name, print just the value
2236 With one argument of the form section.name, print just the value
2237 of that config item.
2237 of that config item.
2238
2238
2239 With multiple arguments, print names and values of all config
2239 With multiple arguments, print names and values of all config
2240 items with matching section names or section.names.
2240 items with matching section names or section.names.
2241
2241
2242 With --edit, start an editor on the user-level config file. With
2242 With --edit, start an editor on the user-level config file. With
2243 --global, edit the system-wide config file. With --local, edit the
2243 --global, edit the system-wide config file. With --local, edit the
2244 repository-level config file.
2244 repository-level config file.
2245
2245
2246 With --source, the source (filename and line number) is printed
2246 With --source, the source (filename and line number) is printed
2247 for each config item.
2247 for each config item.
2248
2248
2249 See :hg:`help config` for more information about config files.
2249 See :hg:`help config` for more information about config files.
2250
2250
2251 .. container:: verbose
2251 .. container:: verbose
2252
2252
2253 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2253 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2254 This file is not shared across shares when in share-safe mode.
2254 This file is not shared across shares when in share-safe mode.
2255
2255
2256 Template:
2256 Template:
2257
2257
2258 The following keywords are supported. See also :hg:`help templates`.
2258 The following keywords are supported. See also :hg:`help templates`.
2259
2259
2260 :name: String. Config name.
2260 :name: String. Config name.
2261 :source: String. Filename and line number where the item is defined.
2261 :source: String. Filename and line number where the item is defined.
2262 :value: String. Config value.
2262 :value: String. Config value.
2263
2263
2264 The --shared flag can be used to edit the config file of shared source
2264 The --shared flag can be used to edit the config file of shared source
2265 repository. It only works when you have shared using the experimental
2265 repository. It only works when you have shared using the experimental
2266 share safe feature.
2266 share safe feature.
2267
2267
2268 Returns 0 on success, 1 if NAME does not exist.
2268 Returns 0 on success, 1 if NAME does not exist.
2269
2269
2270 """
2270 """
2271
2271
2272 opts = pycompat.byteskwargs(opts)
2272 opts = pycompat.byteskwargs(opts)
2273 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2273 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2274 if any(opts.get(o) for o in editopts):
2274 if any(opts.get(o) for o in editopts):
2275 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2275 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2276 if opts.get(b'local'):
2276 if opts.get(b'local'):
2277 if not repo:
2277 if not repo:
2278 raise error.InputError(
2278 raise error.InputError(
2279 _(b"can't use --local outside a repository")
2279 _(b"can't use --local outside a repository")
2280 )
2280 )
2281 paths = [repo.vfs.join(b'hgrc')]
2281 paths = [repo.vfs.join(b'hgrc')]
2282 elif opts.get(b'global'):
2282 elif opts.get(b'global'):
2283 paths = rcutil.systemrcpath()
2283 paths = rcutil.systemrcpath()
2284 elif opts.get(b'shared'):
2284 elif opts.get(b'shared'):
2285 if not repo.shared():
2285 if not repo.shared():
2286 raise error.InputError(
2286 raise error.InputError(
2287 _(b"repository is not shared; can't use --shared")
2287 _(b"repository is not shared; can't use --shared")
2288 )
2288 )
2289 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2289 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2290 raise error.InputError(
2290 raise error.InputError(
2291 _(
2291 _(
2292 b"share safe feature not enabled; "
2292 b"share safe feature not enabled; "
2293 b"unable to edit shared source repository config"
2293 b"unable to edit shared source repository config"
2294 )
2294 )
2295 )
2295 )
2296 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2296 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2297 elif opts.get(b'non_shared'):
2297 elif opts.get(b'non_shared'):
2298 paths = [repo.vfs.join(b'hgrc-not-shared')]
2298 paths = [repo.vfs.join(b'hgrc-not-shared')]
2299 else:
2299 else:
2300 paths = rcutil.userrcpath()
2300 paths = rcutil.userrcpath()
2301
2301
2302 for f in paths:
2302 for f in paths:
2303 if os.path.exists(f):
2303 if os.path.exists(f):
2304 break
2304 break
2305 else:
2305 else:
2306 if opts.get(b'global'):
2306 if opts.get(b'global'):
2307 samplehgrc = uimod.samplehgrcs[b'global']
2307 samplehgrc = uimod.samplehgrcs[b'global']
2308 elif opts.get(b'local'):
2308 elif opts.get(b'local'):
2309 samplehgrc = uimod.samplehgrcs[b'local']
2309 samplehgrc = uimod.samplehgrcs[b'local']
2310 else:
2310 else:
2311 samplehgrc = uimod.samplehgrcs[b'user']
2311 samplehgrc = uimod.samplehgrcs[b'user']
2312
2312
2313 f = paths[0]
2313 f = paths[0]
2314 fp = open(f, b"wb")
2314 fp = open(f, b"wb")
2315 fp.write(util.tonativeeol(samplehgrc))
2315 fp.write(util.tonativeeol(samplehgrc))
2316 fp.close()
2316 fp.close()
2317
2317
2318 editor = ui.geteditor()
2318 editor = ui.geteditor()
2319 ui.system(
2319 ui.system(
2320 b"%s \"%s\"" % (editor, f),
2320 b"%s \"%s\"" % (editor, f),
2321 onerr=error.InputError,
2321 onerr=error.InputError,
2322 errprefix=_(b"edit failed"),
2322 errprefix=_(b"edit failed"),
2323 blockedtag=b'config_edit',
2323 blockedtag=b'config_edit',
2324 )
2324 )
2325 return
2325 return
2326 ui.pager(b'config')
2326 ui.pager(b'config')
2327 fm = ui.formatter(b'config', opts)
2327 fm = ui.formatter(b'config', opts)
2328 for t, f in rcutil.rccomponents():
2328 for t, f in rcutil.rccomponents():
2329 if t == b'path':
2329 if t == b'path':
2330 ui.debug(b'read config from: %s\n' % f)
2330 ui.debug(b'read config from: %s\n' % f)
2331 elif t == b'resource':
2331 elif t == b'resource':
2332 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2332 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2333 elif t == b'items':
2333 elif t == b'items':
2334 # Don't print anything for 'items'.
2334 # Don't print anything for 'items'.
2335 pass
2335 pass
2336 else:
2336 else:
2337 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2337 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2338 untrusted = bool(opts.get(b'untrusted'))
2338 untrusted = bool(opts.get(b'untrusted'))
2339
2339
2340 selsections = selentries = []
2340 selsections = selentries = []
2341 if values:
2341 if values:
2342 selsections = [v for v in values if b'.' not in v]
2342 selsections = [v for v in values if b'.' not in v]
2343 selentries = [v for v in values if b'.' in v]
2343 selentries = [v for v in values if b'.' in v]
2344 uniquesel = len(selentries) == 1 and not selsections
2344 uniquesel = len(selentries) == 1 and not selsections
2345 selsections = set(selsections)
2345 selsections = set(selsections)
2346 selentries = set(selentries)
2346 selentries = set(selentries)
2347
2347
2348 matched = False
2348 matched = False
2349 all_known = opts[b'exp_all_known']
2349 all_known = opts[b'exp_all_known']
2350 show_source = ui.debugflag or opts.get(b'source')
2350 show_source = ui.debugflag or opts.get(b'source')
2351 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2351 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2352 for section, name, value in entries:
2352 for section, name, value in entries:
2353 source = ui.configsource(section, name, untrusted)
2353 source = ui.configsource(section, name, untrusted)
2354 value = pycompat.bytestr(value)
2354 value = pycompat.bytestr(value)
2355 defaultvalue = ui.configdefault(section, name)
2355 defaultvalue = ui.configdefault(section, name)
2356 if fm.isplain():
2356 if fm.isplain():
2357 source = source or b'none'
2357 source = source or b'none'
2358 value = value.replace(b'\n', b'\\n')
2358 value = value.replace(b'\n', b'\\n')
2359 entryname = section + b'.' + name
2359 entryname = section + b'.' + name
2360 if values and not (section in selsections or entryname in selentries):
2360 if values and not (section in selsections or entryname in selentries):
2361 continue
2361 continue
2362 fm.startitem()
2362 fm.startitem()
2363 fm.condwrite(show_source, b'source', b'%s: ', source)
2363 fm.condwrite(show_source, b'source', b'%s: ', source)
2364 if uniquesel:
2364 if uniquesel:
2365 fm.data(name=entryname)
2365 fm.data(name=entryname)
2366 fm.write(b'value', b'%s\n', value)
2366 fm.write(b'value', b'%s\n', value)
2367 else:
2367 else:
2368 fm.write(b'name value', b'%s=%s\n', entryname, value)
2368 fm.write(b'name value', b'%s=%s\n', entryname, value)
2369 if formatter.isprintable(defaultvalue):
2369 if formatter.isprintable(defaultvalue):
2370 fm.data(defaultvalue=defaultvalue)
2370 fm.data(defaultvalue=defaultvalue)
2371 elif isinstance(defaultvalue, list) and all(
2371 elif isinstance(defaultvalue, list) and all(
2372 formatter.isprintable(e) for e in defaultvalue
2372 formatter.isprintable(e) for e in defaultvalue
2373 ):
2373 ):
2374 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2374 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2375 # TODO: no idea how to process unsupported defaultvalue types
2375 # TODO: no idea how to process unsupported defaultvalue types
2376 matched = True
2376 matched = True
2377 fm.end()
2377 fm.end()
2378 if matched:
2378 if matched:
2379 return 0
2379 return 0
2380 return 1
2380 return 1
2381
2381
2382
2382
2383 @command(
2383 @command(
2384 b'continue',
2384 b'continue',
2385 dryrunopts,
2385 dryrunopts,
2386 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2386 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2387 helpbasic=True,
2387 helpbasic=True,
2388 )
2388 )
2389 def continuecmd(ui, repo, **opts):
2389 def continuecmd(ui, repo, **opts):
2390 """resumes an interrupted operation (EXPERIMENTAL)
2390 """resumes an interrupted operation (EXPERIMENTAL)
2391
2391
2392 Finishes a multistep operation like graft, histedit, rebase, merge,
2392 Finishes a multistep operation like graft, histedit, rebase, merge,
2393 and unshelve if they are in an interrupted state.
2393 and unshelve if they are in an interrupted state.
2394
2394
2395 use --dry-run/-n to dry run the command.
2395 use --dry-run/-n to dry run the command.
2396 """
2396 """
2397 dryrun = opts.get('dry_run')
2397 dryrun = opts.get('dry_run')
2398 contstate = cmdutil.getunfinishedstate(repo)
2398 contstate = cmdutil.getunfinishedstate(repo)
2399 if not contstate:
2399 if not contstate:
2400 raise error.StateError(_(b'no operation in progress'))
2400 raise error.StateError(_(b'no operation in progress'))
2401 if not contstate.continuefunc:
2401 if not contstate.continuefunc:
2402 raise error.StateError(
2402 raise error.StateError(
2403 (
2403 (
2404 _(b"%s in progress but does not support 'hg continue'")
2404 _(b"%s in progress but does not support 'hg continue'")
2405 % (contstate._opname)
2405 % (contstate._opname)
2406 ),
2406 ),
2407 hint=contstate.continuemsg(),
2407 hint=contstate.continuemsg(),
2408 )
2408 )
2409 if dryrun:
2409 if dryrun:
2410 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2410 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2411 return
2411 return
2412 return contstate.continuefunc(ui, repo)
2412 return contstate.continuefunc(ui, repo)
2413
2413
2414
2414
2415 @command(
2415 @command(
2416 b'copy|cp',
2416 b'copy|cp',
2417 [
2417 [
2418 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2418 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2419 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2419 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2420 (
2420 (
2421 b'',
2421 b'',
2422 b'at-rev',
2422 b'at-rev',
2423 b'',
2423 b'',
2424 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2424 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2425 _(b'REV'),
2425 _(b'REV'),
2426 ),
2426 ),
2427 (
2427 (
2428 b'f',
2428 b'f',
2429 b'force',
2429 b'force',
2430 None,
2430 None,
2431 _(b'forcibly copy over an existing managed file'),
2431 _(b'forcibly copy over an existing managed file'),
2432 ),
2432 ),
2433 ]
2433 ]
2434 + walkopts
2434 + walkopts
2435 + dryrunopts,
2435 + dryrunopts,
2436 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2436 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2437 helpcategory=command.CATEGORY_FILE_CONTENTS,
2437 helpcategory=command.CATEGORY_FILE_CONTENTS,
2438 )
2438 )
2439 def copy(ui, repo, *pats, **opts):
2439 def copy(ui, repo, *pats, **opts):
2440 """mark files as copied for the next commit
2440 """mark files as copied for the next commit
2441
2441
2442 Mark dest as having copies of source files. If dest is a
2442 Mark dest as having copies of source files. If dest is a
2443 directory, copies are put in that directory. If dest is a file,
2443 directory, copies are put in that directory. If dest is a file,
2444 the source must be a single file.
2444 the source must be a single file.
2445
2445
2446 By default, this command copies the contents of files as they
2446 By default, this command copies the contents of files as they
2447 exist in the working directory. If invoked with -A/--after, the
2447 exist in the working directory. If invoked with -A/--after, the
2448 operation is recorded, but no copying is performed.
2448 operation is recorded, but no copying is performed.
2449
2449
2450 To undo marking a destination file as copied, use --forget. With that
2450 To undo marking a destination file as copied, use --forget. With that
2451 option, all given (positional) arguments are unmarked as copies. The
2451 option, all given (positional) arguments are unmarked as copies. The
2452 destination file(s) will be left in place (still tracked). Note that
2452 destination file(s) will be left in place (still tracked). Note that
2453 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2453 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2454
2454
2455 This command takes effect with the next commit by default.
2455 This command takes effect with the next commit by default.
2456
2456
2457 Returns 0 on success, 1 if errors are encountered.
2457 Returns 0 on success, 1 if errors are encountered.
2458 """
2458 """
2459 opts = pycompat.byteskwargs(opts)
2459 opts = pycompat.byteskwargs(opts)
2460 with repo.wlock():
2460 with repo.wlock():
2461 return cmdutil.copy(ui, repo, pats, opts)
2461 return cmdutil.copy(ui, repo, pats, opts)
2462
2462
2463
2463
2464 @command(
2464 @command(
2465 b'debugcommands',
2465 b'debugcommands',
2466 [],
2466 [],
2467 _(b'[COMMAND]'),
2467 _(b'[COMMAND]'),
2468 helpcategory=command.CATEGORY_HELP,
2468 helpcategory=command.CATEGORY_HELP,
2469 norepo=True,
2469 norepo=True,
2470 )
2470 )
2471 def debugcommands(ui, cmd=b'', *args):
2471 def debugcommands(ui, cmd=b'', *args):
2472 """list all available commands and options"""
2472 """list all available commands and options"""
2473 for cmd, vals in sorted(pycompat.iteritems(table)):
2473 for cmd, vals in sorted(pycompat.iteritems(table)):
2474 cmd = cmd.split(b'|')[0]
2474 cmd = cmd.split(b'|')[0]
2475 opts = b', '.join([i[1] for i in vals[1]])
2475 opts = b', '.join([i[1] for i in vals[1]])
2476 ui.write(b'%s: %s\n' % (cmd, opts))
2476 ui.write(b'%s: %s\n' % (cmd, opts))
2477
2477
2478
2478
2479 @command(
2479 @command(
2480 b'debugcomplete',
2480 b'debugcomplete',
2481 [(b'o', b'options', None, _(b'show the command options'))],
2481 [(b'o', b'options', None, _(b'show the command options'))],
2482 _(b'[-o] CMD'),
2482 _(b'[-o] CMD'),
2483 helpcategory=command.CATEGORY_HELP,
2483 helpcategory=command.CATEGORY_HELP,
2484 norepo=True,
2484 norepo=True,
2485 )
2485 )
2486 def debugcomplete(ui, cmd=b'', **opts):
2486 def debugcomplete(ui, cmd=b'', **opts):
2487 """returns the completion list associated with the given command"""
2487 """returns the completion list associated with the given command"""
2488
2488
2489 if opts.get('options'):
2489 if opts.get('options'):
2490 options = []
2490 options = []
2491 otables = [globalopts]
2491 otables = [globalopts]
2492 if cmd:
2492 if cmd:
2493 aliases, entry = cmdutil.findcmd(cmd, table, False)
2493 aliases, entry = cmdutil.findcmd(cmd, table, False)
2494 otables.append(entry[1])
2494 otables.append(entry[1])
2495 for t in otables:
2495 for t in otables:
2496 for o in t:
2496 for o in t:
2497 if b"(DEPRECATED)" in o[3]:
2497 if b"(DEPRECATED)" in o[3]:
2498 continue
2498 continue
2499 if o[0]:
2499 if o[0]:
2500 options.append(b'-%s' % o[0])
2500 options.append(b'-%s' % o[0])
2501 options.append(b'--%s' % o[1])
2501 options.append(b'--%s' % o[1])
2502 ui.write(b"%s\n" % b"\n".join(options))
2502 ui.write(b"%s\n" % b"\n".join(options))
2503 return
2503 return
2504
2504
2505 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2505 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2506 if ui.verbose:
2506 if ui.verbose:
2507 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2507 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2508 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2508 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2509
2509
2510
2510
2511 @command(
2511 @command(
2512 b'diff',
2512 b'diff',
2513 [
2513 [
2514 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2514 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2515 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2515 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2516 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2516 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2517 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2517 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2518 ]
2518 ]
2519 + diffopts
2519 + diffopts
2520 + diffopts2
2520 + diffopts2
2521 + walkopts
2521 + walkopts
2522 + subrepoopts,
2522 + subrepoopts,
2523 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2523 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2524 helpcategory=command.CATEGORY_FILE_CONTENTS,
2524 helpcategory=command.CATEGORY_FILE_CONTENTS,
2525 helpbasic=True,
2525 helpbasic=True,
2526 inferrepo=True,
2526 inferrepo=True,
2527 intents={INTENT_READONLY},
2527 intents={INTENT_READONLY},
2528 )
2528 )
2529 def diff(ui, repo, *pats, **opts):
2529 def diff(ui, repo, *pats, **opts):
2530 """diff repository (or selected files)
2530 """diff repository (or selected files)
2531
2531
2532 Show differences between revisions for the specified files.
2532 Show differences between revisions for the specified files.
2533
2533
2534 Differences between files are shown using the unified diff format.
2534 Differences between files are shown using the unified diff format.
2535
2535
2536 .. note::
2536 .. note::
2537
2537
2538 :hg:`diff` may generate unexpected results for merges, as it will
2538 :hg:`diff` may generate unexpected results for merges, as it will
2539 default to comparing against the working directory's first
2539 default to comparing against the working directory's first
2540 parent changeset if no revisions are specified.
2540 parent changeset if no revisions are specified.
2541
2541
2542 By default, the working directory files are compared to its first parent. To
2542 By default, the working directory files are compared to its first parent. To
2543 see the differences from another revision, use --from. To see the difference
2543 see the differences from another revision, use --from. To see the difference
2544 to another revision, use --to. For example, :hg:`diff --from .^` will show
2544 to another revision, use --to. For example, :hg:`diff --from .^` will show
2545 the differences from the working copy's grandparent to the working copy,
2545 the differences from the working copy's grandparent to the working copy,
2546 :hg:`diff --to .` will show the diff from the working copy to its parent
2546 :hg:`diff --to .` will show the diff from the working copy to its parent
2547 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2547 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2548 show the diff between those two revisions.
2548 show the diff between those two revisions.
2549
2549
2550 Alternatively you can specify -c/--change with a revision to see the changes
2550 Alternatively you can specify -c/--change with a revision to see the changes
2551 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2551 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2552 equivalent to :hg:`diff --from 42^ --to 42`)
2552 equivalent to :hg:`diff --from 42^ --to 42`)
2553
2553
2554 Without the -a/--text option, diff will avoid generating diffs of
2554 Without the -a/--text option, diff will avoid generating diffs of
2555 files it detects as binary. With -a, diff will generate a diff
2555 files it detects as binary. With -a, diff will generate a diff
2556 anyway, probably with undesirable results.
2556 anyway, probably with undesirable results.
2557
2557
2558 Use the -g/--git option to generate diffs in the git extended diff
2558 Use the -g/--git option to generate diffs in the git extended diff
2559 format. For more information, read :hg:`help diffs`.
2559 format. For more information, read :hg:`help diffs`.
2560
2560
2561 .. container:: verbose
2561 .. container:: verbose
2562
2562
2563 Examples:
2563 Examples:
2564
2564
2565 - compare a file in the current working directory to its parent::
2565 - compare a file in the current working directory to its parent::
2566
2566
2567 hg diff foo.c
2567 hg diff foo.c
2568
2568
2569 - compare two historical versions of a directory, with rename info::
2569 - compare two historical versions of a directory, with rename info::
2570
2570
2571 hg diff --git --from 1.0 --to 1.2 lib/
2571 hg diff --git --from 1.0 --to 1.2 lib/
2572
2572
2573 - get change stats relative to the last change on some date::
2573 - get change stats relative to the last change on some date::
2574
2574
2575 hg diff --stat --from "date('may 2')"
2575 hg diff --stat --from "date('may 2')"
2576
2576
2577 - diff all newly-added files that contain a keyword::
2577 - diff all newly-added files that contain a keyword::
2578
2578
2579 hg diff "set:added() and grep(GNU)"
2579 hg diff "set:added() and grep(GNU)"
2580
2580
2581 - compare a revision and its parents::
2581 - compare a revision and its parents::
2582
2582
2583 hg diff -c 9353 # compare against first parent
2583 hg diff -c 9353 # compare against first parent
2584 hg diff --from 9353^ --to 9353 # same using revset syntax
2584 hg diff --from 9353^ --to 9353 # same using revset syntax
2585 hg diff --from 9353^2 --to 9353 # compare against the second parent
2585 hg diff --from 9353^2 --to 9353 # compare against the second parent
2586
2586
2587 Returns 0 on success.
2587 Returns 0 on success.
2588 """
2588 """
2589
2589
2590 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2590 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2591 opts = pycompat.byteskwargs(opts)
2591 opts = pycompat.byteskwargs(opts)
2592 revs = opts.get(b'rev')
2592 revs = opts.get(b'rev')
2593 change = opts.get(b'change')
2593 change = opts.get(b'change')
2594 from_rev = opts.get(b'from')
2594 from_rev = opts.get(b'from')
2595 to_rev = opts.get(b'to')
2595 to_rev = opts.get(b'to')
2596 stat = opts.get(b'stat')
2596 stat = opts.get(b'stat')
2597 reverse = opts.get(b'reverse')
2597 reverse = opts.get(b'reverse')
2598
2598
2599 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2599 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2600 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2600 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2601 if change:
2601 if change:
2602 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2602 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2603 ctx2 = scmutil.revsingle(repo, change, None)
2603 ctx2 = scmutil.revsingle(repo, change, None)
2604 ctx1 = logcmdutil.diff_parent(ctx2)
2604 ctx1 = logcmdutil.diff_parent(ctx2)
2605 elif from_rev or to_rev:
2605 elif from_rev or to_rev:
2606 repo = scmutil.unhidehashlikerevs(
2606 repo = scmutil.unhidehashlikerevs(
2607 repo, [from_rev] + [to_rev], b'nowarn'
2607 repo, [from_rev] + [to_rev], b'nowarn'
2608 )
2608 )
2609 ctx1 = scmutil.revsingle(repo, from_rev, None)
2609 ctx1 = scmutil.revsingle(repo, from_rev, None)
2610 ctx2 = scmutil.revsingle(repo, to_rev, None)
2610 ctx2 = scmutil.revsingle(repo, to_rev, None)
2611 else:
2611 else:
2612 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2612 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2613 ctx1, ctx2 = scmutil.revpair(repo, revs)
2613 ctx1, ctx2 = scmutil.revpair(repo, revs)
2614
2614
2615 if reverse:
2615 if reverse:
2616 ctxleft = ctx2
2616 ctxleft = ctx2
2617 ctxright = ctx1
2617 ctxright = ctx1
2618 else:
2618 else:
2619 ctxleft = ctx1
2619 ctxleft = ctx1
2620 ctxright = ctx2
2620 ctxright = ctx2
2621
2621
2622 diffopts = patch.diffallopts(ui, opts)
2622 diffopts = patch.diffallopts(ui, opts)
2623 m = scmutil.match(ctx2, pats, opts)
2623 m = scmutil.match(ctx2, pats, opts)
2624 m = repo.narrowmatch(m)
2624 m = repo.narrowmatch(m)
2625 ui.pager(b'diff')
2625 ui.pager(b'diff')
2626 logcmdutil.diffordiffstat(
2626 logcmdutil.diffordiffstat(
2627 ui,
2627 ui,
2628 repo,
2628 repo,
2629 diffopts,
2629 diffopts,
2630 ctxleft,
2630 ctxleft,
2631 ctxright,
2631 ctxright,
2632 m,
2632 m,
2633 stat=stat,
2633 stat=stat,
2634 listsubrepos=opts.get(b'subrepos'),
2634 listsubrepos=opts.get(b'subrepos'),
2635 root=opts.get(b'root'),
2635 root=opts.get(b'root'),
2636 )
2636 )
2637
2637
2638
2638
2639 @command(
2639 @command(
2640 b'export',
2640 b'export',
2641 [
2641 [
2642 (
2642 (
2643 b'B',
2643 b'B',
2644 b'bookmark',
2644 b'bookmark',
2645 b'',
2645 b'',
2646 _(b'export changes only reachable by given bookmark'),
2646 _(b'export changes only reachable by given bookmark'),
2647 _(b'BOOKMARK'),
2647 _(b'BOOKMARK'),
2648 ),
2648 ),
2649 (
2649 (
2650 b'o',
2650 b'o',
2651 b'output',
2651 b'output',
2652 b'',
2652 b'',
2653 _(b'print output to file with formatted name'),
2653 _(b'print output to file with formatted name'),
2654 _(b'FORMAT'),
2654 _(b'FORMAT'),
2655 ),
2655 ),
2656 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2656 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2657 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2657 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2658 ]
2658 ]
2659 + diffopts
2659 + diffopts
2660 + formatteropts,
2660 + formatteropts,
2661 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2661 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2662 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2662 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2663 helpbasic=True,
2663 helpbasic=True,
2664 intents={INTENT_READONLY},
2664 intents={INTENT_READONLY},
2665 )
2665 )
2666 def export(ui, repo, *changesets, **opts):
2666 def export(ui, repo, *changesets, **opts):
2667 """dump the header and diffs for one or more changesets
2667 """dump the header and diffs for one or more changesets
2668
2668
2669 Print the changeset header and diffs for one or more revisions.
2669 Print the changeset header and diffs for one or more revisions.
2670 If no revision is given, the parent of the working directory is used.
2670 If no revision is given, the parent of the working directory is used.
2671
2671
2672 The information shown in the changeset header is: author, date,
2672 The information shown in the changeset header is: author, date,
2673 branch name (if non-default), changeset hash, parent(s) and commit
2673 branch name (if non-default), changeset hash, parent(s) and commit
2674 comment.
2674 comment.
2675
2675
2676 .. note::
2676 .. note::
2677
2677
2678 :hg:`export` may generate unexpected diff output for merge
2678 :hg:`export` may generate unexpected diff output for merge
2679 changesets, as it will compare the merge changeset against its
2679 changesets, as it will compare the merge changeset against its
2680 first parent only.
2680 first parent only.
2681
2681
2682 Output may be to a file, in which case the name of the file is
2682 Output may be to a file, in which case the name of the file is
2683 given using a template string. See :hg:`help templates`. In addition
2683 given using a template string. See :hg:`help templates`. In addition
2684 to the common template keywords, the following formatting rules are
2684 to the common template keywords, the following formatting rules are
2685 supported:
2685 supported:
2686
2686
2687 :``%%``: literal "%" character
2687 :``%%``: literal "%" character
2688 :``%H``: changeset hash (40 hexadecimal digits)
2688 :``%H``: changeset hash (40 hexadecimal digits)
2689 :``%N``: number of patches being generated
2689 :``%N``: number of patches being generated
2690 :``%R``: changeset revision number
2690 :``%R``: changeset revision number
2691 :``%b``: basename of the exporting repository
2691 :``%b``: basename of the exporting repository
2692 :``%h``: short-form changeset hash (12 hexadecimal digits)
2692 :``%h``: short-form changeset hash (12 hexadecimal digits)
2693 :``%m``: first line of the commit message (only alphanumeric characters)
2693 :``%m``: first line of the commit message (only alphanumeric characters)
2694 :``%n``: zero-padded sequence number, starting at 1
2694 :``%n``: zero-padded sequence number, starting at 1
2695 :``%r``: zero-padded changeset revision number
2695 :``%r``: zero-padded changeset revision number
2696 :``\\``: literal "\\" character
2696 :``\\``: literal "\\" character
2697
2697
2698 Without the -a/--text option, export will avoid generating diffs
2698 Without the -a/--text option, export will avoid generating diffs
2699 of files it detects as binary. With -a, export will generate a
2699 of files it detects as binary. With -a, export will generate a
2700 diff anyway, probably with undesirable results.
2700 diff anyway, probably with undesirable results.
2701
2701
2702 With -B/--bookmark changesets reachable by the given bookmark are
2702 With -B/--bookmark changesets reachable by the given bookmark are
2703 selected.
2703 selected.
2704
2704
2705 Use the -g/--git option to generate diffs in the git extended diff
2705 Use the -g/--git option to generate diffs in the git extended diff
2706 format. See :hg:`help diffs` for more information.
2706 format. See :hg:`help diffs` for more information.
2707
2707
2708 With the --switch-parent option, the diff will be against the
2708 With the --switch-parent option, the diff will be against the
2709 second parent. It can be useful to review a merge.
2709 second parent. It can be useful to review a merge.
2710
2710
2711 .. container:: verbose
2711 .. container:: verbose
2712
2712
2713 Template:
2713 Template:
2714
2714
2715 The following keywords are supported in addition to the common template
2715 The following keywords are supported in addition to the common template
2716 keywords and functions. See also :hg:`help templates`.
2716 keywords and functions. See also :hg:`help templates`.
2717
2717
2718 :diff: String. Diff content.
2718 :diff: String. Diff content.
2719 :parents: List of strings. Parent nodes of the changeset.
2719 :parents: List of strings. Parent nodes of the changeset.
2720
2720
2721 Examples:
2721 Examples:
2722
2722
2723 - use export and import to transplant a bugfix to the current
2723 - use export and import to transplant a bugfix to the current
2724 branch::
2724 branch::
2725
2725
2726 hg export -r 9353 | hg import -
2726 hg export -r 9353 | hg import -
2727
2727
2728 - export all the changesets between two revisions to a file with
2728 - export all the changesets between two revisions to a file with
2729 rename information::
2729 rename information::
2730
2730
2731 hg export --git -r 123:150 > changes.txt
2731 hg export --git -r 123:150 > changes.txt
2732
2732
2733 - split outgoing changes into a series of patches with
2733 - split outgoing changes into a series of patches with
2734 descriptive names::
2734 descriptive names::
2735
2735
2736 hg export -r "outgoing()" -o "%n-%m.patch"
2736 hg export -r "outgoing()" -o "%n-%m.patch"
2737
2737
2738 Returns 0 on success.
2738 Returns 0 on success.
2739 """
2739 """
2740 opts = pycompat.byteskwargs(opts)
2740 opts = pycompat.byteskwargs(opts)
2741 bookmark = opts.get(b'bookmark')
2741 bookmark = opts.get(b'bookmark')
2742 changesets += tuple(opts.get(b'rev', []))
2742 changesets += tuple(opts.get(b'rev', []))
2743
2743
2744 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2744 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2745
2745
2746 if bookmark:
2746 if bookmark:
2747 if bookmark not in repo._bookmarks:
2747 if bookmark not in repo._bookmarks:
2748 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2748 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2749
2749
2750 revs = scmutil.bookmarkrevs(repo, bookmark)
2750 revs = scmutil.bookmarkrevs(repo, bookmark)
2751 else:
2751 else:
2752 if not changesets:
2752 if not changesets:
2753 changesets = [b'.']
2753 changesets = [b'.']
2754
2754
2755 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2755 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2756 revs = scmutil.revrange(repo, changesets)
2756 revs = scmutil.revrange(repo, changesets)
2757
2757
2758 if not revs:
2758 if not revs:
2759 raise error.InputError(_(b"export requires at least one changeset"))
2759 raise error.InputError(_(b"export requires at least one changeset"))
2760 if len(revs) > 1:
2760 if len(revs) > 1:
2761 ui.note(_(b'exporting patches:\n'))
2761 ui.note(_(b'exporting patches:\n'))
2762 else:
2762 else:
2763 ui.note(_(b'exporting patch:\n'))
2763 ui.note(_(b'exporting patch:\n'))
2764
2764
2765 fntemplate = opts.get(b'output')
2765 fntemplate = opts.get(b'output')
2766 if cmdutil.isstdiofilename(fntemplate):
2766 if cmdutil.isstdiofilename(fntemplate):
2767 fntemplate = b''
2767 fntemplate = b''
2768
2768
2769 if fntemplate:
2769 if fntemplate:
2770 fm = formatter.nullformatter(ui, b'export', opts)
2770 fm = formatter.nullformatter(ui, b'export', opts)
2771 else:
2771 else:
2772 ui.pager(b'export')
2772 ui.pager(b'export')
2773 fm = ui.formatter(b'export', opts)
2773 fm = ui.formatter(b'export', opts)
2774 with fm:
2774 with fm:
2775 cmdutil.export(
2775 cmdutil.export(
2776 repo,
2776 repo,
2777 revs,
2777 revs,
2778 fm,
2778 fm,
2779 fntemplate=fntemplate,
2779 fntemplate=fntemplate,
2780 switch_parent=opts.get(b'switch_parent'),
2780 switch_parent=opts.get(b'switch_parent'),
2781 opts=patch.diffallopts(ui, opts),
2781 opts=patch.diffallopts(ui, opts),
2782 )
2782 )
2783
2783
2784
2784
2785 @command(
2785 @command(
2786 b'files',
2786 b'files',
2787 [
2787 [
2788 (
2788 (
2789 b'r',
2789 b'r',
2790 b'rev',
2790 b'rev',
2791 b'',
2791 b'',
2792 _(b'search the repository as it is in REV'),
2792 _(b'search the repository as it is in REV'),
2793 _(b'REV'),
2793 _(b'REV'),
2794 ),
2794 ),
2795 (
2795 (
2796 b'0',
2796 b'0',
2797 b'print0',
2797 b'print0',
2798 None,
2798 None,
2799 _(b'end filenames with NUL, for use with xargs'),
2799 _(b'end filenames with NUL, for use with xargs'),
2800 ),
2800 ),
2801 ]
2801 ]
2802 + walkopts
2802 + walkopts
2803 + formatteropts
2803 + formatteropts
2804 + subrepoopts,
2804 + subrepoopts,
2805 _(b'[OPTION]... [FILE]...'),
2805 _(b'[OPTION]... [FILE]...'),
2806 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2806 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2807 intents={INTENT_READONLY},
2807 intents={INTENT_READONLY},
2808 )
2808 )
2809 def files(ui, repo, *pats, **opts):
2809 def files(ui, repo, *pats, **opts):
2810 """list tracked files
2810 """list tracked files
2811
2811
2812 Print files under Mercurial control in the working directory or
2812 Print files under Mercurial control in the working directory or
2813 specified revision for given files (excluding removed files).
2813 specified revision for given files (excluding removed files).
2814 Files can be specified as filenames or filesets.
2814 Files can be specified as filenames or filesets.
2815
2815
2816 If no files are given to match, this command prints the names
2816 If no files are given to match, this command prints the names
2817 of all files under Mercurial control.
2817 of all files under Mercurial control.
2818
2818
2819 .. container:: verbose
2819 .. container:: verbose
2820
2820
2821 Template:
2821 Template:
2822
2822
2823 The following keywords are supported in addition to the common template
2823 The following keywords are supported in addition to the common template
2824 keywords and functions. See also :hg:`help templates`.
2824 keywords and functions. See also :hg:`help templates`.
2825
2825
2826 :flags: String. Character denoting file's symlink and executable bits.
2826 :flags: String. Character denoting file's symlink and executable bits.
2827 :path: String. Repository-absolute path of the file.
2827 :path: String. Repository-absolute path of the file.
2828 :size: Integer. Size of the file in bytes.
2828 :size: Integer. Size of the file in bytes.
2829
2829
2830 Examples:
2830 Examples:
2831
2831
2832 - list all files under the current directory::
2832 - list all files under the current directory::
2833
2833
2834 hg files .
2834 hg files .
2835
2835
2836 - shows sizes and flags for current revision::
2836 - shows sizes and flags for current revision::
2837
2837
2838 hg files -vr .
2838 hg files -vr .
2839
2839
2840 - list all files named README::
2840 - list all files named README::
2841
2841
2842 hg files -I "**/README"
2842 hg files -I "**/README"
2843
2843
2844 - list all binary files::
2844 - list all binary files::
2845
2845
2846 hg files "set:binary()"
2846 hg files "set:binary()"
2847
2847
2848 - find files containing a regular expression::
2848 - find files containing a regular expression::
2849
2849
2850 hg files "set:grep('bob')"
2850 hg files "set:grep('bob')"
2851
2851
2852 - search tracked file contents with xargs and grep::
2852 - search tracked file contents with xargs and grep::
2853
2853
2854 hg files -0 | xargs -0 grep foo
2854 hg files -0 | xargs -0 grep foo
2855
2855
2856 See :hg:`help patterns` and :hg:`help filesets` for more information
2856 See :hg:`help patterns` and :hg:`help filesets` for more information
2857 on specifying file patterns.
2857 on specifying file patterns.
2858
2858
2859 Returns 0 if a match is found, 1 otherwise.
2859 Returns 0 if a match is found, 1 otherwise.
2860
2860
2861 """
2861 """
2862
2862
2863 opts = pycompat.byteskwargs(opts)
2863 opts = pycompat.byteskwargs(opts)
2864 rev = opts.get(b'rev')
2864 rev = opts.get(b'rev')
2865 if rev:
2865 if rev:
2866 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2866 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2867 ctx = scmutil.revsingle(repo, rev, None)
2867 ctx = scmutil.revsingle(repo, rev, None)
2868
2868
2869 end = b'\n'
2869 end = b'\n'
2870 if opts.get(b'print0'):
2870 if opts.get(b'print0'):
2871 end = b'\0'
2871 end = b'\0'
2872 fmt = b'%s' + end
2872 fmt = b'%s' + end
2873
2873
2874 m = scmutil.match(ctx, pats, opts)
2874 m = scmutil.match(ctx, pats, opts)
2875 ui.pager(b'files')
2875 ui.pager(b'files')
2876 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2876 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2877 with ui.formatter(b'files', opts) as fm:
2877 with ui.formatter(b'files', opts) as fm:
2878 return cmdutil.files(
2878 return cmdutil.files(
2879 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2879 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2880 )
2880 )
2881
2881
2882
2882
2883 @command(
2883 @command(
2884 b'forget',
2884 b'forget',
2885 [
2885 [
2886 (b'i', b'interactive', None, _(b'use interactive mode')),
2886 (b'i', b'interactive', None, _(b'use interactive mode')),
2887 ]
2887 ]
2888 + walkopts
2888 + walkopts
2889 + dryrunopts,
2889 + dryrunopts,
2890 _(b'[OPTION]... FILE...'),
2890 _(b'[OPTION]... FILE...'),
2891 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2891 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2892 helpbasic=True,
2892 helpbasic=True,
2893 inferrepo=True,
2893 inferrepo=True,
2894 )
2894 )
2895 def forget(ui, repo, *pats, **opts):
2895 def forget(ui, repo, *pats, **opts):
2896 """forget the specified files on the next commit
2896 """forget the specified files on the next commit
2897
2897
2898 Mark the specified files so they will no longer be tracked
2898 Mark the specified files so they will no longer be tracked
2899 after the next commit.
2899 after the next commit.
2900
2900
2901 This only removes files from the current branch, not from the
2901 This only removes files from the current branch, not from the
2902 entire project history, and it does not delete them from the
2902 entire project history, and it does not delete them from the
2903 working directory.
2903 working directory.
2904
2904
2905 To delete the file from the working directory, see :hg:`remove`.
2905 To delete the file from the working directory, see :hg:`remove`.
2906
2906
2907 To undo a forget before the next commit, see :hg:`add`.
2907 To undo a forget before the next commit, see :hg:`add`.
2908
2908
2909 .. container:: verbose
2909 .. container:: verbose
2910
2910
2911 Examples:
2911 Examples:
2912
2912
2913 - forget newly-added binary files::
2913 - forget newly-added binary files::
2914
2914
2915 hg forget "set:added() and binary()"
2915 hg forget "set:added() and binary()"
2916
2916
2917 - forget files that would be excluded by .hgignore::
2917 - forget files that would be excluded by .hgignore::
2918
2918
2919 hg forget "set:hgignore()"
2919 hg forget "set:hgignore()"
2920
2920
2921 Returns 0 on success.
2921 Returns 0 on success.
2922 """
2922 """
2923
2923
2924 opts = pycompat.byteskwargs(opts)
2924 opts = pycompat.byteskwargs(opts)
2925 if not pats:
2925 if not pats:
2926 raise error.InputError(_(b'no files specified'))
2926 raise error.InputError(_(b'no files specified'))
2927
2927
2928 m = scmutil.match(repo[None], pats, opts)
2928 m = scmutil.match(repo[None], pats, opts)
2929 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2929 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2930 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2930 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2931 rejected = cmdutil.forget(
2931 rejected = cmdutil.forget(
2932 ui,
2932 ui,
2933 repo,
2933 repo,
2934 m,
2934 m,
2935 prefix=b"",
2935 prefix=b"",
2936 uipathfn=uipathfn,
2936 uipathfn=uipathfn,
2937 explicitonly=False,
2937 explicitonly=False,
2938 dryrun=dryrun,
2938 dryrun=dryrun,
2939 interactive=interactive,
2939 interactive=interactive,
2940 )[0]
2940 )[0]
2941 return rejected and 1 or 0
2941 return rejected and 1 or 0
2942
2942
2943
2943
2944 @command(
2944 @command(
2945 b'graft',
2945 b'graft',
2946 [
2946 [
2947 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2947 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2948 (
2948 (
2949 b'',
2949 b'',
2950 b'base',
2950 b'base',
2951 b'',
2951 b'',
2952 _(b'base revision when doing the graft merge (ADVANCED)'),
2952 _(b'base revision when doing the graft merge (ADVANCED)'),
2953 _(b'REV'),
2953 _(b'REV'),
2954 ),
2954 ),
2955 (b'c', b'continue', False, _(b'resume interrupted graft')),
2955 (b'c', b'continue', False, _(b'resume interrupted graft')),
2956 (b'', b'stop', False, _(b'stop interrupted graft')),
2956 (b'', b'stop', False, _(b'stop interrupted graft')),
2957 (b'', b'abort', False, _(b'abort interrupted graft')),
2957 (b'', b'abort', False, _(b'abort interrupted graft')),
2958 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2958 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2959 (b'', b'log', None, _(b'append graft info to log message')),
2959 (b'', b'log', None, _(b'append graft info to log message')),
2960 (
2960 (
2961 b'',
2961 b'',
2962 b'no-commit',
2962 b'no-commit',
2963 None,
2963 None,
2964 _(b"don't commit, just apply the changes in working directory"),
2964 _(b"don't commit, just apply the changes in working directory"),
2965 ),
2965 ),
2966 (b'f', b'force', False, _(b'force graft')),
2966 (b'f', b'force', False, _(b'force graft')),
2967 (
2967 (
2968 b'D',
2968 b'D',
2969 b'currentdate',
2969 b'currentdate',
2970 False,
2970 False,
2971 _(b'record the current date as commit date'),
2971 _(b'record the current date as commit date'),
2972 ),
2972 ),
2973 (
2973 (
2974 b'U',
2974 b'U',
2975 b'currentuser',
2975 b'currentuser',
2976 False,
2976 False,
2977 _(b'record the current user as committer'),
2977 _(b'record the current user as committer'),
2978 ),
2978 ),
2979 ]
2979 ]
2980 + commitopts2
2980 + commitopts2
2981 + mergetoolopts
2981 + mergetoolopts
2982 + dryrunopts,
2982 + dryrunopts,
2983 _(b'[OPTION]... [-r REV]... REV...'),
2983 _(b'[OPTION]... [-r REV]... REV...'),
2984 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2984 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2985 )
2985 )
2986 def graft(ui, repo, *revs, **opts):
2986 def graft(ui, repo, *revs, **opts):
2987 """copy changes from other branches onto the current branch
2987 """copy changes from other branches onto the current branch
2988
2988
2989 This command uses Mercurial's merge logic to copy individual
2989 This command uses Mercurial's merge logic to copy individual
2990 changes from other branches without merging branches in the
2990 changes from other branches without merging branches in the
2991 history graph. This is sometimes known as 'backporting' or
2991 history graph. This is sometimes known as 'backporting' or
2992 'cherry-picking'. By default, graft will copy user, date, and
2992 'cherry-picking'. By default, graft will copy user, date, and
2993 description from the source changesets.
2993 description from the source changesets.
2994
2994
2995 Changesets that are ancestors of the current revision, that have
2995 Changesets that are ancestors of the current revision, that have
2996 already been grafted, or that are merges will be skipped.
2996 already been grafted, or that are merges will be skipped.
2997
2997
2998 If --log is specified, log messages will have a comment appended
2998 If --log is specified, log messages will have a comment appended
2999 of the form::
2999 of the form::
3000
3000
3001 (grafted from CHANGESETHASH)
3001 (grafted from CHANGESETHASH)
3002
3002
3003 If --force is specified, revisions will be grafted even if they
3003 If --force is specified, revisions will be grafted even if they
3004 are already ancestors of, or have been grafted to, the destination.
3004 are already ancestors of, or have been grafted to, the destination.
3005 This is useful when the revisions have since been backed out.
3005 This is useful when the revisions have since been backed out.
3006
3006
3007 If a graft merge results in conflicts, the graft process is
3007 If a graft merge results in conflicts, the graft process is
3008 interrupted so that the current merge can be manually resolved.
3008 interrupted so that the current merge can be manually resolved.
3009 Once all conflicts are addressed, the graft process can be
3009 Once all conflicts are addressed, the graft process can be
3010 continued with the -c/--continue option.
3010 continued with the -c/--continue option.
3011
3011
3012 The -c/--continue option reapplies all the earlier options.
3012 The -c/--continue option reapplies all the earlier options.
3013
3013
3014 .. container:: verbose
3014 .. container:: verbose
3015
3015
3016 The --base option exposes more of how graft internally uses merge with a
3016 The --base option exposes more of how graft internally uses merge with a
3017 custom base revision. --base can be used to specify another ancestor than
3017 custom base revision. --base can be used to specify another ancestor than
3018 the first and only parent.
3018 the first and only parent.
3019
3019
3020 The command::
3020 The command::
3021
3021
3022 hg graft -r 345 --base 234
3022 hg graft -r 345 --base 234
3023
3023
3024 is thus pretty much the same as::
3024 is thus pretty much the same as::
3025
3025
3026 hg diff --from 234 --to 345 | hg import
3026 hg diff --from 234 --to 345 | hg import
3027
3027
3028 but using merge to resolve conflicts and track moved files.
3028 but using merge to resolve conflicts and track moved files.
3029
3029
3030 The result of a merge can thus be backported as a single commit by
3030 The result of a merge can thus be backported as a single commit by
3031 specifying one of the merge parents as base, and thus effectively
3031 specifying one of the merge parents as base, and thus effectively
3032 grafting the changes from the other side.
3032 grafting the changes from the other side.
3033
3033
3034 It is also possible to collapse multiple changesets and clean up history
3034 It is also possible to collapse multiple changesets and clean up history
3035 by specifying another ancestor as base, much like rebase --collapse
3035 by specifying another ancestor as base, much like rebase --collapse
3036 --keep.
3036 --keep.
3037
3037
3038 The commit message can be tweaked after the fact using commit --amend .
3038 The commit message can be tweaked after the fact using commit --amend .
3039
3039
3040 For using non-ancestors as the base to backout changes, see the backout
3040 For using non-ancestors as the base to backout changes, see the backout
3041 command and the hidden --parent option.
3041 command and the hidden --parent option.
3042
3042
3043 .. container:: verbose
3043 .. container:: verbose
3044
3044
3045 Examples:
3045 Examples:
3046
3046
3047 - copy a single change to the stable branch and edit its description::
3047 - copy a single change to the stable branch and edit its description::
3048
3048
3049 hg update stable
3049 hg update stable
3050 hg graft --edit 9393
3050 hg graft --edit 9393
3051
3051
3052 - graft a range of changesets with one exception, updating dates::
3052 - graft a range of changesets with one exception, updating dates::
3053
3053
3054 hg graft -D "2085::2093 and not 2091"
3054 hg graft -D "2085::2093 and not 2091"
3055
3055
3056 - continue a graft after resolving conflicts::
3056 - continue a graft after resolving conflicts::
3057
3057
3058 hg graft -c
3058 hg graft -c
3059
3059
3060 - show the source of a grafted changeset::
3060 - show the source of a grafted changeset::
3061
3061
3062 hg log --debug -r .
3062 hg log --debug -r .
3063
3063
3064 - show revisions sorted by date::
3064 - show revisions sorted by date::
3065
3065
3066 hg log -r "sort(all(), date)"
3066 hg log -r "sort(all(), date)"
3067
3067
3068 - backport the result of a merge as a single commit::
3068 - backport the result of a merge as a single commit::
3069
3069
3070 hg graft -r 123 --base 123^
3070 hg graft -r 123 --base 123^
3071
3071
3072 - land a feature branch as one changeset::
3072 - land a feature branch as one changeset::
3073
3073
3074 hg up -cr default
3074 hg up -cr default
3075 hg graft -r featureX --base "ancestor('featureX', 'default')"
3075 hg graft -r featureX --base "ancestor('featureX', 'default')"
3076
3076
3077 See :hg:`help revisions` for more about specifying revisions.
3077 See :hg:`help revisions` for more about specifying revisions.
3078
3078
3079 Returns 0 on successful completion, 1 if there are unresolved files.
3079 Returns 0 on successful completion, 1 if there are unresolved files.
3080 """
3080 """
3081 with repo.wlock():
3081 with repo.wlock():
3082 return _dograft(ui, repo, *revs, **opts)
3082 return _dograft(ui, repo, *revs, **opts)
3083
3083
3084
3084
3085 def _dograft(ui, repo, *revs, **opts):
3085 def _dograft(ui, repo, *revs, **opts):
3086 if revs and opts.get('rev'):
3086 if revs and opts.get('rev'):
3087 ui.warn(
3087 ui.warn(
3088 _(
3088 _(
3089 b'warning: inconsistent use of --rev might give unexpected '
3089 b'warning: inconsistent use of --rev might give unexpected '
3090 b'revision ordering!\n'
3090 b'revision ordering!\n'
3091 )
3091 )
3092 )
3092 )
3093
3093
3094 revs = list(revs)
3094 revs = list(revs)
3095 revs.extend(opts.get('rev'))
3095 revs.extend(opts.get('rev'))
3096 # a dict of data to be stored in state file
3096 # a dict of data to be stored in state file
3097 statedata = {}
3097 statedata = {}
3098 # list of new nodes created by ongoing graft
3098 # list of new nodes created by ongoing graft
3099 statedata[b'newnodes'] = []
3099 statedata[b'newnodes'] = []
3100
3100
3101 cmdutil.resolve_commit_options(ui, opts)
3101 cmdutil.resolve_commit_options(ui, opts)
3102
3102
3103 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3103 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3104
3104
3105 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3105 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3106
3106
3107 cont = False
3107 cont = False
3108 if opts.get('no_commit'):
3108 if opts.get('no_commit'):
3109 cmdutil.check_incompatible_arguments(
3109 cmdutil.check_incompatible_arguments(
3110 opts,
3110 opts,
3111 'no_commit',
3111 'no_commit',
3112 ['edit', 'currentuser', 'currentdate', 'log'],
3112 ['edit', 'currentuser', 'currentdate', 'log'],
3113 )
3113 )
3114
3114
3115 graftstate = statemod.cmdstate(repo, b'graftstate')
3115 graftstate = statemod.cmdstate(repo, b'graftstate')
3116
3116
3117 if opts.get('stop'):
3117 if opts.get('stop'):
3118 cmdutil.check_incompatible_arguments(
3118 cmdutil.check_incompatible_arguments(
3119 opts,
3119 opts,
3120 'stop',
3120 'stop',
3121 [
3121 [
3122 'edit',
3122 'edit',
3123 'log',
3123 'log',
3124 'user',
3124 'user',
3125 'date',
3125 'date',
3126 'currentdate',
3126 'currentdate',
3127 'currentuser',
3127 'currentuser',
3128 'rev',
3128 'rev',
3129 ],
3129 ],
3130 )
3130 )
3131 return _stopgraft(ui, repo, graftstate)
3131 return _stopgraft(ui, repo, graftstate)
3132 elif opts.get('abort'):
3132 elif opts.get('abort'):
3133 cmdutil.check_incompatible_arguments(
3133 cmdutil.check_incompatible_arguments(
3134 opts,
3134 opts,
3135 'abort',
3135 'abort',
3136 [
3136 [
3137 'edit',
3137 'edit',
3138 'log',
3138 'log',
3139 'user',
3139 'user',
3140 'date',
3140 'date',
3141 'currentdate',
3141 'currentdate',
3142 'currentuser',
3142 'currentuser',
3143 'rev',
3143 'rev',
3144 ],
3144 ],
3145 )
3145 )
3146 return cmdutil.abortgraft(ui, repo, graftstate)
3146 return cmdutil.abortgraft(ui, repo, graftstate)
3147 elif opts.get('continue'):
3147 elif opts.get('continue'):
3148 cont = True
3148 cont = True
3149 if revs:
3149 if revs:
3150 raise error.InputError(_(b"can't specify --continue and revisions"))
3150 raise error.InputError(_(b"can't specify --continue and revisions"))
3151 # read in unfinished revisions
3151 # read in unfinished revisions
3152 if graftstate.exists():
3152 if graftstate.exists():
3153 statedata = cmdutil.readgraftstate(repo, graftstate)
3153 statedata = cmdutil.readgraftstate(repo, graftstate)
3154 if statedata.get(b'date'):
3154 if statedata.get(b'date'):
3155 opts['date'] = statedata[b'date']
3155 opts['date'] = statedata[b'date']
3156 if statedata.get(b'user'):
3156 if statedata.get(b'user'):
3157 opts['user'] = statedata[b'user']
3157 opts['user'] = statedata[b'user']
3158 if statedata.get(b'log'):
3158 if statedata.get(b'log'):
3159 opts['log'] = True
3159 opts['log'] = True
3160 if statedata.get(b'no_commit'):
3160 if statedata.get(b'no_commit'):
3161 opts['no_commit'] = statedata.get(b'no_commit')
3161 opts['no_commit'] = statedata.get(b'no_commit')
3162 if statedata.get(b'base'):
3162 if statedata.get(b'base'):
3163 opts['base'] = statedata.get(b'base')
3163 opts['base'] = statedata.get(b'base')
3164 nodes = statedata[b'nodes']
3164 nodes = statedata[b'nodes']
3165 revs = [repo[node].rev() for node in nodes]
3165 revs = [repo[node].rev() for node in nodes]
3166 else:
3166 else:
3167 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3167 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3168 else:
3168 else:
3169 if not revs:
3169 if not revs:
3170 raise error.InputError(_(b'no revisions specified'))
3170 raise error.InputError(_(b'no revisions specified'))
3171 cmdutil.checkunfinished(repo)
3171 cmdutil.checkunfinished(repo)
3172 cmdutil.bailifchanged(repo)
3172 cmdutil.bailifchanged(repo)
3173 revs = scmutil.revrange(repo, revs)
3173 revs = scmutil.revrange(repo, revs)
3174
3174
3175 skipped = set()
3175 skipped = set()
3176 basectx = None
3176 basectx = None
3177 if opts.get('base'):
3177 if opts.get('base'):
3178 basectx = scmutil.revsingle(repo, opts['base'], None)
3178 basectx = scmutil.revsingle(repo, opts['base'], None)
3179 if basectx is None:
3179 if basectx is None:
3180 # check for merges
3180 # check for merges
3181 for rev in repo.revs(b'%ld and merge()', revs):
3181 for rev in repo.revs(b'%ld and merge()', revs):
3182 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3182 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3183 skipped.add(rev)
3183 skipped.add(rev)
3184 revs = [r for r in revs if r not in skipped]
3184 revs = [r for r in revs if r not in skipped]
3185 if not revs:
3185 if not revs:
3186 return -1
3186 return -1
3187 if basectx is not None and len(revs) != 1:
3187 if basectx is not None and len(revs) != 1:
3188 raise error.InputError(_(b'only one revision allowed with --base '))
3188 raise error.InputError(_(b'only one revision allowed with --base '))
3189
3189
3190 # Don't check in the --continue case, in effect retaining --force across
3190 # Don't check in the --continue case, in effect retaining --force across
3191 # --continues. That's because without --force, any revisions we decided to
3191 # --continues. That's because without --force, any revisions we decided to
3192 # skip would have been filtered out here, so they wouldn't have made their
3192 # skip would have been filtered out here, so they wouldn't have made their
3193 # way to the graftstate. With --force, any revisions we would have otherwise
3193 # way to the graftstate. With --force, any revisions we would have otherwise
3194 # skipped would not have been filtered out, and if they hadn't been applied
3194 # skipped would not have been filtered out, and if they hadn't been applied
3195 # already, they'd have been in the graftstate.
3195 # already, they'd have been in the graftstate.
3196 if not (cont or opts.get('force')) and basectx is None:
3196 if not (cont or opts.get('force')) and basectx is None:
3197 # check for ancestors of dest branch
3197 # check for ancestors of dest branch
3198 ancestors = repo.revs(b'%ld & (::.)', revs)
3198 ancestors = repo.revs(b'%ld & (::.)', revs)
3199 for rev in ancestors:
3199 for rev in ancestors:
3200 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3200 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3201
3201
3202 revs = [r for r in revs if r not in ancestors]
3202 revs = [r for r in revs if r not in ancestors]
3203
3203
3204 if not revs:
3204 if not revs:
3205 return -1
3205 return -1
3206
3206
3207 # analyze revs for earlier grafts
3207 # analyze revs for earlier grafts
3208 ids = {}
3208 ids = {}
3209 for ctx in repo.set(b"%ld", revs):
3209 for ctx in repo.set(b"%ld", revs):
3210 ids[ctx.hex()] = ctx.rev()
3210 ids[ctx.hex()] = ctx.rev()
3211 n = ctx.extra().get(b'source')
3211 n = ctx.extra().get(b'source')
3212 if n:
3212 if n:
3213 ids[n] = ctx.rev()
3213 ids[n] = ctx.rev()
3214
3214
3215 # check ancestors for earlier grafts
3215 # check ancestors for earlier grafts
3216 ui.debug(b'scanning for duplicate grafts\n')
3216 ui.debug(b'scanning for duplicate grafts\n')
3217
3217
3218 # The only changesets we can be sure doesn't contain grafts of any
3218 # The only changesets we can be sure doesn't contain grafts of any
3219 # revs, are the ones that are common ancestors of *all* revs:
3219 # revs, are the ones that are common ancestors of *all* revs:
3220 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3220 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3221 ctx = repo[rev]
3221 ctx = repo[rev]
3222 n = ctx.extra().get(b'source')
3222 n = ctx.extra().get(b'source')
3223 if n in ids:
3223 if n in ids:
3224 try:
3224 try:
3225 r = repo[n].rev()
3225 r = repo[n].rev()
3226 except error.RepoLookupError:
3226 except error.RepoLookupError:
3227 r = None
3227 r = None
3228 if r in revs:
3228 if r in revs:
3229 ui.warn(
3229 ui.warn(
3230 _(
3230 _(
3231 b'skipping revision %d:%s '
3231 b'skipping revision %d:%s '
3232 b'(already grafted to %d:%s)\n'
3232 b'(already grafted to %d:%s)\n'
3233 )
3233 )
3234 % (r, repo[r], rev, ctx)
3234 % (r, repo[r], rev, ctx)
3235 )
3235 )
3236 revs.remove(r)
3236 revs.remove(r)
3237 elif ids[n] in revs:
3237 elif ids[n] in revs:
3238 if r is None:
3238 if r is None:
3239 ui.warn(
3239 ui.warn(
3240 _(
3240 _(
3241 b'skipping already grafted revision %d:%s '
3241 b'skipping already grafted revision %d:%s '
3242 b'(%d:%s also has unknown origin %s)\n'
3242 b'(%d:%s also has unknown origin %s)\n'
3243 )
3243 )
3244 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3244 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3245 )
3245 )
3246 else:
3246 else:
3247 ui.warn(
3247 ui.warn(
3248 _(
3248 _(
3249 b'skipping already grafted revision %d:%s '
3249 b'skipping already grafted revision %d:%s '
3250 b'(%d:%s also has origin %d:%s)\n'
3250 b'(%d:%s also has origin %d:%s)\n'
3251 )
3251 )
3252 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3252 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3253 )
3253 )
3254 revs.remove(ids[n])
3254 revs.remove(ids[n])
3255 elif ctx.hex() in ids:
3255 elif ctx.hex() in ids:
3256 r = ids[ctx.hex()]
3256 r = ids[ctx.hex()]
3257 if r in revs:
3257 if r in revs:
3258 ui.warn(
3258 ui.warn(
3259 _(
3259 _(
3260 b'skipping already grafted revision %d:%s '
3260 b'skipping already grafted revision %d:%s '
3261 b'(was grafted from %d:%s)\n'
3261 b'(was grafted from %d:%s)\n'
3262 )
3262 )
3263 % (r, repo[r], rev, ctx)
3263 % (r, repo[r], rev, ctx)
3264 )
3264 )
3265 revs.remove(r)
3265 revs.remove(r)
3266 if not revs:
3266 if not revs:
3267 return -1
3267 return -1
3268
3268
3269 if opts.get('no_commit'):
3269 if opts.get('no_commit'):
3270 statedata[b'no_commit'] = True
3270 statedata[b'no_commit'] = True
3271 if opts.get('base'):
3271 if opts.get('base'):
3272 statedata[b'base'] = opts['base']
3272 statedata[b'base'] = opts['base']
3273 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3273 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3274 desc = b'%d:%s "%s"' % (
3274 desc = b'%d:%s "%s"' % (
3275 ctx.rev(),
3275 ctx.rev(),
3276 ctx,
3276 ctx,
3277 ctx.description().split(b'\n', 1)[0],
3277 ctx.description().split(b'\n', 1)[0],
3278 )
3278 )
3279 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3279 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3280 if names:
3280 if names:
3281 desc += b' (%s)' % b' '.join(names)
3281 desc += b' (%s)' % b' '.join(names)
3282 ui.status(_(b'grafting %s\n') % desc)
3282 ui.status(_(b'grafting %s\n') % desc)
3283 if opts.get('dry_run'):
3283 if opts.get('dry_run'):
3284 continue
3284 continue
3285
3285
3286 source = ctx.extra().get(b'source')
3286 source = ctx.extra().get(b'source')
3287 extra = {}
3287 extra = {}
3288 if source:
3288 if source:
3289 extra[b'source'] = source
3289 extra[b'source'] = source
3290 extra[b'intermediate-source'] = ctx.hex()
3290 extra[b'intermediate-source'] = ctx.hex()
3291 else:
3291 else:
3292 extra[b'source'] = ctx.hex()
3292 extra[b'source'] = ctx.hex()
3293 user = ctx.user()
3293 user = ctx.user()
3294 if opts.get('user'):
3294 if opts.get('user'):
3295 user = opts['user']
3295 user = opts['user']
3296 statedata[b'user'] = user
3296 statedata[b'user'] = user
3297 date = ctx.date()
3297 date = ctx.date()
3298 if opts.get('date'):
3298 if opts.get('date'):
3299 date = opts['date']
3299 date = opts['date']
3300 statedata[b'date'] = date
3300 statedata[b'date'] = date
3301 message = ctx.description()
3301 message = ctx.description()
3302 if opts.get('log'):
3302 if opts.get('log'):
3303 message += b'\n(grafted from %s)' % ctx.hex()
3303 message += b'\n(grafted from %s)' % ctx.hex()
3304 statedata[b'log'] = True
3304 statedata[b'log'] = True
3305
3305
3306 # we don't merge the first commit when continuing
3306 # we don't merge the first commit when continuing
3307 if not cont:
3307 if not cont:
3308 # perform the graft merge with p1(rev) as 'ancestor'
3308 # perform the graft merge with p1(rev) as 'ancestor'
3309 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3309 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3310 base = ctx.p1() if basectx is None else basectx
3310 base = ctx.p1() if basectx is None else basectx
3311 with ui.configoverride(overrides, b'graft'):
3311 with ui.configoverride(overrides, b'graft'):
3312 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3312 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3313 # report any conflicts
3313 # report any conflicts
3314 if stats.unresolvedcount > 0:
3314 if stats.unresolvedcount > 0:
3315 # write out state for --continue
3315 # write out state for --continue
3316 nodes = [repo[rev].hex() for rev in revs[pos:]]
3316 nodes = [repo[rev].hex() for rev in revs[pos:]]
3317 statedata[b'nodes'] = nodes
3317 statedata[b'nodes'] = nodes
3318 stateversion = 1
3318 stateversion = 1
3319 graftstate.save(stateversion, statedata)
3319 graftstate.save(stateversion, statedata)
3320 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3320 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3321 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3321 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3322 return 1
3322 return 1
3323 else:
3323 else:
3324 cont = False
3324 cont = False
3325
3325
3326 # commit if --no-commit is false
3326 # commit if --no-commit is false
3327 if not opts.get('no_commit'):
3327 if not opts.get('no_commit'):
3328 node = repo.commit(
3328 node = repo.commit(
3329 text=message, user=user, date=date, extra=extra, editor=editor
3329 text=message, user=user, date=date, extra=extra, editor=editor
3330 )
3330 )
3331 if node is None:
3331 if node is None:
3332 ui.warn(
3332 ui.warn(
3333 _(b'note: graft of %d:%s created no changes to commit\n')
3333 _(b'note: graft of %d:%s created no changes to commit\n')
3334 % (ctx.rev(), ctx)
3334 % (ctx.rev(), ctx)
3335 )
3335 )
3336 # checking that newnodes exist because old state files won't have it
3336 # checking that newnodes exist because old state files won't have it
3337 elif statedata.get(b'newnodes') is not None:
3337 elif statedata.get(b'newnodes') is not None:
3338 nn = statedata[b'newnodes']
3338 nn = statedata[b'newnodes']
3339 assert isinstance(nn, list) # list of bytes
3339 assert isinstance(nn, list) # list of bytes
3340 nn.append(node)
3340 nn.append(node)
3341
3341
3342 # remove state when we complete successfully
3342 # remove state when we complete successfully
3343 if not opts.get('dry_run'):
3343 if not opts.get('dry_run'):
3344 graftstate.delete()
3344 graftstate.delete()
3345
3345
3346 return 0
3346 return 0
3347
3347
3348
3348
3349 def _stopgraft(ui, repo, graftstate):
3349 def _stopgraft(ui, repo, graftstate):
3350 """stop the interrupted graft"""
3350 """stop the interrupted graft"""
3351 if not graftstate.exists():
3351 if not graftstate.exists():
3352 raise error.StateError(_(b"no interrupted graft found"))
3352 raise error.StateError(_(b"no interrupted graft found"))
3353 pctx = repo[b'.']
3353 pctx = repo[b'.']
3354 mergemod.clean_update(pctx)
3354 mergemod.clean_update(pctx)
3355 graftstate.delete()
3355 graftstate.delete()
3356 ui.status(_(b"stopped the interrupted graft\n"))
3356 ui.status(_(b"stopped the interrupted graft\n"))
3357 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3357 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3358 return 0
3358 return 0
3359
3359
3360
3360
3361 statemod.addunfinished(
3361 statemod.addunfinished(
3362 b'graft',
3362 b'graft',
3363 fname=b'graftstate',
3363 fname=b'graftstate',
3364 clearable=True,
3364 clearable=True,
3365 stopflag=True,
3365 stopflag=True,
3366 continueflag=True,
3366 continueflag=True,
3367 abortfunc=cmdutil.hgabortgraft,
3367 abortfunc=cmdutil.hgabortgraft,
3368 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3368 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3369 )
3369 )
3370
3370
3371
3371
3372 @command(
3372 @command(
3373 b'grep',
3373 b'grep',
3374 [
3374 [
3375 (b'0', b'print0', None, _(b'end fields with NUL')),
3375 (b'0', b'print0', None, _(b'end fields with NUL')),
3376 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3376 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3377 (
3377 (
3378 b'',
3378 b'',
3379 b'diff',
3379 b'diff',
3380 None,
3380 None,
3381 _(
3381 _(
3382 b'search revision differences for when the pattern was added '
3382 b'search revision differences for when the pattern was added '
3383 b'or removed'
3383 b'or removed'
3384 ),
3384 ),
3385 ),
3385 ),
3386 (b'a', b'text', None, _(b'treat all files as text')),
3386 (b'a', b'text', None, _(b'treat all files as text')),
3387 (
3387 (
3388 b'f',
3388 b'f',
3389 b'follow',
3389 b'follow',
3390 None,
3390 None,
3391 _(
3391 _(
3392 b'follow changeset history,'
3392 b'follow changeset history,'
3393 b' or file history across copies and renames'
3393 b' or file history across copies and renames'
3394 ),
3394 ),
3395 ),
3395 ),
3396 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3396 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3397 (
3397 (
3398 b'l',
3398 b'l',
3399 b'files-with-matches',
3399 b'files-with-matches',
3400 None,
3400 None,
3401 _(b'print only filenames and revisions that match'),
3401 _(b'print only filenames and revisions that match'),
3402 ),
3402 ),
3403 (b'n', b'line-number', None, _(b'print matching line numbers')),
3403 (b'n', b'line-number', None, _(b'print matching line numbers')),
3404 (
3404 (
3405 b'r',
3405 b'r',
3406 b'rev',
3406 b'rev',
3407 [],
3407 [],
3408 _(b'search files changed within revision range'),
3408 _(b'search files changed within revision range'),
3409 _(b'REV'),
3409 _(b'REV'),
3410 ),
3410 ),
3411 (
3411 (
3412 b'',
3412 b'',
3413 b'all-files',
3413 b'all-files',
3414 None,
3414 None,
3415 _(
3415 _(
3416 b'include all files in the changeset while grepping (DEPRECATED)'
3416 b'include all files in the changeset while grepping (DEPRECATED)'
3417 ),
3417 ),
3418 ),
3418 ),
3419 (b'u', b'user', None, _(b'list the author (long with -v)')),
3419 (b'u', b'user', None, _(b'list the author (long with -v)')),
3420 (b'd', b'date', None, _(b'list the date (short with -q)')),
3420 (b'd', b'date', None, _(b'list the date (short with -q)')),
3421 ]
3421 ]
3422 + formatteropts
3422 + formatteropts
3423 + walkopts,
3423 + walkopts,
3424 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3424 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3425 helpcategory=command.CATEGORY_FILE_CONTENTS,
3425 helpcategory=command.CATEGORY_FILE_CONTENTS,
3426 inferrepo=True,
3426 inferrepo=True,
3427 intents={INTENT_READONLY},
3427 intents={INTENT_READONLY},
3428 )
3428 )
3429 def grep(ui, repo, pattern, *pats, **opts):
3429 def grep(ui, repo, pattern, *pats, **opts):
3430 """search for a pattern in specified files
3430 """search for a pattern in specified files
3431
3431
3432 Search the working directory or revision history for a regular
3432 Search the working directory or revision history for a regular
3433 expression in the specified files for the entire repository.
3433 expression in the specified files for the entire repository.
3434
3434
3435 By default, grep searches the repository files in the working
3435 By default, grep searches the repository files in the working
3436 directory and prints the files where it finds a match. To specify
3436 directory and prints the files where it finds a match. To specify
3437 historical revisions instead of the working directory, use the
3437 historical revisions instead of the working directory, use the
3438 --rev flag.
3438 --rev flag.
3439
3439
3440 To search instead historical revision differences that contains a
3440 To search instead historical revision differences that contains a
3441 change in match status ("-" for a match that becomes a non-match,
3441 change in match status ("-" for a match that becomes a non-match,
3442 or "+" for a non-match that becomes a match), use the --diff flag.
3442 or "+" for a non-match that becomes a match), use the --diff flag.
3443
3443
3444 PATTERN can be any Python (roughly Perl-compatible) regular
3444 PATTERN can be any Python (roughly Perl-compatible) regular
3445 expression.
3445 expression.
3446
3446
3447 If no FILEs are specified and the --rev flag isn't supplied, all
3447 If no FILEs are specified and the --rev flag isn't supplied, all
3448 files in the working directory are searched. When using the --rev
3448 files in the working directory are searched. When using the --rev
3449 flag and specifying FILEs, use the --follow argument to also
3449 flag and specifying FILEs, use the --follow argument to also
3450 follow the specified FILEs across renames and copies.
3450 follow the specified FILEs across renames and copies.
3451
3451
3452 .. container:: verbose
3452 .. container:: verbose
3453
3453
3454 Template:
3454 Template:
3455
3455
3456 The following keywords are supported in addition to the common template
3456 The following keywords are supported in addition to the common template
3457 keywords and functions. See also :hg:`help templates`.
3457 keywords and functions. See also :hg:`help templates`.
3458
3458
3459 :change: String. Character denoting insertion ``+`` or removal ``-``.
3459 :change: String. Character denoting insertion ``+`` or removal ``-``.
3460 Available if ``--diff`` is specified.
3460 Available if ``--diff`` is specified.
3461 :lineno: Integer. Line number of the match.
3461 :lineno: Integer. Line number of the match.
3462 :path: String. Repository-absolute path of the file.
3462 :path: String. Repository-absolute path of the file.
3463 :texts: List of text chunks.
3463 :texts: List of text chunks.
3464
3464
3465 And each entry of ``{texts}`` provides the following sub-keywords.
3465 And each entry of ``{texts}`` provides the following sub-keywords.
3466
3466
3467 :matched: Boolean. True if the chunk matches the specified pattern.
3467 :matched: Boolean. True if the chunk matches the specified pattern.
3468 :text: String. Chunk content.
3468 :text: String. Chunk content.
3469
3469
3470 See :hg:`help templates.operators` for the list expansion syntax.
3470 See :hg:`help templates.operators` for the list expansion syntax.
3471
3471
3472 Returns 0 if a match is found, 1 otherwise.
3472 Returns 0 if a match is found, 1 otherwise.
3473
3473
3474 """
3474 """
3475 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3475 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3476 opts = pycompat.byteskwargs(opts)
3476 opts = pycompat.byteskwargs(opts)
3477 diff = opts.get(b'all') or opts.get(b'diff')
3477 diff = opts.get(b'all') or opts.get(b'diff')
3478 follow = opts.get(b'follow')
3478 follow = opts.get(b'follow')
3479 if opts.get(b'all_files') is None and not diff:
3479 if opts.get(b'all_files') is None and not diff:
3480 opts[b'all_files'] = True
3480 opts[b'all_files'] = True
3481 plaingrep = (
3481 plaingrep = (
3482 opts.get(b'all_files')
3482 opts.get(b'all_files')
3483 and not opts.get(b'rev')
3483 and not opts.get(b'rev')
3484 and not opts.get(b'follow')
3484 and not opts.get(b'follow')
3485 )
3485 )
3486 all_files = opts.get(b'all_files')
3486 all_files = opts.get(b'all_files')
3487 if plaingrep:
3487 if plaingrep:
3488 opts[b'rev'] = [b'wdir()']
3488 opts[b'rev'] = [b'wdir()']
3489
3489
3490 reflags = re.M
3490 reflags = re.M
3491 if opts.get(b'ignore_case'):
3491 if opts.get(b'ignore_case'):
3492 reflags |= re.I
3492 reflags |= re.I
3493 try:
3493 try:
3494 regexp = util.re.compile(pattern, reflags)
3494 regexp = util.re.compile(pattern, reflags)
3495 except re.error as inst:
3495 except re.error as inst:
3496 ui.warn(
3496 ui.warn(
3497 _(b"grep: invalid match pattern: %s\n")
3497 _(b"grep: invalid match pattern: %s\n")
3498 % stringutil.forcebytestr(inst)
3498 % stringutil.forcebytestr(inst)
3499 )
3499 )
3500 return 1
3500 return 1
3501 sep, eol = b':', b'\n'
3501 sep, eol = b':', b'\n'
3502 if opts.get(b'print0'):
3502 if opts.get(b'print0'):
3503 sep = eol = b'\0'
3503 sep = eol = b'\0'
3504
3504
3505 searcher = grepmod.grepsearcher(
3505 searcher = grepmod.grepsearcher(
3506 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3506 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3507 )
3507 )
3508
3508
3509 getfile = searcher._getfile
3509 getfile = searcher._getfile
3510
3510
3511 uipathfn = scmutil.getuipathfn(repo)
3511 uipathfn = scmutil.getuipathfn(repo)
3512
3512
3513 def display(fm, fn, ctx, pstates, states):
3513 def display(fm, fn, ctx, pstates, states):
3514 rev = scmutil.intrev(ctx)
3514 rev = scmutil.intrev(ctx)
3515 if fm.isplain():
3515 if fm.isplain():
3516 formatuser = ui.shortuser
3516 formatuser = ui.shortuser
3517 else:
3517 else:
3518 formatuser = pycompat.bytestr
3518 formatuser = pycompat.bytestr
3519 if ui.quiet:
3519 if ui.quiet:
3520 datefmt = b'%Y-%m-%d'
3520 datefmt = b'%Y-%m-%d'
3521 else:
3521 else:
3522 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3522 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3523 found = False
3523 found = False
3524
3524
3525 @util.cachefunc
3525 @util.cachefunc
3526 def binary():
3526 def binary():
3527 flog = getfile(fn)
3527 flog = getfile(fn)
3528 try:
3528 try:
3529 return stringutil.binary(flog.read(ctx.filenode(fn)))
3529 return stringutil.binary(flog.read(ctx.filenode(fn)))
3530 except error.WdirUnsupported:
3530 except error.WdirUnsupported:
3531 return ctx[fn].isbinary()
3531 return ctx[fn].isbinary()
3532
3532
3533 fieldnamemap = {b'linenumber': b'lineno'}
3533 fieldnamemap = {b'linenumber': b'lineno'}
3534 if diff:
3534 if diff:
3535 iter = grepmod.difflinestates(pstates, states)
3535 iter = grepmod.difflinestates(pstates, states)
3536 else:
3536 else:
3537 iter = [(b'', l) for l in states]
3537 iter = [(b'', l) for l in states]
3538 for change, l in iter:
3538 for change, l in iter:
3539 fm.startitem()
3539 fm.startitem()
3540 fm.context(ctx=ctx)
3540 fm.context(ctx=ctx)
3541 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3541 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3542 fm.plain(uipathfn(fn), label=b'grep.filename')
3542 fm.plain(uipathfn(fn), label=b'grep.filename')
3543
3543
3544 cols = [
3544 cols = [
3545 (b'rev', b'%d', rev, not plaingrep, b''),
3545 (b'rev', b'%d', rev, not plaingrep, b''),
3546 (
3546 (
3547 b'linenumber',
3547 b'linenumber',
3548 b'%d',
3548 b'%d',
3549 l.linenum,
3549 l.linenum,
3550 opts.get(b'line_number'),
3550 opts.get(b'line_number'),
3551 b'',
3551 b'',
3552 ),
3552 ),
3553 ]
3553 ]
3554 if diff:
3554 if diff:
3555 cols.append(
3555 cols.append(
3556 (
3556 (
3557 b'change',
3557 b'change',
3558 b'%s',
3558 b'%s',
3559 change,
3559 change,
3560 True,
3560 True,
3561 b'grep.inserted '
3561 b'grep.inserted '
3562 if change == b'+'
3562 if change == b'+'
3563 else b'grep.deleted ',
3563 else b'grep.deleted ',
3564 )
3564 )
3565 )
3565 )
3566 cols.extend(
3566 cols.extend(
3567 [
3567 [
3568 (
3568 (
3569 b'user',
3569 b'user',
3570 b'%s',
3570 b'%s',
3571 formatuser(ctx.user()),
3571 formatuser(ctx.user()),
3572 opts.get(b'user'),
3572 opts.get(b'user'),
3573 b'',
3573 b'',
3574 ),
3574 ),
3575 (
3575 (
3576 b'date',
3576 b'date',
3577 b'%s',
3577 b'%s',
3578 fm.formatdate(ctx.date(), datefmt),
3578 fm.formatdate(ctx.date(), datefmt),
3579 opts.get(b'date'),
3579 opts.get(b'date'),
3580 b'',
3580 b'',
3581 ),
3581 ),
3582 ]
3582 ]
3583 )
3583 )
3584 for name, fmt, data, cond, extra_label in cols:
3584 for name, fmt, data, cond, extra_label in cols:
3585 if cond:
3585 if cond:
3586 fm.plain(sep, label=b'grep.sep')
3586 fm.plain(sep, label=b'grep.sep')
3587 field = fieldnamemap.get(name, name)
3587 field = fieldnamemap.get(name, name)
3588 label = extra_label + (b'grep.%s' % name)
3588 label = extra_label + (b'grep.%s' % name)
3589 fm.condwrite(cond, field, fmt, data, label=label)
3589 fm.condwrite(cond, field, fmt, data, label=label)
3590 if not opts.get(b'files_with_matches'):
3590 if not opts.get(b'files_with_matches'):
3591 fm.plain(sep, label=b'grep.sep')
3591 fm.plain(sep, label=b'grep.sep')
3592 if not opts.get(b'text') and binary():
3592 if not opts.get(b'text') and binary():
3593 fm.plain(_(b" Binary file matches"))
3593 fm.plain(_(b" Binary file matches"))
3594 else:
3594 else:
3595 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3595 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3596 fm.plain(eol)
3596 fm.plain(eol)
3597 found = True
3597 found = True
3598 if opts.get(b'files_with_matches'):
3598 if opts.get(b'files_with_matches'):
3599 break
3599 break
3600 return found
3600 return found
3601
3601
3602 def displaymatches(fm, l):
3602 def displaymatches(fm, l):
3603 p = 0
3603 p = 0
3604 for s, e in l.findpos(regexp):
3604 for s, e in l.findpos(regexp):
3605 if p < s:
3605 if p < s:
3606 fm.startitem()
3606 fm.startitem()
3607 fm.write(b'text', b'%s', l.line[p:s])
3607 fm.write(b'text', b'%s', l.line[p:s])
3608 fm.data(matched=False)
3608 fm.data(matched=False)
3609 fm.startitem()
3609 fm.startitem()
3610 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3610 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3611 fm.data(matched=True)
3611 fm.data(matched=True)
3612 p = e
3612 p = e
3613 if p < len(l.line):
3613 if p < len(l.line):
3614 fm.startitem()
3614 fm.startitem()
3615 fm.write(b'text', b'%s', l.line[p:])
3615 fm.write(b'text', b'%s', l.line[p:])
3616 fm.data(matched=False)
3616 fm.data(matched=False)
3617 fm.end()
3617 fm.end()
3618
3618
3619 found = False
3619 found = False
3620
3620
3621 wopts = logcmdutil.walkopts(
3621 wopts = logcmdutil.walkopts(
3622 pats=pats,
3622 pats=pats,
3623 opts=opts,
3623 opts=opts,
3624 revspec=opts[b'rev'],
3624 revspec=opts[b'rev'],
3625 include_pats=opts[b'include'],
3625 include_pats=opts[b'include'],
3626 exclude_pats=opts[b'exclude'],
3626 exclude_pats=opts[b'exclude'],
3627 follow=follow,
3627 follow=follow,
3628 force_changelog_traversal=all_files,
3628 force_changelog_traversal=all_files,
3629 filter_revisions_by_pats=not all_files,
3629 filter_revisions_by_pats=not all_files,
3630 )
3630 )
3631 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3631 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3632
3632
3633 ui.pager(b'grep')
3633 ui.pager(b'grep')
3634 fm = ui.formatter(b'grep', opts)
3634 fm = ui.formatter(b'grep', opts)
3635 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3635 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3636 r = display(fm, fn, ctx, pstates, states)
3636 r = display(fm, fn, ctx, pstates, states)
3637 found = found or r
3637 found = found or r
3638 if r and not diff and not all_files:
3638 if r and not diff and not all_files:
3639 searcher.skipfile(fn, ctx.rev())
3639 searcher.skipfile(fn, ctx.rev())
3640 fm.end()
3640 fm.end()
3641
3641
3642 return not found
3642 return not found
3643
3643
3644
3644
3645 @command(
3645 @command(
3646 b'heads',
3646 b'heads',
3647 [
3647 [
3648 (
3648 (
3649 b'r',
3649 b'r',
3650 b'rev',
3650 b'rev',
3651 b'',
3651 b'',
3652 _(b'show only heads which are descendants of STARTREV'),
3652 _(b'show only heads which are descendants of STARTREV'),
3653 _(b'STARTREV'),
3653 _(b'STARTREV'),
3654 ),
3654 ),
3655 (b't', b'topo', False, _(b'show topological heads only')),
3655 (b't', b'topo', False, _(b'show topological heads only')),
3656 (
3656 (
3657 b'a',
3657 b'a',
3658 b'active',
3658 b'active',
3659 False,
3659 False,
3660 _(b'show active branchheads only (DEPRECATED)'),
3660 _(b'show active branchheads only (DEPRECATED)'),
3661 ),
3661 ),
3662 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3662 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3663 ]
3663 ]
3664 + templateopts,
3664 + templateopts,
3665 _(b'[-ct] [-r STARTREV] [REV]...'),
3665 _(b'[-ct] [-r STARTREV] [REV]...'),
3666 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3666 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3667 intents={INTENT_READONLY},
3667 intents={INTENT_READONLY},
3668 )
3668 )
3669 def heads(ui, repo, *branchrevs, **opts):
3669 def heads(ui, repo, *branchrevs, **opts):
3670 """show branch heads
3670 """show branch heads
3671
3671
3672 With no arguments, show all open branch heads in the repository.
3672 With no arguments, show all open branch heads in the repository.
3673 Branch heads are changesets that have no descendants on the
3673 Branch heads are changesets that have no descendants on the
3674 same branch. They are where development generally takes place and
3674 same branch. They are where development generally takes place and
3675 are the usual targets for update and merge operations.
3675 are the usual targets for update and merge operations.
3676
3676
3677 If one or more REVs are given, only open branch heads on the
3677 If one or more REVs are given, only open branch heads on the
3678 branches associated with the specified changesets are shown. This
3678 branches associated with the specified changesets are shown. This
3679 means that you can use :hg:`heads .` to see the heads on the
3679 means that you can use :hg:`heads .` to see the heads on the
3680 currently checked-out branch.
3680 currently checked-out branch.
3681
3681
3682 If -c/--closed is specified, also show branch heads marked closed
3682 If -c/--closed is specified, also show branch heads marked closed
3683 (see :hg:`commit --close-branch`).
3683 (see :hg:`commit --close-branch`).
3684
3684
3685 If STARTREV is specified, only those heads that are descendants of
3685 If STARTREV is specified, only those heads that are descendants of
3686 STARTREV will be displayed.
3686 STARTREV will be displayed.
3687
3687
3688 If -t/--topo is specified, named branch mechanics will be ignored and only
3688 If -t/--topo is specified, named branch mechanics will be ignored and only
3689 topological heads (changesets with no children) will be shown.
3689 topological heads (changesets with no children) will be shown.
3690
3690
3691 Returns 0 if matching heads are found, 1 if not.
3691 Returns 0 if matching heads are found, 1 if not.
3692 """
3692 """
3693
3693
3694 opts = pycompat.byteskwargs(opts)
3694 opts = pycompat.byteskwargs(opts)
3695 start = None
3695 start = None
3696 rev = opts.get(b'rev')
3696 rev = opts.get(b'rev')
3697 if rev:
3697 if rev:
3698 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3698 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3699 start = scmutil.revsingle(repo, rev, None).node()
3699 start = scmutil.revsingle(repo, rev, None).node()
3700
3700
3701 if opts.get(b'topo'):
3701 if opts.get(b'topo'):
3702 heads = [repo[h] for h in repo.heads(start)]
3702 heads = [repo[h] for h in repo.heads(start)]
3703 else:
3703 else:
3704 heads = []
3704 heads = []
3705 for branch in repo.branchmap():
3705 for branch in repo.branchmap():
3706 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3706 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3707 heads = [repo[h] for h in heads]
3707 heads = [repo[h] for h in heads]
3708
3708
3709 if branchrevs:
3709 if branchrevs:
3710 branches = {
3710 branches = {
3711 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3711 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3712 }
3712 }
3713 heads = [h for h in heads if h.branch() in branches]
3713 heads = [h for h in heads if h.branch() in branches]
3714
3714
3715 if opts.get(b'active') and branchrevs:
3715 if opts.get(b'active') and branchrevs:
3716 dagheads = repo.heads(start)
3716 dagheads = repo.heads(start)
3717 heads = [h for h in heads if h.node() in dagheads]
3717 heads = [h for h in heads if h.node() in dagheads]
3718
3718
3719 if branchrevs:
3719 if branchrevs:
3720 haveheads = {h.branch() for h in heads}
3720 haveheads = {h.branch() for h in heads}
3721 if branches - haveheads:
3721 if branches - haveheads:
3722 headless = b', '.join(b for b in branches - haveheads)
3722 headless = b', '.join(b for b in branches - haveheads)
3723 msg = _(b'no open branch heads found on branches %s')
3723 msg = _(b'no open branch heads found on branches %s')
3724 if opts.get(b'rev'):
3724 if opts.get(b'rev'):
3725 msg += _(b' (started at %s)') % opts[b'rev']
3725 msg += _(b' (started at %s)') % opts[b'rev']
3726 ui.warn((msg + b'\n') % headless)
3726 ui.warn((msg + b'\n') % headless)
3727
3727
3728 if not heads:
3728 if not heads:
3729 return 1
3729 return 1
3730
3730
3731 ui.pager(b'heads')
3731 ui.pager(b'heads')
3732 heads = sorted(heads, key=lambda x: -(x.rev()))
3732 heads = sorted(heads, key=lambda x: -(x.rev()))
3733 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3733 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3734 for ctx in heads:
3734 for ctx in heads:
3735 displayer.show(ctx)
3735 displayer.show(ctx)
3736 displayer.close()
3736 displayer.close()
3737
3737
3738
3738
3739 @command(
3739 @command(
3740 b'help',
3740 b'help',
3741 [
3741 [
3742 (b'e', b'extension', None, _(b'show only help for extensions')),
3742 (b'e', b'extension', None, _(b'show only help for extensions')),
3743 (b'c', b'command', None, _(b'show only help for commands')),
3743 (b'c', b'command', None, _(b'show only help for commands')),
3744 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3744 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3745 (
3745 (
3746 b's',
3746 b's',
3747 b'system',
3747 b'system',
3748 [],
3748 [],
3749 _(b'show help for specific platform(s)'),
3749 _(b'show help for specific platform(s)'),
3750 _(b'PLATFORM'),
3750 _(b'PLATFORM'),
3751 ),
3751 ),
3752 ],
3752 ],
3753 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3753 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3754 helpcategory=command.CATEGORY_HELP,
3754 helpcategory=command.CATEGORY_HELP,
3755 norepo=True,
3755 norepo=True,
3756 intents={INTENT_READONLY},
3756 intents={INTENT_READONLY},
3757 )
3757 )
3758 def help_(ui, name=None, **opts):
3758 def help_(ui, name=None, **opts):
3759 """show help for a given topic or a help overview
3759 """show help for a given topic or a help overview
3760
3760
3761 With no arguments, print a list of commands with short help messages.
3761 With no arguments, print a list of commands with short help messages.
3762
3762
3763 Given a topic, extension, or command name, print help for that
3763 Given a topic, extension, or command name, print help for that
3764 topic.
3764 topic.
3765
3765
3766 Returns 0 if successful.
3766 Returns 0 if successful.
3767 """
3767 """
3768
3768
3769 keep = opts.get('system') or []
3769 keep = opts.get('system') or []
3770 if len(keep) == 0:
3770 if len(keep) == 0:
3771 if pycompat.sysplatform.startswith(b'win'):
3771 if pycompat.sysplatform.startswith(b'win'):
3772 keep.append(b'windows')
3772 keep.append(b'windows')
3773 elif pycompat.sysplatform == b'OpenVMS':
3773 elif pycompat.sysplatform == b'OpenVMS':
3774 keep.append(b'vms')
3774 keep.append(b'vms')
3775 elif pycompat.sysplatform == b'plan9':
3775 elif pycompat.sysplatform == b'plan9':
3776 keep.append(b'plan9')
3776 keep.append(b'plan9')
3777 else:
3777 else:
3778 keep.append(b'unix')
3778 keep.append(b'unix')
3779 keep.append(pycompat.sysplatform.lower())
3779 keep.append(pycompat.sysplatform.lower())
3780 if ui.verbose:
3780 if ui.verbose:
3781 keep.append(b'verbose')
3781 keep.append(b'verbose')
3782
3782
3783 commands = sys.modules[__name__]
3783 commands = sys.modules[__name__]
3784 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3784 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3785 ui.pager(b'help')
3785 ui.pager(b'help')
3786 ui.write(formatted)
3786 ui.write(formatted)
3787
3787
3788
3788
3789 @command(
3789 @command(
3790 b'identify|id',
3790 b'identify|id',
3791 [
3791 [
3792 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3792 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3793 (b'n', b'num', None, _(b'show local revision number')),
3793 (b'n', b'num', None, _(b'show local revision number')),
3794 (b'i', b'id', None, _(b'show global revision id')),
3794 (b'i', b'id', None, _(b'show global revision id')),
3795 (b'b', b'branch', None, _(b'show branch')),
3795 (b'b', b'branch', None, _(b'show branch')),
3796 (b't', b'tags', None, _(b'show tags')),
3796 (b't', b'tags', None, _(b'show tags')),
3797 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3797 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3798 ]
3798 ]
3799 + remoteopts
3799 + remoteopts
3800 + formatteropts,
3800 + formatteropts,
3801 _(b'[-nibtB] [-r REV] [SOURCE]'),
3801 _(b'[-nibtB] [-r REV] [SOURCE]'),
3802 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3802 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3803 optionalrepo=True,
3803 optionalrepo=True,
3804 intents={INTENT_READONLY},
3804 intents={INTENT_READONLY},
3805 )
3805 )
3806 def identify(
3806 def identify(
3807 ui,
3807 ui,
3808 repo,
3808 repo,
3809 source=None,
3809 source=None,
3810 rev=None,
3810 rev=None,
3811 num=None,
3811 num=None,
3812 id=None,
3812 id=None,
3813 branch=None,
3813 branch=None,
3814 tags=None,
3814 tags=None,
3815 bookmarks=None,
3815 bookmarks=None,
3816 **opts
3816 **opts
3817 ):
3817 ):
3818 """identify the working directory or specified revision
3818 """identify the working directory or specified revision
3819
3819
3820 Print a summary identifying the repository state at REV using one or
3820 Print a summary identifying the repository state at REV using one or
3821 two parent hash identifiers, followed by a "+" if the working
3821 two parent hash identifiers, followed by a "+" if the working
3822 directory has uncommitted changes, the branch name (if not default),
3822 directory has uncommitted changes, the branch name (if not default),
3823 a list of tags, and a list of bookmarks.
3823 a list of tags, and a list of bookmarks.
3824
3824
3825 When REV is not given, print a summary of the current state of the
3825 When REV is not given, print a summary of the current state of the
3826 repository including the working directory. Specify -r. to get information
3826 repository including the working directory. Specify -r. to get information
3827 of the working directory parent without scanning uncommitted changes.
3827 of the working directory parent without scanning uncommitted changes.
3828
3828
3829 Specifying a path to a repository root or Mercurial bundle will
3829 Specifying a path to a repository root or Mercurial bundle will
3830 cause lookup to operate on that repository/bundle.
3830 cause lookup to operate on that repository/bundle.
3831
3831
3832 .. container:: verbose
3832 .. container:: verbose
3833
3833
3834 Template:
3834 Template:
3835
3835
3836 The following keywords are supported in addition to the common template
3836 The following keywords are supported in addition to the common template
3837 keywords and functions. See also :hg:`help templates`.
3837 keywords and functions. See also :hg:`help templates`.
3838
3838
3839 :dirty: String. Character ``+`` denoting if the working directory has
3839 :dirty: String. Character ``+`` denoting if the working directory has
3840 uncommitted changes.
3840 uncommitted changes.
3841 :id: String. One or two nodes, optionally followed by ``+``.
3841 :id: String. One or two nodes, optionally followed by ``+``.
3842 :parents: List of strings. Parent nodes of the changeset.
3842 :parents: List of strings. Parent nodes of the changeset.
3843
3843
3844 Examples:
3844 Examples:
3845
3845
3846 - generate a build identifier for the working directory::
3846 - generate a build identifier for the working directory::
3847
3847
3848 hg id --id > build-id.dat
3848 hg id --id > build-id.dat
3849
3849
3850 - find the revision corresponding to a tag::
3850 - find the revision corresponding to a tag::
3851
3851
3852 hg id -n -r 1.3
3852 hg id -n -r 1.3
3853
3853
3854 - check the most recent revision of a remote repository::
3854 - check the most recent revision of a remote repository::
3855
3855
3856 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3856 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3857
3857
3858 See :hg:`log` for generating more information about specific revisions,
3858 See :hg:`log` for generating more information about specific revisions,
3859 including full hash identifiers.
3859 including full hash identifiers.
3860
3860
3861 Returns 0 if successful.
3861 Returns 0 if successful.
3862 """
3862 """
3863
3863
3864 opts = pycompat.byteskwargs(opts)
3864 opts = pycompat.byteskwargs(opts)
3865 if not repo and not source:
3865 if not repo and not source:
3866 raise error.InputError(
3866 raise error.InputError(
3867 _(b"there is no Mercurial repository here (.hg not found)")
3867 _(b"there is no Mercurial repository here (.hg not found)")
3868 )
3868 )
3869
3869
3870 default = not (num or id or branch or tags or bookmarks)
3870 default = not (num or id or branch or tags or bookmarks)
3871 output = []
3871 output = []
3872 revs = []
3872 revs = []
3873
3873
3874 peer = None
3874 peer = None
3875 try:
3875 try:
3876 if source:
3876 if source:
3877 source, branches = urlutil.get_unique_pull_path(
3877 source, branches = urlutil.get_unique_pull_path(
3878 b'identify', repo, ui, source
3878 b'identify', repo, ui, source
3879 )
3879 )
3880 # only pass ui when no repo
3880 # only pass ui when no repo
3881 peer = hg.peer(repo or ui, opts, source)
3881 peer = hg.peer(repo or ui, opts, source)
3882 repo = peer.local()
3882 repo = peer.local()
3883 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3883 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3884
3884
3885 fm = ui.formatter(b'identify', opts)
3885 fm = ui.formatter(b'identify', opts)
3886 fm.startitem()
3886 fm.startitem()
3887
3887
3888 if not repo:
3888 if not repo:
3889 if num or branch or tags:
3889 if num or branch or tags:
3890 raise error.InputError(
3890 raise error.InputError(
3891 _(b"can't query remote revision number, branch, or tags")
3891 _(b"can't query remote revision number, branch, or tags")
3892 )
3892 )
3893 if not rev and revs:
3893 if not rev and revs:
3894 rev = revs[0]
3894 rev = revs[0]
3895 if not rev:
3895 if not rev:
3896 rev = b"tip"
3896 rev = b"tip"
3897
3897
3898 remoterev = peer.lookup(rev)
3898 remoterev = peer.lookup(rev)
3899 hexrev = fm.hexfunc(remoterev)
3899 hexrev = fm.hexfunc(remoterev)
3900 if default or id:
3900 if default or id:
3901 output = [hexrev]
3901 output = [hexrev]
3902 fm.data(id=hexrev)
3902 fm.data(id=hexrev)
3903
3903
3904 @util.cachefunc
3904 @util.cachefunc
3905 def getbms():
3905 def getbms():
3906 bms = []
3906 bms = []
3907
3907
3908 if b'bookmarks' in peer.listkeys(b'namespaces'):
3908 if b'bookmarks' in peer.listkeys(b'namespaces'):
3909 hexremoterev = hex(remoterev)
3909 hexremoterev = hex(remoterev)
3910 bms = [
3910 bms = [
3911 bm
3911 bm
3912 for bm, bmr in pycompat.iteritems(
3912 for bm, bmr in pycompat.iteritems(
3913 peer.listkeys(b'bookmarks')
3913 peer.listkeys(b'bookmarks')
3914 )
3914 )
3915 if bmr == hexremoterev
3915 if bmr == hexremoterev
3916 ]
3916 ]
3917
3917
3918 return sorted(bms)
3918 return sorted(bms)
3919
3919
3920 if fm.isplain():
3920 if fm.isplain():
3921 if bookmarks:
3921 if bookmarks:
3922 output.extend(getbms())
3922 output.extend(getbms())
3923 elif default and not ui.quiet:
3923 elif default and not ui.quiet:
3924 # multiple bookmarks for a single parent separated by '/'
3924 # multiple bookmarks for a single parent separated by '/'
3925 bm = b'/'.join(getbms())
3925 bm = b'/'.join(getbms())
3926 if bm:
3926 if bm:
3927 output.append(bm)
3927 output.append(bm)
3928 else:
3928 else:
3929 fm.data(node=hex(remoterev))
3929 fm.data(node=hex(remoterev))
3930 if bookmarks or b'bookmarks' in fm.datahint():
3930 if bookmarks or b'bookmarks' in fm.datahint():
3931 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3931 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3932 else:
3932 else:
3933 if rev:
3933 if rev:
3934 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3934 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3935 ctx = scmutil.revsingle(repo, rev, None)
3935 ctx = scmutil.revsingle(repo, rev, None)
3936
3936
3937 if ctx.rev() is None:
3937 if ctx.rev() is None:
3938 ctx = repo[None]
3938 ctx = repo[None]
3939 parents = ctx.parents()
3939 parents = ctx.parents()
3940 taglist = []
3940 taglist = []
3941 for p in parents:
3941 for p in parents:
3942 taglist.extend(p.tags())
3942 taglist.extend(p.tags())
3943
3943
3944 dirty = b""
3944 dirty = b""
3945 if ctx.dirty(missing=True, merge=False, branch=False):
3945 if ctx.dirty(missing=True, merge=False, branch=False):
3946 dirty = b'+'
3946 dirty = b'+'
3947 fm.data(dirty=dirty)
3947 fm.data(dirty=dirty)
3948
3948
3949 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3949 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3950 if default or id:
3950 if default or id:
3951 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3951 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3952 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3952 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3953
3953
3954 if num:
3954 if num:
3955 numoutput = [b"%d" % p.rev() for p in parents]
3955 numoutput = [b"%d" % p.rev() for p in parents]
3956 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3956 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3957
3957
3958 fm.data(
3958 fm.data(
3959 parents=fm.formatlist(
3959 parents=fm.formatlist(
3960 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3960 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3961 )
3961 )
3962 )
3962 )
3963 else:
3963 else:
3964 hexoutput = fm.hexfunc(ctx.node())
3964 hexoutput = fm.hexfunc(ctx.node())
3965 if default or id:
3965 if default or id:
3966 output = [hexoutput]
3966 output = [hexoutput]
3967 fm.data(id=hexoutput)
3967 fm.data(id=hexoutput)
3968
3968
3969 if num:
3969 if num:
3970 output.append(pycompat.bytestr(ctx.rev()))
3970 output.append(pycompat.bytestr(ctx.rev()))
3971 taglist = ctx.tags()
3971 taglist = ctx.tags()
3972
3972
3973 if default and not ui.quiet:
3973 if default and not ui.quiet:
3974 b = ctx.branch()
3974 b = ctx.branch()
3975 if b != b'default':
3975 if b != b'default':
3976 output.append(b"(%s)" % b)
3976 output.append(b"(%s)" % b)
3977
3977
3978 # multiple tags for a single parent separated by '/'
3978 # multiple tags for a single parent separated by '/'
3979 t = b'/'.join(taglist)
3979 t = b'/'.join(taglist)
3980 if t:
3980 if t:
3981 output.append(t)
3981 output.append(t)
3982
3982
3983 # multiple bookmarks for a single parent separated by '/'
3983 # multiple bookmarks for a single parent separated by '/'
3984 bm = b'/'.join(ctx.bookmarks())
3984 bm = b'/'.join(ctx.bookmarks())
3985 if bm:
3985 if bm:
3986 output.append(bm)
3986 output.append(bm)
3987 else:
3987 else:
3988 if branch:
3988 if branch:
3989 output.append(ctx.branch())
3989 output.append(ctx.branch())
3990
3990
3991 if tags:
3991 if tags:
3992 output.extend(taglist)
3992 output.extend(taglist)
3993
3993
3994 if bookmarks:
3994 if bookmarks:
3995 output.extend(ctx.bookmarks())
3995 output.extend(ctx.bookmarks())
3996
3996
3997 fm.data(node=ctx.hex())
3997 fm.data(node=ctx.hex())
3998 fm.data(branch=ctx.branch())
3998 fm.data(branch=ctx.branch())
3999 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3999 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4000 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4000 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4001 fm.context(ctx=ctx)
4001 fm.context(ctx=ctx)
4002
4002
4003 fm.plain(b"%s\n" % b' '.join(output))
4003 fm.plain(b"%s\n" % b' '.join(output))
4004 fm.end()
4004 fm.end()
4005 finally:
4005 finally:
4006 if peer:
4006 if peer:
4007 peer.close()
4007 peer.close()
4008
4008
4009
4009
4010 @command(
4010 @command(
4011 b'import|patch',
4011 b'import|patch',
4012 [
4012 [
4013 (
4013 (
4014 b'p',
4014 b'p',
4015 b'strip',
4015 b'strip',
4016 1,
4016 1,
4017 _(
4017 _(
4018 b'directory strip option for patch. This has the same '
4018 b'directory strip option for patch. This has the same '
4019 b'meaning as the corresponding patch option'
4019 b'meaning as the corresponding patch option'
4020 ),
4020 ),
4021 _(b'NUM'),
4021 _(b'NUM'),
4022 ),
4022 ),
4023 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4023 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4024 (b'', b'secret', None, _(b'use the secret phase for committing')),
4024 (b'', b'secret', None, _(b'use the secret phase for committing')),
4025 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4025 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4026 (
4026 (
4027 b'f',
4027 b'f',
4028 b'force',
4028 b'force',
4029 None,
4029 None,
4030 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4030 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4031 ),
4031 ),
4032 (
4032 (
4033 b'',
4033 b'',
4034 b'no-commit',
4034 b'no-commit',
4035 None,
4035 None,
4036 _(b"don't commit, just update the working directory"),
4036 _(b"don't commit, just update the working directory"),
4037 ),
4037 ),
4038 (
4038 (
4039 b'',
4039 b'',
4040 b'bypass',
4040 b'bypass',
4041 None,
4041 None,
4042 _(b"apply patch without touching the working directory"),
4042 _(b"apply patch without touching the working directory"),
4043 ),
4043 ),
4044 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4044 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4045 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4045 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4046 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4046 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4047 (
4047 (
4048 b'',
4048 b'',
4049 b'import-branch',
4049 b'import-branch',
4050 None,
4050 None,
4051 _(b'use any branch information in patch (implied by --exact)'),
4051 _(b'use any branch information in patch (implied by --exact)'),
4052 ),
4052 ),
4053 ]
4053 ]
4054 + commitopts
4054 + commitopts
4055 + commitopts2
4055 + commitopts2
4056 + similarityopts,
4056 + similarityopts,
4057 _(b'[OPTION]... PATCH...'),
4057 _(b'[OPTION]... PATCH...'),
4058 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4058 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4059 )
4059 )
4060 def import_(ui, repo, patch1=None, *patches, **opts):
4060 def import_(ui, repo, patch1=None, *patches, **opts):
4061 """import an ordered set of patches
4061 """import an ordered set of patches
4062
4062
4063 Import a list of patches and commit them individually (unless
4063 Import a list of patches and commit them individually (unless
4064 --no-commit is specified).
4064 --no-commit is specified).
4065
4065
4066 To read a patch from standard input (stdin), use "-" as the patch
4066 To read a patch from standard input (stdin), use "-" as the patch
4067 name. If a URL is specified, the patch will be downloaded from
4067 name. If a URL is specified, the patch will be downloaded from
4068 there.
4068 there.
4069
4069
4070 Import first applies changes to the working directory (unless
4070 Import first applies changes to the working directory (unless
4071 --bypass is specified), import will abort if there are outstanding
4071 --bypass is specified), import will abort if there are outstanding
4072 changes.
4072 changes.
4073
4073
4074 Use --bypass to apply and commit patches directly to the
4074 Use --bypass to apply and commit patches directly to the
4075 repository, without affecting the working directory. Without
4075 repository, without affecting the working directory. Without
4076 --exact, patches will be applied on top of the working directory
4076 --exact, patches will be applied on top of the working directory
4077 parent revision.
4077 parent revision.
4078
4078
4079 You can import a patch straight from a mail message. Even patches
4079 You can import a patch straight from a mail message. Even patches
4080 as attachments work (to use the body part, it must have type
4080 as attachments work (to use the body part, it must have type
4081 text/plain or text/x-patch). From and Subject headers of email
4081 text/plain or text/x-patch). From and Subject headers of email
4082 message are used as default committer and commit message. All
4082 message are used as default committer and commit message. All
4083 text/plain body parts before first diff are added to the commit
4083 text/plain body parts before first diff are added to the commit
4084 message.
4084 message.
4085
4085
4086 If the imported patch was generated by :hg:`export`, user and
4086 If the imported patch was generated by :hg:`export`, user and
4087 description from patch override values from message headers and
4087 description from patch override values from message headers and
4088 body. Values given on command line with -m/--message and -u/--user
4088 body. Values given on command line with -m/--message and -u/--user
4089 override these.
4089 override these.
4090
4090
4091 If --exact is specified, import will set the working directory to
4091 If --exact is specified, import will set the working directory to
4092 the parent of each patch before applying it, and will abort if the
4092 the parent of each patch before applying it, and will abort if the
4093 resulting changeset has a different ID than the one recorded in
4093 resulting changeset has a different ID than the one recorded in
4094 the patch. This will guard against various ways that portable
4094 the patch. This will guard against various ways that portable
4095 patch formats and mail systems might fail to transfer Mercurial
4095 patch formats and mail systems might fail to transfer Mercurial
4096 data or metadata. See :hg:`bundle` for lossless transmission.
4096 data or metadata. See :hg:`bundle` for lossless transmission.
4097
4097
4098 Use --partial to ensure a changeset will be created from the patch
4098 Use --partial to ensure a changeset will be created from the patch
4099 even if some hunks fail to apply. Hunks that fail to apply will be
4099 even if some hunks fail to apply. Hunks that fail to apply will be
4100 written to a <target-file>.rej file. Conflicts can then be resolved
4100 written to a <target-file>.rej file. Conflicts can then be resolved
4101 by hand before :hg:`commit --amend` is run to update the created
4101 by hand before :hg:`commit --amend` is run to update the created
4102 changeset. This flag exists to let people import patches that
4102 changeset. This flag exists to let people import patches that
4103 partially apply without losing the associated metadata (author,
4103 partially apply without losing the associated metadata (author,
4104 date, description, ...).
4104 date, description, ...).
4105
4105
4106 .. note::
4106 .. note::
4107
4107
4108 When no hunks apply cleanly, :hg:`import --partial` will create
4108 When no hunks apply cleanly, :hg:`import --partial` will create
4109 an empty changeset, importing only the patch metadata.
4109 an empty changeset, importing only the patch metadata.
4110
4110
4111 With -s/--similarity, hg will attempt to discover renames and
4111 With -s/--similarity, hg will attempt to discover renames and
4112 copies in the patch in the same way as :hg:`addremove`.
4112 copies in the patch in the same way as :hg:`addremove`.
4113
4113
4114 It is possible to use external patch programs to perform the patch
4114 It is possible to use external patch programs to perform the patch
4115 by setting the ``ui.patch`` configuration option. For the default
4115 by setting the ``ui.patch`` configuration option. For the default
4116 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4116 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4117 See :hg:`help config` for more information about configuration
4117 See :hg:`help config` for more information about configuration
4118 files and how to use these options.
4118 files and how to use these options.
4119
4119
4120 See :hg:`help dates` for a list of formats valid for -d/--date.
4120 See :hg:`help dates` for a list of formats valid for -d/--date.
4121
4121
4122 .. container:: verbose
4122 .. container:: verbose
4123
4123
4124 Examples:
4124 Examples:
4125
4125
4126 - import a traditional patch from a website and detect renames::
4126 - import a traditional patch from a website and detect renames::
4127
4127
4128 hg import -s 80 http://example.com/bugfix.patch
4128 hg import -s 80 http://example.com/bugfix.patch
4129
4129
4130 - import a changeset from an hgweb server::
4130 - import a changeset from an hgweb server::
4131
4131
4132 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4132 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4133
4133
4134 - import all the patches in an Unix-style mbox::
4134 - import all the patches in an Unix-style mbox::
4135
4135
4136 hg import incoming-patches.mbox
4136 hg import incoming-patches.mbox
4137
4137
4138 - import patches from stdin::
4138 - import patches from stdin::
4139
4139
4140 hg import -
4140 hg import -
4141
4141
4142 - attempt to exactly restore an exported changeset (not always
4142 - attempt to exactly restore an exported changeset (not always
4143 possible)::
4143 possible)::
4144
4144
4145 hg import --exact proposed-fix.patch
4145 hg import --exact proposed-fix.patch
4146
4146
4147 - use an external tool to apply a patch which is too fuzzy for
4147 - use an external tool to apply a patch which is too fuzzy for
4148 the default internal tool.
4148 the default internal tool.
4149
4149
4150 hg import --config ui.patch="patch --merge" fuzzy.patch
4150 hg import --config ui.patch="patch --merge" fuzzy.patch
4151
4151
4152 - change the default fuzzing from 2 to a less strict 7
4152 - change the default fuzzing from 2 to a less strict 7
4153
4153
4154 hg import --config ui.fuzz=7 fuzz.patch
4154 hg import --config ui.fuzz=7 fuzz.patch
4155
4155
4156 Returns 0 on success, 1 on partial success (see --partial).
4156 Returns 0 on success, 1 on partial success (see --partial).
4157 """
4157 """
4158
4158
4159 cmdutil.check_incompatible_arguments(
4159 cmdutil.check_incompatible_arguments(
4160 opts, 'no_commit', ['bypass', 'secret']
4160 opts, 'no_commit', ['bypass', 'secret']
4161 )
4161 )
4162 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4162 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4163 opts = pycompat.byteskwargs(opts)
4163 opts = pycompat.byteskwargs(opts)
4164 if not patch1:
4164 if not patch1:
4165 raise error.InputError(_(b'need at least one patch to import'))
4165 raise error.InputError(_(b'need at least one patch to import'))
4166
4166
4167 patches = (patch1,) + patches
4167 patches = (patch1,) + patches
4168
4168
4169 date = opts.get(b'date')
4169 date = opts.get(b'date')
4170 if date:
4170 if date:
4171 opts[b'date'] = dateutil.parsedate(date)
4171 opts[b'date'] = dateutil.parsedate(date)
4172
4172
4173 exact = opts.get(b'exact')
4173 exact = opts.get(b'exact')
4174 update = not opts.get(b'bypass')
4174 update = not opts.get(b'bypass')
4175 try:
4175 try:
4176 sim = float(opts.get(b'similarity') or 0)
4176 sim = float(opts.get(b'similarity') or 0)
4177 except ValueError:
4177 except ValueError:
4178 raise error.InputError(_(b'similarity must be a number'))
4178 raise error.InputError(_(b'similarity must be a number'))
4179 if sim < 0 or sim > 100:
4179 if sim < 0 or sim > 100:
4180 raise error.InputError(_(b'similarity must be between 0 and 100'))
4180 raise error.InputError(_(b'similarity must be between 0 and 100'))
4181 if sim and not update:
4181 if sim and not update:
4182 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4182 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4183
4183
4184 base = opts[b"base"]
4184 base = opts[b"base"]
4185 msgs = []
4185 msgs = []
4186 ret = 0
4186 ret = 0
4187
4187
4188 with repo.wlock():
4188 with repo.wlock():
4189 if update:
4189 if update:
4190 cmdutil.checkunfinished(repo)
4190 cmdutil.checkunfinished(repo)
4191 if exact or not opts.get(b'force'):
4191 if exact or not opts.get(b'force'):
4192 cmdutil.bailifchanged(repo)
4192 cmdutil.bailifchanged(repo)
4193
4193
4194 if not opts.get(b'no_commit'):
4194 if not opts.get(b'no_commit'):
4195 lock = repo.lock
4195 lock = repo.lock
4196 tr = lambda: repo.transaction(b'import')
4196 tr = lambda: repo.transaction(b'import')
4197 dsguard = util.nullcontextmanager
4197 dsguard = util.nullcontextmanager
4198 else:
4198 else:
4199 lock = util.nullcontextmanager
4199 lock = util.nullcontextmanager
4200 tr = util.nullcontextmanager
4200 tr = util.nullcontextmanager
4201 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4201 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4202 with lock(), tr(), dsguard():
4202 with lock(), tr(), dsguard():
4203 parents = repo[None].parents()
4203 parents = repo[None].parents()
4204 for patchurl in patches:
4204 for patchurl in patches:
4205 if patchurl == b'-':
4205 if patchurl == b'-':
4206 ui.status(_(b'applying patch from stdin\n'))
4206 ui.status(_(b'applying patch from stdin\n'))
4207 patchfile = ui.fin
4207 patchfile = ui.fin
4208 patchurl = b'stdin' # for error message
4208 patchurl = b'stdin' # for error message
4209 else:
4209 else:
4210 patchurl = os.path.join(base, patchurl)
4210 patchurl = os.path.join(base, patchurl)
4211 ui.status(_(b'applying %s\n') % patchurl)
4211 ui.status(_(b'applying %s\n') % patchurl)
4212 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4212 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4213
4213
4214 haspatch = False
4214 haspatch = False
4215 for hunk in patch.split(patchfile):
4215 for hunk in patch.split(patchfile):
4216 with patch.extract(ui, hunk) as patchdata:
4216 with patch.extract(ui, hunk) as patchdata:
4217 msg, node, rej = cmdutil.tryimportone(
4217 msg, node, rej = cmdutil.tryimportone(
4218 ui, repo, patchdata, parents, opts, msgs, hg.clean
4218 ui, repo, patchdata, parents, opts, msgs, hg.clean
4219 )
4219 )
4220 if msg:
4220 if msg:
4221 haspatch = True
4221 haspatch = True
4222 ui.note(msg + b'\n')
4222 ui.note(msg + b'\n')
4223 if update or exact:
4223 if update or exact:
4224 parents = repo[None].parents()
4224 parents = repo[None].parents()
4225 else:
4225 else:
4226 parents = [repo[node]]
4226 parents = [repo[node]]
4227 if rej:
4227 if rej:
4228 ui.write_err(_(b"patch applied partially\n"))
4228 ui.write_err(_(b"patch applied partially\n"))
4229 ui.write_err(
4229 ui.write_err(
4230 _(
4230 _(
4231 b"(fix the .rej files and run "
4231 b"(fix the .rej files and run "
4232 b"`hg commit --amend`)\n"
4232 b"`hg commit --amend`)\n"
4233 )
4233 )
4234 )
4234 )
4235 ret = 1
4235 ret = 1
4236 break
4236 break
4237
4237
4238 if not haspatch:
4238 if not haspatch:
4239 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4239 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4240
4240
4241 if msgs:
4241 if msgs:
4242 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4242 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4243 return ret
4243 return ret
4244
4244
4245
4245
4246 @command(
4246 @command(
4247 b'incoming|in',
4247 b'incoming|in',
4248 [
4248 [
4249 (
4249 (
4250 b'f',
4250 b'f',
4251 b'force',
4251 b'force',
4252 None,
4252 None,
4253 _(b'run even if remote repository is unrelated'),
4253 _(b'run even if remote repository is unrelated'),
4254 ),
4254 ),
4255 (b'n', b'newest-first', None, _(b'show newest record first')),
4255 (b'n', b'newest-first', None, _(b'show newest record first')),
4256 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4256 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4257 (
4257 (
4258 b'r',
4258 b'r',
4259 b'rev',
4259 b'rev',
4260 [],
4260 [],
4261 _(b'a remote changeset intended to be added'),
4261 _(b'a remote changeset intended to be added'),
4262 _(b'REV'),
4262 _(b'REV'),
4263 ),
4263 ),
4264 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4264 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4265 (
4265 (
4266 b'b',
4266 b'b',
4267 b'branch',
4267 b'branch',
4268 [],
4268 [],
4269 _(b'a specific branch you would like to pull'),
4269 _(b'a specific branch you would like to pull'),
4270 _(b'BRANCH'),
4270 _(b'BRANCH'),
4271 ),
4271 ),
4272 ]
4272 ]
4273 + logopts
4273 + logopts
4274 + remoteopts
4274 + remoteopts
4275 + subrepoopts,
4275 + subrepoopts,
4276 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4276 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4277 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4277 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4278 )
4278 )
4279 def incoming(ui, repo, source=b"default", **opts):
4279 def incoming(ui, repo, source=b"default", **opts):
4280 """show new changesets found in source
4280 """show new changesets found in source
4281
4281
4282 Show new changesets found in the specified path/URL or the default
4282 Show new changesets found in the specified path/URL or the default
4283 pull location. These are the changesets that would have been pulled
4283 pull location. These are the changesets that would have been pulled
4284 by :hg:`pull` at the time you issued this command.
4284 by :hg:`pull` at the time you issued this command.
4285
4285
4286 See pull for valid source format details.
4286 See pull for valid source format details.
4287
4287
4288 .. container:: verbose
4288 .. container:: verbose
4289
4289
4290 With -B/--bookmarks, the result of bookmark comparison between
4290 With -B/--bookmarks, the result of bookmark comparison between
4291 local and remote repositories is displayed. With -v/--verbose,
4291 local and remote repositories is displayed. With -v/--verbose,
4292 status is also displayed for each bookmark like below::
4292 status is also displayed for each bookmark like below::
4293
4293
4294 BM1 01234567890a added
4294 BM1 01234567890a added
4295 BM2 1234567890ab advanced
4295 BM2 1234567890ab advanced
4296 BM3 234567890abc diverged
4296 BM3 234567890abc diverged
4297 BM4 34567890abcd changed
4297 BM4 34567890abcd changed
4298
4298
4299 The action taken locally when pulling depends on the
4299 The action taken locally when pulling depends on the
4300 status of each bookmark:
4300 status of each bookmark:
4301
4301
4302 :``added``: pull will create it
4302 :``added``: pull will create it
4303 :``advanced``: pull will update it
4303 :``advanced``: pull will update it
4304 :``diverged``: pull will create a divergent bookmark
4304 :``diverged``: pull will create a divergent bookmark
4305 :``changed``: result depends on remote changesets
4305 :``changed``: result depends on remote changesets
4306
4306
4307 From the point of view of pulling behavior, bookmark
4307 From the point of view of pulling behavior, bookmark
4308 existing only in the remote repository are treated as ``added``,
4308 existing only in the remote repository are treated as ``added``,
4309 even if it is in fact locally deleted.
4309 even if it is in fact locally deleted.
4310
4310
4311 .. container:: verbose
4311 .. container:: verbose
4312
4312
4313 For remote repository, using --bundle avoids downloading the
4313 For remote repository, using --bundle avoids downloading the
4314 changesets twice if the incoming is followed by a pull.
4314 changesets twice if the incoming is followed by a pull.
4315
4315
4316 Examples:
4316 Examples:
4317
4317
4318 - show incoming changes with patches and full description::
4318 - show incoming changes with patches and full description::
4319
4319
4320 hg incoming -vp
4320 hg incoming -vp
4321
4321
4322 - show incoming changes excluding merges, store a bundle::
4322 - show incoming changes excluding merges, store a bundle::
4323
4323
4324 hg in -vpM --bundle incoming.hg
4324 hg in -vpM --bundle incoming.hg
4325 hg pull incoming.hg
4325 hg pull incoming.hg
4326
4326
4327 - briefly list changes inside a bundle::
4327 - briefly list changes inside a bundle::
4328
4328
4329 hg in changes.hg -T "{desc|firstline}\\n"
4329 hg in changes.hg -T "{desc|firstline}\\n"
4330
4330
4331 Returns 0 if there are incoming changes, 1 otherwise.
4331 Returns 0 if there are incoming changes, 1 otherwise.
4332 """
4332 """
4333 opts = pycompat.byteskwargs(opts)
4333 opts = pycompat.byteskwargs(opts)
4334 if opts.get(b'graph'):
4334 if opts.get(b'graph'):
4335 logcmdutil.checkunsupportedgraphflags([], opts)
4335 logcmdutil.checkunsupportedgraphflags([], opts)
4336
4336
4337 def display(other, chlist, displayer):
4337 def display(other, chlist, displayer):
4338 revdag = logcmdutil.graphrevs(other, chlist, opts)
4338 revdag = logcmdutil.graphrevs(other, chlist, opts)
4339 logcmdutil.displaygraph(
4339 logcmdutil.displaygraph(
4340 ui, repo, revdag, displayer, graphmod.asciiedges
4340 ui, repo, revdag, displayer, graphmod.asciiedges
4341 )
4341 )
4342
4342
4343 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4343 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4344 return 0
4344 return 0
4345
4345
4346 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4346 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4347
4347
4348 if opts.get(b'bookmarks'):
4348 if opts.get(b'bookmarks'):
4349 srcs = urlutil.get_pull_paths(repo, ui, [source], opts.get(b'branch'))
4349 srcs = urlutil.get_pull_paths(repo, ui, [source], opts.get(b'branch'))
4350 for source, branches in srcs:
4350 for source, branches in srcs:
4351 other = hg.peer(repo, opts, source)
4351 other = hg.peer(repo, opts, source)
4352 try:
4352 try:
4353 if b'bookmarks' not in other.listkeys(b'namespaces'):
4353 if b'bookmarks' not in other.listkeys(b'namespaces'):
4354 ui.warn(_(b"remote doesn't support bookmarks\n"))
4354 ui.warn(_(b"remote doesn't support bookmarks\n"))
4355 return 0
4355 return 0
4356 ui.pager(b'incoming')
4356 ui.pager(b'incoming')
4357 ui.status(
4357 ui.status(
4358 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4358 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4359 )
4359 )
4360 return bookmarks.incoming(ui, repo, other)
4360 return bookmarks.incoming(ui, repo, other)
4361 finally:
4361 finally:
4362 other.close()
4362 other.close()
4363
4363
4364 return hg.incoming(ui, repo, source, opts)
4364 return hg.incoming(ui, repo, source, opts)
4365
4365
4366
4366
4367 @command(
4367 @command(
4368 b'init',
4368 b'init',
4369 remoteopts,
4369 remoteopts,
4370 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4370 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4371 helpcategory=command.CATEGORY_REPO_CREATION,
4371 helpcategory=command.CATEGORY_REPO_CREATION,
4372 helpbasic=True,
4372 helpbasic=True,
4373 norepo=True,
4373 norepo=True,
4374 )
4374 )
4375 def init(ui, dest=b".", **opts):
4375 def init(ui, dest=b".", **opts):
4376 """create a new repository in the given directory
4376 """create a new repository in the given directory
4377
4377
4378 Initialize a new repository in the given directory. If the given
4378 Initialize a new repository in the given directory. If the given
4379 directory does not exist, it will be created.
4379 directory does not exist, it will be created.
4380
4380
4381 If no directory is given, the current directory is used.
4381 If no directory is given, the current directory is used.
4382
4382
4383 It is possible to specify an ``ssh://`` URL as the destination.
4383 It is possible to specify an ``ssh://`` URL as the destination.
4384 See :hg:`help urls` for more information.
4384 See :hg:`help urls` for more information.
4385
4385
4386 Returns 0 on success.
4386 Returns 0 on success.
4387 """
4387 """
4388 opts = pycompat.byteskwargs(opts)
4388 opts = pycompat.byteskwargs(opts)
4389 path = urlutil.get_clone_path(ui, dest)[1]
4389 path = urlutil.get_clone_path(ui, dest)[1]
4390 peer = hg.peer(ui, opts, path, create=True)
4390 peer = hg.peer(ui, opts, path, create=True)
4391 peer.close()
4391 peer.close()
4392
4392
4393
4393
4394 @command(
4394 @command(
4395 b'locate',
4395 b'locate',
4396 [
4396 [
4397 (
4397 (
4398 b'r',
4398 b'r',
4399 b'rev',
4399 b'rev',
4400 b'',
4400 b'',
4401 _(b'search the repository as it is in REV'),
4401 _(b'search the repository as it is in REV'),
4402 _(b'REV'),
4402 _(b'REV'),
4403 ),
4403 ),
4404 (
4404 (
4405 b'0',
4405 b'0',
4406 b'print0',
4406 b'print0',
4407 None,
4407 None,
4408 _(b'end filenames with NUL, for use with xargs'),
4408 _(b'end filenames with NUL, for use with xargs'),
4409 ),
4409 ),
4410 (
4410 (
4411 b'f',
4411 b'f',
4412 b'fullpath',
4412 b'fullpath',
4413 None,
4413 None,
4414 _(b'print complete paths from the filesystem root'),
4414 _(b'print complete paths from the filesystem root'),
4415 ),
4415 ),
4416 ]
4416 ]
4417 + walkopts,
4417 + walkopts,
4418 _(b'[OPTION]... [PATTERN]...'),
4418 _(b'[OPTION]... [PATTERN]...'),
4419 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4419 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4420 )
4420 )
4421 def locate(ui, repo, *pats, **opts):
4421 def locate(ui, repo, *pats, **opts):
4422 """locate files matching specific patterns (DEPRECATED)
4422 """locate files matching specific patterns (DEPRECATED)
4423
4423
4424 Print files under Mercurial control in the working directory whose
4424 Print files under Mercurial control in the working directory whose
4425 names match the given patterns.
4425 names match the given patterns.
4426
4426
4427 By default, this command searches all directories in the working
4427 By default, this command searches all directories in the working
4428 directory. To search just the current directory and its
4428 directory. To search just the current directory and its
4429 subdirectories, use "--include .".
4429 subdirectories, use "--include .".
4430
4430
4431 If no patterns are given to match, this command prints the names
4431 If no patterns are given to match, this command prints the names
4432 of all files under Mercurial control in the working directory.
4432 of all files under Mercurial control in the working directory.
4433
4433
4434 If you want to feed the output of this command into the "xargs"
4434 If you want to feed the output of this command into the "xargs"
4435 command, use the -0 option to both this command and "xargs". This
4435 command, use the -0 option to both this command and "xargs". This
4436 will avoid the problem of "xargs" treating single filenames that
4436 will avoid the problem of "xargs" treating single filenames that
4437 contain whitespace as multiple filenames.
4437 contain whitespace as multiple filenames.
4438
4438
4439 See :hg:`help files` for a more versatile command.
4439 See :hg:`help files` for a more versatile command.
4440
4440
4441 Returns 0 if a match is found, 1 otherwise.
4441 Returns 0 if a match is found, 1 otherwise.
4442 """
4442 """
4443 opts = pycompat.byteskwargs(opts)
4443 opts = pycompat.byteskwargs(opts)
4444 if opts.get(b'print0'):
4444 if opts.get(b'print0'):
4445 end = b'\0'
4445 end = b'\0'
4446 else:
4446 else:
4447 end = b'\n'
4447 end = b'\n'
4448 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4448 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4449
4449
4450 ret = 1
4450 ret = 1
4451 m = scmutil.match(
4451 m = scmutil.match(
4452 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4452 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4453 )
4453 )
4454
4454
4455 ui.pager(b'locate')
4455 ui.pager(b'locate')
4456 if ctx.rev() is None:
4456 if ctx.rev() is None:
4457 # When run on the working copy, "locate" includes removed files, so
4457 # When run on the working copy, "locate" includes removed files, so
4458 # we get the list of files from the dirstate.
4458 # we get the list of files from the dirstate.
4459 filesgen = sorted(repo.dirstate.matches(m))
4459 filesgen = sorted(repo.dirstate.matches(m))
4460 else:
4460 else:
4461 filesgen = ctx.matches(m)
4461 filesgen = ctx.matches(m)
4462 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4462 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4463 for abs in filesgen:
4463 for abs in filesgen:
4464 if opts.get(b'fullpath'):
4464 if opts.get(b'fullpath'):
4465 ui.write(repo.wjoin(abs), end)
4465 ui.write(repo.wjoin(abs), end)
4466 else:
4466 else:
4467 ui.write(uipathfn(abs), end)
4467 ui.write(uipathfn(abs), end)
4468 ret = 0
4468 ret = 0
4469
4469
4470 return ret
4470 return ret
4471
4471
4472
4472
4473 @command(
4473 @command(
4474 b'log|history',
4474 b'log|history',
4475 [
4475 [
4476 (
4476 (
4477 b'f',
4477 b'f',
4478 b'follow',
4478 b'follow',
4479 None,
4479 None,
4480 _(
4480 _(
4481 b'follow changeset history, or file history across copies and renames'
4481 b'follow changeset history, or file history across copies and renames'
4482 ),
4482 ),
4483 ),
4483 ),
4484 (
4484 (
4485 b'',
4485 b'',
4486 b'follow-first',
4486 b'follow-first',
4487 None,
4487 None,
4488 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4488 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4489 ),
4489 ),
4490 (
4490 (
4491 b'd',
4491 b'd',
4492 b'date',
4492 b'date',
4493 b'',
4493 b'',
4494 _(b'show revisions matching date spec'),
4494 _(b'show revisions matching date spec'),
4495 _(b'DATE'),
4495 _(b'DATE'),
4496 ),
4496 ),
4497 (b'C', b'copies', None, _(b'show copied files')),
4497 (b'C', b'copies', None, _(b'show copied files')),
4498 (
4498 (
4499 b'k',
4499 b'k',
4500 b'keyword',
4500 b'keyword',
4501 [],
4501 [],
4502 _(b'do case-insensitive search for a given text'),
4502 _(b'do case-insensitive search for a given text'),
4503 _(b'TEXT'),
4503 _(b'TEXT'),
4504 ),
4504 ),
4505 (
4505 (
4506 b'r',
4506 b'r',
4507 b'rev',
4507 b'rev',
4508 [],
4508 [],
4509 _(b'revisions to select or follow from'),
4509 _(b'revisions to select or follow from'),
4510 _(b'REV'),
4510 _(b'REV'),
4511 ),
4511 ),
4512 (
4512 (
4513 b'L',
4513 b'L',
4514 b'line-range',
4514 b'line-range',
4515 [],
4515 [],
4516 _(b'follow line range of specified file (EXPERIMENTAL)'),
4516 _(b'follow line range of specified file (EXPERIMENTAL)'),
4517 _(b'FILE,RANGE'),
4517 _(b'FILE,RANGE'),
4518 ),
4518 ),
4519 (
4519 (
4520 b'',
4520 b'',
4521 b'removed',
4521 b'removed',
4522 None,
4522 None,
4523 _(b'include revisions where files were removed'),
4523 _(b'include revisions where files were removed'),
4524 ),
4524 ),
4525 (
4525 (
4526 b'm',
4526 b'm',
4527 b'only-merges',
4527 b'only-merges',
4528 None,
4528 None,
4529 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4529 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4530 ),
4530 ),
4531 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4531 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4532 (
4532 (
4533 b'',
4533 b'',
4534 b'only-branch',
4534 b'only-branch',
4535 [],
4535 [],
4536 _(
4536 _(
4537 b'show only changesets within the given named branch (DEPRECATED)'
4537 b'show only changesets within the given named branch (DEPRECATED)'
4538 ),
4538 ),
4539 _(b'BRANCH'),
4539 _(b'BRANCH'),
4540 ),
4540 ),
4541 (
4541 (
4542 b'b',
4542 b'b',
4543 b'branch',
4543 b'branch',
4544 [],
4544 [],
4545 _(b'show changesets within the given named branch'),
4545 _(b'show changesets within the given named branch'),
4546 _(b'BRANCH'),
4546 _(b'BRANCH'),
4547 ),
4547 ),
4548 (
4548 (
4549 b'B',
4549 b'B',
4550 b'bookmark',
4550 b'bookmark',
4551 [],
4551 [],
4552 _(b"show changesets within the given bookmark"),
4552 _(b"show changesets within the given bookmark"),
4553 _(b'BOOKMARK'),
4553 _(b'BOOKMARK'),
4554 ),
4554 ),
4555 (
4555 (
4556 b'P',
4556 b'P',
4557 b'prune',
4557 b'prune',
4558 [],
4558 [],
4559 _(b'do not display revision or any of its ancestors'),
4559 _(b'do not display revision or any of its ancestors'),
4560 _(b'REV'),
4560 _(b'REV'),
4561 ),
4561 ),
4562 ]
4562 ]
4563 + logopts
4563 + logopts
4564 + walkopts,
4564 + walkopts,
4565 _(b'[OPTION]... [FILE]'),
4565 _(b'[OPTION]... [FILE]'),
4566 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4566 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4567 helpbasic=True,
4567 helpbasic=True,
4568 inferrepo=True,
4568 inferrepo=True,
4569 intents={INTENT_READONLY},
4569 intents={INTENT_READONLY},
4570 )
4570 )
4571 def log(ui, repo, *pats, **opts):
4571 def log(ui, repo, *pats, **opts):
4572 """show revision history of entire repository or files
4572 """show revision history of entire repository or files
4573
4573
4574 Print the revision history of the specified files or the entire
4574 Print the revision history of the specified files or the entire
4575 project.
4575 project.
4576
4576
4577 If no revision range is specified, the default is ``tip:0`` unless
4577 If no revision range is specified, the default is ``tip:0`` unless
4578 --follow is set.
4578 --follow is set.
4579
4579
4580 File history is shown without following rename or copy history of
4580 File history is shown without following rename or copy history of
4581 files. Use -f/--follow with a filename to follow history across
4581 files. Use -f/--follow with a filename to follow history across
4582 renames and copies. --follow without a filename will only show
4582 renames and copies. --follow without a filename will only show
4583 ancestors of the starting revisions. The starting revisions can be
4583 ancestors of the starting revisions. The starting revisions can be
4584 specified by -r/--rev, which default to the working directory parent.
4584 specified by -r/--rev, which default to the working directory parent.
4585
4585
4586 By default this command prints revision number and changeset id,
4586 By default this command prints revision number and changeset id,
4587 tags, non-trivial parents, user, date and time, and a summary for
4587 tags, non-trivial parents, user, date and time, and a summary for
4588 each commit. When the -v/--verbose switch is used, the list of
4588 each commit. When the -v/--verbose switch is used, the list of
4589 changed files and full commit message are shown.
4589 changed files and full commit message are shown.
4590
4590
4591 With --graph the revisions are shown as an ASCII art DAG with the most
4591 With --graph the revisions are shown as an ASCII art DAG with the most
4592 recent changeset at the top.
4592 recent changeset at the top.
4593 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4593 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4594 involved in an unresolved merge conflict, '_' closes a branch,
4594 involved in an unresolved merge conflict, '_' closes a branch,
4595 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4595 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4596 changeset from the lines below is a parent of the 'o' merge on the same
4596 changeset from the lines below is a parent of the 'o' merge on the same
4597 line.
4597 line.
4598 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4598 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4599 of a '|' indicates one or more revisions in a path are omitted.
4599 of a '|' indicates one or more revisions in a path are omitted.
4600
4600
4601 .. container:: verbose
4601 .. container:: verbose
4602
4602
4603 Use -L/--line-range FILE,M:N options to follow the history of lines
4603 Use -L/--line-range FILE,M:N options to follow the history of lines
4604 from M to N in FILE. With -p/--patch only diff hunks affecting
4604 from M to N in FILE. With -p/--patch only diff hunks affecting
4605 specified line range will be shown. This option requires --follow;
4605 specified line range will be shown. This option requires --follow;
4606 it can be specified multiple times. Currently, this option is not
4606 it can be specified multiple times. Currently, this option is not
4607 compatible with --graph. This option is experimental.
4607 compatible with --graph. This option is experimental.
4608
4608
4609 .. note::
4609 .. note::
4610
4610
4611 :hg:`log --patch` may generate unexpected diff output for merge
4611 :hg:`log --patch` may generate unexpected diff output for merge
4612 changesets, as it will only compare the merge changeset against
4612 changesets, as it will only compare the merge changeset against
4613 its first parent. Also, only files different from BOTH parents
4613 its first parent. Also, only files different from BOTH parents
4614 will appear in files:.
4614 will appear in files:.
4615
4615
4616 .. note::
4616 .. note::
4617
4617
4618 For performance reasons, :hg:`log FILE` may omit duplicate changes
4618 For performance reasons, :hg:`log FILE` may omit duplicate changes
4619 made on branches and will not show removals or mode changes. To
4619 made on branches and will not show removals or mode changes. To
4620 see all such changes, use the --removed switch.
4620 see all such changes, use the --removed switch.
4621
4621
4622 .. container:: verbose
4622 .. container:: verbose
4623
4623
4624 .. note::
4624 .. note::
4625
4625
4626 The history resulting from -L/--line-range options depends on diff
4626 The history resulting from -L/--line-range options depends on diff
4627 options; for instance if white-spaces are ignored, respective changes
4627 options; for instance if white-spaces are ignored, respective changes
4628 with only white-spaces in specified line range will not be listed.
4628 with only white-spaces in specified line range will not be listed.
4629
4629
4630 .. container:: verbose
4630 .. container:: verbose
4631
4631
4632 Some examples:
4632 Some examples:
4633
4633
4634 - changesets with full descriptions and file lists::
4634 - changesets with full descriptions and file lists::
4635
4635
4636 hg log -v
4636 hg log -v
4637
4637
4638 - changesets ancestral to the working directory::
4638 - changesets ancestral to the working directory::
4639
4639
4640 hg log -f
4640 hg log -f
4641
4641
4642 - last 10 commits on the current branch::
4642 - last 10 commits on the current branch::
4643
4643
4644 hg log -l 10 -b .
4644 hg log -l 10 -b .
4645
4645
4646 - changesets showing all modifications of a file, including removals::
4646 - changesets showing all modifications of a file, including removals::
4647
4647
4648 hg log --removed file.c
4648 hg log --removed file.c
4649
4649
4650 - all changesets that touch a directory, with diffs, excluding merges::
4650 - all changesets that touch a directory, with diffs, excluding merges::
4651
4651
4652 hg log -Mp lib/
4652 hg log -Mp lib/
4653
4653
4654 - all revision numbers that match a keyword::
4654 - all revision numbers that match a keyword::
4655
4655
4656 hg log -k bug --template "{rev}\\n"
4656 hg log -k bug --template "{rev}\\n"
4657
4657
4658 - the full hash identifier of the working directory parent::
4658 - the full hash identifier of the working directory parent::
4659
4659
4660 hg log -r . --template "{node}\\n"
4660 hg log -r . --template "{node}\\n"
4661
4661
4662 - list available log templates::
4662 - list available log templates::
4663
4663
4664 hg log -T list
4664 hg log -T list
4665
4665
4666 - check if a given changeset is included in a tagged release::
4666 - check if a given changeset is included in a tagged release::
4667
4667
4668 hg log -r "a21ccf and ancestor(1.9)"
4668 hg log -r "a21ccf and ancestor(1.9)"
4669
4669
4670 - find all changesets by some user in a date range::
4670 - find all changesets by some user in a date range::
4671
4671
4672 hg log -k alice -d "may 2008 to jul 2008"
4672 hg log -k alice -d "may 2008 to jul 2008"
4673
4673
4674 - summary of all changesets after the last tag::
4674 - summary of all changesets after the last tag::
4675
4675
4676 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4676 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4677
4677
4678 - changesets touching lines 13 to 23 for file.c::
4678 - changesets touching lines 13 to 23 for file.c::
4679
4679
4680 hg log -L file.c,13:23
4680 hg log -L file.c,13:23
4681
4681
4682 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4682 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4683 main.c with patch::
4683 main.c with patch::
4684
4684
4685 hg log -L file.c,13:23 -L main.c,2:6 -p
4685 hg log -L file.c,13:23 -L main.c,2:6 -p
4686
4686
4687 See :hg:`help dates` for a list of formats valid for -d/--date.
4687 See :hg:`help dates` for a list of formats valid for -d/--date.
4688
4688
4689 See :hg:`help revisions` for more about specifying and ordering
4689 See :hg:`help revisions` for more about specifying and ordering
4690 revisions.
4690 revisions.
4691
4691
4692 See :hg:`help templates` for more about pre-packaged styles and
4692 See :hg:`help templates` for more about pre-packaged styles and
4693 specifying custom templates. The default template used by the log
4693 specifying custom templates. The default template used by the log
4694 command can be customized via the ``command-templates.log`` configuration
4694 command can be customized via the ``command-templates.log`` configuration
4695 setting.
4695 setting.
4696
4696
4697 Returns 0 on success.
4697 Returns 0 on success.
4698
4698
4699 """
4699 """
4700 opts = pycompat.byteskwargs(opts)
4700 opts = pycompat.byteskwargs(opts)
4701 linerange = opts.get(b'line_range')
4701 linerange = opts.get(b'line_range')
4702
4702
4703 if linerange and not opts.get(b'follow'):
4703 if linerange and not opts.get(b'follow'):
4704 raise error.InputError(_(b'--line-range requires --follow'))
4704 raise error.InputError(_(b'--line-range requires --follow'))
4705
4705
4706 if linerange and pats:
4706 if linerange and pats:
4707 # TODO: take pats as patterns with no line-range filter
4707 # TODO: take pats as patterns with no line-range filter
4708 raise error.InputError(
4708 raise error.InputError(
4709 _(b'FILE arguments are not compatible with --line-range option')
4709 _(b'FILE arguments are not compatible with --line-range option')
4710 )
4710 )
4711
4711
4712 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4712 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4713 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4713 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4714 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4714 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4715 if linerange:
4715 if linerange:
4716 # TODO: should follow file history from logcmdutil._initialrevs(),
4716 # TODO: should follow file history from logcmdutil._initialrevs(),
4717 # then filter the result by logcmdutil._makerevset() and --limit
4717 # then filter the result by logcmdutil._makerevset() and --limit
4718 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4718 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4719
4719
4720 getcopies = None
4720 getcopies = None
4721 if opts.get(b'copies'):
4721 if opts.get(b'copies'):
4722 endrev = None
4722 endrev = None
4723 if revs:
4723 if revs:
4724 endrev = revs.max() + 1
4724 endrev = revs.max() + 1
4725 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4725 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4726
4726
4727 ui.pager(b'log')
4727 ui.pager(b'log')
4728 displayer = logcmdutil.changesetdisplayer(
4728 displayer = logcmdutil.changesetdisplayer(
4729 ui, repo, opts, differ, buffered=True
4729 ui, repo, opts, differ, buffered=True
4730 )
4730 )
4731 if opts.get(b'graph'):
4731 if opts.get(b'graph'):
4732 displayfn = logcmdutil.displaygraphrevs
4732 displayfn = logcmdutil.displaygraphrevs
4733 else:
4733 else:
4734 displayfn = logcmdutil.displayrevs
4734 displayfn = logcmdutil.displayrevs
4735 displayfn(ui, repo, revs, displayer, getcopies)
4735 displayfn(ui, repo, revs, displayer, getcopies)
4736
4736
4737
4737
4738 @command(
4738 @command(
4739 b'manifest',
4739 b'manifest',
4740 [
4740 [
4741 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4741 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4742 (b'', b'all', False, _(b"list files from all revisions")),
4742 (b'', b'all', False, _(b"list files from all revisions")),
4743 ]
4743 ]
4744 + formatteropts,
4744 + formatteropts,
4745 _(b'[-r REV]'),
4745 _(b'[-r REV]'),
4746 helpcategory=command.CATEGORY_MAINTENANCE,
4746 helpcategory=command.CATEGORY_MAINTENANCE,
4747 intents={INTENT_READONLY},
4747 intents={INTENT_READONLY},
4748 )
4748 )
4749 def manifest(ui, repo, node=None, rev=None, **opts):
4749 def manifest(ui, repo, node=None, rev=None, **opts):
4750 """output the current or given revision of the project manifest
4750 """output the current or given revision of the project manifest
4751
4751
4752 Print a list of version controlled files for the given revision.
4752 Print a list of version controlled files for the given revision.
4753 If no revision is given, the first parent of the working directory
4753 If no revision is given, the first parent of the working directory
4754 is used, or the null revision if no revision is checked out.
4754 is used, or the null revision if no revision is checked out.
4755
4755
4756 With -v, print file permissions, symlink and executable bits.
4756 With -v, print file permissions, symlink and executable bits.
4757 With --debug, print file revision hashes.
4757 With --debug, print file revision hashes.
4758
4758
4759 If option --all is specified, the list of all files from all revisions
4759 If option --all is specified, the list of all files from all revisions
4760 is printed. This includes deleted and renamed files.
4760 is printed. This includes deleted and renamed files.
4761
4761
4762 Returns 0 on success.
4762 Returns 0 on success.
4763 """
4763 """
4764 opts = pycompat.byteskwargs(opts)
4764 opts = pycompat.byteskwargs(opts)
4765 fm = ui.formatter(b'manifest', opts)
4765 fm = ui.formatter(b'manifest', opts)
4766
4766
4767 if opts.get(b'all'):
4767 if opts.get(b'all'):
4768 if rev or node:
4768 if rev or node:
4769 raise error.InputError(_(b"can't specify a revision with --all"))
4769 raise error.InputError(_(b"can't specify a revision with --all"))
4770
4770
4771 res = set()
4771 res = set()
4772 for rev in repo:
4772 for rev in repo:
4773 ctx = repo[rev]
4773 ctx = repo[rev]
4774 res |= set(ctx.files())
4774 res |= set(ctx.files())
4775
4775
4776 ui.pager(b'manifest')
4776 ui.pager(b'manifest')
4777 for f in sorted(res):
4777 for f in sorted(res):
4778 fm.startitem()
4778 fm.startitem()
4779 fm.write(b"path", b'%s\n', f)
4779 fm.write(b"path", b'%s\n', f)
4780 fm.end()
4780 fm.end()
4781 return
4781 return
4782
4782
4783 if rev and node:
4783 if rev and node:
4784 raise error.InputError(_(b"please specify just one revision"))
4784 raise error.InputError(_(b"please specify just one revision"))
4785
4785
4786 if not node:
4786 if not node:
4787 node = rev
4787 node = rev
4788
4788
4789 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4789 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4790 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4790 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4791 if node:
4791 if node:
4792 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4792 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4793 ctx = scmutil.revsingle(repo, node)
4793 ctx = scmutil.revsingle(repo, node)
4794 mf = ctx.manifest()
4794 mf = ctx.manifest()
4795 ui.pager(b'manifest')
4795 ui.pager(b'manifest')
4796 for f in ctx:
4796 for f in ctx:
4797 fm.startitem()
4797 fm.startitem()
4798 fm.context(ctx=ctx)
4798 fm.context(ctx=ctx)
4799 fl = ctx[f].flags()
4799 fl = ctx[f].flags()
4800 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4800 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4801 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4801 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4802 fm.write(b'path', b'%s\n', f)
4802 fm.write(b'path', b'%s\n', f)
4803 fm.end()
4803 fm.end()
4804
4804
4805
4805
4806 @command(
4806 @command(
4807 b'merge',
4807 b'merge',
4808 [
4808 [
4809 (
4809 (
4810 b'f',
4810 b'f',
4811 b'force',
4811 b'force',
4812 None,
4812 None,
4813 _(b'force a merge including outstanding changes (DEPRECATED)'),
4813 _(b'force a merge including outstanding changes (DEPRECATED)'),
4814 ),
4814 ),
4815 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4815 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4816 (
4816 (
4817 b'P',
4817 b'P',
4818 b'preview',
4818 b'preview',
4819 None,
4819 None,
4820 _(b'review revisions to merge (no merge is performed)'),
4820 _(b'review revisions to merge (no merge is performed)'),
4821 ),
4821 ),
4822 (b'', b'abort', None, _(b'abort the ongoing merge')),
4822 (b'', b'abort', None, _(b'abort the ongoing merge')),
4823 ]
4823 ]
4824 + mergetoolopts,
4824 + mergetoolopts,
4825 _(b'[-P] [[-r] REV]'),
4825 _(b'[-P] [[-r] REV]'),
4826 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4826 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4827 helpbasic=True,
4827 helpbasic=True,
4828 )
4828 )
4829 def merge(ui, repo, node=None, **opts):
4829 def merge(ui, repo, node=None, **opts):
4830 """merge another revision into working directory
4830 """merge another revision into working directory
4831
4831
4832 The current working directory is updated with all changes made in
4832 The current working directory is updated with all changes made in
4833 the requested revision since the last common predecessor revision.
4833 the requested revision since the last common predecessor revision.
4834
4834
4835 Files that changed between either parent are marked as changed for
4835 Files that changed between either parent are marked as changed for
4836 the next commit and a commit must be performed before any further
4836 the next commit and a commit must be performed before any further
4837 updates to the repository are allowed. The next commit will have
4837 updates to the repository are allowed. The next commit will have
4838 two parents.
4838 two parents.
4839
4839
4840 ``--tool`` can be used to specify the merge tool used for file
4840 ``--tool`` can be used to specify the merge tool used for file
4841 merges. It overrides the HGMERGE environment variable and your
4841 merges. It overrides the HGMERGE environment variable and your
4842 configuration files. See :hg:`help merge-tools` for options.
4842 configuration files. See :hg:`help merge-tools` for options.
4843
4843
4844 If no revision is specified, the working directory's parent is a
4844 If no revision is specified, the working directory's parent is a
4845 head revision, and the current branch contains exactly one other
4845 head revision, and the current branch contains exactly one other
4846 head, the other head is merged with by default. Otherwise, an
4846 head, the other head is merged with by default. Otherwise, an
4847 explicit revision with which to merge must be provided.
4847 explicit revision with which to merge must be provided.
4848
4848
4849 See :hg:`help resolve` for information on handling file conflicts.
4849 See :hg:`help resolve` for information on handling file conflicts.
4850
4850
4851 To undo an uncommitted merge, use :hg:`merge --abort` which
4851 To undo an uncommitted merge, use :hg:`merge --abort` which
4852 will check out a clean copy of the original merge parent, losing
4852 will check out a clean copy of the original merge parent, losing
4853 all changes.
4853 all changes.
4854
4854
4855 Returns 0 on success, 1 if there are unresolved files.
4855 Returns 0 on success, 1 if there are unresolved files.
4856 """
4856 """
4857
4857
4858 opts = pycompat.byteskwargs(opts)
4858 opts = pycompat.byteskwargs(opts)
4859 abort = opts.get(b'abort')
4859 abort = opts.get(b'abort')
4860 if abort and repo.dirstate.p2() == repo.nullid:
4860 if abort and repo.dirstate.p2() == repo.nullid:
4861 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4861 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4862 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4862 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4863 if abort:
4863 if abort:
4864 state = cmdutil.getunfinishedstate(repo)
4864 state = cmdutil.getunfinishedstate(repo)
4865 if state and state._opname != b'merge':
4865 if state and state._opname != b'merge':
4866 raise error.StateError(
4866 raise error.StateError(
4867 _(b'cannot abort merge with %s in progress') % (state._opname),
4867 _(b'cannot abort merge with %s in progress') % (state._opname),
4868 hint=state.hint(),
4868 hint=state.hint(),
4869 )
4869 )
4870 if node:
4870 if node:
4871 raise error.InputError(_(b"cannot specify a node with --abort"))
4871 raise error.InputError(_(b"cannot specify a node with --abort"))
4872 return hg.abortmerge(repo.ui, repo)
4872 return hg.abortmerge(repo.ui, repo)
4873
4873
4874 if opts.get(b'rev') and node:
4874 if opts.get(b'rev') and node:
4875 raise error.InputError(_(b"please specify just one revision"))
4875 raise error.InputError(_(b"please specify just one revision"))
4876 if not node:
4876 if not node:
4877 node = opts.get(b'rev')
4877 node = opts.get(b'rev')
4878
4878
4879 if node:
4879 if node:
4880 ctx = scmutil.revsingle(repo, node)
4880 ctx = scmutil.revsingle(repo, node)
4881 else:
4881 else:
4882 if ui.configbool(b'commands', b'merge.require-rev'):
4882 if ui.configbool(b'commands', b'merge.require-rev'):
4883 raise error.InputError(
4883 raise error.InputError(
4884 _(
4884 _(
4885 b'configuration requires specifying revision to merge '
4885 b'configuration requires specifying revision to merge '
4886 b'with'
4886 b'with'
4887 )
4887 )
4888 )
4888 )
4889 ctx = repo[destutil.destmerge(repo)]
4889 ctx = repo[destutil.destmerge(repo)]
4890
4890
4891 if ctx.node() is None:
4891 if ctx.node() is None:
4892 raise error.InputError(
4892 raise error.InputError(
4893 _(b'merging with the working copy has no effect')
4893 _(b'merging with the working copy has no effect')
4894 )
4894 )
4895
4895
4896 if opts.get(b'preview'):
4896 if opts.get(b'preview'):
4897 # find nodes that are ancestors of p2 but not of p1
4897 # find nodes that are ancestors of p2 but not of p1
4898 p1 = repo[b'.'].node()
4898 p1 = repo[b'.'].node()
4899 p2 = ctx.node()
4899 p2 = ctx.node()
4900 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4900 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4901
4901
4902 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4902 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4903 for node in nodes:
4903 for node in nodes:
4904 displayer.show(repo[node])
4904 displayer.show(repo[node])
4905 displayer.close()
4905 displayer.close()
4906 return 0
4906 return 0
4907
4907
4908 # ui.forcemerge is an internal variable, do not document
4908 # ui.forcemerge is an internal variable, do not document
4909 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4909 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4910 with ui.configoverride(overrides, b'merge'):
4910 with ui.configoverride(overrides, b'merge'):
4911 force = opts.get(b'force')
4911 force = opts.get(b'force')
4912 labels = [b'working copy', b'merge rev']
4912 labels = [b'working copy', b'merge rev']
4913 return hg.merge(ctx, force=force, labels=labels)
4913 return hg.merge(ctx, force=force, labels=labels)
4914
4914
4915
4915
4916 statemod.addunfinished(
4916 statemod.addunfinished(
4917 b'merge',
4917 b'merge',
4918 fname=None,
4918 fname=None,
4919 clearable=True,
4919 clearable=True,
4920 allowcommit=True,
4920 allowcommit=True,
4921 cmdmsg=_(b'outstanding uncommitted merge'),
4921 cmdmsg=_(b'outstanding uncommitted merge'),
4922 abortfunc=hg.abortmerge,
4922 abortfunc=hg.abortmerge,
4923 statushint=_(
4923 statushint=_(
4924 b'To continue: hg commit\nTo abort: hg merge --abort'
4924 b'To continue: hg commit\nTo abort: hg merge --abort'
4925 ),
4925 ),
4926 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4926 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4927 )
4927 )
4928
4928
4929
4929
4930 @command(
4930 @command(
4931 b'outgoing|out',
4931 b'outgoing|out',
4932 [
4932 [
4933 (
4933 (
4934 b'f',
4934 b'f',
4935 b'force',
4935 b'force',
4936 None,
4936 None,
4937 _(b'run even when the destination is unrelated'),
4937 _(b'run even when the destination is unrelated'),
4938 ),
4938 ),
4939 (
4939 (
4940 b'r',
4940 b'r',
4941 b'rev',
4941 b'rev',
4942 [],
4942 [],
4943 _(b'a changeset intended to be included in the destination'),
4943 _(b'a changeset intended to be included in the destination'),
4944 _(b'REV'),
4944 _(b'REV'),
4945 ),
4945 ),
4946 (b'n', b'newest-first', None, _(b'show newest record first')),
4946 (b'n', b'newest-first', None, _(b'show newest record first')),
4947 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4947 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4948 (
4948 (
4949 b'b',
4949 b'b',
4950 b'branch',
4950 b'branch',
4951 [],
4951 [],
4952 _(b'a specific branch you would like to push'),
4952 _(b'a specific branch you would like to push'),
4953 _(b'BRANCH'),
4953 _(b'BRANCH'),
4954 ),
4954 ),
4955 ]
4955 ]
4956 + logopts
4956 + logopts
4957 + remoteopts
4957 + remoteopts
4958 + subrepoopts,
4958 + subrepoopts,
4959 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4959 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4960 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4960 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4961 )
4961 )
4962 def outgoing(ui, repo, *dests, **opts):
4962 def outgoing(ui, repo, *dests, **opts):
4963 """show changesets not found in the destination
4963 """show changesets not found in the destination
4964
4964
4965 Show changesets not found in the specified destination repository
4965 Show changesets not found in the specified destination repository
4966 or the default push location. These are the changesets that would
4966 or the default push location. These are the changesets that would
4967 be pushed if a push was requested.
4967 be pushed if a push was requested.
4968
4968
4969 See pull for details of valid destination formats.
4969 See pull for details of valid destination formats.
4970
4970
4971 .. container:: verbose
4971 .. container:: verbose
4972
4972
4973 With -B/--bookmarks, the result of bookmark comparison between
4973 With -B/--bookmarks, the result of bookmark comparison between
4974 local and remote repositories is displayed. With -v/--verbose,
4974 local and remote repositories is displayed. With -v/--verbose,
4975 status is also displayed for each bookmark like below::
4975 status is also displayed for each bookmark like below::
4976
4976
4977 BM1 01234567890a added
4977 BM1 01234567890a added
4978 BM2 deleted
4978 BM2 deleted
4979 BM3 234567890abc advanced
4979 BM3 234567890abc advanced
4980 BM4 34567890abcd diverged
4980 BM4 34567890abcd diverged
4981 BM5 4567890abcde changed
4981 BM5 4567890abcde changed
4982
4982
4983 The action taken when pushing depends on the
4983 The action taken when pushing depends on the
4984 status of each bookmark:
4984 status of each bookmark:
4985
4985
4986 :``added``: push with ``-B`` will create it
4986 :``added``: push with ``-B`` will create it
4987 :``deleted``: push with ``-B`` will delete it
4987 :``deleted``: push with ``-B`` will delete it
4988 :``advanced``: push will update it
4988 :``advanced``: push will update it
4989 :``diverged``: push with ``-B`` will update it
4989 :``diverged``: push with ``-B`` will update it
4990 :``changed``: push with ``-B`` will update it
4990 :``changed``: push with ``-B`` will update it
4991
4991
4992 From the point of view of pushing behavior, bookmarks
4992 From the point of view of pushing behavior, bookmarks
4993 existing only in the remote repository are treated as
4993 existing only in the remote repository are treated as
4994 ``deleted``, even if it is in fact added remotely.
4994 ``deleted``, even if it is in fact added remotely.
4995
4995
4996 Returns 0 if there are outgoing changes, 1 otherwise.
4996 Returns 0 if there are outgoing changes, 1 otherwise.
4997 """
4997 """
4998 opts = pycompat.byteskwargs(opts)
4998 opts = pycompat.byteskwargs(opts)
4999 if opts.get(b'bookmarks'):
4999 if opts.get(b'bookmarks'):
5000 for path in urlutil.get_push_paths(repo, ui, dests):
5000 for path in urlutil.get_push_paths(repo, ui, dests):
5001 dest = path.pushloc or path.loc
5001 dest = path.pushloc or path.loc
5002 other = hg.peer(repo, opts, dest)
5002 other = hg.peer(repo, opts, dest)
5003 try:
5003 try:
5004 if b'bookmarks' not in other.listkeys(b'namespaces'):
5004 if b'bookmarks' not in other.listkeys(b'namespaces'):
5005 ui.warn(_(b"remote doesn't support bookmarks\n"))
5005 ui.warn(_(b"remote doesn't support bookmarks\n"))
5006 return 0
5006 return 0
5007 ui.status(
5007 ui.status(
5008 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5008 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5009 )
5009 )
5010 ui.pager(b'outgoing')
5010 ui.pager(b'outgoing')
5011 return bookmarks.outgoing(ui, repo, other)
5011 return bookmarks.outgoing(ui, repo, other)
5012 finally:
5012 finally:
5013 other.close()
5013 other.close()
5014
5014
5015 return hg.outgoing(ui, repo, dests, opts)
5015 return hg.outgoing(ui, repo, dests, opts)
5016
5016
5017
5017
5018 @command(
5018 @command(
5019 b'parents',
5019 b'parents',
5020 [
5020 [
5021 (
5021 (
5022 b'r',
5022 b'r',
5023 b'rev',
5023 b'rev',
5024 b'',
5024 b'',
5025 _(b'show parents of the specified revision'),
5025 _(b'show parents of the specified revision'),
5026 _(b'REV'),
5026 _(b'REV'),
5027 ),
5027 ),
5028 ]
5028 ]
5029 + templateopts,
5029 + templateopts,
5030 _(b'[-r REV] [FILE]'),
5030 _(b'[-r REV] [FILE]'),
5031 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5031 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5032 inferrepo=True,
5032 inferrepo=True,
5033 )
5033 )
5034 def parents(ui, repo, file_=None, **opts):
5034 def parents(ui, repo, file_=None, **opts):
5035 """show the parents of the working directory or revision (DEPRECATED)
5035 """show the parents of the working directory or revision (DEPRECATED)
5036
5036
5037 Print the working directory's parent revisions. If a revision is
5037 Print the working directory's parent revisions. If a revision is
5038 given via -r/--rev, the parent of that revision will be printed.
5038 given via -r/--rev, the parent of that revision will be printed.
5039 If a file argument is given, the revision in which the file was
5039 If a file argument is given, the revision in which the file was
5040 last changed (before the working directory revision or the
5040 last changed (before the working directory revision or the
5041 argument to --rev if given) is printed.
5041 argument to --rev if given) is printed.
5042
5042
5043 This command is equivalent to::
5043 This command is equivalent to::
5044
5044
5045 hg log -r "p1()+p2()" or
5045 hg log -r "p1()+p2()" or
5046 hg log -r "p1(REV)+p2(REV)" or
5046 hg log -r "p1(REV)+p2(REV)" or
5047 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5047 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5048 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5048 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5049
5049
5050 See :hg:`summary` and :hg:`help revsets` for related information.
5050 See :hg:`summary` and :hg:`help revsets` for related information.
5051
5051
5052 Returns 0 on success.
5052 Returns 0 on success.
5053 """
5053 """
5054
5054
5055 opts = pycompat.byteskwargs(opts)
5055 opts = pycompat.byteskwargs(opts)
5056 rev = opts.get(b'rev')
5056 rev = opts.get(b'rev')
5057 if rev:
5057 if rev:
5058 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5058 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5059 ctx = scmutil.revsingle(repo, rev, None)
5059 ctx = scmutil.revsingle(repo, rev, None)
5060
5060
5061 if file_:
5061 if file_:
5062 m = scmutil.match(ctx, (file_,), opts)
5062 m = scmutil.match(ctx, (file_,), opts)
5063 if m.anypats() or len(m.files()) != 1:
5063 if m.anypats() or len(m.files()) != 1:
5064 raise error.InputError(_(b'can only specify an explicit filename'))
5064 raise error.InputError(_(b'can only specify an explicit filename'))
5065 file_ = m.files()[0]
5065 file_ = m.files()[0]
5066 filenodes = []
5066 filenodes = []
5067 for cp in ctx.parents():
5067 for cp in ctx.parents():
5068 if not cp:
5068 if not cp:
5069 continue
5069 continue
5070 try:
5070 try:
5071 filenodes.append(cp.filenode(file_))
5071 filenodes.append(cp.filenode(file_))
5072 except error.LookupError:
5072 except error.LookupError:
5073 pass
5073 pass
5074 if not filenodes:
5074 if not filenodes:
5075 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5075 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5076 p = []
5076 p = []
5077 for fn in filenodes:
5077 for fn in filenodes:
5078 fctx = repo.filectx(file_, fileid=fn)
5078 fctx = repo.filectx(file_, fileid=fn)
5079 p.append(fctx.node())
5079 p.append(fctx.node())
5080 else:
5080 else:
5081 p = [cp.node() for cp in ctx.parents()]
5081 p = [cp.node() for cp in ctx.parents()]
5082
5082
5083 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5083 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5084 for n in p:
5084 for n in p:
5085 if n != repo.nullid:
5085 if n != repo.nullid:
5086 displayer.show(repo[n])
5086 displayer.show(repo[n])
5087 displayer.close()
5087 displayer.close()
5088
5088
5089
5089
5090 @command(
5090 @command(
5091 b'paths',
5091 b'paths',
5092 formatteropts,
5092 formatteropts,
5093 _(b'[NAME]'),
5093 _(b'[NAME]'),
5094 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5094 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5095 optionalrepo=True,
5095 optionalrepo=True,
5096 intents={INTENT_READONLY},
5096 intents={INTENT_READONLY},
5097 )
5097 )
5098 def paths(ui, repo, search=None, **opts):
5098 def paths(ui, repo, search=None, **opts):
5099 """show aliases for remote repositories
5099 """show aliases for remote repositories
5100
5100
5101 Show definition of symbolic path name NAME. If no name is given,
5101 Show definition of symbolic path name NAME. If no name is given,
5102 show definition of all available names.
5102 show definition of all available names.
5103
5103
5104 Option -q/--quiet suppresses all output when searching for NAME
5104 Option -q/--quiet suppresses all output when searching for NAME
5105 and shows only the path names when listing all definitions.
5105 and shows only the path names when listing all definitions.
5106
5106
5107 Path names are defined in the [paths] section of your
5107 Path names are defined in the [paths] section of your
5108 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5108 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5109 repository, ``.hg/hgrc`` is used, too.
5109 repository, ``.hg/hgrc`` is used, too.
5110
5110
5111 The path names ``default`` and ``default-push`` have a special
5111 The path names ``default`` and ``default-push`` have a special
5112 meaning. When performing a push or pull operation, they are used
5112 meaning. When performing a push or pull operation, they are used
5113 as fallbacks if no location is specified on the command-line.
5113 as fallbacks if no location is specified on the command-line.
5114 When ``default-push`` is set, it will be used for push and
5114 When ``default-push`` is set, it will be used for push and
5115 ``default`` will be used for pull; otherwise ``default`` is used
5115 ``default`` will be used for pull; otherwise ``default`` is used
5116 as the fallback for both. When cloning a repository, the clone
5116 as the fallback for both. When cloning a repository, the clone
5117 source is written as ``default`` in ``.hg/hgrc``.
5117 source is written as ``default`` in ``.hg/hgrc``.
5118
5118
5119 .. note::
5119 .. note::
5120
5120
5121 ``default`` and ``default-push`` apply to all inbound (e.g.
5121 ``default`` and ``default-push`` apply to all inbound (e.g.
5122 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5122 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5123 and :hg:`bundle`) operations.
5123 and :hg:`bundle`) operations.
5124
5124
5125 See :hg:`help urls` for more information.
5125 See :hg:`help urls` for more information.
5126
5126
5127 .. container:: verbose
5127 .. container:: verbose
5128
5128
5129 Template:
5129 Template:
5130
5130
5131 The following keywords are supported. See also :hg:`help templates`.
5131 The following keywords are supported. See also :hg:`help templates`.
5132
5132
5133 :name: String. Symbolic name of the path alias.
5133 :name: String. Symbolic name of the path alias.
5134 :pushurl: String. URL for push operations.
5134 :pushurl: String. URL for push operations.
5135 :url: String. URL or directory path for the other operations.
5135 :url: String. URL or directory path for the other operations.
5136
5136
5137 Returns 0 on success.
5137 Returns 0 on success.
5138 """
5138 """
5139
5139
5140 opts = pycompat.byteskwargs(opts)
5140 opts = pycompat.byteskwargs(opts)
5141
5141
5142 pathitems = urlutil.list_paths(ui, search)
5142 pathitems = urlutil.list_paths(ui, search)
5143 ui.pager(b'paths')
5143 ui.pager(b'paths')
5144
5144
5145 fm = ui.formatter(b'paths', opts)
5145 fm = ui.formatter(b'paths', opts)
5146 if fm.isplain():
5146 if fm.isplain():
5147 hidepassword = urlutil.hidepassword
5147 hidepassword = urlutil.hidepassword
5148 else:
5148 else:
5149 hidepassword = bytes
5149 hidepassword = bytes
5150 if ui.quiet:
5150 if ui.quiet:
5151 namefmt = b'%s\n'
5151 namefmt = b'%s\n'
5152 else:
5152 else:
5153 namefmt = b'%s = '
5153 namefmt = b'%s = '
5154 showsubopts = not search and not ui.quiet
5154 showsubopts = not search and not ui.quiet
5155
5155
5156 for name, path in pathitems:
5156 for name, path in pathitems:
5157 fm.startitem()
5157 fm.startitem()
5158 fm.condwrite(not search, b'name', namefmt, name)
5158 fm.condwrite(not search, b'name', namefmt, name)
5159 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5159 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5160 for subopt, value in sorted(path.suboptions.items()):
5160 for subopt, value in sorted(path.suboptions.items()):
5161 assert subopt not in (b'name', b'url')
5161 assert subopt not in (b'name', b'url')
5162 if showsubopts:
5162 if showsubopts:
5163 fm.plain(b'%s:%s = ' % (name, subopt))
5163 fm.plain(b'%s:%s = ' % (name, subopt))
5164 if isinstance(value, bool):
5164 if isinstance(value, bool):
5165 if value:
5165 if value:
5166 value = b'yes'
5166 value = b'yes'
5167 else:
5167 else:
5168 value = b'no'
5168 value = b'no'
5169 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5169 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5170
5170
5171 fm.end()
5171 fm.end()
5172
5172
5173 if search and not pathitems:
5173 if search and not pathitems:
5174 if not ui.quiet:
5174 if not ui.quiet:
5175 ui.warn(_(b"not found!\n"))
5175 ui.warn(_(b"not found!\n"))
5176 return 1
5176 return 1
5177 else:
5177 else:
5178 return 0
5178 return 0
5179
5179
5180
5180
5181 @command(
5181 @command(
5182 b'phase',
5182 b'phase',
5183 [
5183 [
5184 (b'p', b'public', False, _(b'set changeset phase to public')),
5184 (b'p', b'public', False, _(b'set changeset phase to public')),
5185 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5185 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5186 (b's', b'secret', False, _(b'set changeset phase to secret')),
5186 (b's', b'secret', False, _(b'set changeset phase to secret')),
5187 (b'f', b'force', False, _(b'allow to move boundary backward')),
5187 (b'f', b'force', False, _(b'allow to move boundary backward')),
5188 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5188 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5189 ],
5189 ],
5190 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5190 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5191 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5191 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5192 )
5192 )
5193 def phase(ui, repo, *revs, **opts):
5193 def phase(ui, repo, *revs, **opts):
5194 """set or show the current phase name
5194 """set or show the current phase name
5195
5195
5196 With no argument, show the phase name of the current revision(s).
5196 With no argument, show the phase name of the current revision(s).
5197
5197
5198 With one of -p/--public, -d/--draft or -s/--secret, change the
5198 With one of -p/--public, -d/--draft or -s/--secret, change the
5199 phase value of the specified revisions.
5199 phase value of the specified revisions.
5200
5200
5201 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5201 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5202 lower phase to a higher phase. Phases are ordered as follows::
5202 lower phase to a higher phase. Phases are ordered as follows::
5203
5203
5204 public < draft < secret
5204 public < draft < secret
5205
5205
5206 Returns 0 on success, 1 if some phases could not be changed.
5206 Returns 0 on success, 1 if some phases could not be changed.
5207
5207
5208 (For more information about the phases concept, see :hg:`help phases`.)
5208 (For more information about the phases concept, see :hg:`help phases`.)
5209 """
5209 """
5210 opts = pycompat.byteskwargs(opts)
5210 opts = pycompat.byteskwargs(opts)
5211 # search for a unique phase argument
5211 # search for a unique phase argument
5212 targetphase = None
5212 targetphase = None
5213 for idx, name in enumerate(phases.cmdphasenames):
5213 for idx, name in enumerate(phases.cmdphasenames):
5214 if opts[name]:
5214 if opts[name]:
5215 if targetphase is not None:
5215 if targetphase is not None:
5216 raise error.InputError(_(b'only one phase can be specified'))
5216 raise error.InputError(_(b'only one phase can be specified'))
5217 targetphase = idx
5217 targetphase = idx
5218
5218
5219 # look for specified revision
5219 # look for specified revision
5220 revs = list(revs)
5220 revs = list(revs)
5221 revs.extend(opts[b'rev'])
5221 revs.extend(opts[b'rev'])
5222 if not revs:
5222 if not revs:
5223 # display both parents as the second parent phase can influence
5223 # display both parents as the second parent phase can influence
5224 # the phase of a merge commit
5224 # the phase of a merge commit
5225 revs = [c.rev() for c in repo[None].parents()]
5225 revs = [c.rev() for c in repo[None].parents()]
5226
5226
5227 revs = scmutil.revrange(repo, revs)
5227 revs = scmutil.revrange(repo, revs)
5228
5228
5229 ret = 0
5229 ret = 0
5230 if targetphase is None:
5230 if targetphase is None:
5231 # display
5231 # display
5232 for r in revs:
5232 for r in revs:
5233 ctx = repo[r]
5233 ctx = repo[r]
5234 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5234 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5235 else:
5235 else:
5236 with repo.lock(), repo.transaction(b"phase") as tr:
5236 with repo.lock(), repo.transaction(b"phase") as tr:
5237 # set phase
5237 # set phase
5238 if not revs:
5238 if not revs:
5239 raise error.InputError(_(b'empty revision set'))
5239 raise error.InputError(_(b'empty revision set'))
5240 nodes = [repo[r].node() for r in revs]
5240 nodes = [repo[r].node() for r in revs]
5241 # moving revision from public to draft may hide them
5241 # moving revision from public to draft may hide them
5242 # We have to check result on an unfiltered repository
5242 # We have to check result on an unfiltered repository
5243 unfi = repo.unfiltered()
5243 unfi = repo.unfiltered()
5244 getphase = unfi._phasecache.phase
5244 getphase = unfi._phasecache.phase
5245 olddata = [getphase(unfi, r) for r in unfi]
5245 olddata = [getphase(unfi, r) for r in unfi]
5246 phases.advanceboundary(repo, tr, targetphase, nodes)
5246 phases.advanceboundary(repo, tr, targetphase, nodes)
5247 if opts[b'force']:
5247 if opts[b'force']:
5248 phases.retractboundary(repo, tr, targetphase, nodes)
5248 phases.retractboundary(repo, tr, targetphase, nodes)
5249 getphase = unfi._phasecache.phase
5249 getphase = unfi._phasecache.phase
5250 newdata = [getphase(unfi, r) for r in unfi]
5250 newdata = [getphase(unfi, r) for r in unfi]
5251 changes = sum(newdata[r] != olddata[r] for r in unfi)
5251 changes = sum(newdata[r] != olddata[r] for r in unfi)
5252 cl = unfi.changelog
5252 cl = unfi.changelog
5253 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5253 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5254 if rejected:
5254 if rejected:
5255 ui.warn(
5255 ui.warn(
5256 _(
5256 _(
5257 b'cannot move %i changesets to a higher '
5257 b'cannot move %i changesets to a higher '
5258 b'phase, use --force\n'
5258 b'phase, use --force\n'
5259 )
5259 )
5260 % len(rejected)
5260 % len(rejected)
5261 )
5261 )
5262 ret = 1
5262 ret = 1
5263 if changes:
5263 if changes:
5264 msg = _(b'phase changed for %i changesets\n') % changes
5264 msg = _(b'phase changed for %i changesets\n') % changes
5265 if ret:
5265 if ret:
5266 ui.status(msg)
5266 ui.status(msg)
5267 else:
5267 else:
5268 ui.note(msg)
5268 ui.note(msg)
5269 else:
5269 else:
5270 ui.warn(_(b'no phases changed\n'))
5270 ui.warn(_(b'no phases changed\n'))
5271 return ret
5271 return ret
5272
5272
5273
5273
5274 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5274 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5275 """Run after a changegroup has been added via pull/unbundle
5275 """Run after a changegroup has been added via pull/unbundle
5276
5276
5277 This takes arguments below:
5277 This takes arguments below:
5278
5278
5279 :modheads: change of heads by pull/unbundle
5279 :modheads: change of heads by pull/unbundle
5280 :optupdate: updating working directory is needed or not
5280 :optupdate: updating working directory is needed or not
5281 :checkout: update destination revision (or None to default destination)
5281 :checkout: update destination revision (or None to default destination)
5282 :brev: a name, which might be a bookmark to be activated after updating
5282 :brev: a name, which might be a bookmark to be activated after updating
5283
5283
5284 return True if update raise any conflict, False otherwise.
5284 return True if update raise any conflict, False otherwise.
5285 """
5285 """
5286 if modheads == 0:
5286 if modheads == 0:
5287 return False
5287 return False
5288 if optupdate:
5288 if optupdate:
5289 try:
5289 try:
5290 return hg.updatetotally(ui, repo, checkout, brev)
5290 return hg.updatetotally(ui, repo, checkout, brev)
5291 except error.UpdateAbort as inst:
5291 except error.UpdateAbort as inst:
5292 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5292 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5293 hint = inst.hint
5293 hint = inst.hint
5294 raise error.UpdateAbort(msg, hint=hint)
5294 raise error.UpdateAbort(msg, hint=hint)
5295 if modheads is not None and modheads > 1:
5295 if modheads is not None and modheads > 1:
5296 currentbranchheads = len(repo.branchheads())
5296 currentbranchheads = len(repo.branchheads())
5297 if currentbranchheads == modheads:
5297 if currentbranchheads == modheads:
5298 ui.status(
5298 ui.status(
5299 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5299 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5300 )
5300 )
5301 elif currentbranchheads > 1:
5301 elif currentbranchheads > 1:
5302 ui.status(
5302 ui.status(
5303 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5303 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5304 )
5304 )
5305 else:
5305 else:
5306 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5306 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5307 elif not ui.configbool(b'commands', b'update.requiredest'):
5307 elif not ui.configbool(b'commands', b'update.requiredest'):
5308 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5308 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5309 return False
5309 return False
5310
5310
5311
5311
5312 @command(
5312 @command(
5313 b'pull',
5313 b'pull',
5314 [
5314 [
5315 (
5315 (
5316 b'u',
5316 b'u',
5317 b'update',
5317 b'update',
5318 None,
5318 None,
5319 _(b'update to new branch head if new descendants were pulled'),
5319 _(b'update to new branch head if new descendants were pulled'),
5320 ),
5320 ),
5321 (
5321 (
5322 b'f',
5322 b'f',
5323 b'force',
5323 b'force',
5324 None,
5324 None,
5325 _(b'run even when remote repository is unrelated'),
5325 _(b'run even when remote repository is unrelated'),
5326 ),
5326 ),
5327 (
5327 (
5328 b'',
5328 b'',
5329 b'confirm',
5329 b'confirm',
5330 None,
5330 None,
5331 _(b'confirm pull before applying changes'),
5331 _(b'confirm pull before applying changes'),
5332 ),
5332 ),
5333 (
5333 (
5334 b'r',
5334 b'r',
5335 b'rev',
5335 b'rev',
5336 [],
5336 [],
5337 _(b'a remote changeset intended to be added'),
5337 _(b'a remote changeset intended to be added'),
5338 _(b'REV'),
5338 _(b'REV'),
5339 ),
5339 ),
5340 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5340 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5341 (
5341 (
5342 b'b',
5342 b'b',
5343 b'branch',
5343 b'branch',
5344 [],
5344 [],
5345 _(b'a specific branch you would like to pull'),
5345 _(b'a specific branch you would like to pull'),
5346 _(b'BRANCH'),
5346 _(b'BRANCH'),
5347 ),
5347 ),
5348 ]
5348 ]
5349 + remoteopts,
5349 + remoteopts,
5350 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5350 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5351 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5351 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5352 helpbasic=True,
5352 helpbasic=True,
5353 )
5353 )
5354 def pull(ui, repo, *sources, **opts):
5354 def pull(ui, repo, *sources, **opts):
5355 """pull changes from the specified source
5355 """pull changes from the specified source
5356
5356
5357 Pull changes from a remote repository to a local one.
5357 Pull changes from a remote repository to a local one.
5358
5358
5359 This finds all changes from the repository at the specified path
5359 This finds all changes from the repository at the specified path
5360 or URL and adds them to a local repository (the current one unless
5360 or URL and adds them to a local repository (the current one unless
5361 -R is specified). By default, this does not update the copy of the
5361 -R is specified). By default, this does not update the copy of the
5362 project in the working directory.
5362 project in the working directory.
5363
5363
5364 When cloning from servers that support it, Mercurial may fetch
5364 When cloning from servers that support it, Mercurial may fetch
5365 pre-generated data. When this is done, hooks operating on incoming
5365 pre-generated data. When this is done, hooks operating on incoming
5366 changesets and changegroups may fire more than once, once for each
5366 changesets and changegroups may fire more than once, once for each
5367 pre-generated bundle and as well as for any additional remaining
5367 pre-generated bundle and as well as for any additional remaining
5368 data. See :hg:`help -e clonebundles` for more.
5368 data. See :hg:`help -e clonebundles` for more.
5369
5369
5370 Use :hg:`incoming` if you want to see what would have been added
5370 Use :hg:`incoming` if you want to see what would have been added
5371 by a pull at the time you issued this command. If you then decide
5371 by a pull at the time you issued this command. If you then decide
5372 to add those changes to the repository, you should use :hg:`pull
5372 to add those changes to the repository, you should use :hg:`pull
5373 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5373 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5374
5374
5375 If SOURCE is omitted, the 'default' path will be used.
5375 If SOURCE is omitted, the 'default' path will be used.
5376 See :hg:`help urls` for more information.
5376 See :hg:`help urls` for more information.
5377
5377
5378 If multiple sources are specified, they will be pulled sequentially as if
5378 If multiple sources are specified, they will be pulled sequentially as if
5379 the command was run multiple time. If --update is specify and the command
5379 the command was run multiple time. If --update is specify and the command
5380 will stop at the first failed --update.
5380 will stop at the first failed --update.
5381
5381
5382 Specifying bookmark as ``.`` is equivalent to specifying the active
5382 Specifying bookmark as ``.`` is equivalent to specifying the active
5383 bookmark's name.
5383 bookmark's name.
5384
5384
5385 Returns 0 on success, 1 if an update had unresolved files.
5385 Returns 0 on success, 1 if an update had unresolved files.
5386 """
5386 """
5387
5387
5388 opts = pycompat.byteskwargs(opts)
5388 opts = pycompat.byteskwargs(opts)
5389 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5389 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5390 b'update'
5390 b'update'
5391 ):
5391 ):
5392 msg = _(b'update destination required by configuration')
5392 msg = _(b'update destination required by configuration')
5393 hint = _(b'use hg pull followed by hg update DEST')
5393 hint = _(b'use hg pull followed by hg update DEST')
5394 raise error.InputError(msg, hint=hint)
5394 raise error.InputError(msg, hint=hint)
5395
5395
5396 sources = urlutil.get_pull_paths(repo, ui, sources, opts.get(b'branch'))
5396 sources = urlutil.get_pull_paths(repo, ui, sources, opts.get(b'branch'))
5397 for source, branches in sources:
5397 for source, branches in sources:
5398 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5398 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5399 ui.flush()
5399 ui.flush()
5400 other = hg.peer(repo, opts, source)
5400 other = hg.peer(repo, opts, source)
5401 update_conflict = None
5401 update_conflict = None
5402 try:
5402 try:
5403 revs, checkout = hg.addbranchrevs(
5403 revs, checkout = hg.addbranchrevs(
5404 repo, other, branches, opts.get(b'rev')
5404 repo, other, branches, opts.get(b'rev')
5405 )
5405 )
5406
5406
5407 pullopargs = {}
5407 pullopargs = {}
5408
5408
5409 nodes = None
5409 nodes = None
5410 if opts.get(b'bookmark') or revs:
5410 if opts.get(b'bookmark') or revs:
5411 # The list of bookmark used here is the same used to actually update
5411 # The list of bookmark used here is the same used to actually update
5412 # the bookmark names, to avoid the race from issue 4689 and we do
5412 # the bookmark names, to avoid the race from issue 4689 and we do
5413 # all lookup and bookmark queries in one go so they see the same
5413 # all lookup and bookmark queries in one go so they see the same
5414 # version of the server state (issue 4700).
5414 # version of the server state (issue 4700).
5415 nodes = []
5415 nodes = []
5416 fnodes = []
5416 fnodes = []
5417 revs = revs or []
5417 revs = revs or []
5418 if revs and not other.capable(b'lookup'):
5418 if revs and not other.capable(b'lookup'):
5419 err = _(
5419 err = _(
5420 b"other repository doesn't support revision lookup, "
5420 b"other repository doesn't support revision lookup, "
5421 b"so a rev cannot be specified."
5421 b"so a rev cannot be specified."
5422 )
5422 )
5423 raise error.Abort(err)
5423 raise error.Abort(err)
5424 with other.commandexecutor() as e:
5424 with other.commandexecutor() as e:
5425 fremotebookmarks = e.callcommand(
5425 fremotebookmarks = e.callcommand(
5426 b'listkeys', {b'namespace': b'bookmarks'}
5426 b'listkeys', {b'namespace': b'bookmarks'}
5427 )
5427 )
5428 for r in revs:
5428 for r in revs:
5429 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5429 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5430 remotebookmarks = fremotebookmarks.result()
5430 remotebookmarks = fremotebookmarks.result()
5431 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5431 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5432 pullopargs[b'remotebookmarks'] = remotebookmarks
5432 pullopargs[b'remotebookmarks'] = remotebookmarks
5433 for b in opts.get(b'bookmark', []):
5433 for b in opts.get(b'bookmark', []):
5434 b = repo._bookmarks.expandname(b)
5434 b = repo._bookmarks.expandname(b)
5435 if b not in remotebookmarks:
5435 if b not in remotebookmarks:
5436 raise error.InputError(
5436 raise error.InputError(
5437 _(b'remote bookmark %s not found!') % b
5437 _(b'remote bookmark %s not found!') % b
5438 )
5438 )
5439 nodes.append(remotebookmarks[b])
5439 nodes.append(remotebookmarks[b])
5440 for i, rev in enumerate(revs):
5440 for i, rev in enumerate(revs):
5441 node = fnodes[i].result()
5441 node = fnodes[i].result()
5442 nodes.append(node)
5442 nodes.append(node)
5443 if rev == checkout:
5443 if rev == checkout:
5444 checkout = node
5444 checkout = node
5445
5445
5446 wlock = util.nullcontextmanager()
5446 wlock = util.nullcontextmanager()
5447 if opts.get(b'update'):
5447 if opts.get(b'update'):
5448 wlock = repo.wlock()
5448 wlock = repo.wlock()
5449 with wlock:
5449 with wlock:
5450 pullopargs.update(opts.get(b'opargs', {}))
5450 pullopargs.update(opts.get(b'opargs', {}))
5451 modheads = exchange.pull(
5451 modheads = exchange.pull(
5452 repo,
5452 repo,
5453 other,
5453 other,
5454 heads=nodes,
5454 heads=nodes,
5455 force=opts.get(b'force'),
5455 force=opts.get(b'force'),
5456 bookmarks=opts.get(b'bookmark', ()),
5456 bookmarks=opts.get(b'bookmark', ()),
5457 opargs=pullopargs,
5457 opargs=pullopargs,
5458 confirm=opts.get(b'confirm'),
5458 confirm=opts.get(b'confirm'),
5459 ).cgresult
5459 ).cgresult
5460
5460
5461 # brev is a name, which might be a bookmark to be activated at
5461 # brev is a name, which might be a bookmark to be activated at
5462 # the end of the update. In other words, it is an explicit
5462 # the end of the update. In other words, it is an explicit
5463 # destination of the update
5463 # destination of the update
5464 brev = None
5464 brev = None
5465
5465
5466 if checkout:
5466 if checkout:
5467 checkout = repo.unfiltered().changelog.rev(checkout)
5467 checkout = repo.unfiltered().changelog.rev(checkout)
5468
5468
5469 # order below depends on implementation of
5469 # order below depends on implementation of
5470 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5470 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5471 # because 'checkout' is determined without it.
5471 # because 'checkout' is determined without it.
5472 if opts.get(b'rev'):
5472 if opts.get(b'rev'):
5473 brev = opts[b'rev'][0]
5473 brev = opts[b'rev'][0]
5474 elif opts.get(b'branch'):
5474 elif opts.get(b'branch'):
5475 brev = opts[b'branch'][0]
5475 brev = opts[b'branch'][0]
5476 else:
5476 else:
5477 brev = branches[0]
5477 brev = branches[0]
5478 repo._subtoppath = source
5478 repo._subtoppath = source
5479 try:
5479 try:
5480 update_conflict = postincoming(
5480 update_conflict = postincoming(
5481 ui, repo, modheads, opts.get(b'update'), checkout, brev
5481 ui, repo, modheads, opts.get(b'update'), checkout, brev
5482 )
5482 )
5483 except error.FilteredRepoLookupError as exc:
5483 except error.FilteredRepoLookupError as exc:
5484 msg = _(b'cannot update to target: %s') % exc.args[0]
5484 msg = _(b'cannot update to target: %s') % exc.args[0]
5485 exc.args = (msg,) + exc.args[1:]
5485 exc.args = (msg,) + exc.args[1:]
5486 raise
5486 raise
5487 finally:
5487 finally:
5488 del repo._subtoppath
5488 del repo._subtoppath
5489
5489
5490 finally:
5490 finally:
5491 other.close()
5491 other.close()
5492 # skip the remaining pull source if they are some conflict.
5492 # skip the remaining pull source if they are some conflict.
5493 if update_conflict:
5493 if update_conflict:
5494 break
5494 break
5495 if update_conflict:
5495 if update_conflict:
5496 return 1
5496 return 1
5497 else:
5497 else:
5498 return 0
5498 return 0
5499
5499
5500
5500
5501 @command(
5501 @command(
5502 b'purge|clean',
5502 b'purge|clean',
5503 [
5503 [
5504 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5504 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5505 (b'', b'all', None, _(b'purge ignored files too')),
5505 (b'', b'all', None, _(b'purge ignored files too')),
5506 (b'i', b'ignored', None, _(b'purge only ignored files')),
5506 (b'i', b'ignored', None, _(b'purge only ignored files')),
5507 (b'', b'dirs', None, _(b'purge empty directories')),
5507 (b'', b'dirs', None, _(b'purge empty directories')),
5508 (b'', b'files', None, _(b'purge files')),
5508 (b'', b'files', None, _(b'purge files')),
5509 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5509 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5510 (
5510 (
5511 b'0',
5511 b'0',
5512 b'print0',
5512 b'print0',
5513 None,
5513 None,
5514 _(
5514 _(
5515 b'end filenames with NUL, for use with xargs'
5515 b'end filenames with NUL, for use with xargs'
5516 b' (implies -p/--print)'
5516 b' (implies -p/--print)'
5517 ),
5517 ),
5518 ),
5518 ),
5519 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5519 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5520 ]
5520 ]
5521 + cmdutil.walkopts,
5521 + cmdutil.walkopts,
5522 _(b'hg purge [OPTION]... [DIR]...'),
5522 _(b'hg purge [OPTION]... [DIR]...'),
5523 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5523 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5524 )
5524 )
5525 def purge(ui, repo, *dirs, **opts):
5525 def purge(ui, repo, *dirs, **opts):
5526 """removes files not tracked by Mercurial
5526 """removes files not tracked by Mercurial
5527
5527
5528 Delete files not known to Mercurial. This is useful to test local
5528 Delete files not known to Mercurial. This is useful to test local
5529 and uncommitted changes in an otherwise-clean source tree.
5529 and uncommitted changes in an otherwise-clean source tree.
5530
5530
5531 This means that purge will delete the following by default:
5531 This means that purge will delete the following by default:
5532
5532
5533 - Unknown files: files marked with "?" by :hg:`status`
5533 - Unknown files: files marked with "?" by :hg:`status`
5534 - Empty directories: in fact Mercurial ignores directories unless
5534 - Empty directories: in fact Mercurial ignores directories unless
5535 they contain files under source control management
5535 they contain files under source control management
5536
5536
5537 But it will leave untouched:
5537 But it will leave untouched:
5538
5538
5539 - Modified and unmodified tracked files
5539 - Modified and unmodified tracked files
5540 - Ignored files (unless -i or --all is specified)
5540 - Ignored files (unless -i or --all is specified)
5541 - New files added to the repository (with :hg:`add`)
5541 - New files added to the repository (with :hg:`add`)
5542
5542
5543 The --files and --dirs options can be used to direct purge to delete
5543 The --files and --dirs options can be used to direct purge to delete
5544 only files, only directories, or both. If neither option is given,
5544 only files, only directories, or both. If neither option is given,
5545 both will be deleted.
5545 both will be deleted.
5546
5546
5547 If directories are given on the command line, only files in these
5547 If directories are given on the command line, only files in these
5548 directories are considered.
5548 directories are considered.
5549
5549
5550 Be careful with purge, as you could irreversibly delete some files
5550 Be careful with purge, as you could irreversibly delete some files
5551 you forgot to add to the repository. If you only want to print the
5551 you forgot to add to the repository. If you only want to print the
5552 list of files that this program would delete, use the --print
5552 list of files that this program would delete, use the --print
5553 option.
5553 option.
5554 """
5554 """
5555 opts = pycompat.byteskwargs(opts)
5555 opts = pycompat.byteskwargs(opts)
5556 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5556 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5557
5557
5558 act = not opts.get(b'print')
5558 act = not opts.get(b'print')
5559 eol = b'\n'
5559 eol = b'\n'
5560 if opts.get(b'print0'):
5560 if opts.get(b'print0'):
5561 eol = b'\0'
5561 eol = b'\0'
5562 act = False # --print0 implies --print
5562 act = False # --print0 implies --print
5563 if opts.get(b'all', False):
5563 if opts.get(b'all', False):
5564 ignored = True
5564 ignored = True
5565 unknown = True
5565 unknown = True
5566 else:
5566 else:
5567 ignored = opts.get(b'ignored', False)
5567 ignored = opts.get(b'ignored', False)
5568 unknown = not ignored
5568 unknown = not ignored
5569
5569
5570 removefiles = opts.get(b'files')
5570 removefiles = opts.get(b'files')
5571 removedirs = opts.get(b'dirs')
5571 removedirs = opts.get(b'dirs')
5572 confirm = opts.get(b'confirm')
5572 confirm = opts.get(b'confirm')
5573 if confirm is None:
5573 if confirm is None:
5574 try:
5574 try:
5575 extensions.find(b'purge')
5575 extensions.find(b'purge')
5576 confirm = False
5576 confirm = False
5577 except KeyError:
5577 except KeyError:
5578 confirm = True
5578 confirm = True
5579
5579
5580 if not removefiles and not removedirs:
5580 if not removefiles and not removedirs:
5581 removefiles = True
5581 removefiles = True
5582 removedirs = True
5582 removedirs = True
5583
5583
5584 match = scmutil.match(repo[None], dirs, opts)
5584 match = scmutil.match(repo[None], dirs, opts)
5585
5585
5586 paths = mergemod.purge(
5586 paths = mergemod.purge(
5587 repo,
5587 repo,
5588 match,
5588 match,
5589 unknown=unknown,
5589 unknown=unknown,
5590 ignored=ignored,
5590 ignored=ignored,
5591 removeemptydirs=removedirs,
5591 removeemptydirs=removedirs,
5592 removefiles=removefiles,
5592 removefiles=removefiles,
5593 abortonerror=opts.get(b'abort_on_err'),
5593 abortonerror=opts.get(b'abort_on_err'),
5594 noop=not act,
5594 noop=not act,
5595 confirm=confirm,
5595 confirm=confirm,
5596 )
5596 )
5597
5597
5598 for path in paths:
5598 for path in paths:
5599 if not act:
5599 if not act:
5600 ui.write(b'%s%s' % (path, eol))
5600 ui.write(b'%s%s' % (path, eol))
5601
5601
5602
5602
5603 @command(
5603 @command(
5604 b'push',
5604 b'push',
5605 [
5605 [
5606 (b'f', b'force', None, _(b'force push')),
5606 (b'f', b'force', None, _(b'force push')),
5607 (
5607 (
5608 b'r',
5608 b'r',
5609 b'rev',
5609 b'rev',
5610 [],
5610 [],
5611 _(b'a changeset intended to be included in the destination'),
5611 _(b'a changeset intended to be included in the destination'),
5612 _(b'REV'),
5612 _(b'REV'),
5613 ),
5613 ),
5614 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5614 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5615 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5615 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5616 (
5616 (
5617 b'b',
5617 b'b',
5618 b'branch',
5618 b'branch',
5619 [],
5619 [],
5620 _(b'a specific branch you would like to push'),
5620 _(b'a specific branch you would like to push'),
5621 _(b'BRANCH'),
5621 _(b'BRANCH'),
5622 ),
5622 ),
5623 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5623 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5624 (
5624 (
5625 b'',
5625 b'',
5626 b'pushvars',
5626 b'pushvars',
5627 [],
5627 [],
5628 _(b'variables that can be sent to server (ADVANCED)'),
5628 _(b'variables that can be sent to server (ADVANCED)'),
5629 ),
5629 ),
5630 (
5630 (
5631 b'',
5631 b'',
5632 b'publish',
5632 b'publish',
5633 False,
5633 False,
5634 _(b'push the changeset as public (EXPERIMENTAL)'),
5634 _(b'push the changeset as public (EXPERIMENTAL)'),
5635 ),
5635 ),
5636 ]
5636 ]
5637 + remoteopts,
5637 + remoteopts,
5638 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5638 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5639 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5639 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5640 helpbasic=True,
5640 helpbasic=True,
5641 )
5641 )
5642 def push(ui, repo, *dests, **opts):
5642 def push(ui, repo, *dests, **opts):
5643 """push changes to the specified destination
5643 """push changes to the specified destination
5644
5644
5645 Push changesets from the local repository to the specified
5645 Push changesets from the local repository to the specified
5646 destination.
5646 destination.
5647
5647
5648 This operation is symmetrical to pull: it is identical to a pull
5648 This operation is symmetrical to pull: it is identical to a pull
5649 in the destination repository from the current one.
5649 in the destination repository from the current one.
5650
5650
5651 By default, push will not allow creation of new heads at the
5651 By default, push will not allow creation of new heads at the
5652 destination, since multiple heads would make it unclear which head
5652 destination, since multiple heads would make it unclear which head
5653 to use. In this situation, it is recommended to pull and merge
5653 to use. In this situation, it is recommended to pull and merge
5654 before pushing.
5654 before pushing.
5655
5655
5656 Use --new-branch if you want to allow push to create a new named
5656 Use --new-branch if you want to allow push to create a new named
5657 branch that is not present at the destination. This allows you to
5657 branch that is not present at the destination. This allows you to
5658 only create a new branch without forcing other changes.
5658 only create a new branch without forcing other changes.
5659
5659
5660 .. note::
5660 .. note::
5661
5661
5662 Extra care should be taken with the -f/--force option,
5662 Extra care should be taken with the -f/--force option,
5663 which will push all new heads on all branches, an action which will
5663 which will push all new heads on all branches, an action which will
5664 almost always cause confusion for collaborators.
5664 almost always cause confusion for collaborators.
5665
5665
5666 If -r/--rev is used, the specified revision and all its ancestors
5666 If -r/--rev is used, the specified revision and all its ancestors
5667 will be pushed to the remote repository.
5667 will be pushed to the remote repository.
5668
5668
5669 If -B/--bookmark is used, the specified bookmarked revision, its
5669 If -B/--bookmark is used, the specified bookmarked revision, its
5670 ancestors, and the bookmark will be pushed to the remote
5670 ancestors, and the bookmark will be pushed to the remote
5671 repository. Specifying ``.`` is equivalent to specifying the active
5671 repository. Specifying ``.`` is equivalent to specifying the active
5672 bookmark's name. Use the --all-bookmarks option for pushing all
5672 bookmark's name. Use the --all-bookmarks option for pushing all
5673 current bookmarks.
5673 current bookmarks.
5674
5674
5675 Please see :hg:`help urls` for important details about ``ssh://``
5675 Please see :hg:`help urls` for important details about ``ssh://``
5676 URLs. If DESTINATION is omitted, a default path will be used.
5676 URLs. If DESTINATION is omitted, a default path will be used.
5677
5677
5678 When passed multiple destinations, push will process them one after the
5678 When passed multiple destinations, push will process them one after the
5679 other, but stop should an error occur.
5679 other, but stop should an error occur.
5680
5680
5681 .. container:: verbose
5681 .. container:: verbose
5682
5682
5683 The --pushvars option sends strings to the server that become
5683 The --pushvars option sends strings to the server that become
5684 environment variables prepended with ``HG_USERVAR_``. For example,
5684 environment variables prepended with ``HG_USERVAR_``. For example,
5685 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5685 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5686 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5686 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5687
5687
5688 pushvars can provide for user-overridable hooks as well as set debug
5688 pushvars can provide for user-overridable hooks as well as set debug
5689 levels. One example is having a hook that blocks commits containing
5689 levels. One example is having a hook that blocks commits containing
5690 conflict markers, but enables the user to override the hook if the file
5690 conflict markers, but enables the user to override the hook if the file
5691 is using conflict markers for testing purposes or the file format has
5691 is using conflict markers for testing purposes or the file format has
5692 strings that look like conflict markers.
5692 strings that look like conflict markers.
5693
5693
5694 By default, servers will ignore `--pushvars`. To enable it add the
5694 By default, servers will ignore `--pushvars`. To enable it add the
5695 following to your configuration file::
5695 following to your configuration file::
5696
5696
5697 [push]
5697 [push]
5698 pushvars.server = true
5698 pushvars.server = true
5699
5699
5700 Returns 0 if push was successful, 1 if nothing to push.
5700 Returns 0 if push was successful, 1 if nothing to push.
5701 """
5701 """
5702
5702
5703 opts = pycompat.byteskwargs(opts)
5703 opts = pycompat.byteskwargs(opts)
5704
5704
5705 if opts.get(b'all_bookmarks'):
5705 if opts.get(b'all_bookmarks'):
5706 cmdutil.check_incompatible_arguments(
5706 cmdutil.check_incompatible_arguments(
5707 opts,
5707 opts,
5708 b'all_bookmarks',
5708 b'all_bookmarks',
5709 [b'bookmark', b'rev'],
5709 [b'bookmark', b'rev'],
5710 )
5710 )
5711 opts[b'bookmark'] = list(repo._bookmarks)
5711 opts[b'bookmark'] = list(repo._bookmarks)
5712
5712
5713 if opts.get(b'bookmark'):
5713 if opts.get(b'bookmark'):
5714 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5714 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5715 for b in opts[b'bookmark']:
5715 for b in opts[b'bookmark']:
5716 # translate -B options to -r so changesets get pushed
5716 # translate -B options to -r so changesets get pushed
5717 b = repo._bookmarks.expandname(b)
5717 b = repo._bookmarks.expandname(b)
5718 if b in repo._bookmarks:
5718 if b in repo._bookmarks:
5719 opts.setdefault(b'rev', []).append(b)
5719 opts.setdefault(b'rev', []).append(b)
5720 else:
5720 else:
5721 # if we try to push a deleted bookmark, translate it to null
5721 # if we try to push a deleted bookmark, translate it to null
5722 # this lets simultaneous -r, -b options continue working
5722 # this lets simultaneous -r, -b options continue working
5723 opts.setdefault(b'rev', []).append(b"null")
5723 opts.setdefault(b'rev', []).append(b"null")
5724
5724
5725 some_pushed = False
5725 some_pushed = False
5726 result = 0
5726 result = 0
5727 for path in urlutil.get_push_paths(repo, ui, dests):
5727 for path in urlutil.get_push_paths(repo, ui, dests):
5728 dest = path.pushloc or path.loc
5728 dest = path.pushloc or path.loc
5729 branches = (path.branch, opts.get(b'branch') or [])
5729 branches = (path.branch, opts.get(b'branch') or [])
5730 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5730 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5731 revs, checkout = hg.addbranchrevs(
5731 revs, checkout = hg.addbranchrevs(
5732 repo, repo, branches, opts.get(b'rev')
5732 repo, repo, branches, opts.get(b'rev')
5733 )
5733 )
5734 other = hg.peer(repo, opts, dest)
5734 other = hg.peer(repo, opts, dest)
5735
5735
5736 try:
5736 try:
5737 if revs:
5737 if revs:
5738 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5738 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5739 if not revs:
5739 if not revs:
5740 raise error.InputError(
5740 raise error.InputError(
5741 _(b"specified revisions evaluate to an empty set"),
5741 _(b"specified revisions evaluate to an empty set"),
5742 hint=_(b"use different revision arguments"),
5742 hint=_(b"use different revision arguments"),
5743 )
5743 )
5744 elif path.pushrev:
5744 elif path.pushrev:
5745 # It doesn't make any sense to specify ancestor revisions. So limit
5745 # It doesn't make any sense to specify ancestor revisions. So limit
5746 # to DAG heads to make discovery simpler.
5746 # to DAG heads to make discovery simpler.
5747 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5747 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5748 revs = scmutil.revrange(repo, [expr])
5748 revs = scmutil.revrange(repo, [expr])
5749 revs = [repo[rev].node() for rev in revs]
5749 revs = [repo[rev].node() for rev in revs]
5750 if not revs:
5750 if not revs:
5751 raise error.InputError(
5751 raise error.InputError(
5752 _(
5752 _(
5753 b'default push revset for path evaluates to an empty set'
5753 b'default push revset for path evaluates to an empty set'
5754 )
5754 )
5755 )
5755 )
5756 elif ui.configbool(b'commands', b'push.require-revs'):
5756 elif ui.configbool(b'commands', b'push.require-revs'):
5757 raise error.InputError(
5757 raise error.InputError(
5758 _(b'no revisions specified to push'),
5758 _(b'no revisions specified to push'),
5759 hint=_(b'did you mean "hg push -r ."?'),
5759 hint=_(b'did you mean "hg push -r ."?'),
5760 )
5760 )
5761
5761
5762 repo._subtoppath = dest
5762 repo._subtoppath = dest
5763 try:
5763 try:
5764 # push subrepos depth-first for coherent ordering
5764 # push subrepos depth-first for coherent ordering
5765 c = repo[b'.']
5765 c = repo[b'.']
5766 subs = c.substate # only repos that are committed
5766 subs = c.substate # only repos that are committed
5767 for s in sorted(subs):
5767 for s in sorted(subs):
5768 sub_result = c.sub(s).push(opts)
5768 sub_result = c.sub(s).push(opts)
5769 if sub_result == 0:
5769 if sub_result == 0:
5770 return 1
5770 return 1
5771 finally:
5771 finally:
5772 del repo._subtoppath
5772 del repo._subtoppath
5773
5773
5774 opargs = dict(
5774 opargs = dict(
5775 opts.get(b'opargs', {})
5775 opts.get(b'opargs', {})
5776 ) # copy opargs since we may mutate it
5776 ) # copy opargs since we may mutate it
5777 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5777 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5778
5778
5779 pushop = exchange.push(
5779 pushop = exchange.push(
5780 repo,
5780 repo,
5781 other,
5781 other,
5782 opts.get(b'force'),
5782 opts.get(b'force'),
5783 revs=revs,
5783 revs=revs,
5784 newbranch=opts.get(b'new_branch'),
5784 newbranch=opts.get(b'new_branch'),
5785 bookmarks=opts.get(b'bookmark', ()),
5785 bookmarks=opts.get(b'bookmark', ()),
5786 publish=opts.get(b'publish'),
5786 publish=opts.get(b'publish'),
5787 opargs=opargs,
5787 opargs=opargs,
5788 )
5788 )
5789
5789
5790 if pushop.cgresult == 0:
5790 if pushop.cgresult == 0:
5791 result = 1
5791 result = 1
5792 elif pushop.cgresult is not None:
5792 elif pushop.cgresult is not None:
5793 some_pushed = True
5793 some_pushed = True
5794
5794
5795 if pushop.bkresult is not None:
5795 if pushop.bkresult is not None:
5796 if pushop.bkresult == 2:
5796 if pushop.bkresult == 2:
5797 result = 2
5797 result = 2
5798 elif not result and pushop.bkresult:
5798 elif not result and pushop.bkresult:
5799 result = 2
5799 result = 2
5800
5800
5801 if result:
5801 if result:
5802 break
5802 break
5803
5803
5804 finally:
5804 finally:
5805 other.close()
5805 other.close()
5806 if result == 0 and not some_pushed:
5806 if result == 0 and not some_pushed:
5807 result = 1
5807 result = 1
5808 return result
5808 return result
5809
5809
5810
5810
5811 @command(
5811 @command(
5812 b'recover',
5812 b'recover',
5813 [
5813 [
5814 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5814 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5815 ],
5815 ],
5816 helpcategory=command.CATEGORY_MAINTENANCE,
5816 helpcategory=command.CATEGORY_MAINTENANCE,
5817 )
5817 )
5818 def recover(ui, repo, **opts):
5818 def recover(ui, repo, **opts):
5819 """roll back an interrupted transaction
5819 """roll back an interrupted transaction
5820
5820
5821 Recover from an interrupted commit or pull.
5821 Recover from an interrupted commit or pull.
5822
5822
5823 This command tries to fix the repository status after an
5823 This command tries to fix the repository status after an
5824 interrupted operation. It should only be necessary when Mercurial
5824 interrupted operation. It should only be necessary when Mercurial
5825 suggests it.
5825 suggests it.
5826
5826
5827 Returns 0 if successful, 1 if nothing to recover or verify fails.
5827 Returns 0 if successful, 1 if nothing to recover or verify fails.
5828 """
5828 """
5829 ret = repo.recover()
5829 ret = repo.recover()
5830 if ret:
5830 if ret:
5831 if opts['verify']:
5831 if opts['verify']:
5832 return hg.verify(repo)
5832 return hg.verify(repo)
5833 else:
5833 else:
5834 msg = _(
5834 msg = _(
5835 b"(verify step skipped, run `hg verify` to check your "
5835 b"(verify step skipped, run `hg verify` to check your "
5836 b"repository content)\n"
5836 b"repository content)\n"
5837 )
5837 )
5838 ui.warn(msg)
5838 ui.warn(msg)
5839 return 0
5839 return 0
5840 return 1
5840 return 1
5841
5841
5842
5842
5843 @command(
5843 @command(
5844 b'remove|rm',
5844 b'remove|rm',
5845 [
5845 [
5846 (b'A', b'after', None, _(b'record delete for missing files')),
5846 (b'A', b'after', None, _(b'record delete for missing files')),
5847 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5847 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5848 ]
5848 ]
5849 + subrepoopts
5849 + subrepoopts
5850 + walkopts
5850 + walkopts
5851 + dryrunopts,
5851 + dryrunopts,
5852 _(b'[OPTION]... FILE...'),
5852 _(b'[OPTION]... FILE...'),
5853 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5853 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5854 helpbasic=True,
5854 helpbasic=True,
5855 inferrepo=True,
5855 inferrepo=True,
5856 )
5856 )
5857 def remove(ui, repo, *pats, **opts):
5857 def remove(ui, repo, *pats, **opts):
5858 """remove the specified files on the next commit
5858 """remove the specified files on the next commit
5859
5859
5860 Schedule the indicated files for removal from the current branch.
5860 Schedule the indicated files for removal from the current branch.
5861
5861
5862 This command schedules the files to be removed at the next commit.
5862 This command schedules the files to be removed at the next commit.
5863 To undo a remove before that, see :hg:`revert`. To undo added
5863 To undo a remove before that, see :hg:`revert`. To undo added
5864 files, see :hg:`forget`.
5864 files, see :hg:`forget`.
5865
5865
5866 .. container:: verbose
5866 .. container:: verbose
5867
5867
5868 -A/--after can be used to remove only files that have already
5868 -A/--after can be used to remove only files that have already
5869 been deleted, -f/--force can be used to force deletion, and -Af
5869 been deleted, -f/--force can be used to force deletion, and -Af
5870 can be used to remove files from the next revision without
5870 can be used to remove files from the next revision without
5871 deleting them from the working directory.
5871 deleting them from the working directory.
5872
5872
5873 The following table details the behavior of remove for different
5873 The following table details the behavior of remove for different
5874 file states (columns) and option combinations (rows). The file
5874 file states (columns) and option combinations (rows). The file
5875 states are Added [A], Clean [C], Modified [M] and Missing [!]
5875 states are Added [A], Clean [C], Modified [M] and Missing [!]
5876 (as reported by :hg:`status`). The actions are Warn, Remove
5876 (as reported by :hg:`status`). The actions are Warn, Remove
5877 (from branch) and Delete (from disk):
5877 (from branch) and Delete (from disk):
5878
5878
5879 ========= == == == ==
5879 ========= == == == ==
5880 opt/state A C M !
5880 opt/state A C M !
5881 ========= == == == ==
5881 ========= == == == ==
5882 none W RD W R
5882 none W RD W R
5883 -f R RD RD R
5883 -f R RD RD R
5884 -A W W W R
5884 -A W W W R
5885 -Af R R R R
5885 -Af R R R R
5886 ========= == == == ==
5886 ========= == == == ==
5887
5887
5888 .. note::
5888 .. note::
5889
5889
5890 :hg:`remove` never deletes files in Added [A] state from the
5890 :hg:`remove` never deletes files in Added [A] state from the
5891 working directory, not even if ``--force`` is specified.
5891 working directory, not even if ``--force`` is specified.
5892
5892
5893 Returns 0 on success, 1 if any warnings encountered.
5893 Returns 0 on success, 1 if any warnings encountered.
5894 """
5894 """
5895
5895
5896 opts = pycompat.byteskwargs(opts)
5896 opts = pycompat.byteskwargs(opts)
5897 after, force = opts.get(b'after'), opts.get(b'force')
5897 after, force = opts.get(b'after'), opts.get(b'force')
5898 dryrun = opts.get(b'dry_run')
5898 dryrun = opts.get(b'dry_run')
5899 if not pats and not after:
5899 if not pats and not after:
5900 raise error.InputError(_(b'no files specified'))
5900 raise error.InputError(_(b'no files specified'))
5901
5901
5902 m = scmutil.match(repo[None], pats, opts)
5902 m = scmutil.match(repo[None], pats, opts)
5903 subrepos = opts.get(b'subrepos')
5903 subrepos = opts.get(b'subrepos')
5904 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5904 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5905 return cmdutil.remove(
5905 return cmdutil.remove(
5906 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5906 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5907 )
5907 )
5908
5908
5909
5909
5910 @command(
5910 @command(
5911 b'rename|move|mv',
5911 b'rename|move|mv',
5912 [
5912 [
5913 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5913 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5914 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5914 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5915 (
5915 (
5916 b'',
5916 b'',
5917 b'at-rev',
5917 b'at-rev',
5918 b'',
5918 b'',
5919 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5919 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5920 _(b'REV'),
5920 _(b'REV'),
5921 ),
5921 ),
5922 (
5922 (
5923 b'f',
5923 b'f',
5924 b'force',
5924 b'force',
5925 None,
5925 None,
5926 _(b'forcibly move over an existing managed file'),
5926 _(b'forcibly move over an existing managed file'),
5927 ),
5927 ),
5928 ]
5928 ]
5929 + walkopts
5929 + walkopts
5930 + dryrunopts,
5930 + dryrunopts,
5931 _(b'[OPTION]... SOURCE... DEST'),
5931 _(b'[OPTION]... SOURCE... DEST'),
5932 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5932 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5933 )
5933 )
5934 def rename(ui, repo, *pats, **opts):
5934 def rename(ui, repo, *pats, **opts):
5935 """rename files; equivalent of copy + remove
5935 """rename files; equivalent of copy + remove
5936
5936
5937 Mark dest as copies of sources; mark sources for deletion. If dest
5937 Mark dest as copies of sources; mark sources for deletion. If dest
5938 is a directory, copies are put in that directory. If dest is a
5938 is a directory, copies are put in that directory. If dest is a
5939 file, there can only be one source.
5939 file, there can only be one source.
5940
5940
5941 By default, this command copies the contents of files as they
5941 By default, this command copies the contents of files as they
5942 exist in the working directory. If invoked with -A/--after, the
5942 exist in the working directory. If invoked with -A/--after, the
5943 operation is recorded, but no copying is performed.
5943 operation is recorded, but no copying is performed.
5944
5944
5945 To undo marking a destination file as renamed, use --forget. With that
5945 To undo marking a destination file as renamed, use --forget. With that
5946 option, all given (positional) arguments are unmarked as renames. The
5946 option, all given (positional) arguments are unmarked as renames. The
5947 destination file(s) will be left in place (still tracked). The source
5947 destination file(s) will be left in place (still tracked). The source
5948 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5948 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5949 the same way as :hg:`copy --forget`.
5949 the same way as :hg:`copy --forget`.
5950
5950
5951 This command takes effect with the next commit by default.
5951 This command takes effect with the next commit by default.
5952
5952
5953 Returns 0 on success, 1 if errors are encountered.
5953 Returns 0 on success, 1 if errors are encountered.
5954 """
5954 """
5955 opts = pycompat.byteskwargs(opts)
5955 opts = pycompat.byteskwargs(opts)
5956 with repo.wlock():
5956 with repo.wlock():
5957 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5957 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5958
5958
5959
5959
5960 @command(
5960 @command(
5961 b'resolve',
5961 b'resolve',
5962 [
5962 [
5963 (b'a', b'all', None, _(b'select all unresolved files')),
5963 (b'a', b'all', None, _(b'select all unresolved files')),
5964 (b'l', b'list', None, _(b'list state of files needing merge')),
5964 (b'l', b'list', None, _(b'list state of files needing merge')),
5965 (b'm', b'mark', None, _(b'mark files as resolved')),
5965 (b'm', b'mark', None, _(b'mark files as resolved')),
5966 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5966 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5967 (b'n', b'no-status', None, _(b'hide status prefix')),
5967 (b'n', b'no-status', None, _(b'hide status prefix')),
5968 (b'', b're-merge', None, _(b're-merge files')),
5968 (b'', b're-merge', None, _(b're-merge files')),
5969 ]
5969 ]
5970 + mergetoolopts
5970 + mergetoolopts
5971 + walkopts
5971 + walkopts
5972 + formatteropts,
5972 + formatteropts,
5973 _(b'[OPTION]... [FILE]...'),
5973 _(b'[OPTION]... [FILE]...'),
5974 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5974 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5975 inferrepo=True,
5975 inferrepo=True,
5976 )
5976 )
5977 def resolve(ui, repo, *pats, **opts):
5977 def resolve(ui, repo, *pats, **opts):
5978 """redo merges or set/view the merge status of files
5978 """redo merges or set/view the merge status of files
5979
5979
5980 Merges with unresolved conflicts are often the result of
5980 Merges with unresolved conflicts are often the result of
5981 non-interactive merging using the ``internal:merge`` configuration
5981 non-interactive merging using the ``internal:merge`` configuration
5982 setting, or a command-line merge tool like ``diff3``. The resolve
5982 setting, or a command-line merge tool like ``diff3``. The resolve
5983 command is used to manage the files involved in a merge, after
5983 command is used to manage the files involved in a merge, after
5984 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5984 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5985 working directory must have two parents). See :hg:`help
5985 working directory must have two parents). See :hg:`help
5986 merge-tools` for information on configuring merge tools.
5986 merge-tools` for information on configuring merge tools.
5987
5987
5988 The resolve command can be used in the following ways:
5988 The resolve command can be used in the following ways:
5989
5989
5990 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5990 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5991 the specified files, discarding any previous merge attempts. Re-merging
5991 the specified files, discarding any previous merge attempts. Re-merging
5992 is not performed for files already marked as resolved. Use ``--all/-a``
5992 is not performed for files already marked as resolved. Use ``--all/-a``
5993 to select all unresolved files. ``--tool`` can be used to specify
5993 to select all unresolved files. ``--tool`` can be used to specify
5994 the merge tool used for the given files. It overrides the HGMERGE
5994 the merge tool used for the given files. It overrides the HGMERGE
5995 environment variable and your configuration files. Previous file
5995 environment variable and your configuration files. Previous file
5996 contents are saved with a ``.orig`` suffix.
5996 contents are saved with a ``.orig`` suffix.
5997
5997
5998 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5998 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5999 (e.g. after having manually fixed-up the files). The default is
5999 (e.g. after having manually fixed-up the files). The default is
6000 to mark all unresolved files.
6000 to mark all unresolved files.
6001
6001
6002 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6002 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6003 default is to mark all resolved files.
6003 default is to mark all resolved files.
6004
6004
6005 - :hg:`resolve -l`: list files which had or still have conflicts.
6005 - :hg:`resolve -l`: list files which had or still have conflicts.
6006 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6006 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6007 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6007 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6008 the list. See :hg:`help filesets` for details.
6008 the list. See :hg:`help filesets` for details.
6009
6009
6010 .. note::
6010 .. note::
6011
6011
6012 Mercurial will not let you commit files with unresolved merge
6012 Mercurial will not let you commit files with unresolved merge
6013 conflicts. You must use :hg:`resolve -m ...` before you can
6013 conflicts. You must use :hg:`resolve -m ...` before you can
6014 commit after a conflicting merge.
6014 commit after a conflicting merge.
6015
6015
6016 .. container:: verbose
6016 .. container:: verbose
6017
6017
6018 Template:
6018 Template:
6019
6019
6020 The following keywords are supported in addition to the common template
6020 The following keywords are supported in addition to the common template
6021 keywords and functions. See also :hg:`help templates`.
6021 keywords and functions. See also :hg:`help templates`.
6022
6022
6023 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6023 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6024 :path: String. Repository-absolute path of the file.
6024 :path: String. Repository-absolute path of the file.
6025
6025
6026 Returns 0 on success, 1 if any files fail a resolve attempt.
6026 Returns 0 on success, 1 if any files fail a resolve attempt.
6027 """
6027 """
6028
6028
6029 opts = pycompat.byteskwargs(opts)
6029 opts = pycompat.byteskwargs(opts)
6030 confirm = ui.configbool(b'commands', b'resolve.confirm')
6030 confirm = ui.configbool(b'commands', b'resolve.confirm')
6031 flaglist = b'all mark unmark list no_status re_merge'.split()
6031 flaglist = b'all mark unmark list no_status re_merge'.split()
6032 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6032 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6033
6033
6034 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6034 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6035 if actioncount > 1:
6035 if actioncount > 1:
6036 raise error.InputError(_(b"too many actions specified"))
6036 raise error.InputError(_(b"too many actions specified"))
6037 elif actioncount == 0 and ui.configbool(
6037 elif actioncount == 0 and ui.configbool(
6038 b'commands', b'resolve.explicit-re-merge'
6038 b'commands', b'resolve.explicit-re-merge'
6039 ):
6039 ):
6040 hint = _(b'use --mark, --unmark, --list or --re-merge')
6040 hint = _(b'use --mark, --unmark, --list or --re-merge')
6041 raise error.InputError(_(b'no action specified'), hint=hint)
6041 raise error.InputError(_(b'no action specified'), hint=hint)
6042 if pats and all:
6042 if pats and all:
6043 raise error.InputError(_(b"can't specify --all and patterns"))
6043 raise error.InputError(_(b"can't specify --all and patterns"))
6044 if not (all or pats or show or mark or unmark):
6044 if not (all or pats or show or mark or unmark):
6045 raise error.InputError(
6045 raise error.InputError(
6046 _(b'no files or directories specified'),
6046 _(b'no files or directories specified'),
6047 hint=b'use --all to re-merge all unresolved files',
6047 hint=b'use --all to re-merge all unresolved files',
6048 )
6048 )
6049
6049
6050 if confirm:
6050 if confirm:
6051 if all:
6051 if all:
6052 if ui.promptchoice(
6052 if ui.promptchoice(
6053 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6053 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6054 ):
6054 ):
6055 raise error.CanceledError(_(b'user quit'))
6055 raise error.CanceledError(_(b'user quit'))
6056 if mark and not pats:
6056 if mark and not pats:
6057 if ui.promptchoice(
6057 if ui.promptchoice(
6058 _(
6058 _(
6059 b'mark all unresolved files as resolved (yn)?'
6059 b'mark all unresolved files as resolved (yn)?'
6060 b'$$ &Yes $$ &No'
6060 b'$$ &Yes $$ &No'
6061 )
6061 )
6062 ):
6062 ):
6063 raise error.CanceledError(_(b'user quit'))
6063 raise error.CanceledError(_(b'user quit'))
6064 if unmark and not pats:
6064 if unmark and not pats:
6065 if ui.promptchoice(
6065 if ui.promptchoice(
6066 _(
6066 _(
6067 b'mark all resolved files as unresolved (yn)?'
6067 b'mark all resolved files as unresolved (yn)?'
6068 b'$$ &Yes $$ &No'
6068 b'$$ &Yes $$ &No'
6069 )
6069 )
6070 ):
6070 ):
6071 raise error.CanceledError(_(b'user quit'))
6071 raise error.CanceledError(_(b'user quit'))
6072
6072
6073 uipathfn = scmutil.getuipathfn(repo)
6073 uipathfn = scmutil.getuipathfn(repo)
6074
6074
6075 if show:
6075 if show:
6076 ui.pager(b'resolve')
6076 ui.pager(b'resolve')
6077 fm = ui.formatter(b'resolve', opts)
6077 fm = ui.formatter(b'resolve', opts)
6078 ms = mergestatemod.mergestate.read(repo)
6078 ms = mergestatemod.mergestate.read(repo)
6079 wctx = repo[None]
6079 wctx = repo[None]
6080 m = scmutil.match(wctx, pats, opts)
6080 m = scmutil.match(wctx, pats, opts)
6081
6081
6082 # Labels and keys based on merge state. Unresolved path conflicts show
6082 # Labels and keys based on merge state. Unresolved path conflicts show
6083 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6083 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6084 # resolved conflicts.
6084 # resolved conflicts.
6085 mergestateinfo = {
6085 mergestateinfo = {
6086 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6086 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6087 b'resolve.unresolved',
6087 b'resolve.unresolved',
6088 b'U',
6088 b'U',
6089 ),
6089 ),
6090 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6090 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6091 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6091 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6092 b'resolve.unresolved',
6092 b'resolve.unresolved',
6093 b'P',
6093 b'P',
6094 ),
6094 ),
6095 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6095 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6096 b'resolve.resolved',
6096 b'resolve.resolved',
6097 b'R',
6097 b'R',
6098 ),
6098 ),
6099 }
6099 }
6100
6100
6101 for f in ms:
6101 for f in ms:
6102 if not m(f):
6102 if not m(f):
6103 continue
6103 continue
6104
6104
6105 label, key = mergestateinfo[ms[f]]
6105 label, key = mergestateinfo[ms[f]]
6106 fm.startitem()
6106 fm.startitem()
6107 fm.context(ctx=wctx)
6107 fm.context(ctx=wctx)
6108 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6108 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6109 fm.data(path=f)
6109 fm.data(path=f)
6110 fm.plain(b'%s\n' % uipathfn(f), label=label)
6110 fm.plain(b'%s\n' % uipathfn(f), label=label)
6111 fm.end()
6111 fm.end()
6112 return 0
6112 return 0
6113
6113
6114 with repo.wlock():
6114 with repo.wlock():
6115 ms = mergestatemod.mergestate.read(repo)
6115 ms = mergestatemod.mergestate.read(repo)
6116
6116
6117 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6117 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6118 raise error.StateError(
6118 raise error.StateError(
6119 _(b'resolve command not applicable when not merging')
6119 _(b'resolve command not applicable when not merging')
6120 )
6120 )
6121
6121
6122 wctx = repo[None]
6122 wctx = repo[None]
6123 m = scmutil.match(wctx, pats, opts)
6123 m = scmutil.match(wctx, pats, opts)
6124 ret = 0
6124 ret = 0
6125 didwork = False
6125 didwork = False
6126
6126
6127 tocomplete = []
6127 tocomplete = []
6128 hasconflictmarkers = []
6128 hasconflictmarkers = []
6129 if mark:
6129 if mark:
6130 markcheck = ui.config(b'commands', b'resolve.mark-check')
6130 markcheck = ui.config(b'commands', b'resolve.mark-check')
6131 if markcheck not in [b'warn', b'abort']:
6131 if markcheck not in [b'warn', b'abort']:
6132 # Treat all invalid / unrecognized values as 'none'.
6132 # Treat all invalid / unrecognized values as 'none'.
6133 markcheck = False
6133 markcheck = False
6134 for f in ms:
6134 for f in ms:
6135 if not m(f):
6135 if not m(f):
6136 continue
6136 continue
6137
6137
6138 didwork = True
6138 didwork = True
6139
6139
6140 # path conflicts must be resolved manually
6140 # path conflicts must be resolved manually
6141 if ms[f] in (
6141 if ms[f] in (
6142 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6142 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6143 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6143 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6144 ):
6144 ):
6145 if mark:
6145 if mark:
6146 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6146 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6147 elif unmark:
6147 elif unmark:
6148 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6148 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6149 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6149 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6150 ui.warn(
6150 ui.warn(
6151 _(b'%s: path conflict must be resolved manually\n')
6151 _(b'%s: path conflict must be resolved manually\n')
6152 % uipathfn(f)
6152 % uipathfn(f)
6153 )
6153 )
6154 continue
6154 continue
6155
6155
6156 if mark:
6156 if mark:
6157 if markcheck:
6157 if markcheck:
6158 fdata = repo.wvfs.tryread(f)
6158 fdata = repo.wvfs.tryread(f)
6159 if (
6159 if (
6160 filemerge.hasconflictmarkers(fdata)
6160 filemerge.hasconflictmarkers(fdata)
6161 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6161 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6162 ):
6162 ):
6163 hasconflictmarkers.append(f)
6163 hasconflictmarkers.append(f)
6164 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6164 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6165 elif unmark:
6165 elif unmark:
6166 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6166 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6167 else:
6167 else:
6168 # backup pre-resolve (merge uses .orig for its own purposes)
6168 # backup pre-resolve (merge uses .orig for its own purposes)
6169 a = repo.wjoin(f)
6169 a = repo.wjoin(f)
6170 try:
6170 try:
6171 util.copyfile(a, a + b".resolve")
6171 util.copyfile(a, a + b".resolve")
6172 except (IOError, OSError) as inst:
6172 except (IOError, OSError) as inst:
6173 if inst.errno != errno.ENOENT:
6173 if inst.errno != errno.ENOENT:
6174 raise
6174 raise
6175
6175
6176 try:
6176 try:
6177 # preresolve file
6177 # preresolve file
6178 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6178 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6179 with ui.configoverride(overrides, b'resolve'):
6179 with ui.configoverride(overrides, b'resolve'):
6180 complete, r = ms.preresolve(f, wctx)
6180 complete, r = ms.preresolve(f, wctx)
6181 if not complete:
6181 if not complete:
6182 tocomplete.append(f)
6182 tocomplete.append(f)
6183 elif r:
6183 elif r:
6184 ret = 1
6184 ret = 1
6185 finally:
6185 finally:
6186 ms.commit()
6186 ms.commit()
6187
6187
6188 # replace filemerge's .orig file with our resolve file, but only
6188 # replace filemerge's .orig file with our resolve file, but only
6189 # for merges that are complete
6189 # for merges that are complete
6190 if complete:
6190 if complete:
6191 try:
6191 try:
6192 util.rename(
6192 util.rename(
6193 a + b".resolve", scmutil.backuppath(ui, repo, f)
6193 a + b".resolve", scmutil.backuppath(ui, repo, f)
6194 )
6194 )
6195 except OSError as inst:
6195 except OSError as inst:
6196 if inst.errno != errno.ENOENT:
6196 if inst.errno != errno.ENOENT:
6197 raise
6197 raise
6198
6198
6199 if hasconflictmarkers:
6199 if hasconflictmarkers:
6200 ui.warn(
6200 ui.warn(
6201 _(
6201 _(
6202 b'warning: the following files still have conflict '
6202 b'warning: the following files still have conflict '
6203 b'markers:\n'
6203 b'markers:\n'
6204 )
6204 )
6205 + b''.join(
6205 + b''.join(
6206 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6206 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6207 )
6207 )
6208 )
6208 )
6209 if markcheck == b'abort' and not all and not pats:
6209 if markcheck == b'abort' and not all and not pats:
6210 raise error.StateError(
6210 raise error.StateError(
6211 _(b'conflict markers detected'),
6211 _(b'conflict markers detected'),
6212 hint=_(b'use --all to mark anyway'),
6212 hint=_(b'use --all to mark anyway'),
6213 )
6213 )
6214
6214
6215 for f in tocomplete:
6215 for f in tocomplete:
6216 try:
6216 try:
6217 # resolve file
6217 # resolve file
6218 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6218 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6219 with ui.configoverride(overrides, b'resolve'):
6219 with ui.configoverride(overrides, b'resolve'):
6220 r = ms.resolve(f, wctx)
6220 r = ms.resolve(f, wctx)
6221 if r:
6221 if r:
6222 ret = 1
6222 ret = 1
6223 finally:
6223 finally:
6224 ms.commit()
6224 ms.commit()
6225
6225
6226 # replace filemerge's .orig file with our resolve file
6226 # replace filemerge's .orig file with our resolve file
6227 a = repo.wjoin(f)
6227 a = repo.wjoin(f)
6228 try:
6228 try:
6229 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6229 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6230 except OSError as inst:
6230 except OSError as inst:
6231 if inst.errno != errno.ENOENT:
6231 if inst.errno != errno.ENOENT:
6232 raise
6232 raise
6233
6233
6234 ms.commit()
6234 ms.commit()
6235 branchmerge = repo.dirstate.p2() != repo.nullid
6235 branchmerge = repo.dirstate.p2() != repo.nullid
6236 # resolve is not doing a parent change here, however, `record updates`
6236 # resolve is not doing a parent change here, however, `record updates`
6237 # will call some dirstate API that at intended for parent changes call.
6237 # will call some dirstate API that at intended for parent changes call.
6238 # Ideally we would not need this and could implement a lighter version
6238 # Ideally we would not need this and could implement a lighter version
6239 # of the recordupdateslogic that will not have to deal with the part
6239 # of the recordupdateslogic that will not have to deal with the part
6240 # related to parent changes. However this would requires that:
6240 # related to parent changes. However this would requires that:
6241 # - we are sure we passed around enough information at update/merge
6241 # - we are sure we passed around enough information at update/merge
6242 # time to no longer needs it at `hg resolve time`
6242 # time to no longer needs it at `hg resolve time`
6243 # - we are sure we store that information well enough to be able to reuse it
6243 # - we are sure we store that information well enough to be able to reuse it
6244 # - we are the necessary logic to reuse it right.
6244 # - we are the necessary logic to reuse it right.
6245 #
6245 #
6246 # All this should eventually happens, but in the mean time, we use this
6246 # All this should eventually happens, but in the mean time, we use this
6247 # context manager slightly out of the context it should be.
6247 # context manager slightly out of the context it should be.
6248 with repo.dirstate.parentchange():
6248 with repo.dirstate.parentchange():
6249 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6249 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6250
6250
6251 if not didwork and pats:
6251 if not didwork and pats:
6252 hint = None
6252 hint = None
6253 if not any([p for p in pats if p.find(b':') >= 0]):
6253 if not any([p for p in pats if p.find(b':') >= 0]):
6254 pats = [b'path:%s' % p for p in pats]
6254 pats = [b'path:%s' % p for p in pats]
6255 m = scmutil.match(wctx, pats, opts)
6255 m = scmutil.match(wctx, pats, opts)
6256 for f in ms:
6256 for f in ms:
6257 if not m(f):
6257 if not m(f):
6258 continue
6258 continue
6259
6259
6260 def flag(o):
6260 def flag(o):
6261 if o == b're_merge':
6261 if o == b're_merge':
6262 return b'--re-merge '
6262 return b'--re-merge '
6263 return b'-%s ' % o[0:1]
6263 return b'-%s ' % o[0:1]
6264
6264
6265 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6265 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6266 hint = _(b"(try: hg resolve %s%s)\n") % (
6266 hint = _(b"(try: hg resolve %s%s)\n") % (
6267 flags,
6267 flags,
6268 b' '.join(pats),
6268 b' '.join(pats),
6269 )
6269 )
6270 break
6270 break
6271 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6271 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6272 if hint:
6272 if hint:
6273 ui.warn(hint)
6273 ui.warn(hint)
6274
6274
6275 unresolvedf = ms.unresolvedcount()
6275 unresolvedf = ms.unresolvedcount()
6276 if not unresolvedf:
6276 if not unresolvedf:
6277 ui.status(_(b'(no more unresolved files)\n'))
6277 ui.status(_(b'(no more unresolved files)\n'))
6278 cmdutil.checkafterresolved(repo)
6278 cmdutil.checkafterresolved(repo)
6279
6279
6280 return ret
6280 return ret
6281
6281
6282
6282
6283 @command(
6283 @command(
6284 b'revert',
6284 b'revert',
6285 [
6285 [
6286 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6286 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6287 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6287 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6288 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6288 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6289 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6289 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6290 (b'i', b'interactive', None, _(b'interactively select the changes')),
6290 (b'i', b'interactive', None, _(b'interactively select the changes')),
6291 ]
6291 ]
6292 + walkopts
6292 + walkopts
6293 + dryrunopts,
6293 + dryrunopts,
6294 _(b'[OPTION]... [-r REV] [NAME]...'),
6294 _(b'[OPTION]... [-r REV] [NAME]...'),
6295 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6295 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6296 )
6296 )
6297 def revert(ui, repo, *pats, **opts):
6297 def revert(ui, repo, *pats, **opts):
6298 """restore files to their checkout state
6298 """restore files to their checkout state
6299
6299
6300 .. note::
6300 .. note::
6301
6301
6302 To check out earlier revisions, you should use :hg:`update REV`.
6302 To check out earlier revisions, you should use :hg:`update REV`.
6303 To cancel an uncommitted merge (and lose your changes),
6303 To cancel an uncommitted merge (and lose your changes),
6304 use :hg:`merge --abort`.
6304 use :hg:`merge --abort`.
6305
6305
6306 With no revision specified, revert the specified files or directories
6306 With no revision specified, revert the specified files or directories
6307 to the contents they had in the parent of the working directory.
6307 to the contents they had in the parent of the working directory.
6308 This restores the contents of files to an unmodified
6308 This restores the contents of files to an unmodified
6309 state and unschedules adds, removes, copies, and renames. If the
6309 state and unschedules adds, removes, copies, and renames. If the
6310 working directory has two parents, you must explicitly specify a
6310 working directory has two parents, you must explicitly specify a
6311 revision.
6311 revision.
6312
6312
6313 Using the -r/--rev or -d/--date options, revert the given files or
6313 Using the -r/--rev or -d/--date options, revert the given files or
6314 directories to their states as of a specific revision. Because
6314 directories to their states as of a specific revision. Because
6315 revert does not change the working directory parents, this will
6315 revert does not change the working directory parents, this will
6316 cause these files to appear modified. This can be helpful to "back
6316 cause these files to appear modified. This can be helpful to "back
6317 out" some or all of an earlier change. See :hg:`backout` for a
6317 out" some or all of an earlier change. See :hg:`backout` for a
6318 related method.
6318 related method.
6319
6319
6320 Modified files are saved with a .orig suffix before reverting.
6320 Modified files are saved with a .orig suffix before reverting.
6321 To disable these backups, use --no-backup. It is possible to store
6321 To disable these backups, use --no-backup. It is possible to store
6322 the backup files in a custom directory relative to the root of the
6322 the backup files in a custom directory relative to the root of the
6323 repository by setting the ``ui.origbackuppath`` configuration
6323 repository by setting the ``ui.origbackuppath`` configuration
6324 option.
6324 option.
6325
6325
6326 See :hg:`help dates` for a list of formats valid for -d/--date.
6326 See :hg:`help dates` for a list of formats valid for -d/--date.
6327
6327
6328 See :hg:`help backout` for a way to reverse the effect of an
6328 See :hg:`help backout` for a way to reverse the effect of an
6329 earlier changeset.
6329 earlier changeset.
6330
6330
6331 Returns 0 on success.
6331 Returns 0 on success.
6332 """
6332 """
6333
6333
6334 opts = pycompat.byteskwargs(opts)
6334 opts = pycompat.byteskwargs(opts)
6335 if opts.get(b"date"):
6335 if opts.get(b"date"):
6336 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6336 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6337 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6337 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6338
6338
6339 parent, p2 = repo.dirstate.parents()
6339 parent, p2 = repo.dirstate.parents()
6340 if not opts.get(b'rev') and p2 != repo.nullid:
6340 if not opts.get(b'rev') and p2 != repo.nullid:
6341 # revert after merge is a trap for new users (issue2915)
6341 # revert after merge is a trap for new users (issue2915)
6342 raise error.InputError(
6342 raise error.InputError(
6343 _(b'uncommitted merge with no revision specified'),
6343 _(b'uncommitted merge with no revision specified'),
6344 hint=_(b"use 'hg update' or see 'hg help revert'"),
6344 hint=_(b"use 'hg update' or see 'hg help revert'"),
6345 )
6345 )
6346
6346
6347 rev = opts.get(b'rev')
6347 rev = opts.get(b'rev')
6348 if rev:
6348 if rev:
6349 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6349 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6350 ctx = scmutil.revsingle(repo, rev)
6350 ctx = scmutil.revsingle(repo, rev)
6351
6351
6352 if not (
6352 if not (
6353 pats
6353 pats
6354 or opts.get(b'include')
6354 or opts.get(b'include')
6355 or opts.get(b'exclude')
6355 or opts.get(b'exclude')
6356 or opts.get(b'all')
6356 or opts.get(b'all')
6357 or opts.get(b'interactive')
6357 or opts.get(b'interactive')
6358 ):
6358 ):
6359 msg = _(b"no files or directories specified")
6359 msg = _(b"no files or directories specified")
6360 if p2 != repo.nullid:
6360 if p2 != repo.nullid:
6361 hint = _(
6361 hint = _(
6362 b"uncommitted merge, use --all to discard all changes,"
6362 b"uncommitted merge, use --all to discard all changes,"
6363 b" or 'hg update -C .' to abort the merge"
6363 b" or 'hg update -C .' to abort the merge"
6364 )
6364 )
6365 raise error.InputError(msg, hint=hint)
6365 raise error.InputError(msg, hint=hint)
6366 dirty = any(repo.status())
6366 dirty = any(repo.status())
6367 node = ctx.node()
6367 node = ctx.node()
6368 if node != parent:
6368 if node != parent:
6369 if dirty:
6369 if dirty:
6370 hint = (
6370 hint = (
6371 _(
6371 _(
6372 b"uncommitted changes, use --all to discard all"
6372 b"uncommitted changes, use --all to discard all"
6373 b" changes, or 'hg update %d' to update"
6373 b" changes, or 'hg update %d' to update"
6374 )
6374 )
6375 % ctx.rev()
6375 % ctx.rev()
6376 )
6376 )
6377 else:
6377 else:
6378 hint = (
6378 hint = (
6379 _(
6379 _(
6380 b"use --all to revert all files,"
6380 b"use --all to revert all files,"
6381 b" or 'hg update %d' to update"
6381 b" or 'hg update %d' to update"
6382 )
6382 )
6383 % ctx.rev()
6383 % ctx.rev()
6384 )
6384 )
6385 elif dirty:
6385 elif dirty:
6386 hint = _(b"uncommitted changes, use --all to discard all changes")
6386 hint = _(b"uncommitted changes, use --all to discard all changes")
6387 else:
6387 else:
6388 hint = _(b"use --all to revert all files")
6388 hint = _(b"use --all to revert all files")
6389 raise error.InputError(msg, hint=hint)
6389 raise error.InputError(msg, hint=hint)
6390
6390
6391 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6391 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6392
6392
6393
6393
6394 @command(
6394 @command(
6395 b'rollback',
6395 b'rollback',
6396 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6396 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6397 helpcategory=command.CATEGORY_MAINTENANCE,
6397 helpcategory=command.CATEGORY_MAINTENANCE,
6398 )
6398 )
6399 def rollback(ui, repo, **opts):
6399 def rollback(ui, repo, **opts):
6400 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6400 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6401
6401
6402 Please use :hg:`commit --amend` instead of rollback to correct
6402 Please use :hg:`commit --amend` instead of rollback to correct
6403 mistakes in the last commit.
6403 mistakes in the last commit.
6404
6404
6405 This command should be used with care. There is only one level of
6405 This command should be used with care. There is only one level of
6406 rollback, and there is no way to undo a rollback. It will also
6406 rollback, and there is no way to undo a rollback. It will also
6407 restore the dirstate at the time of the last transaction, losing
6407 restore the dirstate at the time of the last transaction, losing
6408 any dirstate changes since that time. This command does not alter
6408 any dirstate changes since that time. This command does not alter
6409 the working directory.
6409 the working directory.
6410
6410
6411 Transactions are used to encapsulate the effects of all commands
6411 Transactions are used to encapsulate the effects of all commands
6412 that create new changesets or propagate existing changesets into a
6412 that create new changesets or propagate existing changesets into a
6413 repository.
6413 repository.
6414
6414
6415 .. container:: verbose
6415 .. container:: verbose
6416
6416
6417 For example, the following commands are transactional, and their
6417 For example, the following commands are transactional, and their
6418 effects can be rolled back:
6418 effects can be rolled back:
6419
6419
6420 - commit
6420 - commit
6421 - import
6421 - import
6422 - pull
6422 - pull
6423 - push (with this repository as the destination)
6423 - push (with this repository as the destination)
6424 - unbundle
6424 - unbundle
6425
6425
6426 To avoid permanent data loss, rollback will refuse to rollback a
6426 To avoid permanent data loss, rollback will refuse to rollback a
6427 commit transaction if it isn't checked out. Use --force to
6427 commit transaction if it isn't checked out. Use --force to
6428 override this protection.
6428 override this protection.
6429
6429
6430 The rollback command can be entirely disabled by setting the
6430 The rollback command can be entirely disabled by setting the
6431 ``ui.rollback`` configuration setting to false. If you're here
6431 ``ui.rollback`` configuration setting to false. If you're here
6432 because you want to use rollback and it's disabled, you can
6432 because you want to use rollback and it's disabled, you can
6433 re-enable the command by setting ``ui.rollback`` to true.
6433 re-enable the command by setting ``ui.rollback`` to true.
6434
6434
6435 This command is not intended for use on public repositories. Once
6435 This command is not intended for use on public repositories. Once
6436 changes are visible for pull by other users, rolling a transaction
6436 changes are visible for pull by other users, rolling a transaction
6437 back locally is ineffective (someone else may already have pulled
6437 back locally is ineffective (someone else may already have pulled
6438 the changes). Furthermore, a race is possible with readers of the
6438 the changes). Furthermore, a race is possible with readers of the
6439 repository; for example an in-progress pull from the repository
6439 repository; for example an in-progress pull from the repository
6440 may fail if a rollback is performed.
6440 may fail if a rollback is performed.
6441
6441
6442 Returns 0 on success, 1 if no rollback data is available.
6442 Returns 0 on success, 1 if no rollback data is available.
6443 """
6443 """
6444 if not ui.configbool(b'ui', b'rollback'):
6444 if not ui.configbool(b'ui', b'rollback'):
6445 raise error.Abort(
6445 raise error.Abort(
6446 _(b'rollback is disabled because it is unsafe'),
6446 _(b'rollback is disabled because it is unsafe'),
6447 hint=b'see `hg help -v rollback` for information',
6447 hint=b'see `hg help -v rollback` for information',
6448 )
6448 )
6449 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6449 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6450
6450
6451
6451
6452 @command(
6452 @command(
6453 b'root',
6453 b'root',
6454 [] + formatteropts,
6454 [] + formatteropts,
6455 intents={INTENT_READONLY},
6455 intents={INTENT_READONLY},
6456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6456 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6457 )
6457 )
6458 def root(ui, repo, **opts):
6458 def root(ui, repo, **opts):
6459 """print the root (top) of the current working directory
6459 """print the root (top) of the current working directory
6460
6460
6461 Print the root directory of the current repository.
6461 Print the root directory of the current repository.
6462
6462
6463 .. container:: verbose
6463 .. container:: verbose
6464
6464
6465 Template:
6465 Template:
6466
6466
6467 The following keywords are supported in addition to the common template
6467 The following keywords are supported in addition to the common template
6468 keywords and functions. See also :hg:`help templates`.
6468 keywords and functions. See also :hg:`help templates`.
6469
6469
6470 :hgpath: String. Path to the .hg directory.
6470 :hgpath: String. Path to the .hg directory.
6471 :storepath: String. Path to the directory holding versioned data.
6471 :storepath: String. Path to the directory holding versioned data.
6472
6472
6473 Returns 0 on success.
6473 Returns 0 on success.
6474 """
6474 """
6475 opts = pycompat.byteskwargs(opts)
6475 opts = pycompat.byteskwargs(opts)
6476 with ui.formatter(b'root', opts) as fm:
6476 with ui.formatter(b'root', opts) as fm:
6477 fm.startitem()
6477 fm.startitem()
6478 fm.write(b'reporoot', b'%s\n', repo.root)
6478 fm.write(b'reporoot', b'%s\n', repo.root)
6479 fm.data(hgpath=repo.path, storepath=repo.spath)
6479 fm.data(hgpath=repo.path, storepath=repo.spath)
6480
6480
6481
6481
6482 @command(
6482 @command(
6483 b'serve',
6483 b'serve',
6484 [
6484 [
6485 (
6485 (
6486 b'A',
6486 b'A',
6487 b'accesslog',
6487 b'accesslog',
6488 b'',
6488 b'',
6489 _(b'name of access log file to write to'),
6489 _(b'name of access log file to write to'),
6490 _(b'FILE'),
6490 _(b'FILE'),
6491 ),
6491 ),
6492 (b'd', b'daemon', None, _(b'run server in background')),
6492 (b'd', b'daemon', None, _(b'run server in background')),
6493 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6493 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6494 (
6494 (
6495 b'E',
6495 b'E',
6496 b'errorlog',
6496 b'errorlog',
6497 b'',
6497 b'',
6498 _(b'name of error log file to write to'),
6498 _(b'name of error log file to write to'),
6499 _(b'FILE'),
6499 _(b'FILE'),
6500 ),
6500 ),
6501 # use string type, then we can check if something was passed
6501 # use string type, then we can check if something was passed
6502 (
6502 (
6503 b'p',
6503 b'p',
6504 b'port',
6504 b'port',
6505 b'',
6505 b'',
6506 _(b'port to listen on (default: 8000)'),
6506 _(b'port to listen on (default: 8000)'),
6507 _(b'PORT'),
6507 _(b'PORT'),
6508 ),
6508 ),
6509 (
6509 (
6510 b'a',
6510 b'a',
6511 b'address',
6511 b'address',
6512 b'',
6512 b'',
6513 _(b'address to listen on (default: all interfaces)'),
6513 _(b'address to listen on (default: all interfaces)'),
6514 _(b'ADDR'),
6514 _(b'ADDR'),
6515 ),
6515 ),
6516 (
6516 (
6517 b'',
6517 b'',
6518 b'prefix',
6518 b'prefix',
6519 b'',
6519 b'',
6520 _(b'prefix path to serve from (default: server root)'),
6520 _(b'prefix path to serve from (default: server root)'),
6521 _(b'PREFIX'),
6521 _(b'PREFIX'),
6522 ),
6522 ),
6523 (
6523 (
6524 b'n',
6524 b'n',
6525 b'name',
6525 b'name',
6526 b'',
6526 b'',
6527 _(b'name to show in web pages (default: working directory)'),
6527 _(b'name to show in web pages (default: working directory)'),
6528 _(b'NAME'),
6528 _(b'NAME'),
6529 ),
6529 ),
6530 (
6530 (
6531 b'',
6531 b'',
6532 b'web-conf',
6532 b'web-conf',
6533 b'',
6533 b'',
6534 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6534 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6535 _(b'FILE'),
6535 _(b'FILE'),
6536 ),
6536 ),
6537 (
6537 (
6538 b'',
6538 b'',
6539 b'webdir-conf',
6539 b'webdir-conf',
6540 b'',
6540 b'',
6541 _(b'name of the hgweb config file (DEPRECATED)'),
6541 _(b'name of the hgweb config file (DEPRECATED)'),
6542 _(b'FILE'),
6542 _(b'FILE'),
6543 ),
6543 ),
6544 (
6544 (
6545 b'',
6545 b'',
6546 b'pid-file',
6546 b'pid-file',
6547 b'',
6547 b'',
6548 _(b'name of file to write process ID to'),
6548 _(b'name of file to write process ID to'),
6549 _(b'FILE'),
6549 _(b'FILE'),
6550 ),
6550 ),
6551 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6551 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6552 (
6552 (
6553 b'',
6553 b'',
6554 b'cmdserver',
6554 b'cmdserver',
6555 b'',
6555 b'',
6556 _(b'for remote clients (ADVANCED)'),
6556 _(b'for remote clients (ADVANCED)'),
6557 _(b'MODE'),
6557 _(b'MODE'),
6558 ),
6558 ),
6559 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6559 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6560 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6560 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6561 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6561 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6562 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6562 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6563 (b'', b'print-url', None, _(b'start and print only the URL')),
6563 (b'', b'print-url', None, _(b'start and print only the URL')),
6564 ]
6564 ]
6565 + subrepoopts,
6565 + subrepoopts,
6566 _(b'[OPTION]...'),
6566 _(b'[OPTION]...'),
6567 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6567 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6568 helpbasic=True,
6568 helpbasic=True,
6569 optionalrepo=True,
6569 optionalrepo=True,
6570 )
6570 )
6571 def serve(ui, repo, **opts):
6571 def serve(ui, repo, **opts):
6572 """start stand-alone webserver
6572 """start stand-alone webserver
6573
6573
6574 Start a local HTTP repository browser and pull server. You can use
6574 Start a local HTTP repository browser and pull server. You can use
6575 this for ad-hoc sharing and browsing of repositories. It is
6575 this for ad-hoc sharing and browsing of repositories. It is
6576 recommended to use a real web server to serve a repository for
6576 recommended to use a real web server to serve a repository for
6577 longer periods of time.
6577 longer periods of time.
6578
6578
6579 Please note that the server does not implement access control.
6579 Please note that the server does not implement access control.
6580 This means that, by default, anybody can read from the server and
6580 This means that, by default, anybody can read from the server and
6581 nobody can write to it by default. Set the ``web.allow-push``
6581 nobody can write to it by default. Set the ``web.allow-push``
6582 option to ``*`` to allow everybody to push to the server. You
6582 option to ``*`` to allow everybody to push to the server. You
6583 should use a real web server if you need to authenticate users.
6583 should use a real web server if you need to authenticate users.
6584
6584
6585 By default, the server logs accesses to stdout and errors to
6585 By default, the server logs accesses to stdout and errors to
6586 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6586 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6587 files.
6587 files.
6588
6588
6589 To have the server choose a free port number to listen on, specify
6589 To have the server choose a free port number to listen on, specify
6590 a port number of 0; in this case, the server will print the port
6590 a port number of 0; in this case, the server will print the port
6591 number it uses.
6591 number it uses.
6592
6592
6593 Returns 0 on success.
6593 Returns 0 on success.
6594 """
6594 """
6595
6595
6596 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6596 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6597 opts = pycompat.byteskwargs(opts)
6597 opts = pycompat.byteskwargs(opts)
6598 if opts[b"print_url"] and ui.verbose:
6598 if opts[b"print_url"] and ui.verbose:
6599 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6599 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6600
6600
6601 if opts[b"stdio"]:
6601 if opts[b"stdio"]:
6602 if repo is None:
6602 if repo is None:
6603 raise error.RepoError(
6603 raise error.RepoError(
6604 _(b"there is no Mercurial repository here (.hg not found)")
6604 _(b"there is no Mercurial repository here (.hg not found)")
6605 )
6605 )
6606 s = wireprotoserver.sshserver(ui, repo)
6606 s = wireprotoserver.sshserver(ui, repo)
6607 s.serve_forever()
6607 s.serve_forever()
6608 return
6608 return
6609
6609
6610 service = server.createservice(ui, repo, opts)
6610 service = server.createservice(ui, repo, opts)
6611 return server.runservice(opts, initfn=service.init, runfn=service.run)
6611 return server.runservice(opts, initfn=service.init, runfn=service.run)
6612
6612
6613
6613
6614 @command(
6614 @command(
6615 b'shelve',
6615 b'shelve',
6616 [
6616 [
6617 (
6617 (
6618 b'A',
6618 b'A',
6619 b'addremove',
6619 b'addremove',
6620 None,
6620 None,
6621 _(b'mark new/missing files as added/removed before shelving'),
6621 _(b'mark new/missing files as added/removed before shelving'),
6622 ),
6622 ),
6623 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6623 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6624 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6624 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6625 (
6625 (
6626 b'',
6626 b'',
6627 b'date',
6627 b'date',
6628 b'',
6628 b'',
6629 _(b'shelve with the specified commit date'),
6629 _(b'shelve with the specified commit date'),
6630 _(b'DATE'),
6630 _(b'DATE'),
6631 ),
6631 ),
6632 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6632 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6633 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6633 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6634 (
6634 (
6635 b'k',
6635 b'k',
6636 b'keep',
6636 b'keep',
6637 False,
6637 False,
6638 _(b'shelve, but keep changes in the working directory'),
6638 _(b'shelve, but keep changes in the working directory'),
6639 ),
6639 ),
6640 (b'l', b'list', None, _(b'list current shelves')),
6640 (b'l', b'list', None, _(b'list current shelves')),
6641 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6641 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6642 (
6642 (
6643 b'n',
6643 b'n',
6644 b'name',
6644 b'name',
6645 b'',
6645 b'',
6646 _(b'use the given name for the shelved commit'),
6646 _(b'use the given name for the shelved commit'),
6647 _(b'NAME'),
6647 _(b'NAME'),
6648 ),
6648 ),
6649 (
6649 (
6650 b'p',
6650 b'p',
6651 b'patch',
6651 b'patch',
6652 None,
6652 None,
6653 _(
6653 _(
6654 b'output patches for changes (provide the names of the shelved '
6654 b'output patches for changes (provide the names of the shelved '
6655 b'changes as positional arguments)'
6655 b'changes as positional arguments)'
6656 ),
6656 ),
6657 ),
6657 ),
6658 (b'i', b'interactive', None, _(b'interactive mode')),
6658 (b'i', b'interactive', None, _(b'interactive mode')),
6659 (
6659 (
6660 b'',
6660 b'',
6661 b'stat',
6661 b'stat',
6662 None,
6662 None,
6663 _(
6663 _(
6664 b'output diffstat-style summary of changes (provide the names of '
6664 b'output diffstat-style summary of changes (provide the names of '
6665 b'the shelved changes as positional arguments)'
6665 b'the shelved changes as positional arguments)'
6666 ),
6666 ),
6667 ),
6667 ),
6668 ]
6668 ]
6669 + cmdutil.walkopts,
6669 + cmdutil.walkopts,
6670 _(b'hg shelve [OPTION]... [FILE]...'),
6670 _(b'hg shelve [OPTION]... [FILE]...'),
6671 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6671 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6672 )
6672 )
6673 def shelve(ui, repo, *pats, **opts):
6673 def shelve(ui, repo, *pats, **opts):
6674 """save and set aside changes from the working directory
6674 """save and set aside changes from the working directory
6675
6675
6676 Shelving takes files that "hg status" reports as not clean, saves
6676 Shelving takes files that "hg status" reports as not clean, saves
6677 the modifications to a bundle (a shelved change), and reverts the
6677 the modifications to a bundle (a shelved change), and reverts the
6678 files so that their state in the working directory becomes clean.
6678 files so that their state in the working directory becomes clean.
6679
6679
6680 To restore these changes to the working directory, using "hg
6680 To restore these changes to the working directory, using "hg
6681 unshelve"; this will work even if you switch to a different
6681 unshelve"; this will work even if you switch to a different
6682 commit.
6682 commit.
6683
6683
6684 When no files are specified, "hg shelve" saves all not-clean
6684 When no files are specified, "hg shelve" saves all not-clean
6685 files. If specific files or directories are named, only changes to
6685 files. If specific files or directories are named, only changes to
6686 those files are shelved.
6686 those files are shelved.
6687
6687
6688 In bare shelve (when no files are specified, without interactive,
6688 In bare shelve (when no files are specified, without interactive,
6689 include and exclude option), shelving remembers information if the
6689 include and exclude option), shelving remembers information if the
6690 working directory was on newly created branch, in other words working
6690 working directory was on newly created branch, in other words working
6691 directory was on different branch than its first parent. In this
6691 directory was on different branch than its first parent. In this
6692 situation unshelving restores branch information to the working directory.
6692 situation unshelving restores branch information to the working directory.
6693
6693
6694 Each shelved change has a name that makes it easier to find later.
6694 Each shelved change has a name that makes it easier to find later.
6695 The name of a shelved change defaults to being based on the active
6695 The name of a shelved change defaults to being based on the active
6696 bookmark, or if there is no active bookmark, the current named
6696 bookmark, or if there is no active bookmark, the current named
6697 branch. To specify a different name, use ``--name``.
6697 branch. To specify a different name, use ``--name``.
6698
6698
6699 To see a list of existing shelved changes, use the ``--list``
6699 To see a list of existing shelved changes, use the ``--list``
6700 option. For each shelved change, this will print its name, age,
6700 option. For each shelved change, this will print its name, age,
6701 and description; use ``--patch`` or ``--stat`` for more details.
6701 and description; use ``--patch`` or ``--stat`` for more details.
6702
6702
6703 To delete specific shelved changes, use ``--delete``. To delete
6703 To delete specific shelved changes, use ``--delete``. To delete
6704 all shelved changes, use ``--cleanup``.
6704 all shelved changes, use ``--cleanup``.
6705 """
6705 """
6706 opts = pycompat.byteskwargs(opts)
6706 opts = pycompat.byteskwargs(opts)
6707 allowables = [
6707 allowables = [
6708 (b'addremove', {b'create'}), # 'create' is pseudo action
6708 (b'addremove', {b'create'}), # 'create' is pseudo action
6709 (b'unknown', {b'create'}),
6709 (b'unknown', {b'create'}),
6710 (b'cleanup', {b'cleanup'}),
6710 (b'cleanup', {b'cleanup'}),
6711 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6711 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6712 (b'delete', {b'delete'}),
6712 (b'delete', {b'delete'}),
6713 (b'edit', {b'create'}),
6713 (b'edit', {b'create'}),
6714 (b'keep', {b'create'}),
6714 (b'keep', {b'create'}),
6715 (b'list', {b'list'}),
6715 (b'list', {b'list'}),
6716 (b'message', {b'create'}),
6716 (b'message', {b'create'}),
6717 (b'name', {b'create'}),
6717 (b'name', {b'create'}),
6718 (b'patch', {b'patch', b'list'}),
6718 (b'patch', {b'patch', b'list'}),
6719 (b'stat', {b'stat', b'list'}),
6719 (b'stat', {b'stat', b'list'}),
6720 ]
6720 ]
6721
6721
6722 def checkopt(opt):
6722 def checkopt(opt):
6723 if opts.get(opt):
6723 if opts.get(opt):
6724 for i, allowable in allowables:
6724 for i, allowable in allowables:
6725 if opts[i] and opt not in allowable:
6725 if opts[i] and opt not in allowable:
6726 raise error.InputError(
6726 raise error.InputError(
6727 _(
6727 _(
6728 b"options '--%s' and '--%s' may not be "
6728 b"options '--%s' and '--%s' may not be "
6729 b"used together"
6729 b"used together"
6730 )
6730 )
6731 % (opt, i)
6731 % (opt, i)
6732 )
6732 )
6733 return True
6733 return True
6734
6734
6735 if checkopt(b'cleanup'):
6735 if checkopt(b'cleanup'):
6736 if pats:
6736 if pats:
6737 raise error.InputError(
6737 raise error.InputError(
6738 _(b"cannot specify names when using '--cleanup'")
6738 _(b"cannot specify names when using '--cleanup'")
6739 )
6739 )
6740 return shelvemod.cleanupcmd(ui, repo)
6740 return shelvemod.cleanupcmd(ui, repo)
6741 elif checkopt(b'delete'):
6741 elif checkopt(b'delete'):
6742 return shelvemod.deletecmd(ui, repo, pats)
6742 return shelvemod.deletecmd(ui, repo, pats)
6743 elif checkopt(b'list'):
6743 elif checkopt(b'list'):
6744 return shelvemod.listcmd(ui, repo, pats, opts)
6744 return shelvemod.listcmd(ui, repo, pats, opts)
6745 elif checkopt(b'patch') or checkopt(b'stat'):
6745 elif checkopt(b'patch') or checkopt(b'stat'):
6746 return shelvemod.patchcmds(ui, repo, pats, opts)
6746 return shelvemod.patchcmds(ui, repo, pats, opts)
6747 else:
6747 else:
6748 return shelvemod.createcmd(ui, repo, pats, opts)
6748 return shelvemod.createcmd(ui, repo, pats, opts)
6749
6749
6750
6750
6751 _NOTTERSE = b'nothing'
6751 _NOTTERSE = b'nothing'
6752
6752
6753
6753
6754 @command(
6754 @command(
6755 b'status|st',
6755 b'status|st',
6756 [
6756 [
6757 (b'A', b'all', None, _(b'show status of all files')),
6757 (b'A', b'all', None, _(b'show status of all files')),
6758 (b'm', b'modified', None, _(b'show only modified files')),
6758 (b'm', b'modified', None, _(b'show only modified files')),
6759 (b'a', b'added', None, _(b'show only added files')),
6759 (b'a', b'added', None, _(b'show only added files')),
6760 (b'r', b'removed', None, _(b'show only removed files')),
6760 (b'r', b'removed', None, _(b'show only removed files')),
6761 (b'd', b'deleted', None, _(b'show only missing files')),
6761 (b'd', b'deleted', None, _(b'show only missing files')),
6762 (b'c', b'clean', None, _(b'show only files without changes')),
6762 (b'c', b'clean', None, _(b'show only files without changes')),
6763 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6763 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6764 (b'i', b'ignored', None, _(b'show only ignored files')),
6764 (b'i', b'ignored', None, _(b'show only ignored files')),
6765 (b'n', b'no-status', None, _(b'hide status prefix')),
6765 (b'n', b'no-status', None, _(b'hide status prefix')),
6766 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6766 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6767 (
6767 (
6768 b'C',
6768 b'C',
6769 b'copies',
6769 b'copies',
6770 None,
6770 None,
6771 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6771 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6772 ),
6772 ),
6773 (
6773 (
6774 b'0',
6774 b'0',
6775 b'print0',
6775 b'print0',
6776 None,
6776 None,
6777 _(b'end filenames with NUL, for use with xargs'),
6777 _(b'end filenames with NUL, for use with xargs'),
6778 ),
6778 ),
6779 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6779 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6780 (
6780 (
6781 b'',
6781 b'',
6782 b'change',
6782 b'change',
6783 b'',
6783 b'',
6784 _(b'list the changed files of a revision'),
6784 _(b'list the changed files of a revision'),
6785 _(b'REV'),
6785 _(b'REV'),
6786 ),
6786 ),
6787 ]
6787 ]
6788 + walkopts
6788 + walkopts
6789 + subrepoopts
6789 + subrepoopts
6790 + formatteropts,
6790 + formatteropts,
6791 _(b'[OPTION]... [FILE]...'),
6791 _(b'[OPTION]... [FILE]...'),
6792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6792 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6793 helpbasic=True,
6793 helpbasic=True,
6794 inferrepo=True,
6794 inferrepo=True,
6795 intents={INTENT_READONLY},
6795 intents={INTENT_READONLY},
6796 )
6796 )
6797 def status(ui, repo, *pats, **opts):
6797 def status(ui, repo, *pats, **opts):
6798 """show changed files in the working directory
6798 """show changed files in the working directory
6799
6799
6800 Show status of files in the repository. If names are given, only
6800 Show status of files in the repository. If names are given, only
6801 files that match are shown. Files that are clean or ignored or
6801 files that match are shown. Files that are clean or ignored or
6802 the source of a copy/move operation, are not listed unless
6802 the source of a copy/move operation, are not listed unless
6803 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6803 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6804 Unless options described with "show only ..." are given, the
6804 Unless options described with "show only ..." are given, the
6805 options -mardu are used.
6805 options -mardu are used.
6806
6806
6807 Option -q/--quiet hides untracked (unknown and ignored) files
6807 Option -q/--quiet hides untracked (unknown and ignored) files
6808 unless explicitly requested with -u/--unknown or -i/--ignored.
6808 unless explicitly requested with -u/--unknown or -i/--ignored.
6809
6809
6810 .. note::
6810 .. note::
6811
6811
6812 :hg:`status` may appear to disagree with diff if permissions have
6812 :hg:`status` may appear to disagree with diff if permissions have
6813 changed or a merge has occurred. The standard diff format does
6813 changed or a merge has occurred. The standard diff format does
6814 not report permission changes and diff only reports changes
6814 not report permission changes and diff only reports changes
6815 relative to one merge parent.
6815 relative to one merge parent.
6816
6816
6817 If one revision is given, it is used as the base revision.
6817 If one revision is given, it is used as the base revision.
6818 If two revisions are given, the differences between them are
6818 If two revisions are given, the differences between them are
6819 shown. The --change option can also be used as a shortcut to list
6819 shown. The --change option can also be used as a shortcut to list
6820 the changed files of a revision from its first parent.
6820 the changed files of a revision from its first parent.
6821
6821
6822 The codes used to show the status of files are::
6822 The codes used to show the status of files are::
6823
6823
6824 M = modified
6824 M = modified
6825 A = added
6825 A = added
6826 R = removed
6826 R = removed
6827 C = clean
6827 C = clean
6828 ! = missing (deleted by non-hg command, but still tracked)
6828 ! = missing (deleted by non-hg command, but still tracked)
6829 ? = not tracked
6829 ? = not tracked
6830 I = ignored
6830 I = ignored
6831 = origin of the previous file (with --copies)
6831 = origin of the previous file (with --copies)
6832
6832
6833 .. container:: verbose
6833 .. container:: verbose
6834
6834
6835 The -t/--terse option abbreviates the output by showing only the directory
6835 The -t/--terse option abbreviates the output by showing only the directory
6836 name if all the files in it share the same status. The option takes an
6836 name if all the files in it share the same status. The option takes an
6837 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6837 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6838 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6838 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6839 for 'ignored' and 'c' for clean.
6839 for 'ignored' and 'c' for clean.
6840
6840
6841 It abbreviates only those statuses which are passed. Note that clean and
6841 It abbreviates only those statuses which are passed. Note that clean and
6842 ignored files are not displayed with '--terse ic' unless the -c/--clean
6842 ignored files are not displayed with '--terse ic' unless the -c/--clean
6843 and -i/--ignored options are also used.
6843 and -i/--ignored options are also used.
6844
6844
6845 The -v/--verbose option shows information when the repository is in an
6845 The -v/--verbose option shows information when the repository is in an
6846 unfinished merge, shelve, rebase state etc. You can have this behavior
6846 unfinished merge, shelve, rebase state etc. You can have this behavior
6847 turned on by default by enabling the ``commands.status.verbose`` option.
6847 turned on by default by enabling the ``commands.status.verbose`` option.
6848
6848
6849 You can skip displaying some of these states by setting
6849 You can skip displaying some of these states by setting
6850 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6850 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6851 'histedit', 'merge', 'rebase', or 'unshelve'.
6851 'histedit', 'merge', 'rebase', or 'unshelve'.
6852
6852
6853 Template:
6853 Template:
6854
6854
6855 The following keywords are supported in addition to the common template
6855 The following keywords are supported in addition to the common template
6856 keywords and functions. See also :hg:`help templates`.
6856 keywords and functions. See also :hg:`help templates`.
6857
6857
6858 :path: String. Repository-absolute path of the file.
6858 :path: String. Repository-absolute path of the file.
6859 :source: String. Repository-absolute path of the file originated from.
6859 :source: String. Repository-absolute path of the file originated from.
6860 Available if ``--copies`` is specified.
6860 Available if ``--copies`` is specified.
6861 :status: String. Character denoting file's status.
6861 :status: String. Character denoting file's status.
6862
6862
6863 Examples:
6863 Examples:
6864
6864
6865 - show changes in the working directory relative to a
6865 - show changes in the working directory relative to a
6866 changeset::
6866 changeset::
6867
6867
6868 hg status --rev 9353
6868 hg status --rev 9353
6869
6869
6870 - show changes in the working directory relative to the
6870 - show changes in the working directory relative to the
6871 current directory (see :hg:`help patterns` for more information)::
6871 current directory (see :hg:`help patterns` for more information)::
6872
6872
6873 hg status re:
6873 hg status re:
6874
6874
6875 - show all changes including copies in an existing changeset::
6875 - show all changes including copies in an existing changeset::
6876
6876
6877 hg status --copies --change 9353
6877 hg status --copies --change 9353
6878
6878
6879 - get a NUL separated list of added files, suitable for xargs::
6879 - get a NUL separated list of added files, suitable for xargs::
6880
6880
6881 hg status -an0
6881 hg status -an0
6882
6882
6883 - show more information about the repository status, abbreviating
6883 - show more information about the repository status, abbreviating
6884 added, removed, modified, deleted, and untracked paths::
6884 added, removed, modified, deleted, and untracked paths::
6885
6885
6886 hg status -v -t mardu
6886 hg status -v -t mardu
6887
6887
6888 Returns 0 on success.
6888 Returns 0 on success.
6889
6889
6890 """
6890 """
6891
6891
6892 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6892 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6893 opts = pycompat.byteskwargs(opts)
6893 opts = pycompat.byteskwargs(opts)
6894 revs = opts.get(b'rev')
6894 revs = opts.get(b'rev')
6895 change = opts.get(b'change')
6895 change = opts.get(b'change')
6896 terse = opts.get(b'terse')
6896 terse = opts.get(b'terse')
6897 if terse is _NOTTERSE:
6897 if terse is _NOTTERSE:
6898 if revs:
6898 if revs:
6899 terse = b''
6899 terse = b''
6900 else:
6900 else:
6901 terse = ui.config(b'commands', b'status.terse')
6901 terse = ui.config(b'commands', b'status.terse')
6902
6902
6903 if revs and terse:
6903 if revs and terse:
6904 msg = _(b'cannot use --terse with --rev')
6904 msg = _(b'cannot use --terse with --rev')
6905 raise error.InputError(msg)
6905 raise error.InputError(msg)
6906 elif change:
6906 elif change:
6907 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6907 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6908 ctx2 = scmutil.revsingle(repo, change, None)
6908 ctx2 = scmutil.revsingle(repo, change, None)
6909 ctx1 = ctx2.p1()
6909 ctx1 = ctx2.p1()
6910 else:
6910 else:
6911 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6911 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6912 ctx1, ctx2 = scmutil.revpair(repo, revs)
6912 ctx1, ctx2 = scmutil.revpair(repo, revs)
6913
6913
6914 forcerelativevalue = None
6914 forcerelativevalue = None
6915 if ui.hasconfig(b'commands', b'status.relative'):
6915 if ui.hasconfig(b'commands', b'status.relative'):
6916 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6916 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6917 uipathfn = scmutil.getuipathfn(
6917 uipathfn = scmutil.getuipathfn(
6918 repo,
6918 repo,
6919 legacyrelativevalue=bool(pats),
6919 legacyrelativevalue=bool(pats),
6920 forcerelativevalue=forcerelativevalue,
6920 forcerelativevalue=forcerelativevalue,
6921 )
6921 )
6922
6922
6923 if opts.get(b'print0'):
6923 if opts.get(b'print0'):
6924 end = b'\0'
6924 end = b'\0'
6925 else:
6925 else:
6926 end = b'\n'
6926 end = b'\n'
6927 states = b'modified added removed deleted unknown ignored clean'.split()
6927 states = b'modified added removed deleted unknown ignored clean'.split()
6928 show = [k for k in states if opts.get(k)]
6928 show = [k for k in states if opts.get(k)]
6929 if opts.get(b'all'):
6929 if opts.get(b'all'):
6930 show += ui.quiet and (states[:4] + [b'clean']) or states
6930 show += ui.quiet and (states[:4] + [b'clean']) or states
6931
6931
6932 if not show:
6932 if not show:
6933 if ui.quiet:
6933 if ui.quiet:
6934 show = states[:4]
6934 show = states[:4]
6935 else:
6935 else:
6936 show = states[:5]
6936 show = states[:5]
6937
6937
6938 m = scmutil.match(ctx2, pats, opts)
6938 m = scmutil.match(ctx2, pats, opts)
6939 if terse:
6939 if terse:
6940 # we need to compute clean and unknown to terse
6940 # we need to compute clean and unknown to terse
6941 stat = repo.status(
6941 stat = repo.status(
6942 ctx1.node(),
6942 ctx1.node(),
6943 ctx2.node(),
6943 ctx2.node(),
6944 m,
6944 m,
6945 b'ignored' in show or b'i' in terse,
6945 b'ignored' in show or b'i' in terse,
6946 clean=True,
6946 clean=True,
6947 unknown=True,
6947 unknown=True,
6948 listsubrepos=opts.get(b'subrepos'),
6948 listsubrepos=opts.get(b'subrepos'),
6949 )
6949 )
6950
6950
6951 stat = cmdutil.tersedir(stat, terse)
6951 stat = cmdutil.tersedir(stat, terse)
6952 else:
6952 else:
6953 stat = repo.status(
6953 stat = repo.status(
6954 ctx1.node(),
6954 ctx1.node(),
6955 ctx2.node(),
6955 ctx2.node(),
6956 m,
6956 m,
6957 b'ignored' in show,
6957 b'ignored' in show,
6958 b'clean' in show,
6958 b'clean' in show,
6959 b'unknown' in show,
6959 b'unknown' in show,
6960 opts.get(b'subrepos'),
6960 opts.get(b'subrepos'),
6961 )
6961 )
6962
6962
6963 changestates = zip(
6963 changestates = zip(
6964 states,
6964 states,
6965 pycompat.iterbytestr(b'MAR!?IC'),
6965 pycompat.iterbytestr(b'MAR!?IC'),
6966 [getattr(stat, s.decode('utf8')) for s in states],
6966 [getattr(stat, s.decode('utf8')) for s in states],
6967 )
6967 )
6968
6968
6969 copy = {}
6969 copy = {}
6970 if (
6970 if (
6971 opts.get(b'all')
6971 opts.get(b'all')
6972 or opts.get(b'copies')
6972 or opts.get(b'copies')
6973 or ui.configbool(b'ui', b'statuscopies')
6973 or ui.configbool(b'ui', b'statuscopies')
6974 ) and not opts.get(b'no_status'):
6974 ) and not opts.get(b'no_status'):
6975 copy = copies.pathcopies(ctx1, ctx2, m)
6975 copy = copies.pathcopies(ctx1, ctx2, m)
6976
6976
6977 morestatus = None
6977 morestatus = None
6978 if (
6978 if (
6979 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6979 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6980 and not ui.plain()
6980 and not ui.plain()
6981 and not opts.get(b'print0')
6981 and not opts.get(b'print0')
6982 ):
6982 ):
6983 morestatus = cmdutil.readmorestatus(repo)
6983 morestatus = cmdutil.readmorestatus(repo)
6984
6984
6985 ui.pager(b'status')
6985 ui.pager(b'status')
6986 fm = ui.formatter(b'status', opts)
6986 fm = ui.formatter(b'status', opts)
6987 fmt = b'%s' + end
6987 fmt = b'%s' + end
6988 showchar = not opts.get(b'no_status')
6988 showchar = not opts.get(b'no_status')
6989
6989
6990 for state, char, files in changestates:
6990 for state, char, files in changestates:
6991 if state in show:
6991 if state in show:
6992 label = b'status.' + state
6992 label = b'status.' + state
6993 for f in files:
6993 for f in files:
6994 fm.startitem()
6994 fm.startitem()
6995 fm.context(ctx=ctx2)
6995 fm.context(ctx=ctx2)
6996 fm.data(itemtype=b'file', path=f)
6996 fm.data(itemtype=b'file', path=f)
6997 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6997 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6998 fm.plain(fmt % uipathfn(f), label=label)
6998 fm.plain(fmt % uipathfn(f), label=label)
6999 if f in copy:
6999 if f in copy:
7000 fm.data(source=copy[f])
7000 fm.data(source=copy[f])
7001 fm.plain(
7001 fm.plain(
7002 (b' %s' + end) % uipathfn(copy[f]),
7002 (b' %s' + end) % uipathfn(copy[f]),
7003 label=b'status.copied',
7003 label=b'status.copied',
7004 )
7004 )
7005 if morestatus:
7005 if morestatus:
7006 morestatus.formatfile(f, fm)
7006 morestatus.formatfile(f, fm)
7007
7007
7008 if morestatus:
7008 if morestatus:
7009 morestatus.formatfooter(fm)
7009 morestatus.formatfooter(fm)
7010 fm.end()
7010 fm.end()
7011
7011
7012
7012
7013 @command(
7013 @command(
7014 b'summary|sum',
7014 b'summary|sum',
7015 [(b'', b'remote', None, _(b'check for push and pull'))],
7015 [(b'', b'remote', None, _(b'check for push and pull'))],
7016 b'[--remote]',
7016 b'[--remote]',
7017 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7017 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7018 helpbasic=True,
7018 helpbasic=True,
7019 intents={INTENT_READONLY},
7019 intents={INTENT_READONLY},
7020 )
7020 )
7021 def summary(ui, repo, **opts):
7021 def summary(ui, repo, **opts):
7022 """summarize working directory state
7022 """summarize working directory state
7023
7023
7024 This generates a brief summary of the working directory state,
7024 This generates a brief summary of the working directory state,
7025 including parents, branch, commit status, phase and available updates.
7025 including parents, branch, commit status, phase and available updates.
7026
7026
7027 With the --remote option, this will check the default paths for
7027 With the --remote option, this will check the default paths for
7028 incoming and outgoing changes. This can be time-consuming.
7028 incoming and outgoing changes. This can be time-consuming.
7029
7029
7030 Returns 0 on success.
7030 Returns 0 on success.
7031 """
7031 """
7032
7032
7033 opts = pycompat.byteskwargs(opts)
7033 opts = pycompat.byteskwargs(opts)
7034 ui.pager(b'summary')
7034 ui.pager(b'summary')
7035 ctx = repo[None]
7035 ctx = repo[None]
7036 parents = ctx.parents()
7036 parents = ctx.parents()
7037 pnode = parents[0].node()
7037 pnode = parents[0].node()
7038 marks = []
7038 marks = []
7039
7039
7040 try:
7040 try:
7041 ms = mergestatemod.mergestate.read(repo)
7041 ms = mergestatemod.mergestate.read(repo)
7042 except error.UnsupportedMergeRecords as e:
7042 except error.UnsupportedMergeRecords as e:
7043 s = b' '.join(e.recordtypes)
7043 s = b' '.join(e.recordtypes)
7044 ui.warn(
7044 ui.warn(
7045 _(b'warning: merge state has unsupported record types: %s\n') % s
7045 _(b'warning: merge state has unsupported record types: %s\n') % s
7046 )
7046 )
7047 unresolved = []
7047 unresolved = []
7048 else:
7048 else:
7049 unresolved = list(ms.unresolved())
7049 unresolved = list(ms.unresolved())
7050
7050
7051 for p in parents:
7051 for p in parents:
7052 # label with log.changeset (instead of log.parent) since this
7052 # label with log.changeset (instead of log.parent) since this
7053 # shows a working directory parent *changeset*:
7053 # shows a working directory parent *changeset*:
7054 # i18n: column positioning for "hg summary"
7054 # i18n: column positioning for "hg summary"
7055 ui.write(
7055 ui.write(
7056 _(b'parent: %d:%s ') % (p.rev(), p),
7056 _(b'parent: %d:%s ') % (p.rev(), p),
7057 label=logcmdutil.changesetlabels(p),
7057 label=logcmdutil.changesetlabels(p),
7058 )
7058 )
7059 ui.write(b' '.join(p.tags()), label=b'log.tag')
7059 ui.write(b' '.join(p.tags()), label=b'log.tag')
7060 if p.bookmarks():
7060 if p.bookmarks():
7061 marks.extend(p.bookmarks())
7061 marks.extend(p.bookmarks())
7062 if p.rev() == -1:
7062 if p.rev() == -1:
7063 if not len(repo):
7063 if not len(repo):
7064 ui.write(_(b' (empty repository)'))
7064 ui.write(_(b' (empty repository)'))
7065 else:
7065 else:
7066 ui.write(_(b' (no revision checked out)'))
7066 ui.write(_(b' (no revision checked out)'))
7067 if p.obsolete():
7067 if p.obsolete():
7068 ui.write(_(b' (obsolete)'))
7068 ui.write(_(b' (obsolete)'))
7069 if p.isunstable():
7069 if p.isunstable():
7070 instabilities = (
7070 instabilities = (
7071 ui.label(instability, b'trouble.%s' % instability)
7071 ui.label(instability, b'trouble.%s' % instability)
7072 for instability in p.instabilities()
7072 for instability in p.instabilities()
7073 )
7073 )
7074 ui.write(b' (' + b', '.join(instabilities) + b')')
7074 ui.write(b' (' + b', '.join(instabilities) + b')')
7075 ui.write(b'\n')
7075 ui.write(b'\n')
7076 if p.description():
7076 if p.description():
7077 ui.status(
7077 ui.status(
7078 b' ' + p.description().splitlines()[0].strip() + b'\n',
7078 b' ' + p.description().splitlines()[0].strip() + b'\n',
7079 label=b'log.summary',
7079 label=b'log.summary',
7080 )
7080 )
7081
7081
7082 branch = ctx.branch()
7082 branch = ctx.branch()
7083 bheads = repo.branchheads(branch)
7083 bheads = repo.branchheads(branch)
7084 # i18n: column positioning for "hg summary"
7084 # i18n: column positioning for "hg summary"
7085 m = _(b'branch: %s\n') % branch
7085 m = _(b'branch: %s\n') % branch
7086 if branch != b'default':
7086 if branch != b'default':
7087 ui.write(m, label=b'log.branch')
7087 ui.write(m, label=b'log.branch')
7088 else:
7088 else:
7089 ui.status(m, label=b'log.branch')
7089 ui.status(m, label=b'log.branch')
7090
7090
7091 if marks:
7091 if marks:
7092 active = repo._activebookmark
7092 active = repo._activebookmark
7093 # i18n: column positioning for "hg summary"
7093 # i18n: column positioning for "hg summary"
7094 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7094 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7095 if active is not None:
7095 if active is not None:
7096 if active in marks:
7096 if active in marks:
7097 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7097 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7098 marks.remove(active)
7098 marks.remove(active)
7099 else:
7099 else:
7100 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7100 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7101 for m in marks:
7101 for m in marks:
7102 ui.write(b' ' + m, label=b'log.bookmark')
7102 ui.write(b' ' + m, label=b'log.bookmark')
7103 ui.write(b'\n', label=b'log.bookmark')
7103 ui.write(b'\n', label=b'log.bookmark')
7104
7104
7105 status = repo.status(unknown=True)
7105 status = repo.status(unknown=True)
7106
7106
7107 c = repo.dirstate.copies()
7107 c = repo.dirstate.copies()
7108 copied, renamed = [], []
7108 copied, renamed = [], []
7109 for d, s in pycompat.iteritems(c):
7109 for d, s in pycompat.iteritems(c):
7110 if s in status.removed:
7110 if s in status.removed:
7111 status.removed.remove(s)
7111 status.removed.remove(s)
7112 renamed.append(d)
7112 renamed.append(d)
7113 else:
7113 else:
7114 copied.append(d)
7114 copied.append(d)
7115 if d in status.added:
7115 if d in status.added:
7116 status.added.remove(d)
7116 status.added.remove(d)
7117
7117
7118 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7118 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7119
7119
7120 labels = [
7120 labels = [
7121 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7121 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7122 (ui.label(_(b'%d added'), b'status.added'), status.added),
7122 (ui.label(_(b'%d added'), b'status.added'), status.added),
7123 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7123 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7124 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7124 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7125 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7125 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7126 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7126 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7127 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7127 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7128 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7128 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7129 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7129 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7130 ]
7130 ]
7131 t = []
7131 t = []
7132 for l, s in labels:
7132 for l, s in labels:
7133 if s:
7133 if s:
7134 t.append(l % len(s))
7134 t.append(l % len(s))
7135
7135
7136 t = b', '.join(t)
7136 t = b', '.join(t)
7137 cleanworkdir = False
7137 cleanworkdir = False
7138
7138
7139 if repo.vfs.exists(b'graftstate'):
7139 if repo.vfs.exists(b'graftstate'):
7140 t += _(b' (graft in progress)')
7140 t += _(b' (graft in progress)')
7141 if repo.vfs.exists(b'updatestate'):
7141 if repo.vfs.exists(b'updatestate'):
7142 t += _(b' (interrupted update)')
7142 t += _(b' (interrupted update)')
7143 elif len(parents) > 1:
7143 elif len(parents) > 1:
7144 t += _(b' (merge)')
7144 t += _(b' (merge)')
7145 elif branch != parents[0].branch():
7145 elif branch != parents[0].branch():
7146 t += _(b' (new branch)')
7146 t += _(b' (new branch)')
7147 elif parents[0].closesbranch() and pnode in repo.branchheads(
7147 elif parents[0].closesbranch() and pnode in repo.branchheads(
7148 branch, closed=True
7148 branch, closed=True
7149 ):
7149 ):
7150 t += _(b' (head closed)')
7150 t += _(b' (head closed)')
7151 elif not (
7151 elif not (
7152 status.modified
7152 status.modified
7153 or status.added
7153 or status.added
7154 or status.removed
7154 or status.removed
7155 or renamed
7155 or renamed
7156 or copied
7156 or copied
7157 or subs
7157 or subs
7158 ):
7158 ):
7159 t += _(b' (clean)')
7159 t += _(b' (clean)')
7160 cleanworkdir = True
7160 cleanworkdir = True
7161 elif pnode not in bheads:
7161 elif pnode not in bheads:
7162 t += _(b' (new branch head)')
7162 t += _(b' (new branch head)')
7163
7163
7164 if parents:
7164 if parents:
7165 pendingphase = max(p.phase() for p in parents)
7165 pendingphase = max(p.phase() for p in parents)
7166 else:
7166 else:
7167 pendingphase = phases.public
7167 pendingphase = phases.public
7168
7168
7169 if pendingphase > phases.newcommitphase(ui):
7169 if pendingphase > phases.newcommitphase(ui):
7170 t += b' (%s)' % phases.phasenames[pendingphase]
7170 t += b' (%s)' % phases.phasenames[pendingphase]
7171
7171
7172 if cleanworkdir:
7172 if cleanworkdir:
7173 # i18n: column positioning for "hg summary"
7173 # i18n: column positioning for "hg summary"
7174 ui.status(_(b'commit: %s\n') % t.strip())
7174 ui.status(_(b'commit: %s\n') % t.strip())
7175 else:
7175 else:
7176 # i18n: column positioning for "hg summary"
7176 # i18n: column positioning for "hg summary"
7177 ui.write(_(b'commit: %s\n') % t.strip())
7177 ui.write(_(b'commit: %s\n') % t.strip())
7178
7178
7179 # all ancestors of branch heads - all ancestors of parent = new csets
7179 # all ancestors of branch heads - all ancestors of parent = new csets
7180 new = len(
7180 new = len(
7181 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7181 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7182 )
7182 )
7183
7183
7184 if new == 0:
7184 if new == 0:
7185 # i18n: column positioning for "hg summary"
7185 # i18n: column positioning for "hg summary"
7186 ui.status(_(b'update: (current)\n'))
7186 ui.status(_(b'update: (current)\n'))
7187 elif pnode not in bheads:
7187 elif pnode not in bheads:
7188 # i18n: column positioning for "hg summary"
7188 # i18n: column positioning for "hg summary"
7189 ui.write(_(b'update: %d new changesets (update)\n') % new)
7189 ui.write(_(b'update: %d new changesets (update)\n') % new)
7190 else:
7190 else:
7191 # i18n: column positioning for "hg summary"
7191 # i18n: column positioning for "hg summary"
7192 ui.write(
7192 ui.write(
7193 _(b'update: %d new changesets, %d branch heads (merge)\n')
7193 _(b'update: %d new changesets, %d branch heads (merge)\n')
7194 % (new, len(bheads))
7194 % (new, len(bheads))
7195 )
7195 )
7196
7196
7197 t = []
7197 t = []
7198 draft = len(repo.revs(b'draft()'))
7198 draft = len(repo.revs(b'draft()'))
7199 if draft:
7199 if draft:
7200 t.append(_(b'%d draft') % draft)
7200 t.append(_(b'%d draft') % draft)
7201 secret = len(repo.revs(b'secret()'))
7201 secret = len(repo.revs(b'secret()'))
7202 if secret:
7202 if secret:
7203 t.append(_(b'%d secret') % secret)
7203 t.append(_(b'%d secret') % secret)
7204
7204
7205 if draft or secret:
7205 if draft or secret:
7206 ui.status(_(b'phases: %s\n') % b', '.join(t))
7206 ui.status(_(b'phases: %s\n') % b', '.join(t))
7207
7207
7208 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7208 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7209 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7209 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7210 numtrouble = len(repo.revs(trouble + b"()"))
7210 numtrouble = len(repo.revs(trouble + b"()"))
7211 # We write all the possibilities to ease translation
7211 # We write all the possibilities to ease translation
7212 troublemsg = {
7212 troublemsg = {
7213 b"orphan": _(b"orphan: %d changesets"),
7213 b"orphan": _(b"orphan: %d changesets"),
7214 b"contentdivergent": _(b"content-divergent: %d changesets"),
7214 b"contentdivergent": _(b"content-divergent: %d changesets"),
7215 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7215 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7216 }
7216 }
7217 if numtrouble > 0:
7217 if numtrouble > 0:
7218 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7218 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7219
7219
7220 cmdutil.summaryhooks(ui, repo)
7220 cmdutil.summaryhooks(ui, repo)
7221
7221
7222 if opts.get(b'remote'):
7222 if opts.get(b'remote'):
7223 needsincoming, needsoutgoing = True, True
7223 needsincoming, needsoutgoing = True, True
7224 else:
7224 else:
7225 needsincoming, needsoutgoing = False, False
7225 needsincoming, needsoutgoing = False, False
7226 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7226 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7227 if i:
7227 if i:
7228 needsincoming = True
7228 needsincoming = True
7229 if o:
7229 if o:
7230 needsoutgoing = True
7230 needsoutgoing = True
7231 if not needsincoming and not needsoutgoing:
7231 if not needsincoming and not needsoutgoing:
7232 return
7232 return
7233
7233
7234 def getincoming():
7234 def getincoming():
7235 # XXX We should actually skip this if no default is specified, instead
7235 # XXX We should actually skip this if no default is specified, instead
7236 # of passing "default" which will resolve as "./default/" if no default
7236 # of passing "default" which will resolve as "./default/" if no default
7237 # path is defined.
7237 # path is defined.
7238 source, branches = urlutil.get_unique_pull_path(
7238 source, branches = urlutil.get_unique_pull_path(
7239 b'summary', repo, ui, b'default'
7239 b'summary', repo, ui, b'default'
7240 )
7240 )
7241 sbranch = branches[0]
7241 sbranch = branches[0]
7242 try:
7242 try:
7243 other = hg.peer(repo, {}, source)
7243 other = hg.peer(repo, {}, source)
7244 except error.RepoError:
7244 except error.RepoError:
7245 if opts.get(b'remote'):
7245 if opts.get(b'remote'):
7246 raise
7246 raise
7247 return source, sbranch, None, None, None
7247 return source, sbranch, None, None, None
7248 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7248 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7249 if revs:
7249 if revs:
7250 revs = [other.lookup(rev) for rev in revs]
7250 revs = [other.lookup(rev) for rev in revs]
7251 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7251 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7252 with repo.ui.silent():
7252 with repo.ui.silent():
7253 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7253 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7254 return source, sbranch, other, commoninc, commoninc[1]
7254 return source, sbranch, other, commoninc, commoninc[1]
7255
7255
7256 if needsincoming:
7256 if needsincoming:
7257 source, sbranch, sother, commoninc, incoming = getincoming()
7257 source, sbranch, sother, commoninc, incoming = getincoming()
7258 else:
7258 else:
7259 source = sbranch = sother = commoninc = incoming = None
7259 source = sbranch = sother = commoninc = incoming = None
7260
7260
7261 def getoutgoing():
7261 def getoutgoing():
7262 # XXX We should actually skip this if no default is specified, instead
7262 # XXX We should actually skip this if no default is specified, instead
7263 # of passing "default" which will resolve as "./default/" if no default
7263 # of passing "default" which will resolve as "./default/" if no default
7264 # path is defined.
7264 # path is defined.
7265 d = None
7265 d = None
7266 if b'default-push' in ui.paths:
7266 if b'default-push' in ui.paths:
7267 d = b'default-push'
7267 d = b'default-push'
7268 elif b'default' in ui.paths:
7268 elif b'default' in ui.paths:
7269 d = b'default'
7269 d = b'default'
7270 if d is not None:
7270 if d is not None:
7271 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7271 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7272 dest = path.pushloc or path.loc
7272 dest = path.pushloc or path.loc
7273 dbranch = path.branch
7273 dbranch = path.branch
7274 else:
7274 else:
7275 dest = b'default'
7275 dest = b'default'
7276 dbranch = None
7276 dbranch = None
7277 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7277 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7278 if source != dest:
7278 if source != dest:
7279 try:
7279 try:
7280 dother = hg.peer(repo, {}, dest)
7280 dother = hg.peer(repo, {}, dest)
7281 except error.RepoError:
7281 except error.RepoError:
7282 if opts.get(b'remote'):
7282 if opts.get(b'remote'):
7283 raise
7283 raise
7284 return dest, dbranch, None, None
7284 return dest, dbranch, None, None
7285 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7285 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7286 elif sother is None:
7286 elif sother is None:
7287 # there is no explicit destination peer, but source one is invalid
7287 # there is no explicit destination peer, but source one is invalid
7288 return dest, dbranch, None, None
7288 return dest, dbranch, None, None
7289 else:
7289 else:
7290 dother = sother
7290 dother = sother
7291 if source != dest or (sbranch is not None and sbranch != dbranch):
7291 if source != dest or (sbranch is not None and sbranch != dbranch):
7292 common = None
7292 common = None
7293 else:
7293 else:
7294 common = commoninc
7294 common = commoninc
7295 if revs:
7295 if revs:
7296 revs = [repo.lookup(rev) for rev in revs]
7296 revs = [repo.lookup(rev) for rev in revs]
7297 with repo.ui.silent():
7297 with repo.ui.silent():
7298 outgoing = discovery.findcommonoutgoing(
7298 outgoing = discovery.findcommonoutgoing(
7299 repo, dother, onlyheads=revs, commoninc=common
7299 repo, dother, onlyheads=revs, commoninc=common
7300 )
7300 )
7301 return dest, dbranch, dother, outgoing
7301 return dest, dbranch, dother, outgoing
7302
7302
7303 if needsoutgoing:
7303 if needsoutgoing:
7304 dest, dbranch, dother, outgoing = getoutgoing()
7304 dest, dbranch, dother, outgoing = getoutgoing()
7305 else:
7305 else:
7306 dest = dbranch = dother = outgoing = None
7306 dest = dbranch = dother = outgoing = None
7307
7307
7308 if opts.get(b'remote'):
7308 if opts.get(b'remote'):
7309 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7309 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7310 # The former always sets `sother` (or raises an exception if it can't);
7310 # The former always sets `sother` (or raises an exception if it can't);
7311 # the latter always sets `outgoing`.
7311 # the latter always sets `outgoing`.
7312 assert sother is not None
7312 assert sother is not None
7313 assert outgoing is not None
7313 assert outgoing is not None
7314
7314
7315 t = []
7315 t = []
7316 if incoming:
7316 if incoming:
7317 t.append(_(b'1 or more incoming'))
7317 t.append(_(b'1 or more incoming'))
7318 o = outgoing.missing
7318 o = outgoing.missing
7319 if o:
7319 if o:
7320 t.append(_(b'%d outgoing') % len(o))
7320 t.append(_(b'%d outgoing') % len(o))
7321 other = dother or sother
7321 other = dother or sother
7322 if b'bookmarks' in other.listkeys(b'namespaces'):
7322 if b'bookmarks' in other.listkeys(b'namespaces'):
7323 counts = bookmarks.summary(repo, other)
7323 counts = bookmarks.summary(repo, other)
7324 if counts[0] > 0:
7324 if counts[0] > 0:
7325 t.append(_(b'%d incoming bookmarks') % counts[0])
7325 t.append(_(b'%d incoming bookmarks') % counts[0])
7326 if counts[1] > 0:
7326 if counts[1] > 0:
7327 t.append(_(b'%d outgoing bookmarks') % counts[1])
7327 t.append(_(b'%d outgoing bookmarks') % counts[1])
7328
7328
7329 if t:
7329 if t:
7330 # i18n: column positioning for "hg summary"
7330 # i18n: column positioning for "hg summary"
7331 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7331 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7332 else:
7332 else:
7333 # i18n: column positioning for "hg summary"
7333 # i18n: column positioning for "hg summary"
7334 ui.status(_(b'remote: (synced)\n'))
7334 ui.status(_(b'remote: (synced)\n'))
7335
7335
7336 cmdutil.summaryremotehooks(
7336 cmdutil.summaryremotehooks(
7337 ui,
7337 ui,
7338 repo,
7338 repo,
7339 opts,
7339 opts,
7340 (
7340 (
7341 (source, sbranch, sother, commoninc),
7341 (source, sbranch, sother, commoninc),
7342 (dest, dbranch, dother, outgoing),
7342 (dest, dbranch, dother, outgoing),
7343 ),
7343 ),
7344 )
7344 )
7345
7345
7346
7346
7347 @command(
7347 @command(
7348 b'tag',
7348 b'tag',
7349 [
7349 [
7350 (b'f', b'force', None, _(b'force tag')),
7350 (b'f', b'force', None, _(b'force tag')),
7351 (b'l', b'local', None, _(b'make the tag local')),
7351 (b'l', b'local', None, _(b'make the tag local')),
7352 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7352 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7353 (b'', b'remove', None, _(b'remove a tag')),
7353 (b'', b'remove', None, _(b'remove a tag')),
7354 # -l/--local is already there, commitopts cannot be used
7354 # -l/--local is already there, commitopts cannot be used
7355 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7355 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7356 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7356 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7357 ]
7357 ]
7358 + commitopts2,
7358 + commitopts2,
7359 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7359 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7360 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7360 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7361 )
7361 )
7362 def tag(ui, repo, name1, *names, **opts):
7362 def tag(ui, repo, name1, *names, **opts):
7363 """add one or more tags for the current or given revision
7363 """add one or more tags for the current or given revision
7364
7364
7365 Name a particular revision using <name>.
7365 Name a particular revision using <name>.
7366
7366
7367 Tags are used to name particular revisions of the repository and are
7367 Tags are used to name particular revisions of the repository and are
7368 very useful to compare different revisions, to go back to significant
7368 very useful to compare different revisions, to go back to significant
7369 earlier versions or to mark branch points as releases, etc. Changing
7369 earlier versions or to mark branch points as releases, etc. Changing
7370 an existing tag is normally disallowed; use -f/--force to override.
7370 an existing tag is normally disallowed; use -f/--force to override.
7371
7371
7372 If no revision is given, the parent of the working directory is
7372 If no revision is given, the parent of the working directory is
7373 used.
7373 used.
7374
7374
7375 To facilitate version control, distribution, and merging of tags,
7375 To facilitate version control, distribution, and merging of tags,
7376 they are stored as a file named ".hgtags" which is managed similarly
7376 they are stored as a file named ".hgtags" which is managed similarly
7377 to other project files and can be hand-edited if necessary. This
7377 to other project files and can be hand-edited if necessary. This
7378 also means that tagging creates a new commit. The file
7378 also means that tagging creates a new commit. The file
7379 ".hg/localtags" is used for local tags (not shared among
7379 ".hg/localtags" is used for local tags (not shared among
7380 repositories).
7380 repositories).
7381
7381
7382 Tag commits are usually made at the head of a branch. If the parent
7382 Tag commits are usually made at the head of a branch. If the parent
7383 of the working directory is not a branch head, :hg:`tag` aborts; use
7383 of the working directory is not a branch head, :hg:`tag` aborts; use
7384 -f/--force to force the tag commit to be based on a non-head
7384 -f/--force to force the tag commit to be based on a non-head
7385 changeset.
7385 changeset.
7386
7386
7387 See :hg:`help dates` for a list of formats valid for -d/--date.
7387 See :hg:`help dates` for a list of formats valid for -d/--date.
7388
7388
7389 Since tag names have priority over branch names during revision
7389 Since tag names have priority over branch names during revision
7390 lookup, using an existing branch name as a tag name is discouraged.
7390 lookup, using an existing branch name as a tag name is discouraged.
7391
7391
7392 Returns 0 on success.
7392 Returns 0 on success.
7393 """
7393 """
7394 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7394 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7395 opts = pycompat.byteskwargs(opts)
7395 opts = pycompat.byteskwargs(opts)
7396 with repo.wlock(), repo.lock():
7396 with repo.wlock(), repo.lock():
7397 rev_ = b"."
7397 rev_ = b"."
7398 names = [t.strip() for t in (name1,) + names]
7398 names = [t.strip() for t in (name1,) + names]
7399 if len(names) != len(set(names)):
7399 if len(names) != len(set(names)):
7400 raise error.InputError(_(b'tag names must be unique'))
7400 raise error.InputError(_(b'tag names must be unique'))
7401 for n in names:
7401 for n in names:
7402 scmutil.checknewlabel(repo, n, b'tag')
7402 scmutil.checknewlabel(repo, n, b'tag')
7403 if not n:
7403 if not n:
7404 raise error.InputError(
7404 raise error.InputError(
7405 _(b'tag names cannot consist entirely of whitespace')
7405 _(b'tag names cannot consist entirely of whitespace')
7406 )
7406 )
7407 if opts.get(b'rev'):
7407 if opts.get(b'rev'):
7408 rev_ = opts[b'rev']
7408 rev_ = opts[b'rev']
7409 message = opts.get(b'message')
7409 message = opts.get(b'message')
7410 if opts.get(b'remove'):
7410 if opts.get(b'remove'):
7411 if opts.get(b'local'):
7411 if opts.get(b'local'):
7412 expectedtype = b'local'
7412 expectedtype = b'local'
7413 else:
7413 else:
7414 expectedtype = b'global'
7414 expectedtype = b'global'
7415
7415
7416 for n in names:
7416 for n in names:
7417 if repo.tagtype(n) == b'global':
7417 if repo.tagtype(n) == b'global':
7418 alltags = tagsmod.findglobaltags(ui, repo)
7418 alltags = tagsmod.findglobaltags(ui, repo)
7419 if alltags[n][0] == repo.nullid:
7419 if alltags[n][0] == repo.nullid:
7420 raise error.InputError(
7420 raise error.InputError(
7421 _(b"tag '%s' is already removed") % n
7421 _(b"tag '%s' is already removed") % n
7422 )
7422 )
7423 if not repo.tagtype(n):
7423 if not repo.tagtype(n):
7424 raise error.InputError(_(b"tag '%s' does not exist") % n)
7424 raise error.InputError(_(b"tag '%s' does not exist") % n)
7425 if repo.tagtype(n) != expectedtype:
7425 if repo.tagtype(n) != expectedtype:
7426 if expectedtype == b'global':
7426 if expectedtype == b'global':
7427 raise error.InputError(
7427 raise error.InputError(
7428 _(b"tag '%s' is not a global tag") % n
7428 _(b"tag '%s' is not a global tag") % n
7429 )
7429 )
7430 else:
7430 else:
7431 raise error.InputError(
7431 raise error.InputError(
7432 _(b"tag '%s' is not a local tag") % n
7432 _(b"tag '%s' is not a local tag") % n
7433 )
7433 )
7434 rev_ = b'null'
7434 rev_ = b'null'
7435 if not message:
7435 if not message:
7436 # we don't translate commit messages
7436 # we don't translate commit messages
7437 message = b'Removed tag %s' % b', '.join(names)
7437 message = b'Removed tag %s' % b', '.join(names)
7438 elif not opts.get(b'force'):
7438 elif not opts.get(b'force'):
7439 for n in names:
7439 for n in names:
7440 if n in repo.tags():
7440 if n in repo.tags():
7441 raise error.InputError(
7441 raise error.InputError(
7442 _(b"tag '%s' already exists (use -f to force)") % n
7442 _(b"tag '%s' already exists (use -f to force)") % n
7443 )
7443 )
7444 if not opts.get(b'local'):
7444 if not opts.get(b'local'):
7445 p1, p2 = repo.dirstate.parents()
7445 p1, p2 = repo.dirstate.parents()
7446 if p2 != repo.nullid:
7446 if p2 != repo.nullid:
7447 raise error.StateError(_(b'uncommitted merge'))
7447 raise error.StateError(_(b'uncommitted merge'))
7448 bheads = repo.branchheads()
7448 bheads = repo.branchheads()
7449 if not opts.get(b'force') and bheads and p1 not in bheads:
7449 if not opts.get(b'force') and bheads and p1 not in bheads:
7450 raise error.InputError(
7450 raise error.InputError(
7451 _(
7451 _(
7452 b'working directory is not at a branch head '
7452 b'working directory is not at a branch head '
7453 b'(use -f to force)'
7453 b'(use -f to force)'
7454 )
7454 )
7455 )
7455 )
7456 node = scmutil.revsingle(repo, rev_).node()
7456 node = scmutil.revsingle(repo, rev_).node()
7457
7457
7458 if not message:
7458 if not message:
7459 # we don't translate commit messages
7459 # we don't translate commit messages
7460 message = b'Added tag %s for changeset %s' % (
7460 message = b'Added tag %s for changeset %s' % (
7461 b', '.join(names),
7461 b', '.join(names),
7462 short(node),
7462 short(node),
7463 )
7463 )
7464
7464
7465 date = opts.get(b'date')
7465 date = opts.get(b'date')
7466 if date:
7466 if date:
7467 date = dateutil.parsedate(date)
7467 date = dateutil.parsedate(date)
7468
7468
7469 if opts.get(b'remove'):
7469 if opts.get(b'remove'):
7470 editform = b'tag.remove'
7470 editform = b'tag.remove'
7471 else:
7471 else:
7472 editform = b'tag.add'
7472 editform = b'tag.add'
7473 editor = cmdutil.getcommiteditor(
7473 editor = cmdutil.getcommiteditor(
7474 editform=editform, **pycompat.strkwargs(opts)
7474 editform=editform, **pycompat.strkwargs(opts)
7475 )
7475 )
7476
7476
7477 # don't allow tagging the null rev
7477 # don't allow tagging the null rev
7478 if (
7478 if (
7479 not opts.get(b'remove')
7479 not opts.get(b'remove')
7480 and scmutil.revsingle(repo, rev_).rev() == nullrev
7480 and scmutil.revsingle(repo, rev_).rev() == nullrev
7481 ):
7481 ):
7482 raise error.InputError(_(b"cannot tag null revision"))
7482 raise error.InputError(_(b"cannot tag null revision"))
7483
7483
7484 tagsmod.tag(
7484 tagsmod.tag(
7485 repo,
7485 repo,
7486 names,
7486 names,
7487 node,
7487 node,
7488 message,
7488 message,
7489 opts.get(b'local'),
7489 opts.get(b'local'),
7490 opts.get(b'user'),
7490 opts.get(b'user'),
7491 date,
7491 date,
7492 editor=editor,
7492 editor=editor,
7493 )
7493 )
7494
7494
7495
7495
7496 @command(
7496 @command(
7497 b'tags',
7497 b'tags',
7498 formatteropts,
7498 formatteropts,
7499 b'',
7499 b'',
7500 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7500 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7501 intents={INTENT_READONLY},
7501 intents={INTENT_READONLY},
7502 )
7502 )
7503 def tags(ui, repo, **opts):
7503 def tags(ui, repo, **opts):
7504 """list repository tags
7504 """list repository tags
7505
7505
7506 This lists both regular and local tags. When the -v/--verbose
7506 This lists both regular and local tags. When the -v/--verbose
7507 switch is used, a third column "local" is printed for local tags.
7507 switch is used, a third column "local" is printed for local tags.
7508 When the -q/--quiet switch is used, only the tag name is printed.
7508 When the -q/--quiet switch is used, only the tag name is printed.
7509
7509
7510 .. container:: verbose
7510 .. container:: verbose
7511
7511
7512 Template:
7512 Template:
7513
7513
7514 The following keywords are supported in addition to the common template
7514 The following keywords are supported in addition to the common template
7515 keywords and functions such as ``{tag}``. See also
7515 keywords and functions such as ``{tag}``. See also
7516 :hg:`help templates`.
7516 :hg:`help templates`.
7517
7517
7518 :type: String. ``local`` for local tags.
7518 :type: String. ``local`` for local tags.
7519
7519
7520 Returns 0 on success.
7520 Returns 0 on success.
7521 """
7521 """
7522
7522
7523 opts = pycompat.byteskwargs(opts)
7523 opts = pycompat.byteskwargs(opts)
7524 ui.pager(b'tags')
7524 ui.pager(b'tags')
7525 fm = ui.formatter(b'tags', opts)
7525 fm = ui.formatter(b'tags', opts)
7526 hexfunc = fm.hexfunc
7526 hexfunc = fm.hexfunc
7527
7527
7528 for t, n in reversed(repo.tagslist()):
7528 for t, n in reversed(repo.tagslist()):
7529 hn = hexfunc(n)
7529 hn = hexfunc(n)
7530 label = b'tags.normal'
7530 label = b'tags.normal'
7531 tagtype = repo.tagtype(t)
7531 tagtype = repo.tagtype(t)
7532 if not tagtype or tagtype == b'global':
7532 if not tagtype or tagtype == b'global':
7533 tagtype = b''
7533 tagtype = b''
7534 else:
7534 else:
7535 label = b'tags.' + tagtype
7535 label = b'tags.' + tagtype
7536
7536
7537 fm.startitem()
7537 fm.startitem()
7538 fm.context(repo=repo)
7538 fm.context(repo=repo)
7539 fm.write(b'tag', b'%s', t, label=label)
7539 fm.write(b'tag', b'%s', t, label=label)
7540 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7540 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7541 fm.condwrite(
7541 fm.condwrite(
7542 not ui.quiet,
7542 not ui.quiet,
7543 b'rev node',
7543 b'rev node',
7544 fmt,
7544 fmt,
7545 repo.changelog.rev(n),
7545 repo.changelog.rev(n),
7546 hn,
7546 hn,
7547 label=label,
7547 label=label,
7548 )
7548 )
7549 fm.condwrite(
7549 fm.condwrite(
7550 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7550 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7551 )
7551 )
7552 fm.plain(b'\n')
7552 fm.plain(b'\n')
7553 fm.end()
7553 fm.end()
7554
7554
7555
7555
7556 @command(
7556 @command(
7557 b'tip',
7557 b'tip',
7558 [
7558 [
7559 (b'p', b'patch', None, _(b'show patch')),
7559 (b'p', b'patch', None, _(b'show patch')),
7560 (b'g', b'git', None, _(b'use git extended diff format')),
7560 (b'g', b'git', None, _(b'use git extended diff format')),
7561 ]
7561 ]
7562 + templateopts,
7562 + templateopts,
7563 _(b'[-p] [-g]'),
7563 _(b'[-p] [-g]'),
7564 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7564 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7565 )
7565 )
7566 def tip(ui, repo, **opts):
7566 def tip(ui, repo, **opts):
7567 """show the tip revision (DEPRECATED)
7567 """show the tip revision (DEPRECATED)
7568
7568
7569 The tip revision (usually just called the tip) is the changeset
7569 The tip revision (usually just called the tip) is the changeset
7570 most recently added to the repository (and therefore the most
7570 most recently added to the repository (and therefore the most
7571 recently changed head).
7571 recently changed head).
7572
7572
7573 If you have just made a commit, that commit will be the tip. If
7573 If you have just made a commit, that commit will be the tip. If
7574 you have just pulled changes from another repository, the tip of
7574 you have just pulled changes from another repository, the tip of
7575 that repository becomes the current tip. The "tip" tag is special
7575 that repository becomes the current tip. The "tip" tag is special
7576 and cannot be renamed or assigned to a different changeset.
7576 and cannot be renamed or assigned to a different changeset.
7577
7577
7578 This command is deprecated, please use :hg:`heads` instead.
7578 This command is deprecated, please use :hg:`heads` instead.
7579
7579
7580 Returns 0 on success.
7580 Returns 0 on success.
7581 """
7581 """
7582 opts = pycompat.byteskwargs(opts)
7582 opts = pycompat.byteskwargs(opts)
7583 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7583 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7584 displayer.show(repo[b'tip'])
7584 displayer.show(repo[b'tip'])
7585 displayer.close()
7585 displayer.close()
7586
7586
7587
7587
7588 @command(
7588 @command(
7589 b'unbundle',
7589 b'unbundle',
7590 [
7590 [
7591 (
7591 (
7592 b'u',
7592 b'u',
7593 b'update',
7593 b'update',
7594 None,
7594 None,
7595 _(b'update to new branch head if changesets were unbundled'),
7595 _(b'update to new branch head if changesets were unbundled'),
7596 )
7596 )
7597 ],
7597 ],
7598 _(b'[-u] FILE...'),
7598 _(b'[-u] FILE...'),
7599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7599 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7600 )
7600 )
7601 def unbundle(ui, repo, fname1, *fnames, **opts):
7601 def unbundle(ui, repo, fname1, *fnames, **opts):
7602 """apply one or more bundle files
7602 """apply one or more bundle files
7603
7603
7604 Apply one or more bundle files generated by :hg:`bundle`.
7604 Apply one or more bundle files generated by :hg:`bundle`.
7605
7605
7606 Returns 0 on success, 1 if an update has unresolved files.
7606 Returns 0 on success, 1 if an update has unresolved files.
7607 """
7607 """
7608 fnames = (fname1,) + fnames
7608 fnames = (fname1,) + fnames
7609
7609
7610 with repo.lock():
7610 with repo.lock():
7611 for fname in fnames:
7611 for fname in fnames:
7612 f = hg.openpath(ui, fname)
7612 f = hg.openpath(ui, fname)
7613 gen = exchange.readbundle(ui, f, fname)
7613 gen = exchange.readbundle(ui, f, fname)
7614 if isinstance(gen, streamclone.streamcloneapplier):
7614 if isinstance(gen, streamclone.streamcloneapplier):
7615 raise error.InputError(
7615 raise error.InputError(
7616 _(
7616 _(
7617 b'packed bundles cannot be applied with '
7617 b'packed bundles cannot be applied with '
7618 b'"hg unbundle"'
7618 b'"hg unbundle"'
7619 ),
7619 ),
7620 hint=_(b'use "hg debugapplystreamclonebundle"'),
7620 hint=_(b'use "hg debugapplystreamclonebundle"'),
7621 )
7621 )
7622 url = b'bundle:' + fname
7622 url = b'bundle:' + fname
7623 try:
7623 try:
7624 txnname = b'unbundle'
7624 txnname = b'unbundle'
7625 if not isinstance(gen, bundle2.unbundle20):
7625 if not isinstance(gen, bundle2.unbundle20):
7626 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7626 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7627 with repo.transaction(txnname) as tr:
7627 with repo.transaction(txnname) as tr:
7628 op = bundle2.applybundle(
7628 op = bundle2.applybundle(
7629 repo, gen, tr, source=b'unbundle', url=url
7629 repo, gen, tr, source=b'unbundle', url=url
7630 )
7630 )
7631 except error.BundleUnknownFeatureError as exc:
7631 except error.BundleUnknownFeatureError as exc:
7632 raise error.Abort(
7632 raise error.Abort(
7633 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7633 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7634 hint=_(
7634 hint=_(
7635 b"see https://mercurial-scm.org/"
7635 b"see https://mercurial-scm.org/"
7636 b"wiki/BundleFeature for more "
7636 b"wiki/BundleFeature for more "
7637 b"information"
7637 b"information"
7638 ),
7638 ),
7639 )
7639 )
7640 modheads = bundle2.combinechangegroupresults(op)
7640 modheads = bundle2.combinechangegroupresults(op)
7641
7641
7642 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7642 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7643 return 1
7643 return 1
7644 else:
7644 else:
7645 return 0
7645 return 0
7646
7646
7647
7647
7648 @command(
7648 @command(
7649 b'unshelve',
7649 b'unshelve',
7650 [
7650 [
7651 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7651 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7652 (
7652 (
7653 b'c',
7653 b'c',
7654 b'continue',
7654 b'continue',
7655 None,
7655 None,
7656 _(b'continue an incomplete unshelve operation'),
7656 _(b'continue an incomplete unshelve operation'),
7657 ),
7657 ),
7658 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7658 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7659 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7659 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7660 (
7660 (
7661 b'n',
7661 b'n',
7662 b'name',
7662 b'name',
7663 b'',
7663 b'',
7664 _(b'restore shelved change with given name'),
7664 _(b'restore shelved change with given name'),
7665 _(b'NAME'),
7665 _(b'NAME'),
7666 ),
7666 ),
7667 (b't', b'tool', b'', _(b'specify merge tool')),
7667 (b't', b'tool', b'', _(b'specify merge tool')),
7668 (
7668 (
7669 b'',
7669 b'',
7670 b'date',
7670 b'date',
7671 b'',
7671 b'',
7672 _(b'set date for temporary commits (DEPRECATED)'),
7672 _(b'set date for temporary commits (DEPRECATED)'),
7673 _(b'DATE'),
7673 _(b'DATE'),
7674 ),
7674 ),
7675 ],
7675 ],
7676 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7676 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7677 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7678 )
7678 )
7679 def unshelve(ui, repo, *shelved, **opts):
7679 def unshelve(ui, repo, *shelved, **opts):
7680 """restore a shelved change to the working directory
7680 """restore a shelved change to the working directory
7681
7681
7682 This command accepts an optional name of a shelved change to
7682 This command accepts an optional name of a shelved change to
7683 restore. If none is given, the most recent shelved change is used.
7683 restore. If none is given, the most recent shelved change is used.
7684
7684
7685 If a shelved change is applied successfully, the bundle that
7685 If a shelved change is applied successfully, the bundle that
7686 contains the shelved changes is moved to a backup location
7686 contains the shelved changes is moved to a backup location
7687 (.hg/shelve-backup).
7687 (.hg/shelve-backup).
7688
7688
7689 Since you can restore a shelved change on top of an arbitrary
7689 Since you can restore a shelved change on top of an arbitrary
7690 commit, it is possible that unshelving will result in a conflict
7690 commit, it is possible that unshelving will result in a conflict
7691 between your changes and the commits you are unshelving onto. If
7691 between your changes and the commits you are unshelving onto. If
7692 this occurs, you must resolve the conflict, then use
7692 this occurs, you must resolve the conflict, then use
7693 ``--continue`` to complete the unshelve operation. (The bundle
7693 ``--continue`` to complete the unshelve operation. (The bundle
7694 will not be moved until you successfully complete the unshelve.)
7694 will not be moved until you successfully complete the unshelve.)
7695
7695
7696 (Alternatively, you can use ``--abort`` to abandon an unshelve
7696 (Alternatively, you can use ``--abort`` to abandon an unshelve
7697 that causes a conflict. This reverts the unshelved changes, and
7697 that causes a conflict. This reverts the unshelved changes, and
7698 leaves the bundle in place.)
7698 leaves the bundle in place.)
7699
7699
7700 If bare shelved change (without interactive, include and exclude
7700 If bare shelved change (without interactive, include and exclude
7701 option) was done on newly created branch it would restore branch
7701 option) was done on newly created branch it would restore branch
7702 information to the working directory.
7702 information to the working directory.
7703
7703
7704 After a successful unshelve, the shelved changes are stored in a
7704 After a successful unshelve, the shelved changes are stored in a
7705 backup directory. Only the N most recent backups are kept. N
7705 backup directory. Only the N most recent backups are kept. N
7706 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7706 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7707 configuration option.
7707 configuration option.
7708
7708
7709 .. container:: verbose
7709 .. container:: verbose
7710
7710
7711 Timestamp in seconds is used to decide order of backups. More
7711 Timestamp in seconds is used to decide order of backups. More
7712 than ``maxbackups`` backups are kept, if same timestamp
7712 than ``maxbackups`` backups are kept, if same timestamp
7713 prevents from deciding exact order of them, for safety.
7713 prevents from deciding exact order of them, for safety.
7714
7714
7715 Selected changes can be unshelved with ``--interactive`` flag.
7715 Selected changes can be unshelved with ``--interactive`` flag.
7716 The working directory is updated with the selected changes, and
7716 The working directory is updated with the selected changes, and
7717 only the unselected changes remain shelved.
7717 only the unselected changes remain shelved.
7718 Note: The whole shelve is applied to working directory first before
7718 Note: The whole shelve is applied to working directory first before
7719 running interactively. So, this will bring up all the conflicts between
7719 running interactively. So, this will bring up all the conflicts between
7720 working directory and the shelve, irrespective of which changes will be
7720 working directory and the shelve, irrespective of which changes will be
7721 unshelved.
7721 unshelved.
7722 """
7722 """
7723 with repo.wlock():
7723 with repo.wlock():
7724 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7724 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7725
7725
7726
7726
7727 statemod.addunfinished(
7727 statemod.addunfinished(
7728 b'unshelve',
7728 b'unshelve',
7729 fname=b'shelvedstate',
7729 fname=b'shelvedstate',
7730 continueflag=True,
7730 continueflag=True,
7731 abortfunc=shelvemod.hgabortunshelve,
7731 abortfunc=shelvemod.hgabortunshelve,
7732 continuefunc=shelvemod.hgcontinueunshelve,
7732 continuefunc=shelvemod.hgcontinueunshelve,
7733 cmdmsg=_(b'unshelve already in progress'),
7733 cmdmsg=_(b'unshelve already in progress'),
7734 )
7734 )
7735
7735
7736
7736
7737 @command(
7737 @command(
7738 b'update|up|checkout|co',
7738 b'update|up|checkout|co',
7739 [
7739 [
7740 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7740 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7741 (b'c', b'check', None, _(b'require clean working directory')),
7741 (b'c', b'check', None, _(b'require clean working directory')),
7742 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7742 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7743 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7743 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7744 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7744 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7745 ]
7745 ]
7746 + mergetoolopts,
7746 + mergetoolopts,
7747 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7747 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7748 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7748 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7749 helpbasic=True,
7749 helpbasic=True,
7750 )
7750 )
7751 def update(ui, repo, node=None, **opts):
7751 def update(ui, repo, node=None, **opts):
7752 """update working directory (or switch revisions)
7752 """update working directory (or switch revisions)
7753
7753
7754 Update the repository's working directory to the specified
7754 Update the repository's working directory to the specified
7755 changeset. If no changeset is specified, update to the tip of the
7755 changeset. If no changeset is specified, update to the tip of the
7756 current named branch and move the active bookmark (see :hg:`help
7756 current named branch and move the active bookmark (see :hg:`help
7757 bookmarks`).
7757 bookmarks`).
7758
7758
7759 Update sets the working directory's parent revision to the specified
7759 Update sets the working directory's parent revision to the specified
7760 changeset (see :hg:`help parents`).
7760 changeset (see :hg:`help parents`).
7761
7761
7762 If the changeset is not a descendant or ancestor of the working
7762 If the changeset is not a descendant or ancestor of the working
7763 directory's parent and there are uncommitted changes, the update is
7763 directory's parent and there are uncommitted changes, the update is
7764 aborted. With the -c/--check option, the working directory is checked
7764 aborted. With the -c/--check option, the working directory is checked
7765 for uncommitted changes; if none are found, the working directory is
7765 for uncommitted changes; if none are found, the working directory is
7766 updated to the specified changeset.
7766 updated to the specified changeset.
7767
7767
7768 .. container:: verbose
7768 .. container:: verbose
7769
7769
7770 The -C/--clean, -c/--check, and -m/--merge options control what
7770 The -C/--clean, -c/--check, and -m/--merge options control what
7771 happens if the working directory contains uncommitted changes.
7771 happens if the working directory contains uncommitted changes.
7772 At most of one of them can be specified.
7772 At most of one of them can be specified.
7773
7773
7774 1. If no option is specified, and if
7774 1. If no option is specified, and if
7775 the requested changeset is an ancestor or descendant of
7775 the requested changeset is an ancestor or descendant of
7776 the working directory's parent, the uncommitted changes
7776 the working directory's parent, the uncommitted changes
7777 are merged into the requested changeset and the merged
7777 are merged into the requested changeset and the merged
7778 result is left uncommitted. If the requested changeset is
7778 result is left uncommitted. If the requested changeset is
7779 not an ancestor or descendant (that is, it is on another
7779 not an ancestor or descendant (that is, it is on another
7780 branch), the update is aborted and the uncommitted changes
7780 branch), the update is aborted and the uncommitted changes
7781 are preserved.
7781 are preserved.
7782
7782
7783 2. With the -m/--merge option, the update is allowed even if the
7783 2. With the -m/--merge option, the update is allowed even if the
7784 requested changeset is not an ancestor or descendant of
7784 requested changeset is not an ancestor or descendant of
7785 the working directory's parent.
7785 the working directory's parent.
7786
7786
7787 3. With the -c/--check option, the update is aborted and the
7787 3. With the -c/--check option, the update is aborted and the
7788 uncommitted changes are preserved.
7788 uncommitted changes are preserved.
7789
7789
7790 4. With the -C/--clean option, uncommitted changes are discarded and
7790 4. With the -C/--clean option, uncommitted changes are discarded and
7791 the working directory is updated to the requested changeset.
7791 the working directory is updated to the requested changeset.
7792
7792
7793 To cancel an uncommitted merge (and lose your changes), use
7793 To cancel an uncommitted merge (and lose your changes), use
7794 :hg:`merge --abort`.
7794 :hg:`merge --abort`.
7795
7795
7796 Use null as the changeset to remove the working directory (like
7796 Use null as the changeset to remove the working directory (like
7797 :hg:`clone -U`).
7797 :hg:`clone -U`).
7798
7798
7799 If you want to revert just one file to an older revision, use
7799 If you want to revert just one file to an older revision, use
7800 :hg:`revert [-r REV] NAME`.
7800 :hg:`revert [-r REV] NAME`.
7801
7801
7802 See :hg:`help dates` for a list of formats valid for -d/--date.
7802 See :hg:`help dates` for a list of formats valid for -d/--date.
7803
7803
7804 Returns 0 on success, 1 if there are unresolved files.
7804 Returns 0 on success, 1 if there are unresolved files.
7805 """
7805 """
7806 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7806 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7807 rev = opts.get('rev')
7807 rev = opts.get('rev')
7808 date = opts.get('date')
7808 date = opts.get('date')
7809 clean = opts.get('clean')
7809 clean = opts.get('clean')
7810 check = opts.get('check')
7810 check = opts.get('check')
7811 merge = opts.get('merge')
7811 merge = opts.get('merge')
7812 if rev and node:
7812 if rev and node:
7813 raise error.InputError(_(b"please specify just one revision"))
7813 raise error.InputError(_(b"please specify just one revision"))
7814
7814
7815 if ui.configbool(b'commands', b'update.requiredest'):
7815 if ui.configbool(b'commands', b'update.requiredest'):
7816 if not node and not rev and not date:
7816 if not node and not rev and not date:
7817 raise error.InputError(
7817 raise error.InputError(
7818 _(b'you must specify a destination'),
7818 _(b'you must specify a destination'),
7819 hint=_(b'for example: hg update ".::"'),
7819 hint=_(b'for example: hg update ".::"'),
7820 )
7820 )
7821
7821
7822 if rev is None or rev == b'':
7822 if rev is None or rev == b'':
7823 rev = node
7823 rev = node
7824
7824
7825 if date and rev is not None:
7825 if date and rev is not None:
7826 raise error.InputError(_(b"you can't specify a revision and a date"))
7826 raise error.InputError(_(b"you can't specify a revision and a date"))
7827
7827
7828 updatecheck = None
7828 updatecheck = None
7829 if check:
7829 if check:
7830 updatecheck = b'abort'
7830 updatecheck = b'abort'
7831 elif merge:
7831 elif merge:
7832 updatecheck = b'none'
7832 updatecheck = b'none'
7833
7833
7834 with repo.wlock():
7834 with repo.wlock():
7835 cmdutil.clearunfinished(repo)
7835 cmdutil.clearunfinished(repo)
7836 if date:
7836 if date:
7837 rev = cmdutil.finddate(ui, repo, date)
7837 rev = cmdutil.finddate(ui, repo, date)
7838
7838
7839 # if we defined a bookmark, we have to remember the original name
7839 # if we defined a bookmark, we have to remember the original name
7840 brev = rev
7840 brev = rev
7841 if rev:
7841 if rev:
7842 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7842 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7843 ctx = scmutil.revsingle(repo, rev, default=None)
7843 ctx = scmutil.revsingle(repo, rev, default=None)
7844 rev = ctx.rev()
7844 rev = ctx.rev()
7845 hidden = ctx.hidden()
7845 hidden = ctx.hidden()
7846 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7846 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7847 with ui.configoverride(overrides, b'update'):
7847 with ui.configoverride(overrides, b'update'):
7848 ret = hg.updatetotally(
7848 ret = hg.updatetotally(
7849 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7849 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7850 )
7850 )
7851 if hidden:
7851 if hidden:
7852 ctxstr = ctx.hex()[:12]
7852 ctxstr = ctx.hex()[:12]
7853 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7853 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7854
7854
7855 if ctx.obsolete():
7855 if ctx.obsolete():
7856 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7856 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7857 ui.warn(b"(%s)\n" % obsfatemsg)
7857 ui.warn(b"(%s)\n" % obsfatemsg)
7858 return ret
7858 return ret
7859
7859
7860
7860
7861 @command(
7861 @command(
7862 b'verify',
7862 b'verify',
7863 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7863 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7864 helpcategory=command.CATEGORY_MAINTENANCE,
7864 helpcategory=command.CATEGORY_MAINTENANCE,
7865 )
7865 )
7866 def verify(ui, repo, **opts):
7866 def verify(ui, repo, **opts):
7867 """verify the integrity of the repository
7867 """verify the integrity of the repository
7868
7868
7869 Verify the integrity of the current repository.
7869 Verify the integrity of the current repository.
7870
7870
7871 This will perform an extensive check of the repository's
7871 This will perform an extensive check of the repository's
7872 integrity, validating the hashes and checksums of each entry in
7872 integrity, validating the hashes and checksums of each entry in
7873 the changelog, manifest, and tracked files, as well as the
7873 the changelog, manifest, and tracked files, as well as the
7874 integrity of their crosslinks and indices.
7874 integrity of their crosslinks and indices.
7875
7875
7876 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7876 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7877 for more information about recovery from corruption of the
7877 for more information about recovery from corruption of the
7878 repository.
7878 repository.
7879
7879
7880 Returns 0 on success, 1 if errors are encountered.
7880 Returns 0 on success, 1 if errors are encountered.
7881 """
7881 """
7882 opts = pycompat.byteskwargs(opts)
7882 opts = pycompat.byteskwargs(opts)
7883
7883
7884 level = None
7884 level = None
7885 if opts[b'full']:
7885 if opts[b'full']:
7886 level = verifymod.VERIFY_FULL
7886 level = verifymod.VERIFY_FULL
7887 return hg.verify(repo, level)
7887 return hg.verify(repo, level)
7888
7888
7889
7889
7890 @command(
7890 @command(
7891 b'version',
7891 b'version',
7892 [] + formatteropts,
7892 [] + formatteropts,
7893 helpcategory=command.CATEGORY_HELP,
7893 helpcategory=command.CATEGORY_HELP,
7894 norepo=True,
7894 norepo=True,
7895 intents={INTENT_READONLY},
7895 intents={INTENT_READONLY},
7896 )
7896 )
7897 def version_(ui, **opts):
7897 def version_(ui, **opts):
7898 """output version and copyright information
7898 """output version and copyright information
7899
7899
7900 .. container:: verbose
7900 .. container:: verbose
7901
7901
7902 Template:
7902 Template:
7903
7903
7904 The following keywords are supported. See also :hg:`help templates`.
7904 The following keywords are supported. See also :hg:`help templates`.
7905
7905
7906 :extensions: List of extensions.
7906 :extensions: List of extensions.
7907 :ver: String. Version number.
7907 :ver: String. Version number.
7908
7908
7909 And each entry of ``{extensions}`` provides the following sub-keywords
7909 And each entry of ``{extensions}`` provides the following sub-keywords
7910 in addition to ``{ver}``.
7910 in addition to ``{ver}``.
7911
7911
7912 :bundled: Boolean. True if included in the release.
7912 :bundled: Boolean. True if included in the release.
7913 :name: String. Extension name.
7913 :name: String. Extension name.
7914 """
7914 """
7915 opts = pycompat.byteskwargs(opts)
7915 opts = pycompat.byteskwargs(opts)
7916 if ui.verbose:
7916 if ui.verbose:
7917 ui.pager(b'version')
7917 ui.pager(b'version')
7918 fm = ui.formatter(b"version", opts)
7918 fm = ui.formatter(b"version", opts)
7919 fm.startitem()
7919 fm.startitem()
7920 fm.write(
7920 fm.write(
7921 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7921 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7922 )
7922 )
7923 license = _(
7923 license = _(
7924 b"(see https://mercurial-scm.org for more information)\n"
7924 b"(see https://mercurial-scm.org for more information)\n"
7925 b"\nCopyright (C) 2005-2021 Olivia Mackall and others\n"
7925 b"\nCopyright (C) 2005-2021 Olivia Mackall and others\n"
7926 b"This is free software; see the source for copying conditions. "
7926 b"This is free software; see the source for copying conditions. "
7927 b"There is NO\nwarranty; "
7927 b"There is NO\nwarranty; "
7928 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7928 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7929 )
7929 )
7930 if not ui.quiet:
7930 if not ui.quiet:
7931 fm.plain(license)
7931 fm.plain(license)
7932
7932
7933 if ui.verbose:
7933 if ui.verbose:
7934 fm.plain(_(b"\nEnabled extensions:\n\n"))
7934 fm.plain(_(b"\nEnabled extensions:\n\n"))
7935 # format names and versions into columns
7935 # format names and versions into columns
7936 names = []
7936 names = []
7937 vers = []
7937 vers = []
7938 isinternals = []
7938 isinternals = []
7939 for name, module in sorted(extensions.extensions()):
7939 for name, module in sorted(extensions.extensions()):
7940 names.append(name)
7940 names.append(name)
7941 vers.append(extensions.moduleversion(module) or None)
7941 vers.append(extensions.moduleversion(module) or None)
7942 isinternals.append(extensions.ismoduleinternal(module))
7942 isinternals.append(extensions.ismoduleinternal(module))
7943 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7943 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7944 if names:
7944 if names:
7945 namefmt = b" %%-%ds " % max(len(n) for n in names)
7945 namefmt = b" %%-%ds " % max(len(n) for n in names)
7946 places = [_(b"external"), _(b"internal")]
7946 places = [_(b"external"), _(b"internal")]
7947 for n, v, p in zip(names, vers, isinternals):
7947 for n, v, p in zip(names, vers, isinternals):
7948 fn.startitem()
7948 fn.startitem()
7949 fn.condwrite(ui.verbose, b"name", namefmt, n)
7949 fn.condwrite(ui.verbose, b"name", namefmt, n)
7950 if ui.verbose:
7950 if ui.verbose:
7951 fn.plain(b"%s " % places[p])
7951 fn.plain(b"%s " % places[p])
7952 fn.data(bundled=p)
7952 fn.data(bundled=p)
7953 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7953 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7954 if ui.verbose:
7954 if ui.verbose:
7955 fn.plain(b"\n")
7955 fn.plain(b"\n")
7956 fn.end()
7956 fn.end()
7957 fm.end()
7957 fm.end()
7958
7958
7959
7959
7960 def loadcmdtable(ui, name, cmdtable):
7960 def loadcmdtable(ui, name, cmdtable):
7961 """Load command functions from specified cmdtable"""
7961 """Load command functions from specified cmdtable"""
7962 overrides = [cmd for cmd in cmdtable if cmd in table]
7962 overrides = [cmd for cmd in cmdtable if cmd in table]
7963 if overrides:
7963 if overrides:
7964 ui.warn(
7964 ui.warn(
7965 _(b"extension '%s' overrides commands: %s\n")
7965 _(b"extension '%s' overrides commands: %s\n")
7966 % (name, b" ".join(overrides))
7966 % (name, b" ".join(overrides))
7967 )
7967 )
7968 table.update(cmdtable)
7968 table.update(cmdtable)
@@ -1,2289 +1,2289 b''
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Olivia Mackall <olivia@selenic.com>
3 # Copyright Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import glob
11 import glob
12 import os
12 import os
13 import posixpath
13 import posixpath
14 import re
14 import re
15 import subprocess
15 import subprocess
16 import weakref
16 import weakref
17
17
18 from .i18n import _
18 from .i18n import _
19 from .node import (
19 from .node import (
20 bin,
20 bin,
21 hex,
21 hex,
22 nullrev,
22 nullrev,
23 short,
23 short,
24 wdirrev,
24 wdirrev,
25 )
25 )
26 from .pycompat import getattr
26 from .pycompat import getattr
27 from .thirdparty import attr
27 from .thirdparty import attr
28 from . import (
28 from . import (
29 copies as copiesmod,
29 copies as copiesmod,
30 encoding,
30 encoding,
31 error,
31 error,
32 match as matchmod,
32 match as matchmod,
33 obsolete,
33 obsolete,
34 obsutil,
34 obsutil,
35 pathutil,
35 pathutil,
36 phases,
36 phases,
37 policy,
37 policy,
38 pycompat,
38 pycompat,
39 requirements as requirementsmod,
39 requirements as requirementsmod,
40 revsetlang,
40 revsetlang,
41 similar,
41 similar,
42 smartset,
42 smartset,
43 url,
43 url,
44 util,
44 util,
45 vfs,
45 vfs,
46 )
46 )
47
47
48 from .utils import (
48 from .utils import (
49 hashutil,
49 hashutil,
50 procutil,
50 procutil,
51 stringutil,
51 stringutil,
52 )
52 )
53
53
54 if pycompat.iswindows:
54 if pycompat.iswindows:
55 from . import scmwindows as scmplatform
55 from . import scmwindows as scmplatform
56 else:
56 else:
57 from . import scmposix as scmplatform
57 from . import scmposix as scmplatform
58
58
59 parsers = policy.importmod('parsers')
59 parsers = policy.importmod('parsers')
60 rustrevlog = policy.importrust('revlog')
60 rustrevlog = policy.importrust('revlog')
61
61
62 termsize = scmplatform.termsize
62 termsize = scmplatform.termsize
63
63
64
64
65 @attr.s(slots=True, repr=False)
65 @attr.s(slots=True, repr=False)
66 class status(object):
66 class status(object):
67 """Struct with a list of files per status.
67 """Struct with a list of files per status.
68
68
69 The 'deleted', 'unknown' and 'ignored' properties are only
69 The 'deleted', 'unknown' and 'ignored' properties are only
70 relevant to the working copy.
70 relevant to the working copy.
71 """
71 """
72
72
73 modified = attr.ib(default=attr.Factory(list))
73 modified = attr.ib(default=attr.Factory(list))
74 added = attr.ib(default=attr.Factory(list))
74 added = attr.ib(default=attr.Factory(list))
75 removed = attr.ib(default=attr.Factory(list))
75 removed = attr.ib(default=attr.Factory(list))
76 deleted = attr.ib(default=attr.Factory(list))
76 deleted = attr.ib(default=attr.Factory(list))
77 unknown = attr.ib(default=attr.Factory(list))
77 unknown = attr.ib(default=attr.Factory(list))
78 ignored = attr.ib(default=attr.Factory(list))
78 ignored = attr.ib(default=attr.Factory(list))
79 clean = attr.ib(default=attr.Factory(list))
79 clean = attr.ib(default=attr.Factory(list))
80
80
81 def __iter__(self):
81 def __iter__(self):
82 yield self.modified
82 yield self.modified
83 yield self.added
83 yield self.added
84 yield self.removed
84 yield self.removed
85 yield self.deleted
85 yield self.deleted
86 yield self.unknown
86 yield self.unknown
87 yield self.ignored
87 yield self.ignored
88 yield self.clean
88 yield self.clean
89
89
90 def __repr__(self):
90 def __repr__(self):
91 return (
91 return (
92 r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
92 r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
93 r'unknown=%s, ignored=%s, clean=%s>'
93 r'unknown=%s, ignored=%s, clean=%s>'
94 ) % tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)
94 ) % tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)
95
95
96
96
97 def itersubrepos(ctx1, ctx2):
97 def itersubrepos(ctx1, ctx2):
98 """find subrepos in ctx1 or ctx2"""
98 """find subrepos in ctx1 or ctx2"""
99 # Create a (subpath, ctx) mapping where we prefer subpaths from
99 # Create a (subpath, ctx) mapping where we prefer subpaths from
100 # ctx1. The subpaths from ctx2 are important when the .hgsub file
100 # ctx1. The subpaths from ctx2 are important when the .hgsub file
101 # has been modified (in ctx2) but not yet committed (in ctx1).
101 # has been modified (in ctx2) but not yet committed (in ctx1).
102 subpaths = dict.fromkeys(ctx2.substate, ctx2)
102 subpaths = dict.fromkeys(ctx2.substate, ctx2)
103 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
103 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
104
104
105 missing = set()
105 missing = set()
106
106
107 for subpath in ctx2.substate:
107 for subpath in ctx2.substate:
108 if subpath not in ctx1.substate:
108 if subpath not in ctx1.substate:
109 del subpaths[subpath]
109 del subpaths[subpath]
110 missing.add(subpath)
110 missing.add(subpath)
111
111
112 for subpath, ctx in sorted(pycompat.iteritems(subpaths)):
112 for subpath, ctx in sorted(pycompat.iteritems(subpaths)):
113 yield subpath, ctx.sub(subpath)
113 yield subpath, ctx.sub(subpath)
114
114
115 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
115 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
116 # status and diff will have an accurate result when it does
116 # status and diff will have an accurate result when it does
117 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
117 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
118 # against itself.
118 # against itself.
119 for subpath in missing:
119 for subpath in missing:
120 yield subpath, ctx2.nullsub(subpath, ctx1)
120 yield subpath, ctx2.nullsub(subpath, ctx1)
121
121
122
122
123 def nochangesfound(ui, repo, excluded=None):
123 def nochangesfound(ui, repo, excluded=None):
124 """Report no changes for push/pull, excluded is None or a list of
124 """Report no changes for push/pull, excluded is None or a list of
125 nodes excluded from the push/pull.
125 nodes excluded from the push/pull.
126 """
126 """
127 secretlist = []
127 secretlist = []
128 if excluded:
128 if excluded:
129 for n in excluded:
129 for n in excluded:
130 ctx = repo[n]
130 ctx = repo[n]
131 if ctx.phase() >= phases.secret and not ctx.extinct():
131 if ctx.phase() >= phases.secret and not ctx.extinct():
132 secretlist.append(n)
132 secretlist.append(n)
133
133
134 if secretlist:
134 if secretlist:
135 ui.status(
135 ui.status(
136 _(b"no changes found (ignored %d secret changesets)\n")
136 _(b"no changes found (ignored %d secret changesets)\n")
137 % len(secretlist)
137 % len(secretlist)
138 )
138 )
139 else:
139 else:
140 ui.status(_(b"no changes found\n"))
140 ui.status(_(b"no changes found\n"))
141
141
142
142
143 def callcatch(ui, func):
143 def callcatch(ui, func):
144 """call func() with global exception handling
144 """call func() with global exception handling
145
145
146 return func() if no exception happens. otherwise do some error handling
146 return func() if no exception happens. otherwise do some error handling
147 and return an exit code accordingly. does not handle all exceptions.
147 and return an exit code accordingly. does not handle all exceptions.
148 """
148 """
149 coarse_exit_code = -1
149 coarse_exit_code = -1
150 detailed_exit_code = -1
150 detailed_exit_code = -1
151 try:
151 try:
152 try:
152 try:
153 return func()
153 return func()
154 except: # re-raises
154 except: # re-raises
155 ui.traceback()
155 ui.traceback()
156 raise
156 raise
157 # Global exception handling, alphabetically
157 # Global exception handling, alphabetically
158 # Mercurial-specific first, followed by built-in and library exceptions
158 # Mercurial-specific first, followed by built-in and library exceptions
159 except error.LockHeld as inst:
159 except error.LockHeld as inst:
160 detailed_exit_code = 20
160 detailed_exit_code = 20
161 if inst.errno == errno.ETIMEDOUT:
161 if inst.errno == errno.ETIMEDOUT:
162 reason = _(b'timed out waiting for lock held by %r') % (
162 reason = _(b'timed out waiting for lock held by %r') % (
163 pycompat.bytestr(inst.locker)
163 pycompat.bytestr(inst.locker)
164 )
164 )
165 else:
165 else:
166 reason = _(b'lock held by %r') % inst.locker
166 reason = _(b'lock held by %r') % inst.locker
167 ui.error(
167 ui.error(
168 _(b"abort: %s: %s\n")
168 _(b"abort: %s: %s\n")
169 % (inst.desc or stringutil.forcebytestr(inst.filename), reason)
169 % (inst.desc or stringutil.forcebytestr(inst.filename), reason)
170 )
170 )
171 if not inst.locker:
171 if not inst.locker:
172 ui.error(_(b"(lock might be very busy)\n"))
172 ui.error(_(b"(lock might be very busy)\n"))
173 except error.LockUnavailable as inst:
173 except error.LockUnavailable as inst:
174 detailed_exit_code = 20
174 detailed_exit_code = 20
175 ui.error(
175 ui.error(
176 _(b"abort: could not lock %s: %s\n")
176 _(b"abort: could not lock %s: %s\n")
177 % (
177 % (
178 inst.desc or stringutil.forcebytestr(inst.filename),
178 inst.desc or stringutil.forcebytestr(inst.filename),
179 encoding.strtolocal(inst.strerror),
179 encoding.strtolocal(inst.strerror),
180 )
180 )
181 )
181 )
182 except error.RepoError as inst:
182 except error.RepoError as inst:
183 ui.error(_(b"abort: %s\n") % inst)
183 ui.error(_(b"abort: %s\n") % inst)
184 if inst.hint:
184 if inst.hint:
185 ui.error(_(b"(%s)\n") % inst.hint)
185 ui.error(_(b"(%s)\n") % inst.hint)
186 except error.ResponseError as inst:
186 except error.ResponseError as inst:
187 ui.error(_(b"abort: %s") % inst.args[0])
187 ui.error(_(b"abort: %s") % inst.args[0])
188 msg = inst.args[1]
188 msg = inst.args[1]
189 if isinstance(msg, type(u'')):
189 if isinstance(msg, type(u'')):
190 msg = pycompat.sysbytes(msg)
190 msg = pycompat.sysbytes(msg)
191 if msg is None:
191 if msg is None:
192 ui.error(b"\n")
192 ui.error(b"\n")
193 elif not isinstance(msg, bytes):
193 elif not isinstance(msg, bytes):
194 ui.error(b" %r\n" % (msg,))
194 ui.error(b" %r\n" % (msg,))
195 elif not msg:
195 elif not msg:
196 ui.error(_(b" empty string\n"))
196 ui.error(_(b" empty string\n"))
197 else:
197 else:
198 ui.error(b"\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
198 ui.error(b"\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
199 except error.CensoredNodeError as inst:
199 except error.CensoredNodeError as inst:
200 ui.error(_(b"abort: file censored %s\n") % inst)
200 ui.error(_(b"abort: file censored %s\n") % inst)
201 except error.WdirUnsupported:
201 except error.WdirUnsupported:
202 ui.error(_(b"abort: working directory revision cannot be specified\n"))
202 ui.error(_(b"abort: working directory revision cannot be specified\n"))
203 except error.Error as inst:
203 except error.Error as inst:
204 if inst.detailed_exit_code is not None:
204 if inst.detailed_exit_code is not None:
205 detailed_exit_code = inst.detailed_exit_code
205 detailed_exit_code = inst.detailed_exit_code
206 if inst.coarse_exit_code is not None:
206 if inst.coarse_exit_code is not None:
207 coarse_exit_code = inst.coarse_exit_code
207 coarse_exit_code = inst.coarse_exit_code
208 ui.error(inst.format())
208 ui.error(inst.format())
209 except error.WorkerError as inst:
209 except error.WorkerError as inst:
210 # Don't print a message -- the worker already should have
210 # Don't print a message -- the worker already should have
211 return inst.status_code
211 return inst.status_code
212 except ImportError as inst:
212 except ImportError as inst:
213 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
213 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
214 m = stringutil.forcebytestr(inst).split()[-1]
214 m = stringutil.forcebytestr(inst).split()[-1]
215 if m in b"mpatch bdiff".split():
215 if m in b"mpatch bdiff".split():
216 ui.error(_(b"(did you forget to compile extensions?)\n"))
216 ui.error(_(b"(did you forget to compile extensions?)\n"))
217 elif m in b"zlib".split():
217 elif m in b"zlib".split():
218 ui.error(_(b"(is your Python install correct?)\n"))
218 ui.error(_(b"(is your Python install correct?)\n"))
219 except util.urlerr.httperror as inst:
219 except util.urlerr.httperror as inst:
220 detailed_exit_code = 100
220 detailed_exit_code = 100
221 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
221 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
222 except util.urlerr.urlerror as inst:
222 except util.urlerr.urlerror as inst:
223 detailed_exit_code = 100
223 detailed_exit_code = 100
224 try: # usually it is in the form (errno, strerror)
224 try: # usually it is in the form (errno, strerror)
225 reason = inst.reason.args[1]
225 reason = inst.reason.args[1]
226 except (AttributeError, IndexError):
226 except (AttributeError, IndexError):
227 # it might be anything, for example a string
227 # it might be anything, for example a string
228 reason = inst.reason
228 reason = inst.reason
229 if isinstance(reason, pycompat.unicode):
229 if isinstance(reason, pycompat.unicode):
230 # SSLError of Python 2.7.9 contains a unicode
230 # SSLError of Python 2.7.9 contains a unicode
231 reason = encoding.unitolocal(reason)
231 reason = encoding.unitolocal(reason)
232 ui.error(_(b"abort: error: %s\n") % stringutil.forcebytestr(reason))
232 ui.error(_(b"abort: error: %s\n") % stringutil.forcebytestr(reason))
233 except (IOError, OSError) as inst:
233 except (IOError, OSError) as inst:
234 if (
234 if (
235 util.safehasattr(inst, b"args")
235 util.safehasattr(inst, b"args")
236 and inst.args
236 and inst.args
237 and inst.args[0] == errno.EPIPE
237 and inst.args[0] == errno.EPIPE
238 ):
238 ):
239 pass
239 pass
240 elif getattr(inst, "strerror", None): # common IOError or OSError
240 elif getattr(inst, "strerror", None): # common IOError or OSError
241 if getattr(inst, "filename", None) is not None:
241 if getattr(inst, "filename", None) is not None:
242 ui.error(
242 ui.error(
243 _(b"abort: %s: '%s'\n")
243 _(b"abort: %s: '%s'\n")
244 % (
244 % (
245 encoding.strtolocal(inst.strerror),
245 encoding.strtolocal(inst.strerror),
246 stringutil.forcebytestr(inst.filename),
246 stringutil.forcebytestr(inst.filename),
247 )
247 )
248 )
248 )
249 else:
249 else:
250 ui.error(_(b"abort: %s\n") % encoding.strtolocal(inst.strerror))
250 ui.error(_(b"abort: %s\n") % encoding.strtolocal(inst.strerror))
251 else: # suspicious IOError
251 else: # suspicious IOError
252 raise
252 raise
253 except MemoryError:
253 except MemoryError:
254 ui.error(_(b"abort: out of memory\n"))
254 ui.error(_(b"abort: out of memory\n"))
255 except SystemExit as inst:
255 except SystemExit as inst:
256 # Commands shouldn't sys.exit directly, but give a return code.
256 # Commands shouldn't sys.exit directly, but give a return code.
257 # Just in case catch this and and pass exit code to caller.
257 # Just in case catch this and and pass exit code to caller.
258 detailed_exit_code = 254
258 detailed_exit_code = 254
259 coarse_exit_code = inst.code
259 coarse_exit_code = inst.code
260
260
261 if ui.configbool(b'ui', b'detailed-exit-code'):
261 if ui.configbool(b'ui', b'detailed-exit-code'):
262 return detailed_exit_code
262 return detailed_exit_code
263 else:
263 else:
264 return coarse_exit_code
264 return coarse_exit_code
265
265
266
266
267 def checknewlabel(repo, lbl, kind):
267 def checknewlabel(repo, lbl, kind):
268 # Do not use the "kind" parameter in ui output.
268 # Do not use the "kind" parameter in ui output.
269 # It makes strings difficult to translate.
269 # It makes strings difficult to translate.
270 if lbl in [b'tip', b'.', b'null']:
270 if lbl in [b'tip', b'.', b'null']:
271 raise error.InputError(_(b"the name '%s' is reserved") % lbl)
271 raise error.InputError(_(b"the name '%s' is reserved") % lbl)
272 for c in (b':', b'\0', b'\n', b'\r'):
272 for c in (b':', b'\0', b'\n', b'\r'):
273 if c in lbl:
273 if c in lbl:
274 raise error.InputError(
274 raise error.InputError(
275 _(b"%r cannot be used in a name") % pycompat.bytestr(c)
275 _(b"%r cannot be used in a name") % pycompat.bytestr(c)
276 )
276 )
277 try:
277 try:
278 int(lbl)
278 int(lbl)
279 raise error.InputError(_(b"cannot use an integer as a name"))
279 raise error.InputError(_(b"cannot use an integer as a name"))
280 except ValueError:
280 except ValueError:
281 pass
281 pass
282 if lbl.strip() != lbl:
282 if lbl.strip() != lbl:
283 raise error.InputError(
283 raise error.InputError(
284 _(b"leading or trailing whitespace in name %r") % lbl
284 _(b"leading or trailing whitespace in name %r") % lbl
285 )
285 )
286
286
287
287
288 def checkfilename(f):
288 def checkfilename(f):
289 '''Check that the filename f is an acceptable filename for a tracked file'''
289 '''Check that the filename f is an acceptable filename for a tracked file'''
290 if b'\r' in f or b'\n' in f:
290 if b'\r' in f or b'\n' in f:
291 raise error.InputError(
291 raise error.InputError(
292 _(b"'\\n' and '\\r' disallowed in filenames: %r")
292 _(b"'\\n' and '\\r' disallowed in filenames: %r")
293 % pycompat.bytestr(f)
293 % pycompat.bytestr(f)
294 )
294 )
295
295
296
296
297 def checkportable(ui, f):
297 def checkportable(ui, f):
298 '''Check if filename f is portable and warn or abort depending on config'''
298 '''Check if filename f is portable and warn or abort depending on config'''
299 checkfilename(f)
299 checkfilename(f)
300 abort, warn = checkportabilityalert(ui)
300 abort, warn = checkportabilityalert(ui)
301 if abort or warn:
301 if abort or warn:
302 msg = util.checkwinfilename(f)
302 msg = util.checkwinfilename(f)
303 if msg:
303 if msg:
304 msg = b"%s: %s" % (msg, procutil.shellquote(f))
304 msg = b"%s: %s" % (msg, procutil.shellquote(f))
305 if abort:
305 if abort:
306 raise error.InputError(msg)
306 raise error.InputError(msg)
307 ui.warn(_(b"warning: %s\n") % msg)
307 ui.warn(_(b"warning: %s\n") % msg)
308
308
309
309
310 def checkportabilityalert(ui):
310 def checkportabilityalert(ui):
311 """check if the user's config requests nothing, a warning, or abort for
311 """check if the user's config requests nothing, a warning, or abort for
312 non-portable filenames"""
312 non-portable filenames"""
313 val = ui.config(b'ui', b'portablefilenames')
313 val = ui.config(b'ui', b'portablefilenames')
314 lval = val.lower()
314 lval = val.lower()
315 bval = stringutil.parsebool(val)
315 bval = stringutil.parsebool(val)
316 abort = pycompat.iswindows or lval == b'abort'
316 abort = pycompat.iswindows or lval == b'abort'
317 warn = bval or lval == b'warn'
317 warn = bval or lval == b'warn'
318 if bval is None and not (warn or abort or lval == b'ignore'):
318 if bval is None and not (warn or abort or lval == b'ignore'):
319 raise error.ConfigError(
319 raise error.ConfigError(
320 _(b"ui.portablefilenames value is invalid ('%s')") % val
320 _(b"ui.portablefilenames value is invalid ('%s')") % val
321 )
321 )
322 return abort, warn
322 return abort, warn
323
323
324
324
325 class casecollisionauditor(object):
325 class casecollisionauditor(object):
326 def __init__(self, ui, abort, dirstate):
326 def __init__(self, ui, abort, dirstate):
327 self._ui = ui
327 self._ui = ui
328 self._abort = abort
328 self._abort = abort
329 allfiles = b'\0'.join(dirstate)
329 allfiles = b'\0'.join(dirstate)
330 self._loweredfiles = set(encoding.lower(allfiles).split(b'\0'))
330 self._loweredfiles = set(encoding.lower(allfiles).split(b'\0'))
331 self._dirstate = dirstate
331 self._dirstate = dirstate
332 # The purpose of _newfiles is so that we don't complain about
332 # The purpose of _newfiles is so that we don't complain about
333 # case collisions if someone were to call this object with the
333 # case collisions if someone were to call this object with the
334 # same filename twice.
334 # same filename twice.
335 self._newfiles = set()
335 self._newfiles = set()
336
336
337 def __call__(self, f):
337 def __call__(self, f):
338 if f in self._newfiles:
338 if f in self._newfiles:
339 return
339 return
340 fl = encoding.lower(f)
340 fl = encoding.lower(f)
341 if fl in self._loweredfiles and f not in self._dirstate:
341 if fl in self._loweredfiles and f not in self._dirstate:
342 msg = _(b'possible case-folding collision for %s') % f
342 msg = _(b'possible case-folding collision for %s') % f
343 if self._abort:
343 if self._abort:
344 raise error.Abort(msg)
344 raise error.Abort(msg)
345 self._ui.warn(_(b"warning: %s\n") % msg)
345 self._ui.warn(_(b"warning: %s\n") % msg)
346 self._loweredfiles.add(fl)
346 self._loweredfiles.add(fl)
347 self._newfiles.add(f)
347 self._newfiles.add(f)
348
348
349
349
350 def filteredhash(repo, maxrev):
350 def filteredhash(repo, maxrev):
351 """build hash of filtered revisions in the current repoview.
351 """build hash of filtered revisions in the current repoview.
352
352
353 Multiple caches perform up-to-date validation by checking that the
353 Multiple caches perform up-to-date validation by checking that the
354 tiprev and tipnode stored in the cache file match the current repository.
354 tiprev and tipnode stored in the cache file match the current repository.
355 However, this is not sufficient for validating repoviews because the set
355 However, this is not sufficient for validating repoviews because the set
356 of revisions in the view may change without the repository tiprev and
356 of revisions in the view may change without the repository tiprev and
357 tipnode changing.
357 tipnode changing.
358
358
359 This function hashes all the revs filtered from the view and returns
359 This function hashes all the revs filtered from the view and returns
360 that SHA-1 digest.
360 that SHA-1 digest.
361 """
361 """
362 cl = repo.changelog
362 cl = repo.changelog
363 if not cl.filteredrevs:
363 if not cl.filteredrevs:
364 return None
364 return None
365 key = cl._filteredrevs_hashcache.get(maxrev)
365 key = cl._filteredrevs_hashcache.get(maxrev)
366 if not key:
366 if not key:
367 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
367 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
368 if revs:
368 if revs:
369 s = hashutil.sha1()
369 s = hashutil.sha1()
370 for rev in revs:
370 for rev in revs:
371 s.update(b'%d;' % rev)
371 s.update(b'%d;' % rev)
372 key = s.digest()
372 key = s.digest()
373 cl._filteredrevs_hashcache[maxrev] = key
373 cl._filteredrevs_hashcache[maxrev] = key
374 return key
374 return key
375
375
376
376
377 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
377 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
378 """yield every hg repository under path, always recursively.
378 """yield every hg repository under path, always recursively.
379 The recurse flag will only control recursion into repo working dirs"""
379 The recurse flag will only control recursion into repo working dirs"""
380
380
381 def errhandler(err):
381 def errhandler(err):
382 if err.filename == path:
382 if err.filename == path:
383 raise err
383 raise err
384
384
385 samestat = getattr(os.path, 'samestat', None)
385 samestat = getattr(os.path, 'samestat', None)
386 if followsym and samestat is not None:
386 if followsym and samestat is not None:
387
387
388 def adddir(dirlst, dirname):
388 def adddir(dirlst, dirname):
389 dirstat = os.stat(dirname)
389 dirstat = os.stat(dirname)
390 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
390 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
391 if not match:
391 if not match:
392 dirlst.append(dirstat)
392 dirlst.append(dirstat)
393 return not match
393 return not match
394
394
395 else:
395 else:
396 followsym = False
396 followsym = False
397
397
398 if (seen_dirs is None) and followsym:
398 if (seen_dirs is None) and followsym:
399 seen_dirs = []
399 seen_dirs = []
400 adddir(seen_dirs, path)
400 adddir(seen_dirs, path)
401 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
401 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
402 dirs.sort()
402 dirs.sort()
403 if b'.hg' in dirs:
403 if b'.hg' in dirs:
404 yield root # found a repository
404 yield root # found a repository
405 qroot = os.path.join(root, b'.hg', b'patches')
405 qroot = os.path.join(root, b'.hg', b'patches')
406 if os.path.isdir(os.path.join(qroot, b'.hg')):
406 if os.path.isdir(os.path.join(qroot, b'.hg')):
407 yield qroot # we have a patch queue repo here
407 yield qroot # we have a patch queue repo here
408 if recurse:
408 if recurse:
409 # avoid recursing inside the .hg directory
409 # avoid recursing inside the .hg directory
410 dirs.remove(b'.hg')
410 dirs.remove(b'.hg')
411 else:
411 else:
412 dirs[:] = [] # don't descend further
412 dirs[:] = [] # don't descend further
413 elif followsym:
413 elif followsym:
414 newdirs = []
414 newdirs = []
415 for d in dirs:
415 for d in dirs:
416 fname = os.path.join(root, d)
416 fname = os.path.join(root, d)
417 if adddir(seen_dirs, fname):
417 if adddir(seen_dirs, fname):
418 if os.path.islink(fname):
418 if os.path.islink(fname):
419 for hgname in walkrepos(fname, True, seen_dirs):
419 for hgname in walkrepos(fname, True, seen_dirs):
420 yield hgname
420 yield hgname
421 else:
421 else:
422 newdirs.append(d)
422 newdirs.append(d)
423 dirs[:] = newdirs
423 dirs[:] = newdirs
424
424
425
425
426 def binnode(ctx):
426 def binnode(ctx):
427 """Return binary node id for a given basectx"""
427 """Return binary node id for a given basectx"""
428 node = ctx.node()
428 node = ctx.node()
429 if node is None:
429 if node is None:
430 return ctx.repo().nodeconstants.wdirid
430 return ctx.repo().nodeconstants.wdirid
431 return node
431 return node
432
432
433
433
434 def intrev(ctx):
434 def intrev(ctx):
435 """Return integer for a given basectx that can be used in comparison or
435 """Return integer for a given basectx that can be used in comparison or
436 arithmetic operation"""
436 arithmetic operation"""
437 rev = ctx.rev()
437 rev = ctx.rev()
438 if rev is None:
438 if rev is None:
439 return wdirrev
439 return wdirrev
440 return rev
440 return rev
441
441
442
442
443 def formatchangeid(ctx):
443 def formatchangeid(ctx):
444 """Format changectx as '{rev}:{node|formatnode}', which is the default
444 """Format changectx as '{rev}:{node|formatnode}', which is the default
445 template provided by logcmdutil.changesettemplater"""
445 template provided by logcmdutil.changesettemplater"""
446 repo = ctx.repo()
446 repo = ctx.repo()
447 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
447 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
448
448
449
449
450 def formatrevnode(ui, rev, node):
450 def formatrevnode(ui, rev, node):
451 """Format given revision and node depending on the current verbosity"""
451 """Format given revision and node depending on the current verbosity"""
452 if ui.debugflag:
452 if ui.debugflag:
453 hexfunc = hex
453 hexfunc = hex
454 else:
454 else:
455 hexfunc = short
455 hexfunc = short
456 return b'%d:%s' % (rev, hexfunc(node))
456 return b'%d:%s' % (rev, hexfunc(node))
457
457
458
458
459 def resolvehexnodeidprefix(repo, prefix):
459 def resolvehexnodeidprefix(repo, prefix):
460 if prefix.startswith(b'x'):
460 if prefix.startswith(b'x'):
461 prefix = prefix[1:]
461 prefix = prefix[1:]
462 try:
462 try:
463 # Uses unfiltered repo because it's faster when prefix is ambiguous/
463 # Uses unfiltered repo because it's faster when prefix is ambiguous/
464 # This matches the shortesthexnodeidprefix() function below.
464 # This matches the shortesthexnodeidprefix() function below.
465 node = repo.unfiltered().changelog._partialmatch(prefix)
465 node = repo.unfiltered().changelog._partialmatch(prefix)
466 except error.AmbiguousPrefixLookupError:
466 except error.AmbiguousPrefixLookupError:
467 revset = repo.ui.config(
467 revset = repo.ui.config(
468 b'experimental', b'revisions.disambiguatewithin'
468 b'experimental', b'revisions.disambiguatewithin'
469 )
469 )
470 if revset:
470 if revset:
471 # Clear config to avoid infinite recursion
471 # Clear config to avoid infinite recursion
472 configoverrides = {
472 configoverrides = {
473 (b'experimental', b'revisions.disambiguatewithin'): None
473 (b'experimental', b'revisions.disambiguatewithin'): None
474 }
474 }
475 with repo.ui.configoverride(configoverrides):
475 with repo.ui.configoverride(configoverrides):
476 revs = repo.anyrevs([revset], user=True)
476 revs = repo.anyrevs([revset], user=True)
477 matches = []
477 matches = []
478 for rev in revs:
478 for rev in revs:
479 node = repo.changelog.node(rev)
479 node = repo.changelog.node(rev)
480 if hex(node).startswith(prefix):
480 if hex(node).startswith(prefix):
481 matches.append(node)
481 matches.append(node)
482 if len(matches) == 1:
482 if len(matches) == 1:
483 return matches[0]
483 return matches[0]
484 raise
484 raise
485 if node is None:
485 if node is None:
486 return
486 return
487 repo.changelog.rev(node) # make sure node isn't filtered
487 repo.changelog.rev(node) # make sure node isn't filtered
488 return node
488 return node
489
489
490
490
491 def mayberevnum(repo, prefix):
491 def mayberevnum(repo, prefix):
492 """Checks if the given prefix may be mistaken for a revision number"""
492 """Checks if the given prefix may be mistaken for a revision number"""
493 try:
493 try:
494 i = int(prefix)
494 i = int(prefix)
495 # if we are a pure int, then starting with zero will not be
495 # if we are a pure int, then starting with zero will not be
496 # confused as a rev; or, obviously, if the int is larger
496 # confused as a rev; or, obviously, if the int is larger
497 # than the value of the tip rev. We still need to disambiguate if
497 # than the value of the tip rev. We still need to disambiguate if
498 # prefix == '0', since that *is* a valid revnum.
498 # prefix == '0', since that *is* a valid revnum.
499 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
499 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
500 return False
500 return False
501 return True
501 return True
502 except ValueError:
502 except ValueError:
503 return False
503 return False
504
504
505
505
506 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
506 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
507 """Find the shortest unambiguous prefix that matches hexnode.
507 """Find the shortest unambiguous prefix that matches hexnode.
508
508
509 If "cache" is not None, it must be a dictionary that can be used for
509 If "cache" is not None, it must be a dictionary that can be used for
510 caching between calls to this method.
510 caching between calls to this method.
511 """
511 """
512 # _partialmatch() of filtered changelog could take O(len(repo)) time,
512 # _partialmatch() of filtered changelog could take O(len(repo)) time,
513 # which would be unacceptably slow. so we look for hash collision in
513 # which would be unacceptably slow. so we look for hash collision in
514 # unfiltered space, which means some hashes may be slightly longer.
514 # unfiltered space, which means some hashes may be slightly longer.
515
515
516 minlength = max(minlength, 1)
516 minlength = max(minlength, 1)
517
517
518 def disambiguate(prefix):
518 def disambiguate(prefix):
519 """Disambiguate against revnums."""
519 """Disambiguate against revnums."""
520 if repo.ui.configbool(b'experimental', b'revisions.prefixhexnode'):
520 if repo.ui.configbool(b'experimental', b'revisions.prefixhexnode'):
521 if mayberevnum(repo, prefix):
521 if mayberevnum(repo, prefix):
522 return b'x' + prefix
522 return b'x' + prefix
523 else:
523 else:
524 return prefix
524 return prefix
525
525
526 hexnode = hex(node)
526 hexnode = hex(node)
527 for length in range(len(prefix), len(hexnode) + 1):
527 for length in range(len(prefix), len(hexnode) + 1):
528 prefix = hexnode[:length]
528 prefix = hexnode[:length]
529 if not mayberevnum(repo, prefix):
529 if not mayberevnum(repo, prefix):
530 return prefix
530 return prefix
531
531
532 cl = repo.unfiltered().changelog
532 cl = repo.unfiltered().changelog
533 revset = repo.ui.config(b'experimental', b'revisions.disambiguatewithin')
533 revset = repo.ui.config(b'experimental', b'revisions.disambiguatewithin')
534 if revset:
534 if revset:
535 revs = None
535 revs = None
536 if cache is not None:
536 if cache is not None:
537 revs = cache.get(b'disambiguationrevset')
537 revs = cache.get(b'disambiguationrevset')
538 if revs is None:
538 if revs is None:
539 revs = repo.anyrevs([revset], user=True)
539 revs = repo.anyrevs([revset], user=True)
540 if cache is not None:
540 if cache is not None:
541 cache[b'disambiguationrevset'] = revs
541 cache[b'disambiguationrevset'] = revs
542 if cl.rev(node) in revs:
542 if cl.rev(node) in revs:
543 hexnode = hex(node)
543 hexnode = hex(node)
544 nodetree = None
544 nodetree = None
545 if cache is not None:
545 if cache is not None:
546 nodetree = cache.get(b'disambiguationnodetree')
546 nodetree = cache.get(b'disambiguationnodetree')
547 if not nodetree:
547 if not nodetree:
548 if util.safehasattr(parsers, 'nodetree'):
548 if util.safehasattr(parsers, 'nodetree'):
549 # The CExt is the only implementation to provide a nodetree
549 # The CExt is the only implementation to provide a nodetree
550 # class so far.
550 # class so far.
551 index = cl.index
551 index = cl.index
552 if util.safehasattr(index, 'get_cindex'):
552 if util.safehasattr(index, 'get_cindex'):
553 # the rust wrapped need to give access to its internal index
553 # the rust wrapped need to give access to its internal index
554 index = index.get_cindex()
554 index = index.get_cindex()
555 nodetree = parsers.nodetree(index, len(revs))
555 nodetree = parsers.nodetree(index, len(revs))
556 for r in revs:
556 for r in revs:
557 nodetree.insert(r)
557 nodetree.insert(r)
558 if cache is not None:
558 if cache is not None:
559 cache[b'disambiguationnodetree'] = nodetree
559 cache[b'disambiguationnodetree'] = nodetree
560 if nodetree is not None:
560 if nodetree is not None:
561 length = max(nodetree.shortest(node), minlength)
561 length = max(nodetree.shortest(node), minlength)
562 prefix = hexnode[:length]
562 prefix = hexnode[:length]
563 return disambiguate(prefix)
563 return disambiguate(prefix)
564 for length in range(minlength, len(hexnode) + 1):
564 for length in range(minlength, len(hexnode) + 1):
565 matches = []
565 matches = []
566 prefix = hexnode[:length]
566 prefix = hexnode[:length]
567 for rev in revs:
567 for rev in revs:
568 otherhexnode = repo[rev].hex()
568 otherhexnode = repo[rev].hex()
569 if prefix == otherhexnode[:length]:
569 if prefix == otherhexnode[:length]:
570 matches.append(otherhexnode)
570 matches.append(otherhexnode)
571 if len(matches) == 1:
571 if len(matches) == 1:
572 return disambiguate(prefix)
572 return disambiguate(prefix)
573
573
574 try:
574 try:
575 return disambiguate(cl.shortest(node, minlength))
575 return disambiguate(cl.shortest(node, minlength))
576 except error.LookupError:
576 except error.LookupError:
577 raise error.RepoLookupError()
577 raise error.RepoLookupError()
578
578
579
579
580 def isrevsymbol(repo, symbol):
580 def isrevsymbol(repo, symbol):
581 """Checks if a symbol exists in the repo.
581 """Checks if a symbol exists in the repo.
582
582
583 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
583 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
584 symbol is an ambiguous nodeid prefix.
584 symbol is an ambiguous nodeid prefix.
585 """
585 """
586 try:
586 try:
587 revsymbol(repo, symbol)
587 revsymbol(repo, symbol)
588 return True
588 return True
589 except error.RepoLookupError:
589 except error.RepoLookupError:
590 return False
590 return False
591
591
592
592
593 def revsymbol(repo, symbol):
593 def revsymbol(repo, symbol):
594 """Returns a context given a single revision symbol (as string).
594 """Returns a context given a single revision symbol (as string).
595
595
596 This is similar to revsingle(), but accepts only a single revision symbol,
596 This is similar to revsingle(), but accepts only a single revision symbol,
597 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
597 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
598 not "max(public())".
598 not "max(public())".
599 """
599 """
600 if not isinstance(symbol, bytes):
600 if not isinstance(symbol, bytes):
601 msg = (
601 msg = (
602 b"symbol (%s of type %s) was not a string, did you mean "
602 b"symbol (%s of type %s) was not a string, did you mean "
603 b"repo[symbol]?" % (symbol, type(symbol))
603 b"repo[symbol]?" % (symbol, type(symbol))
604 )
604 )
605 raise error.ProgrammingError(msg)
605 raise error.ProgrammingError(msg)
606 try:
606 try:
607 if symbol in (b'.', b'tip', b'null'):
607 if symbol in (b'.', b'tip', b'null'):
608 return repo[symbol]
608 return repo[symbol]
609
609
610 try:
610 try:
611 r = int(symbol)
611 r = int(symbol)
612 if b'%d' % r != symbol:
612 if b'%d' % r != symbol:
613 raise ValueError
613 raise ValueError
614 l = len(repo.changelog)
614 l = len(repo.changelog)
615 if r < 0:
615 if r < 0:
616 r += l
616 r += l
617 if r < 0 or r >= l and r != wdirrev:
617 if r < 0 or r >= l and r != wdirrev:
618 raise ValueError
618 raise ValueError
619 return repo[r]
619 return repo[r]
620 except error.FilteredIndexError:
620 except error.FilteredIndexError:
621 raise
621 raise
622 except (ValueError, OverflowError, IndexError):
622 except (ValueError, OverflowError, IndexError):
623 pass
623 pass
624
624
625 if len(symbol) == 2 * repo.nodeconstants.nodelen:
625 if len(symbol) == 2 * repo.nodeconstants.nodelen:
626 try:
626 try:
627 node = bin(symbol)
627 node = bin(symbol)
628 rev = repo.changelog.rev(node)
628 rev = repo.changelog.rev(node)
629 return repo[rev]
629 return repo[rev]
630 except error.FilteredLookupError:
630 except error.FilteredLookupError:
631 raise
631 raise
632 except (TypeError, LookupError):
632 except (TypeError, LookupError):
633 pass
633 pass
634
634
635 # look up bookmarks through the name interface
635 # look up bookmarks through the name interface
636 try:
636 try:
637 node = repo.names.singlenode(repo, symbol)
637 node = repo.names.singlenode(repo, symbol)
638 rev = repo.changelog.rev(node)
638 rev = repo.changelog.rev(node)
639 return repo[rev]
639 return repo[rev]
640 except KeyError:
640 except KeyError:
641 pass
641 pass
642
642
643 node = resolvehexnodeidprefix(repo, symbol)
643 node = resolvehexnodeidprefix(repo, symbol)
644 if node is not None:
644 if node is not None:
645 rev = repo.changelog.rev(node)
645 rev = repo.changelog.rev(node)
646 return repo[rev]
646 return repo[rev]
647
647
648 raise error.RepoLookupError(_(b"unknown revision '%s'") % symbol)
648 raise error.RepoLookupError(_(b"unknown revision '%s'") % symbol)
649
649
650 except error.WdirUnsupported:
650 except error.WdirUnsupported:
651 return repo[None]
651 return repo[None]
652 except (
652 except (
653 error.FilteredIndexError,
653 error.FilteredIndexError,
654 error.FilteredLookupError,
654 error.FilteredLookupError,
655 error.FilteredRepoLookupError,
655 error.FilteredRepoLookupError,
656 ):
656 ):
657 raise _filterederror(repo, symbol)
657 raise _filterederror(repo, symbol)
658
658
659
659
660 def _filterederror(repo, changeid):
660 def _filterederror(repo, changeid):
661 """build an exception to be raised about a filtered changeid
661 """build an exception to be raised about a filtered changeid
662
662
663 This is extracted in a function to help extensions (eg: evolve) to
663 This is extracted in a function to help extensions (eg: evolve) to
664 experiment with various message variants."""
664 experiment with various message variants."""
665 if repo.filtername.startswith(b'visible'):
665 if repo.filtername.startswith(b'visible'):
666
666
667 # Check if the changeset is obsolete
667 # Check if the changeset is obsolete
668 unfilteredrepo = repo.unfiltered()
668 unfilteredrepo = repo.unfiltered()
669 ctx = revsymbol(unfilteredrepo, changeid)
669 ctx = revsymbol(unfilteredrepo, changeid)
670
670
671 # If the changeset is obsolete, enrich the message with the reason
671 # If the changeset is obsolete, enrich the message with the reason
672 # that made this changeset not visible
672 # that made this changeset not visible
673 if ctx.obsolete():
673 if ctx.obsolete():
674 msg = obsutil._getfilteredreason(repo, changeid, ctx)
674 msg = obsutil._getfilteredreason(repo, changeid, ctx)
675 else:
675 else:
676 msg = _(b"hidden revision '%s'") % changeid
676 msg = _(b"hidden revision '%s'") % changeid
677
677
678 hint = _(b'use --hidden to access hidden revisions')
678 hint = _(b'use --hidden to access hidden revisions')
679
679
680 return error.FilteredRepoLookupError(msg, hint=hint)
680 return error.FilteredRepoLookupError(msg, hint=hint)
681 msg = _(b"filtered revision '%s' (not in '%s' subset)")
681 msg = _(b"filtered revision '%s' (not in '%s' subset)")
682 msg %= (changeid, repo.filtername)
682 msg %= (changeid, repo.filtername)
683 return error.FilteredRepoLookupError(msg)
683 return error.FilteredRepoLookupError(msg)
684
684
685
685
686 def revsingle(repo, revspec, default=b'.', localalias=None):
686 def revsingle(repo, revspec, default=b'.', localalias=None):
687 if not revspec and revspec != 0:
687 if not revspec and revspec != 0:
688 return repo[default]
688 return repo[default]
689
689
690 l = revrange(repo, [revspec], localalias=localalias)
690 l = revrange(repo, [revspec], localalias=localalias)
691 if not l:
691 if not l:
692 raise error.Abort(_(b'empty revision set'))
692 raise error.InputError(_(b'empty revision set'))
693 return repo[l.last()]
693 return repo[l.last()]
694
694
695
695
696 def _pairspec(revspec):
696 def _pairspec(revspec):
697 tree = revsetlang.parse(revspec)
697 tree = revsetlang.parse(revspec)
698 return tree and tree[0] in (
698 return tree and tree[0] in (
699 b'range',
699 b'range',
700 b'rangepre',
700 b'rangepre',
701 b'rangepost',
701 b'rangepost',
702 b'rangeall',
702 b'rangeall',
703 )
703 )
704
704
705
705
706 def revpair(repo, revs):
706 def revpair(repo, revs):
707 if not revs:
707 if not revs:
708 return repo[b'.'], repo[None]
708 return repo[b'.'], repo[None]
709
709
710 l = revrange(repo, revs)
710 l = revrange(repo, revs)
711
711
712 if not l:
712 if not l:
713 raise error.Abort(_(b'empty revision range'))
713 raise error.InputError(_(b'empty revision range'))
714
714
715 first = l.first()
715 first = l.first()
716 second = l.last()
716 second = l.last()
717
717
718 if (
718 if (
719 first == second
719 first == second
720 and len(revs) >= 2
720 and len(revs) >= 2
721 and not all(revrange(repo, [r]) for r in revs)
721 and not all(revrange(repo, [r]) for r in revs)
722 ):
722 ):
723 raise error.Abort(_(b'empty revision on one side of range'))
723 raise error.InputError(_(b'empty revision on one side of range'))
724
724
725 # if top-level is range expression, the result must always be a pair
725 # if top-level is range expression, the result must always be a pair
726 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
726 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
727 return repo[first], repo[None]
727 return repo[first], repo[None]
728
728
729 return repo[first], repo[second]
729 return repo[first], repo[second]
730
730
731
731
732 def revrange(repo, specs, localalias=None):
732 def revrange(repo, specs, localalias=None):
733 """Execute 1 to many revsets and return the union.
733 """Execute 1 to many revsets and return the union.
734
734
735 This is the preferred mechanism for executing revsets using user-specified
735 This is the preferred mechanism for executing revsets using user-specified
736 config options, such as revset aliases.
736 config options, such as revset aliases.
737
737
738 The revsets specified by ``specs`` will be executed via a chained ``OR``
738 The revsets specified by ``specs`` will be executed via a chained ``OR``
739 expression. If ``specs`` is empty, an empty result is returned.
739 expression. If ``specs`` is empty, an empty result is returned.
740
740
741 ``specs`` can contain integers, in which case they are assumed to be
741 ``specs`` can contain integers, in which case they are assumed to be
742 revision numbers.
742 revision numbers.
743
743
744 It is assumed the revsets are already formatted. If you have arguments
744 It is assumed the revsets are already formatted. If you have arguments
745 that need to be expanded in the revset, call ``revsetlang.formatspec()``
745 that need to be expanded in the revset, call ``revsetlang.formatspec()``
746 and pass the result as an element of ``specs``.
746 and pass the result as an element of ``specs``.
747
747
748 Specifying a single revset is allowed.
748 Specifying a single revset is allowed.
749
749
750 Returns a ``smartset.abstractsmartset`` which is a list-like interface over
750 Returns a ``smartset.abstractsmartset`` which is a list-like interface over
751 integer revisions.
751 integer revisions.
752 """
752 """
753 allspecs = []
753 allspecs = []
754 for spec in specs:
754 for spec in specs:
755 if isinstance(spec, int):
755 if isinstance(spec, int):
756 spec = revsetlang.formatspec(b'%d', spec)
756 spec = revsetlang.formatspec(b'%d', spec)
757 allspecs.append(spec)
757 allspecs.append(spec)
758 return repo.anyrevs(allspecs, user=True, localalias=localalias)
758 return repo.anyrevs(allspecs, user=True, localalias=localalias)
759
759
760
760
761 def increasingwindows(windowsize=8, sizelimit=512):
761 def increasingwindows(windowsize=8, sizelimit=512):
762 while True:
762 while True:
763 yield windowsize
763 yield windowsize
764 if windowsize < sizelimit:
764 if windowsize < sizelimit:
765 windowsize *= 2
765 windowsize *= 2
766
766
767
767
768 def walkchangerevs(repo, revs, makefilematcher, prepare):
768 def walkchangerevs(repo, revs, makefilematcher, prepare):
769 """Iterate over files and the revs in a "windowed" way.
769 """Iterate over files and the revs in a "windowed" way.
770
770
771 Callers most commonly need to iterate backwards over the history
771 Callers most commonly need to iterate backwards over the history
772 in which they are interested. Doing so has awful (quadratic-looking)
772 in which they are interested. Doing so has awful (quadratic-looking)
773 performance, so we use iterators in a "windowed" way.
773 performance, so we use iterators in a "windowed" way.
774
774
775 We walk a window of revisions in the desired order. Within the
775 We walk a window of revisions in the desired order. Within the
776 window, we first walk forwards to gather data, then in the desired
776 window, we first walk forwards to gather data, then in the desired
777 order (usually backwards) to display it.
777 order (usually backwards) to display it.
778
778
779 This function returns an iterator yielding contexts. Before
779 This function returns an iterator yielding contexts. Before
780 yielding each context, the iterator will first call the prepare
780 yielding each context, the iterator will first call the prepare
781 function on each context in the window in forward order."""
781 function on each context in the window in forward order."""
782
782
783 if not revs:
783 if not revs:
784 return []
784 return []
785 change = repo.__getitem__
785 change = repo.__getitem__
786
786
787 def iterate():
787 def iterate():
788 it = iter(revs)
788 it = iter(revs)
789 stopiteration = False
789 stopiteration = False
790 for windowsize in increasingwindows():
790 for windowsize in increasingwindows():
791 nrevs = []
791 nrevs = []
792 for i in pycompat.xrange(windowsize):
792 for i in pycompat.xrange(windowsize):
793 rev = next(it, None)
793 rev = next(it, None)
794 if rev is None:
794 if rev is None:
795 stopiteration = True
795 stopiteration = True
796 break
796 break
797 nrevs.append(rev)
797 nrevs.append(rev)
798 for rev in sorted(nrevs):
798 for rev in sorted(nrevs):
799 ctx = change(rev)
799 ctx = change(rev)
800 prepare(ctx, makefilematcher(ctx))
800 prepare(ctx, makefilematcher(ctx))
801 for rev in nrevs:
801 for rev in nrevs:
802 yield change(rev)
802 yield change(rev)
803
803
804 if stopiteration:
804 if stopiteration:
805 break
805 break
806
806
807 return iterate()
807 return iterate()
808
808
809
809
810 def meaningfulparents(repo, ctx):
810 def meaningfulparents(repo, ctx):
811 """Return list of meaningful (or all if debug) parentrevs for rev.
811 """Return list of meaningful (or all if debug) parentrevs for rev.
812
812
813 For merges (two non-nullrev revisions) both parents are meaningful.
813 For merges (two non-nullrev revisions) both parents are meaningful.
814 Otherwise the first parent revision is considered meaningful if it
814 Otherwise the first parent revision is considered meaningful if it
815 is not the preceding revision.
815 is not the preceding revision.
816 """
816 """
817 parents = ctx.parents()
817 parents = ctx.parents()
818 if len(parents) > 1:
818 if len(parents) > 1:
819 return parents
819 return parents
820 if repo.ui.debugflag:
820 if repo.ui.debugflag:
821 return [parents[0], repo[nullrev]]
821 return [parents[0], repo[nullrev]]
822 if parents[0].rev() >= intrev(ctx) - 1:
822 if parents[0].rev() >= intrev(ctx) - 1:
823 return []
823 return []
824 return parents
824 return parents
825
825
826
826
827 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
827 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
828 """Return a function that produced paths for presenting to the user.
828 """Return a function that produced paths for presenting to the user.
829
829
830 The returned function takes a repo-relative path and produces a path
830 The returned function takes a repo-relative path and produces a path
831 that can be presented in the UI.
831 that can be presented in the UI.
832
832
833 Depending on the value of ui.relative-paths, either a repo-relative or
833 Depending on the value of ui.relative-paths, either a repo-relative or
834 cwd-relative path will be produced.
834 cwd-relative path will be produced.
835
835
836 legacyrelativevalue is the value to use if ui.relative-paths=legacy
836 legacyrelativevalue is the value to use if ui.relative-paths=legacy
837
837
838 If forcerelativevalue is not None, then that value will be used regardless
838 If forcerelativevalue is not None, then that value will be used regardless
839 of what ui.relative-paths is set to.
839 of what ui.relative-paths is set to.
840 """
840 """
841 if forcerelativevalue is not None:
841 if forcerelativevalue is not None:
842 relative = forcerelativevalue
842 relative = forcerelativevalue
843 else:
843 else:
844 config = repo.ui.config(b'ui', b'relative-paths')
844 config = repo.ui.config(b'ui', b'relative-paths')
845 if config == b'legacy':
845 if config == b'legacy':
846 relative = legacyrelativevalue
846 relative = legacyrelativevalue
847 else:
847 else:
848 relative = stringutil.parsebool(config)
848 relative = stringutil.parsebool(config)
849 if relative is None:
849 if relative is None:
850 raise error.ConfigError(
850 raise error.ConfigError(
851 _(b"ui.relative-paths is not a boolean ('%s')") % config
851 _(b"ui.relative-paths is not a boolean ('%s')") % config
852 )
852 )
853
853
854 if relative:
854 if relative:
855 cwd = repo.getcwd()
855 cwd = repo.getcwd()
856 if cwd != b'':
856 if cwd != b'':
857 # this branch would work even if cwd == b'' (ie cwd = repo
857 # this branch would work even if cwd == b'' (ie cwd = repo
858 # root), but its generality makes the returned function slower
858 # root), but its generality makes the returned function slower
859 pathto = repo.pathto
859 pathto = repo.pathto
860 return lambda f: pathto(f, cwd)
860 return lambda f: pathto(f, cwd)
861 if repo.ui.configbool(b'ui', b'slash'):
861 if repo.ui.configbool(b'ui', b'slash'):
862 return lambda f: f
862 return lambda f: f
863 else:
863 else:
864 return util.localpath
864 return util.localpath
865
865
866
866
867 def subdiruipathfn(subpath, uipathfn):
867 def subdiruipathfn(subpath, uipathfn):
868 '''Create a new uipathfn that treats the file as relative to subpath.'''
868 '''Create a new uipathfn that treats the file as relative to subpath.'''
869 return lambda f: uipathfn(posixpath.join(subpath, f))
869 return lambda f: uipathfn(posixpath.join(subpath, f))
870
870
871
871
872 def anypats(pats, opts):
872 def anypats(pats, opts):
873 """Checks if any patterns, including --include and --exclude were given.
873 """Checks if any patterns, including --include and --exclude were given.
874
874
875 Some commands (e.g. addremove) use this condition for deciding whether to
875 Some commands (e.g. addremove) use this condition for deciding whether to
876 print absolute or relative paths.
876 print absolute or relative paths.
877 """
877 """
878 return bool(pats or opts.get(b'include') or opts.get(b'exclude'))
878 return bool(pats or opts.get(b'include') or opts.get(b'exclude'))
879
879
880
880
881 def expandpats(pats):
881 def expandpats(pats):
882 """Expand bare globs when running on windows.
882 """Expand bare globs when running on windows.
883 On posix we assume it already has already been done by sh."""
883 On posix we assume it already has already been done by sh."""
884 if not util.expandglobs:
884 if not util.expandglobs:
885 return list(pats)
885 return list(pats)
886 ret = []
886 ret = []
887 for kindpat in pats:
887 for kindpat in pats:
888 kind, pat = matchmod._patsplit(kindpat, None)
888 kind, pat = matchmod._patsplit(kindpat, None)
889 if kind is None:
889 if kind is None:
890 try:
890 try:
891 globbed = glob.glob(pat)
891 globbed = glob.glob(pat)
892 except re.error:
892 except re.error:
893 globbed = [pat]
893 globbed = [pat]
894 if globbed:
894 if globbed:
895 ret.extend(globbed)
895 ret.extend(globbed)
896 continue
896 continue
897 ret.append(kindpat)
897 ret.append(kindpat)
898 return ret
898 return ret
899
899
900
900
901 def matchandpats(
901 def matchandpats(
902 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
902 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
903 ):
903 ):
904 """Return a matcher and the patterns that were used.
904 """Return a matcher and the patterns that were used.
905 The matcher will warn about bad matches, unless an alternate badfn callback
905 The matcher will warn about bad matches, unless an alternate badfn callback
906 is provided."""
906 is provided."""
907 if opts is None:
907 if opts is None:
908 opts = {}
908 opts = {}
909 if not globbed and default == b'relpath':
909 if not globbed and default == b'relpath':
910 pats = expandpats(pats or [])
910 pats = expandpats(pats or [])
911
911
912 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
912 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
913
913
914 def bad(f, msg):
914 def bad(f, msg):
915 ctx.repo().ui.warn(b"%s: %s\n" % (uipathfn(f), msg))
915 ctx.repo().ui.warn(b"%s: %s\n" % (uipathfn(f), msg))
916
916
917 if badfn is None:
917 if badfn is None:
918 badfn = bad
918 badfn = bad
919
919
920 m = ctx.match(
920 m = ctx.match(
921 pats,
921 pats,
922 opts.get(b'include'),
922 opts.get(b'include'),
923 opts.get(b'exclude'),
923 opts.get(b'exclude'),
924 default,
924 default,
925 listsubrepos=opts.get(b'subrepos'),
925 listsubrepos=opts.get(b'subrepos'),
926 badfn=badfn,
926 badfn=badfn,
927 )
927 )
928
928
929 if m.always():
929 if m.always():
930 pats = []
930 pats = []
931 return m, pats
931 return m, pats
932
932
933
933
934 def match(
934 def match(
935 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
935 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
936 ):
936 ):
937 '''Return a matcher that will warn about bad matches.'''
937 '''Return a matcher that will warn about bad matches.'''
938 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
938 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
939
939
940
940
941 def matchall(repo):
941 def matchall(repo):
942 '''Return a matcher that will efficiently match everything.'''
942 '''Return a matcher that will efficiently match everything.'''
943 return matchmod.always()
943 return matchmod.always()
944
944
945
945
946 def matchfiles(repo, files, badfn=None):
946 def matchfiles(repo, files, badfn=None):
947 '''Return a matcher that will efficiently match exactly these files.'''
947 '''Return a matcher that will efficiently match exactly these files.'''
948 return matchmod.exact(files, badfn=badfn)
948 return matchmod.exact(files, badfn=badfn)
949
949
950
950
951 def parsefollowlinespattern(repo, rev, pat, msg):
951 def parsefollowlinespattern(repo, rev, pat, msg):
952 """Return a file name from `pat` pattern suitable for usage in followlines
952 """Return a file name from `pat` pattern suitable for usage in followlines
953 logic.
953 logic.
954 """
954 """
955 if not matchmod.patkind(pat):
955 if not matchmod.patkind(pat):
956 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
956 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
957 else:
957 else:
958 ctx = repo[rev]
958 ctx = repo[rev]
959 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
959 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
960 files = [f for f in ctx if m(f)]
960 files = [f for f in ctx if m(f)]
961 if len(files) != 1:
961 if len(files) != 1:
962 raise error.ParseError(msg)
962 raise error.ParseError(msg)
963 return files[0]
963 return files[0]
964
964
965
965
966 def getorigvfs(ui, repo):
966 def getorigvfs(ui, repo):
967 """return a vfs suitable to save 'orig' file
967 """return a vfs suitable to save 'orig' file
968
968
969 return None if no special directory is configured"""
969 return None if no special directory is configured"""
970 origbackuppath = ui.config(b'ui', b'origbackuppath')
970 origbackuppath = ui.config(b'ui', b'origbackuppath')
971 if not origbackuppath:
971 if not origbackuppath:
972 return None
972 return None
973 return vfs.vfs(repo.wvfs.join(origbackuppath))
973 return vfs.vfs(repo.wvfs.join(origbackuppath))
974
974
975
975
976 def backuppath(ui, repo, filepath):
976 def backuppath(ui, repo, filepath):
977 """customize where working copy backup files (.orig files) are created
977 """customize where working copy backup files (.orig files) are created
978
978
979 Fetch user defined path from config file: [ui] origbackuppath = <path>
979 Fetch user defined path from config file: [ui] origbackuppath = <path>
980 Fall back to default (filepath with .orig suffix) if not specified
980 Fall back to default (filepath with .orig suffix) if not specified
981
981
982 filepath is repo-relative
982 filepath is repo-relative
983
983
984 Returns an absolute path
984 Returns an absolute path
985 """
985 """
986 origvfs = getorigvfs(ui, repo)
986 origvfs = getorigvfs(ui, repo)
987 if origvfs is None:
987 if origvfs is None:
988 return repo.wjoin(filepath + b".orig")
988 return repo.wjoin(filepath + b".orig")
989
989
990 origbackupdir = origvfs.dirname(filepath)
990 origbackupdir = origvfs.dirname(filepath)
991 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
991 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
992 ui.note(_(b'creating directory: %s\n') % origvfs.join(origbackupdir))
992 ui.note(_(b'creating directory: %s\n') % origvfs.join(origbackupdir))
993
993
994 # Remove any files that conflict with the backup file's path
994 # Remove any files that conflict with the backup file's path
995 for f in reversed(list(pathutil.finddirs(filepath))):
995 for f in reversed(list(pathutil.finddirs(filepath))):
996 if origvfs.isfileorlink(f):
996 if origvfs.isfileorlink(f):
997 ui.note(_(b'removing conflicting file: %s\n') % origvfs.join(f))
997 ui.note(_(b'removing conflicting file: %s\n') % origvfs.join(f))
998 origvfs.unlink(f)
998 origvfs.unlink(f)
999 break
999 break
1000
1000
1001 origvfs.makedirs(origbackupdir)
1001 origvfs.makedirs(origbackupdir)
1002
1002
1003 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
1003 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
1004 ui.note(
1004 ui.note(
1005 _(b'removing conflicting directory: %s\n') % origvfs.join(filepath)
1005 _(b'removing conflicting directory: %s\n') % origvfs.join(filepath)
1006 )
1006 )
1007 origvfs.rmtree(filepath, forcibly=True)
1007 origvfs.rmtree(filepath, forcibly=True)
1008
1008
1009 return origvfs.join(filepath)
1009 return origvfs.join(filepath)
1010
1010
1011
1011
1012 class _containsnode(object):
1012 class _containsnode(object):
1013 """proxy __contains__(node) to container.__contains__ which accepts revs"""
1013 """proxy __contains__(node) to container.__contains__ which accepts revs"""
1014
1014
1015 def __init__(self, repo, revcontainer):
1015 def __init__(self, repo, revcontainer):
1016 self._torev = repo.changelog.rev
1016 self._torev = repo.changelog.rev
1017 self._revcontains = revcontainer.__contains__
1017 self._revcontains = revcontainer.__contains__
1018
1018
1019 def __contains__(self, node):
1019 def __contains__(self, node):
1020 return self._revcontains(self._torev(node))
1020 return self._revcontains(self._torev(node))
1021
1021
1022
1022
1023 def cleanupnodes(
1023 def cleanupnodes(
1024 repo,
1024 repo,
1025 replacements,
1025 replacements,
1026 operation,
1026 operation,
1027 moves=None,
1027 moves=None,
1028 metadata=None,
1028 metadata=None,
1029 fixphase=False,
1029 fixphase=False,
1030 targetphase=None,
1030 targetphase=None,
1031 backup=True,
1031 backup=True,
1032 ):
1032 ):
1033 """do common cleanups when old nodes are replaced by new nodes
1033 """do common cleanups when old nodes are replaced by new nodes
1034
1034
1035 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
1035 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
1036 (we might also want to move working directory parent in the future)
1036 (we might also want to move working directory parent in the future)
1037
1037
1038 By default, bookmark moves are calculated automatically from 'replacements',
1038 By default, bookmark moves are calculated automatically from 'replacements',
1039 but 'moves' can be used to override that. Also, 'moves' may include
1039 but 'moves' can be used to override that. Also, 'moves' may include
1040 additional bookmark moves that should not have associated obsmarkers.
1040 additional bookmark moves that should not have associated obsmarkers.
1041
1041
1042 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
1042 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
1043 have replacements. operation is a string, like "rebase".
1043 have replacements. operation is a string, like "rebase".
1044
1044
1045 metadata is dictionary containing metadata to be stored in obsmarker if
1045 metadata is dictionary containing metadata to be stored in obsmarker if
1046 obsolescence is enabled.
1046 obsolescence is enabled.
1047 """
1047 """
1048 assert fixphase or targetphase is None
1048 assert fixphase or targetphase is None
1049 if not replacements and not moves:
1049 if not replacements and not moves:
1050 return
1050 return
1051
1051
1052 # translate mapping's other forms
1052 # translate mapping's other forms
1053 if not util.safehasattr(replacements, b'items'):
1053 if not util.safehasattr(replacements, b'items'):
1054 replacements = {(n,): () for n in replacements}
1054 replacements = {(n,): () for n in replacements}
1055 else:
1055 else:
1056 # upgrading non tuple "source" to tuple ones for BC
1056 # upgrading non tuple "source" to tuple ones for BC
1057 repls = {}
1057 repls = {}
1058 for key, value in replacements.items():
1058 for key, value in replacements.items():
1059 if not isinstance(key, tuple):
1059 if not isinstance(key, tuple):
1060 key = (key,)
1060 key = (key,)
1061 repls[key] = value
1061 repls[key] = value
1062 replacements = repls
1062 replacements = repls
1063
1063
1064 # Unfiltered repo is needed since nodes in replacements might be hidden.
1064 # Unfiltered repo is needed since nodes in replacements might be hidden.
1065 unfi = repo.unfiltered()
1065 unfi = repo.unfiltered()
1066
1066
1067 # Calculate bookmark movements
1067 # Calculate bookmark movements
1068 if moves is None:
1068 if moves is None:
1069 moves = {}
1069 moves = {}
1070 for oldnodes, newnodes in replacements.items():
1070 for oldnodes, newnodes in replacements.items():
1071 for oldnode in oldnodes:
1071 for oldnode in oldnodes:
1072 if oldnode in moves:
1072 if oldnode in moves:
1073 continue
1073 continue
1074 if len(newnodes) > 1:
1074 if len(newnodes) > 1:
1075 # usually a split, take the one with biggest rev number
1075 # usually a split, take the one with biggest rev number
1076 newnode = next(unfi.set(b'max(%ln)', newnodes)).node()
1076 newnode = next(unfi.set(b'max(%ln)', newnodes)).node()
1077 elif len(newnodes) == 0:
1077 elif len(newnodes) == 0:
1078 # move bookmark backwards
1078 # move bookmark backwards
1079 allreplaced = []
1079 allreplaced = []
1080 for rep in replacements:
1080 for rep in replacements:
1081 allreplaced.extend(rep)
1081 allreplaced.extend(rep)
1082 roots = list(
1082 roots = list(
1083 unfi.set(b'max((::%n) - %ln)', oldnode, allreplaced)
1083 unfi.set(b'max((::%n) - %ln)', oldnode, allreplaced)
1084 )
1084 )
1085 if roots:
1085 if roots:
1086 newnode = roots[0].node()
1086 newnode = roots[0].node()
1087 else:
1087 else:
1088 newnode = repo.nullid
1088 newnode = repo.nullid
1089 else:
1089 else:
1090 newnode = newnodes[0]
1090 newnode = newnodes[0]
1091 moves[oldnode] = newnode
1091 moves[oldnode] = newnode
1092
1092
1093 allnewnodes = [n for ns in replacements.values() for n in ns]
1093 allnewnodes = [n for ns in replacements.values() for n in ns]
1094 toretract = {}
1094 toretract = {}
1095 toadvance = {}
1095 toadvance = {}
1096 if fixphase:
1096 if fixphase:
1097 precursors = {}
1097 precursors = {}
1098 for oldnodes, newnodes in replacements.items():
1098 for oldnodes, newnodes in replacements.items():
1099 for oldnode in oldnodes:
1099 for oldnode in oldnodes:
1100 for newnode in newnodes:
1100 for newnode in newnodes:
1101 precursors.setdefault(newnode, []).append(oldnode)
1101 precursors.setdefault(newnode, []).append(oldnode)
1102
1102
1103 allnewnodes.sort(key=lambda n: unfi[n].rev())
1103 allnewnodes.sort(key=lambda n: unfi[n].rev())
1104 newphases = {}
1104 newphases = {}
1105
1105
1106 def phase(ctx):
1106 def phase(ctx):
1107 return newphases.get(ctx.node(), ctx.phase())
1107 return newphases.get(ctx.node(), ctx.phase())
1108
1108
1109 for newnode in allnewnodes:
1109 for newnode in allnewnodes:
1110 ctx = unfi[newnode]
1110 ctx = unfi[newnode]
1111 parentphase = max(phase(p) for p in ctx.parents())
1111 parentphase = max(phase(p) for p in ctx.parents())
1112 if targetphase is None:
1112 if targetphase is None:
1113 oldphase = max(
1113 oldphase = max(
1114 unfi[oldnode].phase() for oldnode in precursors[newnode]
1114 unfi[oldnode].phase() for oldnode in precursors[newnode]
1115 )
1115 )
1116 newphase = max(oldphase, parentphase)
1116 newphase = max(oldphase, parentphase)
1117 else:
1117 else:
1118 newphase = max(targetphase, parentphase)
1118 newphase = max(targetphase, parentphase)
1119 newphases[newnode] = newphase
1119 newphases[newnode] = newphase
1120 if newphase > ctx.phase():
1120 if newphase > ctx.phase():
1121 toretract.setdefault(newphase, []).append(newnode)
1121 toretract.setdefault(newphase, []).append(newnode)
1122 elif newphase < ctx.phase():
1122 elif newphase < ctx.phase():
1123 toadvance.setdefault(newphase, []).append(newnode)
1123 toadvance.setdefault(newphase, []).append(newnode)
1124
1124
1125 with repo.transaction(b'cleanup') as tr:
1125 with repo.transaction(b'cleanup') as tr:
1126 # Move bookmarks
1126 # Move bookmarks
1127 bmarks = repo._bookmarks
1127 bmarks = repo._bookmarks
1128 bmarkchanges = []
1128 bmarkchanges = []
1129 for oldnode, newnode in moves.items():
1129 for oldnode, newnode in moves.items():
1130 oldbmarks = repo.nodebookmarks(oldnode)
1130 oldbmarks = repo.nodebookmarks(oldnode)
1131 if not oldbmarks:
1131 if not oldbmarks:
1132 continue
1132 continue
1133 from . import bookmarks # avoid import cycle
1133 from . import bookmarks # avoid import cycle
1134
1134
1135 repo.ui.debug(
1135 repo.ui.debug(
1136 b'moving bookmarks %r from %s to %s\n'
1136 b'moving bookmarks %r from %s to %s\n'
1137 % (
1137 % (
1138 pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1138 pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1139 hex(oldnode),
1139 hex(oldnode),
1140 hex(newnode),
1140 hex(newnode),
1141 )
1141 )
1142 )
1142 )
1143 # Delete divergent bookmarks being parents of related newnodes
1143 # Delete divergent bookmarks being parents of related newnodes
1144 deleterevs = repo.revs(
1144 deleterevs = repo.revs(
1145 b'parents(roots(%ln & (::%n))) - parents(%n)',
1145 b'parents(roots(%ln & (::%n))) - parents(%n)',
1146 allnewnodes,
1146 allnewnodes,
1147 newnode,
1147 newnode,
1148 oldnode,
1148 oldnode,
1149 )
1149 )
1150 deletenodes = _containsnode(repo, deleterevs)
1150 deletenodes = _containsnode(repo, deleterevs)
1151 for name in oldbmarks:
1151 for name in oldbmarks:
1152 bmarkchanges.append((name, newnode))
1152 bmarkchanges.append((name, newnode))
1153 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1153 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1154 bmarkchanges.append((b, None))
1154 bmarkchanges.append((b, None))
1155
1155
1156 if bmarkchanges:
1156 if bmarkchanges:
1157 bmarks.applychanges(repo, tr, bmarkchanges)
1157 bmarks.applychanges(repo, tr, bmarkchanges)
1158
1158
1159 for phase, nodes in toretract.items():
1159 for phase, nodes in toretract.items():
1160 phases.retractboundary(repo, tr, phase, nodes)
1160 phases.retractboundary(repo, tr, phase, nodes)
1161 for phase, nodes in toadvance.items():
1161 for phase, nodes in toadvance.items():
1162 phases.advanceboundary(repo, tr, phase, nodes)
1162 phases.advanceboundary(repo, tr, phase, nodes)
1163
1163
1164 mayusearchived = repo.ui.config(b'experimental', b'cleanup-as-archived')
1164 mayusearchived = repo.ui.config(b'experimental', b'cleanup-as-archived')
1165 # Obsolete or strip nodes
1165 # Obsolete or strip nodes
1166 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1166 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1167 # If a node is already obsoleted, and we want to obsolete it
1167 # If a node is already obsoleted, and we want to obsolete it
1168 # without a successor, skip that obssolete request since it's
1168 # without a successor, skip that obssolete request since it's
1169 # unnecessary. That's the "if s or not isobs(n)" check below.
1169 # unnecessary. That's the "if s or not isobs(n)" check below.
1170 # Also sort the node in topology order, that might be useful for
1170 # Also sort the node in topology order, that might be useful for
1171 # some obsstore logic.
1171 # some obsstore logic.
1172 # NOTE: the sorting might belong to createmarkers.
1172 # NOTE: the sorting might belong to createmarkers.
1173 torev = unfi.changelog.rev
1173 torev = unfi.changelog.rev
1174 sortfunc = lambda ns: torev(ns[0][0])
1174 sortfunc = lambda ns: torev(ns[0][0])
1175 rels = []
1175 rels = []
1176 for ns, s in sorted(replacements.items(), key=sortfunc):
1176 for ns, s in sorted(replacements.items(), key=sortfunc):
1177 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1177 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1178 rels.append(rel)
1178 rels.append(rel)
1179 if rels:
1179 if rels:
1180 obsolete.createmarkers(
1180 obsolete.createmarkers(
1181 repo, rels, operation=operation, metadata=metadata
1181 repo, rels, operation=operation, metadata=metadata
1182 )
1182 )
1183 elif phases.supportinternal(repo) and mayusearchived:
1183 elif phases.supportinternal(repo) and mayusearchived:
1184 # this assume we do not have "unstable" nodes above the cleaned ones
1184 # this assume we do not have "unstable" nodes above the cleaned ones
1185 allreplaced = set()
1185 allreplaced = set()
1186 for ns in replacements.keys():
1186 for ns in replacements.keys():
1187 allreplaced.update(ns)
1187 allreplaced.update(ns)
1188 if backup:
1188 if backup:
1189 from . import repair # avoid import cycle
1189 from . import repair # avoid import cycle
1190
1190
1191 node = min(allreplaced, key=repo.changelog.rev)
1191 node = min(allreplaced, key=repo.changelog.rev)
1192 repair.backupbundle(
1192 repair.backupbundle(
1193 repo, allreplaced, allreplaced, node, operation
1193 repo, allreplaced, allreplaced, node, operation
1194 )
1194 )
1195 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1195 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1196 else:
1196 else:
1197 from . import repair # avoid import cycle
1197 from . import repair # avoid import cycle
1198
1198
1199 tostrip = list(n for ns in replacements for n in ns)
1199 tostrip = list(n for ns in replacements for n in ns)
1200 if tostrip:
1200 if tostrip:
1201 repair.delayedstrip(
1201 repair.delayedstrip(
1202 repo.ui, repo, tostrip, operation, backup=backup
1202 repo.ui, repo, tostrip, operation, backup=backup
1203 )
1203 )
1204
1204
1205
1205
1206 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1206 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1207 if opts is None:
1207 if opts is None:
1208 opts = {}
1208 opts = {}
1209 m = matcher
1209 m = matcher
1210 dry_run = opts.get(b'dry_run')
1210 dry_run = opts.get(b'dry_run')
1211 try:
1211 try:
1212 similarity = float(opts.get(b'similarity') or 0)
1212 similarity = float(opts.get(b'similarity') or 0)
1213 except ValueError:
1213 except ValueError:
1214 raise error.Abort(_(b'similarity must be a number'))
1214 raise error.Abort(_(b'similarity must be a number'))
1215 if similarity < 0 or similarity > 100:
1215 if similarity < 0 or similarity > 100:
1216 raise error.Abort(_(b'similarity must be between 0 and 100'))
1216 raise error.Abort(_(b'similarity must be between 0 and 100'))
1217 similarity /= 100.0
1217 similarity /= 100.0
1218
1218
1219 ret = 0
1219 ret = 0
1220
1220
1221 wctx = repo[None]
1221 wctx = repo[None]
1222 for subpath in sorted(wctx.substate):
1222 for subpath in sorted(wctx.substate):
1223 submatch = matchmod.subdirmatcher(subpath, m)
1223 submatch = matchmod.subdirmatcher(subpath, m)
1224 if opts.get(b'subrepos') or m.exact(subpath) or any(submatch.files()):
1224 if opts.get(b'subrepos') or m.exact(subpath) or any(submatch.files()):
1225 sub = wctx.sub(subpath)
1225 sub = wctx.sub(subpath)
1226 subprefix = repo.wvfs.reljoin(prefix, subpath)
1226 subprefix = repo.wvfs.reljoin(prefix, subpath)
1227 subuipathfn = subdiruipathfn(subpath, uipathfn)
1227 subuipathfn = subdiruipathfn(subpath, uipathfn)
1228 try:
1228 try:
1229 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1229 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1230 ret = 1
1230 ret = 1
1231 except error.LookupError:
1231 except error.LookupError:
1232 repo.ui.status(
1232 repo.ui.status(
1233 _(b"skipping missing subrepository: %s\n")
1233 _(b"skipping missing subrepository: %s\n")
1234 % uipathfn(subpath)
1234 % uipathfn(subpath)
1235 )
1235 )
1236
1236
1237 rejected = []
1237 rejected = []
1238
1238
1239 def badfn(f, msg):
1239 def badfn(f, msg):
1240 if f in m.files():
1240 if f in m.files():
1241 m.bad(f, msg)
1241 m.bad(f, msg)
1242 rejected.append(f)
1242 rejected.append(f)
1243
1243
1244 badmatch = matchmod.badmatch(m, badfn)
1244 badmatch = matchmod.badmatch(m, badfn)
1245 added, unknown, deleted, removed, forgotten = _interestingfiles(
1245 added, unknown, deleted, removed, forgotten = _interestingfiles(
1246 repo, badmatch
1246 repo, badmatch
1247 )
1247 )
1248
1248
1249 unknownset = set(unknown + forgotten)
1249 unknownset = set(unknown + forgotten)
1250 toprint = unknownset.copy()
1250 toprint = unknownset.copy()
1251 toprint.update(deleted)
1251 toprint.update(deleted)
1252 for abs in sorted(toprint):
1252 for abs in sorted(toprint):
1253 if repo.ui.verbose or not m.exact(abs):
1253 if repo.ui.verbose or not m.exact(abs):
1254 if abs in unknownset:
1254 if abs in unknownset:
1255 status = _(b'adding %s\n') % uipathfn(abs)
1255 status = _(b'adding %s\n') % uipathfn(abs)
1256 label = b'ui.addremove.added'
1256 label = b'ui.addremove.added'
1257 else:
1257 else:
1258 status = _(b'removing %s\n') % uipathfn(abs)
1258 status = _(b'removing %s\n') % uipathfn(abs)
1259 label = b'ui.addremove.removed'
1259 label = b'ui.addremove.removed'
1260 repo.ui.status(status, label=label)
1260 repo.ui.status(status, label=label)
1261
1261
1262 renames = _findrenames(
1262 renames = _findrenames(
1263 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1263 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1264 )
1264 )
1265
1265
1266 if not dry_run:
1266 if not dry_run:
1267 _markchanges(repo, unknown + forgotten, deleted, renames)
1267 _markchanges(repo, unknown + forgotten, deleted, renames)
1268
1268
1269 for f in rejected:
1269 for f in rejected:
1270 if f in m.files():
1270 if f in m.files():
1271 return 1
1271 return 1
1272 return ret
1272 return ret
1273
1273
1274
1274
1275 def marktouched(repo, files, similarity=0.0):
1275 def marktouched(repo, files, similarity=0.0):
1276 """Assert that files have somehow been operated upon. files are relative to
1276 """Assert that files have somehow been operated upon. files are relative to
1277 the repo root."""
1277 the repo root."""
1278 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1278 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1279 rejected = []
1279 rejected = []
1280
1280
1281 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1281 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1282
1282
1283 if repo.ui.verbose:
1283 if repo.ui.verbose:
1284 unknownset = set(unknown + forgotten)
1284 unknownset = set(unknown + forgotten)
1285 toprint = unknownset.copy()
1285 toprint = unknownset.copy()
1286 toprint.update(deleted)
1286 toprint.update(deleted)
1287 for abs in sorted(toprint):
1287 for abs in sorted(toprint):
1288 if abs in unknownset:
1288 if abs in unknownset:
1289 status = _(b'adding %s\n') % abs
1289 status = _(b'adding %s\n') % abs
1290 else:
1290 else:
1291 status = _(b'removing %s\n') % abs
1291 status = _(b'removing %s\n') % abs
1292 repo.ui.status(status)
1292 repo.ui.status(status)
1293
1293
1294 # TODO: We should probably have the caller pass in uipathfn and apply it to
1294 # TODO: We should probably have the caller pass in uipathfn and apply it to
1295 # the messages above too. legacyrelativevalue=True is consistent with how
1295 # the messages above too. legacyrelativevalue=True is consistent with how
1296 # it used to work.
1296 # it used to work.
1297 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1297 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1298 renames = _findrenames(
1298 renames = _findrenames(
1299 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1299 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1300 )
1300 )
1301
1301
1302 _markchanges(repo, unknown + forgotten, deleted, renames)
1302 _markchanges(repo, unknown + forgotten, deleted, renames)
1303
1303
1304 for f in rejected:
1304 for f in rejected:
1305 if f in m.files():
1305 if f in m.files():
1306 return 1
1306 return 1
1307 return 0
1307 return 0
1308
1308
1309
1309
1310 def _interestingfiles(repo, matcher):
1310 def _interestingfiles(repo, matcher):
1311 """Walk dirstate with matcher, looking for files that addremove would care
1311 """Walk dirstate with matcher, looking for files that addremove would care
1312 about.
1312 about.
1313
1313
1314 This is different from dirstate.status because it doesn't care about
1314 This is different from dirstate.status because it doesn't care about
1315 whether files are modified or clean."""
1315 whether files are modified or clean."""
1316 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1316 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1317 audit_path = pathutil.pathauditor(repo.root, cached=True)
1317 audit_path = pathutil.pathauditor(repo.root, cached=True)
1318
1318
1319 ctx = repo[None]
1319 ctx = repo[None]
1320 dirstate = repo.dirstate
1320 dirstate = repo.dirstate
1321 matcher = repo.narrowmatch(matcher, includeexact=True)
1321 matcher = repo.narrowmatch(matcher, includeexact=True)
1322 walkresults = dirstate.walk(
1322 walkresults = dirstate.walk(
1323 matcher,
1323 matcher,
1324 subrepos=sorted(ctx.substate),
1324 subrepos=sorted(ctx.substate),
1325 unknown=True,
1325 unknown=True,
1326 ignored=False,
1326 ignored=False,
1327 full=False,
1327 full=False,
1328 )
1328 )
1329 for abs, st in pycompat.iteritems(walkresults):
1329 for abs, st in pycompat.iteritems(walkresults):
1330 dstate = dirstate[abs]
1330 dstate = dirstate[abs]
1331 if dstate == b'?' and audit_path.check(abs):
1331 if dstate == b'?' and audit_path.check(abs):
1332 unknown.append(abs)
1332 unknown.append(abs)
1333 elif dstate != b'r' and not st:
1333 elif dstate != b'r' and not st:
1334 deleted.append(abs)
1334 deleted.append(abs)
1335 elif dstate == b'r' and st:
1335 elif dstate == b'r' and st:
1336 forgotten.append(abs)
1336 forgotten.append(abs)
1337 # for finding renames
1337 # for finding renames
1338 elif dstate == b'r' and not st:
1338 elif dstate == b'r' and not st:
1339 removed.append(abs)
1339 removed.append(abs)
1340 elif dstate == b'a':
1340 elif dstate == b'a':
1341 added.append(abs)
1341 added.append(abs)
1342
1342
1343 return added, unknown, deleted, removed, forgotten
1343 return added, unknown, deleted, removed, forgotten
1344
1344
1345
1345
1346 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1346 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1347 '''Find renames from removed files to added ones.'''
1347 '''Find renames from removed files to added ones.'''
1348 renames = {}
1348 renames = {}
1349 if similarity > 0:
1349 if similarity > 0:
1350 for old, new, score in similar.findrenames(
1350 for old, new, score in similar.findrenames(
1351 repo, added, removed, similarity
1351 repo, added, removed, similarity
1352 ):
1352 ):
1353 if (
1353 if (
1354 repo.ui.verbose
1354 repo.ui.verbose
1355 or not matcher.exact(old)
1355 or not matcher.exact(old)
1356 or not matcher.exact(new)
1356 or not matcher.exact(new)
1357 ):
1357 ):
1358 repo.ui.status(
1358 repo.ui.status(
1359 _(
1359 _(
1360 b'recording removal of %s as rename to %s '
1360 b'recording removal of %s as rename to %s '
1361 b'(%d%% similar)\n'
1361 b'(%d%% similar)\n'
1362 )
1362 )
1363 % (uipathfn(old), uipathfn(new), score * 100)
1363 % (uipathfn(old), uipathfn(new), score * 100)
1364 )
1364 )
1365 renames[new] = old
1365 renames[new] = old
1366 return renames
1366 return renames
1367
1367
1368
1368
1369 def _markchanges(repo, unknown, deleted, renames):
1369 def _markchanges(repo, unknown, deleted, renames):
1370 """Marks the files in unknown as added, the files in deleted as removed,
1370 """Marks the files in unknown as added, the files in deleted as removed,
1371 and the files in renames as copied."""
1371 and the files in renames as copied."""
1372 wctx = repo[None]
1372 wctx = repo[None]
1373 with repo.wlock():
1373 with repo.wlock():
1374 wctx.forget(deleted)
1374 wctx.forget(deleted)
1375 wctx.add(unknown)
1375 wctx.add(unknown)
1376 for new, old in pycompat.iteritems(renames):
1376 for new, old in pycompat.iteritems(renames):
1377 wctx.copy(old, new)
1377 wctx.copy(old, new)
1378
1378
1379
1379
1380 def getrenamedfn(repo, endrev=None):
1380 def getrenamedfn(repo, endrev=None):
1381 if copiesmod.usechangesetcentricalgo(repo):
1381 if copiesmod.usechangesetcentricalgo(repo):
1382
1382
1383 def getrenamed(fn, rev):
1383 def getrenamed(fn, rev):
1384 ctx = repo[rev]
1384 ctx = repo[rev]
1385 p1copies = ctx.p1copies()
1385 p1copies = ctx.p1copies()
1386 if fn in p1copies:
1386 if fn in p1copies:
1387 return p1copies[fn]
1387 return p1copies[fn]
1388 p2copies = ctx.p2copies()
1388 p2copies = ctx.p2copies()
1389 if fn in p2copies:
1389 if fn in p2copies:
1390 return p2copies[fn]
1390 return p2copies[fn]
1391 return None
1391 return None
1392
1392
1393 return getrenamed
1393 return getrenamed
1394
1394
1395 rcache = {}
1395 rcache = {}
1396 if endrev is None:
1396 if endrev is None:
1397 endrev = len(repo)
1397 endrev = len(repo)
1398
1398
1399 def getrenamed(fn, rev):
1399 def getrenamed(fn, rev):
1400 """looks up all renames for a file (up to endrev) the first
1400 """looks up all renames for a file (up to endrev) the first
1401 time the file is given. It indexes on the changerev and only
1401 time the file is given. It indexes on the changerev and only
1402 parses the manifest if linkrev != changerev.
1402 parses the manifest if linkrev != changerev.
1403 Returns rename info for fn at changerev rev."""
1403 Returns rename info for fn at changerev rev."""
1404 if fn not in rcache:
1404 if fn not in rcache:
1405 rcache[fn] = {}
1405 rcache[fn] = {}
1406 fl = repo.file(fn)
1406 fl = repo.file(fn)
1407 for i in fl:
1407 for i in fl:
1408 lr = fl.linkrev(i)
1408 lr = fl.linkrev(i)
1409 renamed = fl.renamed(fl.node(i))
1409 renamed = fl.renamed(fl.node(i))
1410 rcache[fn][lr] = renamed and renamed[0]
1410 rcache[fn][lr] = renamed and renamed[0]
1411 if lr >= endrev:
1411 if lr >= endrev:
1412 break
1412 break
1413 if rev in rcache[fn]:
1413 if rev in rcache[fn]:
1414 return rcache[fn][rev]
1414 return rcache[fn][rev]
1415
1415
1416 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1416 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1417 # filectx logic.
1417 # filectx logic.
1418 try:
1418 try:
1419 return repo[rev][fn].copysource()
1419 return repo[rev][fn].copysource()
1420 except error.LookupError:
1420 except error.LookupError:
1421 return None
1421 return None
1422
1422
1423 return getrenamed
1423 return getrenamed
1424
1424
1425
1425
1426 def getcopiesfn(repo, endrev=None):
1426 def getcopiesfn(repo, endrev=None):
1427 if copiesmod.usechangesetcentricalgo(repo):
1427 if copiesmod.usechangesetcentricalgo(repo):
1428
1428
1429 def copiesfn(ctx):
1429 def copiesfn(ctx):
1430 if ctx.p2copies():
1430 if ctx.p2copies():
1431 allcopies = ctx.p1copies().copy()
1431 allcopies = ctx.p1copies().copy()
1432 # There should be no overlap
1432 # There should be no overlap
1433 allcopies.update(ctx.p2copies())
1433 allcopies.update(ctx.p2copies())
1434 return sorted(allcopies.items())
1434 return sorted(allcopies.items())
1435 else:
1435 else:
1436 return sorted(ctx.p1copies().items())
1436 return sorted(ctx.p1copies().items())
1437
1437
1438 else:
1438 else:
1439 getrenamed = getrenamedfn(repo, endrev)
1439 getrenamed = getrenamedfn(repo, endrev)
1440
1440
1441 def copiesfn(ctx):
1441 def copiesfn(ctx):
1442 copies = []
1442 copies = []
1443 for fn in ctx.files():
1443 for fn in ctx.files():
1444 rename = getrenamed(fn, ctx.rev())
1444 rename = getrenamed(fn, ctx.rev())
1445 if rename:
1445 if rename:
1446 copies.append((fn, rename))
1446 copies.append((fn, rename))
1447 return copies
1447 return copies
1448
1448
1449 return copiesfn
1449 return copiesfn
1450
1450
1451
1451
1452 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1452 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1453 """Update the dirstate to reflect the intent of copying src to dst. For
1453 """Update the dirstate to reflect the intent of copying src to dst. For
1454 different reasons it might not end with dst being marked as copied from src.
1454 different reasons it might not end with dst being marked as copied from src.
1455 """
1455 """
1456 origsrc = repo.dirstate.copied(src) or src
1456 origsrc = repo.dirstate.copied(src) or src
1457 if dst == origsrc: # copying back a copy?
1457 if dst == origsrc: # copying back a copy?
1458 if repo.dirstate[dst] not in b'mn' and not dryrun:
1458 if repo.dirstate[dst] not in b'mn' and not dryrun:
1459 repo.dirstate.set_tracked(dst)
1459 repo.dirstate.set_tracked(dst)
1460 else:
1460 else:
1461 if repo.dirstate[origsrc] == b'a' and origsrc == src:
1461 if repo.dirstate[origsrc] == b'a' and origsrc == src:
1462 if not ui.quiet:
1462 if not ui.quiet:
1463 ui.warn(
1463 ui.warn(
1464 _(
1464 _(
1465 b"%s has not been committed yet, so no copy "
1465 b"%s has not been committed yet, so no copy "
1466 b"data will be stored for %s.\n"
1466 b"data will be stored for %s.\n"
1467 )
1467 )
1468 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd))
1468 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd))
1469 )
1469 )
1470 if repo.dirstate[dst] in b'?r' and not dryrun:
1470 if repo.dirstate[dst] in b'?r' and not dryrun:
1471 wctx.add([dst])
1471 wctx.add([dst])
1472 elif not dryrun:
1472 elif not dryrun:
1473 wctx.copy(origsrc, dst)
1473 wctx.copy(origsrc, dst)
1474
1474
1475
1475
1476 def movedirstate(repo, newctx, match=None):
1476 def movedirstate(repo, newctx, match=None):
1477 """Move the dirstate to newctx and adjust it as necessary.
1477 """Move the dirstate to newctx and adjust it as necessary.
1478
1478
1479 A matcher can be provided as an optimization. It is probably a bug to pass
1479 A matcher can be provided as an optimization. It is probably a bug to pass
1480 a matcher that doesn't match all the differences between the parent of the
1480 a matcher that doesn't match all the differences between the parent of the
1481 working copy and newctx.
1481 working copy and newctx.
1482 """
1482 """
1483 oldctx = repo[b'.']
1483 oldctx = repo[b'.']
1484 ds = repo.dirstate
1484 ds = repo.dirstate
1485 copies = dict(ds.copies())
1485 copies = dict(ds.copies())
1486 ds.setparents(newctx.node(), repo.nullid)
1486 ds.setparents(newctx.node(), repo.nullid)
1487 s = newctx.status(oldctx, match=match)
1487 s = newctx.status(oldctx, match=match)
1488
1488
1489 for f in s.modified:
1489 for f in s.modified:
1490 ds.update_file_p1(f, p1_tracked=True)
1490 ds.update_file_p1(f, p1_tracked=True)
1491
1491
1492 for f in s.added:
1492 for f in s.added:
1493 ds.update_file_p1(f, p1_tracked=False)
1493 ds.update_file_p1(f, p1_tracked=False)
1494
1494
1495 for f in s.removed:
1495 for f in s.removed:
1496 ds.update_file_p1(f, p1_tracked=True)
1496 ds.update_file_p1(f, p1_tracked=True)
1497
1497
1498 # Merge old parent and old working dir copies
1498 # Merge old parent and old working dir copies
1499 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1499 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1500 oldcopies.update(copies)
1500 oldcopies.update(copies)
1501 copies = {
1501 copies = {
1502 dst: oldcopies.get(src, src)
1502 dst: oldcopies.get(src, src)
1503 for dst, src in pycompat.iteritems(oldcopies)
1503 for dst, src in pycompat.iteritems(oldcopies)
1504 }
1504 }
1505 # Adjust the dirstate copies
1505 # Adjust the dirstate copies
1506 for dst, src in pycompat.iteritems(copies):
1506 for dst, src in pycompat.iteritems(copies):
1507 if src not in newctx or dst in newctx or ds[dst] != b'a':
1507 if src not in newctx or dst in newctx or ds[dst] != b'a':
1508 src = None
1508 src = None
1509 ds.copy(src, dst)
1509 ds.copy(src, dst)
1510 repo._quick_access_changeid_invalidate()
1510 repo._quick_access_changeid_invalidate()
1511
1511
1512
1512
1513 def filterrequirements(requirements):
1513 def filterrequirements(requirements):
1514 """filters the requirements into two sets:
1514 """filters the requirements into two sets:
1515
1515
1516 wcreq: requirements which should be written in .hg/requires
1516 wcreq: requirements which should be written in .hg/requires
1517 storereq: which should be written in .hg/store/requires
1517 storereq: which should be written in .hg/store/requires
1518
1518
1519 Returns (wcreq, storereq)
1519 Returns (wcreq, storereq)
1520 """
1520 """
1521 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
1521 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
1522 wc, store = set(), set()
1522 wc, store = set(), set()
1523 for r in requirements:
1523 for r in requirements:
1524 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
1524 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
1525 wc.add(r)
1525 wc.add(r)
1526 else:
1526 else:
1527 store.add(r)
1527 store.add(r)
1528 return wc, store
1528 return wc, store
1529 return requirements, None
1529 return requirements, None
1530
1530
1531
1531
1532 def istreemanifest(repo):
1532 def istreemanifest(repo):
1533 """returns whether the repository is using treemanifest or not"""
1533 """returns whether the repository is using treemanifest or not"""
1534 return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements
1534 return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements
1535
1535
1536
1536
1537 def writereporequirements(repo, requirements=None):
1537 def writereporequirements(repo, requirements=None):
1538 """writes requirements for the repo
1538 """writes requirements for the repo
1539
1539
1540 Requirements are written to .hg/requires and .hg/store/requires based
1540 Requirements are written to .hg/requires and .hg/store/requires based
1541 on whether share-safe mode is enabled and which requirements are wdir
1541 on whether share-safe mode is enabled and which requirements are wdir
1542 requirements and which are store requirements
1542 requirements and which are store requirements
1543 """
1543 """
1544 if requirements:
1544 if requirements:
1545 repo.requirements = requirements
1545 repo.requirements = requirements
1546 wcreq, storereq = filterrequirements(repo.requirements)
1546 wcreq, storereq = filterrequirements(repo.requirements)
1547 if wcreq is not None:
1547 if wcreq is not None:
1548 writerequires(repo.vfs, wcreq)
1548 writerequires(repo.vfs, wcreq)
1549 if storereq is not None:
1549 if storereq is not None:
1550 writerequires(repo.svfs, storereq)
1550 writerequires(repo.svfs, storereq)
1551 elif repo.ui.configbool(b'format', b'usestore'):
1551 elif repo.ui.configbool(b'format', b'usestore'):
1552 # only remove store requires if we are using store
1552 # only remove store requires if we are using store
1553 repo.svfs.tryunlink(b'requires')
1553 repo.svfs.tryunlink(b'requires')
1554
1554
1555
1555
1556 def writerequires(opener, requirements):
1556 def writerequires(opener, requirements):
1557 with opener(b'requires', b'w', atomictemp=True) as fp:
1557 with opener(b'requires', b'w', atomictemp=True) as fp:
1558 for r in sorted(requirements):
1558 for r in sorted(requirements):
1559 fp.write(b"%s\n" % r)
1559 fp.write(b"%s\n" % r)
1560
1560
1561
1561
1562 class filecachesubentry(object):
1562 class filecachesubentry(object):
1563 def __init__(self, path, stat):
1563 def __init__(self, path, stat):
1564 self.path = path
1564 self.path = path
1565 self.cachestat = None
1565 self.cachestat = None
1566 self._cacheable = None
1566 self._cacheable = None
1567
1567
1568 if stat:
1568 if stat:
1569 self.cachestat = filecachesubentry.stat(self.path)
1569 self.cachestat = filecachesubentry.stat(self.path)
1570
1570
1571 if self.cachestat:
1571 if self.cachestat:
1572 self._cacheable = self.cachestat.cacheable()
1572 self._cacheable = self.cachestat.cacheable()
1573 else:
1573 else:
1574 # None means we don't know yet
1574 # None means we don't know yet
1575 self._cacheable = None
1575 self._cacheable = None
1576
1576
1577 def refresh(self):
1577 def refresh(self):
1578 if self.cacheable():
1578 if self.cacheable():
1579 self.cachestat = filecachesubentry.stat(self.path)
1579 self.cachestat = filecachesubentry.stat(self.path)
1580
1580
1581 def cacheable(self):
1581 def cacheable(self):
1582 if self._cacheable is not None:
1582 if self._cacheable is not None:
1583 return self._cacheable
1583 return self._cacheable
1584
1584
1585 # we don't know yet, assume it is for now
1585 # we don't know yet, assume it is for now
1586 return True
1586 return True
1587
1587
1588 def changed(self):
1588 def changed(self):
1589 # no point in going further if we can't cache it
1589 # no point in going further if we can't cache it
1590 if not self.cacheable():
1590 if not self.cacheable():
1591 return True
1591 return True
1592
1592
1593 newstat = filecachesubentry.stat(self.path)
1593 newstat = filecachesubentry.stat(self.path)
1594
1594
1595 # we may not know if it's cacheable yet, check again now
1595 # we may not know if it's cacheable yet, check again now
1596 if newstat and self._cacheable is None:
1596 if newstat and self._cacheable is None:
1597 self._cacheable = newstat.cacheable()
1597 self._cacheable = newstat.cacheable()
1598
1598
1599 # check again
1599 # check again
1600 if not self._cacheable:
1600 if not self._cacheable:
1601 return True
1601 return True
1602
1602
1603 if self.cachestat != newstat:
1603 if self.cachestat != newstat:
1604 self.cachestat = newstat
1604 self.cachestat = newstat
1605 return True
1605 return True
1606 else:
1606 else:
1607 return False
1607 return False
1608
1608
1609 @staticmethod
1609 @staticmethod
1610 def stat(path):
1610 def stat(path):
1611 try:
1611 try:
1612 return util.cachestat(path)
1612 return util.cachestat(path)
1613 except OSError as e:
1613 except OSError as e:
1614 if e.errno != errno.ENOENT:
1614 if e.errno != errno.ENOENT:
1615 raise
1615 raise
1616
1616
1617
1617
1618 class filecacheentry(object):
1618 class filecacheentry(object):
1619 def __init__(self, paths, stat=True):
1619 def __init__(self, paths, stat=True):
1620 self._entries = []
1620 self._entries = []
1621 for path in paths:
1621 for path in paths:
1622 self._entries.append(filecachesubentry(path, stat))
1622 self._entries.append(filecachesubentry(path, stat))
1623
1623
1624 def changed(self):
1624 def changed(self):
1625 '''true if any entry has changed'''
1625 '''true if any entry has changed'''
1626 for entry in self._entries:
1626 for entry in self._entries:
1627 if entry.changed():
1627 if entry.changed():
1628 return True
1628 return True
1629 return False
1629 return False
1630
1630
1631 def refresh(self):
1631 def refresh(self):
1632 for entry in self._entries:
1632 for entry in self._entries:
1633 entry.refresh()
1633 entry.refresh()
1634
1634
1635
1635
1636 class filecache(object):
1636 class filecache(object):
1637 """A property like decorator that tracks files under .hg/ for updates.
1637 """A property like decorator that tracks files under .hg/ for updates.
1638
1638
1639 On first access, the files defined as arguments are stat()ed and the
1639 On first access, the files defined as arguments are stat()ed and the
1640 results cached. The decorated function is called. The results are stashed
1640 results cached. The decorated function is called. The results are stashed
1641 away in a ``_filecache`` dict on the object whose method is decorated.
1641 away in a ``_filecache`` dict on the object whose method is decorated.
1642
1642
1643 On subsequent access, the cached result is used as it is set to the
1643 On subsequent access, the cached result is used as it is set to the
1644 instance dictionary.
1644 instance dictionary.
1645
1645
1646 On external property set/delete operations, the caller must update the
1646 On external property set/delete operations, the caller must update the
1647 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1647 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1648 instead of directly setting <attr>.
1648 instead of directly setting <attr>.
1649
1649
1650 When using the property API, the cached data is always used if available.
1650 When using the property API, the cached data is always used if available.
1651 No stat() is performed to check if the file has changed.
1651 No stat() is performed to check if the file has changed.
1652
1652
1653 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1653 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1654 can populate an entry before the property's getter is called. In this case,
1654 can populate an entry before the property's getter is called. In this case,
1655 entries in ``_filecache`` will be used during property operations,
1655 entries in ``_filecache`` will be used during property operations,
1656 if available. If the underlying file changes, it is up to external callers
1656 if available. If the underlying file changes, it is up to external callers
1657 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1657 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1658 method result as well as possibly calling ``del obj._filecache[attr]`` to
1658 method result as well as possibly calling ``del obj._filecache[attr]`` to
1659 remove the ``filecacheentry``.
1659 remove the ``filecacheentry``.
1660 """
1660 """
1661
1661
1662 def __init__(self, *paths):
1662 def __init__(self, *paths):
1663 self.paths = paths
1663 self.paths = paths
1664
1664
1665 def join(self, obj, fname):
1665 def join(self, obj, fname):
1666 """Used to compute the runtime path of a cached file.
1666 """Used to compute the runtime path of a cached file.
1667
1667
1668 Users should subclass filecache and provide their own version of this
1668 Users should subclass filecache and provide their own version of this
1669 function to call the appropriate join function on 'obj' (an instance
1669 function to call the appropriate join function on 'obj' (an instance
1670 of the class that its member function was decorated).
1670 of the class that its member function was decorated).
1671 """
1671 """
1672 raise NotImplementedError
1672 raise NotImplementedError
1673
1673
1674 def __call__(self, func):
1674 def __call__(self, func):
1675 self.func = func
1675 self.func = func
1676 self.sname = func.__name__
1676 self.sname = func.__name__
1677 self.name = pycompat.sysbytes(self.sname)
1677 self.name = pycompat.sysbytes(self.sname)
1678 return self
1678 return self
1679
1679
1680 def __get__(self, obj, type=None):
1680 def __get__(self, obj, type=None):
1681 # if accessed on the class, return the descriptor itself.
1681 # if accessed on the class, return the descriptor itself.
1682 if obj is None:
1682 if obj is None:
1683 return self
1683 return self
1684
1684
1685 assert self.sname not in obj.__dict__
1685 assert self.sname not in obj.__dict__
1686
1686
1687 entry = obj._filecache.get(self.name)
1687 entry = obj._filecache.get(self.name)
1688
1688
1689 if entry:
1689 if entry:
1690 if entry.changed():
1690 if entry.changed():
1691 entry.obj = self.func(obj)
1691 entry.obj = self.func(obj)
1692 else:
1692 else:
1693 paths = [self.join(obj, path) for path in self.paths]
1693 paths = [self.join(obj, path) for path in self.paths]
1694
1694
1695 # We stat -before- creating the object so our cache doesn't lie if
1695 # We stat -before- creating the object so our cache doesn't lie if
1696 # a writer modified between the time we read and stat
1696 # a writer modified between the time we read and stat
1697 entry = filecacheentry(paths, True)
1697 entry = filecacheentry(paths, True)
1698 entry.obj = self.func(obj)
1698 entry.obj = self.func(obj)
1699
1699
1700 obj._filecache[self.name] = entry
1700 obj._filecache[self.name] = entry
1701
1701
1702 obj.__dict__[self.sname] = entry.obj
1702 obj.__dict__[self.sname] = entry.obj
1703 return entry.obj
1703 return entry.obj
1704
1704
1705 # don't implement __set__(), which would make __dict__ lookup as slow as
1705 # don't implement __set__(), which would make __dict__ lookup as slow as
1706 # function call.
1706 # function call.
1707
1707
1708 def set(self, obj, value):
1708 def set(self, obj, value):
1709 if self.name not in obj._filecache:
1709 if self.name not in obj._filecache:
1710 # we add an entry for the missing value because X in __dict__
1710 # we add an entry for the missing value because X in __dict__
1711 # implies X in _filecache
1711 # implies X in _filecache
1712 paths = [self.join(obj, path) for path in self.paths]
1712 paths = [self.join(obj, path) for path in self.paths]
1713 ce = filecacheentry(paths, False)
1713 ce = filecacheentry(paths, False)
1714 obj._filecache[self.name] = ce
1714 obj._filecache[self.name] = ce
1715 else:
1715 else:
1716 ce = obj._filecache[self.name]
1716 ce = obj._filecache[self.name]
1717
1717
1718 ce.obj = value # update cached copy
1718 ce.obj = value # update cached copy
1719 obj.__dict__[self.sname] = value # update copy returned by obj.x
1719 obj.__dict__[self.sname] = value # update copy returned by obj.x
1720
1720
1721
1721
1722 def extdatasource(repo, source):
1722 def extdatasource(repo, source):
1723 """Gather a map of rev -> value dict from the specified source
1723 """Gather a map of rev -> value dict from the specified source
1724
1724
1725 A source spec is treated as a URL, with a special case shell: type
1725 A source spec is treated as a URL, with a special case shell: type
1726 for parsing the output from a shell command.
1726 for parsing the output from a shell command.
1727
1727
1728 The data is parsed as a series of newline-separated records where
1728 The data is parsed as a series of newline-separated records where
1729 each record is a revision specifier optionally followed by a space
1729 each record is a revision specifier optionally followed by a space
1730 and a freeform string value. If the revision is known locally, it
1730 and a freeform string value. If the revision is known locally, it
1731 is converted to a rev, otherwise the record is skipped.
1731 is converted to a rev, otherwise the record is skipped.
1732
1732
1733 Note that both key and value are treated as UTF-8 and converted to
1733 Note that both key and value are treated as UTF-8 and converted to
1734 the local encoding. This allows uniformity between local and
1734 the local encoding. This allows uniformity between local and
1735 remote data sources.
1735 remote data sources.
1736 """
1736 """
1737
1737
1738 spec = repo.ui.config(b"extdata", source)
1738 spec = repo.ui.config(b"extdata", source)
1739 if not spec:
1739 if not spec:
1740 raise error.Abort(_(b"unknown extdata source '%s'") % source)
1740 raise error.Abort(_(b"unknown extdata source '%s'") % source)
1741
1741
1742 data = {}
1742 data = {}
1743 src = proc = None
1743 src = proc = None
1744 try:
1744 try:
1745 if spec.startswith(b"shell:"):
1745 if spec.startswith(b"shell:"):
1746 # external commands should be run relative to the repo root
1746 # external commands should be run relative to the repo root
1747 cmd = spec[6:]
1747 cmd = spec[6:]
1748 proc = subprocess.Popen(
1748 proc = subprocess.Popen(
1749 procutil.tonativestr(cmd),
1749 procutil.tonativestr(cmd),
1750 shell=True,
1750 shell=True,
1751 bufsize=-1,
1751 bufsize=-1,
1752 close_fds=procutil.closefds,
1752 close_fds=procutil.closefds,
1753 stdout=subprocess.PIPE,
1753 stdout=subprocess.PIPE,
1754 cwd=procutil.tonativestr(repo.root),
1754 cwd=procutil.tonativestr(repo.root),
1755 )
1755 )
1756 src = proc.stdout
1756 src = proc.stdout
1757 else:
1757 else:
1758 # treat as a URL or file
1758 # treat as a URL or file
1759 src = url.open(repo.ui, spec)
1759 src = url.open(repo.ui, spec)
1760 for l in src:
1760 for l in src:
1761 if b" " in l:
1761 if b" " in l:
1762 k, v = l.strip().split(b" ", 1)
1762 k, v = l.strip().split(b" ", 1)
1763 else:
1763 else:
1764 k, v = l.strip(), b""
1764 k, v = l.strip(), b""
1765
1765
1766 k = encoding.tolocal(k)
1766 k = encoding.tolocal(k)
1767 try:
1767 try:
1768 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1768 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1769 except (error.LookupError, error.RepoLookupError, error.InputError):
1769 except (error.LookupError, error.RepoLookupError, error.InputError):
1770 pass # we ignore data for nodes that don't exist locally
1770 pass # we ignore data for nodes that don't exist locally
1771 finally:
1771 finally:
1772 if proc:
1772 if proc:
1773 try:
1773 try:
1774 proc.communicate()
1774 proc.communicate()
1775 except ValueError:
1775 except ValueError:
1776 # This happens if we started iterating src and then
1776 # This happens if we started iterating src and then
1777 # get a parse error on a line. It should be safe to ignore.
1777 # get a parse error on a line. It should be safe to ignore.
1778 pass
1778 pass
1779 if src:
1779 if src:
1780 src.close()
1780 src.close()
1781 if proc and proc.returncode != 0:
1781 if proc and proc.returncode != 0:
1782 raise error.Abort(
1782 raise error.Abort(
1783 _(b"extdata command '%s' failed: %s")
1783 _(b"extdata command '%s' failed: %s")
1784 % (cmd, procutil.explainexit(proc.returncode))
1784 % (cmd, procutil.explainexit(proc.returncode))
1785 )
1785 )
1786
1786
1787 return data
1787 return data
1788
1788
1789
1789
1790 class progress(object):
1790 class progress(object):
1791 def __init__(self, ui, updatebar, topic, unit=b"", total=None):
1791 def __init__(self, ui, updatebar, topic, unit=b"", total=None):
1792 self.ui = ui
1792 self.ui = ui
1793 self.pos = 0
1793 self.pos = 0
1794 self.topic = topic
1794 self.topic = topic
1795 self.unit = unit
1795 self.unit = unit
1796 self.total = total
1796 self.total = total
1797 self.debug = ui.configbool(b'progress', b'debug')
1797 self.debug = ui.configbool(b'progress', b'debug')
1798 self._updatebar = updatebar
1798 self._updatebar = updatebar
1799
1799
1800 def __enter__(self):
1800 def __enter__(self):
1801 return self
1801 return self
1802
1802
1803 def __exit__(self, exc_type, exc_value, exc_tb):
1803 def __exit__(self, exc_type, exc_value, exc_tb):
1804 self.complete()
1804 self.complete()
1805
1805
1806 def update(self, pos, item=b"", total=None):
1806 def update(self, pos, item=b"", total=None):
1807 assert pos is not None
1807 assert pos is not None
1808 if total:
1808 if total:
1809 self.total = total
1809 self.total = total
1810 self.pos = pos
1810 self.pos = pos
1811 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1811 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1812 if self.debug:
1812 if self.debug:
1813 self._printdebug(item)
1813 self._printdebug(item)
1814
1814
1815 def increment(self, step=1, item=b"", total=None):
1815 def increment(self, step=1, item=b"", total=None):
1816 self.update(self.pos + step, item, total)
1816 self.update(self.pos + step, item, total)
1817
1817
1818 def complete(self):
1818 def complete(self):
1819 self.pos = None
1819 self.pos = None
1820 self.unit = b""
1820 self.unit = b""
1821 self.total = None
1821 self.total = None
1822 self._updatebar(self.topic, self.pos, b"", self.unit, self.total)
1822 self._updatebar(self.topic, self.pos, b"", self.unit, self.total)
1823
1823
1824 def _printdebug(self, item):
1824 def _printdebug(self, item):
1825 unit = b''
1825 unit = b''
1826 if self.unit:
1826 if self.unit:
1827 unit = b' ' + self.unit
1827 unit = b' ' + self.unit
1828 if item:
1828 if item:
1829 item = b' ' + item
1829 item = b' ' + item
1830
1830
1831 if self.total:
1831 if self.total:
1832 pct = 100.0 * self.pos / self.total
1832 pct = 100.0 * self.pos / self.total
1833 self.ui.debug(
1833 self.ui.debug(
1834 b'%s:%s %d/%d%s (%4.2f%%)\n'
1834 b'%s:%s %d/%d%s (%4.2f%%)\n'
1835 % (self.topic, item, self.pos, self.total, unit, pct)
1835 % (self.topic, item, self.pos, self.total, unit, pct)
1836 )
1836 )
1837 else:
1837 else:
1838 self.ui.debug(b'%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1838 self.ui.debug(b'%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1839
1839
1840
1840
1841 def gdinitconfig(ui):
1841 def gdinitconfig(ui):
1842 """helper function to know if a repo should be created as general delta"""
1842 """helper function to know if a repo should be created as general delta"""
1843 # experimental config: format.generaldelta
1843 # experimental config: format.generaldelta
1844 return ui.configbool(b'format', b'generaldelta') or ui.configbool(
1844 return ui.configbool(b'format', b'generaldelta') or ui.configbool(
1845 b'format', b'usegeneraldelta'
1845 b'format', b'usegeneraldelta'
1846 )
1846 )
1847
1847
1848
1848
1849 def gddeltaconfig(ui):
1849 def gddeltaconfig(ui):
1850 """helper function to know if incoming delta should be optimised"""
1850 """helper function to know if incoming delta should be optimised"""
1851 # experimental config: format.generaldelta
1851 # experimental config: format.generaldelta
1852 return ui.configbool(b'format', b'generaldelta')
1852 return ui.configbool(b'format', b'generaldelta')
1853
1853
1854
1854
1855 class simplekeyvaluefile(object):
1855 class simplekeyvaluefile(object):
1856 """A simple file with key=value lines
1856 """A simple file with key=value lines
1857
1857
1858 Keys must be alphanumerics and start with a letter, values must not
1858 Keys must be alphanumerics and start with a letter, values must not
1859 contain '\n' characters"""
1859 contain '\n' characters"""
1860
1860
1861 firstlinekey = b'__firstline'
1861 firstlinekey = b'__firstline'
1862
1862
1863 def __init__(self, vfs, path, keys=None):
1863 def __init__(self, vfs, path, keys=None):
1864 self.vfs = vfs
1864 self.vfs = vfs
1865 self.path = path
1865 self.path = path
1866
1866
1867 def read(self, firstlinenonkeyval=False):
1867 def read(self, firstlinenonkeyval=False):
1868 """Read the contents of a simple key-value file
1868 """Read the contents of a simple key-value file
1869
1869
1870 'firstlinenonkeyval' indicates whether the first line of file should
1870 'firstlinenonkeyval' indicates whether the first line of file should
1871 be treated as a key-value pair or reuturned fully under the
1871 be treated as a key-value pair or reuturned fully under the
1872 __firstline key."""
1872 __firstline key."""
1873 lines = self.vfs.readlines(self.path)
1873 lines = self.vfs.readlines(self.path)
1874 d = {}
1874 d = {}
1875 if firstlinenonkeyval:
1875 if firstlinenonkeyval:
1876 if not lines:
1876 if not lines:
1877 e = _(b"empty simplekeyvalue file")
1877 e = _(b"empty simplekeyvalue file")
1878 raise error.CorruptedState(e)
1878 raise error.CorruptedState(e)
1879 # we don't want to include '\n' in the __firstline
1879 # we don't want to include '\n' in the __firstline
1880 d[self.firstlinekey] = lines[0][:-1]
1880 d[self.firstlinekey] = lines[0][:-1]
1881 del lines[0]
1881 del lines[0]
1882
1882
1883 try:
1883 try:
1884 # the 'if line.strip()' part prevents us from failing on empty
1884 # the 'if line.strip()' part prevents us from failing on empty
1885 # lines which only contain '\n' therefore are not skipped
1885 # lines which only contain '\n' therefore are not skipped
1886 # by 'if line'
1886 # by 'if line'
1887 updatedict = dict(
1887 updatedict = dict(
1888 line[:-1].split(b'=', 1) for line in lines if line.strip()
1888 line[:-1].split(b'=', 1) for line in lines if line.strip()
1889 )
1889 )
1890 if self.firstlinekey in updatedict:
1890 if self.firstlinekey in updatedict:
1891 e = _(b"%r can't be used as a key")
1891 e = _(b"%r can't be used as a key")
1892 raise error.CorruptedState(e % self.firstlinekey)
1892 raise error.CorruptedState(e % self.firstlinekey)
1893 d.update(updatedict)
1893 d.update(updatedict)
1894 except ValueError as e:
1894 except ValueError as e:
1895 raise error.CorruptedState(stringutil.forcebytestr(e))
1895 raise error.CorruptedState(stringutil.forcebytestr(e))
1896 return d
1896 return d
1897
1897
1898 def write(self, data, firstline=None):
1898 def write(self, data, firstline=None):
1899 """Write key=>value mapping to a file
1899 """Write key=>value mapping to a file
1900 data is a dict. Keys must be alphanumerical and start with a letter.
1900 data is a dict. Keys must be alphanumerical and start with a letter.
1901 Values must not contain newline characters.
1901 Values must not contain newline characters.
1902
1902
1903 If 'firstline' is not None, it is written to file before
1903 If 'firstline' is not None, it is written to file before
1904 everything else, as it is, not in a key=value form"""
1904 everything else, as it is, not in a key=value form"""
1905 lines = []
1905 lines = []
1906 if firstline is not None:
1906 if firstline is not None:
1907 lines.append(b'%s\n' % firstline)
1907 lines.append(b'%s\n' % firstline)
1908
1908
1909 for k, v in data.items():
1909 for k, v in data.items():
1910 if k == self.firstlinekey:
1910 if k == self.firstlinekey:
1911 e = b"key name '%s' is reserved" % self.firstlinekey
1911 e = b"key name '%s' is reserved" % self.firstlinekey
1912 raise error.ProgrammingError(e)
1912 raise error.ProgrammingError(e)
1913 if not k[0:1].isalpha():
1913 if not k[0:1].isalpha():
1914 e = b"keys must start with a letter in a key-value file"
1914 e = b"keys must start with a letter in a key-value file"
1915 raise error.ProgrammingError(e)
1915 raise error.ProgrammingError(e)
1916 if not k.isalnum():
1916 if not k.isalnum():
1917 e = b"invalid key name in a simple key-value file"
1917 e = b"invalid key name in a simple key-value file"
1918 raise error.ProgrammingError(e)
1918 raise error.ProgrammingError(e)
1919 if b'\n' in v:
1919 if b'\n' in v:
1920 e = b"invalid value in a simple key-value file"
1920 e = b"invalid value in a simple key-value file"
1921 raise error.ProgrammingError(e)
1921 raise error.ProgrammingError(e)
1922 lines.append(b"%s=%s\n" % (k, v))
1922 lines.append(b"%s=%s\n" % (k, v))
1923 with self.vfs(self.path, mode=b'wb', atomictemp=True) as fp:
1923 with self.vfs(self.path, mode=b'wb', atomictemp=True) as fp:
1924 fp.write(b''.join(lines))
1924 fp.write(b''.join(lines))
1925
1925
1926
1926
1927 _reportobsoletedsource = [
1927 _reportobsoletedsource = [
1928 b'debugobsolete',
1928 b'debugobsolete',
1929 b'pull',
1929 b'pull',
1930 b'push',
1930 b'push',
1931 b'serve',
1931 b'serve',
1932 b'unbundle',
1932 b'unbundle',
1933 ]
1933 ]
1934
1934
1935 _reportnewcssource = [
1935 _reportnewcssource = [
1936 b'pull',
1936 b'pull',
1937 b'unbundle',
1937 b'unbundle',
1938 ]
1938 ]
1939
1939
1940
1940
1941 def prefetchfiles(repo, revmatches):
1941 def prefetchfiles(repo, revmatches):
1942 """Invokes the registered file prefetch functions, allowing extensions to
1942 """Invokes the registered file prefetch functions, allowing extensions to
1943 ensure the corresponding files are available locally, before the command
1943 ensure the corresponding files are available locally, before the command
1944 uses them.
1944 uses them.
1945
1945
1946 Args:
1946 Args:
1947 revmatches: a list of (revision, match) tuples to indicate the files to
1947 revmatches: a list of (revision, match) tuples to indicate the files to
1948 fetch at each revision. If any of the match elements is None, it matches
1948 fetch at each revision. If any of the match elements is None, it matches
1949 all files.
1949 all files.
1950 """
1950 """
1951
1951
1952 def _matcher(m):
1952 def _matcher(m):
1953 if m:
1953 if m:
1954 assert isinstance(m, matchmod.basematcher)
1954 assert isinstance(m, matchmod.basematcher)
1955 # The command itself will complain about files that don't exist, so
1955 # The command itself will complain about files that don't exist, so
1956 # don't duplicate the message.
1956 # don't duplicate the message.
1957 return matchmod.badmatch(m, lambda fn, msg: None)
1957 return matchmod.badmatch(m, lambda fn, msg: None)
1958 else:
1958 else:
1959 return matchall(repo)
1959 return matchall(repo)
1960
1960
1961 revbadmatches = [(rev, _matcher(match)) for (rev, match) in revmatches]
1961 revbadmatches = [(rev, _matcher(match)) for (rev, match) in revmatches]
1962
1962
1963 fileprefetchhooks(repo, revbadmatches)
1963 fileprefetchhooks(repo, revbadmatches)
1964
1964
1965
1965
1966 # a list of (repo, revs, match) prefetch functions
1966 # a list of (repo, revs, match) prefetch functions
1967 fileprefetchhooks = util.hooks()
1967 fileprefetchhooks = util.hooks()
1968
1968
1969 # A marker that tells the evolve extension to suppress its own reporting
1969 # A marker that tells the evolve extension to suppress its own reporting
1970 _reportstroubledchangesets = True
1970 _reportstroubledchangesets = True
1971
1971
1972
1972
1973 def registersummarycallback(repo, otr, txnname=b'', as_validator=False):
1973 def registersummarycallback(repo, otr, txnname=b'', as_validator=False):
1974 """register a callback to issue a summary after the transaction is closed
1974 """register a callback to issue a summary after the transaction is closed
1975
1975
1976 If as_validator is true, then the callbacks are registered as transaction
1976 If as_validator is true, then the callbacks are registered as transaction
1977 validators instead
1977 validators instead
1978 """
1978 """
1979
1979
1980 def txmatch(sources):
1980 def txmatch(sources):
1981 return any(txnname.startswith(source) for source in sources)
1981 return any(txnname.startswith(source) for source in sources)
1982
1982
1983 categories = []
1983 categories = []
1984
1984
1985 def reportsummary(func):
1985 def reportsummary(func):
1986 """decorator for report callbacks."""
1986 """decorator for report callbacks."""
1987 # The repoview life cycle is shorter than the one of the actual
1987 # The repoview life cycle is shorter than the one of the actual
1988 # underlying repository. So the filtered object can die before the
1988 # underlying repository. So the filtered object can die before the
1989 # weakref is used leading to troubles. We keep a reference to the
1989 # weakref is used leading to troubles. We keep a reference to the
1990 # unfiltered object and restore the filtering when retrieving the
1990 # unfiltered object and restore the filtering when retrieving the
1991 # repository through the weakref.
1991 # repository through the weakref.
1992 filtername = repo.filtername
1992 filtername = repo.filtername
1993 reporef = weakref.ref(repo.unfiltered())
1993 reporef = weakref.ref(repo.unfiltered())
1994
1994
1995 def wrapped(tr):
1995 def wrapped(tr):
1996 repo = reporef()
1996 repo = reporef()
1997 if filtername:
1997 if filtername:
1998 assert repo is not None # help pytype
1998 assert repo is not None # help pytype
1999 repo = repo.filtered(filtername)
1999 repo = repo.filtered(filtername)
2000 func(repo, tr)
2000 func(repo, tr)
2001
2001
2002 newcat = b'%02i-txnreport' % len(categories)
2002 newcat = b'%02i-txnreport' % len(categories)
2003 if as_validator:
2003 if as_validator:
2004 otr.addvalidator(newcat, wrapped)
2004 otr.addvalidator(newcat, wrapped)
2005 else:
2005 else:
2006 otr.addpostclose(newcat, wrapped)
2006 otr.addpostclose(newcat, wrapped)
2007 categories.append(newcat)
2007 categories.append(newcat)
2008 return wrapped
2008 return wrapped
2009
2009
2010 @reportsummary
2010 @reportsummary
2011 def reportchangegroup(repo, tr):
2011 def reportchangegroup(repo, tr):
2012 cgchangesets = tr.changes.get(b'changegroup-count-changesets', 0)
2012 cgchangesets = tr.changes.get(b'changegroup-count-changesets', 0)
2013 cgrevisions = tr.changes.get(b'changegroup-count-revisions', 0)
2013 cgrevisions = tr.changes.get(b'changegroup-count-revisions', 0)
2014 cgfiles = tr.changes.get(b'changegroup-count-files', 0)
2014 cgfiles = tr.changes.get(b'changegroup-count-files', 0)
2015 cgheads = tr.changes.get(b'changegroup-count-heads', 0)
2015 cgheads = tr.changes.get(b'changegroup-count-heads', 0)
2016 if cgchangesets or cgrevisions or cgfiles:
2016 if cgchangesets or cgrevisions or cgfiles:
2017 htext = b""
2017 htext = b""
2018 if cgheads:
2018 if cgheads:
2019 htext = _(b" (%+d heads)") % cgheads
2019 htext = _(b" (%+d heads)") % cgheads
2020 msg = _(b"added %d changesets with %d changes to %d files%s\n")
2020 msg = _(b"added %d changesets with %d changes to %d files%s\n")
2021 if as_validator:
2021 if as_validator:
2022 msg = _(b"adding %d changesets with %d changes to %d files%s\n")
2022 msg = _(b"adding %d changesets with %d changes to %d files%s\n")
2023 assert repo is not None # help pytype
2023 assert repo is not None # help pytype
2024 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
2024 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
2025
2025
2026 if txmatch(_reportobsoletedsource):
2026 if txmatch(_reportobsoletedsource):
2027
2027
2028 @reportsummary
2028 @reportsummary
2029 def reportobsoleted(repo, tr):
2029 def reportobsoleted(repo, tr):
2030 obsoleted = obsutil.getobsoleted(repo, tr)
2030 obsoleted = obsutil.getobsoleted(repo, tr)
2031 newmarkers = len(tr.changes.get(b'obsmarkers', ()))
2031 newmarkers = len(tr.changes.get(b'obsmarkers', ()))
2032 if newmarkers:
2032 if newmarkers:
2033 repo.ui.status(_(b'%i new obsolescence markers\n') % newmarkers)
2033 repo.ui.status(_(b'%i new obsolescence markers\n') % newmarkers)
2034 if obsoleted:
2034 if obsoleted:
2035 msg = _(b'obsoleted %i changesets\n')
2035 msg = _(b'obsoleted %i changesets\n')
2036 if as_validator:
2036 if as_validator:
2037 msg = _(b'obsoleting %i changesets\n')
2037 msg = _(b'obsoleting %i changesets\n')
2038 repo.ui.status(msg % len(obsoleted))
2038 repo.ui.status(msg % len(obsoleted))
2039
2039
2040 if obsolete.isenabled(
2040 if obsolete.isenabled(
2041 repo, obsolete.createmarkersopt
2041 repo, obsolete.createmarkersopt
2042 ) and repo.ui.configbool(
2042 ) and repo.ui.configbool(
2043 b'experimental', b'evolution.report-instabilities'
2043 b'experimental', b'evolution.report-instabilities'
2044 ):
2044 ):
2045 instabilitytypes = [
2045 instabilitytypes = [
2046 (b'orphan', b'orphan'),
2046 (b'orphan', b'orphan'),
2047 (b'phase-divergent', b'phasedivergent'),
2047 (b'phase-divergent', b'phasedivergent'),
2048 (b'content-divergent', b'contentdivergent'),
2048 (b'content-divergent', b'contentdivergent'),
2049 ]
2049 ]
2050
2050
2051 def getinstabilitycounts(repo):
2051 def getinstabilitycounts(repo):
2052 filtered = repo.changelog.filteredrevs
2052 filtered = repo.changelog.filteredrevs
2053 counts = {}
2053 counts = {}
2054 for instability, revset in instabilitytypes:
2054 for instability, revset in instabilitytypes:
2055 counts[instability] = len(
2055 counts[instability] = len(
2056 set(obsolete.getrevs(repo, revset)) - filtered
2056 set(obsolete.getrevs(repo, revset)) - filtered
2057 )
2057 )
2058 return counts
2058 return counts
2059
2059
2060 oldinstabilitycounts = getinstabilitycounts(repo)
2060 oldinstabilitycounts = getinstabilitycounts(repo)
2061
2061
2062 @reportsummary
2062 @reportsummary
2063 def reportnewinstabilities(repo, tr):
2063 def reportnewinstabilities(repo, tr):
2064 newinstabilitycounts = getinstabilitycounts(repo)
2064 newinstabilitycounts = getinstabilitycounts(repo)
2065 for instability, revset in instabilitytypes:
2065 for instability, revset in instabilitytypes:
2066 delta = (
2066 delta = (
2067 newinstabilitycounts[instability]
2067 newinstabilitycounts[instability]
2068 - oldinstabilitycounts[instability]
2068 - oldinstabilitycounts[instability]
2069 )
2069 )
2070 msg = getinstabilitymessage(delta, instability)
2070 msg = getinstabilitymessage(delta, instability)
2071 if msg:
2071 if msg:
2072 repo.ui.warn(msg)
2072 repo.ui.warn(msg)
2073
2073
2074 if txmatch(_reportnewcssource):
2074 if txmatch(_reportnewcssource):
2075
2075
2076 @reportsummary
2076 @reportsummary
2077 def reportnewcs(repo, tr):
2077 def reportnewcs(repo, tr):
2078 """Report the range of new revisions pulled/unbundled."""
2078 """Report the range of new revisions pulled/unbundled."""
2079 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2079 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2080 unfi = repo.unfiltered()
2080 unfi = repo.unfiltered()
2081 if origrepolen >= len(unfi):
2081 if origrepolen >= len(unfi):
2082 return
2082 return
2083
2083
2084 # Compute the bounds of new visible revisions' range.
2084 # Compute the bounds of new visible revisions' range.
2085 revs = smartset.spanset(repo, start=origrepolen)
2085 revs = smartset.spanset(repo, start=origrepolen)
2086 if revs:
2086 if revs:
2087 minrev, maxrev = repo[revs.min()], repo[revs.max()]
2087 minrev, maxrev = repo[revs.min()], repo[revs.max()]
2088
2088
2089 if minrev == maxrev:
2089 if minrev == maxrev:
2090 revrange = minrev
2090 revrange = minrev
2091 else:
2091 else:
2092 revrange = b'%s:%s' % (minrev, maxrev)
2092 revrange = b'%s:%s' % (minrev, maxrev)
2093 draft = len(repo.revs(b'%ld and draft()', revs))
2093 draft = len(repo.revs(b'%ld and draft()', revs))
2094 secret = len(repo.revs(b'%ld and secret()', revs))
2094 secret = len(repo.revs(b'%ld and secret()', revs))
2095 if not (draft or secret):
2095 if not (draft or secret):
2096 msg = _(b'new changesets %s\n') % revrange
2096 msg = _(b'new changesets %s\n') % revrange
2097 elif draft and secret:
2097 elif draft and secret:
2098 msg = _(b'new changesets %s (%d drafts, %d secrets)\n')
2098 msg = _(b'new changesets %s (%d drafts, %d secrets)\n')
2099 msg %= (revrange, draft, secret)
2099 msg %= (revrange, draft, secret)
2100 elif draft:
2100 elif draft:
2101 msg = _(b'new changesets %s (%d drafts)\n')
2101 msg = _(b'new changesets %s (%d drafts)\n')
2102 msg %= (revrange, draft)
2102 msg %= (revrange, draft)
2103 elif secret:
2103 elif secret:
2104 msg = _(b'new changesets %s (%d secrets)\n')
2104 msg = _(b'new changesets %s (%d secrets)\n')
2105 msg %= (revrange, secret)
2105 msg %= (revrange, secret)
2106 else:
2106 else:
2107 errormsg = b'entered unreachable condition'
2107 errormsg = b'entered unreachable condition'
2108 raise error.ProgrammingError(errormsg)
2108 raise error.ProgrammingError(errormsg)
2109 repo.ui.status(msg)
2109 repo.ui.status(msg)
2110
2110
2111 # search new changesets directly pulled as obsolete
2111 # search new changesets directly pulled as obsolete
2112 duplicates = tr.changes.get(b'revduplicates', ())
2112 duplicates = tr.changes.get(b'revduplicates', ())
2113 obsadded = unfi.revs(
2113 obsadded = unfi.revs(
2114 b'(%d: + %ld) and obsolete()', origrepolen, duplicates
2114 b'(%d: + %ld) and obsolete()', origrepolen, duplicates
2115 )
2115 )
2116 cl = repo.changelog
2116 cl = repo.changelog
2117 extinctadded = [r for r in obsadded if r not in cl]
2117 extinctadded = [r for r in obsadded if r not in cl]
2118 if extinctadded:
2118 if extinctadded:
2119 # They are not just obsolete, but obsolete and invisible
2119 # They are not just obsolete, but obsolete and invisible
2120 # we call them "extinct" internally but the terms have not been
2120 # we call them "extinct" internally but the terms have not been
2121 # exposed to users.
2121 # exposed to users.
2122 msg = b'(%d other changesets obsolete on arrival)\n'
2122 msg = b'(%d other changesets obsolete on arrival)\n'
2123 repo.ui.status(msg % len(extinctadded))
2123 repo.ui.status(msg % len(extinctadded))
2124
2124
2125 @reportsummary
2125 @reportsummary
2126 def reportphasechanges(repo, tr):
2126 def reportphasechanges(repo, tr):
2127 """Report statistics of phase changes for changesets pre-existing
2127 """Report statistics of phase changes for changesets pre-existing
2128 pull/unbundle.
2128 pull/unbundle.
2129 """
2129 """
2130 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2130 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2131 published = []
2131 published = []
2132 for revs, (old, new) in tr.changes.get(b'phases', []):
2132 for revs, (old, new) in tr.changes.get(b'phases', []):
2133 if new != phases.public:
2133 if new != phases.public:
2134 continue
2134 continue
2135 published.extend(rev for rev in revs if rev < origrepolen)
2135 published.extend(rev for rev in revs if rev < origrepolen)
2136 if not published:
2136 if not published:
2137 return
2137 return
2138 msg = _(b'%d local changesets published\n')
2138 msg = _(b'%d local changesets published\n')
2139 if as_validator:
2139 if as_validator:
2140 msg = _(b'%d local changesets will be published\n')
2140 msg = _(b'%d local changesets will be published\n')
2141 repo.ui.status(msg % len(published))
2141 repo.ui.status(msg % len(published))
2142
2142
2143
2143
2144 def getinstabilitymessage(delta, instability):
2144 def getinstabilitymessage(delta, instability):
2145 """function to return the message to show warning about new instabilities
2145 """function to return the message to show warning about new instabilities
2146
2146
2147 exists as a separate function so that extension can wrap to show more
2147 exists as a separate function so that extension can wrap to show more
2148 information like how to fix instabilities"""
2148 information like how to fix instabilities"""
2149 if delta > 0:
2149 if delta > 0:
2150 return _(b'%i new %s changesets\n') % (delta, instability)
2150 return _(b'%i new %s changesets\n') % (delta, instability)
2151
2151
2152
2152
2153 def nodesummaries(repo, nodes, maxnumnodes=4):
2153 def nodesummaries(repo, nodes, maxnumnodes=4):
2154 if len(nodes) <= maxnumnodes or repo.ui.verbose:
2154 if len(nodes) <= maxnumnodes or repo.ui.verbose:
2155 return b' '.join(short(h) for h in nodes)
2155 return b' '.join(short(h) for h in nodes)
2156 first = b' '.join(short(h) for h in nodes[:maxnumnodes])
2156 first = b' '.join(short(h) for h in nodes[:maxnumnodes])
2157 return _(b"%s and %d others") % (first, len(nodes) - maxnumnodes)
2157 return _(b"%s and %d others") % (first, len(nodes) - maxnumnodes)
2158
2158
2159
2159
2160 def enforcesinglehead(repo, tr, desc, accountclosed, filtername):
2160 def enforcesinglehead(repo, tr, desc, accountclosed, filtername):
2161 """check that no named branch has multiple heads"""
2161 """check that no named branch has multiple heads"""
2162 if desc in (b'strip', b'repair'):
2162 if desc in (b'strip', b'repair'):
2163 # skip the logic during strip
2163 # skip the logic during strip
2164 return
2164 return
2165 visible = repo.filtered(filtername)
2165 visible = repo.filtered(filtername)
2166 # possible improvement: we could restrict the check to affected branch
2166 # possible improvement: we could restrict the check to affected branch
2167 bm = visible.branchmap()
2167 bm = visible.branchmap()
2168 for name in bm:
2168 for name in bm:
2169 heads = bm.branchheads(name, closed=accountclosed)
2169 heads = bm.branchheads(name, closed=accountclosed)
2170 if len(heads) > 1:
2170 if len(heads) > 1:
2171 msg = _(b'rejecting multiple heads on branch "%s"')
2171 msg = _(b'rejecting multiple heads on branch "%s"')
2172 msg %= name
2172 msg %= name
2173 hint = _(b'%d heads: %s')
2173 hint = _(b'%d heads: %s')
2174 hint %= (len(heads), nodesummaries(repo, heads))
2174 hint %= (len(heads), nodesummaries(repo, heads))
2175 raise error.Abort(msg, hint=hint)
2175 raise error.Abort(msg, hint=hint)
2176
2176
2177
2177
2178 def wrapconvertsink(sink):
2178 def wrapconvertsink(sink):
2179 """Allow extensions to wrap the sink returned by convcmd.convertsink()
2179 """Allow extensions to wrap the sink returned by convcmd.convertsink()
2180 before it is used, whether or not the convert extension was formally loaded.
2180 before it is used, whether or not the convert extension was formally loaded.
2181 """
2181 """
2182 return sink
2182 return sink
2183
2183
2184
2184
2185 def unhidehashlikerevs(repo, specs, hiddentype):
2185 def unhidehashlikerevs(repo, specs, hiddentype):
2186 """parse the user specs and unhide changesets whose hash or revision number
2186 """parse the user specs and unhide changesets whose hash or revision number
2187 is passed.
2187 is passed.
2188
2188
2189 hiddentype can be: 1) 'warn': warn while unhiding changesets
2189 hiddentype can be: 1) 'warn': warn while unhiding changesets
2190 2) 'nowarn': don't warn while unhiding changesets
2190 2) 'nowarn': don't warn while unhiding changesets
2191
2191
2192 returns a repo object with the required changesets unhidden
2192 returns a repo object with the required changesets unhidden
2193 """
2193 """
2194 if not repo.filtername or not repo.ui.configbool(
2194 if not repo.filtername or not repo.ui.configbool(
2195 b'experimental', b'directaccess'
2195 b'experimental', b'directaccess'
2196 ):
2196 ):
2197 return repo
2197 return repo
2198
2198
2199 if repo.filtername not in (b'visible', b'visible-hidden'):
2199 if repo.filtername not in (b'visible', b'visible-hidden'):
2200 return repo
2200 return repo
2201
2201
2202 symbols = set()
2202 symbols = set()
2203 for spec in specs:
2203 for spec in specs:
2204 try:
2204 try:
2205 tree = revsetlang.parse(spec)
2205 tree = revsetlang.parse(spec)
2206 except error.ParseError: # will be reported by scmutil.revrange()
2206 except error.ParseError: # will be reported by scmutil.revrange()
2207 continue
2207 continue
2208
2208
2209 symbols.update(revsetlang.gethashlikesymbols(tree))
2209 symbols.update(revsetlang.gethashlikesymbols(tree))
2210
2210
2211 if not symbols:
2211 if not symbols:
2212 return repo
2212 return repo
2213
2213
2214 revs = _getrevsfromsymbols(repo, symbols)
2214 revs = _getrevsfromsymbols(repo, symbols)
2215
2215
2216 if not revs:
2216 if not revs:
2217 return repo
2217 return repo
2218
2218
2219 if hiddentype == b'warn':
2219 if hiddentype == b'warn':
2220 unfi = repo.unfiltered()
2220 unfi = repo.unfiltered()
2221 revstr = b", ".join([pycompat.bytestr(unfi[l]) for l in revs])
2221 revstr = b", ".join([pycompat.bytestr(unfi[l]) for l in revs])
2222 repo.ui.warn(
2222 repo.ui.warn(
2223 _(
2223 _(
2224 b"warning: accessing hidden changesets for write "
2224 b"warning: accessing hidden changesets for write "
2225 b"operation: %s\n"
2225 b"operation: %s\n"
2226 )
2226 )
2227 % revstr
2227 % revstr
2228 )
2228 )
2229
2229
2230 # we have to use new filtername to separate branch/tags cache until we can
2230 # we have to use new filtername to separate branch/tags cache until we can
2231 # disbale these cache when revisions are dynamically pinned.
2231 # disbale these cache when revisions are dynamically pinned.
2232 return repo.filtered(b'visible-hidden', revs)
2232 return repo.filtered(b'visible-hidden', revs)
2233
2233
2234
2234
2235 def _getrevsfromsymbols(repo, symbols):
2235 def _getrevsfromsymbols(repo, symbols):
2236 """parse the list of symbols and returns a set of revision numbers of hidden
2236 """parse the list of symbols and returns a set of revision numbers of hidden
2237 changesets present in symbols"""
2237 changesets present in symbols"""
2238 revs = set()
2238 revs = set()
2239 unfi = repo.unfiltered()
2239 unfi = repo.unfiltered()
2240 unficl = unfi.changelog
2240 unficl = unfi.changelog
2241 cl = repo.changelog
2241 cl = repo.changelog
2242 tiprev = len(unficl)
2242 tiprev = len(unficl)
2243 allowrevnums = repo.ui.configbool(b'experimental', b'directaccess.revnums')
2243 allowrevnums = repo.ui.configbool(b'experimental', b'directaccess.revnums')
2244 for s in symbols:
2244 for s in symbols:
2245 try:
2245 try:
2246 n = int(s)
2246 n = int(s)
2247 if n <= tiprev:
2247 if n <= tiprev:
2248 if not allowrevnums:
2248 if not allowrevnums:
2249 continue
2249 continue
2250 else:
2250 else:
2251 if n not in cl:
2251 if n not in cl:
2252 revs.add(n)
2252 revs.add(n)
2253 continue
2253 continue
2254 except ValueError:
2254 except ValueError:
2255 pass
2255 pass
2256
2256
2257 try:
2257 try:
2258 s = resolvehexnodeidprefix(unfi, s)
2258 s = resolvehexnodeidprefix(unfi, s)
2259 except (error.LookupError, error.WdirUnsupported):
2259 except (error.LookupError, error.WdirUnsupported):
2260 s = None
2260 s = None
2261
2261
2262 if s is not None:
2262 if s is not None:
2263 rev = unficl.rev(s)
2263 rev = unficl.rev(s)
2264 if rev not in cl:
2264 if rev not in cl:
2265 revs.add(rev)
2265 revs.add(rev)
2266
2266
2267 return revs
2267 return revs
2268
2268
2269
2269
2270 def bookmarkrevs(repo, mark):
2270 def bookmarkrevs(repo, mark):
2271 """Select revisions reachable by a given bookmark
2271 """Select revisions reachable by a given bookmark
2272
2272
2273 If the bookmarked revision isn't a head, an empty set will be returned.
2273 If the bookmarked revision isn't a head, an empty set will be returned.
2274 """
2274 """
2275 return repo.revs(format_bookmark_revspec(mark))
2275 return repo.revs(format_bookmark_revspec(mark))
2276
2276
2277
2277
2278 def format_bookmark_revspec(mark):
2278 def format_bookmark_revspec(mark):
2279 """Build a revset expression to select revisions reachable by a given
2279 """Build a revset expression to select revisions reachable by a given
2280 bookmark"""
2280 bookmark"""
2281 mark = b'literal:' + mark
2281 mark = b'literal:' + mark
2282 return revsetlang.formatspec(
2282 return revsetlang.formatspec(
2283 b"ancestors(bookmark(%s)) - "
2283 b"ancestors(bookmark(%s)) - "
2284 b"ancestors(head() and not bookmark(%s)) - "
2284 b"ancestors(head() and not bookmark(%s)) - "
2285 b"ancestors(bookmark() and not bookmark(%s))",
2285 b"ancestors(bookmark() and not bookmark(%s))",
2286 mark,
2286 mark,
2287 mark,
2287 mark,
2288 mark,
2288 mark,
2289 )
2289 )
@@ -1,298 +1,298 b''
1 Testing diff --change, --from, --to
1 Testing diff --change, --from, --to
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5
5
6 $ echo "first" > file.txt
6 $ echo "first" > file.txt
7 $ hg add file.txt
7 $ hg add file.txt
8 $ hg commit -m 'first commit' # 0
8 $ hg commit -m 'first commit' # 0
9
9
10 $ echo "second" > file.txt
10 $ echo "second" > file.txt
11 $ hg commit -m 'second commit' # 1
11 $ hg commit -m 'second commit' # 1
12
12
13 $ echo "third" > file.txt
13 $ echo "third" > file.txt
14 $ hg commit -m 'third commit' # 2
14 $ hg commit -m 'third commit' # 2
15
15
16 $ hg diff --nodates --change 1
16 $ hg diff --nodates --change 1
17 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
17 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
18 --- a/file.txt
18 --- a/file.txt
19 +++ b/file.txt
19 +++ b/file.txt
20 @@ -1,1 +1,1 @@
20 @@ -1,1 +1,1 @@
21 -first
21 -first
22 +second
22 +second
23
23
24 $ hg diff --change e9b286083166
24 $ hg diff --change e9b286083166
25 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
25 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
26 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
26 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
27 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
27 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
28 @@ -1,1 +1,1 @@
28 @@ -1,1 +1,1 @@
29 -first
29 -first
30 +second
30 +second
31
31
32 Test --from and --to
32 Test --from and --to
33
33
34 $ hg diff --from . --rev .
34 $ hg diff --from . --rev .
35 abort: cannot specify both --from and --rev
35 abort: cannot specify both --from and --rev
36 [10]
36 [10]
37 $ hg diff --to . --rev .
37 $ hg diff --to . --rev .
38 abort: cannot specify both --to and --rev
38 abort: cannot specify both --to and --rev
39 [10]
39 [10]
40 $ hg diff --from . --change .
40 $ hg diff --from . --change .
41 abort: cannot specify both --from and --change
41 abort: cannot specify both --from and --change
42 [10]
42 [10]
43 $ hg diff --to . --change .
43 $ hg diff --to . --change .
44 abort: cannot specify both --to and --change
44 abort: cannot specify both --to and --change
45 [10]
45 [10]
46 $ echo dirty > file.txt
46 $ echo dirty > file.txt
47 $ hg diff --from .
47 $ hg diff --from .
48 diff -r bf5ff72eb7e0 file.txt
48 diff -r bf5ff72eb7e0 file.txt
49 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
49 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
50 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
50 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
51 @@ -1,1 +1,1 @@
51 @@ -1,1 +1,1 @@
52 -third
52 -third
53 +dirty
53 +dirty
54 $ hg diff --from . --reverse
54 $ hg diff --from . --reverse
55 diff -r bf5ff72eb7e0 file.txt
55 diff -r bf5ff72eb7e0 file.txt
56 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
56 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
57 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
57 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
58 @@ -1,1 +1,1 @@
58 @@ -1,1 +1,1 @@
59 -dirty
59 -dirty
60 +third
60 +third
61 $ hg diff --to .
61 $ hg diff --to .
62 diff -r bf5ff72eb7e0 file.txt
62 diff -r bf5ff72eb7e0 file.txt
63 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
63 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
64 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
64 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
65 @@ -1,1 +1,1 @@
65 @@ -1,1 +1,1 @@
66 -dirty
66 -dirty
67 +third
67 +third
68 $ hg diff --from 0 --to 2
68 $ hg diff --from 0 --to 2
69 diff -r 4bb65dda5db4 -r bf5ff72eb7e0 file.txt
69 diff -r 4bb65dda5db4 -r bf5ff72eb7e0 file.txt
70 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
70 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
71 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
71 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
72 @@ -1,1 +1,1 @@
72 @@ -1,1 +1,1 @@
73 -first
73 -first
74 +third
74 +third
75 $ hg diff --from 2 --to 0
75 $ hg diff --from 2 --to 0
76 diff -r bf5ff72eb7e0 -r 4bb65dda5db4 file.txt
76 diff -r bf5ff72eb7e0 -r 4bb65dda5db4 file.txt
77 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
77 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
78 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
78 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
79 @@ -1,1 +1,1 @@
79 @@ -1,1 +1,1 @@
80 -third
80 -third
81 +first
81 +first
82 $ hg co -C .
82 $ hg co -C .
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84
84
85 $ cd ..
85 $ cd ..
86
86
87 Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled
87 Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled
88 as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774)
88 as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774)
89
89
90 $ hg clone -q a dumbspec
90 $ hg clone -q a dumbspec
91 $ cd dumbspec
91 $ cd dumbspec
92 $ echo "wdir" > file.txt
92 $ echo "wdir" > file.txt
93
93
94 $ hg diff -r 2:2
94 $ hg diff -r 2:2
95 $ hg diff -r 2:.
95 $ hg diff -r 2:.
96 $ hg diff -r 2:
96 $ hg diff -r 2:
97 $ hg diff -r :0
97 $ hg diff -r :0
98 $ hg diff -r '2:first(2:2)'
98 $ hg diff -r '2:first(2:2)'
99 $ hg diff -r 'first(2:2)' --nodates
99 $ hg diff -r 'first(2:2)' --nodates
100 diff -r bf5ff72eb7e0 file.txt
100 diff -r bf5ff72eb7e0 file.txt
101 --- a/file.txt
101 --- a/file.txt
102 +++ b/file.txt
102 +++ b/file.txt
103 @@ -1,1 +1,1 @@
103 @@ -1,1 +1,1 @@
104 -third
104 -third
105 +wdir
105 +wdir
106 $ hg diff -r '(2:2)' --nodates
106 $ hg diff -r '(2:2)' --nodates
107 diff -r bf5ff72eb7e0 file.txt
107 diff -r bf5ff72eb7e0 file.txt
108 --- a/file.txt
108 --- a/file.txt
109 +++ b/file.txt
109 +++ b/file.txt
110 @@ -1,1 +1,1 @@
110 @@ -1,1 +1,1 @@
111 -third
111 -third
112 +wdir
112 +wdir
113 $ hg diff -r 2::2 --nodates
113 $ hg diff -r 2::2 --nodates
114 diff -r bf5ff72eb7e0 file.txt
114 diff -r bf5ff72eb7e0 file.txt
115 --- a/file.txt
115 --- a/file.txt
116 +++ b/file.txt
116 +++ b/file.txt
117 @@ -1,1 +1,1 @@
117 @@ -1,1 +1,1 @@
118 -third
118 -third
119 +wdir
119 +wdir
120 $ hg diff -r "2 and 1"
120 $ hg diff -r "2 and 1"
121 abort: empty revision range
121 abort: empty revision range
122 [255]
122 [10]
123
123
124 $ cd ..
124 $ cd ..
125
125
126 $ hg clone -qr0 a dumbspec-rev0
126 $ hg clone -qr0 a dumbspec-rev0
127 $ cd dumbspec-rev0
127 $ cd dumbspec-rev0
128 $ echo "wdir" > file.txt
128 $ echo "wdir" > file.txt
129
129
130 $ hg diff -r :
130 $ hg diff -r :
131 $ hg diff -r 'first(:)' --nodates
131 $ hg diff -r 'first(:)' --nodates
132 diff -r 4bb65dda5db4 file.txt
132 diff -r 4bb65dda5db4 file.txt
133 --- a/file.txt
133 --- a/file.txt
134 +++ b/file.txt
134 +++ b/file.txt
135 @@ -1,1 +1,1 @@
135 @@ -1,1 +1,1 @@
136 -first
136 -first
137 +wdir
137 +wdir
138
138
139 $ cd ..
139 $ cd ..
140
140
141 Testing diff --change when merge:
141 Testing diff --change when merge:
142
142
143 $ cd a
143 $ cd a
144
144
145 $ for i in 1 2 3 4 5 6 7 8 9 10; do
145 $ for i in 1 2 3 4 5 6 7 8 9 10; do
146 > echo $i >> file.txt
146 > echo $i >> file.txt
147 > done
147 > done
148 $ hg commit -m "lots of text" # 3
148 $ hg commit -m "lots of text" # 3
149
149
150 $ sed -e 's,^2$,x,' file.txt > file.txt.tmp
150 $ sed -e 's,^2$,x,' file.txt > file.txt.tmp
151 $ mv file.txt.tmp file.txt
151 $ mv file.txt.tmp file.txt
152 $ hg commit -m "change 2 to x" # 4
152 $ hg commit -m "change 2 to x" # 4
153
153
154 $ hg up -r 3
154 $ hg up -r 3
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
156 $ sed -e 's,^8$,y,' file.txt > file.txt.tmp
156 $ sed -e 's,^8$,y,' file.txt > file.txt.tmp
157 $ mv file.txt.tmp file.txt
157 $ mv file.txt.tmp file.txt
158 $ hg commit -m "change 8 to y"
158 $ hg commit -m "change 8 to y"
159 created new head
159 created new head
160
160
161 $ hg up -C -r 4
161 $ hg up -C -r 4
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 $ hg merge -r 5
163 $ hg merge -r 5
164 merging file.txt
164 merging file.txt
165 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
165 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
166 (branch merge, don't forget to commit)
166 (branch merge, don't forget to commit)
167 $ hg commit -m "merge 8 to y" # 6
167 $ hg commit -m "merge 8 to y" # 6
168
168
169 $ hg diff --change 5
169 $ hg diff --change 5
170 diff -r ae119d680c82 -r 9085c5c02e52 file.txt
170 diff -r ae119d680c82 -r 9085c5c02e52 file.txt
171 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
171 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
172 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
172 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
173 @@ -6,6 +6,6 @@
173 @@ -6,6 +6,6 @@
174 5
174 5
175 6
175 6
176 7
176 7
177 -8
177 -8
178 +y
178 +y
179 9
179 9
180 10
180 10
181
181
182 must be similar to 'hg diff --change 5':
182 must be similar to 'hg diff --change 5':
183
183
184 $ hg diff -c 6
184 $ hg diff -c 6
185 diff -r 273b50f17c6d -r 979ca961fd2e file.txt
185 diff -r 273b50f17c6d -r 979ca961fd2e file.txt
186 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
186 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
187 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
187 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
188 @@ -6,6 +6,6 @@
188 @@ -6,6 +6,6 @@
189 5
189 5
190 6
190 6
191 7
191 7
192 -8
192 -8
193 +y
193 +y
194 9
194 9
195 10
195 10
196
196
197 merge diff should show only manual edits to a merge:
197 merge diff should show only manual edits to a merge:
198
198
199 $ hg diff --config diff.merge=yes -c 6
199 $ hg diff --config diff.merge=yes -c 6
200 (no diff output is expected here)
200 (no diff output is expected here)
201
201
202 Construct an "evil merge" that does something other than just the merge.
202 Construct an "evil merge" that does something other than just the merge.
203
203
204 $ hg co ".^"
204 $ hg co ".^"
205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 $ hg merge -r 5
206 $ hg merge -r 5
207 merging file.txt
207 merging file.txt
208 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
208 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
209 (branch merge, don't forget to commit)
209 (branch merge, don't forget to commit)
210 $ echo 11 >> file.txt
210 $ echo 11 >> file.txt
211 $ hg ci -m 'merge 8 to y with manual edit of 11' # 7
211 $ hg ci -m 'merge 8 to y with manual edit of 11' # 7
212 created new head
212 created new head
213 $ hg diff -c 7
213 $ hg diff -c 7
214 diff -r 273b50f17c6d -r 8ad85e839ba7 file.txt
214 diff -r 273b50f17c6d -r 8ad85e839ba7 file.txt
215 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
215 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
216 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
216 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
217 @@ -6,6 +6,7 @@
217 @@ -6,6 +6,7 @@
218 5
218 5
219 6
219 6
220 7
220 7
221 -8
221 -8
222 +y
222 +y
223 9
223 9
224 10
224 10
225 +11
225 +11
226 Contrast with the `hg diff -c 7` version above: only the manual edit shows
226 Contrast with the `hg diff -c 7` version above: only the manual edit shows
227 up, making it easy to identify changes someone is otherwise trying to sneak
227 up, making it easy to identify changes someone is otherwise trying to sneak
228 into a merge.
228 into a merge.
229 $ hg diff --config diff.merge=yes -c 7
229 $ hg diff --config diff.merge=yes -c 7
230 diff -r 8ad85e839ba7 file.txt
230 diff -r 8ad85e839ba7 file.txt
231 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
231 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
232 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
232 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
233 @@ -9,3 +9,4 @@
233 @@ -9,3 +9,4 @@
234 y
234 y
235 9
235 9
236 10
236 10
237 +11
237 +11
238
238
239 Set up a conflict.
239 Set up a conflict.
240 $ hg co ".^"
240 $ hg co ".^"
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 $ sed -e 's,^8$,z,' file.txt > file.txt.tmp
242 $ sed -e 's,^8$,z,' file.txt > file.txt.tmp
243 $ mv file.txt.tmp file.txt
243 $ mv file.txt.tmp file.txt
244 $ hg ci -m 'conflicting edit: 8 to z'
244 $ hg ci -m 'conflicting edit: 8 to z'
245 created new head
245 created new head
246 $ echo "this file is new in p1 of the merge" > new-file-p1.txt
246 $ echo "this file is new in p1 of the merge" > new-file-p1.txt
247 $ hg ci -Am 'new file' new-file-p1.txt
247 $ hg ci -Am 'new file' new-file-p1.txt
248 $ hg log -r . --template 'p1 will be rev {rev}\n'
248 $ hg log -r . --template 'p1 will be rev {rev}\n'
249 p1 will be rev 9
249 p1 will be rev 9
250 $ hg co 5
250 $ hg co 5
251 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
251 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
252 $ echo "this file is new in p2 of the merge" > new-file-p2.txt
252 $ echo "this file is new in p2 of the merge" > new-file-p2.txt
253 $ hg ci -Am 'new file' new-file-p2.txt
253 $ hg ci -Am 'new file' new-file-p2.txt
254 created new head
254 created new head
255 $ hg log -r . --template 'p2 will be rev {rev}\n'
255 $ hg log -r . --template 'p2 will be rev {rev}\n'
256 p2 will be rev 10
256 p2 will be rev 10
257 $ hg co -- 9
257 $ hg co -- 9
258 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
258 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
259 $ hg merge -r 10
259 $ hg merge -r 10
260 merging file.txt
260 merging file.txt
261 warning: conflicts while merging file.txt! (edit, then use 'hg resolve --mark')
261 warning: conflicts while merging file.txt! (edit, then use 'hg resolve --mark')
262 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
262 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
263 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
263 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
264 [1]
264 [1]
265 $ hg revert file.txt -r .
265 $ hg revert file.txt -r .
266 $ hg resolve -ma
266 $ hg resolve -ma
267 (no more unresolved files)
267 (no more unresolved files)
268 $ hg commit -m 'merge conflicted edit'
268 $ hg commit -m 'merge conflicted edit'
269 Without diff.merge, it's a diff against p1
269 Without diff.merge, it's a diff against p1
270 $ hg diff --config diff.merge=no -c 11
270 $ hg diff --config diff.merge=no -c 11
271 diff -r fd1f17c90d7c -r 5010caab09f6 new-file-p2.txt
271 diff -r fd1f17c90d7c -r 5010caab09f6 new-file-p2.txt
272 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
272 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
273 +++ b/new-file-p2.txt Thu Jan 01 00:00:00 1970 +0000
273 +++ b/new-file-p2.txt Thu Jan 01 00:00:00 1970 +0000
274 @@ -0,0 +1,1 @@
274 @@ -0,0 +1,1 @@
275 +this file is new in p2 of the merge
275 +this file is new in p2 of the merge
276 With diff.merge, it's a diff against the conflicted content.
276 With diff.merge, it's a diff against the conflicted content.
277 $ hg diff --config diff.merge=yes -c 11
277 $ hg diff --config diff.merge=yes -c 11
278 diff -r 5010caab09f6 file.txt
278 diff -r 5010caab09f6 file.txt
279 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
279 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
280 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
280 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
281 @@ -6,12 +6,6 @@
281 @@ -6,12 +6,6 @@
282 5
282 5
283 6
283 6
284 7
284 7
285 -<<<<<<< local: fd1f17c90d7c - test: new file
285 -<<<<<<< local: fd1f17c90d7c - test: new file
286 z
286 z
287 -||||||| base
287 -||||||| base
288 -8
288 -8
289 -=======
289 -=======
290 -y
290 -y
291 ->>>>>>> other: d9e7de69eac3 - test: new file
291 ->>>>>>> other: d9e7de69eac3 - test: new file
292 9
292 9
293 10
293 10
294
294
295 There must _NOT_ be a .hg/merge directory leftover.
295 There must _NOT_ be a .hg/merge directory leftover.
296 $ test ! -d .hg/merge
296 $ test ! -d .hg/merge
297 (No output is expected)
297 (No output is expected)
298 $ cd ..
298 $ cd ..
@@ -1,555 +1,555 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "extdiff=" >> $HGRCPATH
2 $ echo "extdiff=" >> $HGRCPATH
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ echo b > b
7 $ echo b > b
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 adding b
10 adding b
11
11
12 Should diff cloned directories:
12 Should diff cloned directories:
13
13
14 $ hg extdiff -o -r
14 $ hg extdiff -o -r
15 Only in a: a
15 Only in a: a
16 Only in a: b
16 Only in a: b
17 [1]
17 [1]
18
18
19 $ cat <<EOF >> $HGRCPATH
19 $ cat <<EOF >> $HGRCPATH
20 > [extdiff]
20 > [extdiff]
21 > cmd.falabala = echo
21 > cmd.falabala = echo
22 > opts.falabala = diffing
22 > opts.falabala = diffing
23 > cmd.edspace = echo
23 > cmd.edspace = echo
24 > opts.edspace = "name <user@example.com>"
24 > opts.edspace = "name <user@example.com>"
25 > alabalaf =
25 > alabalaf =
26 > [merge-tools]
26 > [merge-tools]
27 > alabalaf.executable = echo
27 > alabalaf.executable = echo
28 > alabalaf.diffargs = diffing
28 > alabalaf.diffargs = diffing
29 > EOF
29 > EOF
30
30
31 $ hg falabala
31 $ hg falabala
32 diffing a.000000000000 a
32 diffing a.000000000000 a
33 [1]
33 [1]
34
34
35 $ hg help falabala
35 $ hg help falabala
36 hg falabala [OPTION]... [FILE]...
36 hg falabala [OPTION]... [FILE]...
37
37
38 use external program to diff repository (or selected files)
38 use external program to diff repository (or selected files)
39
39
40 Show differences between revisions for the specified files, using the
40 Show differences between revisions for the specified files, using the
41 following program:
41 following program:
42
42
43 'echo'
43 'echo'
44
44
45 When two revision arguments are given, then changes are shown between
45 When two revision arguments are given, then changes are shown between
46 those revisions. If only one revision is specified then that revision is
46 those revisions. If only one revision is specified then that revision is
47 compared to the working directory, and, when no revisions are specified,
47 compared to the working directory, and, when no revisions are specified,
48 the working directory files are compared to its parent.
48 the working directory files are compared to its parent.
49
49
50 options ([+] can be repeated):
50 options ([+] can be repeated):
51
51
52 -o --option OPT [+] pass option to comparison program
52 -o --option OPT [+] pass option to comparison program
53 --from REV1 revision to diff from
53 --from REV1 revision to diff from
54 --to REV2 revision to diff to
54 --to REV2 revision to diff to
55 -c --change REV change made by revision
55 -c --change REV change made by revision
56 --per-file compare each file instead of revision snapshots
56 --per-file compare each file instead of revision snapshots
57 --confirm prompt user before each external program invocation
57 --confirm prompt user before each external program invocation
58 --patch compare patches for two revisions
58 --patch compare patches for two revisions
59 -I --include PATTERN [+] include names matching the given patterns
59 -I --include PATTERN [+] include names matching the given patterns
60 -X --exclude PATTERN [+] exclude names matching the given patterns
60 -X --exclude PATTERN [+] exclude names matching the given patterns
61 -S --subrepos recurse into subrepositories
61 -S --subrepos recurse into subrepositories
62
62
63 (some details hidden, use --verbose to show complete help)
63 (some details hidden, use --verbose to show complete help)
64
64
65 $ hg ci -d '0 0' -mtest1
65 $ hg ci -d '0 0' -mtest1
66
66
67 $ echo b >> a
67 $ echo b >> a
68 $ hg ci -d '1 0' -mtest2
68 $ hg ci -d '1 0' -mtest2
69
69
70 Should diff cloned files directly:
70 Should diff cloned files directly:
71
71
72 $ hg falabala --from 0 --to 1
72 $ hg falabala --from 0 --to 1
73 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
73 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
74 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
74 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
75 [1]
75 [1]
76
76
77 Can show diff from working copy:
77 Can show diff from working copy:
78 $ echo c >> a
78 $ echo c >> a
79 $ hg falabala --to 1
79 $ hg falabala --to 1
80 diffing "*\\extdiff.*\\a" "a.34eed99112ab\\a" (glob) (windows !)
80 diffing "*\\extdiff.*\\a" "a.34eed99112ab\\a" (glob) (windows !)
81 diffing */extdiff.*/a a.34eed99112ab/a (glob) (no-windows !)
81 diffing */extdiff.*/a a.34eed99112ab/a (glob) (no-windows !)
82 [1]
82 [1]
83 $ hg revert a
83 $ hg revert a
84 $ rm a.orig
84 $ rm a.orig
85
85
86 Specifying an empty revision should abort.
86 Specifying an empty revision should abort.
87
87
88 $ hg extdiff -p diff --patch --rev 'ancestor()' --rev 1
88 $ hg extdiff -p diff --patch --rev 'ancestor()' --rev 1
89 abort: empty revision on one side of range
89 abort: empty revision on one side of range
90 [255]
90 [10]
91
91
92 Test diff during merge:
92 Test diff during merge:
93
93
94 $ hg update -C 0
94 $ hg update -C 0
95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 $ echo c >> c
96 $ echo c >> c
97 $ hg add c
97 $ hg add c
98 $ hg ci -m "new branch" -d '1 0'
98 $ hg ci -m "new branch" -d '1 0'
99 created new head
99 created new head
100 $ hg merge 1
100 $ hg merge 1
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 (branch merge, don't forget to commit)
102 (branch merge, don't forget to commit)
103
103
104 Should diff cloned file against wc file:
104 Should diff cloned file against wc file:
105
105
106 $ hg falabala
106 $ hg falabala
107 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob) (windows !)
107 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "*\\a\\a" (glob) (windows !)
108 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob) (no-windows !)
108 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob) (no-windows !)
109 [1]
109 [1]
110
110
111
111
112 Test --change option:
112 Test --change option:
113
113
114 $ hg ci -d '2 0' -mtest3
114 $ hg ci -d '2 0' -mtest3
115
115
116 $ hg falabala -c 1
116 $ hg falabala -c 1
117 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
117 diffing "*\\extdiff.*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
118 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
118 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
119 [1]
119 [1]
120
120
121 Check diff are made from the first parent:
121 Check diff are made from the first parent:
122
122
123 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
123 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
124 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob) (windows !)
124 diffing "*\\extdiff.*\\a.2a13a4d2da36\\a" "a.46c0e4daeb72\\a" (glob) (windows !)
125 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob) (no-windows !)
125 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob) (no-windows !)
126 diff-like tools yield a non-zero exit code
126 diff-like tools yield a non-zero exit code
127
127
128 issue3153: ensure using extdiff with removed subrepos doesn't crash:
128 issue3153: ensure using extdiff with removed subrepos doesn't crash:
129
129
130 $ hg init suba
130 $ hg init suba
131 $ cd suba
131 $ cd suba
132 $ echo suba > suba
132 $ echo suba > suba
133 $ hg add
133 $ hg add
134 adding suba
134 adding suba
135 $ hg ci -m "adding suba file"
135 $ hg ci -m "adding suba file"
136 $ cd ..
136 $ cd ..
137 $ echo suba=suba > .hgsub
137 $ echo suba=suba > .hgsub
138 $ hg add
138 $ hg add
139 adding .hgsub
139 adding .hgsub
140 $ hg ci -Sm "adding subrepo"
140 $ hg ci -Sm "adding subrepo"
141 $ echo > .hgsub
141 $ echo > .hgsub
142 $ hg ci -m "removing subrepo"
142 $ hg ci -m "removing subrepo"
143 $ hg falabala --from 4 --to 5 -S
143 $ hg falabala --from 4 --to 5 -S
144 diffing a.398e36faf9c6 a.5ab95fb166c4
144 diffing a.398e36faf9c6 a.5ab95fb166c4
145 [1]
145 [1]
146
146
147 Test --per-file option:
147 Test --per-file option:
148
148
149 $ hg up -q -C 3
149 $ hg up -q -C 3
150 $ echo a2 > a
150 $ echo a2 > a
151 $ echo b2 > b
151 $ echo b2 > b
152 $ hg ci -d '3 0' -mtestmode1
152 $ hg ci -d '3 0' -mtestmode1
153 created new head
153 created new head
154 $ hg falabala -c 6 --per-file
154 $ hg falabala -c 6 --per-file
155 diffing "*\\extdiff.*\\a.46c0e4daeb72\\a" "a.81906f2b98ac\\a" (glob) (windows !)
155 diffing "*\\extdiff.*\\a.46c0e4daeb72\\a" "a.81906f2b98ac\\a" (glob) (windows !)
156 diffing */extdiff.*/a.46c0e4daeb72/a a.81906f2b98ac/a (glob) (no-windows !)
156 diffing */extdiff.*/a.46c0e4daeb72/a a.81906f2b98ac/a (glob) (no-windows !)
157 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
157 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
158 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
158 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
159 [1]
159 [1]
160
160
161 Test --per-file option for gui tool:
161 Test --per-file option for gui tool:
162
162
163 $ hg --config extdiff.gui.alabalaf=True alabalaf -c 6 --per-file --debug
163 $ hg --config extdiff.gui.alabalaf=True alabalaf -c 6 --per-file --debug
164 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
164 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
165 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
165 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
166 making snapshot of 2 files from rev 46c0e4daeb72
166 making snapshot of 2 files from rev 46c0e4daeb72
167 a
167 a
168 b
168 b
169 making snapshot of 2 files from rev 81906f2b98ac
169 making snapshot of 2 files from rev 81906f2b98ac
170 a
170 a
171 b
171 b
172 running '* diffing * *' in * (backgrounded) (glob)
172 running '* diffing * *' in * (backgrounded) (glob)
173 running '* diffing * *' in * (backgrounded) (glob)
173 running '* diffing * *' in * (backgrounded) (glob)
174 cleaning up temp directory
174 cleaning up temp directory
175 [1]
175 [1]
176
176
177 Test --per-file option for gui tool again:
177 Test --per-file option for gui tool again:
178
178
179 $ hg --config merge-tools.alabalaf.gui=True alabalaf -c 6 --per-file --debug
179 $ hg --config merge-tools.alabalaf.gui=True alabalaf -c 6 --per-file --debug
180 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
180 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
181 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
181 diffing */extdiff.*/a.46c0e4daeb72/* a.81906f2b98ac/* (glob)
182 making snapshot of 2 files from rev 46c0e4daeb72
182 making snapshot of 2 files from rev 46c0e4daeb72
183 a
183 a
184 b
184 b
185 making snapshot of 2 files from rev 81906f2b98ac
185 making snapshot of 2 files from rev 81906f2b98ac
186 a
186 a
187 b
187 b
188 running '* diffing * *' in * (backgrounded) (glob)
188 running '* diffing * *' in * (backgrounded) (glob)
189 running '* diffing * *' in * (backgrounded) (glob)
189 running '* diffing * *' in * (backgrounded) (glob)
190 cleaning up temp directory
190 cleaning up temp directory
191 [1]
191 [1]
192
192
193 Test --per-file and --confirm options:
193 Test --per-file and --confirm options:
194
194
195 $ hg --config ui.interactive=True falabala -c 6 --per-file --confirm <<EOF
195 $ hg --config ui.interactive=True falabala -c 6 --per-file --confirm <<EOF
196 > n
196 > n
197 > y
197 > y
198 > EOF
198 > EOF
199 diff a (1 of 2) [Yns?] n
199 diff a (1 of 2) [Yns?] n
200 diff b (2 of 2) [Yns?] y
200 diff b (2 of 2) [Yns?] y
201 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
201 diffing "*\\extdiff.*\\a.46c0e4daeb72\\b" "a.81906f2b98ac\\b" (glob) (windows !)
202 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
202 diffing */extdiff.*/a.46c0e4daeb72/b a.81906f2b98ac/b (glob) (no-windows !)
203 [1]
203 [1]
204
204
205 Test --per-file and --confirm options with skipping:
205 Test --per-file and --confirm options with skipping:
206
206
207 $ hg --config ui.interactive=True falabala -c 6 --per-file --confirm <<EOF
207 $ hg --config ui.interactive=True falabala -c 6 --per-file --confirm <<EOF
208 > s
208 > s
209 > EOF
209 > EOF
210 diff a (1 of 2) [Yns?] s
210 diff a (1 of 2) [Yns?] s
211 [1]
211 [1]
212
212
213 issue4463: usage of command line configuration without additional quoting
213 issue4463: usage of command line configuration without additional quoting
214
214
215 $ cat <<EOF >> $HGRCPATH
215 $ cat <<EOF >> $HGRCPATH
216 > [extdiff]
216 > [extdiff]
217 > cmd.4463a = echo
217 > cmd.4463a = echo
218 > opts.4463a = a-naked 'single quoted' "double quoted"
218 > opts.4463a = a-naked 'single quoted' "double quoted"
219 > 4463b = echo b-naked 'single quoted' "double quoted"
219 > 4463b = echo b-naked 'single quoted' "double quoted"
220 > echo =
220 > echo =
221 > EOF
221 > EOF
222 $ hg update -q -C 0
222 $ hg update -q -C 0
223 $ echo a >> a
223 $ echo a >> a
224
224
225 $ hg --debug 4463a | grep '^running'
225 $ hg --debug 4463a | grep '^running'
226 running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
226 running 'echo a-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
227 running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
227 running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
228 $ hg --debug 4463b | grep '^running'
228 $ hg --debug 4463b | grep '^running'
229 running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
229 running 'echo b-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
230 running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
230 running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
231 $ hg --debug echo | grep '^running'
231 $ hg --debug echo | grep '^running'
232 running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
232 running '*echo* "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
233 running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
233 running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
234
234
235 (getting options from other than extdiff section)
235 (getting options from other than extdiff section)
236
236
237 $ cat <<EOF >> $HGRCPATH
237 $ cat <<EOF >> $HGRCPATH
238 > [extdiff]
238 > [extdiff]
239 > # using diff-tools diffargs
239 > # using diff-tools diffargs
240 > 4463b2 = echo
240 > 4463b2 = echo
241 > # using merge-tools diffargs
241 > # using merge-tools diffargs
242 > 4463b3 = echo
242 > 4463b3 = echo
243 > # no diffargs
243 > # no diffargs
244 > 4463b4 = echo
244 > 4463b4 = echo
245 > [diff-tools]
245 > [diff-tools]
246 > 4463b2.diffargs = b2-naked 'single quoted' "double quoted"
246 > 4463b2.diffargs = b2-naked 'single quoted' "double quoted"
247 > [merge-tools]
247 > [merge-tools]
248 > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
248 > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
249 > EOF
249 > EOF
250
250
251 $ hg --debug 4463b2 | grep '^running'
251 $ hg --debug 4463b2 | grep '^running'
252 running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
252 running 'echo b2-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
253 running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
253 running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
254 $ hg --debug 4463b3 | grep '^running'
254 $ hg --debug 4463b3 | grep '^running'
255 running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
255 running 'echo b3-naked \'single quoted\' "double quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
256 running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
256 running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
257 $ hg --debug 4463b4 | grep '^running'
257 $ hg --debug 4463b4 | grep '^running'
258 running 'echo "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
258 running 'echo "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
259 running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
259 running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob) (no-windows !)
260 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
260 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
261 running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
261 running 'echo b4-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
262 running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
262 running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
263 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
263 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
264 running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
264 running 'echo echo-naked "being quoted" "*\\a" "*\\a"' in */extdiff.* (glob) (windows !)
265 running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
265 running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob) (no-windows !)
266
266
267 $ touch 'sp ace'
267 $ touch 'sp ace'
268 $ hg add 'sp ace'
268 $ hg add 'sp ace'
269 $ hg ci -m 'sp ace'
269 $ hg ci -m 'sp ace'
270 created new head
270 created new head
271 $ echo > 'sp ace'
271 $ echo > 'sp ace'
272
272
273 Test pre-72a89cf86fcd backward compatibility with half-baked manual quoting
273 Test pre-72a89cf86fcd backward compatibility with half-baked manual quoting
274
274
275 $ cat <<EOF >> $HGRCPATH
275 $ cat <<EOF >> $HGRCPATH
276 > [extdiff]
276 > [extdiff]
277 > odd =
277 > odd =
278 > [merge-tools]
278 > [merge-tools]
279 > odd.diffargs = --foo='\$clabel' '\$clabel' "--bar=\$clabel" "\$clabel"
279 > odd.diffargs = --foo='\$clabel' '\$clabel' "--bar=\$clabel" "\$clabel"
280 > odd.executable = echo
280 > odd.executable = echo
281 > EOF
281 > EOF
282
282
283 $ hg --debug odd | grep '^running'
283 $ hg --debug odd | grep '^running'
284 running '"*\\echo.exe" --foo="sp ace" "sp ace" --bar="sp ace" "sp ace"' in * (glob) (windows !)
284 running '"*\\echo.exe" --foo="sp ace" "sp ace" --bar="sp ace" "sp ace"' in * (glob) (windows !)
285 running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob) (no-windows !)
285 running "*/echo --foo='sp ace' 'sp ace' --bar='sp ace' 'sp ace'" in * (glob) (no-windows !)
286
286
287 Empty argument must be quoted
287 Empty argument must be quoted
288
288
289 $ cat <<EOF >> $HGRCPATH
289 $ cat <<EOF >> $HGRCPATH
290 > [extdiff]
290 > [extdiff]
291 > kdiff3 = echo
291 > kdiff3 = echo
292 > [merge-tools]
292 > [merge-tools]
293 > kdiff3.diffargs=--L1 \$plabel1 --L2 \$clabel \$parent \$child
293 > kdiff3.diffargs=--L1 \$plabel1 --L2 \$clabel \$parent \$child
294 > EOF
294 > EOF
295
295
296 $ hg --debug kdiff3 --from 0 | grep '^running'
296 $ hg --debug kdiff3 --from 0 | grep '^running'
297 running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob) (windows !)
297 running 'echo --L1 "@0" --L2 "" a.8a5febb7f867 a' in * (glob) (windows !)
298 running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob) (no-windows !)
298 running "echo --L1 '@0' --L2 '' a.8a5febb7f867 a" in * (glob) (no-windows !)
299
299
300
300
301 Test extdiff of multiple files in tmp dir:
301 Test extdiff of multiple files in tmp dir:
302
302
303 $ hg update -C 0 > /dev/null
303 $ hg update -C 0 > /dev/null
304 $ echo changed > a
304 $ echo changed > a
305 $ echo changed > b
305 $ echo changed > b
306 #if execbit
306 #if execbit
307 $ chmod +x b
307 $ chmod +x b
308 #endif
308 #endif
309
309
310 Diff in working directory, before:
310 Diff in working directory, before:
311
311
312 $ hg diff --git
312 $ hg diff --git
313 diff --git a/a b/a
313 diff --git a/a b/a
314 --- a/a
314 --- a/a
315 +++ b/a
315 +++ b/a
316 @@ -1,1 +1,1 @@
316 @@ -1,1 +1,1 @@
317 -a
317 -a
318 +changed
318 +changed
319 diff --git a/b b/b
319 diff --git a/b b/b
320 old mode 100644 (execbit !)
320 old mode 100644 (execbit !)
321 new mode 100755 (execbit !)
321 new mode 100755 (execbit !)
322 --- a/b
322 --- a/b
323 +++ b/b
323 +++ b/b
324 @@ -1,1 +1,1 @@
324 @@ -1,1 +1,1 @@
325 -b
325 -b
326 +changed
326 +changed
327
327
328
328
329 Edit with extdiff -p:
329 Edit with extdiff -p:
330
330
331 Prepare custom diff/edit tool:
331 Prepare custom diff/edit tool:
332
332
333 $ cat > 'diff tool.py' << EOT
333 $ cat > 'diff tool.py' << EOT
334 > #!$PYTHON
334 > #!$PYTHON
335 > import time
335 > import time
336 > time.sleep(1) # avoid unchanged-timestamp problems
336 > time.sleep(1) # avoid unchanged-timestamp problems
337 > open('a/a', 'ab').write(b'edited\n')
337 > open('a/a', 'ab').write(b'edited\n')
338 > open('a/b', 'ab').write(b'edited\n')
338 > open('a/b', 'ab').write(b'edited\n')
339 > EOT
339 > EOT
340
340
341 #if execbit
341 #if execbit
342 $ chmod +x 'diff tool.py'
342 $ chmod +x 'diff tool.py'
343 #endif
343 #endif
344
344
345 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
345 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
346 and start tool
346 and start tool
347
347
348 #if windows
348 #if windows
349 $ cat > 'diff tool.bat' << EOF
349 $ cat > 'diff tool.bat' << EOF
350 > @"$PYTHON" "`pwd`/diff tool.py"
350 > @"$PYTHON" "`pwd`/diff tool.py"
351 > EOF
351 > EOF
352 $ hg extdiff -p "`pwd`/diff tool.bat"
352 $ hg extdiff -p "`pwd`/diff tool.bat"
353 [1]
353 [1]
354 #else
354 #else
355 $ hg extdiff -p "`pwd`/diff tool.py"
355 $ hg extdiff -p "`pwd`/diff tool.py"
356 [1]
356 [1]
357 #endif
357 #endif
358
358
359 Diff in working directory, after:
359 Diff in working directory, after:
360
360
361 $ hg diff --git
361 $ hg diff --git
362 diff --git a/a b/a
362 diff --git a/a b/a
363 --- a/a
363 --- a/a
364 +++ b/a
364 +++ b/a
365 @@ -1,1 +1,2 @@
365 @@ -1,1 +1,2 @@
366 -a
366 -a
367 +changed
367 +changed
368 +edited
368 +edited
369 diff --git a/b b/b
369 diff --git a/b b/b
370 old mode 100644 (execbit !)
370 old mode 100644 (execbit !)
371 new mode 100755 (execbit !)
371 new mode 100755 (execbit !)
372 --- a/b
372 --- a/b
373 +++ b/b
373 +++ b/b
374 @@ -1,1 +1,2 @@
374 @@ -1,1 +1,2 @@
375 -b
375 -b
376 +changed
376 +changed
377 +edited
377 +edited
378
378
379 Test extdiff with --option:
379 Test extdiff with --option:
380
380
381 $ hg extdiff -p echo -o this -c 1
381 $ hg extdiff -p echo -o this -c 1
382 this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
382 this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
383 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
383 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
384 [1]
384 [1]
385
385
386 $ hg falabala -o this -c 1
386 $ hg falabala -o this -c 1
387 diffing this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
387 diffing this "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
388 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
388 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
389 [1]
389 [1]
390
390
391 Test extdiff's handling of options with spaces in them:
391 Test extdiff's handling of options with spaces in them:
392
392
393 $ hg edspace -c 1
393 $ hg edspace -c 1
394 "name <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
394 "name <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
395 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
395 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
396 [1]
396 [1]
397
397
398 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
398 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
399 "name <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
399 "name <user@example.com>" "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
400 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
400 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
401 [1]
401 [1]
402
402
403 Test with revsets:
403 Test with revsets:
404
404
405 $ hg extdif -p echo -c "rev(1)"
405 $ hg extdif -p echo -c "rev(1)"
406 "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
406 "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
407 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
407 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
408 [1]
408 [1]
409
409
410 $ hg extdif -p echo -r "0::1"
410 $ hg extdif -p echo -r "0::1"
411 "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
411 "*\\a.8a5febb7f867\\a" "a.34eed99112ab\\a" (glob) (windows !)
412 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
412 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob) (no-windows !)
413 [1]
413 [1]
414
414
415 Fallback to merge-tools.tool.executable|regkey
415 Fallback to merge-tools.tool.executable|regkey
416 $ mkdir dir
416 $ mkdir dir
417 $ cat > 'dir/tool.sh' << 'EOF'
417 $ cat > 'dir/tool.sh' << 'EOF'
418 > #!/bin/sh
418 > #!/bin/sh
419 > # Mimic a tool that syncs all attrs, including mtime
419 > # Mimic a tool that syncs all attrs, including mtime
420 > cp $1/a $2/a
420 > cp $1/a $2/a
421 > touch -r $1/a $2/a
421 > touch -r $1/a $2/a
422 > chmod +x $2/a
422 > chmod +x $2/a
423 > echo "** custom diff **"
423 > echo "** custom diff **"
424 > EOF
424 > EOF
425 #if execbit
425 #if execbit
426 $ chmod +x dir/tool.sh
426 $ chmod +x dir/tool.sh
427 #endif
427 #endif
428
428
429 Windows can't run *.sh directly, so create a shim executable that can be.
429 Windows can't run *.sh directly, so create a shim executable that can be.
430 Without something executable, the next hg command will try to run `tl` instead
430 Without something executable, the next hg command will try to run `tl` instead
431 of $tool (and fail).
431 of $tool (and fail).
432 #if windows
432 #if windows
433 $ cat > dir/tool.bat <<EOF
433 $ cat > dir/tool.bat <<EOF
434 > @sh -c "`pwd`/dir/tool.sh %1 %2"
434 > @sh -c "`pwd`/dir/tool.sh %1 %2"
435 > EOF
435 > EOF
436 $ tool=`pwd`/dir/tool.bat
436 $ tool=`pwd`/dir/tool.bat
437 #else
437 #else
438 $ tool=`pwd`/dir/tool.sh
438 $ tool=`pwd`/dir/tool.sh
439 #endif
439 #endif
440
440
441 $ cat a
441 $ cat a
442 changed
442 changed
443 edited
443 edited
444 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
444 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
445 making snapshot of 2 files from rev * (glob)
445 making snapshot of 2 files from rev * (glob)
446 a
446 a
447 b
447 b
448 making snapshot of 2 files from working directory
448 making snapshot of 2 files from working directory
449 a
449 a
450 b
450 b
451 running '$TESTTMP/a/dir/tool.bat a.* a' in */extdiff.* (glob) (windows !)
451 running '$TESTTMP/a/dir/tool.bat a.* a' in */extdiff.* (glob) (windows !)
452 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob) (no-windows !)
452 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob) (no-windows !)
453 ** custom diff **
453 ** custom diff **
454 file changed while diffing. Overwriting: $TESTTMP/a/a (src: */extdiff.*/a/a) (glob)
454 file changed while diffing. Overwriting: $TESTTMP/a/a (src: */extdiff.*/a/a) (glob)
455 cleaning up temp directory
455 cleaning up temp directory
456 [1]
456 [1]
457 $ cat a
457 $ cat a
458 a
458 a
459
459
460 #if execbit
460 #if execbit
461 $ [ -x a ]
461 $ [ -x a ]
462
462
463 $ cat > 'dir/tool.sh' << 'EOF'
463 $ cat > 'dir/tool.sh' << 'EOF'
464 > #!/bin/sh
464 > #!/bin/sh
465 > chmod -x $2/a
465 > chmod -x $2/a
466 > echo "** custom diff **"
466 > echo "** custom diff **"
467 > EOF
467 > EOF
468
468
469 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
469 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
470 making snapshot of 2 files from rev * (glob)
470 making snapshot of 2 files from rev * (glob)
471 a
471 a
472 b
472 b
473 making snapshot of 2 files from working directory
473 making snapshot of 2 files from working directory
474 a
474 a
475 b
475 b
476 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
476 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
477 ** custom diff **
477 ** custom diff **
478 file changed while diffing. Overwriting: $TESTTMP/a/a (src: */extdiff.*/a/a) (glob)
478 file changed while diffing. Overwriting: $TESTTMP/a/a (src: */extdiff.*/a/a) (glob)
479 cleaning up temp directory
479 cleaning up temp directory
480 [1]
480 [1]
481
481
482 $ [ -x a ]
482 $ [ -x a ]
483 [1]
483 [1]
484 #endif
484 #endif
485
485
486 $ cd ..
486 $ cd ..
487
487
488 #if symlink
488 #if symlink
489
489
490 Test symlinks handling (issue1909)
490 Test symlinks handling (issue1909)
491
491
492 $ hg init testsymlinks
492 $ hg init testsymlinks
493 $ cd testsymlinks
493 $ cd testsymlinks
494 $ echo a > a
494 $ echo a > a
495 $ hg ci -Am adda
495 $ hg ci -Am adda
496 adding a
496 adding a
497 $ echo a >> a
497 $ echo a >> a
498 $ ln -s missing linka
498 $ ln -s missing linka
499 $ hg add linka
499 $ hg add linka
500 $ hg falabala --from 0 --traceback
500 $ hg falabala --from 0 --traceback
501 diffing testsymlinks.07f494440405 testsymlinks
501 diffing testsymlinks.07f494440405 testsymlinks
502 [1]
502 [1]
503 $ cd ..
503 $ cd ..
504
504
505 #endif
505 #endif
506
506
507 Test handling of non-ASCII paths in generated docstrings (issue5301)
507 Test handling of non-ASCII paths in generated docstrings (issue5301)
508
508
509 >>> with open("u", "wb") as f:
509 >>> with open("u", "wb") as f:
510 ... n = f.write(b"\xa5\xa5")
510 ... n = f.write(b"\xa5\xa5")
511 $ U=`cat u`
511 $ U=`cat u`
512
512
513 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help -k xyzzy
513 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help -k xyzzy
514 abort: no matches
514 abort: no matches
515 (try 'hg help' for a list of topics)
515 (try 'hg help' for a list of topics)
516 [10]
516 [10]
517
517
518 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help td > /dev/null
518 $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help td > /dev/null
519
519
520 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help -k xyzzy
520 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help -k xyzzy
521 abort: no matches
521 abort: no matches
522 (try 'hg help' for a list of topics)
522 (try 'hg help' for a list of topics)
523 [10]
523 [10]
524
524
525 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help td \
525 $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help td \
526 > | grep "^ '"
526 > | grep "^ '"
527 '\xa5\xa5'
527 '\xa5\xa5'
528
528
529 $ cd $TESTTMP
529 $ cd $TESTTMP
530
530
531 Test that diffing a single file works, even if that file is new
531 Test that diffing a single file works, even if that file is new
532
532
533 $ hg init testsinglefile
533 $ hg init testsinglefile
534 $ cd testsinglefile
534 $ cd testsinglefile
535 $ echo a > a
535 $ echo a > a
536 $ hg add a
536 $ hg add a
537 $ hg falabala
537 $ hg falabala
538 diffing nul "*\\a" (glob) (windows !)
538 diffing nul "*\\a" (glob) (windows !)
539 diffing /dev/null */a (glob) (no-windows !)
539 diffing /dev/null */a (glob) (no-windows !)
540 [1]
540 [1]
541 $ hg ci -qm a
541 $ hg ci -qm a
542 $ hg falabala -c .
542 $ hg falabala -c .
543 diffing nul "*\\a" (glob) (windows !)
543 diffing nul "*\\a" (glob) (windows !)
544 diffing /dev/null */a (glob) (no-windows !)
544 diffing /dev/null */a (glob) (no-windows !)
545 [1]
545 [1]
546 $ echo a >> a
546 $ echo a >> a
547 $ hg falabala
547 $ hg falabala
548 diffing "*\\a" "*\\a" (glob) (windows !)
548 diffing "*\\a" "*\\a" (glob) (windows !)
549 diffing */a */a (glob) (no-windows !)
549 diffing */a */a (glob) (no-windows !)
550 [1]
550 [1]
551 $ hg ci -qm 2a
551 $ hg ci -qm 2a
552 $ hg falabala -c .
552 $ hg falabala -c .
553 diffing "*\\a" "*\\a" (glob) (windows !)
553 diffing "*\\a" "*\\a" (glob) (windows !)
554 diffing */a */a (glob) (no-windows !)
554 diffing */a */a (glob) (no-windows !)
555 [1]
555 [1]
@@ -1,531 +1,531 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 >
4 >
5 > [phases]
5 > [phases]
6 > publish=False
6 > publish=False
7 >
7 >
8 > [alias]
8 > [alias]
9 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
9 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
10 > EOF
10 > EOF
11
11
12
12
13 $ hg init a
13 $ hg init a
14 $ cd a
14 $ cd a
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 adding changesets
16 adding changesets
17 adding manifests
17 adding manifests
18 adding file changes
18 adding file changes
19 added 8 changesets with 7 changes to 7 files (+2 heads)
19 added 8 changesets with 7 changes to 7 files (+2 heads)
20 new changesets cd010b8cd998:02de42196ebe (8 drafts)
20 new changesets cd010b8cd998:02de42196ebe (8 drafts)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 $ hg up tip
22 $ hg up tip
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24
24
25 $ echo I > I
25 $ echo I > I
26 $ hg ci -AmI
26 $ hg ci -AmI
27 adding I
27 adding I
28
28
29 $ hg tglog
29 $ hg tglog
30 @ 8: e7ec4e813ba6 'I'
30 @ 8: e7ec4e813ba6 'I'
31 |
31 |
32 o 7: 02de42196ebe 'H'
32 o 7: 02de42196ebe 'H'
33 |
33 |
34 | o 6: eea13746799a 'G'
34 | o 6: eea13746799a 'G'
35 |/|
35 |/|
36 o | 5: 24b6387c8c8c 'F'
36 o | 5: 24b6387c8c8c 'F'
37 | |
37 | |
38 | o 4: 9520eea781bc 'E'
38 | o 4: 9520eea781bc 'E'
39 |/
39 |/
40 | o 3: 32af7686d403 'D'
40 | o 3: 32af7686d403 'D'
41 | |
41 | |
42 | o 2: 5fddd98957c8 'C'
42 | o 2: 5fddd98957c8 'C'
43 | |
43 | |
44 | o 1: 42ccdea3bb16 'B'
44 | o 1: 42ccdea3bb16 'B'
45 |/
45 |/
46 o 0: cd010b8cd998 'A'
46 o 0: cd010b8cd998 'A'
47
47
48 $ cd ..
48 $ cd ..
49
49
50 Version with only two heads (to allow default destination to work)
50 Version with only two heads (to allow default destination to work)
51
51
52 $ hg clone -q -u . a a2heads -r 3 -r 8
52 $ hg clone -q -u . a a2heads -r 3 -r 8
53
53
54 These fail:
54 These fail:
55
55
56 $ hg clone -q -u . a a0
56 $ hg clone -q -u . a a0
57 $ cd a0
57 $ cd a0
58
58
59 $ hg rebase -s 8 -d 7
59 $ hg rebase -s 8 -d 7
60 nothing to rebase
60 nothing to rebase
61 [1]
61 [1]
62
62
63 $ hg rebase --continue --abort
63 $ hg rebase --continue --abort
64 abort: cannot specify both --abort and --continue
64 abort: cannot specify both --abort and --continue
65 [10]
65 [10]
66
66
67 $ hg rebase --continue --collapse
67 $ hg rebase --continue --collapse
68 abort: cannot use collapse with continue or abort
68 abort: cannot use collapse with continue or abort
69 [10]
69 [10]
70
70
71 $ hg rebase --continue --dest 4
71 $ hg rebase --continue --dest 4
72 abort: cannot specify both --continue and --dest
72 abort: cannot specify both --continue and --dest
73 [10]
73 [10]
74
74
75 $ hg rebase --base 5 --source 4
75 $ hg rebase --base 5 --source 4
76 abort: cannot specify both --source and --base
76 abort: cannot specify both --source and --base
77 [10]
77 [10]
78
78
79 $ hg rebase --rev 5 --source 4
79 $ hg rebase --rev 5 --source 4
80 abort: cannot specify both --rev and --source
80 abort: cannot specify both --rev and --source
81 [10]
81 [10]
82 $ hg rebase --base 5 --rev 4
82 $ hg rebase --base 5 --rev 4
83 abort: cannot specify both --rev and --base
83 abort: cannot specify both --rev and --base
84 [10]
84 [10]
85
85
86 $ hg rebase --base 6
86 $ hg rebase --base 6
87 abort: branch 'default' has 3 heads - please rebase to an explicit rev
87 abort: branch 'default' has 3 heads - please rebase to an explicit rev
88 (run 'hg heads .' to see heads, specify destination with -d)
88 (run 'hg heads .' to see heads, specify destination with -d)
89 [255]
89 [255]
90
90
91 $ hg rebase --rev '1 & !1' --dest 8
91 $ hg rebase --rev '1 & !1' --dest 8
92 empty "rev" revision set - nothing to rebase
92 empty "rev" revision set - nothing to rebase
93 [1]
93 [1]
94
94
95 $ hg rebase --rev 'wdir()' --dest 6
95 $ hg rebase --rev 'wdir()' --dest 6
96 abort: cannot rebase the working copy
96 abort: cannot rebase the working copy
97 [10]
97 [10]
98
98
99 $ hg rebase --source 'wdir()' --dest 6
99 $ hg rebase --source 'wdir()' --dest 6
100 abort: cannot rebase the working copy
100 abort: cannot rebase the working copy
101 [10]
101 [10]
102
102
103 $ hg rebase --source 1 --source 'wdir()' --dest 6
103 $ hg rebase --source 1 --source 'wdir()' --dest 6
104 abort: cannot rebase the working copy
104 abort: cannot rebase the working copy
105 [10]
105 [10]
106
106
107 $ hg rebase --source '1 & !1' --dest 8
107 $ hg rebase --source '1 & !1' --dest 8
108 empty "source" revision set - nothing to rebase
108 empty "source" revision set - nothing to rebase
109 [1]
109 [1]
110
110
111 $ hg rebase --base '1 & !1' --dest 8
111 $ hg rebase --base '1 & !1' --dest 8
112 empty "base" revision set - can't compute rebase set
112 empty "base" revision set - can't compute rebase set
113 [1]
113 [1]
114
114
115 $ hg rebase --dest 8
115 $ hg rebase --dest 8
116 nothing to rebase - working directory parent is also destination
116 nothing to rebase - working directory parent is also destination
117 [1]
117 [1]
118
118
119 $ hg rebase -b . --dest 8
119 $ hg rebase -b . --dest 8
120 nothing to rebase - e7ec4e813ba6 is both "base" and destination
120 nothing to rebase - e7ec4e813ba6 is both "base" and destination
121 [1]
121 [1]
122
122
123 $ hg up -q 7
123 $ hg up -q 7
124
124
125 $ hg rebase --dest 8 --traceback
125 $ hg rebase --dest 8 --traceback
126 nothing to rebase - working directory parent is already an ancestor of destination e7ec4e813ba6
126 nothing to rebase - working directory parent is already an ancestor of destination e7ec4e813ba6
127 [1]
127 [1]
128
128
129 $ hg rebase --dest 8 -b.
129 $ hg rebase --dest 8 -b.
130 nothing to rebase - "base" 02de42196ebe is already an ancestor of destination e7ec4e813ba6
130 nothing to rebase - "base" 02de42196ebe is already an ancestor of destination e7ec4e813ba6
131 [1]
131 [1]
132
132
133 $ hg rebase --dest '1 & !1'
133 $ hg rebase --dest '1 & !1'
134 abort: empty revision set
134 abort: empty revision set
135 [255]
135 [10]
136
136
137 These work:
137 These work:
138
138
139 Rebase with no arguments (from 3 onto 8):
139 Rebase with no arguments (from 3 onto 8):
140
140
141 $ cd ..
141 $ cd ..
142 $ hg clone -q -u . a2heads a1
142 $ hg clone -q -u . a2heads a1
143 $ cd a1
143 $ cd a1
144 $ hg up -q -C 3
144 $ hg up -q -C 3
145
145
146 $ hg rebase
146 $ hg rebase
147 rebasing 1:42ccdea3bb16 "B"
147 rebasing 1:42ccdea3bb16 "B"
148 rebasing 2:5fddd98957c8 "C"
148 rebasing 2:5fddd98957c8 "C"
149 rebasing 3:32af7686d403 "D"
149 rebasing 3:32af7686d403 "D"
150 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
150 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
151
151
152 $ hg tglog
152 $ hg tglog
153 @ 6: ed65089c18f8 'D'
153 @ 6: ed65089c18f8 'D'
154 |
154 |
155 o 5: 7621bf1a2f17 'C'
155 o 5: 7621bf1a2f17 'C'
156 |
156 |
157 o 4: 9430a62369c6 'B'
157 o 4: 9430a62369c6 'B'
158 |
158 |
159 o 3: e7ec4e813ba6 'I'
159 o 3: e7ec4e813ba6 'I'
160 |
160 |
161 o 2: 02de42196ebe 'H'
161 o 2: 02de42196ebe 'H'
162 |
162 |
163 o 1: 24b6387c8c8c 'F'
163 o 1: 24b6387c8c8c 'F'
164 |
164 |
165 o 0: cd010b8cd998 'A'
165 o 0: cd010b8cd998 'A'
166
166
167 Try to rollback after a rebase (fail):
167 Try to rollback after a rebase (fail):
168
168
169 $ hg rollback
169 $ hg rollback
170 no rollback information available
170 no rollback information available
171 [1]
171 [1]
172
172
173 $ cd ..
173 $ cd ..
174
174
175 Rebase with base == '.' => same as no arguments (from 3 onto 8):
175 Rebase with base == '.' => same as no arguments (from 3 onto 8):
176
176
177 $ hg clone -q -u 3 a2heads a2
177 $ hg clone -q -u 3 a2heads a2
178 $ cd a2
178 $ cd a2
179
179
180 $ hg rebase --base .
180 $ hg rebase --base .
181 rebasing 1:42ccdea3bb16 "B"
181 rebasing 1:42ccdea3bb16 "B"
182 rebasing 2:5fddd98957c8 "C"
182 rebasing 2:5fddd98957c8 "C"
183 rebasing 3:32af7686d403 "D"
183 rebasing 3:32af7686d403 "D"
184 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
184 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
185
185
186 $ hg tglog
186 $ hg tglog
187 @ 6: ed65089c18f8 'D'
187 @ 6: ed65089c18f8 'D'
188 |
188 |
189 o 5: 7621bf1a2f17 'C'
189 o 5: 7621bf1a2f17 'C'
190 |
190 |
191 o 4: 9430a62369c6 'B'
191 o 4: 9430a62369c6 'B'
192 |
192 |
193 o 3: e7ec4e813ba6 'I'
193 o 3: e7ec4e813ba6 'I'
194 |
194 |
195 o 2: 02de42196ebe 'H'
195 o 2: 02de42196ebe 'H'
196 |
196 |
197 o 1: 24b6387c8c8c 'F'
197 o 1: 24b6387c8c8c 'F'
198 |
198 |
199 o 0: cd010b8cd998 'A'
199 o 0: cd010b8cd998 'A'
200
200
201 $ cd ..
201 $ cd ..
202
202
203
203
204 Rebase with dest == branch(.) => same as no arguments (from 3 onto 8):
204 Rebase with dest == branch(.) => same as no arguments (from 3 onto 8):
205
205
206 $ hg clone -q -u 3 a a3
206 $ hg clone -q -u 3 a a3
207 $ cd a3
207 $ cd a3
208
208
209 $ hg rebase --dest 'branch(.)'
209 $ hg rebase --dest 'branch(.)'
210 rebasing 1:42ccdea3bb16 "B"
210 rebasing 1:42ccdea3bb16 "B"
211 rebasing 2:5fddd98957c8 "C"
211 rebasing 2:5fddd98957c8 "C"
212 rebasing 3:32af7686d403 "D"
212 rebasing 3:32af7686d403 "D"
213 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
213 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
214
214
215 $ hg tglog
215 $ hg tglog
216 @ 8: ed65089c18f8 'D'
216 @ 8: ed65089c18f8 'D'
217 |
217 |
218 o 7: 7621bf1a2f17 'C'
218 o 7: 7621bf1a2f17 'C'
219 |
219 |
220 o 6: 9430a62369c6 'B'
220 o 6: 9430a62369c6 'B'
221 |
221 |
222 o 5: e7ec4e813ba6 'I'
222 o 5: e7ec4e813ba6 'I'
223 |
223 |
224 o 4: 02de42196ebe 'H'
224 o 4: 02de42196ebe 'H'
225 |
225 |
226 | o 3: eea13746799a 'G'
226 | o 3: eea13746799a 'G'
227 |/|
227 |/|
228 o | 2: 24b6387c8c8c 'F'
228 o | 2: 24b6387c8c8c 'F'
229 | |
229 | |
230 | o 1: 9520eea781bc 'E'
230 | o 1: 9520eea781bc 'E'
231 |/
231 |/
232 o 0: cd010b8cd998 'A'
232 o 0: cd010b8cd998 'A'
233
233
234 $ cd ..
234 $ cd ..
235
235
236
236
237 Specify only source (from 2 onto 8):
237 Specify only source (from 2 onto 8):
238
238
239 $ hg clone -q -u . a2heads a4
239 $ hg clone -q -u . a2heads a4
240 $ cd a4
240 $ cd a4
241
241
242 $ hg rebase --source 'desc("C")'
242 $ hg rebase --source 'desc("C")'
243 rebasing 2:5fddd98957c8 "C"
243 rebasing 2:5fddd98957c8 "C"
244 rebasing 3:32af7686d403 "D"
244 rebasing 3:32af7686d403 "D"
245 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
245 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
246
246
247 $ hg tglog
247 $ hg tglog
248 o 6: 7726e9fd58f7 'D'
248 o 6: 7726e9fd58f7 'D'
249 |
249 |
250 o 5: 72c8333623d0 'C'
250 o 5: 72c8333623d0 'C'
251 |
251 |
252 @ 4: e7ec4e813ba6 'I'
252 @ 4: e7ec4e813ba6 'I'
253 |
253 |
254 o 3: 02de42196ebe 'H'
254 o 3: 02de42196ebe 'H'
255 |
255 |
256 o 2: 24b6387c8c8c 'F'
256 o 2: 24b6387c8c8c 'F'
257 |
257 |
258 | o 1: 42ccdea3bb16 'B'
258 | o 1: 42ccdea3bb16 'B'
259 |/
259 |/
260 o 0: cd010b8cd998 'A'
260 o 0: cd010b8cd998 'A'
261
261
262 $ cd ..
262 $ cd ..
263
263
264
264
265 Specify only dest (from 3 onto 6):
265 Specify only dest (from 3 onto 6):
266
266
267 $ hg clone -q -u 3 a a5
267 $ hg clone -q -u 3 a a5
268 $ cd a5
268 $ cd a5
269
269
270 $ hg rebase --dest 6
270 $ hg rebase --dest 6
271 rebasing 1:42ccdea3bb16 "B"
271 rebasing 1:42ccdea3bb16 "B"
272 rebasing 2:5fddd98957c8 "C"
272 rebasing 2:5fddd98957c8 "C"
273 rebasing 3:32af7686d403 "D"
273 rebasing 3:32af7686d403 "D"
274 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
274 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
275
275
276 $ hg tglog
276 $ hg tglog
277 @ 8: 8eeb3c33ad33 'D'
277 @ 8: 8eeb3c33ad33 'D'
278 |
278 |
279 o 7: 2327fea05063 'C'
279 o 7: 2327fea05063 'C'
280 |
280 |
281 o 6: e4e5be0395b2 'B'
281 o 6: e4e5be0395b2 'B'
282 |
282 |
283 | o 5: e7ec4e813ba6 'I'
283 | o 5: e7ec4e813ba6 'I'
284 | |
284 | |
285 | o 4: 02de42196ebe 'H'
285 | o 4: 02de42196ebe 'H'
286 | |
286 | |
287 o | 3: eea13746799a 'G'
287 o | 3: eea13746799a 'G'
288 |\|
288 |\|
289 | o 2: 24b6387c8c8c 'F'
289 | o 2: 24b6387c8c8c 'F'
290 | |
290 | |
291 o | 1: 9520eea781bc 'E'
291 o | 1: 9520eea781bc 'E'
292 |/
292 |/
293 o 0: cd010b8cd998 'A'
293 o 0: cd010b8cd998 'A'
294
294
295 $ cd ..
295 $ cd ..
296
296
297
297
298 Specify only base (from 1 onto 8):
298 Specify only base (from 1 onto 8):
299
299
300 $ hg clone -q -u . a2heads a6
300 $ hg clone -q -u . a2heads a6
301 $ cd a6
301 $ cd a6
302
302
303 $ hg rebase --base 'desc("D")'
303 $ hg rebase --base 'desc("D")'
304 rebasing 1:42ccdea3bb16 "B"
304 rebasing 1:42ccdea3bb16 "B"
305 rebasing 2:5fddd98957c8 "C"
305 rebasing 2:5fddd98957c8 "C"
306 rebasing 3:32af7686d403 "D"
306 rebasing 3:32af7686d403 "D"
307 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
307 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
308
308
309 $ hg tglog
309 $ hg tglog
310 o 6: ed65089c18f8 'D'
310 o 6: ed65089c18f8 'D'
311 |
311 |
312 o 5: 7621bf1a2f17 'C'
312 o 5: 7621bf1a2f17 'C'
313 |
313 |
314 o 4: 9430a62369c6 'B'
314 o 4: 9430a62369c6 'B'
315 |
315 |
316 @ 3: e7ec4e813ba6 'I'
316 @ 3: e7ec4e813ba6 'I'
317 |
317 |
318 o 2: 02de42196ebe 'H'
318 o 2: 02de42196ebe 'H'
319 |
319 |
320 o 1: 24b6387c8c8c 'F'
320 o 1: 24b6387c8c8c 'F'
321 |
321 |
322 o 0: cd010b8cd998 'A'
322 o 0: cd010b8cd998 'A'
323
323
324 $ cd ..
324 $ cd ..
325
325
326
326
327 Specify source and dest (from 2 onto 7):
327 Specify source and dest (from 2 onto 7):
328
328
329 $ hg clone -q -u . a a7
329 $ hg clone -q -u . a a7
330 $ cd a7
330 $ cd a7
331
331
332 $ hg rebase --source 2 --dest 7
332 $ hg rebase --source 2 --dest 7
333 rebasing 2:5fddd98957c8 "C"
333 rebasing 2:5fddd98957c8 "C"
334 rebasing 3:32af7686d403 "D"
334 rebasing 3:32af7686d403 "D"
335 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
335 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
336
336
337 $ hg tglog
337 $ hg tglog
338 o 8: 668acadedd30 'D'
338 o 8: 668acadedd30 'D'
339 |
339 |
340 o 7: 09eb682ba906 'C'
340 o 7: 09eb682ba906 'C'
341 |
341 |
342 | @ 6: e7ec4e813ba6 'I'
342 | @ 6: e7ec4e813ba6 'I'
343 |/
343 |/
344 o 5: 02de42196ebe 'H'
344 o 5: 02de42196ebe 'H'
345 |
345 |
346 | o 4: eea13746799a 'G'
346 | o 4: eea13746799a 'G'
347 |/|
347 |/|
348 o | 3: 24b6387c8c8c 'F'
348 o | 3: 24b6387c8c8c 'F'
349 | |
349 | |
350 | o 2: 9520eea781bc 'E'
350 | o 2: 9520eea781bc 'E'
351 |/
351 |/
352 | o 1: 42ccdea3bb16 'B'
352 | o 1: 42ccdea3bb16 'B'
353 |/
353 |/
354 o 0: cd010b8cd998 'A'
354 o 0: cd010b8cd998 'A'
355
355
356 $ cd ..
356 $ cd ..
357
357
358
358
359 Specify base and dest (from 1 onto 7):
359 Specify base and dest (from 1 onto 7):
360
360
361 $ hg clone -q -u . a a8
361 $ hg clone -q -u . a a8
362 $ cd a8
362 $ cd a8
363
363
364 $ hg rebase --base 3 --dest 7
364 $ hg rebase --base 3 --dest 7
365 rebasing 1:42ccdea3bb16 "B"
365 rebasing 1:42ccdea3bb16 "B"
366 rebasing 2:5fddd98957c8 "C"
366 rebasing 2:5fddd98957c8 "C"
367 rebasing 3:32af7686d403 "D"
367 rebasing 3:32af7686d403 "D"
368 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
368 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
369
369
370 $ hg tglog
370 $ hg tglog
371 o 8: 287cc92ba5a4 'D'
371 o 8: 287cc92ba5a4 'D'
372 |
372 |
373 o 7: 6824f610a250 'C'
373 o 7: 6824f610a250 'C'
374 |
374 |
375 o 6: 7c6027df6a99 'B'
375 o 6: 7c6027df6a99 'B'
376 |
376 |
377 | @ 5: e7ec4e813ba6 'I'
377 | @ 5: e7ec4e813ba6 'I'
378 |/
378 |/
379 o 4: 02de42196ebe 'H'
379 o 4: 02de42196ebe 'H'
380 |
380 |
381 | o 3: eea13746799a 'G'
381 | o 3: eea13746799a 'G'
382 |/|
382 |/|
383 o | 2: 24b6387c8c8c 'F'
383 o | 2: 24b6387c8c8c 'F'
384 | |
384 | |
385 | o 1: 9520eea781bc 'E'
385 | o 1: 9520eea781bc 'E'
386 |/
386 |/
387 o 0: cd010b8cd998 'A'
387 o 0: cd010b8cd998 'A'
388
388
389 $ cd ..
389 $ cd ..
390
390
391
391
392 Specify only revs (from 2 onto 8)
392 Specify only revs (from 2 onto 8)
393
393
394 $ hg clone -q -u . a2heads a9
394 $ hg clone -q -u . a2heads a9
395 $ cd a9
395 $ cd a9
396
396
397 $ hg rebase --rev 'desc("C")::'
397 $ hg rebase --rev 'desc("C")::'
398 rebasing 2:5fddd98957c8 "C"
398 rebasing 2:5fddd98957c8 "C"
399 rebasing 3:32af7686d403 "D"
399 rebasing 3:32af7686d403 "D"
400 saved backup bundle to $TESTTMP/a9/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
400 saved backup bundle to $TESTTMP/a9/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
401
401
402 $ hg tglog
402 $ hg tglog
403 o 6: 7726e9fd58f7 'D'
403 o 6: 7726e9fd58f7 'D'
404 |
404 |
405 o 5: 72c8333623d0 'C'
405 o 5: 72c8333623d0 'C'
406 |
406 |
407 @ 4: e7ec4e813ba6 'I'
407 @ 4: e7ec4e813ba6 'I'
408 |
408 |
409 o 3: 02de42196ebe 'H'
409 o 3: 02de42196ebe 'H'
410 |
410 |
411 o 2: 24b6387c8c8c 'F'
411 o 2: 24b6387c8c8c 'F'
412 |
412 |
413 | o 1: 42ccdea3bb16 'B'
413 | o 1: 42ccdea3bb16 'B'
414 |/
414 |/
415 o 0: cd010b8cd998 'A'
415 o 0: cd010b8cd998 'A'
416
416
417 $ cd ..
417 $ cd ..
418
418
419 Rebasing both a single revision and a merge in one command
419 Rebasing both a single revision and a merge in one command
420
420
421 $ hg clone -q -u . a aX
421 $ hg clone -q -u . a aX
422 $ cd aX
422 $ cd aX
423 $ hg rebase -r 3 -r 6 --dest 8
423 $ hg rebase -r 3 -r 6 --dest 8
424 rebasing 3:32af7686d403 "D"
424 rebasing 3:32af7686d403 "D"
425 rebasing 6:eea13746799a "G"
425 rebasing 6:eea13746799a "G"
426 saved backup bundle to $TESTTMP/aX/.hg/strip-backup/eea13746799a-ad273fd6-rebase.hg
426 saved backup bundle to $TESTTMP/aX/.hg/strip-backup/eea13746799a-ad273fd6-rebase.hg
427 $ cd ..
427 $ cd ..
428
428
429 Test --tool parameter:
429 Test --tool parameter:
430
430
431 $ hg init b
431 $ hg init b
432 $ cd b
432 $ cd b
433
433
434 $ echo c1 > c1
434 $ echo c1 > c1
435 $ hg ci -Am c1
435 $ hg ci -Am c1
436 adding c1
436 adding c1
437
437
438 $ echo c2 > c2
438 $ echo c2 > c2
439 $ hg ci -Am c2
439 $ hg ci -Am c2
440 adding c2
440 adding c2
441
441
442 $ hg up -q 0
442 $ hg up -q 0
443 $ echo c2b > c2
443 $ echo c2b > c2
444 $ hg ci -Am c2b
444 $ hg ci -Am c2b
445 adding c2
445 adding c2
446 created new head
446 created new head
447
447
448 $ cd ..
448 $ cd ..
449
449
450 $ hg clone -q -u . b b1
450 $ hg clone -q -u . b b1
451 $ cd b1
451 $ cd b1
452
452
453 $ hg rebase -s 2 -d 1 --tool internal:local
453 $ hg rebase -s 2 -d 1 --tool internal:local
454 rebasing 2:e4e3f3546619 tip "c2b"
454 rebasing 2:e4e3f3546619 tip "c2b"
455 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
455 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
456 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
456 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
457
457
458 $ hg cat c2
458 $ hg cat c2
459 c2
459 c2
460
460
461 $ cd ..
461 $ cd ..
462
462
463
463
464 $ hg clone -q -u . b b2
464 $ hg clone -q -u . b b2
465 $ cd b2
465 $ cd b2
466
466
467 $ hg rebase -s 2 -d 1 --tool internal:other
467 $ hg rebase -s 2 -d 1 --tool internal:other
468 rebasing 2:e4e3f3546619 tip "c2b"
468 rebasing 2:e4e3f3546619 tip "c2b"
469 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
469 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
470
470
471 $ hg cat c2
471 $ hg cat c2
472 c2b
472 c2b
473
473
474 $ cd ..
474 $ cd ..
475
475
476
476
477 $ hg clone -q -u . b b3
477 $ hg clone -q -u . b b3
478 $ cd b3
478 $ cd b3
479
479
480 $ hg rebase -s 2 -d 1 --tool internal:fail
480 $ hg rebase -s 2 -d 1 --tool internal:fail
481 rebasing 2:e4e3f3546619 tip "c2b"
481 rebasing 2:e4e3f3546619 tip "c2b"
482 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
482 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
483 [240]
483 [240]
484
484
485 $ hg summary
485 $ hg summary
486 parent: 1:56daeba07f4b
486 parent: 1:56daeba07f4b
487 c2
487 c2
488 branch: default
488 branch: default
489 commit: 1 unresolved (clean)
489 commit: 1 unresolved (clean)
490 update: 1 new changesets, 2 branch heads (merge)
490 update: 1 new changesets, 2 branch heads (merge)
491 phases: 3 draft
491 phases: 3 draft
492 rebase: 0 rebased, 1 remaining (rebase --continue)
492 rebase: 0 rebased, 1 remaining (rebase --continue)
493
493
494 $ hg resolve -l
494 $ hg resolve -l
495 U c2
495 U c2
496
496
497 $ hg resolve -m c2
497 $ hg resolve -m c2
498 (no more unresolved files)
498 (no more unresolved files)
499 continue: hg rebase --continue
499 continue: hg rebase --continue
500 $ hg graft --continue
500 $ hg graft --continue
501 abort: no graft in progress
501 abort: no graft in progress
502 (continue: hg rebase --continue)
502 (continue: hg rebase --continue)
503 [20]
503 [20]
504 $ hg rebase -c --tool internal:fail
504 $ hg rebase -c --tool internal:fail
505 rebasing 2:e4e3f3546619 tip "c2b"
505 rebasing 2:e4e3f3546619 tip "c2b"
506 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
506 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
507 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
507 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
508
508
509 $ hg rebase -i
509 $ hg rebase -i
510 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
510 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
511 [10]
511 [10]
512
512
513 $ hg rebase --interactive
513 $ hg rebase --interactive
514 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
514 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
515 [10]
515 [10]
516
516
517 $ cd ..
517 $ cd ..
518
518
519 No common ancestor
519 No common ancestor
520
520
521 $ hg init separaterepo
521 $ hg init separaterepo
522 $ cd separaterepo
522 $ cd separaterepo
523 $ touch a
523 $ touch a
524 $ hg commit -Aqm a
524 $ hg commit -Aqm a
525 $ hg up -q null
525 $ hg up -q null
526 $ touch b
526 $ touch b
527 $ hg commit -Aqm b
527 $ hg commit -Aqm b
528 $ hg rebase -d 0
528 $ hg rebase -d 0
529 nothing to rebase from d7486e00c6f1 to 3903775176ed
529 nothing to rebase from d7486e00c6f1 to 3903775176ed
530 [1]
530 [1]
531 $ cd ..
531 $ cd ..
@@ -1,1852 +1,1852 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > drawdag=$TESTDIR/drawdag.py
5 > drawdag=$TESTDIR/drawdag.py
6 > EOF
6 > EOF
7
7
8 $ try() {
8 $ try() {
9 > hg debugrevspec --debug "$@"
9 > hg debugrevspec --debug "$@"
10 > }
10 > }
11
11
12 $ log() {
12 $ log() {
13 > hg log --template '{rev}\n' -r "$1"
13 > hg log --template '{rev}\n' -r "$1"
14 > }
14 > }
15
15
16 $ hg init repo
16 $ hg init repo
17 $ cd repo
17 $ cd repo
18
18
19 $ echo a > a
19 $ echo a > a
20 $ hg branch a
20 $ hg branch a
21 marked working directory as branch a
21 marked working directory as branch a
22 (branches are permanent and global, did you want a bookmark?)
22 (branches are permanent and global, did you want a bookmark?)
23 $ hg ci -Aqm0
23 $ hg ci -Aqm0
24
24
25 $ echo b > b
25 $ echo b > b
26 $ hg branch b
26 $ hg branch b
27 marked working directory as branch b
27 marked working directory as branch b
28 $ hg ci -Aqm1
28 $ hg ci -Aqm1
29
29
30 $ rm a
30 $ rm a
31 $ hg branch a-b-c-
31 $ hg branch a-b-c-
32 marked working directory as branch a-b-c-
32 marked working directory as branch a-b-c-
33 $ hg ci -Aqm2 -u Bob
33 $ hg ci -Aqm2 -u Bob
34
34
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
36 2
36 2
37 $ hg log -r "extra('branch')" --template '{rev}\n'
37 $ hg log -r "extra('branch')" --template '{rev}\n'
38 0
38 0
39 1
39 1
40 2
40 2
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
42 0 a
42 0 a
43 2 a-b-c-
43 2 a-b-c-
44
44
45 $ hg co 1
45 $ hg co 1
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg branch +a+b+c+
47 $ hg branch +a+b+c+
48 marked working directory as branch +a+b+c+
48 marked working directory as branch +a+b+c+
49 $ hg ci -Aqm3
49 $ hg ci -Aqm3
50
50
51 $ hg co 2 # interleave
51 $ hg co 2 # interleave
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
53 $ echo bb > b
53 $ echo bb > b
54 $ hg branch -- -a-b-c-
54 $ hg branch -- -a-b-c-
55 marked working directory as branch -a-b-c-
55 marked working directory as branch -a-b-c-
56 $ hg ci -Aqm4 -d "May 12 2005"
56 $ hg ci -Aqm4 -d "May 12 2005"
57
57
58 $ hg co 3
58 $ hg co 3
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch !a/b/c/
60 $ hg branch !a/b/c/
61 marked working directory as branch !a/b/c/
61 marked working directory as branch !a/b/c/
62 $ hg ci -Aqm"5 bug"
62 $ hg ci -Aqm"5 bug"
63
63
64 $ hg merge 4
64 $ hg merge 4
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 (branch merge, don't forget to commit)
66 (branch merge, don't forget to commit)
67 $ hg branch _a_b_c_
67 $ hg branch _a_b_c_
68 marked working directory as branch _a_b_c_
68 marked working directory as branch _a_b_c_
69 $ hg ci -Aqm"6 issue619"
69 $ hg ci -Aqm"6 issue619"
70
70
71 $ hg branch .a.b.c.
71 $ hg branch .a.b.c.
72 marked working directory as branch .a.b.c.
72 marked working directory as branch .a.b.c.
73 $ hg ci -Aqm7
73 $ hg ci -Aqm7
74
74
75 $ hg branch all
75 $ hg branch all
76 marked working directory as branch all
76 marked working directory as branch all
77
77
78 $ hg co 4
78 $ hg co 4
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 $ hg branch Γ©
80 $ hg branch Γ©
81 marked working directory as branch \xc3\xa9 (esc)
81 marked working directory as branch \xc3\xa9 (esc)
82 $ hg ci -Aqm9
82 $ hg ci -Aqm9
83
83
84 $ hg tag -r6 1.0
84 $ hg tag -r6 1.0
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
86
86
87 $ hg clone --quiet -U -r 7 . ../remote1
87 $ hg clone --quiet -U -r 7 . ../remote1
88 $ hg clone --quiet -U -r 8 . ../remote2
88 $ hg clone --quiet -U -r 8 . ../remote2
89 $ echo "[paths]" >> .hg/hgrc
89 $ echo "[paths]" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
91
91
92 test subtracting something from an addset
92 test subtracting something from an addset
93
93
94 $ log '(outgoing() or removes(a)) - removes(a)'
94 $ log '(outgoing() or removes(a)) - removes(a)'
95 8
95 8
96 9
96 9
97
97
98 test intersecting something with an addset
98 test intersecting something with an addset
99
99
100 $ log 'parents(outgoing() or removes(a))'
100 $ log 'parents(outgoing() or removes(a))'
101 1
101 1
102 4
102 4
103 8
103 8
104
104
105 test that `or` operation combines elements in the right order:
105 test that `or` operation combines elements in the right order:
106
106
107 $ log '3:4 or 2:5'
107 $ log '3:4 or 2:5'
108 3
108 3
109 4
109 4
110 2
110 2
111 5
111 5
112 $ log '3:4 or 5:2'
112 $ log '3:4 or 5:2'
113 3
113 3
114 4
114 4
115 5
115 5
116 2
116 2
117 $ log 'sort(3:4 or 2:5)'
117 $ log 'sort(3:4 or 2:5)'
118 2
118 2
119 3
119 3
120 4
120 4
121 5
121 5
122 $ log 'sort(3:4 or 5:2)'
122 $ log 'sort(3:4 or 5:2)'
123 2
123 2
124 3
124 3
125 4
125 4
126 5
126 5
127
127
128 test that more than one `-r`s are combined in the right order and deduplicated:
128 test that more than one `-r`s are combined in the right order and deduplicated:
129
129
130 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
130 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
131 3
131 3
132 4
132 4
133 5
133 5
134 2
134 2
135 0
135 0
136 1
136 1
137
137
138 test that `or` operation skips duplicated revisions from right-hand side
138 test that `or` operation skips duplicated revisions from right-hand side
139
139
140 $ try 'reverse(1::5) or ancestors(4)'
140 $ try 'reverse(1::5) or ancestors(4)'
141 (or
141 (or
142 (list
142 (list
143 (func
143 (func
144 (symbol 'reverse')
144 (symbol 'reverse')
145 (dagrange
145 (dagrange
146 (symbol '1')
146 (symbol '1')
147 (symbol '5')))
147 (symbol '5')))
148 (func
148 (func
149 (symbol 'ancestors')
149 (symbol 'ancestors')
150 (symbol '4'))))
150 (symbol '4'))))
151 * set:
151 * set:
152 <addset
152 <addset
153 <baseset- [1, 3, 5]>,
153 <baseset- [1, 3, 5]>,
154 <generatorsetdesc+>>
154 <generatorsetdesc+>>
155 5
155 5
156 3
156 3
157 1
157 1
158 0
158 0
159 2
159 2
160 4
160 4
161 $ try 'sort(ancestors(4) or reverse(1::5))'
161 $ try 'sort(ancestors(4) or reverse(1::5))'
162 (func
162 (func
163 (symbol 'sort')
163 (symbol 'sort')
164 (or
164 (or
165 (list
165 (list
166 (func
166 (func
167 (symbol 'ancestors')
167 (symbol 'ancestors')
168 (symbol '4'))
168 (symbol '4'))
169 (func
169 (func
170 (symbol 'reverse')
170 (symbol 'reverse')
171 (dagrange
171 (dagrange
172 (symbol '1')
172 (symbol '1')
173 (symbol '5'))))))
173 (symbol '5'))))))
174 * set:
174 * set:
175 <addset+
175 <addset+
176 <generatorsetdesc+>,
176 <generatorsetdesc+>,
177 <baseset- [1, 3, 5]>>
177 <baseset- [1, 3, 5]>>
178 0
178 0
179 1
179 1
180 2
180 2
181 3
181 3
182 4
182 4
183 5
183 5
184
184
185 test optimization of trivial `or` operation
185 test optimization of trivial `or` operation
186
186
187 $ try --optimize '0|(1)|"2"|-2|tip|null'
187 $ try --optimize '0|(1)|"2"|-2|tip|null'
188 (or
188 (or
189 (list
189 (list
190 (symbol '0')
190 (symbol '0')
191 (group
191 (group
192 (symbol '1'))
192 (symbol '1'))
193 (string '2')
193 (string '2')
194 (negate
194 (negate
195 (symbol '2'))
195 (symbol '2'))
196 (symbol 'tip')
196 (symbol 'tip')
197 (symbol 'null')))
197 (symbol 'null')))
198 * optimized:
198 * optimized:
199 (func
199 (func
200 (symbol '_list')
200 (symbol '_list')
201 (string '0\x001\x002\x00-2\x00tip\x00null'))
201 (string '0\x001\x002\x00-2\x00tip\x00null'))
202 * set:
202 * set:
203 <baseset [0, 1, 2, 8, 9, -1]>
203 <baseset [0, 1, 2, 8, 9, -1]>
204 0
204 0
205 1
205 1
206 2
206 2
207 8
207 8
208 9
208 9
209 -1
209 -1
210
210
211 $ try --optimize '0|1|2:3'
211 $ try --optimize '0|1|2:3'
212 (or
212 (or
213 (list
213 (list
214 (symbol '0')
214 (symbol '0')
215 (symbol '1')
215 (symbol '1')
216 (range
216 (range
217 (symbol '2')
217 (symbol '2')
218 (symbol '3'))))
218 (symbol '3'))))
219 * optimized:
219 * optimized:
220 (or
220 (or
221 (list
221 (list
222 (func
222 (func
223 (symbol '_list')
223 (symbol '_list')
224 (string '0\x001'))
224 (string '0\x001'))
225 (range
225 (range
226 (symbol '2')
226 (symbol '2')
227 (symbol '3'))))
227 (symbol '3'))))
228 * set:
228 * set:
229 <addset
229 <addset
230 <baseset [0, 1]>,
230 <baseset [0, 1]>,
231 <spanset+ 2:4>>
231 <spanset+ 2:4>>
232 0
232 0
233 1
233 1
234 2
234 2
235 3
235 3
236
236
237 $ try --optimize '0:1|2|3:4|5|6'
237 $ try --optimize '0:1|2|3:4|5|6'
238 (or
238 (or
239 (list
239 (list
240 (range
240 (range
241 (symbol '0')
241 (symbol '0')
242 (symbol '1'))
242 (symbol '1'))
243 (symbol '2')
243 (symbol '2')
244 (range
244 (range
245 (symbol '3')
245 (symbol '3')
246 (symbol '4'))
246 (symbol '4'))
247 (symbol '5')
247 (symbol '5')
248 (symbol '6')))
248 (symbol '6')))
249 * optimized:
249 * optimized:
250 (or
250 (or
251 (list
251 (list
252 (range
252 (range
253 (symbol '0')
253 (symbol '0')
254 (symbol '1'))
254 (symbol '1'))
255 (symbol '2')
255 (symbol '2')
256 (range
256 (range
257 (symbol '3')
257 (symbol '3')
258 (symbol '4'))
258 (symbol '4'))
259 (func
259 (func
260 (symbol '_list')
260 (symbol '_list')
261 (string '5\x006'))))
261 (string '5\x006'))))
262 * set:
262 * set:
263 <addset
263 <addset
264 <addset
264 <addset
265 <spanset+ 0:2>,
265 <spanset+ 0:2>,
266 <baseset [2]>>,
266 <baseset [2]>>,
267 <addset
267 <addset
268 <spanset+ 3:5>,
268 <spanset+ 3:5>,
269 <baseset [5, 6]>>>
269 <baseset [5, 6]>>>
270 0
270 0
271 1
271 1
272 2
272 2
273 3
273 3
274 4
274 4
275 5
275 5
276 6
276 6
277
277
278 unoptimized `or` looks like this
278 unoptimized `or` looks like this
279
279
280 $ try --no-optimized -p analyzed '0|1|2|3|4'
280 $ try --no-optimized -p analyzed '0|1|2|3|4'
281 * analyzed:
281 * analyzed:
282 (or
282 (or
283 (list
283 (list
284 (symbol '0')
284 (symbol '0')
285 (symbol '1')
285 (symbol '1')
286 (symbol '2')
286 (symbol '2')
287 (symbol '3')
287 (symbol '3')
288 (symbol '4')))
288 (symbol '4')))
289 * set:
289 * set:
290 <addset
290 <addset
291 <addset
291 <addset
292 <baseset [0]>,
292 <baseset [0]>,
293 <baseset [1]>>,
293 <baseset [1]>>,
294 <addset
294 <addset
295 <baseset [2]>,
295 <baseset [2]>,
296 <addset
296 <addset
297 <baseset [3]>,
297 <baseset [3]>,
298 <baseset [4]>>>>
298 <baseset [4]>>>>
299 0
299 0
300 1
300 1
301 2
301 2
302 3
302 3
303 4
303 4
304
304
305 test that `_list` should be narrowed by provided `subset`
305 test that `_list` should be narrowed by provided `subset`
306
306
307 $ log '0:2 and (null|1|2|3)'
307 $ log '0:2 and (null|1|2|3)'
308 1
308 1
309 2
309 2
310
310
311 test that `_list` should remove duplicates
311 test that `_list` should remove duplicates
312
312
313 $ log '0|1|2|1|2|-1|tip'
313 $ log '0|1|2|1|2|-1|tip'
314 0
314 0
315 1
315 1
316 2
316 2
317 9
317 9
318
318
319 test unknown revision in `_list`
319 test unknown revision in `_list`
320
320
321 $ log '0|unknown'
321 $ log '0|unknown'
322 abort: unknown revision 'unknown'
322 abort: unknown revision 'unknown'
323 [255]
323 [255]
324
324
325 test integer range in `_list`
325 test integer range in `_list`
326
326
327 $ log '-1|-10'
327 $ log '-1|-10'
328 9
328 9
329 0
329 0
330
330
331 $ log '-10|-11'
331 $ log '-10|-11'
332 abort: unknown revision '-11'
332 abort: unknown revision '-11'
333 [255]
333 [255]
334
334
335 $ log '9|10'
335 $ log '9|10'
336 abort: unknown revision '10'
336 abort: unknown revision '10'
337 [255]
337 [255]
338
338
339 test '0000' != '0' in `_list`
339 test '0000' != '0' in `_list`
340
340
341 $ log '0|0000'
341 $ log '0|0000'
342 0
342 0
343 -1
343 -1
344
344
345 test ',' in `_list`
345 test ',' in `_list`
346 $ log '0,1'
346 $ log '0,1'
347 hg: parse error: can't use a list in this context
347 hg: parse error: can't use a list in this context
348 (see 'hg help "revsets.x or y"')
348 (see 'hg help "revsets.x or y"')
349 [10]
349 [10]
350 $ try '0,1,2'
350 $ try '0,1,2'
351 (list
351 (list
352 (symbol '0')
352 (symbol '0')
353 (symbol '1')
353 (symbol '1')
354 (symbol '2'))
354 (symbol '2'))
355 hg: parse error: can't use a list in this context
355 hg: parse error: can't use a list in this context
356 (see 'hg help "revsets.x or y"')
356 (see 'hg help "revsets.x or y"')
357 [10]
357 [10]
358
358
359 test that chained `or` operations make balanced addsets
359 test that chained `or` operations make balanced addsets
360
360
361 $ try '0:1|1:2|2:3|3:4|4:5'
361 $ try '0:1|1:2|2:3|3:4|4:5'
362 (or
362 (or
363 (list
363 (list
364 (range
364 (range
365 (symbol '0')
365 (symbol '0')
366 (symbol '1'))
366 (symbol '1'))
367 (range
367 (range
368 (symbol '1')
368 (symbol '1')
369 (symbol '2'))
369 (symbol '2'))
370 (range
370 (range
371 (symbol '2')
371 (symbol '2')
372 (symbol '3'))
372 (symbol '3'))
373 (range
373 (range
374 (symbol '3')
374 (symbol '3')
375 (symbol '4'))
375 (symbol '4'))
376 (range
376 (range
377 (symbol '4')
377 (symbol '4')
378 (symbol '5'))))
378 (symbol '5'))))
379 * set:
379 * set:
380 <addset
380 <addset
381 <addset
381 <addset
382 <spanset+ 0:2>,
382 <spanset+ 0:2>,
383 <spanset+ 1:3>>,
383 <spanset+ 1:3>>,
384 <addset
384 <addset
385 <spanset+ 2:4>,
385 <spanset+ 2:4>,
386 <addset
386 <addset
387 <spanset+ 3:5>,
387 <spanset+ 3:5>,
388 <spanset+ 4:6>>>>
388 <spanset+ 4:6>>>>
389 0
389 0
390 1
390 1
391 2
391 2
392 3
392 3
393 4
393 4
394 5
394 5
395
395
396 no crash by empty group "()" while optimizing `or` operations
396 no crash by empty group "()" while optimizing `or` operations
397
397
398 $ try --optimize '0|()'
398 $ try --optimize '0|()'
399 (or
399 (or
400 (list
400 (list
401 (symbol '0')
401 (symbol '0')
402 (group
402 (group
403 None)))
403 None)))
404 * optimized:
404 * optimized:
405 (or
405 (or
406 (list
406 (list
407 (symbol '0')
407 (symbol '0')
408 None))
408 None))
409 hg: parse error: missing argument
409 hg: parse error: missing argument
410 [10]
410 [10]
411
411
412 test that chained `or` operations never eat up stack (issue4624)
412 test that chained `or` operations never eat up stack (issue4624)
413 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
413 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
414
414
415 $ hg log -T '{rev}\n' -r `"$PYTHON" -c "print('+'.join(['0:1'] * 500))"`
415 $ hg log -T '{rev}\n' -r `"$PYTHON" -c "print('+'.join(['0:1'] * 500))"`
416 0
416 0
417 1
417 1
418
418
419 test that repeated `-r` options never eat up stack (issue4565)
419 test that repeated `-r` options never eat up stack (issue4565)
420 (uses `-r 0::1` to avoid possible optimization at old-style parser)
420 (uses `-r 0::1` to avoid possible optimization at old-style parser)
421
421
422 $ hg log -T '{rev}\n' `"$PYTHON" -c "for i in range(500): print('-r 0::1 ')"`
422 $ hg log -T '{rev}\n' `"$PYTHON" -c "for i in range(500): print('-r 0::1 ')"`
423 0
423 0
424 1
424 1
425
425
426 check that conversion to only works
426 check that conversion to only works
427 $ try --optimize '::3 - ::1'
427 $ try --optimize '::3 - ::1'
428 (minus
428 (minus
429 (dagrangepre
429 (dagrangepre
430 (symbol '3'))
430 (symbol '3'))
431 (dagrangepre
431 (dagrangepre
432 (symbol '1')))
432 (symbol '1')))
433 * optimized:
433 * optimized:
434 (func
434 (func
435 (symbol 'only')
435 (symbol 'only')
436 (list
436 (list
437 (symbol '3')
437 (symbol '3')
438 (symbol '1')))
438 (symbol '1')))
439 * set:
439 * set:
440 <baseset+ [3]>
440 <baseset+ [3]>
441 3
441 3
442 $ try --optimize 'ancestors(1) - ancestors(3)'
442 $ try --optimize 'ancestors(1) - ancestors(3)'
443 (minus
443 (minus
444 (func
444 (func
445 (symbol 'ancestors')
445 (symbol 'ancestors')
446 (symbol '1'))
446 (symbol '1'))
447 (func
447 (func
448 (symbol 'ancestors')
448 (symbol 'ancestors')
449 (symbol '3')))
449 (symbol '3')))
450 * optimized:
450 * optimized:
451 (func
451 (func
452 (symbol 'only')
452 (symbol 'only')
453 (list
453 (list
454 (symbol '1')
454 (symbol '1')
455 (symbol '3')))
455 (symbol '3')))
456 * set:
456 * set:
457 <baseset+ []>
457 <baseset+ []>
458 $ try --optimize 'not ::2 and ::6'
458 $ try --optimize 'not ::2 and ::6'
459 (and
459 (and
460 (not
460 (not
461 (dagrangepre
461 (dagrangepre
462 (symbol '2')))
462 (symbol '2')))
463 (dagrangepre
463 (dagrangepre
464 (symbol '6')))
464 (symbol '6')))
465 * optimized:
465 * optimized:
466 (func
466 (func
467 (symbol 'only')
467 (symbol 'only')
468 (list
468 (list
469 (symbol '6')
469 (symbol '6')
470 (symbol '2')))
470 (symbol '2')))
471 * set:
471 * set:
472 <baseset+ [3, 4, 5, 6]>
472 <baseset+ [3, 4, 5, 6]>
473 3
473 3
474 4
474 4
475 5
475 5
476 6
476 6
477 $ try --optimize 'ancestors(6) and not ancestors(4)'
477 $ try --optimize 'ancestors(6) and not ancestors(4)'
478 (and
478 (and
479 (func
479 (func
480 (symbol 'ancestors')
480 (symbol 'ancestors')
481 (symbol '6'))
481 (symbol '6'))
482 (not
482 (not
483 (func
483 (func
484 (symbol 'ancestors')
484 (symbol 'ancestors')
485 (symbol '4'))))
485 (symbol '4'))))
486 * optimized:
486 * optimized:
487 (func
487 (func
488 (symbol 'only')
488 (symbol 'only')
489 (list
489 (list
490 (symbol '6')
490 (symbol '6')
491 (symbol '4')))
491 (symbol '4')))
492 * set:
492 * set:
493 <baseset+ [3, 5, 6]>
493 <baseset+ [3, 5, 6]>
494 3
494 3
495 5
495 5
496 6
496 6
497
497
498 no crash by empty group "()" while optimizing to "only()"
498 no crash by empty group "()" while optimizing to "only()"
499
499
500 $ try --optimize '::1 and ()'
500 $ try --optimize '::1 and ()'
501 (and
501 (and
502 (dagrangepre
502 (dagrangepre
503 (symbol '1'))
503 (symbol '1'))
504 (group
504 (group
505 None))
505 None))
506 * optimized:
506 * optimized:
507 (andsmally
507 (andsmally
508 (func
508 (func
509 (symbol 'ancestors')
509 (symbol 'ancestors')
510 (symbol '1'))
510 (symbol '1'))
511 None)
511 None)
512 hg: parse error: missing argument
512 hg: parse error: missing argument
513 [10]
513 [10]
514
514
515 optimization to only() works only if ancestors() takes only one argument
515 optimization to only() works only if ancestors() takes only one argument
516
516
517 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
517 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
518 * optimized:
518 * optimized:
519 (difference
519 (difference
520 (func
520 (func
521 (symbol 'ancestors')
521 (symbol 'ancestors')
522 (symbol '6'))
522 (symbol '6'))
523 (func
523 (func
524 (symbol 'ancestors')
524 (symbol 'ancestors')
525 (list
525 (list
526 (symbol '4')
526 (symbol '4')
527 (symbol '1'))))
527 (symbol '1'))))
528 0
528 0
529 1
529 1
530 3
530 3
531 5
531 5
532 6
532 6
533 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
533 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
534 * optimized:
534 * optimized:
535 (difference
535 (difference
536 (func
536 (func
537 (symbol 'ancestors')
537 (symbol 'ancestors')
538 (list
538 (list
539 (symbol '6')
539 (symbol '6')
540 (symbol '1')))
540 (symbol '1')))
541 (func
541 (func
542 (symbol 'ancestors')
542 (symbol 'ancestors')
543 (symbol '4')))
543 (symbol '4')))
544 5
544 5
545 6
545 6
546
546
547 optimization disabled if keyword arguments passed (because we're too lazy
547 optimization disabled if keyword arguments passed (because we're too lazy
548 to support it)
548 to support it)
549
549
550 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
550 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
551 * optimized:
551 * optimized:
552 (difference
552 (difference
553 (func
553 (func
554 (symbol 'ancestors')
554 (symbol 'ancestors')
555 (keyvalue
555 (keyvalue
556 (symbol 'set')
556 (symbol 'set')
557 (symbol '6')))
557 (symbol '6')))
558 (func
558 (func
559 (symbol 'ancestors')
559 (symbol 'ancestors')
560 (keyvalue
560 (keyvalue
561 (symbol 'set')
561 (symbol 'set')
562 (symbol '4'))))
562 (symbol '4'))))
563 3
563 3
564 5
564 5
565 6
565 6
566
566
567 invalid function call should not be optimized to only()
567 invalid function call should not be optimized to only()
568
568
569 $ log '"ancestors"(6) and not ancestors(4)'
569 $ log '"ancestors"(6) and not ancestors(4)'
570 hg: parse error: not a symbol
570 hg: parse error: not a symbol
571 [10]
571 [10]
572
572
573 $ log 'ancestors(6) and not "ancestors"(4)'
573 $ log 'ancestors(6) and not "ancestors"(4)'
574 hg: parse error: not a symbol
574 hg: parse error: not a symbol
575 [10]
575 [10]
576
576
577 test empty string
577 test empty string
578
578
579 $ log ''
579 $ log ''
580 hg: parse error: empty query
580 hg: parse error: empty query
581 [10]
581 [10]
582 $ log 'parents("")'
582 $ log 'parents("")'
583 hg: parse error: empty string is not a valid revision
583 hg: parse error: empty string is not a valid revision
584 [10]
584 [10]
585
585
586 test empty revset
586 test empty revset
587 $ hg log 'none()'
587 $ hg log 'none()'
588
588
589 we can use patterns when searching for tags
589 we can use patterns when searching for tags
590
590
591 $ log 'tag("1..*")'
591 $ log 'tag("1..*")'
592 abort: tag '1..*' does not exist
592 abort: tag '1..*' does not exist
593 [255]
593 [255]
594 $ log 'tag("re:1..*")'
594 $ log 'tag("re:1..*")'
595 6
595 6
596 $ log 'tag("re:[0-9].[0-9]")'
596 $ log 'tag("re:[0-9].[0-9]")'
597 6
597 6
598 $ log 'tag("literal:1.0")'
598 $ log 'tag("literal:1.0")'
599 6
599 6
600 $ log 'tag("re:0..*")'
600 $ log 'tag("re:0..*")'
601
601
602 $ log 'tag(unknown)'
602 $ log 'tag(unknown)'
603 abort: tag 'unknown' does not exist
603 abort: tag 'unknown' does not exist
604 [255]
604 [255]
605 $ log 'tag("re:unknown")'
605 $ log 'tag("re:unknown")'
606 $ log 'present(tag("unknown"))'
606 $ log 'present(tag("unknown"))'
607 $ log 'present(tag("re:unknown"))'
607 $ log 'present(tag("re:unknown"))'
608 $ log 'branch(unknown)'
608 $ log 'branch(unknown)'
609 abort: unknown revision 'unknown'
609 abort: unknown revision 'unknown'
610 [255]
610 [255]
611 $ log 'branch("literal:unknown")'
611 $ log 'branch("literal:unknown")'
612 abort: branch 'unknown' does not exist
612 abort: branch 'unknown' does not exist
613 [255]
613 [255]
614 $ log 'branch("re:unknown")'
614 $ log 'branch("re:unknown")'
615 $ log 'present(branch("unknown"))'
615 $ log 'present(branch("unknown"))'
616 $ log 'present(branch("re:unknown"))'
616 $ log 'present(branch("re:unknown"))'
617 $ log 'user(bob)'
617 $ log 'user(bob)'
618 2
618 2
619
619
620 $ log '4::8'
620 $ log '4::8'
621 4
621 4
622 8
622 8
623 $ log '4:8'
623 $ log '4:8'
624 4
624 4
625 5
625 5
626 6
626 6
627 7
627 7
628 8
628 8
629
629
630 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
630 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
631 4
631 4
632 2
632 2
633 5
633 5
634
634
635 $ log 'not 0 and 0:2'
635 $ log 'not 0 and 0:2'
636 1
636 1
637 2
637 2
638 $ log 'not 1 and 0:2'
638 $ log 'not 1 and 0:2'
639 0
639 0
640 2
640 2
641 $ log 'not 2 and 0:2'
641 $ log 'not 2 and 0:2'
642 0
642 0
643 1
643 1
644 $ log '(1 and 2)::'
644 $ log '(1 and 2)::'
645 $ log '(1 and 2):'
645 $ log '(1 and 2):'
646 $ log '(1 and 2):3'
646 $ log '(1 and 2):3'
647 $ log 'sort(head(), -rev)'
647 $ log 'sort(head(), -rev)'
648 9
648 9
649 7
649 7
650 6
650 6
651 5
651 5
652 4
652 4
653 3
653 3
654 2
654 2
655 1
655 1
656 0
656 0
657 $ log '4::8 - 8'
657 $ log '4::8 - 8'
658 4
658 4
659
659
660 matching() should preserve the order of the input set:
660 matching() should preserve the order of the input set:
661
661
662 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
662 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
663 2
663 2
664 3
664 3
665 1
665 1
666
666
667 $ log 'named("unknown")'
667 $ log 'named("unknown")'
668 abort: namespace 'unknown' does not exist
668 abort: namespace 'unknown' does not exist
669 [255]
669 [255]
670 $ log 'named("re:unknown")'
670 $ log 'named("re:unknown")'
671 $ log 'present(named("unknown"))'
671 $ log 'present(named("unknown"))'
672 $ log 'present(named("re:unknown"))'
672 $ log 'present(named("re:unknown"))'
673
673
674 $ log 'tag()'
674 $ log 'tag()'
675 6
675 6
676 $ log 'named("tags")'
676 $ log 'named("tags")'
677 6
677 6
678
678
679 issue2437
679 issue2437
680
680
681 $ log '3 and p1(5)'
681 $ log '3 and p1(5)'
682 3
682 3
683 $ log '4 and p2(6)'
683 $ log '4 and p2(6)'
684 4
684 4
685 $ log '1 and parents(:2)'
685 $ log '1 and parents(:2)'
686 1
686 1
687 $ log '2 and children(1:)'
687 $ log '2 and children(1:)'
688 2
688 2
689 $ log 'roots(all()) or roots(all())'
689 $ log 'roots(all()) or roots(all())'
690 0
690 0
691 $ hg debugrevspec 'roots(all()) or roots(all())'
691 $ hg debugrevspec 'roots(all()) or roots(all())'
692 0
692 0
693 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
693 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
694 9
694 9
695 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
695 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
696 4
696 4
697
697
698 issue2654: report a parse error if the revset was not completely parsed
698 issue2654: report a parse error if the revset was not completely parsed
699
699
700 $ log '1 OR 2'
700 $ log '1 OR 2'
701 hg: parse error at 2: invalid token
701 hg: parse error at 2: invalid token
702 (1 OR 2
702 (1 OR 2
703 ^ here)
703 ^ here)
704 [10]
704 [10]
705
705
706 or operator should preserve ordering:
706 or operator should preserve ordering:
707 $ log 'reverse(2::4) or tip'
707 $ log 'reverse(2::4) or tip'
708 4
708 4
709 2
709 2
710 9
710 9
711
711
712 parentrevspec
712 parentrevspec
713
713
714 $ log 'merge()^0'
714 $ log 'merge()^0'
715 6
715 6
716 $ log 'merge()^'
716 $ log 'merge()^'
717 5
717 5
718 $ log 'merge()^1'
718 $ log 'merge()^1'
719 5
719 5
720 $ log 'merge()^2'
720 $ log 'merge()^2'
721 4
721 4
722 $ log '(not merge())^2'
722 $ log '(not merge())^2'
723 $ log 'merge()^^'
723 $ log 'merge()^^'
724 3
724 3
725 $ log 'merge()^1^'
725 $ log 'merge()^1^'
726 3
726 3
727 $ log 'merge()^^^'
727 $ log 'merge()^^^'
728 1
728 1
729
729
730 $ hg debugrevspec -s '(merge() | 0)~-1'
730 $ hg debugrevspec -s '(merge() | 0)~-1'
731 * set:
731 * set:
732 <baseset+ [1, 7]>
732 <baseset+ [1, 7]>
733 1
733 1
734 7
734 7
735 $ log 'merge()~-1'
735 $ log 'merge()~-1'
736 7
736 7
737 $ log 'tip~-1'
737 $ log 'tip~-1'
738 $ log '(tip | merge())~-1'
738 $ log '(tip | merge())~-1'
739 7
739 7
740 $ log 'merge()~0'
740 $ log 'merge()~0'
741 6
741 6
742 $ log 'merge()~1'
742 $ log 'merge()~1'
743 5
743 5
744 $ log 'merge()~2'
744 $ log 'merge()~2'
745 3
745 3
746 $ log 'merge()~2^1'
746 $ log 'merge()~2^1'
747 1
747 1
748 $ log 'merge()~3'
748 $ log 'merge()~3'
749 1
749 1
750
750
751 $ log '(-3:tip)^'
751 $ log '(-3:tip)^'
752 4
752 4
753 6
753 6
754 8
754 8
755
755
756 $ log 'tip^foo'
756 $ log 'tip^foo'
757 hg: parse error: ^ expects a number 0, 1, or 2
757 hg: parse error: ^ expects a number 0, 1, or 2
758 [10]
758 [10]
759
759
760 $ log 'branchpoint()~-1'
760 $ log 'branchpoint()~-1'
761 abort: revision in set has more than one child
761 abort: revision in set has more than one child
762 [255]
762 [255]
763
763
764 Bogus function gets suggestions
764 Bogus function gets suggestions
765 $ log 'add()'
765 $ log 'add()'
766 hg: parse error: unknown identifier: add
766 hg: parse error: unknown identifier: add
767 (did you mean adds?)
767 (did you mean adds?)
768 [10]
768 [10]
769 $ log 'added()'
769 $ log 'added()'
770 hg: parse error: unknown identifier: added
770 hg: parse error: unknown identifier: added
771 (did you mean adds?)
771 (did you mean adds?)
772 [10]
772 [10]
773 $ log 'remo()'
773 $ log 'remo()'
774 hg: parse error: unknown identifier: remo
774 hg: parse error: unknown identifier: remo
775 (did you mean one of remote, removes?)
775 (did you mean one of remote, removes?)
776 [10]
776 [10]
777 $ log 'babar()'
777 $ log 'babar()'
778 hg: parse error: unknown identifier: babar
778 hg: parse error: unknown identifier: babar
779 [10]
779 [10]
780
780
781 Bogus function with a similar internal name doesn't suggest the internal name
781 Bogus function with a similar internal name doesn't suggest the internal name
782 $ log 'matches()'
782 $ log 'matches()'
783 hg: parse error: unknown identifier: matches
783 hg: parse error: unknown identifier: matches
784 (did you mean matching?)
784 (did you mean matching?)
785 [10]
785 [10]
786
786
787 Undocumented functions aren't suggested as similar either
787 Undocumented functions aren't suggested as similar either
788 $ log 'tagged2()'
788 $ log 'tagged2()'
789 hg: parse error: unknown identifier: tagged2
789 hg: parse error: unknown identifier: tagged2
790 [10]
790 [10]
791
791
792 multiple revspecs
792 multiple revspecs
793
793
794 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
794 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
795 8
795 8
796 9
796 9
797 4
797 4
798 5
798 5
799 6
799 6
800 7
800 7
801
801
802 test usage in revpair (with "+")
802 test usage in revpair (with "+")
803
803
804 (real pair)
804 (real pair)
805
805
806 $ hg diff -r 'tip^^' -r 'tip'
806 $ hg diff -r 'tip^^' -r 'tip'
807 diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
807 diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
808 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
808 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
809 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
809 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
810 @@ -0,0 +1,1 @@
810 @@ -0,0 +1,1 @@
811 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
811 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
812 $ hg diff -r 'tip^^::tip'
812 $ hg diff -r 'tip^^::tip'
813 diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
813 diff -r 2326846efdab -r d2e607fcf9e4 .hgtags
814 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
814 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
815 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
815 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
816 @@ -0,0 +1,1 @@
816 @@ -0,0 +1,1 @@
817 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
817 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
818
818
819 (single rev)
819 (single rev)
820
820
821 $ hg diff -r 'tip^' -r 'tip^'
821 $ hg diff -r 'tip^' -r 'tip^'
822 $ hg diff -r 'tip^:tip^'
822 $ hg diff -r 'tip^:tip^'
823
823
824 (single rev that does not looks like a range)
824 (single rev that does not looks like a range)
825
825
826 $ hg diff -r 'tip^::tip^ or tip^'
826 $ hg diff -r 'tip^::tip^ or tip^'
827 diff -r d5d0dcbdc4d9 .hgtags
827 diff -r d5d0dcbdc4d9 .hgtags
828 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
828 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
829 +++ b/.hgtags * (glob)
829 +++ b/.hgtags * (glob)
830 @@ -0,0 +1,1 @@
830 @@ -0,0 +1,1 @@
831 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
831 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
832 $ hg diff -r 'tip^ or tip^'
832 $ hg diff -r 'tip^ or tip^'
833 diff -r d5d0dcbdc4d9 .hgtags
833 diff -r d5d0dcbdc4d9 .hgtags
834 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
834 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
835 +++ b/.hgtags * (glob)
835 +++ b/.hgtags * (glob)
836 @@ -0,0 +1,1 @@
836 @@ -0,0 +1,1 @@
837 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
837 +d5e6808a86077d6f5c1ff626d4352d01da7d2a1f 1.0
838
838
839 (no rev)
839 (no rev)
840
840
841 $ hg diff -r 'author("babar") or author("celeste")'
841 $ hg diff -r 'author("babar") or author("celeste")'
842 abort: empty revision range
842 abort: empty revision range
843 [255]
843 [10]
844
844
845 aliases:
845 aliases:
846
846
847 $ echo '[revsetalias]' >> .hg/hgrc
847 $ echo '[revsetalias]' >> .hg/hgrc
848 $ echo 'm = merge()' >> .hg/hgrc
848 $ echo 'm = merge()' >> .hg/hgrc
849 (revset aliases can override builtin revsets)
849 (revset aliases can override builtin revsets)
850 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
850 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
851 $ echo 'sincem = descendants(m)' >> .hg/hgrc
851 $ echo 'sincem = descendants(m)' >> .hg/hgrc
852 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
852 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
853 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
853 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
854 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
854 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
855
855
856 $ try m
856 $ try m
857 (symbol 'm')
857 (symbol 'm')
858 * expanded:
858 * expanded:
859 (func
859 (func
860 (symbol 'merge')
860 (symbol 'merge')
861 None)
861 None)
862 * set:
862 * set:
863 <filteredset
863 <filteredset
864 <fullreposet+ 0:10>,
864 <fullreposet+ 0:10>,
865 <merge>>
865 <merge>>
866 6
866 6
867
867
868 $ HGPLAIN=1
868 $ HGPLAIN=1
869 $ export HGPLAIN
869 $ export HGPLAIN
870 $ try m
870 $ try m
871 (symbol 'm')
871 (symbol 'm')
872 abort: unknown revision 'm'
872 abort: unknown revision 'm'
873 [255]
873 [255]
874
874
875 $ HGPLAINEXCEPT=revsetalias
875 $ HGPLAINEXCEPT=revsetalias
876 $ export HGPLAINEXCEPT
876 $ export HGPLAINEXCEPT
877 $ try m
877 $ try m
878 (symbol 'm')
878 (symbol 'm')
879 * expanded:
879 * expanded:
880 (func
880 (func
881 (symbol 'merge')
881 (symbol 'merge')
882 None)
882 None)
883 * set:
883 * set:
884 <filteredset
884 <filteredset
885 <fullreposet+ 0:10>,
885 <fullreposet+ 0:10>,
886 <merge>>
886 <merge>>
887 6
887 6
888
888
889 $ unset HGPLAIN
889 $ unset HGPLAIN
890 $ unset HGPLAINEXCEPT
890 $ unset HGPLAINEXCEPT
891
891
892 $ try 'p2(.)'
892 $ try 'p2(.)'
893 (func
893 (func
894 (symbol 'p2')
894 (symbol 'p2')
895 (symbol '.'))
895 (symbol '.'))
896 * expanded:
896 * expanded:
897 (func
897 (func
898 (symbol 'p1')
898 (symbol 'p1')
899 (symbol '.'))
899 (symbol '.'))
900 * set:
900 * set:
901 <baseset+ [8]>
901 <baseset+ [8]>
902 8
902 8
903
903
904 $ HGPLAIN=1
904 $ HGPLAIN=1
905 $ export HGPLAIN
905 $ export HGPLAIN
906 $ try 'p2(.)'
906 $ try 'p2(.)'
907 (func
907 (func
908 (symbol 'p2')
908 (symbol 'p2')
909 (symbol '.'))
909 (symbol '.'))
910 * set:
910 * set:
911 <baseset+ []>
911 <baseset+ []>
912
912
913 $ HGPLAINEXCEPT=revsetalias
913 $ HGPLAINEXCEPT=revsetalias
914 $ export HGPLAINEXCEPT
914 $ export HGPLAINEXCEPT
915 $ try 'p2(.)'
915 $ try 'p2(.)'
916 (func
916 (func
917 (symbol 'p2')
917 (symbol 'p2')
918 (symbol '.'))
918 (symbol '.'))
919 * expanded:
919 * expanded:
920 (func
920 (func
921 (symbol 'p1')
921 (symbol 'p1')
922 (symbol '.'))
922 (symbol '.'))
923 * set:
923 * set:
924 <baseset+ [8]>
924 <baseset+ [8]>
925 8
925 8
926
926
927 $ unset HGPLAIN
927 $ unset HGPLAIN
928 $ unset HGPLAINEXCEPT
928 $ unset HGPLAINEXCEPT
929
929
930 test alias recursion
930 test alias recursion
931
931
932 $ try sincem
932 $ try sincem
933 (symbol 'sincem')
933 (symbol 'sincem')
934 * expanded:
934 * expanded:
935 (func
935 (func
936 (symbol 'descendants')
936 (symbol 'descendants')
937 (func
937 (func
938 (symbol 'merge')
938 (symbol 'merge')
939 None))
939 None))
940 * set:
940 * set:
941 <generatorsetasc+>
941 <generatorsetasc+>
942 6
942 6
943 7
943 7
944
944
945 test infinite recursion
945 test infinite recursion
946
946
947 $ echo 'recurse1 = recurse2' >> .hg/hgrc
947 $ echo 'recurse1 = recurse2' >> .hg/hgrc
948 $ echo 'recurse2 = recurse1' >> .hg/hgrc
948 $ echo 'recurse2 = recurse1' >> .hg/hgrc
949 $ try recurse1
949 $ try recurse1
950 (symbol 'recurse1')
950 (symbol 'recurse1')
951 hg: parse error: infinite expansion of revset alias "recurse1" detected
951 hg: parse error: infinite expansion of revset alias "recurse1" detected
952 [10]
952 [10]
953
953
954 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
954 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
955 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
955 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
956 $ try "level2(level1(1, 2), 3)"
956 $ try "level2(level1(1, 2), 3)"
957 (func
957 (func
958 (symbol 'level2')
958 (symbol 'level2')
959 (list
959 (list
960 (func
960 (func
961 (symbol 'level1')
961 (symbol 'level1')
962 (list
962 (list
963 (symbol '1')
963 (symbol '1')
964 (symbol '2')))
964 (symbol '2')))
965 (symbol '3')))
965 (symbol '3')))
966 * expanded:
966 * expanded:
967 (or
967 (or
968 (list
968 (list
969 (symbol '3')
969 (symbol '3')
970 (or
970 (or
971 (list
971 (list
972 (symbol '1')
972 (symbol '1')
973 (symbol '2')))))
973 (symbol '2')))))
974 * set:
974 * set:
975 <addset
975 <addset
976 <baseset [3]>,
976 <baseset [3]>,
977 <baseset [1, 2]>>
977 <baseset [1, 2]>>
978 3
978 3
979 1
979 1
980 2
980 2
981
981
982 test nesting and variable passing
982 test nesting and variable passing
983
983
984 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
984 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
985 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
985 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
986 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
986 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
987 $ try 'nested(2:5)'
987 $ try 'nested(2:5)'
988 (func
988 (func
989 (symbol 'nested')
989 (symbol 'nested')
990 (range
990 (range
991 (symbol '2')
991 (symbol '2')
992 (symbol '5')))
992 (symbol '5')))
993 * expanded:
993 * expanded:
994 (func
994 (func
995 (symbol 'max')
995 (symbol 'max')
996 (range
996 (range
997 (symbol '2')
997 (symbol '2')
998 (symbol '5')))
998 (symbol '5')))
999 * set:
999 * set:
1000 <baseset
1000 <baseset
1001 <max
1001 <max
1002 <fullreposet+ 0:10>,
1002 <fullreposet+ 0:10>,
1003 <spanset+ 2:6>>>
1003 <spanset+ 2:6>>>
1004 5
1004 5
1005
1005
1006 test chained `or` operations are flattened at parsing phase
1006 test chained `or` operations are flattened at parsing phase
1007
1007
1008 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1008 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1009 $ try 'chainedorops(0:1, 1:2, 2:3)'
1009 $ try 'chainedorops(0:1, 1:2, 2:3)'
1010 (func
1010 (func
1011 (symbol 'chainedorops')
1011 (symbol 'chainedorops')
1012 (list
1012 (list
1013 (range
1013 (range
1014 (symbol '0')
1014 (symbol '0')
1015 (symbol '1'))
1015 (symbol '1'))
1016 (range
1016 (range
1017 (symbol '1')
1017 (symbol '1')
1018 (symbol '2'))
1018 (symbol '2'))
1019 (range
1019 (range
1020 (symbol '2')
1020 (symbol '2')
1021 (symbol '3'))))
1021 (symbol '3'))))
1022 * expanded:
1022 * expanded:
1023 (or
1023 (or
1024 (list
1024 (list
1025 (range
1025 (range
1026 (symbol '0')
1026 (symbol '0')
1027 (symbol '1'))
1027 (symbol '1'))
1028 (range
1028 (range
1029 (symbol '1')
1029 (symbol '1')
1030 (symbol '2'))
1030 (symbol '2'))
1031 (range
1031 (range
1032 (symbol '2')
1032 (symbol '2')
1033 (symbol '3'))))
1033 (symbol '3'))))
1034 * set:
1034 * set:
1035 <addset
1035 <addset
1036 <spanset+ 0:2>,
1036 <spanset+ 0:2>,
1037 <addset
1037 <addset
1038 <spanset+ 1:3>,
1038 <spanset+ 1:3>,
1039 <spanset+ 2:4>>>
1039 <spanset+ 2:4>>>
1040 0
1040 0
1041 1
1041 1
1042 2
1042 2
1043 3
1043 3
1044
1044
1045 test variable isolation, variable placeholders are rewritten as string
1045 test variable isolation, variable placeholders are rewritten as string
1046 then parsed and matched again as string. Check they do not leak too
1046 then parsed and matched again as string. Check they do not leak too
1047 far away.
1047 far away.
1048
1048
1049 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1049 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1050 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1050 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1051 $ try 'callinjection(2:5)'
1051 $ try 'callinjection(2:5)'
1052 (func
1052 (func
1053 (symbol 'callinjection')
1053 (symbol 'callinjection')
1054 (range
1054 (range
1055 (symbol '2')
1055 (symbol '2')
1056 (symbol '5')))
1056 (symbol '5')))
1057 * expanded:
1057 * expanded:
1058 (func
1058 (func
1059 (symbol 'descendants')
1059 (symbol 'descendants')
1060 (func
1060 (func
1061 (symbol 'max')
1061 (symbol 'max')
1062 (string '$1')))
1062 (string '$1')))
1063 abort: unknown revision '$1'
1063 abort: unknown revision '$1'
1064 [255]
1064 [255]
1065
1065
1066 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1066 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1067 but 'all()' should never be substituted to '0()'.
1067 but 'all()' should never be substituted to '0()'.
1068
1068
1069 $ echo 'universe = all()' >> .hg/hgrc
1069 $ echo 'universe = all()' >> .hg/hgrc
1070 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1070 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1071 $ try 'shadowall(0)'
1071 $ try 'shadowall(0)'
1072 (func
1072 (func
1073 (symbol 'shadowall')
1073 (symbol 'shadowall')
1074 (symbol '0'))
1074 (symbol '0'))
1075 * expanded:
1075 * expanded:
1076 (and
1076 (and
1077 (symbol '0')
1077 (symbol '0')
1078 (func
1078 (func
1079 (symbol 'all')
1079 (symbol 'all')
1080 None))
1080 None))
1081 * set:
1081 * set:
1082 <filteredset
1082 <filteredset
1083 <baseset [0]>,
1083 <baseset [0]>,
1084 <spanset+ 0:10>>
1084 <spanset+ 0:10>>
1085 0
1085 0
1086
1086
1087 test unknown reference:
1087 test unknown reference:
1088
1088
1089 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1089 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1090 (func
1090 (func
1091 (symbol 'unknownref')
1091 (symbol 'unknownref')
1092 (symbol '0'))
1092 (symbol '0'))
1093 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1093 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1094 [255]
1094 [255]
1095
1095
1096 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1096 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1097 (symbol 'tip')
1097 (symbol 'tip')
1098 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1098 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1099 * set:
1099 * set:
1100 <baseset [9]>
1100 <baseset [9]>
1101 9
1101 9
1102
1102
1103 $ try 'tip'
1103 $ try 'tip'
1104 (symbol 'tip')
1104 (symbol 'tip')
1105 * set:
1105 * set:
1106 <baseset [9]>
1106 <baseset [9]>
1107 9
1107 9
1108
1108
1109 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1109 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1110 (symbol 'tip')
1110 (symbol 'tip')
1111 warning: bad declaration of revset alias "bad name": at 4: invalid token
1111 warning: bad declaration of revset alias "bad name": at 4: invalid token
1112 * set:
1112 * set:
1113 <baseset [9]>
1113 <baseset [9]>
1114 9
1114 9
1115 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1115 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1116 $ try 'strictreplacing("foo", tip)'
1116 $ try 'strictreplacing("foo", tip)'
1117 (func
1117 (func
1118 (symbol 'strictreplacing')
1118 (symbol 'strictreplacing')
1119 (list
1119 (list
1120 (string 'foo')
1120 (string 'foo')
1121 (symbol 'tip')))
1121 (symbol 'tip')))
1122 * expanded:
1122 * expanded:
1123 (or
1123 (or
1124 (list
1124 (list
1125 (symbol 'tip')
1125 (symbol 'tip')
1126 (func
1126 (func
1127 (symbol 'desc')
1127 (symbol 'desc')
1128 (string '$1'))))
1128 (string '$1'))))
1129 * set:
1129 * set:
1130 <addset
1130 <addset
1131 <baseset [9]>,
1131 <baseset [9]>,
1132 <filteredset
1132 <filteredset
1133 <fullreposet+ 0:10>,
1133 <fullreposet+ 0:10>,
1134 <desc '$1'>>>
1134 <desc '$1'>>>
1135 9
1135 9
1136
1136
1137 $ try 'd(2:5)'
1137 $ try 'd(2:5)'
1138 (func
1138 (func
1139 (symbol 'd')
1139 (symbol 'd')
1140 (range
1140 (range
1141 (symbol '2')
1141 (symbol '2')
1142 (symbol '5')))
1142 (symbol '5')))
1143 * expanded:
1143 * expanded:
1144 (func
1144 (func
1145 (symbol 'reverse')
1145 (symbol 'reverse')
1146 (func
1146 (func
1147 (symbol 'sort')
1147 (symbol 'sort')
1148 (list
1148 (list
1149 (range
1149 (range
1150 (symbol '2')
1150 (symbol '2')
1151 (symbol '5'))
1151 (symbol '5'))
1152 (symbol 'date'))))
1152 (symbol 'date'))))
1153 * set:
1153 * set:
1154 <baseset [4, 5, 3, 2]>
1154 <baseset [4, 5, 3, 2]>
1155 4
1155 4
1156 5
1156 5
1157 3
1157 3
1158 2
1158 2
1159 $ try 'rs(2 or 3, date)'
1159 $ try 'rs(2 or 3, date)'
1160 (func
1160 (func
1161 (symbol 'rs')
1161 (symbol 'rs')
1162 (list
1162 (list
1163 (or
1163 (or
1164 (list
1164 (list
1165 (symbol '2')
1165 (symbol '2')
1166 (symbol '3')))
1166 (symbol '3')))
1167 (symbol 'date')))
1167 (symbol 'date')))
1168 * expanded:
1168 * expanded:
1169 (func
1169 (func
1170 (symbol 'reverse')
1170 (symbol 'reverse')
1171 (func
1171 (func
1172 (symbol 'sort')
1172 (symbol 'sort')
1173 (list
1173 (list
1174 (or
1174 (or
1175 (list
1175 (list
1176 (symbol '2')
1176 (symbol '2')
1177 (symbol '3')))
1177 (symbol '3')))
1178 (symbol 'date'))))
1178 (symbol 'date'))))
1179 * set:
1179 * set:
1180 <baseset [3, 2]>
1180 <baseset [3, 2]>
1181 3
1181 3
1182 2
1182 2
1183 $ try 'rs()'
1183 $ try 'rs()'
1184 (func
1184 (func
1185 (symbol 'rs')
1185 (symbol 'rs')
1186 None)
1186 None)
1187 hg: parse error: invalid number of arguments: 0
1187 hg: parse error: invalid number of arguments: 0
1188 [10]
1188 [10]
1189 $ try 'rs(2)'
1189 $ try 'rs(2)'
1190 (func
1190 (func
1191 (symbol 'rs')
1191 (symbol 'rs')
1192 (symbol '2'))
1192 (symbol '2'))
1193 hg: parse error: invalid number of arguments: 1
1193 hg: parse error: invalid number of arguments: 1
1194 [10]
1194 [10]
1195 $ try 'rs(2, data, 7)'
1195 $ try 'rs(2, data, 7)'
1196 (func
1196 (func
1197 (symbol 'rs')
1197 (symbol 'rs')
1198 (list
1198 (list
1199 (symbol '2')
1199 (symbol '2')
1200 (symbol 'data')
1200 (symbol 'data')
1201 (symbol '7')))
1201 (symbol '7')))
1202 hg: parse error: invalid number of arguments: 3
1202 hg: parse error: invalid number of arguments: 3
1203 [10]
1203 [10]
1204 $ try 'rs4(2 or 3, x, x, date)'
1204 $ try 'rs4(2 or 3, x, x, date)'
1205 (func
1205 (func
1206 (symbol 'rs4')
1206 (symbol 'rs4')
1207 (list
1207 (list
1208 (or
1208 (or
1209 (list
1209 (list
1210 (symbol '2')
1210 (symbol '2')
1211 (symbol '3')))
1211 (symbol '3')))
1212 (symbol 'x')
1212 (symbol 'x')
1213 (symbol 'x')
1213 (symbol 'x')
1214 (symbol 'date')))
1214 (symbol 'date')))
1215 * expanded:
1215 * expanded:
1216 (func
1216 (func
1217 (symbol 'reverse')
1217 (symbol 'reverse')
1218 (func
1218 (func
1219 (symbol 'sort')
1219 (symbol 'sort')
1220 (list
1220 (list
1221 (or
1221 (or
1222 (list
1222 (list
1223 (symbol '2')
1223 (symbol '2')
1224 (symbol '3')))
1224 (symbol '3')))
1225 (symbol 'date'))))
1225 (symbol 'date'))))
1226 * set:
1226 * set:
1227 <baseset [3, 2]>
1227 <baseset [3, 2]>
1228 3
1228 3
1229 2
1229 2
1230
1230
1231 issue4553: check that revset aliases override existing hash prefix
1231 issue4553: check that revset aliases override existing hash prefix
1232
1232
1233 $ hg log -qr d5e
1233 $ hg log -qr d5e
1234 6:d5e6808a8607
1234 6:d5e6808a8607
1235
1235
1236 $ hg log -qr d5e --config revsetalias.d5e="all()"
1236 $ hg log -qr d5e --config revsetalias.d5e="all()"
1237 0:2785f51eece5
1237 0:2785f51eece5
1238 1:d75937da8da0
1238 1:d75937da8da0
1239 2:5ed5505e9f1c
1239 2:5ed5505e9f1c
1240 3:8528aa5637f2
1240 3:8528aa5637f2
1241 4:2326846efdab
1241 4:2326846efdab
1242 5:904fa392b941
1242 5:904fa392b941
1243 6:d5e6808a8607
1243 6:d5e6808a8607
1244 7:586353d483b3
1244 7:586353d483b3
1245 8:d5d0dcbdc4d9
1245 8:d5d0dcbdc4d9
1246 9:d2e607fcf9e4
1246 9:d2e607fcf9e4
1247
1247
1248 $ hg log -qr d5e: --config revsetalias.d5e="0"
1248 $ hg log -qr d5e: --config revsetalias.d5e="0"
1249 0:2785f51eece5
1249 0:2785f51eece5
1250 1:d75937da8da0
1250 1:d75937da8da0
1251 2:5ed5505e9f1c
1251 2:5ed5505e9f1c
1252 3:8528aa5637f2
1252 3:8528aa5637f2
1253 4:2326846efdab
1253 4:2326846efdab
1254 5:904fa392b941
1254 5:904fa392b941
1255 6:d5e6808a8607
1255 6:d5e6808a8607
1256 7:586353d483b3
1256 7:586353d483b3
1257 8:d5d0dcbdc4d9
1257 8:d5d0dcbdc4d9
1258 9:d2e607fcf9e4
1258 9:d2e607fcf9e4
1259
1259
1260 $ hg log -qr :d5e --config revsetalias.d5e="9"
1260 $ hg log -qr :d5e --config revsetalias.d5e="9"
1261 0:2785f51eece5
1261 0:2785f51eece5
1262 1:d75937da8da0
1262 1:d75937da8da0
1263 2:5ed5505e9f1c
1263 2:5ed5505e9f1c
1264 3:8528aa5637f2
1264 3:8528aa5637f2
1265 4:2326846efdab
1265 4:2326846efdab
1266 5:904fa392b941
1266 5:904fa392b941
1267 6:d5e6808a8607
1267 6:d5e6808a8607
1268 7:586353d483b3
1268 7:586353d483b3
1269 8:d5d0dcbdc4d9
1269 8:d5d0dcbdc4d9
1270 9:d2e607fcf9e4
1270 9:d2e607fcf9e4
1271
1271
1272 $ hg log -qr d5e:
1272 $ hg log -qr d5e:
1273 6:d5e6808a8607
1273 6:d5e6808a8607
1274 7:586353d483b3
1274 7:586353d483b3
1275 8:d5d0dcbdc4d9
1275 8:d5d0dcbdc4d9
1276 9:d2e607fcf9e4
1276 9:d2e607fcf9e4
1277
1277
1278 $ hg log -qr :d5e
1278 $ hg log -qr :d5e
1279 0:2785f51eece5
1279 0:2785f51eece5
1280 1:d75937da8da0
1280 1:d75937da8da0
1281 2:5ed5505e9f1c
1281 2:5ed5505e9f1c
1282 3:8528aa5637f2
1282 3:8528aa5637f2
1283 4:2326846efdab
1283 4:2326846efdab
1284 5:904fa392b941
1284 5:904fa392b941
1285 6:d5e6808a8607
1285 6:d5e6808a8607
1286
1286
1287 issue2549 - correct optimizations
1287 issue2549 - correct optimizations
1288
1288
1289 $ try 'limit(1 or 2 or 3, 2) and not 2'
1289 $ try 'limit(1 or 2 or 3, 2) and not 2'
1290 (and
1290 (and
1291 (func
1291 (func
1292 (symbol 'limit')
1292 (symbol 'limit')
1293 (list
1293 (list
1294 (or
1294 (or
1295 (list
1295 (list
1296 (symbol '1')
1296 (symbol '1')
1297 (symbol '2')
1297 (symbol '2')
1298 (symbol '3')))
1298 (symbol '3')))
1299 (symbol '2')))
1299 (symbol '2')))
1300 (not
1300 (not
1301 (symbol '2')))
1301 (symbol '2')))
1302 * set:
1302 * set:
1303 <filteredset
1303 <filteredset
1304 <baseset [1, 2]>,
1304 <baseset [1, 2]>,
1305 <not
1305 <not
1306 <baseset [2]>>>
1306 <baseset [2]>>>
1307 1
1307 1
1308 $ try 'max(1 or 2) and not 2'
1308 $ try 'max(1 or 2) and not 2'
1309 (and
1309 (and
1310 (func
1310 (func
1311 (symbol 'max')
1311 (symbol 'max')
1312 (or
1312 (or
1313 (list
1313 (list
1314 (symbol '1')
1314 (symbol '1')
1315 (symbol '2'))))
1315 (symbol '2'))))
1316 (not
1316 (not
1317 (symbol '2')))
1317 (symbol '2')))
1318 * set:
1318 * set:
1319 <filteredset
1319 <filteredset
1320 <baseset
1320 <baseset
1321 <max
1321 <max
1322 <fullreposet+ 0:10>,
1322 <fullreposet+ 0:10>,
1323 <baseset [1, 2]>>>,
1323 <baseset [1, 2]>>>,
1324 <not
1324 <not
1325 <baseset [2]>>>
1325 <baseset [2]>>>
1326 $ try 'min(1 or 2) and not 1'
1326 $ try 'min(1 or 2) and not 1'
1327 (and
1327 (and
1328 (func
1328 (func
1329 (symbol 'min')
1329 (symbol 'min')
1330 (or
1330 (or
1331 (list
1331 (list
1332 (symbol '1')
1332 (symbol '1')
1333 (symbol '2'))))
1333 (symbol '2'))))
1334 (not
1334 (not
1335 (symbol '1')))
1335 (symbol '1')))
1336 * set:
1336 * set:
1337 <filteredset
1337 <filteredset
1338 <baseset
1338 <baseset
1339 <min
1339 <min
1340 <fullreposet+ 0:10>,
1340 <fullreposet+ 0:10>,
1341 <baseset [1, 2]>>>,
1341 <baseset [1, 2]>>>,
1342 <not
1342 <not
1343 <baseset [1]>>>
1343 <baseset [1]>>>
1344 $ try 'last(1 or 2, 1) and not 2'
1344 $ try 'last(1 or 2, 1) and not 2'
1345 (and
1345 (and
1346 (func
1346 (func
1347 (symbol 'last')
1347 (symbol 'last')
1348 (list
1348 (list
1349 (or
1349 (or
1350 (list
1350 (list
1351 (symbol '1')
1351 (symbol '1')
1352 (symbol '2')))
1352 (symbol '2')))
1353 (symbol '1')))
1353 (symbol '1')))
1354 (not
1354 (not
1355 (symbol '2')))
1355 (symbol '2')))
1356 * set:
1356 * set:
1357 <filteredset
1357 <filteredset
1358 <baseset [2]>,
1358 <baseset [2]>,
1359 <not
1359 <not
1360 <baseset [2]>>>
1360 <baseset [2]>>>
1361
1361
1362 issue4289 - ordering of built-ins
1362 issue4289 - ordering of built-ins
1363 $ hg log -M -q -r 3:2
1363 $ hg log -M -q -r 3:2
1364 3:8528aa5637f2
1364 3:8528aa5637f2
1365 2:5ed5505e9f1c
1365 2:5ed5505e9f1c
1366
1366
1367 test revsets started with 40-chars hash (issue3669)
1367 test revsets started with 40-chars hash (issue3669)
1368
1368
1369 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1369 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1370 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1370 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1371 9
1371 9
1372 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1372 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1373 8
1373 8
1374
1374
1375 test or-ed indirect predicates (issue3775)
1375 test or-ed indirect predicates (issue3775)
1376
1376
1377 $ log '6 or 6^1' | sort
1377 $ log '6 or 6^1' | sort
1378 5
1378 5
1379 6
1379 6
1380 $ log '6^1 or 6' | sort
1380 $ log '6^1 or 6' | sort
1381 5
1381 5
1382 6
1382 6
1383 $ log '4 or 4~1' | sort
1383 $ log '4 or 4~1' | sort
1384 2
1384 2
1385 4
1385 4
1386 $ log '4~1 or 4' | sort
1386 $ log '4~1 or 4' | sort
1387 2
1387 2
1388 4
1388 4
1389 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1389 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1390 0
1390 0
1391 1
1391 1
1392 2
1392 2
1393 3
1393 3
1394 4
1394 4
1395 5
1395 5
1396 6
1396 6
1397 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1397 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1398 0
1398 0
1399 1
1399 1
1400 2
1400 2
1401 3
1401 3
1402 4
1402 4
1403 5
1403 5
1404 6
1404 6
1405
1405
1406 tests for 'remote()' predicate:
1406 tests for 'remote()' predicate:
1407 #. (csets in remote) (id) (remote)
1407 #. (csets in remote) (id) (remote)
1408 1. less than local current branch "default"
1408 1. less than local current branch "default"
1409 2. same with local specified "default"
1409 2. same with local specified "default"
1410 3. more than local specified specified
1410 3. more than local specified specified
1411
1411
1412 $ hg clone --quiet -U . ../remote3
1412 $ hg clone --quiet -U . ../remote3
1413 $ cd ../remote3
1413 $ cd ../remote3
1414 $ hg update -q 7
1414 $ hg update -q 7
1415 $ echo r > r
1415 $ echo r > r
1416 $ hg ci -Aqm 10
1416 $ hg ci -Aqm 10
1417 $ log 'remote()'
1417 $ log 'remote()'
1418 7
1418 7
1419 $ log 'remote("a-b-c-")'
1419 $ log 'remote("a-b-c-")'
1420 2
1420 2
1421 $ cd ../repo
1421 $ cd ../repo
1422 $ log 'remote(".a.b.c.", "../remote3")'
1422 $ log 'remote(".a.b.c.", "../remote3")'
1423
1423
1424 tests for concatenation of strings/symbols by "##"
1424 tests for concatenation of strings/symbols by "##"
1425
1425
1426 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1426 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1427 (_concat
1427 (_concat
1428 (_concat
1428 (_concat
1429 (_concat
1429 (_concat
1430 (symbol '278')
1430 (symbol '278')
1431 (string '5f5'))
1431 (string '5f5'))
1432 (symbol '1ee'))
1432 (symbol '1ee'))
1433 (string 'ce5'))
1433 (string 'ce5'))
1434 * concatenated:
1434 * concatenated:
1435 (string '2785f51eece5')
1435 (string '2785f51eece5')
1436 * set:
1436 * set:
1437 <baseset [0]>
1437 <baseset [0]>
1438 0
1438 0
1439
1439
1440 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1440 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1441 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1441 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1442 (func
1442 (func
1443 (symbol 'cat4')
1443 (symbol 'cat4')
1444 (list
1444 (list
1445 (symbol '278')
1445 (symbol '278')
1446 (string '5f5')
1446 (string '5f5')
1447 (symbol '1ee')
1447 (symbol '1ee')
1448 (string 'ce5')))
1448 (string 'ce5')))
1449 * expanded:
1449 * expanded:
1450 (_concat
1450 (_concat
1451 (_concat
1451 (_concat
1452 (_concat
1452 (_concat
1453 (symbol '278')
1453 (symbol '278')
1454 (string '5f5'))
1454 (string '5f5'))
1455 (symbol '1ee'))
1455 (symbol '1ee'))
1456 (string 'ce5'))
1456 (string 'ce5'))
1457 * concatenated:
1457 * concatenated:
1458 (string '2785f51eece5')
1458 (string '2785f51eece5')
1459 * set:
1459 * set:
1460 <baseset [0]>
1460 <baseset [0]>
1461 0
1461 0
1462
1462
1463 (check concatenation in alias nesting)
1463 (check concatenation in alias nesting)
1464
1464
1465 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1465 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1466 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1466 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1467 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1467 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1468 0
1468 0
1469
1469
1470 (check operator priority)
1470 (check operator priority)
1471
1471
1472 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1472 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1473 $ log "cat2n2(2785f5, 1eece5, d2e607, fcf9e4)"
1473 $ log "cat2n2(2785f5, 1eece5, d2e607, fcf9e4)"
1474 0
1474 0
1475 4
1475 4
1476
1476
1477 $ cd ..
1477 $ cd ..
1478
1478
1479 prepare repository that has "default" branches of multiple roots
1479 prepare repository that has "default" branches of multiple roots
1480
1480
1481 $ hg init namedbranch
1481 $ hg init namedbranch
1482 $ cd namedbranch
1482 $ cd namedbranch
1483
1483
1484 $ echo default0 >> a
1484 $ echo default0 >> a
1485 $ hg ci -Aqm0
1485 $ hg ci -Aqm0
1486 $ echo default1 >> a
1486 $ echo default1 >> a
1487 $ hg ci -m1
1487 $ hg ci -m1
1488
1488
1489 $ hg branch -q stable
1489 $ hg branch -q stable
1490 $ echo stable2 >> a
1490 $ echo stable2 >> a
1491 $ hg ci -m2
1491 $ hg ci -m2
1492 $ echo stable3 >> a
1492 $ echo stable3 >> a
1493 $ hg ci -m3
1493 $ hg ci -m3
1494
1494
1495 $ hg update -q null
1495 $ hg update -q null
1496 $ echo default4 >> a
1496 $ echo default4 >> a
1497 $ hg ci -Aqm4
1497 $ hg ci -Aqm4
1498 $ echo default5 >> a
1498 $ echo default5 >> a
1499 $ hg ci -m5
1499 $ hg ci -m5
1500
1500
1501 "null" revision belongs to "default" branch (issue4683)
1501 "null" revision belongs to "default" branch (issue4683)
1502
1502
1503 $ log 'branch(null)'
1503 $ log 'branch(null)'
1504 0
1504 0
1505 1
1505 1
1506 4
1506 4
1507 5
1507 5
1508
1508
1509 "null" revision belongs to "default" branch, but it shouldn't appear in set
1509 "null" revision belongs to "default" branch, but it shouldn't appear in set
1510 unless explicitly specified (issue4682)
1510 unless explicitly specified (issue4682)
1511
1511
1512 $ log 'children(branch(default))'
1512 $ log 'children(branch(default))'
1513 1
1513 1
1514 2
1514 2
1515 5
1515 5
1516
1516
1517 $ cd ..
1517 $ cd ..
1518
1518
1519 test author/desc/keyword in problematic encoding
1519 test author/desc/keyword in problematic encoding
1520 # unicode: cp932:
1520 # unicode: cp932:
1521 # u30A2 0x83 0x41(= 'A')
1521 # u30A2 0x83 0x41(= 'A')
1522 # u30C2 0x83 0x61(= 'a')
1522 # u30C2 0x83 0x61(= 'a')
1523
1523
1524 $ hg init problematicencoding
1524 $ hg init problematicencoding
1525 $ cd problematicencoding
1525 $ cd problematicencoding
1526
1526
1527 $ "$PYTHON" <<EOF
1527 $ "$PYTHON" <<EOF
1528 > open('setup.sh', 'wb').write(u'''
1528 > open('setup.sh', 'wb').write(u'''
1529 > echo a > text
1529 > echo a > text
1530 > hg add text
1530 > hg add text
1531 > hg --encoding utf-8 commit -u '\u30A2' -m none
1531 > hg --encoding utf-8 commit -u '\u30A2' -m none
1532 > echo b > text
1532 > echo b > text
1533 > hg --encoding utf-8 commit -u '\u30C2' -m none
1533 > hg --encoding utf-8 commit -u '\u30C2' -m none
1534 > echo c > text
1534 > echo c > text
1535 > hg --encoding utf-8 commit -u none -m '\u30A2'
1535 > hg --encoding utf-8 commit -u none -m '\u30A2'
1536 > echo d > text
1536 > echo d > text
1537 > hg --encoding utf-8 commit -u none -m '\u30C2'
1537 > hg --encoding utf-8 commit -u none -m '\u30C2'
1538 > '''.encode('utf-8'))
1538 > '''.encode('utf-8'))
1539 > EOF
1539 > EOF
1540 $ sh < setup.sh
1540 $ sh < setup.sh
1541
1541
1542 test in problematic encoding
1542 test in problematic encoding
1543 $ "$PYTHON" <<EOF
1543 $ "$PYTHON" <<EOF
1544 > open('test.sh', 'wb').write(u'''
1544 > open('test.sh', 'wb').write(u'''
1545 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1545 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1546 > echo ====
1546 > echo ====
1547 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1547 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1548 > echo ====
1548 > echo ====
1549 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1549 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1550 > echo ====
1550 > echo ====
1551 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1551 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1552 > echo ====
1552 > echo ====
1553 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1553 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1554 > echo ====
1554 > echo ====
1555 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1555 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1556 > '''.encode('cp932'))
1556 > '''.encode('cp932'))
1557 > EOF
1557 > EOF
1558 $ sh < test.sh
1558 $ sh < test.sh
1559 0
1559 0
1560 ====
1560 ====
1561 1
1561 1
1562 ====
1562 ====
1563 2
1563 2
1564 ====
1564 ====
1565 3
1565 3
1566 ====
1566 ====
1567 0
1567 0
1568 2
1568 2
1569 ====
1569 ====
1570 1
1570 1
1571 3
1571 3
1572
1572
1573 test error message of bad revset
1573 test error message of bad revset
1574 $ hg log -r 'foo\\'
1574 $ hg log -r 'foo\\'
1575 hg: parse error at 3: syntax error in revset 'foo\\'
1575 hg: parse error at 3: syntax error in revset 'foo\\'
1576 (foo\\
1576 (foo\\
1577 ^ here)
1577 ^ here)
1578 [10]
1578 [10]
1579
1579
1580 $ cd ..
1580 $ cd ..
1581
1581
1582 Test that revset predicate of extension isn't loaded at failure of
1582 Test that revset predicate of extension isn't loaded at failure of
1583 loading it
1583 loading it
1584
1584
1585 $ cd repo
1585 $ cd repo
1586
1586
1587 $ cat <<EOF > $TESTTMP/custompredicate.py
1587 $ cat <<EOF > $TESTTMP/custompredicate.py
1588 > from mercurial import error, registrar, revset
1588 > from mercurial import error, registrar, revset
1589 >
1589 >
1590 > revsetpredicate = registrar.revsetpredicate()
1590 > revsetpredicate = registrar.revsetpredicate()
1591 >
1591 >
1592 > @revsetpredicate(b'custom1()')
1592 > @revsetpredicate(b'custom1()')
1593 > def custom1(repo, subset, x):
1593 > def custom1(repo, subset, x):
1594 > return revset.baseset([1])
1594 > return revset.baseset([1])
1595 >
1595 >
1596 > raise error.Abort(b'intentional failure of loading extension')
1596 > raise error.Abort(b'intentional failure of loading extension')
1597 > EOF
1597 > EOF
1598 $ cat <<EOF > .hg/hgrc
1598 $ cat <<EOF > .hg/hgrc
1599 > [extensions]
1599 > [extensions]
1600 > custompredicate = $TESTTMP/custompredicate.py
1600 > custompredicate = $TESTTMP/custompredicate.py
1601 > EOF
1601 > EOF
1602
1602
1603 $ hg debugrevspec "custom1()"
1603 $ hg debugrevspec "custom1()"
1604 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1604 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1605 hg: parse error: unknown identifier: custom1
1605 hg: parse error: unknown identifier: custom1
1606 [10]
1606 [10]
1607
1607
1608 Test repo.anyrevs with customized revset overrides
1608 Test repo.anyrevs with customized revset overrides
1609
1609
1610 $ cat > $TESTTMP/printprevset.py <<EOF
1610 $ cat > $TESTTMP/printprevset.py <<EOF
1611 > from mercurial import encoding, registrar
1611 > from mercurial import encoding, registrar
1612 > cmdtable = {}
1612 > cmdtable = {}
1613 > command = registrar.command(cmdtable)
1613 > command = registrar.command(cmdtable)
1614 > @command(b'printprevset')
1614 > @command(b'printprevset')
1615 > def printprevset(ui, repo):
1615 > def printprevset(ui, repo):
1616 > alias = {}
1616 > alias = {}
1617 > p = encoding.environ.get(b'P')
1617 > p = encoding.environ.get(b'P')
1618 > if p:
1618 > if p:
1619 > alias[b'P'] = p
1619 > alias[b'P'] = p
1620 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1620 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1621 > ui.write(b'P=%r\n' % list(revs))
1621 > ui.write(b'P=%r\n' % list(revs))
1622 > EOF
1622 > EOF
1623
1623
1624 $ cat >> .hg/hgrc <<EOF
1624 $ cat >> .hg/hgrc <<EOF
1625 > custompredicate = !
1625 > custompredicate = !
1626 > printprevset = $TESTTMP/printprevset.py
1626 > printprevset = $TESTTMP/printprevset.py
1627 > EOF
1627 > EOF
1628
1628
1629 $ unset P
1629 $ unset P
1630 $ hg --config revsetalias.P=1 printprevset
1630 $ hg --config revsetalias.P=1 printprevset
1631 P=[1]
1631 P=[1]
1632 $ P=3 hg --config revsetalias.P=2 printprevset
1632 $ P=3 hg --config revsetalias.P=2 printprevset
1633 P=[3]
1633 P=[3]
1634
1634
1635 $ cd ..
1635 $ cd ..
1636
1636
1637 Test obsstore related revsets
1637 Test obsstore related revsets
1638
1638
1639 $ hg init repo1
1639 $ hg init repo1
1640 $ cd repo1
1640 $ cd repo1
1641 $ cat <<EOF >> .hg/hgrc
1641 $ cat <<EOF >> .hg/hgrc
1642 > [experimental]
1642 > [experimental]
1643 > evolution.createmarkers=True
1643 > evolution.createmarkers=True
1644 > EOF
1644 > EOF
1645
1645
1646 $ hg debugdrawdag <<'EOS'
1646 $ hg debugdrawdag <<'EOS'
1647 > F G
1647 > F G
1648 > |/ # split: B -> E, F
1648 > |/ # split: B -> E, F
1649 > B C D E # amend: B -> C -> D
1649 > B C D E # amend: B -> C -> D
1650 > \|/ | # amend: F -> G
1650 > \|/ | # amend: F -> G
1651 > A A Z # amend: A -> Z
1651 > A A Z # amend: A -> Z
1652 > EOS
1652 > EOS
1653 3 new orphan changesets
1653 3 new orphan changesets
1654 3 new content-divergent changesets
1654 3 new content-divergent changesets
1655
1655
1656 $ hg log -r 'successors(Z)' -T '{desc}\n'
1656 $ hg log -r 'successors(Z)' -T '{desc}\n'
1657 Z
1657 Z
1658
1658
1659 $ hg log -r 'successors(F)' -T '{desc}\n'
1659 $ hg log -r 'successors(F)' -T '{desc}\n'
1660 F
1660 F
1661 G
1661 G
1662
1662
1663 $ hg tag --remove --local C D E F G
1663 $ hg tag --remove --local C D E F G
1664
1664
1665 $ hg log -r 'successors(B)' -T '{desc}\n'
1665 $ hg log -r 'successors(B)' -T '{desc}\n'
1666 B
1666 B
1667 D
1667 D
1668 E
1668 E
1669 G
1669 G
1670
1670
1671 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1671 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1672 B
1672 B
1673 C
1673 C
1674 D
1674 D
1675 E
1675 E
1676 F
1676 F
1677 G
1677 G
1678
1678
1679 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1679 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1680 D
1680 D
1681 E
1681 E
1682 G
1682 G
1683
1683
1684 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1684 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1685 A
1685 A
1686 Z
1686 Z
1687 B
1687 B
1688
1688
1689 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1689 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1690 Z
1690 Z
1691
1691
1692 Test `draft() & ::x` optimization
1692 Test `draft() & ::x` optimization
1693
1693
1694 $ hg init $TESTTMP/repo2
1694 $ hg init $TESTTMP/repo2
1695 $ cd $TESTTMP/repo2
1695 $ cd $TESTTMP/repo2
1696 $ hg debugdrawdag <<'EOS'
1696 $ hg debugdrawdag <<'EOS'
1697 > P5 S1
1697 > P5 S1
1698 > | |
1698 > | |
1699 > S2 | D3
1699 > S2 | D3
1700 > \|/
1700 > \|/
1701 > P4
1701 > P4
1702 > |
1702 > |
1703 > P3 D2
1703 > P3 D2
1704 > | |
1704 > | |
1705 > P2 D1
1705 > P2 D1
1706 > |/
1706 > |/
1707 > P1
1707 > P1
1708 > |
1708 > |
1709 > P0
1709 > P0
1710 > EOS
1710 > EOS
1711 $ hg phase --public -r P5
1711 $ hg phase --public -r P5
1712 $ hg phase --force --secret -r S1+S2
1712 $ hg phase --force --secret -r S1+S2
1713 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1713 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1714 o 8 P5 public
1714 o 8 P5 public
1715 |
1715 |
1716 | o 10 S1 secret
1716 | o 10 S1 secret
1717 | |
1717 | |
1718 | o 7 D3 draft
1718 | o 7 D3 draft
1719 |/
1719 |/
1720 | o 9 S2 secret
1720 | o 9 S2 secret
1721 |/
1721 |/
1722 o 6 P4 public
1722 o 6 P4 public
1723 |
1723 |
1724 o 5 P3 public
1724 o 5 P3 public
1725 |
1725 |
1726 o 3 P2 public
1726 o 3 P2 public
1727 |
1727 |
1728 | o 4 D2 draft
1728 | o 4 D2 draft
1729 | |
1729 | |
1730 | o 2 D1 draft
1730 | o 2 D1 draft
1731 |/
1731 |/
1732 o 1 P1 public
1732 o 1 P1 public
1733 |
1733 |
1734 o 0 P0 public
1734 o 0 P0 public
1735
1735
1736 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1736 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1737 * analyzed:
1737 * analyzed:
1738 (and
1738 (and
1739 (func
1739 (func
1740 (symbol 'draft')
1740 (symbol 'draft')
1741 None)
1741 None)
1742 (func
1742 (func
1743 (symbol 'ancestors')
1743 (symbol 'ancestors')
1744 (or
1744 (or
1745 (list
1745 (list
1746 (and
1746 (and
1747 (or
1747 (or
1748 (list
1748 (list
1749 (symbol 'S1')
1749 (symbol 'S1')
1750 (symbol 'D1')
1750 (symbol 'D1')
1751 (symbol 'P5')))
1751 (symbol 'P5')))
1752 (not
1752 (not
1753 (symbol 'D3')))
1753 (symbol 'D3')))
1754 (symbol 'S2')))))
1754 (symbol 'S2')))))
1755 * optimized:
1755 * optimized:
1756 (func
1756 (func
1757 (symbol '_phaseandancestors')
1757 (symbol '_phaseandancestors')
1758 (list
1758 (list
1759 (symbol 'draft')
1759 (symbol 'draft')
1760 (or
1760 (or
1761 (list
1761 (list
1762 (difference
1762 (difference
1763 (func
1763 (func
1764 (symbol '_list')
1764 (symbol '_list')
1765 (string 'S1\x00D1\x00P5'))
1765 (string 'S1\x00D1\x00P5'))
1766 (symbol 'D3'))
1766 (symbol 'D3'))
1767 (symbol 'S2')))))
1767 (symbol 'S2')))))
1768 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1768 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1769 * analyzed:
1769 * analyzed:
1770 (and
1770 (and
1771 (func
1771 (func
1772 (symbol 'secret')
1772 (symbol 'secret')
1773 None)
1773 None)
1774 (func
1774 (func
1775 (symbol 'ancestors')
1775 (symbol 'ancestors')
1776 (symbol '9')))
1776 (symbol '9')))
1777 * optimized:
1777 * optimized:
1778 (func
1778 (func
1779 (symbol '_phaseandancestors')
1779 (symbol '_phaseandancestors')
1780 (list
1780 (list
1781 (symbol 'secret')
1781 (symbol 'secret')
1782 (symbol '9')))
1782 (symbol '9')))
1783 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1783 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1784 * analyzed:
1784 * analyzed:
1785 (and
1785 (and
1786 (symbol '7')
1786 (symbol '7')
1787 (and
1787 (and
1788 (not
1788 (not
1789 (func
1789 (func
1790 (symbol 'public')
1790 (symbol 'public')
1791 None))
1791 None))
1792 (func
1792 (func
1793 (symbol 'ancestors')
1793 (symbol 'ancestors')
1794 (func
1794 (func
1795 (symbol 'tag')
1795 (symbol 'tag')
1796 None))))
1796 None))))
1797 * optimized:
1797 * optimized:
1798 (and
1798 (and
1799 (symbol '7')
1799 (symbol '7')
1800 (func
1800 (func
1801 (symbol '_phaseandancestors')
1801 (symbol '_phaseandancestors')
1802 (list
1802 (list
1803 (symbol '_notpublic')
1803 (symbol '_notpublic')
1804 (func
1804 (func
1805 (symbol 'tag')
1805 (symbol 'tag')
1806 None))))
1806 None))))
1807 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1807 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1808 * optimized:
1808 * optimized:
1809 (and
1809 (and
1810 (func
1810 (func
1811 (symbol '_notpublic')
1811 (symbol '_notpublic')
1812 None)
1812 None)
1813 (func
1813 (func
1814 (symbol 'ancestors')
1814 (symbol 'ancestors')
1815 (list
1815 (list
1816 (func
1816 (func
1817 (symbol '_list')
1817 (symbol '_list')
1818 (string 'S1\x00D2\x00P5'))
1818 (string 'S1\x00D2\x00P5'))
1819 (symbol '1'))))
1819 (symbol '1'))))
1820 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1820 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1821 * optimized:
1821 * optimized:
1822 (and
1822 (and
1823 (func
1823 (func
1824 (symbol '_notpublic')
1824 (symbol '_notpublic')
1825 None)
1825 None)
1826 (func
1826 (func
1827 (symbol 'ancestors')
1827 (symbol 'ancestors')
1828 (list
1828 (list
1829 (func
1829 (func
1830 (symbol '_list')
1830 (symbol '_list')
1831 (string 'S1\x00D2\x00P5'))
1831 (string 'S1\x00D2\x00P5'))
1832 (keyvalue
1832 (keyvalue
1833 (symbol 'depth')
1833 (symbol 'depth')
1834 (symbol '1')))))
1834 (symbol '1')))))
1835
1835
1836 test commonancestors and its optimization
1836 test commonancestors and its optimization
1837
1837
1838 $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))'
1838 $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))'
1839 * analyzed:
1839 * analyzed:
1840 (func
1840 (func
1841 (symbol 'heads')
1841 (symbol 'heads')
1842 (func
1842 (func
1843 (symbol 'commonancestors')
1843 (symbol 'commonancestors')
1844 (func
1844 (func
1845 (symbol 'head')
1845 (symbol 'head')
1846 None)))
1846 None)))
1847 * optimized:
1847 * optimized:
1848 (func
1848 (func
1849 (symbol '_commonancestorheads')
1849 (symbol '_commonancestorheads')
1850 (func
1850 (func
1851 (symbol 'head')
1851 (symbol 'head')
1852 None))
1852 None))
General Comments 0
You need to be logged in to leave comments. Login now