##// END OF EJS Templates
config: move edition code in its own module...
marmoute -
r53314:c97e0fd2 default
parent child Browse files
Show More
@@ -0,0 +1,108
1 # Gather code related to command dealing with configuration.
2
3 from __future__ import annotations
4
5 import os
6
7 from typing import Any, Dict, Optional
8
9 from ..i18n import _
10
11 from .. import (
12 cmdutil,
13 error,
14 requirements,
15 ui as uimod,
16 util,
17 vfs as vfsmod,
18 )
19
20 from . import rcutil
21
22 EDIT_FLAG = 'edit'
23
24
25 # keep typing simple for now
26 ConfigLevelT = str
27 LEVEL_USER = 'user' # "user" is the default level and never passed explicitly
28 LEVEL_LOCAL = 'local'
29 LEVEL_GLOBAL = 'global'
30 LEVEL_SHARED = 'shared'
31 LEVEL_NON_SHARED = 'non_shared'
32 EDIT_LEVELS = (
33 LEVEL_USER,
34 LEVEL_LOCAL,
35 LEVEL_GLOBAL,
36 LEVEL_SHARED,
37 LEVEL_NON_SHARED,
38 )
39
40
41 def find_edit_level(
42 ui: uimod.ui, repo, opts: Dict[str, Any]
43 ) -> Optional[ConfigLevelT]:
44 """return the level we should edit, if any.
45
46 Parse the command option to detect when an edit is requested, and if so the
47 configuration level we should edit.
48 """
49 if opts.get(EDIT_FLAG) or any(opts.get(o) for o in EDIT_LEVELS):
50 cmdutil.check_at_most_one_arg(opts, *EDIT_LEVELS)
51 for level in EDIT_LEVELS:
52 if opts.get(level):
53 return level
54 return EDIT_LEVELS[0]
55 return None
56
57
58 def edit_config(ui: uimod.ui, repo, level: ConfigLevelT) -> None:
59 """let the user edit configuration file for the given level"""
60
61 if level == LEVEL_USER:
62 paths = rcutil.userrcpath()
63 elif level == LEVEL_GLOBAL:
64 paths = rcutil.systemrcpath()
65 elif level == LEVEL_LOCAL:
66 if not repo:
67 raise error.InputError(_(b"can't use --local outside a repository"))
68 paths = [repo.vfs.join(b'hgrc')]
69 elif level == LEVEL_NON_SHARED:
70 paths = [repo.vfs.join(b'hgrc-not-shared')]
71 elif level == LEVEL_SHARED:
72 if not repo.shared():
73 raise error.InputError(
74 _(b"repository is not shared; can't use --shared")
75 )
76 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
77 raise error.InputError(
78 _(
79 b"share safe feature not enabled; "
80 b"unable to edit shared source repository config"
81 )
82 )
83 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
84 else:
85 msg = 'unknown config level: %s' % level
86 raise error.ProgrammingError(msg)
87
88 for f in paths:
89 if os.path.exists(f):
90 break
91 else:
92 if LEVEL_GLOBAL:
93 samplehgrc = uimod.samplehgrcs[b'global']
94 elif LEVEL_LOCAL:
95 samplehgrc = uimod.samplehgrcs[b'local']
96 else:
97 samplehgrc = uimod.samplehgrcs[b'user']
98
99 f = paths[0]
100 util.writefile(f, util.tonativeeol(samplehgrc))
101
102 editor = ui.geteditor()
103 ui.system(
104 b"%s \"%s\"" % (editor, f),
105 onerr=error.InputError,
106 errprefix=_(b"edit failed"),
107 blockedtag=b'config_edit',
108 )
@@ -1,7759 +1,7710
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import annotations
9 9
10 10 import os
11 11 import re
12 12 import sys
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 nullid,
18 18 nullrev,
19 19 short,
20 20 wdirrev,
21 21 )
22 22 from . import (
23 23 admin_commands as admin_commands_mod,
24 24 archival,
25 25 bookmarks,
26 26 bundle2,
27 27 bundlecaches,
28 28 changegroup,
29 29 cmdutil,
30 30 copies,
31 31 debugcommands as debugcommandsmod,
32 32 destutil,
33 33 diffutil,
34 34 discovery,
35 35 encoding,
36 36 error,
37 37 exchange,
38 38 extensions,
39 39 filemerge,
40 40 formatter,
41 41 graphmod,
42 42 grep as grepmod,
43 43 hbisect,
44 44 help,
45 45 hg,
46 46 logcmdutil,
47 47 merge as mergemod,
48 48 mergestate as mergestatemod,
49 49 narrowspec,
50 50 obsolete,
51 51 obsutil,
52 52 patch,
53 53 phases,
54 54 pycompat,
55 55 registrar,
56 requirements,
57 56 revsetlang,
58 57 rewriteutil,
59 58 scmutil,
60 59 server,
61 60 shelve as shelvemod,
62 61 state as statemod,
63 62 tags as tagsmod,
64 ui as uimod,
65 63 util,
66 64 verify as verifymod,
67 vfs as vfsmod,
68 65 wireprotoserver,
69 66 )
70 67
71 68 from .cmd_impls import graft as graft_impl
72 from .configuration import rcutil
69 from .configuration import (
70 command as config_command,
71 rcutil,
72 )
73 73 from .utils import (
74 74 dateutil,
75 75 procutil,
76 76 stringutil,
77 77 urlutil,
78 78 )
79 79
80 80 table = {}
81 81 table.update(debugcommandsmod.command._table)
82 82 table.update(admin_commands_mod.command._table)
83 83
84 84 command = registrar.command(table)
85 85 INTENT_READONLY = registrar.INTENT_READONLY
86 86
87 87 # common command options
88 88
89 89 globalopts = [
90 90 (
91 91 b'R',
92 92 b'repository',
93 93 b'',
94 94 _(b'repository root directory or name of overlay bundle file'),
95 95 _(b'REPO'),
96 96 ),
97 97 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
98 98 (
99 99 b'y',
100 100 b'noninteractive',
101 101 None,
102 102 _(
103 103 b'do not prompt, automatically pick the first choice for all prompts'
104 104 ),
105 105 ),
106 106 (b'q', b'quiet', None, _(b'suppress output')),
107 107 (b'v', b'verbose', None, _(b'enable additional output')),
108 108 (
109 109 b'',
110 110 b'color',
111 111 b'',
112 112 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
113 113 # and should not be translated
114 114 _(b"when to colorize (boolean, always, auto, never, or debug)"),
115 115 _(b'TYPE'),
116 116 ),
117 117 (
118 118 b'',
119 119 b'config',
120 120 [],
121 121 _(b'set/override config option (use \'section.name=value\')'),
122 122 _(b'CONFIG'),
123 123 ),
124 124 (b'', b'debug', None, _(b'enable debugging output')),
125 125 (b'', b'debugger', None, _(b'start debugger')),
126 126 (
127 127 b'',
128 128 b'encoding',
129 129 encoding.encoding,
130 130 _(b'set the charset encoding'),
131 131 _(b'ENCODE'),
132 132 ),
133 133 (
134 134 b'',
135 135 b'encodingmode',
136 136 encoding.encodingmode,
137 137 _(b'set the charset encoding mode'),
138 138 _(b'MODE'),
139 139 ),
140 140 (b'', b'traceback', None, _(b'always print a traceback on exception')),
141 141 (b'', b'time', None, _(b'time how long the command takes')),
142 142 (b'', b'profile', None, _(b'print command execution profile')),
143 143 (b'', b'version', None, _(b'output version information and exit')),
144 144 (b'h', b'help', None, _(b'display help and exit')),
145 145 (b'', b'hidden', False, _(b'consider hidden changesets')),
146 146 (
147 147 b'',
148 148 b'pager',
149 149 b'auto',
150 150 _(b"when to paginate (boolean, always, auto, or never)"),
151 151 _(b'TYPE'),
152 152 ),
153 153 ]
154 154
155 155 dryrunopts = cmdutil.dryrunopts
156 156 remoteopts = cmdutil.remoteopts
157 157 walkopts = cmdutil.walkopts
158 158 commitopts = cmdutil.commitopts
159 159 commitopts2 = cmdutil.commitopts2
160 160 commitopts3 = cmdutil.commitopts3
161 161 formatteropts = cmdutil.formatteropts
162 162 templateopts = cmdutil.templateopts
163 163 logopts = cmdutil.logopts
164 164 diffopts = cmdutil.diffopts
165 165 diffwsopts = cmdutil.diffwsopts
166 166 diffopts2 = cmdutil.diffopts2
167 167 mergetoolopts = cmdutil.mergetoolopts
168 168 similarityopts = cmdutil.similarityopts
169 169 subrepoopts = cmdutil.subrepoopts
170 170 debugrevlogopts = cmdutil.debugrevlogopts
171 171
172 172 # Commands start here, listed alphabetically
173 173
174 174
175 175 @command(
176 176 b'abort',
177 177 dryrunopts,
178 178 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
179 179 helpbasic=True,
180 180 )
181 181 def abort(ui, repo, **opts):
182 182 """abort an unfinished operation (EXPERIMENTAL)
183 183
184 184 Aborts a multistep operation like graft, histedit, rebase, merge,
185 185 and unshelve if they are in an unfinished state.
186 186
187 187 use --dry-run/-n to dry run the command.
188 188 """
189 189 dryrun = opts.get('dry_run')
190 190 abortstate = cmdutil.getunfinishedstate(repo)
191 191 if not abortstate:
192 192 raise error.StateError(_(b'no operation in progress'))
193 193 if not abortstate.abortfunc:
194 194 raise error.InputError(
195 195 (
196 196 _(b"%s in progress but does not support 'hg abort'")
197 197 % (abortstate._opname)
198 198 ),
199 199 hint=abortstate.hint(),
200 200 )
201 201 if dryrun:
202 202 ui.status(
203 203 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
204 204 )
205 205 return
206 206 return abortstate.abortfunc(ui, repo)
207 207
208 208
209 209 @command(
210 210 b'add',
211 211 walkopts + subrepoopts + dryrunopts,
212 212 _(b'[OPTION]... [FILE]...'),
213 213 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
214 214 helpbasic=True,
215 215 inferrepo=True,
216 216 )
217 217 def add(ui, repo, *pats, **opts):
218 218 """add the specified files on the next commit
219 219
220 220 Schedule files to be version controlled and added to the
221 221 repository.
222 222
223 223 The files will be added to the repository at the next commit. To
224 224 undo an add before that, see :hg:`forget`.
225 225
226 226 If no names are given, add all files to the repository (except
227 227 files matching ``.hgignore``).
228 228
229 229 .. container:: verbose
230 230
231 231 Examples:
232 232
233 233 - New (unknown) files are added
234 234 automatically by :hg:`add`::
235 235
236 236 $ ls
237 237 foo.c
238 238 $ hg status
239 239 ? foo.c
240 240 $ hg add
241 241 adding foo.c
242 242 $ hg status
243 243 A foo.c
244 244
245 245 - Specific files to be added can be specified::
246 246
247 247 $ ls
248 248 bar.c foo.c
249 249 $ hg status
250 250 ? bar.c
251 251 ? foo.c
252 252 $ hg add bar.c
253 253 $ hg status
254 254 A bar.c
255 255 ? foo.c
256 256
257 257 Returns 0 if all files are successfully added.
258 258 """
259 259
260 260 with repo.wlock(), repo.dirstate.changing_files(repo):
261 261 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
262 262 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
263 263 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
264 264 return rejected and 1 or 0
265 265
266 266
267 267 @command(
268 268 b'addremove',
269 269 similarityopts + subrepoopts + walkopts + dryrunopts,
270 270 _(b'[OPTION]... [FILE]...'),
271 271 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
272 272 inferrepo=True,
273 273 )
274 274 def addremove(ui, repo, *pats, **opts):
275 275 """add all new files, delete all missing files
276 276
277 277 Add all new files and remove all missing files from the
278 278 repository.
279 279
280 280 Unless names are given, new files are ignored if they match any of
281 281 the patterns in ``.hgignore``. As with add, these changes take
282 282 effect at the next commit.
283 283
284 284 Use the -s/--similarity option to detect renamed files. This
285 285 option takes a percentage between 0 (disabled) and 100 (files must
286 286 be identical) as its parameter. With a parameter greater than 0,
287 287 this compares every removed file with every added file and records
288 288 those similar enough as renames. Detecting renamed files this way
289 289 can be expensive. After using this option, :hg:`status -C` can be
290 290 used to check which files were identified as moved or renamed. If
291 291 not specified, -s/--similarity defaults to 100 and only renames of
292 292 identical files are detected.
293 293
294 294 .. container:: verbose
295 295
296 296 Examples:
297 297
298 298 - A number of files (bar.c and foo.c) are new,
299 299 while foobar.c has been removed (without using :hg:`remove`)
300 300 from the repository::
301 301
302 302 $ ls
303 303 bar.c foo.c
304 304 $ hg status
305 305 ! foobar.c
306 306 ? bar.c
307 307 ? foo.c
308 308 $ hg addremove
309 309 adding bar.c
310 310 adding foo.c
311 311 removing foobar.c
312 312 $ hg status
313 313 A bar.c
314 314 A foo.c
315 315 R foobar.c
316 316
317 317 - A file foobar.c was moved to foo.c without using :hg:`rename`.
318 318 Afterwards, it was edited slightly::
319 319
320 320 $ ls
321 321 foo.c
322 322 $ hg status
323 323 ! foobar.c
324 324 ? foo.c
325 325 $ hg addremove --similarity 90
326 326 removing foobar.c
327 327 adding foo.c
328 328 recording removal of foobar.c as rename to foo.c (94% similar)
329 329 $ hg status -C
330 330 A foo.c
331 331 foobar.c
332 332 R foobar.c
333 333
334 334 Returns 0 if all files are successfully added.
335 335 """
336 336 opts = pycompat.byteskwargs(opts)
337 337 if not opts.get(b'similarity'):
338 338 opts[b'similarity'] = b'100'
339 339 with repo.wlock(), repo.dirstate.changing_files(repo):
340 340 matcher = scmutil.match(repo[None], pats, opts)
341 341 relative = scmutil.anypats(pats, opts)
342 342 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
343 343 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
344 344
345 345
346 346 @command(
347 347 b'annotate|blame',
348 348 [
349 349 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
350 350 (
351 351 b'',
352 352 b'follow',
353 353 None,
354 354 _(b'follow copies/renames and list the filename (DEPRECATED)'),
355 355 ),
356 356 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
357 357 (b'a', b'text', None, _(b'treat all files as text')),
358 358 (b'u', b'user', None, _(b'list the author (long with -v)')),
359 359 (b'f', b'file', None, _(b'list the filename')),
360 360 (b'd', b'date', None, _(b'list the date (short with -q)')),
361 361 (b'n', b'number', None, _(b'list the revision number (default)')),
362 362 (b'c', b'changeset', None, _(b'list the changeset')),
363 363 (
364 364 b'l',
365 365 b'line-number',
366 366 None,
367 367 _(b'show line number at the first appearance'),
368 368 ),
369 369 (
370 370 b'',
371 371 b'skip',
372 372 [],
373 373 _(b'revset to not display (EXPERIMENTAL)'),
374 374 _(b'REV'),
375 375 ),
376 376 (
377 377 b'L',
378 378 b'line-range',
379 379 [],
380 380 _(b'follow line range of specified file (EXPERIMENTAL)'),
381 381 _(b'FILE,RANGE'),
382 382 ),
383 383 ]
384 384 + diffwsopts
385 385 + walkopts
386 386 + formatteropts,
387 387 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
388 388 helpcategory=command.CATEGORY_FILE_CONTENTS,
389 389 helpbasic=True,
390 390 inferrepo=True,
391 391 )
392 392 def annotate(ui, repo, *pats, **opts):
393 393 """show changeset information by line for each file
394 394
395 395 List changes in files, showing the revision id responsible for
396 396 each line.
397 397
398 398 This command is useful for discovering when a change was made and
399 399 by whom.
400 400
401 401 If you include --file, --user, or --date, the revision number is
402 402 suppressed unless you also include --number.
403 403
404 404 Without the -a/--text option, annotate will avoid processing files
405 405 it detects as binary. With -a, annotate will annotate the file
406 406 anyway, although the results will probably be neither useful
407 407 nor desirable.
408 408
409 409 .. container:: verbose
410 410
411 411 Use -L/--line-range FILE,M:N options to filter the output to the lines
412 412 from M to N in FILE. This option is incompatible with --no-follow and
413 413 cannot be combined with file pattern arguments. When combined with --rev
414 414 the line ranges refer to the state of the file at the requested revision.
415 415
416 416 .. container:: verbose
417 417
418 418 Template:
419 419
420 420 The following keywords are supported in addition to the common template
421 421 keywords and functions. See also :hg:`help templates`.
422 422
423 423 :lines: List of lines with annotation data.
424 424 :path: String. Repository-absolute path of the specified file.
425 425
426 426 And each entry of ``{lines}`` provides the following sub-keywords in
427 427 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
428 428
429 429 :line: String. Line content.
430 430 :lineno: Integer. Line number at that revision.
431 431 :path: String. Repository-absolute path of the file at that revision.
432 432
433 433 See :hg:`help templates.operators` for the list expansion syntax.
434 434
435 435 Returns 0 on success.
436 436 """
437 437 opts = pycompat.byteskwargs(opts)
438 438
439 439 linerange = opts.get(b'line_range')
440 440
441 441 if linerange and opts.get(b'no_follow'):
442 442 raise error.InputError(
443 443 _(b'--line-range is incompatible with --no-follow')
444 444 )
445 445
446 446 if pats and linerange:
447 447 raise error.InputError(
448 448 _(b'cannot combine filename or pattern and --line-range')
449 449 )
450 450
451 451 if not pats and not linerange:
452 452 raise error.InputError(
453 453 _(b'at least one filename or pattern is required')
454 454 )
455 455
456 456 if opts.get(b'follow'):
457 457 # --follow is deprecated and now just an alias for -f/--file
458 458 # to mimic the behavior of Mercurial before version 1.5
459 459 opts[b'file'] = True
460 460
461 461 if (
462 462 not opts.get(b'user')
463 463 and not opts.get(b'changeset')
464 464 and not opts.get(b'date')
465 465 and not opts.get(b'file')
466 466 ):
467 467 opts[b'number'] = True
468 468
469 469 linenumber = opts.get(b'line_number') is not None
470 470 if (
471 471 linenumber
472 472 and (not opts.get(b'changeset'))
473 473 and (not opts.get(b'number'))
474 474 ):
475 475 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
476 476
477 477 rev = opts.get(b'rev')
478 478 if rev:
479 479 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
480 480 ctx = logcmdutil.revsingle(repo, rev)
481 481
482 482 if not pats:
483 483 pats = [
484 484 fname
485 485 for fname, _ranges in logcmdutil._parselinerangeopt(repo, opts)
486 486 ]
487 487
488 488 ui.pager(b'annotate')
489 489 rootfm = ui.formatter(b'annotate', opts)
490 490 if ui.debugflag:
491 491 shorthex = pycompat.identity
492 492 else:
493 493
494 494 def shorthex(h):
495 495 return h[:12]
496 496
497 497 if ui.quiet:
498 498 datefunc = dateutil.shortdate
499 499 else:
500 500 datefunc = dateutil.datestr
501 501 if ctx.rev() is None:
502 502 if opts.get(b'changeset'):
503 503 # omit "+" suffix which is appended to node hex
504 504 def formatrev(rev):
505 505 if rev == wdirrev:
506 506 return b'%d' % ctx.p1().rev()
507 507 else:
508 508 return b'%d' % rev
509 509
510 510 else:
511 511
512 512 def formatrev(rev):
513 513 if rev == wdirrev:
514 514 return b'%d+' % ctx.p1().rev()
515 515 else:
516 516 return b'%d ' % rev
517 517
518 518 def formathex(h):
519 519 if h == repo.nodeconstants.wdirhex:
520 520 return b'%s+' % shorthex(hex(ctx.p1().node()))
521 521 else:
522 522 return b'%s ' % shorthex(h)
523 523
524 524 else:
525 525 formatrev = b'%d'.__mod__
526 526 formathex = shorthex
527 527
528 528 opmap = [
529 529 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
530 530 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
531 531 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
532 532 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
533 533 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
534 534 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
535 535 ]
536 536 opnamemap = {
537 537 b'rev': b'number',
538 538 b'node': b'changeset',
539 539 b'path': b'file',
540 540 b'lineno': b'line_number',
541 541 }
542 542
543 543 if rootfm.isplain():
544 544
545 545 def makefunc(get, fmt):
546 546 return lambda x: fmt(get(x))
547 547
548 548 else:
549 549
550 550 def makefunc(get, fmt):
551 551 return get
552 552
553 553 datahint = rootfm.datahint()
554 554 funcmap = [
555 555 (makefunc(get, fmt), sep)
556 556 for fn, sep, get, fmt in opmap
557 557 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
558 558 ]
559 559 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
560 560 fields = b' '.join(
561 561 fn
562 562 for fn, sep, get, fmt in opmap
563 563 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
564 564 )
565 565
566 566 def bad(x, y):
567 567 raise error.InputError(b"%s: %s" % (x, y))
568 568
569 569 m = scmutil.match(ctx, pats, opts, badfn=bad)
570 570
571 571 follow = not opts.get(b'no_follow')
572 572 diffopts = patch.difffeatureopts(
573 573 ui, opts, section=b'annotate', whitespace=True
574 574 )
575 575 skiprevs = opts.get(b'skip')
576 576 if skiprevs:
577 577 skiprevs = logcmdutil.revrange(repo, skiprevs)
578 578
579 579 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
580 580 for abs in ctx.walk(m):
581 581 fctx = ctx[abs]
582 582 rootfm.startitem()
583 583 rootfm.data(path=abs)
584 584 if not opts.get(b'text') and fctx.isbinary():
585 585 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
586 586 continue
587 587
588 588 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
589 589 lines = fctx.annotate(
590 590 follow=follow, skiprevs=skiprevs, diffopts=diffopts
591 591 )
592 592 if linerange:
593 593 _fname, (line_start, line_end) = list(
594 594 logcmdutil._parselinerangeopt(repo, opts)
595 595 )[0]
596 596 lines = [
597 597 line
598 598 for no, line in enumerate(lines)
599 599 if line_start <= no < line_end
600 600 ]
601 601
602 602 if not lines:
603 603 fm.end()
604 604 continue
605 605 formats = []
606 606 pieces = []
607 607
608 608 for f, sep in funcmap:
609 609 l = [f(n) for n in lines]
610 610 if fm.isplain():
611 611 sizes = [encoding.colwidth(x) for x in l]
612 612 ml = max(sizes)
613 613 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
614 614 else:
615 615 formats.append([b'%s'] * len(l))
616 616 pieces.append(l)
617 617
618 618 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
619 619 fm.startitem()
620 620 fm.context(fctx=n.fctx)
621 621 fm.write(fields, b"".join(f), *p)
622 622 if n.skip:
623 623 fmt = b"* %s"
624 624 else:
625 625 fmt = b": %s"
626 626 fm.write(b'line', fmt, n.text)
627 627
628 628 if not lines[-1].text.endswith(b'\n'):
629 629 fm.plain(b'\n')
630 630 fm.end()
631 631
632 632 rootfm.end()
633 633
634 634
635 635 @command(
636 636 b'archive',
637 637 [
638 638 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
639 639 (
640 640 b'p',
641 641 b'prefix',
642 642 b'',
643 643 _(b'directory prefix for files in archive'),
644 644 _(b'PREFIX'),
645 645 ),
646 646 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
647 647 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
648 648 ]
649 649 + subrepoopts
650 650 + walkopts,
651 651 _(b'[OPTION]... DEST'),
652 652 helpcategory=command.CATEGORY_IMPORT_EXPORT,
653 653 )
654 654 def archive(ui, repo, dest, **opts):
655 655 """create an unversioned archive of a repository revision
656 656
657 657 By default, the revision used is the parent of the working
658 658 directory; use -r/--rev to specify a different revision.
659 659
660 660 The archive type is automatically detected based on file
661 661 extension (to override, use -t/--type).
662 662
663 663 .. container:: verbose
664 664
665 665 Examples:
666 666
667 667 - create a zip file containing the 1.0 release::
668 668
669 669 hg archive -r 1.0 project-1.0.zip
670 670
671 671 - create a tarball excluding .hg files::
672 672
673 673 hg archive project.tar.gz -X ".hg*"
674 674
675 675 Valid types are:
676 676
677 677 :``files``: a directory full of files (default)
678 678 :``tar``: tar archive, uncompressed
679 679 :``tbz2``: tar archive, compressed using bzip2
680 680 :``tgz``: tar archive, compressed using gzip
681 681 :``txz``: tar archive, compressed using lzma (only in Python 3)
682 682 :``uzip``: zip archive, uncompressed
683 683 :``zip``: zip archive, compressed using deflate
684 684
685 685 The exact name of the destination archive or directory is given
686 686 using a format string; see :hg:`help export` for details.
687 687
688 688 Each member added to an archive file has a directory prefix
689 689 prepended. Use -p/--prefix to specify a format string for the
690 690 prefix. The default is the basename of the archive, with suffixes
691 691 removed.
692 692
693 693 Returns 0 on success.
694 694 """
695 695
696 696 rev = opts.get('rev')
697 697 if rev:
698 698 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
699 699 ctx = logcmdutil.revsingle(repo, rev)
700 700 if not ctx:
701 701 raise error.InputError(
702 702 _(b'no working directory: please specify a revision')
703 703 )
704 704 node = ctx.node()
705 705 dest = cmdutil.makefilename(ctx, dest)
706 706 if os.path.realpath(dest) == repo.root:
707 707 raise error.InputError(_(b'repository root cannot be destination'))
708 708
709 709 kind = opts.get('type') or archival.guesskind(dest) or b'files'
710 710 prefix = opts.get('prefix')
711 711
712 712 if dest == b'-':
713 713 if kind == b'files':
714 714 raise error.InputError(_(b'cannot archive plain files to stdout'))
715 715 realdest = dest
716 716 dest = lambda: cmdutil.makefileobj(ctx, realdest)
717 717 if not prefix:
718 718 prefix = os.path.basename(repo.root) + b'-%h'
719 719
720 720 prefix = cmdutil.makefilename(ctx, prefix)
721 721 match = scmutil.match(ctx, [], pycompat.byteskwargs(opts))
722 722 archival.archive(
723 723 repo,
724 724 dest,
725 725 node,
726 726 kind,
727 727 not opts.get('no_decode'),
728 728 match,
729 729 prefix,
730 730 subrepos=opts.get('subrepos'),
731 731 )
732 732
733 733
734 734 @command(
735 735 b'backout',
736 736 [
737 737 (
738 738 b'',
739 739 b'merge',
740 740 None,
741 741 _(b'merge with old dirstate parent after backout'),
742 742 ),
743 743 (
744 744 b'',
745 745 b'commit',
746 746 None,
747 747 _(b'commit if no conflicts were encountered (DEPRECATED)'),
748 748 ),
749 749 (b'', b'no-commit', None, _(b'do not commit')),
750 750 (
751 751 b'',
752 752 b'parent',
753 753 b'',
754 754 _(b'parent to choose when backing out merge (DEPRECATED)'),
755 755 _(b'REV'),
756 756 ),
757 757 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
758 758 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
759 759 ]
760 760 + mergetoolopts
761 761 + walkopts
762 762 + commitopts
763 763 + commitopts2,
764 764 _(b'[OPTION]... [-r] REV'),
765 765 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
766 766 )
767 767 def backout(ui, repo, node=None, rev=None, **opts):
768 768 """reverse effect of earlier changeset
769 769
770 770 Prepare a new changeset with the effect of REV undone in the
771 771 current working directory. If no conflicts were encountered,
772 772 it will be committed immediately.
773 773
774 774 If REV is the parent of the working directory, then this new changeset
775 775 is committed automatically (unless --no-commit is specified).
776 776
777 777 .. note::
778 778
779 779 :hg:`backout` cannot be used to fix either an unwanted or
780 780 incorrect merge.
781 781
782 782 .. container:: verbose
783 783
784 784 Examples:
785 785
786 786 - Reverse the effect of the parent of the working directory.
787 787 This backout will be committed immediately::
788 788
789 789 hg backout -r .
790 790
791 791 - Reverse the effect of previous bad revision 23::
792 792
793 793 hg backout -r 23
794 794
795 795 - Reverse the effect of previous bad revision 23 and
796 796 leave changes uncommitted::
797 797
798 798 hg backout -r 23 --no-commit
799 799 hg commit -m "Backout revision 23"
800 800
801 801 By default, the pending changeset will have one parent,
802 802 maintaining a linear history. With --merge, the pending
803 803 changeset will instead have two parents: the old parent of the
804 804 working directory and a new child of REV that simply undoes REV.
805 805
806 806 Before version 1.7, the behavior without --merge was equivalent
807 807 to specifying --merge followed by :hg:`update --clean .` to
808 808 cancel the merge and leave the child of REV as a head to be
809 809 merged separately.
810 810
811 811 See :hg:`help dates` for a list of formats valid for -d/--date.
812 812
813 813 See :hg:`help revert` for a way to restore files to the state
814 814 of another revision.
815 815
816 816 Returns 0 on success, 1 if nothing to backout or there are unresolved
817 817 files.
818 818 """
819 819 with repo.wlock(), repo.lock():
820 820 return _dobackout(ui, repo, node, rev, **opts)
821 821
822 822
823 823 def _dobackout(ui, repo, node=None, rev=None, **opts):
824 824 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
825 825
826 826 if rev and node:
827 827 raise error.InputError(_(b"please specify just one revision"))
828 828
829 829 if not rev:
830 830 rev = node
831 831
832 832 if not rev:
833 833 raise error.InputError(_(b"please specify a revision to backout"))
834 834
835 835 date = opts.get('date')
836 836 if date:
837 837 opts['date'] = dateutil.parsedate(date)
838 838
839 839 cmdutil.checkunfinished(repo)
840 840 cmdutil.bailifchanged(repo)
841 841 ctx = logcmdutil.revsingle(repo, rev)
842 842 node = ctx.node()
843 843
844 844 op1, op2 = repo.dirstate.parents()
845 845 if not repo.changelog.isancestor(node, op1):
846 846 raise error.InputError(
847 847 _(b'cannot backout change that is not an ancestor')
848 848 )
849 849
850 850 p1, p2 = repo.changelog.parents(node)
851 851 if p1 == repo.nullid:
852 852 raise error.InputError(_(b'cannot backout a change with no parents'))
853 853 if p2 != repo.nullid:
854 854 if not opts.get('parent'):
855 855 raise error.InputError(_(b'cannot backout a merge changeset'))
856 856 p = repo.lookup(opts['parent'])
857 857 if p not in (p1, p2):
858 858 raise error.InputError(
859 859 _(b'%s is not a parent of %s') % (short(p), short(node))
860 860 )
861 861 parent = p
862 862 else:
863 863 if opts.get('parent'):
864 864 raise error.InputError(
865 865 _(b'cannot use --parent on non-merge changeset')
866 866 )
867 867 parent = p1
868 868
869 869 # the backout should appear on the same branch
870 870 branch = repo.dirstate.branch()
871 871 bheads = repo.branchheads(branch)
872 872 rctx = scmutil.revsingle(repo, hex(parent))
873 873 if not opts.get('merge') and op1 != node:
874 874 with repo.transaction(b"backout"):
875 875 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
876 876 with ui.configoverride(overrides, b'backout'):
877 877 stats = mergemod.back_out(ctx, parent=repo[parent])
878 878 repo.setparents(op1, op2)
879 879 hg._showstats(repo, stats)
880 880 if stats.unresolvedcount:
881 881 repo.ui.status(
882 882 _(b"use 'hg resolve' to retry unresolved file merges\n")
883 883 )
884 884 return 1
885 885 else:
886 886 hg.clean(repo, node, show_stats=False)
887 887 repo.dirstate.setbranch(branch, repo.currenttransaction())
888 888 cmdutil.revert(ui, repo, rctx)
889 889
890 890 if opts.get('no_commit'):
891 891 msg = _(b"changeset %s backed out, don't forget to commit.\n")
892 892 ui.status(msg % short(node))
893 893 return 0
894 894
895 895 def commitfunc(ui, repo, message, match, opts):
896 896 editform = b'backout'
897 897 e = cmdutil.getcommiteditor(
898 898 editform=editform, **pycompat.strkwargs(opts)
899 899 )
900 900 if not message:
901 901 # we don't translate commit messages
902 902 message = b"Backed out changeset %s" % short(node)
903 903 e = cmdutil.getcommiteditor(edit=True, editform=editform)
904 904 return repo.commit(
905 905 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
906 906 )
907 907
908 908 # save to detect changes
909 909 tip = repo.changelog.tip()
910 910
911 911 newnode = cmdutil.commit(
912 912 ui, repo, commitfunc, [], pycompat.byteskwargs(opts)
913 913 )
914 914 if not newnode:
915 915 ui.status(_(b"nothing changed\n"))
916 916 return 1
917 917 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
918 918
919 919 def nice(node):
920 920 return b'%d:%s' % (repo.changelog.rev(node), short(node))
921 921
922 922 ui.status(
923 923 _(b'changeset %s backs out changeset %s\n')
924 924 % (nice(newnode), nice(node))
925 925 )
926 926 if opts.get('merge') and op1 != node:
927 927 hg.clean(repo, op1, show_stats=False)
928 928 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
929 929 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
930 930 with ui.configoverride(overrides, b'backout'):
931 931 return hg.merge(repo[b'tip'])
932 932 return 0
933 933
934 934
935 935 @command(
936 936 b'bisect',
937 937 [
938 938 (b'r', b'reset', False, _(b'reset bisect state')),
939 939 (b'g', b'good', False, _(b'mark changeset good')),
940 940 (b'b', b'bad', False, _(b'mark changeset bad')),
941 941 (b's', b'skip', False, _(b'skip testing changeset')),
942 942 (b'e', b'extend', False, _(b'extend the bisect range')),
943 943 (
944 944 b'c',
945 945 b'command',
946 946 b'',
947 947 _(b'use command to check changeset state'),
948 948 _(b'CMD'),
949 949 ),
950 950 (b'U', b'noupdate', False, _(b'do not update to target')),
951 951 ],
952 952 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
953 953 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
954 954 )
955 955 def bisect(
956 956 ui,
957 957 repo,
958 958 positional_1=None,
959 959 positional_2=None,
960 960 command=None,
961 961 reset=None,
962 962 good=None,
963 963 bad=None,
964 964 skip=None,
965 965 extend=None,
966 966 noupdate=None,
967 967 ):
968 968 """subdivision search of changesets
969 969
970 970 This command helps to find changesets which introduce problems. To
971 971 use, mark the earliest changeset you know exhibits the problem as
972 972 bad, then mark the latest changeset which is free from the problem
973 973 as good. Bisect will update your working directory to a revision
974 974 for testing (unless the -U/--noupdate option is specified). Once
975 975 you have performed tests, mark the working directory as good or
976 976 bad, and bisect will either update to another candidate changeset
977 977 or announce that it has found the bad revision.
978 978
979 979 As a shortcut, you can also use the revision argument to mark a
980 980 revision as good or bad without checking it out first.
981 981
982 982 If you supply a command, it will be used for automatic bisection.
983 983 The environment variable HG_NODE will contain the ID of the
984 984 changeset being tested. The exit status of the command will be
985 985 used to mark revisions as good or bad: status 0 means good, 125
986 986 means to skip the revision, 127 (command not found) will abort the
987 987 bisection, and any other non-zero exit status means the revision
988 988 is bad.
989 989
990 990 .. container:: verbose
991 991
992 992 Some examples:
993 993
994 994 - start a bisection with known bad revision 34, and good revision 12::
995 995
996 996 hg bisect --bad 34
997 997 hg bisect --good 12
998 998
999 999 - advance the current bisection by marking current revision as good or
1000 1000 bad::
1001 1001
1002 1002 hg bisect --good
1003 1003 hg bisect --bad
1004 1004
1005 1005 - mark the current revision, or a known revision, to be skipped (e.g. if
1006 1006 that revision is not usable because of another issue)::
1007 1007
1008 1008 hg bisect --skip
1009 1009 hg bisect --skip 23
1010 1010
1011 1011 - skip all revisions that do not touch directories ``foo`` or ``bar``::
1012 1012
1013 1013 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
1014 1014
1015 1015 - forget the current bisection::
1016 1016
1017 1017 hg bisect --reset
1018 1018
1019 1019 - use 'make && make tests' to automatically find the first broken
1020 1020 revision::
1021 1021
1022 1022 hg bisect --reset
1023 1023 hg bisect --bad 34
1024 1024 hg bisect --good 12
1025 1025 hg bisect --command "make && make tests"
1026 1026
1027 1027 - see all changesets whose states are already known in the current
1028 1028 bisection::
1029 1029
1030 1030 hg log -r "bisect(pruned)"
1031 1031
1032 1032 - see the changeset currently being bisected (especially useful
1033 1033 if running with -U/--noupdate)::
1034 1034
1035 1035 hg log -r "bisect(current)"
1036 1036
1037 1037 - see all changesets that took part in the current bisection::
1038 1038
1039 1039 hg log -r "bisect(range)"
1040 1040
1041 1041 - you can even get a nice graph::
1042 1042
1043 1043 hg log --graph -r "bisect(range)"
1044 1044
1045 1045 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
1046 1046
1047 1047 Returns 0 on success.
1048 1048 """
1049 1049 rev = []
1050 1050 # backward compatibility
1051 1051 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1052 1052 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1053 1053 cmd = positional_1
1054 1054 rev.append(positional_2)
1055 1055 if cmd == b"good":
1056 1056 good = True
1057 1057 elif cmd == b"bad":
1058 1058 bad = True
1059 1059 else:
1060 1060 reset = True
1061 1061 elif positional_2:
1062 1062 raise error.InputError(_(b'incompatible arguments'))
1063 1063 elif positional_1 is not None:
1064 1064 rev.append(positional_1)
1065 1065
1066 1066 incompatibles = {
1067 1067 b'--bad': bad,
1068 1068 b'--command': bool(command),
1069 1069 b'--extend': extend,
1070 1070 b'--good': good,
1071 1071 b'--reset': reset,
1072 1072 b'--skip': skip,
1073 1073 }
1074 1074
1075 1075 enabled = [x for x in incompatibles if incompatibles[x]]
1076 1076
1077 1077 if len(enabled) > 1:
1078 1078 raise error.InputError(
1079 1079 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1080 1080 )
1081 1081
1082 1082 if reset:
1083 1083 hbisect.resetstate(repo)
1084 1084 return
1085 1085
1086 1086 state = hbisect.load_state(repo)
1087 1087
1088 1088 if rev:
1089 1089 revs = logcmdutil.revrange(repo, rev)
1090 1090 goodnodes = state[b'good']
1091 1091 badnodes = state[b'bad']
1092 1092 if goodnodes and badnodes:
1093 1093 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1094 1094 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1095 1095 revs = candidates & revs
1096 1096 nodes = [repo.changelog.node(i) for i in revs]
1097 1097 else:
1098 1098 nodes = [repo.lookup(b'.')]
1099 1099
1100 1100 # update state
1101 1101 if good or bad or skip:
1102 1102 if good:
1103 1103 state[b'good'] += nodes
1104 1104 elif bad:
1105 1105 state[b'bad'] += nodes
1106 1106 elif skip:
1107 1107 state[b'skip'] += nodes
1108 1108 hbisect.save_state(repo, state)
1109 1109 if not (state[b'good'] and state[b'bad']):
1110 1110 return
1111 1111
1112 1112 def mayupdate(repo, node, show_stats=True):
1113 1113 """common used update sequence"""
1114 1114 if noupdate:
1115 1115 return
1116 1116 cmdutil.checkunfinished(repo)
1117 1117 cmdutil.bailifchanged(repo)
1118 1118 return hg.clean(repo, node, show_stats=show_stats)
1119 1119
1120 1120 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1121 1121
1122 1122 if command:
1123 1123 changesets = 1
1124 1124 if noupdate:
1125 1125 try:
1126 1126 node = state[b'current'][0]
1127 1127 except LookupError:
1128 1128 raise error.StateError(
1129 1129 _(
1130 1130 b'current bisect revision is unknown - '
1131 1131 b'start a new bisect to fix'
1132 1132 )
1133 1133 )
1134 1134 else:
1135 1135 node, p2 = repo.dirstate.parents()
1136 1136 if p2 != repo.nullid:
1137 1137 raise error.StateError(_(b'current bisect revision is a merge'))
1138 1138 if rev:
1139 1139 if not nodes:
1140 1140 raise error.InputError(_(b'empty revision set'))
1141 1141 node = repo[nodes[-1]].node()
1142 1142 with hbisect.restore_state(repo, state, node):
1143 1143 while changesets:
1144 1144 # update state
1145 1145 state[b'current'] = [node]
1146 1146 hbisect.save_state(repo, state)
1147 1147 status = ui.system(
1148 1148 command,
1149 1149 environ={b'HG_NODE': hex(node)},
1150 1150 blockedtag=b'bisect_check',
1151 1151 )
1152 1152 if status == 125:
1153 1153 transition = b"skip"
1154 1154 elif status == 0:
1155 1155 transition = b"good"
1156 1156 # status < 0 means process was killed
1157 1157 elif status == 127:
1158 1158 raise error.Abort(_(b"failed to execute %s") % command)
1159 1159 elif status < 0:
1160 1160 raise error.Abort(_(b"%s killed") % command)
1161 1161 else:
1162 1162 transition = b"bad"
1163 1163 state[transition].append(node)
1164 1164 ctx = repo[node]
1165 1165 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1166 1166 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1167 1167 hbisect.checkstate(state)
1168 1168 # bisect
1169 1169 nodes, changesets, bgood = hbisect.bisect(repo, state)
1170 1170 # update to next check
1171 1171 node = nodes[0]
1172 1172 mayupdate(repo, node, show_stats=False)
1173 1173 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1174 1174 return
1175 1175
1176 1176 hbisect.checkstate(state)
1177 1177
1178 1178 # actually bisect
1179 1179 nodes, changesets, good = hbisect.bisect(repo, state)
1180 1180 if extend:
1181 1181 if not changesets:
1182 1182 extendctx = hbisect.extendrange(repo, state, nodes, good)
1183 1183 if extendctx is not None:
1184 1184 ui.write(
1185 1185 _(b"Extending search to changeset %s\n")
1186 1186 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1187 1187 )
1188 1188 state[b'current'] = [extendctx.node()]
1189 1189 hbisect.save_state(repo, state)
1190 1190 return mayupdate(repo, extendctx.node())
1191 1191 raise error.StateError(_(b"nothing to extend"))
1192 1192
1193 1193 if changesets == 0:
1194 1194 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1195 1195 else:
1196 1196 assert len(nodes) == 1 # only a single node can be tested next
1197 1197 node = nodes[0]
1198 1198 # compute the approximate number of remaining tests
1199 1199 tests, size = 0, 2
1200 1200 while size <= changesets:
1201 1201 tests, size = tests + 1, size * 2
1202 1202 rev = repo.changelog.rev(node)
1203 1203 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1204 1204 ui.write(
1205 1205 _(
1206 1206 b"Testing changeset %s "
1207 1207 b"(%d changesets remaining, ~%d tests)\n"
1208 1208 )
1209 1209 % (summary, changesets, tests)
1210 1210 )
1211 1211 state[b'current'] = [node]
1212 1212 hbisect.save_state(repo, state)
1213 1213 return mayupdate(repo, node)
1214 1214
1215 1215
1216 1216 @command(
1217 1217 b'bookmarks|bookmark',
1218 1218 [
1219 1219 (b'f', b'force', False, _(b'force')),
1220 1220 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1221 1221 (b'd', b'delete', False, _(b'delete a given bookmark')),
1222 1222 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1223 1223 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1224 1224 (b'l', b'list', False, _(b'list existing bookmarks')),
1225 1225 ]
1226 1226 + formatteropts,
1227 1227 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1228 1228 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1229 1229 )
1230 1230 def bookmark(ui, repo, *names, **opts):
1231 1231 """create a new bookmark or list existing bookmarks
1232 1232
1233 1233 Bookmarks are labels on changesets to help track lines of development.
1234 1234 Bookmarks are unversioned and can be moved, renamed and deleted.
1235 1235 Deleting or moving a bookmark has no effect on the associated changesets.
1236 1236
1237 1237 Creating or updating to a bookmark causes it to be marked as 'active'.
1238 1238 The active bookmark is indicated with a '*'.
1239 1239 When a commit is made, the active bookmark will advance to the new commit.
1240 1240 A plain :hg:`update` will also advance an active bookmark, if possible.
1241 1241 Updating away from a bookmark will cause it to be deactivated.
1242 1242
1243 1243 Bookmarks can be pushed and pulled between repositories (see
1244 1244 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1245 1245 diverged, a new 'divergent bookmark' of the form 'name@path' will
1246 1246 be created. Using :hg:`merge` will resolve the divergence.
1247 1247
1248 1248 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1249 1249 the active bookmark's name.
1250 1250
1251 1251 A bookmark named '@' has the special property that :hg:`clone` will
1252 1252 check it out by default if it exists.
1253 1253
1254 1254 .. container:: verbose
1255 1255
1256 1256 Template:
1257 1257
1258 1258 The following keywords are supported in addition to the common template
1259 1259 keywords and functions such as ``{bookmark}``. See also
1260 1260 :hg:`help templates`.
1261 1261
1262 1262 :active: Boolean. True if the bookmark is active.
1263 1263
1264 1264 Examples:
1265 1265
1266 1266 - create an active bookmark for a new line of development::
1267 1267
1268 1268 hg book new-feature
1269 1269
1270 1270 - create an inactive bookmark as a place marker::
1271 1271
1272 1272 hg book -i reviewed
1273 1273
1274 1274 - create an inactive bookmark on another changeset::
1275 1275
1276 1276 hg book -r .^ tested
1277 1277
1278 1278 - rename bookmark turkey to dinner::
1279 1279
1280 1280 hg book -m turkey dinner
1281 1281
1282 1282 - move the '@' bookmark from another branch::
1283 1283
1284 1284 hg book -f @
1285 1285
1286 1286 - print only the active bookmark name::
1287 1287
1288 1288 hg book -ql .
1289 1289 """
1290 1290 force = opts.get('force')
1291 1291 rev = opts.get('rev')
1292 1292 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1293 1293
1294 1294 action = cmdutil.check_at_most_one_arg(opts, 'delete', 'rename', 'list')
1295 1295 if action:
1296 1296 cmdutil.check_incompatible_arguments(opts, action, ['rev'])
1297 1297 elif names or rev:
1298 1298 action = 'add'
1299 1299 elif inactive:
1300 1300 action = 'inactive' # meaning deactivate
1301 1301 else:
1302 1302 action = 'list'
1303 1303
1304 1304 cmdutil.check_incompatible_arguments(opts, 'inactive', ['delete', 'list'])
1305 1305 if not names and action in {'add', 'delete'}:
1306 1306 raise error.InputError(_(b"bookmark name required"))
1307 1307
1308 1308 if action in {'add', 'delete', 'rename', 'inactive'}:
1309 1309 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1310 1310 if action == 'delete':
1311 1311 names = pycompat.maplist(repo._bookmarks.expandname, names)
1312 1312 bookmarks.delete(repo, tr, names)
1313 1313 elif action == 'rename':
1314 1314 if not names:
1315 1315 raise error.InputError(_(b"new bookmark name required"))
1316 1316 elif len(names) > 1:
1317 1317 raise error.InputError(
1318 1318 _(b"only one new bookmark name allowed")
1319 1319 )
1320 1320 oldname = repo._bookmarks.expandname(opts['rename'])
1321 1321 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1322 1322 elif action == 'add':
1323 1323 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1324 1324 elif action == 'inactive':
1325 1325 if len(repo._bookmarks) == 0:
1326 1326 ui.status(_(b"no bookmarks set\n"))
1327 1327 elif not repo._activebookmark:
1328 1328 ui.status(_(b"no active bookmark\n"))
1329 1329 else:
1330 1330 bookmarks.deactivate(repo)
1331 1331 elif action == 'list':
1332 1332 names = pycompat.maplist(repo._bookmarks.expandname, names)
1333 1333 with ui.formatter(b'bookmarks', pycompat.byteskwargs(opts)) as fm:
1334 1334 bookmarks.printbookmarks(ui, repo, fm, names)
1335 1335 else:
1336 1336 raise error.ProgrammingError(
1337 1337 b'invalid action: %s' % pycompat.sysbytes(action)
1338 1338 )
1339 1339
1340 1340
1341 1341 @command(
1342 1342 b'branch',
1343 1343 [
1344 1344 (
1345 1345 b'f',
1346 1346 b'force',
1347 1347 None,
1348 1348 _(b'set branch name even if it shadows an existing branch'),
1349 1349 ),
1350 1350 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1351 1351 (
1352 1352 b'r',
1353 1353 b'rev',
1354 1354 [],
1355 1355 _(b'change branches of the given revs (EXPERIMENTAL)'),
1356 1356 ),
1357 1357 ],
1358 1358 _(b'[-fC] [NAME]'),
1359 1359 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1360 1360 )
1361 1361 def branch(ui, repo, label=None, **opts):
1362 1362 """set or show the current branch name
1363 1363
1364 1364 .. note::
1365 1365
1366 1366 Branch names are permanent and global. Use :hg:`bookmark` to create a
1367 1367 light-weight bookmark instead. See :hg:`help glossary` for more
1368 1368 information about named branches and bookmarks.
1369 1369
1370 1370 With no argument, show the current branch name. With one argument,
1371 1371 set the working directory branch name (the branch will not exist
1372 1372 in the repository until the next commit). Standard practice
1373 1373 recommends that primary development take place on the 'default'
1374 1374 branch.
1375 1375
1376 1376 Unless -f/--force is specified, branch will not let you set a
1377 1377 branch name that already exists.
1378 1378
1379 1379 Use -C/--clean to reset the working directory branch to that of
1380 1380 the parent of the working directory, negating a previous branch
1381 1381 change.
1382 1382
1383 1383 Use the command :hg:`update` to switch to an existing branch. Use
1384 1384 :hg:`commit --close-branch` to mark this branch head as closed.
1385 1385 When all heads of a branch are closed, the branch will be
1386 1386 considered closed.
1387 1387
1388 1388 Returns 0 on success.
1389 1389 """
1390 1390 revs = opts.get('rev')
1391 1391 if label:
1392 1392 label = label.strip()
1393 1393
1394 1394 if not opts.get('clean') and not label:
1395 1395 if revs:
1396 1396 raise error.InputError(
1397 1397 _(b"no branch name specified for the revisions")
1398 1398 )
1399 1399 ui.write(b"%s\n" % repo.dirstate.branch())
1400 1400 return
1401 1401
1402 1402 with repo.wlock():
1403 1403 if opts.get('clean'):
1404 1404 label = repo[b'.'].branch()
1405 1405 repo.dirstate.setbranch(label, repo.currenttransaction())
1406 1406 ui.status(_(b'reset working directory to branch %s\n') % label)
1407 1407 elif label:
1408 1408 scmutil.checknewlabel(repo, label, b'branch')
1409 1409 if revs:
1410 1410 return cmdutil.changebranch(ui, repo, revs, label, **opts)
1411 1411
1412 1412 if not opts.get('force') and label in repo.branchmap():
1413 1413 if label not in [p.branch() for p in repo[None].parents()]:
1414 1414 raise error.InputError(
1415 1415 _(b'a branch of the same name already exists'),
1416 1416 # i18n: "it" refers to an existing branch
1417 1417 hint=_(b"use 'hg update' to switch to it"),
1418 1418 )
1419 1419
1420 1420 repo.dirstate.setbranch(label, repo.currenttransaction())
1421 1421 ui.status(_(b'marked working directory as branch %s\n') % label)
1422 1422
1423 1423 # find any open named branches aside from default
1424 1424 for n, h, t, c in repo.branchmap().iterbranches():
1425 1425 if n != b"default" and not c:
1426 1426 return 0
1427 1427 ui.status(
1428 1428 _(
1429 1429 b'(branches are permanent and global, '
1430 1430 b'did you want a bookmark?)\n'
1431 1431 )
1432 1432 )
1433 1433
1434 1434
1435 1435 @command(
1436 1436 b'branches',
1437 1437 [
1438 1438 (
1439 1439 b'a',
1440 1440 b'active',
1441 1441 False,
1442 1442 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1443 1443 ),
1444 1444 (b'c', b'closed', False, _(b'show normal and closed branches')),
1445 1445 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1446 1446 ]
1447 1447 + formatteropts,
1448 1448 _(b'[-c]'),
1449 1449 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1450 1450 intents={INTENT_READONLY},
1451 1451 )
1452 1452 def branches(ui, repo, active=False, closed=False, **opts):
1453 1453 """list repository named branches
1454 1454
1455 1455 List the repository's named branches, indicating which ones are
1456 1456 inactive. If -c/--closed is specified, also list branches which have
1457 1457 been marked closed (see :hg:`commit --close-branch`).
1458 1458
1459 1459 Use the command :hg:`update` to switch to an existing branch.
1460 1460
1461 1461 .. container:: verbose
1462 1462
1463 1463 Template:
1464 1464
1465 1465 The following keywords are supported in addition to the common template
1466 1466 keywords and functions such as ``{branch}``. See also
1467 1467 :hg:`help templates`.
1468 1468
1469 1469 :active: Boolean. True if the branch is active.
1470 1470 :closed: Boolean. True if the branch is closed.
1471 1471 :current: Boolean. True if it is the current branch.
1472 1472
1473 1473 Returns 0.
1474 1474 """
1475 1475
1476 1476 revs = opts.get('rev')
1477 1477 selectedbranches = None
1478 1478 if revs:
1479 1479 revs = logcmdutil.revrange(repo, revs)
1480 1480 getbi = repo.revbranchcache().branchinfo
1481 1481 selectedbranches = {getbi(r)[0] for r in revs}
1482 1482
1483 1483 ui.pager(b'branches')
1484 1484 fm = ui.formatter(b'branches', pycompat.byteskwargs(opts))
1485 1485 hexfunc = fm.hexfunc
1486 1486
1487 1487 allheads = set(repo.heads())
1488 1488 branches = []
1489 1489 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1490 1490 if selectedbranches is not None and tag not in selectedbranches:
1491 1491 continue
1492 1492 isactive = False
1493 1493 if not isclosed:
1494 1494 openheads = set(repo.branchmap().iteropen(heads))
1495 1495 isactive = bool(openheads & allheads)
1496 1496 branches.append((tag, repo[tip], isactive, not isclosed))
1497 1497 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1498 1498
1499 1499 for tag, ctx, isactive, isopen in branches:
1500 1500 if active and not isactive:
1501 1501 continue
1502 1502 if isactive:
1503 1503 label = b'branches.active'
1504 1504 notice = b''
1505 1505 elif not isopen:
1506 1506 if not closed:
1507 1507 continue
1508 1508 label = b'branches.closed'
1509 1509 notice = _(b' (closed)')
1510 1510 else:
1511 1511 label = b'branches.inactive'
1512 1512 notice = _(b' (inactive)')
1513 1513 current = tag == repo.dirstate.branch()
1514 1514 if current:
1515 1515 label = b'branches.current'
1516 1516
1517 1517 fm.startitem()
1518 1518 fm.write(b'branch', b'%s', tag, label=label)
1519 1519 rev = ctx.rev()
1520 1520 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1521 1521 fmt = b' ' * padsize + b' %d:%s'
1522 1522 fm.condwrite(
1523 1523 not ui.quiet,
1524 1524 b'rev node',
1525 1525 fmt,
1526 1526 rev,
1527 1527 hexfunc(ctx.node()),
1528 1528 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1529 1529 )
1530 1530 fm.context(ctx=ctx)
1531 1531 fm.data(active=isactive, closed=not isopen, current=current)
1532 1532 if not ui.quiet:
1533 1533 fm.plain(notice)
1534 1534 fm.plain(b'\n')
1535 1535 fm.end()
1536 1536
1537 1537
1538 1538 @command(
1539 1539 b'bundle',
1540 1540 [
1541 1541 (
1542 1542 b'',
1543 1543 b'exact',
1544 1544 None,
1545 1545 _(b'compute the base from the revision specified'),
1546 1546 ),
1547 1547 (
1548 1548 b'f',
1549 1549 b'force',
1550 1550 None,
1551 1551 _(b'run even when the destination is unrelated'),
1552 1552 ),
1553 1553 (
1554 1554 b'r',
1555 1555 b'rev',
1556 1556 [],
1557 1557 _(b'a changeset intended to be added to the destination'),
1558 1558 _(b'REV'),
1559 1559 ),
1560 1560 (
1561 1561 b'b',
1562 1562 b'branch',
1563 1563 [],
1564 1564 _(b'a specific branch you would like to bundle'),
1565 1565 _(b'BRANCH'),
1566 1566 ),
1567 1567 (
1568 1568 b'',
1569 1569 b'base',
1570 1570 [],
1571 1571 _(b'a base changeset assumed to be available at the destination'),
1572 1572 _(b'REV'),
1573 1573 ),
1574 1574 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1575 1575 (
1576 1576 b't',
1577 1577 b'type',
1578 1578 b'bzip2',
1579 1579 _(b'bundle compression type to use'),
1580 1580 _(b'TYPE'),
1581 1581 ),
1582 1582 ]
1583 1583 + remoteopts,
1584 1584 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1585 1585 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1586 1586 )
1587 1587 def bundle(ui, repo, fname, *dests, **opts):
1588 1588 """create a bundle file
1589 1589
1590 1590 Generate a bundle file containing data to be transferred to another
1591 1591 repository.
1592 1592
1593 1593 To create a bundle containing all changesets, use -a/--all
1594 1594 (or --base null). Otherwise, hg assumes the destination will have
1595 1595 all the nodes you specify with --base parameters. Otherwise, hg
1596 1596 will assume the repository has all the nodes in destination, or
1597 1597 default-push/default if no destination is specified, where destination
1598 1598 is the repositories you provide through DEST option.
1599 1599
1600 1600 You can change bundle format with the -t/--type option. See
1601 1601 :hg:`help bundlespec` for documentation on this format. By default,
1602 1602 the most appropriate format is used and compression defaults to
1603 1603 bzip2.
1604 1604
1605 1605 The bundle file can then be transferred using conventional means
1606 1606 and applied to another repository with the unbundle or pull
1607 1607 command. This is useful when direct push and pull are not
1608 1608 available or when exporting an entire repository is undesirable.
1609 1609
1610 1610 Applying bundles preserves all changeset contents including
1611 1611 permissions, copy/rename information, and revision history.
1612 1612
1613 1613 Returns 0 on success, 1 if no changes found.
1614 1614 """
1615 1615
1616 1616 revs = None
1617 1617 if 'rev' in opts:
1618 1618 revstrings = opts['rev']
1619 1619 revs = logcmdutil.revrange(repo, revstrings)
1620 1620 if revstrings and not revs:
1621 1621 raise error.InputError(_(b'no commits to bundle'))
1622 1622
1623 1623 bundletype = opts.get('type', b'bzip2').lower()
1624 1624 try:
1625 1625 bundlespec = bundlecaches.parsebundlespec(
1626 1626 repo, bundletype, strict=False
1627 1627 )
1628 1628 except error.UnsupportedBundleSpecification as e:
1629 1629 raise error.InputError(
1630 1630 pycompat.bytestr(e),
1631 1631 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1632 1632 )
1633 1633
1634 1634 has_changegroup = bundlespec.params.get(b"changegroup", False)
1635 1635 cgversion = bundlespec.params[b"cg.version"]
1636 1636
1637 1637 # Packed bundles are a pseudo bundle format for now.
1638 1638 if cgversion == b's1':
1639 1639 raise error.InputError(
1640 1640 _(b'packed bundles cannot be produced by "hg bundle"'),
1641 1641 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1642 1642 )
1643 1643 base_opt = opts.get('base')
1644 1644 if opts.get('all'):
1645 1645 if dests:
1646 1646 raise error.InputError(
1647 1647 _(b"--all is incompatible with specifying destinations")
1648 1648 )
1649 1649 if base_opt:
1650 1650 ui.warn(_(b"ignoring --base because --all was specified\n"))
1651 1651 if opts.get('exact'):
1652 1652 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1653 1653 base = [nullrev]
1654 1654 elif opts.get('exact'):
1655 1655 if dests:
1656 1656 raise error.InputError(
1657 1657 _(b"--exact is incompatible with specifying destinations")
1658 1658 )
1659 1659 if base_opt:
1660 1660 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1661 1661 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1662 1662 if not base:
1663 1663 base = [nullrev]
1664 1664 elif base_opt:
1665 1665 base = logcmdutil.revrange(repo, base_opt)
1666 1666 if not base:
1667 1667 # base specified, but nothing was selected
1668 1668 base = [nullrev]
1669 1669 else:
1670 1670 base = None
1671 1671 supported_cg_versions = changegroup.supportedoutgoingversions(repo)
1672 1672 if has_changegroup and cgversion not in supported_cg_versions:
1673 1673 raise error.Abort(
1674 1674 _(b"repository does not support bundle version %s") % cgversion
1675 1675 )
1676 1676
1677 1677 if base is not None:
1678 1678 if dests:
1679 1679 raise error.InputError(
1680 1680 _(b"--base is incompatible with specifying destinations")
1681 1681 )
1682 1682 cl = repo.changelog
1683 1683 common = [cl.node(rev) for rev in base]
1684 1684 heads = [cl.node(r) for r in revs] if revs else None
1685 1685 outgoing = discovery.outgoing(repo, common, heads)
1686 1686 missing = outgoing.missing
1687 1687 excluded = outgoing.excluded
1688 1688 else:
1689 1689 missing = set()
1690 1690 excluded = set()
1691 1691 for path in urlutil.get_push_paths(repo, ui, dests):
1692 1692 other = hg.peer(repo, pycompat.byteskwargs(opts), path)
1693 1693 if revs is not None:
1694 1694 hex_revs = [repo[r].hex() for r in revs]
1695 1695 else:
1696 1696 hex_revs = None
1697 1697 branches = (path.branch, [])
1698 1698 head_revs, checkout = hg.addbranchrevs(
1699 1699 repo, repo, branches, hex_revs
1700 1700 )
1701 1701 heads = (
1702 1702 head_revs
1703 1703 and pycompat.maplist(repo.lookup, head_revs)
1704 1704 or head_revs
1705 1705 )
1706 1706 outgoing = discovery.findcommonoutgoing(
1707 1707 repo,
1708 1708 other,
1709 1709 onlyheads=heads,
1710 1710 force=opts.get('force'),
1711 1711 portable=True,
1712 1712 )
1713 1713 missing.update(outgoing.missing)
1714 1714 excluded.update(outgoing.excluded)
1715 1715
1716 1716 if not missing:
1717 1717 scmutil.nochangesfound(ui, repo, not base and excluded)
1718 1718 return 1
1719 1719
1720 1720 # internal changeset are internal implementation details that should not
1721 1721 # leave the repository. Bundling with `hg bundle` create such risk.
1722 1722 bundled_internal = repo.revs(b"%ln and _internal()", missing)
1723 1723 if bundled_internal:
1724 1724 msg = _(b"cannot bundle internal changesets")
1725 1725 hint = _(b"%d internal changesets selected") % len(bundled_internal)
1726 1726 raise error.Abort(msg, hint=hint)
1727 1727
1728 1728 if heads:
1729 1729 outgoing = discovery.outgoing(
1730 1730 repo, missingroots=missing, ancestorsof=heads
1731 1731 )
1732 1732 else:
1733 1733 outgoing = discovery.outgoing(repo, missingroots=missing)
1734 1734 outgoing.excluded = sorted(excluded)
1735 1735
1736 1736 if cgversion == b'01': # bundle1
1737 1737 bversion = b'HG10' + bundlespec.wirecompression
1738 1738 bcompression = None
1739 1739 elif cgversion in (b'02', b'03'):
1740 1740 bversion = b'HG20'
1741 1741 bcompression = bundlespec.wirecompression
1742 1742 else:
1743 1743 raise error.ProgrammingError(
1744 1744 b'bundle: unexpected changegroup version %s' % cgversion
1745 1745 )
1746 1746
1747 1747 # TODO compression options should be derived from bundlespec parsing.
1748 1748 # This is a temporary hack to allow adjusting bundle compression
1749 1749 # level without a) formalizing the bundlespec changes to declare it
1750 1750 # b) introducing a command flag.
1751 1751 compopts = {}
1752 1752 complevel = ui.configint(
1753 1753 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1754 1754 )
1755 1755 if complevel is None:
1756 1756 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1757 1757 if complevel is not None:
1758 1758 compopts[b'level'] = complevel
1759 1759
1760 1760 compthreads = ui.configint(
1761 1761 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1762 1762 )
1763 1763 if compthreads is None:
1764 1764 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1765 1765 if compthreads is not None:
1766 1766 compopts[b'threads'] = compthreads
1767 1767
1768 1768 # Bundling of obsmarker and phases is optional as not all clients
1769 1769 # support the necessary features.
1770 1770 cfg = ui.configbool
1771 1771 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1772 1772 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1773 1773 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1774 1774 bundlespec.set_param(
1775 1775 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1776 1776 )
1777 1777 if not bundlespec.params.get(b'phases', False):
1778 1778 phases_cfg = cfg(b'experimental', b'bundle-phases')
1779 1779 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1780 1780
1781 1781 bundle2.writenewbundle(
1782 1782 ui,
1783 1783 repo,
1784 1784 b'bundle',
1785 1785 fname,
1786 1786 bversion,
1787 1787 outgoing,
1788 1788 bundlespec.params,
1789 1789 compression=bcompression,
1790 1790 compopts=compopts,
1791 1791 )
1792 1792
1793 1793
1794 1794 @command(
1795 1795 b'cat',
1796 1796 [
1797 1797 (
1798 1798 b'o',
1799 1799 b'output',
1800 1800 b'',
1801 1801 _(b'print output to file with formatted name'),
1802 1802 _(b'FORMAT'),
1803 1803 ),
1804 1804 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1805 1805 (b'', b'decode', None, _(b'apply any matching decode filter')),
1806 1806 ]
1807 1807 + walkopts
1808 1808 + formatteropts,
1809 1809 _(b'[OPTION]... FILE...'),
1810 1810 helpcategory=command.CATEGORY_FILE_CONTENTS,
1811 1811 inferrepo=True,
1812 1812 intents={INTENT_READONLY},
1813 1813 )
1814 1814 def cat(ui, repo, file1, *pats, **opts):
1815 1815 """output the current or given revision of files
1816 1816
1817 1817 Print the specified files as they were at the given revision. If
1818 1818 no revision is given, the parent of the working directory is used.
1819 1819
1820 1820 Output may be to a file, in which case the name of the file is
1821 1821 given using a template string. See :hg:`help templates`. In addition
1822 1822 to the common template keywords, the following formatting rules are
1823 1823 supported:
1824 1824
1825 1825 :``%%``: literal "%" character
1826 1826 :``%s``: basename of file being printed
1827 1827 :``%d``: dirname of file being printed, or '.' if in repository root
1828 1828 :``%p``: root-relative path name of file being printed
1829 1829 :``%H``: changeset hash (40 hexadecimal digits)
1830 1830 :``%R``: changeset revision number
1831 1831 :``%h``: short-form changeset hash (12 hexadecimal digits)
1832 1832 :``%r``: zero-padded changeset revision number
1833 1833 :``%b``: basename of the exporting repository
1834 1834 :``\\``: literal "\\" character
1835 1835
1836 1836 .. container:: verbose
1837 1837
1838 1838 Template:
1839 1839
1840 1840 The following keywords are supported in addition to the common template
1841 1841 keywords and functions. See also :hg:`help templates`.
1842 1842
1843 1843 :data: String. File content.
1844 1844 :path: String. Repository-absolute path of the file.
1845 1845
1846 1846 Returns 0 on success.
1847 1847 """
1848 1848 rev = opts.get('rev')
1849 1849 if rev:
1850 1850 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1851 1851 ctx = logcmdutil.revsingle(repo, rev)
1852 1852 m = scmutil.match(ctx, (file1,) + pats, pycompat.byteskwargs(opts))
1853 1853 fntemplate = opts.pop('output', b'')
1854 1854 if cmdutil.isstdiofilename(fntemplate):
1855 1855 fntemplate = b''
1856 1856
1857 1857 if fntemplate:
1858 1858 fm = formatter.nullformatter(ui, b'cat', pycompat.byteskwargs(opts))
1859 1859 else:
1860 1860 ui.pager(b'cat')
1861 1861 fm = ui.formatter(b'cat', pycompat.byteskwargs(opts))
1862 1862 with fm:
1863 1863 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, b'', **opts)
1864 1864
1865 1865
1866 1866 @command(
1867 1867 b'clone',
1868 1868 [
1869 1869 (
1870 1870 b'U',
1871 1871 b'noupdate',
1872 1872 None,
1873 1873 _(
1874 1874 b'the clone will include an empty working '
1875 1875 b'directory (only a repository)'
1876 1876 ),
1877 1877 ),
1878 1878 (
1879 1879 b'u',
1880 1880 b'updaterev',
1881 1881 b'',
1882 1882 _(b'revision, tag, or branch to check out'),
1883 1883 _(b'REV'),
1884 1884 ),
1885 1885 (
1886 1886 b'r',
1887 1887 b'rev',
1888 1888 [],
1889 1889 _(
1890 1890 b'do not clone everything, but include this changeset'
1891 1891 b' and its ancestors'
1892 1892 ),
1893 1893 _(b'REV'),
1894 1894 ),
1895 1895 (
1896 1896 b'b',
1897 1897 b'branch',
1898 1898 [],
1899 1899 _(
1900 1900 b'do not clone everything, but include this branch\'s'
1901 1901 b' changesets and their ancestors'
1902 1902 ),
1903 1903 _(b'BRANCH'),
1904 1904 ),
1905 1905 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1906 1906 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1907 1907 (b'', b'stream', None, _(b'clone with minimal data processing')),
1908 1908 ]
1909 1909 + remoteopts,
1910 1910 _(b'[OPTION]... SOURCE [DEST]'),
1911 1911 helpcategory=command.CATEGORY_REPO_CREATION,
1912 1912 helpbasic=True,
1913 1913 norepo=True,
1914 1914 )
1915 1915 def clone(ui, source, dest=None, **opts):
1916 1916 """make a copy of an existing repository
1917 1917
1918 1918 Create a copy of an existing repository in a new directory.
1919 1919
1920 1920 If no destination directory name is specified, it defaults to the
1921 1921 basename of the source.
1922 1922
1923 1923 The location of the source is added to the new repository's
1924 1924 ``.hg/hgrc`` file, as the default to be used for future pulls.
1925 1925
1926 1926 Only local paths and ``ssh://`` URLs are supported as
1927 1927 destinations. For ``ssh://`` destinations, no working directory or
1928 1928 ``.hg/hgrc`` will be created on the remote side.
1929 1929
1930 1930 If the source repository has a bookmark called '@' set, that
1931 1931 revision will be checked out in the new repository by default.
1932 1932
1933 1933 To check out a particular version, use -u/--update, or
1934 1934 -U/--noupdate to create a clone with no working directory.
1935 1935
1936 1936 To pull only a subset of changesets, specify one or more revisions
1937 1937 identifiers with -r/--rev or branches with -b/--branch. The
1938 1938 resulting clone will contain only the specified changesets and
1939 1939 their ancestors. These options (or 'clone src#rev dest') imply
1940 1940 --pull, even for local source repositories.
1941 1941
1942 1942 In normal clone mode, the remote normalizes repository data into a common
1943 1943 exchange format and the receiving end translates this data into its local
1944 1944 storage format. --stream activates a different clone mode that essentially
1945 1945 copies repository files from the remote with minimal data processing. This
1946 1946 significantly reduces the CPU cost of a clone both remotely and locally.
1947 1947 However, it often increases the transferred data size by 30-40%. This can
1948 1948 result in substantially faster clones where I/O throughput is plentiful,
1949 1949 especially for larger repositories. A side-effect of --stream clones is
1950 1950 that storage settings and requirements on the remote are applied locally:
1951 1951 a modern client may inherit legacy or inefficient storage used by the
1952 1952 remote or a legacy Mercurial client may not be able to clone from a
1953 1953 modern Mercurial remote.
1954 1954
1955 1955 .. note::
1956 1956
1957 1957 Specifying a tag will include the tagged changeset but not the
1958 1958 changeset containing the tag.
1959 1959
1960 1960 .. container:: verbose
1961 1961
1962 1962 For efficiency, hardlinks are used for cloning whenever the
1963 1963 source and destination are on the same filesystem (note this
1964 1964 applies only to the repository data, not to the working
1965 1965 directory). Some filesystems, such as AFS, implement hardlinking
1966 1966 incorrectly, but do not report errors. In these cases, use the
1967 1967 --pull option to avoid hardlinking.
1968 1968
1969 1969 Mercurial will update the working directory to the first applicable
1970 1970 revision from this list:
1971 1971
1972 1972 a) null if -U or the source repository has no changesets
1973 1973 b) if -u . and the source repository is local, the first parent of
1974 1974 the source repository's working directory
1975 1975 c) the changeset specified with -u (if a branch name, this means the
1976 1976 latest head of that branch)
1977 1977 d) the changeset specified with -r
1978 1978 e) the tipmost head specified with -b
1979 1979 f) the tipmost head specified with the url#branch source syntax
1980 1980 g) the revision marked with the '@' bookmark, if present
1981 1981 h) the tipmost head of the default branch
1982 1982 i) tip
1983 1983
1984 1984 When cloning from servers that support it, Mercurial may fetch
1985 1985 pre-generated data from a server-advertised URL or inline from the
1986 1986 same stream. When this is done, hooks operating on incoming changesets
1987 1987 and changegroups may fire more than once, once for each pre-generated
1988 1988 bundle and as well as for any additional remaining data. In addition,
1989 1989 if an error occurs, the repository may be rolled back to a partial
1990 1990 clone. This behavior may change in future releases.
1991 1991 See :hg:`help -e clonebundles` for more.
1992 1992
1993 1993 Examples:
1994 1994
1995 1995 - clone a remote repository to a new directory named hg/::
1996 1996
1997 1997 hg clone https://www.mercurial-scm.org/repo/hg/
1998 1998
1999 1999 - create a lightweight local clone::
2000 2000
2001 2001 hg clone project/ project-feature/
2002 2002
2003 2003 - clone from an absolute path on an ssh server (note double-slash)::
2004 2004
2005 2005 hg clone ssh://user@server//home/projects/alpha/
2006 2006
2007 2007 - do a streaming clone while checking out a specified version::
2008 2008
2009 2009 hg clone --stream http://server/repo -u 1.5
2010 2010
2011 2011 - create a repository without changesets after a particular revision::
2012 2012
2013 2013 hg clone -r 04e544 experimental/ good/
2014 2014
2015 2015 - clone (and track) a particular named branch::
2016 2016
2017 2017 hg clone https://www.mercurial-scm.org/repo/hg/#stable
2018 2018
2019 2019 See :hg:`help urls` for details on specifying URLs.
2020 2020
2021 2021 Returns 0 on success.
2022 2022 """
2023 2023 cmdutil.check_at_most_one_arg(opts, 'noupdate', 'updaterev')
2024 2024
2025 2025 # --include/--exclude can come from narrow or sparse.
2026 2026 includepats, excludepats = None, None
2027 2027
2028 2028 # hg.clone() differentiates between None and an empty set. So make sure
2029 2029 # patterns are sets if narrow is requested without patterns.
2030 2030 if opts.get('narrow'):
2031 2031 includepats = set()
2032 2032 excludepats = set()
2033 2033
2034 2034 if opts.get('include'):
2035 2035 includepats = narrowspec.parsepatterns(opts.get('include'))
2036 2036 if opts.get('exclude'):
2037 2037 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
2038 2038
2039 2039 r = hg.clone(
2040 2040 ui,
2041 2041 pycompat.byteskwargs(opts),
2042 2042 source,
2043 2043 dest,
2044 2044 pull=opts.get('pull'),
2045 2045 stream=opts.get('stream') or opts.get('uncompressed'),
2046 2046 revs=opts.get('rev'),
2047 2047 update=opts.get('updaterev') or not opts.get('noupdate'),
2048 2048 branch=opts.get('branch'),
2049 2049 shareopts=opts.get('shareopts'),
2050 2050 storeincludepats=includepats,
2051 2051 storeexcludepats=excludepats,
2052 2052 depth=opts.get('depth') or None,
2053 2053 )
2054 2054
2055 2055 return r is None
2056 2056
2057 2057
2058 2058 @command(
2059 2059 b'commit|ci',
2060 2060 [
2061 2061 (
2062 2062 b'A',
2063 2063 b'addremove',
2064 2064 None,
2065 2065 _(b'mark new/missing files as added/removed before committing'),
2066 2066 ),
2067 2067 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2068 2068 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2069 2069 (b's', b'secret', None, _(b'use the secret phase for committing')),
2070 2070 (b'', b'draft', None, _(b'use the draft phase for committing')),
2071 2071 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2072 2072 (
2073 2073 b'',
2074 2074 b'force-close-branch',
2075 2075 None,
2076 2076 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2077 2077 ),
2078 2078 (b'i', b'interactive', None, _(b'use interactive mode')),
2079 2079 ]
2080 2080 + walkopts
2081 2081 + commitopts
2082 2082 + commitopts2
2083 2083 + subrepoopts,
2084 2084 _(b'[OPTION]... [FILE]...'),
2085 2085 helpcategory=command.CATEGORY_COMMITTING,
2086 2086 helpbasic=True,
2087 2087 inferrepo=True,
2088 2088 )
2089 2089 def commit(ui, repo, *pats, **opts):
2090 2090 """commit the specified files or all outstanding changes
2091 2091
2092 2092 Commit changes to the given files into the repository. Unlike a
2093 2093 centralized SCM, this operation is a local operation. See
2094 2094 :hg:`push` for a way to actively distribute your changes.
2095 2095
2096 2096 If a list of files is omitted, all changes reported by :hg:`status`
2097 2097 will be committed.
2098 2098
2099 2099 If you are committing the result of a merge, do not provide any
2100 2100 filenames or -I/-X filters.
2101 2101
2102 2102 If no commit message is specified, Mercurial starts your
2103 2103 configured editor where you can enter a message. In case your
2104 2104 commit fails, you will find a backup of your message in
2105 2105 ``.hg/last-message.txt``.
2106 2106
2107 2107 The --close-branch flag can be used to mark the current branch
2108 2108 head closed. When all heads of a branch are closed, the branch
2109 2109 will be considered closed and no longer listed.
2110 2110
2111 2111 The --amend flag can be used to amend the parent of the
2112 2112 working directory with a new commit that contains the changes
2113 2113 in the parent in addition to those currently reported by :hg:`status`,
2114 2114 if there are any. The old commit is stored in a backup bundle in
2115 2115 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2116 2116 on how to restore it).
2117 2117
2118 2118 Message, user and date are taken from the amended commit unless
2119 2119 specified. When a message isn't specified on the command line,
2120 2120 the editor will open with the message of the amended commit.
2121 2121
2122 2122 It is not possible to amend public changesets (see :hg:`help phases`)
2123 2123 or changesets that have children.
2124 2124
2125 2125 See :hg:`help dates` for a list of formats valid for -d/--date.
2126 2126
2127 2127 Returns 0 on success, 1 if nothing changed.
2128 2128
2129 2129 .. container:: verbose
2130 2130
2131 2131 Examples:
2132 2132
2133 2133 - commit all files ending in .py::
2134 2134
2135 2135 hg commit --include "set:**.py"
2136 2136
2137 2137 - commit all non-binary files::
2138 2138
2139 2139 hg commit --exclude "set:binary()"
2140 2140
2141 2141 - amend the current commit and set the date to now::
2142 2142
2143 2143 hg commit --amend --date now
2144 2144 """
2145 2145 cmdutil.check_at_most_one_arg(opts, 'draft', 'secret')
2146 2146 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2147 2147 with repo.wlock(), repo.lock():
2148 2148 return _docommit(ui, repo, *pats, **opts)
2149 2149
2150 2150
2151 2151 def _docommit(ui, repo, *pats, **opts):
2152 2152 if opts.get('interactive'):
2153 2153 opts.pop('interactive')
2154 2154 ret = cmdutil.dorecord(
2155 2155 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2156 2156 )
2157 2157 # ret can be 0 (no changes to record) or the value returned by
2158 2158 # commit(), 1 if nothing changed or None on success.
2159 2159 return 1 if ret == 0 else ret
2160 2160
2161 2161 if opts.get('subrepos'):
2162 2162 # Let --subrepos on the command line override config setting.
2163 2163 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2164 2164
2165 2165 cmdutil.checkunfinished(repo, commit=True)
2166 2166
2167 2167 branch = repo[None].branch()
2168 2168 bheads = repo.branchheads(branch)
2169 2169 tip = repo.changelog.tip()
2170 2170
2171 2171 extra = {}
2172 2172 if opts.get('close_branch') or opts.get('force_close_branch'):
2173 2173 extra[b'close'] = b'1'
2174 2174
2175 2175 if repo[b'.'].closesbranch():
2176 2176 # Not ideal, but let us do an extra status early to prevent early
2177 2177 # bail out.
2178 2178 matcher = scmutil.match(
2179 2179 repo[None], pats, pycompat.byteskwargs(opts)
2180 2180 )
2181 2181 s = repo.status(match=matcher)
2182 2182 if s.modified or s.added or s.removed:
2183 2183 bheads = repo.branchheads(branch, closed=True)
2184 2184 else:
2185 2185 msg = _(b'current revision is already a branch closing head')
2186 2186 raise error.InputError(msg)
2187 2187
2188 2188 if not bheads:
2189 2189 raise error.InputError(
2190 2190 _(b'branch "%s" has no heads to close') % branch
2191 2191 )
2192 2192 elif (
2193 2193 branch == repo[b'.'].branch()
2194 2194 and repo[b'.'].node() not in bheads
2195 2195 and not opts.get('force_close_branch')
2196 2196 ):
2197 2197 hint = _(
2198 2198 b'use --force-close-branch to close branch from a non-head'
2199 2199 b' changeset'
2200 2200 )
2201 2201 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2202 2202 elif opts.get('amend'):
2203 2203 if (
2204 2204 repo[b'.'].p1().branch() != branch
2205 2205 and repo[b'.'].p2().branch() != branch
2206 2206 ):
2207 2207 raise error.InputError(_(b'can only close branch heads'))
2208 2208
2209 2209 if opts.get('amend'):
2210 2210 if ui.configbool(b'ui', b'commitsubrepos'):
2211 2211 raise error.InputError(
2212 2212 _(b'cannot amend with ui.commitsubrepos enabled')
2213 2213 )
2214 2214
2215 2215 old = repo[b'.']
2216 2216 rewriteutil.precheck(repo, [old.rev()], b'amend')
2217 2217
2218 2218 # Currently histedit gets confused if an amend happens while histedit
2219 2219 # is in progress. Since we have a checkunfinished command, we are
2220 2220 # temporarily honoring it.
2221 2221 #
2222 2222 # Note: eventually this guard will be removed. Please do not expect
2223 2223 # this behavior to remain.
2224 2224 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2225 2225 cmdutil.checkunfinished(repo)
2226 2226
2227 2227 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2228 2228 if node == old.node():
2229 2229 ui.status(_(b"nothing changed\n"))
2230 2230 return 1
2231 2231 else:
2232 2232
2233 2233 def commitfunc(ui, repo, message, match, opts):
2234 2234 overrides = {}
2235 2235 if opts.get(b'secret'):
2236 2236 overrides[(b'phases', b'new-commit')] = b'secret'
2237 2237 elif opts.get(b'draft'):
2238 2238 overrides[(b'phases', b'new-commit')] = b'draft'
2239 2239
2240 2240 baseui = repo.baseui
2241 2241 with baseui.configoverride(overrides, b'commit'):
2242 2242 with ui.configoverride(overrides, b'commit'):
2243 2243 editform = cmdutil.mergeeditform(
2244 2244 repo[None], b'commit.normal'
2245 2245 )
2246 2246 editor = cmdutil.getcommiteditor(
2247 2247 editform=editform, **pycompat.strkwargs(opts)
2248 2248 )
2249 2249 return repo.commit(
2250 2250 message,
2251 2251 opts.get(b'user'),
2252 2252 opts.get(b'date'),
2253 2253 match,
2254 2254 editor=editor,
2255 2255 extra=extra,
2256 2256 )
2257 2257
2258 2258 node = cmdutil.commit(
2259 2259 ui, repo, commitfunc, pats, pycompat.byteskwargs(opts)
2260 2260 )
2261 2261
2262 2262 if not node:
2263 2263 stat = cmdutil.postcommitstatus(
2264 2264 repo, pats, pycompat.byteskwargs(opts)
2265 2265 )
2266 2266 if stat.deleted:
2267 2267 ui.status(
2268 2268 _(
2269 2269 b"nothing changed (%d missing files, see "
2270 2270 b"'hg status')\n"
2271 2271 )
2272 2272 % len(stat.deleted)
2273 2273 )
2274 2274 else:
2275 2275 ui.status(_(b"nothing changed\n"))
2276 2276 return 1
2277 2277
2278 2278 cmdutil.commitstatus(repo, node, branch, bheads, tip, **opts)
2279 2279
2280 2280 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2281 2281 status(
2282 2282 ui,
2283 2283 repo,
2284 2284 modified=True,
2285 2285 added=True,
2286 2286 removed=True,
2287 2287 deleted=True,
2288 2288 unknown=True,
2289 2289 subrepos=opts.get('subrepos'),
2290 2290 )
2291 2291
2292 2292
2293 2293 @command(
2294 2294 b'config|showconfig|debugconfig',
2295 2295 [
2296 2296 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2297 2297 # This is experimental because we need
2298 2298 # * reasonable behavior around aliases,
2299 2299 # * decide if we display [debug] [experimental] and [devel] section par
2300 2300 # default
2301 2301 # * some way to display "generic" config entry (the one matching
2302 2302 # regexp,
2303 2303 # * proper display of the different value type
2304 2304 # * a better way to handle <DYNAMIC> values (and variable types),
2305 2305 # * maybe some type information ?
2306 2306 (
2307 2307 b'',
2308 2308 b'exp-all-known',
2309 2309 None,
2310 2310 _(b'show all known config option (EXPERIMENTAL)'),
2311 2311 ),
2312 2312 (b'e', b'edit', None, _(b'edit user config')),
2313 2313 (b'l', b'local', None, _(b'edit repository config')),
2314 2314 (b'', b'source', None, _(b'show source of configuration value')),
2315 2315 (
2316 2316 b'',
2317 2317 b'shared',
2318 2318 None,
2319 2319 _(b'edit shared source repository config (EXPERIMENTAL)'),
2320 2320 ),
2321 2321 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2322 2322 (b'g', b'global', None, _(b'edit global config')),
2323 2323 ]
2324 2324 + formatteropts,
2325 2325 _(b'[-u] [NAME]...'),
2326 2326 helpcategory=command.CATEGORY_HELP,
2327 2327 optionalrepo=True,
2328 2328 intents={INTENT_READONLY},
2329 2329 )
2330 2330 def config(ui, repo, *values, **opts):
2331 2331 """show combined config settings from all hgrc files
2332 2332
2333 2333 With no arguments, print names and values of all config items.
2334 2334
2335 2335 With one argument of the form section.name, print just the value
2336 2336 of that config item.
2337 2337
2338 2338 With multiple arguments, print names and values of all config
2339 2339 items with matching section names or section.names.
2340 2340
2341 2341 With --edit, start an editor on the user-level config file. With
2342 2342 --global, edit the system-wide config file. With --local, edit the
2343 2343 repository-level config file.
2344 2344
2345 2345 With --source, the source (filename and line number) is printed
2346 2346 for each config item.
2347 2347
2348 2348 See :hg:`help config` for more information about config files.
2349 2349
2350 2350 .. container:: verbose
2351 2351
2352 2352 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2353 2353 This file is not shared across shares when in share-safe mode.
2354 2354
2355 2355 Template:
2356 2356
2357 2357 The following keywords are supported. See also :hg:`help templates`.
2358 2358
2359 2359 :name: String. Config name.
2360 2360 :source: String. Filename and line number where the item is defined.
2361 2361 :value: String. Config value.
2362 2362
2363 2363 The --shared flag can be used to edit the config file of shared source
2364 2364 repository. It only works when you have shared using the experimental
2365 2365 share safe feature.
2366 2366
2367 2367 Returns 0 on success, 1 if NAME does not exist.
2368 2368
2369 2369 """
2370
2371 editopts = ('edit', 'local', 'global', 'shared', 'non_shared')
2372 if any(opts.get(o) for o in editopts):
2373 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2374 if opts.get('local'):
2375 if not repo:
2376 raise error.InputError(
2377 _(b"can't use --local outside a repository")
2378 )
2379 paths = [repo.vfs.join(b'hgrc')]
2380 elif opts.get('global'):
2381 paths = rcutil.systemrcpath()
2382 elif opts.get('shared'):
2383 if not repo.shared():
2384 raise error.InputError(
2385 _(b"repository is not shared; can't use --shared")
2386 )
2387 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2388 raise error.InputError(
2389 _(
2390 b"share safe feature not enabled; "
2391 b"unable to edit shared source repository config"
2392 )
2393 )
2394 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2395 elif opts.get('non_shared'):
2396 paths = [repo.vfs.join(b'hgrc-not-shared')]
2397 else:
2398 paths = rcutil.userrcpath()
2399
2400 for f in paths:
2401 if os.path.exists(f):
2402 break
2403 else:
2404 if opts.get('global'):
2405 samplehgrc = uimod.samplehgrcs[b'global']
2406 elif opts.get('local'):
2407 samplehgrc = uimod.samplehgrcs[b'local']
2408 else:
2409 samplehgrc = uimod.samplehgrcs[b'user']
2410
2411 f = paths[0]
2412 util.writefile(f, util.tonativeeol(samplehgrc))
2413
2414 editor = ui.geteditor()
2415 ui.system(
2416 b"%s \"%s\"" % (editor, f),
2417 onerr=error.InputError,
2418 errprefix=_(b"edit failed"),
2419 blockedtag=b'config_edit',
2420 )
2421 return
2370 edit_level = config_command.find_edit_level(ui, repo, opts)
2371 if edit_level is not None:
2372 return config_command.edit_config(ui, repo, edit_level)
2422 2373 ui.pager(b'config')
2423 2374 fm = ui.formatter(b'config', pycompat.byteskwargs(opts))
2424 2375 for t, f in rcutil.rccomponents():
2425 2376 if t == b'path':
2426 2377 ui.debug(b'read config from: %s\n' % f)
2427 2378 elif t == b'resource':
2428 2379 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2429 2380 elif t == b'items':
2430 2381 # Don't print anything for 'items'.
2431 2382 pass
2432 2383 else:
2433 2384 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2434 2385 untrusted = bool(opts.get('untrusted'))
2435 2386
2436 2387 selsections = selentries = []
2437 2388 if values:
2438 2389 selsections = [v for v in values if b'.' not in v]
2439 2390 selentries = [v for v in values if b'.' in v]
2440 2391 uniquesel = len(selentries) == 1 and not selsections
2441 2392 selsections = set(selsections)
2442 2393 selentries = set(selentries)
2443 2394
2444 2395 matched = False
2445 2396 all_known = opts['exp_all_known']
2446 2397 show_source = ui.debugflag or opts.get('source')
2447 2398 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2448 2399 for section, name, value in entries:
2449 2400 source = ui.configsource(section, name, untrusted)
2450 2401 value = pycompat.bytestr(value)
2451 2402 defaultvalue = ui.configdefault(section, name)
2452 2403 if fm.isplain():
2453 2404 source = source or b'none'
2454 2405 value = value.replace(b'\n', b'\\n')
2455 2406 entryname = section + b'.' + name
2456 2407 if values and not (section in selsections or entryname in selentries):
2457 2408 continue
2458 2409 fm.startitem()
2459 2410 fm.condwrite(show_source, b'source', b'%s: ', source)
2460 2411 if uniquesel:
2461 2412 fm.data(name=entryname)
2462 2413 fm.write(b'value', b'%s\n', value)
2463 2414 else:
2464 2415 fm.write(b'name value', b'%s=%s\n', entryname, value)
2465 2416 if formatter.isprintable(defaultvalue):
2466 2417 fm.data(defaultvalue=defaultvalue)
2467 2418 elif isinstance(defaultvalue, list) and all(
2468 2419 formatter.isprintable(e) for e in defaultvalue
2469 2420 ):
2470 2421 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2471 2422 # TODO: no idea how to process unsupported defaultvalue types
2472 2423 matched = True
2473 2424 fm.end()
2474 2425 if matched:
2475 2426 return 0
2476 2427 return 1
2477 2428
2478 2429
2479 2430 @command(
2480 2431 b'continue',
2481 2432 dryrunopts,
2482 2433 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2483 2434 helpbasic=True,
2484 2435 )
2485 2436 def continuecmd(ui, repo, **opts):
2486 2437 """resumes an interrupted operation (EXPERIMENTAL)
2487 2438
2488 2439 Finishes a multistep operation like graft, histedit, rebase, merge,
2489 2440 and unshelve if they are in an interrupted state.
2490 2441
2491 2442 use --dry-run/-n to dry run the command.
2492 2443 """
2493 2444 dryrun = opts.get('dry_run')
2494 2445 contstate = cmdutil.getunfinishedstate(repo)
2495 2446 if not contstate:
2496 2447 raise error.StateError(_(b'no operation in progress'))
2497 2448 if not contstate.continuefunc:
2498 2449 raise error.StateError(
2499 2450 (
2500 2451 _(b"%s in progress but does not support 'hg continue'")
2501 2452 % (contstate._opname)
2502 2453 ),
2503 2454 hint=contstate.continuemsg(),
2504 2455 )
2505 2456 if dryrun:
2506 2457 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2507 2458 return
2508 2459 return contstate.continuefunc(ui, repo)
2509 2460
2510 2461
2511 2462 @command(
2512 2463 b'copy|cp',
2513 2464 [
2514 2465 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2515 2466 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2516 2467 (
2517 2468 b'',
2518 2469 b'at-rev',
2519 2470 b'',
2520 2471 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2521 2472 _(b'REV'),
2522 2473 ),
2523 2474 (
2524 2475 b'f',
2525 2476 b'force',
2526 2477 None,
2527 2478 _(b'forcibly copy over an existing managed file'),
2528 2479 ),
2529 2480 ]
2530 2481 + walkopts
2531 2482 + dryrunopts,
2532 2483 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2533 2484 helpcategory=command.CATEGORY_FILE_CONTENTS,
2534 2485 )
2535 2486 def copy(ui, repo, *pats, **opts):
2536 2487 """mark files as copied for the next commit
2537 2488
2538 2489 Mark dest as having copies of source files. If dest is a
2539 2490 directory, copies are put in that directory. If dest is a file,
2540 2491 the source must be a single file.
2541 2492
2542 2493 By default, this command copies the contents of files as they
2543 2494 exist in the working directory. If invoked with -A/--after, the
2544 2495 operation is recorded, but no copying is performed.
2545 2496
2546 2497 To undo marking a destination file as copied, use --forget. With that
2547 2498 option, all given (positional) arguments are unmarked as copies. The
2548 2499 destination file(s) will be left in place (still tracked). Note that
2549 2500 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2550 2501
2551 2502 This command takes effect with the next commit by default.
2552 2503
2553 2504 Returns 0 on success, 1 if errors are encountered.
2554 2505 """
2555 2506
2556 2507 context = lambda repo: repo.dirstate.changing_files(repo)
2557 2508 rev = opts.get('at_rev')
2558 2509
2559 2510 if rev:
2560 2511 ctx = logcmdutil.revsingle(repo, rev)
2561 2512 if ctx.rev() is not None:
2562 2513
2563 2514 def context(repo):
2564 2515 return util.nullcontextmanager()
2565 2516
2566 2517 opts['at_rev'] = ctx.rev()
2567 2518 with repo.wlock(), context(repo):
2568 2519 return cmdutil.copy(ui, repo, pats, pycompat.byteskwargs(opts))
2569 2520
2570 2521
2571 2522 @command(
2572 2523 b'debugcommands',
2573 2524 [],
2574 2525 _(b'[COMMAND]'),
2575 2526 helpcategory=command.CATEGORY_HELP,
2576 2527 norepo=True,
2577 2528 )
2578 2529 def debugcommands(ui, cmd=b'', *args):
2579 2530 """list all available commands and options"""
2580 2531 for cmd, vals in sorted(table.items()):
2581 2532 cmd = cmd.split(b'|')[0]
2582 2533 opts = b', '.join([i[1] for i in vals[1]])
2583 2534 ui.write(b'%s: %s\n' % (cmd, opts))
2584 2535
2585 2536
2586 2537 @command(
2587 2538 b'debugcomplete',
2588 2539 [(b'o', b'options', None, _(b'show the command options'))],
2589 2540 _(b'[-o] CMD'),
2590 2541 helpcategory=command.CATEGORY_HELP,
2591 2542 norepo=True,
2592 2543 )
2593 2544 def debugcomplete(ui, cmd=b'', **opts):
2594 2545 """returns the completion list associated with the given command"""
2595 2546
2596 2547 if opts.get('options'):
2597 2548 options = []
2598 2549 otables = [globalopts]
2599 2550 if cmd:
2600 2551 aliases, entry = cmdutil.findcmd(cmd, table, False)
2601 2552 otables.append(entry[1])
2602 2553 for t in otables:
2603 2554 for o in t:
2604 2555 if b"(DEPRECATED)" in o[3]:
2605 2556 continue
2606 2557 if o[0]:
2607 2558 options.append(b'-%s' % o[0])
2608 2559 options.append(b'--%s' % o[1])
2609 2560 ui.write(b"%s\n" % b"\n".join(options))
2610 2561 return
2611 2562
2612 2563 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2613 2564 if ui.verbose:
2614 2565 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2615 2566 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2616 2567
2617 2568
2618 2569 @command(
2619 2570 b'diff',
2620 2571 [
2621 2572 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2622 2573 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2623 2574 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2624 2575 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2625 2576 ]
2626 2577 + diffopts
2627 2578 + diffopts2
2628 2579 + walkopts
2629 2580 + subrepoopts,
2630 2581 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2631 2582 helpcategory=command.CATEGORY_FILE_CONTENTS,
2632 2583 helpbasic=True,
2633 2584 inferrepo=True,
2634 2585 intents={INTENT_READONLY},
2635 2586 )
2636 2587 def diff(ui, repo, *pats, **opts):
2637 2588 """diff repository (or selected files)
2638 2589
2639 2590 Show differences between revisions for the specified files.
2640 2591
2641 2592 Differences between files are shown using the unified diff format.
2642 2593
2643 2594 .. note::
2644 2595
2645 2596 :hg:`diff` may generate unexpected results for merges, as it will
2646 2597 default to comparing against the working directory's first
2647 2598 parent changeset if no revisions are specified. To diff against the
2648 2599 conflict regions, you can use `--config diff.merge=yes`.
2649 2600
2650 2601 By default, the working directory files are compared to its first parent. To
2651 2602 see the differences from another revision, use --from. To see the difference
2652 2603 to another revision, use --to. For example, :hg:`diff --from .^` will show
2653 2604 the differences from the working copy's grandparent to the working copy,
2654 2605 :hg:`diff --to .` will show the diff from the working copy to its parent
2655 2606 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2656 2607 show the diff between those two revisions.
2657 2608
2658 2609 Alternatively you can specify -c/--change with a revision to see the changes
2659 2610 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2660 2611 equivalent to :hg:`diff --from 42^ --to 42`)
2661 2612
2662 2613 Without the -a/--text option, diff will avoid generating diffs of
2663 2614 files it detects as binary. With -a, diff will generate a diff
2664 2615 anyway, probably with undesirable results.
2665 2616
2666 2617 Use the -g/--git option to generate diffs in the git extended diff
2667 2618 format. For more information, read :hg:`help diffs`.
2668 2619
2669 2620 .. container:: verbose
2670 2621
2671 2622 Examples:
2672 2623
2673 2624 - compare a file in the current working directory to its parent::
2674 2625
2675 2626 hg diff foo.c
2676 2627
2677 2628 - compare two historical versions of a directory, with rename info::
2678 2629
2679 2630 hg diff --git --from 1.0 --to 1.2 lib/
2680 2631
2681 2632 - get change stats relative to the last change on some date::
2682 2633
2683 2634 hg diff --stat --from "date('may 2')"
2684 2635
2685 2636 - diff all newly-added files that contain a keyword::
2686 2637
2687 2638 hg diff "set:added() and grep(GNU)"
2688 2639
2689 2640 - compare a revision and its parents::
2690 2641
2691 2642 hg diff -c 9353 # compare against first parent
2692 2643 hg diff --from 9353^ --to 9353 # same using revset syntax
2693 2644 hg diff --from 9353^2 --to 9353 # compare against the second parent
2694 2645
2695 2646 Returns 0 on success.
2696 2647 """
2697 2648
2698 2649 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2699 2650 opts = pycompat.byteskwargs(opts)
2700 2651 revs = opts.get(b'rev')
2701 2652 change = opts.get(b'change')
2702 2653 from_rev = opts.get(b'from')
2703 2654 to_rev = opts.get(b'to')
2704 2655 stat = opts.get(b'stat')
2705 2656 reverse = opts.get(b'reverse')
2706 2657
2707 2658 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2708 2659 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2709 2660 if change:
2710 2661 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2711 2662 ctx2 = logcmdutil.revsingle(repo, change, None)
2712 2663 ctx1 = diffutil.diff_parent(ctx2)
2713 2664 elif from_rev or to_rev:
2714 2665 repo = scmutil.unhidehashlikerevs(
2715 2666 repo, [from_rev] + [to_rev], b'nowarn'
2716 2667 )
2717 2668 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2718 2669 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2719 2670 else:
2720 2671 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2721 2672 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2722 2673
2723 2674 if reverse:
2724 2675 ctxleft = ctx2
2725 2676 ctxright = ctx1
2726 2677 else:
2727 2678 ctxleft = ctx1
2728 2679 ctxright = ctx2
2729 2680
2730 2681 diffopts = patch.diffallopts(ui, opts)
2731 2682 m = scmutil.match(ctx2, pats, opts)
2732 2683 m = repo.narrowmatch(m)
2733 2684 ui.pager(b'diff')
2734 2685 logcmdutil.diffordiffstat(
2735 2686 ui,
2736 2687 repo,
2737 2688 diffopts,
2738 2689 ctxleft,
2739 2690 ctxright,
2740 2691 m,
2741 2692 stat=stat,
2742 2693 listsubrepos=opts.get(b'subrepos'),
2743 2694 root=opts.get(b'root'),
2744 2695 )
2745 2696
2746 2697
2747 2698 @command(
2748 2699 b'export',
2749 2700 [
2750 2701 (
2751 2702 b'B',
2752 2703 b'bookmark',
2753 2704 b'',
2754 2705 _(b'export changes only reachable by given bookmark'),
2755 2706 _(b'BOOKMARK'),
2756 2707 ),
2757 2708 (
2758 2709 b'o',
2759 2710 b'output',
2760 2711 b'',
2761 2712 _(b'print output to file with formatted name'),
2762 2713 _(b'FORMAT'),
2763 2714 ),
2764 2715 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2765 2716 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2766 2717 ]
2767 2718 + diffopts
2768 2719 + formatteropts,
2769 2720 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2770 2721 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2771 2722 helpbasic=True,
2772 2723 intents={INTENT_READONLY},
2773 2724 )
2774 2725 def export(ui, repo, *changesets, **opts):
2775 2726 """dump the header and diffs for one or more changesets
2776 2727
2777 2728 Print the changeset header and diffs for one or more revisions.
2778 2729 If no revision is given, the parent of the working directory is used.
2779 2730
2780 2731 The information shown in the changeset header is: author, date,
2781 2732 branch name (if non-default), changeset hash, parent(s) and commit
2782 2733 comment.
2783 2734
2784 2735 .. note::
2785 2736
2786 2737 :hg:`export` may generate unexpected diff output for merge
2787 2738 changesets, as it will compare the merge changeset against its
2788 2739 first parent only.
2789 2740
2790 2741 Output may be to a file, in which case the name of the file is
2791 2742 given using a template string. See :hg:`help templates`. In addition
2792 2743 to the common template keywords, the following formatting rules are
2793 2744 supported:
2794 2745
2795 2746 :``%%``: literal "%" character
2796 2747 :``%H``: changeset hash (40 hexadecimal digits)
2797 2748 :``%N``: number of patches being generated
2798 2749 :``%R``: changeset revision number
2799 2750 :``%b``: basename of the exporting repository
2800 2751 :``%h``: short-form changeset hash (12 hexadecimal digits)
2801 2752 :``%m``: first line of the commit message (only alphanumeric characters)
2802 2753 :``%n``: zero-padded sequence number, starting at 1
2803 2754 :``%r``: zero-padded changeset revision number
2804 2755 :``\\``: literal "\\" character
2805 2756
2806 2757 Without the -a/--text option, export will avoid generating diffs
2807 2758 of files it detects as binary. With -a, export will generate a
2808 2759 diff anyway, probably with undesirable results.
2809 2760
2810 2761 With -B/--bookmark changesets reachable by the given bookmark are
2811 2762 selected.
2812 2763
2813 2764 Use the -g/--git option to generate diffs in the git extended diff
2814 2765 format. See :hg:`help diffs` for more information.
2815 2766
2816 2767 With the --switch-parent option, the diff will be against the
2817 2768 second parent. It can be useful to review a merge.
2818 2769
2819 2770 .. container:: verbose
2820 2771
2821 2772 Template:
2822 2773
2823 2774 The following keywords are supported in addition to the common template
2824 2775 keywords and functions. See also :hg:`help templates`.
2825 2776
2826 2777 :diff: String. Diff content.
2827 2778 :parents: List of strings. Parent nodes of the changeset.
2828 2779
2829 2780 Examples:
2830 2781
2831 2782 - use export and import to transplant a bugfix to the current
2832 2783 branch::
2833 2784
2834 2785 hg export -r 9353 | hg import -
2835 2786
2836 2787 - export all the changesets between two revisions to a file with
2837 2788 rename information::
2838 2789
2839 2790 hg export --git -r 123:150 > changes.txt
2840 2791
2841 2792 - split outgoing changes into a series of patches with
2842 2793 descriptive names::
2843 2794
2844 2795 hg export -r "outgoing()" -o "%n-%m.patch"
2845 2796
2846 2797 Returns 0 on success.
2847 2798 """
2848 2799 opts = pycompat.byteskwargs(opts)
2849 2800 bookmark = opts.get(b'bookmark')
2850 2801 changesets += tuple(opts.get(b'rev', []))
2851 2802
2852 2803 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2853 2804
2854 2805 if bookmark:
2855 2806 if bookmark not in repo._bookmarks:
2856 2807 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2857 2808
2858 2809 revs = scmutil.bookmarkrevs(repo, bookmark)
2859 2810 else:
2860 2811 if not changesets:
2861 2812 changesets = [b'.']
2862 2813
2863 2814 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2864 2815 revs = logcmdutil.revrange(repo, changesets)
2865 2816
2866 2817 if not revs:
2867 2818 raise error.InputError(_(b"export requires at least one changeset"))
2868 2819 if len(revs) > 1:
2869 2820 ui.note(_(b'exporting patches:\n'))
2870 2821 else:
2871 2822 ui.note(_(b'exporting patch:\n'))
2872 2823
2873 2824 fntemplate = opts.get(b'output')
2874 2825 if cmdutil.isstdiofilename(fntemplate):
2875 2826 fntemplate = b''
2876 2827
2877 2828 if fntemplate:
2878 2829 fm = formatter.nullformatter(ui, b'export', opts)
2879 2830 else:
2880 2831 ui.pager(b'export')
2881 2832 fm = ui.formatter(b'export', opts)
2882 2833 with fm:
2883 2834 cmdutil.export(
2884 2835 repo,
2885 2836 revs,
2886 2837 fm,
2887 2838 fntemplate=fntemplate,
2888 2839 switch_parent=opts.get(b'switch_parent'),
2889 2840 opts=patch.diffallopts(ui, opts),
2890 2841 )
2891 2842
2892 2843
2893 2844 @command(
2894 2845 b'files',
2895 2846 [
2896 2847 (
2897 2848 b'r',
2898 2849 b'rev',
2899 2850 b'',
2900 2851 _(b'search the repository as it is in REV'),
2901 2852 _(b'REV'),
2902 2853 ),
2903 2854 (
2904 2855 b'0',
2905 2856 b'print0',
2906 2857 None,
2907 2858 _(b'end filenames with NUL, for use with xargs'),
2908 2859 ),
2909 2860 ]
2910 2861 + walkopts
2911 2862 + formatteropts
2912 2863 + subrepoopts,
2913 2864 _(b'[OPTION]... [FILE]...'),
2914 2865 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2915 2866 intents={INTENT_READONLY},
2916 2867 )
2917 2868 def files(ui, repo, *pats, **opts):
2918 2869 """list tracked files
2919 2870
2920 2871 Print files under Mercurial control in the working directory or
2921 2872 specified revision for given files (excluding removed files).
2922 2873 Files can be specified as filenames or filesets.
2923 2874
2924 2875 If no files are given to match, this command prints the names
2925 2876 of all files under Mercurial control.
2926 2877
2927 2878 .. container:: verbose
2928 2879
2929 2880 Template:
2930 2881
2931 2882 The following keywords are supported in addition to the common template
2932 2883 keywords and functions. See also :hg:`help templates`.
2933 2884
2934 2885 :flags: String. Character denoting file's symlink and executable bits.
2935 2886 :path: String. Repository-absolute path of the file.
2936 2887 :size: Integer. Size of the file in bytes.
2937 2888
2938 2889 Examples:
2939 2890
2940 2891 - list all files under the current directory::
2941 2892
2942 2893 hg files .
2943 2894
2944 2895 - shows sizes and flags for current revision::
2945 2896
2946 2897 hg files -vr .
2947 2898
2948 2899 - list all files named README::
2949 2900
2950 2901 hg files -I "**/README"
2951 2902
2952 2903 - list all binary files::
2953 2904
2954 2905 hg files "set:binary()"
2955 2906
2956 2907 - find files containing a regular expression::
2957 2908
2958 2909 hg files "set:grep('bob')"
2959 2910
2960 2911 - search tracked file contents with xargs and grep::
2961 2912
2962 2913 hg files -0 | xargs -0 grep foo
2963 2914
2964 2915 See :hg:`help patterns` and :hg:`help filesets` for more information
2965 2916 on specifying file patterns.
2966 2917
2967 2918 Returns 0 if a match is found, 1 otherwise.
2968 2919
2969 2920 """
2970 2921
2971 2922 opts = pycompat.byteskwargs(opts)
2972 2923 rev = opts.get(b'rev')
2973 2924 if rev:
2974 2925 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2975 2926 ctx = logcmdutil.revsingle(repo, rev, None)
2976 2927
2977 2928 end = b'\n'
2978 2929 if opts.get(b'print0'):
2979 2930 end = b'\0'
2980 2931 fmt = b'%s' + end
2981 2932
2982 2933 m = scmutil.match(ctx, pats, opts)
2983 2934 ui.pager(b'files')
2984 2935 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2985 2936 with ui.formatter(b'files', opts) as fm:
2986 2937 return cmdutil.files(
2987 2938 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2988 2939 )
2989 2940
2990 2941
2991 2942 @command(
2992 2943 b'forget',
2993 2944 [
2994 2945 (b'i', b'interactive', None, _(b'use interactive mode')),
2995 2946 ]
2996 2947 + walkopts
2997 2948 + dryrunopts,
2998 2949 _(b'[OPTION]... FILE...'),
2999 2950 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
3000 2951 helpbasic=True,
3001 2952 inferrepo=True,
3002 2953 )
3003 2954 def forget(ui, repo, *pats, **opts):
3004 2955 """forget the specified files on the next commit
3005 2956
3006 2957 Mark the specified files so they will no longer be tracked
3007 2958 after the next commit.
3008 2959
3009 2960 This only removes files from the current branch, not from the
3010 2961 entire project history, and it does not delete them from the
3011 2962 working directory.
3012 2963
3013 2964 To delete the file from the working directory, see :hg:`remove`.
3014 2965
3015 2966 To undo a forget before the next commit, see :hg:`add`.
3016 2967
3017 2968 .. container:: verbose
3018 2969
3019 2970 Examples:
3020 2971
3021 2972 - forget newly-added binary files::
3022 2973
3023 2974 hg forget "set:added() and binary()"
3024 2975
3025 2976 - forget files that would be excluded by .hgignore::
3026 2977
3027 2978 hg forget "set:hgignore()"
3028 2979
3029 2980 Returns 0 on success.
3030 2981 """
3031 2982
3032 2983 if not pats:
3033 2984 raise error.InputError(_(b'no files specified'))
3034 2985
3035 2986 with repo.wlock(), repo.dirstate.changing_files(repo):
3036 2987 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
3037 2988 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
3038 2989 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3039 2990 rejected = cmdutil.forget(
3040 2991 ui,
3041 2992 repo,
3042 2993 m,
3043 2994 prefix=b"",
3044 2995 uipathfn=uipathfn,
3045 2996 explicitonly=False,
3046 2997 dryrun=dryrun,
3047 2998 interactive=interactive,
3048 2999 )[0]
3049 3000 return rejected and 1 or 0
3050 3001
3051 3002
3052 3003 @command(
3053 3004 b'graft',
3054 3005 [
3055 3006 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
3056 3007 (
3057 3008 b'',
3058 3009 b'base',
3059 3010 b'',
3060 3011 _(b'base revision when doing the graft merge (ADVANCED)'),
3061 3012 _(b'REV'),
3062 3013 ),
3063 3014 (b'c', b'continue', False, _(b'resume interrupted graft')),
3064 3015 (b'', b'stop', False, _(b'stop interrupted graft')),
3065 3016 (b'', b'abort', False, _(b'abort interrupted graft')),
3066 3017 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3067 3018 (b'', b'log', None, _(b'append graft info to log message')),
3068 3019 (
3069 3020 b'',
3070 3021 b'no-commit',
3071 3022 None,
3072 3023 _(b"don't commit, just apply the changes in working directory"),
3073 3024 ),
3074 3025 (b'f', b'force', False, _(b'force graft')),
3075 3026 (
3076 3027 b'D',
3077 3028 b'currentdate',
3078 3029 False,
3079 3030 _(b'record the current date as commit date'),
3080 3031 ),
3081 3032 (
3082 3033 b'U',
3083 3034 b'currentuser',
3084 3035 False,
3085 3036 _(b'record the current user as committer'),
3086 3037 ),
3087 3038 ]
3088 3039 + commitopts2
3089 3040 + mergetoolopts
3090 3041 + dryrunopts,
3091 3042 _(b'[OPTION]... [-r REV]... REV...'),
3092 3043 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3093 3044 )
3094 3045 def graft(ui, repo, *revs, **opts):
3095 3046 """copy changes from other branches onto the current branch
3096 3047
3097 3048 This command uses Mercurial's merge logic to copy individual
3098 3049 changes from other branches without merging branches in the
3099 3050 history graph. This is sometimes known as 'backporting' or
3100 3051 'cherry-picking'. By default, graft will copy user, date, and
3101 3052 description from the source changesets.
3102 3053
3103 3054 Changesets that are ancestors of the current revision, that have
3104 3055 already been grafted, or that are merges will be skipped.
3105 3056
3106 3057 If --log is specified, log messages will have a comment appended
3107 3058 of the form::
3108 3059
3109 3060 (grafted from CHANGESETHASH)
3110 3061
3111 3062 If --force is specified, revisions will be grafted even if they
3112 3063 are already ancestors of, or have been grafted to, the destination.
3113 3064 This is useful when the revisions have since been backed out.
3114 3065
3115 3066 If a graft merge results in conflicts, the graft process is
3116 3067 interrupted so that the current merge can be manually resolved.
3117 3068 Once all conflicts are addressed, the graft process can be
3118 3069 continued with the -c/--continue option.
3119 3070
3120 3071 The -c/--continue option reapplies all the earlier options.
3121 3072
3122 3073 .. container:: verbose
3123 3074
3124 3075 The --base option exposes more of how graft internally uses merge with a
3125 3076 custom base revision. --base can be used to specify another ancestor than
3126 3077 the first and only parent.
3127 3078
3128 3079 The command::
3129 3080
3130 3081 hg graft -r 345 --base 234
3131 3082
3132 3083 is thus pretty much the same as::
3133 3084
3134 3085 hg diff --from 234 --to 345 | hg import
3135 3086
3136 3087 but using merge to resolve conflicts and track moved files.
3137 3088
3138 3089 The result of a merge can thus be backported as a single commit by
3139 3090 specifying one of the merge parents as base, and thus effectively
3140 3091 grafting the changes from the other side.
3141 3092
3142 3093 It is also possible to collapse multiple changesets and clean up history
3143 3094 by specifying another ancestor as base, much like rebase --collapse
3144 3095 --keep.
3145 3096
3146 3097 The commit message can be tweaked after the fact using commit --amend .
3147 3098
3148 3099 For using non-ancestors as the base to backout changes, see the backout
3149 3100 command and the hidden --parent option.
3150 3101
3151 3102 .. container:: verbose
3152 3103
3153 3104 Examples:
3154 3105
3155 3106 - copy a single change to the stable branch and edit its description::
3156 3107
3157 3108 hg update stable
3158 3109 hg graft --edit 9393
3159 3110
3160 3111 - graft a range of changesets with one exception, updating dates::
3161 3112
3162 3113 hg graft -D "2085::2093 and not 2091"
3163 3114
3164 3115 - continue a graft after resolving conflicts::
3165 3116
3166 3117 hg graft -c
3167 3118
3168 3119 - show the source of a grafted changeset::
3169 3120
3170 3121 hg log --debug -r .
3171 3122
3172 3123 - show revisions sorted by date::
3173 3124
3174 3125 hg log -r "sort(all(), date)"
3175 3126
3176 3127 - backport the result of a merge as a single commit::
3177 3128
3178 3129 hg graft -r 123 --base 123^
3179 3130
3180 3131 - land a feature branch as one changeset::
3181 3132
3182 3133 hg up -cr default
3183 3134 hg graft -r featureX --base "ancestor('featureX', 'default')"
3184 3135
3185 3136 See :hg:`help revisions` for more about specifying revisions.
3186 3137
3187 3138 Returns 0 on successful completion, 1 if there are unresolved files.
3188 3139 """
3189 3140 with repo.wlock():
3190 3141 return graft_impl.cmd_graft(ui, repo, *revs, **opts)
3191 3142
3192 3143
3193 3144 statemod.addunfinished(
3194 3145 b'graft',
3195 3146 fname=b'graftstate',
3196 3147 clearable=True,
3197 3148 stopflag=True,
3198 3149 continueflag=True,
3199 3150 abortfunc=cmdutil.hgabortgraft,
3200 3151 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3201 3152 )
3202 3153
3203 3154
3204 3155 @command(
3205 3156 b'grep',
3206 3157 [
3207 3158 (b'0', b'print0', None, _(b'end fields with NUL')),
3208 3159 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3209 3160 (
3210 3161 b'',
3211 3162 b'diff',
3212 3163 None,
3213 3164 _(
3214 3165 b'search revision differences for when the pattern was added '
3215 3166 b'or removed'
3216 3167 ),
3217 3168 ),
3218 3169 (b'a', b'text', None, _(b'treat all files as text')),
3219 3170 (
3220 3171 b'f',
3221 3172 b'follow',
3222 3173 None,
3223 3174 _(
3224 3175 b'follow changeset history,'
3225 3176 b' or file history across copies and renames'
3226 3177 ),
3227 3178 ),
3228 3179 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3229 3180 (
3230 3181 b'l',
3231 3182 b'files-with-matches',
3232 3183 None,
3233 3184 _(b'print only filenames and revisions that match'),
3234 3185 ),
3235 3186 (b'n', b'line-number', None, _(b'print matching line numbers')),
3236 3187 (
3237 3188 b'r',
3238 3189 b'rev',
3239 3190 [],
3240 3191 _(b'search files changed within revision range'),
3241 3192 _(b'REV'),
3242 3193 ),
3243 3194 (
3244 3195 b'',
3245 3196 b'all-files',
3246 3197 None,
3247 3198 _(
3248 3199 b'include all files in the changeset while grepping (DEPRECATED)'
3249 3200 ),
3250 3201 ),
3251 3202 (b'u', b'user', None, _(b'list the author (long with -v)')),
3252 3203 (b'd', b'date', None, _(b'list the date (short with -q)')),
3253 3204 ]
3254 3205 + formatteropts
3255 3206 + walkopts,
3256 3207 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3257 3208 helpcategory=command.CATEGORY_FILE_CONTENTS,
3258 3209 inferrepo=True,
3259 3210 intents={INTENT_READONLY},
3260 3211 )
3261 3212 def grep(ui, repo, pattern, *pats, **opts):
3262 3213 """search for a pattern in specified files
3263 3214
3264 3215 Search the working directory or revision history for a regular
3265 3216 expression in the specified files for the entire repository.
3266 3217
3267 3218 By default, grep searches the repository files in the working
3268 3219 directory and prints the files where it finds a match. To specify
3269 3220 historical revisions instead of the working directory, use the
3270 3221 --rev flag.
3271 3222
3272 3223 To search instead historical revision differences that contains a
3273 3224 change in match status ("-" for a match that becomes a non-match,
3274 3225 or "+" for a non-match that becomes a match), use the --diff flag.
3275 3226
3276 3227 PATTERN can be any Python (roughly Perl-compatible) regular
3277 3228 expression.
3278 3229
3279 3230 If no FILEs are specified and the --rev flag isn't supplied, all
3280 3231 files in the working directory are searched. When using the --rev
3281 3232 flag and specifying FILEs, use the --follow argument to also
3282 3233 follow the specified FILEs across renames and copies.
3283 3234
3284 3235 .. container:: verbose
3285 3236
3286 3237 Template:
3287 3238
3288 3239 The following keywords are supported in addition to the common template
3289 3240 keywords and functions. See also :hg:`help templates`.
3290 3241
3291 3242 :change: String. Character denoting insertion ``+`` or removal ``-``.
3292 3243 Available if ``--diff`` is specified.
3293 3244 :lineno: Integer. Line number of the match.
3294 3245 :path: String. Repository-absolute path of the file.
3295 3246 :texts: List of text chunks.
3296 3247
3297 3248 And each entry of ``{texts}`` provides the following sub-keywords.
3298 3249
3299 3250 :matched: Boolean. True if the chunk matches the specified pattern.
3300 3251 :text: String. Chunk content.
3301 3252
3302 3253 See :hg:`help templates.operators` for the list expansion syntax.
3303 3254
3304 3255 Returns 0 if a match is found, 1 otherwise.
3305 3256
3306 3257 """
3307 3258 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3308 3259
3309 3260 diff = opts.get('all') or opts.get('diff')
3310 3261 follow = opts.get('follow')
3311 3262 if opts.get('all_files') is None and not diff:
3312 3263 opts['all_files'] = True
3313 3264 plaingrep = (
3314 3265 opts.get('all_files') and not opts.get('rev') and not opts.get('follow')
3315 3266 )
3316 3267 all_files = opts.get('all_files')
3317 3268 if plaingrep:
3318 3269 opts['rev'] = [b'wdir()']
3319 3270
3320 3271 reflags = re.M
3321 3272 if opts.get('ignore_case'):
3322 3273 reflags |= re.I
3323 3274 try:
3324 3275 regexp = util.re.compile(pattern, reflags)
3325 3276 except re.error as inst:
3326 3277 ui.warn(
3327 3278 _(b"grep: invalid match pattern: %s\n")
3328 3279 % stringutil.forcebytestr(inst)
3329 3280 )
3330 3281 return 1
3331 3282 sep, eol = b':', b'\n'
3332 3283 if opts.get('print0'):
3333 3284 sep = eol = b'\0'
3334 3285
3335 3286 searcher = grepmod.grepsearcher(
3336 3287 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3337 3288 )
3338 3289
3339 3290 getfile = searcher._getfile
3340 3291
3341 3292 uipathfn = scmutil.getuipathfn(repo)
3342 3293
3343 3294 def display(fm, fn, ctx, pstates, states):
3344 3295 rev = scmutil.intrev(ctx)
3345 3296 if fm.isplain():
3346 3297 formatuser = ui.shortuser
3347 3298 else:
3348 3299 formatuser = pycompat.bytestr
3349 3300 if ui.quiet:
3350 3301 datefmt = b'%Y-%m-%d'
3351 3302 else:
3352 3303 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3353 3304 found = False
3354 3305
3355 3306 @util.cachefunc
3356 3307 def binary():
3357 3308 flog = getfile(fn)
3358 3309 try:
3359 3310 return stringutil.binary(flog.read(ctx.filenode(fn)))
3360 3311 except error.WdirUnsupported:
3361 3312 return ctx[fn].isbinary()
3362 3313
3363 3314 fieldnamemap = {b'linenumber': b'lineno'}
3364 3315 if diff:
3365 3316 iter = grepmod.difflinestates(pstates, states)
3366 3317 else:
3367 3318 iter = [(b'', l) for l in states]
3368 3319 for change, l in iter:
3369 3320 fm.startitem()
3370 3321 fm.context(ctx=ctx)
3371 3322 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3372 3323 fm.plain(uipathfn(fn), label=b'grep.filename')
3373 3324
3374 3325 cols = [
3375 3326 (b'rev', b'%d', rev, not plaingrep, b''),
3376 3327 (
3377 3328 b'linenumber',
3378 3329 b'%d',
3379 3330 l.linenum,
3380 3331 opts.get('line_number'),
3381 3332 b'',
3382 3333 ),
3383 3334 ]
3384 3335 if diff:
3385 3336 cols.append(
3386 3337 (
3387 3338 b'change',
3388 3339 b'%s',
3389 3340 change,
3390 3341 True,
3391 3342 b'grep.inserted '
3392 3343 if change == b'+'
3393 3344 else b'grep.deleted ',
3394 3345 )
3395 3346 )
3396 3347 cols.extend(
3397 3348 [
3398 3349 (
3399 3350 b'user',
3400 3351 b'%s',
3401 3352 formatuser(ctx.user()),
3402 3353 opts.get('user'),
3403 3354 b'',
3404 3355 ),
3405 3356 (
3406 3357 b'date',
3407 3358 b'%s',
3408 3359 fm.formatdate(ctx.date(), datefmt),
3409 3360 opts.get('date'),
3410 3361 b'',
3411 3362 ),
3412 3363 ]
3413 3364 )
3414 3365 for name, fmt, data, cond, extra_label in cols:
3415 3366 if cond:
3416 3367 fm.plain(sep, label=b'grep.sep')
3417 3368 field = fieldnamemap.get(name, name)
3418 3369 label = extra_label + (b'grep.%s' % name)
3419 3370 fm.condwrite(cond, field, fmt, data, label=label)
3420 3371 if not opts.get('files_with_matches'):
3421 3372 fm.plain(sep, label=b'grep.sep')
3422 3373 if not opts.get('text') and binary():
3423 3374 fm.plain(_(b" Binary file matches"))
3424 3375 else:
3425 3376 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3426 3377 fm.plain(eol)
3427 3378 found = True
3428 3379 if opts.get('files_with_matches'):
3429 3380 break
3430 3381 return found
3431 3382
3432 3383 def displaymatches(fm, l):
3433 3384 p = 0
3434 3385 for s, e in l.findpos(regexp):
3435 3386 if p < s:
3436 3387 fm.startitem()
3437 3388 fm.write(b'text', b'%s', l.line[p:s])
3438 3389 fm.data(matched=False)
3439 3390 fm.startitem()
3440 3391 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3441 3392 fm.data(matched=True)
3442 3393 p = e
3443 3394 if p < len(l.line):
3444 3395 fm.startitem()
3445 3396 fm.write(b'text', b'%s', l.line[p:])
3446 3397 fm.data(matched=False)
3447 3398 fm.end()
3448 3399
3449 3400 found = False
3450 3401
3451 3402 wopts = logcmdutil.walkopts(
3452 3403 pats=pats,
3453 3404 opts=pycompat.byteskwargs(opts),
3454 3405 revspec=opts['rev'],
3455 3406 include_pats=opts['include'],
3456 3407 exclude_pats=opts['exclude'],
3457 3408 follow=follow,
3458 3409 force_changelog_traversal=all_files,
3459 3410 filter_revisions_by_pats=not all_files,
3460 3411 )
3461 3412 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3462 3413
3463 3414 ui.pager(b'grep')
3464 3415 fm = ui.formatter(b'grep', pycompat.byteskwargs(opts))
3465 3416 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3466 3417 r = display(fm, fn, ctx, pstates, states)
3467 3418 found = found or r
3468 3419 if r and not diff and not all_files:
3469 3420 searcher.skipfile(fn, ctx.rev())
3470 3421 fm.end()
3471 3422
3472 3423 return not found
3473 3424
3474 3425
3475 3426 @command(
3476 3427 b'heads',
3477 3428 [
3478 3429 (
3479 3430 b'r',
3480 3431 b'rev',
3481 3432 b'',
3482 3433 _(b'show only heads which are descendants of STARTREV'),
3483 3434 _(b'STARTREV'),
3484 3435 ),
3485 3436 (b't', b'topo', False, _(b'show topological heads only')),
3486 3437 (
3487 3438 b'a',
3488 3439 b'active',
3489 3440 False,
3490 3441 _(b'show active branchheads only (DEPRECATED)'),
3491 3442 ),
3492 3443 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3493 3444 ]
3494 3445 + templateopts,
3495 3446 _(b'[-ct] [-r STARTREV] [REV]...'),
3496 3447 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3497 3448 intents={INTENT_READONLY},
3498 3449 )
3499 3450 def heads(ui, repo, *branchrevs, **opts):
3500 3451 """show branch heads
3501 3452
3502 3453 With no arguments, show all open branch heads in the repository.
3503 3454 Branch heads are changesets that have no descendants on the
3504 3455 same branch. They are where development generally takes place and
3505 3456 are the usual targets for update and merge operations.
3506 3457
3507 3458 If one or more REVs are given, only open branch heads on the
3508 3459 branches associated with the specified changesets are shown. This
3509 3460 means that you can use :hg:`heads .` to see the heads on the
3510 3461 currently checked-out branch.
3511 3462
3512 3463 If -c/--closed is specified, also show branch heads marked closed
3513 3464 (see :hg:`commit --close-branch`).
3514 3465
3515 3466 If STARTREV is specified, only those heads that are descendants of
3516 3467 STARTREV will be displayed.
3517 3468
3518 3469 If -t/--topo is specified, named branch mechanics will be ignored and only
3519 3470 topological heads (changesets with no children) will be shown.
3520 3471
3521 3472 Returns 0 if matching heads are found, 1 if not.
3522 3473 """
3523 3474
3524 3475 start = None
3525 3476 rev = opts.get('rev')
3526 3477 if rev:
3527 3478 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3528 3479 start = logcmdutil.revsingle(repo, rev, None).node()
3529 3480
3530 3481 if opts.get('topo'):
3531 3482 heads = [repo[h] for h in repo.heads(start)]
3532 3483 else:
3533 3484 heads = []
3534 3485 for branch in repo.branchmap():
3535 3486 heads += repo.branchheads(branch, start, opts.get('closed'))
3536 3487 heads = [repo[h] for h in heads]
3537 3488
3538 3489 if branchrevs:
3539 3490 branches = {
3540 3491 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3541 3492 }
3542 3493 heads = [h for h in heads if h.branch() in branches]
3543 3494
3544 3495 if opts.get('active') and branchrevs:
3545 3496 dagheads = repo.heads(start)
3546 3497 heads = [h for h in heads if h.node() in dagheads]
3547 3498
3548 3499 if branchrevs:
3549 3500 haveheads = {h.branch() for h in heads}
3550 3501 if branches - haveheads:
3551 3502 headless = b', '.join(b for b in branches - haveheads)
3552 3503 msg = _(b'no open branch heads found on branches %s')
3553 3504 if opts.get('rev'):
3554 3505 msg += _(b' (started at %s)') % opts['rev']
3555 3506 ui.warn((msg + b'\n') % headless)
3556 3507
3557 3508 if not heads:
3558 3509 return 1
3559 3510
3560 3511 ui.pager(b'heads')
3561 3512 heads = sorted(heads, key=lambda x: -(x.rev()))
3562 3513 displayer = logcmdutil.changesetdisplayer(
3563 3514 ui, repo, pycompat.byteskwargs(opts)
3564 3515 )
3565 3516 for ctx in heads:
3566 3517 displayer.show(ctx)
3567 3518 displayer.close()
3568 3519
3569 3520
3570 3521 @command(
3571 3522 b'help',
3572 3523 [
3573 3524 (b'e', b'extension', None, _(b'show only help for extensions')),
3574 3525 (b'c', b'command', None, _(b'show only help for commands')),
3575 3526 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3576 3527 (
3577 3528 b's',
3578 3529 b'system',
3579 3530 [],
3580 3531 _(b'show help for specific platform(s)'),
3581 3532 _(b'PLATFORM'),
3582 3533 ),
3583 3534 ],
3584 3535 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3585 3536 helpcategory=command.CATEGORY_HELP,
3586 3537 norepo=True,
3587 3538 intents={INTENT_READONLY},
3588 3539 )
3589 3540 def help_(ui, name=None, **opts):
3590 3541 """show help for a given topic or a help overview
3591 3542
3592 3543 With no arguments, print a list of commands with short help messages.
3593 3544
3594 3545 Given a topic, extension, or command name, print help for that
3595 3546 topic.
3596 3547
3597 3548 Returns 0 if successful.
3598 3549 """
3599 3550
3600 3551 keep = opts.get('system') or []
3601 3552 if len(keep) == 0:
3602 3553 if pycompat.sysplatform.startswith(b'win'):
3603 3554 keep.append(b'windows')
3604 3555 elif pycompat.sysplatform == b'OpenVMS':
3605 3556 keep.append(b'vms')
3606 3557 elif pycompat.sysplatform == b'plan9':
3607 3558 keep.append(b'plan9')
3608 3559 else:
3609 3560 keep.append(b'unix')
3610 3561 keep.append(pycompat.sysplatform.lower())
3611 3562 if ui.verbose:
3612 3563 keep.append(b'verbose')
3613 3564
3614 3565 commands = sys.modules[__name__]
3615 3566 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3616 3567 ui.pager(b'help')
3617 3568 ui.write(formatted)
3618 3569
3619 3570
3620 3571 @command(
3621 3572 b'identify|id',
3622 3573 [
3623 3574 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3624 3575 (b'n', b'num', None, _(b'show local revision number')),
3625 3576 (b'i', b'id', None, _(b'show global revision id')),
3626 3577 (b'b', b'branch', None, _(b'show branch')),
3627 3578 (b't', b'tags', None, _(b'show tags')),
3628 3579 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3629 3580 ]
3630 3581 + remoteopts
3631 3582 + formatteropts,
3632 3583 _(b'[-nibtB] [-r REV] [SOURCE]'),
3633 3584 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3634 3585 optionalrepo=True,
3635 3586 intents={INTENT_READONLY},
3636 3587 )
3637 3588 def identify(
3638 3589 ui,
3639 3590 repo,
3640 3591 source=None,
3641 3592 rev=None,
3642 3593 num=None,
3643 3594 id=None,
3644 3595 branch=None,
3645 3596 tags=None,
3646 3597 bookmarks=None,
3647 3598 **opts,
3648 3599 ):
3649 3600 """identify the working directory or specified revision
3650 3601
3651 3602 Print a summary identifying the repository state at REV using one or
3652 3603 two parent hash identifiers, followed by a "+" if the working
3653 3604 directory has uncommitted changes, the branch name (if not default),
3654 3605 a list of tags, and a list of bookmarks.
3655 3606
3656 3607 When REV is not given, print a summary of the current state of the
3657 3608 repository including the working directory. Specify -r. to get information
3658 3609 of the working directory parent without scanning uncommitted changes.
3659 3610
3660 3611 Specifying a path to a repository root or Mercurial bundle will
3661 3612 cause lookup to operate on that repository/bundle.
3662 3613
3663 3614 .. container:: verbose
3664 3615
3665 3616 Template:
3666 3617
3667 3618 The following keywords are supported in addition to the common template
3668 3619 keywords and functions. See also :hg:`help templates`.
3669 3620
3670 3621 :dirty: String. Character ``+`` denoting if the working directory has
3671 3622 uncommitted changes.
3672 3623 :id: String. One or two nodes, optionally followed by ``+``.
3673 3624 :parents: List of strings. Parent nodes of the changeset.
3674 3625
3675 3626 Examples:
3676 3627
3677 3628 - generate a build identifier for the working directory::
3678 3629
3679 3630 hg id --id > build-id.dat
3680 3631
3681 3632 - find the revision corresponding to a tag::
3682 3633
3683 3634 hg id -n -r 1.3
3684 3635
3685 3636 - check the most recent revision of a remote repository::
3686 3637
3687 3638 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3688 3639
3689 3640 See :hg:`log` for generating more information about specific revisions,
3690 3641 including full hash identifiers.
3691 3642
3692 3643 Returns 0 if successful.
3693 3644 """
3694 3645
3695 3646 opts = pycompat.byteskwargs(opts)
3696 3647 if not repo and not source:
3697 3648 raise error.InputError(
3698 3649 _(b"there is no Mercurial repository here (.hg not found)")
3699 3650 )
3700 3651
3701 3652 default = not (num or id or branch or tags or bookmarks)
3702 3653 output = []
3703 3654 revs = []
3704 3655
3705 3656 peer = None
3706 3657 try:
3707 3658 if source:
3708 3659 path = urlutil.get_unique_pull_path_obj(b'identify', ui, source)
3709 3660 # only pass ui when no repo
3710 3661 peer = hg.peer(repo or ui, opts, path)
3711 3662 repo = peer.local()
3712 3663 branches = (path.branch, [])
3713 3664 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3714 3665
3715 3666 fm = ui.formatter(b'identify', opts)
3716 3667 fm.startitem()
3717 3668
3718 3669 if not repo:
3719 3670 if num or branch or tags:
3720 3671 raise error.InputError(
3721 3672 _(b"can't query remote revision number, branch, or tags")
3722 3673 )
3723 3674 if not rev and revs:
3724 3675 rev = revs[0]
3725 3676 if not rev:
3726 3677 rev = b"tip"
3727 3678
3728 3679 remoterev = peer.lookup(rev)
3729 3680 hexrev = fm.hexfunc(remoterev)
3730 3681 if default or id:
3731 3682 output = [hexrev]
3732 3683 fm.data(id=hexrev)
3733 3684
3734 3685 @util.cachefunc
3735 3686 def getbms():
3736 3687 bms = []
3737 3688
3738 3689 if b'bookmarks' in peer.listkeys(b'namespaces'):
3739 3690 hexremoterev = hex(remoterev)
3740 3691 bms = [
3741 3692 bm
3742 3693 for bm, bmr in peer.listkeys(b'bookmarks').items()
3743 3694 if bmr == hexremoterev
3744 3695 ]
3745 3696
3746 3697 return sorted(bms)
3747 3698
3748 3699 if fm.isplain():
3749 3700 if bookmarks:
3750 3701 output.extend(getbms())
3751 3702 elif default and not ui.quiet:
3752 3703 # multiple bookmarks for a single parent separated by '/'
3753 3704 bm = b'/'.join(getbms())
3754 3705 if bm:
3755 3706 output.append(bm)
3756 3707 else:
3757 3708 fm.data(node=hex(remoterev))
3758 3709 if bookmarks or b'bookmarks' in fm.datahint():
3759 3710 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3760 3711 else:
3761 3712 if rev:
3762 3713 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3763 3714 ctx = logcmdutil.revsingle(repo, rev, None)
3764 3715
3765 3716 if ctx.rev() is None:
3766 3717 ctx = repo[None]
3767 3718 parents = ctx.parents()
3768 3719 taglist = []
3769 3720 for p in parents:
3770 3721 taglist.extend(p.tags())
3771 3722
3772 3723 dirty = b""
3773 3724 if ctx.dirty(missing=True, merge=False, branch=False):
3774 3725 dirty = b'+'
3775 3726 fm.data(dirty=dirty)
3776 3727
3777 3728 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3778 3729 if default or id:
3779 3730 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3780 3731 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3781 3732
3782 3733 if num:
3783 3734 numoutput = [b"%d" % p.rev() for p in parents]
3784 3735 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3785 3736
3786 3737 fm.data(
3787 3738 parents=fm.formatlist(
3788 3739 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3789 3740 )
3790 3741 )
3791 3742 else:
3792 3743 hexoutput = fm.hexfunc(ctx.node())
3793 3744 if default or id:
3794 3745 output = [hexoutput]
3795 3746 fm.data(id=hexoutput)
3796 3747
3797 3748 if num:
3798 3749 output.append(pycompat.bytestr(ctx.rev()))
3799 3750 taglist = ctx.tags()
3800 3751
3801 3752 if default and not ui.quiet:
3802 3753 b = ctx.branch()
3803 3754 if b != b'default':
3804 3755 output.append(b"(%s)" % b)
3805 3756
3806 3757 # multiple tags for a single parent separated by '/'
3807 3758 t = b'/'.join(taglist)
3808 3759 if t:
3809 3760 output.append(t)
3810 3761
3811 3762 # multiple bookmarks for a single parent separated by '/'
3812 3763 bm = b'/'.join(ctx.bookmarks())
3813 3764 if bm:
3814 3765 output.append(bm)
3815 3766 else:
3816 3767 if branch:
3817 3768 output.append(ctx.branch())
3818 3769
3819 3770 if tags:
3820 3771 output.extend(taglist)
3821 3772
3822 3773 if bookmarks:
3823 3774 output.extend(ctx.bookmarks())
3824 3775
3825 3776 fm.data(node=ctx.hex())
3826 3777 fm.data(branch=ctx.branch())
3827 3778 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3828 3779 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3829 3780 fm.context(ctx=ctx)
3830 3781
3831 3782 fm.plain(b"%s\n" % b' '.join(output))
3832 3783 fm.end()
3833 3784 finally:
3834 3785 if peer:
3835 3786 peer.close()
3836 3787
3837 3788
3838 3789 @command(
3839 3790 b'import|patch',
3840 3791 [
3841 3792 (
3842 3793 b'p',
3843 3794 b'strip',
3844 3795 1,
3845 3796 _(
3846 3797 b'directory strip option for patch. This has the same '
3847 3798 b'meaning as the corresponding patch option'
3848 3799 ),
3849 3800 _(b'NUM'),
3850 3801 ),
3851 3802 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3852 3803 (b'', b'secret', None, _(b'use the secret phase for committing')),
3853 3804 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3854 3805 (
3855 3806 b'f',
3856 3807 b'force',
3857 3808 None,
3858 3809 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3859 3810 ),
3860 3811 (
3861 3812 b'',
3862 3813 b'no-commit',
3863 3814 None,
3864 3815 _(b"don't commit, just update the working directory"),
3865 3816 ),
3866 3817 (
3867 3818 b'',
3868 3819 b'bypass',
3869 3820 None,
3870 3821 _(b"apply patch without touching the working directory"),
3871 3822 ),
3872 3823 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3873 3824 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3874 3825 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3875 3826 (
3876 3827 b'',
3877 3828 b'import-branch',
3878 3829 None,
3879 3830 _(b'use any branch information in patch (implied by --exact)'),
3880 3831 ),
3881 3832 ]
3882 3833 + commitopts
3883 3834 + commitopts2
3884 3835 + similarityopts,
3885 3836 _(b'[OPTION]... PATCH...'),
3886 3837 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3887 3838 )
3888 3839 def import_(ui, repo, patch1=None, *patches, **opts):
3889 3840 """import an ordered set of patches
3890 3841
3891 3842 Import a list of patches and commit them individually (unless
3892 3843 --no-commit is specified).
3893 3844
3894 3845 To read a patch from standard input (stdin), use "-" as the patch
3895 3846 name. If a URL is specified, the patch will be downloaded from
3896 3847 there.
3897 3848
3898 3849 Import first applies changes to the working directory (unless
3899 3850 --bypass is specified), import will abort if there are outstanding
3900 3851 changes.
3901 3852
3902 3853 Use --bypass to apply and commit patches directly to the
3903 3854 repository, without affecting the working directory. Without
3904 3855 --exact, patches will be applied on top of the working directory
3905 3856 parent revision.
3906 3857
3907 3858 You can import a patch straight from a mail message. Even patches
3908 3859 as attachments work (to use the body part, it must have type
3909 3860 text/plain or text/x-patch). From and Subject headers of email
3910 3861 message are used as default committer and commit message. All
3911 3862 text/plain body parts before first diff are added to the commit
3912 3863 message.
3913 3864
3914 3865 If the imported patch was generated by :hg:`export`, user and
3915 3866 description from patch override values from message headers and
3916 3867 body. Values given on command line with -m/--message and -u/--user
3917 3868 override these.
3918 3869
3919 3870 If --exact is specified, import will set the working directory to
3920 3871 the parent of each patch before applying it, and will abort if the
3921 3872 resulting changeset has a different ID than the one recorded in
3922 3873 the patch. This will guard against various ways that portable
3923 3874 patch formats and mail systems might fail to transfer Mercurial
3924 3875 data or metadata. See :hg:`bundle` for lossless transmission.
3925 3876
3926 3877 Use --partial to ensure a changeset will be created from the patch
3927 3878 even if some hunks fail to apply. Hunks that fail to apply will be
3928 3879 written to a <target-file>.rej file. Conflicts can then be resolved
3929 3880 by hand before :hg:`commit --amend` is run to update the created
3930 3881 changeset. This flag exists to let people import patches that
3931 3882 partially apply without losing the associated metadata (author,
3932 3883 date, description, ...).
3933 3884
3934 3885 .. note::
3935 3886
3936 3887 When no hunks apply cleanly, :hg:`import --partial` will create
3937 3888 an empty changeset, importing only the patch metadata.
3938 3889
3939 3890 With -s/--similarity, hg will attempt to discover renames and
3940 3891 copies in the patch in the same way as :hg:`addremove`.
3941 3892
3942 3893 It is possible to use external patch programs to perform the patch
3943 3894 by setting the ``ui.patch`` configuration option. For the default
3944 3895 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3945 3896 See :hg:`help config` for more information about configuration
3946 3897 files and how to use these options.
3947 3898
3948 3899 See :hg:`help dates` for a list of formats valid for -d/--date.
3949 3900
3950 3901 .. container:: verbose
3951 3902
3952 3903 Examples:
3953 3904
3954 3905 - import a traditional patch from a website and detect renames::
3955 3906
3956 3907 hg import -s 80 http://example.com/bugfix.patch
3957 3908
3958 3909 - import a changeset from an hgweb server::
3959 3910
3960 3911 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3961 3912
3962 3913 - import all the patches in an Unix-style mbox::
3963 3914
3964 3915 hg import incoming-patches.mbox
3965 3916
3966 3917 - import patches from stdin::
3967 3918
3968 3919 hg import -
3969 3920
3970 3921 - attempt to exactly restore an exported changeset (not always
3971 3922 possible)::
3972 3923
3973 3924 hg import --exact proposed-fix.patch
3974 3925
3975 3926 - use an external tool to apply a patch which is too fuzzy for
3976 3927 the default internal tool.
3977 3928
3978 3929 hg import --config ui.patch="patch --merge" fuzzy.patch
3979 3930
3980 3931 - change the default fuzzing from 2 to a less strict 7
3981 3932
3982 3933 hg import --config ui.fuzz=7 fuzz.patch
3983 3934
3984 3935 Returns 0 on success, 1 on partial success (see --partial).
3985 3936 """
3986 3937
3987 3938 cmdutil.check_incompatible_arguments(
3988 3939 opts, 'no_commit', ['bypass', 'secret']
3989 3940 )
3990 3941 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
3991 3942
3992 3943 if not patch1:
3993 3944 raise error.InputError(_(b'need at least one patch to import'))
3994 3945
3995 3946 patches = (patch1,) + patches
3996 3947
3997 3948 date = opts.get('date')
3998 3949 if date:
3999 3950 opts['date'] = dateutil.parsedate(date)
4000 3951
4001 3952 exact = opts.get('exact')
4002 3953 update = not opts.get('bypass')
4003 3954 try:
4004 3955 sim = float(opts.get('similarity') or 0)
4005 3956 except ValueError:
4006 3957 raise error.InputError(_(b'similarity must be a number'))
4007 3958 if sim < 0 or sim > 100:
4008 3959 raise error.InputError(_(b'similarity must be between 0 and 100'))
4009 3960 if sim and not update:
4010 3961 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4011 3962
4012 3963 base = opts["base"]
4013 3964 msgs = []
4014 3965 ret = 0
4015 3966
4016 3967 with repo.wlock():
4017 3968 if update:
4018 3969 cmdutil.checkunfinished(repo)
4019 3970 if exact or not opts.get('force'):
4020 3971 cmdutil.bailifchanged(repo)
4021 3972
4022 3973 if not opts.get('no_commit'):
4023 3974 lock = repo.lock
4024 3975 tr = lambda: repo.transaction(b'import')
4025 3976 else:
4026 3977 lock = util.nullcontextmanager
4027 3978 tr = util.nullcontextmanager
4028 3979 with lock(), tr():
4029 3980 parents = repo[None].parents()
4030 3981 for patchurl in patches:
4031 3982 if patchurl == b'-':
4032 3983 ui.status(_(b'applying patch from stdin\n'))
4033 3984 patchfile = ui.fin
4034 3985 patchurl = b'stdin' # for error message
4035 3986 else:
4036 3987 patchurl = os.path.join(base, patchurl)
4037 3988 ui.status(_(b'applying %s\n') % patchurl)
4038 3989 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4039 3990
4040 3991 haspatch = False
4041 3992 for hunk in patch.split(patchfile):
4042 3993 with patch.extract(ui, hunk) as patchdata:
4043 3994 msg, node, rej = cmdutil.tryimportone(
4044 3995 ui,
4045 3996 repo,
4046 3997 patchdata,
4047 3998 parents,
4048 3999 pycompat.byteskwargs(opts),
4049 4000 msgs,
4050 4001 hg.clean,
4051 4002 )
4052 4003 if msg:
4053 4004 haspatch = True
4054 4005 ui.note(msg + b'\n')
4055 4006 if update or exact:
4056 4007 parents = repo[None].parents()
4057 4008 else:
4058 4009 parents = [repo[node]]
4059 4010 if rej:
4060 4011 ui.write_err(_(b"patch applied partially\n"))
4061 4012 ui.write_err(
4062 4013 _(
4063 4014 b"(fix the .rej files and run "
4064 4015 b"`hg commit --amend`)\n"
4065 4016 )
4066 4017 )
4067 4018 ret = 1
4068 4019 break
4069 4020
4070 4021 if not haspatch:
4071 4022 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4072 4023
4073 4024 if msgs:
4074 4025 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4075 4026 return ret
4076 4027
4077 4028
4078 4029 @command(
4079 4030 b'incoming|in',
4080 4031 [
4081 4032 (
4082 4033 b'f',
4083 4034 b'force',
4084 4035 None,
4085 4036 _(b'run even if remote repository is unrelated'),
4086 4037 ),
4087 4038 (b'n', b'newest-first', None, _(b'show newest record first')),
4088 4039 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4089 4040 (
4090 4041 b'r',
4091 4042 b'rev',
4092 4043 [],
4093 4044 _(b'a remote changeset intended to be added'),
4094 4045 _(b'REV'),
4095 4046 ),
4096 4047 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4097 4048 (
4098 4049 b'b',
4099 4050 b'branch',
4100 4051 [],
4101 4052 _(b'a specific branch you would like to pull'),
4102 4053 _(b'BRANCH'),
4103 4054 ),
4104 4055 ]
4105 4056 + logopts
4106 4057 + remoteopts
4107 4058 + subrepoopts,
4108 4059 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4109 4060 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4110 4061 )
4111 4062 def incoming(ui, repo, source=b"default", **opts):
4112 4063 """show new changesets found in source
4113 4064
4114 4065 Show new changesets found in the specified path/URL or the default
4115 4066 pull location. These are the changesets that would have been pulled
4116 4067 by :hg:`pull` at the time you issued this command.
4117 4068
4118 4069 See pull for valid source format details.
4119 4070
4120 4071 .. container:: verbose
4121 4072
4122 4073 With -B/--bookmarks, the result of bookmark comparison between
4123 4074 local and remote repositories is displayed. With -v/--verbose,
4124 4075 status is also displayed for each bookmark like below::
4125 4076
4126 4077 BM1 01234567890a added
4127 4078 BM2 1234567890ab advanced
4128 4079 BM3 234567890abc diverged
4129 4080 BM4 34567890abcd changed
4130 4081
4131 4082 The action taken locally when pulling depends on the
4132 4083 status of each bookmark:
4133 4084
4134 4085 :``added``: pull will create it
4135 4086 :``advanced``: pull will update it
4136 4087 :``diverged``: pull will create a divergent bookmark
4137 4088 :``changed``: result depends on remote changesets
4138 4089
4139 4090 From the point of view of pulling behavior, bookmark
4140 4091 existing only in the remote repository are treated as ``added``,
4141 4092 even if it is in fact locally deleted.
4142 4093
4143 4094 .. container:: verbose
4144 4095
4145 4096 For remote repository, using --bundle avoids downloading the
4146 4097 changesets twice if the incoming is followed by a pull.
4147 4098
4148 4099 Examples:
4149 4100
4150 4101 - show incoming changes with patches and full description::
4151 4102
4152 4103 hg incoming -vp
4153 4104
4154 4105 - show incoming changes excluding merges, store a bundle::
4155 4106
4156 4107 hg in -vpM --bundle incoming.hg
4157 4108 hg pull incoming.hg
4158 4109
4159 4110 - briefly list changes inside a bundle::
4160 4111
4161 4112 hg in changes.hg -T "{desc|firstline}\\n"
4162 4113
4163 4114 Returns 0 if there are incoming changes, 1 otherwise.
4164 4115 """
4165 4116 opts = pycompat.byteskwargs(opts)
4166 4117 if opts.get(b'graph'):
4167 4118 logcmdutil.checkunsupportedgraphflags([], opts)
4168 4119
4169 4120 def display(other, chlist, displayer):
4170 4121 revdag = logcmdutil.graphrevs(other, chlist, opts)
4171 4122 logcmdutil.displaygraph(
4172 4123 ui, repo, revdag, displayer, graphmod.asciiedges
4173 4124 )
4174 4125
4175 4126 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4176 4127 return 0
4177 4128
4178 4129 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4179 4130
4180 4131 if opts.get(b'bookmarks'):
4181 4132 srcs = urlutil.get_pull_paths(repo, ui, [source])
4182 4133 for path in srcs:
4183 4134 # XXX the "branches" options are not used. Should it be used?
4184 4135 other = hg.peer(repo, opts, path)
4185 4136 try:
4186 4137 if b'bookmarks' not in other.listkeys(b'namespaces'):
4187 4138 ui.warn(_(b"remote doesn't support bookmarks\n"))
4188 4139 return 0
4189 4140 ui.pager(b'incoming')
4190 4141 ui.status(
4191 4142 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4192 4143 )
4193 4144 return bookmarks.incoming(
4194 4145 ui, repo, other, mode=path.bookmarks_mode
4195 4146 )
4196 4147 finally:
4197 4148 other.close()
4198 4149
4199 4150 return hg.incoming(ui, repo, source, opts)
4200 4151
4201 4152
4202 4153 @command(
4203 4154 b'init',
4204 4155 remoteopts,
4205 4156 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4206 4157 helpcategory=command.CATEGORY_REPO_CREATION,
4207 4158 helpbasic=True,
4208 4159 norepo=True,
4209 4160 )
4210 4161 def init(ui, dest=b".", **opts):
4211 4162 """create a new repository in the given directory
4212 4163
4213 4164 Initialize a new repository in the given directory. If the given
4214 4165 directory does not exist, it will be created.
4215 4166
4216 4167 If no directory is given, the current directory is used.
4217 4168
4218 4169 It is possible to specify an ``ssh://`` URL as the destination.
4219 4170 See :hg:`help urls` for more information.
4220 4171
4221 4172 Returns 0 on success.
4222 4173 """
4223 4174 opts = pycompat.byteskwargs(opts)
4224 4175 path = urlutil.get_clone_path_obj(ui, dest)
4225 4176 peer = hg.peer(ui, opts, path, create=True)
4226 4177 peer.close()
4227 4178
4228 4179
4229 4180 @command(
4230 4181 b'locate',
4231 4182 [
4232 4183 (
4233 4184 b'r',
4234 4185 b'rev',
4235 4186 b'',
4236 4187 _(b'search the repository as it is in REV'),
4237 4188 _(b'REV'),
4238 4189 ),
4239 4190 (
4240 4191 b'0',
4241 4192 b'print0',
4242 4193 None,
4243 4194 _(b'end filenames with NUL, for use with xargs'),
4244 4195 ),
4245 4196 (
4246 4197 b'f',
4247 4198 b'fullpath',
4248 4199 None,
4249 4200 _(b'print complete paths from the filesystem root'),
4250 4201 ),
4251 4202 ]
4252 4203 + walkopts,
4253 4204 _(b'[OPTION]... [PATTERN]...'),
4254 4205 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4255 4206 )
4256 4207 def locate(ui, repo, *pats, **opts):
4257 4208 """locate files matching specific patterns (DEPRECATED)
4258 4209
4259 4210 Print files under Mercurial control in the working directory whose
4260 4211 names match the given patterns.
4261 4212
4262 4213 By default, this command searches all directories in the working
4263 4214 directory. To search just the current directory and its
4264 4215 subdirectories, use "--include .".
4265 4216
4266 4217 If no patterns are given to match, this command prints the names
4267 4218 of all files under Mercurial control in the working directory.
4268 4219
4269 4220 If you want to feed the output of this command into the "xargs"
4270 4221 command, use the -0 option to both this command and "xargs". This
4271 4222 will avoid the problem of "xargs" treating single filenames that
4272 4223 contain whitespace as multiple filenames.
4273 4224
4274 4225 See :hg:`help files` for a more versatile command.
4275 4226
4276 4227 Returns 0 if a match is found, 1 otherwise.
4277 4228 """
4278 4229 if opts.get('print0'):
4279 4230 end = b'\0'
4280 4231 else:
4281 4232 end = b'\n'
4282 4233 ctx = logcmdutil.revsingle(repo, opts.get('rev'), None)
4283 4234
4284 4235 ret = 1
4285 4236 m = scmutil.match(
4286 4237 ctx,
4287 4238 pats,
4288 4239 pycompat.byteskwargs(opts),
4289 4240 default=b'relglob',
4290 4241 badfn=lambda x, y: False,
4291 4242 )
4292 4243
4293 4244 ui.pager(b'locate')
4294 4245 if ctx.rev() is None:
4295 4246 # When run on the working copy, "locate" includes removed files, so
4296 4247 # we get the list of files from the dirstate.
4297 4248 filesgen = sorted(repo.dirstate.matches(m))
4298 4249 else:
4299 4250 filesgen = ctx.matches(m)
4300 4251 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4301 4252 for abs in filesgen:
4302 4253 if opts.get('fullpath'):
4303 4254 ui.write(repo.wjoin(abs), end)
4304 4255 else:
4305 4256 ui.write(uipathfn(abs), end)
4306 4257 ret = 0
4307 4258
4308 4259 return ret
4309 4260
4310 4261
4311 4262 @command(
4312 4263 b'log|history',
4313 4264 [
4314 4265 (
4315 4266 b'f',
4316 4267 b'follow',
4317 4268 None,
4318 4269 _(
4319 4270 b'follow changeset history, or file history across copies and renames'
4320 4271 ),
4321 4272 ),
4322 4273 (
4323 4274 b'',
4324 4275 b'follow-first',
4325 4276 None,
4326 4277 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4327 4278 ),
4328 4279 (
4329 4280 b'd',
4330 4281 b'date',
4331 4282 b'',
4332 4283 _(b'show revisions matching date spec'),
4333 4284 _(b'DATE'),
4334 4285 ),
4335 4286 (b'C', b'copies', None, _(b'show copied files')),
4336 4287 (
4337 4288 b'k',
4338 4289 b'keyword',
4339 4290 [],
4340 4291 _(b'do case-insensitive search for a given text'),
4341 4292 _(b'TEXT'),
4342 4293 ),
4343 4294 (
4344 4295 b'r',
4345 4296 b'rev',
4346 4297 [],
4347 4298 _(b'revisions to select or follow from'),
4348 4299 _(b'REV'),
4349 4300 ),
4350 4301 (
4351 4302 b'L',
4352 4303 b'line-range',
4353 4304 [],
4354 4305 _(b'follow line range of specified file (EXPERIMENTAL)'),
4355 4306 _(b'FILE,RANGE'),
4356 4307 ),
4357 4308 (
4358 4309 b'',
4359 4310 b'removed',
4360 4311 None,
4361 4312 _(b'include revisions where files were removed'),
4362 4313 ),
4363 4314 (
4364 4315 b'm',
4365 4316 b'only-merges',
4366 4317 None,
4367 4318 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4368 4319 ),
4369 4320 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4370 4321 (
4371 4322 b'',
4372 4323 b'only-branch',
4373 4324 [],
4374 4325 _(
4375 4326 b'show only changesets within the given named branch (DEPRECATED)'
4376 4327 ),
4377 4328 _(b'BRANCH'),
4378 4329 ),
4379 4330 (
4380 4331 b'b',
4381 4332 b'branch',
4382 4333 [],
4383 4334 _(b'show changesets within the given named branch'),
4384 4335 _(b'BRANCH'),
4385 4336 ),
4386 4337 (
4387 4338 b'B',
4388 4339 b'bookmark',
4389 4340 [],
4390 4341 _(b"show changesets within the given bookmark"),
4391 4342 _(b'BOOKMARK'),
4392 4343 ),
4393 4344 (
4394 4345 b'P',
4395 4346 b'prune',
4396 4347 [],
4397 4348 _(b'do not display revision or any of its ancestors'),
4398 4349 _(b'REV'),
4399 4350 ),
4400 4351 ]
4401 4352 + logopts
4402 4353 + walkopts,
4403 4354 _(b'[OPTION]... [FILE]'),
4404 4355 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4405 4356 helpbasic=True,
4406 4357 inferrepo=True,
4407 4358 intents={INTENT_READONLY},
4408 4359 )
4409 4360 def log(ui, repo, *pats, **opts):
4410 4361 """show revision history of entire repository or files
4411 4362
4412 4363 Print the revision history of the specified files or the entire
4413 4364 project.
4414 4365
4415 4366 If no revision range is specified, the default is ``tip:0`` unless
4416 4367 --follow is set.
4417 4368
4418 4369 File history is shown without following rename or copy history of
4419 4370 files. Use -f/--follow with a filename to follow history across
4420 4371 renames and copies. --follow without a filename will only show
4421 4372 ancestors of the starting revisions. The starting revisions can be
4422 4373 specified by -r/--rev, which default to the working directory parent.
4423 4374
4424 4375 By default this command prints revision number and changeset id,
4425 4376 tags, non-trivial parents, user, date and time, and a summary for
4426 4377 each commit. When the -v/--verbose switch is used, the list of
4427 4378 changed files and full commit message are shown.
4428 4379
4429 4380 With --graph the revisions are shown as an ASCII art DAG with the most
4430 4381 recent changeset at the top.
4431 4382 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4432 4383 involved in an unresolved merge conflict, '_' closes a branch,
4433 4384 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4434 4385 changeset from the lines below is a parent of the 'o' merge on the same
4435 4386 line.
4436 4387 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4437 4388 of a '|' indicates one or more revisions in a path are omitted.
4438 4389
4439 4390 .. container:: verbose
4440 4391
4441 4392 Use -L/--line-range FILE,M:N options to follow the history of lines
4442 4393 from M to N in FILE. With -p/--patch only diff hunks affecting
4443 4394 specified line range will be shown. This option requires --follow;
4444 4395 it can be specified multiple times. Currently, this option is not
4445 4396 compatible with --graph. This option is experimental.
4446 4397
4447 4398 .. note::
4448 4399
4449 4400 :hg:`log --patch` may generate unexpected diff output for merge
4450 4401 changesets, as it will only compare the merge changeset against
4451 4402 its first parent. Also, only files different from BOTH parents
4452 4403 will appear in files:.
4453 4404
4454 4405 .. note::
4455 4406
4456 4407 For performance reasons, :hg:`log FILE` may omit duplicate changes
4457 4408 made on branches and will not show removals or mode changes. To
4458 4409 see all such changes, use the --removed switch.
4459 4410
4460 4411 .. container:: verbose
4461 4412
4462 4413 .. note::
4463 4414
4464 4415 The history resulting from -L/--line-range options depends on diff
4465 4416 options; for instance if white-spaces are ignored, respective changes
4466 4417 with only white-spaces in specified line range will not be listed.
4467 4418
4468 4419 .. container:: verbose
4469 4420
4470 4421 Some examples:
4471 4422
4472 4423 - changesets with full descriptions and file lists::
4473 4424
4474 4425 hg log -v
4475 4426
4476 4427 - changesets ancestral to the working directory::
4477 4428
4478 4429 hg log -f
4479 4430
4480 4431 - last 10 commits on the current branch::
4481 4432
4482 4433 hg log -l 10 -b .
4483 4434
4484 4435 - changesets showing all modifications of a file, including removals::
4485 4436
4486 4437 hg log --removed file.c
4487 4438
4488 4439 - all changesets that touch a directory, with diffs, excluding merges::
4489 4440
4490 4441 hg log -Mp lib/
4491 4442
4492 4443 - all revision numbers that match a keyword::
4493 4444
4494 4445 hg log -k bug --template "{rev}\\n"
4495 4446
4496 4447 - the full hash identifier of the working directory parent::
4497 4448
4498 4449 hg log -r . --template "{node}\\n"
4499 4450
4500 4451 - list available log templates::
4501 4452
4502 4453 hg log -T list
4503 4454
4504 4455 - check if a given changeset is included in a tagged release::
4505 4456
4506 4457 hg log -r "a21ccf and ancestor(1.9)"
4507 4458
4508 4459 - find all changesets by some user in a date range::
4509 4460
4510 4461 hg log -k alice -d "may 2008 to jul 2008"
4511 4462
4512 4463 - summary of all changesets after the last tag::
4513 4464
4514 4465 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4515 4466
4516 4467 - changesets touching lines 13 to 23 for file.c::
4517 4468
4518 4469 hg log -L file.c,13:23
4519 4470
4520 4471 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4521 4472 main.c with patch::
4522 4473
4523 4474 hg log -L file.c,13:23 -L main.c,2:6 -p
4524 4475
4525 4476 See :hg:`help dates` for a list of formats valid for -d/--date.
4526 4477
4527 4478 See :hg:`help revisions` for more about specifying and ordering
4528 4479 revisions.
4529 4480
4530 4481 See :hg:`help templates` for more about pre-packaged styles and
4531 4482 specifying custom templates. The default template used by the log
4532 4483 command can be customized via the ``command-templates.log`` configuration
4533 4484 setting.
4534 4485
4535 4486 Returns 0 on success.
4536 4487
4537 4488 """
4538 4489 opts = pycompat.byteskwargs(opts)
4539 4490 linerange = opts.get(b'line_range')
4540 4491
4541 4492 if linerange and not opts.get(b'follow'):
4542 4493 raise error.InputError(_(b'--line-range requires --follow'))
4543 4494
4544 4495 if linerange and pats:
4545 4496 # TODO: take pats as patterns with no line-range filter
4546 4497 raise error.InputError(
4547 4498 _(b'FILE arguments are not compatible with --line-range option')
4548 4499 )
4549 4500
4550 4501 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4551 4502 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4552 4503 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4553 4504 if linerange:
4554 4505 # TODO: should follow file history from logcmdutil._initialrevs(),
4555 4506 # then filter the result by logcmdutil._makerevset() and --limit
4556 4507 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4557 4508
4558 4509 getcopies = None
4559 4510 if opts.get(b'copies'):
4560 4511 endrev = None
4561 4512 if revs:
4562 4513 endrev = revs.max() + 1
4563 4514 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4564 4515
4565 4516 ui.pager(b'log')
4566 4517 displayer = logcmdutil.changesetdisplayer(
4567 4518 ui, repo, opts, differ, buffered=True
4568 4519 )
4569 4520 if opts.get(b'graph'):
4570 4521 displayfn = logcmdutil.displaygraphrevs
4571 4522 else:
4572 4523 displayfn = logcmdutil.displayrevs
4573 4524 displayfn(ui, repo, revs, displayer, getcopies)
4574 4525
4575 4526
4576 4527 @command(
4577 4528 b'manifest',
4578 4529 [
4579 4530 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4580 4531 (b'', b'all', False, _(b"list files from all revisions")),
4581 4532 ]
4582 4533 + formatteropts,
4583 4534 _(b'[-r REV]'),
4584 4535 helpcategory=command.CATEGORY_MAINTENANCE,
4585 4536 intents={INTENT_READONLY},
4586 4537 )
4587 4538 def manifest(ui, repo, node=None, rev=None, **opts):
4588 4539 """output the current or given revision of the project manifest
4589 4540
4590 4541 Print a list of version controlled files for the given revision.
4591 4542 If no revision is given, the first parent of the working directory
4592 4543 is used, or the null revision if no revision is checked out.
4593 4544
4594 4545 With -v, print file permissions, symlink and executable bits.
4595 4546 With --debug, print file revision hashes.
4596 4547
4597 4548 If option --all is specified, the list of all files from all revisions
4598 4549 is printed. This includes deleted and renamed files.
4599 4550
4600 4551 Returns 0 on success.
4601 4552 """
4602 4553 fm = ui.formatter(b'manifest', pycompat.byteskwargs(opts))
4603 4554
4604 4555 if opts.get('all'):
4605 4556 if rev or node:
4606 4557 raise error.InputError(_(b"can't specify a revision with --all"))
4607 4558
4608 4559 res = set()
4609 4560 for rev in repo:
4610 4561 ctx = repo[rev]
4611 4562 res |= set(ctx.files())
4612 4563
4613 4564 ui.pager(b'manifest')
4614 4565 for f in sorted(res):
4615 4566 fm.startitem()
4616 4567 fm.write(b"path", b'%s\n', f)
4617 4568 fm.end()
4618 4569 return
4619 4570
4620 4571 if rev and node:
4621 4572 raise error.InputError(_(b"please specify just one revision"))
4622 4573
4623 4574 if not node:
4624 4575 node = rev
4625 4576
4626 4577 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4627 4578 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4628 4579 if node:
4629 4580 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4630 4581 ctx = logcmdutil.revsingle(repo, node)
4631 4582 mf = ctx.manifest()
4632 4583 ui.pager(b'manifest')
4633 4584 for f in ctx:
4634 4585 fm.startitem()
4635 4586 fm.context(ctx=ctx)
4636 4587 fl = ctx[f].flags()
4637 4588 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4638 4589 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4639 4590 fm.write(b'path', b'%s\n', f)
4640 4591 fm.end()
4641 4592
4642 4593
4643 4594 @command(
4644 4595 b'merge',
4645 4596 [
4646 4597 (
4647 4598 b'f',
4648 4599 b'force',
4649 4600 None,
4650 4601 _(b'force a merge including outstanding changes (DEPRECATED)'),
4651 4602 ),
4652 4603 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4653 4604 (
4654 4605 b'P',
4655 4606 b'preview',
4656 4607 None,
4657 4608 _(b'review revisions to merge (no merge is performed)'),
4658 4609 ),
4659 4610 (b'', b'abort', None, _(b'abort the ongoing merge')),
4660 4611 ]
4661 4612 + mergetoolopts,
4662 4613 _(b'[-P] [[-r] REV]'),
4663 4614 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4664 4615 helpbasic=True,
4665 4616 )
4666 4617 def merge(ui, repo, node=None, **opts):
4667 4618 """merge another revision into working directory
4668 4619
4669 4620 The current working directory is updated with all changes made in
4670 4621 the requested revision since the last common predecessor revision.
4671 4622
4672 4623 Files that changed between either parent are marked as changed for
4673 4624 the next commit and a commit must be performed before any further
4674 4625 updates to the repository are allowed. The next commit will have
4675 4626 two parents.
4676 4627
4677 4628 ``--tool`` can be used to specify the merge tool used for file
4678 4629 merges. It overrides the HGMERGE environment variable and your
4679 4630 configuration files. See :hg:`help merge-tools` for options.
4680 4631
4681 4632 If no revision is specified, the working directory's parent is a
4682 4633 head revision, and the current branch contains exactly one other
4683 4634 head, the other head is merged with by default. Otherwise, an
4684 4635 explicit revision with which to merge must be provided.
4685 4636
4686 4637 See :hg:`help resolve` for information on handling file conflicts.
4687 4638
4688 4639 To undo an uncommitted merge, use :hg:`merge --abort` which
4689 4640 will check out a clean copy of the original merge parent, losing
4690 4641 all changes.
4691 4642
4692 4643 Returns 0 on success, 1 if there are unresolved files.
4693 4644 """
4694 4645
4695 4646 abort = opts.get('abort')
4696 4647 if abort and repo.dirstate.p2() == repo.nullid:
4697 4648 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4698 4649 cmdutil.check_incompatible_arguments(opts, 'abort', ['rev', 'preview'])
4699 4650 if abort:
4700 4651 state = cmdutil.getunfinishedstate(repo)
4701 4652 if state and state._opname != b'merge':
4702 4653 raise error.StateError(
4703 4654 _(b'cannot abort merge with %s in progress') % (state._opname),
4704 4655 hint=state.hint(),
4705 4656 )
4706 4657 if node:
4707 4658 raise error.InputError(_(b"cannot specify a node with --abort"))
4708 4659 return hg.abortmerge(repo.ui, repo)
4709 4660
4710 4661 if opts.get('rev') and node:
4711 4662 raise error.InputError(_(b"please specify just one revision"))
4712 4663 if not node:
4713 4664 node = opts.get('rev')
4714 4665
4715 4666 if node:
4716 4667 ctx = logcmdutil.revsingle(repo, node)
4717 4668 else:
4718 4669 if ui.configbool(b'commands', b'merge.require-rev'):
4719 4670 raise error.InputError(
4720 4671 _(
4721 4672 b'configuration requires specifying revision to merge '
4722 4673 b'with'
4723 4674 )
4724 4675 )
4725 4676 ctx = repo[destutil.destmerge(repo)]
4726 4677
4727 4678 if ctx.node() is None:
4728 4679 raise error.InputError(
4729 4680 _(b'merging with the working copy has no effect')
4730 4681 )
4731 4682
4732 4683 if opts.get('preview'):
4733 4684 # find nodes that are ancestors of p2 but not of p1
4734 4685 p1 = repo[b'.'].node()
4735 4686 p2 = ctx.node()
4736 4687 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4737 4688
4738 4689 displayer = logcmdutil.changesetdisplayer(
4739 4690 ui, repo, pycompat.byteskwargs(opts)
4740 4691 )
4741 4692 for node in nodes:
4742 4693 displayer.show(repo[node])
4743 4694 displayer.close()
4744 4695 return 0
4745 4696
4746 4697 # ui.forcemerge is an internal variable, do not document
4747 4698 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
4748 4699 with ui.configoverride(overrides, b'merge'):
4749 4700 force = opts.get('force')
4750 4701 labels = [b'working copy', b'merge rev', b'common ancestor']
4751 4702 return hg.merge(ctx, force=force, labels=labels)
4752 4703
4753 4704
4754 4705 statemod.addunfinished(
4755 4706 b'merge',
4756 4707 fname=None,
4757 4708 clearable=True,
4758 4709 allowcommit=True,
4759 4710 cmdmsg=_(b'outstanding uncommitted merge'),
4760 4711 abortfunc=hg.abortmerge,
4761 4712 statushint=_(
4762 4713 b'To continue: hg commit\nTo abort: hg merge --abort'
4763 4714 ),
4764 4715 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4765 4716 )
4766 4717
4767 4718
4768 4719 @command(
4769 4720 b'outgoing|out',
4770 4721 [
4771 4722 (
4772 4723 b'f',
4773 4724 b'force',
4774 4725 None,
4775 4726 _(b'run even when the destination is unrelated'),
4776 4727 ),
4777 4728 (
4778 4729 b'r',
4779 4730 b'rev',
4780 4731 [],
4781 4732 _(b'a changeset intended to be included in the destination'),
4782 4733 _(b'REV'),
4783 4734 ),
4784 4735 (b'n', b'newest-first', None, _(b'show newest record first')),
4785 4736 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4786 4737 (
4787 4738 b'b',
4788 4739 b'branch',
4789 4740 [],
4790 4741 _(b'a specific branch you would like to push'),
4791 4742 _(b'BRANCH'),
4792 4743 ),
4793 4744 ]
4794 4745 + logopts
4795 4746 + remoteopts
4796 4747 + subrepoopts,
4797 4748 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4798 4749 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4799 4750 )
4800 4751 def outgoing(ui, repo, *dests, **opts):
4801 4752 """show changesets not found in the destination
4802 4753
4803 4754 Show changesets not found in the specified destination repository
4804 4755 or the default push location. These are the changesets that would
4805 4756 be pushed if a push was requested.
4806 4757
4807 4758 See pull for details of valid destination formats.
4808 4759
4809 4760 .. container:: verbose
4810 4761
4811 4762 With -B/--bookmarks, the result of bookmark comparison between
4812 4763 local and remote repositories is displayed. With -v/--verbose,
4813 4764 status is also displayed for each bookmark like below::
4814 4765
4815 4766 BM1 01234567890a added
4816 4767 BM2 deleted
4817 4768 BM3 234567890abc advanced
4818 4769 BM4 34567890abcd diverged
4819 4770 BM5 4567890abcde changed
4820 4771
4821 4772 The action taken when pushing depends on the
4822 4773 status of each bookmark:
4823 4774
4824 4775 :``added``: push with ``-B`` will create it
4825 4776 :``deleted``: push with ``-B`` will delete it
4826 4777 :``advanced``: push will update it
4827 4778 :``diverged``: push with ``-B`` will update it
4828 4779 :``changed``: push with ``-B`` will update it
4829 4780
4830 4781 From the point of view of pushing behavior, bookmarks
4831 4782 existing only in the remote repository are treated as
4832 4783 ``deleted``, even if it is in fact added remotely.
4833 4784
4834 4785 Returns 0 if there are outgoing changes, 1 otherwise.
4835 4786 """
4836 4787 opts = pycompat.byteskwargs(opts)
4837 4788 if opts.get(b'bookmarks'):
4838 4789 for path in urlutil.get_push_paths(repo, ui, dests):
4839 4790 other = hg.peer(repo, opts, path)
4840 4791 try:
4841 4792 if b'bookmarks' not in other.listkeys(b'namespaces'):
4842 4793 ui.warn(_(b"remote doesn't support bookmarks\n"))
4843 4794 return 0
4844 4795 ui.status(
4845 4796 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4846 4797 )
4847 4798 ui.pager(b'outgoing')
4848 4799 return bookmarks.outgoing(ui, repo, other)
4849 4800 finally:
4850 4801 other.close()
4851 4802
4852 4803 return hg.outgoing(ui, repo, dests, opts)
4853 4804
4854 4805
4855 4806 @command(
4856 4807 b'parents',
4857 4808 [
4858 4809 (
4859 4810 b'r',
4860 4811 b'rev',
4861 4812 b'',
4862 4813 _(b'show parents of the specified revision'),
4863 4814 _(b'REV'),
4864 4815 ),
4865 4816 ]
4866 4817 + templateopts,
4867 4818 _(b'[-r REV] [FILE]'),
4868 4819 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4869 4820 inferrepo=True,
4870 4821 )
4871 4822 def parents(ui, repo, file_=None, **opts):
4872 4823 """show the parents of the working directory or revision (DEPRECATED)
4873 4824
4874 4825 Print the working directory's parent revisions. If a revision is
4875 4826 given via -r/--rev, the parent of that revision will be printed.
4876 4827 If a file argument is given, the revision in which the file was
4877 4828 last changed (before the working directory revision or the
4878 4829 argument to --rev if given) is printed.
4879 4830
4880 4831 This command is equivalent to::
4881 4832
4882 4833 hg log -r "p1()+p2()" or
4883 4834 hg log -r "p1(REV)+p2(REV)" or
4884 4835 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4885 4836 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4886 4837
4887 4838 See :hg:`summary` and :hg:`help revsets` for related information.
4888 4839
4889 4840 Returns 0 on success.
4890 4841 """
4891 4842
4892 4843 opts = pycompat.byteskwargs(opts)
4893 4844 rev = opts.get(b'rev')
4894 4845 if rev:
4895 4846 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
4896 4847 ctx = logcmdutil.revsingle(repo, rev, None)
4897 4848
4898 4849 if file_:
4899 4850 m = scmutil.match(ctx, (file_,), opts)
4900 4851 if m.anypats() or len(m.files()) != 1:
4901 4852 raise error.InputError(_(b'can only specify an explicit filename'))
4902 4853 file_ = m.files()[0]
4903 4854 filenodes = []
4904 4855 for cp in ctx.parents():
4905 4856 if not cp:
4906 4857 continue
4907 4858 try:
4908 4859 filenodes.append(cp.filenode(file_))
4909 4860 except error.LookupError:
4910 4861 pass
4911 4862 if not filenodes:
4912 4863 raise error.InputError(_(b"'%s' not found in manifest") % file_)
4913 4864 p = []
4914 4865 for fn in filenodes:
4915 4866 fctx = repo.filectx(file_, fileid=fn)
4916 4867 p.append(fctx.node())
4917 4868 else:
4918 4869 p = [cp.node() for cp in ctx.parents()]
4919 4870
4920 4871 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4921 4872 for n in p:
4922 4873 if n != repo.nullid:
4923 4874 displayer.show(repo[n])
4924 4875 displayer.close()
4925 4876
4926 4877
4927 4878 @command(
4928 4879 b'paths',
4929 4880 formatteropts,
4930 4881 _(b'[NAME]'),
4931 4882 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4932 4883 optionalrepo=True,
4933 4884 intents={INTENT_READONLY},
4934 4885 )
4935 4886 def paths(ui, repo, search=None, **opts):
4936 4887 """show aliases for remote repositories
4937 4888
4938 4889 Show definition of symbolic path name NAME. If no name is given,
4939 4890 show definition of all available names.
4940 4891
4941 4892 Option -q/--quiet suppresses all output when searching for NAME
4942 4893 and shows only the path names when listing all definitions.
4943 4894
4944 4895 Path names are defined in the [paths] section of your
4945 4896 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4946 4897 repository, ``.hg/hgrc`` is used, too.
4947 4898
4948 4899 The path names ``default`` and ``default-push`` have a special
4949 4900 meaning. When performing a push or pull operation, they are used
4950 4901 as fallbacks if no location is specified on the command-line.
4951 4902 When ``default-push`` is set, it will be used for push and
4952 4903 ``default`` will be used for pull; otherwise ``default`` is used
4953 4904 as the fallback for both. When cloning a repository, the clone
4954 4905 source is written as ``default`` in ``.hg/hgrc``.
4955 4906
4956 4907 .. note::
4957 4908
4958 4909 ``default`` and ``default-push`` apply to all inbound (e.g.
4959 4910 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4960 4911 and :hg:`bundle`) operations.
4961 4912
4962 4913 See :hg:`help urls` for more information.
4963 4914
4964 4915 .. container:: verbose
4965 4916
4966 4917 Template:
4967 4918
4968 4919 The following keywords are supported. See also :hg:`help templates`.
4969 4920
4970 4921 :name: String. Symbolic name of the path alias.
4971 4922 :pushurl: String. URL for push operations.
4972 4923 :url: String. URL or directory path for the other operations.
4973 4924
4974 4925 Returns 0 on success.
4975 4926 """
4976 4927
4977 4928 pathitems = urlutil.list_paths(ui, search)
4978 4929 ui.pager(b'paths')
4979 4930
4980 4931 fm = ui.formatter(b'paths', pycompat.byteskwargs(opts))
4981 4932 if fm.isplain():
4982 4933 hidepassword = urlutil.hidepassword
4983 4934 else:
4984 4935 hidepassword = bytes
4985 4936 if ui.quiet:
4986 4937 namefmt = b'%s\n'
4987 4938 else:
4988 4939 namefmt = b'%s = '
4989 4940 showsubopts = not search and not ui.quiet
4990 4941
4991 4942 for name, path in pathitems:
4992 4943 fm.startitem()
4993 4944 fm.condwrite(not search, b'name', namefmt, name)
4994 4945 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
4995 4946 for subopt, value in sorted(path.suboptions.items()):
4996 4947 assert subopt not in (b'name', b'url')
4997 4948 if showsubopts:
4998 4949 fm.plain(b'%s:%s = ' % (name, subopt))
4999 4950 display = urlutil.path_suboptions_display[subopt]
5000 4951 value = display(value)
5001 4952 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5002 4953
5003 4954 fm.end()
5004 4955
5005 4956 if search and not pathitems:
5006 4957 if not ui.quiet:
5007 4958 ui.warn(_(b"not found!\n"))
5008 4959 return 1
5009 4960 else:
5010 4961 return 0
5011 4962
5012 4963
5013 4964 @command(
5014 4965 b'phase',
5015 4966 [
5016 4967 (b'p', b'public', False, _(b'set changeset phase to public')),
5017 4968 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5018 4969 (b's', b'secret', False, _(b'set changeset phase to secret')),
5019 4970 (b'f', b'force', False, _(b'allow to move boundary backward')),
5020 4971 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5021 4972 ],
5022 4973 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5023 4974 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5024 4975 )
5025 4976 def phase(ui, repo, *revs, **opts):
5026 4977 """set or show the current phase name
5027 4978
5028 4979 With no argument, show the phase name of the current revision(s).
5029 4980
5030 4981 With one of -p/--public, -d/--draft or -s/--secret, change the
5031 4982 phase value of the specified revisions.
5032 4983
5033 4984 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5034 4985 lower phase to a higher phase. Phases are ordered as follows::
5035 4986
5036 4987 public < draft < secret
5037 4988
5038 4989 Returns 0 on success, 1 if some phases could not be changed.
5039 4990
5040 4991 (For more information about the phases concept, see :hg:`help phases`.)
5041 4992 """
5042 4993 opts = pycompat.byteskwargs(opts)
5043 4994 # search for a unique phase argument
5044 4995 targetphase = None
5045 4996 for idx, name in enumerate(phases.cmdphasenames):
5046 4997 if opts[name]:
5047 4998 if targetphase is not None:
5048 4999 raise error.InputError(_(b'only one phase can be specified'))
5049 5000 targetphase = idx
5050 5001
5051 5002 # look for specified revision
5052 5003 revs = list(revs)
5053 5004 revs.extend(opts[b'rev'])
5054 5005 if revs:
5055 5006 revs = logcmdutil.revrange(repo, revs)
5056 5007 else:
5057 5008 # display both parents as the second parent phase can influence
5058 5009 # the phase of a merge commit
5059 5010 revs = [c.rev() for c in repo[None].parents()]
5060 5011
5061 5012 ret = 0
5062 5013 if targetphase is None:
5063 5014 # display
5064 5015 for r in revs:
5065 5016 ctx = repo[r]
5066 5017 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5067 5018 else:
5068 5019 with repo.lock(), repo.transaction(b"phase") as tr:
5069 5020 # set phase
5070 5021 if not revs:
5071 5022 raise error.InputError(_(b'empty revision set'))
5072 5023 nodes = [repo[r].node() for r in revs]
5073 5024 # moving revision from public to draft may hide them
5074 5025 # We have to check result on an unfiltered repository
5075 5026 unfi = repo.unfiltered()
5076 5027 getphase = unfi._phasecache.phase
5077 5028 olddata = [getphase(unfi, r) for r in unfi]
5078 5029 phases.advanceboundary(repo, tr, targetphase, nodes)
5079 5030 if opts[b'force']:
5080 5031 phases.retractboundary(repo, tr, targetphase, nodes)
5081 5032 getphase = unfi._phasecache.phase
5082 5033 newdata = [getphase(unfi, r) for r in unfi]
5083 5034 changes = sum(newdata[r] != olddata[r] for r in unfi)
5084 5035 cl = unfi.changelog
5085 5036 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5086 5037 if rejected:
5087 5038 ui.warn(
5088 5039 _(
5089 5040 b'cannot move %i changesets to a higher '
5090 5041 b'phase, use --force\n'
5091 5042 )
5092 5043 % len(rejected)
5093 5044 )
5094 5045 ret = 1
5095 5046 if changes:
5096 5047 msg = _(b'phase changed for %i changesets\n') % changes
5097 5048 if ret:
5098 5049 ui.status(msg)
5099 5050 else:
5100 5051 ui.note(msg)
5101 5052 else:
5102 5053 ui.warn(_(b'no phases changed\n'))
5103 5054 return ret
5104 5055
5105 5056
5106 5057 @command(
5107 5058 b'pull',
5108 5059 [
5109 5060 (
5110 5061 b'u',
5111 5062 b'update',
5112 5063 None,
5113 5064 _(b'update to new branch head if new descendants were pulled'),
5114 5065 ),
5115 5066 (
5116 5067 b'f',
5117 5068 b'force',
5118 5069 None,
5119 5070 _(b'run even when remote repository is unrelated'),
5120 5071 ),
5121 5072 (
5122 5073 b'',
5123 5074 b'confirm',
5124 5075 None,
5125 5076 _(b'confirm pull before applying changes'),
5126 5077 ),
5127 5078 (
5128 5079 b'r',
5129 5080 b'rev',
5130 5081 [],
5131 5082 _(b'a remote changeset intended to be added'),
5132 5083 _(b'REV'),
5133 5084 ),
5134 5085 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5135 5086 (
5136 5087 b'b',
5137 5088 b'branch',
5138 5089 [],
5139 5090 _(b'a specific branch you would like to pull'),
5140 5091 _(b'BRANCH'),
5141 5092 ),
5142 5093 (
5143 5094 b'',
5144 5095 b'remote-hidden',
5145 5096 False,
5146 5097 _(b"include changesets hidden on the remote (EXPERIMENTAL)"),
5147 5098 ),
5148 5099 ]
5149 5100 + remoteopts,
5150 5101 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5151 5102 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5152 5103 helpbasic=True,
5153 5104 )
5154 5105 def pull(ui, repo, *sources, **opts):
5155 5106 """pull changes from the specified source
5156 5107
5157 5108 Pull changes from a remote repository to a local one.
5158 5109
5159 5110 This finds all changes from the repository at the specified path
5160 5111 or URL and adds them to a local repository (the current one unless
5161 5112 -R is specified). By default, this does not update the copy of the
5162 5113 project in the working directory.
5163 5114
5164 5115 When cloning from servers that support it, Mercurial may fetch
5165 5116 pre-generated data. When this is done, hooks operating on incoming
5166 5117 changesets and changegroups may fire more than once, once for each
5167 5118 pre-generated bundle and as well as for any additional remaining
5168 5119 data. See :hg:`help -e clonebundles` for more.
5169 5120
5170 5121 Use :hg:`incoming` if you want to see what would have been added
5171 5122 by a pull at the time you issued this command. If you then decide
5172 5123 to add those changes to the repository, you should use :hg:`pull
5173 5124 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5174 5125
5175 5126 If SOURCE is omitted, the 'default' path will be used.
5176 5127 See :hg:`help urls` for more information.
5177 5128
5178 5129 If multiple sources are specified, they will be pulled sequentially as if
5179 5130 the command was run multiple time. If --update is specify and the command
5180 5131 will stop at the first failed --update.
5181 5132
5182 5133 Specifying bookmark as ``.`` is equivalent to specifying the active
5183 5134 bookmark's name.
5184 5135
5185 5136 .. container:: verbose
5186 5137
5187 5138 One can use the `--remote-hidden` flag to pull changesets
5188 5139 hidden on the remote. This flag is "best effort", and will only
5189 5140 work if the server supports the feature and is configured to
5190 5141 allow the user to access hidden changesets. This option is
5191 5142 experimental and backwards compatibility is not garanteed.
5192 5143
5193 5144 Returns 0 on success, 1 if an update had unresolved files.
5194 5145 """
5195 5146
5196 5147 if ui.configbool(b'commands', b'update.requiredest') and opts.get('update'):
5197 5148 msg = _(b'update destination required by configuration')
5198 5149 hint = _(b'use hg pull followed by hg update DEST')
5199 5150 raise error.InputError(msg, hint=hint)
5200 5151
5201 5152 update_conflict = None
5202 5153
5203 5154 for path in urlutil.get_pull_paths(repo, ui, sources):
5204 5155 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(path.loc))
5205 5156 ui.flush()
5206 5157 other = hg.peer(
5207 5158 repo,
5208 5159 pycompat.byteskwargs(opts),
5209 5160 path,
5210 5161 remotehidden=opts['remote_hidden'],
5211 5162 )
5212 5163 update_conflict = None
5213 5164 try:
5214 5165 branches = (path.branch, opts.get('branch', []))
5215 5166 revs, checkout = hg.addbranchrevs(
5216 5167 repo,
5217 5168 other,
5218 5169 branches,
5219 5170 opts.get('rev'),
5220 5171 remotehidden=opts['remote_hidden'],
5221 5172 )
5222 5173
5223 5174 pullopargs = {}
5224 5175
5225 5176 nodes = None
5226 5177 if opts.get('bookmark') or revs:
5227 5178 # The list of bookmark used here is the same used to actually update
5228 5179 # the bookmark names, to avoid the race from issue 4689 and we do
5229 5180 # all lookup and bookmark queries in one go so they see the same
5230 5181 # version of the server state (issue 4700).
5231 5182 nodes = []
5232 5183 fnodes = []
5233 5184 revs = revs or []
5234 5185 if revs and not other.capable(b'lookup'):
5235 5186 err = _(
5236 5187 b"other repository doesn't support revision lookup, "
5237 5188 b"so a rev cannot be specified."
5238 5189 )
5239 5190 raise error.Abort(err)
5240 5191 with other.commandexecutor() as e:
5241 5192 fremotebookmarks = e.callcommand(
5242 5193 b'listkeys', {b'namespace': b'bookmarks'}
5243 5194 )
5244 5195 for r in revs:
5245 5196 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5246 5197 remotebookmarks = fremotebookmarks.result()
5247 5198 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5248 5199 pullopargs[b'remotebookmarks'] = remotebookmarks
5249 5200 for b in opts.get('bookmark', []):
5250 5201 b = repo._bookmarks.expandname(b)
5251 5202 if b not in remotebookmarks:
5252 5203 raise error.InputError(
5253 5204 _(b'remote bookmark %s not found!') % b
5254 5205 )
5255 5206 nodes.append(remotebookmarks[b])
5256 5207 for i, rev in enumerate(revs):
5257 5208 node = fnodes[i].result()
5258 5209 nodes.append(node)
5259 5210 if rev == checkout:
5260 5211 checkout = node
5261 5212
5262 5213 wlock = util.nullcontextmanager()
5263 5214 if opts.get('update'):
5264 5215 wlock = repo.wlock()
5265 5216 with wlock:
5266 5217 pullopargs.update(opts.get('opargs', {}))
5267 5218 modheads = exchange.pull(
5268 5219 repo,
5269 5220 other,
5270 5221 path=path,
5271 5222 heads=nodes,
5272 5223 force=opts.get('force'),
5273 5224 bookmarks=opts.get('bookmark', ()),
5274 5225 opargs=pullopargs,
5275 5226 confirm=opts.get('confirm'),
5276 5227 ).cgresult
5277 5228
5278 5229 # brev is a name, which might be a bookmark to be activated at
5279 5230 # the end of the update. In other words, it is an explicit
5280 5231 # destination of the update
5281 5232 brev = None
5282 5233
5283 5234 if checkout:
5284 5235 checkout = repo.unfiltered().changelog.rev(checkout)
5285 5236
5286 5237 # order below depends on implementation of
5287 5238 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5288 5239 # because 'checkout' is determined without it.
5289 5240 if opts.get('rev'):
5290 5241 brev = opts['rev'][0]
5291 5242 elif opts.get('branch'):
5292 5243 brev = opts['branch'][0]
5293 5244 else:
5294 5245 brev = path.branch
5295 5246
5296 5247 # XXX path: we are losing the `path` object here. Keeping it
5297 5248 # would be valuable. For example as a "variant" as we do
5298 5249 # for pushes.
5299 5250 repo._subtoppath = path.loc
5300 5251 try:
5301 5252 update_conflict = cmdutil.postincoming(
5302 5253 ui, repo, modheads, opts.get('update'), checkout, brev
5303 5254 )
5304 5255 except error.FilteredRepoLookupError as exc:
5305 5256 msg = _(b'cannot update to target: %s') % exc.args[0]
5306 5257 exc.args = (msg,) + exc.args[1:]
5307 5258 raise
5308 5259 finally:
5309 5260 del repo._subtoppath
5310 5261
5311 5262 finally:
5312 5263 other.close()
5313 5264 # skip the remaining pull source if they are some conflict.
5314 5265 if update_conflict:
5315 5266 break
5316 5267 if update_conflict:
5317 5268 return 1
5318 5269 else:
5319 5270 return 0
5320 5271
5321 5272
5322 5273 @command(
5323 5274 b'purge|clean',
5324 5275 [
5325 5276 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5326 5277 (b'', b'all', None, _(b'purge ignored files too')),
5327 5278 (b'i', b'ignored', None, _(b'purge only ignored files')),
5328 5279 (b'', b'dirs', None, _(b'purge empty directories')),
5329 5280 (b'', b'files', None, _(b'purge files')),
5330 5281 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5331 5282 (
5332 5283 b'0',
5333 5284 b'print0',
5334 5285 None,
5335 5286 _(
5336 5287 b'end filenames with NUL, for use with xargs'
5337 5288 b' (implies -p/--print)'
5338 5289 ),
5339 5290 ),
5340 5291 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5341 5292 ]
5342 5293 + cmdutil.walkopts,
5343 5294 _(b'hg purge [OPTION]... [DIR]...'),
5344 5295 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5345 5296 )
5346 5297 def purge(ui, repo, *dirs, **opts):
5347 5298 """removes files not tracked by Mercurial
5348 5299
5349 5300 Delete files not known to Mercurial. This is useful to test local
5350 5301 and uncommitted changes in an otherwise-clean source tree.
5351 5302
5352 5303 This means that purge will delete the following by default:
5353 5304
5354 5305 - Unknown files: files marked with "?" by :hg:`status`
5355 5306 - Empty directories: in fact Mercurial ignores directories unless
5356 5307 they contain files under source control management
5357 5308
5358 5309 But it will leave untouched:
5359 5310
5360 5311 - Modified and unmodified tracked files
5361 5312 - Ignored files (unless -i or --all is specified)
5362 5313 - New files added to the repository (with :hg:`add`)
5363 5314
5364 5315 The --files and --dirs options can be used to direct purge to delete
5365 5316 only files, only directories, or both. If neither option is given,
5366 5317 both will be deleted.
5367 5318
5368 5319 If directories are given on the command line, only files in these
5369 5320 directories are considered.
5370 5321
5371 5322 Be careful with purge, as you could irreversibly delete some files
5372 5323 you forgot to add to the repository. If you only want to print the
5373 5324 list of files that this program would delete, use the --print
5374 5325 option.
5375 5326 """
5376 5327 cmdutil.check_at_most_one_arg(opts, 'all', 'ignored')
5377 5328
5378 5329 act = not opts.get('print')
5379 5330 eol = b'\n'
5380 5331 if opts.get('print0'):
5381 5332 eol = b'\0'
5382 5333 act = False # --print0 implies --print
5383 5334 if opts.get('all', False):
5384 5335 ignored = True
5385 5336 unknown = True
5386 5337 else:
5387 5338 ignored = opts.get('ignored', False)
5388 5339 unknown = not ignored
5389 5340
5390 5341 removefiles = opts.get('files')
5391 5342 removedirs = opts.get('dirs')
5392 5343 confirm = opts.get('confirm')
5393 5344 if confirm is None:
5394 5345 try:
5395 5346 extensions.find(b'purge')
5396 5347 confirm = False
5397 5348 except KeyError:
5398 5349 confirm = True
5399 5350
5400 5351 if not removefiles and not removedirs:
5401 5352 removefiles = True
5402 5353 removedirs = True
5403 5354
5404 5355 match = scmutil.match(repo[None], dirs, pycompat.byteskwargs(opts))
5405 5356
5406 5357 paths = mergemod.purge(
5407 5358 repo,
5408 5359 match,
5409 5360 unknown=unknown,
5410 5361 ignored=ignored,
5411 5362 removeemptydirs=removedirs,
5412 5363 removefiles=removefiles,
5413 5364 abortonerror=opts.get('abort_on_err'),
5414 5365 noop=not act,
5415 5366 confirm=confirm,
5416 5367 )
5417 5368
5418 5369 for path in paths:
5419 5370 if not act:
5420 5371 ui.write(b'%s%s' % (path, eol))
5421 5372
5422 5373
5423 5374 @command(
5424 5375 b'push',
5425 5376 [
5426 5377 (b'f', b'force', None, _(b'force push')),
5427 5378 (
5428 5379 b'r',
5429 5380 b'rev',
5430 5381 [],
5431 5382 _(b'a changeset intended to be included in the destination'),
5432 5383 _(b'REV'),
5433 5384 ),
5434 5385 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5435 5386 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5436 5387 (
5437 5388 b'b',
5438 5389 b'branch',
5439 5390 [],
5440 5391 _(b'a specific branch you would like to push'),
5441 5392 _(b'BRANCH'),
5442 5393 ),
5443 5394 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5444 5395 (
5445 5396 b'',
5446 5397 b'pushvars',
5447 5398 [],
5448 5399 _(b'variables that can be sent to server (ADVANCED)'),
5449 5400 ),
5450 5401 (
5451 5402 b'',
5452 5403 b'publish',
5453 5404 False,
5454 5405 _(b'push the changeset as public (EXPERIMENTAL)'),
5455 5406 ),
5456 5407 ]
5457 5408 + remoteopts,
5458 5409 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5459 5410 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5460 5411 helpbasic=True,
5461 5412 )
5462 5413 def push(ui, repo, *dests, **opts):
5463 5414 """push changes to the specified destination
5464 5415
5465 5416 Push changesets from the local repository to the specified
5466 5417 destination.
5467 5418
5468 5419 This operation is symmetrical to pull: it is identical to a pull
5469 5420 in the destination repository from the current one.
5470 5421
5471 5422 By default, push will not allow creation of new heads at the
5472 5423 destination, since multiple heads would make it unclear which head
5473 5424 to use. In this situation, it is recommended to pull and merge
5474 5425 before pushing.
5475 5426
5476 5427 Use --new-branch if you want to allow push to create a new named
5477 5428 branch that is not present at the destination. This allows you to
5478 5429 only create a new branch without forcing other changes.
5479 5430
5480 5431 .. note::
5481 5432
5482 5433 Extra care should be taken with the -f/--force option,
5483 5434 which will push all new heads on all branches, an action which will
5484 5435 almost always cause confusion for collaborators.
5485 5436
5486 5437 If -r/--rev is used, the specified revision and all its ancestors
5487 5438 will be pushed to the remote repository.
5488 5439
5489 5440 If -B/--bookmark is used, the specified bookmarked revision, its
5490 5441 ancestors, and the bookmark will be pushed to the remote
5491 5442 repository. Specifying ``.`` is equivalent to specifying the active
5492 5443 bookmark's name. Use the --all-bookmarks option for pushing all
5493 5444 current bookmarks.
5494 5445
5495 5446 Please see :hg:`help urls` for important details about ``ssh://``
5496 5447 URLs. If DESTINATION is omitted, a default path will be used.
5497 5448
5498 5449 When passed multiple destinations, push will process them one after the
5499 5450 other, but stop should an error occur.
5500 5451
5501 5452 .. container:: verbose
5502 5453
5503 5454 The --pushvars option sends strings to the server that become
5504 5455 environment variables prepended with ``HG_USERVAR_``. For example,
5505 5456 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5506 5457 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5507 5458
5508 5459 pushvars can provide for user-overridable hooks as well as set debug
5509 5460 levels. One example is having a hook that blocks commits containing
5510 5461 conflict markers, but enables the user to override the hook if the file
5511 5462 is using conflict markers for testing purposes or the file format has
5512 5463 strings that look like conflict markers.
5513 5464
5514 5465 By default, servers will ignore `--pushvars`. To enable it add the
5515 5466 following to your configuration file::
5516 5467
5517 5468 [push]
5518 5469 pushvars.server = true
5519 5470
5520 5471 Returns 0 if push was successful, 1 if nothing to push.
5521 5472 """
5522 5473
5523 5474 opts = pycompat.byteskwargs(opts)
5524 5475
5525 5476 if opts.get(b'all_bookmarks'):
5526 5477 cmdutil.check_incompatible_arguments(
5527 5478 opts,
5528 5479 b'all_bookmarks',
5529 5480 [b'bookmark', b'rev'],
5530 5481 )
5531 5482 opts[b'bookmark'] = list(repo._bookmarks)
5532 5483
5533 5484 if opts.get(b'bookmark'):
5534 5485 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5535 5486 for b in opts[b'bookmark']:
5536 5487 # translate -B options to -r so changesets get pushed
5537 5488 b = repo._bookmarks.expandname(b)
5538 5489 if b in repo._bookmarks:
5539 5490 opts.setdefault(b'rev', []).append(b)
5540 5491 else:
5541 5492 # if we try to push a deleted bookmark, translate it to null
5542 5493 # this lets simultaneous -r, -b options continue working
5543 5494 opts.setdefault(b'rev', []).append(b"null")
5544 5495
5545 5496 some_pushed = False
5546 5497 result = 0
5547 5498 for path in urlutil.get_push_paths(repo, ui, dests):
5548 5499 dest = path.loc
5549 5500 branches = (path.branch, opts.get(b'branch') or [])
5550 5501 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5551 5502 revs, checkout = hg.addbranchrevs(
5552 5503 repo, repo, branches, opts.get(b'rev')
5553 5504 )
5554 5505 other = hg.peer(repo, opts, dest)
5555 5506
5556 5507 try:
5557 5508 if revs:
5558 5509 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5559 5510 if not revs:
5560 5511 raise error.InputError(
5561 5512 _(b"specified revisions evaluate to an empty set"),
5562 5513 hint=_(b"use different revision arguments"),
5563 5514 )
5564 5515 elif path.pushrev:
5565 5516 # It doesn't make any sense to specify ancestor revisions. So limit
5566 5517 # to DAG heads to make discovery simpler.
5567 5518 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5568 5519 revs = scmutil.revrange(repo, [expr])
5569 5520 revs = [repo[rev].node() for rev in revs]
5570 5521 if not revs:
5571 5522 raise error.InputError(
5572 5523 _(
5573 5524 b'default push revset for path evaluates to an empty set'
5574 5525 )
5575 5526 )
5576 5527 elif ui.configbool(b'commands', b'push.require-revs'):
5577 5528 raise error.InputError(
5578 5529 _(b'no revisions specified to push'),
5579 5530 hint=_(b'did you mean "hg push -r ."?'),
5580 5531 )
5581 5532
5582 5533 repo._subtoppath = dest
5583 5534 try:
5584 5535 # push subrepos depth-first for coherent ordering
5585 5536 c = repo[b'.']
5586 5537 subs = c.substate # only repos that are committed
5587 5538 for s in sorted(subs):
5588 5539 sub_result = c.sub(s).push(opts)
5589 5540 if sub_result == 0:
5590 5541 return 1
5591 5542 finally:
5592 5543 del repo._subtoppath
5593 5544
5594 5545 opargs = dict(
5595 5546 opts.get(b'opargs', {})
5596 5547 ) # copy opargs since we may mutate it
5597 5548 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5598 5549
5599 5550 pushop = exchange.push(
5600 5551 repo,
5601 5552 other,
5602 5553 opts.get(b'force'),
5603 5554 revs=revs,
5604 5555 newbranch=opts.get(b'new_branch'),
5605 5556 bookmarks=opts.get(b'bookmark', ()),
5606 5557 publish=opts.get(b'publish'),
5607 5558 opargs=opargs,
5608 5559 )
5609 5560
5610 5561 if pushop.cgresult == 0:
5611 5562 result = 1
5612 5563 elif pushop.cgresult is not None:
5613 5564 some_pushed = True
5614 5565
5615 5566 if pushop.bkresult is not None:
5616 5567 if pushop.bkresult == 2:
5617 5568 result = 2
5618 5569 elif not result and pushop.bkresult:
5619 5570 result = 2
5620 5571
5621 5572 if result:
5622 5573 break
5623 5574
5624 5575 finally:
5625 5576 other.close()
5626 5577 if result == 0 and not some_pushed:
5627 5578 result = 1
5628 5579 return result
5629 5580
5630 5581
5631 5582 @command(
5632 5583 b'recover',
5633 5584 [
5634 5585 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5635 5586 ],
5636 5587 helpcategory=command.CATEGORY_MAINTENANCE,
5637 5588 )
5638 5589 def recover(ui, repo, **opts):
5639 5590 """roll back an interrupted transaction
5640 5591
5641 5592 Recover from an interrupted commit or pull.
5642 5593
5643 5594 This command tries to fix the repository status after an
5644 5595 interrupted operation. It should only be necessary when Mercurial
5645 5596 suggests it.
5646 5597
5647 5598 Returns 0 if successful, 1 if nothing to recover or verify fails.
5648 5599 """
5649 5600 ret = repo.recover()
5650 5601 if ret:
5651 5602 if opts['verify']:
5652 5603 return hg.verify(repo)
5653 5604 else:
5654 5605 msg = _(
5655 5606 b"(verify step skipped, run `hg verify` to check your "
5656 5607 b"repository content)\n"
5657 5608 )
5658 5609 ui.warn(msg)
5659 5610 return 0
5660 5611 return 1
5661 5612
5662 5613
5663 5614 @command(
5664 5615 b'remove|rm',
5665 5616 [
5666 5617 (b'A', b'after', None, _(b'record delete for missing files')),
5667 5618 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5668 5619 ]
5669 5620 + subrepoopts
5670 5621 + walkopts
5671 5622 + dryrunopts,
5672 5623 _(b'[OPTION]... FILE...'),
5673 5624 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5674 5625 helpbasic=True,
5675 5626 inferrepo=True,
5676 5627 )
5677 5628 def remove(ui, repo, *pats, **opts):
5678 5629 """remove the specified files on the next commit
5679 5630
5680 5631 Schedule the indicated files for removal from the current branch.
5681 5632
5682 5633 This command schedules the files to be removed at the next commit.
5683 5634 To undo a remove before that, see :hg:`revert`. To undo added
5684 5635 files, see :hg:`forget`.
5685 5636
5686 5637 .. container:: verbose
5687 5638
5688 5639 -A/--after can be used to remove only files that have already
5689 5640 been deleted, -f/--force can be used to force deletion, and -Af
5690 5641 can be used to remove files from the next revision without
5691 5642 deleting them from the working directory.
5692 5643
5693 5644 The following table details the behavior of remove for different
5694 5645 file states (columns) and option combinations (rows). The file
5695 5646 states are Added [A], Clean [C], Modified [M] and Missing [!]
5696 5647 (as reported by :hg:`status`). The actions are Warn, Remove
5697 5648 (from branch) and Delete (from disk):
5698 5649
5699 5650 ========= == == == ==
5700 5651 opt/state A C M !
5701 5652 ========= == == == ==
5702 5653 none W RD W R
5703 5654 -f R RD RD R
5704 5655 -A W W W R
5705 5656 -Af R R R R
5706 5657 ========= == == == ==
5707 5658
5708 5659 .. note::
5709 5660
5710 5661 :hg:`remove` never deletes files in Added [A] state from the
5711 5662 working directory, not even if ``--force`` is specified.
5712 5663
5713 5664 Returns 0 on success, 1 if any warnings encountered.
5714 5665 """
5715 5666
5716 5667 after, force = opts.get('after'), opts.get('force')
5717 5668 dryrun = opts.get('dry_run')
5718 5669 if not pats and not after:
5719 5670 raise error.InputError(_(b'no files specified'))
5720 5671
5721 5672 with repo.wlock(), repo.dirstate.changing_files(repo):
5722 5673 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
5723 5674 subrepos = opts.get('subrepos')
5724 5675 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5725 5676 return cmdutil.remove(
5726 5677 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5727 5678 )
5728 5679
5729 5680
5730 5681 @command(
5731 5682 b'rename|move|mv',
5732 5683 [
5733 5684 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5734 5685 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5735 5686 (
5736 5687 b'',
5737 5688 b'at-rev',
5738 5689 b'',
5739 5690 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5740 5691 _(b'REV'),
5741 5692 ),
5742 5693 (
5743 5694 b'f',
5744 5695 b'force',
5745 5696 None,
5746 5697 _(b'forcibly move over an existing managed file'),
5747 5698 ),
5748 5699 ]
5749 5700 + walkopts
5750 5701 + dryrunopts,
5751 5702 _(b'[OPTION]... SOURCE... DEST'),
5752 5703 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5753 5704 )
5754 5705 def rename(ui, repo, *pats, **opts):
5755 5706 """rename files; equivalent of copy + remove
5756 5707
5757 5708 Mark dest as copies of sources; mark sources for deletion. If dest
5758 5709 is a directory, copies are put in that directory. If dest is a
5759 5710 file, there can only be one source.
5760 5711
5761 5712 By default, this command copies the contents of files as they
5762 5713 exist in the working directory. If invoked with -A/--after, the
5763 5714 operation is recorded, but no copying is performed.
5764 5715
5765 5716 To undo marking a destination file as renamed, use --forget. With that
5766 5717 option, all given (positional) arguments are unmarked as renames. The
5767 5718 destination file(s) will be left in place (still tracked). The source
5768 5719 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5769 5720 the same way as :hg:`copy --forget`.
5770 5721
5771 5722 This command takes effect with the next commit by default.
5772 5723
5773 5724 Returns 0 on success, 1 if errors are encountered.
5774 5725 """
5775 5726 context = lambda repo: repo.dirstate.changing_files(repo)
5776 5727 rev = opts.get('at_rev')
5777 5728
5778 5729 if rev:
5779 5730 ctx = logcmdutil.revsingle(repo, rev)
5780 5731 if ctx.rev() is not None:
5781 5732
5782 5733 def context(repo):
5783 5734 return util.nullcontextmanager()
5784 5735
5785 5736 opts['at_rev'] = ctx.rev()
5786 5737 with repo.wlock(), context(repo):
5787 5738 return cmdutil.copy(
5788 5739 ui, repo, pats, pycompat.byteskwargs(opts), rename=True
5789 5740 )
5790 5741
5791 5742
5792 5743 @command(
5793 5744 b'resolve',
5794 5745 [
5795 5746 (b'a', b'all', None, _(b'select all unresolved files')),
5796 5747 (b'l', b'list', None, _(b'list state of files needing merge')),
5797 5748 (b'm', b'mark', None, _(b'mark files as resolved')),
5798 5749 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5799 5750 (b'n', b'no-status', None, _(b'hide status prefix')),
5800 5751 (b'', b're-merge', None, _(b're-merge files')),
5801 5752 ]
5802 5753 + mergetoolopts
5803 5754 + walkopts
5804 5755 + formatteropts,
5805 5756 _(b'[OPTION]... [FILE]...'),
5806 5757 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5807 5758 inferrepo=True,
5808 5759 )
5809 5760 def resolve(ui, repo, *pats, **opts):
5810 5761 """redo merges or set/view the merge status of files
5811 5762
5812 5763 Merges with unresolved conflicts are often the result of
5813 5764 non-interactive merging using the ``internal:merge`` configuration
5814 5765 setting, or a command-line merge tool like ``diff3``. The resolve
5815 5766 command is used to manage the files involved in a merge, after
5816 5767 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5817 5768 working directory must have two parents). See :hg:`help
5818 5769 merge-tools` for information on configuring merge tools.
5819 5770
5820 5771 The resolve command can be used in the following ways:
5821 5772
5822 5773 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5823 5774 the specified files, discarding any previous merge attempts. Re-merging
5824 5775 is not performed for files already marked as resolved. Use ``--all/-a``
5825 5776 to select all unresolved files. ``--tool`` can be used to specify
5826 5777 the merge tool used for the given files. It overrides the HGMERGE
5827 5778 environment variable and your configuration files. Previous file
5828 5779 contents are saved with a ``.orig`` suffix.
5829 5780
5830 5781 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5831 5782 (e.g. after having manually fixed-up the files). The default is
5832 5783 to mark all unresolved files.
5833 5784
5834 5785 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5835 5786 default is to mark all resolved files.
5836 5787
5837 5788 - :hg:`resolve -l`: list files which had or still have conflicts.
5838 5789 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5839 5790 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5840 5791 the list. See :hg:`help filesets` for details.
5841 5792
5842 5793 .. note::
5843 5794
5844 5795 Mercurial will not let you commit files with unresolved merge
5845 5796 conflicts. You must use :hg:`resolve -m ...` before you can
5846 5797 commit after a conflicting merge.
5847 5798
5848 5799 .. container:: verbose
5849 5800
5850 5801 Template:
5851 5802
5852 5803 The following keywords are supported in addition to the common template
5853 5804 keywords and functions. See also :hg:`help templates`.
5854 5805
5855 5806 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5856 5807 :path: String. Repository-absolute path of the file.
5857 5808
5858 5809 Returns 0 on success, 1 if any files fail a resolve attempt.
5859 5810 """
5860 5811
5861 5812 opts = pycompat.byteskwargs(opts)
5862 5813 confirm = ui.configbool(b'commands', b'resolve.confirm')
5863 5814 flaglist = b'all mark unmark list no_status re_merge'.split()
5864 5815 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5865 5816
5866 5817 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5867 5818 if actioncount > 1:
5868 5819 raise error.InputError(_(b"too many actions specified"))
5869 5820 elif actioncount == 0 and ui.configbool(
5870 5821 b'commands', b'resolve.explicit-re-merge'
5871 5822 ):
5872 5823 hint = _(b'use --mark, --unmark, --list or --re-merge')
5873 5824 raise error.InputError(_(b'no action specified'), hint=hint)
5874 5825 if pats and all:
5875 5826 raise error.InputError(_(b"can't specify --all and patterns"))
5876 5827 if not (all or pats or show or mark or unmark):
5877 5828 raise error.InputError(
5878 5829 _(b'no files or directories specified'),
5879 5830 hint=b'use --all to re-merge all unresolved files',
5880 5831 )
5881 5832
5882 5833 if confirm:
5883 5834 if all:
5884 5835 if ui.promptchoice(
5885 5836 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5886 5837 ):
5887 5838 raise error.CanceledError(_(b'user quit'))
5888 5839 if mark and not pats:
5889 5840 if ui.promptchoice(
5890 5841 _(
5891 5842 b'mark all unresolved files as resolved (yn)?'
5892 5843 b'$$ &Yes $$ &No'
5893 5844 )
5894 5845 ):
5895 5846 raise error.CanceledError(_(b'user quit'))
5896 5847 if unmark and not pats:
5897 5848 if ui.promptchoice(
5898 5849 _(
5899 5850 b'mark all resolved files as unresolved (yn)?'
5900 5851 b'$$ &Yes $$ &No'
5901 5852 )
5902 5853 ):
5903 5854 raise error.CanceledError(_(b'user quit'))
5904 5855
5905 5856 uipathfn = scmutil.getuipathfn(repo)
5906 5857
5907 5858 if show:
5908 5859 ui.pager(b'resolve')
5909 5860 fm = ui.formatter(b'resolve', opts)
5910 5861 ms = mergestatemod.mergestate.read(repo)
5911 5862 wctx = repo[None]
5912 5863 m = scmutil.match(wctx, pats, opts)
5913 5864
5914 5865 # Labels and keys based on merge state. Unresolved path conflicts show
5915 5866 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5916 5867 # resolved conflicts.
5917 5868 mergestateinfo = {
5918 5869 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5919 5870 b'resolve.unresolved',
5920 5871 b'U',
5921 5872 ),
5922 5873 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5923 5874 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5924 5875 b'resolve.unresolved',
5925 5876 b'P',
5926 5877 ),
5927 5878 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5928 5879 b'resolve.resolved',
5929 5880 b'R',
5930 5881 ),
5931 5882 }
5932 5883
5933 5884 for f in ms:
5934 5885 if not m(f):
5935 5886 continue
5936 5887
5937 5888 label, key = mergestateinfo[ms[f]]
5938 5889 fm.startitem()
5939 5890 fm.context(ctx=wctx)
5940 5891 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5941 5892 fm.data(path=f)
5942 5893 fm.plain(b'%s\n' % uipathfn(f), label=label)
5943 5894 fm.end()
5944 5895 return 0
5945 5896
5946 5897 with repo.wlock():
5947 5898 ms = mergestatemod.mergestate.read(repo)
5948 5899
5949 5900 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
5950 5901 raise error.StateError(
5951 5902 _(b'resolve command not applicable when not merging')
5952 5903 )
5953 5904
5954 5905 wctx = repo[None]
5955 5906 m = scmutil.match(wctx, pats, opts)
5956 5907 ret = 0
5957 5908 didwork = False
5958 5909
5959 5910 hasconflictmarkers = []
5960 5911 if mark:
5961 5912 markcheck = ui.config(b'commands', b'resolve.mark-check')
5962 5913 if markcheck not in [b'warn', b'abort']:
5963 5914 # Treat all invalid / unrecognized values as 'none'.
5964 5915 markcheck = False
5965 5916 for f in ms:
5966 5917 if not m(f):
5967 5918 continue
5968 5919
5969 5920 didwork = True
5970 5921
5971 5922 # path conflicts must be resolved manually
5972 5923 if ms[f] in (
5973 5924 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5974 5925 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5975 5926 ):
5976 5927 if mark:
5977 5928 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5978 5929 elif unmark:
5979 5930 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5980 5931 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5981 5932 ui.warn(
5982 5933 _(b'%s: path conflict must be resolved manually\n')
5983 5934 % uipathfn(f)
5984 5935 )
5985 5936 continue
5986 5937
5987 5938 if mark:
5988 5939 if markcheck:
5989 5940 fdata = repo.wvfs.tryread(f)
5990 5941 if (
5991 5942 filemerge.hasconflictmarkers(fdata)
5992 5943 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5993 5944 ):
5994 5945 hasconflictmarkers.append(f)
5995 5946 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5996 5947 elif unmark:
5997 5948 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5998 5949 else:
5999 5950 # backup pre-resolve (merge uses .orig for its own purposes)
6000 5951 a = repo.wjoin(f)
6001 5952 try:
6002 5953 util.copyfile(a, a + b".resolve")
6003 5954 except FileNotFoundError:
6004 5955 pass
6005 5956
6006 5957 try:
6007 5958 # preresolve file
6008 5959 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6009 5960 with ui.configoverride(overrides, b'resolve'):
6010 5961 r = ms.resolve(f, wctx)
6011 5962 if r:
6012 5963 ret = 1
6013 5964 finally:
6014 5965 ms.commit()
6015 5966
6016 5967 # replace filemerge's .orig file with our resolve file
6017 5968 try:
6018 5969 util.rename(
6019 5970 a + b".resolve", scmutil.backuppath(ui, repo, f)
6020 5971 )
6021 5972 except FileNotFoundError:
6022 5973 pass
6023 5974
6024 5975 if hasconflictmarkers:
6025 5976 ui.warn(
6026 5977 _(
6027 5978 b'warning: the following files still have conflict '
6028 5979 b'markers:\n'
6029 5980 )
6030 5981 + b''.join(
6031 5982 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6032 5983 )
6033 5984 )
6034 5985 if markcheck == b'abort' and not all and not pats:
6035 5986 raise error.StateError(
6036 5987 _(b'conflict markers detected'),
6037 5988 hint=_(b'use --all to mark anyway'),
6038 5989 )
6039 5990
6040 5991 ms.commit()
6041 5992 branchmerge = repo.dirstate.p2() != repo.nullid
6042 5993 # resolve is not doing a parent change here, however, `record updates`
6043 5994 # will call some dirstate API that at intended for parent changes call.
6044 5995 # Ideally we would not need this and could implement a lighter version
6045 5996 # of the recordupdateslogic that will not have to deal with the part
6046 5997 # related to parent changes. However this would requires that:
6047 5998 # - we are sure we passed around enough information at update/merge
6048 5999 # time to no longer needs it at `hg resolve time`
6049 6000 # - we are sure we store that information well enough to be able to reuse it
6050 6001 # - we are the necessary logic to reuse it right.
6051 6002 #
6052 6003 # All this should eventually happens, but in the mean time, we use this
6053 6004 # context manager slightly out of the context it should be.
6054 6005 with repo.dirstate.changing_parents(repo):
6055 6006 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6056 6007
6057 6008 if not didwork and pats:
6058 6009 hint = None
6059 6010 if not any([p for p in pats if p.find(b':') >= 0]):
6060 6011 pats = [b'path:%s' % p for p in pats]
6061 6012 m = scmutil.match(wctx, pats, opts)
6062 6013 for f in ms:
6063 6014 if not m(f):
6064 6015 continue
6065 6016
6066 6017 def flag(o):
6067 6018 if o == b're_merge':
6068 6019 return b'--re-merge '
6069 6020 return b'-%s ' % o[0:1]
6070 6021
6071 6022 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6072 6023 hint = _(b"(try: hg resolve %s%s)\n") % (
6073 6024 flags,
6074 6025 b' '.join(pats),
6075 6026 )
6076 6027 break
6077 6028 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6078 6029 if hint:
6079 6030 ui.warn(hint)
6080 6031
6081 6032 unresolvedf = ms.unresolvedcount()
6082 6033 if not unresolvedf:
6083 6034 ui.status(_(b'(no more unresolved files)\n'))
6084 6035 cmdutil.checkafterresolved(repo)
6085 6036
6086 6037 return ret
6087 6038
6088 6039
6089 6040 @command(
6090 6041 b'revert',
6091 6042 [
6092 6043 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6093 6044 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6094 6045 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6095 6046 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6096 6047 (b'i', b'interactive', None, _(b'interactively select the changes')),
6097 6048 ]
6098 6049 + walkopts
6099 6050 + dryrunopts,
6100 6051 _(b'[OPTION]... [-r REV] [NAME]...'),
6101 6052 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6102 6053 )
6103 6054 def revert(ui, repo, *pats, **opts):
6104 6055 """restore files to their checkout state
6105 6056
6106 6057 .. note::
6107 6058
6108 6059 To check out earlier revisions, you should use :hg:`update REV`.
6109 6060 To cancel an uncommitted merge (and lose your changes),
6110 6061 use :hg:`merge --abort`.
6111 6062
6112 6063 With no revision specified, revert the specified files or directories
6113 6064 to the contents they had in the parent of the working directory.
6114 6065 This restores the contents of files to an unmodified
6115 6066 state and unschedules adds, removes, copies, and renames. If the
6116 6067 working directory has two parents, you must explicitly specify a
6117 6068 revision.
6118 6069
6119 6070 Using the -r/--rev or -d/--date options, revert the given files or
6120 6071 directories to their states as of a specific revision. Because
6121 6072 revert does not change the working directory parents, this will
6122 6073 cause these files to appear modified. This can be helpful to "back
6123 6074 out" some or all of an earlier change. See :hg:`backout` for a
6124 6075 related method.
6125 6076
6126 6077 Modified files are saved with a .orig suffix before reverting.
6127 6078 To disable these backups, use --no-backup. It is possible to store
6128 6079 the backup files in a custom directory relative to the root of the
6129 6080 repository by setting the ``ui.origbackuppath`` configuration
6130 6081 option.
6131 6082
6132 6083 See :hg:`help dates` for a list of formats valid for -d/--date.
6133 6084
6134 6085 See :hg:`help backout` for a way to reverse the effect of an
6135 6086 earlier changeset.
6136 6087
6137 6088 Returns 0 on success.
6138 6089 """
6139 6090
6140 6091 if opts.get("date"):
6141 6092 cmdutil.check_incompatible_arguments(opts, 'date', ['rev'])
6142 6093 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6143 6094
6144 6095 parent, p2 = repo.dirstate.parents()
6145 6096 if not opts.get('rev') and p2 != repo.nullid:
6146 6097 # revert after merge is a trap for new users (issue2915)
6147 6098 raise error.InputError(
6148 6099 _(b'uncommitted merge with no revision specified'),
6149 6100 hint=_(b"use 'hg update' or see 'hg help revert'"),
6150 6101 )
6151 6102
6152 6103 rev = opts.get('rev')
6153 6104 if rev:
6154 6105 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6155 6106 ctx = logcmdutil.revsingle(repo, rev)
6156 6107
6157 6108 if not (
6158 6109 pats
6159 6110 or opts.get('include')
6160 6111 or opts.get('exclude')
6161 6112 or opts.get('all')
6162 6113 or opts.get('interactive')
6163 6114 ):
6164 6115 msg = _(b"no files or directories specified")
6165 6116 if p2 != repo.nullid:
6166 6117 hint = _(
6167 6118 b"uncommitted merge, use --all to discard all changes,"
6168 6119 b" or 'hg update -C .' to abort the merge"
6169 6120 )
6170 6121 raise error.InputError(msg, hint=hint)
6171 6122 dirty = any(repo.status())
6172 6123 node = ctx.node()
6173 6124 if node != parent:
6174 6125 if dirty:
6175 6126 hint = (
6176 6127 _(
6177 6128 b"uncommitted changes, use --all to discard all"
6178 6129 b" changes, or 'hg update %d' to update"
6179 6130 )
6180 6131 % ctx.rev()
6181 6132 )
6182 6133 else:
6183 6134 hint = (
6184 6135 _(
6185 6136 b"use --all to revert all files,"
6186 6137 b" or 'hg update %d' to update"
6187 6138 )
6188 6139 % ctx.rev()
6189 6140 )
6190 6141 elif dirty:
6191 6142 hint = _(b"uncommitted changes, use --all to discard all changes")
6192 6143 else:
6193 6144 hint = _(b"use --all to revert all files")
6194 6145 raise error.InputError(msg, hint=hint)
6195 6146
6196 6147 return cmdutil.revert(ui, repo, ctx, *pats, **opts)
6197 6148
6198 6149
6199 6150 @command(
6200 6151 b'rollback',
6201 6152 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6202 6153 helpcategory=command.CATEGORY_MAINTENANCE,
6203 6154 )
6204 6155 def rollback(ui, repo, **opts):
6205 6156 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6206 6157
6207 6158 Please use :hg:`commit --amend` instead of rollback to correct
6208 6159 mistakes in the last commit.
6209 6160
6210 6161 This command should be used with care. There is only one level of
6211 6162 rollback, and there is no way to undo a rollback. It will also
6212 6163 restore the dirstate at the time of the last transaction, losing
6213 6164 any dirstate changes since that time. This command does not alter
6214 6165 the working directory.
6215 6166
6216 6167 Transactions are used to encapsulate the effects of all commands
6217 6168 that create new changesets or propagate existing changesets into a
6218 6169 repository.
6219 6170
6220 6171 .. container:: verbose
6221 6172
6222 6173 For example, the following commands are transactional, and their
6223 6174 effects can be rolled back:
6224 6175
6225 6176 - commit
6226 6177 - import
6227 6178 - pull
6228 6179 - push (with this repository as the destination)
6229 6180 - unbundle
6230 6181
6231 6182 To avoid permanent data loss, rollback will refuse to rollback a
6232 6183 commit transaction if it isn't checked out. Use --force to
6233 6184 override this protection.
6234 6185
6235 6186 The rollback command can be entirely disabled by setting the
6236 6187 ``ui.rollback`` configuration setting to false. If you're here
6237 6188 because you want to use rollback and it's disabled, you can
6238 6189 re-enable the command by setting ``ui.rollback`` to true.
6239 6190
6240 6191 This command is not intended for use on public repositories. Once
6241 6192 changes are visible for pull by other users, rolling a transaction
6242 6193 back locally is ineffective (someone else may already have pulled
6243 6194 the changes). Furthermore, a race is possible with readers of the
6244 6195 repository; for example an in-progress pull from the repository
6245 6196 may fail if a rollback is performed.
6246 6197
6247 6198 Returns 0 on success, 1 if no rollback data is available.
6248 6199 """
6249 6200 if not ui.configbool(b'ui', b'rollback'):
6250 6201 raise error.Abort(
6251 6202 _(b'rollback is disabled because it is unsafe'),
6252 6203 hint=b'see `hg help -v rollback` for information',
6253 6204 )
6254 6205 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6255 6206
6256 6207
6257 6208 @command(
6258 6209 b'root',
6259 6210 [] + formatteropts,
6260 6211 intents={INTENT_READONLY},
6261 6212 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6262 6213 )
6263 6214 def root(ui, repo, **opts):
6264 6215 """print the root (top) of the current working directory
6265 6216
6266 6217 Print the root directory of the current repository.
6267 6218
6268 6219 .. container:: verbose
6269 6220
6270 6221 Template:
6271 6222
6272 6223 The following keywords are supported in addition to the common template
6273 6224 keywords and functions. See also :hg:`help templates`.
6274 6225
6275 6226 :hgpath: String. Path to the .hg directory.
6276 6227 :storepath: String. Path to the directory holding versioned data.
6277 6228
6278 6229 Returns 0 on success.
6279 6230 """
6280 6231 opts = pycompat.byteskwargs(opts)
6281 6232 with ui.formatter(b'root', opts) as fm:
6282 6233 fm.startitem()
6283 6234 fm.write(b'reporoot', b'%s\n', repo.root)
6284 6235 fm.data(hgpath=repo.path, storepath=repo.spath)
6285 6236
6286 6237
6287 6238 @command(
6288 6239 b'serve',
6289 6240 [
6290 6241 (
6291 6242 b'A',
6292 6243 b'accesslog',
6293 6244 b'',
6294 6245 _(b'name of access log file to write to'),
6295 6246 _(b'FILE'),
6296 6247 ),
6297 6248 (b'd', b'daemon', None, _(b'run server in background')),
6298 6249 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6299 6250 (
6300 6251 b'E',
6301 6252 b'errorlog',
6302 6253 b'',
6303 6254 _(b'name of error log file to write to'),
6304 6255 _(b'FILE'),
6305 6256 ),
6306 6257 # use string type, then we can check if something was passed
6307 6258 (
6308 6259 b'p',
6309 6260 b'port',
6310 6261 b'',
6311 6262 _(b'port to listen on (default: 8000)'),
6312 6263 _(b'PORT'),
6313 6264 ),
6314 6265 (
6315 6266 b'a',
6316 6267 b'address',
6317 6268 b'',
6318 6269 _(b'address to listen on (default: all interfaces)'),
6319 6270 _(b'ADDR'),
6320 6271 ),
6321 6272 (
6322 6273 b'',
6323 6274 b'prefix',
6324 6275 b'',
6325 6276 _(b'prefix path to serve from (default: server root)'),
6326 6277 _(b'PREFIX'),
6327 6278 ),
6328 6279 (
6329 6280 b'n',
6330 6281 b'name',
6331 6282 b'',
6332 6283 _(b'name to show in web pages (default: working directory)'),
6333 6284 _(b'NAME'),
6334 6285 ),
6335 6286 (
6336 6287 b'',
6337 6288 b'web-conf',
6338 6289 b'',
6339 6290 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6340 6291 _(b'FILE'),
6341 6292 ),
6342 6293 (
6343 6294 b'',
6344 6295 b'webdir-conf',
6345 6296 b'',
6346 6297 _(b'name of the hgweb config file (DEPRECATED)'),
6347 6298 _(b'FILE'),
6348 6299 ),
6349 6300 (
6350 6301 b'',
6351 6302 b'pid-file',
6352 6303 b'',
6353 6304 _(b'name of file to write process ID to'),
6354 6305 _(b'FILE'),
6355 6306 ),
6356 6307 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6357 6308 (
6358 6309 b'',
6359 6310 b'cmdserver',
6360 6311 b'',
6361 6312 _(b'for remote clients (ADVANCED)'),
6362 6313 _(b'MODE'),
6363 6314 ),
6364 6315 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6365 6316 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6366 6317 (b'6', b'ipv6', None, _(b'use IPv6 instead of IPv4')),
6367 6318 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6368 6319 (b'', b'print-url', None, _(b'start and print only the URL')),
6369 6320 ]
6370 6321 + subrepoopts,
6371 6322 _(b'[OPTION]...'),
6372 6323 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6373 6324 helpbasic=True,
6374 6325 optionalrepo=True,
6375 6326 )
6376 6327 def serve(ui, repo, **opts):
6377 6328 """start stand-alone webserver
6378 6329
6379 6330 Start a local HTTP repository browser and pull server. You can use
6380 6331 this for ad-hoc sharing and browsing of repositories. It is
6381 6332 recommended to use a real web server to serve a repository for
6382 6333 longer periods of time.
6383 6334
6384 6335 Please note that the server does not implement access control.
6385 6336 This means that, by default, anybody can read from the server and
6386 6337 nobody can write to it by default. Set the ``web.allow-push``
6387 6338 option to ``*`` to allow everybody to push to the server. You
6388 6339 should use a real web server if you need to authenticate users.
6389 6340
6390 6341 By default, the server logs accesses to stdout and errors to
6391 6342 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6392 6343 files.
6393 6344
6394 6345 To have the server choose a free port number to listen on, specify
6395 6346 a port number of 0; in this case, the server will print the port
6396 6347 number it uses.
6397 6348
6398 6349 Returns 0 on success.
6399 6350 """
6400 6351
6401 6352 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6402 6353 opts = pycompat.byteskwargs(opts)
6403 6354 if opts[b"print_url"] and ui.verbose:
6404 6355 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6405 6356
6406 6357 if opts[b"stdio"]:
6407 6358 if repo is None:
6408 6359 raise error.RepoError(
6409 6360 _(b"there is no Mercurial repository here (.hg not found)")
6410 6361 )
6411 6362 accesshidden = False
6412 6363 if repo.filtername is None:
6413 6364 allow = ui.configlist(
6414 6365 b'experimental', b'server.allow-hidden-access'
6415 6366 )
6416 6367 user = procutil.getuser()
6417 6368 if allow and scmutil.ismember(ui, user, allow):
6418 6369 accesshidden = True
6419 6370 else:
6420 6371 msg = (
6421 6372 _(
6422 6373 b'ignoring request to access hidden changeset by '
6423 6374 b'unauthorized user: %s\n'
6424 6375 )
6425 6376 % user
6426 6377 )
6427 6378 ui.warn(msg)
6428 6379
6429 6380 s = wireprotoserver.sshserver(ui, repo, accesshidden=accesshidden)
6430 6381 s.serve_forever()
6431 6382 return
6432 6383
6433 6384 service = server.createservice(ui, repo, opts)
6434 6385 return server.runservice(opts, initfn=service.init, runfn=service.run)
6435 6386
6436 6387
6437 6388 @command(
6438 6389 b'shelve',
6439 6390 [
6440 6391 (
6441 6392 b'A',
6442 6393 b'addremove',
6443 6394 None,
6444 6395 _(b'mark new/missing files as added/removed before shelving'),
6445 6396 ),
6446 6397 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6447 6398 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6448 6399 (
6449 6400 b'',
6450 6401 b'date',
6451 6402 b'',
6452 6403 _(b'shelve with the specified commit date'),
6453 6404 _(b'DATE'),
6454 6405 ),
6455 6406 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6456 6407 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6457 6408 (
6458 6409 b'k',
6459 6410 b'keep',
6460 6411 False,
6461 6412 _(b'shelve, but keep changes in the working directory'),
6462 6413 ),
6463 6414 (b'l', b'list', None, _(b'list current shelves')),
6464 6415 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6465 6416 (
6466 6417 b'n',
6467 6418 b'name',
6468 6419 b'',
6469 6420 _(b'use the given name for the shelved commit'),
6470 6421 _(b'NAME'),
6471 6422 ),
6472 6423 (
6473 6424 b'p',
6474 6425 b'patch',
6475 6426 None,
6476 6427 _(
6477 6428 b'output patches for changes (provide the names of the shelved '
6478 6429 b'changes as positional arguments)'
6479 6430 ),
6480 6431 ),
6481 6432 (b'i', b'interactive', None, _(b'interactive mode')),
6482 6433 (
6483 6434 b'',
6484 6435 b'stat',
6485 6436 None,
6486 6437 _(
6487 6438 b'output diffstat-style summary of changes (provide the names of '
6488 6439 b'the shelved changes as positional arguments)'
6489 6440 ),
6490 6441 ),
6491 6442 ]
6492 6443 + cmdutil.walkopts,
6493 6444 _(b'hg shelve [OPTION]... [FILE]...'),
6494 6445 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6495 6446 )
6496 6447 def shelve(ui, repo, *pats, **opts):
6497 6448 """save and set aside changes from the working directory
6498 6449
6499 6450 Shelving takes files that "hg status" reports as not clean, saves
6500 6451 the modifications to a bundle (a shelved change), and reverts the
6501 6452 files so that their state in the working directory becomes clean.
6502 6453
6503 6454 To restore these changes to the working directory, using "hg
6504 6455 unshelve"; this will work even if you switch to a different
6505 6456 commit.
6506 6457
6507 6458 When no files are specified, "hg shelve" saves all not-clean
6508 6459 files. If specific files or directories are named, only changes to
6509 6460 those files are shelved.
6510 6461
6511 6462 In bare shelve (when no files are specified, without interactive,
6512 6463 include and exclude option), shelving remembers information if the
6513 6464 working directory was on newly created branch, in other words working
6514 6465 directory was on different branch than its first parent. In this
6515 6466 situation unshelving restores branch information to the working directory.
6516 6467
6517 6468 Each shelved change has a name that makes it easier to find later.
6518 6469 The name of a shelved change defaults to being based on the active
6519 6470 bookmark, or if there is no active bookmark, the current named
6520 6471 branch. To specify a different name, use ``--name``.
6521 6472
6522 6473 To see a list of existing shelved changes, use the ``--list``
6523 6474 option. For each shelved change, this will print its name, age,
6524 6475 and description; use ``--patch`` or ``--stat`` for more details.
6525 6476
6526 6477 To delete specific shelved changes, use ``--delete``. To delete
6527 6478 all shelved changes, use ``--cleanup``.
6528 6479 """
6529 6480 opts = pycompat.byteskwargs(opts)
6530 6481 allowables = [
6531 6482 (b'addremove', {b'create'}), # 'create' is pseudo action
6532 6483 (b'unknown', {b'create'}),
6533 6484 (b'cleanup', {b'cleanup'}),
6534 6485 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6535 6486 (b'delete', {b'delete'}),
6536 6487 (b'edit', {b'create'}),
6537 6488 (b'keep', {b'create'}),
6538 6489 (b'list', {b'list'}),
6539 6490 (b'message', {b'create'}),
6540 6491 (b'name', {b'create'}),
6541 6492 (b'patch', {b'patch', b'list'}),
6542 6493 (b'stat', {b'stat', b'list'}),
6543 6494 ]
6544 6495
6545 6496 def checkopt(opt):
6546 6497 if opts.get(opt):
6547 6498 for i, allowable in allowables:
6548 6499 if opts[i] and opt not in allowable:
6549 6500 raise error.InputError(
6550 6501 _(
6551 6502 b"options '--%s' and '--%s' may not be "
6552 6503 b"used together"
6553 6504 )
6554 6505 % (opt, i)
6555 6506 )
6556 6507 return True
6557 6508
6558 6509 if checkopt(b'cleanup'):
6559 6510 if pats:
6560 6511 raise error.InputError(
6561 6512 _(b"cannot specify names when using '--cleanup'")
6562 6513 )
6563 6514 return shelvemod.cleanupcmd(ui, repo)
6564 6515 elif checkopt(b'delete'):
6565 6516 return shelvemod.deletecmd(ui, repo, pats)
6566 6517 elif checkopt(b'list'):
6567 6518 return shelvemod.listcmd(ui, repo, pats, opts)
6568 6519 elif checkopt(b'patch') or checkopt(b'stat'):
6569 6520 return shelvemod.patchcmds(ui, repo, pats, opts)
6570 6521 else:
6571 6522 return shelvemod.createcmd(ui, repo, pats, opts)
6572 6523
6573 6524
6574 6525 _NOTTERSE = b'nothing'
6575 6526
6576 6527
6577 6528 @command(
6578 6529 b'status|st',
6579 6530 [
6580 6531 (b'A', b'all', None, _(b'show status of all files')),
6581 6532 (b'm', b'modified', None, _(b'show only modified files')),
6582 6533 (b'a', b'added', None, _(b'show only added files')),
6583 6534 (b'r', b'removed', None, _(b'show only removed files')),
6584 6535 (b'd', b'deleted', None, _(b'show only missing files')),
6585 6536 (b'c', b'clean', None, _(b'show only files without changes')),
6586 6537 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6587 6538 (b'i', b'ignored', None, _(b'show only ignored files')),
6588 6539 (b'n', b'no-status', None, _(b'hide status prefix')),
6589 6540 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6590 6541 (
6591 6542 b'C',
6592 6543 b'copies',
6593 6544 None,
6594 6545 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6595 6546 ),
6596 6547 (
6597 6548 b'0',
6598 6549 b'print0',
6599 6550 None,
6600 6551 _(b'end filenames with NUL, for use with xargs'),
6601 6552 ),
6602 6553 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6603 6554 (
6604 6555 b'',
6605 6556 b'change',
6606 6557 b'',
6607 6558 _(b'list the changed files of a revision'),
6608 6559 _(b'REV'),
6609 6560 ),
6610 6561 ]
6611 6562 + walkopts
6612 6563 + subrepoopts
6613 6564 + formatteropts,
6614 6565 _(b'[OPTION]... [FILE]...'),
6615 6566 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6616 6567 helpbasic=True,
6617 6568 inferrepo=True,
6618 6569 intents={INTENT_READONLY},
6619 6570 )
6620 6571 def status(ui, repo, *pats, **opts):
6621 6572 """show changed files in the working directory
6622 6573
6623 6574 Show status of files in the repository. If names are given, only
6624 6575 files that match are shown. Files that are clean or ignored or
6625 6576 the source of a copy/move operation, are not listed unless
6626 6577 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6627 6578 Unless options described with "show only ..." are given, the
6628 6579 options -mardu are used.
6629 6580
6630 6581 Option -q/--quiet hides untracked (unknown and ignored) files
6631 6582 unless explicitly requested with -u/--unknown or -i/--ignored.
6632 6583
6633 6584 .. note::
6634 6585
6635 6586 :hg:`status` may appear to disagree with diff if permissions have
6636 6587 changed or a merge has occurred. The standard diff format does
6637 6588 not report permission changes and diff only reports changes
6638 6589 relative to one merge parent.
6639 6590
6640 6591 If one revision is given, it is used as the base revision.
6641 6592 If two revisions are given, the differences between them are
6642 6593 shown. The --change option can also be used as a shortcut to list
6643 6594 the changed files of a revision from its first parent.
6644 6595
6645 6596 The codes used to show the status of files are::
6646 6597
6647 6598 M = modified
6648 6599 A = added
6649 6600 R = removed
6650 6601 C = clean
6651 6602 ! = missing (deleted by non-hg command, but still tracked)
6652 6603 ? = not tracked
6653 6604 I = ignored
6654 6605 = origin of the previous file (with --copies)
6655 6606
6656 6607 .. container:: verbose
6657 6608
6658 6609 The -t/--terse option abbreviates the output by showing only the directory
6659 6610 name if all the files in it share the same status. The option takes an
6660 6611 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6661 6612 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6662 6613 for 'ignored' and 'c' for clean.
6663 6614
6664 6615 It abbreviates only those statuses which are passed. Note that clean and
6665 6616 ignored files are not displayed with '--terse ic' unless the -c/--clean
6666 6617 and -i/--ignored options are also used.
6667 6618
6668 6619 The -v/--verbose option shows information when the repository is in an
6669 6620 unfinished merge, shelve, rebase state etc. You can have this behavior
6670 6621 turned on by default by enabling the ``commands.status.verbose`` option.
6671 6622
6672 6623 You can skip displaying some of these states by setting
6673 6624 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6674 6625 'histedit', 'merge', 'rebase', or 'unshelve'.
6675 6626
6676 6627 Template:
6677 6628
6678 6629 The following keywords are supported in addition to the common template
6679 6630 keywords and functions. See also :hg:`help templates`.
6680 6631
6681 6632 :path: String. Repository-absolute path of the file.
6682 6633 :source: String. Repository-absolute path of the file originated from.
6683 6634 Available if ``--copies`` is specified.
6684 6635 :status: String. Character denoting file's status.
6685 6636
6686 6637 Examples:
6687 6638
6688 6639 - show changes in the working directory relative to a
6689 6640 changeset::
6690 6641
6691 6642 hg status --rev 9353
6692 6643
6693 6644 - show changes in the working directory relative to the
6694 6645 current directory (see :hg:`help patterns` for more information)::
6695 6646
6696 6647 hg status re:
6697 6648
6698 6649 - show all changes including copies in an existing changeset::
6699 6650
6700 6651 hg status --copies --change 9353
6701 6652
6702 6653 - get a NUL separated list of added files, suitable for xargs::
6703 6654
6704 6655 hg status -an0
6705 6656
6706 6657 - show more information about the repository status, abbreviating
6707 6658 added, removed, modified, deleted, and untracked paths::
6708 6659
6709 6660 hg status -v -t mardu
6710 6661
6711 6662 Returns 0 on success.
6712 6663
6713 6664 """
6714 6665
6715 6666 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6716 6667 opts = pycompat.byteskwargs(opts)
6717 6668 revs = opts.get(b'rev', [])
6718 6669 change = opts.get(b'change', b'')
6719 6670 terse = opts.get(b'terse', _NOTTERSE)
6720 6671 if terse is _NOTTERSE:
6721 6672 if revs:
6722 6673 terse = b''
6723 6674 else:
6724 6675 terse = ui.config(b'commands', b'status.terse')
6725 6676
6726 6677 if revs and terse:
6727 6678 msg = _(b'cannot use --terse with --rev')
6728 6679 raise error.InputError(msg)
6729 6680 elif change:
6730 6681 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6731 6682 ctx2 = logcmdutil.revsingle(repo, change, None)
6732 6683 ctx1 = ctx2.p1()
6733 6684 else:
6734 6685 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6735 6686 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6736 6687
6737 6688 forcerelativevalue = None
6738 6689 if ui.hasconfig(b'commands', b'status.relative'):
6739 6690 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6740 6691 uipathfn = scmutil.getuipathfn(
6741 6692 repo,
6742 6693 legacyrelativevalue=bool(pats),
6743 6694 forcerelativevalue=forcerelativevalue,
6744 6695 )
6745 6696
6746 6697 if opts.get(b'print0'):
6747 6698 end = b'\0'
6748 6699 else:
6749 6700 end = b'\n'
6750 6701 states = b'modified added removed deleted unknown ignored clean'.split()
6751 6702 show = [k for k in states if opts.get(k)]
6752 6703 if opts.get(b'all'):
6753 6704 show += ui.quiet and (states[:4] + [b'clean']) or states
6754 6705
6755 6706 if not show:
6756 6707 if ui.quiet:
6757 6708 show = states[:4]
6758 6709 else:
6759 6710 show = states[:5]
6760 6711
6761 6712 m = scmutil.match(ctx2, pats, opts)
6762 6713 if terse:
6763 6714 # we need to compute clean and unknown to terse
6764 6715 stat = repo.status(
6765 6716 ctx1.node(),
6766 6717 ctx2.node(),
6767 6718 m,
6768 6719 b'ignored' in show or b'i' in terse,
6769 6720 clean=True,
6770 6721 unknown=True,
6771 6722 listsubrepos=opts.get(b'subrepos'),
6772 6723 )
6773 6724
6774 6725 stat = cmdutil.tersedir(stat, terse)
6775 6726 else:
6776 6727 stat = repo.status(
6777 6728 ctx1.node(),
6778 6729 ctx2.node(),
6779 6730 m,
6780 6731 b'ignored' in show,
6781 6732 b'clean' in show,
6782 6733 b'unknown' in show,
6783 6734 opts.get(b'subrepos'),
6784 6735 )
6785 6736
6786 6737 changestates = zip(
6787 6738 states,
6788 6739 pycompat.iterbytestr(b'MAR!?IC'),
6789 6740 [getattr(stat, s.decode('utf8')) for s in states],
6790 6741 )
6791 6742
6792 6743 copy = {}
6793 6744 show_copies = ui.configbool(b'ui', b'statuscopies')
6794 6745 if opts.get(b'copies') is not None:
6795 6746 show_copies = opts.get(b'copies')
6796 6747 show_copies = (show_copies or opts.get(b'all')) and not opts.get(
6797 6748 b'no_status'
6798 6749 )
6799 6750 if show_copies:
6800 6751 copy = copies.pathcopies(ctx1, ctx2, m)
6801 6752
6802 6753 morestatus = None
6803 6754 if (
6804 6755 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6805 6756 and not ui.plain()
6806 6757 and not opts.get(b'print0')
6807 6758 ):
6808 6759 morestatus = cmdutil.readmorestatus(repo)
6809 6760
6810 6761 ui.pager(b'status')
6811 6762 fm = ui.formatter(b'status', opts)
6812 6763 fmt = b'%s' + end
6813 6764 showchar = not opts.get(b'no_status')
6814 6765
6815 6766 for state, char, files in changestates:
6816 6767 if state in show:
6817 6768 label = b'status.' + state
6818 6769 for f in files:
6819 6770 fm.startitem()
6820 6771 fm.context(ctx=ctx2)
6821 6772 fm.data(itemtype=b'file', path=f)
6822 6773 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6823 6774 fm.plain(fmt % uipathfn(f), label=label)
6824 6775 if f in copy:
6825 6776 fm.data(source=copy[f])
6826 6777 fm.plain(
6827 6778 (b' %s' + end) % uipathfn(copy[f]),
6828 6779 label=b'status.copied',
6829 6780 )
6830 6781 if morestatus:
6831 6782 morestatus.formatfile(f, fm)
6832 6783
6833 6784 if morestatus:
6834 6785 morestatus.formatfooter(fm)
6835 6786 fm.end()
6836 6787
6837 6788
6838 6789 @command(
6839 6790 b'summary|sum',
6840 6791 [(b'', b'remote', None, _(b'check for push and pull'))],
6841 6792 b'[--remote]',
6842 6793 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6843 6794 helpbasic=True,
6844 6795 intents={INTENT_READONLY},
6845 6796 )
6846 6797 def summary(ui, repo, **opts):
6847 6798 """summarize working directory state
6848 6799
6849 6800 This generates a brief summary of the working directory state,
6850 6801 including parents, branch, commit status, phase and available updates.
6851 6802
6852 6803 With the --remote option, this will check the default paths for
6853 6804 incoming and outgoing changes. This can be time-consuming.
6854 6805
6855 6806 Returns 0 on success.
6856 6807 """
6857 6808
6858 6809 ui.pager(b'summary')
6859 6810 ctx = repo[None]
6860 6811 parents = ctx.parents()
6861 6812 pnode = parents[0].node()
6862 6813 marks = []
6863 6814
6864 6815 try:
6865 6816 ms = mergestatemod.mergestate.read(repo)
6866 6817 except error.UnsupportedMergeRecords as e:
6867 6818 s = b' '.join(e.recordtypes)
6868 6819 ui.warn(
6869 6820 _(b'warning: merge state has unsupported record types: %s\n') % s
6870 6821 )
6871 6822 unresolved = []
6872 6823 else:
6873 6824 unresolved = list(ms.unresolved())
6874 6825
6875 6826 for p in parents:
6876 6827 # label with log.changeset (instead of log.parent) since this
6877 6828 # shows a working directory parent *changeset*:
6878 6829 # i18n: column positioning for "hg summary"
6879 6830 ui.write(
6880 6831 _(b'parent: %d:%s ') % (p.rev(), p),
6881 6832 label=logcmdutil.changesetlabels(p),
6882 6833 )
6883 6834 ui.write(b' '.join(p.tags()), label=b'log.tag')
6884 6835 if p.bookmarks():
6885 6836 marks.extend(p.bookmarks())
6886 6837 if p.rev() == -1:
6887 6838 if not len(repo):
6888 6839 ui.write(_(b' (empty repository)'))
6889 6840 else:
6890 6841 ui.write(_(b' (no revision checked out)'))
6891 6842 if p.obsolete():
6892 6843 ui.write(_(b' (obsolete)'))
6893 6844 if p.isunstable():
6894 6845 instabilities = (
6895 6846 ui.label(instability, b'trouble.%s' % instability)
6896 6847 for instability in p.instabilities()
6897 6848 )
6898 6849 ui.write(b' (' + b', '.join(instabilities) + b')')
6899 6850 ui.write(b'\n')
6900 6851 if p.description():
6901 6852 ui.status(
6902 6853 b' ' + p.description().splitlines()[0].strip() + b'\n',
6903 6854 label=b'log.summary',
6904 6855 )
6905 6856
6906 6857 branch = ctx.branch()
6907 6858 bheads = repo.branchheads(branch)
6908 6859 # i18n: column positioning for "hg summary"
6909 6860 m = _(b'branch: %s\n') % branch
6910 6861 if branch != b'default':
6911 6862 ui.write(m, label=b'log.branch')
6912 6863 else:
6913 6864 ui.status(m, label=b'log.branch')
6914 6865
6915 6866 if marks:
6916 6867 active = repo._activebookmark
6917 6868 # i18n: column positioning for "hg summary"
6918 6869 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6919 6870 if active is not None:
6920 6871 if active in marks:
6921 6872 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6922 6873 marks.remove(active)
6923 6874 else:
6924 6875 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6925 6876 for m in marks:
6926 6877 ui.write(b' ' + m, label=b'log.bookmark')
6927 6878 ui.write(b'\n', label=b'log.bookmark')
6928 6879
6929 6880 status = repo.status(unknown=True)
6930 6881
6931 6882 c = repo.dirstate.copies()
6932 6883 copied, renamed = [], []
6933 6884 for d, s in c.items():
6934 6885 if s in status.removed:
6935 6886 status.removed.remove(s)
6936 6887 renamed.append(d)
6937 6888 else:
6938 6889 copied.append(d)
6939 6890 if d in status.added:
6940 6891 status.added.remove(d)
6941 6892
6942 6893 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6943 6894
6944 6895 labels = [
6945 6896 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6946 6897 (ui.label(_(b'%d added'), b'status.added'), status.added),
6947 6898 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6948 6899 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6949 6900 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6950 6901 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6951 6902 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6952 6903 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6953 6904 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6954 6905 ]
6955 6906 t = []
6956 6907 for l, s in labels:
6957 6908 if s:
6958 6909 t.append(l % len(s))
6959 6910
6960 6911 t = b', '.join(t)
6961 6912 cleanworkdir = False
6962 6913
6963 6914 if repo.vfs.exists(b'graftstate'):
6964 6915 t += _(b' (graft in progress)')
6965 6916 if repo.vfs.exists(b'updatestate'):
6966 6917 t += _(b' (interrupted update)')
6967 6918 elif len(parents) > 1:
6968 6919 t += _(b' (merge)')
6969 6920 elif branch != parents[0].branch():
6970 6921 t += _(b' (new branch)')
6971 6922 elif parents[0].closesbranch() and pnode in repo.branchheads(
6972 6923 branch, closed=True
6973 6924 ):
6974 6925 t += _(b' (head closed)')
6975 6926 elif not (
6976 6927 status.modified
6977 6928 or status.added
6978 6929 or status.removed
6979 6930 or renamed
6980 6931 or copied
6981 6932 or subs
6982 6933 ):
6983 6934 t += _(b' (clean)')
6984 6935 cleanworkdir = True
6985 6936 elif pnode not in bheads:
6986 6937 t += _(b' (new branch head)')
6987 6938
6988 6939 if parents:
6989 6940 pendingphase = max(p.phase() for p in parents)
6990 6941 else:
6991 6942 pendingphase = phases.public
6992 6943
6993 6944 if pendingphase > phases.newcommitphase(ui):
6994 6945 t += b' (%s)' % phases.phasenames[pendingphase]
6995 6946
6996 6947 if cleanworkdir:
6997 6948 # i18n: column positioning for "hg summary"
6998 6949 ui.status(_(b'commit: %s\n') % t.strip())
6999 6950 else:
7000 6951 # i18n: column positioning for "hg summary"
7001 6952 ui.write(_(b'commit: %s\n') % t.strip())
7002 6953
7003 6954 # all ancestors of branch heads - all ancestors of parent = new csets
7004 6955 new = len(
7005 6956 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7006 6957 )
7007 6958
7008 6959 if new == 0:
7009 6960 # i18n: column positioning for "hg summary"
7010 6961 ui.status(_(b'update: (current)\n'))
7011 6962 elif pnode not in bheads:
7012 6963 # i18n: column positioning for "hg summary"
7013 6964 ui.write(_(b'update: %d new changesets (update)\n') % new)
7014 6965 else:
7015 6966 # i18n: column positioning for "hg summary"
7016 6967 ui.write(
7017 6968 _(b'update: %d new changesets, %d branch heads (merge)\n')
7018 6969 % (new, len(bheads))
7019 6970 )
7020 6971
7021 6972 t = []
7022 6973 draft = len(repo.revs(b'draft()'))
7023 6974 if draft:
7024 6975 t.append(_(b'%d draft') % draft)
7025 6976 secret = len(repo.revs(b'secret()'))
7026 6977 if secret:
7027 6978 t.append(_(b'%d secret') % secret)
7028 6979
7029 6980 if draft or secret:
7030 6981 ui.status(_(b'phases: %s\n') % b', '.join(t))
7031 6982
7032 6983 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7033 6984 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7034 6985 numtrouble = len(repo.revs(trouble + b"()"))
7035 6986 # We write all the possibilities to ease translation
7036 6987 troublemsg = {
7037 6988 b"orphan": _(b"orphan: %d changesets"),
7038 6989 b"contentdivergent": _(b"content-divergent: %d changesets"),
7039 6990 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7040 6991 }
7041 6992 if numtrouble > 0:
7042 6993 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7043 6994
7044 6995 cmdutil.summaryhooks(ui, repo)
7045 6996
7046 6997 if opts.get('remote'):
7047 6998 needsincoming, needsoutgoing = True, True
7048 6999 else:
7049 7000 needsincoming, needsoutgoing = False, False
7050 7001 for i, o in cmdutil.summaryremotehooks(
7051 7002 ui, repo, pycompat.byteskwargs(opts), None
7052 7003 ):
7053 7004 if i:
7054 7005 needsincoming = True
7055 7006 if o:
7056 7007 needsoutgoing = True
7057 7008 if not needsincoming and not needsoutgoing:
7058 7009 return
7059 7010
7060 7011 def getincoming():
7061 7012 # XXX We should actually skip this if no default is specified, instead
7062 7013 # of passing "default" which will resolve as "./default/" if no default
7063 7014 # path is defined.
7064 7015 path = urlutil.get_unique_pull_path_obj(b'summary', ui, b'default')
7065 7016 sbranch = path.branch
7066 7017 try:
7067 7018 other = hg.peer(repo, {}, path)
7068 7019 except error.RepoError:
7069 7020 if opts.get('remote'):
7070 7021 raise
7071 7022 return path.loc, sbranch, None, None, None
7072 7023 branches = (path.branch, [])
7073 7024 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7074 7025 if revs:
7075 7026 revs = [other.lookup(rev) for rev in revs]
7076 7027 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(path.loc))
7077 7028 with repo.ui.silent():
7078 7029 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7079 7030 return path.loc, sbranch, other, commoninc, commoninc[1]
7080 7031
7081 7032 if needsincoming:
7082 7033 source, sbranch, sother, commoninc, incoming = getincoming()
7083 7034 else:
7084 7035 source = sbranch = sother = commoninc = incoming = None
7085 7036
7086 7037 def getoutgoing():
7087 7038 # XXX We should actually skip this if no default is specified, instead
7088 7039 # of passing "default" which will resolve as "./default/" if no default
7089 7040 # path is defined.
7090 7041 d = None
7091 7042 if b'default-push' in ui.paths:
7092 7043 d = b'default-push'
7093 7044 elif b'default' in ui.paths:
7094 7045 d = b'default'
7095 7046 path = None
7096 7047 if d is not None:
7097 7048 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7098 7049 dest = path.loc
7099 7050 dbranch = path.branch
7100 7051 else:
7101 7052 dest = b'default'
7102 7053 dbranch = None
7103 7054 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7104 7055 if source != dest:
7105 7056 try:
7106 7057 dother = hg.peer(repo, {}, path if path is not None else dest)
7107 7058 except error.RepoError:
7108 7059 if opts.get('remote'):
7109 7060 raise
7110 7061 return dest, dbranch, None, None
7111 7062 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7112 7063 elif sother is None:
7113 7064 # there is no explicit destination peer, but source one is invalid
7114 7065 return dest, dbranch, None, None
7115 7066 else:
7116 7067 dother = sother
7117 7068 if source != dest or (sbranch is not None and sbranch != dbranch):
7118 7069 common = None
7119 7070 else:
7120 7071 common = commoninc
7121 7072 if revs:
7122 7073 revs = [repo.lookup(rev) for rev in revs]
7123 7074 with repo.ui.silent():
7124 7075 outgoing = discovery.findcommonoutgoing(
7125 7076 repo, dother, onlyheads=revs, commoninc=common
7126 7077 )
7127 7078 return dest, dbranch, dother, outgoing
7128 7079
7129 7080 if needsoutgoing:
7130 7081 dest, dbranch, dother, outgoing = getoutgoing()
7131 7082 else:
7132 7083 dest = dbranch = dother = outgoing = None
7133 7084
7134 7085 if opts.get('remote'):
7135 7086 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7136 7087 # The former always sets `sother` (or raises an exception if it can't);
7137 7088 # the latter always sets `outgoing`.
7138 7089 assert sother is not None
7139 7090 assert outgoing is not None
7140 7091
7141 7092 t = []
7142 7093 if incoming:
7143 7094 t.append(_(b'1 or more incoming'))
7144 7095 o = outgoing.missing
7145 7096 if o:
7146 7097 t.append(_(b'%d outgoing') % len(o))
7147 7098 other = dother or sother
7148 7099 if b'bookmarks' in other.listkeys(b'namespaces'):
7149 7100 counts = bookmarks.summary(repo, other)
7150 7101 if counts[0] > 0:
7151 7102 t.append(_(b'%d incoming bookmarks') % counts[0])
7152 7103 if counts[1] > 0:
7153 7104 t.append(_(b'%d outgoing bookmarks') % counts[1])
7154 7105
7155 7106 if t:
7156 7107 # i18n: column positioning for "hg summary"
7157 7108 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7158 7109 else:
7159 7110 # i18n: column positioning for "hg summary"
7160 7111 ui.status(_(b'remote: (synced)\n'))
7161 7112
7162 7113 cmdutil.summaryremotehooks(
7163 7114 ui,
7164 7115 repo,
7165 7116 pycompat.byteskwargs(opts),
7166 7117 (
7167 7118 (source, sbranch, sother, commoninc),
7168 7119 (dest, dbranch, dother, outgoing),
7169 7120 ),
7170 7121 )
7171 7122
7172 7123
7173 7124 @command(
7174 7125 b'tag',
7175 7126 [
7176 7127 (b'f', b'force', None, _(b'force tag')),
7177 7128 (b'l', b'local', None, _(b'make the tag local')),
7178 7129 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7179 7130 (b'', b'remove', None, _(b'remove a tag')),
7180 7131 # -l/--local is already there, commitopts cannot be used
7181 7132 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7182 7133 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7183 7134 ]
7184 7135 + commitopts2,
7185 7136 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7186 7137 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7187 7138 )
7188 7139 def tag(ui, repo, name1, *names, **opts):
7189 7140 """add one or more tags for the current or given revision
7190 7141
7191 7142 Name a particular revision using <name>.
7192 7143
7193 7144 Tags are used to name particular revisions of the repository and are
7194 7145 very useful to compare different revisions, to go back to significant
7195 7146 earlier versions or to mark branch points as releases, etc. Changing
7196 7147 an existing tag is normally disallowed; use -f/--force to override.
7197 7148
7198 7149 If no revision is given, the parent of the working directory is
7199 7150 used.
7200 7151
7201 7152 To facilitate version control, distribution, and merging of tags,
7202 7153 they are stored as a file named ".hgtags" which is managed similarly
7203 7154 to other project files and can be hand-edited if necessary. This
7204 7155 also means that tagging creates a new commit. The file
7205 7156 ".hg/localtags" is used for local tags (not shared among
7206 7157 repositories).
7207 7158
7208 7159 Tag commits are usually made at the head of a branch. If the parent
7209 7160 of the working directory is not a branch head, :hg:`tag` aborts; use
7210 7161 -f/--force to force the tag commit to be based on a non-head
7211 7162 changeset.
7212 7163
7213 7164 See :hg:`help dates` for a list of formats valid for -d/--date.
7214 7165
7215 7166 Since tag names have priority over branch names during revision
7216 7167 lookup, using an existing branch name as a tag name is discouraged.
7217 7168
7218 7169 Returns 0 on success.
7219 7170 """
7220 7171 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7221 7172
7222 7173 with repo.wlock(), repo.lock():
7223 7174 rev_ = b"."
7224 7175 names = [t.strip() for t in (name1,) + names]
7225 7176 if len(names) != len(set(names)):
7226 7177 raise error.InputError(_(b'tag names must be unique'))
7227 7178 for n in names:
7228 7179 scmutil.checknewlabel(repo, n, b'tag')
7229 7180 if not n:
7230 7181 raise error.InputError(
7231 7182 _(b'tag names cannot consist entirely of whitespace')
7232 7183 )
7233 7184 if opts.get('rev'):
7234 7185 rev_ = opts['rev']
7235 7186 message = opts.get('message')
7236 7187 if opts.get('remove'):
7237 7188 if opts.get('local'):
7238 7189 expectedtype = b'local'
7239 7190 else:
7240 7191 expectedtype = b'global'
7241 7192
7242 7193 for n in names:
7243 7194 if repo.tagtype(n) == b'global':
7244 7195 alltags = tagsmod.findglobaltags(ui, repo)
7245 7196 if alltags[n][0] == repo.nullid:
7246 7197 raise error.InputError(
7247 7198 _(b"tag '%s' is already removed") % n
7248 7199 )
7249 7200 if not repo.tagtype(n):
7250 7201 raise error.InputError(_(b"tag '%s' does not exist") % n)
7251 7202 if repo.tagtype(n) != expectedtype:
7252 7203 if expectedtype == b'global':
7253 7204 raise error.InputError(
7254 7205 _(b"tag '%s' is not a global tag") % n
7255 7206 )
7256 7207 else:
7257 7208 raise error.InputError(
7258 7209 _(b"tag '%s' is not a local tag") % n
7259 7210 )
7260 7211 rev_ = b'null'
7261 7212 if not message:
7262 7213 # we don't translate commit messages
7263 7214 message = b'Removed tag %s' % b', '.join(names)
7264 7215 elif not opts.get('force'):
7265 7216 for n in names:
7266 7217 if n in repo.tags():
7267 7218 raise error.InputError(
7268 7219 _(b"tag '%s' already exists (use -f to force)") % n
7269 7220 )
7270 7221 if not opts.get('local'):
7271 7222 p1, p2 = repo.dirstate.parents()
7272 7223 if p2 != repo.nullid:
7273 7224 raise error.StateError(_(b'uncommitted merge'))
7274 7225 bheads = repo.branchheads()
7275 7226 if not opts.get('force') and bheads and p1 not in bheads:
7276 7227 raise error.InputError(
7277 7228 _(
7278 7229 b'working directory is not at a branch head '
7279 7230 b'(use -f to force)'
7280 7231 )
7281 7232 )
7282 7233 node = logcmdutil.revsingle(repo, rev_).node()
7283 7234
7284 7235 # don't allow tagging the null rev or the working directory
7285 7236 if node is None:
7286 7237 raise error.InputError(_(b"cannot tag working directory"))
7287 7238 elif not opts.get('remove') and node == nullid:
7288 7239 raise error.InputError(_(b"cannot tag null revision"))
7289 7240
7290 7241 if not message:
7291 7242 # we don't translate commit messages
7292 7243 message = b'Added tag %s for changeset %s' % (
7293 7244 b', '.join(names),
7294 7245 short(node),
7295 7246 )
7296 7247
7297 7248 date = opts.get('date')
7298 7249 if date:
7299 7250 date = dateutil.parsedate(date)
7300 7251
7301 7252 if opts.get('remove'):
7302 7253 editform = b'tag.remove'
7303 7254 else:
7304 7255 editform = b'tag.add'
7305 7256 editor = cmdutil.getcommiteditor(editform=editform, **opts)
7306 7257
7307 7258 tagsmod.tag(
7308 7259 repo,
7309 7260 names,
7310 7261 node,
7311 7262 message,
7312 7263 opts.get('local'),
7313 7264 opts.get('user'),
7314 7265 date,
7315 7266 editor=editor,
7316 7267 )
7317 7268
7318 7269
7319 7270 @command(
7320 7271 b'tags',
7321 7272 formatteropts,
7322 7273 b'',
7323 7274 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7324 7275 intents={INTENT_READONLY},
7325 7276 )
7326 7277 def tags(ui, repo, **opts):
7327 7278 """list repository tags
7328 7279
7329 7280 This lists both regular and local tags. When the -v/--verbose
7330 7281 switch is used, a third column "local" is printed for local tags.
7331 7282 When the -q/--quiet switch is used, only the tag name is printed.
7332 7283
7333 7284 .. container:: verbose
7334 7285
7335 7286 Template:
7336 7287
7337 7288 The following keywords are supported in addition to the common template
7338 7289 keywords and functions such as ``{tag}``. See also
7339 7290 :hg:`help templates`.
7340 7291
7341 7292 :type: String. ``local`` for local tags.
7342 7293
7343 7294 Returns 0 on success.
7344 7295 """
7345 7296
7346 7297 ui.pager(b'tags')
7347 7298 fm = ui.formatter(b'tags', pycompat.byteskwargs(opts))
7348 7299 hexfunc = fm.hexfunc
7349 7300
7350 7301 for t, n in reversed(repo.tagslist()):
7351 7302 hn = hexfunc(n)
7352 7303 label = b'tags.normal'
7353 7304 tagtype = repo.tagtype(t)
7354 7305 if not tagtype or tagtype == b'global':
7355 7306 tagtype = b''
7356 7307 else:
7357 7308 label = b'tags.' + tagtype
7358 7309
7359 7310 fm.startitem()
7360 7311 fm.context(repo=repo)
7361 7312 fm.write(b'tag', b'%s', t, label=label)
7362 7313 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7363 7314 fm.condwrite(
7364 7315 not ui.quiet,
7365 7316 b'rev node',
7366 7317 fmt,
7367 7318 repo.changelog.rev(n),
7368 7319 hn,
7369 7320 label=label,
7370 7321 )
7371 7322 fm.condwrite(
7372 7323 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7373 7324 )
7374 7325 fm.plain(b'\n')
7375 7326 fm.end()
7376 7327
7377 7328
7378 7329 @command(
7379 7330 b'tip',
7380 7331 [
7381 7332 (b'p', b'patch', None, _(b'show patch')),
7382 7333 (b'g', b'git', None, _(b'use git extended diff format')),
7383 7334 ]
7384 7335 + templateopts,
7385 7336 _(b'[-p] [-g]'),
7386 7337 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7387 7338 )
7388 7339 def tip(ui, repo, **opts):
7389 7340 """show the tip revision (DEPRECATED)
7390 7341
7391 7342 The tip revision (usually just called the tip) is the changeset
7392 7343 most recently added to the repository (and therefore the most
7393 7344 recently changed head).
7394 7345
7395 7346 If you have just made a commit, that commit will be the tip. If
7396 7347 you have just pulled changes from another repository, the tip of
7397 7348 that repository becomes the current tip. The "tip" tag is special
7398 7349 and cannot be renamed or assigned to a different changeset.
7399 7350
7400 7351 This command is deprecated, please use :hg:`heads` instead.
7401 7352
7402 7353 Returns 0 on success.
7403 7354 """
7404 7355 opts = pycompat.byteskwargs(opts)
7405 7356 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7406 7357 displayer.show(repo[b'tip'])
7407 7358 displayer.close()
7408 7359
7409 7360
7410 7361 @command(
7411 7362 b'unbundle',
7412 7363 [
7413 7364 (
7414 7365 b'u',
7415 7366 b'update',
7416 7367 None,
7417 7368 _(b'update to new branch head if changesets were unbundled'),
7418 7369 )
7419 7370 ],
7420 7371 _(b'[-u] FILE...'),
7421 7372 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7422 7373 )
7423 7374 def unbundle(ui, repo, fname1, *fnames, **opts):
7424 7375 """apply one or more bundle files
7425 7376
7426 7377 Apply one or more bundle files generated by :hg:`bundle`.
7427 7378
7428 7379 Returns 0 on success, 1 if an update has unresolved files.
7429 7380 """
7430 7381 fnames = (fname1,) + fnames
7431 7382 modheads = cmdutil.unbundle_files(ui, repo, fnames)
7432 7383
7433 7384 if cmdutil.postincoming(ui, repo, modheads, opts.get('update'), None, None):
7434 7385 return 1
7435 7386 else:
7436 7387 return 0
7437 7388
7438 7389
7439 7390 @command(
7440 7391 b'unshelve',
7441 7392 [
7442 7393 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7443 7394 (
7444 7395 b'c',
7445 7396 b'continue',
7446 7397 None,
7447 7398 _(b'continue an incomplete unshelve operation'),
7448 7399 ),
7449 7400 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7450 7401 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7451 7402 (
7452 7403 b'n',
7453 7404 b'name',
7454 7405 b'',
7455 7406 _(b'restore shelved change with given name'),
7456 7407 _(b'NAME'),
7457 7408 ),
7458 7409 (b't', b'tool', b'', _(b'specify merge tool')),
7459 7410 (
7460 7411 b'',
7461 7412 b'date',
7462 7413 b'',
7463 7414 _(b'set date for temporary commits (DEPRECATED)'),
7464 7415 _(b'DATE'),
7465 7416 ),
7466 7417 ],
7467 7418 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7468 7419 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7469 7420 )
7470 7421 def unshelve(ui, repo, *shelved, **opts):
7471 7422 """restore a shelved change to the working directory
7472 7423
7473 7424 This command accepts an optional name of a shelved change to
7474 7425 restore. If none is given, the most recent shelved change is used.
7475 7426
7476 7427 If a shelved change is applied successfully, the bundle that
7477 7428 contains the shelved changes is moved to a backup location
7478 7429 (.hg/shelve-backup).
7479 7430
7480 7431 Since you can restore a shelved change on top of an arbitrary
7481 7432 commit, it is possible that unshelving will result in a conflict
7482 7433 between your changes and the commits you are unshelving onto. If
7483 7434 this occurs, you must resolve the conflict, then use
7484 7435 ``--continue`` to complete the unshelve operation. (The bundle
7485 7436 will not be moved until you successfully complete the unshelve.)
7486 7437
7487 7438 (Alternatively, you can use ``--abort`` to abandon an unshelve
7488 7439 that causes a conflict. This reverts the unshelved changes, and
7489 7440 leaves the bundle in place.)
7490 7441
7491 7442 If bare shelved change (without interactive, include and exclude
7492 7443 option) was done on newly created branch it would restore branch
7493 7444 information to the working directory.
7494 7445
7495 7446 After a successful unshelve, the shelved changes are stored in a
7496 7447 backup directory. Only the N most recent backups are kept. N
7497 7448 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7498 7449 configuration option.
7499 7450
7500 7451 .. container:: verbose
7501 7452
7502 7453 Timestamp in seconds is used to decide order of backups. More
7503 7454 than ``maxbackups`` backups are kept, if same timestamp
7504 7455 prevents from deciding exact order of them, for safety.
7505 7456
7506 7457 Selected changes can be unshelved with ``--interactive`` flag.
7507 7458 The working directory is updated with the selected changes, and
7508 7459 only the unselected changes remain shelved.
7509 7460 Note: The whole shelve is applied to working directory first before
7510 7461 running interactively. So, this will bring up all the conflicts between
7511 7462 working directory and the shelve, irrespective of which changes will be
7512 7463 unshelved.
7513 7464 """
7514 7465 with repo.wlock():
7515 7466 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7516 7467
7517 7468
7518 7469 statemod.addunfinished(
7519 7470 b'unshelve',
7520 7471 fname=b'shelvedstate',
7521 7472 continueflag=True,
7522 7473 abortfunc=shelvemod.hgabortunshelve,
7523 7474 continuefunc=shelvemod.hgcontinueunshelve,
7524 7475 cmdmsg=_(b'unshelve already in progress'),
7525 7476 )
7526 7477
7527 7478
7528 7479 @command(
7529 7480 b'update|up|checkout|co',
7530 7481 [
7531 7482 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7532 7483 (b'c', b'check', None, _(b'require clean working directory')),
7533 7484 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7534 7485 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7535 7486 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7536 7487 ]
7537 7488 + mergetoolopts,
7538 7489 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7539 7490 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7540 7491 helpbasic=True,
7541 7492 )
7542 7493 def update(ui, repo, node=None, **opts):
7543 7494 """update working directory (or switch revisions)
7544 7495
7545 7496 Update the repository's working directory to the specified
7546 7497 changeset. If no changeset is specified, update to the tip of the
7547 7498 current named branch and move the active bookmark (see :hg:`help
7548 7499 bookmarks`).
7549 7500
7550 7501 Update sets the working directory's parent revision to the specified
7551 7502 changeset (see :hg:`help parents`).
7552 7503
7553 7504 If the changeset is not a descendant or ancestor of the working
7554 7505 directory's parent and there are uncommitted changes, the update is
7555 7506 aborted. With the -c/--check option, the working directory is checked
7556 7507 for uncommitted changes; if none are found, the working directory is
7557 7508 updated to the specified changeset.
7558 7509
7559 7510 .. container:: verbose
7560 7511
7561 7512 The -C/--clean, -c/--check, and -m/--merge options control what
7562 7513 happens if the working directory contains uncommitted changes.
7563 7514 At most of one of them can be specified.
7564 7515
7565 7516 1. If no option is specified, and if
7566 7517 the requested changeset is an ancestor or descendant of
7567 7518 the working directory's parent, the uncommitted changes
7568 7519 are merged into the requested changeset and the merged
7569 7520 result is left uncommitted. If the requested changeset is
7570 7521 not an ancestor or descendant (that is, it is on another
7571 7522 branch), the update is aborted and the uncommitted changes
7572 7523 are preserved.
7573 7524
7574 7525 2. With the -m/--merge option, the update is allowed even if the
7575 7526 requested changeset is not an ancestor or descendant of
7576 7527 the working directory's parent.
7577 7528
7578 7529 3. With the -c/--check option, the update is aborted and the
7579 7530 uncommitted changes are preserved.
7580 7531
7581 7532 4. With the -C/--clean option, uncommitted changes are discarded and
7582 7533 the working directory is updated to the requested changeset.
7583 7534
7584 7535 To cancel an uncommitted merge (and lose your changes), use
7585 7536 :hg:`merge --abort`.
7586 7537
7587 7538 Use null as the changeset to remove the working directory (like
7588 7539 :hg:`clone -U`).
7589 7540
7590 7541 If you want to revert just one file to an older revision, use
7591 7542 :hg:`revert [-r REV] NAME`.
7592 7543
7593 7544 See :hg:`help dates` for a list of formats valid for -d/--date.
7594 7545
7595 7546 Returns 0 on success, 1 if there are unresolved files.
7596 7547 """
7597 7548 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7598 7549 rev = opts.get('rev')
7599 7550 date = opts.get('date')
7600 7551 clean = opts.get('clean')
7601 7552 check = opts.get('check')
7602 7553 merge = opts.get('merge')
7603 7554 if rev and node:
7604 7555 raise error.InputError(_(b"please specify just one revision"))
7605 7556
7606 7557 if ui.configbool(b'commands', b'update.requiredest'):
7607 7558 if not node and not rev and not date:
7608 7559 raise error.InputError(
7609 7560 _(b'you must specify a destination'),
7610 7561 hint=_(b'for example: hg update ".::"'),
7611 7562 )
7612 7563
7613 7564 if rev is None or rev == b'':
7614 7565 rev = node
7615 7566
7616 7567 if date and rev is not None:
7617 7568 raise error.InputError(_(b"you can't specify a revision and a date"))
7618 7569
7619 7570 updatecheck = None
7620 7571 if check or merge is not None and not merge:
7621 7572 updatecheck = b'abort'
7622 7573 elif merge or check is not None and not check:
7623 7574 updatecheck = b'none'
7624 7575
7625 7576 with repo.wlock():
7626 7577 cmdutil.clearunfinished(repo)
7627 7578 if date:
7628 7579 rev = cmdutil.finddate(ui, repo, date)
7629 7580
7630 7581 # if we defined a bookmark, we have to remember the original name
7631 7582 brev = rev
7632 7583 if rev:
7633 7584 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7634 7585 ctx = logcmdutil.revsingle(repo, rev, default=None)
7635 7586 rev = ctx.rev()
7636 7587 hidden = ctx.hidden()
7637 7588 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7638 7589 with ui.configoverride(overrides, b'update'):
7639 7590 ret = hg.updatetotally(
7640 7591 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7641 7592 )
7642 7593 if hidden:
7643 7594 ctxstr = ctx.hex()[:12]
7644 7595 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7645 7596
7646 7597 if ctx.obsolete():
7647 7598 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7648 7599 ui.warn(b"(%s)\n" % obsfatemsg)
7649 7600 return ret
7650 7601
7651 7602
7652 7603 @command(
7653 7604 b'verify',
7654 7605 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7655 7606 helpcategory=command.CATEGORY_MAINTENANCE,
7656 7607 )
7657 7608 def verify(ui, repo, **opts):
7658 7609 """verify the integrity of the repository
7659 7610
7660 7611 Verify the integrity of the current repository.
7661 7612
7662 7613 This will perform an extensive check of the repository's
7663 7614 integrity, validating the hashes and checksums of each entry in
7664 7615 the changelog, manifest, and tracked files, as well as the
7665 7616 integrity of their crosslinks and indices.
7666 7617
7667 7618 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7668 7619 for more information about recovery from corruption of the
7669 7620 repository.
7670 7621
7671 7622 For an alternative UI with a lot more control over the verification
7672 7623 process and better error reporting, try `hg help admin::verify`.
7673 7624
7674 7625 Returns 0 on success, 1 if errors are encountered.
7675 7626 """
7676 7627 level = None
7677 7628 if opts['full']:
7678 7629 level = verifymod.VERIFY_FULL
7679 7630 return hg.verify(repo, level)
7680 7631
7681 7632
7682 7633 @command(
7683 7634 b'version',
7684 7635 [] + formatteropts,
7685 7636 helpcategory=command.CATEGORY_HELP,
7686 7637 norepo=True,
7687 7638 intents={INTENT_READONLY},
7688 7639 )
7689 7640 def version_(ui, **opts):
7690 7641 """output version and copyright information
7691 7642
7692 7643 .. container:: verbose
7693 7644
7694 7645 Template:
7695 7646
7696 7647 The following keywords are supported. See also :hg:`help templates`.
7697 7648
7698 7649 :extensions: List of extensions.
7699 7650 :ver: String. Version number.
7700 7651
7701 7652 And each entry of ``{extensions}`` provides the following sub-keywords
7702 7653 in addition to ``{ver}``.
7703 7654
7704 7655 :bundled: Boolean. True if included in the release.
7705 7656 :name: String. Extension name.
7706 7657 """
7707 7658 if ui.verbose:
7708 7659 ui.pager(b'version')
7709 7660 fm = ui.formatter(b"version", pycompat.byteskwargs(opts))
7710 7661 fm.startitem()
7711 7662 fm.write(
7712 7663 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7713 7664 )
7714 7665 license = _(
7715 7666 b"(see https://mercurial-scm.org for more information)\n"
7716 7667 b"\nCopyright (C) 2005-2024 Olivia Mackall and others\n"
7717 7668 b"This is free software; see the source for copying conditions. "
7718 7669 b"There is NO\nwarranty; "
7719 7670 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7720 7671 )
7721 7672 if not ui.quiet:
7722 7673 fm.plain(license)
7723 7674
7724 7675 if ui.verbose:
7725 7676 fm.plain(_(b"\nEnabled extensions:\n\n"))
7726 7677 # format names and versions into columns
7727 7678 names = []
7728 7679 vers = []
7729 7680 isinternals = []
7730 7681 for name, module in sorted(extensions.extensions()):
7731 7682 names.append(name)
7732 7683 vers.append(extensions.moduleversion(module) or None)
7733 7684 isinternals.append(extensions.ismoduleinternal(module))
7734 7685 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7735 7686 if names:
7736 7687 namefmt = b" %%-%ds " % max(len(n) for n in names)
7737 7688 places = [_(b"external"), _(b"internal")]
7738 7689 for n, v, p in zip(names, vers, isinternals):
7739 7690 fn.startitem()
7740 7691 fn.condwrite(ui.verbose, b"name", namefmt, n)
7741 7692 if ui.verbose:
7742 7693 fn.plain(b"%s " % places[p])
7743 7694 fn.data(bundled=p)
7744 7695 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7745 7696 if ui.verbose:
7746 7697 fn.plain(b"\n")
7747 7698 fn.end()
7748 7699 fm.end()
7749 7700
7750 7701
7751 7702 def loadcmdtable(ui, name, cmdtable):
7752 7703 """Load command functions from specified cmdtable"""
7753 7704 overrides = [cmd for cmd in cmdtable if cmd in table]
7754 7705 if overrides:
7755 7706 ui.warn(
7756 7707 _(b"extension '%s' overrides commands: %s\n")
7757 7708 % (name, b" ".join(overrides))
7758 7709 )
7759 7710 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now