##// END OF EJS Templates
diff: extract function for getting possibly re-merged parent to diff against...
Martin von Zweigbergk -
r47225:09fee527 default draft
parent child Browse files
Show More
@@ -1,7876 +1,7855 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 wdirhex,
21 wdirhex,
22 wdirrev,
22 wdirrev,
23 )
23 )
24 from .pycompat import open
24 from .pycompat import open
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 bundlecaches,
29 bundlecaches,
30 changegroup,
30 changegroup,
31 cmdutil,
31 cmdutil,
32 context as contextmod,
33 copies,
32 copies,
34 debugcommands as debugcommandsmod,
33 debugcommands as debugcommandsmod,
35 destutil,
34 destutil,
36 dirstateguard,
35 dirstateguard,
37 discovery,
36 discovery,
38 encoding,
37 encoding,
39 error,
38 error,
40 exchange,
39 exchange,
41 extensions,
40 extensions,
42 filemerge,
41 filemerge,
43 formatter,
42 formatter,
44 graphmod,
43 graphmod,
45 grep as grepmod,
44 grep as grepmod,
46 hbisect,
45 hbisect,
47 help,
46 help,
48 hg,
47 hg,
49 logcmdutil,
48 logcmdutil,
50 merge as mergemod,
49 merge as mergemod,
51 mergestate as mergestatemod,
50 mergestate as mergestatemod,
52 narrowspec,
51 narrowspec,
53 obsolete,
52 obsolete,
54 obsutil,
53 obsutil,
55 patch,
54 patch,
56 phases,
55 phases,
57 pycompat,
56 pycompat,
58 rcutil,
57 rcutil,
59 registrar,
58 registrar,
60 requirements,
59 requirements,
61 revsetlang,
60 revsetlang,
62 rewriteutil,
61 rewriteutil,
63 scmutil,
62 scmutil,
64 server,
63 server,
65 shelve as shelvemod,
64 shelve as shelvemod,
66 state as statemod,
65 state as statemod,
67 streamclone,
66 streamclone,
68 tags as tagsmod,
67 tags as tagsmod,
69 ui as uimod,
68 ui as uimod,
70 util,
69 util,
71 verify as verifymod,
70 verify as verifymod,
72 vfs as vfsmod,
71 vfs as vfsmod,
73 wireprotoserver,
72 wireprotoserver,
74 )
73 )
75 from .utils import (
74 from .utils import (
76 dateutil,
75 dateutil,
77 stringutil,
76 stringutil,
78 )
77 )
79
78
80 table = {}
79 table = {}
81 table.update(debugcommandsmod.command._table)
80 table.update(debugcommandsmod.command._table)
82
81
83 command = registrar.command(table)
82 command = registrar.command(table)
84 INTENT_READONLY = registrar.INTENT_READONLY
83 INTENT_READONLY = registrar.INTENT_READONLY
85
84
86 # common command options
85 # common command options
87
86
88 globalopts = [
87 globalopts = [
89 (
88 (
90 b'R',
89 b'R',
91 b'repository',
90 b'repository',
92 b'',
91 b'',
93 _(b'repository root directory or name of overlay bundle file'),
92 _(b'repository root directory or name of overlay bundle file'),
94 _(b'REPO'),
93 _(b'REPO'),
95 ),
94 ),
96 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
97 (
96 (
98 b'y',
97 b'y',
99 b'noninteractive',
98 b'noninteractive',
100 None,
99 None,
101 _(
100 _(
102 b'do not prompt, automatically pick the first choice for all prompts'
101 b'do not prompt, automatically pick the first choice for all prompts'
103 ),
102 ),
104 ),
103 ),
105 (b'q', b'quiet', None, _(b'suppress output')),
104 (b'q', b'quiet', None, _(b'suppress output')),
106 (b'v', b'verbose', None, _(b'enable additional output')),
105 (b'v', b'verbose', None, _(b'enable additional output')),
107 (
106 (
108 b'',
107 b'',
109 b'color',
108 b'color',
110 b'',
109 b'',
111 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
112 # and should not be translated
111 # and should not be translated
113 _(b"when to colorize (boolean, always, auto, never, or debug)"),
112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
114 _(b'TYPE'),
113 _(b'TYPE'),
115 ),
114 ),
116 (
115 (
117 b'',
116 b'',
118 b'config',
117 b'config',
119 [],
118 [],
120 _(b'set/override config option (use \'section.name=value\')'),
119 _(b'set/override config option (use \'section.name=value\')'),
121 _(b'CONFIG'),
120 _(b'CONFIG'),
122 ),
121 ),
123 (b'', b'debug', None, _(b'enable debugging output')),
122 (b'', b'debug', None, _(b'enable debugging output')),
124 (b'', b'debugger', None, _(b'start debugger')),
123 (b'', b'debugger', None, _(b'start debugger')),
125 (
124 (
126 b'',
125 b'',
127 b'encoding',
126 b'encoding',
128 encoding.encoding,
127 encoding.encoding,
129 _(b'set the charset encoding'),
128 _(b'set the charset encoding'),
130 _(b'ENCODE'),
129 _(b'ENCODE'),
131 ),
130 ),
132 (
131 (
133 b'',
132 b'',
134 b'encodingmode',
133 b'encodingmode',
135 encoding.encodingmode,
134 encoding.encodingmode,
136 _(b'set the charset encoding mode'),
135 _(b'set the charset encoding mode'),
137 _(b'MODE'),
136 _(b'MODE'),
138 ),
137 ),
139 (b'', b'traceback', None, _(b'always print a traceback on exception')),
138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
140 (b'', b'time', None, _(b'time how long the command takes')),
139 (b'', b'time', None, _(b'time how long the command takes')),
141 (b'', b'profile', None, _(b'print command execution profile')),
140 (b'', b'profile', None, _(b'print command execution profile')),
142 (b'', b'version', None, _(b'output version information and exit')),
141 (b'', b'version', None, _(b'output version information and exit')),
143 (b'h', b'help', None, _(b'display help and exit')),
142 (b'h', b'help', None, _(b'display help and exit')),
144 (b'', b'hidden', False, _(b'consider hidden changesets')),
143 (b'', b'hidden', False, _(b'consider hidden changesets')),
145 (
144 (
146 b'',
145 b'',
147 b'pager',
146 b'pager',
148 b'auto',
147 b'auto',
149 _(b"when to paginate (boolean, always, auto, or never)"),
148 _(b"when to paginate (boolean, always, auto, or never)"),
150 _(b'TYPE'),
149 _(b'TYPE'),
151 ),
150 ),
152 ]
151 ]
153
152
154 dryrunopts = cmdutil.dryrunopts
153 dryrunopts = cmdutil.dryrunopts
155 remoteopts = cmdutil.remoteopts
154 remoteopts = cmdutil.remoteopts
156 walkopts = cmdutil.walkopts
155 walkopts = cmdutil.walkopts
157 commitopts = cmdutil.commitopts
156 commitopts = cmdutil.commitopts
158 commitopts2 = cmdutil.commitopts2
157 commitopts2 = cmdutil.commitopts2
159 commitopts3 = cmdutil.commitopts3
158 commitopts3 = cmdutil.commitopts3
160 formatteropts = cmdutil.formatteropts
159 formatteropts = cmdutil.formatteropts
161 templateopts = cmdutil.templateopts
160 templateopts = cmdutil.templateopts
162 logopts = cmdutil.logopts
161 logopts = cmdutil.logopts
163 diffopts = cmdutil.diffopts
162 diffopts = cmdutil.diffopts
164 diffwsopts = cmdutil.diffwsopts
163 diffwsopts = cmdutil.diffwsopts
165 diffopts2 = cmdutil.diffopts2
164 diffopts2 = cmdutil.diffopts2
166 mergetoolopts = cmdutil.mergetoolopts
165 mergetoolopts = cmdutil.mergetoolopts
167 similarityopts = cmdutil.similarityopts
166 similarityopts = cmdutil.similarityopts
168 subrepoopts = cmdutil.subrepoopts
167 subrepoopts = cmdutil.subrepoopts
169 debugrevlogopts = cmdutil.debugrevlogopts
168 debugrevlogopts = cmdutil.debugrevlogopts
170
169
171 # Commands start here, listed alphabetically
170 # Commands start here, listed alphabetically
172
171
173
172
174 @command(
173 @command(
175 b'abort',
174 b'abort',
176 dryrunopts,
175 dryrunopts,
177 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
178 helpbasic=True,
177 helpbasic=True,
179 )
178 )
180 def abort(ui, repo, **opts):
179 def abort(ui, repo, **opts):
181 """abort an unfinished operation (EXPERIMENTAL)
180 """abort an unfinished operation (EXPERIMENTAL)
182
181
183 Aborts a multistep operation like graft, histedit, rebase, merge,
182 Aborts a multistep operation like graft, histedit, rebase, merge,
184 and unshelve if they are in an unfinished state.
183 and unshelve if they are in an unfinished state.
185
184
186 use --dry-run/-n to dry run the command.
185 use --dry-run/-n to dry run the command.
187 """
186 """
188 dryrun = opts.get('dry_run')
187 dryrun = opts.get('dry_run')
189 abortstate = cmdutil.getunfinishedstate(repo)
188 abortstate = cmdutil.getunfinishedstate(repo)
190 if not abortstate:
189 if not abortstate:
191 raise error.StateError(_(b'no operation in progress'))
190 raise error.StateError(_(b'no operation in progress'))
192 if not abortstate.abortfunc:
191 if not abortstate.abortfunc:
193 raise error.InputError(
192 raise error.InputError(
194 (
193 (
195 _(b"%s in progress but does not support 'hg abort'")
194 _(b"%s in progress but does not support 'hg abort'")
196 % (abortstate._opname)
195 % (abortstate._opname)
197 ),
196 ),
198 hint=abortstate.hint(),
197 hint=abortstate.hint(),
199 )
198 )
200 if dryrun:
199 if dryrun:
201 ui.status(
200 ui.status(
202 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
203 )
202 )
204 return
203 return
205 return abortstate.abortfunc(ui, repo)
204 return abortstate.abortfunc(ui, repo)
206
205
207
206
208 @command(
207 @command(
209 b'add',
208 b'add',
210 walkopts + subrepoopts + dryrunopts,
209 walkopts + subrepoopts + dryrunopts,
211 _(b'[OPTION]... [FILE]...'),
210 _(b'[OPTION]... [FILE]...'),
212 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
213 helpbasic=True,
212 helpbasic=True,
214 inferrepo=True,
213 inferrepo=True,
215 )
214 )
216 def add(ui, repo, *pats, **opts):
215 def add(ui, repo, *pats, **opts):
217 """add the specified files on the next commit
216 """add the specified files on the next commit
218
217
219 Schedule files to be version controlled and added to the
218 Schedule files to be version controlled and added to the
220 repository.
219 repository.
221
220
222 The files will be added to the repository at the next commit. To
221 The files will be added to the repository at the next commit. To
223 undo an add before that, see :hg:`forget`.
222 undo an add before that, see :hg:`forget`.
224
223
225 If no names are given, add all files to the repository (except
224 If no names are given, add all files to the repository (except
226 files matching ``.hgignore``).
225 files matching ``.hgignore``).
227
226
228 .. container:: verbose
227 .. container:: verbose
229
228
230 Examples:
229 Examples:
231
230
232 - New (unknown) files are added
231 - New (unknown) files are added
233 automatically by :hg:`add`::
232 automatically by :hg:`add`::
234
233
235 $ ls
234 $ ls
236 foo.c
235 foo.c
237 $ hg status
236 $ hg status
238 ? foo.c
237 ? foo.c
239 $ hg add
238 $ hg add
240 adding foo.c
239 adding foo.c
241 $ hg status
240 $ hg status
242 A foo.c
241 A foo.c
243
242
244 - Specific files to be added can be specified::
243 - Specific files to be added can be specified::
245
244
246 $ ls
245 $ ls
247 bar.c foo.c
246 bar.c foo.c
248 $ hg status
247 $ hg status
249 ? bar.c
248 ? bar.c
250 ? foo.c
249 ? foo.c
251 $ hg add bar.c
250 $ hg add bar.c
252 $ hg status
251 $ hg status
253 A bar.c
252 A bar.c
254 ? foo.c
253 ? foo.c
255
254
256 Returns 0 if all files are successfully added.
255 Returns 0 if all files are successfully added.
257 """
256 """
258
257
259 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
260 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
261 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
262 return rejected and 1 or 0
261 return rejected and 1 or 0
263
262
264
263
265 @command(
264 @command(
266 b'addremove',
265 b'addremove',
267 similarityopts + subrepoopts + walkopts + dryrunopts,
266 similarityopts + subrepoopts + walkopts + dryrunopts,
268 _(b'[OPTION]... [FILE]...'),
267 _(b'[OPTION]... [FILE]...'),
269 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
270 inferrepo=True,
269 inferrepo=True,
271 )
270 )
272 def addremove(ui, repo, *pats, **opts):
271 def addremove(ui, repo, *pats, **opts):
273 """add all new files, delete all missing files
272 """add all new files, delete all missing files
274
273
275 Add all new files and remove all missing files from the
274 Add all new files and remove all missing files from the
276 repository.
275 repository.
277
276
278 Unless names are given, new files are ignored if they match any of
277 Unless names are given, new files are ignored if they match any of
279 the patterns in ``.hgignore``. As with add, these changes take
278 the patterns in ``.hgignore``. As with add, these changes take
280 effect at the next commit.
279 effect at the next commit.
281
280
282 Use the -s/--similarity option to detect renamed files. This
281 Use the -s/--similarity option to detect renamed files. This
283 option takes a percentage between 0 (disabled) and 100 (files must
282 option takes a percentage between 0 (disabled) and 100 (files must
284 be identical) as its parameter. With a parameter greater than 0,
283 be identical) as its parameter. With a parameter greater than 0,
285 this compares every removed file with every added file and records
284 this compares every removed file with every added file and records
286 those similar enough as renames. Detecting renamed files this way
285 those similar enough as renames. Detecting renamed files this way
287 can be expensive. After using this option, :hg:`status -C` can be
286 can be expensive. After using this option, :hg:`status -C` can be
288 used to check which files were identified as moved or renamed. If
287 used to check which files were identified as moved or renamed. If
289 not specified, -s/--similarity defaults to 100 and only renames of
288 not specified, -s/--similarity defaults to 100 and only renames of
290 identical files are detected.
289 identical files are detected.
291
290
292 .. container:: verbose
291 .. container:: verbose
293
292
294 Examples:
293 Examples:
295
294
296 - A number of files (bar.c and foo.c) are new,
295 - A number of files (bar.c and foo.c) are new,
297 while foobar.c has been removed (without using :hg:`remove`)
296 while foobar.c has been removed (without using :hg:`remove`)
298 from the repository::
297 from the repository::
299
298
300 $ ls
299 $ ls
301 bar.c foo.c
300 bar.c foo.c
302 $ hg status
301 $ hg status
303 ! foobar.c
302 ! foobar.c
304 ? bar.c
303 ? bar.c
305 ? foo.c
304 ? foo.c
306 $ hg addremove
305 $ hg addremove
307 adding bar.c
306 adding bar.c
308 adding foo.c
307 adding foo.c
309 removing foobar.c
308 removing foobar.c
310 $ hg status
309 $ hg status
311 A bar.c
310 A bar.c
312 A foo.c
311 A foo.c
313 R foobar.c
312 R foobar.c
314
313
315 - A file foobar.c was moved to foo.c without using :hg:`rename`.
314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
316 Afterwards, it was edited slightly::
315 Afterwards, it was edited slightly::
317
316
318 $ ls
317 $ ls
319 foo.c
318 foo.c
320 $ hg status
319 $ hg status
321 ! foobar.c
320 ! foobar.c
322 ? foo.c
321 ? foo.c
323 $ hg addremove --similarity 90
322 $ hg addremove --similarity 90
324 removing foobar.c
323 removing foobar.c
325 adding foo.c
324 adding foo.c
326 recording removal of foobar.c as rename to foo.c (94% similar)
325 recording removal of foobar.c as rename to foo.c (94% similar)
327 $ hg status -C
326 $ hg status -C
328 A foo.c
327 A foo.c
329 foobar.c
328 foobar.c
330 R foobar.c
329 R foobar.c
331
330
332 Returns 0 if all files are successfully added.
331 Returns 0 if all files are successfully added.
333 """
332 """
334 opts = pycompat.byteskwargs(opts)
333 opts = pycompat.byteskwargs(opts)
335 if not opts.get(b'similarity'):
334 if not opts.get(b'similarity'):
336 opts[b'similarity'] = b'100'
335 opts[b'similarity'] = b'100'
337 matcher = scmutil.match(repo[None], pats, opts)
336 matcher = scmutil.match(repo[None], pats, opts)
338 relative = scmutil.anypats(pats, opts)
337 relative = scmutil.anypats(pats, opts)
339 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
340 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
341
340
342
341
343 @command(
342 @command(
344 b'annotate|blame',
343 b'annotate|blame',
345 [
344 [
346 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
347 (
346 (
348 b'',
347 b'',
349 b'follow',
348 b'follow',
350 None,
349 None,
351 _(b'follow copies/renames and list the filename (DEPRECATED)'),
350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
352 ),
351 ),
353 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
354 (b'a', b'text', None, _(b'treat all files as text')),
353 (b'a', b'text', None, _(b'treat all files as text')),
355 (b'u', b'user', None, _(b'list the author (long with -v)')),
354 (b'u', b'user', None, _(b'list the author (long with -v)')),
356 (b'f', b'file', None, _(b'list the filename')),
355 (b'f', b'file', None, _(b'list the filename')),
357 (b'd', b'date', None, _(b'list the date (short with -q)')),
356 (b'd', b'date', None, _(b'list the date (short with -q)')),
358 (b'n', b'number', None, _(b'list the revision number (default)')),
357 (b'n', b'number', None, _(b'list the revision number (default)')),
359 (b'c', b'changeset', None, _(b'list the changeset')),
358 (b'c', b'changeset', None, _(b'list the changeset')),
360 (
359 (
361 b'l',
360 b'l',
362 b'line-number',
361 b'line-number',
363 None,
362 None,
364 _(b'show line number at the first appearance'),
363 _(b'show line number at the first appearance'),
365 ),
364 ),
366 (
365 (
367 b'',
366 b'',
368 b'skip',
367 b'skip',
369 [],
368 [],
370 _(b'revset to not display (EXPERIMENTAL)'),
369 _(b'revset to not display (EXPERIMENTAL)'),
371 _(b'REV'),
370 _(b'REV'),
372 ),
371 ),
373 ]
372 ]
374 + diffwsopts
373 + diffwsopts
375 + walkopts
374 + walkopts
376 + formatteropts,
375 + formatteropts,
377 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
378 helpcategory=command.CATEGORY_FILE_CONTENTS,
377 helpcategory=command.CATEGORY_FILE_CONTENTS,
379 helpbasic=True,
378 helpbasic=True,
380 inferrepo=True,
379 inferrepo=True,
381 )
380 )
382 def annotate(ui, repo, *pats, **opts):
381 def annotate(ui, repo, *pats, **opts):
383 """show changeset information by line for each file
382 """show changeset information by line for each file
384
383
385 List changes in files, showing the revision id responsible for
384 List changes in files, showing the revision id responsible for
386 each line.
385 each line.
387
386
388 This command is useful for discovering when a change was made and
387 This command is useful for discovering when a change was made and
389 by whom.
388 by whom.
390
389
391 If you include --file, --user, or --date, the revision number is
390 If you include --file, --user, or --date, the revision number is
392 suppressed unless you also include --number.
391 suppressed unless you also include --number.
393
392
394 Without the -a/--text option, annotate will avoid processing files
393 Without the -a/--text option, annotate will avoid processing files
395 it detects as binary. With -a, annotate will annotate the file
394 it detects as binary. With -a, annotate will annotate the file
396 anyway, although the results will probably be neither useful
395 anyway, although the results will probably be neither useful
397 nor desirable.
396 nor desirable.
398
397
399 .. container:: verbose
398 .. container:: verbose
400
399
401 Template:
400 Template:
402
401
403 The following keywords are supported in addition to the common template
402 The following keywords are supported in addition to the common template
404 keywords and functions. See also :hg:`help templates`.
403 keywords and functions. See also :hg:`help templates`.
405
404
406 :lines: List of lines with annotation data.
405 :lines: List of lines with annotation data.
407 :path: String. Repository-absolute path of the specified file.
406 :path: String. Repository-absolute path of the specified file.
408
407
409 And each entry of ``{lines}`` provides the following sub-keywords in
408 And each entry of ``{lines}`` provides the following sub-keywords in
410 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
411
410
412 :line: String. Line content.
411 :line: String. Line content.
413 :lineno: Integer. Line number at that revision.
412 :lineno: Integer. Line number at that revision.
414 :path: String. Repository-absolute path of the file at that revision.
413 :path: String. Repository-absolute path of the file at that revision.
415
414
416 See :hg:`help templates.operators` for the list expansion syntax.
415 See :hg:`help templates.operators` for the list expansion syntax.
417
416
418 Returns 0 on success.
417 Returns 0 on success.
419 """
418 """
420 opts = pycompat.byteskwargs(opts)
419 opts = pycompat.byteskwargs(opts)
421 if not pats:
420 if not pats:
422 raise error.InputError(
421 raise error.InputError(
423 _(b'at least one filename or pattern is required')
422 _(b'at least one filename or pattern is required')
424 )
423 )
425
424
426 if opts.get(b'follow'):
425 if opts.get(b'follow'):
427 # --follow is deprecated and now just an alias for -f/--file
426 # --follow is deprecated and now just an alias for -f/--file
428 # to mimic the behavior of Mercurial before version 1.5
427 # to mimic the behavior of Mercurial before version 1.5
429 opts[b'file'] = True
428 opts[b'file'] = True
430
429
431 if (
430 if (
432 not opts.get(b'user')
431 not opts.get(b'user')
433 and not opts.get(b'changeset')
432 and not opts.get(b'changeset')
434 and not opts.get(b'date')
433 and not opts.get(b'date')
435 and not opts.get(b'file')
434 and not opts.get(b'file')
436 ):
435 ):
437 opts[b'number'] = True
436 opts[b'number'] = True
438
437
439 linenumber = opts.get(b'line_number') is not None
438 linenumber = opts.get(b'line_number') is not None
440 if (
439 if (
441 linenumber
440 linenumber
442 and (not opts.get(b'changeset'))
441 and (not opts.get(b'changeset'))
443 and (not opts.get(b'number'))
442 and (not opts.get(b'number'))
444 ):
443 ):
445 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
444 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
446
445
447 rev = opts.get(b'rev')
446 rev = opts.get(b'rev')
448 if rev:
447 if rev:
449 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
448 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
450 ctx = scmutil.revsingle(repo, rev)
449 ctx = scmutil.revsingle(repo, rev)
451
450
452 ui.pager(b'annotate')
451 ui.pager(b'annotate')
453 rootfm = ui.formatter(b'annotate', opts)
452 rootfm = ui.formatter(b'annotate', opts)
454 if ui.debugflag:
453 if ui.debugflag:
455 shorthex = pycompat.identity
454 shorthex = pycompat.identity
456 else:
455 else:
457
456
458 def shorthex(h):
457 def shorthex(h):
459 return h[:12]
458 return h[:12]
460
459
461 if ui.quiet:
460 if ui.quiet:
462 datefunc = dateutil.shortdate
461 datefunc = dateutil.shortdate
463 else:
462 else:
464 datefunc = dateutil.datestr
463 datefunc = dateutil.datestr
465 if ctx.rev() is None:
464 if ctx.rev() is None:
466 if opts.get(b'changeset'):
465 if opts.get(b'changeset'):
467 # omit "+" suffix which is appended to node hex
466 # omit "+" suffix which is appended to node hex
468 def formatrev(rev):
467 def formatrev(rev):
469 if rev == wdirrev:
468 if rev == wdirrev:
470 return b'%d' % ctx.p1().rev()
469 return b'%d' % ctx.p1().rev()
471 else:
470 else:
472 return b'%d' % rev
471 return b'%d' % rev
473
472
474 else:
473 else:
475
474
476 def formatrev(rev):
475 def formatrev(rev):
477 if rev == wdirrev:
476 if rev == wdirrev:
478 return b'%d+' % ctx.p1().rev()
477 return b'%d+' % ctx.p1().rev()
479 else:
478 else:
480 return b'%d ' % rev
479 return b'%d ' % rev
481
480
482 def formathex(h):
481 def formathex(h):
483 if h == wdirhex:
482 if h == wdirhex:
484 return b'%s+' % shorthex(hex(ctx.p1().node()))
483 return b'%s+' % shorthex(hex(ctx.p1().node()))
485 else:
484 else:
486 return b'%s ' % shorthex(h)
485 return b'%s ' % shorthex(h)
487
486
488 else:
487 else:
489 formatrev = b'%d'.__mod__
488 formatrev = b'%d'.__mod__
490 formathex = shorthex
489 formathex = shorthex
491
490
492 opmap = [
491 opmap = [
493 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
492 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
494 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
493 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
495 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
494 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
496 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
495 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
497 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
496 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
498 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
497 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
499 ]
498 ]
500 opnamemap = {
499 opnamemap = {
501 b'rev': b'number',
500 b'rev': b'number',
502 b'node': b'changeset',
501 b'node': b'changeset',
503 b'path': b'file',
502 b'path': b'file',
504 b'lineno': b'line_number',
503 b'lineno': b'line_number',
505 }
504 }
506
505
507 if rootfm.isplain():
506 if rootfm.isplain():
508
507
509 def makefunc(get, fmt):
508 def makefunc(get, fmt):
510 return lambda x: fmt(get(x))
509 return lambda x: fmt(get(x))
511
510
512 else:
511 else:
513
512
514 def makefunc(get, fmt):
513 def makefunc(get, fmt):
515 return get
514 return get
516
515
517 datahint = rootfm.datahint()
516 datahint = rootfm.datahint()
518 funcmap = [
517 funcmap = [
519 (makefunc(get, fmt), sep)
518 (makefunc(get, fmt), sep)
520 for fn, sep, get, fmt in opmap
519 for fn, sep, get, fmt in opmap
521 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
522 ]
521 ]
523 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
522 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
524 fields = b' '.join(
523 fields = b' '.join(
525 fn
524 fn
526 for fn, sep, get, fmt in opmap
525 for fn, sep, get, fmt in opmap
527 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
528 )
527 )
529
528
530 def bad(x, y):
529 def bad(x, y):
531 raise error.Abort(b"%s: %s" % (x, y))
530 raise error.Abort(b"%s: %s" % (x, y))
532
531
533 m = scmutil.match(ctx, pats, opts, badfn=bad)
532 m = scmutil.match(ctx, pats, opts, badfn=bad)
534
533
535 follow = not opts.get(b'no_follow')
534 follow = not opts.get(b'no_follow')
536 diffopts = patch.difffeatureopts(
535 diffopts = patch.difffeatureopts(
537 ui, opts, section=b'annotate', whitespace=True
536 ui, opts, section=b'annotate', whitespace=True
538 )
537 )
539 skiprevs = opts.get(b'skip')
538 skiprevs = opts.get(b'skip')
540 if skiprevs:
539 if skiprevs:
541 skiprevs = scmutil.revrange(repo, skiprevs)
540 skiprevs = scmutil.revrange(repo, skiprevs)
542
541
543 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
542 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
544 for abs in ctx.walk(m):
543 for abs in ctx.walk(m):
545 fctx = ctx[abs]
544 fctx = ctx[abs]
546 rootfm.startitem()
545 rootfm.startitem()
547 rootfm.data(path=abs)
546 rootfm.data(path=abs)
548 if not opts.get(b'text') and fctx.isbinary():
547 if not opts.get(b'text') and fctx.isbinary():
549 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
548 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
550 continue
549 continue
551
550
552 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
551 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
553 lines = fctx.annotate(
552 lines = fctx.annotate(
554 follow=follow, skiprevs=skiprevs, diffopts=diffopts
553 follow=follow, skiprevs=skiprevs, diffopts=diffopts
555 )
554 )
556 if not lines:
555 if not lines:
557 fm.end()
556 fm.end()
558 continue
557 continue
559 formats = []
558 formats = []
560 pieces = []
559 pieces = []
561
560
562 for f, sep in funcmap:
561 for f, sep in funcmap:
563 l = [f(n) for n in lines]
562 l = [f(n) for n in lines]
564 if fm.isplain():
563 if fm.isplain():
565 sizes = [encoding.colwidth(x) for x in l]
564 sizes = [encoding.colwidth(x) for x in l]
566 ml = max(sizes)
565 ml = max(sizes)
567 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
566 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
568 else:
567 else:
569 formats.append([b'%s'] * len(l))
568 formats.append([b'%s'] * len(l))
570 pieces.append(l)
569 pieces.append(l)
571
570
572 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
571 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
573 fm.startitem()
572 fm.startitem()
574 fm.context(fctx=n.fctx)
573 fm.context(fctx=n.fctx)
575 fm.write(fields, b"".join(f), *p)
574 fm.write(fields, b"".join(f), *p)
576 if n.skip:
575 if n.skip:
577 fmt = b"* %s"
576 fmt = b"* %s"
578 else:
577 else:
579 fmt = b": %s"
578 fmt = b": %s"
580 fm.write(b'line', fmt, n.text)
579 fm.write(b'line', fmt, n.text)
581
580
582 if not lines[-1].text.endswith(b'\n'):
581 if not lines[-1].text.endswith(b'\n'):
583 fm.plain(b'\n')
582 fm.plain(b'\n')
584 fm.end()
583 fm.end()
585
584
586 rootfm.end()
585 rootfm.end()
587
586
588
587
589 @command(
588 @command(
590 b'archive',
589 b'archive',
591 [
590 [
592 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
591 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
593 (
592 (
594 b'p',
593 b'p',
595 b'prefix',
594 b'prefix',
596 b'',
595 b'',
597 _(b'directory prefix for files in archive'),
596 _(b'directory prefix for files in archive'),
598 _(b'PREFIX'),
597 _(b'PREFIX'),
599 ),
598 ),
600 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
599 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
601 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
600 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
602 ]
601 ]
603 + subrepoopts
602 + subrepoopts
604 + walkopts,
603 + walkopts,
605 _(b'[OPTION]... DEST'),
604 _(b'[OPTION]... DEST'),
606 helpcategory=command.CATEGORY_IMPORT_EXPORT,
605 helpcategory=command.CATEGORY_IMPORT_EXPORT,
607 )
606 )
608 def archive(ui, repo, dest, **opts):
607 def archive(ui, repo, dest, **opts):
609 """create an unversioned archive of a repository revision
608 """create an unversioned archive of a repository revision
610
609
611 By default, the revision used is the parent of the working
610 By default, the revision used is the parent of the working
612 directory; use -r/--rev to specify a different revision.
611 directory; use -r/--rev to specify a different revision.
613
612
614 The archive type is automatically detected based on file
613 The archive type is automatically detected based on file
615 extension (to override, use -t/--type).
614 extension (to override, use -t/--type).
616
615
617 .. container:: verbose
616 .. container:: verbose
618
617
619 Examples:
618 Examples:
620
619
621 - create a zip file containing the 1.0 release::
620 - create a zip file containing the 1.0 release::
622
621
623 hg archive -r 1.0 project-1.0.zip
622 hg archive -r 1.0 project-1.0.zip
624
623
625 - create a tarball excluding .hg files::
624 - create a tarball excluding .hg files::
626
625
627 hg archive project.tar.gz -X ".hg*"
626 hg archive project.tar.gz -X ".hg*"
628
627
629 Valid types are:
628 Valid types are:
630
629
631 :``files``: a directory full of files (default)
630 :``files``: a directory full of files (default)
632 :``tar``: tar archive, uncompressed
631 :``tar``: tar archive, uncompressed
633 :``tbz2``: tar archive, compressed using bzip2
632 :``tbz2``: tar archive, compressed using bzip2
634 :``tgz``: tar archive, compressed using gzip
633 :``tgz``: tar archive, compressed using gzip
635 :``txz``: tar archive, compressed using lzma (only in Python 3)
634 :``txz``: tar archive, compressed using lzma (only in Python 3)
636 :``uzip``: zip archive, uncompressed
635 :``uzip``: zip archive, uncompressed
637 :``zip``: zip archive, compressed using deflate
636 :``zip``: zip archive, compressed using deflate
638
637
639 The exact name of the destination archive or directory is given
638 The exact name of the destination archive or directory is given
640 using a format string; see :hg:`help export` for details.
639 using a format string; see :hg:`help export` for details.
641
640
642 Each member added to an archive file has a directory prefix
641 Each member added to an archive file has a directory prefix
643 prepended. Use -p/--prefix to specify a format string for the
642 prepended. Use -p/--prefix to specify a format string for the
644 prefix. The default is the basename of the archive, with suffixes
643 prefix. The default is the basename of the archive, with suffixes
645 removed.
644 removed.
646
645
647 Returns 0 on success.
646 Returns 0 on success.
648 """
647 """
649
648
650 opts = pycompat.byteskwargs(opts)
649 opts = pycompat.byteskwargs(opts)
651 rev = opts.get(b'rev')
650 rev = opts.get(b'rev')
652 if rev:
651 if rev:
653 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
652 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
654 ctx = scmutil.revsingle(repo, rev)
653 ctx = scmutil.revsingle(repo, rev)
655 if not ctx:
654 if not ctx:
656 raise error.InputError(
655 raise error.InputError(
657 _(b'no working directory: please specify a revision')
656 _(b'no working directory: please specify a revision')
658 )
657 )
659 node = ctx.node()
658 node = ctx.node()
660 dest = cmdutil.makefilename(ctx, dest)
659 dest = cmdutil.makefilename(ctx, dest)
661 if os.path.realpath(dest) == repo.root:
660 if os.path.realpath(dest) == repo.root:
662 raise error.InputError(_(b'repository root cannot be destination'))
661 raise error.InputError(_(b'repository root cannot be destination'))
663
662
664 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
663 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
665 prefix = opts.get(b'prefix')
664 prefix = opts.get(b'prefix')
666
665
667 if dest == b'-':
666 if dest == b'-':
668 if kind == b'files':
667 if kind == b'files':
669 raise error.InputError(_(b'cannot archive plain files to stdout'))
668 raise error.InputError(_(b'cannot archive plain files to stdout'))
670 dest = cmdutil.makefileobj(ctx, dest)
669 dest = cmdutil.makefileobj(ctx, dest)
671 if not prefix:
670 if not prefix:
672 prefix = os.path.basename(repo.root) + b'-%h'
671 prefix = os.path.basename(repo.root) + b'-%h'
673
672
674 prefix = cmdutil.makefilename(ctx, prefix)
673 prefix = cmdutil.makefilename(ctx, prefix)
675 match = scmutil.match(ctx, [], opts)
674 match = scmutil.match(ctx, [], opts)
676 archival.archive(
675 archival.archive(
677 repo,
676 repo,
678 dest,
677 dest,
679 node,
678 node,
680 kind,
679 kind,
681 not opts.get(b'no_decode'),
680 not opts.get(b'no_decode'),
682 match,
681 match,
683 prefix,
682 prefix,
684 subrepos=opts.get(b'subrepos'),
683 subrepos=opts.get(b'subrepos'),
685 )
684 )
686
685
687
686
688 @command(
687 @command(
689 b'backout',
688 b'backout',
690 [
689 [
691 (
690 (
692 b'',
691 b'',
693 b'merge',
692 b'merge',
694 None,
693 None,
695 _(b'merge with old dirstate parent after backout'),
694 _(b'merge with old dirstate parent after backout'),
696 ),
695 ),
697 (
696 (
698 b'',
697 b'',
699 b'commit',
698 b'commit',
700 None,
699 None,
701 _(b'commit if no conflicts were encountered (DEPRECATED)'),
700 _(b'commit if no conflicts were encountered (DEPRECATED)'),
702 ),
701 ),
703 (b'', b'no-commit', None, _(b'do not commit')),
702 (b'', b'no-commit', None, _(b'do not commit')),
704 (
703 (
705 b'',
704 b'',
706 b'parent',
705 b'parent',
707 b'',
706 b'',
708 _(b'parent to choose when backing out merge (DEPRECATED)'),
707 _(b'parent to choose when backing out merge (DEPRECATED)'),
709 _(b'REV'),
708 _(b'REV'),
710 ),
709 ),
711 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
710 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
712 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
711 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
713 ]
712 ]
714 + mergetoolopts
713 + mergetoolopts
715 + walkopts
714 + walkopts
716 + commitopts
715 + commitopts
717 + commitopts2,
716 + commitopts2,
718 _(b'[OPTION]... [-r] REV'),
717 _(b'[OPTION]... [-r] REV'),
719 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
718 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
720 )
719 )
721 def backout(ui, repo, node=None, rev=None, **opts):
720 def backout(ui, repo, node=None, rev=None, **opts):
722 """reverse effect of earlier changeset
721 """reverse effect of earlier changeset
723
722
724 Prepare a new changeset with the effect of REV undone in the
723 Prepare a new changeset with the effect of REV undone in the
725 current working directory. If no conflicts were encountered,
724 current working directory. If no conflicts were encountered,
726 it will be committed immediately.
725 it will be committed immediately.
727
726
728 If REV is the parent of the working directory, then this new changeset
727 If REV is the parent of the working directory, then this new changeset
729 is committed automatically (unless --no-commit is specified).
728 is committed automatically (unless --no-commit is specified).
730
729
731 .. note::
730 .. note::
732
731
733 :hg:`backout` cannot be used to fix either an unwanted or
732 :hg:`backout` cannot be used to fix either an unwanted or
734 incorrect merge.
733 incorrect merge.
735
734
736 .. container:: verbose
735 .. container:: verbose
737
736
738 Examples:
737 Examples:
739
738
740 - Reverse the effect of the parent of the working directory.
739 - Reverse the effect of the parent of the working directory.
741 This backout will be committed immediately::
740 This backout will be committed immediately::
742
741
743 hg backout -r .
742 hg backout -r .
744
743
745 - Reverse the effect of previous bad revision 23::
744 - Reverse the effect of previous bad revision 23::
746
745
747 hg backout -r 23
746 hg backout -r 23
748
747
749 - Reverse the effect of previous bad revision 23 and
748 - Reverse the effect of previous bad revision 23 and
750 leave changes uncommitted::
749 leave changes uncommitted::
751
750
752 hg backout -r 23 --no-commit
751 hg backout -r 23 --no-commit
753 hg commit -m "Backout revision 23"
752 hg commit -m "Backout revision 23"
754
753
755 By default, the pending changeset will have one parent,
754 By default, the pending changeset will have one parent,
756 maintaining a linear history. With --merge, the pending
755 maintaining a linear history. With --merge, the pending
757 changeset will instead have two parents: the old parent of the
756 changeset will instead have two parents: the old parent of the
758 working directory and a new child of REV that simply undoes REV.
757 working directory and a new child of REV that simply undoes REV.
759
758
760 Before version 1.7, the behavior without --merge was equivalent
759 Before version 1.7, the behavior without --merge was equivalent
761 to specifying --merge followed by :hg:`update --clean .` to
760 to specifying --merge followed by :hg:`update --clean .` to
762 cancel the merge and leave the child of REV as a head to be
761 cancel the merge and leave the child of REV as a head to be
763 merged separately.
762 merged separately.
764
763
765 See :hg:`help dates` for a list of formats valid for -d/--date.
764 See :hg:`help dates` for a list of formats valid for -d/--date.
766
765
767 See :hg:`help revert` for a way to restore files to the state
766 See :hg:`help revert` for a way to restore files to the state
768 of another revision.
767 of another revision.
769
768
770 Returns 0 on success, 1 if nothing to backout or there are unresolved
769 Returns 0 on success, 1 if nothing to backout or there are unresolved
771 files.
770 files.
772 """
771 """
773 with repo.wlock(), repo.lock():
772 with repo.wlock(), repo.lock():
774 return _dobackout(ui, repo, node, rev, **opts)
773 return _dobackout(ui, repo, node, rev, **opts)
775
774
776
775
777 def _dobackout(ui, repo, node=None, rev=None, **opts):
776 def _dobackout(ui, repo, node=None, rev=None, **opts):
778 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
777 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
779 opts = pycompat.byteskwargs(opts)
778 opts = pycompat.byteskwargs(opts)
780
779
781 if rev and node:
780 if rev and node:
782 raise error.InputError(_(b"please specify just one revision"))
781 raise error.InputError(_(b"please specify just one revision"))
783
782
784 if not rev:
783 if not rev:
785 rev = node
784 rev = node
786
785
787 if not rev:
786 if not rev:
788 raise error.InputError(_(b"please specify a revision to backout"))
787 raise error.InputError(_(b"please specify a revision to backout"))
789
788
790 date = opts.get(b'date')
789 date = opts.get(b'date')
791 if date:
790 if date:
792 opts[b'date'] = dateutil.parsedate(date)
791 opts[b'date'] = dateutil.parsedate(date)
793
792
794 cmdutil.checkunfinished(repo)
793 cmdutil.checkunfinished(repo)
795 cmdutil.bailifchanged(repo)
794 cmdutil.bailifchanged(repo)
796 ctx = scmutil.revsingle(repo, rev)
795 ctx = scmutil.revsingle(repo, rev)
797 node = ctx.node()
796 node = ctx.node()
798
797
799 op1, op2 = repo.dirstate.parents()
798 op1, op2 = repo.dirstate.parents()
800 if not repo.changelog.isancestor(node, op1):
799 if not repo.changelog.isancestor(node, op1):
801 raise error.InputError(
800 raise error.InputError(
802 _(b'cannot backout change that is not an ancestor')
801 _(b'cannot backout change that is not an ancestor')
803 )
802 )
804
803
805 p1, p2 = repo.changelog.parents(node)
804 p1, p2 = repo.changelog.parents(node)
806 if p1 == nullid:
805 if p1 == nullid:
807 raise error.InputError(_(b'cannot backout a change with no parents'))
806 raise error.InputError(_(b'cannot backout a change with no parents'))
808 if p2 != nullid:
807 if p2 != nullid:
809 if not opts.get(b'parent'):
808 if not opts.get(b'parent'):
810 raise error.InputError(_(b'cannot backout a merge changeset'))
809 raise error.InputError(_(b'cannot backout a merge changeset'))
811 p = repo.lookup(opts[b'parent'])
810 p = repo.lookup(opts[b'parent'])
812 if p not in (p1, p2):
811 if p not in (p1, p2):
813 raise error.InputError(
812 raise error.InputError(
814 _(b'%s is not a parent of %s') % (short(p), short(node))
813 _(b'%s is not a parent of %s') % (short(p), short(node))
815 )
814 )
816 parent = p
815 parent = p
817 else:
816 else:
818 if opts.get(b'parent'):
817 if opts.get(b'parent'):
819 raise error.InputError(
818 raise error.InputError(
820 _(b'cannot use --parent on non-merge changeset')
819 _(b'cannot use --parent on non-merge changeset')
821 )
820 )
822 parent = p1
821 parent = p1
823
822
824 # the backout should appear on the same branch
823 # the backout should appear on the same branch
825 branch = repo.dirstate.branch()
824 branch = repo.dirstate.branch()
826 bheads = repo.branchheads(branch)
825 bheads = repo.branchheads(branch)
827 rctx = scmutil.revsingle(repo, hex(parent))
826 rctx = scmutil.revsingle(repo, hex(parent))
828 if not opts.get(b'merge') and op1 != node:
827 if not opts.get(b'merge') and op1 != node:
829 with dirstateguard.dirstateguard(repo, b'backout'):
828 with dirstateguard.dirstateguard(repo, b'backout'):
830 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
829 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
831 with ui.configoverride(overrides, b'backout'):
830 with ui.configoverride(overrides, b'backout'):
832 stats = mergemod.back_out(ctx, parent=repo[parent])
831 stats = mergemod.back_out(ctx, parent=repo[parent])
833 repo.setparents(op1, op2)
832 repo.setparents(op1, op2)
834 hg._showstats(repo, stats)
833 hg._showstats(repo, stats)
835 if stats.unresolvedcount:
834 if stats.unresolvedcount:
836 repo.ui.status(
835 repo.ui.status(
837 _(b"use 'hg resolve' to retry unresolved file merges\n")
836 _(b"use 'hg resolve' to retry unresolved file merges\n")
838 )
837 )
839 return 1
838 return 1
840 else:
839 else:
841 hg.clean(repo, node, show_stats=False)
840 hg.clean(repo, node, show_stats=False)
842 repo.dirstate.setbranch(branch)
841 repo.dirstate.setbranch(branch)
843 cmdutil.revert(ui, repo, rctx)
842 cmdutil.revert(ui, repo, rctx)
844
843
845 if opts.get(b'no_commit'):
844 if opts.get(b'no_commit'):
846 msg = _(b"changeset %s backed out, don't forget to commit.\n")
845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
847 ui.status(msg % short(node))
846 ui.status(msg % short(node))
848 return 0
847 return 0
849
848
850 def commitfunc(ui, repo, message, match, opts):
849 def commitfunc(ui, repo, message, match, opts):
851 editform = b'backout'
850 editform = b'backout'
852 e = cmdutil.getcommiteditor(
851 e = cmdutil.getcommiteditor(
853 editform=editform, **pycompat.strkwargs(opts)
852 editform=editform, **pycompat.strkwargs(opts)
854 )
853 )
855 if not message:
854 if not message:
856 # we don't translate commit messages
855 # we don't translate commit messages
857 message = b"Backed out changeset %s" % short(node)
856 message = b"Backed out changeset %s" % short(node)
858 e = cmdutil.getcommiteditor(edit=True, editform=editform)
857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
859 return repo.commit(
858 return repo.commit(
860 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
861 )
860 )
862
861
863 # save to detect changes
862 # save to detect changes
864 tip = repo.changelog.tip()
863 tip = repo.changelog.tip()
865
864
866 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
865 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
867 if not newnode:
866 if not newnode:
868 ui.status(_(b"nothing changed\n"))
867 ui.status(_(b"nothing changed\n"))
869 return 1
868 return 1
870 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
869 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
871
870
872 def nice(node):
871 def nice(node):
873 return b'%d:%s' % (repo.changelog.rev(node), short(node))
872 return b'%d:%s' % (repo.changelog.rev(node), short(node))
874
873
875 ui.status(
874 ui.status(
876 _(b'changeset %s backs out changeset %s\n')
875 _(b'changeset %s backs out changeset %s\n')
877 % (nice(newnode), nice(node))
876 % (nice(newnode), nice(node))
878 )
877 )
879 if opts.get(b'merge') and op1 != node:
878 if opts.get(b'merge') and op1 != node:
880 hg.clean(repo, op1, show_stats=False)
879 hg.clean(repo, op1, show_stats=False)
881 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
880 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
882 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
881 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
883 with ui.configoverride(overrides, b'backout'):
882 with ui.configoverride(overrides, b'backout'):
884 return hg.merge(repo[b'tip'])
883 return hg.merge(repo[b'tip'])
885 return 0
884 return 0
886
885
887
886
888 @command(
887 @command(
889 b'bisect',
888 b'bisect',
890 [
889 [
891 (b'r', b'reset', False, _(b'reset bisect state')),
890 (b'r', b'reset', False, _(b'reset bisect state')),
892 (b'g', b'good', False, _(b'mark changeset good')),
891 (b'g', b'good', False, _(b'mark changeset good')),
893 (b'b', b'bad', False, _(b'mark changeset bad')),
892 (b'b', b'bad', False, _(b'mark changeset bad')),
894 (b's', b'skip', False, _(b'skip testing changeset')),
893 (b's', b'skip', False, _(b'skip testing changeset')),
895 (b'e', b'extend', False, _(b'extend the bisect range')),
894 (b'e', b'extend', False, _(b'extend the bisect range')),
896 (
895 (
897 b'c',
896 b'c',
898 b'command',
897 b'command',
899 b'',
898 b'',
900 _(b'use command to check changeset state'),
899 _(b'use command to check changeset state'),
901 _(b'CMD'),
900 _(b'CMD'),
902 ),
901 ),
903 (b'U', b'noupdate', False, _(b'do not update to target')),
902 (b'U', b'noupdate', False, _(b'do not update to target')),
904 ],
903 ],
905 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
906 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
907 )
906 )
908 def bisect(
907 def bisect(
909 ui,
908 ui,
910 repo,
909 repo,
911 positional_1=None,
910 positional_1=None,
912 positional_2=None,
911 positional_2=None,
913 command=None,
912 command=None,
914 reset=None,
913 reset=None,
915 good=None,
914 good=None,
916 bad=None,
915 bad=None,
917 skip=None,
916 skip=None,
918 extend=None,
917 extend=None,
919 noupdate=None,
918 noupdate=None,
920 ):
919 ):
921 """subdivision search of changesets
920 """subdivision search of changesets
922
921
923 This command helps to find changesets which introduce problems. To
922 This command helps to find changesets which introduce problems. To
924 use, mark the earliest changeset you know exhibits the problem as
923 use, mark the earliest changeset you know exhibits the problem as
925 bad, then mark the latest changeset which is free from the problem
924 bad, then mark the latest changeset which is free from the problem
926 as good. Bisect will update your working directory to a revision
925 as good. Bisect will update your working directory to a revision
927 for testing (unless the -U/--noupdate option is specified). Once
926 for testing (unless the -U/--noupdate option is specified). Once
928 you have performed tests, mark the working directory as good or
927 you have performed tests, mark the working directory as good or
929 bad, and bisect will either update to another candidate changeset
928 bad, and bisect will either update to another candidate changeset
930 or announce that it has found the bad revision.
929 or announce that it has found the bad revision.
931
930
932 As a shortcut, you can also use the revision argument to mark a
931 As a shortcut, you can also use the revision argument to mark a
933 revision as good or bad without checking it out first.
932 revision as good or bad without checking it out first.
934
933
935 If you supply a command, it will be used for automatic bisection.
934 If you supply a command, it will be used for automatic bisection.
936 The environment variable HG_NODE will contain the ID of the
935 The environment variable HG_NODE will contain the ID of the
937 changeset being tested. The exit status of the command will be
936 changeset being tested. The exit status of the command will be
938 used to mark revisions as good or bad: status 0 means good, 125
937 used to mark revisions as good or bad: status 0 means good, 125
939 means to skip the revision, 127 (command not found) will abort the
938 means to skip the revision, 127 (command not found) will abort the
940 bisection, and any other non-zero exit status means the revision
939 bisection, and any other non-zero exit status means the revision
941 is bad.
940 is bad.
942
941
943 .. container:: verbose
942 .. container:: verbose
944
943
945 Some examples:
944 Some examples:
946
945
947 - start a bisection with known bad revision 34, and good revision 12::
946 - start a bisection with known bad revision 34, and good revision 12::
948
947
949 hg bisect --bad 34
948 hg bisect --bad 34
950 hg bisect --good 12
949 hg bisect --good 12
951
950
952 - advance the current bisection by marking current revision as good or
951 - advance the current bisection by marking current revision as good or
953 bad::
952 bad::
954
953
955 hg bisect --good
954 hg bisect --good
956 hg bisect --bad
955 hg bisect --bad
957
956
958 - mark the current revision, or a known revision, to be skipped (e.g. if
957 - mark the current revision, or a known revision, to be skipped (e.g. if
959 that revision is not usable because of another issue)::
958 that revision is not usable because of another issue)::
960
959
961 hg bisect --skip
960 hg bisect --skip
962 hg bisect --skip 23
961 hg bisect --skip 23
963
962
964 - skip all revisions that do not touch directories ``foo`` or ``bar``::
963 - skip all revisions that do not touch directories ``foo`` or ``bar``::
965
964
966 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
967
966
968 - forget the current bisection::
967 - forget the current bisection::
969
968
970 hg bisect --reset
969 hg bisect --reset
971
970
972 - use 'make && make tests' to automatically find the first broken
971 - use 'make && make tests' to automatically find the first broken
973 revision::
972 revision::
974
973
975 hg bisect --reset
974 hg bisect --reset
976 hg bisect --bad 34
975 hg bisect --bad 34
977 hg bisect --good 12
976 hg bisect --good 12
978 hg bisect --command "make && make tests"
977 hg bisect --command "make && make tests"
979
978
980 - see all changesets whose states are already known in the current
979 - see all changesets whose states are already known in the current
981 bisection::
980 bisection::
982
981
983 hg log -r "bisect(pruned)"
982 hg log -r "bisect(pruned)"
984
983
985 - see the changeset currently being bisected (especially useful
984 - see the changeset currently being bisected (especially useful
986 if running with -U/--noupdate)::
985 if running with -U/--noupdate)::
987
986
988 hg log -r "bisect(current)"
987 hg log -r "bisect(current)"
989
988
990 - see all changesets that took part in the current bisection::
989 - see all changesets that took part in the current bisection::
991
990
992 hg log -r "bisect(range)"
991 hg log -r "bisect(range)"
993
992
994 - you can even get a nice graph::
993 - you can even get a nice graph::
995
994
996 hg log --graph -r "bisect(range)"
995 hg log --graph -r "bisect(range)"
997
996
998 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
999
998
1000 Returns 0 on success.
999 Returns 0 on success.
1001 """
1000 """
1002 rev = []
1001 rev = []
1003 # backward compatibility
1002 # backward compatibility
1004 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1003 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1005 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1006 cmd = positional_1
1005 cmd = positional_1
1007 rev.append(positional_2)
1006 rev.append(positional_2)
1008 if cmd == b"good":
1007 if cmd == b"good":
1009 good = True
1008 good = True
1010 elif cmd == b"bad":
1009 elif cmd == b"bad":
1011 bad = True
1010 bad = True
1012 else:
1011 else:
1013 reset = True
1012 reset = True
1014 elif positional_2:
1013 elif positional_2:
1015 raise error.InputError(_(b'incompatible arguments'))
1014 raise error.InputError(_(b'incompatible arguments'))
1016 elif positional_1 is not None:
1015 elif positional_1 is not None:
1017 rev.append(positional_1)
1016 rev.append(positional_1)
1018
1017
1019 incompatibles = {
1018 incompatibles = {
1020 b'--bad': bad,
1019 b'--bad': bad,
1021 b'--command': bool(command),
1020 b'--command': bool(command),
1022 b'--extend': extend,
1021 b'--extend': extend,
1023 b'--good': good,
1022 b'--good': good,
1024 b'--reset': reset,
1023 b'--reset': reset,
1025 b'--skip': skip,
1024 b'--skip': skip,
1026 }
1025 }
1027
1026
1028 enabled = [x for x in incompatibles if incompatibles[x]]
1027 enabled = [x for x in incompatibles if incompatibles[x]]
1029
1028
1030 if len(enabled) > 1:
1029 if len(enabled) > 1:
1031 raise error.InputError(
1030 raise error.InputError(
1032 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1031 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1033 )
1032 )
1034
1033
1035 if reset:
1034 if reset:
1036 hbisect.resetstate(repo)
1035 hbisect.resetstate(repo)
1037 return
1036 return
1038
1037
1039 state = hbisect.load_state(repo)
1038 state = hbisect.load_state(repo)
1040
1039
1041 if rev:
1040 if rev:
1042 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1041 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1043 else:
1042 else:
1044 nodes = [repo.lookup(b'.')]
1043 nodes = [repo.lookup(b'.')]
1045
1044
1046 # update state
1045 # update state
1047 if good or bad or skip:
1046 if good or bad or skip:
1048 if good:
1047 if good:
1049 state[b'good'] += nodes
1048 state[b'good'] += nodes
1050 elif bad:
1049 elif bad:
1051 state[b'bad'] += nodes
1050 state[b'bad'] += nodes
1052 elif skip:
1051 elif skip:
1053 state[b'skip'] += nodes
1052 state[b'skip'] += nodes
1054 hbisect.save_state(repo, state)
1053 hbisect.save_state(repo, state)
1055 if not (state[b'good'] and state[b'bad']):
1054 if not (state[b'good'] and state[b'bad']):
1056 return
1055 return
1057
1056
1058 def mayupdate(repo, node, show_stats=True):
1057 def mayupdate(repo, node, show_stats=True):
1059 """common used update sequence"""
1058 """common used update sequence"""
1060 if noupdate:
1059 if noupdate:
1061 return
1060 return
1062 cmdutil.checkunfinished(repo)
1061 cmdutil.checkunfinished(repo)
1063 cmdutil.bailifchanged(repo)
1062 cmdutil.bailifchanged(repo)
1064 return hg.clean(repo, node, show_stats=show_stats)
1063 return hg.clean(repo, node, show_stats=show_stats)
1065
1064
1066 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1065 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1067
1066
1068 if command:
1067 if command:
1069 changesets = 1
1068 changesets = 1
1070 if noupdate:
1069 if noupdate:
1071 try:
1070 try:
1072 node = state[b'current'][0]
1071 node = state[b'current'][0]
1073 except LookupError:
1072 except LookupError:
1074 raise error.StateError(
1073 raise error.StateError(
1075 _(
1074 _(
1076 b'current bisect revision is unknown - '
1075 b'current bisect revision is unknown - '
1077 b'start a new bisect to fix'
1076 b'start a new bisect to fix'
1078 )
1077 )
1079 )
1078 )
1080 else:
1079 else:
1081 node, p2 = repo.dirstate.parents()
1080 node, p2 = repo.dirstate.parents()
1082 if p2 != nullid:
1081 if p2 != nullid:
1083 raise error.StateError(_(b'current bisect revision is a merge'))
1082 raise error.StateError(_(b'current bisect revision is a merge'))
1084 if rev:
1083 if rev:
1085 if not nodes:
1084 if not nodes:
1086 raise error.Abort(_(b'empty revision set'))
1085 raise error.Abort(_(b'empty revision set'))
1087 node = repo[nodes.last()].node()
1086 node = repo[nodes.last()].node()
1088 with hbisect.restore_state(repo, state, node):
1087 with hbisect.restore_state(repo, state, node):
1089 while changesets:
1088 while changesets:
1090 # update state
1089 # update state
1091 state[b'current'] = [node]
1090 state[b'current'] = [node]
1092 hbisect.save_state(repo, state)
1091 hbisect.save_state(repo, state)
1093 status = ui.system(
1092 status = ui.system(
1094 command,
1093 command,
1095 environ={b'HG_NODE': hex(node)},
1094 environ={b'HG_NODE': hex(node)},
1096 blockedtag=b'bisect_check',
1095 blockedtag=b'bisect_check',
1097 )
1096 )
1098 if status == 125:
1097 if status == 125:
1099 transition = b"skip"
1098 transition = b"skip"
1100 elif status == 0:
1099 elif status == 0:
1101 transition = b"good"
1100 transition = b"good"
1102 # status < 0 means process was killed
1101 # status < 0 means process was killed
1103 elif status == 127:
1102 elif status == 127:
1104 raise error.Abort(_(b"failed to execute %s") % command)
1103 raise error.Abort(_(b"failed to execute %s") % command)
1105 elif status < 0:
1104 elif status < 0:
1106 raise error.Abort(_(b"%s killed") % command)
1105 raise error.Abort(_(b"%s killed") % command)
1107 else:
1106 else:
1108 transition = b"bad"
1107 transition = b"bad"
1109 state[transition].append(node)
1108 state[transition].append(node)
1110 ctx = repo[node]
1109 ctx = repo[node]
1111 ui.status(
1110 ui.status(
1112 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1111 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1113 )
1112 )
1114 hbisect.checkstate(state)
1113 hbisect.checkstate(state)
1115 # bisect
1114 # bisect
1116 nodes, changesets, bgood = hbisect.bisect(repo, state)
1115 nodes, changesets, bgood = hbisect.bisect(repo, state)
1117 # update to next check
1116 # update to next check
1118 node = nodes[0]
1117 node = nodes[0]
1119 mayupdate(repo, node, show_stats=False)
1118 mayupdate(repo, node, show_stats=False)
1120 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1119 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1121 return
1120 return
1122
1121
1123 hbisect.checkstate(state)
1122 hbisect.checkstate(state)
1124
1123
1125 # actually bisect
1124 # actually bisect
1126 nodes, changesets, good = hbisect.bisect(repo, state)
1125 nodes, changesets, good = hbisect.bisect(repo, state)
1127 if extend:
1126 if extend:
1128 if not changesets:
1127 if not changesets:
1129 extendnode = hbisect.extendrange(repo, state, nodes, good)
1128 extendnode = hbisect.extendrange(repo, state, nodes, good)
1130 if extendnode is not None:
1129 if extendnode is not None:
1131 ui.write(
1130 ui.write(
1132 _(b"Extending search to changeset %d:%s\n")
1131 _(b"Extending search to changeset %d:%s\n")
1133 % (extendnode.rev(), extendnode)
1132 % (extendnode.rev(), extendnode)
1134 )
1133 )
1135 state[b'current'] = [extendnode.node()]
1134 state[b'current'] = [extendnode.node()]
1136 hbisect.save_state(repo, state)
1135 hbisect.save_state(repo, state)
1137 return mayupdate(repo, extendnode.node())
1136 return mayupdate(repo, extendnode.node())
1138 raise error.StateError(_(b"nothing to extend"))
1137 raise error.StateError(_(b"nothing to extend"))
1139
1138
1140 if changesets == 0:
1139 if changesets == 0:
1141 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1140 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1142 else:
1141 else:
1143 assert len(nodes) == 1 # only a single node can be tested next
1142 assert len(nodes) == 1 # only a single node can be tested next
1144 node = nodes[0]
1143 node = nodes[0]
1145 # compute the approximate number of remaining tests
1144 # compute the approximate number of remaining tests
1146 tests, size = 0, 2
1145 tests, size = 0, 2
1147 while size <= changesets:
1146 while size <= changesets:
1148 tests, size = tests + 1, size * 2
1147 tests, size = tests + 1, size * 2
1149 rev = repo.changelog.rev(node)
1148 rev = repo.changelog.rev(node)
1150 ui.write(
1149 ui.write(
1151 _(
1150 _(
1152 b"Testing changeset %d:%s "
1151 b"Testing changeset %d:%s "
1153 b"(%d changesets remaining, ~%d tests)\n"
1152 b"(%d changesets remaining, ~%d tests)\n"
1154 )
1153 )
1155 % (rev, short(node), changesets, tests)
1154 % (rev, short(node), changesets, tests)
1156 )
1155 )
1157 state[b'current'] = [node]
1156 state[b'current'] = [node]
1158 hbisect.save_state(repo, state)
1157 hbisect.save_state(repo, state)
1159 return mayupdate(repo, node)
1158 return mayupdate(repo, node)
1160
1159
1161
1160
1162 @command(
1161 @command(
1163 b'bookmarks|bookmark',
1162 b'bookmarks|bookmark',
1164 [
1163 [
1165 (b'f', b'force', False, _(b'force')),
1164 (b'f', b'force', False, _(b'force')),
1166 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1165 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1167 (b'd', b'delete', False, _(b'delete a given bookmark')),
1166 (b'd', b'delete', False, _(b'delete a given bookmark')),
1168 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1167 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1169 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1168 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1170 (b'l', b'list', False, _(b'list existing bookmarks')),
1169 (b'l', b'list', False, _(b'list existing bookmarks')),
1171 ]
1170 ]
1172 + formatteropts,
1171 + formatteropts,
1173 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1172 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1174 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1173 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1175 )
1174 )
1176 def bookmark(ui, repo, *names, **opts):
1175 def bookmark(ui, repo, *names, **opts):
1177 """create a new bookmark or list existing bookmarks
1176 """create a new bookmark or list existing bookmarks
1178
1177
1179 Bookmarks are labels on changesets to help track lines of development.
1178 Bookmarks are labels on changesets to help track lines of development.
1180 Bookmarks are unversioned and can be moved, renamed and deleted.
1179 Bookmarks are unversioned and can be moved, renamed and deleted.
1181 Deleting or moving a bookmark has no effect on the associated changesets.
1180 Deleting or moving a bookmark has no effect on the associated changesets.
1182
1181
1183 Creating or updating to a bookmark causes it to be marked as 'active'.
1182 Creating or updating to a bookmark causes it to be marked as 'active'.
1184 The active bookmark is indicated with a '*'.
1183 The active bookmark is indicated with a '*'.
1185 When a commit is made, the active bookmark will advance to the new commit.
1184 When a commit is made, the active bookmark will advance to the new commit.
1186 A plain :hg:`update` will also advance an active bookmark, if possible.
1185 A plain :hg:`update` will also advance an active bookmark, if possible.
1187 Updating away from a bookmark will cause it to be deactivated.
1186 Updating away from a bookmark will cause it to be deactivated.
1188
1187
1189 Bookmarks can be pushed and pulled between repositories (see
1188 Bookmarks can be pushed and pulled between repositories (see
1190 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1189 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1191 diverged, a new 'divergent bookmark' of the form 'name@path' will
1190 diverged, a new 'divergent bookmark' of the form 'name@path' will
1192 be created. Using :hg:`merge` will resolve the divergence.
1191 be created. Using :hg:`merge` will resolve the divergence.
1193
1192
1194 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1193 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1195 the active bookmark's name.
1194 the active bookmark's name.
1196
1195
1197 A bookmark named '@' has the special property that :hg:`clone` will
1196 A bookmark named '@' has the special property that :hg:`clone` will
1198 check it out by default if it exists.
1197 check it out by default if it exists.
1199
1198
1200 .. container:: verbose
1199 .. container:: verbose
1201
1200
1202 Template:
1201 Template:
1203
1202
1204 The following keywords are supported in addition to the common template
1203 The following keywords are supported in addition to the common template
1205 keywords and functions such as ``{bookmark}``. See also
1204 keywords and functions such as ``{bookmark}``. See also
1206 :hg:`help templates`.
1205 :hg:`help templates`.
1207
1206
1208 :active: Boolean. True if the bookmark is active.
1207 :active: Boolean. True if the bookmark is active.
1209
1208
1210 Examples:
1209 Examples:
1211
1210
1212 - create an active bookmark for a new line of development::
1211 - create an active bookmark for a new line of development::
1213
1212
1214 hg book new-feature
1213 hg book new-feature
1215
1214
1216 - create an inactive bookmark as a place marker::
1215 - create an inactive bookmark as a place marker::
1217
1216
1218 hg book -i reviewed
1217 hg book -i reviewed
1219
1218
1220 - create an inactive bookmark on another changeset::
1219 - create an inactive bookmark on another changeset::
1221
1220
1222 hg book -r .^ tested
1221 hg book -r .^ tested
1223
1222
1224 - rename bookmark turkey to dinner::
1223 - rename bookmark turkey to dinner::
1225
1224
1226 hg book -m turkey dinner
1225 hg book -m turkey dinner
1227
1226
1228 - move the '@' bookmark from another branch::
1227 - move the '@' bookmark from another branch::
1229
1228
1230 hg book -f @
1229 hg book -f @
1231
1230
1232 - print only the active bookmark name::
1231 - print only the active bookmark name::
1233
1232
1234 hg book -ql .
1233 hg book -ql .
1235 """
1234 """
1236 opts = pycompat.byteskwargs(opts)
1235 opts = pycompat.byteskwargs(opts)
1237 force = opts.get(b'force')
1236 force = opts.get(b'force')
1238 rev = opts.get(b'rev')
1237 rev = opts.get(b'rev')
1239 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1238 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1240
1239
1241 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1240 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1242 if action:
1241 if action:
1243 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1242 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1244 elif names or rev:
1243 elif names or rev:
1245 action = b'add'
1244 action = b'add'
1246 elif inactive:
1245 elif inactive:
1247 action = b'inactive' # meaning deactivate
1246 action = b'inactive' # meaning deactivate
1248 else:
1247 else:
1249 action = b'list'
1248 action = b'list'
1250
1249
1251 cmdutil.check_incompatible_arguments(
1250 cmdutil.check_incompatible_arguments(
1252 opts, b'inactive', [b'delete', b'list']
1251 opts, b'inactive', [b'delete', b'list']
1253 )
1252 )
1254 if not names and action in {b'add', b'delete'}:
1253 if not names and action in {b'add', b'delete'}:
1255 raise error.InputError(_(b"bookmark name required"))
1254 raise error.InputError(_(b"bookmark name required"))
1256
1255
1257 if action in {b'add', b'delete', b'rename', b'inactive'}:
1256 if action in {b'add', b'delete', b'rename', b'inactive'}:
1258 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1257 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1259 if action == b'delete':
1258 if action == b'delete':
1260 names = pycompat.maplist(repo._bookmarks.expandname, names)
1259 names = pycompat.maplist(repo._bookmarks.expandname, names)
1261 bookmarks.delete(repo, tr, names)
1260 bookmarks.delete(repo, tr, names)
1262 elif action == b'rename':
1261 elif action == b'rename':
1263 if not names:
1262 if not names:
1264 raise error.InputError(_(b"new bookmark name required"))
1263 raise error.InputError(_(b"new bookmark name required"))
1265 elif len(names) > 1:
1264 elif len(names) > 1:
1266 raise error.InputError(
1265 raise error.InputError(
1267 _(b"only one new bookmark name allowed")
1266 _(b"only one new bookmark name allowed")
1268 )
1267 )
1269 oldname = repo._bookmarks.expandname(opts[b'rename'])
1268 oldname = repo._bookmarks.expandname(opts[b'rename'])
1270 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1269 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1271 elif action == b'add':
1270 elif action == b'add':
1272 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1271 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1273 elif action == b'inactive':
1272 elif action == b'inactive':
1274 if len(repo._bookmarks) == 0:
1273 if len(repo._bookmarks) == 0:
1275 ui.status(_(b"no bookmarks set\n"))
1274 ui.status(_(b"no bookmarks set\n"))
1276 elif not repo._activebookmark:
1275 elif not repo._activebookmark:
1277 ui.status(_(b"no active bookmark\n"))
1276 ui.status(_(b"no active bookmark\n"))
1278 else:
1277 else:
1279 bookmarks.deactivate(repo)
1278 bookmarks.deactivate(repo)
1280 elif action == b'list':
1279 elif action == b'list':
1281 names = pycompat.maplist(repo._bookmarks.expandname, names)
1280 names = pycompat.maplist(repo._bookmarks.expandname, names)
1282 with ui.formatter(b'bookmarks', opts) as fm:
1281 with ui.formatter(b'bookmarks', opts) as fm:
1283 bookmarks.printbookmarks(ui, repo, fm, names)
1282 bookmarks.printbookmarks(ui, repo, fm, names)
1284 else:
1283 else:
1285 raise error.ProgrammingError(b'invalid action: %s' % action)
1284 raise error.ProgrammingError(b'invalid action: %s' % action)
1286
1285
1287
1286
1288 @command(
1287 @command(
1289 b'branch',
1288 b'branch',
1290 [
1289 [
1291 (
1290 (
1292 b'f',
1291 b'f',
1293 b'force',
1292 b'force',
1294 None,
1293 None,
1295 _(b'set branch name even if it shadows an existing branch'),
1294 _(b'set branch name even if it shadows an existing branch'),
1296 ),
1295 ),
1297 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1296 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1298 (
1297 (
1299 b'r',
1298 b'r',
1300 b'rev',
1299 b'rev',
1301 [],
1300 [],
1302 _(b'change branches of the given revs (EXPERIMENTAL)'),
1301 _(b'change branches of the given revs (EXPERIMENTAL)'),
1303 ),
1302 ),
1304 ],
1303 ],
1305 _(b'[-fC] [NAME]'),
1304 _(b'[-fC] [NAME]'),
1306 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1305 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1307 )
1306 )
1308 def branch(ui, repo, label=None, **opts):
1307 def branch(ui, repo, label=None, **opts):
1309 """set or show the current branch name
1308 """set or show the current branch name
1310
1309
1311 .. note::
1310 .. note::
1312
1311
1313 Branch names are permanent and global. Use :hg:`bookmark` to create a
1312 Branch names are permanent and global. Use :hg:`bookmark` to create a
1314 light-weight bookmark instead. See :hg:`help glossary` for more
1313 light-weight bookmark instead. See :hg:`help glossary` for more
1315 information about named branches and bookmarks.
1314 information about named branches and bookmarks.
1316
1315
1317 With no argument, show the current branch name. With one argument,
1316 With no argument, show the current branch name. With one argument,
1318 set the working directory branch name (the branch will not exist
1317 set the working directory branch name (the branch will not exist
1319 in the repository until the next commit). Standard practice
1318 in the repository until the next commit). Standard practice
1320 recommends that primary development take place on the 'default'
1319 recommends that primary development take place on the 'default'
1321 branch.
1320 branch.
1322
1321
1323 Unless -f/--force is specified, branch will not let you set a
1322 Unless -f/--force is specified, branch will not let you set a
1324 branch name that already exists.
1323 branch name that already exists.
1325
1324
1326 Use -C/--clean to reset the working directory branch to that of
1325 Use -C/--clean to reset the working directory branch to that of
1327 the parent of the working directory, negating a previous branch
1326 the parent of the working directory, negating a previous branch
1328 change.
1327 change.
1329
1328
1330 Use the command :hg:`update` to switch to an existing branch. Use
1329 Use the command :hg:`update` to switch to an existing branch. Use
1331 :hg:`commit --close-branch` to mark this branch head as closed.
1330 :hg:`commit --close-branch` to mark this branch head as closed.
1332 When all heads of a branch are closed, the branch will be
1331 When all heads of a branch are closed, the branch will be
1333 considered closed.
1332 considered closed.
1334
1333
1335 Returns 0 on success.
1334 Returns 0 on success.
1336 """
1335 """
1337 opts = pycompat.byteskwargs(opts)
1336 opts = pycompat.byteskwargs(opts)
1338 revs = opts.get(b'rev')
1337 revs = opts.get(b'rev')
1339 if label:
1338 if label:
1340 label = label.strip()
1339 label = label.strip()
1341
1340
1342 if not opts.get(b'clean') and not label:
1341 if not opts.get(b'clean') and not label:
1343 if revs:
1342 if revs:
1344 raise error.InputError(
1343 raise error.InputError(
1345 _(b"no branch name specified for the revisions")
1344 _(b"no branch name specified for the revisions")
1346 )
1345 )
1347 ui.write(b"%s\n" % repo.dirstate.branch())
1346 ui.write(b"%s\n" % repo.dirstate.branch())
1348 return
1347 return
1349
1348
1350 with repo.wlock():
1349 with repo.wlock():
1351 if opts.get(b'clean'):
1350 if opts.get(b'clean'):
1352 label = repo[b'.'].branch()
1351 label = repo[b'.'].branch()
1353 repo.dirstate.setbranch(label)
1352 repo.dirstate.setbranch(label)
1354 ui.status(_(b'reset working directory to branch %s\n') % label)
1353 ui.status(_(b'reset working directory to branch %s\n') % label)
1355 elif label:
1354 elif label:
1356
1355
1357 scmutil.checknewlabel(repo, label, b'branch')
1356 scmutil.checknewlabel(repo, label, b'branch')
1358 if revs:
1357 if revs:
1359 return cmdutil.changebranch(ui, repo, revs, label, opts)
1358 return cmdutil.changebranch(ui, repo, revs, label, opts)
1360
1359
1361 if not opts.get(b'force') and label in repo.branchmap():
1360 if not opts.get(b'force') and label in repo.branchmap():
1362 if label not in [p.branch() for p in repo[None].parents()]:
1361 if label not in [p.branch() for p in repo[None].parents()]:
1363 raise error.InputError(
1362 raise error.InputError(
1364 _(b'a branch of the same name already exists'),
1363 _(b'a branch of the same name already exists'),
1365 # i18n: "it" refers to an existing branch
1364 # i18n: "it" refers to an existing branch
1366 hint=_(b"use 'hg update' to switch to it"),
1365 hint=_(b"use 'hg update' to switch to it"),
1367 )
1366 )
1368
1367
1369 repo.dirstate.setbranch(label)
1368 repo.dirstate.setbranch(label)
1370 ui.status(_(b'marked working directory as branch %s\n') % label)
1369 ui.status(_(b'marked working directory as branch %s\n') % label)
1371
1370
1372 # find any open named branches aside from default
1371 # find any open named branches aside from default
1373 for n, h, t, c in repo.branchmap().iterbranches():
1372 for n, h, t, c in repo.branchmap().iterbranches():
1374 if n != b"default" and not c:
1373 if n != b"default" and not c:
1375 return 0
1374 return 0
1376 ui.status(
1375 ui.status(
1377 _(
1376 _(
1378 b'(branches are permanent and global, '
1377 b'(branches are permanent and global, '
1379 b'did you want a bookmark?)\n'
1378 b'did you want a bookmark?)\n'
1380 )
1379 )
1381 )
1380 )
1382
1381
1383
1382
1384 @command(
1383 @command(
1385 b'branches',
1384 b'branches',
1386 [
1385 [
1387 (
1386 (
1388 b'a',
1387 b'a',
1389 b'active',
1388 b'active',
1390 False,
1389 False,
1391 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1390 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1392 ),
1391 ),
1393 (b'c', b'closed', False, _(b'show normal and closed branches')),
1392 (b'c', b'closed', False, _(b'show normal and closed branches')),
1394 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1393 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1395 ]
1394 ]
1396 + formatteropts,
1395 + formatteropts,
1397 _(b'[-c]'),
1396 _(b'[-c]'),
1398 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1397 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1399 intents={INTENT_READONLY},
1398 intents={INTENT_READONLY},
1400 )
1399 )
1401 def branches(ui, repo, active=False, closed=False, **opts):
1400 def branches(ui, repo, active=False, closed=False, **opts):
1402 """list repository named branches
1401 """list repository named branches
1403
1402
1404 List the repository's named branches, indicating which ones are
1403 List the repository's named branches, indicating which ones are
1405 inactive. If -c/--closed is specified, also list branches which have
1404 inactive. If -c/--closed is specified, also list branches which have
1406 been marked closed (see :hg:`commit --close-branch`).
1405 been marked closed (see :hg:`commit --close-branch`).
1407
1406
1408 Use the command :hg:`update` to switch to an existing branch.
1407 Use the command :hg:`update` to switch to an existing branch.
1409
1408
1410 .. container:: verbose
1409 .. container:: verbose
1411
1410
1412 Template:
1411 Template:
1413
1412
1414 The following keywords are supported in addition to the common template
1413 The following keywords are supported in addition to the common template
1415 keywords and functions such as ``{branch}``. See also
1414 keywords and functions such as ``{branch}``. See also
1416 :hg:`help templates`.
1415 :hg:`help templates`.
1417
1416
1418 :active: Boolean. True if the branch is active.
1417 :active: Boolean. True if the branch is active.
1419 :closed: Boolean. True if the branch is closed.
1418 :closed: Boolean. True if the branch is closed.
1420 :current: Boolean. True if it is the current branch.
1419 :current: Boolean. True if it is the current branch.
1421
1420
1422 Returns 0.
1421 Returns 0.
1423 """
1422 """
1424
1423
1425 opts = pycompat.byteskwargs(opts)
1424 opts = pycompat.byteskwargs(opts)
1426 revs = opts.get(b'rev')
1425 revs = opts.get(b'rev')
1427 selectedbranches = None
1426 selectedbranches = None
1428 if revs:
1427 if revs:
1429 revs = scmutil.revrange(repo, revs)
1428 revs = scmutil.revrange(repo, revs)
1430 getbi = repo.revbranchcache().branchinfo
1429 getbi = repo.revbranchcache().branchinfo
1431 selectedbranches = {getbi(r)[0] for r in revs}
1430 selectedbranches = {getbi(r)[0] for r in revs}
1432
1431
1433 ui.pager(b'branches')
1432 ui.pager(b'branches')
1434 fm = ui.formatter(b'branches', opts)
1433 fm = ui.formatter(b'branches', opts)
1435 hexfunc = fm.hexfunc
1434 hexfunc = fm.hexfunc
1436
1435
1437 allheads = set(repo.heads())
1436 allheads = set(repo.heads())
1438 branches = []
1437 branches = []
1439 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1438 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1440 if selectedbranches is not None and tag not in selectedbranches:
1439 if selectedbranches is not None and tag not in selectedbranches:
1441 continue
1440 continue
1442 isactive = False
1441 isactive = False
1443 if not isclosed:
1442 if not isclosed:
1444 openheads = set(repo.branchmap().iteropen(heads))
1443 openheads = set(repo.branchmap().iteropen(heads))
1445 isactive = bool(openheads & allheads)
1444 isactive = bool(openheads & allheads)
1446 branches.append((tag, repo[tip], isactive, not isclosed))
1445 branches.append((tag, repo[tip], isactive, not isclosed))
1447 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1446 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1448
1447
1449 for tag, ctx, isactive, isopen in branches:
1448 for tag, ctx, isactive, isopen in branches:
1450 if active and not isactive:
1449 if active and not isactive:
1451 continue
1450 continue
1452 if isactive:
1451 if isactive:
1453 label = b'branches.active'
1452 label = b'branches.active'
1454 notice = b''
1453 notice = b''
1455 elif not isopen:
1454 elif not isopen:
1456 if not closed:
1455 if not closed:
1457 continue
1456 continue
1458 label = b'branches.closed'
1457 label = b'branches.closed'
1459 notice = _(b' (closed)')
1458 notice = _(b' (closed)')
1460 else:
1459 else:
1461 label = b'branches.inactive'
1460 label = b'branches.inactive'
1462 notice = _(b' (inactive)')
1461 notice = _(b' (inactive)')
1463 current = tag == repo.dirstate.branch()
1462 current = tag == repo.dirstate.branch()
1464 if current:
1463 if current:
1465 label = b'branches.current'
1464 label = b'branches.current'
1466
1465
1467 fm.startitem()
1466 fm.startitem()
1468 fm.write(b'branch', b'%s', tag, label=label)
1467 fm.write(b'branch', b'%s', tag, label=label)
1469 rev = ctx.rev()
1468 rev = ctx.rev()
1470 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1469 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1471 fmt = b' ' * padsize + b' %d:%s'
1470 fmt = b' ' * padsize + b' %d:%s'
1472 fm.condwrite(
1471 fm.condwrite(
1473 not ui.quiet,
1472 not ui.quiet,
1474 b'rev node',
1473 b'rev node',
1475 fmt,
1474 fmt,
1476 rev,
1475 rev,
1477 hexfunc(ctx.node()),
1476 hexfunc(ctx.node()),
1478 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1477 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1479 )
1478 )
1480 fm.context(ctx=ctx)
1479 fm.context(ctx=ctx)
1481 fm.data(active=isactive, closed=not isopen, current=current)
1480 fm.data(active=isactive, closed=not isopen, current=current)
1482 if not ui.quiet:
1481 if not ui.quiet:
1483 fm.plain(notice)
1482 fm.plain(notice)
1484 fm.plain(b'\n')
1483 fm.plain(b'\n')
1485 fm.end()
1484 fm.end()
1486
1485
1487
1486
1488 @command(
1487 @command(
1489 b'bundle',
1488 b'bundle',
1490 [
1489 [
1491 (
1490 (
1492 b'f',
1491 b'f',
1493 b'force',
1492 b'force',
1494 None,
1493 None,
1495 _(b'run even when the destination is unrelated'),
1494 _(b'run even when the destination is unrelated'),
1496 ),
1495 ),
1497 (
1496 (
1498 b'r',
1497 b'r',
1499 b'rev',
1498 b'rev',
1500 [],
1499 [],
1501 _(b'a changeset intended to be added to the destination'),
1500 _(b'a changeset intended to be added to the destination'),
1502 _(b'REV'),
1501 _(b'REV'),
1503 ),
1502 ),
1504 (
1503 (
1505 b'b',
1504 b'b',
1506 b'branch',
1505 b'branch',
1507 [],
1506 [],
1508 _(b'a specific branch you would like to bundle'),
1507 _(b'a specific branch you would like to bundle'),
1509 _(b'BRANCH'),
1508 _(b'BRANCH'),
1510 ),
1509 ),
1511 (
1510 (
1512 b'',
1511 b'',
1513 b'base',
1512 b'base',
1514 [],
1513 [],
1515 _(b'a base changeset assumed to be available at the destination'),
1514 _(b'a base changeset assumed to be available at the destination'),
1516 _(b'REV'),
1515 _(b'REV'),
1517 ),
1516 ),
1518 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1517 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1519 (
1518 (
1520 b't',
1519 b't',
1521 b'type',
1520 b'type',
1522 b'bzip2',
1521 b'bzip2',
1523 _(b'bundle compression type to use'),
1522 _(b'bundle compression type to use'),
1524 _(b'TYPE'),
1523 _(b'TYPE'),
1525 ),
1524 ),
1526 ]
1525 ]
1527 + remoteopts,
1526 + remoteopts,
1528 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1527 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1529 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1528 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1530 )
1529 )
1531 def bundle(ui, repo, fname, dest=None, **opts):
1530 def bundle(ui, repo, fname, dest=None, **opts):
1532 """create a bundle file
1531 """create a bundle file
1533
1532
1534 Generate a bundle file containing data to be transferred to another
1533 Generate a bundle file containing data to be transferred to another
1535 repository.
1534 repository.
1536
1535
1537 To create a bundle containing all changesets, use -a/--all
1536 To create a bundle containing all changesets, use -a/--all
1538 (or --base null). Otherwise, hg assumes the destination will have
1537 (or --base null). Otherwise, hg assumes the destination will have
1539 all the nodes you specify with --base parameters. Otherwise, hg
1538 all the nodes you specify with --base parameters. Otherwise, hg
1540 will assume the repository has all the nodes in destination, or
1539 will assume the repository has all the nodes in destination, or
1541 default-push/default if no destination is specified, where destination
1540 default-push/default if no destination is specified, where destination
1542 is the repository you provide through DEST option.
1541 is the repository you provide through DEST option.
1543
1542
1544 You can change bundle format with the -t/--type option. See
1543 You can change bundle format with the -t/--type option. See
1545 :hg:`help bundlespec` for documentation on this format. By default,
1544 :hg:`help bundlespec` for documentation on this format. By default,
1546 the most appropriate format is used and compression defaults to
1545 the most appropriate format is used and compression defaults to
1547 bzip2.
1546 bzip2.
1548
1547
1549 The bundle file can then be transferred using conventional means
1548 The bundle file can then be transferred using conventional means
1550 and applied to another repository with the unbundle or pull
1549 and applied to another repository with the unbundle or pull
1551 command. This is useful when direct push and pull are not
1550 command. This is useful when direct push and pull are not
1552 available or when exporting an entire repository is undesirable.
1551 available or when exporting an entire repository is undesirable.
1553
1552
1554 Applying bundles preserves all changeset contents including
1553 Applying bundles preserves all changeset contents including
1555 permissions, copy/rename information, and revision history.
1554 permissions, copy/rename information, and revision history.
1556
1555
1557 Returns 0 on success, 1 if no changes found.
1556 Returns 0 on success, 1 if no changes found.
1558 """
1557 """
1559 opts = pycompat.byteskwargs(opts)
1558 opts = pycompat.byteskwargs(opts)
1560 revs = None
1559 revs = None
1561 if b'rev' in opts:
1560 if b'rev' in opts:
1562 revstrings = opts[b'rev']
1561 revstrings = opts[b'rev']
1563 revs = scmutil.revrange(repo, revstrings)
1562 revs = scmutil.revrange(repo, revstrings)
1564 if revstrings and not revs:
1563 if revstrings and not revs:
1565 raise error.InputError(_(b'no commits to bundle'))
1564 raise error.InputError(_(b'no commits to bundle'))
1566
1565
1567 bundletype = opts.get(b'type', b'bzip2').lower()
1566 bundletype = opts.get(b'type', b'bzip2').lower()
1568 try:
1567 try:
1569 bundlespec = bundlecaches.parsebundlespec(
1568 bundlespec = bundlecaches.parsebundlespec(
1570 repo, bundletype, strict=False
1569 repo, bundletype, strict=False
1571 )
1570 )
1572 except error.UnsupportedBundleSpecification as e:
1571 except error.UnsupportedBundleSpecification as e:
1573 raise error.InputError(
1572 raise error.InputError(
1574 pycompat.bytestr(e),
1573 pycompat.bytestr(e),
1575 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1574 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1576 )
1575 )
1577 cgversion = bundlespec.contentopts[b"cg.version"]
1576 cgversion = bundlespec.contentopts[b"cg.version"]
1578
1577
1579 # Packed bundles are a pseudo bundle format for now.
1578 # Packed bundles are a pseudo bundle format for now.
1580 if cgversion == b's1':
1579 if cgversion == b's1':
1581 raise error.InputError(
1580 raise error.InputError(
1582 _(b'packed bundles cannot be produced by "hg bundle"'),
1581 _(b'packed bundles cannot be produced by "hg bundle"'),
1583 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1582 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1584 )
1583 )
1585
1584
1586 if opts.get(b'all'):
1585 if opts.get(b'all'):
1587 if dest:
1586 if dest:
1588 raise error.InputError(
1587 raise error.InputError(
1589 _(b"--all is incompatible with specifying a destination")
1588 _(b"--all is incompatible with specifying a destination")
1590 )
1589 )
1591 if opts.get(b'base'):
1590 if opts.get(b'base'):
1592 ui.warn(_(b"ignoring --base because --all was specified\n"))
1591 ui.warn(_(b"ignoring --base because --all was specified\n"))
1593 base = [nullrev]
1592 base = [nullrev]
1594 else:
1593 else:
1595 base = scmutil.revrange(repo, opts.get(b'base'))
1594 base = scmutil.revrange(repo, opts.get(b'base'))
1596 if cgversion not in changegroup.supportedoutgoingversions(repo):
1595 if cgversion not in changegroup.supportedoutgoingversions(repo):
1597 raise error.Abort(
1596 raise error.Abort(
1598 _(b"repository does not support bundle version %s") % cgversion
1597 _(b"repository does not support bundle version %s") % cgversion
1599 )
1598 )
1600
1599
1601 if base:
1600 if base:
1602 if dest:
1601 if dest:
1603 raise error.InputError(
1602 raise error.InputError(
1604 _(b"--base is incompatible with specifying a destination")
1603 _(b"--base is incompatible with specifying a destination")
1605 )
1604 )
1606 common = [repo[rev].node() for rev in base]
1605 common = [repo[rev].node() for rev in base]
1607 heads = [repo[r].node() for r in revs] if revs else None
1606 heads = [repo[r].node() for r in revs] if revs else None
1608 outgoing = discovery.outgoing(repo, common, heads)
1607 outgoing = discovery.outgoing(repo, common, heads)
1609 else:
1608 else:
1610 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1609 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1611 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1610 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1612 other = hg.peer(repo, opts, dest)
1611 other = hg.peer(repo, opts, dest)
1613 revs = [repo[r].hex() for r in revs]
1612 revs = [repo[r].hex() for r in revs]
1614 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1613 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1615 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1614 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1616 outgoing = discovery.findcommonoutgoing(
1615 outgoing = discovery.findcommonoutgoing(
1617 repo,
1616 repo,
1618 other,
1617 other,
1619 onlyheads=heads,
1618 onlyheads=heads,
1620 force=opts.get(b'force'),
1619 force=opts.get(b'force'),
1621 portable=True,
1620 portable=True,
1622 )
1621 )
1623
1622
1624 if not outgoing.missing:
1623 if not outgoing.missing:
1625 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1624 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1626 return 1
1625 return 1
1627
1626
1628 if cgversion == b'01': # bundle1
1627 if cgversion == b'01': # bundle1
1629 bversion = b'HG10' + bundlespec.wirecompression
1628 bversion = b'HG10' + bundlespec.wirecompression
1630 bcompression = None
1629 bcompression = None
1631 elif cgversion in (b'02', b'03'):
1630 elif cgversion in (b'02', b'03'):
1632 bversion = b'HG20'
1631 bversion = b'HG20'
1633 bcompression = bundlespec.wirecompression
1632 bcompression = bundlespec.wirecompression
1634 else:
1633 else:
1635 raise error.ProgrammingError(
1634 raise error.ProgrammingError(
1636 b'bundle: unexpected changegroup version %s' % cgversion
1635 b'bundle: unexpected changegroup version %s' % cgversion
1637 )
1636 )
1638
1637
1639 # TODO compression options should be derived from bundlespec parsing.
1638 # TODO compression options should be derived from bundlespec parsing.
1640 # This is a temporary hack to allow adjusting bundle compression
1639 # This is a temporary hack to allow adjusting bundle compression
1641 # level without a) formalizing the bundlespec changes to declare it
1640 # level without a) formalizing the bundlespec changes to declare it
1642 # b) introducing a command flag.
1641 # b) introducing a command flag.
1643 compopts = {}
1642 compopts = {}
1644 complevel = ui.configint(
1643 complevel = ui.configint(
1645 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1644 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1646 )
1645 )
1647 if complevel is None:
1646 if complevel is None:
1648 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1647 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1649 if complevel is not None:
1648 if complevel is not None:
1650 compopts[b'level'] = complevel
1649 compopts[b'level'] = complevel
1651
1650
1652 # Bundling of obsmarker and phases is optional as not all clients
1651 # Bundling of obsmarker and phases is optional as not all clients
1653 # support the necessary features.
1652 # support the necessary features.
1654 cfg = ui.configbool
1653 cfg = ui.configbool
1655 contentopts = {
1654 contentopts = {
1656 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1655 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1657 b'obsolescence-mandatory': cfg(
1656 b'obsolescence-mandatory': cfg(
1658 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1657 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1659 ),
1658 ),
1660 b'phases': cfg(b'experimental', b'bundle-phases'),
1659 b'phases': cfg(b'experimental', b'bundle-phases'),
1661 }
1660 }
1662 bundlespec.contentopts.update(contentopts)
1661 bundlespec.contentopts.update(contentopts)
1663
1662
1664 bundle2.writenewbundle(
1663 bundle2.writenewbundle(
1665 ui,
1664 ui,
1666 repo,
1665 repo,
1667 b'bundle',
1666 b'bundle',
1668 fname,
1667 fname,
1669 bversion,
1668 bversion,
1670 outgoing,
1669 outgoing,
1671 bundlespec.contentopts,
1670 bundlespec.contentopts,
1672 compression=bcompression,
1671 compression=bcompression,
1673 compopts=compopts,
1672 compopts=compopts,
1674 )
1673 )
1675
1674
1676
1675
1677 @command(
1676 @command(
1678 b'cat',
1677 b'cat',
1679 [
1678 [
1680 (
1679 (
1681 b'o',
1680 b'o',
1682 b'output',
1681 b'output',
1683 b'',
1682 b'',
1684 _(b'print output to file with formatted name'),
1683 _(b'print output to file with formatted name'),
1685 _(b'FORMAT'),
1684 _(b'FORMAT'),
1686 ),
1685 ),
1687 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1686 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1688 (b'', b'decode', None, _(b'apply any matching decode filter')),
1687 (b'', b'decode', None, _(b'apply any matching decode filter')),
1689 ]
1688 ]
1690 + walkopts
1689 + walkopts
1691 + formatteropts,
1690 + formatteropts,
1692 _(b'[OPTION]... FILE...'),
1691 _(b'[OPTION]... FILE...'),
1693 helpcategory=command.CATEGORY_FILE_CONTENTS,
1692 helpcategory=command.CATEGORY_FILE_CONTENTS,
1694 inferrepo=True,
1693 inferrepo=True,
1695 intents={INTENT_READONLY},
1694 intents={INTENT_READONLY},
1696 )
1695 )
1697 def cat(ui, repo, file1, *pats, **opts):
1696 def cat(ui, repo, file1, *pats, **opts):
1698 """output the current or given revision of files
1697 """output the current or given revision of files
1699
1698
1700 Print the specified files as they were at the given revision. If
1699 Print the specified files as they were at the given revision. If
1701 no revision is given, the parent of the working directory is used.
1700 no revision is given, the parent of the working directory is used.
1702
1701
1703 Output may be to a file, in which case the name of the file is
1702 Output may be to a file, in which case the name of the file is
1704 given using a template string. See :hg:`help templates`. In addition
1703 given using a template string. See :hg:`help templates`. In addition
1705 to the common template keywords, the following formatting rules are
1704 to the common template keywords, the following formatting rules are
1706 supported:
1705 supported:
1707
1706
1708 :``%%``: literal "%" character
1707 :``%%``: literal "%" character
1709 :``%s``: basename of file being printed
1708 :``%s``: basename of file being printed
1710 :``%d``: dirname of file being printed, or '.' if in repository root
1709 :``%d``: dirname of file being printed, or '.' if in repository root
1711 :``%p``: root-relative path name of file being printed
1710 :``%p``: root-relative path name of file being printed
1712 :``%H``: changeset hash (40 hexadecimal digits)
1711 :``%H``: changeset hash (40 hexadecimal digits)
1713 :``%R``: changeset revision number
1712 :``%R``: changeset revision number
1714 :``%h``: short-form changeset hash (12 hexadecimal digits)
1713 :``%h``: short-form changeset hash (12 hexadecimal digits)
1715 :``%r``: zero-padded changeset revision number
1714 :``%r``: zero-padded changeset revision number
1716 :``%b``: basename of the exporting repository
1715 :``%b``: basename of the exporting repository
1717 :``\\``: literal "\\" character
1716 :``\\``: literal "\\" character
1718
1717
1719 .. container:: verbose
1718 .. container:: verbose
1720
1719
1721 Template:
1720 Template:
1722
1721
1723 The following keywords are supported in addition to the common template
1722 The following keywords are supported in addition to the common template
1724 keywords and functions. See also :hg:`help templates`.
1723 keywords and functions. See also :hg:`help templates`.
1725
1724
1726 :data: String. File content.
1725 :data: String. File content.
1727 :path: String. Repository-absolute path of the file.
1726 :path: String. Repository-absolute path of the file.
1728
1727
1729 Returns 0 on success.
1728 Returns 0 on success.
1730 """
1729 """
1731 opts = pycompat.byteskwargs(opts)
1730 opts = pycompat.byteskwargs(opts)
1732 rev = opts.get(b'rev')
1731 rev = opts.get(b'rev')
1733 if rev:
1732 if rev:
1734 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1733 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1735 ctx = scmutil.revsingle(repo, rev)
1734 ctx = scmutil.revsingle(repo, rev)
1736 m = scmutil.match(ctx, (file1,) + pats, opts)
1735 m = scmutil.match(ctx, (file1,) + pats, opts)
1737 fntemplate = opts.pop(b'output', b'')
1736 fntemplate = opts.pop(b'output', b'')
1738 if cmdutil.isstdiofilename(fntemplate):
1737 if cmdutil.isstdiofilename(fntemplate):
1739 fntemplate = b''
1738 fntemplate = b''
1740
1739
1741 if fntemplate:
1740 if fntemplate:
1742 fm = formatter.nullformatter(ui, b'cat', opts)
1741 fm = formatter.nullformatter(ui, b'cat', opts)
1743 else:
1742 else:
1744 ui.pager(b'cat')
1743 ui.pager(b'cat')
1745 fm = ui.formatter(b'cat', opts)
1744 fm = ui.formatter(b'cat', opts)
1746 with fm:
1745 with fm:
1747 return cmdutil.cat(
1746 return cmdutil.cat(
1748 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1747 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1749 )
1748 )
1750
1749
1751
1750
1752 @command(
1751 @command(
1753 b'clone',
1752 b'clone',
1754 [
1753 [
1755 (
1754 (
1756 b'U',
1755 b'U',
1757 b'noupdate',
1756 b'noupdate',
1758 None,
1757 None,
1759 _(
1758 _(
1760 b'the clone will include an empty working '
1759 b'the clone will include an empty working '
1761 b'directory (only a repository)'
1760 b'directory (only a repository)'
1762 ),
1761 ),
1763 ),
1762 ),
1764 (
1763 (
1765 b'u',
1764 b'u',
1766 b'updaterev',
1765 b'updaterev',
1767 b'',
1766 b'',
1768 _(b'revision, tag, or branch to check out'),
1767 _(b'revision, tag, or branch to check out'),
1769 _(b'REV'),
1768 _(b'REV'),
1770 ),
1769 ),
1771 (
1770 (
1772 b'r',
1771 b'r',
1773 b'rev',
1772 b'rev',
1774 [],
1773 [],
1775 _(
1774 _(
1776 b'do not clone everything, but include this changeset'
1775 b'do not clone everything, but include this changeset'
1777 b' and its ancestors'
1776 b' and its ancestors'
1778 ),
1777 ),
1779 _(b'REV'),
1778 _(b'REV'),
1780 ),
1779 ),
1781 (
1780 (
1782 b'b',
1781 b'b',
1783 b'branch',
1782 b'branch',
1784 [],
1783 [],
1785 _(
1784 _(
1786 b'do not clone everything, but include this branch\'s'
1785 b'do not clone everything, but include this branch\'s'
1787 b' changesets and their ancestors'
1786 b' changesets and their ancestors'
1788 ),
1787 ),
1789 _(b'BRANCH'),
1788 _(b'BRANCH'),
1790 ),
1789 ),
1791 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1790 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1792 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1791 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1793 (b'', b'stream', None, _(b'clone with minimal data processing')),
1792 (b'', b'stream', None, _(b'clone with minimal data processing')),
1794 ]
1793 ]
1795 + remoteopts,
1794 + remoteopts,
1796 _(b'[OPTION]... SOURCE [DEST]'),
1795 _(b'[OPTION]... SOURCE [DEST]'),
1797 helpcategory=command.CATEGORY_REPO_CREATION,
1796 helpcategory=command.CATEGORY_REPO_CREATION,
1798 helpbasic=True,
1797 helpbasic=True,
1799 norepo=True,
1798 norepo=True,
1800 )
1799 )
1801 def clone(ui, source, dest=None, **opts):
1800 def clone(ui, source, dest=None, **opts):
1802 """make a copy of an existing repository
1801 """make a copy of an existing repository
1803
1802
1804 Create a copy of an existing repository in a new directory.
1803 Create a copy of an existing repository in a new directory.
1805
1804
1806 If no destination directory name is specified, it defaults to the
1805 If no destination directory name is specified, it defaults to the
1807 basename of the source.
1806 basename of the source.
1808
1807
1809 The location of the source is added to the new repository's
1808 The location of the source is added to the new repository's
1810 ``.hg/hgrc`` file, as the default to be used for future pulls.
1809 ``.hg/hgrc`` file, as the default to be used for future pulls.
1811
1810
1812 Only local paths and ``ssh://`` URLs are supported as
1811 Only local paths and ``ssh://`` URLs are supported as
1813 destinations. For ``ssh://`` destinations, no working directory or
1812 destinations. For ``ssh://`` destinations, no working directory or
1814 ``.hg/hgrc`` will be created on the remote side.
1813 ``.hg/hgrc`` will be created on the remote side.
1815
1814
1816 If the source repository has a bookmark called '@' set, that
1815 If the source repository has a bookmark called '@' set, that
1817 revision will be checked out in the new repository by default.
1816 revision will be checked out in the new repository by default.
1818
1817
1819 To check out a particular version, use -u/--update, or
1818 To check out a particular version, use -u/--update, or
1820 -U/--noupdate to create a clone with no working directory.
1819 -U/--noupdate to create a clone with no working directory.
1821
1820
1822 To pull only a subset of changesets, specify one or more revisions
1821 To pull only a subset of changesets, specify one or more revisions
1823 identifiers with -r/--rev or branches with -b/--branch. The
1822 identifiers with -r/--rev or branches with -b/--branch. The
1824 resulting clone will contain only the specified changesets and
1823 resulting clone will contain only the specified changesets and
1825 their ancestors. These options (or 'clone src#rev dest') imply
1824 their ancestors. These options (or 'clone src#rev dest') imply
1826 --pull, even for local source repositories.
1825 --pull, even for local source repositories.
1827
1826
1828 In normal clone mode, the remote normalizes repository data into a common
1827 In normal clone mode, the remote normalizes repository data into a common
1829 exchange format and the receiving end translates this data into its local
1828 exchange format and the receiving end translates this data into its local
1830 storage format. --stream activates a different clone mode that essentially
1829 storage format. --stream activates a different clone mode that essentially
1831 copies repository files from the remote with minimal data processing. This
1830 copies repository files from the remote with minimal data processing. This
1832 significantly reduces the CPU cost of a clone both remotely and locally.
1831 significantly reduces the CPU cost of a clone both remotely and locally.
1833 However, it often increases the transferred data size by 30-40%. This can
1832 However, it often increases the transferred data size by 30-40%. This can
1834 result in substantially faster clones where I/O throughput is plentiful,
1833 result in substantially faster clones where I/O throughput is plentiful,
1835 especially for larger repositories. A side-effect of --stream clones is
1834 especially for larger repositories. A side-effect of --stream clones is
1836 that storage settings and requirements on the remote are applied locally:
1835 that storage settings and requirements on the remote are applied locally:
1837 a modern client may inherit legacy or inefficient storage used by the
1836 a modern client may inherit legacy or inefficient storage used by the
1838 remote or a legacy Mercurial client may not be able to clone from a
1837 remote or a legacy Mercurial client may not be able to clone from a
1839 modern Mercurial remote.
1838 modern Mercurial remote.
1840
1839
1841 .. note::
1840 .. note::
1842
1841
1843 Specifying a tag will include the tagged changeset but not the
1842 Specifying a tag will include the tagged changeset but not the
1844 changeset containing the tag.
1843 changeset containing the tag.
1845
1844
1846 .. container:: verbose
1845 .. container:: verbose
1847
1846
1848 For efficiency, hardlinks are used for cloning whenever the
1847 For efficiency, hardlinks are used for cloning whenever the
1849 source and destination are on the same filesystem (note this
1848 source and destination are on the same filesystem (note this
1850 applies only to the repository data, not to the working
1849 applies only to the repository data, not to the working
1851 directory). Some filesystems, such as AFS, implement hardlinking
1850 directory). Some filesystems, such as AFS, implement hardlinking
1852 incorrectly, but do not report errors. In these cases, use the
1851 incorrectly, but do not report errors. In these cases, use the
1853 --pull option to avoid hardlinking.
1852 --pull option to avoid hardlinking.
1854
1853
1855 Mercurial will update the working directory to the first applicable
1854 Mercurial will update the working directory to the first applicable
1856 revision from this list:
1855 revision from this list:
1857
1856
1858 a) null if -U or the source repository has no changesets
1857 a) null if -U or the source repository has no changesets
1859 b) if -u . and the source repository is local, the first parent of
1858 b) if -u . and the source repository is local, the first parent of
1860 the source repository's working directory
1859 the source repository's working directory
1861 c) the changeset specified with -u (if a branch name, this means the
1860 c) the changeset specified with -u (if a branch name, this means the
1862 latest head of that branch)
1861 latest head of that branch)
1863 d) the changeset specified with -r
1862 d) the changeset specified with -r
1864 e) the tipmost head specified with -b
1863 e) the tipmost head specified with -b
1865 f) the tipmost head specified with the url#branch source syntax
1864 f) the tipmost head specified with the url#branch source syntax
1866 g) the revision marked with the '@' bookmark, if present
1865 g) the revision marked with the '@' bookmark, if present
1867 h) the tipmost head of the default branch
1866 h) the tipmost head of the default branch
1868 i) tip
1867 i) tip
1869
1868
1870 When cloning from servers that support it, Mercurial may fetch
1869 When cloning from servers that support it, Mercurial may fetch
1871 pre-generated data from a server-advertised URL or inline from the
1870 pre-generated data from a server-advertised URL or inline from the
1872 same stream. When this is done, hooks operating on incoming changesets
1871 same stream. When this is done, hooks operating on incoming changesets
1873 and changegroups may fire more than once, once for each pre-generated
1872 and changegroups may fire more than once, once for each pre-generated
1874 bundle and as well as for any additional remaining data. In addition,
1873 bundle and as well as for any additional remaining data. In addition,
1875 if an error occurs, the repository may be rolled back to a partial
1874 if an error occurs, the repository may be rolled back to a partial
1876 clone. This behavior may change in future releases.
1875 clone. This behavior may change in future releases.
1877 See :hg:`help -e clonebundles` for more.
1876 See :hg:`help -e clonebundles` for more.
1878
1877
1879 Examples:
1878 Examples:
1880
1879
1881 - clone a remote repository to a new directory named hg/::
1880 - clone a remote repository to a new directory named hg/::
1882
1881
1883 hg clone https://www.mercurial-scm.org/repo/hg/
1882 hg clone https://www.mercurial-scm.org/repo/hg/
1884
1883
1885 - create a lightweight local clone::
1884 - create a lightweight local clone::
1886
1885
1887 hg clone project/ project-feature/
1886 hg clone project/ project-feature/
1888
1887
1889 - clone from an absolute path on an ssh server (note double-slash)::
1888 - clone from an absolute path on an ssh server (note double-slash)::
1890
1889
1891 hg clone ssh://user@server//home/projects/alpha/
1890 hg clone ssh://user@server//home/projects/alpha/
1892
1891
1893 - do a streaming clone while checking out a specified version::
1892 - do a streaming clone while checking out a specified version::
1894
1893
1895 hg clone --stream http://server/repo -u 1.5
1894 hg clone --stream http://server/repo -u 1.5
1896
1895
1897 - create a repository without changesets after a particular revision::
1896 - create a repository without changesets after a particular revision::
1898
1897
1899 hg clone -r 04e544 experimental/ good/
1898 hg clone -r 04e544 experimental/ good/
1900
1899
1901 - clone (and track) a particular named branch::
1900 - clone (and track) a particular named branch::
1902
1901
1903 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1902 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1904
1903
1905 See :hg:`help urls` for details on specifying URLs.
1904 See :hg:`help urls` for details on specifying URLs.
1906
1905
1907 Returns 0 on success.
1906 Returns 0 on success.
1908 """
1907 """
1909 opts = pycompat.byteskwargs(opts)
1908 opts = pycompat.byteskwargs(opts)
1910 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1909 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1911
1910
1912 # --include/--exclude can come from narrow or sparse.
1911 # --include/--exclude can come from narrow or sparse.
1913 includepats, excludepats = None, None
1912 includepats, excludepats = None, None
1914
1913
1915 # hg.clone() differentiates between None and an empty set. So make sure
1914 # hg.clone() differentiates between None and an empty set. So make sure
1916 # patterns are sets if narrow is requested without patterns.
1915 # patterns are sets if narrow is requested without patterns.
1917 if opts.get(b'narrow'):
1916 if opts.get(b'narrow'):
1918 includepats = set()
1917 includepats = set()
1919 excludepats = set()
1918 excludepats = set()
1920
1919
1921 if opts.get(b'include'):
1920 if opts.get(b'include'):
1922 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1921 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1923 if opts.get(b'exclude'):
1922 if opts.get(b'exclude'):
1924 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1923 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1925
1924
1926 r = hg.clone(
1925 r = hg.clone(
1927 ui,
1926 ui,
1928 opts,
1927 opts,
1929 source,
1928 source,
1930 dest,
1929 dest,
1931 pull=opts.get(b'pull'),
1930 pull=opts.get(b'pull'),
1932 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1931 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1933 revs=opts.get(b'rev'),
1932 revs=opts.get(b'rev'),
1934 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1933 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1935 branch=opts.get(b'branch'),
1934 branch=opts.get(b'branch'),
1936 shareopts=opts.get(b'shareopts'),
1935 shareopts=opts.get(b'shareopts'),
1937 storeincludepats=includepats,
1936 storeincludepats=includepats,
1938 storeexcludepats=excludepats,
1937 storeexcludepats=excludepats,
1939 depth=opts.get(b'depth') or None,
1938 depth=opts.get(b'depth') or None,
1940 )
1939 )
1941
1940
1942 return r is None
1941 return r is None
1943
1942
1944
1943
1945 @command(
1944 @command(
1946 b'commit|ci',
1945 b'commit|ci',
1947 [
1946 [
1948 (
1947 (
1949 b'A',
1948 b'A',
1950 b'addremove',
1949 b'addremove',
1951 None,
1950 None,
1952 _(b'mark new/missing files as added/removed before committing'),
1951 _(b'mark new/missing files as added/removed before committing'),
1953 ),
1952 ),
1954 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1953 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1955 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1954 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1956 (b's', b'secret', None, _(b'use the secret phase for committing')),
1955 (b's', b'secret', None, _(b'use the secret phase for committing')),
1957 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1956 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1958 (
1957 (
1959 b'',
1958 b'',
1960 b'force-close-branch',
1959 b'force-close-branch',
1961 None,
1960 None,
1962 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1961 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1963 ),
1962 ),
1964 (b'i', b'interactive', None, _(b'use interactive mode')),
1963 (b'i', b'interactive', None, _(b'use interactive mode')),
1965 ]
1964 ]
1966 + walkopts
1965 + walkopts
1967 + commitopts
1966 + commitopts
1968 + commitopts2
1967 + commitopts2
1969 + subrepoopts,
1968 + subrepoopts,
1970 _(b'[OPTION]... [FILE]...'),
1969 _(b'[OPTION]... [FILE]...'),
1971 helpcategory=command.CATEGORY_COMMITTING,
1970 helpcategory=command.CATEGORY_COMMITTING,
1972 helpbasic=True,
1971 helpbasic=True,
1973 inferrepo=True,
1972 inferrepo=True,
1974 )
1973 )
1975 def commit(ui, repo, *pats, **opts):
1974 def commit(ui, repo, *pats, **opts):
1976 """commit the specified files or all outstanding changes
1975 """commit the specified files or all outstanding changes
1977
1976
1978 Commit changes to the given files into the repository. Unlike a
1977 Commit changes to the given files into the repository. Unlike a
1979 centralized SCM, this operation is a local operation. See
1978 centralized SCM, this operation is a local operation. See
1980 :hg:`push` for a way to actively distribute your changes.
1979 :hg:`push` for a way to actively distribute your changes.
1981
1980
1982 If a list of files is omitted, all changes reported by :hg:`status`
1981 If a list of files is omitted, all changes reported by :hg:`status`
1983 will be committed.
1982 will be committed.
1984
1983
1985 If you are committing the result of a merge, do not provide any
1984 If you are committing the result of a merge, do not provide any
1986 filenames or -I/-X filters.
1985 filenames or -I/-X filters.
1987
1986
1988 If no commit message is specified, Mercurial starts your
1987 If no commit message is specified, Mercurial starts your
1989 configured editor where you can enter a message. In case your
1988 configured editor where you can enter a message. In case your
1990 commit fails, you will find a backup of your message in
1989 commit fails, you will find a backup of your message in
1991 ``.hg/last-message.txt``.
1990 ``.hg/last-message.txt``.
1992
1991
1993 The --close-branch flag can be used to mark the current branch
1992 The --close-branch flag can be used to mark the current branch
1994 head closed. When all heads of a branch are closed, the branch
1993 head closed. When all heads of a branch are closed, the branch
1995 will be considered closed and no longer listed.
1994 will be considered closed and no longer listed.
1996
1995
1997 The --amend flag can be used to amend the parent of the
1996 The --amend flag can be used to amend the parent of the
1998 working directory with a new commit that contains the changes
1997 working directory with a new commit that contains the changes
1999 in the parent in addition to those currently reported by :hg:`status`,
1998 in the parent in addition to those currently reported by :hg:`status`,
2000 if there are any. The old commit is stored in a backup bundle in
1999 if there are any. The old commit is stored in a backup bundle in
2001 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2000 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2002 on how to restore it).
2001 on how to restore it).
2003
2002
2004 Message, user and date are taken from the amended commit unless
2003 Message, user and date are taken from the amended commit unless
2005 specified. When a message isn't specified on the command line,
2004 specified. When a message isn't specified on the command line,
2006 the editor will open with the message of the amended commit.
2005 the editor will open with the message of the amended commit.
2007
2006
2008 It is not possible to amend public changesets (see :hg:`help phases`)
2007 It is not possible to amend public changesets (see :hg:`help phases`)
2009 or changesets that have children.
2008 or changesets that have children.
2010
2009
2011 See :hg:`help dates` for a list of formats valid for -d/--date.
2010 See :hg:`help dates` for a list of formats valid for -d/--date.
2012
2011
2013 Returns 0 on success, 1 if nothing changed.
2012 Returns 0 on success, 1 if nothing changed.
2014
2013
2015 .. container:: verbose
2014 .. container:: verbose
2016
2015
2017 Examples:
2016 Examples:
2018
2017
2019 - commit all files ending in .py::
2018 - commit all files ending in .py::
2020
2019
2021 hg commit --include "set:**.py"
2020 hg commit --include "set:**.py"
2022
2021
2023 - commit all non-binary files::
2022 - commit all non-binary files::
2024
2023
2025 hg commit --exclude "set:binary()"
2024 hg commit --exclude "set:binary()"
2026
2025
2027 - amend the current commit and set the date to now::
2026 - amend the current commit and set the date to now::
2028
2027
2029 hg commit --amend --date now
2028 hg commit --amend --date now
2030 """
2029 """
2031 with repo.wlock(), repo.lock():
2030 with repo.wlock(), repo.lock():
2032 return _docommit(ui, repo, *pats, **opts)
2031 return _docommit(ui, repo, *pats, **opts)
2033
2032
2034
2033
2035 def _docommit(ui, repo, *pats, **opts):
2034 def _docommit(ui, repo, *pats, **opts):
2036 if opts.get('interactive'):
2035 if opts.get('interactive'):
2037 opts.pop('interactive')
2036 opts.pop('interactive')
2038 ret = cmdutil.dorecord(
2037 ret = cmdutil.dorecord(
2039 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2038 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2040 )
2039 )
2041 # ret can be 0 (no changes to record) or the value returned by
2040 # ret can be 0 (no changes to record) or the value returned by
2042 # commit(), 1 if nothing changed or None on success.
2041 # commit(), 1 if nothing changed or None on success.
2043 return 1 if ret == 0 else ret
2042 return 1 if ret == 0 else ret
2044
2043
2045 opts = pycompat.byteskwargs(opts)
2044 opts = pycompat.byteskwargs(opts)
2046 if opts.get(b'subrepos'):
2045 if opts.get(b'subrepos'):
2047 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2046 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2048 # Let --subrepos on the command line override config setting.
2047 # Let --subrepos on the command line override config setting.
2049 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2048 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2050
2049
2051 cmdutil.checkunfinished(repo, commit=True)
2050 cmdutil.checkunfinished(repo, commit=True)
2052
2051
2053 branch = repo[None].branch()
2052 branch = repo[None].branch()
2054 bheads = repo.branchheads(branch)
2053 bheads = repo.branchheads(branch)
2055 tip = repo.changelog.tip()
2054 tip = repo.changelog.tip()
2056
2055
2057 extra = {}
2056 extra = {}
2058 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2057 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2059 extra[b'close'] = b'1'
2058 extra[b'close'] = b'1'
2060
2059
2061 if repo[b'.'].closesbranch():
2060 if repo[b'.'].closesbranch():
2062 raise error.InputError(
2061 raise error.InputError(
2063 _(b'current revision is already a branch closing head')
2062 _(b'current revision is already a branch closing head')
2064 )
2063 )
2065 elif not bheads:
2064 elif not bheads:
2066 raise error.InputError(
2065 raise error.InputError(
2067 _(b'branch "%s" has no heads to close') % branch
2066 _(b'branch "%s" has no heads to close') % branch
2068 )
2067 )
2069 elif (
2068 elif (
2070 branch == repo[b'.'].branch()
2069 branch == repo[b'.'].branch()
2071 and repo[b'.'].node() not in bheads
2070 and repo[b'.'].node() not in bheads
2072 and not opts.get(b'force_close_branch')
2071 and not opts.get(b'force_close_branch')
2073 ):
2072 ):
2074 hint = _(
2073 hint = _(
2075 b'use --force-close-branch to close branch from a non-head'
2074 b'use --force-close-branch to close branch from a non-head'
2076 b' changeset'
2075 b' changeset'
2077 )
2076 )
2078 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2077 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2079 elif opts.get(b'amend'):
2078 elif opts.get(b'amend'):
2080 if (
2079 if (
2081 repo[b'.'].p1().branch() != branch
2080 repo[b'.'].p1().branch() != branch
2082 and repo[b'.'].p2().branch() != branch
2081 and repo[b'.'].p2().branch() != branch
2083 ):
2082 ):
2084 raise error.InputError(_(b'can only close branch heads'))
2083 raise error.InputError(_(b'can only close branch heads'))
2085
2084
2086 if opts.get(b'amend'):
2085 if opts.get(b'amend'):
2087 if ui.configbool(b'ui', b'commitsubrepos'):
2086 if ui.configbool(b'ui', b'commitsubrepos'):
2088 raise error.InputError(
2087 raise error.InputError(
2089 _(b'cannot amend with ui.commitsubrepos enabled')
2088 _(b'cannot amend with ui.commitsubrepos enabled')
2090 )
2089 )
2091
2090
2092 old = repo[b'.']
2091 old = repo[b'.']
2093 rewriteutil.precheck(repo, [old.rev()], b'amend')
2092 rewriteutil.precheck(repo, [old.rev()], b'amend')
2094
2093
2095 # Currently histedit gets confused if an amend happens while histedit
2094 # Currently histedit gets confused if an amend happens while histedit
2096 # is in progress. Since we have a checkunfinished command, we are
2095 # is in progress. Since we have a checkunfinished command, we are
2097 # temporarily honoring it.
2096 # temporarily honoring it.
2098 #
2097 #
2099 # Note: eventually this guard will be removed. Please do not expect
2098 # Note: eventually this guard will be removed. Please do not expect
2100 # this behavior to remain.
2099 # this behavior to remain.
2101 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2100 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2102 cmdutil.checkunfinished(repo)
2101 cmdutil.checkunfinished(repo)
2103
2102
2104 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2103 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2105 if node == old.node():
2104 if node == old.node():
2106 ui.status(_(b"nothing changed\n"))
2105 ui.status(_(b"nothing changed\n"))
2107 return 1
2106 return 1
2108 else:
2107 else:
2109
2108
2110 def commitfunc(ui, repo, message, match, opts):
2109 def commitfunc(ui, repo, message, match, opts):
2111 overrides = {}
2110 overrides = {}
2112 if opts.get(b'secret'):
2111 if opts.get(b'secret'):
2113 overrides[(b'phases', b'new-commit')] = b'secret'
2112 overrides[(b'phases', b'new-commit')] = b'secret'
2114
2113
2115 baseui = repo.baseui
2114 baseui = repo.baseui
2116 with baseui.configoverride(overrides, b'commit'):
2115 with baseui.configoverride(overrides, b'commit'):
2117 with ui.configoverride(overrides, b'commit'):
2116 with ui.configoverride(overrides, b'commit'):
2118 editform = cmdutil.mergeeditform(
2117 editform = cmdutil.mergeeditform(
2119 repo[None], b'commit.normal'
2118 repo[None], b'commit.normal'
2120 )
2119 )
2121 editor = cmdutil.getcommiteditor(
2120 editor = cmdutil.getcommiteditor(
2122 editform=editform, **pycompat.strkwargs(opts)
2121 editform=editform, **pycompat.strkwargs(opts)
2123 )
2122 )
2124 return repo.commit(
2123 return repo.commit(
2125 message,
2124 message,
2126 opts.get(b'user'),
2125 opts.get(b'user'),
2127 opts.get(b'date'),
2126 opts.get(b'date'),
2128 match,
2127 match,
2129 editor=editor,
2128 editor=editor,
2130 extra=extra,
2129 extra=extra,
2131 )
2130 )
2132
2131
2133 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2132 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2134
2133
2135 if not node:
2134 if not node:
2136 stat = cmdutil.postcommitstatus(repo, pats, opts)
2135 stat = cmdutil.postcommitstatus(repo, pats, opts)
2137 if stat.deleted:
2136 if stat.deleted:
2138 ui.status(
2137 ui.status(
2139 _(
2138 _(
2140 b"nothing changed (%d missing files, see "
2139 b"nothing changed (%d missing files, see "
2141 b"'hg status')\n"
2140 b"'hg status')\n"
2142 )
2141 )
2143 % len(stat.deleted)
2142 % len(stat.deleted)
2144 )
2143 )
2145 else:
2144 else:
2146 ui.status(_(b"nothing changed\n"))
2145 ui.status(_(b"nothing changed\n"))
2147 return 1
2146 return 1
2148
2147
2149 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2148 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2150
2149
2151 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2150 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2152 status(
2151 status(
2153 ui,
2152 ui,
2154 repo,
2153 repo,
2155 modified=True,
2154 modified=True,
2156 added=True,
2155 added=True,
2157 removed=True,
2156 removed=True,
2158 deleted=True,
2157 deleted=True,
2159 unknown=True,
2158 unknown=True,
2160 subrepos=opts.get(b'subrepos'),
2159 subrepos=opts.get(b'subrepos'),
2161 )
2160 )
2162
2161
2163
2162
2164 @command(
2163 @command(
2165 b'config|showconfig|debugconfig',
2164 b'config|showconfig|debugconfig',
2166 [
2165 [
2167 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2166 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2168 (b'e', b'edit', None, _(b'edit user config')),
2167 (b'e', b'edit', None, _(b'edit user config')),
2169 (b'l', b'local', None, _(b'edit repository config')),
2168 (b'l', b'local', None, _(b'edit repository config')),
2170 (
2169 (
2171 b'',
2170 b'',
2172 b'shared',
2171 b'shared',
2173 None,
2172 None,
2174 _(b'edit shared source repository config (EXPERIMENTAL)'),
2173 _(b'edit shared source repository config (EXPERIMENTAL)'),
2175 ),
2174 ),
2176 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2175 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2177 (b'g', b'global', None, _(b'edit global config')),
2176 (b'g', b'global', None, _(b'edit global config')),
2178 ]
2177 ]
2179 + formatteropts,
2178 + formatteropts,
2180 _(b'[-u] [NAME]...'),
2179 _(b'[-u] [NAME]...'),
2181 helpcategory=command.CATEGORY_HELP,
2180 helpcategory=command.CATEGORY_HELP,
2182 optionalrepo=True,
2181 optionalrepo=True,
2183 intents={INTENT_READONLY},
2182 intents={INTENT_READONLY},
2184 )
2183 )
2185 def config(ui, repo, *values, **opts):
2184 def config(ui, repo, *values, **opts):
2186 """show combined config settings from all hgrc files
2185 """show combined config settings from all hgrc files
2187
2186
2188 With no arguments, print names and values of all config items.
2187 With no arguments, print names and values of all config items.
2189
2188
2190 With one argument of the form section.name, print just the value
2189 With one argument of the form section.name, print just the value
2191 of that config item.
2190 of that config item.
2192
2191
2193 With multiple arguments, print names and values of all config
2192 With multiple arguments, print names and values of all config
2194 items with matching section names or section.names.
2193 items with matching section names or section.names.
2195
2194
2196 With --edit, start an editor on the user-level config file. With
2195 With --edit, start an editor on the user-level config file. With
2197 --global, edit the system-wide config file. With --local, edit the
2196 --global, edit the system-wide config file. With --local, edit the
2198 repository-level config file.
2197 repository-level config file.
2199
2198
2200 With --debug, the source (filename and line number) is printed
2199 With --debug, the source (filename and line number) is printed
2201 for each config item.
2200 for each config item.
2202
2201
2203 See :hg:`help config` for more information about config files.
2202 See :hg:`help config` for more information about config files.
2204
2203
2205 .. container:: verbose
2204 .. container:: verbose
2206
2205
2207 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2206 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2208 This file is not shared across shares when in share-safe mode.
2207 This file is not shared across shares when in share-safe mode.
2209
2208
2210 Template:
2209 Template:
2211
2210
2212 The following keywords are supported. See also :hg:`help templates`.
2211 The following keywords are supported. See also :hg:`help templates`.
2213
2212
2214 :name: String. Config name.
2213 :name: String. Config name.
2215 :source: String. Filename and line number where the item is defined.
2214 :source: String. Filename and line number where the item is defined.
2216 :value: String. Config value.
2215 :value: String. Config value.
2217
2216
2218 The --shared flag can be used to edit the config file of shared source
2217 The --shared flag can be used to edit the config file of shared source
2219 repository. It only works when you have shared using the experimental
2218 repository. It only works when you have shared using the experimental
2220 share safe feature.
2219 share safe feature.
2221
2220
2222 Returns 0 on success, 1 if NAME does not exist.
2221 Returns 0 on success, 1 if NAME does not exist.
2223
2222
2224 """
2223 """
2225
2224
2226 opts = pycompat.byteskwargs(opts)
2225 opts = pycompat.byteskwargs(opts)
2227 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2226 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2228 if any(opts.get(o) for o in editopts):
2227 if any(opts.get(o) for o in editopts):
2229 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2228 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2230 if opts.get(b'local'):
2229 if opts.get(b'local'):
2231 if not repo:
2230 if not repo:
2232 raise error.InputError(
2231 raise error.InputError(
2233 _(b"can't use --local outside a repository")
2232 _(b"can't use --local outside a repository")
2234 )
2233 )
2235 paths = [repo.vfs.join(b'hgrc')]
2234 paths = [repo.vfs.join(b'hgrc')]
2236 elif opts.get(b'global'):
2235 elif opts.get(b'global'):
2237 paths = rcutil.systemrcpath()
2236 paths = rcutil.systemrcpath()
2238 elif opts.get(b'shared'):
2237 elif opts.get(b'shared'):
2239 if not repo.shared():
2238 if not repo.shared():
2240 raise error.InputError(
2239 raise error.InputError(
2241 _(b"repository is not shared; can't use --shared")
2240 _(b"repository is not shared; can't use --shared")
2242 )
2241 )
2243 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2242 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2244 raise error.InputError(
2243 raise error.InputError(
2245 _(
2244 _(
2246 b"share safe feature not enabled; "
2245 b"share safe feature not enabled; "
2247 b"unable to edit shared source repository config"
2246 b"unable to edit shared source repository config"
2248 )
2247 )
2249 )
2248 )
2250 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2249 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2251 elif opts.get(b'non_shared'):
2250 elif opts.get(b'non_shared'):
2252 paths = [repo.vfs.join(b'hgrc-not-shared')]
2251 paths = [repo.vfs.join(b'hgrc-not-shared')]
2253 else:
2252 else:
2254 paths = rcutil.userrcpath()
2253 paths = rcutil.userrcpath()
2255
2254
2256 for f in paths:
2255 for f in paths:
2257 if os.path.exists(f):
2256 if os.path.exists(f):
2258 break
2257 break
2259 else:
2258 else:
2260 if opts.get(b'global'):
2259 if opts.get(b'global'):
2261 samplehgrc = uimod.samplehgrcs[b'global']
2260 samplehgrc = uimod.samplehgrcs[b'global']
2262 elif opts.get(b'local'):
2261 elif opts.get(b'local'):
2263 samplehgrc = uimod.samplehgrcs[b'local']
2262 samplehgrc = uimod.samplehgrcs[b'local']
2264 else:
2263 else:
2265 samplehgrc = uimod.samplehgrcs[b'user']
2264 samplehgrc = uimod.samplehgrcs[b'user']
2266
2265
2267 f = paths[0]
2266 f = paths[0]
2268 fp = open(f, b"wb")
2267 fp = open(f, b"wb")
2269 fp.write(util.tonativeeol(samplehgrc))
2268 fp.write(util.tonativeeol(samplehgrc))
2270 fp.close()
2269 fp.close()
2271
2270
2272 editor = ui.geteditor()
2271 editor = ui.geteditor()
2273 ui.system(
2272 ui.system(
2274 b"%s \"%s\"" % (editor, f),
2273 b"%s \"%s\"" % (editor, f),
2275 onerr=error.InputError,
2274 onerr=error.InputError,
2276 errprefix=_(b"edit failed"),
2275 errprefix=_(b"edit failed"),
2277 blockedtag=b'config_edit',
2276 blockedtag=b'config_edit',
2278 )
2277 )
2279 return
2278 return
2280 ui.pager(b'config')
2279 ui.pager(b'config')
2281 fm = ui.formatter(b'config', opts)
2280 fm = ui.formatter(b'config', opts)
2282 for t, f in rcutil.rccomponents():
2281 for t, f in rcutil.rccomponents():
2283 if t == b'path':
2282 if t == b'path':
2284 ui.debug(b'read config from: %s\n' % f)
2283 ui.debug(b'read config from: %s\n' % f)
2285 elif t == b'resource':
2284 elif t == b'resource':
2286 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2285 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2287 elif t == b'items':
2286 elif t == b'items':
2288 # Don't print anything for 'items'.
2287 # Don't print anything for 'items'.
2289 pass
2288 pass
2290 else:
2289 else:
2291 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2290 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2292 untrusted = bool(opts.get(b'untrusted'))
2291 untrusted = bool(opts.get(b'untrusted'))
2293
2292
2294 selsections = selentries = []
2293 selsections = selentries = []
2295 if values:
2294 if values:
2296 selsections = [v for v in values if b'.' not in v]
2295 selsections = [v for v in values if b'.' not in v]
2297 selentries = [v for v in values if b'.' in v]
2296 selentries = [v for v in values if b'.' in v]
2298 uniquesel = len(selentries) == 1 and not selsections
2297 uniquesel = len(selentries) == 1 and not selsections
2299 selsections = set(selsections)
2298 selsections = set(selsections)
2300 selentries = set(selentries)
2299 selentries = set(selentries)
2301
2300
2302 matched = False
2301 matched = False
2303 for section, name, value in ui.walkconfig(untrusted=untrusted):
2302 for section, name, value in ui.walkconfig(untrusted=untrusted):
2304 source = ui.configsource(section, name, untrusted)
2303 source = ui.configsource(section, name, untrusted)
2305 value = pycompat.bytestr(value)
2304 value = pycompat.bytestr(value)
2306 defaultvalue = ui.configdefault(section, name)
2305 defaultvalue = ui.configdefault(section, name)
2307 if fm.isplain():
2306 if fm.isplain():
2308 source = source or b'none'
2307 source = source or b'none'
2309 value = value.replace(b'\n', b'\\n')
2308 value = value.replace(b'\n', b'\\n')
2310 entryname = section + b'.' + name
2309 entryname = section + b'.' + name
2311 if values and not (section in selsections or entryname in selentries):
2310 if values and not (section in selsections or entryname in selentries):
2312 continue
2311 continue
2313 fm.startitem()
2312 fm.startitem()
2314 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2313 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2315 if uniquesel:
2314 if uniquesel:
2316 fm.data(name=entryname)
2315 fm.data(name=entryname)
2317 fm.write(b'value', b'%s\n', value)
2316 fm.write(b'value', b'%s\n', value)
2318 else:
2317 else:
2319 fm.write(b'name value', b'%s=%s\n', entryname, value)
2318 fm.write(b'name value', b'%s=%s\n', entryname, value)
2320 if formatter.isprintable(defaultvalue):
2319 if formatter.isprintable(defaultvalue):
2321 fm.data(defaultvalue=defaultvalue)
2320 fm.data(defaultvalue=defaultvalue)
2322 elif isinstance(defaultvalue, list) and all(
2321 elif isinstance(defaultvalue, list) and all(
2323 formatter.isprintable(e) for e in defaultvalue
2322 formatter.isprintable(e) for e in defaultvalue
2324 ):
2323 ):
2325 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2324 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2326 # TODO: no idea how to process unsupported defaultvalue types
2325 # TODO: no idea how to process unsupported defaultvalue types
2327 matched = True
2326 matched = True
2328 fm.end()
2327 fm.end()
2329 if matched:
2328 if matched:
2330 return 0
2329 return 0
2331 return 1
2330 return 1
2332
2331
2333
2332
2334 @command(
2333 @command(
2335 b'continue',
2334 b'continue',
2336 dryrunopts,
2335 dryrunopts,
2337 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2336 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2338 helpbasic=True,
2337 helpbasic=True,
2339 )
2338 )
2340 def continuecmd(ui, repo, **opts):
2339 def continuecmd(ui, repo, **opts):
2341 """resumes an interrupted operation (EXPERIMENTAL)
2340 """resumes an interrupted operation (EXPERIMENTAL)
2342
2341
2343 Finishes a multistep operation like graft, histedit, rebase, merge,
2342 Finishes a multistep operation like graft, histedit, rebase, merge,
2344 and unshelve if they are in an interrupted state.
2343 and unshelve if they are in an interrupted state.
2345
2344
2346 use --dry-run/-n to dry run the command.
2345 use --dry-run/-n to dry run the command.
2347 """
2346 """
2348 dryrun = opts.get('dry_run')
2347 dryrun = opts.get('dry_run')
2349 contstate = cmdutil.getunfinishedstate(repo)
2348 contstate = cmdutil.getunfinishedstate(repo)
2350 if not contstate:
2349 if not contstate:
2351 raise error.StateError(_(b'no operation in progress'))
2350 raise error.StateError(_(b'no operation in progress'))
2352 if not contstate.continuefunc:
2351 if not contstate.continuefunc:
2353 raise error.StateError(
2352 raise error.StateError(
2354 (
2353 (
2355 _(b"%s in progress but does not support 'hg continue'")
2354 _(b"%s in progress but does not support 'hg continue'")
2356 % (contstate._opname)
2355 % (contstate._opname)
2357 ),
2356 ),
2358 hint=contstate.continuemsg(),
2357 hint=contstate.continuemsg(),
2359 )
2358 )
2360 if dryrun:
2359 if dryrun:
2361 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2360 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2362 return
2361 return
2363 return contstate.continuefunc(ui, repo)
2362 return contstate.continuefunc(ui, repo)
2364
2363
2365
2364
2366 @command(
2365 @command(
2367 b'copy|cp',
2366 b'copy|cp',
2368 [
2367 [
2369 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2368 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2370 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2369 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2371 (
2370 (
2372 b'',
2371 b'',
2373 b'at-rev',
2372 b'at-rev',
2374 b'',
2373 b'',
2375 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2374 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2376 _(b'REV'),
2375 _(b'REV'),
2377 ),
2376 ),
2378 (
2377 (
2379 b'f',
2378 b'f',
2380 b'force',
2379 b'force',
2381 None,
2380 None,
2382 _(b'forcibly copy over an existing managed file'),
2381 _(b'forcibly copy over an existing managed file'),
2383 ),
2382 ),
2384 ]
2383 ]
2385 + walkopts
2384 + walkopts
2386 + dryrunopts,
2385 + dryrunopts,
2387 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2386 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2388 helpcategory=command.CATEGORY_FILE_CONTENTS,
2387 helpcategory=command.CATEGORY_FILE_CONTENTS,
2389 )
2388 )
2390 def copy(ui, repo, *pats, **opts):
2389 def copy(ui, repo, *pats, **opts):
2391 """mark files as copied for the next commit
2390 """mark files as copied for the next commit
2392
2391
2393 Mark dest as having copies of source files. If dest is a
2392 Mark dest as having copies of source files. If dest is a
2394 directory, copies are put in that directory. If dest is a file,
2393 directory, copies are put in that directory. If dest is a file,
2395 the source must be a single file.
2394 the source must be a single file.
2396
2395
2397 By default, this command copies the contents of files as they
2396 By default, this command copies the contents of files as they
2398 exist in the working directory. If invoked with -A/--after, the
2397 exist in the working directory. If invoked with -A/--after, the
2399 operation is recorded, but no copying is performed.
2398 operation is recorded, but no copying is performed.
2400
2399
2401 To undo marking a destination file as copied, use --forget. With that
2400 To undo marking a destination file as copied, use --forget. With that
2402 option, all given (positional) arguments are unmarked as copies. The
2401 option, all given (positional) arguments are unmarked as copies. The
2403 destination file(s) will be left in place (still tracked).
2402 destination file(s) will be left in place (still tracked).
2404
2403
2405 This command takes effect with the next commit by default.
2404 This command takes effect with the next commit by default.
2406
2405
2407 Returns 0 on success, 1 if errors are encountered.
2406 Returns 0 on success, 1 if errors are encountered.
2408 """
2407 """
2409 opts = pycompat.byteskwargs(opts)
2408 opts = pycompat.byteskwargs(opts)
2410 with repo.wlock():
2409 with repo.wlock():
2411 return cmdutil.copy(ui, repo, pats, opts)
2410 return cmdutil.copy(ui, repo, pats, opts)
2412
2411
2413
2412
2414 @command(
2413 @command(
2415 b'debugcommands',
2414 b'debugcommands',
2416 [],
2415 [],
2417 _(b'[COMMAND]'),
2416 _(b'[COMMAND]'),
2418 helpcategory=command.CATEGORY_HELP,
2417 helpcategory=command.CATEGORY_HELP,
2419 norepo=True,
2418 norepo=True,
2420 )
2419 )
2421 def debugcommands(ui, cmd=b'', *args):
2420 def debugcommands(ui, cmd=b'', *args):
2422 """list all available commands and options"""
2421 """list all available commands and options"""
2423 for cmd, vals in sorted(pycompat.iteritems(table)):
2422 for cmd, vals in sorted(pycompat.iteritems(table)):
2424 cmd = cmd.split(b'|')[0]
2423 cmd = cmd.split(b'|')[0]
2425 opts = b', '.join([i[1] for i in vals[1]])
2424 opts = b', '.join([i[1] for i in vals[1]])
2426 ui.write(b'%s: %s\n' % (cmd, opts))
2425 ui.write(b'%s: %s\n' % (cmd, opts))
2427
2426
2428
2427
2429 @command(
2428 @command(
2430 b'debugcomplete',
2429 b'debugcomplete',
2431 [(b'o', b'options', None, _(b'show the command options'))],
2430 [(b'o', b'options', None, _(b'show the command options'))],
2432 _(b'[-o] CMD'),
2431 _(b'[-o] CMD'),
2433 helpcategory=command.CATEGORY_HELP,
2432 helpcategory=command.CATEGORY_HELP,
2434 norepo=True,
2433 norepo=True,
2435 )
2434 )
2436 def debugcomplete(ui, cmd=b'', **opts):
2435 def debugcomplete(ui, cmd=b'', **opts):
2437 """returns the completion list associated with the given command"""
2436 """returns the completion list associated with the given command"""
2438
2437
2439 if opts.get('options'):
2438 if opts.get('options'):
2440 options = []
2439 options = []
2441 otables = [globalopts]
2440 otables = [globalopts]
2442 if cmd:
2441 if cmd:
2443 aliases, entry = cmdutil.findcmd(cmd, table, False)
2442 aliases, entry = cmdutil.findcmd(cmd, table, False)
2444 otables.append(entry[1])
2443 otables.append(entry[1])
2445 for t in otables:
2444 for t in otables:
2446 for o in t:
2445 for o in t:
2447 if b"(DEPRECATED)" in o[3]:
2446 if b"(DEPRECATED)" in o[3]:
2448 continue
2447 continue
2449 if o[0]:
2448 if o[0]:
2450 options.append(b'-%s' % o[0])
2449 options.append(b'-%s' % o[0])
2451 options.append(b'--%s' % o[1])
2450 options.append(b'--%s' % o[1])
2452 ui.write(b"%s\n" % b"\n".join(options))
2451 ui.write(b"%s\n" % b"\n".join(options))
2453 return
2452 return
2454
2453
2455 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2454 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2456 if ui.verbose:
2455 if ui.verbose:
2457 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2456 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2458 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2457 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2459
2458
2460
2459
2461 @command(
2460 @command(
2462 b'diff',
2461 b'diff',
2463 [
2462 [
2464 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2463 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2465 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2464 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2466 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2465 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2467 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2466 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2468 ]
2467 ]
2469 + diffopts
2468 + diffopts
2470 + diffopts2
2469 + diffopts2
2471 + walkopts
2470 + walkopts
2472 + subrepoopts,
2471 + subrepoopts,
2473 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2472 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2474 helpcategory=command.CATEGORY_FILE_CONTENTS,
2473 helpcategory=command.CATEGORY_FILE_CONTENTS,
2475 helpbasic=True,
2474 helpbasic=True,
2476 inferrepo=True,
2475 inferrepo=True,
2477 intents={INTENT_READONLY},
2476 intents={INTENT_READONLY},
2478 )
2477 )
2479 def diff(ui, repo, *pats, **opts):
2478 def diff(ui, repo, *pats, **opts):
2480 """diff repository (or selected files)
2479 """diff repository (or selected files)
2481
2480
2482 Show differences between revisions for the specified files.
2481 Show differences between revisions for the specified files.
2483
2482
2484 Differences between files are shown using the unified diff format.
2483 Differences between files are shown using the unified diff format.
2485
2484
2486 .. note::
2485 .. note::
2487
2486
2488 :hg:`diff` may generate unexpected results for merges, as it will
2487 :hg:`diff` may generate unexpected results for merges, as it will
2489 default to comparing against the working directory's first
2488 default to comparing against the working directory's first
2490 parent changeset if no revisions are specified.
2489 parent changeset if no revisions are specified.
2491
2490
2492 By default, the working directory files are compared to its first parent. To
2491 By default, the working directory files are compared to its first parent. To
2493 see the differences from another revision, use --from. To see the difference
2492 see the differences from another revision, use --from. To see the difference
2494 to another revision, use --to. For example, :hg:`diff --from .^` will show
2493 to another revision, use --to. For example, :hg:`diff --from .^` will show
2495 the differences from the working copy's grandparent to the working copy,
2494 the differences from the working copy's grandparent to the working copy,
2496 :hg:`diff --to .` will show the diff from the working copy to its parent
2495 :hg:`diff --to .` will show the diff from the working copy to its parent
2497 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2496 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2498 show the diff between those two revisions.
2497 show the diff between those two revisions.
2499
2498
2500 Alternatively you can specify -c/--change with a revision to see the changes
2499 Alternatively you can specify -c/--change with a revision to see the changes
2501 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2500 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2502 equivalent to :hg:`diff --from 42^ --to 42`)
2501 equivalent to :hg:`diff --from 42^ --to 42`)
2503
2502
2504 Without the -a/--text option, diff will avoid generating diffs of
2503 Without the -a/--text option, diff will avoid generating diffs of
2505 files it detects as binary. With -a, diff will generate a diff
2504 files it detects as binary. With -a, diff will generate a diff
2506 anyway, probably with undesirable results.
2505 anyway, probably with undesirable results.
2507
2506
2508 Use the -g/--git option to generate diffs in the git extended diff
2507 Use the -g/--git option to generate diffs in the git extended diff
2509 format. For more information, read :hg:`help diffs`.
2508 format. For more information, read :hg:`help diffs`.
2510
2509
2511 .. container:: verbose
2510 .. container:: verbose
2512
2511
2513 Examples:
2512 Examples:
2514
2513
2515 - compare a file in the current working directory to its parent::
2514 - compare a file in the current working directory to its parent::
2516
2515
2517 hg diff foo.c
2516 hg diff foo.c
2518
2517
2519 - compare two historical versions of a directory, with rename info::
2518 - compare two historical versions of a directory, with rename info::
2520
2519
2521 hg diff --git --from 1.0 --to 1.2 lib/
2520 hg diff --git --from 1.0 --to 1.2 lib/
2522
2521
2523 - get change stats relative to the last change on some date::
2522 - get change stats relative to the last change on some date::
2524
2523
2525 hg diff --stat --from "date('may 2')"
2524 hg diff --stat --from "date('may 2')"
2526
2525
2527 - diff all newly-added files that contain a keyword::
2526 - diff all newly-added files that contain a keyword::
2528
2527
2529 hg diff "set:added() and grep(GNU)"
2528 hg diff "set:added() and grep(GNU)"
2530
2529
2531 - compare a revision and its parents::
2530 - compare a revision and its parents::
2532
2531
2533 hg diff -c 9353 # compare against first parent
2532 hg diff -c 9353 # compare against first parent
2534 hg diff --from 9353^ --to 9353 # same using revset syntax
2533 hg diff --from 9353^ --to 9353 # same using revset syntax
2535 hg diff --from 9353^2 --to 9353 # compare against the second parent
2534 hg diff --from 9353^2 --to 9353 # compare against the second parent
2536
2535
2537 Returns 0 on success.
2536 Returns 0 on success.
2538 """
2537 """
2539
2538
2540 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2539 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2541 opts = pycompat.byteskwargs(opts)
2540 opts = pycompat.byteskwargs(opts)
2542 revs = opts.get(b'rev')
2541 revs = opts.get(b'rev')
2543 change = opts.get(b'change')
2542 change = opts.get(b'change')
2544 from_rev = opts.get(b'from')
2543 from_rev = opts.get(b'from')
2545 to_rev = opts.get(b'to')
2544 to_rev = opts.get(b'to')
2546 stat = opts.get(b'stat')
2545 stat = opts.get(b'stat')
2547 reverse = opts.get(b'reverse')
2546 reverse = opts.get(b'reverse')
2548 diffmerge = ui.configbool(b'diff', b'merge')
2549
2547
2550 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2548 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2551 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2549 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2552 if change:
2550 if change:
2553 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2551 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2554 ctx2 = scmutil.revsingle(repo, change, None)
2552 ctx2 = scmutil.revsingle(repo, change, None)
2555 if diffmerge and ctx2.p2().node() != nullid:
2553 ctx1 = logcmdutil.diff_parent(ctx2)
2556 pctx1 = ctx2.p1()
2557 pctx2 = ctx2.p2()
2558 wctx = contextmod.overlayworkingctx(repo)
2559 wctx.setbase(pctx1)
2560 with ui.configoverride(
2561 {
2562 (
2563 b'ui',
2564 b'forcemerge',
2565 ): b'internal:merge3-lie-about-conflicts',
2566 },
2567 b'diff --merge',
2568 ):
2569 repo.ui.pushbuffer()
2570 mergemod.merge(pctx2, wc=wctx)
2571 repo.ui.popbuffer()
2572 ctx1 = wctx
2573 else:
2574 ctx1 = ctx2.p1()
2575 elif from_rev or to_rev:
2554 elif from_rev or to_rev:
2576 repo = scmutil.unhidehashlikerevs(
2555 repo = scmutil.unhidehashlikerevs(
2577 repo, [from_rev] + [to_rev], b'nowarn'
2556 repo, [from_rev] + [to_rev], b'nowarn'
2578 )
2557 )
2579 ctx1 = scmutil.revsingle(repo, from_rev, None)
2558 ctx1 = scmutil.revsingle(repo, from_rev, None)
2580 ctx2 = scmutil.revsingle(repo, to_rev, None)
2559 ctx2 = scmutil.revsingle(repo, to_rev, None)
2581 else:
2560 else:
2582 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2561 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2583 ctx1, ctx2 = scmutil.revpair(repo, revs)
2562 ctx1, ctx2 = scmutil.revpair(repo, revs)
2584
2563
2585 if reverse:
2564 if reverse:
2586 ctxleft = ctx2
2565 ctxleft = ctx2
2587 ctxright = ctx1
2566 ctxright = ctx1
2588 else:
2567 else:
2589 ctxleft = ctx1
2568 ctxleft = ctx1
2590 ctxright = ctx2
2569 ctxright = ctx2
2591
2570
2592 diffopts = patch.diffallopts(ui, opts)
2571 diffopts = patch.diffallopts(ui, opts)
2593 m = scmutil.match(ctx2, pats, opts)
2572 m = scmutil.match(ctx2, pats, opts)
2594 m = repo.narrowmatch(m)
2573 m = repo.narrowmatch(m)
2595 ui.pager(b'diff')
2574 ui.pager(b'diff')
2596 logcmdutil.diffordiffstat(
2575 logcmdutil.diffordiffstat(
2597 ui,
2576 ui,
2598 repo,
2577 repo,
2599 diffopts,
2578 diffopts,
2600 ctxleft,
2579 ctxleft,
2601 ctxright,
2580 ctxright,
2602 m,
2581 m,
2603 stat=stat,
2582 stat=stat,
2604 listsubrepos=opts.get(b'subrepos'),
2583 listsubrepos=opts.get(b'subrepos'),
2605 root=opts.get(b'root'),
2584 root=opts.get(b'root'),
2606 )
2585 )
2607
2586
2608
2587
2609 @command(
2588 @command(
2610 b'export',
2589 b'export',
2611 [
2590 [
2612 (
2591 (
2613 b'B',
2592 b'B',
2614 b'bookmark',
2593 b'bookmark',
2615 b'',
2594 b'',
2616 _(b'export changes only reachable by given bookmark'),
2595 _(b'export changes only reachable by given bookmark'),
2617 _(b'BOOKMARK'),
2596 _(b'BOOKMARK'),
2618 ),
2597 ),
2619 (
2598 (
2620 b'o',
2599 b'o',
2621 b'output',
2600 b'output',
2622 b'',
2601 b'',
2623 _(b'print output to file with formatted name'),
2602 _(b'print output to file with formatted name'),
2624 _(b'FORMAT'),
2603 _(b'FORMAT'),
2625 ),
2604 ),
2626 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2605 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2627 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2606 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2628 ]
2607 ]
2629 + diffopts
2608 + diffopts
2630 + formatteropts,
2609 + formatteropts,
2631 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2610 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2632 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2611 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2633 helpbasic=True,
2612 helpbasic=True,
2634 intents={INTENT_READONLY},
2613 intents={INTENT_READONLY},
2635 )
2614 )
2636 def export(ui, repo, *changesets, **opts):
2615 def export(ui, repo, *changesets, **opts):
2637 """dump the header and diffs for one or more changesets
2616 """dump the header and diffs for one or more changesets
2638
2617
2639 Print the changeset header and diffs for one or more revisions.
2618 Print the changeset header and diffs for one or more revisions.
2640 If no revision is given, the parent of the working directory is used.
2619 If no revision is given, the parent of the working directory is used.
2641
2620
2642 The information shown in the changeset header is: author, date,
2621 The information shown in the changeset header is: author, date,
2643 branch name (if non-default), changeset hash, parent(s) and commit
2622 branch name (if non-default), changeset hash, parent(s) and commit
2644 comment.
2623 comment.
2645
2624
2646 .. note::
2625 .. note::
2647
2626
2648 :hg:`export` may generate unexpected diff output for merge
2627 :hg:`export` may generate unexpected diff output for merge
2649 changesets, as it will compare the merge changeset against its
2628 changesets, as it will compare the merge changeset against its
2650 first parent only.
2629 first parent only.
2651
2630
2652 Output may be to a file, in which case the name of the file is
2631 Output may be to a file, in which case the name of the file is
2653 given using a template string. See :hg:`help templates`. In addition
2632 given using a template string. See :hg:`help templates`. In addition
2654 to the common template keywords, the following formatting rules are
2633 to the common template keywords, the following formatting rules are
2655 supported:
2634 supported:
2656
2635
2657 :``%%``: literal "%" character
2636 :``%%``: literal "%" character
2658 :``%H``: changeset hash (40 hexadecimal digits)
2637 :``%H``: changeset hash (40 hexadecimal digits)
2659 :``%N``: number of patches being generated
2638 :``%N``: number of patches being generated
2660 :``%R``: changeset revision number
2639 :``%R``: changeset revision number
2661 :``%b``: basename of the exporting repository
2640 :``%b``: basename of the exporting repository
2662 :``%h``: short-form changeset hash (12 hexadecimal digits)
2641 :``%h``: short-form changeset hash (12 hexadecimal digits)
2663 :``%m``: first line of the commit message (only alphanumeric characters)
2642 :``%m``: first line of the commit message (only alphanumeric characters)
2664 :``%n``: zero-padded sequence number, starting at 1
2643 :``%n``: zero-padded sequence number, starting at 1
2665 :``%r``: zero-padded changeset revision number
2644 :``%r``: zero-padded changeset revision number
2666 :``\\``: literal "\\" character
2645 :``\\``: literal "\\" character
2667
2646
2668 Without the -a/--text option, export will avoid generating diffs
2647 Without the -a/--text option, export will avoid generating diffs
2669 of files it detects as binary. With -a, export will generate a
2648 of files it detects as binary. With -a, export will generate a
2670 diff anyway, probably with undesirable results.
2649 diff anyway, probably with undesirable results.
2671
2650
2672 With -B/--bookmark changesets reachable by the given bookmark are
2651 With -B/--bookmark changesets reachable by the given bookmark are
2673 selected.
2652 selected.
2674
2653
2675 Use the -g/--git option to generate diffs in the git extended diff
2654 Use the -g/--git option to generate diffs in the git extended diff
2676 format. See :hg:`help diffs` for more information.
2655 format. See :hg:`help diffs` for more information.
2677
2656
2678 With the --switch-parent option, the diff will be against the
2657 With the --switch-parent option, the diff will be against the
2679 second parent. It can be useful to review a merge.
2658 second parent. It can be useful to review a merge.
2680
2659
2681 .. container:: verbose
2660 .. container:: verbose
2682
2661
2683 Template:
2662 Template:
2684
2663
2685 The following keywords are supported in addition to the common template
2664 The following keywords are supported in addition to the common template
2686 keywords and functions. See also :hg:`help templates`.
2665 keywords and functions. See also :hg:`help templates`.
2687
2666
2688 :diff: String. Diff content.
2667 :diff: String. Diff content.
2689 :parents: List of strings. Parent nodes of the changeset.
2668 :parents: List of strings. Parent nodes of the changeset.
2690
2669
2691 Examples:
2670 Examples:
2692
2671
2693 - use export and import to transplant a bugfix to the current
2672 - use export and import to transplant a bugfix to the current
2694 branch::
2673 branch::
2695
2674
2696 hg export -r 9353 | hg import -
2675 hg export -r 9353 | hg import -
2697
2676
2698 - export all the changesets between two revisions to a file with
2677 - export all the changesets between two revisions to a file with
2699 rename information::
2678 rename information::
2700
2679
2701 hg export --git -r 123:150 > changes.txt
2680 hg export --git -r 123:150 > changes.txt
2702
2681
2703 - split outgoing changes into a series of patches with
2682 - split outgoing changes into a series of patches with
2704 descriptive names::
2683 descriptive names::
2705
2684
2706 hg export -r "outgoing()" -o "%n-%m.patch"
2685 hg export -r "outgoing()" -o "%n-%m.patch"
2707
2686
2708 Returns 0 on success.
2687 Returns 0 on success.
2709 """
2688 """
2710 opts = pycompat.byteskwargs(opts)
2689 opts = pycompat.byteskwargs(opts)
2711 bookmark = opts.get(b'bookmark')
2690 bookmark = opts.get(b'bookmark')
2712 changesets += tuple(opts.get(b'rev', []))
2691 changesets += tuple(opts.get(b'rev', []))
2713
2692
2714 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2693 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2715
2694
2716 if bookmark:
2695 if bookmark:
2717 if bookmark not in repo._bookmarks:
2696 if bookmark not in repo._bookmarks:
2718 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2697 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2719
2698
2720 revs = scmutil.bookmarkrevs(repo, bookmark)
2699 revs = scmutil.bookmarkrevs(repo, bookmark)
2721 else:
2700 else:
2722 if not changesets:
2701 if not changesets:
2723 changesets = [b'.']
2702 changesets = [b'.']
2724
2703
2725 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2704 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2726 revs = scmutil.revrange(repo, changesets)
2705 revs = scmutil.revrange(repo, changesets)
2727
2706
2728 if not revs:
2707 if not revs:
2729 raise error.InputError(_(b"export requires at least one changeset"))
2708 raise error.InputError(_(b"export requires at least one changeset"))
2730 if len(revs) > 1:
2709 if len(revs) > 1:
2731 ui.note(_(b'exporting patches:\n'))
2710 ui.note(_(b'exporting patches:\n'))
2732 else:
2711 else:
2733 ui.note(_(b'exporting patch:\n'))
2712 ui.note(_(b'exporting patch:\n'))
2734
2713
2735 fntemplate = opts.get(b'output')
2714 fntemplate = opts.get(b'output')
2736 if cmdutil.isstdiofilename(fntemplate):
2715 if cmdutil.isstdiofilename(fntemplate):
2737 fntemplate = b''
2716 fntemplate = b''
2738
2717
2739 if fntemplate:
2718 if fntemplate:
2740 fm = formatter.nullformatter(ui, b'export', opts)
2719 fm = formatter.nullformatter(ui, b'export', opts)
2741 else:
2720 else:
2742 ui.pager(b'export')
2721 ui.pager(b'export')
2743 fm = ui.formatter(b'export', opts)
2722 fm = ui.formatter(b'export', opts)
2744 with fm:
2723 with fm:
2745 cmdutil.export(
2724 cmdutil.export(
2746 repo,
2725 repo,
2747 revs,
2726 revs,
2748 fm,
2727 fm,
2749 fntemplate=fntemplate,
2728 fntemplate=fntemplate,
2750 switch_parent=opts.get(b'switch_parent'),
2729 switch_parent=opts.get(b'switch_parent'),
2751 opts=patch.diffallopts(ui, opts),
2730 opts=patch.diffallopts(ui, opts),
2752 )
2731 )
2753
2732
2754
2733
2755 @command(
2734 @command(
2756 b'files',
2735 b'files',
2757 [
2736 [
2758 (
2737 (
2759 b'r',
2738 b'r',
2760 b'rev',
2739 b'rev',
2761 b'',
2740 b'',
2762 _(b'search the repository as it is in REV'),
2741 _(b'search the repository as it is in REV'),
2763 _(b'REV'),
2742 _(b'REV'),
2764 ),
2743 ),
2765 (
2744 (
2766 b'0',
2745 b'0',
2767 b'print0',
2746 b'print0',
2768 None,
2747 None,
2769 _(b'end filenames with NUL, for use with xargs'),
2748 _(b'end filenames with NUL, for use with xargs'),
2770 ),
2749 ),
2771 ]
2750 ]
2772 + walkopts
2751 + walkopts
2773 + formatteropts
2752 + formatteropts
2774 + subrepoopts,
2753 + subrepoopts,
2775 _(b'[OPTION]... [FILE]...'),
2754 _(b'[OPTION]... [FILE]...'),
2776 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2755 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2777 intents={INTENT_READONLY},
2756 intents={INTENT_READONLY},
2778 )
2757 )
2779 def files(ui, repo, *pats, **opts):
2758 def files(ui, repo, *pats, **opts):
2780 """list tracked files
2759 """list tracked files
2781
2760
2782 Print files under Mercurial control in the working directory or
2761 Print files under Mercurial control in the working directory or
2783 specified revision for given files (excluding removed files).
2762 specified revision for given files (excluding removed files).
2784 Files can be specified as filenames or filesets.
2763 Files can be specified as filenames or filesets.
2785
2764
2786 If no files are given to match, this command prints the names
2765 If no files are given to match, this command prints the names
2787 of all files under Mercurial control.
2766 of all files under Mercurial control.
2788
2767
2789 .. container:: verbose
2768 .. container:: verbose
2790
2769
2791 Template:
2770 Template:
2792
2771
2793 The following keywords are supported in addition to the common template
2772 The following keywords are supported in addition to the common template
2794 keywords and functions. See also :hg:`help templates`.
2773 keywords and functions. See also :hg:`help templates`.
2795
2774
2796 :flags: String. Character denoting file's symlink and executable bits.
2775 :flags: String. Character denoting file's symlink and executable bits.
2797 :path: String. Repository-absolute path of the file.
2776 :path: String. Repository-absolute path of the file.
2798 :size: Integer. Size of the file in bytes.
2777 :size: Integer. Size of the file in bytes.
2799
2778
2800 Examples:
2779 Examples:
2801
2780
2802 - list all files under the current directory::
2781 - list all files under the current directory::
2803
2782
2804 hg files .
2783 hg files .
2805
2784
2806 - shows sizes and flags for current revision::
2785 - shows sizes and flags for current revision::
2807
2786
2808 hg files -vr .
2787 hg files -vr .
2809
2788
2810 - list all files named README::
2789 - list all files named README::
2811
2790
2812 hg files -I "**/README"
2791 hg files -I "**/README"
2813
2792
2814 - list all binary files::
2793 - list all binary files::
2815
2794
2816 hg files "set:binary()"
2795 hg files "set:binary()"
2817
2796
2818 - find files containing a regular expression::
2797 - find files containing a regular expression::
2819
2798
2820 hg files "set:grep('bob')"
2799 hg files "set:grep('bob')"
2821
2800
2822 - search tracked file contents with xargs and grep::
2801 - search tracked file contents with xargs and grep::
2823
2802
2824 hg files -0 | xargs -0 grep foo
2803 hg files -0 | xargs -0 grep foo
2825
2804
2826 See :hg:`help patterns` and :hg:`help filesets` for more information
2805 See :hg:`help patterns` and :hg:`help filesets` for more information
2827 on specifying file patterns.
2806 on specifying file patterns.
2828
2807
2829 Returns 0 if a match is found, 1 otherwise.
2808 Returns 0 if a match is found, 1 otherwise.
2830
2809
2831 """
2810 """
2832
2811
2833 opts = pycompat.byteskwargs(opts)
2812 opts = pycompat.byteskwargs(opts)
2834 rev = opts.get(b'rev')
2813 rev = opts.get(b'rev')
2835 if rev:
2814 if rev:
2836 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2815 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2837 ctx = scmutil.revsingle(repo, rev, None)
2816 ctx = scmutil.revsingle(repo, rev, None)
2838
2817
2839 end = b'\n'
2818 end = b'\n'
2840 if opts.get(b'print0'):
2819 if opts.get(b'print0'):
2841 end = b'\0'
2820 end = b'\0'
2842 fmt = b'%s' + end
2821 fmt = b'%s' + end
2843
2822
2844 m = scmutil.match(ctx, pats, opts)
2823 m = scmutil.match(ctx, pats, opts)
2845 ui.pager(b'files')
2824 ui.pager(b'files')
2846 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2825 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2847 with ui.formatter(b'files', opts) as fm:
2826 with ui.formatter(b'files', opts) as fm:
2848 return cmdutil.files(
2827 return cmdutil.files(
2849 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2828 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2850 )
2829 )
2851
2830
2852
2831
2853 @command(
2832 @command(
2854 b'forget',
2833 b'forget',
2855 [
2834 [
2856 (b'i', b'interactive', None, _(b'use interactive mode')),
2835 (b'i', b'interactive', None, _(b'use interactive mode')),
2857 ]
2836 ]
2858 + walkopts
2837 + walkopts
2859 + dryrunopts,
2838 + dryrunopts,
2860 _(b'[OPTION]... FILE...'),
2839 _(b'[OPTION]... FILE...'),
2861 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2840 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2862 helpbasic=True,
2841 helpbasic=True,
2863 inferrepo=True,
2842 inferrepo=True,
2864 )
2843 )
2865 def forget(ui, repo, *pats, **opts):
2844 def forget(ui, repo, *pats, **opts):
2866 """forget the specified files on the next commit
2845 """forget the specified files on the next commit
2867
2846
2868 Mark the specified files so they will no longer be tracked
2847 Mark the specified files so they will no longer be tracked
2869 after the next commit.
2848 after the next commit.
2870
2849
2871 This only removes files from the current branch, not from the
2850 This only removes files from the current branch, not from the
2872 entire project history, and it does not delete them from the
2851 entire project history, and it does not delete them from the
2873 working directory.
2852 working directory.
2874
2853
2875 To delete the file from the working directory, see :hg:`remove`.
2854 To delete the file from the working directory, see :hg:`remove`.
2876
2855
2877 To undo a forget before the next commit, see :hg:`add`.
2856 To undo a forget before the next commit, see :hg:`add`.
2878
2857
2879 .. container:: verbose
2858 .. container:: verbose
2880
2859
2881 Examples:
2860 Examples:
2882
2861
2883 - forget newly-added binary files::
2862 - forget newly-added binary files::
2884
2863
2885 hg forget "set:added() and binary()"
2864 hg forget "set:added() and binary()"
2886
2865
2887 - forget files that would be excluded by .hgignore::
2866 - forget files that would be excluded by .hgignore::
2888
2867
2889 hg forget "set:hgignore()"
2868 hg forget "set:hgignore()"
2890
2869
2891 Returns 0 on success.
2870 Returns 0 on success.
2892 """
2871 """
2893
2872
2894 opts = pycompat.byteskwargs(opts)
2873 opts = pycompat.byteskwargs(opts)
2895 if not pats:
2874 if not pats:
2896 raise error.InputError(_(b'no files specified'))
2875 raise error.InputError(_(b'no files specified'))
2897
2876
2898 m = scmutil.match(repo[None], pats, opts)
2877 m = scmutil.match(repo[None], pats, opts)
2899 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2878 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2900 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2879 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2901 rejected = cmdutil.forget(
2880 rejected = cmdutil.forget(
2902 ui,
2881 ui,
2903 repo,
2882 repo,
2904 m,
2883 m,
2905 prefix=b"",
2884 prefix=b"",
2906 uipathfn=uipathfn,
2885 uipathfn=uipathfn,
2907 explicitonly=False,
2886 explicitonly=False,
2908 dryrun=dryrun,
2887 dryrun=dryrun,
2909 interactive=interactive,
2888 interactive=interactive,
2910 )[0]
2889 )[0]
2911 return rejected and 1 or 0
2890 return rejected and 1 or 0
2912
2891
2913
2892
2914 @command(
2893 @command(
2915 b'graft',
2894 b'graft',
2916 [
2895 [
2917 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2896 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2918 (
2897 (
2919 b'',
2898 b'',
2920 b'base',
2899 b'base',
2921 b'',
2900 b'',
2922 _(b'base revision when doing the graft merge (ADVANCED)'),
2901 _(b'base revision when doing the graft merge (ADVANCED)'),
2923 _(b'REV'),
2902 _(b'REV'),
2924 ),
2903 ),
2925 (b'c', b'continue', False, _(b'resume interrupted graft')),
2904 (b'c', b'continue', False, _(b'resume interrupted graft')),
2926 (b'', b'stop', False, _(b'stop interrupted graft')),
2905 (b'', b'stop', False, _(b'stop interrupted graft')),
2927 (b'', b'abort', False, _(b'abort interrupted graft')),
2906 (b'', b'abort', False, _(b'abort interrupted graft')),
2928 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2907 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2929 (b'', b'log', None, _(b'append graft info to log message')),
2908 (b'', b'log', None, _(b'append graft info to log message')),
2930 (
2909 (
2931 b'',
2910 b'',
2932 b'no-commit',
2911 b'no-commit',
2933 None,
2912 None,
2934 _(b"don't commit, just apply the changes in working directory"),
2913 _(b"don't commit, just apply the changes in working directory"),
2935 ),
2914 ),
2936 (b'f', b'force', False, _(b'force graft')),
2915 (b'f', b'force', False, _(b'force graft')),
2937 (
2916 (
2938 b'D',
2917 b'D',
2939 b'currentdate',
2918 b'currentdate',
2940 False,
2919 False,
2941 _(b'record the current date as commit date'),
2920 _(b'record the current date as commit date'),
2942 ),
2921 ),
2943 (
2922 (
2944 b'U',
2923 b'U',
2945 b'currentuser',
2924 b'currentuser',
2946 False,
2925 False,
2947 _(b'record the current user as committer'),
2926 _(b'record the current user as committer'),
2948 ),
2927 ),
2949 ]
2928 ]
2950 + commitopts2
2929 + commitopts2
2951 + mergetoolopts
2930 + mergetoolopts
2952 + dryrunopts,
2931 + dryrunopts,
2953 _(b'[OPTION]... [-r REV]... REV...'),
2932 _(b'[OPTION]... [-r REV]... REV...'),
2954 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2933 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2955 )
2934 )
2956 def graft(ui, repo, *revs, **opts):
2935 def graft(ui, repo, *revs, **opts):
2957 """copy changes from other branches onto the current branch
2936 """copy changes from other branches onto the current branch
2958
2937
2959 This command uses Mercurial's merge logic to copy individual
2938 This command uses Mercurial's merge logic to copy individual
2960 changes from other branches without merging branches in the
2939 changes from other branches without merging branches in the
2961 history graph. This is sometimes known as 'backporting' or
2940 history graph. This is sometimes known as 'backporting' or
2962 'cherry-picking'. By default, graft will copy user, date, and
2941 'cherry-picking'. By default, graft will copy user, date, and
2963 description from the source changesets.
2942 description from the source changesets.
2964
2943
2965 Changesets that are ancestors of the current revision, that have
2944 Changesets that are ancestors of the current revision, that have
2966 already been grafted, or that are merges will be skipped.
2945 already been grafted, or that are merges will be skipped.
2967
2946
2968 If --log is specified, log messages will have a comment appended
2947 If --log is specified, log messages will have a comment appended
2969 of the form::
2948 of the form::
2970
2949
2971 (grafted from CHANGESETHASH)
2950 (grafted from CHANGESETHASH)
2972
2951
2973 If --force is specified, revisions will be grafted even if they
2952 If --force is specified, revisions will be grafted even if they
2974 are already ancestors of, or have been grafted to, the destination.
2953 are already ancestors of, or have been grafted to, the destination.
2975 This is useful when the revisions have since been backed out.
2954 This is useful when the revisions have since been backed out.
2976
2955
2977 If a graft merge results in conflicts, the graft process is
2956 If a graft merge results in conflicts, the graft process is
2978 interrupted so that the current merge can be manually resolved.
2957 interrupted so that the current merge can be manually resolved.
2979 Once all conflicts are addressed, the graft process can be
2958 Once all conflicts are addressed, the graft process can be
2980 continued with the -c/--continue option.
2959 continued with the -c/--continue option.
2981
2960
2982 The -c/--continue option reapplies all the earlier options.
2961 The -c/--continue option reapplies all the earlier options.
2983
2962
2984 .. container:: verbose
2963 .. container:: verbose
2985
2964
2986 The --base option exposes more of how graft internally uses merge with a
2965 The --base option exposes more of how graft internally uses merge with a
2987 custom base revision. --base can be used to specify another ancestor than
2966 custom base revision. --base can be used to specify another ancestor than
2988 the first and only parent.
2967 the first and only parent.
2989
2968
2990 The command::
2969 The command::
2991
2970
2992 hg graft -r 345 --base 234
2971 hg graft -r 345 --base 234
2993
2972
2994 is thus pretty much the same as::
2973 is thus pretty much the same as::
2995
2974
2996 hg diff --from 234 --to 345 | hg import
2975 hg diff --from 234 --to 345 | hg import
2997
2976
2998 but using merge to resolve conflicts and track moved files.
2977 but using merge to resolve conflicts and track moved files.
2999
2978
3000 The result of a merge can thus be backported as a single commit by
2979 The result of a merge can thus be backported as a single commit by
3001 specifying one of the merge parents as base, and thus effectively
2980 specifying one of the merge parents as base, and thus effectively
3002 grafting the changes from the other side.
2981 grafting the changes from the other side.
3003
2982
3004 It is also possible to collapse multiple changesets and clean up history
2983 It is also possible to collapse multiple changesets and clean up history
3005 by specifying another ancestor as base, much like rebase --collapse
2984 by specifying another ancestor as base, much like rebase --collapse
3006 --keep.
2985 --keep.
3007
2986
3008 The commit message can be tweaked after the fact using commit --amend .
2987 The commit message can be tweaked after the fact using commit --amend .
3009
2988
3010 For using non-ancestors as the base to backout changes, see the backout
2989 For using non-ancestors as the base to backout changes, see the backout
3011 command and the hidden --parent option.
2990 command and the hidden --parent option.
3012
2991
3013 .. container:: verbose
2992 .. container:: verbose
3014
2993
3015 Examples:
2994 Examples:
3016
2995
3017 - copy a single change to the stable branch and edit its description::
2996 - copy a single change to the stable branch and edit its description::
3018
2997
3019 hg update stable
2998 hg update stable
3020 hg graft --edit 9393
2999 hg graft --edit 9393
3021
3000
3022 - graft a range of changesets with one exception, updating dates::
3001 - graft a range of changesets with one exception, updating dates::
3023
3002
3024 hg graft -D "2085::2093 and not 2091"
3003 hg graft -D "2085::2093 and not 2091"
3025
3004
3026 - continue a graft after resolving conflicts::
3005 - continue a graft after resolving conflicts::
3027
3006
3028 hg graft -c
3007 hg graft -c
3029
3008
3030 - show the source of a grafted changeset::
3009 - show the source of a grafted changeset::
3031
3010
3032 hg log --debug -r .
3011 hg log --debug -r .
3033
3012
3034 - show revisions sorted by date::
3013 - show revisions sorted by date::
3035
3014
3036 hg log -r "sort(all(), date)"
3015 hg log -r "sort(all(), date)"
3037
3016
3038 - backport the result of a merge as a single commit::
3017 - backport the result of a merge as a single commit::
3039
3018
3040 hg graft -r 123 --base 123^
3019 hg graft -r 123 --base 123^
3041
3020
3042 - land a feature branch as one changeset::
3021 - land a feature branch as one changeset::
3043
3022
3044 hg up -cr default
3023 hg up -cr default
3045 hg graft -r featureX --base "ancestor('featureX', 'default')"
3024 hg graft -r featureX --base "ancestor('featureX', 'default')"
3046
3025
3047 See :hg:`help revisions` for more about specifying revisions.
3026 See :hg:`help revisions` for more about specifying revisions.
3048
3027
3049 Returns 0 on successful completion, 1 if there are unresolved files.
3028 Returns 0 on successful completion, 1 if there are unresolved files.
3050 """
3029 """
3051 with repo.wlock():
3030 with repo.wlock():
3052 return _dograft(ui, repo, *revs, **opts)
3031 return _dograft(ui, repo, *revs, **opts)
3053
3032
3054
3033
3055 def _dograft(ui, repo, *revs, **opts):
3034 def _dograft(ui, repo, *revs, **opts):
3056 opts = pycompat.byteskwargs(opts)
3035 opts = pycompat.byteskwargs(opts)
3057 if revs and opts.get(b'rev'):
3036 if revs and opts.get(b'rev'):
3058 ui.warn(
3037 ui.warn(
3059 _(
3038 _(
3060 b'warning: inconsistent use of --rev might give unexpected '
3039 b'warning: inconsistent use of --rev might give unexpected '
3061 b'revision ordering!\n'
3040 b'revision ordering!\n'
3062 )
3041 )
3063 )
3042 )
3064
3043
3065 revs = list(revs)
3044 revs = list(revs)
3066 revs.extend(opts.get(b'rev'))
3045 revs.extend(opts.get(b'rev'))
3067 # a dict of data to be stored in state file
3046 # a dict of data to be stored in state file
3068 statedata = {}
3047 statedata = {}
3069 # list of new nodes created by ongoing graft
3048 # list of new nodes created by ongoing graft
3070 statedata[b'newnodes'] = []
3049 statedata[b'newnodes'] = []
3071
3050
3072 cmdutil.resolvecommitoptions(ui, opts)
3051 cmdutil.resolvecommitoptions(ui, opts)
3073
3052
3074 editor = cmdutil.getcommiteditor(
3053 editor = cmdutil.getcommiteditor(
3075 editform=b'graft', **pycompat.strkwargs(opts)
3054 editform=b'graft', **pycompat.strkwargs(opts)
3076 )
3055 )
3077
3056
3078 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3057 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3079
3058
3080 cont = False
3059 cont = False
3081 if opts.get(b'no_commit'):
3060 if opts.get(b'no_commit'):
3082 cmdutil.check_incompatible_arguments(
3061 cmdutil.check_incompatible_arguments(
3083 opts,
3062 opts,
3084 b'no_commit',
3063 b'no_commit',
3085 [b'edit', b'currentuser', b'currentdate', b'log'],
3064 [b'edit', b'currentuser', b'currentdate', b'log'],
3086 )
3065 )
3087
3066
3088 graftstate = statemod.cmdstate(repo, b'graftstate')
3067 graftstate = statemod.cmdstate(repo, b'graftstate')
3089
3068
3090 if opts.get(b'stop'):
3069 if opts.get(b'stop'):
3091 cmdutil.check_incompatible_arguments(
3070 cmdutil.check_incompatible_arguments(
3092 opts,
3071 opts,
3093 b'stop',
3072 b'stop',
3094 [
3073 [
3095 b'edit',
3074 b'edit',
3096 b'log',
3075 b'log',
3097 b'user',
3076 b'user',
3098 b'date',
3077 b'date',
3099 b'currentdate',
3078 b'currentdate',
3100 b'currentuser',
3079 b'currentuser',
3101 b'rev',
3080 b'rev',
3102 ],
3081 ],
3103 )
3082 )
3104 return _stopgraft(ui, repo, graftstate)
3083 return _stopgraft(ui, repo, graftstate)
3105 elif opts.get(b'abort'):
3084 elif opts.get(b'abort'):
3106 cmdutil.check_incompatible_arguments(
3085 cmdutil.check_incompatible_arguments(
3107 opts,
3086 opts,
3108 b'abort',
3087 b'abort',
3109 [
3088 [
3110 b'edit',
3089 b'edit',
3111 b'log',
3090 b'log',
3112 b'user',
3091 b'user',
3113 b'date',
3092 b'date',
3114 b'currentdate',
3093 b'currentdate',
3115 b'currentuser',
3094 b'currentuser',
3116 b'rev',
3095 b'rev',
3117 ],
3096 ],
3118 )
3097 )
3119 return cmdutil.abortgraft(ui, repo, graftstate)
3098 return cmdutil.abortgraft(ui, repo, graftstate)
3120 elif opts.get(b'continue'):
3099 elif opts.get(b'continue'):
3121 cont = True
3100 cont = True
3122 if revs:
3101 if revs:
3123 raise error.InputError(_(b"can't specify --continue and revisions"))
3102 raise error.InputError(_(b"can't specify --continue and revisions"))
3124 # read in unfinished revisions
3103 # read in unfinished revisions
3125 if graftstate.exists():
3104 if graftstate.exists():
3126 statedata = cmdutil.readgraftstate(repo, graftstate)
3105 statedata = cmdutil.readgraftstate(repo, graftstate)
3127 if statedata.get(b'date'):
3106 if statedata.get(b'date'):
3128 opts[b'date'] = statedata[b'date']
3107 opts[b'date'] = statedata[b'date']
3129 if statedata.get(b'user'):
3108 if statedata.get(b'user'):
3130 opts[b'user'] = statedata[b'user']
3109 opts[b'user'] = statedata[b'user']
3131 if statedata.get(b'log'):
3110 if statedata.get(b'log'):
3132 opts[b'log'] = True
3111 opts[b'log'] = True
3133 if statedata.get(b'no_commit'):
3112 if statedata.get(b'no_commit'):
3134 opts[b'no_commit'] = statedata.get(b'no_commit')
3113 opts[b'no_commit'] = statedata.get(b'no_commit')
3135 if statedata.get(b'base'):
3114 if statedata.get(b'base'):
3136 opts[b'base'] = statedata.get(b'base')
3115 opts[b'base'] = statedata.get(b'base')
3137 nodes = statedata[b'nodes']
3116 nodes = statedata[b'nodes']
3138 revs = [repo[node].rev() for node in nodes]
3117 revs = [repo[node].rev() for node in nodes]
3139 else:
3118 else:
3140 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3119 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3141 else:
3120 else:
3142 if not revs:
3121 if not revs:
3143 raise error.InputError(_(b'no revisions specified'))
3122 raise error.InputError(_(b'no revisions specified'))
3144 cmdutil.checkunfinished(repo)
3123 cmdutil.checkunfinished(repo)
3145 cmdutil.bailifchanged(repo)
3124 cmdutil.bailifchanged(repo)
3146 revs = scmutil.revrange(repo, revs)
3125 revs = scmutil.revrange(repo, revs)
3147
3126
3148 skipped = set()
3127 skipped = set()
3149 basectx = None
3128 basectx = None
3150 if opts.get(b'base'):
3129 if opts.get(b'base'):
3151 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3130 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3152 if basectx is None:
3131 if basectx is None:
3153 # check for merges
3132 # check for merges
3154 for rev in repo.revs(b'%ld and merge()', revs):
3133 for rev in repo.revs(b'%ld and merge()', revs):
3155 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3134 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3156 skipped.add(rev)
3135 skipped.add(rev)
3157 revs = [r for r in revs if r not in skipped]
3136 revs = [r for r in revs if r not in skipped]
3158 if not revs:
3137 if not revs:
3159 return -1
3138 return -1
3160 if basectx is not None and len(revs) != 1:
3139 if basectx is not None and len(revs) != 1:
3161 raise error.InputError(_(b'only one revision allowed with --base '))
3140 raise error.InputError(_(b'only one revision allowed with --base '))
3162
3141
3163 # Don't check in the --continue case, in effect retaining --force across
3142 # Don't check in the --continue case, in effect retaining --force across
3164 # --continues. That's because without --force, any revisions we decided to
3143 # --continues. That's because without --force, any revisions we decided to
3165 # skip would have been filtered out here, so they wouldn't have made their
3144 # skip would have been filtered out here, so they wouldn't have made their
3166 # way to the graftstate. With --force, any revisions we would have otherwise
3145 # way to the graftstate. With --force, any revisions we would have otherwise
3167 # skipped would not have been filtered out, and if they hadn't been applied
3146 # skipped would not have been filtered out, and if they hadn't been applied
3168 # already, they'd have been in the graftstate.
3147 # already, they'd have been in the graftstate.
3169 if not (cont or opts.get(b'force')) and basectx is None:
3148 if not (cont or opts.get(b'force')) and basectx is None:
3170 # check for ancestors of dest branch
3149 # check for ancestors of dest branch
3171 ancestors = repo.revs(b'%ld & (::.)', revs)
3150 ancestors = repo.revs(b'%ld & (::.)', revs)
3172 for rev in ancestors:
3151 for rev in ancestors:
3173 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3152 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3174
3153
3175 revs = [r for r in revs if r not in ancestors]
3154 revs = [r for r in revs if r not in ancestors]
3176
3155
3177 if not revs:
3156 if not revs:
3178 return -1
3157 return -1
3179
3158
3180 # analyze revs for earlier grafts
3159 # analyze revs for earlier grafts
3181 ids = {}
3160 ids = {}
3182 for ctx in repo.set(b"%ld", revs):
3161 for ctx in repo.set(b"%ld", revs):
3183 ids[ctx.hex()] = ctx.rev()
3162 ids[ctx.hex()] = ctx.rev()
3184 n = ctx.extra().get(b'source')
3163 n = ctx.extra().get(b'source')
3185 if n:
3164 if n:
3186 ids[n] = ctx.rev()
3165 ids[n] = ctx.rev()
3187
3166
3188 # check ancestors for earlier grafts
3167 # check ancestors for earlier grafts
3189 ui.debug(b'scanning for duplicate grafts\n')
3168 ui.debug(b'scanning for duplicate grafts\n')
3190
3169
3191 # The only changesets we can be sure doesn't contain grafts of any
3170 # The only changesets we can be sure doesn't contain grafts of any
3192 # revs, are the ones that are common ancestors of *all* revs:
3171 # revs, are the ones that are common ancestors of *all* revs:
3193 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3172 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3194 ctx = repo[rev]
3173 ctx = repo[rev]
3195 n = ctx.extra().get(b'source')
3174 n = ctx.extra().get(b'source')
3196 if n in ids:
3175 if n in ids:
3197 try:
3176 try:
3198 r = repo[n].rev()
3177 r = repo[n].rev()
3199 except error.RepoLookupError:
3178 except error.RepoLookupError:
3200 r = None
3179 r = None
3201 if r in revs:
3180 if r in revs:
3202 ui.warn(
3181 ui.warn(
3203 _(
3182 _(
3204 b'skipping revision %d:%s '
3183 b'skipping revision %d:%s '
3205 b'(already grafted to %d:%s)\n'
3184 b'(already grafted to %d:%s)\n'
3206 )
3185 )
3207 % (r, repo[r], rev, ctx)
3186 % (r, repo[r], rev, ctx)
3208 )
3187 )
3209 revs.remove(r)
3188 revs.remove(r)
3210 elif ids[n] in revs:
3189 elif ids[n] in revs:
3211 if r is None:
3190 if r is None:
3212 ui.warn(
3191 ui.warn(
3213 _(
3192 _(
3214 b'skipping already grafted revision %d:%s '
3193 b'skipping already grafted revision %d:%s '
3215 b'(%d:%s also has unknown origin %s)\n'
3194 b'(%d:%s also has unknown origin %s)\n'
3216 )
3195 )
3217 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3196 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3218 )
3197 )
3219 else:
3198 else:
3220 ui.warn(
3199 ui.warn(
3221 _(
3200 _(
3222 b'skipping already grafted revision %d:%s '
3201 b'skipping already grafted revision %d:%s '
3223 b'(%d:%s also has origin %d:%s)\n'
3202 b'(%d:%s also has origin %d:%s)\n'
3224 )
3203 )
3225 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3204 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3226 )
3205 )
3227 revs.remove(ids[n])
3206 revs.remove(ids[n])
3228 elif ctx.hex() in ids:
3207 elif ctx.hex() in ids:
3229 r = ids[ctx.hex()]
3208 r = ids[ctx.hex()]
3230 if r in revs:
3209 if r in revs:
3231 ui.warn(
3210 ui.warn(
3232 _(
3211 _(
3233 b'skipping already grafted revision %d:%s '
3212 b'skipping already grafted revision %d:%s '
3234 b'(was grafted from %d:%s)\n'
3213 b'(was grafted from %d:%s)\n'
3235 )
3214 )
3236 % (r, repo[r], rev, ctx)
3215 % (r, repo[r], rev, ctx)
3237 )
3216 )
3238 revs.remove(r)
3217 revs.remove(r)
3239 if not revs:
3218 if not revs:
3240 return -1
3219 return -1
3241
3220
3242 if opts.get(b'no_commit'):
3221 if opts.get(b'no_commit'):
3243 statedata[b'no_commit'] = True
3222 statedata[b'no_commit'] = True
3244 if opts.get(b'base'):
3223 if opts.get(b'base'):
3245 statedata[b'base'] = opts[b'base']
3224 statedata[b'base'] = opts[b'base']
3246 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3225 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3247 desc = b'%d:%s "%s"' % (
3226 desc = b'%d:%s "%s"' % (
3248 ctx.rev(),
3227 ctx.rev(),
3249 ctx,
3228 ctx,
3250 ctx.description().split(b'\n', 1)[0],
3229 ctx.description().split(b'\n', 1)[0],
3251 )
3230 )
3252 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3231 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3253 if names:
3232 if names:
3254 desc += b' (%s)' % b' '.join(names)
3233 desc += b' (%s)' % b' '.join(names)
3255 ui.status(_(b'grafting %s\n') % desc)
3234 ui.status(_(b'grafting %s\n') % desc)
3256 if opts.get(b'dry_run'):
3235 if opts.get(b'dry_run'):
3257 continue
3236 continue
3258
3237
3259 source = ctx.extra().get(b'source')
3238 source = ctx.extra().get(b'source')
3260 extra = {}
3239 extra = {}
3261 if source:
3240 if source:
3262 extra[b'source'] = source
3241 extra[b'source'] = source
3263 extra[b'intermediate-source'] = ctx.hex()
3242 extra[b'intermediate-source'] = ctx.hex()
3264 else:
3243 else:
3265 extra[b'source'] = ctx.hex()
3244 extra[b'source'] = ctx.hex()
3266 user = ctx.user()
3245 user = ctx.user()
3267 if opts.get(b'user'):
3246 if opts.get(b'user'):
3268 user = opts[b'user']
3247 user = opts[b'user']
3269 statedata[b'user'] = user
3248 statedata[b'user'] = user
3270 date = ctx.date()
3249 date = ctx.date()
3271 if opts.get(b'date'):
3250 if opts.get(b'date'):
3272 date = opts[b'date']
3251 date = opts[b'date']
3273 statedata[b'date'] = date
3252 statedata[b'date'] = date
3274 message = ctx.description()
3253 message = ctx.description()
3275 if opts.get(b'log'):
3254 if opts.get(b'log'):
3276 message += b'\n(grafted from %s)' % ctx.hex()
3255 message += b'\n(grafted from %s)' % ctx.hex()
3277 statedata[b'log'] = True
3256 statedata[b'log'] = True
3278
3257
3279 # we don't merge the first commit when continuing
3258 # we don't merge the first commit when continuing
3280 if not cont:
3259 if not cont:
3281 # perform the graft merge with p1(rev) as 'ancestor'
3260 # perform the graft merge with p1(rev) as 'ancestor'
3282 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3261 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3283 base = ctx.p1() if basectx is None else basectx
3262 base = ctx.p1() if basectx is None else basectx
3284 with ui.configoverride(overrides, b'graft'):
3263 with ui.configoverride(overrides, b'graft'):
3285 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3264 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3286 # report any conflicts
3265 # report any conflicts
3287 if stats.unresolvedcount > 0:
3266 if stats.unresolvedcount > 0:
3288 # write out state for --continue
3267 # write out state for --continue
3289 nodes = [repo[rev].hex() for rev in revs[pos:]]
3268 nodes = [repo[rev].hex() for rev in revs[pos:]]
3290 statedata[b'nodes'] = nodes
3269 statedata[b'nodes'] = nodes
3291 stateversion = 1
3270 stateversion = 1
3292 graftstate.save(stateversion, statedata)
3271 graftstate.save(stateversion, statedata)
3293 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3272 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3294 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3273 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3295 return 1
3274 return 1
3296 else:
3275 else:
3297 cont = False
3276 cont = False
3298
3277
3299 # commit if --no-commit is false
3278 # commit if --no-commit is false
3300 if not opts.get(b'no_commit'):
3279 if not opts.get(b'no_commit'):
3301 node = repo.commit(
3280 node = repo.commit(
3302 text=message, user=user, date=date, extra=extra, editor=editor
3281 text=message, user=user, date=date, extra=extra, editor=editor
3303 )
3282 )
3304 if node is None:
3283 if node is None:
3305 ui.warn(
3284 ui.warn(
3306 _(b'note: graft of %d:%s created no changes to commit\n')
3285 _(b'note: graft of %d:%s created no changes to commit\n')
3307 % (ctx.rev(), ctx)
3286 % (ctx.rev(), ctx)
3308 )
3287 )
3309 # checking that newnodes exist because old state files won't have it
3288 # checking that newnodes exist because old state files won't have it
3310 elif statedata.get(b'newnodes') is not None:
3289 elif statedata.get(b'newnodes') is not None:
3311 statedata[b'newnodes'].append(node)
3290 statedata[b'newnodes'].append(node)
3312
3291
3313 # remove state when we complete successfully
3292 # remove state when we complete successfully
3314 if not opts.get(b'dry_run'):
3293 if not opts.get(b'dry_run'):
3315 graftstate.delete()
3294 graftstate.delete()
3316
3295
3317 return 0
3296 return 0
3318
3297
3319
3298
3320 def _stopgraft(ui, repo, graftstate):
3299 def _stopgraft(ui, repo, graftstate):
3321 """stop the interrupted graft"""
3300 """stop the interrupted graft"""
3322 if not graftstate.exists():
3301 if not graftstate.exists():
3323 raise error.StateError(_(b"no interrupted graft found"))
3302 raise error.StateError(_(b"no interrupted graft found"))
3324 pctx = repo[b'.']
3303 pctx = repo[b'.']
3325 mergemod.clean_update(pctx)
3304 mergemod.clean_update(pctx)
3326 graftstate.delete()
3305 graftstate.delete()
3327 ui.status(_(b"stopped the interrupted graft\n"))
3306 ui.status(_(b"stopped the interrupted graft\n"))
3328 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3307 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3329 return 0
3308 return 0
3330
3309
3331
3310
3332 statemod.addunfinished(
3311 statemod.addunfinished(
3333 b'graft',
3312 b'graft',
3334 fname=b'graftstate',
3313 fname=b'graftstate',
3335 clearable=True,
3314 clearable=True,
3336 stopflag=True,
3315 stopflag=True,
3337 continueflag=True,
3316 continueflag=True,
3338 abortfunc=cmdutil.hgabortgraft,
3317 abortfunc=cmdutil.hgabortgraft,
3339 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3318 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3340 )
3319 )
3341
3320
3342
3321
3343 @command(
3322 @command(
3344 b'grep',
3323 b'grep',
3345 [
3324 [
3346 (b'0', b'print0', None, _(b'end fields with NUL')),
3325 (b'0', b'print0', None, _(b'end fields with NUL')),
3347 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3326 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3348 (
3327 (
3349 b'',
3328 b'',
3350 b'diff',
3329 b'diff',
3351 None,
3330 None,
3352 _(
3331 _(
3353 b'search revision differences for when the pattern was added '
3332 b'search revision differences for when the pattern was added '
3354 b'or removed'
3333 b'or removed'
3355 ),
3334 ),
3356 ),
3335 ),
3357 (b'a', b'text', None, _(b'treat all files as text')),
3336 (b'a', b'text', None, _(b'treat all files as text')),
3358 (
3337 (
3359 b'f',
3338 b'f',
3360 b'follow',
3339 b'follow',
3361 None,
3340 None,
3362 _(
3341 _(
3363 b'follow changeset history,'
3342 b'follow changeset history,'
3364 b' or file history across copies and renames'
3343 b' or file history across copies and renames'
3365 ),
3344 ),
3366 ),
3345 ),
3367 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3346 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3368 (
3347 (
3369 b'l',
3348 b'l',
3370 b'files-with-matches',
3349 b'files-with-matches',
3371 None,
3350 None,
3372 _(b'print only filenames and revisions that match'),
3351 _(b'print only filenames and revisions that match'),
3373 ),
3352 ),
3374 (b'n', b'line-number', None, _(b'print matching line numbers')),
3353 (b'n', b'line-number', None, _(b'print matching line numbers')),
3375 (
3354 (
3376 b'r',
3355 b'r',
3377 b'rev',
3356 b'rev',
3378 [],
3357 [],
3379 _(b'search files changed within revision range'),
3358 _(b'search files changed within revision range'),
3380 _(b'REV'),
3359 _(b'REV'),
3381 ),
3360 ),
3382 (
3361 (
3383 b'',
3362 b'',
3384 b'all-files',
3363 b'all-files',
3385 None,
3364 None,
3386 _(
3365 _(
3387 b'include all files in the changeset while grepping (DEPRECATED)'
3366 b'include all files in the changeset while grepping (DEPRECATED)'
3388 ),
3367 ),
3389 ),
3368 ),
3390 (b'u', b'user', None, _(b'list the author (long with -v)')),
3369 (b'u', b'user', None, _(b'list the author (long with -v)')),
3391 (b'd', b'date', None, _(b'list the date (short with -q)')),
3370 (b'd', b'date', None, _(b'list the date (short with -q)')),
3392 ]
3371 ]
3393 + formatteropts
3372 + formatteropts
3394 + walkopts,
3373 + walkopts,
3395 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3374 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3396 helpcategory=command.CATEGORY_FILE_CONTENTS,
3375 helpcategory=command.CATEGORY_FILE_CONTENTS,
3397 inferrepo=True,
3376 inferrepo=True,
3398 intents={INTENT_READONLY},
3377 intents={INTENT_READONLY},
3399 )
3378 )
3400 def grep(ui, repo, pattern, *pats, **opts):
3379 def grep(ui, repo, pattern, *pats, **opts):
3401 """search for a pattern in specified files
3380 """search for a pattern in specified files
3402
3381
3403 Search the working directory or revision history for a regular
3382 Search the working directory or revision history for a regular
3404 expression in the specified files for the entire repository.
3383 expression in the specified files for the entire repository.
3405
3384
3406 By default, grep searches the repository files in the working
3385 By default, grep searches the repository files in the working
3407 directory and prints the files where it finds a match. To specify
3386 directory and prints the files where it finds a match. To specify
3408 historical revisions instead of the working directory, use the
3387 historical revisions instead of the working directory, use the
3409 --rev flag.
3388 --rev flag.
3410
3389
3411 To search instead historical revision differences that contains a
3390 To search instead historical revision differences that contains a
3412 change in match status ("-" for a match that becomes a non-match,
3391 change in match status ("-" for a match that becomes a non-match,
3413 or "+" for a non-match that becomes a match), use the --diff flag.
3392 or "+" for a non-match that becomes a match), use the --diff flag.
3414
3393
3415 PATTERN can be any Python (roughly Perl-compatible) regular
3394 PATTERN can be any Python (roughly Perl-compatible) regular
3416 expression.
3395 expression.
3417
3396
3418 If no FILEs are specified and the --rev flag isn't supplied, all
3397 If no FILEs are specified and the --rev flag isn't supplied, all
3419 files in the working directory are searched. When using the --rev
3398 files in the working directory are searched. When using the --rev
3420 flag and specifying FILEs, use the --follow argument to also
3399 flag and specifying FILEs, use the --follow argument to also
3421 follow the specified FILEs across renames and copies.
3400 follow the specified FILEs across renames and copies.
3422
3401
3423 .. container:: verbose
3402 .. container:: verbose
3424
3403
3425 Template:
3404 Template:
3426
3405
3427 The following keywords are supported in addition to the common template
3406 The following keywords are supported in addition to the common template
3428 keywords and functions. See also :hg:`help templates`.
3407 keywords and functions. See also :hg:`help templates`.
3429
3408
3430 :change: String. Character denoting insertion ``+`` or removal ``-``.
3409 :change: String. Character denoting insertion ``+`` or removal ``-``.
3431 Available if ``--diff`` is specified.
3410 Available if ``--diff`` is specified.
3432 :lineno: Integer. Line number of the match.
3411 :lineno: Integer. Line number of the match.
3433 :path: String. Repository-absolute path of the file.
3412 :path: String. Repository-absolute path of the file.
3434 :texts: List of text chunks.
3413 :texts: List of text chunks.
3435
3414
3436 And each entry of ``{texts}`` provides the following sub-keywords.
3415 And each entry of ``{texts}`` provides the following sub-keywords.
3437
3416
3438 :matched: Boolean. True if the chunk matches the specified pattern.
3417 :matched: Boolean. True if the chunk matches the specified pattern.
3439 :text: String. Chunk content.
3418 :text: String. Chunk content.
3440
3419
3441 See :hg:`help templates.operators` for the list expansion syntax.
3420 See :hg:`help templates.operators` for the list expansion syntax.
3442
3421
3443 Returns 0 if a match is found, 1 otherwise.
3422 Returns 0 if a match is found, 1 otherwise.
3444
3423
3445 """
3424 """
3446 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3425 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3447 opts = pycompat.byteskwargs(opts)
3426 opts = pycompat.byteskwargs(opts)
3448 diff = opts.get(b'all') or opts.get(b'diff')
3427 diff = opts.get(b'all') or opts.get(b'diff')
3449 follow = opts.get(b'follow')
3428 follow = opts.get(b'follow')
3450 if opts.get(b'all_files') is None and not diff:
3429 if opts.get(b'all_files') is None and not diff:
3451 opts[b'all_files'] = True
3430 opts[b'all_files'] = True
3452 plaingrep = (
3431 plaingrep = (
3453 opts.get(b'all_files')
3432 opts.get(b'all_files')
3454 and not opts.get(b'rev')
3433 and not opts.get(b'rev')
3455 and not opts.get(b'follow')
3434 and not opts.get(b'follow')
3456 )
3435 )
3457 all_files = opts.get(b'all_files')
3436 all_files = opts.get(b'all_files')
3458 if plaingrep:
3437 if plaingrep:
3459 opts[b'rev'] = [b'wdir()']
3438 opts[b'rev'] = [b'wdir()']
3460
3439
3461 reflags = re.M
3440 reflags = re.M
3462 if opts.get(b'ignore_case'):
3441 if opts.get(b'ignore_case'):
3463 reflags |= re.I
3442 reflags |= re.I
3464 try:
3443 try:
3465 regexp = util.re.compile(pattern, reflags)
3444 regexp = util.re.compile(pattern, reflags)
3466 except re.error as inst:
3445 except re.error as inst:
3467 ui.warn(
3446 ui.warn(
3468 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3447 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3469 )
3448 )
3470 return 1
3449 return 1
3471 sep, eol = b':', b'\n'
3450 sep, eol = b':', b'\n'
3472 if opts.get(b'print0'):
3451 if opts.get(b'print0'):
3473 sep = eol = b'\0'
3452 sep = eol = b'\0'
3474
3453
3475 searcher = grepmod.grepsearcher(
3454 searcher = grepmod.grepsearcher(
3476 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3455 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3477 )
3456 )
3478
3457
3479 getfile = searcher._getfile
3458 getfile = searcher._getfile
3480
3459
3481 uipathfn = scmutil.getuipathfn(repo)
3460 uipathfn = scmutil.getuipathfn(repo)
3482
3461
3483 def display(fm, fn, ctx, pstates, states):
3462 def display(fm, fn, ctx, pstates, states):
3484 rev = scmutil.intrev(ctx)
3463 rev = scmutil.intrev(ctx)
3485 if fm.isplain():
3464 if fm.isplain():
3486 formatuser = ui.shortuser
3465 formatuser = ui.shortuser
3487 else:
3466 else:
3488 formatuser = pycompat.bytestr
3467 formatuser = pycompat.bytestr
3489 if ui.quiet:
3468 if ui.quiet:
3490 datefmt = b'%Y-%m-%d'
3469 datefmt = b'%Y-%m-%d'
3491 else:
3470 else:
3492 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3471 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3493 found = False
3472 found = False
3494
3473
3495 @util.cachefunc
3474 @util.cachefunc
3496 def binary():
3475 def binary():
3497 flog = getfile(fn)
3476 flog = getfile(fn)
3498 try:
3477 try:
3499 return stringutil.binary(flog.read(ctx.filenode(fn)))
3478 return stringutil.binary(flog.read(ctx.filenode(fn)))
3500 except error.WdirUnsupported:
3479 except error.WdirUnsupported:
3501 return ctx[fn].isbinary()
3480 return ctx[fn].isbinary()
3502
3481
3503 fieldnamemap = {b'linenumber': b'lineno'}
3482 fieldnamemap = {b'linenumber': b'lineno'}
3504 if diff:
3483 if diff:
3505 iter = grepmod.difflinestates(pstates, states)
3484 iter = grepmod.difflinestates(pstates, states)
3506 else:
3485 else:
3507 iter = [(b'', l) for l in states]
3486 iter = [(b'', l) for l in states]
3508 for change, l in iter:
3487 for change, l in iter:
3509 fm.startitem()
3488 fm.startitem()
3510 fm.context(ctx=ctx)
3489 fm.context(ctx=ctx)
3511 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3490 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3512 fm.plain(uipathfn(fn), label=b'grep.filename')
3491 fm.plain(uipathfn(fn), label=b'grep.filename')
3513
3492
3514 cols = [
3493 cols = [
3515 (b'rev', b'%d', rev, not plaingrep, b''),
3494 (b'rev', b'%d', rev, not plaingrep, b''),
3516 (
3495 (
3517 b'linenumber',
3496 b'linenumber',
3518 b'%d',
3497 b'%d',
3519 l.linenum,
3498 l.linenum,
3520 opts.get(b'line_number'),
3499 opts.get(b'line_number'),
3521 b'',
3500 b'',
3522 ),
3501 ),
3523 ]
3502 ]
3524 if diff:
3503 if diff:
3525 cols.append(
3504 cols.append(
3526 (
3505 (
3527 b'change',
3506 b'change',
3528 b'%s',
3507 b'%s',
3529 change,
3508 change,
3530 True,
3509 True,
3531 b'grep.inserted '
3510 b'grep.inserted '
3532 if change == b'+'
3511 if change == b'+'
3533 else b'grep.deleted ',
3512 else b'grep.deleted ',
3534 )
3513 )
3535 )
3514 )
3536 cols.extend(
3515 cols.extend(
3537 [
3516 [
3538 (
3517 (
3539 b'user',
3518 b'user',
3540 b'%s',
3519 b'%s',
3541 formatuser(ctx.user()),
3520 formatuser(ctx.user()),
3542 opts.get(b'user'),
3521 opts.get(b'user'),
3543 b'',
3522 b'',
3544 ),
3523 ),
3545 (
3524 (
3546 b'date',
3525 b'date',
3547 b'%s',
3526 b'%s',
3548 fm.formatdate(ctx.date(), datefmt),
3527 fm.formatdate(ctx.date(), datefmt),
3549 opts.get(b'date'),
3528 opts.get(b'date'),
3550 b'',
3529 b'',
3551 ),
3530 ),
3552 ]
3531 ]
3553 )
3532 )
3554 for name, fmt, data, cond, extra_label in cols:
3533 for name, fmt, data, cond, extra_label in cols:
3555 if cond:
3534 if cond:
3556 fm.plain(sep, label=b'grep.sep')
3535 fm.plain(sep, label=b'grep.sep')
3557 field = fieldnamemap.get(name, name)
3536 field = fieldnamemap.get(name, name)
3558 label = extra_label + (b'grep.%s' % name)
3537 label = extra_label + (b'grep.%s' % name)
3559 fm.condwrite(cond, field, fmt, data, label=label)
3538 fm.condwrite(cond, field, fmt, data, label=label)
3560 if not opts.get(b'files_with_matches'):
3539 if not opts.get(b'files_with_matches'):
3561 fm.plain(sep, label=b'grep.sep')
3540 fm.plain(sep, label=b'grep.sep')
3562 if not opts.get(b'text') and binary():
3541 if not opts.get(b'text') and binary():
3563 fm.plain(_(b" Binary file matches"))
3542 fm.plain(_(b" Binary file matches"))
3564 else:
3543 else:
3565 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3544 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3566 fm.plain(eol)
3545 fm.plain(eol)
3567 found = True
3546 found = True
3568 if opts.get(b'files_with_matches'):
3547 if opts.get(b'files_with_matches'):
3569 break
3548 break
3570 return found
3549 return found
3571
3550
3572 def displaymatches(fm, l):
3551 def displaymatches(fm, l):
3573 p = 0
3552 p = 0
3574 for s, e in l.findpos(regexp):
3553 for s, e in l.findpos(regexp):
3575 if p < s:
3554 if p < s:
3576 fm.startitem()
3555 fm.startitem()
3577 fm.write(b'text', b'%s', l.line[p:s])
3556 fm.write(b'text', b'%s', l.line[p:s])
3578 fm.data(matched=False)
3557 fm.data(matched=False)
3579 fm.startitem()
3558 fm.startitem()
3580 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3559 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3581 fm.data(matched=True)
3560 fm.data(matched=True)
3582 p = e
3561 p = e
3583 if p < len(l.line):
3562 if p < len(l.line):
3584 fm.startitem()
3563 fm.startitem()
3585 fm.write(b'text', b'%s', l.line[p:])
3564 fm.write(b'text', b'%s', l.line[p:])
3586 fm.data(matched=False)
3565 fm.data(matched=False)
3587 fm.end()
3566 fm.end()
3588
3567
3589 found = False
3568 found = False
3590
3569
3591 wopts = logcmdutil.walkopts(
3570 wopts = logcmdutil.walkopts(
3592 pats=pats,
3571 pats=pats,
3593 opts=opts,
3572 opts=opts,
3594 revspec=opts[b'rev'],
3573 revspec=opts[b'rev'],
3595 include_pats=opts[b'include'],
3574 include_pats=opts[b'include'],
3596 exclude_pats=opts[b'exclude'],
3575 exclude_pats=opts[b'exclude'],
3597 follow=follow,
3576 follow=follow,
3598 force_changelog_traversal=all_files,
3577 force_changelog_traversal=all_files,
3599 filter_revisions_by_pats=not all_files,
3578 filter_revisions_by_pats=not all_files,
3600 )
3579 )
3601 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3580 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3602
3581
3603 ui.pager(b'grep')
3582 ui.pager(b'grep')
3604 fm = ui.formatter(b'grep', opts)
3583 fm = ui.formatter(b'grep', opts)
3605 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3584 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3606 r = display(fm, fn, ctx, pstates, states)
3585 r = display(fm, fn, ctx, pstates, states)
3607 found = found or r
3586 found = found or r
3608 if r and not diff and not all_files:
3587 if r and not diff and not all_files:
3609 searcher.skipfile(fn, ctx.rev())
3588 searcher.skipfile(fn, ctx.rev())
3610 fm.end()
3589 fm.end()
3611
3590
3612 return not found
3591 return not found
3613
3592
3614
3593
3615 @command(
3594 @command(
3616 b'heads',
3595 b'heads',
3617 [
3596 [
3618 (
3597 (
3619 b'r',
3598 b'r',
3620 b'rev',
3599 b'rev',
3621 b'',
3600 b'',
3622 _(b'show only heads which are descendants of STARTREV'),
3601 _(b'show only heads which are descendants of STARTREV'),
3623 _(b'STARTREV'),
3602 _(b'STARTREV'),
3624 ),
3603 ),
3625 (b't', b'topo', False, _(b'show topological heads only')),
3604 (b't', b'topo', False, _(b'show topological heads only')),
3626 (
3605 (
3627 b'a',
3606 b'a',
3628 b'active',
3607 b'active',
3629 False,
3608 False,
3630 _(b'show active branchheads only (DEPRECATED)'),
3609 _(b'show active branchheads only (DEPRECATED)'),
3631 ),
3610 ),
3632 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3611 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3633 ]
3612 ]
3634 + templateopts,
3613 + templateopts,
3635 _(b'[-ct] [-r STARTREV] [REV]...'),
3614 _(b'[-ct] [-r STARTREV] [REV]...'),
3636 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3615 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3637 intents={INTENT_READONLY},
3616 intents={INTENT_READONLY},
3638 )
3617 )
3639 def heads(ui, repo, *branchrevs, **opts):
3618 def heads(ui, repo, *branchrevs, **opts):
3640 """show branch heads
3619 """show branch heads
3641
3620
3642 With no arguments, show all open branch heads in the repository.
3621 With no arguments, show all open branch heads in the repository.
3643 Branch heads are changesets that have no descendants on the
3622 Branch heads are changesets that have no descendants on the
3644 same branch. They are where development generally takes place and
3623 same branch. They are where development generally takes place and
3645 are the usual targets for update and merge operations.
3624 are the usual targets for update and merge operations.
3646
3625
3647 If one or more REVs are given, only open branch heads on the
3626 If one or more REVs are given, only open branch heads on the
3648 branches associated with the specified changesets are shown. This
3627 branches associated with the specified changesets are shown. This
3649 means that you can use :hg:`heads .` to see the heads on the
3628 means that you can use :hg:`heads .` to see the heads on the
3650 currently checked-out branch.
3629 currently checked-out branch.
3651
3630
3652 If -c/--closed is specified, also show branch heads marked closed
3631 If -c/--closed is specified, also show branch heads marked closed
3653 (see :hg:`commit --close-branch`).
3632 (see :hg:`commit --close-branch`).
3654
3633
3655 If STARTREV is specified, only those heads that are descendants of
3634 If STARTREV is specified, only those heads that are descendants of
3656 STARTREV will be displayed.
3635 STARTREV will be displayed.
3657
3636
3658 If -t/--topo is specified, named branch mechanics will be ignored and only
3637 If -t/--topo is specified, named branch mechanics will be ignored and only
3659 topological heads (changesets with no children) will be shown.
3638 topological heads (changesets with no children) will be shown.
3660
3639
3661 Returns 0 if matching heads are found, 1 if not.
3640 Returns 0 if matching heads are found, 1 if not.
3662 """
3641 """
3663
3642
3664 opts = pycompat.byteskwargs(opts)
3643 opts = pycompat.byteskwargs(opts)
3665 start = None
3644 start = None
3666 rev = opts.get(b'rev')
3645 rev = opts.get(b'rev')
3667 if rev:
3646 if rev:
3668 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3647 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3669 start = scmutil.revsingle(repo, rev, None).node()
3648 start = scmutil.revsingle(repo, rev, None).node()
3670
3649
3671 if opts.get(b'topo'):
3650 if opts.get(b'topo'):
3672 heads = [repo[h] for h in repo.heads(start)]
3651 heads = [repo[h] for h in repo.heads(start)]
3673 else:
3652 else:
3674 heads = []
3653 heads = []
3675 for branch in repo.branchmap():
3654 for branch in repo.branchmap():
3676 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3655 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3677 heads = [repo[h] for h in heads]
3656 heads = [repo[h] for h in heads]
3678
3657
3679 if branchrevs:
3658 if branchrevs:
3680 branches = {
3659 branches = {
3681 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3660 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3682 }
3661 }
3683 heads = [h for h in heads if h.branch() in branches]
3662 heads = [h for h in heads if h.branch() in branches]
3684
3663
3685 if opts.get(b'active') and branchrevs:
3664 if opts.get(b'active') and branchrevs:
3686 dagheads = repo.heads(start)
3665 dagheads = repo.heads(start)
3687 heads = [h for h in heads if h.node() in dagheads]
3666 heads = [h for h in heads if h.node() in dagheads]
3688
3667
3689 if branchrevs:
3668 if branchrevs:
3690 haveheads = {h.branch() for h in heads}
3669 haveheads = {h.branch() for h in heads}
3691 if branches - haveheads:
3670 if branches - haveheads:
3692 headless = b', '.join(b for b in branches - haveheads)
3671 headless = b', '.join(b for b in branches - haveheads)
3693 msg = _(b'no open branch heads found on branches %s')
3672 msg = _(b'no open branch heads found on branches %s')
3694 if opts.get(b'rev'):
3673 if opts.get(b'rev'):
3695 msg += _(b' (started at %s)') % opts[b'rev']
3674 msg += _(b' (started at %s)') % opts[b'rev']
3696 ui.warn((msg + b'\n') % headless)
3675 ui.warn((msg + b'\n') % headless)
3697
3676
3698 if not heads:
3677 if not heads:
3699 return 1
3678 return 1
3700
3679
3701 ui.pager(b'heads')
3680 ui.pager(b'heads')
3702 heads = sorted(heads, key=lambda x: -(x.rev()))
3681 heads = sorted(heads, key=lambda x: -(x.rev()))
3703 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3682 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3704 for ctx in heads:
3683 for ctx in heads:
3705 displayer.show(ctx)
3684 displayer.show(ctx)
3706 displayer.close()
3685 displayer.close()
3707
3686
3708
3687
3709 @command(
3688 @command(
3710 b'help',
3689 b'help',
3711 [
3690 [
3712 (b'e', b'extension', None, _(b'show only help for extensions')),
3691 (b'e', b'extension', None, _(b'show only help for extensions')),
3713 (b'c', b'command', None, _(b'show only help for commands')),
3692 (b'c', b'command', None, _(b'show only help for commands')),
3714 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3693 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3715 (
3694 (
3716 b's',
3695 b's',
3717 b'system',
3696 b'system',
3718 [],
3697 [],
3719 _(b'show help for specific platform(s)'),
3698 _(b'show help for specific platform(s)'),
3720 _(b'PLATFORM'),
3699 _(b'PLATFORM'),
3721 ),
3700 ),
3722 ],
3701 ],
3723 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3702 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3724 helpcategory=command.CATEGORY_HELP,
3703 helpcategory=command.CATEGORY_HELP,
3725 norepo=True,
3704 norepo=True,
3726 intents={INTENT_READONLY},
3705 intents={INTENT_READONLY},
3727 )
3706 )
3728 def help_(ui, name=None, **opts):
3707 def help_(ui, name=None, **opts):
3729 """show help for a given topic or a help overview
3708 """show help for a given topic or a help overview
3730
3709
3731 With no arguments, print a list of commands with short help messages.
3710 With no arguments, print a list of commands with short help messages.
3732
3711
3733 Given a topic, extension, or command name, print help for that
3712 Given a topic, extension, or command name, print help for that
3734 topic.
3713 topic.
3735
3714
3736 Returns 0 if successful.
3715 Returns 0 if successful.
3737 """
3716 """
3738
3717
3739 keep = opts.get('system') or []
3718 keep = opts.get('system') or []
3740 if len(keep) == 0:
3719 if len(keep) == 0:
3741 if pycompat.sysplatform.startswith(b'win'):
3720 if pycompat.sysplatform.startswith(b'win'):
3742 keep.append(b'windows')
3721 keep.append(b'windows')
3743 elif pycompat.sysplatform == b'OpenVMS':
3722 elif pycompat.sysplatform == b'OpenVMS':
3744 keep.append(b'vms')
3723 keep.append(b'vms')
3745 elif pycompat.sysplatform == b'plan9':
3724 elif pycompat.sysplatform == b'plan9':
3746 keep.append(b'plan9')
3725 keep.append(b'plan9')
3747 else:
3726 else:
3748 keep.append(b'unix')
3727 keep.append(b'unix')
3749 keep.append(pycompat.sysplatform.lower())
3728 keep.append(pycompat.sysplatform.lower())
3750 if ui.verbose:
3729 if ui.verbose:
3751 keep.append(b'verbose')
3730 keep.append(b'verbose')
3752
3731
3753 commands = sys.modules[__name__]
3732 commands = sys.modules[__name__]
3754 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3733 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3755 ui.pager(b'help')
3734 ui.pager(b'help')
3756 ui.write(formatted)
3735 ui.write(formatted)
3757
3736
3758
3737
3759 @command(
3738 @command(
3760 b'identify|id',
3739 b'identify|id',
3761 [
3740 [
3762 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3741 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3763 (b'n', b'num', None, _(b'show local revision number')),
3742 (b'n', b'num', None, _(b'show local revision number')),
3764 (b'i', b'id', None, _(b'show global revision id')),
3743 (b'i', b'id', None, _(b'show global revision id')),
3765 (b'b', b'branch', None, _(b'show branch')),
3744 (b'b', b'branch', None, _(b'show branch')),
3766 (b't', b'tags', None, _(b'show tags')),
3745 (b't', b'tags', None, _(b'show tags')),
3767 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3746 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3768 ]
3747 ]
3769 + remoteopts
3748 + remoteopts
3770 + formatteropts,
3749 + formatteropts,
3771 _(b'[-nibtB] [-r REV] [SOURCE]'),
3750 _(b'[-nibtB] [-r REV] [SOURCE]'),
3772 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3751 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3773 optionalrepo=True,
3752 optionalrepo=True,
3774 intents={INTENT_READONLY},
3753 intents={INTENT_READONLY},
3775 )
3754 )
3776 def identify(
3755 def identify(
3777 ui,
3756 ui,
3778 repo,
3757 repo,
3779 source=None,
3758 source=None,
3780 rev=None,
3759 rev=None,
3781 num=None,
3760 num=None,
3782 id=None,
3761 id=None,
3783 branch=None,
3762 branch=None,
3784 tags=None,
3763 tags=None,
3785 bookmarks=None,
3764 bookmarks=None,
3786 **opts
3765 **opts
3787 ):
3766 ):
3788 """identify the working directory or specified revision
3767 """identify the working directory or specified revision
3789
3768
3790 Print a summary identifying the repository state at REV using one or
3769 Print a summary identifying the repository state at REV using one or
3791 two parent hash identifiers, followed by a "+" if the working
3770 two parent hash identifiers, followed by a "+" if the working
3792 directory has uncommitted changes, the branch name (if not default),
3771 directory has uncommitted changes, the branch name (if not default),
3793 a list of tags, and a list of bookmarks.
3772 a list of tags, and a list of bookmarks.
3794
3773
3795 When REV is not given, print a summary of the current state of the
3774 When REV is not given, print a summary of the current state of the
3796 repository including the working directory. Specify -r. to get information
3775 repository including the working directory. Specify -r. to get information
3797 of the working directory parent without scanning uncommitted changes.
3776 of the working directory parent without scanning uncommitted changes.
3798
3777
3799 Specifying a path to a repository root or Mercurial bundle will
3778 Specifying a path to a repository root or Mercurial bundle will
3800 cause lookup to operate on that repository/bundle.
3779 cause lookup to operate on that repository/bundle.
3801
3780
3802 .. container:: verbose
3781 .. container:: verbose
3803
3782
3804 Template:
3783 Template:
3805
3784
3806 The following keywords are supported in addition to the common template
3785 The following keywords are supported in addition to the common template
3807 keywords and functions. See also :hg:`help templates`.
3786 keywords and functions. See also :hg:`help templates`.
3808
3787
3809 :dirty: String. Character ``+`` denoting if the working directory has
3788 :dirty: String. Character ``+`` denoting if the working directory has
3810 uncommitted changes.
3789 uncommitted changes.
3811 :id: String. One or two nodes, optionally followed by ``+``.
3790 :id: String. One or two nodes, optionally followed by ``+``.
3812 :parents: List of strings. Parent nodes of the changeset.
3791 :parents: List of strings. Parent nodes of the changeset.
3813
3792
3814 Examples:
3793 Examples:
3815
3794
3816 - generate a build identifier for the working directory::
3795 - generate a build identifier for the working directory::
3817
3796
3818 hg id --id > build-id.dat
3797 hg id --id > build-id.dat
3819
3798
3820 - find the revision corresponding to a tag::
3799 - find the revision corresponding to a tag::
3821
3800
3822 hg id -n -r 1.3
3801 hg id -n -r 1.3
3823
3802
3824 - check the most recent revision of a remote repository::
3803 - check the most recent revision of a remote repository::
3825
3804
3826 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3805 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3827
3806
3828 See :hg:`log` for generating more information about specific revisions,
3807 See :hg:`log` for generating more information about specific revisions,
3829 including full hash identifiers.
3808 including full hash identifiers.
3830
3809
3831 Returns 0 if successful.
3810 Returns 0 if successful.
3832 """
3811 """
3833
3812
3834 opts = pycompat.byteskwargs(opts)
3813 opts = pycompat.byteskwargs(opts)
3835 if not repo and not source:
3814 if not repo and not source:
3836 raise error.InputError(
3815 raise error.InputError(
3837 _(b"there is no Mercurial repository here (.hg not found)")
3816 _(b"there is no Mercurial repository here (.hg not found)")
3838 )
3817 )
3839
3818
3840 default = not (num or id or branch or tags or bookmarks)
3819 default = not (num or id or branch or tags or bookmarks)
3841 output = []
3820 output = []
3842 revs = []
3821 revs = []
3843
3822
3844 if source:
3823 if source:
3845 source, branches = hg.parseurl(ui.expandpath(source))
3824 source, branches = hg.parseurl(ui.expandpath(source))
3846 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3825 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3847 repo = peer.local()
3826 repo = peer.local()
3848 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3827 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3849
3828
3850 fm = ui.formatter(b'identify', opts)
3829 fm = ui.formatter(b'identify', opts)
3851 fm.startitem()
3830 fm.startitem()
3852
3831
3853 if not repo:
3832 if not repo:
3854 if num or branch or tags:
3833 if num or branch or tags:
3855 raise error.InputError(
3834 raise error.InputError(
3856 _(b"can't query remote revision number, branch, or tags")
3835 _(b"can't query remote revision number, branch, or tags")
3857 )
3836 )
3858 if not rev and revs:
3837 if not rev and revs:
3859 rev = revs[0]
3838 rev = revs[0]
3860 if not rev:
3839 if not rev:
3861 rev = b"tip"
3840 rev = b"tip"
3862
3841
3863 remoterev = peer.lookup(rev)
3842 remoterev = peer.lookup(rev)
3864 hexrev = fm.hexfunc(remoterev)
3843 hexrev = fm.hexfunc(remoterev)
3865 if default or id:
3844 if default or id:
3866 output = [hexrev]
3845 output = [hexrev]
3867 fm.data(id=hexrev)
3846 fm.data(id=hexrev)
3868
3847
3869 @util.cachefunc
3848 @util.cachefunc
3870 def getbms():
3849 def getbms():
3871 bms = []
3850 bms = []
3872
3851
3873 if b'bookmarks' in peer.listkeys(b'namespaces'):
3852 if b'bookmarks' in peer.listkeys(b'namespaces'):
3874 hexremoterev = hex(remoterev)
3853 hexremoterev = hex(remoterev)
3875 bms = [
3854 bms = [
3876 bm
3855 bm
3877 for bm, bmr in pycompat.iteritems(
3856 for bm, bmr in pycompat.iteritems(
3878 peer.listkeys(b'bookmarks')
3857 peer.listkeys(b'bookmarks')
3879 )
3858 )
3880 if bmr == hexremoterev
3859 if bmr == hexremoterev
3881 ]
3860 ]
3882
3861
3883 return sorted(bms)
3862 return sorted(bms)
3884
3863
3885 if fm.isplain():
3864 if fm.isplain():
3886 if bookmarks:
3865 if bookmarks:
3887 output.extend(getbms())
3866 output.extend(getbms())
3888 elif default and not ui.quiet:
3867 elif default and not ui.quiet:
3889 # multiple bookmarks for a single parent separated by '/'
3868 # multiple bookmarks for a single parent separated by '/'
3890 bm = b'/'.join(getbms())
3869 bm = b'/'.join(getbms())
3891 if bm:
3870 if bm:
3892 output.append(bm)
3871 output.append(bm)
3893 else:
3872 else:
3894 fm.data(node=hex(remoterev))
3873 fm.data(node=hex(remoterev))
3895 if bookmarks or b'bookmarks' in fm.datahint():
3874 if bookmarks or b'bookmarks' in fm.datahint():
3896 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3875 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3897 else:
3876 else:
3898 if rev:
3877 if rev:
3899 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3878 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3900 ctx = scmutil.revsingle(repo, rev, None)
3879 ctx = scmutil.revsingle(repo, rev, None)
3901
3880
3902 if ctx.rev() is None:
3881 if ctx.rev() is None:
3903 ctx = repo[None]
3882 ctx = repo[None]
3904 parents = ctx.parents()
3883 parents = ctx.parents()
3905 taglist = []
3884 taglist = []
3906 for p in parents:
3885 for p in parents:
3907 taglist.extend(p.tags())
3886 taglist.extend(p.tags())
3908
3887
3909 dirty = b""
3888 dirty = b""
3910 if ctx.dirty(missing=True, merge=False, branch=False):
3889 if ctx.dirty(missing=True, merge=False, branch=False):
3911 dirty = b'+'
3890 dirty = b'+'
3912 fm.data(dirty=dirty)
3891 fm.data(dirty=dirty)
3913
3892
3914 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3893 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3915 if default or id:
3894 if default or id:
3916 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3895 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3917 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3896 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3918
3897
3919 if num:
3898 if num:
3920 numoutput = [b"%d" % p.rev() for p in parents]
3899 numoutput = [b"%d" % p.rev() for p in parents]
3921 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3900 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3922
3901
3923 fm.data(
3902 fm.data(
3924 parents=fm.formatlist(
3903 parents=fm.formatlist(
3925 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3904 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3926 )
3905 )
3927 )
3906 )
3928 else:
3907 else:
3929 hexoutput = fm.hexfunc(ctx.node())
3908 hexoutput = fm.hexfunc(ctx.node())
3930 if default or id:
3909 if default or id:
3931 output = [hexoutput]
3910 output = [hexoutput]
3932 fm.data(id=hexoutput)
3911 fm.data(id=hexoutput)
3933
3912
3934 if num:
3913 if num:
3935 output.append(pycompat.bytestr(ctx.rev()))
3914 output.append(pycompat.bytestr(ctx.rev()))
3936 taglist = ctx.tags()
3915 taglist = ctx.tags()
3937
3916
3938 if default and not ui.quiet:
3917 if default and not ui.quiet:
3939 b = ctx.branch()
3918 b = ctx.branch()
3940 if b != b'default':
3919 if b != b'default':
3941 output.append(b"(%s)" % b)
3920 output.append(b"(%s)" % b)
3942
3921
3943 # multiple tags for a single parent separated by '/'
3922 # multiple tags for a single parent separated by '/'
3944 t = b'/'.join(taglist)
3923 t = b'/'.join(taglist)
3945 if t:
3924 if t:
3946 output.append(t)
3925 output.append(t)
3947
3926
3948 # multiple bookmarks for a single parent separated by '/'
3927 # multiple bookmarks for a single parent separated by '/'
3949 bm = b'/'.join(ctx.bookmarks())
3928 bm = b'/'.join(ctx.bookmarks())
3950 if bm:
3929 if bm:
3951 output.append(bm)
3930 output.append(bm)
3952 else:
3931 else:
3953 if branch:
3932 if branch:
3954 output.append(ctx.branch())
3933 output.append(ctx.branch())
3955
3934
3956 if tags:
3935 if tags:
3957 output.extend(taglist)
3936 output.extend(taglist)
3958
3937
3959 if bookmarks:
3938 if bookmarks:
3960 output.extend(ctx.bookmarks())
3939 output.extend(ctx.bookmarks())
3961
3940
3962 fm.data(node=ctx.hex())
3941 fm.data(node=ctx.hex())
3963 fm.data(branch=ctx.branch())
3942 fm.data(branch=ctx.branch())
3964 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3943 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3965 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3944 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3966 fm.context(ctx=ctx)
3945 fm.context(ctx=ctx)
3967
3946
3968 fm.plain(b"%s\n" % b' '.join(output))
3947 fm.plain(b"%s\n" % b' '.join(output))
3969 fm.end()
3948 fm.end()
3970
3949
3971
3950
3972 @command(
3951 @command(
3973 b'import|patch',
3952 b'import|patch',
3974 [
3953 [
3975 (
3954 (
3976 b'p',
3955 b'p',
3977 b'strip',
3956 b'strip',
3978 1,
3957 1,
3979 _(
3958 _(
3980 b'directory strip option for patch. This has the same '
3959 b'directory strip option for patch. This has the same '
3981 b'meaning as the corresponding patch option'
3960 b'meaning as the corresponding patch option'
3982 ),
3961 ),
3983 _(b'NUM'),
3962 _(b'NUM'),
3984 ),
3963 ),
3985 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3964 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3986 (b'', b'secret', None, _(b'use the secret phase for committing')),
3965 (b'', b'secret', None, _(b'use the secret phase for committing')),
3987 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3966 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3988 (
3967 (
3989 b'f',
3968 b'f',
3990 b'force',
3969 b'force',
3991 None,
3970 None,
3992 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3971 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3993 ),
3972 ),
3994 (
3973 (
3995 b'',
3974 b'',
3996 b'no-commit',
3975 b'no-commit',
3997 None,
3976 None,
3998 _(b"don't commit, just update the working directory"),
3977 _(b"don't commit, just update the working directory"),
3999 ),
3978 ),
4000 (
3979 (
4001 b'',
3980 b'',
4002 b'bypass',
3981 b'bypass',
4003 None,
3982 None,
4004 _(b"apply patch without touching the working directory"),
3983 _(b"apply patch without touching the working directory"),
4005 ),
3984 ),
4006 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3985 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4007 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3986 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4008 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3987 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4009 (
3988 (
4010 b'',
3989 b'',
4011 b'import-branch',
3990 b'import-branch',
4012 None,
3991 None,
4013 _(b'use any branch information in patch (implied by --exact)'),
3992 _(b'use any branch information in patch (implied by --exact)'),
4014 ),
3993 ),
4015 ]
3994 ]
4016 + commitopts
3995 + commitopts
4017 + commitopts2
3996 + commitopts2
4018 + similarityopts,
3997 + similarityopts,
4019 _(b'[OPTION]... PATCH...'),
3998 _(b'[OPTION]... PATCH...'),
4020 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3999 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4021 )
4000 )
4022 def import_(ui, repo, patch1=None, *patches, **opts):
4001 def import_(ui, repo, patch1=None, *patches, **opts):
4023 """import an ordered set of patches
4002 """import an ordered set of patches
4024
4003
4025 Import a list of patches and commit them individually (unless
4004 Import a list of patches and commit them individually (unless
4026 --no-commit is specified).
4005 --no-commit is specified).
4027
4006
4028 To read a patch from standard input (stdin), use "-" as the patch
4007 To read a patch from standard input (stdin), use "-" as the patch
4029 name. If a URL is specified, the patch will be downloaded from
4008 name. If a URL is specified, the patch will be downloaded from
4030 there.
4009 there.
4031
4010
4032 Import first applies changes to the working directory (unless
4011 Import first applies changes to the working directory (unless
4033 --bypass is specified), import will abort if there are outstanding
4012 --bypass is specified), import will abort if there are outstanding
4034 changes.
4013 changes.
4035
4014
4036 Use --bypass to apply and commit patches directly to the
4015 Use --bypass to apply and commit patches directly to the
4037 repository, without affecting the working directory. Without
4016 repository, without affecting the working directory. Without
4038 --exact, patches will be applied on top of the working directory
4017 --exact, patches will be applied on top of the working directory
4039 parent revision.
4018 parent revision.
4040
4019
4041 You can import a patch straight from a mail message. Even patches
4020 You can import a patch straight from a mail message. Even patches
4042 as attachments work (to use the body part, it must have type
4021 as attachments work (to use the body part, it must have type
4043 text/plain or text/x-patch). From and Subject headers of email
4022 text/plain or text/x-patch). From and Subject headers of email
4044 message are used as default committer and commit message. All
4023 message are used as default committer and commit message. All
4045 text/plain body parts before first diff are added to the commit
4024 text/plain body parts before first diff are added to the commit
4046 message.
4025 message.
4047
4026
4048 If the imported patch was generated by :hg:`export`, user and
4027 If the imported patch was generated by :hg:`export`, user and
4049 description from patch override values from message headers and
4028 description from patch override values from message headers and
4050 body. Values given on command line with -m/--message and -u/--user
4029 body. Values given on command line with -m/--message and -u/--user
4051 override these.
4030 override these.
4052
4031
4053 If --exact is specified, import will set the working directory to
4032 If --exact is specified, import will set the working directory to
4054 the parent of each patch before applying it, and will abort if the
4033 the parent of each patch before applying it, and will abort if the
4055 resulting changeset has a different ID than the one recorded in
4034 resulting changeset has a different ID than the one recorded in
4056 the patch. This will guard against various ways that portable
4035 the patch. This will guard against various ways that portable
4057 patch formats and mail systems might fail to transfer Mercurial
4036 patch formats and mail systems might fail to transfer Mercurial
4058 data or metadata. See :hg:`bundle` for lossless transmission.
4037 data or metadata. See :hg:`bundle` for lossless transmission.
4059
4038
4060 Use --partial to ensure a changeset will be created from the patch
4039 Use --partial to ensure a changeset will be created from the patch
4061 even if some hunks fail to apply. Hunks that fail to apply will be
4040 even if some hunks fail to apply. Hunks that fail to apply will be
4062 written to a <target-file>.rej file. Conflicts can then be resolved
4041 written to a <target-file>.rej file. Conflicts can then be resolved
4063 by hand before :hg:`commit --amend` is run to update the created
4042 by hand before :hg:`commit --amend` is run to update the created
4064 changeset. This flag exists to let people import patches that
4043 changeset. This flag exists to let people import patches that
4065 partially apply without losing the associated metadata (author,
4044 partially apply without losing the associated metadata (author,
4066 date, description, ...).
4045 date, description, ...).
4067
4046
4068 .. note::
4047 .. note::
4069
4048
4070 When no hunks apply cleanly, :hg:`import --partial` will create
4049 When no hunks apply cleanly, :hg:`import --partial` will create
4071 an empty changeset, importing only the patch metadata.
4050 an empty changeset, importing only the patch metadata.
4072
4051
4073 With -s/--similarity, hg will attempt to discover renames and
4052 With -s/--similarity, hg will attempt to discover renames and
4074 copies in the patch in the same way as :hg:`addremove`.
4053 copies in the patch in the same way as :hg:`addremove`.
4075
4054
4076 It is possible to use external patch programs to perform the patch
4055 It is possible to use external patch programs to perform the patch
4077 by setting the ``ui.patch`` configuration option. For the default
4056 by setting the ``ui.patch`` configuration option. For the default
4078 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4057 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4079 See :hg:`help config` for more information about configuration
4058 See :hg:`help config` for more information about configuration
4080 files and how to use these options.
4059 files and how to use these options.
4081
4060
4082 See :hg:`help dates` for a list of formats valid for -d/--date.
4061 See :hg:`help dates` for a list of formats valid for -d/--date.
4083
4062
4084 .. container:: verbose
4063 .. container:: verbose
4085
4064
4086 Examples:
4065 Examples:
4087
4066
4088 - import a traditional patch from a website and detect renames::
4067 - import a traditional patch from a website and detect renames::
4089
4068
4090 hg import -s 80 http://example.com/bugfix.patch
4069 hg import -s 80 http://example.com/bugfix.patch
4091
4070
4092 - import a changeset from an hgweb server::
4071 - import a changeset from an hgweb server::
4093
4072
4094 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4073 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4095
4074
4096 - import all the patches in an Unix-style mbox::
4075 - import all the patches in an Unix-style mbox::
4097
4076
4098 hg import incoming-patches.mbox
4077 hg import incoming-patches.mbox
4099
4078
4100 - import patches from stdin::
4079 - import patches from stdin::
4101
4080
4102 hg import -
4081 hg import -
4103
4082
4104 - attempt to exactly restore an exported changeset (not always
4083 - attempt to exactly restore an exported changeset (not always
4105 possible)::
4084 possible)::
4106
4085
4107 hg import --exact proposed-fix.patch
4086 hg import --exact proposed-fix.patch
4108
4087
4109 - use an external tool to apply a patch which is too fuzzy for
4088 - use an external tool to apply a patch which is too fuzzy for
4110 the default internal tool.
4089 the default internal tool.
4111
4090
4112 hg import --config ui.patch="patch --merge" fuzzy.patch
4091 hg import --config ui.patch="patch --merge" fuzzy.patch
4113
4092
4114 - change the default fuzzing from 2 to a less strict 7
4093 - change the default fuzzing from 2 to a less strict 7
4115
4094
4116 hg import --config ui.fuzz=7 fuzz.patch
4095 hg import --config ui.fuzz=7 fuzz.patch
4117
4096
4118 Returns 0 on success, 1 on partial success (see --partial).
4097 Returns 0 on success, 1 on partial success (see --partial).
4119 """
4098 """
4120
4099
4121 cmdutil.check_incompatible_arguments(
4100 cmdutil.check_incompatible_arguments(
4122 opts, 'no_commit', ['bypass', 'secret']
4101 opts, 'no_commit', ['bypass', 'secret']
4123 )
4102 )
4124 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4103 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4125 opts = pycompat.byteskwargs(opts)
4104 opts = pycompat.byteskwargs(opts)
4126 if not patch1:
4105 if not patch1:
4127 raise error.InputError(_(b'need at least one patch to import'))
4106 raise error.InputError(_(b'need at least one patch to import'))
4128
4107
4129 patches = (patch1,) + patches
4108 patches = (patch1,) + patches
4130
4109
4131 date = opts.get(b'date')
4110 date = opts.get(b'date')
4132 if date:
4111 if date:
4133 opts[b'date'] = dateutil.parsedate(date)
4112 opts[b'date'] = dateutil.parsedate(date)
4134
4113
4135 exact = opts.get(b'exact')
4114 exact = opts.get(b'exact')
4136 update = not opts.get(b'bypass')
4115 update = not opts.get(b'bypass')
4137 try:
4116 try:
4138 sim = float(opts.get(b'similarity') or 0)
4117 sim = float(opts.get(b'similarity') or 0)
4139 except ValueError:
4118 except ValueError:
4140 raise error.InputError(_(b'similarity must be a number'))
4119 raise error.InputError(_(b'similarity must be a number'))
4141 if sim < 0 or sim > 100:
4120 if sim < 0 or sim > 100:
4142 raise error.InputError(_(b'similarity must be between 0 and 100'))
4121 raise error.InputError(_(b'similarity must be between 0 and 100'))
4143 if sim and not update:
4122 if sim and not update:
4144 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4123 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4145
4124
4146 base = opts[b"base"]
4125 base = opts[b"base"]
4147 msgs = []
4126 msgs = []
4148 ret = 0
4127 ret = 0
4149
4128
4150 with repo.wlock():
4129 with repo.wlock():
4151 if update:
4130 if update:
4152 cmdutil.checkunfinished(repo)
4131 cmdutil.checkunfinished(repo)
4153 if exact or not opts.get(b'force'):
4132 if exact or not opts.get(b'force'):
4154 cmdutil.bailifchanged(repo)
4133 cmdutil.bailifchanged(repo)
4155
4134
4156 if not opts.get(b'no_commit'):
4135 if not opts.get(b'no_commit'):
4157 lock = repo.lock
4136 lock = repo.lock
4158 tr = lambda: repo.transaction(b'import')
4137 tr = lambda: repo.transaction(b'import')
4159 dsguard = util.nullcontextmanager
4138 dsguard = util.nullcontextmanager
4160 else:
4139 else:
4161 lock = util.nullcontextmanager
4140 lock = util.nullcontextmanager
4162 tr = util.nullcontextmanager
4141 tr = util.nullcontextmanager
4163 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4142 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4164 with lock(), tr(), dsguard():
4143 with lock(), tr(), dsguard():
4165 parents = repo[None].parents()
4144 parents = repo[None].parents()
4166 for patchurl in patches:
4145 for patchurl in patches:
4167 if patchurl == b'-':
4146 if patchurl == b'-':
4168 ui.status(_(b'applying patch from stdin\n'))
4147 ui.status(_(b'applying patch from stdin\n'))
4169 patchfile = ui.fin
4148 patchfile = ui.fin
4170 patchurl = b'stdin' # for error message
4149 patchurl = b'stdin' # for error message
4171 else:
4150 else:
4172 patchurl = os.path.join(base, patchurl)
4151 patchurl = os.path.join(base, patchurl)
4173 ui.status(_(b'applying %s\n') % patchurl)
4152 ui.status(_(b'applying %s\n') % patchurl)
4174 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4153 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4175
4154
4176 haspatch = False
4155 haspatch = False
4177 for hunk in patch.split(patchfile):
4156 for hunk in patch.split(patchfile):
4178 with patch.extract(ui, hunk) as patchdata:
4157 with patch.extract(ui, hunk) as patchdata:
4179 msg, node, rej = cmdutil.tryimportone(
4158 msg, node, rej = cmdutil.tryimportone(
4180 ui, repo, patchdata, parents, opts, msgs, hg.clean
4159 ui, repo, patchdata, parents, opts, msgs, hg.clean
4181 )
4160 )
4182 if msg:
4161 if msg:
4183 haspatch = True
4162 haspatch = True
4184 ui.note(msg + b'\n')
4163 ui.note(msg + b'\n')
4185 if update or exact:
4164 if update or exact:
4186 parents = repo[None].parents()
4165 parents = repo[None].parents()
4187 else:
4166 else:
4188 parents = [repo[node]]
4167 parents = [repo[node]]
4189 if rej:
4168 if rej:
4190 ui.write_err(_(b"patch applied partially\n"))
4169 ui.write_err(_(b"patch applied partially\n"))
4191 ui.write_err(
4170 ui.write_err(
4192 _(
4171 _(
4193 b"(fix the .rej files and run "
4172 b"(fix the .rej files and run "
4194 b"`hg commit --amend`)\n"
4173 b"`hg commit --amend`)\n"
4195 )
4174 )
4196 )
4175 )
4197 ret = 1
4176 ret = 1
4198 break
4177 break
4199
4178
4200 if not haspatch:
4179 if not haspatch:
4201 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4180 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4202
4181
4203 if msgs:
4182 if msgs:
4204 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4183 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4205 return ret
4184 return ret
4206
4185
4207
4186
4208 @command(
4187 @command(
4209 b'incoming|in',
4188 b'incoming|in',
4210 [
4189 [
4211 (
4190 (
4212 b'f',
4191 b'f',
4213 b'force',
4192 b'force',
4214 None,
4193 None,
4215 _(b'run even if remote repository is unrelated'),
4194 _(b'run even if remote repository is unrelated'),
4216 ),
4195 ),
4217 (b'n', b'newest-first', None, _(b'show newest record first')),
4196 (b'n', b'newest-first', None, _(b'show newest record first')),
4218 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4197 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4219 (
4198 (
4220 b'r',
4199 b'r',
4221 b'rev',
4200 b'rev',
4222 [],
4201 [],
4223 _(b'a remote changeset intended to be added'),
4202 _(b'a remote changeset intended to be added'),
4224 _(b'REV'),
4203 _(b'REV'),
4225 ),
4204 ),
4226 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4205 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4227 (
4206 (
4228 b'b',
4207 b'b',
4229 b'branch',
4208 b'branch',
4230 [],
4209 [],
4231 _(b'a specific branch you would like to pull'),
4210 _(b'a specific branch you would like to pull'),
4232 _(b'BRANCH'),
4211 _(b'BRANCH'),
4233 ),
4212 ),
4234 ]
4213 ]
4235 + logopts
4214 + logopts
4236 + remoteopts
4215 + remoteopts
4237 + subrepoopts,
4216 + subrepoopts,
4238 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4217 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4239 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4218 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4240 )
4219 )
4241 def incoming(ui, repo, source=b"default", **opts):
4220 def incoming(ui, repo, source=b"default", **opts):
4242 """show new changesets found in source
4221 """show new changesets found in source
4243
4222
4244 Show new changesets found in the specified path/URL or the default
4223 Show new changesets found in the specified path/URL or the default
4245 pull location. These are the changesets that would have been pulled
4224 pull location. These are the changesets that would have been pulled
4246 by :hg:`pull` at the time you issued this command.
4225 by :hg:`pull` at the time you issued this command.
4247
4226
4248 See pull for valid source format details.
4227 See pull for valid source format details.
4249
4228
4250 .. container:: verbose
4229 .. container:: verbose
4251
4230
4252 With -B/--bookmarks, the result of bookmark comparison between
4231 With -B/--bookmarks, the result of bookmark comparison between
4253 local and remote repositories is displayed. With -v/--verbose,
4232 local and remote repositories is displayed. With -v/--verbose,
4254 status is also displayed for each bookmark like below::
4233 status is also displayed for each bookmark like below::
4255
4234
4256 BM1 01234567890a added
4235 BM1 01234567890a added
4257 BM2 1234567890ab advanced
4236 BM2 1234567890ab advanced
4258 BM3 234567890abc diverged
4237 BM3 234567890abc diverged
4259 BM4 34567890abcd changed
4238 BM4 34567890abcd changed
4260
4239
4261 The action taken locally when pulling depends on the
4240 The action taken locally when pulling depends on the
4262 status of each bookmark:
4241 status of each bookmark:
4263
4242
4264 :``added``: pull will create it
4243 :``added``: pull will create it
4265 :``advanced``: pull will update it
4244 :``advanced``: pull will update it
4266 :``diverged``: pull will create a divergent bookmark
4245 :``diverged``: pull will create a divergent bookmark
4267 :``changed``: result depends on remote changesets
4246 :``changed``: result depends on remote changesets
4268
4247
4269 From the point of view of pulling behavior, bookmark
4248 From the point of view of pulling behavior, bookmark
4270 existing only in the remote repository are treated as ``added``,
4249 existing only in the remote repository are treated as ``added``,
4271 even if it is in fact locally deleted.
4250 even if it is in fact locally deleted.
4272
4251
4273 .. container:: verbose
4252 .. container:: verbose
4274
4253
4275 For remote repository, using --bundle avoids downloading the
4254 For remote repository, using --bundle avoids downloading the
4276 changesets twice if the incoming is followed by a pull.
4255 changesets twice if the incoming is followed by a pull.
4277
4256
4278 Examples:
4257 Examples:
4279
4258
4280 - show incoming changes with patches and full description::
4259 - show incoming changes with patches and full description::
4281
4260
4282 hg incoming -vp
4261 hg incoming -vp
4283
4262
4284 - show incoming changes excluding merges, store a bundle::
4263 - show incoming changes excluding merges, store a bundle::
4285
4264
4286 hg in -vpM --bundle incoming.hg
4265 hg in -vpM --bundle incoming.hg
4287 hg pull incoming.hg
4266 hg pull incoming.hg
4288
4267
4289 - briefly list changes inside a bundle::
4268 - briefly list changes inside a bundle::
4290
4269
4291 hg in changes.hg -T "{desc|firstline}\\n"
4270 hg in changes.hg -T "{desc|firstline}\\n"
4292
4271
4293 Returns 0 if there are incoming changes, 1 otherwise.
4272 Returns 0 if there are incoming changes, 1 otherwise.
4294 """
4273 """
4295 opts = pycompat.byteskwargs(opts)
4274 opts = pycompat.byteskwargs(opts)
4296 if opts.get(b'graph'):
4275 if opts.get(b'graph'):
4297 logcmdutil.checkunsupportedgraphflags([], opts)
4276 logcmdutil.checkunsupportedgraphflags([], opts)
4298
4277
4299 def display(other, chlist, displayer):
4278 def display(other, chlist, displayer):
4300 revdag = logcmdutil.graphrevs(other, chlist, opts)
4279 revdag = logcmdutil.graphrevs(other, chlist, opts)
4301 logcmdutil.displaygraph(
4280 logcmdutil.displaygraph(
4302 ui, repo, revdag, displayer, graphmod.asciiedges
4281 ui, repo, revdag, displayer, graphmod.asciiedges
4303 )
4282 )
4304
4283
4305 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4284 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4306 return 0
4285 return 0
4307
4286
4308 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4287 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4309
4288
4310 if opts.get(b'bookmarks'):
4289 if opts.get(b'bookmarks'):
4311 source, branches = hg.parseurl(
4290 source, branches = hg.parseurl(
4312 ui.expandpath(source), opts.get(b'branch')
4291 ui.expandpath(source), opts.get(b'branch')
4313 )
4292 )
4314 other = hg.peer(repo, opts, source)
4293 other = hg.peer(repo, opts, source)
4315 if b'bookmarks' not in other.listkeys(b'namespaces'):
4294 if b'bookmarks' not in other.listkeys(b'namespaces'):
4316 ui.warn(_(b"remote doesn't support bookmarks\n"))
4295 ui.warn(_(b"remote doesn't support bookmarks\n"))
4317 return 0
4296 return 0
4318 ui.pager(b'incoming')
4297 ui.pager(b'incoming')
4319 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4298 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4320 return bookmarks.incoming(ui, repo, other)
4299 return bookmarks.incoming(ui, repo, other)
4321
4300
4322 repo._subtoppath = ui.expandpath(source)
4301 repo._subtoppath = ui.expandpath(source)
4323 try:
4302 try:
4324 return hg.incoming(ui, repo, source, opts)
4303 return hg.incoming(ui, repo, source, opts)
4325 finally:
4304 finally:
4326 del repo._subtoppath
4305 del repo._subtoppath
4327
4306
4328
4307
4329 @command(
4308 @command(
4330 b'init',
4309 b'init',
4331 remoteopts,
4310 remoteopts,
4332 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4311 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4333 helpcategory=command.CATEGORY_REPO_CREATION,
4312 helpcategory=command.CATEGORY_REPO_CREATION,
4334 helpbasic=True,
4313 helpbasic=True,
4335 norepo=True,
4314 norepo=True,
4336 )
4315 )
4337 def init(ui, dest=b".", **opts):
4316 def init(ui, dest=b".", **opts):
4338 """create a new repository in the given directory
4317 """create a new repository in the given directory
4339
4318
4340 Initialize a new repository in the given directory. If the given
4319 Initialize a new repository in the given directory. If the given
4341 directory does not exist, it will be created.
4320 directory does not exist, it will be created.
4342
4321
4343 If no directory is given, the current directory is used.
4322 If no directory is given, the current directory is used.
4344
4323
4345 It is possible to specify an ``ssh://`` URL as the destination.
4324 It is possible to specify an ``ssh://`` URL as the destination.
4346 See :hg:`help urls` for more information.
4325 See :hg:`help urls` for more information.
4347
4326
4348 Returns 0 on success.
4327 Returns 0 on success.
4349 """
4328 """
4350 opts = pycompat.byteskwargs(opts)
4329 opts = pycompat.byteskwargs(opts)
4351 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4330 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4352
4331
4353
4332
4354 @command(
4333 @command(
4355 b'locate',
4334 b'locate',
4356 [
4335 [
4357 (
4336 (
4358 b'r',
4337 b'r',
4359 b'rev',
4338 b'rev',
4360 b'',
4339 b'',
4361 _(b'search the repository as it is in REV'),
4340 _(b'search the repository as it is in REV'),
4362 _(b'REV'),
4341 _(b'REV'),
4363 ),
4342 ),
4364 (
4343 (
4365 b'0',
4344 b'0',
4366 b'print0',
4345 b'print0',
4367 None,
4346 None,
4368 _(b'end filenames with NUL, for use with xargs'),
4347 _(b'end filenames with NUL, for use with xargs'),
4369 ),
4348 ),
4370 (
4349 (
4371 b'f',
4350 b'f',
4372 b'fullpath',
4351 b'fullpath',
4373 None,
4352 None,
4374 _(b'print complete paths from the filesystem root'),
4353 _(b'print complete paths from the filesystem root'),
4375 ),
4354 ),
4376 ]
4355 ]
4377 + walkopts,
4356 + walkopts,
4378 _(b'[OPTION]... [PATTERN]...'),
4357 _(b'[OPTION]... [PATTERN]...'),
4379 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4358 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4380 )
4359 )
4381 def locate(ui, repo, *pats, **opts):
4360 def locate(ui, repo, *pats, **opts):
4382 """locate files matching specific patterns (DEPRECATED)
4361 """locate files matching specific patterns (DEPRECATED)
4383
4362
4384 Print files under Mercurial control in the working directory whose
4363 Print files under Mercurial control in the working directory whose
4385 names match the given patterns.
4364 names match the given patterns.
4386
4365
4387 By default, this command searches all directories in the working
4366 By default, this command searches all directories in the working
4388 directory. To search just the current directory and its
4367 directory. To search just the current directory and its
4389 subdirectories, use "--include .".
4368 subdirectories, use "--include .".
4390
4369
4391 If no patterns are given to match, this command prints the names
4370 If no patterns are given to match, this command prints the names
4392 of all files under Mercurial control in the working directory.
4371 of all files under Mercurial control in the working directory.
4393
4372
4394 If you want to feed the output of this command into the "xargs"
4373 If you want to feed the output of this command into the "xargs"
4395 command, use the -0 option to both this command and "xargs". This
4374 command, use the -0 option to both this command and "xargs". This
4396 will avoid the problem of "xargs" treating single filenames that
4375 will avoid the problem of "xargs" treating single filenames that
4397 contain whitespace as multiple filenames.
4376 contain whitespace as multiple filenames.
4398
4377
4399 See :hg:`help files` for a more versatile command.
4378 See :hg:`help files` for a more versatile command.
4400
4379
4401 Returns 0 if a match is found, 1 otherwise.
4380 Returns 0 if a match is found, 1 otherwise.
4402 """
4381 """
4403 opts = pycompat.byteskwargs(opts)
4382 opts = pycompat.byteskwargs(opts)
4404 if opts.get(b'print0'):
4383 if opts.get(b'print0'):
4405 end = b'\0'
4384 end = b'\0'
4406 else:
4385 else:
4407 end = b'\n'
4386 end = b'\n'
4408 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4387 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4409
4388
4410 ret = 1
4389 ret = 1
4411 m = scmutil.match(
4390 m = scmutil.match(
4412 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4391 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4413 )
4392 )
4414
4393
4415 ui.pager(b'locate')
4394 ui.pager(b'locate')
4416 if ctx.rev() is None:
4395 if ctx.rev() is None:
4417 # When run on the working copy, "locate" includes removed files, so
4396 # When run on the working copy, "locate" includes removed files, so
4418 # we get the list of files from the dirstate.
4397 # we get the list of files from the dirstate.
4419 filesgen = sorted(repo.dirstate.matches(m))
4398 filesgen = sorted(repo.dirstate.matches(m))
4420 else:
4399 else:
4421 filesgen = ctx.matches(m)
4400 filesgen = ctx.matches(m)
4422 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4401 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4423 for abs in filesgen:
4402 for abs in filesgen:
4424 if opts.get(b'fullpath'):
4403 if opts.get(b'fullpath'):
4425 ui.write(repo.wjoin(abs), end)
4404 ui.write(repo.wjoin(abs), end)
4426 else:
4405 else:
4427 ui.write(uipathfn(abs), end)
4406 ui.write(uipathfn(abs), end)
4428 ret = 0
4407 ret = 0
4429
4408
4430 return ret
4409 return ret
4431
4410
4432
4411
4433 @command(
4412 @command(
4434 b'log|history',
4413 b'log|history',
4435 [
4414 [
4436 (
4415 (
4437 b'f',
4416 b'f',
4438 b'follow',
4417 b'follow',
4439 None,
4418 None,
4440 _(
4419 _(
4441 b'follow changeset history, or file history across copies and renames'
4420 b'follow changeset history, or file history across copies and renames'
4442 ),
4421 ),
4443 ),
4422 ),
4444 (
4423 (
4445 b'',
4424 b'',
4446 b'follow-first',
4425 b'follow-first',
4447 None,
4426 None,
4448 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4427 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4449 ),
4428 ),
4450 (
4429 (
4451 b'd',
4430 b'd',
4452 b'date',
4431 b'date',
4453 b'',
4432 b'',
4454 _(b'show revisions matching date spec'),
4433 _(b'show revisions matching date spec'),
4455 _(b'DATE'),
4434 _(b'DATE'),
4456 ),
4435 ),
4457 (b'C', b'copies', None, _(b'show copied files')),
4436 (b'C', b'copies', None, _(b'show copied files')),
4458 (
4437 (
4459 b'k',
4438 b'k',
4460 b'keyword',
4439 b'keyword',
4461 [],
4440 [],
4462 _(b'do case-insensitive search for a given text'),
4441 _(b'do case-insensitive search for a given text'),
4463 _(b'TEXT'),
4442 _(b'TEXT'),
4464 ),
4443 ),
4465 (
4444 (
4466 b'r',
4445 b'r',
4467 b'rev',
4446 b'rev',
4468 [],
4447 [],
4469 _(b'revisions to select or follow from'),
4448 _(b'revisions to select or follow from'),
4470 _(b'REV'),
4449 _(b'REV'),
4471 ),
4450 ),
4472 (
4451 (
4473 b'L',
4452 b'L',
4474 b'line-range',
4453 b'line-range',
4475 [],
4454 [],
4476 _(b'follow line range of specified file (EXPERIMENTAL)'),
4455 _(b'follow line range of specified file (EXPERIMENTAL)'),
4477 _(b'FILE,RANGE'),
4456 _(b'FILE,RANGE'),
4478 ),
4457 ),
4479 (
4458 (
4480 b'',
4459 b'',
4481 b'removed',
4460 b'removed',
4482 None,
4461 None,
4483 _(b'include revisions where files were removed'),
4462 _(b'include revisions where files were removed'),
4484 ),
4463 ),
4485 (
4464 (
4486 b'm',
4465 b'm',
4487 b'only-merges',
4466 b'only-merges',
4488 None,
4467 None,
4489 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4468 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4490 ),
4469 ),
4491 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4470 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4492 (
4471 (
4493 b'',
4472 b'',
4494 b'only-branch',
4473 b'only-branch',
4495 [],
4474 [],
4496 _(
4475 _(
4497 b'show only changesets within the given named branch (DEPRECATED)'
4476 b'show only changesets within the given named branch (DEPRECATED)'
4498 ),
4477 ),
4499 _(b'BRANCH'),
4478 _(b'BRANCH'),
4500 ),
4479 ),
4501 (
4480 (
4502 b'b',
4481 b'b',
4503 b'branch',
4482 b'branch',
4504 [],
4483 [],
4505 _(b'show changesets within the given named branch'),
4484 _(b'show changesets within the given named branch'),
4506 _(b'BRANCH'),
4485 _(b'BRANCH'),
4507 ),
4486 ),
4508 (
4487 (
4509 b'B',
4488 b'B',
4510 b'bookmark',
4489 b'bookmark',
4511 [],
4490 [],
4512 _(b"show changesets within the given bookmark"),
4491 _(b"show changesets within the given bookmark"),
4513 _(b'BOOKMARK'),
4492 _(b'BOOKMARK'),
4514 ),
4493 ),
4515 (
4494 (
4516 b'P',
4495 b'P',
4517 b'prune',
4496 b'prune',
4518 [],
4497 [],
4519 _(b'do not display revision or any of its ancestors'),
4498 _(b'do not display revision or any of its ancestors'),
4520 _(b'REV'),
4499 _(b'REV'),
4521 ),
4500 ),
4522 ]
4501 ]
4523 + logopts
4502 + logopts
4524 + walkopts,
4503 + walkopts,
4525 _(b'[OPTION]... [FILE]'),
4504 _(b'[OPTION]... [FILE]'),
4526 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4505 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4527 helpbasic=True,
4506 helpbasic=True,
4528 inferrepo=True,
4507 inferrepo=True,
4529 intents={INTENT_READONLY},
4508 intents={INTENT_READONLY},
4530 )
4509 )
4531 def log(ui, repo, *pats, **opts):
4510 def log(ui, repo, *pats, **opts):
4532 """show revision history of entire repository or files
4511 """show revision history of entire repository or files
4533
4512
4534 Print the revision history of the specified files or the entire
4513 Print the revision history of the specified files or the entire
4535 project.
4514 project.
4536
4515
4537 If no revision range is specified, the default is ``tip:0`` unless
4516 If no revision range is specified, the default is ``tip:0`` unless
4538 --follow is set.
4517 --follow is set.
4539
4518
4540 File history is shown without following rename or copy history of
4519 File history is shown without following rename or copy history of
4541 files. Use -f/--follow with a filename to follow history across
4520 files. Use -f/--follow with a filename to follow history across
4542 renames and copies. --follow without a filename will only show
4521 renames and copies. --follow without a filename will only show
4543 ancestors of the starting revisions. The starting revisions can be
4522 ancestors of the starting revisions. The starting revisions can be
4544 specified by -r/--rev, which default to the working directory parent.
4523 specified by -r/--rev, which default to the working directory parent.
4545
4524
4546 By default this command prints revision number and changeset id,
4525 By default this command prints revision number and changeset id,
4547 tags, non-trivial parents, user, date and time, and a summary for
4526 tags, non-trivial parents, user, date and time, and a summary for
4548 each commit. When the -v/--verbose switch is used, the list of
4527 each commit. When the -v/--verbose switch is used, the list of
4549 changed files and full commit message are shown.
4528 changed files and full commit message are shown.
4550
4529
4551 With --graph the revisions are shown as an ASCII art DAG with the most
4530 With --graph the revisions are shown as an ASCII art DAG with the most
4552 recent changeset at the top.
4531 recent changeset at the top.
4553 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4532 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4554 involved in an unresolved merge conflict, '_' closes a branch,
4533 involved in an unresolved merge conflict, '_' closes a branch,
4555 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4534 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4556 changeset from the lines below is a parent of the 'o' merge on the same
4535 changeset from the lines below is a parent of the 'o' merge on the same
4557 line.
4536 line.
4558 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4537 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4559 of a '|' indicates one or more revisions in a path are omitted.
4538 of a '|' indicates one or more revisions in a path are omitted.
4560
4539
4561 .. container:: verbose
4540 .. container:: verbose
4562
4541
4563 Use -L/--line-range FILE,M:N options to follow the history of lines
4542 Use -L/--line-range FILE,M:N options to follow the history of lines
4564 from M to N in FILE. With -p/--patch only diff hunks affecting
4543 from M to N in FILE. With -p/--patch only diff hunks affecting
4565 specified line range will be shown. This option requires --follow;
4544 specified line range will be shown. This option requires --follow;
4566 it can be specified multiple times. Currently, this option is not
4545 it can be specified multiple times. Currently, this option is not
4567 compatible with --graph. This option is experimental.
4546 compatible with --graph. This option is experimental.
4568
4547
4569 .. note::
4548 .. note::
4570
4549
4571 :hg:`log --patch` may generate unexpected diff output for merge
4550 :hg:`log --patch` may generate unexpected diff output for merge
4572 changesets, as it will only compare the merge changeset against
4551 changesets, as it will only compare the merge changeset against
4573 its first parent. Also, only files different from BOTH parents
4552 its first parent. Also, only files different from BOTH parents
4574 will appear in files:.
4553 will appear in files:.
4575
4554
4576 .. note::
4555 .. note::
4577
4556
4578 For performance reasons, :hg:`log FILE` may omit duplicate changes
4557 For performance reasons, :hg:`log FILE` may omit duplicate changes
4579 made on branches and will not show removals or mode changes. To
4558 made on branches and will not show removals or mode changes. To
4580 see all such changes, use the --removed switch.
4559 see all such changes, use the --removed switch.
4581
4560
4582 .. container:: verbose
4561 .. container:: verbose
4583
4562
4584 .. note::
4563 .. note::
4585
4564
4586 The history resulting from -L/--line-range options depends on diff
4565 The history resulting from -L/--line-range options depends on diff
4587 options; for instance if white-spaces are ignored, respective changes
4566 options; for instance if white-spaces are ignored, respective changes
4588 with only white-spaces in specified line range will not be listed.
4567 with only white-spaces in specified line range will not be listed.
4589
4568
4590 .. container:: verbose
4569 .. container:: verbose
4591
4570
4592 Some examples:
4571 Some examples:
4593
4572
4594 - changesets with full descriptions and file lists::
4573 - changesets with full descriptions and file lists::
4595
4574
4596 hg log -v
4575 hg log -v
4597
4576
4598 - changesets ancestral to the working directory::
4577 - changesets ancestral to the working directory::
4599
4578
4600 hg log -f
4579 hg log -f
4601
4580
4602 - last 10 commits on the current branch::
4581 - last 10 commits on the current branch::
4603
4582
4604 hg log -l 10 -b .
4583 hg log -l 10 -b .
4605
4584
4606 - changesets showing all modifications of a file, including removals::
4585 - changesets showing all modifications of a file, including removals::
4607
4586
4608 hg log --removed file.c
4587 hg log --removed file.c
4609
4588
4610 - all changesets that touch a directory, with diffs, excluding merges::
4589 - all changesets that touch a directory, with diffs, excluding merges::
4611
4590
4612 hg log -Mp lib/
4591 hg log -Mp lib/
4613
4592
4614 - all revision numbers that match a keyword::
4593 - all revision numbers that match a keyword::
4615
4594
4616 hg log -k bug --template "{rev}\\n"
4595 hg log -k bug --template "{rev}\\n"
4617
4596
4618 - the full hash identifier of the working directory parent::
4597 - the full hash identifier of the working directory parent::
4619
4598
4620 hg log -r . --template "{node}\\n"
4599 hg log -r . --template "{node}\\n"
4621
4600
4622 - list available log templates::
4601 - list available log templates::
4623
4602
4624 hg log -T list
4603 hg log -T list
4625
4604
4626 - check if a given changeset is included in a tagged release::
4605 - check if a given changeset is included in a tagged release::
4627
4606
4628 hg log -r "a21ccf and ancestor(1.9)"
4607 hg log -r "a21ccf and ancestor(1.9)"
4629
4608
4630 - find all changesets by some user in a date range::
4609 - find all changesets by some user in a date range::
4631
4610
4632 hg log -k alice -d "may 2008 to jul 2008"
4611 hg log -k alice -d "may 2008 to jul 2008"
4633
4612
4634 - summary of all changesets after the last tag::
4613 - summary of all changesets after the last tag::
4635
4614
4636 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4615 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4637
4616
4638 - changesets touching lines 13 to 23 for file.c::
4617 - changesets touching lines 13 to 23 for file.c::
4639
4618
4640 hg log -L file.c,13:23
4619 hg log -L file.c,13:23
4641
4620
4642 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4621 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4643 main.c with patch::
4622 main.c with patch::
4644
4623
4645 hg log -L file.c,13:23 -L main.c,2:6 -p
4624 hg log -L file.c,13:23 -L main.c,2:6 -p
4646
4625
4647 See :hg:`help dates` for a list of formats valid for -d/--date.
4626 See :hg:`help dates` for a list of formats valid for -d/--date.
4648
4627
4649 See :hg:`help revisions` for more about specifying and ordering
4628 See :hg:`help revisions` for more about specifying and ordering
4650 revisions.
4629 revisions.
4651
4630
4652 See :hg:`help templates` for more about pre-packaged styles and
4631 See :hg:`help templates` for more about pre-packaged styles and
4653 specifying custom templates. The default template used by the log
4632 specifying custom templates. The default template used by the log
4654 command can be customized via the ``command-templates.log`` configuration
4633 command can be customized via the ``command-templates.log`` configuration
4655 setting.
4634 setting.
4656
4635
4657 Returns 0 on success.
4636 Returns 0 on success.
4658
4637
4659 """
4638 """
4660 opts = pycompat.byteskwargs(opts)
4639 opts = pycompat.byteskwargs(opts)
4661 linerange = opts.get(b'line_range')
4640 linerange = opts.get(b'line_range')
4662
4641
4663 if linerange and not opts.get(b'follow'):
4642 if linerange and not opts.get(b'follow'):
4664 raise error.InputError(_(b'--line-range requires --follow'))
4643 raise error.InputError(_(b'--line-range requires --follow'))
4665
4644
4666 if linerange and pats:
4645 if linerange and pats:
4667 # TODO: take pats as patterns with no line-range filter
4646 # TODO: take pats as patterns with no line-range filter
4668 raise error.InputError(
4647 raise error.InputError(
4669 _(b'FILE arguments are not compatible with --line-range option')
4648 _(b'FILE arguments are not compatible with --line-range option')
4670 )
4649 )
4671
4650
4672 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4651 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4673 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4652 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4674 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4653 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4675 if linerange:
4654 if linerange:
4676 # TODO: should follow file history from logcmdutil._initialrevs(),
4655 # TODO: should follow file history from logcmdutil._initialrevs(),
4677 # then filter the result by logcmdutil._makerevset() and --limit
4656 # then filter the result by logcmdutil._makerevset() and --limit
4678 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4657 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4679
4658
4680 getcopies = None
4659 getcopies = None
4681 if opts.get(b'copies'):
4660 if opts.get(b'copies'):
4682 endrev = None
4661 endrev = None
4683 if revs:
4662 if revs:
4684 endrev = revs.max() + 1
4663 endrev = revs.max() + 1
4685 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4664 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4686
4665
4687 ui.pager(b'log')
4666 ui.pager(b'log')
4688 displayer = logcmdutil.changesetdisplayer(
4667 displayer = logcmdutil.changesetdisplayer(
4689 ui, repo, opts, differ, buffered=True
4668 ui, repo, opts, differ, buffered=True
4690 )
4669 )
4691 if opts.get(b'graph'):
4670 if opts.get(b'graph'):
4692 displayfn = logcmdutil.displaygraphrevs
4671 displayfn = logcmdutil.displaygraphrevs
4693 else:
4672 else:
4694 displayfn = logcmdutil.displayrevs
4673 displayfn = logcmdutil.displayrevs
4695 displayfn(ui, repo, revs, displayer, getcopies)
4674 displayfn(ui, repo, revs, displayer, getcopies)
4696
4675
4697
4676
4698 @command(
4677 @command(
4699 b'manifest',
4678 b'manifest',
4700 [
4679 [
4701 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4680 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4702 (b'', b'all', False, _(b"list files from all revisions")),
4681 (b'', b'all', False, _(b"list files from all revisions")),
4703 ]
4682 ]
4704 + formatteropts,
4683 + formatteropts,
4705 _(b'[-r REV]'),
4684 _(b'[-r REV]'),
4706 helpcategory=command.CATEGORY_MAINTENANCE,
4685 helpcategory=command.CATEGORY_MAINTENANCE,
4707 intents={INTENT_READONLY},
4686 intents={INTENT_READONLY},
4708 )
4687 )
4709 def manifest(ui, repo, node=None, rev=None, **opts):
4688 def manifest(ui, repo, node=None, rev=None, **opts):
4710 """output the current or given revision of the project manifest
4689 """output the current or given revision of the project manifest
4711
4690
4712 Print a list of version controlled files for the given revision.
4691 Print a list of version controlled files for the given revision.
4713 If no revision is given, the first parent of the working directory
4692 If no revision is given, the first parent of the working directory
4714 is used, or the null revision if no revision is checked out.
4693 is used, or the null revision if no revision is checked out.
4715
4694
4716 With -v, print file permissions, symlink and executable bits.
4695 With -v, print file permissions, symlink and executable bits.
4717 With --debug, print file revision hashes.
4696 With --debug, print file revision hashes.
4718
4697
4719 If option --all is specified, the list of all files from all revisions
4698 If option --all is specified, the list of all files from all revisions
4720 is printed. This includes deleted and renamed files.
4699 is printed. This includes deleted and renamed files.
4721
4700
4722 Returns 0 on success.
4701 Returns 0 on success.
4723 """
4702 """
4724 opts = pycompat.byteskwargs(opts)
4703 opts = pycompat.byteskwargs(opts)
4725 fm = ui.formatter(b'manifest', opts)
4704 fm = ui.formatter(b'manifest', opts)
4726
4705
4727 if opts.get(b'all'):
4706 if opts.get(b'all'):
4728 if rev or node:
4707 if rev or node:
4729 raise error.InputError(_(b"can't specify a revision with --all"))
4708 raise error.InputError(_(b"can't specify a revision with --all"))
4730
4709
4731 res = set()
4710 res = set()
4732 for rev in repo:
4711 for rev in repo:
4733 ctx = repo[rev]
4712 ctx = repo[rev]
4734 res |= set(ctx.files())
4713 res |= set(ctx.files())
4735
4714
4736 ui.pager(b'manifest')
4715 ui.pager(b'manifest')
4737 for f in sorted(res):
4716 for f in sorted(res):
4738 fm.startitem()
4717 fm.startitem()
4739 fm.write(b"path", b'%s\n', f)
4718 fm.write(b"path", b'%s\n', f)
4740 fm.end()
4719 fm.end()
4741 return
4720 return
4742
4721
4743 if rev and node:
4722 if rev and node:
4744 raise error.InputError(_(b"please specify just one revision"))
4723 raise error.InputError(_(b"please specify just one revision"))
4745
4724
4746 if not node:
4725 if not node:
4747 node = rev
4726 node = rev
4748
4727
4749 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4728 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4750 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4729 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4751 if node:
4730 if node:
4752 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4731 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4753 ctx = scmutil.revsingle(repo, node)
4732 ctx = scmutil.revsingle(repo, node)
4754 mf = ctx.manifest()
4733 mf = ctx.manifest()
4755 ui.pager(b'manifest')
4734 ui.pager(b'manifest')
4756 for f in ctx:
4735 for f in ctx:
4757 fm.startitem()
4736 fm.startitem()
4758 fm.context(ctx=ctx)
4737 fm.context(ctx=ctx)
4759 fl = ctx[f].flags()
4738 fl = ctx[f].flags()
4760 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4739 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4761 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4740 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4762 fm.write(b'path', b'%s\n', f)
4741 fm.write(b'path', b'%s\n', f)
4763 fm.end()
4742 fm.end()
4764
4743
4765
4744
4766 @command(
4745 @command(
4767 b'merge',
4746 b'merge',
4768 [
4747 [
4769 (
4748 (
4770 b'f',
4749 b'f',
4771 b'force',
4750 b'force',
4772 None,
4751 None,
4773 _(b'force a merge including outstanding changes (DEPRECATED)'),
4752 _(b'force a merge including outstanding changes (DEPRECATED)'),
4774 ),
4753 ),
4775 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4754 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4776 (
4755 (
4777 b'P',
4756 b'P',
4778 b'preview',
4757 b'preview',
4779 None,
4758 None,
4780 _(b'review revisions to merge (no merge is performed)'),
4759 _(b'review revisions to merge (no merge is performed)'),
4781 ),
4760 ),
4782 (b'', b'abort', None, _(b'abort the ongoing merge')),
4761 (b'', b'abort', None, _(b'abort the ongoing merge')),
4783 ]
4762 ]
4784 + mergetoolopts,
4763 + mergetoolopts,
4785 _(b'[-P] [[-r] REV]'),
4764 _(b'[-P] [[-r] REV]'),
4786 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4765 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4787 helpbasic=True,
4766 helpbasic=True,
4788 )
4767 )
4789 def merge(ui, repo, node=None, **opts):
4768 def merge(ui, repo, node=None, **opts):
4790 """merge another revision into working directory
4769 """merge another revision into working directory
4791
4770
4792 The current working directory is updated with all changes made in
4771 The current working directory is updated with all changes made in
4793 the requested revision since the last common predecessor revision.
4772 the requested revision since the last common predecessor revision.
4794
4773
4795 Files that changed between either parent are marked as changed for
4774 Files that changed between either parent are marked as changed for
4796 the next commit and a commit must be performed before any further
4775 the next commit and a commit must be performed before any further
4797 updates to the repository are allowed. The next commit will have
4776 updates to the repository are allowed. The next commit will have
4798 two parents.
4777 two parents.
4799
4778
4800 ``--tool`` can be used to specify the merge tool used for file
4779 ``--tool`` can be used to specify the merge tool used for file
4801 merges. It overrides the HGMERGE environment variable and your
4780 merges. It overrides the HGMERGE environment variable and your
4802 configuration files. See :hg:`help merge-tools` for options.
4781 configuration files. See :hg:`help merge-tools` for options.
4803
4782
4804 If no revision is specified, the working directory's parent is a
4783 If no revision is specified, the working directory's parent is a
4805 head revision, and the current branch contains exactly one other
4784 head revision, and the current branch contains exactly one other
4806 head, the other head is merged with by default. Otherwise, an
4785 head, the other head is merged with by default. Otherwise, an
4807 explicit revision with which to merge must be provided.
4786 explicit revision with which to merge must be provided.
4808
4787
4809 See :hg:`help resolve` for information on handling file conflicts.
4788 See :hg:`help resolve` for information on handling file conflicts.
4810
4789
4811 To undo an uncommitted merge, use :hg:`merge --abort` which
4790 To undo an uncommitted merge, use :hg:`merge --abort` which
4812 will check out a clean copy of the original merge parent, losing
4791 will check out a clean copy of the original merge parent, losing
4813 all changes.
4792 all changes.
4814
4793
4815 Returns 0 on success, 1 if there are unresolved files.
4794 Returns 0 on success, 1 if there are unresolved files.
4816 """
4795 """
4817
4796
4818 opts = pycompat.byteskwargs(opts)
4797 opts = pycompat.byteskwargs(opts)
4819 abort = opts.get(b'abort')
4798 abort = opts.get(b'abort')
4820 if abort and repo.dirstate.p2() == nullid:
4799 if abort and repo.dirstate.p2() == nullid:
4821 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4800 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4822 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4801 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4823 if abort:
4802 if abort:
4824 state = cmdutil.getunfinishedstate(repo)
4803 state = cmdutil.getunfinishedstate(repo)
4825 if state and state._opname != b'merge':
4804 if state and state._opname != b'merge':
4826 raise error.StateError(
4805 raise error.StateError(
4827 _(b'cannot abort merge with %s in progress') % (state._opname),
4806 _(b'cannot abort merge with %s in progress') % (state._opname),
4828 hint=state.hint(),
4807 hint=state.hint(),
4829 )
4808 )
4830 if node:
4809 if node:
4831 raise error.InputError(_(b"cannot specify a node with --abort"))
4810 raise error.InputError(_(b"cannot specify a node with --abort"))
4832 return hg.abortmerge(repo.ui, repo)
4811 return hg.abortmerge(repo.ui, repo)
4833
4812
4834 if opts.get(b'rev') and node:
4813 if opts.get(b'rev') and node:
4835 raise error.InputError(_(b"please specify just one revision"))
4814 raise error.InputError(_(b"please specify just one revision"))
4836 if not node:
4815 if not node:
4837 node = opts.get(b'rev')
4816 node = opts.get(b'rev')
4838
4817
4839 if node:
4818 if node:
4840 ctx = scmutil.revsingle(repo, node)
4819 ctx = scmutil.revsingle(repo, node)
4841 else:
4820 else:
4842 if ui.configbool(b'commands', b'merge.require-rev'):
4821 if ui.configbool(b'commands', b'merge.require-rev'):
4843 raise error.InputError(
4822 raise error.InputError(
4844 _(
4823 _(
4845 b'configuration requires specifying revision to merge '
4824 b'configuration requires specifying revision to merge '
4846 b'with'
4825 b'with'
4847 )
4826 )
4848 )
4827 )
4849 ctx = repo[destutil.destmerge(repo)]
4828 ctx = repo[destutil.destmerge(repo)]
4850
4829
4851 if ctx.node() is None:
4830 if ctx.node() is None:
4852 raise error.InputError(
4831 raise error.InputError(
4853 _(b'merging with the working copy has no effect')
4832 _(b'merging with the working copy has no effect')
4854 )
4833 )
4855
4834
4856 if opts.get(b'preview'):
4835 if opts.get(b'preview'):
4857 # find nodes that are ancestors of p2 but not of p1
4836 # find nodes that are ancestors of p2 but not of p1
4858 p1 = repo[b'.'].node()
4837 p1 = repo[b'.'].node()
4859 p2 = ctx.node()
4838 p2 = ctx.node()
4860 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4839 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4861
4840
4862 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4841 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4863 for node in nodes:
4842 for node in nodes:
4864 displayer.show(repo[node])
4843 displayer.show(repo[node])
4865 displayer.close()
4844 displayer.close()
4866 return 0
4845 return 0
4867
4846
4868 # ui.forcemerge is an internal variable, do not document
4847 # ui.forcemerge is an internal variable, do not document
4869 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4848 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4870 with ui.configoverride(overrides, b'merge'):
4849 with ui.configoverride(overrides, b'merge'):
4871 force = opts.get(b'force')
4850 force = opts.get(b'force')
4872 labels = [b'working copy', b'merge rev']
4851 labels = [b'working copy', b'merge rev']
4873 return hg.merge(ctx, force=force, labels=labels)
4852 return hg.merge(ctx, force=force, labels=labels)
4874
4853
4875
4854
4876 statemod.addunfinished(
4855 statemod.addunfinished(
4877 b'merge',
4856 b'merge',
4878 fname=None,
4857 fname=None,
4879 clearable=True,
4858 clearable=True,
4880 allowcommit=True,
4859 allowcommit=True,
4881 cmdmsg=_(b'outstanding uncommitted merge'),
4860 cmdmsg=_(b'outstanding uncommitted merge'),
4882 abortfunc=hg.abortmerge,
4861 abortfunc=hg.abortmerge,
4883 statushint=_(
4862 statushint=_(
4884 b'To continue: hg commit\nTo abort: hg merge --abort'
4863 b'To continue: hg commit\nTo abort: hg merge --abort'
4885 ),
4864 ),
4886 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4865 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4887 )
4866 )
4888
4867
4889
4868
4890 @command(
4869 @command(
4891 b'outgoing|out',
4870 b'outgoing|out',
4892 [
4871 [
4893 (
4872 (
4894 b'f',
4873 b'f',
4895 b'force',
4874 b'force',
4896 None,
4875 None,
4897 _(b'run even when the destination is unrelated'),
4876 _(b'run even when the destination is unrelated'),
4898 ),
4877 ),
4899 (
4878 (
4900 b'r',
4879 b'r',
4901 b'rev',
4880 b'rev',
4902 [],
4881 [],
4903 _(b'a changeset intended to be included in the destination'),
4882 _(b'a changeset intended to be included in the destination'),
4904 _(b'REV'),
4883 _(b'REV'),
4905 ),
4884 ),
4906 (b'n', b'newest-first', None, _(b'show newest record first')),
4885 (b'n', b'newest-first', None, _(b'show newest record first')),
4907 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4886 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4908 (
4887 (
4909 b'b',
4888 b'b',
4910 b'branch',
4889 b'branch',
4911 [],
4890 [],
4912 _(b'a specific branch you would like to push'),
4891 _(b'a specific branch you would like to push'),
4913 _(b'BRANCH'),
4892 _(b'BRANCH'),
4914 ),
4893 ),
4915 ]
4894 ]
4916 + logopts
4895 + logopts
4917 + remoteopts
4896 + remoteopts
4918 + subrepoopts,
4897 + subrepoopts,
4919 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4898 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4920 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4899 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4921 )
4900 )
4922 def outgoing(ui, repo, dest=None, **opts):
4901 def outgoing(ui, repo, dest=None, **opts):
4923 """show changesets not found in the destination
4902 """show changesets not found in the destination
4924
4903
4925 Show changesets not found in the specified destination repository
4904 Show changesets not found in the specified destination repository
4926 or the default push location. These are the changesets that would
4905 or the default push location. These are the changesets that would
4927 be pushed if a push was requested.
4906 be pushed if a push was requested.
4928
4907
4929 See pull for details of valid destination formats.
4908 See pull for details of valid destination formats.
4930
4909
4931 .. container:: verbose
4910 .. container:: verbose
4932
4911
4933 With -B/--bookmarks, the result of bookmark comparison between
4912 With -B/--bookmarks, the result of bookmark comparison between
4934 local and remote repositories is displayed. With -v/--verbose,
4913 local and remote repositories is displayed. With -v/--verbose,
4935 status is also displayed for each bookmark like below::
4914 status is also displayed for each bookmark like below::
4936
4915
4937 BM1 01234567890a added
4916 BM1 01234567890a added
4938 BM2 deleted
4917 BM2 deleted
4939 BM3 234567890abc advanced
4918 BM3 234567890abc advanced
4940 BM4 34567890abcd diverged
4919 BM4 34567890abcd diverged
4941 BM5 4567890abcde changed
4920 BM5 4567890abcde changed
4942
4921
4943 The action taken when pushing depends on the
4922 The action taken when pushing depends on the
4944 status of each bookmark:
4923 status of each bookmark:
4945
4924
4946 :``added``: push with ``-B`` will create it
4925 :``added``: push with ``-B`` will create it
4947 :``deleted``: push with ``-B`` will delete it
4926 :``deleted``: push with ``-B`` will delete it
4948 :``advanced``: push will update it
4927 :``advanced``: push will update it
4949 :``diverged``: push with ``-B`` will update it
4928 :``diverged``: push with ``-B`` will update it
4950 :``changed``: push with ``-B`` will update it
4929 :``changed``: push with ``-B`` will update it
4951
4930
4952 From the point of view of pushing behavior, bookmarks
4931 From the point of view of pushing behavior, bookmarks
4953 existing only in the remote repository are treated as
4932 existing only in the remote repository are treated as
4954 ``deleted``, even if it is in fact added remotely.
4933 ``deleted``, even if it is in fact added remotely.
4955
4934
4956 Returns 0 if there are outgoing changes, 1 otherwise.
4935 Returns 0 if there are outgoing changes, 1 otherwise.
4957 """
4936 """
4958 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4937 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4959 # style URLs, so don't overwrite dest.
4938 # style URLs, so don't overwrite dest.
4960 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4939 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4961 if not path:
4940 if not path:
4962 raise error.ConfigError(
4941 raise error.ConfigError(
4963 _(b'default repository not configured!'),
4942 _(b'default repository not configured!'),
4964 hint=_(b"see 'hg help config.paths'"),
4943 hint=_(b"see 'hg help config.paths'"),
4965 )
4944 )
4966
4945
4967 opts = pycompat.byteskwargs(opts)
4946 opts = pycompat.byteskwargs(opts)
4968 if opts.get(b'graph'):
4947 if opts.get(b'graph'):
4969 logcmdutil.checkunsupportedgraphflags([], opts)
4948 logcmdutil.checkunsupportedgraphflags([], opts)
4970 o, other = hg._outgoing(ui, repo, dest, opts)
4949 o, other = hg._outgoing(ui, repo, dest, opts)
4971 if not o:
4950 if not o:
4972 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4951 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4973 return
4952 return
4974
4953
4975 revdag = logcmdutil.graphrevs(repo, o, opts)
4954 revdag = logcmdutil.graphrevs(repo, o, opts)
4976 ui.pager(b'outgoing')
4955 ui.pager(b'outgoing')
4977 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4956 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4978 logcmdutil.displaygraph(
4957 logcmdutil.displaygraph(
4979 ui, repo, revdag, displayer, graphmod.asciiedges
4958 ui, repo, revdag, displayer, graphmod.asciiedges
4980 )
4959 )
4981 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4960 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4982 return 0
4961 return 0
4983
4962
4984 if opts.get(b'bookmarks'):
4963 if opts.get(b'bookmarks'):
4985 dest = path.pushloc or path.loc
4964 dest = path.pushloc or path.loc
4986 other = hg.peer(repo, opts, dest)
4965 other = hg.peer(repo, opts, dest)
4987 if b'bookmarks' not in other.listkeys(b'namespaces'):
4966 if b'bookmarks' not in other.listkeys(b'namespaces'):
4988 ui.warn(_(b"remote doesn't support bookmarks\n"))
4967 ui.warn(_(b"remote doesn't support bookmarks\n"))
4989 return 0
4968 return 0
4990 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4969 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4991 ui.pager(b'outgoing')
4970 ui.pager(b'outgoing')
4992 return bookmarks.outgoing(ui, repo, other)
4971 return bookmarks.outgoing(ui, repo, other)
4993
4972
4994 repo._subtoppath = path.pushloc or path.loc
4973 repo._subtoppath = path.pushloc or path.loc
4995 try:
4974 try:
4996 return hg.outgoing(ui, repo, dest, opts)
4975 return hg.outgoing(ui, repo, dest, opts)
4997 finally:
4976 finally:
4998 del repo._subtoppath
4977 del repo._subtoppath
4999
4978
5000
4979
5001 @command(
4980 @command(
5002 b'parents',
4981 b'parents',
5003 [
4982 [
5004 (
4983 (
5005 b'r',
4984 b'r',
5006 b'rev',
4985 b'rev',
5007 b'',
4986 b'',
5008 _(b'show parents of the specified revision'),
4987 _(b'show parents of the specified revision'),
5009 _(b'REV'),
4988 _(b'REV'),
5010 ),
4989 ),
5011 ]
4990 ]
5012 + templateopts,
4991 + templateopts,
5013 _(b'[-r REV] [FILE]'),
4992 _(b'[-r REV] [FILE]'),
5014 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4993 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5015 inferrepo=True,
4994 inferrepo=True,
5016 )
4995 )
5017 def parents(ui, repo, file_=None, **opts):
4996 def parents(ui, repo, file_=None, **opts):
5018 """show the parents of the working directory or revision (DEPRECATED)
4997 """show the parents of the working directory or revision (DEPRECATED)
5019
4998
5020 Print the working directory's parent revisions. If a revision is
4999 Print the working directory's parent revisions. If a revision is
5021 given via -r/--rev, the parent of that revision will be printed.
5000 given via -r/--rev, the parent of that revision will be printed.
5022 If a file argument is given, the revision in which the file was
5001 If a file argument is given, the revision in which the file was
5023 last changed (before the working directory revision or the
5002 last changed (before the working directory revision or the
5024 argument to --rev if given) is printed.
5003 argument to --rev if given) is printed.
5025
5004
5026 This command is equivalent to::
5005 This command is equivalent to::
5027
5006
5028 hg log -r "p1()+p2()" or
5007 hg log -r "p1()+p2()" or
5029 hg log -r "p1(REV)+p2(REV)" or
5008 hg log -r "p1(REV)+p2(REV)" or
5030 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5009 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5031 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5010 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5032
5011
5033 See :hg:`summary` and :hg:`help revsets` for related information.
5012 See :hg:`summary` and :hg:`help revsets` for related information.
5034
5013
5035 Returns 0 on success.
5014 Returns 0 on success.
5036 """
5015 """
5037
5016
5038 opts = pycompat.byteskwargs(opts)
5017 opts = pycompat.byteskwargs(opts)
5039 rev = opts.get(b'rev')
5018 rev = opts.get(b'rev')
5040 if rev:
5019 if rev:
5041 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5020 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5042 ctx = scmutil.revsingle(repo, rev, None)
5021 ctx = scmutil.revsingle(repo, rev, None)
5043
5022
5044 if file_:
5023 if file_:
5045 m = scmutil.match(ctx, (file_,), opts)
5024 m = scmutil.match(ctx, (file_,), opts)
5046 if m.anypats() or len(m.files()) != 1:
5025 if m.anypats() or len(m.files()) != 1:
5047 raise error.InputError(_(b'can only specify an explicit filename'))
5026 raise error.InputError(_(b'can only specify an explicit filename'))
5048 file_ = m.files()[0]
5027 file_ = m.files()[0]
5049 filenodes = []
5028 filenodes = []
5050 for cp in ctx.parents():
5029 for cp in ctx.parents():
5051 if not cp:
5030 if not cp:
5052 continue
5031 continue
5053 try:
5032 try:
5054 filenodes.append(cp.filenode(file_))
5033 filenodes.append(cp.filenode(file_))
5055 except error.LookupError:
5034 except error.LookupError:
5056 pass
5035 pass
5057 if not filenodes:
5036 if not filenodes:
5058 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5037 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5059 p = []
5038 p = []
5060 for fn in filenodes:
5039 for fn in filenodes:
5061 fctx = repo.filectx(file_, fileid=fn)
5040 fctx = repo.filectx(file_, fileid=fn)
5062 p.append(fctx.node())
5041 p.append(fctx.node())
5063 else:
5042 else:
5064 p = [cp.node() for cp in ctx.parents()]
5043 p = [cp.node() for cp in ctx.parents()]
5065
5044
5066 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5045 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5067 for n in p:
5046 for n in p:
5068 if n != nullid:
5047 if n != nullid:
5069 displayer.show(repo[n])
5048 displayer.show(repo[n])
5070 displayer.close()
5049 displayer.close()
5071
5050
5072
5051
5073 @command(
5052 @command(
5074 b'paths',
5053 b'paths',
5075 formatteropts,
5054 formatteropts,
5076 _(b'[NAME]'),
5055 _(b'[NAME]'),
5077 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5056 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5078 optionalrepo=True,
5057 optionalrepo=True,
5079 intents={INTENT_READONLY},
5058 intents={INTENT_READONLY},
5080 )
5059 )
5081 def paths(ui, repo, search=None, **opts):
5060 def paths(ui, repo, search=None, **opts):
5082 """show aliases for remote repositories
5061 """show aliases for remote repositories
5083
5062
5084 Show definition of symbolic path name NAME. If no name is given,
5063 Show definition of symbolic path name NAME. If no name is given,
5085 show definition of all available names.
5064 show definition of all available names.
5086
5065
5087 Option -q/--quiet suppresses all output when searching for NAME
5066 Option -q/--quiet suppresses all output when searching for NAME
5088 and shows only the path names when listing all definitions.
5067 and shows only the path names when listing all definitions.
5089
5068
5090 Path names are defined in the [paths] section of your
5069 Path names are defined in the [paths] section of your
5091 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5070 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5092 repository, ``.hg/hgrc`` is used, too.
5071 repository, ``.hg/hgrc`` is used, too.
5093
5072
5094 The path names ``default`` and ``default-push`` have a special
5073 The path names ``default`` and ``default-push`` have a special
5095 meaning. When performing a push or pull operation, they are used
5074 meaning. When performing a push or pull operation, they are used
5096 as fallbacks if no location is specified on the command-line.
5075 as fallbacks if no location is specified on the command-line.
5097 When ``default-push`` is set, it will be used for push and
5076 When ``default-push`` is set, it will be used for push and
5098 ``default`` will be used for pull; otherwise ``default`` is used
5077 ``default`` will be used for pull; otherwise ``default`` is used
5099 as the fallback for both. When cloning a repository, the clone
5078 as the fallback for both. When cloning a repository, the clone
5100 source is written as ``default`` in ``.hg/hgrc``.
5079 source is written as ``default`` in ``.hg/hgrc``.
5101
5080
5102 .. note::
5081 .. note::
5103
5082
5104 ``default`` and ``default-push`` apply to all inbound (e.g.
5083 ``default`` and ``default-push`` apply to all inbound (e.g.
5105 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5084 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5106 and :hg:`bundle`) operations.
5085 and :hg:`bundle`) operations.
5107
5086
5108 See :hg:`help urls` for more information.
5087 See :hg:`help urls` for more information.
5109
5088
5110 .. container:: verbose
5089 .. container:: verbose
5111
5090
5112 Template:
5091 Template:
5113
5092
5114 The following keywords are supported. See also :hg:`help templates`.
5093 The following keywords are supported. See also :hg:`help templates`.
5115
5094
5116 :name: String. Symbolic name of the path alias.
5095 :name: String. Symbolic name of the path alias.
5117 :pushurl: String. URL for push operations.
5096 :pushurl: String. URL for push operations.
5118 :url: String. URL or directory path for the other operations.
5097 :url: String. URL or directory path for the other operations.
5119
5098
5120 Returns 0 on success.
5099 Returns 0 on success.
5121 """
5100 """
5122
5101
5123 opts = pycompat.byteskwargs(opts)
5102 opts = pycompat.byteskwargs(opts)
5124 ui.pager(b'paths')
5103 ui.pager(b'paths')
5125 if search:
5104 if search:
5126 pathitems = [
5105 pathitems = [
5127 (name, path)
5106 (name, path)
5128 for name, path in pycompat.iteritems(ui.paths)
5107 for name, path in pycompat.iteritems(ui.paths)
5129 if name == search
5108 if name == search
5130 ]
5109 ]
5131 else:
5110 else:
5132 pathitems = sorted(pycompat.iteritems(ui.paths))
5111 pathitems = sorted(pycompat.iteritems(ui.paths))
5133
5112
5134 fm = ui.formatter(b'paths', opts)
5113 fm = ui.formatter(b'paths', opts)
5135 if fm.isplain():
5114 if fm.isplain():
5136 hidepassword = util.hidepassword
5115 hidepassword = util.hidepassword
5137 else:
5116 else:
5138 hidepassword = bytes
5117 hidepassword = bytes
5139 if ui.quiet:
5118 if ui.quiet:
5140 namefmt = b'%s\n'
5119 namefmt = b'%s\n'
5141 else:
5120 else:
5142 namefmt = b'%s = '
5121 namefmt = b'%s = '
5143 showsubopts = not search and not ui.quiet
5122 showsubopts = not search and not ui.quiet
5144
5123
5145 for name, path in pathitems:
5124 for name, path in pathitems:
5146 fm.startitem()
5125 fm.startitem()
5147 fm.condwrite(not search, b'name', namefmt, name)
5126 fm.condwrite(not search, b'name', namefmt, name)
5148 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5127 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5149 for subopt, value in sorted(path.suboptions.items()):
5128 for subopt, value in sorted(path.suboptions.items()):
5150 assert subopt not in (b'name', b'url')
5129 assert subopt not in (b'name', b'url')
5151 if showsubopts:
5130 if showsubopts:
5152 fm.plain(b'%s:%s = ' % (name, subopt))
5131 fm.plain(b'%s:%s = ' % (name, subopt))
5153 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5132 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5154
5133
5155 fm.end()
5134 fm.end()
5156
5135
5157 if search and not pathitems:
5136 if search and not pathitems:
5158 if not ui.quiet:
5137 if not ui.quiet:
5159 ui.warn(_(b"not found!\n"))
5138 ui.warn(_(b"not found!\n"))
5160 return 1
5139 return 1
5161 else:
5140 else:
5162 return 0
5141 return 0
5163
5142
5164
5143
5165 @command(
5144 @command(
5166 b'phase',
5145 b'phase',
5167 [
5146 [
5168 (b'p', b'public', False, _(b'set changeset phase to public')),
5147 (b'p', b'public', False, _(b'set changeset phase to public')),
5169 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5148 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5170 (b's', b'secret', False, _(b'set changeset phase to secret')),
5149 (b's', b'secret', False, _(b'set changeset phase to secret')),
5171 (b'f', b'force', False, _(b'allow to move boundary backward')),
5150 (b'f', b'force', False, _(b'allow to move boundary backward')),
5172 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5151 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5173 ],
5152 ],
5174 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5153 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5175 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5154 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5176 )
5155 )
5177 def phase(ui, repo, *revs, **opts):
5156 def phase(ui, repo, *revs, **opts):
5178 """set or show the current phase name
5157 """set or show the current phase name
5179
5158
5180 With no argument, show the phase name of the current revision(s).
5159 With no argument, show the phase name of the current revision(s).
5181
5160
5182 With one of -p/--public, -d/--draft or -s/--secret, change the
5161 With one of -p/--public, -d/--draft or -s/--secret, change the
5183 phase value of the specified revisions.
5162 phase value of the specified revisions.
5184
5163
5185 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5164 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5186 lower phase to a higher phase. Phases are ordered as follows::
5165 lower phase to a higher phase. Phases are ordered as follows::
5187
5166
5188 public < draft < secret
5167 public < draft < secret
5189
5168
5190 Returns 0 on success, 1 if some phases could not be changed.
5169 Returns 0 on success, 1 if some phases could not be changed.
5191
5170
5192 (For more information about the phases concept, see :hg:`help phases`.)
5171 (For more information about the phases concept, see :hg:`help phases`.)
5193 """
5172 """
5194 opts = pycompat.byteskwargs(opts)
5173 opts = pycompat.byteskwargs(opts)
5195 # search for a unique phase argument
5174 # search for a unique phase argument
5196 targetphase = None
5175 targetphase = None
5197 for idx, name in enumerate(phases.cmdphasenames):
5176 for idx, name in enumerate(phases.cmdphasenames):
5198 if opts[name]:
5177 if opts[name]:
5199 if targetphase is not None:
5178 if targetphase is not None:
5200 raise error.InputError(_(b'only one phase can be specified'))
5179 raise error.InputError(_(b'only one phase can be specified'))
5201 targetphase = idx
5180 targetphase = idx
5202
5181
5203 # look for specified revision
5182 # look for specified revision
5204 revs = list(revs)
5183 revs = list(revs)
5205 revs.extend(opts[b'rev'])
5184 revs.extend(opts[b'rev'])
5206 if not revs:
5185 if not revs:
5207 # display both parents as the second parent phase can influence
5186 # display both parents as the second parent phase can influence
5208 # the phase of a merge commit
5187 # the phase of a merge commit
5209 revs = [c.rev() for c in repo[None].parents()]
5188 revs = [c.rev() for c in repo[None].parents()]
5210
5189
5211 revs = scmutil.revrange(repo, revs)
5190 revs = scmutil.revrange(repo, revs)
5212
5191
5213 ret = 0
5192 ret = 0
5214 if targetphase is None:
5193 if targetphase is None:
5215 # display
5194 # display
5216 for r in revs:
5195 for r in revs:
5217 ctx = repo[r]
5196 ctx = repo[r]
5218 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5197 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5219 else:
5198 else:
5220 with repo.lock(), repo.transaction(b"phase") as tr:
5199 with repo.lock(), repo.transaction(b"phase") as tr:
5221 # set phase
5200 # set phase
5222 if not revs:
5201 if not revs:
5223 raise error.InputError(_(b'empty revision set'))
5202 raise error.InputError(_(b'empty revision set'))
5224 nodes = [repo[r].node() for r in revs]
5203 nodes = [repo[r].node() for r in revs]
5225 # moving revision from public to draft may hide them
5204 # moving revision from public to draft may hide them
5226 # We have to check result on an unfiltered repository
5205 # We have to check result on an unfiltered repository
5227 unfi = repo.unfiltered()
5206 unfi = repo.unfiltered()
5228 getphase = unfi._phasecache.phase
5207 getphase = unfi._phasecache.phase
5229 olddata = [getphase(unfi, r) for r in unfi]
5208 olddata = [getphase(unfi, r) for r in unfi]
5230 phases.advanceboundary(repo, tr, targetphase, nodes)
5209 phases.advanceboundary(repo, tr, targetphase, nodes)
5231 if opts[b'force']:
5210 if opts[b'force']:
5232 phases.retractboundary(repo, tr, targetphase, nodes)
5211 phases.retractboundary(repo, tr, targetphase, nodes)
5233 getphase = unfi._phasecache.phase
5212 getphase = unfi._phasecache.phase
5234 newdata = [getphase(unfi, r) for r in unfi]
5213 newdata = [getphase(unfi, r) for r in unfi]
5235 changes = sum(newdata[r] != olddata[r] for r in unfi)
5214 changes = sum(newdata[r] != olddata[r] for r in unfi)
5236 cl = unfi.changelog
5215 cl = unfi.changelog
5237 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5216 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5238 if rejected:
5217 if rejected:
5239 ui.warn(
5218 ui.warn(
5240 _(
5219 _(
5241 b'cannot move %i changesets to a higher '
5220 b'cannot move %i changesets to a higher '
5242 b'phase, use --force\n'
5221 b'phase, use --force\n'
5243 )
5222 )
5244 % len(rejected)
5223 % len(rejected)
5245 )
5224 )
5246 ret = 1
5225 ret = 1
5247 if changes:
5226 if changes:
5248 msg = _(b'phase changed for %i changesets\n') % changes
5227 msg = _(b'phase changed for %i changesets\n') % changes
5249 if ret:
5228 if ret:
5250 ui.status(msg)
5229 ui.status(msg)
5251 else:
5230 else:
5252 ui.note(msg)
5231 ui.note(msg)
5253 else:
5232 else:
5254 ui.warn(_(b'no phases changed\n'))
5233 ui.warn(_(b'no phases changed\n'))
5255 return ret
5234 return ret
5256
5235
5257
5236
5258 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5237 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5259 """Run after a changegroup has been added via pull/unbundle
5238 """Run after a changegroup has been added via pull/unbundle
5260
5239
5261 This takes arguments below:
5240 This takes arguments below:
5262
5241
5263 :modheads: change of heads by pull/unbundle
5242 :modheads: change of heads by pull/unbundle
5264 :optupdate: updating working directory is needed or not
5243 :optupdate: updating working directory is needed or not
5265 :checkout: update destination revision (or None to default destination)
5244 :checkout: update destination revision (or None to default destination)
5266 :brev: a name, which might be a bookmark to be activated after updating
5245 :brev: a name, which might be a bookmark to be activated after updating
5267 """
5246 """
5268 if modheads == 0:
5247 if modheads == 0:
5269 return
5248 return
5270 if optupdate:
5249 if optupdate:
5271 try:
5250 try:
5272 return hg.updatetotally(ui, repo, checkout, brev)
5251 return hg.updatetotally(ui, repo, checkout, brev)
5273 except error.UpdateAbort as inst:
5252 except error.UpdateAbort as inst:
5274 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5253 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5275 hint = inst.hint
5254 hint = inst.hint
5276 raise error.UpdateAbort(msg, hint=hint)
5255 raise error.UpdateAbort(msg, hint=hint)
5277 if modheads is not None and modheads > 1:
5256 if modheads is not None and modheads > 1:
5278 currentbranchheads = len(repo.branchheads())
5257 currentbranchheads = len(repo.branchheads())
5279 if currentbranchheads == modheads:
5258 if currentbranchheads == modheads:
5280 ui.status(
5259 ui.status(
5281 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5260 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5282 )
5261 )
5283 elif currentbranchheads > 1:
5262 elif currentbranchheads > 1:
5284 ui.status(
5263 ui.status(
5285 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5264 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5286 )
5265 )
5287 else:
5266 else:
5288 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5267 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5289 elif not ui.configbool(b'commands', b'update.requiredest'):
5268 elif not ui.configbool(b'commands', b'update.requiredest'):
5290 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5269 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5291
5270
5292
5271
5293 @command(
5272 @command(
5294 b'pull',
5273 b'pull',
5295 [
5274 [
5296 (
5275 (
5297 b'u',
5276 b'u',
5298 b'update',
5277 b'update',
5299 None,
5278 None,
5300 _(b'update to new branch head if new descendants were pulled'),
5279 _(b'update to new branch head if new descendants were pulled'),
5301 ),
5280 ),
5302 (
5281 (
5303 b'f',
5282 b'f',
5304 b'force',
5283 b'force',
5305 None,
5284 None,
5306 _(b'run even when remote repository is unrelated'),
5285 _(b'run even when remote repository is unrelated'),
5307 ),
5286 ),
5308 (
5287 (
5309 b'',
5288 b'',
5310 b'confirm',
5289 b'confirm',
5311 None,
5290 None,
5312 _(b'confirm pull before applying changes'),
5291 _(b'confirm pull before applying changes'),
5313 ),
5292 ),
5314 (
5293 (
5315 b'r',
5294 b'r',
5316 b'rev',
5295 b'rev',
5317 [],
5296 [],
5318 _(b'a remote changeset intended to be added'),
5297 _(b'a remote changeset intended to be added'),
5319 _(b'REV'),
5298 _(b'REV'),
5320 ),
5299 ),
5321 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5300 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5322 (
5301 (
5323 b'b',
5302 b'b',
5324 b'branch',
5303 b'branch',
5325 [],
5304 [],
5326 _(b'a specific branch you would like to pull'),
5305 _(b'a specific branch you would like to pull'),
5327 _(b'BRANCH'),
5306 _(b'BRANCH'),
5328 ),
5307 ),
5329 ]
5308 ]
5330 + remoteopts,
5309 + remoteopts,
5331 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5310 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5332 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5311 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5333 helpbasic=True,
5312 helpbasic=True,
5334 )
5313 )
5335 def pull(ui, repo, source=b"default", **opts):
5314 def pull(ui, repo, source=b"default", **opts):
5336 """pull changes from the specified source
5315 """pull changes from the specified source
5337
5316
5338 Pull changes from a remote repository to a local one.
5317 Pull changes from a remote repository to a local one.
5339
5318
5340 This finds all changes from the repository at the specified path
5319 This finds all changes from the repository at the specified path
5341 or URL and adds them to a local repository (the current one unless
5320 or URL and adds them to a local repository (the current one unless
5342 -R is specified). By default, this does not update the copy of the
5321 -R is specified). By default, this does not update the copy of the
5343 project in the working directory.
5322 project in the working directory.
5344
5323
5345 When cloning from servers that support it, Mercurial may fetch
5324 When cloning from servers that support it, Mercurial may fetch
5346 pre-generated data. When this is done, hooks operating on incoming
5325 pre-generated data. When this is done, hooks operating on incoming
5347 changesets and changegroups may fire more than once, once for each
5326 changesets and changegroups may fire more than once, once for each
5348 pre-generated bundle and as well as for any additional remaining
5327 pre-generated bundle and as well as for any additional remaining
5349 data. See :hg:`help -e clonebundles` for more.
5328 data. See :hg:`help -e clonebundles` for more.
5350
5329
5351 Use :hg:`incoming` if you want to see what would have been added
5330 Use :hg:`incoming` if you want to see what would have been added
5352 by a pull at the time you issued this command. If you then decide
5331 by a pull at the time you issued this command. If you then decide
5353 to add those changes to the repository, you should use :hg:`pull
5332 to add those changes to the repository, you should use :hg:`pull
5354 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5333 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5355
5334
5356 If SOURCE is omitted, the 'default' path will be used.
5335 If SOURCE is omitted, the 'default' path will be used.
5357 See :hg:`help urls` for more information.
5336 See :hg:`help urls` for more information.
5358
5337
5359 Specifying bookmark as ``.`` is equivalent to specifying the active
5338 Specifying bookmark as ``.`` is equivalent to specifying the active
5360 bookmark's name.
5339 bookmark's name.
5361
5340
5362 Returns 0 on success, 1 if an update had unresolved files.
5341 Returns 0 on success, 1 if an update had unresolved files.
5363 """
5342 """
5364
5343
5365 opts = pycompat.byteskwargs(opts)
5344 opts = pycompat.byteskwargs(opts)
5366 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5345 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5367 b'update'
5346 b'update'
5368 ):
5347 ):
5369 msg = _(b'update destination required by configuration')
5348 msg = _(b'update destination required by configuration')
5370 hint = _(b'use hg pull followed by hg update DEST')
5349 hint = _(b'use hg pull followed by hg update DEST')
5371 raise error.InputError(msg, hint=hint)
5350 raise error.InputError(msg, hint=hint)
5372
5351
5373 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5352 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5374 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5353 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5375 ui.flush()
5354 ui.flush()
5376 other = hg.peer(repo, opts, source)
5355 other = hg.peer(repo, opts, source)
5377 try:
5356 try:
5378 revs, checkout = hg.addbranchrevs(
5357 revs, checkout = hg.addbranchrevs(
5379 repo, other, branches, opts.get(b'rev')
5358 repo, other, branches, opts.get(b'rev')
5380 )
5359 )
5381
5360
5382 pullopargs = {}
5361 pullopargs = {}
5383
5362
5384 nodes = None
5363 nodes = None
5385 if opts.get(b'bookmark') or revs:
5364 if opts.get(b'bookmark') or revs:
5386 # The list of bookmark used here is the same used to actually update
5365 # The list of bookmark used here is the same used to actually update
5387 # the bookmark names, to avoid the race from issue 4689 and we do
5366 # the bookmark names, to avoid the race from issue 4689 and we do
5388 # all lookup and bookmark queries in one go so they see the same
5367 # all lookup and bookmark queries in one go so they see the same
5389 # version of the server state (issue 4700).
5368 # version of the server state (issue 4700).
5390 nodes = []
5369 nodes = []
5391 fnodes = []
5370 fnodes = []
5392 revs = revs or []
5371 revs = revs or []
5393 if revs and not other.capable(b'lookup'):
5372 if revs and not other.capable(b'lookup'):
5394 err = _(
5373 err = _(
5395 b"other repository doesn't support revision lookup, "
5374 b"other repository doesn't support revision lookup, "
5396 b"so a rev cannot be specified."
5375 b"so a rev cannot be specified."
5397 )
5376 )
5398 raise error.Abort(err)
5377 raise error.Abort(err)
5399 with other.commandexecutor() as e:
5378 with other.commandexecutor() as e:
5400 fremotebookmarks = e.callcommand(
5379 fremotebookmarks = e.callcommand(
5401 b'listkeys', {b'namespace': b'bookmarks'}
5380 b'listkeys', {b'namespace': b'bookmarks'}
5402 )
5381 )
5403 for r in revs:
5382 for r in revs:
5404 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5383 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5405 remotebookmarks = fremotebookmarks.result()
5384 remotebookmarks = fremotebookmarks.result()
5406 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5385 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5407 pullopargs[b'remotebookmarks'] = remotebookmarks
5386 pullopargs[b'remotebookmarks'] = remotebookmarks
5408 for b in opts.get(b'bookmark', []):
5387 for b in opts.get(b'bookmark', []):
5409 b = repo._bookmarks.expandname(b)
5388 b = repo._bookmarks.expandname(b)
5410 if b not in remotebookmarks:
5389 if b not in remotebookmarks:
5411 raise error.InputError(
5390 raise error.InputError(
5412 _(b'remote bookmark %s not found!') % b
5391 _(b'remote bookmark %s not found!') % b
5413 )
5392 )
5414 nodes.append(remotebookmarks[b])
5393 nodes.append(remotebookmarks[b])
5415 for i, rev in enumerate(revs):
5394 for i, rev in enumerate(revs):
5416 node = fnodes[i].result()
5395 node = fnodes[i].result()
5417 nodes.append(node)
5396 nodes.append(node)
5418 if rev == checkout:
5397 if rev == checkout:
5419 checkout = node
5398 checkout = node
5420
5399
5421 wlock = util.nullcontextmanager()
5400 wlock = util.nullcontextmanager()
5422 if opts.get(b'update'):
5401 if opts.get(b'update'):
5423 wlock = repo.wlock()
5402 wlock = repo.wlock()
5424 with wlock:
5403 with wlock:
5425 pullopargs.update(opts.get(b'opargs', {}))
5404 pullopargs.update(opts.get(b'opargs', {}))
5426 modheads = exchange.pull(
5405 modheads = exchange.pull(
5427 repo,
5406 repo,
5428 other,
5407 other,
5429 heads=nodes,
5408 heads=nodes,
5430 force=opts.get(b'force'),
5409 force=opts.get(b'force'),
5431 bookmarks=opts.get(b'bookmark', ()),
5410 bookmarks=opts.get(b'bookmark', ()),
5432 opargs=pullopargs,
5411 opargs=pullopargs,
5433 confirm=opts.get(b'confirm'),
5412 confirm=opts.get(b'confirm'),
5434 ).cgresult
5413 ).cgresult
5435
5414
5436 # brev is a name, which might be a bookmark to be activated at
5415 # brev is a name, which might be a bookmark to be activated at
5437 # the end of the update. In other words, it is an explicit
5416 # the end of the update. In other words, it is an explicit
5438 # destination of the update
5417 # destination of the update
5439 brev = None
5418 brev = None
5440
5419
5441 if checkout:
5420 if checkout:
5442 checkout = repo.unfiltered().changelog.rev(checkout)
5421 checkout = repo.unfiltered().changelog.rev(checkout)
5443
5422
5444 # order below depends on implementation of
5423 # order below depends on implementation of
5445 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5424 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5446 # because 'checkout' is determined without it.
5425 # because 'checkout' is determined without it.
5447 if opts.get(b'rev'):
5426 if opts.get(b'rev'):
5448 brev = opts[b'rev'][0]
5427 brev = opts[b'rev'][0]
5449 elif opts.get(b'branch'):
5428 elif opts.get(b'branch'):
5450 brev = opts[b'branch'][0]
5429 brev = opts[b'branch'][0]
5451 else:
5430 else:
5452 brev = branches[0]
5431 brev = branches[0]
5453 repo._subtoppath = source
5432 repo._subtoppath = source
5454 try:
5433 try:
5455 ret = postincoming(
5434 ret = postincoming(
5456 ui, repo, modheads, opts.get(b'update'), checkout, brev
5435 ui, repo, modheads, opts.get(b'update'), checkout, brev
5457 )
5436 )
5458 except error.FilteredRepoLookupError as exc:
5437 except error.FilteredRepoLookupError as exc:
5459 msg = _(b'cannot update to target: %s') % exc.args[0]
5438 msg = _(b'cannot update to target: %s') % exc.args[0]
5460 exc.args = (msg,) + exc.args[1:]
5439 exc.args = (msg,) + exc.args[1:]
5461 raise
5440 raise
5462 finally:
5441 finally:
5463 del repo._subtoppath
5442 del repo._subtoppath
5464
5443
5465 finally:
5444 finally:
5466 other.close()
5445 other.close()
5467 return ret
5446 return ret
5468
5447
5469
5448
5470 @command(
5449 @command(
5471 b'purge|clean',
5450 b'purge|clean',
5472 [
5451 [
5473 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5452 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5474 (b'', b'all', None, _(b'purge ignored files too')),
5453 (b'', b'all', None, _(b'purge ignored files too')),
5475 (b'i', b'ignored', None, _(b'purge only ignored files')),
5454 (b'i', b'ignored', None, _(b'purge only ignored files')),
5476 (b'', b'dirs', None, _(b'purge empty directories')),
5455 (b'', b'dirs', None, _(b'purge empty directories')),
5477 (b'', b'files', None, _(b'purge files')),
5456 (b'', b'files', None, _(b'purge files')),
5478 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5457 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5479 (
5458 (
5480 b'0',
5459 b'0',
5481 b'print0',
5460 b'print0',
5482 None,
5461 None,
5483 _(
5462 _(
5484 b'end filenames with NUL, for use with xargs'
5463 b'end filenames with NUL, for use with xargs'
5485 b' (implies -p/--print)'
5464 b' (implies -p/--print)'
5486 ),
5465 ),
5487 ),
5466 ),
5488 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5467 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5489 ]
5468 ]
5490 + cmdutil.walkopts,
5469 + cmdutil.walkopts,
5491 _(b'hg purge [OPTION]... [DIR]...'),
5470 _(b'hg purge [OPTION]... [DIR]...'),
5492 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5471 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5493 )
5472 )
5494 def purge(ui, repo, *dirs, **opts):
5473 def purge(ui, repo, *dirs, **opts):
5495 """removes files not tracked by Mercurial
5474 """removes files not tracked by Mercurial
5496
5475
5497 Delete files not known to Mercurial. This is useful to test local
5476 Delete files not known to Mercurial. This is useful to test local
5498 and uncommitted changes in an otherwise-clean source tree.
5477 and uncommitted changes in an otherwise-clean source tree.
5499
5478
5500 This means that purge will delete the following by default:
5479 This means that purge will delete the following by default:
5501
5480
5502 - Unknown files: files marked with "?" by :hg:`status`
5481 - Unknown files: files marked with "?" by :hg:`status`
5503 - Empty directories: in fact Mercurial ignores directories unless
5482 - Empty directories: in fact Mercurial ignores directories unless
5504 they contain files under source control management
5483 they contain files under source control management
5505
5484
5506 But it will leave untouched:
5485 But it will leave untouched:
5507
5486
5508 - Modified and unmodified tracked files
5487 - Modified and unmodified tracked files
5509 - Ignored files (unless -i or --all is specified)
5488 - Ignored files (unless -i or --all is specified)
5510 - New files added to the repository (with :hg:`add`)
5489 - New files added to the repository (with :hg:`add`)
5511
5490
5512 The --files and --dirs options can be used to direct purge to delete
5491 The --files and --dirs options can be used to direct purge to delete
5513 only files, only directories, or both. If neither option is given,
5492 only files, only directories, or both. If neither option is given,
5514 both will be deleted.
5493 both will be deleted.
5515
5494
5516 If directories are given on the command line, only files in these
5495 If directories are given on the command line, only files in these
5517 directories are considered.
5496 directories are considered.
5518
5497
5519 Be careful with purge, as you could irreversibly delete some files
5498 Be careful with purge, as you could irreversibly delete some files
5520 you forgot to add to the repository. If you only want to print the
5499 you forgot to add to the repository. If you only want to print the
5521 list of files that this program would delete, use the --print
5500 list of files that this program would delete, use the --print
5522 option.
5501 option.
5523 """
5502 """
5524 opts = pycompat.byteskwargs(opts)
5503 opts = pycompat.byteskwargs(opts)
5525 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5504 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5526
5505
5527 act = not opts.get(b'print')
5506 act = not opts.get(b'print')
5528 eol = b'\n'
5507 eol = b'\n'
5529 if opts.get(b'print0'):
5508 if opts.get(b'print0'):
5530 eol = b'\0'
5509 eol = b'\0'
5531 act = False # --print0 implies --print
5510 act = False # --print0 implies --print
5532 if opts.get(b'all', False):
5511 if opts.get(b'all', False):
5533 ignored = True
5512 ignored = True
5534 unknown = True
5513 unknown = True
5535 else:
5514 else:
5536 ignored = opts.get(b'ignored', False)
5515 ignored = opts.get(b'ignored', False)
5537 unknown = not ignored
5516 unknown = not ignored
5538
5517
5539 removefiles = opts.get(b'files')
5518 removefiles = opts.get(b'files')
5540 removedirs = opts.get(b'dirs')
5519 removedirs = opts.get(b'dirs')
5541 confirm = opts.get(b'confirm')
5520 confirm = opts.get(b'confirm')
5542 if confirm is None:
5521 if confirm is None:
5543 try:
5522 try:
5544 extensions.find(b'purge')
5523 extensions.find(b'purge')
5545 confirm = False
5524 confirm = False
5546 except KeyError:
5525 except KeyError:
5547 confirm = True
5526 confirm = True
5548
5527
5549 if not removefiles and not removedirs:
5528 if not removefiles and not removedirs:
5550 removefiles = True
5529 removefiles = True
5551 removedirs = True
5530 removedirs = True
5552
5531
5553 match = scmutil.match(repo[None], dirs, opts)
5532 match = scmutil.match(repo[None], dirs, opts)
5554
5533
5555 paths = mergemod.purge(
5534 paths = mergemod.purge(
5556 repo,
5535 repo,
5557 match,
5536 match,
5558 unknown=unknown,
5537 unknown=unknown,
5559 ignored=ignored,
5538 ignored=ignored,
5560 removeemptydirs=removedirs,
5539 removeemptydirs=removedirs,
5561 removefiles=removefiles,
5540 removefiles=removefiles,
5562 abortonerror=opts.get(b'abort_on_err'),
5541 abortonerror=opts.get(b'abort_on_err'),
5563 noop=not act,
5542 noop=not act,
5564 confirm=confirm,
5543 confirm=confirm,
5565 )
5544 )
5566
5545
5567 for path in paths:
5546 for path in paths:
5568 if not act:
5547 if not act:
5569 ui.write(b'%s%s' % (path, eol))
5548 ui.write(b'%s%s' % (path, eol))
5570
5549
5571
5550
5572 @command(
5551 @command(
5573 b'push',
5552 b'push',
5574 [
5553 [
5575 (b'f', b'force', None, _(b'force push')),
5554 (b'f', b'force', None, _(b'force push')),
5576 (
5555 (
5577 b'r',
5556 b'r',
5578 b'rev',
5557 b'rev',
5579 [],
5558 [],
5580 _(b'a changeset intended to be included in the destination'),
5559 _(b'a changeset intended to be included in the destination'),
5581 _(b'REV'),
5560 _(b'REV'),
5582 ),
5561 ),
5583 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5562 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5584 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5563 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5585 (
5564 (
5586 b'b',
5565 b'b',
5587 b'branch',
5566 b'branch',
5588 [],
5567 [],
5589 _(b'a specific branch you would like to push'),
5568 _(b'a specific branch you would like to push'),
5590 _(b'BRANCH'),
5569 _(b'BRANCH'),
5591 ),
5570 ),
5592 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5571 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5593 (
5572 (
5594 b'',
5573 b'',
5595 b'pushvars',
5574 b'pushvars',
5596 [],
5575 [],
5597 _(b'variables that can be sent to server (ADVANCED)'),
5576 _(b'variables that can be sent to server (ADVANCED)'),
5598 ),
5577 ),
5599 (
5578 (
5600 b'',
5579 b'',
5601 b'publish',
5580 b'publish',
5602 False,
5581 False,
5603 _(b'push the changeset as public (EXPERIMENTAL)'),
5582 _(b'push the changeset as public (EXPERIMENTAL)'),
5604 ),
5583 ),
5605 ]
5584 ]
5606 + remoteopts,
5585 + remoteopts,
5607 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5586 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5608 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5587 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5609 helpbasic=True,
5588 helpbasic=True,
5610 )
5589 )
5611 def push(ui, repo, dest=None, **opts):
5590 def push(ui, repo, dest=None, **opts):
5612 """push changes to the specified destination
5591 """push changes to the specified destination
5613
5592
5614 Push changesets from the local repository to the specified
5593 Push changesets from the local repository to the specified
5615 destination.
5594 destination.
5616
5595
5617 This operation is symmetrical to pull: it is identical to a pull
5596 This operation is symmetrical to pull: it is identical to a pull
5618 in the destination repository from the current one.
5597 in the destination repository from the current one.
5619
5598
5620 By default, push will not allow creation of new heads at the
5599 By default, push will not allow creation of new heads at the
5621 destination, since multiple heads would make it unclear which head
5600 destination, since multiple heads would make it unclear which head
5622 to use. In this situation, it is recommended to pull and merge
5601 to use. In this situation, it is recommended to pull and merge
5623 before pushing.
5602 before pushing.
5624
5603
5625 Use --new-branch if you want to allow push to create a new named
5604 Use --new-branch if you want to allow push to create a new named
5626 branch that is not present at the destination. This allows you to
5605 branch that is not present at the destination. This allows you to
5627 only create a new branch without forcing other changes.
5606 only create a new branch without forcing other changes.
5628
5607
5629 .. note::
5608 .. note::
5630
5609
5631 Extra care should be taken with the -f/--force option,
5610 Extra care should be taken with the -f/--force option,
5632 which will push all new heads on all branches, an action which will
5611 which will push all new heads on all branches, an action which will
5633 almost always cause confusion for collaborators.
5612 almost always cause confusion for collaborators.
5634
5613
5635 If -r/--rev is used, the specified revision and all its ancestors
5614 If -r/--rev is used, the specified revision and all its ancestors
5636 will be pushed to the remote repository.
5615 will be pushed to the remote repository.
5637
5616
5638 If -B/--bookmark is used, the specified bookmarked revision, its
5617 If -B/--bookmark is used, the specified bookmarked revision, its
5639 ancestors, and the bookmark will be pushed to the remote
5618 ancestors, and the bookmark will be pushed to the remote
5640 repository. Specifying ``.`` is equivalent to specifying the active
5619 repository. Specifying ``.`` is equivalent to specifying the active
5641 bookmark's name. Use the --all-bookmarks option for pushing all
5620 bookmark's name. Use the --all-bookmarks option for pushing all
5642 current bookmarks.
5621 current bookmarks.
5643
5622
5644 Please see :hg:`help urls` for important details about ``ssh://``
5623 Please see :hg:`help urls` for important details about ``ssh://``
5645 URLs. If DESTINATION is omitted, a default path will be used.
5624 URLs. If DESTINATION is omitted, a default path will be used.
5646
5625
5647 .. container:: verbose
5626 .. container:: verbose
5648
5627
5649 The --pushvars option sends strings to the server that become
5628 The --pushvars option sends strings to the server that become
5650 environment variables prepended with ``HG_USERVAR_``. For example,
5629 environment variables prepended with ``HG_USERVAR_``. For example,
5651 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5630 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5652 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5631 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5653
5632
5654 pushvars can provide for user-overridable hooks as well as set debug
5633 pushvars can provide for user-overridable hooks as well as set debug
5655 levels. One example is having a hook that blocks commits containing
5634 levels. One example is having a hook that blocks commits containing
5656 conflict markers, but enables the user to override the hook if the file
5635 conflict markers, but enables the user to override the hook if the file
5657 is using conflict markers for testing purposes or the file format has
5636 is using conflict markers for testing purposes or the file format has
5658 strings that look like conflict markers.
5637 strings that look like conflict markers.
5659
5638
5660 By default, servers will ignore `--pushvars`. To enable it add the
5639 By default, servers will ignore `--pushvars`. To enable it add the
5661 following to your configuration file::
5640 following to your configuration file::
5662
5641
5663 [push]
5642 [push]
5664 pushvars.server = true
5643 pushvars.server = true
5665
5644
5666 Returns 0 if push was successful, 1 if nothing to push.
5645 Returns 0 if push was successful, 1 if nothing to push.
5667 """
5646 """
5668
5647
5669 opts = pycompat.byteskwargs(opts)
5648 opts = pycompat.byteskwargs(opts)
5670
5649
5671 if opts.get(b'all_bookmarks'):
5650 if opts.get(b'all_bookmarks'):
5672 cmdutil.check_incompatible_arguments(
5651 cmdutil.check_incompatible_arguments(
5673 opts,
5652 opts,
5674 b'all_bookmarks',
5653 b'all_bookmarks',
5675 [b'bookmark', b'rev'],
5654 [b'bookmark', b'rev'],
5676 )
5655 )
5677 opts[b'bookmark'] = list(repo._bookmarks)
5656 opts[b'bookmark'] = list(repo._bookmarks)
5678
5657
5679 if opts.get(b'bookmark'):
5658 if opts.get(b'bookmark'):
5680 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5659 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5681 for b in opts[b'bookmark']:
5660 for b in opts[b'bookmark']:
5682 # translate -B options to -r so changesets get pushed
5661 # translate -B options to -r so changesets get pushed
5683 b = repo._bookmarks.expandname(b)
5662 b = repo._bookmarks.expandname(b)
5684 if b in repo._bookmarks:
5663 if b in repo._bookmarks:
5685 opts.setdefault(b'rev', []).append(b)
5664 opts.setdefault(b'rev', []).append(b)
5686 else:
5665 else:
5687 # if we try to push a deleted bookmark, translate it to null
5666 # if we try to push a deleted bookmark, translate it to null
5688 # this lets simultaneous -r, -b options continue working
5667 # this lets simultaneous -r, -b options continue working
5689 opts.setdefault(b'rev', []).append(b"null")
5668 opts.setdefault(b'rev', []).append(b"null")
5690
5669
5691 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5670 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5692 if not path:
5671 if not path:
5693 raise error.ConfigError(
5672 raise error.ConfigError(
5694 _(b'default repository not configured!'),
5673 _(b'default repository not configured!'),
5695 hint=_(b"see 'hg help config.paths'"),
5674 hint=_(b"see 'hg help config.paths'"),
5696 )
5675 )
5697 dest = path.pushloc or path.loc
5676 dest = path.pushloc or path.loc
5698 branches = (path.branch, opts.get(b'branch') or [])
5677 branches = (path.branch, opts.get(b'branch') or [])
5699 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5678 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5700 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5679 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5701 other = hg.peer(repo, opts, dest)
5680 other = hg.peer(repo, opts, dest)
5702
5681
5703 if revs:
5682 if revs:
5704 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5683 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5705 if not revs:
5684 if not revs:
5706 raise error.InputError(
5685 raise error.InputError(
5707 _(b"specified revisions evaluate to an empty set"),
5686 _(b"specified revisions evaluate to an empty set"),
5708 hint=_(b"use different revision arguments"),
5687 hint=_(b"use different revision arguments"),
5709 )
5688 )
5710 elif path.pushrev:
5689 elif path.pushrev:
5711 # It doesn't make any sense to specify ancestor revisions. So limit
5690 # It doesn't make any sense to specify ancestor revisions. So limit
5712 # to DAG heads to make discovery simpler.
5691 # to DAG heads to make discovery simpler.
5713 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5692 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5714 revs = scmutil.revrange(repo, [expr])
5693 revs = scmutil.revrange(repo, [expr])
5715 revs = [repo[rev].node() for rev in revs]
5694 revs = [repo[rev].node() for rev in revs]
5716 if not revs:
5695 if not revs:
5717 raise error.InputError(
5696 raise error.InputError(
5718 _(b'default push revset for path evaluates to an empty set')
5697 _(b'default push revset for path evaluates to an empty set')
5719 )
5698 )
5720 elif ui.configbool(b'commands', b'push.require-revs'):
5699 elif ui.configbool(b'commands', b'push.require-revs'):
5721 raise error.InputError(
5700 raise error.InputError(
5722 _(b'no revisions specified to push'),
5701 _(b'no revisions specified to push'),
5723 hint=_(b'did you mean "hg push -r ."?'),
5702 hint=_(b'did you mean "hg push -r ."?'),
5724 )
5703 )
5725
5704
5726 repo._subtoppath = dest
5705 repo._subtoppath = dest
5727 try:
5706 try:
5728 # push subrepos depth-first for coherent ordering
5707 # push subrepos depth-first for coherent ordering
5729 c = repo[b'.']
5708 c = repo[b'.']
5730 subs = c.substate # only repos that are committed
5709 subs = c.substate # only repos that are committed
5731 for s in sorted(subs):
5710 for s in sorted(subs):
5732 result = c.sub(s).push(opts)
5711 result = c.sub(s).push(opts)
5733 if result == 0:
5712 if result == 0:
5734 return not result
5713 return not result
5735 finally:
5714 finally:
5736 del repo._subtoppath
5715 del repo._subtoppath
5737
5716
5738 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5717 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5739 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5718 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5740
5719
5741 pushop = exchange.push(
5720 pushop = exchange.push(
5742 repo,
5721 repo,
5743 other,
5722 other,
5744 opts.get(b'force'),
5723 opts.get(b'force'),
5745 revs=revs,
5724 revs=revs,
5746 newbranch=opts.get(b'new_branch'),
5725 newbranch=opts.get(b'new_branch'),
5747 bookmarks=opts.get(b'bookmark', ()),
5726 bookmarks=opts.get(b'bookmark', ()),
5748 publish=opts.get(b'publish'),
5727 publish=opts.get(b'publish'),
5749 opargs=opargs,
5728 opargs=opargs,
5750 )
5729 )
5751
5730
5752 result = not pushop.cgresult
5731 result = not pushop.cgresult
5753
5732
5754 if pushop.bkresult is not None:
5733 if pushop.bkresult is not None:
5755 if pushop.bkresult == 2:
5734 if pushop.bkresult == 2:
5756 result = 2
5735 result = 2
5757 elif not result and pushop.bkresult:
5736 elif not result and pushop.bkresult:
5758 result = 2
5737 result = 2
5759
5738
5760 return result
5739 return result
5761
5740
5762
5741
5763 @command(
5742 @command(
5764 b'recover',
5743 b'recover',
5765 [
5744 [
5766 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5745 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5767 ],
5746 ],
5768 helpcategory=command.CATEGORY_MAINTENANCE,
5747 helpcategory=command.CATEGORY_MAINTENANCE,
5769 )
5748 )
5770 def recover(ui, repo, **opts):
5749 def recover(ui, repo, **opts):
5771 """roll back an interrupted transaction
5750 """roll back an interrupted transaction
5772
5751
5773 Recover from an interrupted commit or pull.
5752 Recover from an interrupted commit or pull.
5774
5753
5775 This command tries to fix the repository status after an
5754 This command tries to fix the repository status after an
5776 interrupted operation. It should only be necessary when Mercurial
5755 interrupted operation. It should only be necessary when Mercurial
5777 suggests it.
5756 suggests it.
5778
5757
5779 Returns 0 if successful, 1 if nothing to recover or verify fails.
5758 Returns 0 if successful, 1 if nothing to recover or verify fails.
5780 """
5759 """
5781 ret = repo.recover()
5760 ret = repo.recover()
5782 if ret:
5761 if ret:
5783 if opts['verify']:
5762 if opts['verify']:
5784 return hg.verify(repo)
5763 return hg.verify(repo)
5785 else:
5764 else:
5786 msg = _(
5765 msg = _(
5787 b"(verify step skipped, run `hg verify` to check your "
5766 b"(verify step skipped, run `hg verify` to check your "
5788 b"repository content)\n"
5767 b"repository content)\n"
5789 )
5768 )
5790 ui.warn(msg)
5769 ui.warn(msg)
5791 return 0
5770 return 0
5792 return 1
5771 return 1
5793
5772
5794
5773
5795 @command(
5774 @command(
5796 b'remove|rm',
5775 b'remove|rm',
5797 [
5776 [
5798 (b'A', b'after', None, _(b'record delete for missing files')),
5777 (b'A', b'after', None, _(b'record delete for missing files')),
5799 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5778 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5800 ]
5779 ]
5801 + subrepoopts
5780 + subrepoopts
5802 + walkopts
5781 + walkopts
5803 + dryrunopts,
5782 + dryrunopts,
5804 _(b'[OPTION]... FILE...'),
5783 _(b'[OPTION]... FILE...'),
5805 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5784 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5806 helpbasic=True,
5785 helpbasic=True,
5807 inferrepo=True,
5786 inferrepo=True,
5808 )
5787 )
5809 def remove(ui, repo, *pats, **opts):
5788 def remove(ui, repo, *pats, **opts):
5810 """remove the specified files on the next commit
5789 """remove the specified files on the next commit
5811
5790
5812 Schedule the indicated files for removal from the current branch.
5791 Schedule the indicated files for removal from the current branch.
5813
5792
5814 This command schedules the files to be removed at the next commit.
5793 This command schedules the files to be removed at the next commit.
5815 To undo a remove before that, see :hg:`revert`. To undo added
5794 To undo a remove before that, see :hg:`revert`. To undo added
5816 files, see :hg:`forget`.
5795 files, see :hg:`forget`.
5817
5796
5818 .. container:: verbose
5797 .. container:: verbose
5819
5798
5820 -A/--after can be used to remove only files that have already
5799 -A/--after can be used to remove only files that have already
5821 been deleted, -f/--force can be used to force deletion, and -Af
5800 been deleted, -f/--force can be used to force deletion, and -Af
5822 can be used to remove files from the next revision without
5801 can be used to remove files from the next revision without
5823 deleting them from the working directory.
5802 deleting them from the working directory.
5824
5803
5825 The following table details the behavior of remove for different
5804 The following table details the behavior of remove for different
5826 file states (columns) and option combinations (rows). The file
5805 file states (columns) and option combinations (rows). The file
5827 states are Added [A], Clean [C], Modified [M] and Missing [!]
5806 states are Added [A], Clean [C], Modified [M] and Missing [!]
5828 (as reported by :hg:`status`). The actions are Warn, Remove
5807 (as reported by :hg:`status`). The actions are Warn, Remove
5829 (from branch) and Delete (from disk):
5808 (from branch) and Delete (from disk):
5830
5809
5831 ========= == == == ==
5810 ========= == == == ==
5832 opt/state A C M !
5811 opt/state A C M !
5833 ========= == == == ==
5812 ========= == == == ==
5834 none W RD W R
5813 none W RD W R
5835 -f R RD RD R
5814 -f R RD RD R
5836 -A W W W R
5815 -A W W W R
5837 -Af R R R R
5816 -Af R R R R
5838 ========= == == == ==
5817 ========= == == == ==
5839
5818
5840 .. note::
5819 .. note::
5841
5820
5842 :hg:`remove` never deletes files in Added [A] state from the
5821 :hg:`remove` never deletes files in Added [A] state from the
5843 working directory, not even if ``--force`` is specified.
5822 working directory, not even if ``--force`` is specified.
5844
5823
5845 Returns 0 on success, 1 if any warnings encountered.
5824 Returns 0 on success, 1 if any warnings encountered.
5846 """
5825 """
5847
5826
5848 opts = pycompat.byteskwargs(opts)
5827 opts = pycompat.byteskwargs(opts)
5849 after, force = opts.get(b'after'), opts.get(b'force')
5828 after, force = opts.get(b'after'), opts.get(b'force')
5850 dryrun = opts.get(b'dry_run')
5829 dryrun = opts.get(b'dry_run')
5851 if not pats and not after:
5830 if not pats and not after:
5852 raise error.InputError(_(b'no files specified'))
5831 raise error.InputError(_(b'no files specified'))
5853
5832
5854 m = scmutil.match(repo[None], pats, opts)
5833 m = scmutil.match(repo[None], pats, opts)
5855 subrepos = opts.get(b'subrepos')
5834 subrepos = opts.get(b'subrepos')
5856 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5835 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5857 return cmdutil.remove(
5836 return cmdutil.remove(
5858 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5837 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5859 )
5838 )
5860
5839
5861
5840
5862 @command(
5841 @command(
5863 b'rename|move|mv',
5842 b'rename|move|mv',
5864 [
5843 [
5865 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5844 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5866 (
5845 (
5867 b'',
5846 b'',
5868 b'at-rev',
5847 b'at-rev',
5869 b'',
5848 b'',
5870 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5849 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5871 _(b'REV'),
5850 _(b'REV'),
5872 ),
5851 ),
5873 (
5852 (
5874 b'f',
5853 b'f',
5875 b'force',
5854 b'force',
5876 None,
5855 None,
5877 _(b'forcibly move over an existing managed file'),
5856 _(b'forcibly move over an existing managed file'),
5878 ),
5857 ),
5879 ]
5858 ]
5880 + walkopts
5859 + walkopts
5881 + dryrunopts,
5860 + dryrunopts,
5882 _(b'[OPTION]... SOURCE... DEST'),
5861 _(b'[OPTION]... SOURCE... DEST'),
5883 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5862 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5884 )
5863 )
5885 def rename(ui, repo, *pats, **opts):
5864 def rename(ui, repo, *pats, **opts):
5886 """rename files; equivalent of copy + remove
5865 """rename files; equivalent of copy + remove
5887
5866
5888 Mark dest as copies of sources; mark sources for deletion. If dest
5867 Mark dest as copies of sources; mark sources for deletion. If dest
5889 is a directory, copies are put in that directory. If dest is a
5868 is a directory, copies are put in that directory. If dest is a
5890 file, there can only be one source.
5869 file, there can only be one source.
5891
5870
5892 By default, this command copies the contents of files as they
5871 By default, this command copies the contents of files as they
5893 exist in the working directory. If invoked with -A/--after, the
5872 exist in the working directory. If invoked with -A/--after, the
5894 operation is recorded, but no copying is performed.
5873 operation is recorded, but no copying is performed.
5895
5874
5896 This command takes effect at the next commit. To undo a rename
5875 This command takes effect at the next commit. To undo a rename
5897 before that, see :hg:`revert`.
5876 before that, see :hg:`revert`.
5898
5877
5899 Returns 0 on success, 1 if errors are encountered.
5878 Returns 0 on success, 1 if errors are encountered.
5900 """
5879 """
5901 opts = pycompat.byteskwargs(opts)
5880 opts = pycompat.byteskwargs(opts)
5902 with repo.wlock():
5881 with repo.wlock():
5903 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5882 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5904
5883
5905
5884
5906 @command(
5885 @command(
5907 b'resolve',
5886 b'resolve',
5908 [
5887 [
5909 (b'a', b'all', None, _(b'select all unresolved files')),
5888 (b'a', b'all', None, _(b'select all unresolved files')),
5910 (b'l', b'list', None, _(b'list state of files needing merge')),
5889 (b'l', b'list', None, _(b'list state of files needing merge')),
5911 (b'm', b'mark', None, _(b'mark files as resolved')),
5890 (b'm', b'mark', None, _(b'mark files as resolved')),
5912 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5891 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5913 (b'n', b'no-status', None, _(b'hide status prefix')),
5892 (b'n', b'no-status', None, _(b'hide status prefix')),
5914 (b'', b're-merge', None, _(b're-merge files')),
5893 (b'', b're-merge', None, _(b're-merge files')),
5915 ]
5894 ]
5916 + mergetoolopts
5895 + mergetoolopts
5917 + walkopts
5896 + walkopts
5918 + formatteropts,
5897 + formatteropts,
5919 _(b'[OPTION]... [FILE]...'),
5898 _(b'[OPTION]... [FILE]...'),
5920 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5899 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5921 inferrepo=True,
5900 inferrepo=True,
5922 )
5901 )
5923 def resolve(ui, repo, *pats, **opts):
5902 def resolve(ui, repo, *pats, **opts):
5924 """redo merges or set/view the merge status of files
5903 """redo merges or set/view the merge status of files
5925
5904
5926 Merges with unresolved conflicts are often the result of
5905 Merges with unresolved conflicts are often the result of
5927 non-interactive merging using the ``internal:merge`` configuration
5906 non-interactive merging using the ``internal:merge`` configuration
5928 setting, or a command-line merge tool like ``diff3``. The resolve
5907 setting, or a command-line merge tool like ``diff3``. The resolve
5929 command is used to manage the files involved in a merge, after
5908 command is used to manage the files involved in a merge, after
5930 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5909 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5931 working directory must have two parents). See :hg:`help
5910 working directory must have two parents). See :hg:`help
5932 merge-tools` for information on configuring merge tools.
5911 merge-tools` for information on configuring merge tools.
5933
5912
5934 The resolve command can be used in the following ways:
5913 The resolve command can be used in the following ways:
5935
5914
5936 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5915 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5937 the specified files, discarding any previous merge attempts. Re-merging
5916 the specified files, discarding any previous merge attempts. Re-merging
5938 is not performed for files already marked as resolved. Use ``--all/-a``
5917 is not performed for files already marked as resolved. Use ``--all/-a``
5939 to select all unresolved files. ``--tool`` can be used to specify
5918 to select all unresolved files. ``--tool`` can be used to specify
5940 the merge tool used for the given files. It overrides the HGMERGE
5919 the merge tool used for the given files. It overrides the HGMERGE
5941 environment variable and your configuration files. Previous file
5920 environment variable and your configuration files. Previous file
5942 contents are saved with a ``.orig`` suffix.
5921 contents are saved with a ``.orig`` suffix.
5943
5922
5944 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5923 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5945 (e.g. after having manually fixed-up the files). The default is
5924 (e.g. after having manually fixed-up the files). The default is
5946 to mark all unresolved files.
5925 to mark all unresolved files.
5947
5926
5948 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5927 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5949 default is to mark all resolved files.
5928 default is to mark all resolved files.
5950
5929
5951 - :hg:`resolve -l`: list files which had or still have conflicts.
5930 - :hg:`resolve -l`: list files which had or still have conflicts.
5952 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5931 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5953 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5932 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5954 the list. See :hg:`help filesets` for details.
5933 the list. See :hg:`help filesets` for details.
5955
5934
5956 .. note::
5935 .. note::
5957
5936
5958 Mercurial will not let you commit files with unresolved merge
5937 Mercurial will not let you commit files with unresolved merge
5959 conflicts. You must use :hg:`resolve -m ...` before you can
5938 conflicts. You must use :hg:`resolve -m ...` before you can
5960 commit after a conflicting merge.
5939 commit after a conflicting merge.
5961
5940
5962 .. container:: verbose
5941 .. container:: verbose
5963
5942
5964 Template:
5943 Template:
5965
5944
5966 The following keywords are supported in addition to the common template
5945 The following keywords are supported in addition to the common template
5967 keywords and functions. See also :hg:`help templates`.
5946 keywords and functions. See also :hg:`help templates`.
5968
5947
5969 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5948 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5970 :path: String. Repository-absolute path of the file.
5949 :path: String. Repository-absolute path of the file.
5971
5950
5972 Returns 0 on success, 1 if any files fail a resolve attempt.
5951 Returns 0 on success, 1 if any files fail a resolve attempt.
5973 """
5952 """
5974
5953
5975 opts = pycompat.byteskwargs(opts)
5954 opts = pycompat.byteskwargs(opts)
5976 confirm = ui.configbool(b'commands', b'resolve.confirm')
5955 confirm = ui.configbool(b'commands', b'resolve.confirm')
5977 flaglist = b'all mark unmark list no_status re_merge'.split()
5956 flaglist = b'all mark unmark list no_status re_merge'.split()
5978 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5957 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5979
5958
5980 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5959 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5981 if actioncount > 1:
5960 if actioncount > 1:
5982 raise error.InputError(_(b"too many actions specified"))
5961 raise error.InputError(_(b"too many actions specified"))
5983 elif actioncount == 0 and ui.configbool(
5962 elif actioncount == 0 and ui.configbool(
5984 b'commands', b'resolve.explicit-re-merge'
5963 b'commands', b'resolve.explicit-re-merge'
5985 ):
5964 ):
5986 hint = _(b'use --mark, --unmark, --list or --re-merge')
5965 hint = _(b'use --mark, --unmark, --list or --re-merge')
5987 raise error.InputError(_(b'no action specified'), hint=hint)
5966 raise error.InputError(_(b'no action specified'), hint=hint)
5988 if pats and all:
5967 if pats and all:
5989 raise error.InputError(_(b"can't specify --all and patterns"))
5968 raise error.InputError(_(b"can't specify --all and patterns"))
5990 if not (all or pats or show or mark or unmark):
5969 if not (all or pats or show or mark or unmark):
5991 raise error.InputError(
5970 raise error.InputError(
5992 _(b'no files or directories specified'),
5971 _(b'no files or directories specified'),
5993 hint=b'use --all to re-merge all unresolved files',
5972 hint=b'use --all to re-merge all unresolved files',
5994 )
5973 )
5995
5974
5996 if confirm:
5975 if confirm:
5997 if all:
5976 if all:
5998 if ui.promptchoice(
5977 if ui.promptchoice(
5999 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5978 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6000 ):
5979 ):
6001 raise error.CanceledError(_(b'user quit'))
5980 raise error.CanceledError(_(b'user quit'))
6002 if mark and not pats:
5981 if mark and not pats:
6003 if ui.promptchoice(
5982 if ui.promptchoice(
6004 _(
5983 _(
6005 b'mark all unresolved files as resolved (yn)?'
5984 b'mark all unresolved files as resolved (yn)?'
6006 b'$$ &Yes $$ &No'
5985 b'$$ &Yes $$ &No'
6007 )
5986 )
6008 ):
5987 ):
6009 raise error.CanceledError(_(b'user quit'))
5988 raise error.CanceledError(_(b'user quit'))
6010 if unmark and not pats:
5989 if unmark and not pats:
6011 if ui.promptchoice(
5990 if ui.promptchoice(
6012 _(
5991 _(
6013 b'mark all resolved files as unresolved (yn)?'
5992 b'mark all resolved files as unresolved (yn)?'
6014 b'$$ &Yes $$ &No'
5993 b'$$ &Yes $$ &No'
6015 )
5994 )
6016 ):
5995 ):
6017 raise error.CanceledError(_(b'user quit'))
5996 raise error.CanceledError(_(b'user quit'))
6018
5997
6019 uipathfn = scmutil.getuipathfn(repo)
5998 uipathfn = scmutil.getuipathfn(repo)
6020
5999
6021 if show:
6000 if show:
6022 ui.pager(b'resolve')
6001 ui.pager(b'resolve')
6023 fm = ui.formatter(b'resolve', opts)
6002 fm = ui.formatter(b'resolve', opts)
6024 ms = mergestatemod.mergestate.read(repo)
6003 ms = mergestatemod.mergestate.read(repo)
6025 wctx = repo[None]
6004 wctx = repo[None]
6026 m = scmutil.match(wctx, pats, opts)
6005 m = scmutil.match(wctx, pats, opts)
6027
6006
6028 # Labels and keys based on merge state. Unresolved path conflicts show
6007 # Labels and keys based on merge state. Unresolved path conflicts show
6029 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6008 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6030 # resolved conflicts.
6009 # resolved conflicts.
6031 mergestateinfo = {
6010 mergestateinfo = {
6032 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6011 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6033 b'resolve.unresolved',
6012 b'resolve.unresolved',
6034 b'U',
6013 b'U',
6035 ),
6014 ),
6036 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6015 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6037 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6016 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6038 b'resolve.unresolved',
6017 b'resolve.unresolved',
6039 b'P',
6018 b'P',
6040 ),
6019 ),
6041 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6020 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6042 b'resolve.resolved',
6021 b'resolve.resolved',
6043 b'R',
6022 b'R',
6044 ),
6023 ),
6045 }
6024 }
6046
6025
6047 for f in ms:
6026 for f in ms:
6048 if not m(f):
6027 if not m(f):
6049 continue
6028 continue
6050
6029
6051 label, key = mergestateinfo[ms[f]]
6030 label, key = mergestateinfo[ms[f]]
6052 fm.startitem()
6031 fm.startitem()
6053 fm.context(ctx=wctx)
6032 fm.context(ctx=wctx)
6054 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6033 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6055 fm.data(path=f)
6034 fm.data(path=f)
6056 fm.plain(b'%s\n' % uipathfn(f), label=label)
6035 fm.plain(b'%s\n' % uipathfn(f), label=label)
6057 fm.end()
6036 fm.end()
6058 return 0
6037 return 0
6059
6038
6060 with repo.wlock():
6039 with repo.wlock():
6061 ms = mergestatemod.mergestate.read(repo)
6040 ms = mergestatemod.mergestate.read(repo)
6062
6041
6063 if not (ms.active() or repo.dirstate.p2() != nullid):
6042 if not (ms.active() or repo.dirstate.p2() != nullid):
6064 raise error.StateError(
6043 raise error.StateError(
6065 _(b'resolve command not applicable when not merging')
6044 _(b'resolve command not applicable when not merging')
6066 )
6045 )
6067
6046
6068 wctx = repo[None]
6047 wctx = repo[None]
6069 m = scmutil.match(wctx, pats, opts)
6048 m = scmutil.match(wctx, pats, opts)
6070 ret = 0
6049 ret = 0
6071 didwork = False
6050 didwork = False
6072
6051
6073 tocomplete = []
6052 tocomplete = []
6074 hasconflictmarkers = []
6053 hasconflictmarkers = []
6075 if mark:
6054 if mark:
6076 markcheck = ui.config(b'commands', b'resolve.mark-check')
6055 markcheck = ui.config(b'commands', b'resolve.mark-check')
6077 if markcheck not in [b'warn', b'abort']:
6056 if markcheck not in [b'warn', b'abort']:
6078 # Treat all invalid / unrecognized values as 'none'.
6057 # Treat all invalid / unrecognized values as 'none'.
6079 markcheck = False
6058 markcheck = False
6080 for f in ms:
6059 for f in ms:
6081 if not m(f):
6060 if not m(f):
6082 continue
6061 continue
6083
6062
6084 didwork = True
6063 didwork = True
6085
6064
6086 # path conflicts must be resolved manually
6065 # path conflicts must be resolved manually
6087 if ms[f] in (
6066 if ms[f] in (
6088 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6067 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6089 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6068 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6090 ):
6069 ):
6091 if mark:
6070 if mark:
6092 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6071 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6093 elif unmark:
6072 elif unmark:
6094 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6073 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6095 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6074 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6096 ui.warn(
6075 ui.warn(
6097 _(b'%s: path conflict must be resolved manually\n')
6076 _(b'%s: path conflict must be resolved manually\n')
6098 % uipathfn(f)
6077 % uipathfn(f)
6099 )
6078 )
6100 continue
6079 continue
6101
6080
6102 if mark:
6081 if mark:
6103 if markcheck:
6082 if markcheck:
6104 fdata = repo.wvfs.tryread(f)
6083 fdata = repo.wvfs.tryread(f)
6105 if (
6084 if (
6106 filemerge.hasconflictmarkers(fdata)
6085 filemerge.hasconflictmarkers(fdata)
6107 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6086 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6108 ):
6087 ):
6109 hasconflictmarkers.append(f)
6088 hasconflictmarkers.append(f)
6110 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6089 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6111 elif unmark:
6090 elif unmark:
6112 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6091 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6113 else:
6092 else:
6114 # backup pre-resolve (merge uses .orig for its own purposes)
6093 # backup pre-resolve (merge uses .orig for its own purposes)
6115 a = repo.wjoin(f)
6094 a = repo.wjoin(f)
6116 try:
6095 try:
6117 util.copyfile(a, a + b".resolve")
6096 util.copyfile(a, a + b".resolve")
6118 except (IOError, OSError) as inst:
6097 except (IOError, OSError) as inst:
6119 if inst.errno != errno.ENOENT:
6098 if inst.errno != errno.ENOENT:
6120 raise
6099 raise
6121
6100
6122 try:
6101 try:
6123 # preresolve file
6102 # preresolve file
6124 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6103 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6125 with ui.configoverride(overrides, b'resolve'):
6104 with ui.configoverride(overrides, b'resolve'):
6126 complete, r = ms.preresolve(f, wctx)
6105 complete, r = ms.preresolve(f, wctx)
6127 if not complete:
6106 if not complete:
6128 tocomplete.append(f)
6107 tocomplete.append(f)
6129 elif r:
6108 elif r:
6130 ret = 1
6109 ret = 1
6131 finally:
6110 finally:
6132 ms.commit()
6111 ms.commit()
6133
6112
6134 # replace filemerge's .orig file with our resolve file, but only
6113 # replace filemerge's .orig file with our resolve file, but only
6135 # for merges that are complete
6114 # for merges that are complete
6136 if complete:
6115 if complete:
6137 try:
6116 try:
6138 util.rename(
6117 util.rename(
6139 a + b".resolve", scmutil.backuppath(ui, repo, f)
6118 a + b".resolve", scmutil.backuppath(ui, repo, f)
6140 )
6119 )
6141 except OSError as inst:
6120 except OSError as inst:
6142 if inst.errno != errno.ENOENT:
6121 if inst.errno != errno.ENOENT:
6143 raise
6122 raise
6144
6123
6145 if hasconflictmarkers:
6124 if hasconflictmarkers:
6146 ui.warn(
6125 ui.warn(
6147 _(
6126 _(
6148 b'warning: the following files still have conflict '
6127 b'warning: the following files still have conflict '
6149 b'markers:\n'
6128 b'markers:\n'
6150 )
6129 )
6151 + b''.join(
6130 + b''.join(
6152 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6131 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6153 )
6132 )
6154 )
6133 )
6155 if markcheck == b'abort' and not all and not pats:
6134 if markcheck == b'abort' and not all and not pats:
6156 raise error.StateError(
6135 raise error.StateError(
6157 _(b'conflict markers detected'),
6136 _(b'conflict markers detected'),
6158 hint=_(b'use --all to mark anyway'),
6137 hint=_(b'use --all to mark anyway'),
6159 )
6138 )
6160
6139
6161 for f in tocomplete:
6140 for f in tocomplete:
6162 try:
6141 try:
6163 # resolve file
6142 # resolve file
6164 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6143 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6165 with ui.configoverride(overrides, b'resolve'):
6144 with ui.configoverride(overrides, b'resolve'):
6166 r = ms.resolve(f, wctx)
6145 r = ms.resolve(f, wctx)
6167 if r:
6146 if r:
6168 ret = 1
6147 ret = 1
6169 finally:
6148 finally:
6170 ms.commit()
6149 ms.commit()
6171
6150
6172 # replace filemerge's .orig file with our resolve file
6151 # replace filemerge's .orig file with our resolve file
6173 a = repo.wjoin(f)
6152 a = repo.wjoin(f)
6174 try:
6153 try:
6175 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6154 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6176 except OSError as inst:
6155 except OSError as inst:
6177 if inst.errno != errno.ENOENT:
6156 if inst.errno != errno.ENOENT:
6178 raise
6157 raise
6179
6158
6180 ms.commit()
6159 ms.commit()
6181 branchmerge = repo.dirstate.p2() != nullid
6160 branchmerge = repo.dirstate.p2() != nullid
6182 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6161 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6183
6162
6184 if not didwork and pats:
6163 if not didwork and pats:
6185 hint = None
6164 hint = None
6186 if not any([p for p in pats if p.find(b':') >= 0]):
6165 if not any([p for p in pats if p.find(b':') >= 0]):
6187 pats = [b'path:%s' % p for p in pats]
6166 pats = [b'path:%s' % p for p in pats]
6188 m = scmutil.match(wctx, pats, opts)
6167 m = scmutil.match(wctx, pats, opts)
6189 for f in ms:
6168 for f in ms:
6190 if not m(f):
6169 if not m(f):
6191 continue
6170 continue
6192
6171
6193 def flag(o):
6172 def flag(o):
6194 if o == b're_merge':
6173 if o == b're_merge':
6195 return b'--re-merge '
6174 return b'--re-merge '
6196 return b'-%s ' % o[0:1]
6175 return b'-%s ' % o[0:1]
6197
6176
6198 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6177 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6199 hint = _(b"(try: hg resolve %s%s)\n") % (
6178 hint = _(b"(try: hg resolve %s%s)\n") % (
6200 flags,
6179 flags,
6201 b' '.join(pats),
6180 b' '.join(pats),
6202 )
6181 )
6203 break
6182 break
6204 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6183 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6205 if hint:
6184 if hint:
6206 ui.warn(hint)
6185 ui.warn(hint)
6207
6186
6208 unresolvedf = ms.unresolvedcount()
6187 unresolvedf = ms.unresolvedcount()
6209 if not unresolvedf:
6188 if not unresolvedf:
6210 ui.status(_(b'(no more unresolved files)\n'))
6189 ui.status(_(b'(no more unresolved files)\n'))
6211 cmdutil.checkafterresolved(repo)
6190 cmdutil.checkafterresolved(repo)
6212
6191
6213 return ret
6192 return ret
6214
6193
6215
6194
6216 @command(
6195 @command(
6217 b'revert',
6196 b'revert',
6218 [
6197 [
6219 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6198 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6220 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6199 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6221 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6200 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6222 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6201 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6223 (b'i', b'interactive', None, _(b'interactively select the changes')),
6202 (b'i', b'interactive', None, _(b'interactively select the changes')),
6224 ]
6203 ]
6225 + walkopts
6204 + walkopts
6226 + dryrunopts,
6205 + dryrunopts,
6227 _(b'[OPTION]... [-r REV] [NAME]...'),
6206 _(b'[OPTION]... [-r REV] [NAME]...'),
6228 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6229 )
6208 )
6230 def revert(ui, repo, *pats, **opts):
6209 def revert(ui, repo, *pats, **opts):
6231 """restore files to their checkout state
6210 """restore files to their checkout state
6232
6211
6233 .. note::
6212 .. note::
6234
6213
6235 To check out earlier revisions, you should use :hg:`update REV`.
6214 To check out earlier revisions, you should use :hg:`update REV`.
6236 To cancel an uncommitted merge (and lose your changes),
6215 To cancel an uncommitted merge (and lose your changes),
6237 use :hg:`merge --abort`.
6216 use :hg:`merge --abort`.
6238
6217
6239 With no revision specified, revert the specified files or directories
6218 With no revision specified, revert the specified files or directories
6240 to the contents they had in the parent of the working directory.
6219 to the contents they had in the parent of the working directory.
6241 This restores the contents of files to an unmodified
6220 This restores the contents of files to an unmodified
6242 state and unschedules adds, removes, copies, and renames. If the
6221 state and unschedules adds, removes, copies, and renames. If the
6243 working directory has two parents, you must explicitly specify a
6222 working directory has two parents, you must explicitly specify a
6244 revision.
6223 revision.
6245
6224
6246 Using the -r/--rev or -d/--date options, revert the given files or
6225 Using the -r/--rev or -d/--date options, revert the given files or
6247 directories to their states as of a specific revision. Because
6226 directories to their states as of a specific revision. Because
6248 revert does not change the working directory parents, this will
6227 revert does not change the working directory parents, this will
6249 cause these files to appear modified. This can be helpful to "back
6228 cause these files to appear modified. This can be helpful to "back
6250 out" some or all of an earlier change. See :hg:`backout` for a
6229 out" some or all of an earlier change. See :hg:`backout` for a
6251 related method.
6230 related method.
6252
6231
6253 Modified files are saved with a .orig suffix before reverting.
6232 Modified files are saved with a .orig suffix before reverting.
6254 To disable these backups, use --no-backup. It is possible to store
6233 To disable these backups, use --no-backup. It is possible to store
6255 the backup files in a custom directory relative to the root of the
6234 the backup files in a custom directory relative to the root of the
6256 repository by setting the ``ui.origbackuppath`` configuration
6235 repository by setting the ``ui.origbackuppath`` configuration
6257 option.
6236 option.
6258
6237
6259 See :hg:`help dates` for a list of formats valid for -d/--date.
6238 See :hg:`help dates` for a list of formats valid for -d/--date.
6260
6239
6261 See :hg:`help backout` for a way to reverse the effect of an
6240 See :hg:`help backout` for a way to reverse the effect of an
6262 earlier changeset.
6241 earlier changeset.
6263
6242
6264 Returns 0 on success.
6243 Returns 0 on success.
6265 """
6244 """
6266
6245
6267 opts = pycompat.byteskwargs(opts)
6246 opts = pycompat.byteskwargs(opts)
6268 if opts.get(b"date"):
6247 if opts.get(b"date"):
6269 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6248 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6270 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6249 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6271
6250
6272 parent, p2 = repo.dirstate.parents()
6251 parent, p2 = repo.dirstate.parents()
6273 if not opts.get(b'rev') and p2 != nullid:
6252 if not opts.get(b'rev') and p2 != nullid:
6274 # revert after merge is a trap for new users (issue2915)
6253 # revert after merge is a trap for new users (issue2915)
6275 raise error.InputError(
6254 raise error.InputError(
6276 _(b'uncommitted merge with no revision specified'),
6255 _(b'uncommitted merge with no revision specified'),
6277 hint=_(b"use 'hg update' or see 'hg help revert'"),
6256 hint=_(b"use 'hg update' or see 'hg help revert'"),
6278 )
6257 )
6279
6258
6280 rev = opts.get(b'rev')
6259 rev = opts.get(b'rev')
6281 if rev:
6260 if rev:
6282 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6261 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6283 ctx = scmutil.revsingle(repo, rev)
6262 ctx = scmutil.revsingle(repo, rev)
6284
6263
6285 if not (
6264 if not (
6286 pats
6265 pats
6287 or opts.get(b'include')
6266 or opts.get(b'include')
6288 or opts.get(b'exclude')
6267 or opts.get(b'exclude')
6289 or opts.get(b'all')
6268 or opts.get(b'all')
6290 or opts.get(b'interactive')
6269 or opts.get(b'interactive')
6291 ):
6270 ):
6292 msg = _(b"no files or directories specified")
6271 msg = _(b"no files or directories specified")
6293 if p2 != nullid:
6272 if p2 != nullid:
6294 hint = _(
6273 hint = _(
6295 b"uncommitted merge, use --all to discard all changes,"
6274 b"uncommitted merge, use --all to discard all changes,"
6296 b" or 'hg update -C .' to abort the merge"
6275 b" or 'hg update -C .' to abort the merge"
6297 )
6276 )
6298 raise error.InputError(msg, hint=hint)
6277 raise error.InputError(msg, hint=hint)
6299 dirty = any(repo.status())
6278 dirty = any(repo.status())
6300 node = ctx.node()
6279 node = ctx.node()
6301 if node != parent:
6280 if node != parent:
6302 if dirty:
6281 if dirty:
6303 hint = (
6282 hint = (
6304 _(
6283 _(
6305 b"uncommitted changes, use --all to discard all"
6284 b"uncommitted changes, use --all to discard all"
6306 b" changes, or 'hg update %d' to update"
6285 b" changes, or 'hg update %d' to update"
6307 )
6286 )
6308 % ctx.rev()
6287 % ctx.rev()
6309 )
6288 )
6310 else:
6289 else:
6311 hint = (
6290 hint = (
6312 _(
6291 _(
6313 b"use --all to revert all files,"
6292 b"use --all to revert all files,"
6314 b" or 'hg update %d' to update"
6293 b" or 'hg update %d' to update"
6315 )
6294 )
6316 % ctx.rev()
6295 % ctx.rev()
6317 )
6296 )
6318 elif dirty:
6297 elif dirty:
6319 hint = _(b"uncommitted changes, use --all to discard all changes")
6298 hint = _(b"uncommitted changes, use --all to discard all changes")
6320 else:
6299 else:
6321 hint = _(b"use --all to revert all files")
6300 hint = _(b"use --all to revert all files")
6322 raise error.InputError(msg, hint=hint)
6301 raise error.InputError(msg, hint=hint)
6323
6302
6324 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6303 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6325
6304
6326
6305
6327 @command(
6306 @command(
6328 b'rollback',
6307 b'rollback',
6329 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6308 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6330 helpcategory=command.CATEGORY_MAINTENANCE,
6309 helpcategory=command.CATEGORY_MAINTENANCE,
6331 )
6310 )
6332 def rollback(ui, repo, **opts):
6311 def rollback(ui, repo, **opts):
6333 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6312 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6334
6313
6335 Please use :hg:`commit --amend` instead of rollback to correct
6314 Please use :hg:`commit --amend` instead of rollback to correct
6336 mistakes in the last commit.
6315 mistakes in the last commit.
6337
6316
6338 This command should be used with care. There is only one level of
6317 This command should be used with care. There is only one level of
6339 rollback, and there is no way to undo a rollback. It will also
6318 rollback, and there is no way to undo a rollback. It will also
6340 restore the dirstate at the time of the last transaction, losing
6319 restore the dirstate at the time of the last transaction, losing
6341 any dirstate changes since that time. This command does not alter
6320 any dirstate changes since that time. This command does not alter
6342 the working directory.
6321 the working directory.
6343
6322
6344 Transactions are used to encapsulate the effects of all commands
6323 Transactions are used to encapsulate the effects of all commands
6345 that create new changesets or propagate existing changesets into a
6324 that create new changesets or propagate existing changesets into a
6346 repository.
6325 repository.
6347
6326
6348 .. container:: verbose
6327 .. container:: verbose
6349
6328
6350 For example, the following commands are transactional, and their
6329 For example, the following commands are transactional, and their
6351 effects can be rolled back:
6330 effects can be rolled back:
6352
6331
6353 - commit
6332 - commit
6354 - import
6333 - import
6355 - pull
6334 - pull
6356 - push (with this repository as the destination)
6335 - push (with this repository as the destination)
6357 - unbundle
6336 - unbundle
6358
6337
6359 To avoid permanent data loss, rollback will refuse to rollback a
6338 To avoid permanent data loss, rollback will refuse to rollback a
6360 commit transaction if it isn't checked out. Use --force to
6339 commit transaction if it isn't checked out. Use --force to
6361 override this protection.
6340 override this protection.
6362
6341
6363 The rollback command can be entirely disabled by setting the
6342 The rollback command can be entirely disabled by setting the
6364 ``ui.rollback`` configuration setting to false. If you're here
6343 ``ui.rollback`` configuration setting to false. If you're here
6365 because you want to use rollback and it's disabled, you can
6344 because you want to use rollback and it's disabled, you can
6366 re-enable the command by setting ``ui.rollback`` to true.
6345 re-enable the command by setting ``ui.rollback`` to true.
6367
6346
6368 This command is not intended for use on public repositories. Once
6347 This command is not intended for use on public repositories. Once
6369 changes are visible for pull by other users, rolling a transaction
6348 changes are visible for pull by other users, rolling a transaction
6370 back locally is ineffective (someone else may already have pulled
6349 back locally is ineffective (someone else may already have pulled
6371 the changes). Furthermore, a race is possible with readers of the
6350 the changes). Furthermore, a race is possible with readers of the
6372 repository; for example an in-progress pull from the repository
6351 repository; for example an in-progress pull from the repository
6373 may fail if a rollback is performed.
6352 may fail if a rollback is performed.
6374
6353
6375 Returns 0 on success, 1 if no rollback data is available.
6354 Returns 0 on success, 1 if no rollback data is available.
6376 """
6355 """
6377 if not ui.configbool(b'ui', b'rollback'):
6356 if not ui.configbool(b'ui', b'rollback'):
6378 raise error.Abort(
6357 raise error.Abort(
6379 _(b'rollback is disabled because it is unsafe'),
6358 _(b'rollback is disabled because it is unsafe'),
6380 hint=b'see `hg help -v rollback` for information',
6359 hint=b'see `hg help -v rollback` for information',
6381 )
6360 )
6382 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6361 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6383
6362
6384
6363
6385 @command(
6364 @command(
6386 b'root',
6365 b'root',
6387 [] + formatteropts,
6366 [] + formatteropts,
6388 intents={INTENT_READONLY},
6367 intents={INTENT_READONLY},
6389 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6368 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6390 )
6369 )
6391 def root(ui, repo, **opts):
6370 def root(ui, repo, **opts):
6392 """print the root (top) of the current working directory
6371 """print the root (top) of the current working directory
6393
6372
6394 Print the root directory of the current repository.
6373 Print the root directory of the current repository.
6395
6374
6396 .. container:: verbose
6375 .. container:: verbose
6397
6376
6398 Template:
6377 Template:
6399
6378
6400 The following keywords are supported in addition to the common template
6379 The following keywords are supported in addition to the common template
6401 keywords and functions. See also :hg:`help templates`.
6380 keywords and functions. See also :hg:`help templates`.
6402
6381
6403 :hgpath: String. Path to the .hg directory.
6382 :hgpath: String. Path to the .hg directory.
6404 :storepath: String. Path to the directory holding versioned data.
6383 :storepath: String. Path to the directory holding versioned data.
6405
6384
6406 Returns 0 on success.
6385 Returns 0 on success.
6407 """
6386 """
6408 opts = pycompat.byteskwargs(opts)
6387 opts = pycompat.byteskwargs(opts)
6409 with ui.formatter(b'root', opts) as fm:
6388 with ui.formatter(b'root', opts) as fm:
6410 fm.startitem()
6389 fm.startitem()
6411 fm.write(b'reporoot', b'%s\n', repo.root)
6390 fm.write(b'reporoot', b'%s\n', repo.root)
6412 fm.data(hgpath=repo.path, storepath=repo.spath)
6391 fm.data(hgpath=repo.path, storepath=repo.spath)
6413
6392
6414
6393
6415 @command(
6394 @command(
6416 b'serve',
6395 b'serve',
6417 [
6396 [
6418 (
6397 (
6419 b'A',
6398 b'A',
6420 b'accesslog',
6399 b'accesslog',
6421 b'',
6400 b'',
6422 _(b'name of access log file to write to'),
6401 _(b'name of access log file to write to'),
6423 _(b'FILE'),
6402 _(b'FILE'),
6424 ),
6403 ),
6425 (b'd', b'daemon', None, _(b'run server in background')),
6404 (b'd', b'daemon', None, _(b'run server in background')),
6426 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6405 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6427 (
6406 (
6428 b'E',
6407 b'E',
6429 b'errorlog',
6408 b'errorlog',
6430 b'',
6409 b'',
6431 _(b'name of error log file to write to'),
6410 _(b'name of error log file to write to'),
6432 _(b'FILE'),
6411 _(b'FILE'),
6433 ),
6412 ),
6434 # use string type, then we can check if something was passed
6413 # use string type, then we can check if something was passed
6435 (
6414 (
6436 b'p',
6415 b'p',
6437 b'port',
6416 b'port',
6438 b'',
6417 b'',
6439 _(b'port to listen on (default: 8000)'),
6418 _(b'port to listen on (default: 8000)'),
6440 _(b'PORT'),
6419 _(b'PORT'),
6441 ),
6420 ),
6442 (
6421 (
6443 b'a',
6422 b'a',
6444 b'address',
6423 b'address',
6445 b'',
6424 b'',
6446 _(b'address to listen on (default: all interfaces)'),
6425 _(b'address to listen on (default: all interfaces)'),
6447 _(b'ADDR'),
6426 _(b'ADDR'),
6448 ),
6427 ),
6449 (
6428 (
6450 b'',
6429 b'',
6451 b'prefix',
6430 b'prefix',
6452 b'',
6431 b'',
6453 _(b'prefix path to serve from (default: server root)'),
6432 _(b'prefix path to serve from (default: server root)'),
6454 _(b'PREFIX'),
6433 _(b'PREFIX'),
6455 ),
6434 ),
6456 (
6435 (
6457 b'n',
6436 b'n',
6458 b'name',
6437 b'name',
6459 b'',
6438 b'',
6460 _(b'name to show in web pages (default: working directory)'),
6439 _(b'name to show in web pages (default: working directory)'),
6461 _(b'NAME'),
6440 _(b'NAME'),
6462 ),
6441 ),
6463 (
6442 (
6464 b'',
6443 b'',
6465 b'web-conf',
6444 b'web-conf',
6466 b'',
6445 b'',
6467 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6446 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6468 _(b'FILE'),
6447 _(b'FILE'),
6469 ),
6448 ),
6470 (
6449 (
6471 b'',
6450 b'',
6472 b'webdir-conf',
6451 b'webdir-conf',
6473 b'',
6452 b'',
6474 _(b'name of the hgweb config file (DEPRECATED)'),
6453 _(b'name of the hgweb config file (DEPRECATED)'),
6475 _(b'FILE'),
6454 _(b'FILE'),
6476 ),
6455 ),
6477 (
6456 (
6478 b'',
6457 b'',
6479 b'pid-file',
6458 b'pid-file',
6480 b'',
6459 b'',
6481 _(b'name of file to write process ID to'),
6460 _(b'name of file to write process ID to'),
6482 _(b'FILE'),
6461 _(b'FILE'),
6483 ),
6462 ),
6484 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6463 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6485 (
6464 (
6486 b'',
6465 b'',
6487 b'cmdserver',
6466 b'cmdserver',
6488 b'',
6467 b'',
6489 _(b'for remote clients (ADVANCED)'),
6468 _(b'for remote clients (ADVANCED)'),
6490 _(b'MODE'),
6469 _(b'MODE'),
6491 ),
6470 ),
6492 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6471 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6493 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6472 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6494 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6473 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6495 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6474 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6496 (b'', b'print-url', None, _(b'start and print only the URL')),
6475 (b'', b'print-url', None, _(b'start and print only the URL')),
6497 ]
6476 ]
6498 + subrepoopts,
6477 + subrepoopts,
6499 _(b'[OPTION]...'),
6478 _(b'[OPTION]...'),
6500 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6479 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6501 helpbasic=True,
6480 helpbasic=True,
6502 optionalrepo=True,
6481 optionalrepo=True,
6503 )
6482 )
6504 def serve(ui, repo, **opts):
6483 def serve(ui, repo, **opts):
6505 """start stand-alone webserver
6484 """start stand-alone webserver
6506
6485
6507 Start a local HTTP repository browser and pull server. You can use
6486 Start a local HTTP repository browser and pull server. You can use
6508 this for ad-hoc sharing and browsing of repositories. It is
6487 this for ad-hoc sharing and browsing of repositories. It is
6509 recommended to use a real web server to serve a repository for
6488 recommended to use a real web server to serve a repository for
6510 longer periods of time.
6489 longer periods of time.
6511
6490
6512 Please note that the server does not implement access control.
6491 Please note that the server does not implement access control.
6513 This means that, by default, anybody can read from the server and
6492 This means that, by default, anybody can read from the server and
6514 nobody can write to it by default. Set the ``web.allow-push``
6493 nobody can write to it by default. Set the ``web.allow-push``
6515 option to ``*`` to allow everybody to push to the server. You
6494 option to ``*`` to allow everybody to push to the server. You
6516 should use a real web server if you need to authenticate users.
6495 should use a real web server if you need to authenticate users.
6517
6496
6518 By default, the server logs accesses to stdout and errors to
6497 By default, the server logs accesses to stdout and errors to
6519 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6498 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6520 files.
6499 files.
6521
6500
6522 To have the server choose a free port number to listen on, specify
6501 To have the server choose a free port number to listen on, specify
6523 a port number of 0; in this case, the server will print the port
6502 a port number of 0; in this case, the server will print the port
6524 number it uses.
6503 number it uses.
6525
6504
6526 Returns 0 on success.
6505 Returns 0 on success.
6527 """
6506 """
6528
6507
6529 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6508 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6530 opts = pycompat.byteskwargs(opts)
6509 opts = pycompat.byteskwargs(opts)
6531 if opts[b"print_url"] and ui.verbose:
6510 if opts[b"print_url"] and ui.verbose:
6532 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6511 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6533
6512
6534 if opts[b"stdio"]:
6513 if opts[b"stdio"]:
6535 if repo is None:
6514 if repo is None:
6536 raise error.RepoError(
6515 raise error.RepoError(
6537 _(b"there is no Mercurial repository here (.hg not found)")
6516 _(b"there is no Mercurial repository here (.hg not found)")
6538 )
6517 )
6539 s = wireprotoserver.sshserver(ui, repo)
6518 s = wireprotoserver.sshserver(ui, repo)
6540 s.serve_forever()
6519 s.serve_forever()
6541 return
6520 return
6542
6521
6543 service = server.createservice(ui, repo, opts)
6522 service = server.createservice(ui, repo, opts)
6544 return server.runservice(opts, initfn=service.init, runfn=service.run)
6523 return server.runservice(opts, initfn=service.init, runfn=service.run)
6545
6524
6546
6525
6547 @command(
6526 @command(
6548 b'shelve',
6527 b'shelve',
6549 [
6528 [
6550 (
6529 (
6551 b'A',
6530 b'A',
6552 b'addremove',
6531 b'addremove',
6553 None,
6532 None,
6554 _(b'mark new/missing files as added/removed before shelving'),
6533 _(b'mark new/missing files as added/removed before shelving'),
6555 ),
6534 ),
6556 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6535 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6557 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6536 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6558 (
6537 (
6559 b'',
6538 b'',
6560 b'date',
6539 b'date',
6561 b'',
6540 b'',
6562 _(b'shelve with the specified commit date'),
6541 _(b'shelve with the specified commit date'),
6563 _(b'DATE'),
6542 _(b'DATE'),
6564 ),
6543 ),
6565 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6544 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6566 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6545 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6567 (
6546 (
6568 b'k',
6547 b'k',
6569 b'keep',
6548 b'keep',
6570 False,
6549 False,
6571 _(b'shelve, but keep changes in the working directory'),
6550 _(b'shelve, but keep changes in the working directory'),
6572 ),
6551 ),
6573 (b'l', b'list', None, _(b'list current shelves')),
6552 (b'l', b'list', None, _(b'list current shelves')),
6574 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6553 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6575 (
6554 (
6576 b'n',
6555 b'n',
6577 b'name',
6556 b'name',
6578 b'',
6557 b'',
6579 _(b'use the given name for the shelved commit'),
6558 _(b'use the given name for the shelved commit'),
6580 _(b'NAME'),
6559 _(b'NAME'),
6581 ),
6560 ),
6582 (
6561 (
6583 b'p',
6562 b'p',
6584 b'patch',
6563 b'patch',
6585 None,
6564 None,
6586 _(
6565 _(
6587 b'output patches for changes (provide the names of the shelved '
6566 b'output patches for changes (provide the names of the shelved '
6588 b'changes as positional arguments)'
6567 b'changes as positional arguments)'
6589 ),
6568 ),
6590 ),
6569 ),
6591 (b'i', b'interactive', None, _(b'interactive mode')),
6570 (b'i', b'interactive', None, _(b'interactive mode')),
6592 (
6571 (
6593 b'',
6572 b'',
6594 b'stat',
6573 b'stat',
6595 None,
6574 None,
6596 _(
6575 _(
6597 b'output diffstat-style summary of changes (provide the names of '
6576 b'output diffstat-style summary of changes (provide the names of '
6598 b'the shelved changes as positional arguments)'
6577 b'the shelved changes as positional arguments)'
6599 ),
6578 ),
6600 ),
6579 ),
6601 ]
6580 ]
6602 + cmdutil.walkopts,
6581 + cmdutil.walkopts,
6603 _(b'hg shelve [OPTION]... [FILE]...'),
6582 _(b'hg shelve [OPTION]... [FILE]...'),
6604 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6583 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6605 )
6584 )
6606 def shelve(ui, repo, *pats, **opts):
6585 def shelve(ui, repo, *pats, **opts):
6607 """save and set aside changes from the working directory
6586 """save and set aside changes from the working directory
6608
6587
6609 Shelving takes files that "hg status" reports as not clean, saves
6588 Shelving takes files that "hg status" reports as not clean, saves
6610 the modifications to a bundle (a shelved change), and reverts the
6589 the modifications to a bundle (a shelved change), and reverts the
6611 files so that their state in the working directory becomes clean.
6590 files so that their state in the working directory becomes clean.
6612
6591
6613 To restore these changes to the working directory, using "hg
6592 To restore these changes to the working directory, using "hg
6614 unshelve"; this will work even if you switch to a different
6593 unshelve"; this will work even if you switch to a different
6615 commit.
6594 commit.
6616
6595
6617 When no files are specified, "hg shelve" saves all not-clean
6596 When no files are specified, "hg shelve" saves all not-clean
6618 files. If specific files or directories are named, only changes to
6597 files. If specific files or directories are named, only changes to
6619 those files are shelved.
6598 those files are shelved.
6620
6599
6621 In bare shelve (when no files are specified, without interactive,
6600 In bare shelve (when no files are specified, without interactive,
6622 include and exclude option), shelving remembers information if the
6601 include and exclude option), shelving remembers information if the
6623 working directory was on newly created branch, in other words working
6602 working directory was on newly created branch, in other words working
6624 directory was on different branch than its first parent. In this
6603 directory was on different branch than its first parent. In this
6625 situation unshelving restores branch information to the working directory.
6604 situation unshelving restores branch information to the working directory.
6626
6605
6627 Each shelved change has a name that makes it easier to find later.
6606 Each shelved change has a name that makes it easier to find later.
6628 The name of a shelved change defaults to being based on the active
6607 The name of a shelved change defaults to being based on the active
6629 bookmark, or if there is no active bookmark, the current named
6608 bookmark, or if there is no active bookmark, the current named
6630 branch. To specify a different name, use ``--name``.
6609 branch. To specify a different name, use ``--name``.
6631
6610
6632 To see a list of existing shelved changes, use the ``--list``
6611 To see a list of existing shelved changes, use the ``--list``
6633 option. For each shelved change, this will print its name, age,
6612 option. For each shelved change, this will print its name, age,
6634 and description; use ``--patch`` or ``--stat`` for more details.
6613 and description; use ``--patch`` or ``--stat`` for more details.
6635
6614
6636 To delete specific shelved changes, use ``--delete``. To delete
6615 To delete specific shelved changes, use ``--delete``. To delete
6637 all shelved changes, use ``--cleanup``.
6616 all shelved changes, use ``--cleanup``.
6638 """
6617 """
6639 opts = pycompat.byteskwargs(opts)
6618 opts = pycompat.byteskwargs(opts)
6640 allowables = [
6619 allowables = [
6641 (b'addremove', {b'create'}), # 'create' is pseudo action
6620 (b'addremove', {b'create'}), # 'create' is pseudo action
6642 (b'unknown', {b'create'}),
6621 (b'unknown', {b'create'}),
6643 (b'cleanup', {b'cleanup'}),
6622 (b'cleanup', {b'cleanup'}),
6644 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6623 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6645 (b'delete', {b'delete'}),
6624 (b'delete', {b'delete'}),
6646 (b'edit', {b'create'}),
6625 (b'edit', {b'create'}),
6647 (b'keep', {b'create'}),
6626 (b'keep', {b'create'}),
6648 (b'list', {b'list'}),
6627 (b'list', {b'list'}),
6649 (b'message', {b'create'}),
6628 (b'message', {b'create'}),
6650 (b'name', {b'create'}),
6629 (b'name', {b'create'}),
6651 (b'patch', {b'patch', b'list'}),
6630 (b'patch', {b'patch', b'list'}),
6652 (b'stat', {b'stat', b'list'}),
6631 (b'stat', {b'stat', b'list'}),
6653 ]
6632 ]
6654
6633
6655 def checkopt(opt):
6634 def checkopt(opt):
6656 if opts.get(opt):
6635 if opts.get(opt):
6657 for i, allowable in allowables:
6636 for i, allowable in allowables:
6658 if opts[i] and opt not in allowable:
6637 if opts[i] and opt not in allowable:
6659 raise error.InputError(
6638 raise error.InputError(
6660 _(
6639 _(
6661 b"options '--%s' and '--%s' may not be "
6640 b"options '--%s' and '--%s' may not be "
6662 b"used together"
6641 b"used together"
6663 )
6642 )
6664 % (opt, i)
6643 % (opt, i)
6665 )
6644 )
6666 return True
6645 return True
6667
6646
6668 if checkopt(b'cleanup'):
6647 if checkopt(b'cleanup'):
6669 if pats:
6648 if pats:
6670 raise error.InputError(
6649 raise error.InputError(
6671 _(b"cannot specify names when using '--cleanup'")
6650 _(b"cannot specify names when using '--cleanup'")
6672 )
6651 )
6673 return shelvemod.cleanupcmd(ui, repo)
6652 return shelvemod.cleanupcmd(ui, repo)
6674 elif checkopt(b'delete'):
6653 elif checkopt(b'delete'):
6675 return shelvemod.deletecmd(ui, repo, pats)
6654 return shelvemod.deletecmd(ui, repo, pats)
6676 elif checkopt(b'list'):
6655 elif checkopt(b'list'):
6677 return shelvemod.listcmd(ui, repo, pats, opts)
6656 return shelvemod.listcmd(ui, repo, pats, opts)
6678 elif checkopt(b'patch') or checkopt(b'stat'):
6657 elif checkopt(b'patch') or checkopt(b'stat'):
6679 return shelvemod.patchcmds(ui, repo, pats, opts)
6658 return shelvemod.patchcmds(ui, repo, pats, opts)
6680 else:
6659 else:
6681 return shelvemod.createcmd(ui, repo, pats, opts)
6660 return shelvemod.createcmd(ui, repo, pats, opts)
6682
6661
6683
6662
6684 _NOTTERSE = b'nothing'
6663 _NOTTERSE = b'nothing'
6685
6664
6686
6665
6687 @command(
6666 @command(
6688 b'status|st',
6667 b'status|st',
6689 [
6668 [
6690 (b'A', b'all', None, _(b'show status of all files')),
6669 (b'A', b'all', None, _(b'show status of all files')),
6691 (b'm', b'modified', None, _(b'show only modified files')),
6670 (b'm', b'modified', None, _(b'show only modified files')),
6692 (b'a', b'added', None, _(b'show only added files')),
6671 (b'a', b'added', None, _(b'show only added files')),
6693 (b'r', b'removed', None, _(b'show only removed files')),
6672 (b'r', b'removed', None, _(b'show only removed files')),
6694 (b'd', b'deleted', None, _(b'show only missing files')),
6673 (b'd', b'deleted', None, _(b'show only missing files')),
6695 (b'c', b'clean', None, _(b'show only files without changes')),
6674 (b'c', b'clean', None, _(b'show only files without changes')),
6696 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6675 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6697 (b'i', b'ignored', None, _(b'show only ignored files')),
6676 (b'i', b'ignored', None, _(b'show only ignored files')),
6698 (b'n', b'no-status', None, _(b'hide status prefix')),
6677 (b'n', b'no-status', None, _(b'hide status prefix')),
6699 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6678 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6700 (
6679 (
6701 b'C',
6680 b'C',
6702 b'copies',
6681 b'copies',
6703 None,
6682 None,
6704 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6683 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6705 ),
6684 ),
6706 (
6685 (
6707 b'0',
6686 b'0',
6708 b'print0',
6687 b'print0',
6709 None,
6688 None,
6710 _(b'end filenames with NUL, for use with xargs'),
6689 _(b'end filenames with NUL, for use with xargs'),
6711 ),
6690 ),
6712 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6691 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6713 (
6692 (
6714 b'',
6693 b'',
6715 b'change',
6694 b'change',
6716 b'',
6695 b'',
6717 _(b'list the changed files of a revision'),
6696 _(b'list the changed files of a revision'),
6718 _(b'REV'),
6697 _(b'REV'),
6719 ),
6698 ),
6720 ]
6699 ]
6721 + walkopts
6700 + walkopts
6722 + subrepoopts
6701 + subrepoopts
6723 + formatteropts,
6702 + formatteropts,
6724 _(b'[OPTION]... [FILE]...'),
6703 _(b'[OPTION]... [FILE]...'),
6725 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6704 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6726 helpbasic=True,
6705 helpbasic=True,
6727 inferrepo=True,
6706 inferrepo=True,
6728 intents={INTENT_READONLY},
6707 intents={INTENT_READONLY},
6729 )
6708 )
6730 def status(ui, repo, *pats, **opts):
6709 def status(ui, repo, *pats, **opts):
6731 """show changed files in the working directory
6710 """show changed files in the working directory
6732
6711
6733 Show status of files in the repository. If names are given, only
6712 Show status of files in the repository. If names are given, only
6734 files that match are shown. Files that are clean or ignored or
6713 files that match are shown. Files that are clean or ignored or
6735 the source of a copy/move operation, are not listed unless
6714 the source of a copy/move operation, are not listed unless
6736 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6715 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6737 Unless options described with "show only ..." are given, the
6716 Unless options described with "show only ..." are given, the
6738 options -mardu are used.
6717 options -mardu are used.
6739
6718
6740 Option -q/--quiet hides untracked (unknown and ignored) files
6719 Option -q/--quiet hides untracked (unknown and ignored) files
6741 unless explicitly requested with -u/--unknown or -i/--ignored.
6720 unless explicitly requested with -u/--unknown or -i/--ignored.
6742
6721
6743 .. note::
6722 .. note::
6744
6723
6745 :hg:`status` may appear to disagree with diff if permissions have
6724 :hg:`status` may appear to disagree with diff if permissions have
6746 changed or a merge has occurred. The standard diff format does
6725 changed or a merge has occurred. The standard diff format does
6747 not report permission changes and diff only reports changes
6726 not report permission changes and diff only reports changes
6748 relative to one merge parent.
6727 relative to one merge parent.
6749
6728
6750 If one revision is given, it is used as the base revision.
6729 If one revision is given, it is used as the base revision.
6751 If two revisions are given, the differences between them are
6730 If two revisions are given, the differences between them are
6752 shown. The --change option can also be used as a shortcut to list
6731 shown. The --change option can also be used as a shortcut to list
6753 the changed files of a revision from its first parent.
6732 the changed files of a revision from its first parent.
6754
6733
6755 The codes used to show the status of files are::
6734 The codes used to show the status of files are::
6756
6735
6757 M = modified
6736 M = modified
6758 A = added
6737 A = added
6759 R = removed
6738 R = removed
6760 C = clean
6739 C = clean
6761 ! = missing (deleted by non-hg command, but still tracked)
6740 ! = missing (deleted by non-hg command, but still tracked)
6762 ? = not tracked
6741 ? = not tracked
6763 I = ignored
6742 I = ignored
6764 = origin of the previous file (with --copies)
6743 = origin of the previous file (with --copies)
6765
6744
6766 .. container:: verbose
6745 .. container:: verbose
6767
6746
6768 The -t/--terse option abbreviates the output by showing only the directory
6747 The -t/--terse option abbreviates the output by showing only the directory
6769 name if all the files in it share the same status. The option takes an
6748 name if all the files in it share the same status. The option takes an
6770 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6749 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6771 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6750 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6772 for 'ignored' and 'c' for clean.
6751 for 'ignored' and 'c' for clean.
6773
6752
6774 It abbreviates only those statuses which are passed. Note that clean and
6753 It abbreviates only those statuses which are passed. Note that clean and
6775 ignored files are not displayed with '--terse ic' unless the -c/--clean
6754 ignored files are not displayed with '--terse ic' unless the -c/--clean
6776 and -i/--ignored options are also used.
6755 and -i/--ignored options are also used.
6777
6756
6778 The -v/--verbose option shows information when the repository is in an
6757 The -v/--verbose option shows information when the repository is in an
6779 unfinished merge, shelve, rebase state etc. You can have this behavior
6758 unfinished merge, shelve, rebase state etc. You can have this behavior
6780 turned on by default by enabling the ``commands.status.verbose`` option.
6759 turned on by default by enabling the ``commands.status.verbose`` option.
6781
6760
6782 You can skip displaying some of these states by setting
6761 You can skip displaying some of these states by setting
6783 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6762 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6784 'histedit', 'merge', 'rebase', or 'unshelve'.
6763 'histedit', 'merge', 'rebase', or 'unshelve'.
6785
6764
6786 Template:
6765 Template:
6787
6766
6788 The following keywords are supported in addition to the common template
6767 The following keywords are supported in addition to the common template
6789 keywords and functions. See also :hg:`help templates`.
6768 keywords and functions. See also :hg:`help templates`.
6790
6769
6791 :path: String. Repository-absolute path of the file.
6770 :path: String. Repository-absolute path of the file.
6792 :source: String. Repository-absolute path of the file originated from.
6771 :source: String. Repository-absolute path of the file originated from.
6793 Available if ``--copies`` is specified.
6772 Available if ``--copies`` is specified.
6794 :status: String. Character denoting file's status.
6773 :status: String. Character denoting file's status.
6795
6774
6796 Examples:
6775 Examples:
6797
6776
6798 - show changes in the working directory relative to a
6777 - show changes in the working directory relative to a
6799 changeset::
6778 changeset::
6800
6779
6801 hg status --rev 9353
6780 hg status --rev 9353
6802
6781
6803 - show changes in the working directory relative to the
6782 - show changes in the working directory relative to the
6804 current directory (see :hg:`help patterns` for more information)::
6783 current directory (see :hg:`help patterns` for more information)::
6805
6784
6806 hg status re:
6785 hg status re:
6807
6786
6808 - show all changes including copies in an existing changeset::
6787 - show all changes including copies in an existing changeset::
6809
6788
6810 hg status --copies --change 9353
6789 hg status --copies --change 9353
6811
6790
6812 - get a NUL separated list of added files, suitable for xargs::
6791 - get a NUL separated list of added files, suitable for xargs::
6813
6792
6814 hg status -an0
6793 hg status -an0
6815
6794
6816 - show more information about the repository status, abbreviating
6795 - show more information about the repository status, abbreviating
6817 added, removed, modified, deleted, and untracked paths::
6796 added, removed, modified, deleted, and untracked paths::
6818
6797
6819 hg status -v -t mardu
6798 hg status -v -t mardu
6820
6799
6821 Returns 0 on success.
6800 Returns 0 on success.
6822
6801
6823 """
6802 """
6824
6803
6825 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6804 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6826 opts = pycompat.byteskwargs(opts)
6805 opts = pycompat.byteskwargs(opts)
6827 revs = opts.get(b'rev')
6806 revs = opts.get(b'rev')
6828 change = opts.get(b'change')
6807 change = opts.get(b'change')
6829 terse = opts.get(b'terse')
6808 terse = opts.get(b'terse')
6830 if terse is _NOTTERSE:
6809 if terse is _NOTTERSE:
6831 if revs:
6810 if revs:
6832 terse = b''
6811 terse = b''
6833 else:
6812 else:
6834 terse = ui.config(b'commands', b'status.terse')
6813 terse = ui.config(b'commands', b'status.terse')
6835
6814
6836 if revs and terse:
6815 if revs and terse:
6837 msg = _(b'cannot use --terse with --rev')
6816 msg = _(b'cannot use --terse with --rev')
6838 raise error.InputError(msg)
6817 raise error.InputError(msg)
6839 elif change:
6818 elif change:
6840 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6819 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6841 ctx2 = scmutil.revsingle(repo, change, None)
6820 ctx2 = scmutil.revsingle(repo, change, None)
6842 ctx1 = ctx2.p1()
6821 ctx1 = ctx2.p1()
6843 else:
6822 else:
6844 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6823 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6845 ctx1, ctx2 = scmutil.revpair(repo, revs)
6824 ctx1, ctx2 = scmutil.revpair(repo, revs)
6846
6825
6847 forcerelativevalue = None
6826 forcerelativevalue = None
6848 if ui.hasconfig(b'commands', b'status.relative'):
6827 if ui.hasconfig(b'commands', b'status.relative'):
6849 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6828 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6850 uipathfn = scmutil.getuipathfn(
6829 uipathfn = scmutil.getuipathfn(
6851 repo,
6830 repo,
6852 legacyrelativevalue=bool(pats),
6831 legacyrelativevalue=bool(pats),
6853 forcerelativevalue=forcerelativevalue,
6832 forcerelativevalue=forcerelativevalue,
6854 )
6833 )
6855
6834
6856 if opts.get(b'print0'):
6835 if opts.get(b'print0'):
6857 end = b'\0'
6836 end = b'\0'
6858 else:
6837 else:
6859 end = b'\n'
6838 end = b'\n'
6860 states = b'modified added removed deleted unknown ignored clean'.split()
6839 states = b'modified added removed deleted unknown ignored clean'.split()
6861 show = [k for k in states if opts.get(k)]
6840 show = [k for k in states if opts.get(k)]
6862 if opts.get(b'all'):
6841 if opts.get(b'all'):
6863 show += ui.quiet and (states[:4] + [b'clean']) or states
6842 show += ui.quiet and (states[:4] + [b'clean']) or states
6864
6843
6865 if not show:
6844 if not show:
6866 if ui.quiet:
6845 if ui.quiet:
6867 show = states[:4]
6846 show = states[:4]
6868 else:
6847 else:
6869 show = states[:5]
6848 show = states[:5]
6870
6849
6871 m = scmutil.match(ctx2, pats, opts)
6850 m = scmutil.match(ctx2, pats, opts)
6872 if terse:
6851 if terse:
6873 # we need to compute clean and unknown to terse
6852 # we need to compute clean and unknown to terse
6874 stat = repo.status(
6853 stat = repo.status(
6875 ctx1.node(),
6854 ctx1.node(),
6876 ctx2.node(),
6855 ctx2.node(),
6877 m,
6856 m,
6878 b'ignored' in show or b'i' in terse,
6857 b'ignored' in show or b'i' in terse,
6879 clean=True,
6858 clean=True,
6880 unknown=True,
6859 unknown=True,
6881 listsubrepos=opts.get(b'subrepos'),
6860 listsubrepos=opts.get(b'subrepos'),
6882 )
6861 )
6883
6862
6884 stat = cmdutil.tersedir(stat, terse)
6863 stat = cmdutil.tersedir(stat, terse)
6885 else:
6864 else:
6886 stat = repo.status(
6865 stat = repo.status(
6887 ctx1.node(),
6866 ctx1.node(),
6888 ctx2.node(),
6867 ctx2.node(),
6889 m,
6868 m,
6890 b'ignored' in show,
6869 b'ignored' in show,
6891 b'clean' in show,
6870 b'clean' in show,
6892 b'unknown' in show,
6871 b'unknown' in show,
6893 opts.get(b'subrepos'),
6872 opts.get(b'subrepos'),
6894 )
6873 )
6895
6874
6896 changestates = zip(
6875 changestates = zip(
6897 states,
6876 states,
6898 pycompat.iterbytestr(b'MAR!?IC'),
6877 pycompat.iterbytestr(b'MAR!?IC'),
6899 [getattr(stat, s.decode('utf8')) for s in states],
6878 [getattr(stat, s.decode('utf8')) for s in states],
6900 )
6879 )
6901
6880
6902 copy = {}
6881 copy = {}
6903 if (
6882 if (
6904 opts.get(b'all')
6883 opts.get(b'all')
6905 or opts.get(b'copies')
6884 or opts.get(b'copies')
6906 or ui.configbool(b'ui', b'statuscopies')
6885 or ui.configbool(b'ui', b'statuscopies')
6907 ) and not opts.get(b'no_status'):
6886 ) and not opts.get(b'no_status'):
6908 copy = copies.pathcopies(ctx1, ctx2, m)
6887 copy = copies.pathcopies(ctx1, ctx2, m)
6909
6888
6910 morestatus = None
6889 morestatus = None
6911 if (
6890 if (
6912 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6891 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6913 and not ui.plain()
6892 and not ui.plain()
6914 and not opts.get(b'print0')
6893 and not opts.get(b'print0')
6915 ):
6894 ):
6916 morestatus = cmdutil.readmorestatus(repo)
6895 morestatus = cmdutil.readmorestatus(repo)
6917
6896
6918 ui.pager(b'status')
6897 ui.pager(b'status')
6919 fm = ui.formatter(b'status', opts)
6898 fm = ui.formatter(b'status', opts)
6920 fmt = b'%s' + end
6899 fmt = b'%s' + end
6921 showchar = not opts.get(b'no_status')
6900 showchar = not opts.get(b'no_status')
6922
6901
6923 for state, char, files in changestates:
6902 for state, char, files in changestates:
6924 if state in show:
6903 if state in show:
6925 label = b'status.' + state
6904 label = b'status.' + state
6926 for f in files:
6905 for f in files:
6927 fm.startitem()
6906 fm.startitem()
6928 fm.context(ctx=ctx2)
6907 fm.context(ctx=ctx2)
6929 fm.data(itemtype=b'file', path=f)
6908 fm.data(itemtype=b'file', path=f)
6930 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6909 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6931 fm.plain(fmt % uipathfn(f), label=label)
6910 fm.plain(fmt % uipathfn(f), label=label)
6932 if f in copy:
6911 if f in copy:
6933 fm.data(source=copy[f])
6912 fm.data(source=copy[f])
6934 fm.plain(
6913 fm.plain(
6935 (b' %s' + end) % uipathfn(copy[f]),
6914 (b' %s' + end) % uipathfn(copy[f]),
6936 label=b'status.copied',
6915 label=b'status.copied',
6937 )
6916 )
6938 if morestatus:
6917 if morestatus:
6939 morestatus.formatfile(f, fm)
6918 morestatus.formatfile(f, fm)
6940
6919
6941 if morestatus:
6920 if morestatus:
6942 morestatus.formatfooter(fm)
6921 morestatus.formatfooter(fm)
6943 fm.end()
6922 fm.end()
6944
6923
6945
6924
6946 @command(
6925 @command(
6947 b'summary|sum',
6926 b'summary|sum',
6948 [(b'', b'remote', None, _(b'check for push and pull'))],
6927 [(b'', b'remote', None, _(b'check for push and pull'))],
6949 b'[--remote]',
6928 b'[--remote]',
6950 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6929 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6951 helpbasic=True,
6930 helpbasic=True,
6952 intents={INTENT_READONLY},
6931 intents={INTENT_READONLY},
6953 )
6932 )
6954 def summary(ui, repo, **opts):
6933 def summary(ui, repo, **opts):
6955 """summarize working directory state
6934 """summarize working directory state
6956
6935
6957 This generates a brief summary of the working directory state,
6936 This generates a brief summary of the working directory state,
6958 including parents, branch, commit status, phase and available updates.
6937 including parents, branch, commit status, phase and available updates.
6959
6938
6960 With the --remote option, this will check the default paths for
6939 With the --remote option, this will check the default paths for
6961 incoming and outgoing changes. This can be time-consuming.
6940 incoming and outgoing changes. This can be time-consuming.
6962
6941
6963 Returns 0 on success.
6942 Returns 0 on success.
6964 """
6943 """
6965
6944
6966 opts = pycompat.byteskwargs(opts)
6945 opts = pycompat.byteskwargs(opts)
6967 ui.pager(b'summary')
6946 ui.pager(b'summary')
6968 ctx = repo[None]
6947 ctx = repo[None]
6969 parents = ctx.parents()
6948 parents = ctx.parents()
6970 pnode = parents[0].node()
6949 pnode = parents[0].node()
6971 marks = []
6950 marks = []
6972
6951
6973 try:
6952 try:
6974 ms = mergestatemod.mergestate.read(repo)
6953 ms = mergestatemod.mergestate.read(repo)
6975 except error.UnsupportedMergeRecords as e:
6954 except error.UnsupportedMergeRecords as e:
6976 s = b' '.join(e.recordtypes)
6955 s = b' '.join(e.recordtypes)
6977 ui.warn(
6956 ui.warn(
6978 _(b'warning: merge state has unsupported record types: %s\n') % s
6957 _(b'warning: merge state has unsupported record types: %s\n') % s
6979 )
6958 )
6980 unresolved = []
6959 unresolved = []
6981 else:
6960 else:
6982 unresolved = list(ms.unresolved())
6961 unresolved = list(ms.unresolved())
6983
6962
6984 for p in parents:
6963 for p in parents:
6985 # label with log.changeset (instead of log.parent) since this
6964 # label with log.changeset (instead of log.parent) since this
6986 # shows a working directory parent *changeset*:
6965 # shows a working directory parent *changeset*:
6987 # i18n: column positioning for "hg summary"
6966 # i18n: column positioning for "hg summary"
6988 ui.write(
6967 ui.write(
6989 _(b'parent: %d:%s ') % (p.rev(), p),
6968 _(b'parent: %d:%s ') % (p.rev(), p),
6990 label=logcmdutil.changesetlabels(p),
6969 label=logcmdutil.changesetlabels(p),
6991 )
6970 )
6992 ui.write(b' '.join(p.tags()), label=b'log.tag')
6971 ui.write(b' '.join(p.tags()), label=b'log.tag')
6993 if p.bookmarks():
6972 if p.bookmarks():
6994 marks.extend(p.bookmarks())
6973 marks.extend(p.bookmarks())
6995 if p.rev() == -1:
6974 if p.rev() == -1:
6996 if not len(repo):
6975 if not len(repo):
6997 ui.write(_(b' (empty repository)'))
6976 ui.write(_(b' (empty repository)'))
6998 else:
6977 else:
6999 ui.write(_(b' (no revision checked out)'))
6978 ui.write(_(b' (no revision checked out)'))
7000 if p.obsolete():
6979 if p.obsolete():
7001 ui.write(_(b' (obsolete)'))
6980 ui.write(_(b' (obsolete)'))
7002 if p.isunstable():
6981 if p.isunstable():
7003 instabilities = (
6982 instabilities = (
7004 ui.label(instability, b'trouble.%s' % instability)
6983 ui.label(instability, b'trouble.%s' % instability)
7005 for instability in p.instabilities()
6984 for instability in p.instabilities()
7006 )
6985 )
7007 ui.write(b' (' + b', '.join(instabilities) + b')')
6986 ui.write(b' (' + b', '.join(instabilities) + b')')
7008 ui.write(b'\n')
6987 ui.write(b'\n')
7009 if p.description():
6988 if p.description():
7010 ui.status(
6989 ui.status(
7011 b' ' + p.description().splitlines()[0].strip() + b'\n',
6990 b' ' + p.description().splitlines()[0].strip() + b'\n',
7012 label=b'log.summary',
6991 label=b'log.summary',
7013 )
6992 )
7014
6993
7015 branch = ctx.branch()
6994 branch = ctx.branch()
7016 bheads = repo.branchheads(branch)
6995 bheads = repo.branchheads(branch)
7017 # i18n: column positioning for "hg summary"
6996 # i18n: column positioning for "hg summary"
7018 m = _(b'branch: %s\n') % branch
6997 m = _(b'branch: %s\n') % branch
7019 if branch != b'default':
6998 if branch != b'default':
7020 ui.write(m, label=b'log.branch')
6999 ui.write(m, label=b'log.branch')
7021 else:
7000 else:
7022 ui.status(m, label=b'log.branch')
7001 ui.status(m, label=b'log.branch')
7023
7002
7024 if marks:
7003 if marks:
7025 active = repo._activebookmark
7004 active = repo._activebookmark
7026 # i18n: column positioning for "hg summary"
7005 # i18n: column positioning for "hg summary"
7027 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7006 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7028 if active is not None:
7007 if active is not None:
7029 if active in marks:
7008 if active in marks:
7030 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7009 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7031 marks.remove(active)
7010 marks.remove(active)
7032 else:
7011 else:
7033 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7012 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7034 for m in marks:
7013 for m in marks:
7035 ui.write(b' ' + m, label=b'log.bookmark')
7014 ui.write(b' ' + m, label=b'log.bookmark')
7036 ui.write(b'\n', label=b'log.bookmark')
7015 ui.write(b'\n', label=b'log.bookmark')
7037
7016
7038 status = repo.status(unknown=True)
7017 status = repo.status(unknown=True)
7039
7018
7040 c = repo.dirstate.copies()
7019 c = repo.dirstate.copies()
7041 copied, renamed = [], []
7020 copied, renamed = [], []
7042 for d, s in pycompat.iteritems(c):
7021 for d, s in pycompat.iteritems(c):
7043 if s in status.removed:
7022 if s in status.removed:
7044 status.removed.remove(s)
7023 status.removed.remove(s)
7045 renamed.append(d)
7024 renamed.append(d)
7046 else:
7025 else:
7047 copied.append(d)
7026 copied.append(d)
7048 if d in status.added:
7027 if d in status.added:
7049 status.added.remove(d)
7028 status.added.remove(d)
7050
7029
7051 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7030 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7052
7031
7053 labels = [
7032 labels = [
7054 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7033 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7055 (ui.label(_(b'%d added'), b'status.added'), status.added),
7034 (ui.label(_(b'%d added'), b'status.added'), status.added),
7056 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7035 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7057 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7036 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7058 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7037 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7059 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7038 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7060 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7039 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7061 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7040 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7062 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7041 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7063 ]
7042 ]
7064 t = []
7043 t = []
7065 for l, s in labels:
7044 for l, s in labels:
7066 if s:
7045 if s:
7067 t.append(l % len(s))
7046 t.append(l % len(s))
7068
7047
7069 t = b', '.join(t)
7048 t = b', '.join(t)
7070 cleanworkdir = False
7049 cleanworkdir = False
7071
7050
7072 if repo.vfs.exists(b'graftstate'):
7051 if repo.vfs.exists(b'graftstate'):
7073 t += _(b' (graft in progress)')
7052 t += _(b' (graft in progress)')
7074 if repo.vfs.exists(b'updatestate'):
7053 if repo.vfs.exists(b'updatestate'):
7075 t += _(b' (interrupted update)')
7054 t += _(b' (interrupted update)')
7076 elif len(parents) > 1:
7055 elif len(parents) > 1:
7077 t += _(b' (merge)')
7056 t += _(b' (merge)')
7078 elif branch != parents[0].branch():
7057 elif branch != parents[0].branch():
7079 t += _(b' (new branch)')
7058 t += _(b' (new branch)')
7080 elif parents[0].closesbranch() and pnode in repo.branchheads(
7059 elif parents[0].closesbranch() and pnode in repo.branchheads(
7081 branch, closed=True
7060 branch, closed=True
7082 ):
7061 ):
7083 t += _(b' (head closed)')
7062 t += _(b' (head closed)')
7084 elif not (
7063 elif not (
7085 status.modified
7064 status.modified
7086 or status.added
7065 or status.added
7087 or status.removed
7066 or status.removed
7088 or renamed
7067 or renamed
7089 or copied
7068 or copied
7090 or subs
7069 or subs
7091 ):
7070 ):
7092 t += _(b' (clean)')
7071 t += _(b' (clean)')
7093 cleanworkdir = True
7072 cleanworkdir = True
7094 elif pnode not in bheads:
7073 elif pnode not in bheads:
7095 t += _(b' (new branch head)')
7074 t += _(b' (new branch head)')
7096
7075
7097 if parents:
7076 if parents:
7098 pendingphase = max(p.phase() for p in parents)
7077 pendingphase = max(p.phase() for p in parents)
7099 else:
7078 else:
7100 pendingphase = phases.public
7079 pendingphase = phases.public
7101
7080
7102 if pendingphase > phases.newcommitphase(ui):
7081 if pendingphase > phases.newcommitphase(ui):
7103 t += b' (%s)' % phases.phasenames[pendingphase]
7082 t += b' (%s)' % phases.phasenames[pendingphase]
7104
7083
7105 if cleanworkdir:
7084 if cleanworkdir:
7106 # i18n: column positioning for "hg summary"
7085 # i18n: column positioning for "hg summary"
7107 ui.status(_(b'commit: %s\n') % t.strip())
7086 ui.status(_(b'commit: %s\n') % t.strip())
7108 else:
7087 else:
7109 # i18n: column positioning for "hg summary"
7088 # i18n: column positioning for "hg summary"
7110 ui.write(_(b'commit: %s\n') % t.strip())
7089 ui.write(_(b'commit: %s\n') % t.strip())
7111
7090
7112 # all ancestors of branch heads - all ancestors of parent = new csets
7091 # all ancestors of branch heads - all ancestors of parent = new csets
7113 new = len(
7092 new = len(
7114 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7093 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7115 )
7094 )
7116
7095
7117 if new == 0:
7096 if new == 0:
7118 # i18n: column positioning for "hg summary"
7097 # i18n: column positioning for "hg summary"
7119 ui.status(_(b'update: (current)\n'))
7098 ui.status(_(b'update: (current)\n'))
7120 elif pnode not in bheads:
7099 elif pnode not in bheads:
7121 # i18n: column positioning for "hg summary"
7100 # i18n: column positioning for "hg summary"
7122 ui.write(_(b'update: %d new changesets (update)\n') % new)
7101 ui.write(_(b'update: %d new changesets (update)\n') % new)
7123 else:
7102 else:
7124 # i18n: column positioning for "hg summary"
7103 # i18n: column positioning for "hg summary"
7125 ui.write(
7104 ui.write(
7126 _(b'update: %d new changesets, %d branch heads (merge)\n')
7105 _(b'update: %d new changesets, %d branch heads (merge)\n')
7127 % (new, len(bheads))
7106 % (new, len(bheads))
7128 )
7107 )
7129
7108
7130 t = []
7109 t = []
7131 draft = len(repo.revs(b'draft()'))
7110 draft = len(repo.revs(b'draft()'))
7132 if draft:
7111 if draft:
7133 t.append(_(b'%d draft') % draft)
7112 t.append(_(b'%d draft') % draft)
7134 secret = len(repo.revs(b'secret()'))
7113 secret = len(repo.revs(b'secret()'))
7135 if secret:
7114 if secret:
7136 t.append(_(b'%d secret') % secret)
7115 t.append(_(b'%d secret') % secret)
7137
7116
7138 if draft or secret:
7117 if draft or secret:
7139 ui.status(_(b'phases: %s\n') % b', '.join(t))
7118 ui.status(_(b'phases: %s\n') % b', '.join(t))
7140
7119
7141 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7120 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7142 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7121 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7143 numtrouble = len(repo.revs(trouble + b"()"))
7122 numtrouble = len(repo.revs(trouble + b"()"))
7144 # We write all the possibilities to ease translation
7123 # We write all the possibilities to ease translation
7145 troublemsg = {
7124 troublemsg = {
7146 b"orphan": _(b"orphan: %d changesets"),
7125 b"orphan": _(b"orphan: %d changesets"),
7147 b"contentdivergent": _(b"content-divergent: %d changesets"),
7126 b"contentdivergent": _(b"content-divergent: %d changesets"),
7148 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7127 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7149 }
7128 }
7150 if numtrouble > 0:
7129 if numtrouble > 0:
7151 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7130 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7152
7131
7153 cmdutil.summaryhooks(ui, repo)
7132 cmdutil.summaryhooks(ui, repo)
7154
7133
7155 if opts.get(b'remote'):
7134 if opts.get(b'remote'):
7156 needsincoming, needsoutgoing = True, True
7135 needsincoming, needsoutgoing = True, True
7157 else:
7136 else:
7158 needsincoming, needsoutgoing = False, False
7137 needsincoming, needsoutgoing = False, False
7159 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7138 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7160 if i:
7139 if i:
7161 needsincoming = True
7140 needsincoming = True
7162 if o:
7141 if o:
7163 needsoutgoing = True
7142 needsoutgoing = True
7164 if not needsincoming and not needsoutgoing:
7143 if not needsincoming and not needsoutgoing:
7165 return
7144 return
7166
7145
7167 def getincoming():
7146 def getincoming():
7168 source, branches = hg.parseurl(ui.expandpath(b'default'))
7147 source, branches = hg.parseurl(ui.expandpath(b'default'))
7169 sbranch = branches[0]
7148 sbranch = branches[0]
7170 try:
7149 try:
7171 other = hg.peer(repo, {}, source)
7150 other = hg.peer(repo, {}, source)
7172 except error.RepoError:
7151 except error.RepoError:
7173 if opts.get(b'remote'):
7152 if opts.get(b'remote'):
7174 raise
7153 raise
7175 return source, sbranch, None, None, None
7154 return source, sbranch, None, None, None
7176 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7155 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7177 if revs:
7156 if revs:
7178 revs = [other.lookup(rev) for rev in revs]
7157 revs = [other.lookup(rev) for rev in revs]
7179 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7158 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7180 repo.ui.pushbuffer()
7159 repo.ui.pushbuffer()
7181 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7160 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7182 repo.ui.popbuffer()
7161 repo.ui.popbuffer()
7183 return source, sbranch, other, commoninc, commoninc[1]
7162 return source, sbranch, other, commoninc, commoninc[1]
7184
7163
7185 if needsincoming:
7164 if needsincoming:
7186 source, sbranch, sother, commoninc, incoming = getincoming()
7165 source, sbranch, sother, commoninc, incoming = getincoming()
7187 else:
7166 else:
7188 source = sbranch = sother = commoninc = incoming = None
7167 source = sbranch = sother = commoninc = incoming = None
7189
7168
7190 def getoutgoing():
7169 def getoutgoing():
7191 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7170 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7192 dbranch = branches[0]
7171 dbranch = branches[0]
7193 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7172 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7194 if source != dest:
7173 if source != dest:
7195 try:
7174 try:
7196 dother = hg.peer(repo, {}, dest)
7175 dother = hg.peer(repo, {}, dest)
7197 except error.RepoError:
7176 except error.RepoError:
7198 if opts.get(b'remote'):
7177 if opts.get(b'remote'):
7199 raise
7178 raise
7200 return dest, dbranch, None, None
7179 return dest, dbranch, None, None
7201 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7180 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7202 elif sother is None:
7181 elif sother is None:
7203 # there is no explicit destination peer, but source one is invalid
7182 # there is no explicit destination peer, but source one is invalid
7204 return dest, dbranch, None, None
7183 return dest, dbranch, None, None
7205 else:
7184 else:
7206 dother = sother
7185 dother = sother
7207 if source != dest or (sbranch is not None and sbranch != dbranch):
7186 if source != dest or (sbranch is not None and sbranch != dbranch):
7208 common = None
7187 common = None
7209 else:
7188 else:
7210 common = commoninc
7189 common = commoninc
7211 if revs:
7190 if revs:
7212 revs = [repo.lookup(rev) for rev in revs]
7191 revs = [repo.lookup(rev) for rev in revs]
7213 repo.ui.pushbuffer()
7192 repo.ui.pushbuffer()
7214 outgoing = discovery.findcommonoutgoing(
7193 outgoing = discovery.findcommonoutgoing(
7215 repo, dother, onlyheads=revs, commoninc=common
7194 repo, dother, onlyheads=revs, commoninc=common
7216 )
7195 )
7217 repo.ui.popbuffer()
7196 repo.ui.popbuffer()
7218 return dest, dbranch, dother, outgoing
7197 return dest, dbranch, dother, outgoing
7219
7198
7220 if needsoutgoing:
7199 if needsoutgoing:
7221 dest, dbranch, dother, outgoing = getoutgoing()
7200 dest, dbranch, dother, outgoing = getoutgoing()
7222 else:
7201 else:
7223 dest = dbranch = dother = outgoing = None
7202 dest = dbranch = dother = outgoing = None
7224
7203
7225 if opts.get(b'remote'):
7204 if opts.get(b'remote'):
7226 t = []
7205 t = []
7227 if incoming:
7206 if incoming:
7228 t.append(_(b'1 or more incoming'))
7207 t.append(_(b'1 or more incoming'))
7229 o = outgoing.missing
7208 o = outgoing.missing
7230 if o:
7209 if o:
7231 t.append(_(b'%d outgoing') % len(o))
7210 t.append(_(b'%d outgoing') % len(o))
7232 other = dother or sother
7211 other = dother or sother
7233 if b'bookmarks' in other.listkeys(b'namespaces'):
7212 if b'bookmarks' in other.listkeys(b'namespaces'):
7234 counts = bookmarks.summary(repo, other)
7213 counts = bookmarks.summary(repo, other)
7235 if counts[0] > 0:
7214 if counts[0] > 0:
7236 t.append(_(b'%d incoming bookmarks') % counts[0])
7215 t.append(_(b'%d incoming bookmarks') % counts[0])
7237 if counts[1] > 0:
7216 if counts[1] > 0:
7238 t.append(_(b'%d outgoing bookmarks') % counts[1])
7217 t.append(_(b'%d outgoing bookmarks') % counts[1])
7239
7218
7240 if t:
7219 if t:
7241 # i18n: column positioning for "hg summary"
7220 # i18n: column positioning for "hg summary"
7242 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7221 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7243 else:
7222 else:
7244 # i18n: column positioning for "hg summary"
7223 # i18n: column positioning for "hg summary"
7245 ui.status(_(b'remote: (synced)\n'))
7224 ui.status(_(b'remote: (synced)\n'))
7246
7225
7247 cmdutil.summaryremotehooks(
7226 cmdutil.summaryremotehooks(
7248 ui,
7227 ui,
7249 repo,
7228 repo,
7250 opts,
7229 opts,
7251 (
7230 (
7252 (source, sbranch, sother, commoninc),
7231 (source, sbranch, sother, commoninc),
7253 (dest, dbranch, dother, outgoing),
7232 (dest, dbranch, dother, outgoing),
7254 ),
7233 ),
7255 )
7234 )
7256
7235
7257
7236
7258 @command(
7237 @command(
7259 b'tag',
7238 b'tag',
7260 [
7239 [
7261 (b'f', b'force', None, _(b'force tag')),
7240 (b'f', b'force', None, _(b'force tag')),
7262 (b'l', b'local', None, _(b'make the tag local')),
7241 (b'l', b'local', None, _(b'make the tag local')),
7263 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7242 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7264 (b'', b'remove', None, _(b'remove a tag')),
7243 (b'', b'remove', None, _(b'remove a tag')),
7265 # -l/--local is already there, commitopts cannot be used
7244 # -l/--local is already there, commitopts cannot be used
7266 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7245 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7267 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7246 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7268 ]
7247 ]
7269 + commitopts2,
7248 + commitopts2,
7270 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7249 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7271 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7250 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7272 )
7251 )
7273 def tag(ui, repo, name1, *names, **opts):
7252 def tag(ui, repo, name1, *names, **opts):
7274 """add one or more tags for the current or given revision
7253 """add one or more tags for the current or given revision
7275
7254
7276 Name a particular revision using <name>.
7255 Name a particular revision using <name>.
7277
7256
7278 Tags are used to name particular revisions of the repository and are
7257 Tags are used to name particular revisions of the repository and are
7279 very useful to compare different revisions, to go back to significant
7258 very useful to compare different revisions, to go back to significant
7280 earlier versions or to mark branch points as releases, etc. Changing
7259 earlier versions or to mark branch points as releases, etc. Changing
7281 an existing tag is normally disallowed; use -f/--force to override.
7260 an existing tag is normally disallowed; use -f/--force to override.
7282
7261
7283 If no revision is given, the parent of the working directory is
7262 If no revision is given, the parent of the working directory is
7284 used.
7263 used.
7285
7264
7286 To facilitate version control, distribution, and merging of tags,
7265 To facilitate version control, distribution, and merging of tags,
7287 they are stored as a file named ".hgtags" which is managed similarly
7266 they are stored as a file named ".hgtags" which is managed similarly
7288 to other project files and can be hand-edited if necessary. This
7267 to other project files and can be hand-edited if necessary. This
7289 also means that tagging creates a new commit. The file
7268 also means that tagging creates a new commit. The file
7290 ".hg/localtags" is used for local tags (not shared among
7269 ".hg/localtags" is used for local tags (not shared among
7291 repositories).
7270 repositories).
7292
7271
7293 Tag commits are usually made at the head of a branch. If the parent
7272 Tag commits are usually made at the head of a branch. If the parent
7294 of the working directory is not a branch head, :hg:`tag` aborts; use
7273 of the working directory is not a branch head, :hg:`tag` aborts; use
7295 -f/--force to force the tag commit to be based on a non-head
7274 -f/--force to force the tag commit to be based on a non-head
7296 changeset.
7275 changeset.
7297
7276
7298 See :hg:`help dates` for a list of formats valid for -d/--date.
7277 See :hg:`help dates` for a list of formats valid for -d/--date.
7299
7278
7300 Since tag names have priority over branch names during revision
7279 Since tag names have priority over branch names during revision
7301 lookup, using an existing branch name as a tag name is discouraged.
7280 lookup, using an existing branch name as a tag name is discouraged.
7302
7281
7303 Returns 0 on success.
7282 Returns 0 on success.
7304 """
7283 """
7305 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7284 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7306 opts = pycompat.byteskwargs(opts)
7285 opts = pycompat.byteskwargs(opts)
7307 with repo.wlock(), repo.lock():
7286 with repo.wlock(), repo.lock():
7308 rev_ = b"."
7287 rev_ = b"."
7309 names = [t.strip() for t in (name1,) + names]
7288 names = [t.strip() for t in (name1,) + names]
7310 if len(names) != len(set(names)):
7289 if len(names) != len(set(names)):
7311 raise error.InputError(_(b'tag names must be unique'))
7290 raise error.InputError(_(b'tag names must be unique'))
7312 for n in names:
7291 for n in names:
7313 scmutil.checknewlabel(repo, n, b'tag')
7292 scmutil.checknewlabel(repo, n, b'tag')
7314 if not n:
7293 if not n:
7315 raise error.InputError(
7294 raise error.InputError(
7316 _(b'tag names cannot consist entirely of whitespace')
7295 _(b'tag names cannot consist entirely of whitespace')
7317 )
7296 )
7318 if opts.get(b'rev'):
7297 if opts.get(b'rev'):
7319 rev_ = opts[b'rev']
7298 rev_ = opts[b'rev']
7320 message = opts.get(b'message')
7299 message = opts.get(b'message')
7321 if opts.get(b'remove'):
7300 if opts.get(b'remove'):
7322 if opts.get(b'local'):
7301 if opts.get(b'local'):
7323 expectedtype = b'local'
7302 expectedtype = b'local'
7324 else:
7303 else:
7325 expectedtype = b'global'
7304 expectedtype = b'global'
7326
7305
7327 for n in names:
7306 for n in names:
7328 if repo.tagtype(n) == b'global':
7307 if repo.tagtype(n) == b'global':
7329 alltags = tagsmod.findglobaltags(ui, repo)
7308 alltags = tagsmod.findglobaltags(ui, repo)
7330 if alltags[n][0] == nullid:
7309 if alltags[n][0] == nullid:
7331 raise error.InputError(
7310 raise error.InputError(
7332 _(b"tag '%s' is already removed") % n
7311 _(b"tag '%s' is already removed") % n
7333 )
7312 )
7334 if not repo.tagtype(n):
7313 if not repo.tagtype(n):
7335 raise error.InputError(_(b"tag '%s' does not exist") % n)
7314 raise error.InputError(_(b"tag '%s' does not exist") % n)
7336 if repo.tagtype(n) != expectedtype:
7315 if repo.tagtype(n) != expectedtype:
7337 if expectedtype == b'global':
7316 if expectedtype == b'global':
7338 raise error.InputError(
7317 raise error.InputError(
7339 _(b"tag '%s' is not a global tag") % n
7318 _(b"tag '%s' is not a global tag") % n
7340 )
7319 )
7341 else:
7320 else:
7342 raise error.InputError(
7321 raise error.InputError(
7343 _(b"tag '%s' is not a local tag") % n
7322 _(b"tag '%s' is not a local tag") % n
7344 )
7323 )
7345 rev_ = b'null'
7324 rev_ = b'null'
7346 if not message:
7325 if not message:
7347 # we don't translate commit messages
7326 # we don't translate commit messages
7348 message = b'Removed tag %s' % b', '.join(names)
7327 message = b'Removed tag %s' % b', '.join(names)
7349 elif not opts.get(b'force'):
7328 elif not opts.get(b'force'):
7350 for n in names:
7329 for n in names:
7351 if n in repo.tags():
7330 if n in repo.tags():
7352 raise error.InputError(
7331 raise error.InputError(
7353 _(b"tag '%s' already exists (use -f to force)") % n
7332 _(b"tag '%s' already exists (use -f to force)") % n
7354 )
7333 )
7355 if not opts.get(b'local'):
7334 if not opts.get(b'local'):
7356 p1, p2 = repo.dirstate.parents()
7335 p1, p2 = repo.dirstate.parents()
7357 if p2 != nullid:
7336 if p2 != nullid:
7358 raise error.StateError(_(b'uncommitted merge'))
7337 raise error.StateError(_(b'uncommitted merge'))
7359 bheads = repo.branchheads()
7338 bheads = repo.branchheads()
7360 if not opts.get(b'force') and bheads and p1 not in bheads:
7339 if not opts.get(b'force') and bheads and p1 not in bheads:
7361 raise error.InputError(
7340 raise error.InputError(
7362 _(
7341 _(
7363 b'working directory is not at a branch head '
7342 b'working directory is not at a branch head '
7364 b'(use -f to force)'
7343 b'(use -f to force)'
7365 )
7344 )
7366 )
7345 )
7367 node = scmutil.revsingle(repo, rev_).node()
7346 node = scmutil.revsingle(repo, rev_).node()
7368
7347
7369 if not message:
7348 if not message:
7370 # we don't translate commit messages
7349 # we don't translate commit messages
7371 message = b'Added tag %s for changeset %s' % (
7350 message = b'Added tag %s for changeset %s' % (
7372 b', '.join(names),
7351 b', '.join(names),
7373 short(node),
7352 short(node),
7374 )
7353 )
7375
7354
7376 date = opts.get(b'date')
7355 date = opts.get(b'date')
7377 if date:
7356 if date:
7378 date = dateutil.parsedate(date)
7357 date = dateutil.parsedate(date)
7379
7358
7380 if opts.get(b'remove'):
7359 if opts.get(b'remove'):
7381 editform = b'tag.remove'
7360 editform = b'tag.remove'
7382 else:
7361 else:
7383 editform = b'tag.add'
7362 editform = b'tag.add'
7384 editor = cmdutil.getcommiteditor(
7363 editor = cmdutil.getcommiteditor(
7385 editform=editform, **pycompat.strkwargs(opts)
7364 editform=editform, **pycompat.strkwargs(opts)
7386 )
7365 )
7387
7366
7388 # don't allow tagging the null rev
7367 # don't allow tagging the null rev
7389 if (
7368 if (
7390 not opts.get(b'remove')
7369 not opts.get(b'remove')
7391 and scmutil.revsingle(repo, rev_).rev() == nullrev
7370 and scmutil.revsingle(repo, rev_).rev() == nullrev
7392 ):
7371 ):
7393 raise error.InputError(_(b"cannot tag null revision"))
7372 raise error.InputError(_(b"cannot tag null revision"))
7394
7373
7395 tagsmod.tag(
7374 tagsmod.tag(
7396 repo,
7375 repo,
7397 names,
7376 names,
7398 node,
7377 node,
7399 message,
7378 message,
7400 opts.get(b'local'),
7379 opts.get(b'local'),
7401 opts.get(b'user'),
7380 opts.get(b'user'),
7402 date,
7381 date,
7403 editor=editor,
7382 editor=editor,
7404 )
7383 )
7405
7384
7406
7385
7407 @command(
7386 @command(
7408 b'tags',
7387 b'tags',
7409 formatteropts,
7388 formatteropts,
7410 b'',
7389 b'',
7411 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7390 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7412 intents={INTENT_READONLY},
7391 intents={INTENT_READONLY},
7413 )
7392 )
7414 def tags(ui, repo, **opts):
7393 def tags(ui, repo, **opts):
7415 """list repository tags
7394 """list repository tags
7416
7395
7417 This lists both regular and local tags. When the -v/--verbose
7396 This lists both regular and local tags. When the -v/--verbose
7418 switch is used, a third column "local" is printed for local tags.
7397 switch is used, a third column "local" is printed for local tags.
7419 When the -q/--quiet switch is used, only the tag name is printed.
7398 When the -q/--quiet switch is used, only the tag name is printed.
7420
7399
7421 .. container:: verbose
7400 .. container:: verbose
7422
7401
7423 Template:
7402 Template:
7424
7403
7425 The following keywords are supported in addition to the common template
7404 The following keywords are supported in addition to the common template
7426 keywords and functions such as ``{tag}``. See also
7405 keywords and functions such as ``{tag}``. See also
7427 :hg:`help templates`.
7406 :hg:`help templates`.
7428
7407
7429 :type: String. ``local`` for local tags.
7408 :type: String. ``local`` for local tags.
7430
7409
7431 Returns 0 on success.
7410 Returns 0 on success.
7432 """
7411 """
7433
7412
7434 opts = pycompat.byteskwargs(opts)
7413 opts = pycompat.byteskwargs(opts)
7435 ui.pager(b'tags')
7414 ui.pager(b'tags')
7436 fm = ui.formatter(b'tags', opts)
7415 fm = ui.formatter(b'tags', opts)
7437 hexfunc = fm.hexfunc
7416 hexfunc = fm.hexfunc
7438
7417
7439 for t, n in reversed(repo.tagslist()):
7418 for t, n in reversed(repo.tagslist()):
7440 hn = hexfunc(n)
7419 hn = hexfunc(n)
7441 label = b'tags.normal'
7420 label = b'tags.normal'
7442 tagtype = repo.tagtype(t)
7421 tagtype = repo.tagtype(t)
7443 if not tagtype or tagtype == b'global':
7422 if not tagtype or tagtype == b'global':
7444 tagtype = b''
7423 tagtype = b''
7445 else:
7424 else:
7446 label = b'tags.' + tagtype
7425 label = b'tags.' + tagtype
7447
7426
7448 fm.startitem()
7427 fm.startitem()
7449 fm.context(repo=repo)
7428 fm.context(repo=repo)
7450 fm.write(b'tag', b'%s', t, label=label)
7429 fm.write(b'tag', b'%s', t, label=label)
7451 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7430 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7452 fm.condwrite(
7431 fm.condwrite(
7453 not ui.quiet,
7432 not ui.quiet,
7454 b'rev node',
7433 b'rev node',
7455 fmt,
7434 fmt,
7456 repo.changelog.rev(n),
7435 repo.changelog.rev(n),
7457 hn,
7436 hn,
7458 label=label,
7437 label=label,
7459 )
7438 )
7460 fm.condwrite(
7439 fm.condwrite(
7461 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7440 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7462 )
7441 )
7463 fm.plain(b'\n')
7442 fm.plain(b'\n')
7464 fm.end()
7443 fm.end()
7465
7444
7466
7445
7467 @command(
7446 @command(
7468 b'tip',
7447 b'tip',
7469 [
7448 [
7470 (b'p', b'patch', None, _(b'show patch')),
7449 (b'p', b'patch', None, _(b'show patch')),
7471 (b'g', b'git', None, _(b'use git extended diff format')),
7450 (b'g', b'git', None, _(b'use git extended diff format')),
7472 ]
7451 ]
7473 + templateopts,
7452 + templateopts,
7474 _(b'[-p] [-g]'),
7453 _(b'[-p] [-g]'),
7475 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7454 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7476 )
7455 )
7477 def tip(ui, repo, **opts):
7456 def tip(ui, repo, **opts):
7478 """show the tip revision (DEPRECATED)
7457 """show the tip revision (DEPRECATED)
7479
7458
7480 The tip revision (usually just called the tip) is the changeset
7459 The tip revision (usually just called the tip) is the changeset
7481 most recently added to the repository (and therefore the most
7460 most recently added to the repository (and therefore the most
7482 recently changed head).
7461 recently changed head).
7483
7462
7484 If you have just made a commit, that commit will be the tip. If
7463 If you have just made a commit, that commit will be the tip. If
7485 you have just pulled changes from another repository, the tip of
7464 you have just pulled changes from another repository, the tip of
7486 that repository becomes the current tip. The "tip" tag is special
7465 that repository becomes the current tip. The "tip" tag is special
7487 and cannot be renamed or assigned to a different changeset.
7466 and cannot be renamed or assigned to a different changeset.
7488
7467
7489 This command is deprecated, please use :hg:`heads` instead.
7468 This command is deprecated, please use :hg:`heads` instead.
7490
7469
7491 Returns 0 on success.
7470 Returns 0 on success.
7492 """
7471 """
7493 opts = pycompat.byteskwargs(opts)
7472 opts = pycompat.byteskwargs(opts)
7494 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7473 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7495 displayer.show(repo[b'tip'])
7474 displayer.show(repo[b'tip'])
7496 displayer.close()
7475 displayer.close()
7497
7476
7498
7477
7499 @command(
7478 @command(
7500 b'unbundle',
7479 b'unbundle',
7501 [
7480 [
7502 (
7481 (
7503 b'u',
7482 b'u',
7504 b'update',
7483 b'update',
7505 None,
7484 None,
7506 _(b'update to new branch head if changesets were unbundled'),
7485 _(b'update to new branch head if changesets were unbundled'),
7507 )
7486 )
7508 ],
7487 ],
7509 _(b'[-u] FILE...'),
7488 _(b'[-u] FILE...'),
7510 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7489 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7511 )
7490 )
7512 def unbundle(ui, repo, fname1, *fnames, **opts):
7491 def unbundle(ui, repo, fname1, *fnames, **opts):
7513 """apply one or more bundle files
7492 """apply one or more bundle files
7514
7493
7515 Apply one or more bundle files generated by :hg:`bundle`.
7494 Apply one or more bundle files generated by :hg:`bundle`.
7516
7495
7517 Returns 0 on success, 1 if an update has unresolved files.
7496 Returns 0 on success, 1 if an update has unresolved files.
7518 """
7497 """
7519 fnames = (fname1,) + fnames
7498 fnames = (fname1,) + fnames
7520
7499
7521 with repo.lock():
7500 with repo.lock():
7522 for fname in fnames:
7501 for fname in fnames:
7523 f = hg.openpath(ui, fname)
7502 f = hg.openpath(ui, fname)
7524 gen = exchange.readbundle(ui, f, fname)
7503 gen = exchange.readbundle(ui, f, fname)
7525 if isinstance(gen, streamclone.streamcloneapplier):
7504 if isinstance(gen, streamclone.streamcloneapplier):
7526 raise error.InputError(
7505 raise error.InputError(
7527 _(
7506 _(
7528 b'packed bundles cannot be applied with '
7507 b'packed bundles cannot be applied with '
7529 b'"hg unbundle"'
7508 b'"hg unbundle"'
7530 ),
7509 ),
7531 hint=_(b'use "hg debugapplystreamclonebundle"'),
7510 hint=_(b'use "hg debugapplystreamclonebundle"'),
7532 )
7511 )
7533 url = b'bundle:' + fname
7512 url = b'bundle:' + fname
7534 try:
7513 try:
7535 txnname = b'unbundle'
7514 txnname = b'unbundle'
7536 if not isinstance(gen, bundle2.unbundle20):
7515 if not isinstance(gen, bundle2.unbundle20):
7537 txnname = b'unbundle\n%s' % util.hidepassword(url)
7516 txnname = b'unbundle\n%s' % util.hidepassword(url)
7538 with repo.transaction(txnname) as tr:
7517 with repo.transaction(txnname) as tr:
7539 op = bundle2.applybundle(
7518 op = bundle2.applybundle(
7540 repo, gen, tr, source=b'unbundle', url=url
7519 repo, gen, tr, source=b'unbundle', url=url
7541 )
7520 )
7542 except error.BundleUnknownFeatureError as exc:
7521 except error.BundleUnknownFeatureError as exc:
7543 raise error.Abort(
7522 raise error.Abort(
7544 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7523 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7545 hint=_(
7524 hint=_(
7546 b"see https://mercurial-scm.org/"
7525 b"see https://mercurial-scm.org/"
7547 b"wiki/BundleFeature for more "
7526 b"wiki/BundleFeature for more "
7548 b"information"
7527 b"information"
7549 ),
7528 ),
7550 )
7529 )
7551 modheads = bundle2.combinechangegroupresults(op)
7530 modheads = bundle2.combinechangegroupresults(op)
7552
7531
7553 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7532 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7554
7533
7555
7534
7556 @command(
7535 @command(
7557 b'unshelve',
7536 b'unshelve',
7558 [
7537 [
7559 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7538 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7560 (
7539 (
7561 b'c',
7540 b'c',
7562 b'continue',
7541 b'continue',
7563 None,
7542 None,
7564 _(b'continue an incomplete unshelve operation'),
7543 _(b'continue an incomplete unshelve operation'),
7565 ),
7544 ),
7566 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7545 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7567 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7546 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7568 (
7547 (
7569 b'n',
7548 b'n',
7570 b'name',
7549 b'name',
7571 b'',
7550 b'',
7572 _(b'restore shelved change with given name'),
7551 _(b'restore shelved change with given name'),
7573 _(b'NAME'),
7552 _(b'NAME'),
7574 ),
7553 ),
7575 (b't', b'tool', b'', _(b'specify merge tool')),
7554 (b't', b'tool', b'', _(b'specify merge tool')),
7576 (
7555 (
7577 b'',
7556 b'',
7578 b'date',
7557 b'date',
7579 b'',
7558 b'',
7580 _(b'set date for temporary commits (DEPRECATED)'),
7559 _(b'set date for temporary commits (DEPRECATED)'),
7581 _(b'DATE'),
7560 _(b'DATE'),
7582 ),
7561 ),
7583 ],
7562 ],
7584 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7563 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7585 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7564 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7586 )
7565 )
7587 def unshelve(ui, repo, *shelved, **opts):
7566 def unshelve(ui, repo, *shelved, **opts):
7588 """restore a shelved change to the working directory
7567 """restore a shelved change to the working directory
7589
7568
7590 This command accepts an optional name of a shelved change to
7569 This command accepts an optional name of a shelved change to
7591 restore. If none is given, the most recent shelved change is used.
7570 restore. If none is given, the most recent shelved change is used.
7592
7571
7593 If a shelved change is applied successfully, the bundle that
7572 If a shelved change is applied successfully, the bundle that
7594 contains the shelved changes is moved to a backup location
7573 contains the shelved changes is moved to a backup location
7595 (.hg/shelve-backup).
7574 (.hg/shelve-backup).
7596
7575
7597 Since you can restore a shelved change on top of an arbitrary
7576 Since you can restore a shelved change on top of an arbitrary
7598 commit, it is possible that unshelving will result in a conflict
7577 commit, it is possible that unshelving will result in a conflict
7599 between your changes and the commits you are unshelving onto. If
7578 between your changes and the commits you are unshelving onto. If
7600 this occurs, you must resolve the conflict, then use
7579 this occurs, you must resolve the conflict, then use
7601 ``--continue`` to complete the unshelve operation. (The bundle
7580 ``--continue`` to complete the unshelve operation. (The bundle
7602 will not be moved until you successfully complete the unshelve.)
7581 will not be moved until you successfully complete the unshelve.)
7603
7582
7604 (Alternatively, you can use ``--abort`` to abandon an unshelve
7583 (Alternatively, you can use ``--abort`` to abandon an unshelve
7605 that causes a conflict. This reverts the unshelved changes, and
7584 that causes a conflict. This reverts the unshelved changes, and
7606 leaves the bundle in place.)
7585 leaves the bundle in place.)
7607
7586
7608 If bare shelved change (without interactive, include and exclude
7587 If bare shelved change (without interactive, include and exclude
7609 option) was done on newly created branch it would restore branch
7588 option) was done on newly created branch it would restore branch
7610 information to the working directory.
7589 information to the working directory.
7611
7590
7612 After a successful unshelve, the shelved changes are stored in a
7591 After a successful unshelve, the shelved changes are stored in a
7613 backup directory. Only the N most recent backups are kept. N
7592 backup directory. Only the N most recent backups are kept. N
7614 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7593 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7615 configuration option.
7594 configuration option.
7616
7595
7617 .. container:: verbose
7596 .. container:: verbose
7618
7597
7619 Timestamp in seconds is used to decide order of backups. More
7598 Timestamp in seconds is used to decide order of backups. More
7620 than ``maxbackups`` backups are kept, if same timestamp
7599 than ``maxbackups`` backups are kept, if same timestamp
7621 prevents from deciding exact order of them, for safety.
7600 prevents from deciding exact order of them, for safety.
7622
7601
7623 Selected changes can be unshelved with ``--interactive`` flag.
7602 Selected changes can be unshelved with ``--interactive`` flag.
7624 The working directory is updated with the selected changes, and
7603 The working directory is updated with the selected changes, and
7625 only the unselected changes remain shelved.
7604 only the unselected changes remain shelved.
7626 Note: The whole shelve is applied to working directory first before
7605 Note: The whole shelve is applied to working directory first before
7627 running interactively. So, this will bring up all the conflicts between
7606 running interactively. So, this will bring up all the conflicts between
7628 working directory and the shelve, irrespective of which changes will be
7607 working directory and the shelve, irrespective of which changes will be
7629 unshelved.
7608 unshelved.
7630 """
7609 """
7631 with repo.wlock():
7610 with repo.wlock():
7632 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7611 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7633
7612
7634
7613
7635 statemod.addunfinished(
7614 statemod.addunfinished(
7636 b'unshelve',
7615 b'unshelve',
7637 fname=b'shelvedstate',
7616 fname=b'shelvedstate',
7638 continueflag=True,
7617 continueflag=True,
7639 abortfunc=shelvemod.hgabortunshelve,
7618 abortfunc=shelvemod.hgabortunshelve,
7640 continuefunc=shelvemod.hgcontinueunshelve,
7619 continuefunc=shelvemod.hgcontinueunshelve,
7641 cmdmsg=_(b'unshelve already in progress'),
7620 cmdmsg=_(b'unshelve already in progress'),
7642 )
7621 )
7643
7622
7644
7623
7645 @command(
7624 @command(
7646 b'update|up|checkout|co',
7625 b'update|up|checkout|co',
7647 [
7626 [
7648 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7627 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7649 (b'c', b'check', None, _(b'require clean working directory')),
7628 (b'c', b'check', None, _(b'require clean working directory')),
7650 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7629 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7651 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7630 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7652 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7631 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7653 ]
7632 ]
7654 + mergetoolopts,
7633 + mergetoolopts,
7655 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7634 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7656 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7635 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7657 helpbasic=True,
7636 helpbasic=True,
7658 )
7637 )
7659 def update(ui, repo, node=None, **opts):
7638 def update(ui, repo, node=None, **opts):
7660 """update working directory (or switch revisions)
7639 """update working directory (or switch revisions)
7661
7640
7662 Update the repository's working directory to the specified
7641 Update the repository's working directory to the specified
7663 changeset. If no changeset is specified, update to the tip of the
7642 changeset. If no changeset is specified, update to the tip of the
7664 current named branch and move the active bookmark (see :hg:`help
7643 current named branch and move the active bookmark (see :hg:`help
7665 bookmarks`).
7644 bookmarks`).
7666
7645
7667 Update sets the working directory's parent revision to the specified
7646 Update sets the working directory's parent revision to the specified
7668 changeset (see :hg:`help parents`).
7647 changeset (see :hg:`help parents`).
7669
7648
7670 If the changeset is not a descendant or ancestor of the working
7649 If the changeset is not a descendant or ancestor of the working
7671 directory's parent and there are uncommitted changes, the update is
7650 directory's parent and there are uncommitted changes, the update is
7672 aborted. With the -c/--check option, the working directory is checked
7651 aborted. With the -c/--check option, the working directory is checked
7673 for uncommitted changes; if none are found, the working directory is
7652 for uncommitted changes; if none are found, the working directory is
7674 updated to the specified changeset.
7653 updated to the specified changeset.
7675
7654
7676 .. container:: verbose
7655 .. container:: verbose
7677
7656
7678 The -C/--clean, -c/--check, and -m/--merge options control what
7657 The -C/--clean, -c/--check, and -m/--merge options control what
7679 happens if the working directory contains uncommitted changes.
7658 happens if the working directory contains uncommitted changes.
7680 At most of one of them can be specified.
7659 At most of one of them can be specified.
7681
7660
7682 1. If no option is specified, and if
7661 1. If no option is specified, and if
7683 the requested changeset is an ancestor or descendant of
7662 the requested changeset is an ancestor or descendant of
7684 the working directory's parent, the uncommitted changes
7663 the working directory's parent, the uncommitted changes
7685 are merged into the requested changeset and the merged
7664 are merged into the requested changeset and the merged
7686 result is left uncommitted. If the requested changeset is
7665 result is left uncommitted. If the requested changeset is
7687 not an ancestor or descendant (that is, it is on another
7666 not an ancestor or descendant (that is, it is on another
7688 branch), the update is aborted and the uncommitted changes
7667 branch), the update is aborted and the uncommitted changes
7689 are preserved.
7668 are preserved.
7690
7669
7691 2. With the -m/--merge option, the update is allowed even if the
7670 2. With the -m/--merge option, the update is allowed even if the
7692 requested changeset is not an ancestor or descendant of
7671 requested changeset is not an ancestor or descendant of
7693 the working directory's parent.
7672 the working directory's parent.
7694
7673
7695 3. With the -c/--check option, the update is aborted and the
7674 3. With the -c/--check option, the update is aborted and the
7696 uncommitted changes are preserved.
7675 uncommitted changes are preserved.
7697
7676
7698 4. With the -C/--clean option, uncommitted changes are discarded and
7677 4. With the -C/--clean option, uncommitted changes are discarded and
7699 the working directory is updated to the requested changeset.
7678 the working directory is updated to the requested changeset.
7700
7679
7701 To cancel an uncommitted merge (and lose your changes), use
7680 To cancel an uncommitted merge (and lose your changes), use
7702 :hg:`merge --abort`.
7681 :hg:`merge --abort`.
7703
7682
7704 Use null as the changeset to remove the working directory (like
7683 Use null as the changeset to remove the working directory (like
7705 :hg:`clone -U`).
7684 :hg:`clone -U`).
7706
7685
7707 If you want to revert just one file to an older revision, use
7686 If you want to revert just one file to an older revision, use
7708 :hg:`revert [-r REV] NAME`.
7687 :hg:`revert [-r REV] NAME`.
7709
7688
7710 See :hg:`help dates` for a list of formats valid for -d/--date.
7689 See :hg:`help dates` for a list of formats valid for -d/--date.
7711
7690
7712 Returns 0 on success, 1 if there are unresolved files.
7691 Returns 0 on success, 1 if there are unresolved files.
7713 """
7692 """
7714 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7693 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7715 rev = opts.get('rev')
7694 rev = opts.get('rev')
7716 date = opts.get('date')
7695 date = opts.get('date')
7717 clean = opts.get('clean')
7696 clean = opts.get('clean')
7718 check = opts.get('check')
7697 check = opts.get('check')
7719 merge = opts.get('merge')
7698 merge = opts.get('merge')
7720 if rev and node:
7699 if rev and node:
7721 raise error.InputError(_(b"please specify just one revision"))
7700 raise error.InputError(_(b"please specify just one revision"))
7722
7701
7723 if ui.configbool(b'commands', b'update.requiredest'):
7702 if ui.configbool(b'commands', b'update.requiredest'):
7724 if not node and not rev and not date:
7703 if not node and not rev and not date:
7725 raise error.InputError(
7704 raise error.InputError(
7726 _(b'you must specify a destination'),
7705 _(b'you must specify a destination'),
7727 hint=_(b'for example: hg update ".::"'),
7706 hint=_(b'for example: hg update ".::"'),
7728 )
7707 )
7729
7708
7730 if rev is None or rev == b'':
7709 if rev is None or rev == b'':
7731 rev = node
7710 rev = node
7732
7711
7733 if date and rev is not None:
7712 if date and rev is not None:
7734 raise error.InputError(_(b"you can't specify a revision and a date"))
7713 raise error.InputError(_(b"you can't specify a revision and a date"))
7735
7714
7736 updatecheck = None
7715 updatecheck = None
7737 if check:
7716 if check:
7738 updatecheck = b'abort'
7717 updatecheck = b'abort'
7739 elif merge:
7718 elif merge:
7740 updatecheck = b'none'
7719 updatecheck = b'none'
7741
7720
7742 with repo.wlock():
7721 with repo.wlock():
7743 cmdutil.clearunfinished(repo)
7722 cmdutil.clearunfinished(repo)
7744 if date:
7723 if date:
7745 rev = cmdutil.finddate(ui, repo, date)
7724 rev = cmdutil.finddate(ui, repo, date)
7746
7725
7747 # if we defined a bookmark, we have to remember the original name
7726 # if we defined a bookmark, we have to remember the original name
7748 brev = rev
7727 brev = rev
7749 if rev:
7728 if rev:
7750 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7729 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7751 ctx = scmutil.revsingle(repo, rev, default=None)
7730 ctx = scmutil.revsingle(repo, rev, default=None)
7752 rev = ctx.rev()
7731 rev = ctx.rev()
7753 hidden = ctx.hidden()
7732 hidden = ctx.hidden()
7754 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7733 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7755 with ui.configoverride(overrides, b'update'):
7734 with ui.configoverride(overrides, b'update'):
7756 ret = hg.updatetotally(
7735 ret = hg.updatetotally(
7757 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7736 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7758 )
7737 )
7759 if hidden:
7738 if hidden:
7760 ctxstr = ctx.hex()[:12]
7739 ctxstr = ctx.hex()[:12]
7761 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7740 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7762
7741
7763 if ctx.obsolete():
7742 if ctx.obsolete():
7764 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7743 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7765 ui.warn(b"(%s)\n" % obsfatemsg)
7744 ui.warn(b"(%s)\n" % obsfatemsg)
7766 return ret
7745 return ret
7767
7746
7768
7747
7769 @command(
7748 @command(
7770 b'verify',
7749 b'verify',
7771 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7750 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7772 helpcategory=command.CATEGORY_MAINTENANCE,
7751 helpcategory=command.CATEGORY_MAINTENANCE,
7773 )
7752 )
7774 def verify(ui, repo, **opts):
7753 def verify(ui, repo, **opts):
7775 """verify the integrity of the repository
7754 """verify the integrity of the repository
7776
7755
7777 Verify the integrity of the current repository.
7756 Verify the integrity of the current repository.
7778
7757
7779 This will perform an extensive check of the repository's
7758 This will perform an extensive check of the repository's
7780 integrity, validating the hashes and checksums of each entry in
7759 integrity, validating the hashes and checksums of each entry in
7781 the changelog, manifest, and tracked files, as well as the
7760 the changelog, manifest, and tracked files, as well as the
7782 integrity of their crosslinks and indices.
7761 integrity of their crosslinks and indices.
7783
7762
7784 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7763 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7785 for more information about recovery from corruption of the
7764 for more information about recovery from corruption of the
7786 repository.
7765 repository.
7787
7766
7788 Returns 0 on success, 1 if errors are encountered.
7767 Returns 0 on success, 1 if errors are encountered.
7789 """
7768 """
7790 opts = pycompat.byteskwargs(opts)
7769 opts = pycompat.byteskwargs(opts)
7791
7770
7792 level = None
7771 level = None
7793 if opts[b'full']:
7772 if opts[b'full']:
7794 level = verifymod.VERIFY_FULL
7773 level = verifymod.VERIFY_FULL
7795 return hg.verify(repo, level)
7774 return hg.verify(repo, level)
7796
7775
7797
7776
7798 @command(
7777 @command(
7799 b'version',
7778 b'version',
7800 [] + formatteropts,
7779 [] + formatteropts,
7801 helpcategory=command.CATEGORY_HELP,
7780 helpcategory=command.CATEGORY_HELP,
7802 norepo=True,
7781 norepo=True,
7803 intents={INTENT_READONLY},
7782 intents={INTENT_READONLY},
7804 )
7783 )
7805 def version_(ui, **opts):
7784 def version_(ui, **opts):
7806 """output version and copyright information
7785 """output version and copyright information
7807
7786
7808 .. container:: verbose
7787 .. container:: verbose
7809
7788
7810 Template:
7789 Template:
7811
7790
7812 The following keywords are supported. See also :hg:`help templates`.
7791 The following keywords are supported. See also :hg:`help templates`.
7813
7792
7814 :extensions: List of extensions.
7793 :extensions: List of extensions.
7815 :ver: String. Version number.
7794 :ver: String. Version number.
7816
7795
7817 And each entry of ``{extensions}`` provides the following sub-keywords
7796 And each entry of ``{extensions}`` provides the following sub-keywords
7818 in addition to ``{ver}``.
7797 in addition to ``{ver}``.
7819
7798
7820 :bundled: Boolean. True if included in the release.
7799 :bundled: Boolean. True if included in the release.
7821 :name: String. Extension name.
7800 :name: String. Extension name.
7822 """
7801 """
7823 opts = pycompat.byteskwargs(opts)
7802 opts = pycompat.byteskwargs(opts)
7824 if ui.verbose:
7803 if ui.verbose:
7825 ui.pager(b'version')
7804 ui.pager(b'version')
7826 fm = ui.formatter(b"version", opts)
7805 fm = ui.formatter(b"version", opts)
7827 fm.startitem()
7806 fm.startitem()
7828 fm.write(
7807 fm.write(
7829 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7808 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7830 )
7809 )
7831 license = _(
7810 license = _(
7832 b"(see https://mercurial-scm.org for more information)\n"
7811 b"(see https://mercurial-scm.org for more information)\n"
7833 b"\nCopyright (C) 2005-2021 Matt Mackall and others\n"
7812 b"\nCopyright (C) 2005-2021 Matt Mackall and others\n"
7834 b"This is free software; see the source for copying conditions. "
7813 b"This is free software; see the source for copying conditions. "
7835 b"There is NO\nwarranty; "
7814 b"There is NO\nwarranty; "
7836 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7815 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7837 )
7816 )
7838 if not ui.quiet:
7817 if not ui.quiet:
7839 fm.plain(license)
7818 fm.plain(license)
7840
7819
7841 if ui.verbose:
7820 if ui.verbose:
7842 fm.plain(_(b"\nEnabled extensions:\n\n"))
7821 fm.plain(_(b"\nEnabled extensions:\n\n"))
7843 # format names and versions into columns
7822 # format names and versions into columns
7844 names = []
7823 names = []
7845 vers = []
7824 vers = []
7846 isinternals = []
7825 isinternals = []
7847 for name, module in sorted(extensions.extensions()):
7826 for name, module in sorted(extensions.extensions()):
7848 names.append(name)
7827 names.append(name)
7849 vers.append(extensions.moduleversion(module) or None)
7828 vers.append(extensions.moduleversion(module) or None)
7850 isinternals.append(extensions.ismoduleinternal(module))
7829 isinternals.append(extensions.ismoduleinternal(module))
7851 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7830 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7852 if names:
7831 if names:
7853 namefmt = b" %%-%ds " % max(len(n) for n in names)
7832 namefmt = b" %%-%ds " % max(len(n) for n in names)
7854 places = [_(b"external"), _(b"internal")]
7833 places = [_(b"external"), _(b"internal")]
7855 for n, v, p in zip(names, vers, isinternals):
7834 for n, v, p in zip(names, vers, isinternals):
7856 fn.startitem()
7835 fn.startitem()
7857 fn.condwrite(ui.verbose, b"name", namefmt, n)
7836 fn.condwrite(ui.verbose, b"name", namefmt, n)
7858 if ui.verbose:
7837 if ui.verbose:
7859 fn.plain(b"%s " % places[p])
7838 fn.plain(b"%s " % places[p])
7860 fn.data(bundled=p)
7839 fn.data(bundled=p)
7861 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7840 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7862 if ui.verbose:
7841 if ui.verbose:
7863 fn.plain(b"\n")
7842 fn.plain(b"\n")
7864 fn.end()
7843 fn.end()
7865 fm.end()
7844 fm.end()
7866
7845
7867
7846
7868 def loadcmdtable(ui, name, cmdtable):
7847 def loadcmdtable(ui, name, cmdtable):
7869 """Load command functions from specified cmdtable"""
7848 """Load command functions from specified cmdtable"""
7870 overrides = [cmd for cmd in cmdtable if cmd in table]
7849 overrides = [cmd for cmd in cmdtable if cmd in table]
7871 if overrides:
7850 if overrides:
7872 ui.warn(
7851 ui.warn(
7873 _(b"extension '%s' overrides commands: %s\n")
7852 _(b"extension '%s' overrides commands: %s\n")
7874 % (name, b" ".join(overrides))
7853 % (name, b" ".join(overrides))
7875 )
7854 )
7876 table.update(cmdtable)
7855 table.update(cmdtable)
@@ -1,1219 +1,1250 b''
1 # logcmdutil.py - utility for log-like commands
1 # logcmdutil.py - utility for log-like commands
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import itertools
10 import itertools
11 import os
11 import os
12 import posixpath
12 import posixpath
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 nullid,
16 nullid,
17 wdirid,
17 wdirid,
18 wdirrev,
18 wdirrev,
19 )
19 )
20
20
21 from .thirdparty import attr
21 from .thirdparty import attr
22
22
23 from . import (
23 from . import (
24 dagop,
24 dagop,
25 error,
25 error,
26 formatter,
26 formatter,
27 graphmod,
27 graphmod,
28 match as matchmod,
28 match as matchmod,
29 mdiff,
29 mdiff,
30 merge,
30 patch,
31 patch,
31 pathutil,
32 pathutil,
32 pycompat,
33 pycompat,
33 revset,
34 revset,
34 revsetlang,
35 revsetlang,
35 scmutil,
36 scmutil,
36 smartset,
37 smartset,
37 templatekw,
38 templatekw,
38 templater,
39 templater,
39 util,
40 util,
40 )
41 )
41 from .utils import (
42 from .utils import (
42 dateutil,
43 dateutil,
43 stringutil,
44 stringutil,
44 )
45 )
45
46
46
47
47 if pycompat.TYPE_CHECKING:
48 if pycompat.TYPE_CHECKING:
48 from typing import (
49 from typing import (
49 Any,
50 Any,
50 Callable,
51 Callable,
51 Dict,
52 Dict,
52 List,
53 List,
53 Optional,
54 Optional,
54 Tuple,
55 Tuple,
55 )
56 )
56
57
57 for t in (Any, Callable, Dict, List, Optional, Tuple):
58 for t in (Any, Callable, Dict, List, Optional, Tuple):
58 assert t
59 assert t
59
60
60
61
61 def getlimit(opts):
62 def getlimit(opts):
62 """get the log limit according to option -l/--limit"""
63 """get the log limit according to option -l/--limit"""
63 limit = opts.get(b'limit')
64 limit = opts.get(b'limit')
64 if limit:
65 if limit:
65 try:
66 try:
66 limit = int(limit)
67 limit = int(limit)
67 except ValueError:
68 except ValueError:
68 raise error.Abort(_(b'limit must be a positive integer'))
69 raise error.Abort(_(b'limit must be a positive integer'))
69 if limit <= 0:
70 if limit <= 0:
70 raise error.Abort(_(b'limit must be positive'))
71 raise error.Abort(_(b'limit must be positive'))
71 else:
72 else:
72 limit = None
73 limit = None
73 return limit
74 return limit
74
75
75
76
77 def diff_parent(ctx):
78 """get the context object to use as parent when diffing
79
80
81 If diff.merge is enabled, an overlayworkingctx of the auto-merged parents will be returned.
82 """
83 repo = ctx.repo()
84 if repo.ui.configbool(b"diff", b"merge") and ctx.p2().node() != nullid:
85 # avoid cycle context -> subrepo -> cmdutil -> logcmdutil
86 from . import context
87
88 wctx = context.overlayworkingctx(repo)
89 wctx.setbase(ctx.p1())
90 with repo.ui.configoverride(
91 {
92 (
93 b"ui",
94 b"forcemerge",
95 ): b"internal:merge3-lie-about-conflicts",
96 },
97 b"merge-diff",
98 ):
99 repo.ui.pushbuffer()
100 merge.merge(ctx.p2(), wc=wctx)
101 repo.ui.popbuffer()
102 return wctx
103 else:
104 return ctx.p1()
105
106
76 def diffordiffstat(
107 def diffordiffstat(
77 ui,
108 ui,
78 repo,
109 repo,
79 diffopts,
110 diffopts,
80 ctx1,
111 ctx1,
81 ctx2,
112 ctx2,
82 match,
113 match,
83 changes=None,
114 changes=None,
84 stat=False,
115 stat=False,
85 fp=None,
116 fp=None,
86 graphwidth=0,
117 graphwidth=0,
87 prefix=b'',
118 prefix=b'',
88 root=b'',
119 root=b'',
89 listsubrepos=False,
120 listsubrepos=False,
90 hunksfilterfn=None,
121 hunksfilterfn=None,
91 ):
122 ):
92 '''show diff or diffstat.'''
123 '''show diff or diffstat.'''
93 if root:
124 if root:
94 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
125 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
95 else:
126 else:
96 relroot = b''
127 relroot = b''
97 copysourcematch = None
128 copysourcematch = None
98
129
99 def compose(f, g):
130 def compose(f, g):
100 return lambda x: f(g(x))
131 return lambda x: f(g(x))
101
132
102 def pathfn(f):
133 def pathfn(f):
103 return posixpath.join(prefix, f)
134 return posixpath.join(prefix, f)
104
135
105 if relroot != b'':
136 if relroot != b'':
106 # XXX relative roots currently don't work if the root is within a
137 # XXX relative roots currently don't work if the root is within a
107 # subrepo
138 # subrepo
108 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
139 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
109 uirelroot = uipathfn(pathfn(relroot))
140 uirelroot = uipathfn(pathfn(relroot))
110 relroot += b'/'
141 relroot += b'/'
111 for matchroot in match.files():
142 for matchroot in match.files():
112 if not matchroot.startswith(relroot):
143 if not matchroot.startswith(relroot):
113 ui.warn(
144 ui.warn(
114 _(b'warning: %s not inside relative root %s\n')
145 _(b'warning: %s not inside relative root %s\n')
115 % (uipathfn(pathfn(matchroot)), uirelroot)
146 % (uipathfn(pathfn(matchroot)), uirelroot)
116 )
147 )
117
148
118 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
149 relrootmatch = scmutil.match(ctx2, pats=[relroot], default=b'path')
119 match = matchmod.intersectmatchers(match, relrootmatch)
150 match = matchmod.intersectmatchers(match, relrootmatch)
120 copysourcematch = relrootmatch
151 copysourcematch = relrootmatch
121
152
122 checkroot = repo.ui.configbool(
153 checkroot = repo.ui.configbool(
123 b'devel', b'all-warnings'
154 b'devel', b'all-warnings'
124 ) or repo.ui.configbool(b'devel', b'check-relroot')
155 ) or repo.ui.configbool(b'devel', b'check-relroot')
125
156
126 def relrootpathfn(f):
157 def relrootpathfn(f):
127 if checkroot and not f.startswith(relroot):
158 if checkroot and not f.startswith(relroot):
128 raise AssertionError(
159 raise AssertionError(
129 b"file %s doesn't start with relroot %s" % (f, relroot)
160 b"file %s doesn't start with relroot %s" % (f, relroot)
130 )
161 )
131 return f[len(relroot) :]
162 return f[len(relroot) :]
132
163
133 pathfn = compose(relrootpathfn, pathfn)
164 pathfn = compose(relrootpathfn, pathfn)
134
165
135 if stat:
166 if stat:
136 diffopts = diffopts.copy(context=0, noprefix=False)
167 diffopts = diffopts.copy(context=0, noprefix=False)
137 width = 80
168 width = 80
138 if not ui.plain():
169 if not ui.plain():
139 width = ui.termwidth() - graphwidth
170 width = ui.termwidth() - graphwidth
140 # If an explicit --root was given, don't respect ui.relative-paths
171 # If an explicit --root was given, don't respect ui.relative-paths
141 if not relroot:
172 if not relroot:
142 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
173 pathfn = compose(scmutil.getuipathfn(repo), pathfn)
143
174
144 chunks = ctx2.diff(
175 chunks = ctx2.diff(
145 ctx1,
176 ctx1,
146 match,
177 match,
147 changes,
178 changes,
148 opts=diffopts,
179 opts=diffopts,
149 pathfn=pathfn,
180 pathfn=pathfn,
150 copysourcematch=copysourcematch,
181 copysourcematch=copysourcematch,
151 hunksfilterfn=hunksfilterfn,
182 hunksfilterfn=hunksfilterfn,
152 )
183 )
153
184
154 if fp is not None or ui.canwritewithoutlabels():
185 if fp is not None or ui.canwritewithoutlabels():
155 out = fp or ui
186 out = fp or ui
156 if stat:
187 if stat:
157 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
188 chunks = [patch.diffstat(util.iterlines(chunks), width=width)]
158 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
189 for chunk in util.filechunkiter(util.chunkbuffer(chunks)):
159 out.write(chunk)
190 out.write(chunk)
160 else:
191 else:
161 if stat:
192 if stat:
162 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
193 chunks = patch.diffstatui(util.iterlines(chunks), width=width)
163 else:
194 else:
164 chunks = patch.difflabel(
195 chunks = patch.difflabel(
165 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
196 lambda chunks, **kwargs: chunks, chunks, opts=diffopts
166 )
197 )
167 if ui.canbatchlabeledwrites():
198 if ui.canbatchlabeledwrites():
168
199
169 def gen():
200 def gen():
170 for chunk, label in chunks:
201 for chunk, label in chunks:
171 yield ui.label(chunk, label=label)
202 yield ui.label(chunk, label=label)
172
203
173 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
204 for chunk in util.filechunkiter(util.chunkbuffer(gen())):
174 ui.write(chunk)
205 ui.write(chunk)
175 else:
206 else:
176 for chunk, label in chunks:
207 for chunk, label in chunks:
177 ui.write(chunk, label=label)
208 ui.write(chunk, label=label)
178
209
179 node2 = ctx2.node()
210 node2 = ctx2.node()
180 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
211 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
181 tempnode2 = node2
212 tempnode2 = node2
182 try:
213 try:
183 if node2 is not None:
214 if node2 is not None:
184 tempnode2 = ctx2.substate[subpath][1]
215 tempnode2 = ctx2.substate[subpath][1]
185 except KeyError:
216 except KeyError:
186 # A subrepo that existed in node1 was deleted between node1 and
217 # A subrepo that existed in node1 was deleted between node1 and
187 # node2 (inclusive). Thus, ctx2's substate won't contain that
218 # node2 (inclusive). Thus, ctx2's substate won't contain that
188 # subpath. The best we can do is to ignore it.
219 # subpath. The best we can do is to ignore it.
189 tempnode2 = None
220 tempnode2 = None
190 submatch = matchmod.subdirmatcher(subpath, match)
221 submatch = matchmod.subdirmatcher(subpath, match)
191 subprefix = repo.wvfs.reljoin(prefix, subpath)
222 subprefix = repo.wvfs.reljoin(prefix, subpath)
192 if listsubrepos or match.exact(subpath) or any(submatch.files()):
223 if listsubrepos or match.exact(subpath) or any(submatch.files()):
193 sub.diff(
224 sub.diff(
194 ui,
225 ui,
195 diffopts,
226 diffopts,
196 tempnode2,
227 tempnode2,
197 submatch,
228 submatch,
198 changes=changes,
229 changes=changes,
199 stat=stat,
230 stat=stat,
200 fp=fp,
231 fp=fp,
201 prefix=subprefix,
232 prefix=subprefix,
202 )
233 )
203
234
204
235
205 class changesetdiffer(object):
236 class changesetdiffer(object):
206 """Generate diff of changeset with pre-configured filtering functions"""
237 """Generate diff of changeset with pre-configured filtering functions"""
207
238
208 def _makefilematcher(self, ctx):
239 def _makefilematcher(self, ctx):
209 return scmutil.matchall(ctx.repo())
240 return scmutil.matchall(ctx.repo())
210
241
211 def _makehunksfilter(self, ctx):
242 def _makehunksfilter(self, ctx):
212 return None
243 return None
213
244
214 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
245 def showdiff(self, ui, ctx, diffopts, graphwidth=0, stat=False):
215 diffordiffstat(
246 diffordiffstat(
216 ui,
247 ui,
217 ctx.repo(),
248 ctx.repo(),
218 diffopts,
249 diffopts,
219 ctx.p1(),
250 ctx.p1(),
220 ctx,
251 ctx,
221 match=self._makefilematcher(ctx),
252 match=self._makefilematcher(ctx),
222 stat=stat,
253 stat=stat,
223 graphwidth=graphwidth,
254 graphwidth=graphwidth,
224 hunksfilterfn=self._makehunksfilter(ctx),
255 hunksfilterfn=self._makehunksfilter(ctx),
225 )
256 )
226
257
227
258
228 def changesetlabels(ctx):
259 def changesetlabels(ctx):
229 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
260 labels = [b'log.changeset', b'changeset.%s' % ctx.phasestr()]
230 if ctx.obsolete():
261 if ctx.obsolete():
231 labels.append(b'changeset.obsolete')
262 labels.append(b'changeset.obsolete')
232 if ctx.isunstable():
263 if ctx.isunstable():
233 labels.append(b'changeset.unstable')
264 labels.append(b'changeset.unstable')
234 for instability in ctx.instabilities():
265 for instability in ctx.instabilities():
235 labels.append(b'instability.%s' % instability)
266 labels.append(b'instability.%s' % instability)
236 return b' '.join(labels)
267 return b' '.join(labels)
237
268
238
269
239 class changesetprinter(object):
270 class changesetprinter(object):
240 '''show changeset information when templating not requested.'''
271 '''show changeset information when templating not requested.'''
241
272
242 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
273 def __init__(self, ui, repo, differ=None, diffopts=None, buffered=False):
243 self.ui = ui
274 self.ui = ui
244 self.repo = repo
275 self.repo = repo
245 self.buffered = buffered
276 self.buffered = buffered
246 self._differ = differ or changesetdiffer()
277 self._differ = differ or changesetdiffer()
247 self._diffopts = patch.diffallopts(ui, diffopts)
278 self._diffopts = patch.diffallopts(ui, diffopts)
248 self._includestat = diffopts and diffopts.get(b'stat')
279 self._includestat = diffopts and diffopts.get(b'stat')
249 self._includediff = diffopts and diffopts.get(b'patch')
280 self._includediff = diffopts and diffopts.get(b'patch')
250 self.header = {}
281 self.header = {}
251 self.hunk = {}
282 self.hunk = {}
252 self.lastheader = None
283 self.lastheader = None
253 self.footer = None
284 self.footer = None
254 self._columns = templatekw.getlogcolumns()
285 self._columns = templatekw.getlogcolumns()
255
286
256 def flush(self, ctx):
287 def flush(self, ctx):
257 rev = ctx.rev()
288 rev = ctx.rev()
258 if rev in self.header:
289 if rev in self.header:
259 h = self.header[rev]
290 h = self.header[rev]
260 if h != self.lastheader:
291 if h != self.lastheader:
261 self.lastheader = h
292 self.lastheader = h
262 self.ui.write(h)
293 self.ui.write(h)
263 del self.header[rev]
294 del self.header[rev]
264 if rev in self.hunk:
295 if rev in self.hunk:
265 self.ui.write(self.hunk[rev])
296 self.ui.write(self.hunk[rev])
266 del self.hunk[rev]
297 del self.hunk[rev]
267
298
268 def close(self):
299 def close(self):
269 if self.footer:
300 if self.footer:
270 self.ui.write(self.footer)
301 self.ui.write(self.footer)
271
302
272 def show(self, ctx, copies=None, **props):
303 def show(self, ctx, copies=None, **props):
273 props = pycompat.byteskwargs(props)
304 props = pycompat.byteskwargs(props)
274 if self.buffered:
305 if self.buffered:
275 self.ui.pushbuffer(labeled=True)
306 self.ui.pushbuffer(labeled=True)
276 self._show(ctx, copies, props)
307 self._show(ctx, copies, props)
277 self.hunk[ctx.rev()] = self.ui.popbuffer()
308 self.hunk[ctx.rev()] = self.ui.popbuffer()
278 else:
309 else:
279 self._show(ctx, copies, props)
310 self._show(ctx, copies, props)
280
311
281 def _show(self, ctx, copies, props):
312 def _show(self, ctx, copies, props):
282 '''show a single changeset or file revision'''
313 '''show a single changeset or file revision'''
283 changenode = ctx.node()
314 changenode = ctx.node()
284 graphwidth = props.get(b'graphwidth', 0)
315 graphwidth = props.get(b'graphwidth', 0)
285
316
286 if self.ui.quiet:
317 if self.ui.quiet:
287 self.ui.write(
318 self.ui.write(
288 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
319 b"%s\n" % scmutil.formatchangeid(ctx), label=b'log.node'
289 )
320 )
290 return
321 return
291
322
292 columns = self._columns
323 columns = self._columns
293 self.ui.write(
324 self.ui.write(
294 columns[b'changeset'] % scmutil.formatchangeid(ctx),
325 columns[b'changeset'] % scmutil.formatchangeid(ctx),
295 label=changesetlabels(ctx),
326 label=changesetlabels(ctx),
296 )
327 )
297
328
298 # branches are shown first before any other names due to backwards
329 # branches are shown first before any other names due to backwards
299 # compatibility
330 # compatibility
300 branch = ctx.branch()
331 branch = ctx.branch()
301 # don't show the default branch name
332 # don't show the default branch name
302 if branch != b'default':
333 if branch != b'default':
303 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
334 self.ui.write(columns[b'branch'] % branch, label=b'log.branch')
304
335
305 for nsname, ns in pycompat.iteritems(self.repo.names):
336 for nsname, ns in pycompat.iteritems(self.repo.names):
306 # branches has special logic already handled above, so here we just
337 # branches has special logic already handled above, so here we just
307 # skip it
338 # skip it
308 if nsname == b'branches':
339 if nsname == b'branches':
309 continue
340 continue
310 # we will use the templatename as the color name since those two
341 # we will use the templatename as the color name since those two
311 # should be the same
342 # should be the same
312 for name in ns.names(self.repo, changenode):
343 for name in ns.names(self.repo, changenode):
313 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
344 self.ui.write(ns.logfmt % name, label=b'log.%s' % ns.colorname)
314 if self.ui.debugflag:
345 if self.ui.debugflag:
315 self.ui.write(
346 self.ui.write(
316 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
347 columns[b'phase'] % ctx.phasestr(), label=b'log.phase'
317 )
348 )
318 for pctx in scmutil.meaningfulparents(self.repo, ctx):
349 for pctx in scmutil.meaningfulparents(self.repo, ctx):
319 label = b'log.parent changeset.%s' % pctx.phasestr()
350 label = b'log.parent changeset.%s' % pctx.phasestr()
320 self.ui.write(
351 self.ui.write(
321 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
352 columns[b'parent'] % scmutil.formatchangeid(pctx), label=label
322 )
353 )
323
354
324 if self.ui.debugflag:
355 if self.ui.debugflag:
325 mnode = ctx.manifestnode()
356 mnode = ctx.manifestnode()
326 if mnode is None:
357 if mnode is None:
327 mnode = wdirid
358 mnode = wdirid
328 mrev = wdirrev
359 mrev = wdirrev
329 else:
360 else:
330 mrev = self.repo.manifestlog.rev(mnode)
361 mrev = self.repo.manifestlog.rev(mnode)
331 self.ui.write(
362 self.ui.write(
332 columns[b'manifest']
363 columns[b'manifest']
333 % scmutil.formatrevnode(self.ui, mrev, mnode),
364 % scmutil.formatrevnode(self.ui, mrev, mnode),
334 label=b'ui.debug log.manifest',
365 label=b'ui.debug log.manifest',
335 )
366 )
336 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
367 self.ui.write(columns[b'user'] % ctx.user(), label=b'log.user')
337 self.ui.write(
368 self.ui.write(
338 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
369 columns[b'date'] % dateutil.datestr(ctx.date()), label=b'log.date'
339 )
370 )
340
371
341 if ctx.isunstable():
372 if ctx.isunstable():
342 instabilities = ctx.instabilities()
373 instabilities = ctx.instabilities()
343 self.ui.write(
374 self.ui.write(
344 columns[b'instability'] % b', '.join(instabilities),
375 columns[b'instability'] % b', '.join(instabilities),
345 label=b'log.instability',
376 label=b'log.instability',
346 )
377 )
347
378
348 elif ctx.obsolete():
379 elif ctx.obsolete():
349 self._showobsfate(ctx)
380 self._showobsfate(ctx)
350
381
351 self._exthook(ctx)
382 self._exthook(ctx)
352
383
353 if self.ui.debugflag:
384 if self.ui.debugflag:
354 files = ctx.p1().status(ctx)
385 files = ctx.p1().status(ctx)
355 for key, value in zip(
386 for key, value in zip(
356 [b'files', b'files+', b'files-'],
387 [b'files', b'files+', b'files-'],
357 [files.modified, files.added, files.removed],
388 [files.modified, files.added, files.removed],
358 ):
389 ):
359 if value:
390 if value:
360 self.ui.write(
391 self.ui.write(
361 columns[key] % b" ".join(value),
392 columns[key] % b" ".join(value),
362 label=b'ui.debug log.files',
393 label=b'ui.debug log.files',
363 )
394 )
364 elif ctx.files() and self.ui.verbose:
395 elif ctx.files() and self.ui.verbose:
365 self.ui.write(
396 self.ui.write(
366 columns[b'files'] % b" ".join(ctx.files()),
397 columns[b'files'] % b" ".join(ctx.files()),
367 label=b'ui.note log.files',
398 label=b'ui.note log.files',
368 )
399 )
369 if copies and self.ui.verbose:
400 if copies and self.ui.verbose:
370 copies = [b'%s (%s)' % c for c in copies]
401 copies = [b'%s (%s)' % c for c in copies]
371 self.ui.write(
402 self.ui.write(
372 columns[b'copies'] % b' '.join(copies),
403 columns[b'copies'] % b' '.join(copies),
373 label=b'ui.note log.copies',
404 label=b'ui.note log.copies',
374 )
405 )
375
406
376 extra = ctx.extra()
407 extra = ctx.extra()
377 if extra and self.ui.debugflag:
408 if extra and self.ui.debugflag:
378 for key, value in sorted(extra.items()):
409 for key, value in sorted(extra.items()):
379 self.ui.write(
410 self.ui.write(
380 columns[b'extra'] % (key, stringutil.escapestr(value)),
411 columns[b'extra'] % (key, stringutil.escapestr(value)),
381 label=b'ui.debug log.extra',
412 label=b'ui.debug log.extra',
382 )
413 )
383
414
384 description = ctx.description().strip()
415 description = ctx.description().strip()
385 if description:
416 if description:
386 if self.ui.verbose:
417 if self.ui.verbose:
387 self.ui.write(
418 self.ui.write(
388 _(b"description:\n"), label=b'ui.note log.description'
419 _(b"description:\n"), label=b'ui.note log.description'
389 )
420 )
390 self.ui.write(description, label=b'ui.note log.description')
421 self.ui.write(description, label=b'ui.note log.description')
391 self.ui.write(b"\n\n")
422 self.ui.write(b"\n\n")
392 else:
423 else:
393 self.ui.write(
424 self.ui.write(
394 columns[b'summary'] % description.splitlines()[0],
425 columns[b'summary'] % description.splitlines()[0],
395 label=b'log.summary',
426 label=b'log.summary',
396 )
427 )
397 self.ui.write(b"\n")
428 self.ui.write(b"\n")
398
429
399 self._showpatch(ctx, graphwidth)
430 self._showpatch(ctx, graphwidth)
400
431
401 def _showobsfate(self, ctx):
432 def _showobsfate(self, ctx):
402 # TODO: do not depend on templater
433 # TODO: do not depend on templater
403 tres = formatter.templateresources(self.repo.ui, self.repo)
434 tres = formatter.templateresources(self.repo.ui, self.repo)
404 t = formatter.maketemplater(
435 t = formatter.maketemplater(
405 self.repo.ui,
436 self.repo.ui,
406 b'{join(obsfate, "\n")}',
437 b'{join(obsfate, "\n")}',
407 defaults=templatekw.keywords,
438 defaults=templatekw.keywords,
408 resources=tres,
439 resources=tres,
409 )
440 )
410 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
441 obsfate = t.renderdefault({b'ctx': ctx}).splitlines()
411
442
412 if obsfate:
443 if obsfate:
413 for obsfateline in obsfate:
444 for obsfateline in obsfate:
414 self.ui.write(
445 self.ui.write(
415 self._columns[b'obsolete'] % obsfateline,
446 self._columns[b'obsolete'] % obsfateline,
416 label=b'log.obsfate',
447 label=b'log.obsfate',
417 )
448 )
418
449
419 def _exthook(self, ctx):
450 def _exthook(self, ctx):
420 """empty method used by extension as a hook point"""
451 """empty method used by extension as a hook point"""
421
452
422 def _showpatch(self, ctx, graphwidth=0):
453 def _showpatch(self, ctx, graphwidth=0):
423 if self._includestat:
454 if self._includestat:
424 self._differ.showdiff(
455 self._differ.showdiff(
425 self.ui, ctx, self._diffopts, graphwidth, stat=True
456 self.ui, ctx, self._diffopts, graphwidth, stat=True
426 )
457 )
427 if self._includestat and self._includediff:
458 if self._includestat and self._includediff:
428 self.ui.write(b"\n")
459 self.ui.write(b"\n")
429 if self._includediff:
460 if self._includediff:
430 self._differ.showdiff(
461 self._differ.showdiff(
431 self.ui, ctx, self._diffopts, graphwidth, stat=False
462 self.ui, ctx, self._diffopts, graphwidth, stat=False
432 )
463 )
433 if self._includestat or self._includediff:
464 if self._includestat or self._includediff:
434 self.ui.write(b"\n")
465 self.ui.write(b"\n")
435
466
436
467
437 class changesetformatter(changesetprinter):
468 class changesetformatter(changesetprinter):
438 """Format changeset information by generic formatter"""
469 """Format changeset information by generic formatter"""
439
470
440 def __init__(
471 def __init__(
441 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
472 self, ui, repo, fm, differ=None, diffopts=None, buffered=False
442 ):
473 ):
443 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
474 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
444 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
475 self._diffopts = patch.difffeatureopts(ui, diffopts, git=True)
445 self._fm = fm
476 self._fm = fm
446
477
447 def close(self):
478 def close(self):
448 self._fm.end()
479 self._fm.end()
449
480
450 def _show(self, ctx, copies, props):
481 def _show(self, ctx, copies, props):
451 '''show a single changeset or file revision'''
482 '''show a single changeset or file revision'''
452 fm = self._fm
483 fm = self._fm
453 fm.startitem()
484 fm.startitem()
454 fm.context(ctx=ctx)
485 fm.context(ctx=ctx)
455 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
486 fm.data(rev=scmutil.intrev(ctx), node=fm.hexfunc(scmutil.binnode(ctx)))
456
487
457 datahint = fm.datahint()
488 datahint = fm.datahint()
458 if self.ui.quiet and not datahint:
489 if self.ui.quiet and not datahint:
459 return
490 return
460
491
461 fm.data(
492 fm.data(
462 branch=ctx.branch(),
493 branch=ctx.branch(),
463 phase=ctx.phasestr(),
494 phase=ctx.phasestr(),
464 user=ctx.user(),
495 user=ctx.user(),
465 date=fm.formatdate(ctx.date()),
496 date=fm.formatdate(ctx.date()),
466 desc=ctx.description(),
497 desc=ctx.description(),
467 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
498 bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'),
468 tags=fm.formatlist(ctx.tags(), name=b'tag'),
499 tags=fm.formatlist(ctx.tags(), name=b'tag'),
469 parents=fm.formatlist(
500 parents=fm.formatlist(
470 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
501 [fm.hexfunc(c.node()) for c in ctx.parents()], name=b'node'
471 ),
502 ),
472 )
503 )
473
504
474 if self.ui.debugflag or b'manifest' in datahint:
505 if self.ui.debugflag or b'manifest' in datahint:
475 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid))
506 fm.data(manifest=fm.hexfunc(ctx.manifestnode() or wdirid))
476 if self.ui.debugflag or b'extra' in datahint:
507 if self.ui.debugflag or b'extra' in datahint:
477 fm.data(extra=fm.formatdict(ctx.extra()))
508 fm.data(extra=fm.formatdict(ctx.extra()))
478
509
479 if (
510 if (
480 self.ui.debugflag
511 self.ui.debugflag
481 or b'modified' in datahint
512 or b'modified' in datahint
482 or b'added' in datahint
513 or b'added' in datahint
483 or b'removed' in datahint
514 or b'removed' in datahint
484 ):
515 ):
485 files = ctx.p1().status(ctx)
516 files = ctx.p1().status(ctx)
486 fm.data(
517 fm.data(
487 modified=fm.formatlist(files.modified, name=b'file'),
518 modified=fm.formatlist(files.modified, name=b'file'),
488 added=fm.formatlist(files.added, name=b'file'),
519 added=fm.formatlist(files.added, name=b'file'),
489 removed=fm.formatlist(files.removed, name=b'file'),
520 removed=fm.formatlist(files.removed, name=b'file'),
490 )
521 )
491
522
492 verbose = not self.ui.debugflag and self.ui.verbose
523 verbose = not self.ui.debugflag and self.ui.verbose
493 if verbose or b'files' in datahint:
524 if verbose or b'files' in datahint:
494 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
525 fm.data(files=fm.formatlist(ctx.files(), name=b'file'))
495 if verbose and copies or b'copies' in datahint:
526 if verbose and copies or b'copies' in datahint:
496 fm.data(
527 fm.data(
497 copies=fm.formatdict(copies or {}, key=b'name', value=b'source')
528 copies=fm.formatdict(copies or {}, key=b'name', value=b'source')
498 )
529 )
499
530
500 if self._includestat or b'diffstat' in datahint:
531 if self._includestat or b'diffstat' in datahint:
501 self.ui.pushbuffer()
532 self.ui.pushbuffer()
502 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
533 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=True)
503 fm.data(diffstat=self.ui.popbuffer())
534 fm.data(diffstat=self.ui.popbuffer())
504 if self._includediff or b'diff' in datahint:
535 if self._includediff or b'diff' in datahint:
505 self.ui.pushbuffer()
536 self.ui.pushbuffer()
506 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
537 self._differ.showdiff(self.ui, ctx, self._diffopts, stat=False)
507 fm.data(diff=self.ui.popbuffer())
538 fm.data(diff=self.ui.popbuffer())
508
539
509
540
510 class changesettemplater(changesetprinter):
541 class changesettemplater(changesetprinter):
511 """format changeset information.
542 """format changeset information.
512
543
513 Note: there are a variety of convenience functions to build a
544 Note: there are a variety of convenience functions to build a
514 changesettemplater for common cases. See functions such as:
545 changesettemplater for common cases. See functions such as:
515 maketemplater, changesetdisplayer, buildcommittemplate, or other
546 maketemplater, changesetdisplayer, buildcommittemplate, or other
516 functions that use changesest_templater.
547 functions that use changesest_templater.
517 """
548 """
518
549
519 # Arguments before "buffered" used to be positional. Consider not
550 # Arguments before "buffered" used to be positional. Consider not
520 # adding/removing arguments before "buffered" to not break callers.
551 # adding/removing arguments before "buffered" to not break callers.
521 def __init__(
552 def __init__(
522 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
553 self, ui, repo, tmplspec, differ=None, diffopts=None, buffered=False
523 ):
554 ):
524 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
555 changesetprinter.__init__(self, ui, repo, differ, diffopts, buffered)
525 # tres is shared with _graphnodeformatter()
556 # tres is shared with _graphnodeformatter()
526 self._tresources = tres = formatter.templateresources(ui, repo)
557 self._tresources = tres = formatter.templateresources(ui, repo)
527 self.t = formatter.loadtemplater(
558 self.t = formatter.loadtemplater(
528 ui,
559 ui,
529 tmplspec,
560 tmplspec,
530 defaults=templatekw.keywords,
561 defaults=templatekw.keywords,
531 resources=tres,
562 resources=tres,
532 cache=templatekw.defaulttempl,
563 cache=templatekw.defaulttempl,
533 )
564 )
534 self._counter = itertools.count()
565 self._counter = itertools.count()
535
566
536 self._tref = tmplspec.ref
567 self._tref = tmplspec.ref
537 self._parts = {
568 self._parts = {
538 b'header': b'',
569 b'header': b'',
539 b'footer': b'',
570 b'footer': b'',
540 tmplspec.ref: tmplspec.ref,
571 tmplspec.ref: tmplspec.ref,
541 b'docheader': b'',
572 b'docheader': b'',
542 b'docfooter': b'',
573 b'docfooter': b'',
543 b'separator': b'',
574 b'separator': b'',
544 }
575 }
545 if tmplspec.mapfile:
576 if tmplspec.mapfile:
546 # find correct templates for current mode, for backward
577 # find correct templates for current mode, for backward
547 # compatibility with 'log -v/-q/--debug' using a mapfile
578 # compatibility with 'log -v/-q/--debug' using a mapfile
548 tmplmodes = [
579 tmplmodes = [
549 (True, b''),
580 (True, b''),
550 (self.ui.verbose, b'_verbose'),
581 (self.ui.verbose, b'_verbose'),
551 (self.ui.quiet, b'_quiet'),
582 (self.ui.quiet, b'_quiet'),
552 (self.ui.debugflag, b'_debug'),
583 (self.ui.debugflag, b'_debug'),
553 ]
584 ]
554 for mode, postfix in tmplmodes:
585 for mode, postfix in tmplmodes:
555 for t in self._parts:
586 for t in self._parts:
556 cur = t + postfix
587 cur = t + postfix
557 if mode and cur in self.t:
588 if mode and cur in self.t:
558 self._parts[t] = cur
589 self._parts[t] = cur
559 else:
590 else:
560 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
591 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
561 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
592 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
562 self._parts.update(m)
593 self._parts.update(m)
563
594
564 if self._parts[b'docheader']:
595 if self._parts[b'docheader']:
565 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
596 self.ui.write(self.t.render(self._parts[b'docheader'], {}))
566
597
567 def close(self):
598 def close(self):
568 if self._parts[b'docfooter']:
599 if self._parts[b'docfooter']:
569 if not self.footer:
600 if not self.footer:
570 self.footer = b""
601 self.footer = b""
571 self.footer += self.t.render(self._parts[b'docfooter'], {})
602 self.footer += self.t.render(self._parts[b'docfooter'], {})
572 return super(changesettemplater, self).close()
603 return super(changesettemplater, self).close()
573
604
574 def _show(self, ctx, copies, props):
605 def _show(self, ctx, copies, props):
575 '''show a single changeset or file revision'''
606 '''show a single changeset or file revision'''
576 props = props.copy()
607 props = props.copy()
577 props[b'ctx'] = ctx
608 props[b'ctx'] = ctx
578 props[b'index'] = index = next(self._counter)
609 props[b'index'] = index = next(self._counter)
579 props[b'revcache'] = {b'copies': copies}
610 props[b'revcache'] = {b'copies': copies}
580 graphwidth = props.get(b'graphwidth', 0)
611 graphwidth = props.get(b'graphwidth', 0)
581
612
582 # write separator, which wouldn't work well with the header part below
613 # write separator, which wouldn't work well with the header part below
583 # since there's inherently a conflict between header (across items) and
614 # since there's inherently a conflict between header (across items) and
584 # separator (per item)
615 # separator (per item)
585 if self._parts[b'separator'] and index > 0:
616 if self._parts[b'separator'] and index > 0:
586 self.ui.write(self.t.render(self._parts[b'separator'], {}))
617 self.ui.write(self.t.render(self._parts[b'separator'], {}))
587
618
588 # write header
619 # write header
589 if self._parts[b'header']:
620 if self._parts[b'header']:
590 h = self.t.render(self._parts[b'header'], props)
621 h = self.t.render(self._parts[b'header'], props)
591 if self.buffered:
622 if self.buffered:
592 self.header[ctx.rev()] = h
623 self.header[ctx.rev()] = h
593 else:
624 else:
594 if self.lastheader != h:
625 if self.lastheader != h:
595 self.lastheader = h
626 self.lastheader = h
596 self.ui.write(h)
627 self.ui.write(h)
597
628
598 # write changeset metadata, then patch if requested
629 # write changeset metadata, then patch if requested
599 key = self._parts[self._tref]
630 key = self._parts[self._tref]
600 self.ui.write(self.t.render(key, props))
631 self.ui.write(self.t.render(key, props))
601 self._exthook(ctx)
632 self._exthook(ctx)
602 self._showpatch(ctx, graphwidth)
633 self._showpatch(ctx, graphwidth)
603
634
604 if self._parts[b'footer']:
635 if self._parts[b'footer']:
605 if not self.footer:
636 if not self.footer:
606 self.footer = self.t.render(self._parts[b'footer'], props)
637 self.footer = self.t.render(self._parts[b'footer'], props)
607
638
608
639
609 def templatespec(tmpl, mapfile):
640 def templatespec(tmpl, mapfile):
610 assert not (tmpl and mapfile)
641 assert not (tmpl and mapfile)
611 if mapfile:
642 if mapfile:
612 return formatter.mapfile_templatespec(b'changeset', mapfile)
643 return formatter.mapfile_templatespec(b'changeset', mapfile)
613 else:
644 else:
614 return formatter.literal_templatespec(tmpl)
645 return formatter.literal_templatespec(tmpl)
615
646
616
647
617 def _lookuptemplate(ui, tmpl, style):
648 def _lookuptemplate(ui, tmpl, style):
618 """Find the template matching the given template spec or style
649 """Find the template matching the given template spec or style
619
650
620 See formatter.lookuptemplate() for details.
651 See formatter.lookuptemplate() for details.
621 """
652 """
622
653
623 # ui settings
654 # ui settings
624 if not tmpl and not style: # template are stronger than style
655 if not tmpl and not style: # template are stronger than style
625 tmpl = ui.config(b'command-templates', b'log')
656 tmpl = ui.config(b'command-templates', b'log')
626 if tmpl:
657 if tmpl:
627 return formatter.literal_templatespec(templater.unquotestring(tmpl))
658 return formatter.literal_templatespec(templater.unquotestring(tmpl))
628 else:
659 else:
629 style = util.expandpath(ui.config(b'ui', b'style'))
660 style = util.expandpath(ui.config(b'ui', b'style'))
630
661
631 if not tmpl and style:
662 if not tmpl and style:
632 mapfile = style
663 mapfile = style
633 fp = None
664 fp = None
634 if not os.path.split(mapfile)[0]:
665 if not os.path.split(mapfile)[0]:
635 (mapname, fp) = templater.try_open_template(
666 (mapname, fp) = templater.try_open_template(
636 b'map-cmdline.' + mapfile
667 b'map-cmdline.' + mapfile
637 ) or templater.try_open_template(mapfile)
668 ) or templater.try_open_template(mapfile)
638 if mapname:
669 if mapname:
639 mapfile = mapname
670 mapfile = mapname
640 return formatter.mapfile_templatespec(b'changeset', mapfile, fp)
671 return formatter.mapfile_templatespec(b'changeset', mapfile, fp)
641
672
642 return formatter.lookuptemplate(ui, b'changeset', tmpl)
673 return formatter.lookuptemplate(ui, b'changeset', tmpl)
643
674
644
675
645 def maketemplater(ui, repo, tmpl, buffered=False):
676 def maketemplater(ui, repo, tmpl, buffered=False):
646 """Create a changesettemplater from a literal template 'tmpl'
677 """Create a changesettemplater from a literal template 'tmpl'
647 byte-string."""
678 byte-string."""
648 spec = formatter.literal_templatespec(tmpl)
679 spec = formatter.literal_templatespec(tmpl)
649 return changesettemplater(ui, repo, spec, buffered=buffered)
680 return changesettemplater(ui, repo, spec, buffered=buffered)
650
681
651
682
652 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
683 def changesetdisplayer(ui, repo, opts, differ=None, buffered=False):
653 """show one changeset using template or regular display.
684 """show one changeset using template or regular display.
654
685
655 Display format will be the first non-empty hit of:
686 Display format will be the first non-empty hit of:
656 1. option 'template'
687 1. option 'template'
657 2. option 'style'
688 2. option 'style'
658 3. [command-templates] setting 'log'
689 3. [command-templates] setting 'log'
659 4. [ui] setting 'style'
690 4. [ui] setting 'style'
660 If all of these values are either the unset or the empty string,
691 If all of these values are either the unset or the empty string,
661 regular display via changesetprinter() is done.
692 regular display via changesetprinter() is done.
662 """
693 """
663 postargs = (differ, opts, buffered)
694 postargs = (differ, opts, buffered)
664 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
695 spec = _lookuptemplate(ui, opts.get(b'template'), opts.get(b'style'))
665
696
666 # machine-readable formats have slightly different keyword set than
697 # machine-readable formats have slightly different keyword set than
667 # plain templates, which are handled by changesetformatter.
698 # plain templates, which are handled by changesetformatter.
668 # note that {b'pickle', b'debug'} can also be added to the list if needed.
699 # note that {b'pickle', b'debug'} can also be added to the list if needed.
669 if spec.ref in {b'cbor', b'json'}:
700 if spec.ref in {b'cbor', b'json'}:
670 fm = ui.formatter(b'log', opts)
701 fm = ui.formatter(b'log', opts)
671 return changesetformatter(ui, repo, fm, *postargs)
702 return changesetformatter(ui, repo, fm, *postargs)
672
703
673 if not spec.ref and not spec.tmpl and not spec.mapfile:
704 if not spec.ref and not spec.tmpl and not spec.mapfile:
674 return changesetprinter(ui, repo, *postargs)
705 return changesetprinter(ui, repo, *postargs)
675
706
676 return changesettemplater(ui, repo, spec, *postargs)
707 return changesettemplater(ui, repo, spec, *postargs)
677
708
678
709
679 @attr.s
710 @attr.s
680 class walkopts(object):
711 class walkopts(object):
681 """Options to configure a set of revisions and file matcher factory
712 """Options to configure a set of revisions and file matcher factory
682 to scan revision/file history
713 to scan revision/file history
683 """
714 """
684
715
685 # raw command-line parameters, which a matcher will be built from
716 # raw command-line parameters, which a matcher will be built from
686 pats = attr.ib() # type: List[bytes]
717 pats = attr.ib() # type: List[bytes]
687 opts = attr.ib() # type: Dict[bytes, Any]
718 opts = attr.ib() # type: Dict[bytes, Any]
688
719
689 # a list of revset expressions to be traversed; if follow, it specifies
720 # a list of revset expressions to be traversed; if follow, it specifies
690 # the start revisions
721 # the start revisions
691 revspec = attr.ib() # type: List[bytes]
722 revspec = attr.ib() # type: List[bytes]
692
723
693 # miscellaneous queries to filter revisions (see "hg help log" for details)
724 # miscellaneous queries to filter revisions (see "hg help log" for details)
694 bookmarks = attr.ib(default=attr.Factory(list)) # type: List[bytes]
725 bookmarks = attr.ib(default=attr.Factory(list)) # type: List[bytes]
695 branches = attr.ib(default=attr.Factory(list)) # type: List[bytes]
726 branches = attr.ib(default=attr.Factory(list)) # type: List[bytes]
696 date = attr.ib(default=None) # type: Optional[bytes]
727 date = attr.ib(default=None) # type: Optional[bytes]
697 keywords = attr.ib(default=attr.Factory(list)) # type: List[bytes]
728 keywords = attr.ib(default=attr.Factory(list)) # type: List[bytes]
698 no_merges = attr.ib(default=False) # type: bool
729 no_merges = attr.ib(default=False) # type: bool
699 only_merges = attr.ib(default=False) # type: bool
730 only_merges = attr.ib(default=False) # type: bool
700 prune_ancestors = attr.ib(default=attr.Factory(list)) # type: List[bytes]
731 prune_ancestors = attr.ib(default=attr.Factory(list)) # type: List[bytes]
701 users = attr.ib(default=attr.Factory(list)) # type: List[bytes]
732 users = attr.ib(default=attr.Factory(list)) # type: List[bytes]
702
733
703 # miscellaneous matcher arguments
734 # miscellaneous matcher arguments
704 include_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
735 include_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
705 exclude_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
736 exclude_pats = attr.ib(default=attr.Factory(list)) # type: List[bytes]
706
737
707 # 0: no follow, 1: follow first, 2: follow both parents
738 # 0: no follow, 1: follow first, 2: follow both parents
708 follow = attr.ib(default=0) # type: int
739 follow = attr.ib(default=0) # type: int
709
740
710 # do not attempt filelog-based traversal, which may be fast but cannot
741 # do not attempt filelog-based traversal, which may be fast but cannot
711 # include revisions where files were removed
742 # include revisions where files were removed
712 force_changelog_traversal = attr.ib(default=False) # type: bool
743 force_changelog_traversal = attr.ib(default=False) # type: bool
713
744
714 # filter revisions by file patterns, which should be disabled only if
745 # filter revisions by file patterns, which should be disabled only if
715 # you want to include revisions where files were unmodified
746 # you want to include revisions where files were unmodified
716 filter_revisions_by_pats = attr.ib(default=True) # type: bool
747 filter_revisions_by_pats = attr.ib(default=True) # type: bool
717
748
718 # sort revisions prior to traversal: 'desc', 'topo', or None
749 # sort revisions prior to traversal: 'desc', 'topo', or None
719 sort_revisions = attr.ib(default=None) # type: Optional[bytes]
750 sort_revisions = attr.ib(default=None) # type: Optional[bytes]
720
751
721 # limit number of changes displayed; None means unlimited
752 # limit number of changes displayed; None means unlimited
722 limit = attr.ib(default=None) # type: Optional[int]
753 limit = attr.ib(default=None) # type: Optional[int]
723
754
724
755
725 def parseopts(ui, pats, opts):
756 def parseopts(ui, pats, opts):
726 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts
757 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts
727 """Parse log command options into walkopts
758 """Parse log command options into walkopts
728
759
729 The returned walkopts will be passed in to getrevs() or makewalker().
760 The returned walkopts will be passed in to getrevs() or makewalker().
730 """
761 """
731 if opts.get(b'follow_first'):
762 if opts.get(b'follow_first'):
732 follow = 1
763 follow = 1
733 elif opts.get(b'follow'):
764 elif opts.get(b'follow'):
734 follow = 2
765 follow = 2
735 else:
766 else:
736 follow = 0
767 follow = 0
737
768
738 if opts.get(b'graph'):
769 if opts.get(b'graph'):
739 if ui.configbool(b'experimental', b'log.topo'):
770 if ui.configbool(b'experimental', b'log.topo'):
740 sort_revisions = b'topo'
771 sort_revisions = b'topo'
741 else:
772 else:
742 sort_revisions = b'desc'
773 sort_revisions = b'desc'
743 else:
774 else:
744 sort_revisions = None
775 sort_revisions = None
745
776
746 return walkopts(
777 return walkopts(
747 pats=pats,
778 pats=pats,
748 opts=opts,
779 opts=opts,
749 revspec=opts.get(b'rev', []),
780 revspec=opts.get(b'rev', []),
750 bookmarks=opts.get(b'bookmark', []),
781 bookmarks=opts.get(b'bookmark', []),
751 # branch and only_branch are really aliases and must be handled at
782 # branch and only_branch are really aliases and must be handled at
752 # the same time
783 # the same time
753 branches=opts.get(b'branch', []) + opts.get(b'only_branch', []),
784 branches=opts.get(b'branch', []) + opts.get(b'only_branch', []),
754 date=opts.get(b'date'),
785 date=opts.get(b'date'),
755 keywords=opts.get(b'keyword', []),
786 keywords=opts.get(b'keyword', []),
756 no_merges=bool(opts.get(b'no_merges')),
787 no_merges=bool(opts.get(b'no_merges')),
757 only_merges=bool(opts.get(b'only_merges')),
788 only_merges=bool(opts.get(b'only_merges')),
758 prune_ancestors=opts.get(b'prune', []),
789 prune_ancestors=opts.get(b'prune', []),
759 users=opts.get(b'user', []),
790 users=opts.get(b'user', []),
760 include_pats=opts.get(b'include', []),
791 include_pats=opts.get(b'include', []),
761 exclude_pats=opts.get(b'exclude', []),
792 exclude_pats=opts.get(b'exclude', []),
762 follow=follow,
793 follow=follow,
763 force_changelog_traversal=bool(opts.get(b'removed')),
794 force_changelog_traversal=bool(opts.get(b'removed')),
764 sort_revisions=sort_revisions,
795 sort_revisions=sort_revisions,
765 limit=getlimit(opts),
796 limit=getlimit(opts),
766 )
797 )
767
798
768
799
769 def _makematcher(repo, revs, wopts):
800 def _makematcher(repo, revs, wopts):
770 """Build matcher and expanded patterns from log options
801 """Build matcher and expanded patterns from log options
771
802
772 If --follow, revs are the revisions to follow from.
803 If --follow, revs are the revisions to follow from.
773
804
774 Returns (match, pats, slowpath) where
805 Returns (match, pats, slowpath) where
775 - match: a matcher built from the given pats and -I/-X opts
806 - match: a matcher built from the given pats and -I/-X opts
776 - pats: patterns used (globs are expanded on Windows)
807 - pats: patterns used (globs are expanded on Windows)
777 - slowpath: True if patterns aren't as simple as scanning filelogs
808 - slowpath: True if patterns aren't as simple as scanning filelogs
778 """
809 """
779 # pats/include/exclude are passed to match.match() directly in
810 # pats/include/exclude are passed to match.match() directly in
780 # _matchfiles() revset, but a log-like command should build its matcher
811 # _matchfiles() revset, but a log-like command should build its matcher
781 # with scmutil.match(). The difference is input pats are globbed on
812 # with scmutil.match(). The difference is input pats are globbed on
782 # platforms without shell expansion (windows).
813 # platforms without shell expansion (windows).
783 wctx = repo[None]
814 wctx = repo[None]
784 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts)
815 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts)
785 slowpath = match.anypats() or (
816 slowpath = match.anypats() or (
786 not match.always() and wopts.force_changelog_traversal
817 not match.always() and wopts.force_changelog_traversal
787 )
818 )
788 if not slowpath:
819 if not slowpath:
789 if wopts.follow and wopts.revspec:
820 if wopts.follow and wopts.revspec:
790 # There may be the case that a path doesn't exist in some (but
821 # There may be the case that a path doesn't exist in some (but
791 # not all) of the specified start revisions, but let's consider
822 # not all) of the specified start revisions, but let's consider
792 # the path is valid. Missing files will be warned by the matcher.
823 # the path is valid. Missing files will be warned by the matcher.
793 startctxs = [repo[r] for r in revs]
824 startctxs = [repo[r] for r in revs]
794 for f in match.files():
825 for f in match.files():
795 found = False
826 found = False
796 for c in startctxs:
827 for c in startctxs:
797 if f in c:
828 if f in c:
798 found = True
829 found = True
799 elif c.hasdir(f):
830 elif c.hasdir(f):
800 # If a directory exists in any of the start revisions,
831 # If a directory exists in any of the start revisions,
801 # take the slow path.
832 # take the slow path.
802 found = slowpath = True
833 found = slowpath = True
803 if not found:
834 if not found:
804 raise error.Abort(
835 raise error.Abort(
805 _(
836 _(
806 b'cannot follow file not in any of the specified '
837 b'cannot follow file not in any of the specified '
807 b'revisions: "%s"'
838 b'revisions: "%s"'
808 )
839 )
809 % f
840 % f
810 )
841 )
811 elif wopts.follow:
842 elif wopts.follow:
812 for f in match.files():
843 for f in match.files():
813 if f not in wctx:
844 if f not in wctx:
814 # If the file exists, it may be a directory, so let it
845 # If the file exists, it may be a directory, so let it
815 # take the slow path.
846 # take the slow path.
816 if os.path.exists(repo.wjoin(f)):
847 if os.path.exists(repo.wjoin(f)):
817 slowpath = True
848 slowpath = True
818 continue
849 continue
819 else:
850 else:
820 raise error.Abort(
851 raise error.Abort(
821 _(
852 _(
822 b'cannot follow file not in parent '
853 b'cannot follow file not in parent '
823 b'revision: "%s"'
854 b'revision: "%s"'
824 )
855 )
825 % f
856 % f
826 )
857 )
827 filelog = repo.file(f)
858 filelog = repo.file(f)
828 if not filelog:
859 if not filelog:
829 # A file exists in wdir but not in history, which means
860 # A file exists in wdir but not in history, which means
830 # the file isn't committed yet.
861 # the file isn't committed yet.
831 raise error.Abort(
862 raise error.Abort(
832 _(b'cannot follow nonexistent file: "%s"') % f
863 _(b'cannot follow nonexistent file: "%s"') % f
833 )
864 )
834 else:
865 else:
835 for f in match.files():
866 for f in match.files():
836 filelog = repo.file(f)
867 filelog = repo.file(f)
837 if not filelog:
868 if not filelog:
838 # A zero count may be a directory or deleted file, so
869 # A zero count may be a directory or deleted file, so
839 # try to find matching entries on the slow path.
870 # try to find matching entries on the slow path.
840 slowpath = True
871 slowpath = True
841
872
842 # We decided to fall back to the slowpath because at least one
873 # We decided to fall back to the slowpath because at least one
843 # of the paths was not a file. Check to see if at least one of them
874 # of the paths was not a file. Check to see if at least one of them
844 # existed in history - in that case, we'll continue down the
875 # existed in history - in that case, we'll continue down the
845 # slowpath; otherwise, we can turn off the slowpath
876 # slowpath; otherwise, we can turn off the slowpath
846 if slowpath:
877 if slowpath:
847 for path in match.files():
878 for path in match.files():
848 if path == b'.' or path in repo.store:
879 if path == b'.' or path in repo.store:
849 break
880 break
850 else:
881 else:
851 slowpath = False
882 slowpath = False
852
883
853 return match, pats, slowpath
884 return match, pats, slowpath
854
885
855
886
856 def _fileancestors(repo, revs, match, followfirst):
887 def _fileancestors(repo, revs, match, followfirst):
857 fctxs = []
888 fctxs = []
858 for r in revs:
889 for r in revs:
859 ctx = repo[r]
890 ctx = repo[r]
860 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
891 fctxs.extend(ctx[f].introfilectx() for f in ctx.walk(match))
861
892
862 # When displaying a revision with --patch --follow FILE, we have
893 # When displaying a revision with --patch --follow FILE, we have
863 # to know which file of the revision must be diffed. With
894 # to know which file of the revision must be diffed. With
864 # --follow, we want the names of the ancestors of FILE in the
895 # --follow, we want the names of the ancestors of FILE in the
865 # revision, stored in "fcache". "fcache" is populated as a side effect
896 # revision, stored in "fcache". "fcache" is populated as a side effect
866 # of the graph traversal.
897 # of the graph traversal.
867 fcache = {}
898 fcache = {}
868
899
869 def filematcher(ctx):
900 def filematcher(ctx):
870 return scmutil.matchfiles(repo, fcache.get(scmutil.intrev(ctx), []))
901 return scmutil.matchfiles(repo, fcache.get(scmutil.intrev(ctx), []))
871
902
872 def revgen():
903 def revgen():
873 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
904 for rev, cs in dagop.filectxancestors(fctxs, followfirst=followfirst):
874 fcache[rev] = [c.path() for c in cs]
905 fcache[rev] = [c.path() for c in cs]
875 yield rev
906 yield rev
876
907
877 return smartset.generatorset(revgen(), iterasc=False), filematcher
908 return smartset.generatorset(revgen(), iterasc=False), filematcher
878
909
879
910
880 def _makenofollowfilematcher(repo, pats, opts):
911 def _makenofollowfilematcher(repo, pats, opts):
881 '''hook for extensions to override the filematcher for non-follow cases'''
912 '''hook for extensions to override the filematcher for non-follow cases'''
882 return None
913 return None
883
914
884
915
885 _opt2logrevset = {
916 _opt2logrevset = {
886 b'no_merges': (b'not merge()', None),
917 b'no_merges': (b'not merge()', None),
887 b'only_merges': (b'merge()', None),
918 b'only_merges': (b'merge()', None),
888 b'_matchfiles': (None, b'_matchfiles(%ps)'),
919 b'_matchfiles': (None, b'_matchfiles(%ps)'),
889 b'date': (b'date(%s)', None),
920 b'date': (b'date(%s)', None),
890 b'branch': (b'branch(%s)', b'%lr'),
921 b'branch': (b'branch(%s)', b'%lr'),
891 b'_patslog': (b'filelog(%s)', b'%lr'),
922 b'_patslog': (b'filelog(%s)', b'%lr'),
892 b'keyword': (b'keyword(%s)', b'%lr'),
923 b'keyword': (b'keyword(%s)', b'%lr'),
893 b'prune': (b'ancestors(%s)', b'not %lr'),
924 b'prune': (b'ancestors(%s)', b'not %lr'),
894 b'user': (b'user(%s)', b'%lr'),
925 b'user': (b'user(%s)', b'%lr'),
895 }
926 }
896
927
897
928
898 def _makerevset(repo, wopts, slowpath):
929 def _makerevset(repo, wopts, slowpath):
899 """Return a revset string built from log options and file patterns"""
930 """Return a revset string built from log options and file patterns"""
900 opts = {
931 opts = {
901 b'branch': [b'literal:' + repo.lookupbranch(b) for b in wopts.branches],
932 b'branch': [b'literal:' + repo.lookupbranch(b) for b in wopts.branches],
902 b'date': wopts.date,
933 b'date': wopts.date,
903 b'keyword': wopts.keywords,
934 b'keyword': wopts.keywords,
904 b'no_merges': wopts.no_merges,
935 b'no_merges': wopts.no_merges,
905 b'only_merges': wopts.only_merges,
936 b'only_merges': wopts.only_merges,
906 b'prune': wopts.prune_ancestors,
937 b'prune': wopts.prune_ancestors,
907 b'user': [b'literal:' + v for v in wopts.users],
938 b'user': [b'literal:' + v for v in wopts.users],
908 }
939 }
909
940
910 if wopts.filter_revisions_by_pats and slowpath:
941 if wopts.filter_revisions_by_pats and slowpath:
911 # pats/include/exclude cannot be represented as separate
942 # pats/include/exclude cannot be represented as separate
912 # revset expressions as their filtering logic applies at file
943 # revset expressions as their filtering logic applies at file
913 # level. For instance "-I a -X b" matches a revision touching
944 # level. For instance "-I a -X b" matches a revision touching
914 # "a" and "b" while "file(a) and not file(b)" does
945 # "a" and "b" while "file(a) and not file(b)" does
915 # not. Besides, filesets are evaluated against the working
946 # not. Besides, filesets are evaluated against the working
916 # directory.
947 # directory.
917 matchargs = [b'r:', b'd:relpath']
948 matchargs = [b'r:', b'd:relpath']
918 for p in wopts.pats:
949 for p in wopts.pats:
919 matchargs.append(b'p:' + p)
950 matchargs.append(b'p:' + p)
920 for p in wopts.include_pats:
951 for p in wopts.include_pats:
921 matchargs.append(b'i:' + p)
952 matchargs.append(b'i:' + p)
922 for p in wopts.exclude_pats:
953 for p in wopts.exclude_pats:
923 matchargs.append(b'x:' + p)
954 matchargs.append(b'x:' + p)
924 opts[b'_matchfiles'] = matchargs
955 opts[b'_matchfiles'] = matchargs
925 elif wopts.filter_revisions_by_pats and not wopts.follow:
956 elif wopts.filter_revisions_by_pats and not wopts.follow:
926 opts[b'_patslog'] = list(wopts.pats)
957 opts[b'_patslog'] = list(wopts.pats)
927
958
928 expr = []
959 expr = []
929 for op, val in sorted(pycompat.iteritems(opts)):
960 for op, val in sorted(pycompat.iteritems(opts)):
930 if not val:
961 if not val:
931 continue
962 continue
932 revop, listop = _opt2logrevset[op]
963 revop, listop = _opt2logrevset[op]
933 if revop and b'%' not in revop:
964 if revop and b'%' not in revop:
934 expr.append(revop)
965 expr.append(revop)
935 elif not listop:
966 elif not listop:
936 expr.append(revsetlang.formatspec(revop, val))
967 expr.append(revsetlang.formatspec(revop, val))
937 else:
968 else:
938 if revop:
969 if revop:
939 val = [revsetlang.formatspec(revop, v) for v in val]
970 val = [revsetlang.formatspec(revop, v) for v in val]
940 expr.append(revsetlang.formatspec(listop, val))
971 expr.append(revsetlang.formatspec(listop, val))
941
972
942 if wopts.bookmarks:
973 if wopts.bookmarks:
943 expr.append(
974 expr.append(
944 revsetlang.formatspec(
975 revsetlang.formatspec(
945 b'%lr',
976 b'%lr',
946 [scmutil.format_bookmark_revspec(v) for v in wopts.bookmarks],
977 [scmutil.format_bookmark_revspec(v) for v in wopts.bookmarks],
947 )
978 )
948 )
979 )
949
980
950 if expr:
981 if expr:
951 expr = b'(' + b' and '.join(expr) + b')'
982 expr = b'(' + b' and '.join(expr) + b')'
952 else:
983 else:
953 expr = None
984 expr = None
954 return expr
985 return expr
955
986
956
987
957 def _initialrevs(repo, wopts):
988 def _initialrevs(repo, wopts):
958 """Return the initial set of revisions to be filtered or followed"""
989 """Return the initial set of revisions to be filtered or followed"""
959 if wopts.revspec:
990 if wopts.revspec:
960 revs = scmutil.revrange(repo, wopts.revspec)
991 revs = scmutil.revrange(repo, wopts.revspec)
961 elif wopts.follow and repo.dirstate.p1() == nullid:
992 elif wopts.follow and repo.dirstate.p1() == nullid:
962 revs = smartset.baseset()
993 revs = smartset.baseset()
963 elif wopts.follow:
994 elif wopts.follow:
964 revs = repo.revs(b'.')
995 revs = repo.revs(b'.')
965 else:
996 else:
966 revs = smartset.spanset(repo)
997 revs = smartset.spanset(repo)
967 revs.reverse()
998 revs.reverse()
968 return revs
999 return revs
969
1000
970
1001
971 def makewalker(repo, wopts):
1002 def makewalker(repo, wopts):
972 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[Callable[[Any], matchmod.basematcher]]]
1003 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[Callable[[Any], matchmod.basematcher]]]
973 """Build (revs, makefilematcher) to scan revision/file history
1004 """Build (revs, makefilematcher) to scan revision/file history
974
1005
975 - revs is the smartset to be traversed.
1006 - revs is the smartset to be traversed.
976 - makefilematcher is a function to map ctx to a matcher for that revision
1007 - makefilematcher is a function to map ctx to a matcher for that revision
977 """
1008 """
978 revs = _initialrevs(repo, wopts)
1009 revs = _initialrevs(repo, wopts)
979 if not revs:
1010 if not revs:
980 return smartset.baseset(), None
1011 return smartset.baseset(), None
981 # TODO: might want to merge slowpath with wopts.force_changelog_traversal
1012 # TODO: might want to merge slowpath with wopts.force_changelog_traversal
982 match, pats, slowpath = _makematcher(repo, revs, wopts)
1013 match, pats, slowpath = _makematcher(repo, revs, wopts)
983 wopts = attr.evolve(wopts, pats=pats)
1014 wopts = attr.evolve(wopts, pats=pats)
984
1015
985 filematcher = None
1016 filematcher = None
986 if wopts.follow:
1017 if wopts.follow:
987 if slowpath or match.always():
1018 if slowpath or match.always():
988 revs = dagop.revancestors(repo, revs, followfirst=wopts.follow == 1)
1019 revs = dagop.revancestors(repo, revs, followfirst=wopts.follow == 1)
989 else:
1020 else:
990 assert not wopts.force_changelog_traversal
1021 assert not wopts.force_changelog_traversal
991 revs, filematcher = _fileancestors(
1022 revs, filematcher = _fileancestors(
992 repo, revs, match, followfirst=wopts.follow == 1
1023 repo, revs, match, followfirst=wopts.follow == 1
993 )
1024 )
994 revs.reverse()
1025 revs.reverse()
995 if filematcher is None:
1026 if filematcher is None:
996 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts)
1027 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts)
997 if filematcher is None:
1028 if filematcher is None:
998
1029
999 def filematcher(ctx):
1030 def filematcher(ctx):
1000 return match
1031 return match
1001
1032
1002 expr = _makerevset(repo, wopts, slowpath)
1033 expr = _makerevset(repo, wopts, slowpath)
1003 if wopts.sort_revisions:
1034 if wopts.sort_revisions:
1004 assert wopts.sort_revisions in {b'topo', b'desc'}
1035 assert wopts.sort_revisions in {b'topo', b'desc'}
1005 if wopts.sort_revisions == b'topo':
1036 if wopts.sort_revisions == b'topo':
1006 if not revs.istopo():
1037 if not revs.istopo():
1007 revs = dagop.toposort(revs, repo.changelog.parentrevs)
1038 revs = dagop.toposort(revs, repo.changelog.parentrevs)
1008 # TODO: try to iterate the set lazily
1039 # TODO: try to iterate the set lazily
1009 revs = revset.baseset(list(revs), istopo=True)
1040 revs = revset.baseset(list(revs), istopo=True)
1010 elif not (revs.isdescending() or revs.istopo()):
1041 elif not (revs.isdescending() or revs.istopo()):
1011 # User-specified revs might be unsorted
1042 # User-specified revs might be unsorted
1012 revs.sort(reverse=True)
1043 revs.sort(reverse=True)
1013 if expr:
1044 if expr:
1014 matcher = revset.match(None, expr)
1045 matcher = revset.match(None, expr)
1015 revs = matcher(repo, revs)
1046 revs = matcher(repo, revs)
1016 if wopts.limit is not None:
1047 if wopts.limit is not None:
1017 revs = revs.slice(0, wopts.limit)
1048 revs = revs.slice(0, wopts.limit)
1018
1049
1019 return revs, filematcher
1050 return revs, filematcher
1020
1051
1021
1052
1022 def getrevs(repo, wopts):
1053 def getrevs(repo, wopts):
1023 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
1054 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
1024 """Return (revs, differ) where revs is a smartset
1055 """Return (revs, differ) where revs is a smartset
1025
1056
1026 differ is a changesetdiffer with pre-configured file matcher.
1057 differ is a changesetdiffer with pre-configured file matcher.
1027 """
1058 """
1028 revs, filematcher = makewalker(repo, wopts)
1059 revs, filematcher = makewalker(repo, wopts)
1029 if not revs:
1060 if not revs:
1030 return revs, None
1061 return revs, None
1031 differ = changesetdiffer()
1062 differ = changesetdiffer()
1032 differ._makefilematcher = filematcher
1063 differ._makefilematcher = filematcher
1033 return revs, differ
1064 return revs, differ
1034
1065
1035
1066
1036 def _parselinerangeopt(repo, opts):
1067 def _parselinerangeopt(repo, opts):
1037 """Parse --line-range log option and return a list of tuples (filename,
1068 """Parse --line-range log option and return a list of tuples (filename,
1038 (fromline, toline)).
1069 (fromline, toline)).
1039 """
1070 """
1040 linerangebyfname = []
1071 linerangebyfname = []
1041 for pat in opts.get(b'line_range', []):
1072 for pat in opts.get(b'line_range', []):
1042 try:
1073 try:
1043 pat, linerange = pat.rsplit(b',', 1)
1074 pat, linerange = pat.rsplit(b',', 1)
1044 except ValueError:
1075 except ValueError:
1045 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
1076 raise error.Abort(_(b'malformatted line-range pattern %s') % pat)
1046 try:
1077 try:
1047 fromline, toline = map(int, linerange.split(b':'))
1078 fromline, toline = map(int, linerange.split(b':'))
1048 except ValueError:
1079 except ValueError:
1049 raise error.Abort(_(b"invalid line range for %s") % pat)
1080 raise error.Abort(_(b"invalid line range for %s") % pat)
1050 msg = _(b"line range pattern '%s' must match exactly one file") % pat
1081 msg = _(b"line range pattern '%s' must match exactly one file") % pat
1051 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
1082 fname = scmutil.parsefollowlinespattern(repo, None, pat, msg)
1052 linerangebyfname.append(
1083 linerangebyfname.append(
1053 (fname, util.processlinerange(fromline, toline))
1084 (fname, util.processlinerange(fromline, toline))
1054 )
1085 )
1055 return linerangebyfname
1086 return linerangebyfname
1056
1087
1057
1088
1058 def getlinerangerevs(repo, userrevs, opts):
1089 def getlinerangerevs(repo, userrevs, opts):
1059 """Return (revs, differ).
1090 """Return (revs, differ).
1060
1091
1061 "revs" are revisions obtained by processing "line-range" log options and
1092 "revs" are revisions obtained by processing "line-range" log options and
1062 walking block ancestors of each specified file/line-range.
1093 walking block ancestors of each specified file/line-range.
1063
1094
1064 "differ" is a changesetdiffer with pre-configured file matcher and hunks
1095 "differ" is a changesetdiffer with pre-configured file matcher and hunks
1065 filter.
1096 filter.
1066 """
1097 """
1067 wctx = repo[None]
1098 wctx = repo[None]
1068
1099
1069 # Two-levels map of "rev -> file ctx -> [line range]".
1100 # Two-levels map of "rev -> file ctx -> [line range]".
1070 linerangesbyrev = {}
1101 linerangesbyrev = {}
1071 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
1102 for fname, (fromline, toline) in _parselinerangeopt(repo, opts):
1072 if fname not in wctx:
1103 if fname not in wctx:
1073 raise error.Abort(
1104 raise error.Abort(
1074 _(b'cannot follow file not in parent revision: "%s"') % fname
1105 _(b'cannot follow file not in parent revision: "%s"') % fname
1075 )
1106 )
1076 fctx = wctx.filectx(fname)
1107 fctx = wctx.filectx(fname)
1077 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
1108 for fctx, linerange in dagop.blockancestors(fctx, fromline, toline):
1078 rev = fctx.introrev()
1109 rev = fctx.introrev()
1079 if rev is None:
1110 if rev is None:
1080 rev = wdirrev
1111 rev = wdirrev
1081 if rev not in userrevs:
1112 if rev not in userrevs:
1082 continue
1113 continue
1083 linerangesbyrev.setdefault(rev, {}).setdefault(
1114 linerangesbyrev.setdefault(rev, {}).setdefault(
1084 fctx.path(), []
1115 fctx.path(), []
1085 ).append(linerange)
1116 ).append(linerange)
1086
1117
1087 def nofilterhunksfn(fctx, hunks):
1118 def nofilterhunksfn(fctx, hunks):
1088 return hunks
1119 return hunks
1089
1120
1090 def hunksfilter(ctx):
1121 def hunksfilter(ctx):
1091 fctxlineranges = linerangesbyrev.get(scmutil.intrev(ctx))
1122 fctxlineranges = linerangesbyrev.get(scmutil.intrev(ctx))
1092 if fctxlineranges is None:
1123 if fctxlineranges is None:
1093 return nofilterhunksfn
1124 return nofilterhunksfn
1094
1125
1095 def filterfn(fctx, hunks):
1126 def filterfn(fctx, hunks):
1096 lineranges = fctxlineranges.get(fctx.path())
1127 lineranges = fctxlineranges.get(fctx.path())
1097 if lineranges is not None:
1128 if lineranges is not None:
1098 for hr, lines in hunks:
1129 for hr, lines in hunks:
1099 if hr is None: # binary
1130 if hr is None: # binary
1100 yield hr, lines
1131 yield hr, lines
1101 continue
1132 continue
1102 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
1133 if any(mdiff.hunkinrange(hr[2:], lr) for lr in lineranges):
1103 yield hr, lines
1134 yield hr, lines
1104 else:
1135 else:
1105 for hunk in hunks:
1136 for hunk in hunks:
1106 yield hunk
1137 yield hunk
1107
1138
1108 return filterfn
1139 return filterfn
1109
1140
1110 def filematcher(ctx):
1141 def filematcher(ctx):
1111 files = list(linerangesbyrev.get(scmutil.intrev(ctx), []))
1142 files = list(linerangesbyrev.get(scmutil.intrev(ctx), []))
1112 return scmutil.matchfiles(repo, files)
1143 return scmutil.matchfiles(repo, files)
1113
1144
1114 revs = sorted(linerangesbyrev, reverse=True)
1145 revs = sorted(linerangesbyrev, reverse=True)
1115
1146
1116 differ = changesetdiffer()
1147 differ = changesetdiffer()
1117 differ._makefilematcher = filematcher
1148 differ._makefilematcher = filematcher
1118 differ._makehunksfilter = hunksfilter
1149 differ._makehunksfilter = hunksfilter
1119 return smartset.baseset(revs), differ
1150 return smartset.baseset(revs), differ
1120
1151
1121
1152
1122 def _graphnodeformatter(ui, displayer):
1153 def _graphnodeformatter(ui, displayer):
1123 spec = ui.config(b'command-templates', b'graphnode')
1154 spec = ui.config(b'command-templates', b'graphnode')
1124 if not spec:
1155 if not spec:
1125 return templatekw.getgraphnode # fast path for "{graphnode}"
1156 return templatekw.getgraphnode # fast path for "{graphnode}"
1126
1157
1127 spec = templater.unquotestring(spec)
1158 spec = templater.unquotestring(spec)
1128 if isinstance(displayer, changesettemplater):
1159 if isinstance(displayer, changesettemplater):
1129 # reuse cache of slow templates
1160 # reuse cache of slow templates
1130 tres = displayer._tresources
1161 tres = displayer._tresources
1131 else:
1162 else:
1132 tres = formatter.templateresources(ui)
1163 tres = formatter.templateresources(ui)
1133 templ = formatter.maketemplater(
1164 templ = formatter.maketemplater(
1134 ui, spec, defaults=templatekw.keywords, resources=tres
1165 ui, spec, defaults=templatekw.keywords, resources=tres
1135 )
1166 )
1136
1167
1137 def formatnode(repo, ctx, cache):
1168 def formatnode(repo, ctx, cache):
1138 props = {b'ctx': ctx, b'repo': repo}
1169 props = {b'ctx': ctx, b'repo': repo}
1139 return templ.renderdefault(props)
1170 return templ.renderdefault(props)
1140
1171
1141 return formatnode
1172 return formatnode
1142
1173
1143
1174
1144 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
1175 def displaygraph(ui, repo, dag, displayer, edgefn, getcopies=None, props=None):
1145 props = props or {}
1176 props = props or {}
1146 formatnode = _graphnodeformatter(ui, displayer)
1177 formatnode = _graphnodeformatter(ui, displayer)
1147 state = graphmod.asciistate()
1178 state = graphmod.asciistate()
1148 styles = state.styles
1179 styles = state.styles
1149
1180
1150 # only set graph styling if HGPLAIN is not set.
1181 # only set graph styling if HGPLAIN is not set.
1151 if ui.plain(b'graph'):
1182 if ui.plain(b'graph'):
1152 # set all edge styles to |, the default pre-3.8 behaviour
1183 # set all edge styles to |, the default pre-3.8 behaviour
1153 styles.update(dict.fromkeys(styles, b'|'))
1184 styles.update(dict.fromkeys(styles, b'|'))
1154 else:
1185 else:
1155 edgetypes = {
1186 edgetypes = {
1156 b'parent': graphmod.PARENT,
1187 b'parent': graphmod.PARENT,
1157 b'grandparent': graphmod.GRANDPARENT,
1188 b'grandparent': graphmod.GRANDPARENT,
1158 b'missing': graphmod.MISSINGPARENT,
1189 b'missing': graphmod.MISSINGPARENT,
1159 }
1190 }
1160 for name, key in edgetypes.items():
1191 for name, key in edgetypes.items():
1161 # experimental config: experimental.graphstyle.*
1192 # experimental config: experimental.graphstyle.*
1162 styles[key] = ui.config(
1193 styles[key] = ui.config(
1163 b'experimental', b'graphstyle.%s' % name, styles[key]
1194 b'experimental', b'graphstyle.%s' % name, styles[key]
1164 )
1195 )
1165 if not styles[key]:
1196 if not styles[key]:
1166 styles[key] = None
1197 styles[key] = None
1167
1198
1168 # experimental config: experimental.graphshorten
1199 # experimental config: experimental.graphshorten
1169 state.graphshorten = ui.configbool(b'experimental', b'graphshorten')
1200 state.graphshorten = ui.configbool(b'experimental', b'graphshorten')
1170
1201
1171 formatnode_cache = {}
1202 formatnode_cache = {}
1172 for rev, type, ctx, parents in dag:
1203 for rev, type, ctx, parents in dag:
1173 char = formatnode(repo, ctx, formatnode_cache)
1204 char = formatnode(repo, ctx, formatnode_cache)
1174 copies = getcopies(ctx) if getcopies else None
1205 copies = getcopies(ctx) if getcopies else None
1175 edges = edgefn(type, char, state, rev, parents)
1206 edges = edgefn(type, char, state, rev, parents)
1176 firstedge = next(edges)
1207 firstedge = next(edges)
1177 width = firstedge[2]
1208 width = firstedge[2]
1178 displayer.show(
1209 displayer.show(
1179 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1210 ctx, copies=copies, graphwidth=width, **pycompat.strkwargs(props)
1180 )
1211 )
1181 lines = displayer.hunk.pop(rev).split(b'\n')
1212 lines = displayer.hunk.pop(rev).split(b'\n')
1182 if not lines[-1]:
1213 if not lines[-1]:
1183 del lines[-1]
1214 del lines[-1]
1184 displayer.flush(ctx)
1215 displayer.flush(ctx)
1185 for type, char, width, coldata in itertools.chain([firstedge], edges):
1216 for type, char, width, coldata in itertools.chain([firstedge], edges):
1186 graphmod.ascii(ui, state, type, char, lines, coldata)
1217 graphmod.ascii(ui, state, type, char, lines, coldata)
1187 lines = []
1218 lines = []
1188 displayer.close()
1219 displayer.close()
1189
1220
1190
1221
1191 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1222 def displaygraphrevs(ui, repo, revs, displayer, getrenamed):
1192 revdag = graphmod.dagwalker(repo, revs)
1223 revdag = graphmod.dagwalker(repo, revs)
1193 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1224 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
1194
1225
1195
1226
1196 def displayrevs(ui, repo, revs, displayer, getcopies):
1227 def displayrevs(ui, repo, revs, displayer, getcopies):
1197 for rev in revs:
1228 for rev in revs:
1198 ctx = repo[rev]
1229 ctx = repo[rev]
1199 copies = getcopies(ctx) if getcopies else None
1230 copies = getcopies(ctx) if getcopies else None
1200 displayer.show(ctx, copies=copies)
1231 displayer.show(ctx, copies=copies)
1201 displayer.flush(ctx)
1232 displayer.flush(ctx)
1202 displayer.close()
1233 displayer.close()
1203
1234
1204
1235
1205 def checkunsupportedgraphflags(pats, opts):
1236 def checkunsupportedgraphflags(pats, opts):
1206 for op in [b"newest_first"]:
1237 for op in [b"newest_first"]:
1207 if op in opts and opts[op]:
1238 if op in opts and opts[op]:
1208 raise error.Abort(
1239 raise error.Abort(
1209 _(b"-G/--graph option is incompatible with --%s")
1240 _(b"-G/--graph option is incompatible with --%s")
1210 % op.replace(b"_", b"-")
1241 % op.replace(b"_", b"-")
1211 )
1242 )
1212
1243
1213
1244
1214 def graphrevs(repo, nodes, opts):
1245 def graphrevs(repo, nodes, opts):
1215 limit = getlimit(opts)
1246 limit = getlimit(opts)
1216 nodes.reverse()
1247 nodes.reverse()
1217 if limit is not None:
1248 if limit is not None:
1218 nodes = nodes[:limit]
1249 nodes = nodes[:limit]
1219 return graphmod.nodes(repo, nodes)
1250 return graphmod.nodes(repo, nodes)
General Comments 0
You need to be logged in to leave comments. Login now